From a3adee3467594b4c0ea5ca39f4ccd52c7cf2fb4f Mon Sep 17 00:00:00 2001
From: austinkelsay <austinkelsay@yahoo.com>
Date: Tue, 19 Nov 2024 17:45:09 -0600
Subject: [PATCH] Full listing out of lesson and course starts / completions in
 progress, new styles for progress and purchase tables

---
 .../charts/GithubContributionChart.js         |  14 +-
 .../profile/DataTables/UserProgressTable.js   | 163 +++++++++++-------
 .../profile/DataTables/UserPurchaseTable.js   |  95 ++++++++++
 src/components/profile/UserProfile.js         |  51 ++----
 src/hooks/tracking/useTrackVideoLesson.js     |   2 +-
 5 files changed, 222 insertions(+), 103 deletions(-)
 create mode 100644 src/components/profile/DataTables/UserPurchaseTable.js

diff --git a/src/components/charts/GithubContributionChart.js b/src/components/charts/GithubContributionChart.js
index 1ef3c36..24c6df0 100644
--- a/src/components/charts/GithubContributionChart.js
+++ b/src/components/charts/GithubContributionChart.js
@@ -91,7 +91,7 @@ const GithubContributionChart = ({ username }) => {
                 {/* Days of week labels */}
                 <div className="flex flex-col gap-[3px] text-[11px] text-gray-400 pr-3">
                     {weekDays.map((day, index) => (
-                        <div key={day} className="h-[12px] leading-[12px]">
+                        <div key={day} className="h-[13px] leading-[13px]">
                             {index % 2 === 0 && day}
                         </div>
                     ))}
@@ -105,7 +105,7 @@ const GithubContributionChart = ({ username }) => {
                                     row[weekIndex] && (
                                         <div
                                             key={`${weekIndex}-${dayIndex}`}
-                                            className={`w-[12px] h-[12px] ${getColor(row[weekIndex].count)} rounded-[2px] cursor-pointer transition-colors duration-100`}
+                                            className={`w-[13px] h-[13px] ${getColor(row[weekIndex].count)} rounded-[2px] cursor-pointer transition-colors duration-100`}
                                             title={`${row[weekIndex].date.toDateString()}: ${row[weekIndex].count} contribution${row[weekIndex].count !== 1 ? 's' : ''}`}
                                         ></div>
                                     )
@@ -130,11 +130,11 @@ const GithubContributionChart = ({ username }) => {
             <div className="text-[11px] text-gray-400 flex items-center justify-end">
                 <span className="mr-2">Less</span>
                 <div className="flex gap-[3px]">
-                    <div className="w-[12px] h-[12px] bg-gray-100 rounded-[2px]"></div>
-                    <div className="w-[12px] h-[12px] bg-green-300 rounded-[2px]"></div>
-                    <div className="w-[12px] h-[12px] bg-green-400 rounded-[2px]"></div>
-                    <div className="w-[12px] h-[12px] bg-green-600 rounded-[2px]"></div>
-                    <div className="w-[12px] h-[12px] bg-green-700 rounded-[2px]"></div>
+                    <div className="w-[13px] h-[13px] bg-gray-100 rounded-[2px]"></div>
+                    <div className="w-[13px] h-[13px] bg-green-300 rounded-[2px]"></div>
+                    <div className="w-[13px] h-[13px] bg-green-400 rounded-[2px]"></div>
+                    <div className="w-[13px] h-[13px] bg-green-600 rounded-[2px]"></div>
+                    <div className="w-[13px] h-[13px] bg-green-700 rounded-[2px]"></div>
                 </div>
                 <span className="ml-2">More</span>
             </div>
diff --git a/src/components/profile/DataTables/UserProgressTable.js b/src/components/profile/DataTables/UserProgressTable.js
index 431aa96..13cf250 100644
--- a/src/components/profile/DataTables/UserProgressTable.js
+++ b/src/components/profile/DataTables/UserProgressTable.js
@@ -13,16 +13,29 @@ const UserProgressTable = ({ session, ndk, windowWidth }) => {
         const progressData = [];
         
         session.user.userCourses.forEach(courseProgress => {
-            progressData.push({
-                id: courseProgress.id,
-                type: 'course',
-                name: courseProgress.course?.name,
-                started: courseProgress.started,
-                startedAt: courseProgress.startedAt,
-                completed: courseProgress.completed,
-                completedAt: courseProgress.completedAt,
-                courseId: courseProgress.courseId
-            });
+            // Add course start entry
+            if (courseProgress.started) {
+                progressData.push({
+                    id: `${courseProgress.id}-start`,
+                    type: 'course',
+                    name: courseProgress.course?.name,
+                    eventType: 'started',
+                    date: courseProgress.startedAt,
+                    courseId: courseProgress.courseId
+                });
+            }
+            
+            // Add course completion entry
+            if (courseProgress.completed) {
+                progressData.push({
+                    id: `${courseProgress.id}-complete`,
+                    type: 'course',
+                    name: courseProgress.course?.name,
+                    eventType: 'completed',
+                    date: courseProgress.completedAt,
+                    courseId: courseProgress.courseId
+                });
+            }
             
             // Add lesson entries
             const courseLessons = session.user.userLessons?.filter(
@@ -30,22 +43,38 @@ const UserProgressTable = ({ session, ndk, windowWidth }) => {
             ) || [];
             
             courseLessons.forEach(lessonProgress => {
-                progressData.push({
-                    id: lessonProgress.id,
-                    type: 'lesson',
-                    name: lessonProgress.lesson?.name,
-                    started: lessonProgress.opened,
-                    startedAt: lessonProgress.openedAt,
-                    completed: lessonProgress.completed,
-                    completedAt: lessonProgress.completedAt,
-                    courseId: courseProgress.courseId,
-                    lessonId: lessonProgress.lessonId,
-                    resourceId: lessonProgress.lesson?.resourceId
-                });
+                // Add lesson start entry
+                if (lessonProgress.opened) {
+                    progressData.push({
+                        id: `${lessonProgress.id}-start`,
+                        type: 'lesson',
+                        name: lessonProgress.lesson?.name,
+                        eventType: 'started',
+                        date: lessonProgress.openedAt,
+                        courseId: courseProgress.courseId,
+                        lessonId: lessonProgress.lessonId,
+                        resourceId: lessonProgress.lesson?.resourceId
+                    });
+                }
+                
+                // Add lesson completion entry
+                if (lessonProgress.completed) {
+                    progressData.push({
+                        id: `${lessonProgress.id}-complete`,
+                        type: 'lesson',
+                        name: lessonProgress.lesson?.name,
+                        eventType: 'completed',
+                        date: lessonProgress.completedAt,
+                        courseId: courseProgress.courseId,
+                        lessonId: lessonProgress.lessonId,
+                        resourceId: lessonProgress.lesson?.resourceId
+                    });
+                }
             });
         });
         
-        return progressData;
+        // Sort by date, most recent first
+        return progressData.sort((a, b) => new Date(b.date) - new Date(a.date));
     };
 
     const header = (
@@ -54,6 +83,37 @@ const UserProgressTable = ({ session, ndk, windowWidth }) => {
         </div>
     );
 
+    const typeTemplate = (rowData) => (
+        <div className="flex items-center gap-2">
+            <i className={`pi ${rowData.type === 'course' ? 'pi-book' : 'pi-file'} text-lg`}></i>
+            <span className="capitalize">{rowData.type}</span>
+        </div>
+    );
+
+    const eventTemplate = (rowData) => (
+        <div className="flex items-center gap-2">
+            <i className={`pi ${rowData.eventType === 'started' ? 'pi-play' : 'pi-check-circle'} 
+                ${rowData.eventType === 'started' ? 'text-blue-500' : 'text-green-500'} text-lg`}></i>
+            <span className="capitalize">{rowData.eventType}</span>
+        </div>
+    );
+
+    const nameTemplate = (rowData) => (
+        <div className="flex items-center">
+            {rowData.type === 'course' 
+                ? <ProgressListItem dTag={rowData.courseId} category="name" type="course" />
+                : <ProgressListItem dTag={rowData.resourceId} category="name" type="lesson" />
+            }
+        </div>
+    );
+
+    const dateTemplate = (rowData) => (
+        <div className="flex items-center gap-2">
+            <i className="pi pi-calendar text-gray-400"></i>
+            <span>{formatDateTime(rowData.date)}</span>
+        </div>
+    );
+
     if (!session || !session?.user || !ndk) {
         return <div className='w-full h-full flex items-center justify-center'><ProgressSpinner /></div>;
     }
@@ -63,60 +123,45 @@ const UserProgressTable = ({ session, ndk, windowWidth }) => {
             emptyMessage="No Courses or Milestones completed"
             value={prepareProgressData()}
             header={header}
-            style={{ maxWidth: windowWidth < 768 ? "100%" : "90%", margin: "0 auto", borderRadius: "10px" }}
+            style={{ maxWidth: windowWidth < 768 ? "100%" : "90%", margin: "0 auto" }}
             pt={{
                 wrapper: {
-                    className: "rounded-lg rounded-t-none"
+                    className: "rounded-b-lg shadow-md"
                 },
                 header: {
-                    className: "rounded-t-lg"
+                    className: "rounded-t-lg border-b border-gray-700"
+                },
+                th: {
+                    className: "text-gray-300 font-semibold"
+                },
+                bodyRow: {
+                    className: "border-b border-gray-700"
+                },
+                bodyCell: {
+                    className: "text-gray-200 p-4"
                 }
             }}
+            stripedRows
         >
             <Column
                 field="type"
                 header="Type"
-                body={(rowData) => (
-                    <span>{rowData.type}</span>
-                )}
+                body={typeTemplate}
             ></Column>
             <Column
-                field="started"
-                header="Started"
-                body={(rowData) => (
-                    <i className={classNames('pi', { 
-                        'pi-check-circle text-blue-500': rowData.started, 
-                        'pi-times-circle text-gray-500': !rowData.started 
-                    })}></i>
-                )}
-            ></Column>
-            <Column
-                field="completed"
-                header="Completed"
-                body={(rowData) => (
-                    <i className={classNames('pi', { 
-                        'pi-check-circle text-green-500': rowData.completed, 
-                        'pi-times-circle text-red-500': !rowData.completed 
-                    })}></i>
-                )}
+                field="eventType"
+                header="Event"
+                body={eventTemplate}
             ></Column>
             <Column
                 field="name"
                 header="Name"
-                body={(rowData) => (
-                    rowData.type === 'course' 
-                        ? <ProgressListItem dTag={rowData.courseId} category="name" type="course" />
-                        : <ProgressListItem dTag={rowData.resourceId} category="name" type="lesson" />
-                )}
+                body={nameTemplate}
             ></Column>
             <Column 
-                body={rowData => {
-                    if (rowData.completed) {
-                        return formatDateTime(rowData.completedAt);
-                    }
-                    return formatDateTime(rowData.startedAt) || formatDateTime(rowData.createdAt);
-                }} 
-                header="Last Activity"
+                field="date"
+                body={dateTemplate}
+                header="Date"
             ></Column>
         </DataTable>
     );
diff --git a/src/components/profile/DataTables/UserPurchaseTable.js b/src/components/profile/DataTables/UserPurchaseTable.js
new file mode 100644
index 0000000..bdac7d8
--- /dev/null
+++ b/src/components/profile/DataTables/UserPurchaseTable.js
@@ -0,0 +1,95 @@
+import React from 'react';
+import { DataTable } from "primereact/datatable";
+import { Column } from "primereact/column";
+import PurchasedListItem from "@/components/content/lists/PurchasedListItem";
+import { formatDateTime } from "@/utils/time";
+
+const UserPurchaseTable = ({ session, windowWidth }) => {
+    const purchasesHeader = (
+        <div className="flex flex-wrap align-items-center justify-content-between gap-2">
+            <span className="text-xl text-gray-200 font-bold">Purchases</span>
+        </div>
+    );
+
+    const costTemplate = (rowData) => (
+        <div className="flex items-center gap-2">
+            <i className="pi pi-wallet text-yellow-500 text-lg"></i>
+            <span>{rowData.amountPaid} sats</span>
+        </div>
+    );
+
+    const nameTemplate = (rowData) => (
+        <div className="flex items-center">
+            <PurchasedListItem 
+                eventId={rowData?.resource?.noteId || rowData?.course?.noteId} 
+                category={rowData?.course ? "courses" : "resources"} 
+            />
+        </div>
+    );
+
+    const categoryTemplate = (rowData) => (
+        <div className="flex items-center gap-2">
+            <i className={`pi ${rowData?.course ? 'pi-book' : 'pi-file'} text-lg`}></i>
+            <span className="capitalize">{rowData?.course ? 'course' : 'resource'}</span>
+        </div>
+    );
+
+    const dateTemplate = (rowData) => (
+        <div className="flex items-center gap-2">
+            <i className="pi pi-calendar text-gray-400"></i>
+            <span>{formatDateTime(rowData?.createdAt)}</span>
+        </div>
+    );
+
+    return (
+        session && session?.user && (
+            <DataTable
+                emptyMessage="No purchases"
+                value={session.user?.purchased}
+                header={purchasesHeader}
+                style={{ maxWidth: windowWidth < 768 ? "100%" : "90%", margin: "0 auto", marginTop: "2rem" }}
+                pt={{
+                    wrapper: {
+                        className: "rounded-b-lg shadow-md"
+                    },
+                    header: {
+                        className: "rounded-t-lg border-b border-gray-700"
+                    },
+                    th: {
+                        className: "text-gray-300 font-semibold"
+                    },
+                    bodyRow: {
+                        className: "border-b border-gray-700"
+                    },
+                    bodyCell: {
+                        className: "text-gray-200 p-4"
+                    }
+                }}
+                stripedRows
+            >
+                <Column 
+                    field="amountPaid" 
+                    header="Cost" 
+                    body={costTemplate}
+                ></Column>
+                <Column
+                    field="name"
+                    header="Name"
+                    body={nameTemplate}
+                ></Column>
+                <Column 
+                    field="category" 
+                    header="Category"
+                    body={categoryTemplate}
+                ></Column>
+                <Column 
+                    field="createdAt" 
+                    header="Date"
+                    body={dateTemplate}
+                ></Column>
+            </DataTable>
+        )
+    );
+};
+
+export default UserPurchaseTable;
diff --git a/src/components/profile/UserProfile.js b/src/components/profile/UserProfile.js
index c7251f8..a83e410 100644
--- a/src/components/profile/UserProfile.js
+++ b/src/components/profile/UserProfile.js
@@ -20,6 +20,7 @@ import { useToast } from "@/hooks/useToast";
 import UserProgress from "@/components/profile/progress/UserProgress";
 import { classNames } from "primereact/utils";
 import UserProgressTable from '@/components/profile/DataTables/UserProgressTable';
+import UserPurchaseTable from '@/components/profile/DataTables/UserPurchaseTable';
 
 const UserProfile = () => {
     const windowWidth = useWindowWidth();
@@ -103,14 +104,14 @@ const UserProfile = () => {
                             className="rounded-full my-4"
                         />
                         <div className="absolute top-8 right-80 max-tab:right-20 max-mob:left-0">
-                            <i 
+                            <i
                                 className="pi pi-ellipsis-h text-2xl cursor-pointer"
                                 onClick={(e) => menu.current.toggle(e)}
                             />
-                            <Menu 
-                                model={menuItems} 
-                                popup 
-                                ref={menu} 
+                            <Menu
+                                model={menuItems}
+                                popup
+                                ref={menu}
                                 id="profile-options-menu"
                             />
                         </div>
@@ -143,37 +144,15 @@ const UserProfile = () => {
                     )}
                     <UserProgress />
                 </div>
-                <UserProgressTable 
-                    session={session} 
-                    ndk={ndk} 
-                    windowWidth={windowWidth} 
-                />
-                {session && session?.user && (
-                    <DataTable
-                        emptyMessage="No purchases"
-                        value={session.user?.purchased}
-                        header={purchasesHeader}
-                        style={{ maxWidth: windowWidth < 768 ? "100%" : "90%", margin: "0 auto", borderRadius: "10px" }}
-                        pt={{
-                            wrapper: {
-                                className: "rounded-lg rounded-t-none"
-                            },
-                            header: {
-                                className: "rounded-t-lg mt-4"
-                            }
-                        }}
-                    >
-                        <Column field="amountPaid" header="Cost"></Column>
-                        <Column
-                            body={(rowData) => {
-                                return <PurchasedListItem eventId={rowData?.resource?.noteId || rowData?.course?.noteId} category={rowData?.course ? "courses" : "resources"} />
-                            }}
-                            header="Name"
-                        ></Column>
-                        <Column body={session.user?.purchased?.some((item) => item.courseId) ? "course" : "resource"} header="Category"></Column>
-                        <Column body={rowData => formatDateTime(rowData?.createdAt)} header="Date"></Column>
-                    </DataTable>
-                )}
+                    <UserProgressTable
+                        session={session}
+                        ndk={ndk}
+                        windowWidth={windowWidth}
+                    />
+                    <UserPurchaseTable
+                        session={session}
+                        windowWidth={windowWidth}
+                    />
             </div>
         )
     );
diff --git a/src/hooks/tracking/useTrackVideoLesson.js b/src/hooks/tracking/useTrackVideoLesson.js
index c2a5af2..9a77669 100644
--- a/src/hooks/tracking/useTrackVideoLesson.js
+++ b/src/hooks/tracking/useTrackVideoLesson.js
@@ -85,7 +85,7 @@ const useTrackVideoLesson = ({lessonId, videoDuration, courseId, videoPlayed, pa
         timerRef.current = setInterval(() => {
           setTimeSpent(prevTime => {
             const newTime = prevTime + 1;
-            // console.log(`⏱️ Time spent: ${newTime}s / ${videoDuration}s (${((newTime/videoDuration)*100).toFixed(1)}%)`);
+            console.log(`⏱️ Time spent: ${newTime}s / ${videoDuration}s (${((newTime/videoDuration)*100).toFixed(1)}%)`);
             return newTime;
           });
         }, 1000);