diff --git a/nextjs/src/app/CourseList.tsx b/nextjs/src/app/CourseList.tsx index 7fa6715..040b114 100644 --- a/nextjs/src/app/CourseList.tsx +++ b/nextjs/src/app/CourseList.tsx @@ -1,5 +1,5 @@ "use client"; -import { useLocalCourseNamesQuery } from "@/hooks/localCoursesHooks"; +import { useLocalCourseNamesQuery } from "@/hooks/localCourse/localCoursesHooks"; import Link from "next/link"; export default function CourseList() { diff --git a/nextjs/src/app/course/[courseName]/CourseSettings.tsx b/nextjs/src/app/course/[courseName]/CourseSettings.tsx index 8c66eaf..09ac4e5 100644 --- a/nextjs/src/app/course/[courseName]/CourseSettings.tsx +++ b/nextjs/src/app/course/[courseName]/CourseSettings.tsx @@ -1,6 +1,7 @@ "use client"; -import { useLocalCourseSettingsQuery } from "@/hooks/localCoursesHooks"; +import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; + export default function CourseSettings({ courseName }: { courseName: string }) { const { data: settings } = useLocalCourseSettingsQuery(courseName); diff --git a/nextjs/src/app/course/[courseName]/context/CourseContextProvider.tsx b/nextjs/src/app/course/[courseName]/context/CourseContextProvider.tsx index 36cd600..57ab783 100644 --- a/nextjs/src/app/course/[courseName]/context/CourseContextProvider.tsx +++ b/nextjs/src/app/course/[courseName]/context/CourseContextProvider.tsx @@ -50,6 +50,7 @@ export default function CourseContextProvider({ return ( { setItemBeingDragged(d); }, diff --git a/nextjs/src/app/course/[courseName]/context/courseContext.ts b/nextjs/src/app/course/[courseName]/context/courseContext.ts index 9ac2399..a7bebb4 100644 --- a/nextjs/src/app/course/[courseName]/context/courseContext.ts +++ b/nextjs/src/app/course/[courseName]/context/courseContext.ts @@ -8,15 +8,17 @@ export interface DraggableItem { } export interface CourseContextInterface { + courseName: string; startItemDrag: (dragging: DraggableItem) => void; endItemDrag: () => void; itemDrop: (droppedOnDay?: Date) => void; } const defaultValue: CourseContextInterface = { - startItemDrag: () => {}, - endItemDrag: () => {}, - itemDrop: () => {}, + startItemDrag: () => { }, + endItemDrag: () => { }, + itemDrop: () => { }, + courseName: "" }; export const CourseContext = diff --git a/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx b/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx index 2d748a8..ca7c89f 100644 --- a/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx +++ b/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx @@ -2,14 +2,50 @@ import { IModuleItem } from "@/models/local/IModuleItem"; import { LocalModule } from "@/models/local/localModules"; import { getDateFromStringOrThrow } from "@/models/local/timeUtils"; import React, { useState } from "react"; +import { useCourseContext } from "../context/courseContext"; +import { + useAssignmentNamesQuery, + useAssignmentsQueries, +} from "@/hooks/localCourse/assignmentHooks"; +import { + useQuizNamesQuery, + useQuizzesQueries, +} from "@/hooks/localCourse/quizHooks"; +import { + usePageNamesQuery, + usePagesQueries, +} from "@/hooks/localCourse/pageHooks"; + +export default function ExpandableModule({ + moduleName, +}: { + moduleName: string; +}) { + const { courseName } = useCourseContext(); + const { data: assignmentNames } = useAssignmentNamesQuery( + courseName, + moduleName + ); + const { data: assignments } = useAssignmentsQueries( + courseName, + moduleName, + assignmentNames + ); + const { data: quizNames } = useQuizNamesQuery(courseName, moduleName); + const { data: quizzes } = useQuizzesQueries( + courseName, + moduleName, + quizNames + ); + const { data: pageNames } = usePageNamesQuery(courseName, moduleName); + const { data: pages } = usePagesQueries(courseName, moduleName, pageNames); -export default function ExpandableModule({ module }: { module: LocalModule }) { const [expanded, setExpanded] = useState(false); const moduleItems: { type: "assignment" | "quiz" | "page"; item: IModuleItem; - }[] = module.assignments + }[] = assignments .map( ( a @@ -21,8 +57,8 @@ export default function ExpandableModule({ module }: { module: LocalModule }) { item: a, }) ) - .concat(module.quizzes.map((q) => ({ type: "quiz", item: q }))) - .concat(module.pages.map((p) => ({ type: "page", item: p }))) + .concat(quizzes.map((q) => ({ type: "quiz", item: q }))) + .concat(pages.map((p) => ({ type: "page", item: p }))) .sort( (a, b) => getDateFromStringOrThrow( @@ -42,7 +78,7 @@ export default function ExpandableModule({ module }: { module: LocalModule }) { role="button" onClick={() => setExpanded((e) => !e)} > - {module.name} + {moduleName}
modules here - {/* {modules.map((m) => ( - - ))} */} + {moduleNames.map((m) => ( + + ))}
); } diff --git a/nextjs/src/hooks/hookHydration.ts b/nextjs/src/hooks/hookHydration.ts index 478c771..c2f067f 100644 --- a/nextjs/src/hooks/hookHydration.ts +++ b/nextjs/src/hooks/hookHydration.ts @@ -1,5 +1,5 @@ import { QueryClient } from "@tanstack/react-query"; -import { localCourseKeys } from "./localCoursesHooks"; +import { localCourseKeys } from "./localCourse/localCoursesHooks"; import { fileStorageService } from "@/services/fileStorage/fileStorageService"; export const hydrateCourses = async (queryClient: QueryClient) => { diff --git a/nextjs/src/hooks/localCourse/assignmentHooks.ts b/nextjs/src/hooks/localCourse/assignmentHooks.ts new file mode 100644 index 0000000..99ab1fb --- /dev/null +++ b/nextjs/src/hooks/localCourse/assignmentHooks.ts @@ -0,0 +1,59 @@ +import axios from "axios"; +import { localCourseKeys } from "./localCoursesHooks"; +import { LocalAssignment } from "@/models/local/assignmnet/localAssignment"; +import { useSuspenseQuery, useSuspenseQueries } from "@tanstack/react-query"; + +export const useAssignmentNamesQuery = ( + courseName: string, + moduleName: string +) => + useSuspenseQuery({ + queryKey: localCourseKeys.moduleAssignmentNames(courseName, moduleName), + queryFn: async (): Promise => { + const url = `/api/courses/${courseName}/modules/${moduleName}/assignments`; + const response = await axios.get(url); + return response.data; + }, + }); + +const getAssignmentQueryConfig = ( + courseName: string, + moduleName: string, + assignmentName: string +) => { + return { + queryKey: localCourseKeys.assignment( + courseName, + moduleName, + assignmentName + ), + queryFn: async (): Promise => { + const url = `/api/courses/${courseName}/modules/${moduleName}/assignments/${assignmentName}`; + const response = await axios.get(url); + return response.data; + }, + }; +}; +export const useAssignmentQuery = ( + courseName: string, + moduleName: string, + assignmentName: string +) => + useSuspenseQuery( + getAssignmentQueryConfig(courseName, moduleName, assignmentName) + ); + +export const useAssignmentsQueries = ( + courseName: string, + moduleName: string, + assignmentNames: string[] +) => + useSuspenseQueries({ + queries: assignmentNames.map((name) => + getAssignmentQueryConfig(courseName, moduleName, name) + ), + combine: (results) => ({ + data: results.map((r) => r.data), + pending: results.some((r) => r.isPending), + }), + }); diff --git a/nextjs/src/hooks/localCoursesHooks.ts b/nextjs/src/hooks/localCourse/localCoursesHooks.ts similarity index 51% rename from nextjs/src/hooks/localCoursesHooks.ts rename to nextjs/src/hooks/localCourse/localCoursesHooks.ts index 9c9d46d..b638489 100644 --- a/nextjs/src/hooks/localCoursesHooks.ts +++ b/nextjs/src/hooks/localCourse/localCoursesHooks.ts @@ -1,12 +1,5 @@ -import { LocalAssignment } from "@/models/local/assignmnet/localAssignment"; -import { LocalCourse, LocalCourseSettings } from "@/models/local/localCourse"; -import { LocalModule } from "@/models/local/localModules"; -import { LocalCoursePage } from "@/models/local/page/localCoursePage"; -import { LocalQuiz } from "@/models/local/quiz/localQuiz"; +import { LocalCourseSettings } from "@/models/local/localCourse"; import { - useMutation, - useQueryClient, - useSuspenseQueries, useSuspenseQuery, } from "@tanstack/react-query"; import axios from "axios"; @@ -97,89 +90,7 @@ export const useModuleNamesQuery = (courseName: string) => }, }); -export const useModuleAssignmentNamesQuery = ( - courseName: string, - moduleName: string -) => - useSuspenseQuery({ - queryKey: localCourseKeys.moduleAssignmentNames(courseName, moduleName), - queryFn: async (): Promise => { - const url = `/api/courses/${courseName}/modules/${moduleName}/assignments`; - const response = await axios.get(url); - return response.data; - }, - }); -export const useModuleQuizNamesQuery = ( - courseName: string, - moduleName: string -) => - useSuspenseQuery({ - queryKey: localCourseKeys.moduleQuizzeNames(courseName, moduleName), - queryFn: async (): Promise => { - const url = `/api/courses/${courseName}/modules/${moduleName}/quizzes`; - const response = await axios.get(url); - return response.data; - }, - }); -export const useModulePageNamesQuery = ( - courseName: string, - moduleName: string -) => - useSuspenseQuery({ - queryKey: localCourseKeys.modulePageNames(courseName, moduleName), - queryFn: async (): Promise => { - const url = `/api/courses/${courseName}/modules/${moduleName}/pages`; - const response = await axios.get(url); - return response.data; - }, - }); - -export const useAssignmentQuery = ( - courseName: string, - moduleName: string, - assignmentName: string -) => - useSuspenseQuery({ - queryKey: localCourseKeys.assignment( - courseName, - moduleName, - assignmentName - ), - queryFn: async (): Promise => { - const url = `/api/courses/${courseName}/modules/${moduleName}/assignments/${assignmentName}`; - const response = await axios.get(url); - return response.data; - }, - }); - -export const useQuizQuery = ( - courseName: string, - moduleName: string, - quizName: string -) => - useSuspenseQuery({ - queryKey: localCourseKeys.quiz(courseName, moduleName, quizName), - queryFn: async (): Promise => { - const url = `/api/courses/${courseName}/modules/${moduleName}/quizzes/${quizName}`; - const response = await axios.get(url); - return response.data; - }, - }); - -export const usePageQuery = ( - courseName: string, - moduleName: string, - pageName: string -) => - useSuspenseQuery({ - queryKey: localCourseKeys.quiz(courseName, moduleName, pageName), - queryFn: async (): Promise => { - const url = `/api/courses/${courseName}/modules/${moduleName}/pages/${pageName}`; - const response = await axios.get(url); - return response.data; - }, - }); // export const useUpdateCourseMutation = (courseName: string) => { // const queryClient = useQueryClient(); diff --git a/nextjs/src/hooks/localCourse/pageHooks.ts b/nextjs/src/hooks/localCourse/pageHooks.ts new file mode 100644 index 0000000..7db4e70 --- /dev/null +++ b/nextjs/src/hooks/localCourse/pageHooks.ts @@ -0,0 +1,49 @@ +import { LocalCoursePage } from "@/models/local/page/localCoursePage"; +import { useSuspenseQueries, useSuspenseQuery } from "@tanstack/react-query"; +import axios from "axios"; +import { localCourseKeys } from "./localCoursesHooks"; + +export const usePageNamesQuery = (courseName: string, moduleName: string) => + useSuspenseQuery({ + queryKey: localCourseKeys.modulePageNames(courseName, moduleName), + queryFn: async (): Promise => { + const url = `/api/courses/${courseName}/modules/${moduleName}/pages`; + const response = await axios.get(url); + return response.data; + }, + }); +export const usePageQuery = ( + courseName: string, + moduleName: string, + pageName: string +) => useSuspenseQuery(getPageQueryConfig(courseName, moduleName, pageName)); + +export const usePagesQueries = ( + courseName: string, + moduleName: string, + pageNames: string[] +) => + useSuspenseQueries({ + queries: pageNames.map((name) => + getPageQueryConfig(courseName, moduleName, name) + ), + combine: (results) => ({ + data: results.map((r) => r.data), + pending: results.some((r) => r.isPending), + }), + }); + +function getPageQueryConfig( + courseName: string, + moduleName: string, + pageName: string +) { + return { + queryKey: localCourseKeys.quiz(courseName, moduleName, pageName), + queryFn: async (): Promise => { + const url = `/api/courses/${courseName}/modules/${moduleName}/pages/${pageName}`; + const response = await axios.get(url); + return response.data; + }, + }; +} diff --git a/nextjs/src/hooks/localCourse/quizHooks.ts b/nextjs/src/hooks/localCourse/quizHooks.ts new file mode 100644 index 0000000..1b95d94 --- /dev/null +++ b/nextjs/src/hooks/localCourse/quizHooks.ts @@ -0,0 +1,50 @@ +import { LocalQuiz } from "@/models/local/quiz/localQuiz"; +import { useSuspenseQueries, useSuspenseQuery } from "@tanstack/react-query"; +import axios from "axios"; +import { localCourseKeys } from "./localCoursesHooks"; + +export const useQuizNamesQuery = (courseName: string, moduleName: string) => + useSuspenseQuery({ + queryKey: localCourseKeys.moduleQuizzeNames(courseName, moduleName), + queryFn: async (): Promise => { + const url = `/api/courses/${courseName}/modules/${moduleName}/quizzes`; + const response = await axios.get(url); + return response.data; + }, + }); + +export const useQuizQuery = ( + courseName: string, + moduleName: string, + quizName: string +) => useSuspenseQuery(getQuizQueryConfig(courseName, moduleName, quizName)); + +export const useQuizzesQueries = ( + courseName: string, + moduleName: string, + quizNames: string[] +) => + useSuspenseQueries({ + queries: quizNames.map((name) => + getQuizQueryConfig(courseName, moduleName, name) + ), + combine: (results) => ({ + data: results.map((r) => r.data), + pending: results.some((r) => r.isPending), + }), + }); + +function getQuizQueryConfig( + courseName: string, + moduleName: string, + quizName: string +) { + return { + queryKey: localCourseKeys.quiz(courseName, moduleName, quizName), + queryFn: async (): Promise => { + const url = `/api/courses/${courseName}/modules/${moduleName}/quizzes/${quizName}`; + const response = await axios.get(url); + return response.data; + }, + }; +} diff --git a/nextjs/src/services/fileStorage/fileStorageService.ts b/nextjs/src/services/fileStorage/fileStorageService.ts index 1b2da93..bce45f6 100644 --- a/nextjs/src/services/fileStorage/fileStorageService.ts +++ b/nextjs/src/services/fileStorage/fileStorageService.ts @@ -127,7 +127,7 @@ export const fileStorageService = { courseName, moduleName, "assignments", - assignmentName + ".md" + assignmentName ); const rawFile = (await fs.readFile(filePath, "utf-8")).replace( /\r\n/g, @@ -142,7 +142,7 @@ export const fileStorageService = { courseName, moduleName, "quizzes", - quizName + ".md" + quizName ); const rawFile = (await fs.readFile(filePath, "utf-8")).replace( /\r\n/g, @@ -157,7 +157,7 @@ export const fileStorageService = { courseName, moduleName, "pages", - pageName + ".md" + pageName ); const rawFile = (await fs.readFile(filePath, "utf-8")).replace( /\r\n/g,