From b2d9d2bbe67c1aaac2d1d98068f92eadf2fdb472 Mon Sep 17 00:00:00 2001
From: austinkelsay <austinkelsay@yahoo.com>
Date: Sat, 24 Aug 2024 15:55:59 -0500
Subject: [PATCH] Improvements to courseform ui, fix draftcourse delete

---
 prisma/schema.prisma                          |  2 +
 src/components/content/SelectedContentItem.js | 16 +++-
 .../content/courses/DraftCourseDetails.js     |  2 +
 .../content/courses/DraftCourseLesson.js      |  9 ++
 src/components/forms/course/LessonSelector.js | 95 +++++++++++++------
 src/db/models/courseDraftModels.js            | 12 ++-
 src/utils/time.js                             | 13 ++-
 7 files changed, 110 insertions(+), 39 deletions(-)

diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 0ead04f..472a96a 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -80,6 +80,7 @@ model Course {
     updatedAt DateTime   @updatedAt
 }
 
+// Additional resources
 model Resource {
     id        String     @id // Client generates UUID
     userId    String
@@ -93,6 +94,7 @@ model Resource {
     updatedAt DateTime   @updatedAt
 }
 
+// Additional resources
 model Draft {
     id           String        @id @default(uuid())
     userId       String
diff --git a/src/components/content/SelectedContentItem.js b/src/components/content/SelectedContentItem.js
index 3f737da..72888ec 100644
--- a/src/components/content/SelectedContentItem.js
+++ b/src/components/content/SelectedContentItem.js
@@ -2,12 +2,22 @@ import React from "react";
 import Image from "next/image";
 import { useImageProxy } from "@/hooks/useImageProxy";
 import { formatUnixTimestamp } from "@/utils/time";
+import { Button } from 'primereact/button';
 
-const SelectedContentItem = ({ content }) => {
+const SelectedContentItem = ({ content, onRemove }) => {
     const { returnImageProxy } = useImageProxy();
 
     return (
-        <div className="w-full border-2 rounded-lg border-gray-700 p-2 rounded-tr-none rounded-br-none">
+        <div className="w-full border-2 rounded-lg border-gray-700 p-2 rounded-tr-none rounded-br-none relative">
+            <Button
+                icon="pi pi-times"
+                className="absolute top-2 right-2 py-1 px-2 w-auto h-auto"
+                severity="danger"
+                size="small"
+                rounded
+                onClick={onRemove}
+                aria-label="Remove"
+            />
             <div className="flex flex-row gap-4">
                 <Image
                     alt="content thumbnail"
@@ -28,4 +38,4 @@ const SelectedContentItem = ({ content }) => {
     );
 };
 
-export default SelectedContentItem;
+export default SelectedContentItem;
\ No newline at end of file
diff --git a/src/components/content/courses/DraftCourseDetails.js b/src/components/content/courses/DraftCourseDetails.js
index 684f51a..2573edc 100644
--- a/src/components/content/courses/DraftCourseDetails.js
+++ b/src/components/content/courses/DraftCourseDetails.js
@@ -13,6 +13,7 @@ import { useNDKContext } from "@/context/NDKContext";
 import { NDKEvent } from "@nostr-dev-kit/ndk";
 import { findKind0Fields } from '@/utils/nostr';
 import { useToast } from '@/hooks/useToast';
+import { formatDateTime } from '@/utils/time';
 import 'primeicons/primeicons.css';
 
 const MDDisplay = dynamic(
@@ -353,6 +354,7 @@ export default function DraftCourseDetails({ processedEvent, draftId, lessons })
                                 </a>
                             </p>
                         </div>
+                        <p className="pt-8 text-sm text-gray-400">{processedEvent?.createdAt && formatDateTime(processedEvent?.createdAt)}</p>
                     </div>
                     <div className='flex flex-col max-tab:mt-12 max-mob:mt-12'>
                         {processedEvent && (
diff --git a/src/components/content/courses/DraftCourseLesson.js b/src/components/content/courses/DraftCourseLesson.js
index 86c1dd5..92fa509 100644
--- a/src/components/content/courses/DraftCourseLesson.js
+++ b/src/components/content/courses/DraftCourseLesson.js
@@ -3,6 +3,7 @@ import { Tag } from "primereact/tag";
 import { Message } from "primereact/message";
 import Image from "next/image";
 import { useImageProxy } from "@/hooks/useImageProxy";
+import { formatDateTime, formatUnixTimestamp } from "@/utils/time";
 import dynamic from "next/dynamic";
 
 const MDDisplay = dynamic(
@@ -17,6 +18,7 @@ const DraftCourseLesson = ({ lesson, course }) => {
     const [isPublished, setIsPublished] = useState(false);
     useEffect(() => {
         if (lesson?.kind) {
+            console.log(lesson);
             setIsPublished(true);
         } else {
             setIsPublished(false);
@@ -52,6 +54,13 @@ const DraftCourseLesson = ({ lesson, course }) => {
                                 </a>
                             </p>
                         </div>
+                        {
+                            lesson?.createdAt ? (
+                                <p className="pt-8 text-sm text-gray-400">{formatDateTime(lesson?.createdAt)}</p>
+                            ) : (
+                                <p className="pt-8 text-sm text-gray-400">{formatUnixTimestamp(lesson?.published_at)}</p>
+                            )
+                        }
                         <div className='flex flex-row w-full mt-6 items-center'>
                             {isPublished ? (
                                 <Message severity="success" text="published" />
diff --git a/src/components/forms/course/LessonSelector.js b/src/components/forms/course/LessonSelector.js
index d8eaa66..b53c80d 100644
--- a/src/components/forms/course/LessonSelector.js
+++ b/src/components/forms/course/LessonSelector.js
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
 import { Dropdown } from 'primereact/dropdown';
 import { Button } from 'primereact/button';
 import { Dialog } from 'primereact/dialog';
+import { Accordion, AccordionTab } from 'primereact/accordion';
 import ResourceForm from '../ResourceForm';
 import WorkshopForm from '../WorkshopForm';
 import ContentDropdownItem from '@/components/content/dropdowns/ContentDropdownItem';
@@ -82,18 +83,31 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent }) => {
         console.log("contentOptions", contentOptions);
     }, [contentOptions]);
 
-    const handleContentSelect = (selectedContent) => {
-        if (selectedContent && !lessons.some(lesson => lesson.id === selectedContent.id)) {
-            setLessons([...lessons, { ...selectedContent, index: lessons.length }]);
+    const handleContentSelect = (selectedContent, index) => {
+        if (selectedContent) {
+            const updatedLessons = [...lessons];
+            updatedLessons[index] = { ...selectedContent, index };
+            setLessons(updatedLessons);
         }
     };
 
+    const handleRemoveContent = (index) => {
+        const updatedLessons = [...lessons];
+        updatedLessons[index] = { index }; // Reset the lesson to an empty state
+        setLessons(updatedLessons);
+    };
+
     const removeLesson = (index) => {
         const updatedLessons = lessons.filter((_, i) => i !== index)
             .map((lesson, newIndex) => ({ ...lesson, index: newIndex }));
         setLessons(updatedLessons);
     };
 
+    const addNewLesson = (e) => {
+        e.preventDefault(); // Prevent form submission
+        setLessons([...lessons, { index: lessons.length }]);
+    };
+
     const handleNewResourceSave = (newResource) => {
         setLessons([...lessons, { ...newResource, index: lessons.length }]);
         setShowResourceForm(false);
@@ -104,35 +118,58 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent }) => {
         setShowWorkshopForm(false);
     };
 
+    const AccordianHeader = ({lesson, index}) => {
+        return (
+            <div className="flex justify-between items-center">
+                <p>Lesson {index + 1}</p>
+                <Button icon="pi pi-times" className="p-button-danger" onClick={() => removeLesson(index)} />
+            </div>
+        );
+    };
+
     return (
         <div className="mt-8">
             <h3>Lessons</h3>
-            {lessons.map((lesson, index) => (
-                <div key={lesson.id} className="flex mt-4">
-                    <SelectedContentItem content={{ ...lesson, index }} />
-                    <Button 
-                        icon="pi pi-times"
-                        className="p-button-danger rounded-tl-none rounded-bl-none" 
-                        onClick={() => removeLesson(index)}
-                    />
-                </div>
-            ))}
-            <div className="p-inputgroup flex-1 mt-4">
-                <Dropdown
-                    options={contentOptions}
-                    onChange={(e) => handleContentSelect(e.value)}
-                    placeholder="Select Existing Lesson"
-                    optionLabel="label"
-                    optionGroupLabel="label"
-                    optionGroupChildren="items"
-                    itemTemplate={(option) => <ContentDropdownItem content={option.value} onSelect={handleContentSelect} />}
-                    value={null}
-                />
-            </div>
-            <div className="flex mt-4">
-                <Button label="New Resource" onClick={() => setShowResourceForm(true)} className="mr-2" />
-                <Button label="New Workshop" onClick={() => setShowWorkshopForm(true)} />
-            </div>
+            <Accordion multiple>
+                {lessons.map((lesson, index) => (
+                    <AccordionTab key={index} header={<AccordianHeader lesson={lesson} index={index} />}>
+                        <div className="p-inputgroup flex-1 mt-4">
+                            <Dropdown
+                                options={contentOptions}
+                                onChange={(e) => handleContentSelect(e.value, index)}
+                                placeholder="Select Existing Lesson"
+                                optionLabel="label"
+                                optionGroupLabel="label"
+                                optionGroupChildren="items"
+                                itemTemplate={(option) => <ContentDropdownItem content={option.value} onSelect={(content) => handleContentSelect(content, index)} />}
+                                value={lesson.id ? lesson : null}
+                            />
+                        </div>
+                        <div className="flex mt-4">
+                            {lesson.id ? null : (
+                                <>
+                                    <Button label="New Resource" onClick={() => setShowResourceForm(true)} className="mr-2" />
+                                    <Button label="New Workshop" onClick={() => setShowWorkshopForm(true)} className="mr-2" />
+                                </>
+                            )}
+                        </div>
+                        {lesson.id && (
+                            <div className="mt-4">
+                                <SelectedContentItem 
+                                    content={lesson} 
+                                    onRemove={() => handleRemoveContent(index)}
+                                />
+                            </div>
+                        )}
+                    </AccordionTab>
+                ))}
+            </Accordion>
+            <Button 
+                label="Add New Lesson" 
+                onClick={addNewLesson} 
+                className="mt-4" 
+                type="button" // Explicitly set type to "button"
+            />
 
             <Dialog visible={showResourceForm} onHide={() => setShowResourceForm(false)} header="Create New Resource">
                 <ResourceForm onSave={handleNewResourceSave} isPaid={isPaidCourse} />
diff --git a/src/db/models/courseDraftModels.js b/src/db/models/courseDraftModels.js
index e14629a..871ecb6 100644
--- a/src/db/models/courseDraftModels.js
+++ b/src/db/models/courseDraftModels.js
@@ -91,7 +91,15 @@ export const updateCourseDraft = async (id, data) => {
 
 // Delete a CourseDraft by its ID
 export const deleteCourseDraft = async (id) => {
-    return await prisma.courseDraft.delete({
-        where: { id },
+    return await prisma.$transaction(async (prisma) => {
+        // First, delete all associated DraftLessons
+        await prisma.draftLesson.deleteMany({
+            where: { courseDraftId: id },
+        });
+
+        // Then, delete the CourseDraft
+        return await prisma.courseDraft.delete({
+            where: { id },
+        });
     });
 };
\ No newline at end of file
diff --git a/src/utils/time.js b/src/utils/time.js
index 0abe0c7..b3aad10 100644
--- a/src/utils/time.js
+++ b/src/utils/time.js
@@ -1,6 +1,12 @@
 export const formatUnixTimestamp = (time) => {
     const date = new Date(time * 1000); // Convert to milliseconds
-    return date.toDateString();
+    return date.toLocaleDateString("en-US", {
+        timeZone: "UTC",
+        weekday: "long",
+        year: "numeric",
+        month: "long",
+        day: "numeric",
+    });
 }
 
 export const formatDateTime = (isoDate) => {
@@ -12,10 +18,7 @@ export const formatDateTime = (isoDate) => {
         weekday: "long", // "long" for full name, "short" for abbreviated
         year: "numeric",
         month: "long", // "long" for full name, "short" for abbreviated
-        day: "numeric",
-        hour: "numeric",
-        minute: "numeric",
-        second: "numeric",
+        day: "numeric"
     });
 
     return formattedDate;