diff --git a/nextjs/build.sh b/nextjs/build.sh index c5e2989..d6a3ca2 100755 --- a/nextjs/build.sh +++ b/nextjs/build.sh @@ -1,7 +1,7 @@ #!/bin/bash MAJOR_VERSION="2" -MINOR_VERSION="1" +MINOR_VERSION="2" VERSION="$MAJOR_VERSION.$MINOR_VERSION" docker build -t canvas_management:$VERSION . diff --git a/nextjs/src/app/course/[courseName]/context/drag/useItemDropOnDay.ts b/nextjs/src/app/course/[courseName]/context/drag/useItemDropOnDay.ts index 26ca5a2..027284b 100644 --- a/nextjs/src/app/course/[courseName]/context/drag/useItemDropOnDay.ts +++ b/nextjs/src/app/course/[courseName]/context/drag/useItemDropOnDay.ts @@ -18,6 +18,7 @@ import { DraggableItem } from "./draggingContext"; import { getNewLockDate } from "./getNewLockDate"; import { trpc } from "@/services/trpc/utils"; import { useUpdateQuizMutation } from "@/hooks/localCourse/quizHooks"; +import { useCourseContext } from "../courseContext"; export function useItemDropOnDay({ setIsDragging, @@ -33,6 +34,7 @@ export function useItemDropOnDay({ modal: { isOpen: boolean; openModal: () => void; closeModal: () => void }; }) { const [settings] = useLocalCourseSettingsQuery(); + const { courseName } = useCourseContext(); // const { data: weeks } = useLecturesByWeekQuery(); const [weeks] = trpc.lectures.getLectures.useSuspenseQuery({ courseName: settings.name, @@ -95,6 +97,8 @@ export function useItemDropOnDay({ ...lecture, date: getDateOnlyMarkdownString(dayAsDate), }, + courseName, + settings, }); setModalText(""); setModalCallback(() => {}); @@ -110,6 +114,8 @@ export function useItemDropOnDay({ ...lecture, date: getDateOnlyMarkdownString(dayAsDate), }, + courseName, + settings, }); } } diff --git a/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx b/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx index fba74a7..1c1a746 100644 --- a/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx +++ b/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx @@ -1,9 +1,7 @@ "use client"; import { MonacoEditor } from "@/components/editor/MonacoEditor"; -import { - useLectureUpdateMutation, -} from "@/hooks/localCourse/lectureHooks"; +import { useLectureUpdateMutation } from "@/hooks/localCourse/lectureHooks"; import { lectureToString, parseLecture, @@ -14,10 +12,11 @@ import EditLectureTitle from "./EditLectureTitle"; import LectureButtons from "./LectureButtons"; import { trpc } from "@/services/trpc/utils"; import { useCourseContext } from "../../context/courseContext"; +import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; export default function EditLecture({ lectureDay }: { lectureDay: string }) { - // const { data: weeks } = useLecturesByWeekQuery(); const { courseName } = useCourseContext(); + const [settings] = useLocalCourseSettingsQuery(); const [weeks] = trpc.lectures.getLectures.useSuspenseQuery({ courseName }); const updateLecture = useLectureUpdateMutation(); @@ -42,7 +41,7 @@ Date: ${lectureDay} const parsed = parseLecture(text); if (!lecture || lectureToString(parsed) !== lectureToString(lecture)) { console.log("updating lecture"); - updateLecture.mutate({ lecture: parsed }); + updateLecture.mutate({ lecture: parsed, settings, courseName }); } setError(""); } catch (e: any) { diff --git a/nextjs/src/app/course/[courseName]/modules/CreateModule.tsx b/nextjs/src/app/course/[courseName]/modules/CreateModule.tsx index f738b1d..b9ac7a1 100644 --- a/nextjs/src/app/course/[courseName]/modules/CreateModule.tsx +++ b/nextjs/src/app/course/[courseName]/modules/CreateModule.tsx @@ -2,8 +2,10 @@ import { Expandable } from "@/components/Expandable"; import TextInput from "@/components/form/TextInput"; import { useCreateModuleMutation } from "@/hooks/localCourse/localCourseModuleHooks"; import React, { useState } from "react"; +import { useCourseContext } from "../context/courseContext"; export default function CreateModule() { + const { courseName } = useCourseContext(); const createModule = useCreateModuleMutation(); const [moduleName, setModuleName] = useState(""); return ( @@ -19,7 +21,7 @@ export default function CreateModule() { onSubmit={async (e) => { e.preventDefault(); if (moduleName) { - await createModule.mutateAsync(moduleName); + await createModule.mutateAsync({ moduleName, courseName }); setModuleName(""); } }} diff --git a/nextjs/src/app/course/[courseName]/modules/ModuleList.tsx b/nextjs/src/app/course/[courseName]/modules/ModuleList.tsx index 8bba203..e438ffc 100644 --- a/nextjs/src/app/course/[courseName]/modules/ModuleList.tsx +++ b/nextjs/src/app/course/[courseName]/modules/ModuleList.tsx @@ -4,7 +4,7 @@ import ExpandableModule from "./ExpandableModule"; import CreateModule from "./CreateModule"; export default function ModuleList() { - const { data: moduleNames } = useModuleNamesQuery(); + const [moduleNames] = useModuleNamesQuery(); return (
{/* {moduleNames.map((m) => ( diff --git a/nextjs/src/app/course/[courseName]/modules/NewItemForm.tsx b/nextjs/src/app/course/[courseName]/modules/NewItemForm.tsx index 3220d8f..e4628c7 100644 --- a/nextjs/src/app/course/[courseName]/modules/NewItemForm.tsx +++ b/nextjs/src/app/course/[courseName]/modules/NewItemForm.tsx @@ -28,7 +28,7 @@ export default function NewItemForm({ }) { const [settings] = useLocalCourseSettingsQuery(); const { courseName } = useCourseContext(); - const { data: modules } = useModuleNamesQuery(); + const [modules] = useModuleNamesQuery(); const [type, setType] = useState<"Assignment" | "Quiz" | "Page">( "Assignment" ); diff --git a/nextjs/src/app/layout.tsx b/nextjs/src/app/layout.tsx index 5509364..782c845 100644 --- a/nextjs/src/app/layout.tsx +++ b/nextjs/src/app/layout.tsx @@ -56,33 +56,36 @@ async function DataHydration({ }, }, }); - // const allSettings = await fileStorageService.settings.getAllCoursesSettings(); - // await Promise.all( - // allSettings.map(async (settings) => { - // const courseName = settings.name; - // const moduleNames = await fileStorageService.modules.getModuleNames( - // courseName - // ); - // await Promise.all( - // moduleNames.map( - // async (moduleName) => - // await trpcHelper.assignment.getAllAssignments.fetch({ - // courseName, - // moduleName, - // }) - // ) - // ); - // }) - // ); - // await Promise.all( - // allSettings.map( - // async (settings) => - // await trpcHelper.lectures.getLectures.fetch({ - // courseName: settings.name, - // }) - // ) - // ); + const allSettings = await fileStorageService.settings.getAllCoursesSettings(); + // assignments + await Promise.all( + allSettings.map(async (settings) => { + const courseName = settings.name; + const moduleNames = await fileStorageService.modules.getModuleNames( + courseName + ); + await Promise.all( + moduleNames.map( + async (moduleName) => + await trpcHelper.assignment.getAllAssignments.fetch({ + courseName, + moduleName, + }) + ) + ); + }) + ); + + // lectures + await Promise.all( + allSettings.map( + async (settings) => + await trpcHelper.lectures.getLectures.fetch({ + courseName: settings.name, + }) + ) + ); // await hydrateCourses(trpcHelper.queryClient); diff --git a/nextjs/src/app/providers.tsx b/nextjs/src/app/providers.tsx index e39a1c6..2ef622e 100644 --- a/nextjs/src/app/providers.tsx +++ b/nextjs/src/app/providers.tsx @@ -1,5 +1,5 @@ "use client"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { QueryClientProvider } from "@tanstack/react-query"; import { ReactNode } from "react"; import { getQueryClient } from "./providersQueryClientUtils"; import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling"; diff --git a/nextjs/src/hooks/localCourse/assignmentHooks.ts b/nextjs/src/hooks/localCourse/assignmentHooks.ts index 36971b0..5f68633 100644 --- a/nextjs/src/hooks/localCourse/assignmentHooks.ts +++ b/nextjs/src/hooks/localCourse/assignmentHooks.ts @@ -48,7 +48,7 @@ export const useUpdateAssignmentMutation = () => { export const useCreateAssignmentMutation = () => { const utils = trpc.useUtils(); return trpc.assignment.createAssignment.useMutation({ - onSuccess: (_, { courseName, moduleName, assignmentName }) => { + onSuccess: (_, { courseName, moduleName }) => { utils.assignment.getAllAssignments.invalidate({ courseName, moduleName }); }, }); diff --git a/nextjs/src/hooks/localCourse/courseItemHooks.ts b/nextjs/src/hooks/localCourse/courseItemHooks.ts deleted file mode 100644 index 0ebd0b3..0000000 --- a/nextjs/src/hooks/localCourse/courseItemHooks.ts +++ /dev/null @@ -1,233 +0,0 @@ -// import { localCourseKeys } from "./localCourseKeys"; -// import { -// CourseItemReturnType, -// CourseItemType, -// } from "@/models/local/courseItemTypes"; -// import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; -// import { -// useMutation, -// useQueryClient, -// useSuspenseQueries, -// useSuspenseQuery, -// } from "@tanstack/react-query"; -// // import { -// // createItemOnServer, -// // deleteItemOnServer, -// // getAllItemsFromServer, -// // getItemFromServer, -// // updateItemOnServer, -// // } from "./courseItemServerActions"; - -// export const getAllItemsQueryConfig = ( -// courseName: string, -// moduleName: string, -// type: T -// ) => ({ -// queryKey: localCourseKeys.allItemsOfType(courseName, moduleName, type), -// queryFn: async (): Promise[]> => { -// return await getAllItemsFromServer({ -// courseName, -// moduleName, -// type, -// }); -// }, -// }); - -// export const getItemQueryConfig = ( -// courseName: string, -// moduleName: string, -// name: string, -// type: T -// ) => { -// return { -// queryKey: localCourseKeys.itemOfType(courseName, moduleName, name, type), -// queryFn: async () => { -// return await getItemFromServer({ -// moduleName, -// courseName, -// itemName: name, -// type, -// }); -// }, -// }; -// }; - -// export const useItemQuery = ( -// moduleName: string, -// name: string, -// type: T -// ) => { -// const { courseName } = useCourseContext(); -// return useSuspenseQuery( -// getItemQueryConfig(courseName, moduleName, name, type) -// ); -// }; - -// const useAllItemsQuery = ( -// moduleName: string, -// type: T -// ) => { -// const { courseName } = useCourseContext(); -// return useSuspenseQuery(getAllItemsQueryConfig(courseName, moduleName, type)); -// }; - -// export const useItemsQueries = ( -// moduleName: string, -// type: T -// ) => { -// const { data: allItems } = useAllItemsQuery(moduleName, type); -// const { courseName } = useCourseContext(); -// return useSuspenseQueries({ -// queries: allItems.map((item) => ({ -// ...getItemQueryConfig(courseName, moduleName, item.name, type), -// })), -// combine: (results) => ({ -// data: results.map((r) => r.data), -// pending: results.some((r) => r.isPending), -// }), -// }); -// }; - -// export const useUpdateItemMutation = (type: T) => { -// const { courseName } = useCourseContext(); -// const queryClient = useQueryClient(); -// return useMutation({ -// mutationFn: async ({ -// item, -// moduleName, -// previousModuleName, -// previousItemName, -// itemName, -// }: { -// item: CourseItemReturnType; -// moduleName: string; -// previousModuleName: string; -// previousItemName: string; -// itemName: string; -// }) => { -// if (previousItemName !== item.name || previousModuleName !== moduleName) { -// queryClient.removeQueries({ -// queryKey: localCourseKeys.itemOfType( -// courseName, -// previousModuleName, -// previousItemName, -// type -// ), -// }); -// queryClient.removeQueries({ -// queryKey: localCourseKeys.allItemsOfType( -// courseName, -// previousModuleName, -// type -// ), -// }); -// } - -// queryClient.setQueryData( -// localCourseKeys.itemOfType(courseName, moduleName, itemName, type), -// item -// ); -// await updateItemOnServer({ -// courseName, -// moduleName, -// item, -// type, -// previousItemName, -// previousModuleName, -// itemName, -// }); -// }, -// onSuccess: async (_, { moduleName, itemName }) => { -// await queryClient.invalidateQueries({ -// queryKey: localCourseKeys.itemOfType( -// courseName, -// moduleName, -// itemName, -// type -// ), -// refetchType: "all", -// }); -// await queryClient.invalidateQueries({ -// queryKey: localCourseKeys.allItemsOfType(courseName, moduleName, type), -// refetchType: "all", -// }); -// }, -// }); -// }; - -// export const useCreateItemMutation = (type: T) => { -// const { courseName } = useCourseContext(); -// const queryClient = useQueryClient(); -// return useMutation({ -// mutationFn: async ({ -// item, -// moduleName, -// itemName, -// }: { -// item: CourseItemReturnType; -// moduleName: string; -// itemName: string; -// }) => { -// queryClient.setQueryData( -// localCourseKeys.itemOfType(courseName, moduleName, itemName, type), -// item -// ); -// await createItemOnServer({ -// courseName, -// moduleName, -// item, -// type, -// itemName, -// }); -// }, -// onSuccess: async (_, { moduleName, itemName }) => { -// await queryClient.invalidateQueries({ -// queryKey: localCourseKeys.itemOfType( -// courseName, -// moduleName, -// itemName, -// type -// ), -// }); -// await queryClient.invalidateQueries({ -// queryKey: localCourseKeys.allItemsOfType(courseName, moduleName, type), -// }); -// }, -// }); -// }; - -// export const useDeleteItemMutation = (type: T) => { -// const { courseName } = useCourseContext(); -// const queryClient = useQueryClient(); -// return useMutation({ -// mutationFn: async ({ -// moduleName, -// itemName, -// }: { -// moduleName: string; -// itemName: string; -// }) => { -// await deleteItemOnServer({ -// courseName, -// itemName, -// moduleName, -// type, -// }); -// }, -// onSuccess: async (_, { moduleName, itemName }) => { -// await queryClient.invalidateQueries({ -// queryKey: localCourseKeys.allItemsOfType(courseName, moduleName, type), -// // refetchType: "all", -// }); -// await queryClient.invalidateQueries({ -// queryKey: localCourseKeys.itemOfType( -// courseName, -// moduleName, -// itemName, -// type -// ), -// refetchType: "none", -// }); -// }, -// }); -// }; diff --git a/nextjs/src/hooks/localCourse/courseItemServerActions.ts b/nextjs/src/hooks/localCourse/courseItemServerActions.ts deleted file mode 100644 index c38f403..0000000 --- a/nextjs/src/hooks/localCourse/courseItemServerActions.ts +++ /dev/null @@ -1,237 +0,0 @@ -// "use server"; - -// import { ItemInDay } from "@/app/course/[courseName]/calendar/day/ItemInDay"; -// import { LocalAssignment } from "@/models/local/assignment/localAssignment"; -// import { -// CourseItemReturnType, -// CourseItemType, -// } from "@/models/local/courseItemTypes"; -// import { LocalCoursePage } from "@/models/local/page/localCoursePage"; -// import { LocalQuiz } from "@/models/local/quiz/localQuiz"; -// import { fileStorageService } from "@/services/fileStorage/fileStorageService"; - -// export async function getAllItemsFromServer({ -// courseName, -// moduleName, -// type, -// }: { -// courseName: string; -// moduleName: string; -// type: T; -// }): Promise[]> { -// if (type === "Assignment") { -// const assignments = await fileStorageService.assignments.getAssignments( -// courseName, -// moduleName -// ); -// return assignments as CourseItemReturnType[]; -// } -// if (type === "Quiz") { -// const quizzes = await fileStorageService.quizzes.getQuizzes( -// courseName, -// moduleName -// ); -// return quizzes as CourseItemReturnType[]; -// } -// if (type === "Page") { -// const pages = await fileStorageService.pages.getPages( -// courseName, -// moduleName -// ); -// return pages as CourseItemReturnType[]; -// } - -// throw Error(`cannot get item from server, invalid type: ${type}`) -// } - -// export async function getItemFromServer({ -// courseName, -// moduleName, -// type, -// itemName, -// }: { -// courseName: string; -// moduleName: string; -// type: T; -// itemName: string; -// }): Promise> { -// if (type === "Assignment") { -// const assignment = await fileStorageService.assignments.getAssignment( -// courseName, -// moduleName, -// itemName -// ); -// return assignment as CourseItemReturnType; -// } -// if (type === "Assignment") { -// const quiz = await fileStorageService.quizzes.getQuiz( -// courseName, -// moduleName, -// itemName -// ); -// return quiz as CourseItemReturnType; -// } -// const page = await fileStorageService.pages.getPage( -// courseName, -// moduleName, -// itemName -// ); -// return page as CourseItemReturnType; -// } - -// export async function createItemOnServer({ -// courseName, -// moduleName, -// type, -// item, -// itemName, -// }: { -// courseName: string; -// moduleName: string; -// type: T; -// item: CourseItemReturnType; -// itemName: string; -// }) { -// if (type === "Assignment") { -// const assignment = item as LocalAssignment; -// await fileStorageService.assignments.updateOrCreateAssignment({ -// courseName, -// moduleName, -// assignmentName: itemName, -// assignment, -// }); -// } -// if (type === "Quiz") { -// const quiz = item as LocalQuiz; -// await fileStorageService.quizzes.updateQuiz( -// courseName, -// moduleName, -// itemName, -// quiz -// ); -// } -// if (type === "Page") { -// const page = item as LocalCoursePage; -// await fileStorageService.pages.updatePage( -// courseName, -// moduleName, -// itemName, -// page -// ); -// } -// } - -// export async function updateItemOnServer({ -// item, -// courseName, -// moduleName, -// previousModuleName, -// previousItemName, -// itemName, -// type, -// }: { -// item: CourseItemReturnType; -// courseName: string; -// moduleName: string; -// previousModuleName: string; -// previousItemName: string; -// itemName: string; -// type: T; -// }) { -// if (type === "Assignment") { -// const assignment = item as LocalAssignment; -// await fileStorageService.assignments.updateOrCreateAssignment({ -// courseName, -// moduleName, -// assignmentName: itemName, -// assignment, -// }); - -// if ( -// assignment.name !== previousItemName || -// moduleName !== previousModuleName -// ) { -// fileStorageService.assignments.delete({ -// courseName, -// moduleName: previousModuleName, -// assignmentName: previousItemName, -// }); -// } -// } -// if (type === "Quiz") { -// const quiz = item as LocalQuiz; -// await fileStorageService.quizzes.updateQuiz( -// courseName, -// moduleName, -// itemName, -// quiz -// ); - -// if ( -// previousModuleName && -// previousItemName && -// (quiz.name !== previousItemName || moduleName !== previousModuleName) -// ) { -// fileStorageService.quizzes.delete({ -// courseName, -// moduleName: previousModuleName, -// quizName: previousItemName, -// }); -// } -// } -// if (type === "Page") { -// const page = item as LocalCoursePage; -// await fileStorageService.pages.updatePage( -// courseName, -// moduleName, -// itemName, -// page -// ); - -// if ( -// previousModuleName && -// previousItemName && -// (page.name !== previousItemName || moduleName !== previousModuleName) -// ) { -// fileStorageService.pages.delete({ -// courseName, -// moduleName: previousModuleName, -// pageName: previousItemName, -// }); -// } -// } -// } - -// export async function deleteItemOnServer({ -// courseName, -// moduleName, -// itemName, -// type, -// }: { -// courseName: string; -// moduleName: string; -// itemName: string; -// type: T; -// }) { -// if (type === "Assignment") { -// await fileStorageService.assignments.delete({ -// courseName, -// moduleName, -// assignmentName: itemName, -// }); -// } -// if (type === "Quiz") { -// await fileStorageService.quizzes.delete({ -// courseName, -// moduleName, -// quizName: itemName, -// }); -// } -// if (type === "Page") { -// await fileStorageService.pages.delete({ -// courseName, -// moduleName, -// pageName: itemName, -// }); -// } -// } diff --git a/nextjs/src/hooks/localCourse/lectureHooks.ts b/nextjs/src/hooks/localCourse/lectureHooks.ts index 67bddf8..84a6d50 100644 --- a/nextjs/src/hooks/localCourse/lectureHooks.ts +++ b/nextjs/src/hooks/localCourse/lectureHooks.ts @@ -1,51 +1,10 @@ -import { - useMutation, - useQueryClient, - useSuspenseQuery, -} from "@tanstack/react-query"; -import { lectureKeys } from "./lectureKeys"; -import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; -import { - deleteLecture, - getLectures, - updateLecture, -} from "@/services/fileStorage/lectureFileStorageService"; -import { Lecture } from "@/models/local/lecture"; -import { useLocalCourseSettingsQuery } from "./localCoursesHooks"; - -// export const getLecturesQueryConfig = (courseName: string) => -// ({ -// queryKey: lectureKeys.allLectures(courseName), -// queryFn: async () => await getLectures(courseName), -// } as const); - -// export const useLecturesByWeekQuery = () => { -// const { courseName } = useCourseContext(); -// return useSuspenseQuery(getLecturesQueryConfig(courseName)); -// }; +import { trpc } from "@/services/trpc/utils"; export const useLectureUpdateMutation = () => { - const { courseName } = useCourseContext(); - const [settings] = useLocalCourseSettingsQuery(); - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async ({ - lecture, - previousDay, - }: { - lecture: Lecture; - previousDay?: string; - }) => { - await updateLecture(courseName, settings, lecture); - - if (previousDay && previousDay !== lecture.date) { - await deleteLecture(courseName, settings, previousDay); - } - }, + const utils = trpc.useUtils(); + return trpc.lectures.updateLecture.useMutation({ onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: lectureKeys.allLectures(courseName), - }); + utils.lectures.getLectures.invalidate(); }, }); }; diff --git a/nextjs/src/hooks/localCourse/lectureKeys.ts b/nextjs/src/hooks/localCourse/lectureKeys.ts deleted file mode 100644 index e865898..0000000 --- a/nextjs/src/hooks/localCourse/lectureKeys.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const lectureKeys = { - allLectures: (courseName: string) => ["lectures", courseName] as const -} \ No newline at end of file diff --git a/nextjs/src/hooks/localCourse/localCourseKeys.ts b/nextjs/src/hooks/localCourse/localCourseKeys.ts deleted file mode 100644 index 8b3c4d7..0000000 --- a/nextjs/src/hooks/localCourse/localCourseKeys.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CourseItemType, typeToFolder } from "@/models/local/courseItemTypes"; - -export const localCourseKeys = { - allCoursesSettings: ["all courses settings"] as const, - allCoursesNames: ["all courses names"] as const, - settings: (courseName: string) => - ["course details", courseName, "settings"] as const, - moduleNames: (courseName: string) => - [ - "course details", - courseName, - "modules", - { type: "names" } as const, - ] as const, - allItemsOfType: ( - courseName: string, - moduleName: string, - type: CourseItemType - ) => - [ - "course details", - courseName, - "modules", - moduleName, - typeToFolder[type], - { type: "all" }, - ] as const, - itemOfType: ( - courseName: string, - moduleName: string, - name: string, - type: CourseItemType - ) => - [ - "course details", - courseName, - "modules", - moduleName, - typeToFolder[type], - name, - ] as const, -}; diff --git a/nextjs/src/hooks/localCourse/localCourseModuleHooks.ts b/nextjs/src/hooks/localCourse/localCourseModuleHooks.ts index c8c356c..615d393 100644 --- a/nextjs/src/hooks/localCourse/localCourseModuleHooks.ts +++ b/nextjs/src/hooks/localCourse/localCourseModuleHooks.ts @@ -1,38 +1,19 @@ import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; -import { - useMutation, - useQueryClient, - useSuspenseQuery, -} from "@tanstack/react-query"; -import { localCourseKeys } from "./localCourseKeys"; -import { - createModuleOnServer, - getModuleNamesFromServer, -} from "./localCourseModuleServerActions"; import { trpc } from "@/services/trpc/utils"; import { LocalAssignment } from "@/models/local/assignment/localAssignment"; export const useModuleNamesQuery = () => { const { courseName } = useCourseContext(); - return useSuspenseQuery({ - queryKey: localCourseKeys.moduleNames(courseName), - queryFn: async (): Promise => { - return await getModuleNamesFromServer({ courseName }); - }, - }); + return trpc.module.getModuleNames.useSuspenseQuery({ courseName }); }; export const useCreateModuleMutation = () => { const { courseName } = useCourseContext(); - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async (moduleName: string) => { - await createModuleOnServer({ courseName, moduleName }); - }, + const utils = trpc.useUtils(); + + return trpc.module.createModule.useMutation({ onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: localCourseKeys.moduleNames(courseName), - }); + utils.module.getModuleNames.invalidate({ courseName }); }, }); }; @@ -41,67 +22,39 @@ export const useAllCourseDataQuery = (): { assignmentsAndModules: { moduleName: string; assignment: LocalAssignment; -}[]; + }[]; quizzesAndModules: any[]; pagesAndModules: any[]; } => { const { courseName } = useCourseContext(); - const { data: moduleNames } = useModuleNamesQuery(); - + const [moduleNames] = useModuleNamesQuery(); const [assignments] = trpc.useSuspenseQueries((t) => moduleNames.map((moduleName) => t.assignment.getAllAssignments({ courseName, moduleName }) ) ); + const [quizzes] = trpc.useSuspenseQueries((t) => + moduleNames.map((moduleName) => + t.quiz.getAllQuizzes({ courseName, moduleName }) + ) + ); + + const [pages] = trpc.useSuspenseQueries((t) => + moduleNames.map((moduleName) => + t.page.getAllPages({ courseName, moduleName }) + ) + ); + const assignmentsAndModules = moduleNames.flatMap((moduleName, index) => { return assignments[index].map((assignment) => ({ moduleName, assignment })); }); + const quizzesAndModules = moduleNames.flatMap((moduleName, index) => { + return quizzes[index].map((quiz) => ({ moduleName, quiz })); + }); + const pagesAndModules = moduleNames.flatMap((moduleName, index) => { + return pages[index].map((page) => ({ moduleName, page })); + }); - // const { data: assignmentsAndModules } = useSuspenseQueries({ - // queries: moduleNames.map((moduleName) => - // getAllAssignmentsQueryConfig(courseName, moduleName) - // ), - // combine: (results) => ({ - // data: results.flatMap((r, i) => - // r.data.map((assignment) => ({ - // moduleName: moduleNames[i], - // assignment, - // })) - // ), - // pending: results.some((r) => r.isPending), - // }), - // }); - - // const { data: quizzesAndModules } = useSuspenseQueries({ - // queries: moduleNames.map((moduleName) => - // getAllItemsQueryConfig(courseName, moduleName, "Quiz") - // ), - // combine: (results) => ({ - // data: results.flatMap((r, i) => - // r.data.map((quiz) => ({ - // moduleName: moduleNames[i], - // quiz, - // })) - // ), - // pending: results.some((r) => r.isPending), - // }), - // }); - - // const { data: pagesAndModules } = useSuspenseQueries({ - // queries: moduleNames.map((moduleName) => - // getAllItemsQueryConfig(courseName, moduleName, "Page") - // ), - // combine: (results) => ({ - // data: results.flatMap((r, i) => - // r.data.map((page) => ({ - // moduleName: moduleNames[i], - // page, - // })) - // ), - // pending: results.some((r) => r.isPending), - // }), - // }); - - return { assignmentsAndModules, quizzesAndModules: [], pagesAndModules: [] }; + return { assignmentsAndModules, quizzesAndModules, pagesAndModules }; }; diff --git a/nextjs/src/hooks/localCourse/localCourseModuleServerActions.ts b/nextjs/src/hooks/localCourse/localCourseModuleServerActions.ts deleted file mode 100644 index 48926d3..0000000 --- a/nextjs/src/hooks/localCourse/localCourseModuleServerActions.ts +++ /dev/null @@ -1,21 +0,0 @@ -"use server"; - -import { fileStorageService } from "@/services/fileStorage/fileStorageService"; - -export async function getModuleNamesFromServer({ - courseName, -}: { - courseName: string; -}) { - return await fileStorageService.modules.getModuleNames(courseName); -} - -export async function createModuleOnServer({ - courseName, - moduleName, -}: { - courseName: string; - moduleName: string; -}) { - await fileStorageService.modules.createModule(courseName, moduleName); -} diff --git a/nextjs/src/hooks/localCourse/localCoursesHooks.ts b/nextjs/src/hooks/localCourse/localCoursesHooks.ts index a2821d6..ab1f84a 100644 --- a/nextjs/src/hooks/localCourse/localCoursesHooks.ts +++ b/nextjs/src/hooks/localCourse/localCoursesHooks.ts @@ -5,11 +5,9 @@ import { trpc } from "@/services/trpc/utils"; export const useLocalCoursesSettingsQuery = () => trpc.settings.allCoursesSettings.useSuspenseQuery(); - export const useLocalCourseSettingsQuery = () => { const { courseName } = useCourseContext(); return trpc.settings.courseSettings.useSuspenseQuery({ courseName }); - }; export const useCreateLocalCourseMutation = () => { @@ -17,6 +15,7 @@ export const useCreateLocalCourseMutation = () => { return trpc.settings.createCourse.useMutation({ onSuccess: () => { utils.settings.allCoursesSettings.invalidate(); + utils.directories.getEmptyDirectories.invalidate(); }, }); }; diff --git a/nextjs/src/hooks/localCourse/localCoursesServerActions.ts b/nextjs/src/hooks/localCourse/localCoursesServerActions.ts deleted file mode 100644 index 8e153fa..0000000 --- a/nextjs/src/hooks/localCourse/localCoursesServerActions.ts +++ /dev/null @@ -1,37 +0,0 @@ -"use server"; -import { - LocalCourseSettings, -} from "@/models/local/localCourseSettings"; -import { fileStorageService } from "@/services/fileStorage/fileStorageService"; - -export async function getCourseSettingsFromServer({ - courseName, -}: { - courseName: string; -}) { - return await fileStorageService.settings.getCourseSettings(courseName); -} -export async function getAllCoursesSettingsFromServer() { - return await fileStorageService.settings.getAllCoursesSettings(); -} - -// export async function createCourseOnServer({ -// course, -// }: { -// course: LocalCourse; -// }) { -// await fileStorageService.settings.updateCourseSettings( -// course.settings.name, -// course.settings -// ); -// } - -// export async function updateCourseSettingsOnServer({ -// courseName, -// settings, -// }: { -// courseName: string; -// settings: LocalCourseSettings; -// }) { -// await fileStorageService.settings.updateCourseSettings(courseName, settings); -// } diff --git a/nextjs/src/hooks/localCourse/storageDirectoryHooks.ts b/nextjs/src/hooks/localCourse/storageDirectoryHooks.ts index 339045e..0ce8b9c 100644 --- a/nextjs/src/hooks/localCourse/storageDirectoryHooks.ts +++ b/nextjs/src/hooks/localCourse/storageDirectoryHooks.ts @@ -1,19 +1,8 @@ -import { axiosClient } from "@/services/axiosUtils"; -import { useQuery, useSuspenseQuery } from "@tanstack/react-query"; -import { getEmptyDirectories } from "./storageDirectoryServerActions"; +import { trpc } from "@/services/trpc/utils"; export const directoryKeys = { emptyFolders: ["empty folders"] as const, }; export const useEmptyDirectoriesQuery = () => - useQuery({ - queryKey: directoryKeys.emptyFolders, - queryFn: async () => await getEmptyDirectories(), - // queryFn: getEmptyDirectories, - // async () => { - // const url = "/api/directories/empty"; - // const { data } = await axiosClient.get(url); - // return data; - // }, - }); + trpc.directories.getEmptyDirectories.useSuspenseQuery(); diff --git a/nextjs/src/hooks/localCourse/storageDirectoryServerActions.ts b/nextjs/src/hooks/localCourse/storageDirectoryServerActions.ts deleted file mode 100644 index 544dbc5..0000000 --- a/nextjs/src/hooks/localCourse/storageDirectoryServerActions.ts +++ /dev/null @@ -1,7 +0,0 @@ -"use server" - -import { fileStorageService } from "@/services/fileStorage/fileStorageService" - -export async function getEmptyDirectories() { - return await fileStorageService.getEmptyDirectories() -} \ No newline at end of file diff --git a/nextjs/src/models/local/lecture.ts b/nextjs/src/models/local/lecture.ts index e5d7517..4e44dc8 100644 --- a/nextjs/src/models/local/lecture.ts +++ b/nextjs/src/models/local/lecture.ts @@ -1,5 +1,13 @@ +import { z } from "zod"; + export interface Lecture { name: string date: string content: string -} \ No newline at end of file +} + +export const zodLecture = z.object({ + name: z.string(), + date: z.string(), + content: z.string(), +}); diff --git a/nextjs/src/services/fileStorage/lectureFileStorageService.ts b/nextjs/src/services/fileStorage/lectureFileStorageService.ts index fe73863..09e1ca7 100644 --- a/nextjs/src/services/fileStorage/lectureFileStorageService.ts +++ b/nextjs/src/services/fileStorage/lectureFileStorageService.ts @@ -1,4 +1,4 @@ -"use server"; + import path from "path"; import { basePath } from "./utils/fileSystemUtils"; import fs from "fs/promises"; diff --git a/nextjs/src/services/trpc/router/app.ts b/nextjs/src/services/trpc/router/app.ts index 166bc3d..4e59a68 100644 --- a/nextjs/src/services/trpc/router/app.ts +++ b/nextjs/src/services/trpc/router/app.ts @@ -2,7 +2,9 @@ import { createTrpcContext } from "../context"; import publicProcedure from "../procedures/public"; import { createCallerFactory, router } from "../trpc"; import { assignmentRouter } from "./assignmentRouter"; +import { directoriesRouter } from "./directoriesRouter"; import { lectureRouter } from "./lectureRouter"; +import { moduleRouter } from "./moduleRouter"; import { pageRouter } from "./pageRouter"; import { quizRouter } from "./quizRouter"; import { settingsRouter } from "./settingsRouter"; @@ -22,6 +24,8 @@ export const trpcAppRouter = router({ settings: settingsRouter, quiz: quizRouter, page: pageRouter, + module: moduleRouter, + directories: directoriesRouter, }); export const createCaller = createCallerFactory(trpcAppRouter); diff --git a/nextjs/src/services/trpc/router/directoriesRouter.ts b/nextjs/src/services/trpc/router/directoriesRouter.ts new file mode 100644 index 0000000..10f111a --- /dev/null +++ b/nextjs/src/services/trpc/router/directoriesRouter.ts @@ -0,0 +1,11 @@ +import publicProcedure from "../procedures/public"; +import { z } from "zod"; +import { router } from "../trpc"; +import { fileStorageService } from "@/services/fileStorage/fileStorageService"; +import { zodLocalAssignment } from "@/models/local/assignment/localAssignment"; + +export const directoriesRouter = router({ + getEmptyDirectories: publicProcedure.query(async () => { + return await fileStorageService.getEmptyDirectories() + }) +}) \ No newline at end of file diff --git a/nextjs/src/services/trpc/router/lectureRouter.ts b/nextjs/src/services/trpc/router/lectureRouter.ts index 8f33bc9..931b6a4 100644 --- a/nextjs/src/services/trpc/router/lectureRouter.ts +++ b/nextjs/src/services/trpc/router/lectureRouter.ts @@ -1,15 +1,40 @@ import { z } from "zod"; import publicProcedure from "../procedures/public"; import { router } from "../trpc"; -import { getLectures } from "@/services/fileStorage/lectureFileStorageService"; - +import { + deleteLecture, + getLectures, + updateLecture, +} from "@/services/fileStorage/lectureFileStorageService"; +import { zodLecture } from "@/models/local/lecture"; +import { zodLocalCourseSettings } from "@/models/local/localCourseSettings"; export const lectureRouter = router({ getLectures: publicProcedure - .input(z.object({ - courseName: z.string() - })) - .query(async ({input: {courseName}}) => { - return await getLectures(courseName) - }) -}) \ No newline at end of file + .input( + z.object({ + courseName: z.string(), + }) + ) + .query(async ({ input: { courseName } }) => { + return await getLectures(courseName); + }), + updateLecture: publicProcedure + .input( + z.object({ + courseName: z.string(), + lecture: zodLecture, + previousDay: z.string().optional(), + settings: zodLocalCourseSettings, + }) + ) + .mutation( + async ({ input: { courseName, settings, lecture, previousDay } }) => { + await updateLecture(courseName, settings, lecture); + + if (previousDay && previousDay !== lecture.date) { + await deleteLecture(courseName, settings, previousDay); + } + } + ), +}); diff --git a/nextjs/src/services/trpc/router/moduleRouter.ts b/nextjs/src/services/trpc/router/moduleRouter.ts new file mode 100644 index 0000000..544ef6d --- /dev/null +++ b/nextjs/src/services/trpc/router/moduleRouter.ts @@ -0,0 +1,26 @@ +import { z } from "zod"; +import publicProcedure from "../procedures/public"; +import { router } from "../trpc"; +import { fileStorageService } from "@/services/fileStorage/fileStorageService"; + +export const moduleRouter = router({ + getModuleNames: publicProcedure + .input( + z.object({ + courseName: z.string(), + }) + ) + .query(async ({ input: { courseName } }) => { + return await fileStorageService.modules.getModuleNames(courseName); + }), + createModule: publicProcedure + .input( + z.object({ + courseName: z.string(), + moduleName: z.string(), + }) + ) + .mutation(async ({ input: { courseName, moduleName } }) => { + await fileStorageService.modules.createModule(courseName, moduleName); + }), +});