From 51069a856aa9167ca9979d4b5e49c2d546b0363a Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Fri, 30 Aug 2024 12:47:53 -0600 Subject: [PATCH] back to displaying months --- .../[courseName]/calendar/CourseCalendar.tsx | 27 ++-- .../app/course/[courseName]/calendar/Day.tsx | 102 +++------------- .../calendar/DayItemsInModule.tsx | 76 ++++++++++++ .../[courseName]/modules/ExpandableModule.tsx | 31 +---- .../[courseName]/modules/ModuleList.tsx | 1 - nextjs/src/app/providers.tsx | 3 +- nextjs/src/hooks/hookHydration.ts | 115 +++++++++++++++++- .../src/hooks/localCourse/assignmentHooks.ts | 4 +- .../src/hooks/localCourse/localCourseKeys.ts | 55 +++++++++ .../hooks/localCourse/localCoursesHooks.ts | 93 ++++++-------- nextjs/src/hooks/localCourse/pageHooks.ts | 4 +- nextjs/src/hooks/localCourse/quizHooks.ts | 4 +- 12 files changed, 322 insertions(+), 193 deletions(-) create mode 100644 nextjs/src/app/course/[courseName]/calendar/DayItemsInModule.tsx create mode 100644 nextjs/src/hooks/localCourse/localCourseKeys.ts diff --git a/nextjs/src/app/course/[courseName]/calendar/CourseCalendar.tsx b/nextjs/src/app/course/[courseName]/calendar/CourseCalendar.tsx index 698a1b3..150278e 100644 --- a/nextjs/src/app/course/[courseName]/calendar/CourseCalendar.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/CourseCalendar.tsx @@ -3,20 +3,21 @@ import { getDateFromStringOrThrow } from "@/models/local/timeUtils"; import { useCourseContext } from "../context/courseContext"; import { getMonthsBetweenDates } from "./calendarMonthUtils"; import { CalendarMonth } from "./CalendarMonth"; +import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; export default function CourseCalendar() { - // const { - // localCourse: { - // settings: { startDate, endDate }, - // }, - // } = useCourseContext(); + const { courseName } = useCourseContext(); + const { data: settings } = useLocalCourseSettingsQuery(courseName); - // const startDateTime = getDateFromStringOrThrow( - // startDate, - // "course start date" - // ); - // const endDateTime = getDateFromStringOrThrow(endDate, "course end date"); - // const months = getMonthsBetweenDates(startDateTime, endDateTime); + const startDateTime = getDateFromStringOrThrow( + settings.startDate, + "course start date" + ); + const endDateTime = getDateFromStringOrThrow( + settings.endDate, + "course end date" + ); + const months = getMonthsBetweenDates(startDateTime, endDateTime); return (
Month Goes Here - {/* {months.map((month) => ( + {months.map((month) => ( - ))} */} + ))}
); } diff --git a/nextjs/src/app/course/[courseName]/calendar/Day.tsx b/nextjs/src/app/course/[courseName]/calendar/Day.tsx index e640758..7e9b618 100644 --- a/nextjs/src/app/course/[courseName]/calendar/Day.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/Day.tsx @@ -1,93 +1,29 @@ "use client"; - -import { getDateFromStringOrThrow } from "@/models/local/timeUtils"; import { useCourseContext } from "../context/courseContext"; +import { useModuleNamesQuery } from "@/hooks/localCourse/localCoursesHooks"; +import DayItemsInModule from "./DayItemsInModule"; export default function Day({ day, month }: { day: Date; month: number }) { - const { - localCourse: { modules }, - startItemDrag, - endItemDrag, - itemDrop, - } = useCourseContext(); - + const { courseName, itemDrop } = useCourseContext(); + const { data: moduleNames } = useModuleNamesQuery(courseName); const isInSameMonth = day.getMonth() + 1 != month; const backgroundClass = isInSameMonth ? "" : "bg-slate-900"; - const todaysAssignments = modules - .flatMap((m) => m.assignments) - .filter((a) => { - const dueDate = getDateFromStringOrThrow( - a.dueAt, - "due at for assignment in day" - ); - return ( - dueDate.getFullYear() === day.getFullYear() && - dueDate.getMonth() === day.getMonth() && - dueDate.getDate() === day.getDate() - ); - }); - const todaysQuizzes = modules - .flatMap((m) => m.quizzes) - .filter((q) => { - const dueDate = getDateFromStringOrThrow( - q.dueAt, - "due at for quiz in day" - ); - return ( - dueDate.getFullYear() === day.getFullYear() && - dueDate.getMonth() === day.getMonth() && - dueDate.getDate() === day.getDate() - ); - }); - const todaysPages = modules - .flatMap((m) => m.pages) - .filter((p) => { - const dueDate = getDateFromStringOrThrow( - p.dueAt, - "due at for page in day" - ); - return ( - dueDate.getFullYear() === day.getFullYear() && - dueDate.getMonth() === day.getMonth() && - dueDate.getDate() === day.getDate() - ); - }); return ( -
itemDrop(day)} - onDragOver={(e) => e.preventDefault()} - > - {day.getDate()} - -
+ <> + {moduleNames.map((moduleName) => ( +
itemDrop(day)} + onDragOver={(e) => e.preventDefault()} + > + {day.getDate()} + +
+ ))} + ); } diff --git a/nextjs/src/app/course/[courseName]/calendar/DayItemsInModule.tsx b/nextjs/src/app/course/[courseName]/calendar/DayItemsInModule.tsx new file mode 100644 index 0000000..75f053b --- /dev/null +++ b/nextjs/src/app/course/[courseName]/calendar/DayItemsInModule.tsx @@ -0,0 +1,76 @@ +import React from "react"; +import { useCourseContext } from "../context/courseContext"; +import { useModuleDataQuery } from "@/hooks/localCourse/localCoursesHooks"; +import { getDateFromStringOrThrow } from "@/models/local/timeUtils"; + +export default function DayItemsInModule({ + day, + moduleName, +}: { + day: Date; + moduleName: string; +}) { + const { courseName, endItemDrag, startItemDrag } = useCourseContext(); + const { assignments, quizzes, pages } = useModuleDataQuery( + courseName, + moduleName + ); + + const todaysAssignments = assignments.filter((a) => { + const dueDate = getDateFromStringOrThrow( + a.dueAt, + "due at for assignment in day" + ); + return ( + dueDate.getFullYear() === day.getFullYear() && + dueDate.getMonth() === day.getMonth() && + dueDate.getDate() === day.getDate() + ); + }); + const todaysQuizzes = quizzes.filter((q) => { + const dueDate = getDateFromStringOrThrow(q.dueAt, "due at for quiz in day"); + return ( + dueDate.getFullYear() === day.getFullYear() && + dueDate.getMonth() === day.getMonth() && + dueDate.getDate() === day.getDate() + ); + }); + const todaysPages = pages.filter((p) => { + const dueDate = getDateFromStringOrThrow(p.dueAt, "due at for page in day"); + return ( + dueDate.getFullYear() === day.getFullYear() && + dueDate.getMonth() === day.getMonth() && + dueDate.getDate() === day.getDate() + ); + }); + return ( + <> + + + ); +} diff --git a/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx b/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx index ca7c89f..ad3c9f5 100644 --- a/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx +++ b/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx @@ -1,20 +1,8 @@ import { IModuleItem } from "@/models/local/IModuleItem"; -import { LocalModule } from "@/models/local/localModules"; import { getDateFromStringOrThrow } from "@/models/local/timeUtils"; -import React, { useState } from "react"; +import { 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"; +import { useModuleDataQuery } from "@/hooks/localCourse/localCoursesHooks"; export default function ExpandableModule({ moduleName, @@ -22,23 +10,10 @@ export default function ExpandableModule({ moduleName: string; }) { const { courseName } = useCourseContext(); - const { data: assignmentNames } = useAssignmentNamesQuery( + const { assignments, quizzes, pages } = useModuleDataQuery( 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); const [expanded, setExpanded] = useState(false); diff --git a/nextjs/src/app/course/[courseName]/modules/ModuleList.tsx b/nextjs/src/app/course/[courseName]/modules/ModuleList.tsx index 3c74ef7..529e3b3 100644 --- a/nextjs/src/app/course/[courseName]/modules/ModuleList.tsx +++ b/nextjs/src/app/course/[courseName]/modules/ModuleList.tsx @@ -8,7 +8,6 @@ export default function ModuleList() { const { data: moduleNames } = useModuleNamesQuery(courseName); return (
- modules here {moduleNames.map((m) => ( ))} diff --git a/nextjs/src/app/providers.tsx b/nextjs/src/app/providers.tsx index 717c15c..aefb57c 100644 --- a/nextjs/src/app/providers.tsx +++ b/nextjs/src/app/providers.tsx @@ -12,7 +12,7 @@ function makeQueryClient() { queries: { // With SSR, we usually want to set some default staleTime // above 0 to avoid refetching immediately on the client - // staleTime: 1000, + staleTime: 1000, }, }, }); @@ -39,6 +39,7 @@ export default function Providers({ children }: { children: ReactNode }) { // have a suspense boundary between this and the code that may // suspend because React will throw away the client on the initial // render if it suspends and there is no boundary + const queryClient = getQueryClient(); return ( diff --git a/nextjs/src/hooks/hookHydration.ts b/nextjs/src/hooks/hookHydration.ts index c2f067f..c0f6139 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 "./localCourse/localCoursesHooks"; +import { localCourseKeys } from "./localCourse/localCourseKeys"; import { fileStorageService } from "@/services/fileStorage/fileStorageService"; export const hydrateCourses = async (queryClient: QueryClient) => { @@ -14,7 +14,52 @@ export const hydrateCourse = async ( courseName: string ) => { const settings = await fileStorageService.getCourseSettings(courseName); - const moduleNames = await fileStorageService.getModuleNames(courseName) + const moduleNames = await fileStorageService.getModuleNames(courseName); + const modulesData = await Promise.all( + moduleNames.map(async (moduleName) => { + const [assignmentNames, pageNames, quizNames] = await Promise.all([ + await fileStorageService.getAssignmentNames(courseName, moduleName), + await fileStorageService.getPageNames(courseName, moduleName), + await fileStorageService.getQuizNames(courseName, moduleName), + ]); + + const [assignments, quizzes, pages] = await Promise.all([ + await Promise.all( + assignmentNames.map( + async (assignmentName) => + await fileStorageService.getAssignment( + courseName, + moduleName, + assignmentName + ) + ) + ), + await Promise.all( + quizNames.map( + async (quizName) => + await fileStorageService.getQuiz(courseName, moduleName, quizName) + ) + ), + await Promise.all( + pageNames.map( + async (pageName) => + await fileStorageService.getPage(courseName, moduleName, pageName) + ) + ), + ]); + + return { + moduleName, + assignmentNames, + pageNames, + quizNames, + assignments, + quizzes, + pages, + }; + }) + ); + await queryClient.prefetchQuery({ queryKey: localCourseKeys.settings(courseName), queryFn: () => settings, @@ -23,4 +68,70 @@ export const hydrateCourse = async ( queryKey: localCourseKeys.moduleNames(courseName), queryFn: () => moduleNames, }); + + await Promise.all( + modulesData.map( + async ({ + moduleName, + assignmentNames, + pageNames, + quizNames, + assignments, + quizzes, + pages, + }) => { + await queryClient.prefetchQuery({ + queryKey: localCourseKeys.assignmentNames(courseName, moduleName), + queryFn: () => assignmentNames, + }); + await Promise.all( + assignments.map( + async (assignment) => + await queryClient.prefetchQuery({ + queryKey: localCourseKeys.assignment( + courseName, + moduleName, + assignment.name + ), + queryFn: () => assignment, + }) + ) + ); + await queryClient.prefetchQuery({ + queryKey: localCourseKeys.quizNames(courseName, moduleName), + queryFn: () => quizNames, + }); + await Promise.all( + quizzes.map( + async (quiz) => + await queryClient.prefetchQuery({ + queryKey: localCourseKeys.quiz( + courseName, + moduleName, + quiz.name + ), + queryFn: () => quiz, + }) + ) + ); + await queryClient.prefetchQuery({ + queryKey: localCourseKeys.pageNames(courseName, moduleName), + queryFn: () => pageNames, + }); + await Promise.all( + pages.map( + async (page) => + await queryClient.prefetchQuery({ + queryKey: localCourseKeys.page( + courseName, + moduleName, + page.name + ), + queryFn: () => page, + }) + ) + ); + } + ) + ); }; diff --git a/nextjs/src/hooks/localCourse/assignmentHooks.ts b/nextjs/src/hooks/localCourse/assignmentHooks.ts index 99ab1fb..4601334 100644 --- a/nextjs/src/hooks/localCourse/assignmentHooks.ts +++ b/nextjs/src/hooks/localCourse/assignmentHooks.ts @@ -1,5 +1,5 @@ import axios from "axios"; -import { localCourseKeys } from "./localCoursesHooks"; +import { localCourseKeys } from "./localCourseKeys"; import { LocalAssignment } from "@/models/local/assignmnet/localAssignment"; import { useSuspenseQuery, useSuspenseQueries } from "@tanstack/react-query"; @@ -8,7 +8,7 @@ export const useAssignmentNamesQuery = ( moduleName: string ) => useSuspenseQuery({ - queryKey: localCourseKeys.moduleAssignmentNames(courseName, moduleName), + queryKey: localCourseKeys.assignmentNames(courseName, moduleName), queryFn: async (): Promise => { const url = `/api/courses/${courseName}/modules/${moduleName}/assignments`; const response = await axios.get(url); diff --git a/nextjs/src/hooks/localCourse/localCourseKeys.ts b/nextjs/src/hooks/localCourse/localCourseKeys.ts new file mode 100644 index 0000000..e142f01 --- /dev/null +++ b/nextjs/src/hooks/localCourse/localCourseKeys.ts @@ -0,0 +1,55 @@ +export const localCourseKeys = { + allCourses: ["all courses"] as const, + settings: (courseName: string) => + ["course details", courseName, "settings"] as const, + moduleNames: (courseName: string) => + [ + "course details", + courseName, + "modules", + { type: "names" } as const, + ] as const, + assignmentNames: (courseName: string, moduleName: string) => + [ + "course details", + courseName, + "modules", + moduleName, + "assignments", + ] as const, + quizNames: (courseName: string, moduleName: string) => + ["course details", courseName, "modules", moduleName, "quizzes"] as const, + pageNames: (courseName: string, moduleName: string) => + ["course details", courseName, "modules", moduleName, "pages"] as const, + assignment: ( + courseName: string, + moduleName: string, + assignmentName: string + ) => + [ + "course details", + courseName, + "modules", + moduleName, + "assignments", + assignmentName, + ] as const, + quiz: (courseName: string, moduleName: string, quizName: string) => + [ + "course details", + courseName, + "modules", + moduleName, + "quizzes", + quizName, + ] as const, + page: (courseName: string, moduleName: string, pageName: string) => + [ + "course details", + courseName, + "modules", + moduleName, + "pages", + pageName, + ] as const, +}; diff --git a/nextjs/src/hooks/localCourse/localCoursesHooks.ts b/nextjs/src/hooks/localCourse/localCoursesHooks.ts index b638489..cb807bf 100644 --- a/nextjs/src/hooks/localCourse/localCoursesHooks.ts +++ b/nextjs/src/hooks/localCourse/localCoursesHooks.ts @@ -1,64 +1,13 @@ import { LocalCourseSettings } from "@/models/local/localCourse"; -import { - useSuspenseQuery, -} from "@tanstack/react-query"; +import { useSuspenseQuery } from "@tanstack/react-query"; import axios from "axios"; - -export const localCourseKeys = { - allCourses: ["all courses"] as const, - settings: (courseName: string) => - ["course details", courseName, "settings"] as const, - moduleNames: (courseName: string) => - [ - "course details", - courseName, - "modules", - { type: "names" } as const, - ] as const, - moduleAssignmentNames: (courseName: string, moduleName: string) => - [ - "course details", - courseName, - "modules", - moduleName, - "assignments", - ] as const, - moduleQuizzeNames: (courseName: string, moduleName: string) => - ["course details", courseName, "modules", moduleName, "quizzes"] as const, - modulePageNames: (courseName: string, moduleName: string) => - ["course details", courseName, "modules", moduleName, "pages"] as const, - assignment: ( - courseName: string, - moduleName: string, - assignmentName: string - ) => - [ - "course details", - courseName, - "modules", - moduleName, - "assignments", - assignmentName, - ] as const, - quiz: (courseName: string, moduleName: string, quizName: string) => - [ - "course details", - courseName, - "modules", - moduleName, - "quizzes", - quizName, - ] as const, - page: (courseName: string, moduleName: string, pageName: string) => - [ - "course details", - courseName, - "modules", - moduleName, - "pages", - pageName, - ] as const, -}; +import { localCourseKeys } from "./localCourseKeys"; +import { + useAssignmentNamesQuery, + useAssignmentsQueries, +} from "./assignmentHooks"; +import { usePageNamesQuery, usePagesQueries } from "./pageHooks"; +import { useQuizNamesQuery, useQuizzesQueries } from "./quizHooks"; export const useLocalCourseNamesQuery = () => useSuspenseQuery({ @@ -90,7 +39,33 @@ export const useModuleNamesQuery = (courseName: string) => }, }); +export const useModuleDataQuery = (courseName: string, moduleName: string) => { + const { data: assignmentNames } = useAssignmentNamesQuery( + courseName, + moduleName + ); + const { data: quizNames } = useQuizNamesQuery(courseName, moduleName); + const { data: pageNames } = usePageNamesQuery(courseName, moduleName); + const { data: assignments } = useAssignmentsQueries( + courseName, + moduleName, + assignmentNames + ); + const { data: quizzes } = useQuizzesQueries( + courseName, + moduleName, + quizNames + ); + const { data: pages } = usePagesQueries(courseName, moduleName, pageNames); + + + return { + assignments, + quizzes, + pages, + }; +}; // export const useUpdateCourseMutation = (courseName: string) => { // const queryClient = useQueryClient(); diff --git a/nextjs/src/hooks/localCourse/pageHooks.ts b/nextjs/src/hooks/localCourse/pageHooks.ts index 7db4e70..bcfc158 100644 --- a/nextjs/src/hooks/localCourse/pageHooks.ts +++ b/nextjs/src/hooks/localCourse/pageHooks.ts @@ -1,11 +1,11 @@ import { LocalCoursePage } from "@/models/local/page/localCoursePage"; import { useSuspenseQueries, useSuspenseQuery } from "@tanstack/react-query"; import axios from "axios"; -import { localCourseKeys } from "./localCoursesHooks"; +import { localCourseKeys } from "./localCourseKeys"; export const usePageNamesQuery = (courseName: string, moduleName: string) => useSuspenseQuery({ - queryKey: localCourseKeys.modulePageNames(courseName, moduleName), + queryKey: localCourseKeys.pageNames(courseName, moduleName), queryFn: async (): Promise => { const url = `/api/courses/${courseName}/modules/${moduleName}/pages`; const response = await axios.get(url); diff --git a/nextjs/src/hooks/localCourse/quizHooks.ts b/nextjs/src/hooks/localCourse/quizHooks.ts index 1b95d94..c1aaab9 100644 --- a/nextjs/src/hooks/localCourse/quizHooks.ts +++ b/nextjs/src/hooks/localCourse/quizHooks.ts @@ -1,11 +1,11 @@ import { LocalQuiz } from "@/models/local/quiz/localQuiz"; import { useSuspenseQueries, useSuspenseQuery } from "@tanstack/react-query"; import axios from "axios"; -import { localCourseKeys } from "./localCoursesHooks"; +import { localCourseKeys } from "./localCourseKeys"; export const useQuizNamesQuery = (courseName: string, moduleName: string) => useSuspenseQuery({ - queryKey: localCourseKeys.moduleQuizzeNames(courseName, moduleName), + queryKey: localCourseKeys.quizNames(courseName, moduleName), queryFn: async (): Promise => { const url = `/api/courses/${courseName}/modules/${moduleName}/quizzes`; const response = await axios.get(url);