Skip to content

Commit 546f137

Browse files
committed
Feature Create Lesson Page: Expanding functionality to support attachments
1 parent 62e77a5 commit 546f137

File tree

3 files changed

+90
-21
lines changed

3 files changed

+90
-21
lines changed

src/app/admin/courses/[courseId]/lesson/create/CreateLessonForm.tsx

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,18 @@ export function CreateLessonForm({
5454
const [units, setUnits] = React.useState<UnitOption[]>(initialUnits);
5555
const [openUnitDialog, setOpenUnitDialog] = React.useState(false);
5656

57-
// Relax types around useForm to avoid resolver/control generic issues
58-
const form = useForm<LessonFormSchema>({
59-
resolver: zodResolver(lessonFormSchema) as any,
60-
defaultValues: {
61-
title: "",
62-
description: "",
63-
unitId: 0,
64-
courseId,
65-
},
66-
}) as any;
57+
const form = useForm<LessonFormSchema>({
58+
resolver: zodResolver(lessonFormSchema) as any,
59+
defaultValues: {
60+
title: "",
61+
description: "",
62+
unitId: 0,
63+
courseId,
64+
mediaType: "markdown", // sensible default
65+
contentUrl: "",
66+
},
67+
}) as any;
68+
6769

6870
const { execute, status, result } = useAction(createLessonAction, {
6971
onSuccess: ({ data }) => {
@@ -143,6 +145,55 @@ export function CreateLessonForm({
143145
{/* Hidden courseId so it gets sent along with the form */}
144146
<input type="hidden" {...form.register("courseId")} value={courseId} />
145147

148+
{/* Media type */}
149+
<FormField
150+
control={form.control}
151+
name="mediaType"
152+
render={({ field }) => (
153+
<FormItem>
154+
<FormLabel>Content type</FormLabel>
155+
<Select
156+
value={field.value}
157+
onValueChange={field.onChange}
158+
>
159+
<FormControl>
160+
<SelectTrigger>
161+
<SelectValue placeholder="Select a content type" />
162+
</SelectTrigger>
163+
</FormControl>
164+
<SelectContent>
165+
<SelectItem value="youtube">YouTube video</SelectItem>
166+
<SelectItem value="markdown">Markdown page</SelectItem>
167+
<SelectItem value="pdf">PDF</SelectItem>
168+
<SelectItem value="image">Image</SelectItem>
169+
<SelectItem value="audio">Audio</SelectItem>
170+
<SelectItem value="other">Other</SelectItem>
171+
</SelectContent>
172+
</Select>
173+
<FormMessage />
174+
</FormItem>
175+
)}
176+
/>
177+
178+
{/* Main content URL */}
179+
<FormField
180+
control={form.control}
181+
name="contentUrl"
182+
render={({ field }) => (
183+
<FormItem>
184+
<FormLabel>Content URL</FormLabel>
185+
<FormControl>
186+
<Input
187+
placeholder="https://..."
188+
{...field}
189+
/>
190+
</FormControl>
191+
<FormMessage />
192+
</FormItem>
193+
)}
194+
/>
195+
196+
146197
<FormField
147198
control={form.control}
148199
name="unitId"

src/lib/lesson.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1-
// src/lib/validations/lesson.ts
1+
// src/lib/lesson.ts
22
import { z } from "zod";
33

4+
// keep this in sync with mediaTypeValues in your schema.ts
5+
const mediaTypeValues = [
6+
"youtube",
7+
"markdown",
8+
"pdf",
9+
"image",
10+
"audio",
11+
"other",
12+
] as const;
13+
414
export const lessonFormSchema = z.object({
515
title: z.string().min(1, "Title is required"),
616
description: z.string().optional(),
717
unitId: z.coerce.number().int().positive("Unit is required"),
818
courseId: z.string().min(1),
19+
20+
mediaType: z.enum(mediaTypeValues),
21+
contentUrl: z
22+
.string()
23+
.url("Must be a valid URL (starts with http:// or https://)"),
924
});
1025

1126
export type LessonFormSchema = z.infer<typeof lessonFormSchema>;

src/lib/lessonActions.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,31 @@ import {
1414
} from "@/lib/lesson";
1515
import { actionClient } from "@/lib/safe-action";
1616

17-
// Create lesson
1817
export const createLessonAction = actionClient
1918
.schema(lessonFormSchema)
2019
.action(async ({ parsedInput }) => {
21-
const { title, description, unitId, courseId } = parsedInput;
20+
const {
21+
title,
22+
description,
23+
unitId,
24+
courseId,
25+
mediaType,
26+
contentUrl,
27+
} = parsedInput;
2228

23-
// Build metadata JSON stored in lessons.metadata
2429
const metadata = JSON.stringify({
2530
title,
2631
description: description ?? "",
2732
});
2833

2934
await db.insert(lessons).values({
30-
unitId, // integer
31-
metadata, // JSON string
32-
contentUrl: "http://placeholder", // satisfy CHECK (exactly one of contentUrl/contentBlobId)
33-
// mediaType & position are using DB defaults:
34-
// mediaType: "markdown"
35-
// position: 1
35+
unitId,
36+
mediaType, // now real value from the form
37+
metadata,
38+
contentUrl, // real URL from the form
39+
// contentBlobId stays null
3640
});
3741

38-
// Revalidate course page or lessons list
3942
revalidatePath(`/admin/courses/${courseId}`);
4043

4144
return { success: true };

0 commit comments

Comments
 (0)