Skip to content

Commit b44e67b

Browse files
authored
Merge pull request #51 from acmutsa/feature/create-lesson-page
Feature/create lesson page
2 parents 2b948b7 + a45abdb commit b44e67b

File tree

16 files changed

+890
-574
lines changed

16 files changed

+890
-574
lines changed

pnpm-lock.yaml

Lines changed: 327 additions & 557 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/actions/admin/lesson.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use server';
2+
import { revalidatePath } from "next/cache";
3+
import { db } from "@/db/index";
4+
import { lessons } from "@/db/schema";
5+
import { lessonFormSchema } from "@/lib/validations/lesson";
6+
import { actionClient } from "@/lib/safe-action";
7+
8+
export const createLessonAction = actionClient
9+
.schema(lessonFormSchema)
10+
.action(async ({ parsedInput }) => {
11+
const {
12+
title,
13+
description,
14+
unitId,
15+
courseId,
16+
mediaType,
17+
contentUrl,
18+
} = parsedInput;
19+
20+
const metadata = JSON.stringify({
21+
title,
22+
description: description ?? "",
23+
});
24+
25+
await db.insert(lessons).values({
26+
unitId,
27+
mediaType, // now real value from the form
28+
metadata,
29+
contentUrl, // real URL from the form
30+
// contentBlobId stays null
31+
});
32+
33+
revalidatePath(`/admin/courses/${courseId}`);
34+
35+
return { success: true };
36+
});

src/actions/admin/tag.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use server';
22
import { tags, users, coursesTags } from "@/db/schema";
3-
import { type TagFormValues } from "@/lib/validations/course";
3+
import { type TagFormValues } from "@/lib/validations/tag";
44
import { db } from "@/db";
55
import { auth } from "@/lib/auth";
66
import { headers } from "next/headers";

src/actions/admin/units.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// src/app/admin/courses/[courseId]/lesson/create/actions.ts
2+
'use server';
3+
4+
import { revalidatePath } from "next/cache";
5+
import { db } from "@/db/index";
6+
import { units } from "@/db/schema";
7+
import { createUnitSchema } from "@/lib/validations/unit";
8+
import { actionClient } from "@/lib/safe-action";
9+
10+
// Create unit (for the modal)
11+
export const createUnitAction = actionClient
12+
.schema(createUnitSchema)
13+
.action(async ({ parsedInput }) => {
14+
const { title, courseId } = parsedInput;
15+
// Minimal insert: courseId (text) + title; position defaults to 1
16+
const [unit] = await db
17+
.insert(units)
18+
.values({
19+
courseId,
20+
title,
21+
})
22+
.returning();
23+
24+
revalidatePath(`/admin/courses/${courseId}`);
25+
26+
return {
27+
success: true,
28+
unitId: unit.id,
29+
unitTitle: unit.title,
30+
};
31+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { db } from "@/db/index";
2+
import { units } from "@/db/schema";
3+
import { eq } from "drizzle-orm";
4+
import { CreateLessonForm } from "@/components/admin/CreateLessonForm";
5+
import {
6+
Card,
7+
CardHeader,
8+
CardTitle,
9+
CardDescription,
10+
CardContent,
11+
} from "@/components/ui/card";
12+
13+
type PageProps = {
14+
params: Promise<{ courseId: string }>;
15+
};
16+
17+
export default async function CreateLessonPage({ params }: PageProps) {
18+
const { courseId } = await params;
19+
20+
const courseUnits = await db
21+
.select()
22+
.from(units)
23+
.where(eq(units.courseId, courseId));
24+
25+
return (
26+
<div className="mx-auto flex max-w-3xl flex-col gap-6">
27+
<div>
28+
<h1 className="text-3xl font-semibold tracking-tight">
29+
Create lesson
30+
</h1>
31+
<p className="mt-1 text-sm text-muted-foreground">
32+
Define lesson details, add content, and assign it to a unit in this
33+
course.
34+
</p>
35+
</div>
36+
37+
<Card className="border border-border/60 shadow-sm">
38+
<CardHeader className="pb-4">
39+
<CardTitle className="text-lg">Lesson details</CardTitle>
40+
<CardDescription>
41+
Fill out the fields below to create a new lesson. You can also
42+
create a new unit on the fly if you need one.
43+
</CardDescription>
44+
</CardHeader>
45+
<CardContent className="pt-2">
46+
<CreateLessonForm
47+
courseId={courseId}
48+
initialUnits={courseUnits.map((u) => ({
49+
id: u.id,
50+
title: u.title ?? `Unit ${u.id}`,
51+
}))}
52+
/>
53+
</CardContent>
54+
</Card>
55+
</div>
56+
);
57+
}
Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
export default async function Page({
2-
params,
3-
}: {
4-
params: Promise<{ courseId: string }>;
1+
2+
export default async function CourseIdPage({
3+
params
4+
}: {
5+
params: Promise<{ courseId: string}>
56
}) {
6-
const { courseId } = await params;
7-
return <div>This is the admin course page for {courseId}</div>;
8-
}
7+
const { courseId } = await params;
8+
const id = Number(courseId);
9+
10+
return <div className="">
11+
Course ID: {id}
12+
</div>
13+
}

src/app/admin/layout.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from "react";
2-
import AdminNavbar from "@/components/AdminNavbar";
2+
import AdminNavbar from "@/components/admin/AdminNavbar";
3+
import { Toaster } from "sonner"
4+
35

46
export default function AdminLayout({
57
children,
@@ -14,6 +16,7 @@ export default function AdminLayout({
1416
{/* Page content */}
1517
<main className="flex-1 p-6">
1618
{children}
19+
<Toaster richColors position="top-right" />
1720
</main>
1821

1922
{/* Footer */}

src/components/admin/CourseTable.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
import { getAllCourses } from "@/actions/admin/course";
3939
import { CourseWithData } from "@/lib/types";
4040
import { useEffect, useState } from "react";
41+
import Link from "next/link";
4142

4243
const columns: ColumnDef<CourseWithData>[] = [
4344
{
@@ -80,7 +81,14 @@ const columns: ColumnDef<CourseWithData>[] = [
8081
</div>
8182
)
8283
},
83-
cell: ({ row }) => <div className="text-left pl-3">{row.getValue("title")}</div>,
84+
cell: ({ row }) => {
85+
const course = row.original;
86+
return (
87+
<div className="text-left pl-3">
88+
<Link className="hover:underline" href={`/admin/courses/${course.id}`}>{row.getValue("title")}</Link>
89+
</div>
90+
);
91+
},
8492
},
8593
{
8694
accessorKey: "description",

0 commit comments

Comments
 (0)