Improvements to courseform ui, fix draftcourse delete

This commit is contained in:
austinkelsay 2024-08-24 15:55:59 -05:00
parent 128234c7ad
commit b2d9d2bbe6
7 changed files with 110 additions and 39 deletions

View File

@ -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

View File

@ -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"

View File

@ -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 && (

View File

@ -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" />

View File

@ -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>
<Accordion multiple>
{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>
))}
<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)}
onChange={(e) => handleContentSelect(e.value, index)}
placeholder="Select Existing Lesson"
optionLabel="label"
optionGroupLabel="label"
optionGroupChildren="items"
itemTemplate={(option) => <ContentDropdownItem content={option.value} onSelect={handleContentSelect} />}
value={null}
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)} />
<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} />

View File

@ -91,7 +91,15 @@ export const updateCourseDraft = async (id, data) => {
// Delete a CourseDraft by its ID
export const deleteCourseDraft = async (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 },
});
});
};

View File

@ -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;