refactor: swapped 'topic' with 'lesson' semantically

This commit is contained in:
2023-12-17 01:23:28 -06:00
parent aa825f115f
commit c1ea29e2b2
9 changed files with 72 additions and 61 deletions

View File

@@ -4,15 +4,27 @@ import { api } from "~/trpc/server";
export default async function Home() {
const self = await api.user.getSelf();
if (!self?.activeCourseId) redirect("/courses");
const activeCourse = await api.course.getCourse({
courseId: self.activeCourseId,
includeTopics: true,
});
if (!activeCourse) redirect("/courses");
return (
<main className="mx-auto max-w-2xl">
<h1 className="text-2xl font-bold">Home</h1>
<p>Active Course:</p>
<pre>{JSON.stringify(activeCourse, undefined, 2)}</pre>
<h2 className="text-lg font-bold">Topics</h2>
<ol className="list-inside list-decimal">
{activeCourse.topics.map((topic) => (
<li>
<p className="inline">{topic.name}</p>
<p className="ml-5 text-sm">{topic.description}</p>
</li>
))}
</ol>
</main>
);
}

View File

@@ -51,11 +51,6 @@ export default function RootLayout({
Courses
</a>
</li>
<li className="">
<a className="hover:underline" href="/lesson">
Lesson
</a>
</li>
<li className="">
<a className="hover:underline" href="/practice">
Practice

View File

@@ -1,7 +1,12 @@
import Link from "next/link";
export default async function Landing() {
return (
<main className="mx-auto max-w-2xl">
<h1 className="text-2xl font-bold">Landing</h1>
<Link href="/home" className="hover:underline">
Get started
</Link>
</main>
);
}

View File

@@ -13,8 +13,8 @@ import { createTRPCRouter } from "~/server/api/trpc";
export const appRouter = createTRPCRouter({
user: userRouter,
course: courseRouter,
lesson: lessonRouter,
topic: topicRouter,
lesson: lessonRouter,
exercise: exerciseRouter,
});

View File

@@ -13,14 +13,14 @@ export const courseRouter = createTRPCRouter({
.input(
z.object({
courseId: z.number(),
includeLessons: z.boolean().optional().default(false),
includeTopics: z.boolean().optional().default(false),
}),
)
.query(async ({ ctx, input }) => {
const lessons = input.includeLessons ? true : undefined;
const topics = input.includeTopics ? true : undefined;
return await ctx.db.query.courses.findFirst({
where: eq(courses.courseId, input.courseId),
with: { lessons },
with: { topics },
});
}),
});

View File

@@ -1,14 +1,14 @@
import { eq } from "drizzle-orm";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { exercises, topicToExercise } from "~/server/db/schema";
import { exercises, lessonToExercise } from "~/server/db/schema";
export const exerciseRouter = createTRPCRouter({
getAllTopicExercises: protectedProcedure
.input(z.object({ topicId: z.number() }))
getAllLessonExercises: protectedProcedure
.input(z.object({ lessonId: z.number() }))
.query(async ({ ctx, input }) => {
return await ctx.db.query.topicToExercise.findMany({
where: eq(topicToExercise.topicId, input.topicId),
return await ctx.db.query.lessonToExercise.findMany({
where: eq(lessonToExercise.lessonId, input.lessonId),
with: { exercise: true },
});
}),

View File

@@ -1,28 +1,29 @@
import { eq } from "drizzle-orm";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { lessons } from "~/server/db/schema";
import { lessons, topicToLesson } from "~/server/db/schema";
export const lessonRouter = createTRPCRouter({
getAllCourseLessons: protectedProcedure
.input(z.object({ courseId: z.number() }))
getAllTopicLessons: protectedProcedure
.input(z.object({ topicId: z.number() }))
.query(async ({ ctx, input }) => {
return await ctx.db.query.lessons.findMany({
where: eq(lessons.courseId, input.courseId),
return await ctx.db.query.topicToLesson.findMany({
where: eq(topicToLesson.topicId, input.topicId),
with: { lesson: true },
});
}),
getLesson: protectedProcedure
.input(
z.object({
lessonId: z.number(),
includeTopics: z.boolean().optional().default(false),
includeExercises: z.boolean().optional().default(false),
}),
)
.query(async ({ ctx, input }) => {
const topics = input.includeTopics ? true : undefined;
const exercises = input.includeExercises ? true : undefined;
return await ctx.db.query.lessons.findFirst({
where: eq(lessons.lessonId, input.lessonId),
with: { topics },
with: { exercises },
});
}),
});

View File

@@ -1,30 +1,28 @@
import { eq } from "drizzle-orm";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { lessonToTopic, topics } from "~/server/db/schema";
import { topics } from "~/server/db/schema";
export const topicRouter = createTRPCRouter({
getAllLessonTopics: protectedProcedure
.input(z.object({ lessonId: z.number() }))
getAllCourseTopics: protectedProcedure
.input(z.object({ courseId: z.number() }))
.query(async ({ ctx, input }) => {
return await ctx.db.query.lessonToTopic.findMany({
where: eq(lessonToTopic.lessonId, input.lessonId),
with: { topic: true },
return await ctx.db.query.topics.findMany({
where: eq(topics.courseId, input.courseId),
});
}),
getTopic: protectedProcedure
.input(
z.object({
topicId: z.number(),
includeExercises: z.boolean().optional().default(false),
includeLessons: z.boolean().optional().default(false),
}),
)
.query(async ({ ctx, input }) => {
const exercises = input.includeExercises ? true : undefined;
const lessons = input.includeLessons ? true : undefined;
return await ctx.db.query.topics.findFirst({
where: eq(topics.topicId, input.topicId),
with: { exercises },
// with: { exercises: { with: { exercise: { with: { topics: true } } } } },
with: { lessons },
});
}),
});

View File

@@ -32,16 +32,16 @@ export const courses = mysqlTable("course", {
guide: json("guide"),
});
export const lessons = mysqlTable("lesson", {
lessonId: serial("lesson_id").primaryKey(),
export const topics = mysqlTable("topic", {
topicId: serial("topic_id").primaryKey(),
courseId: bigint("course_id", { mode: "number" }).notNull(),
name: varchar("name", { length: 255 }),
description: text("description"),
guide: json("guide"),
});
export const topics = mysqlTable("topic", {
topicId: serial("topic_id").primaryKey(),
export const lessons = mysqlTable("lesson", {
lessonId: serial("lesson_id").primaryKey(),
name: varchar("name", { length: 255 }),
description: text("description"),
guide: json("guide"),
@@ -63,8 +63,8 @@ export const courseHistory = mysqlTable("course_history", {
courseHistoryId: serial("course_history_id").primaryKey(),
userId: varchar("user_id", { length: 255 }),
courseId: bigint("course_id", { mode: "number" }).notNull(),
lessonId: bigint("lesson_id", { mode: "number" }).notNull(),
topicId: bigint("topic_id", { mode: "number" }).notNull(),
lessonId: bigint("lesson_id", { mode: "number" }).notNull(),
exerciseId: bigint("exercise_id", { mode: "number" }).notNull(),
completionTime: timestamp("completion_time", { mode: "date" }),
isCorrect: boolean("is_correct"),
@@ -72,22 +72,22 @@ export const courseHistory = mysqlTable("course_history", {
// === Junctions =========================================================
export const lessonToTopic = mysqlTable(
"lesson_to_topic",
export const topicToLesson = mysqlTable(
"topic_to_lesson",
{
lessonId: bigint("lesson_id", { mode: "number" }).notNull(),
topicId: bigint("topic_id", { mode: "number" }).notNull(),
lessonId: bigint("lesson_id", { mode: "number" }).notNull(),
},
(t) => ({ pk: primaryKey(t.topicId, t.lessonId) }),
);
export const topicToExercise = mysqlTable(
"topic_to_exercise",
export const lessonToExercise = mysqlTable(
"lesson_to_exercise",
{
topicId: bigint("topic_id", { mode: "number" }).notNull(),
lessonId: bigint("lesson_id", { mode: "number" }).notNull(),
exerciseId: bigint("exercise_id", { mode: "number" }).notNull(),
},
(t) => ({ pk: primaryKey(t.topicId, t.exerciseId) }),
(t) => ({ pk: primaryKey(t.lessonId, t.exerciseId) }),
);
// === Relations =========================================================
@@ -101,50 +101,50 @@ export const userRelations = relations(users, ({ one, many }) => ({
}));
export const courseRelations = relations(courses, ({ many }) => ({
lessons: many(lessons),
topics: many(topics),
courseHistory: many(courseHistory),
}));
export const lessonRelations = relations(lessons, ({ one, many }) => ({
export const topicRelations = relations(topics, ({ one, many }) => ({
course: one(courses, {
fields: [lessons.courseId],
fields: [topics.courseId],
references: [courses.courseId],
}),
topics: many(lessonToTopic),
lessons: many(topicToLesson),
courseHistory: many(courseHistory),
}));
export const topicRelations = relations(topics, ({ many }) => ({
lessons: many(lessonToTopic),
exercises: many(topicToExercise),
export const lessonRelations = relations(lessons, ({ many }) => ({
topics: many(topicToLesson),
exercises: many(lessonToExercise),
courseHistory: many(courseHistory),
}));
export const exerciseRelations = relations(exercises, ({ many }) => ({
topics: many(topicToExercise),
lessons: many(lessonToExercise),
courseHistory: many(courseHistory),
}));
export const lessonToTopicRelations = relations(lessonToTopic, ({ one }) => ({
export const topicToLessonRelations = relations(topicToLesson, ({ one }) => ({
lesson: one(lessons, {
fields: [lessonToTopic.lessonId],
fields: [topicToLesson.lessonId],
references: [lessons.lessonId],
}),
topic: one(topics, {
fields: [lessonToTopic.topicId],
fields: [topicToLesson.topicId],
references: [topics.topicId],
}),
}));
export const topicToExerciseRelations = relations(
topicToExercise,
export const lessonToExerciseRelations = relations(
lessonToExercise,
({ one }) => ({
topic: one(topics, {
fields: [topicToExercise.topicId],
references: [topics.topicId],
lesson: one(lessons, {
fields: [lessonToExercise.lessonId],
references: [lessons.lessonId],
}),
exercise: one(exercises, {
fields: [topicToExercise.exerciseId],
fields: [lessonToExercise.exerciseId],
references: [exercises.exerciseId],
}),
}),