diff --git a/nextjs/src/app/course/[courseName]/calendar/CalendarMonth.tsx b/nextjs/src/app/course/[courseName]/calendar/CalendarMonth.tsx index 7e8d033..d94371b 100644 --- a/nextjs/src/app/course/[courseName]/calendar/CalendarMonth.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/CalendarMonth.tsx @@ -26,7 +26,7 @@ export const CalendarMonth = ({ month }: { month: CalendarMonthModel }) => {

setIsExpanded((e) => !e)} role="button" diff --git a/nextjs/src/app/course/[courseName]/calendar/CalendarWeek.tsx b/nextjs/src/app/course/[courseName]/calendar/CalendarWeek.tsx index bb34262..bf68b4f 100644 --- a/nextjs/src/app/course/[courseName]/calendar/CalendarWeek.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/CalendarWeek.tsx @@ -23,7 +23,7 @@ export function CalendarWeek({ const weekNumber = getWeekNumber(startDate, firstDateString); return (
-
+
{weekNumber.toString().padStart(2, "0")}
diff --git a/nextjs/src/app/course/[courseName]/calendar/day/Day.tsx b/nextjs/src/app/course/[courseName]/calendar/day/Day.tsx index 879a3f5..f9050d2 100644 --- a/nextjs/src/app/course/[courseName]/calendar/day/Day.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/day/Day.tsx @@ -1,5 +1,6 @@ "use client"; import { + dateToMarkdownString, getDateFromStringOrThrow, getDateOnlyMarkdownString, } from "@/models/local/timeUtils"; @@ -30,6 +31,23 @@ export default function Day({ day, month }: { day: string; month: number }) { const isInSameMonth = dayAsDate.getMonth() + 1 == month; const classOnThisDay = settings.daysOfWeek.includes(getDayOfWeek(dayAsDate)); + // maybe this is slow? + const holidayNameToday = settings.holidays.reduce( + (holidaysHappeningToday, holiday) => { + const holidayDates = holiday.days.map((d) => + getDateOnlyMarkdownString( + getDateFromStringOrThrow(d, "holiday date in day component") + ) + ); + const today = getDateOnlyMarkdownString(dayAsDate); + + if (holidayDates.includes(today)) + return [...holidaysHappeningToday, holiday.name]; + return holidaysHappeningToday; + }, + [] as string[] + ); + const semesterStart = getDateFromStringOrThrow( settings.startDate, "comparing start date in day" @@ -42,7 +60,9 @@ export default function Day({ day, month }: { day: string; month: number }) { const isInSemester = semesterStart < dayAsDate && semesterEnd > dayAsDate; const meetingClasses = - classOnThisDay && isInSemester ? " bg-slate-900 " : " "; + classOnThisDay && isInSemester && holidayNameToday.length === 0 + ? " bg-slate-900 " + : " "; const todayClasses = isToday ? " border border-blue-700 shadow-[0_0px_10px_0px] shadow-blue-500/50 " @@ -53,13 +73,15 @@ export default function Day({ day, month }: { day: string; month: number }) { return (
itemDropOnDay(e, day)} onDragOver={(e) => e.preventDefault()} > -
+
-
+
{todaysAssignments.map( ({ assignment, moduleName, status, message }) => ( ))}
+
+ {holidayNameToday.map((n) => ( +
+ {n} +
+ ))} +
); diff --git a/nextjs/src/app/course/[courseName]/settings/HolidayConfig.tsx b/nextjs/src/app/course/[courseName]/settings/HolidayConfig.tsx index 22b57fd..57b4bd3 100644 --- a/nextjs/src/app/course/[courseName]/settings/HolidayConfig.tsx +++ b/nextjs/src/app/course/[courseName]/settings/HolidayConfig.tsx @@ -6,12 +6,7 @@ import { useLocalCourseSettingsQuery, useUpdateLocalCourseSettingsMutation, } from "@/hooks/localCourse/localCoursesHooks"; -import { - dateToMarkdownString, - getDateFromString, - getDateFromStringOrThrow, - getDateOnlyMarkdownString, -} from "@/models/local/timeUtils"; +import { getDateFromString } from "@/models/local/timeUtils"; import { useEffect, useState } from "react"; import { holidaysToString, @@ -26,27 +21,33 @@ laborDay: - 9/1/2024`; export const holidaysAreEqual = ( - obj1: { [key: string]: string[] }, - obj2: { [key: string]: string[] } + obj1: { + name: string; + days: string[]; + }[], + obj2: { + name: string; + days: string[]; + }[] ): boolean => { - const keys1 = Object.keys(obj1); - const keys2 = Object.keys(obj2); + if (obj1.length !== obj2.length) return false; - if (keys1.length !== keys2.length) return false; + const sortedObj1 = [...obj1].sort((a, b) => a.name.localeCompare(b.name)); + const sortedObj2 = [...obj2].sort((a, b) => a.name.localeCompare(b.name)); - for (const key of keys1) { - if (!obj2.hasOwnProperty(key)) return false; + for (let i = 0; i < sortedObj1.length; i++) { + const holiday1 = sortedObj1[i]; + const holiday2 = sortedObj2[i]; - const arr1 = obj1[key]; - const arr2 = obj2[key]; + if (holiday1.name !== holiday2.name) return false; - if (arr1.length !== arr2.length) return false; + const sortedDays1 = [...holiday1.days].sort(); + const sortedDays2 = [...holiday2.days].sort(); - const sortedArr1 = [...arr1].sort(); - const sortedArr2 = [...arr2].sort(); + if (sortedDays1.length !== sortedDays2.length) return false; - for (let i = 0; i < sortedArr1.length; i++) { - if (sortedArr1[i] !== sortedArr2[i]) return false; + for (let j = 0; j < sortedDays1.length; j++) { + if (sortedDays1[j] !== sortedDays2[j]) return false; } } @@ -62,6 +63,7 @@ export default function HolidayConfig() { } function InnerHolidayConfig() { const { data: settings } = useLocalCourseSettingsQuery(); + console.log(settings.holidays); const updateSettings = useUpdateLocalCourseSettingsMutation(); const [rawText, setRawText] = useState(holidaysToString(settings.holidays)); @@ -71,15 +73,17 @@ function InnerHolidayConfig() { try { const parsed = parseHolidays(rawText); - if (!holidaysAreEqual(settings.holidays, parsed)) + if (!holidaysAreEqual(settings.holidays, parsed)) { + console.log("different holiday configs", settings.holidays, parsed); updateSettings.mutate({ ...settings, holidays: parsed, }); + } } catch (error: any) {} }, 500); return () => clearTimeout(id); - }, [rawText, settings, updateSettings]); + }, [rawText, settings.holidays, settings, updateSettings]); return (
@@ -107,9 +111,12 @@ function InnerHolidayConfig() { } function ParsedHolidaysDisplay({ value }: { value: string }) { - const [parsedHolidays, setParsedHolidays] = useState<{ - [holidayName: string]: string[]; - }>({}); + const [parsedHolidays, setParsedHolidays] = useState< + { + name: string; + days: string[]; + }[] + >([]); const [error, setError] = useState(""); useEffect(() => { @@ -125,11 +132,11 @@ function ParsedHolidaysDisplay({ value }: { value: string }) { return (
{error}
- {Object.keys(parsedHolidays).map((k) => ( -
-
{k}
+ {parsedHolidays.map((holiday) => ( +
+
{holiday.name}
- {parsedHolidays[k].map((day) => { + {holiday.days.map((day) => { const date = getDateFromString(day); return (
diff --git a/nextjs/src/app/layout.tsx b/nextjs/src/app/layout.tsx index 31e11c9..af35e14 100644 --- a/nextjs/src/app/layout.tsx +++ b/nextjs/src/app/layout.tsx @@ -7,6 +7,7 @@ import { hydrateCourses } from "@/hooks/hookHydration"; import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; import { MyToaster } from "./MyToaster"; import { cookies } from "next/headers"; +export const dynamic = 'force-dynamic' export const metadata: Metadata = { title: "Canvas Manager 2.0", diff --git a/nextjs/src/hooks/localCourse/localCoursesHooks.ts b/nextjs/src/hooks/localCourse/localCoursesHooks.ts index a1e9b5a..eefa4b0 100644 --- a/nextjs/src/hooks/localCourse/localCoursesHooks.ts +++ b/nextjs/src/hooks/localCourse/localCoursesHooks.ts @@ -23,10 +23,11 @@ export const useLocalCoursesSettingsQuery = () => export const useLocalCourseSettingsQuery = () => { const { courseName } = useCourseContext(); - const { data: settingsList } = useLocalCoursesSettingsQuery(); + // const { data: settingsList } = useLocalCoursesSettingsQuery(); return useSuspenseQuery({ queryKey: localCourseKeys.settings(courseName), - queryFn: () => { + queryFn: async () => { + const settingsList = await getAllCoursesSettingsFromServer(); const s = settingsList.find((s) => s.name === courseName); if (!s) { console.log(courseName, settingsList); @@ -56,19 +57,22 @@ export const useUpdateLocalCourseSettingsMutation = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (updatedSettings: LocalCourseSettings) => { - queryClient.setQueryData( - localCourseKeys.settings(courseName), - updatedSettings - ); - await updateCourseSettingsOnServer({ courseName, settings: updatedSettings }); - }, - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: localCourseKeys.settings(courseName), + // queryClient.setQueryData( + // localCourseKeys.settings(courseName), + // updatedSettings + // ); + await updateCourseSettingsOnServer({ + courseName, + settings: updatedSettings, }); - queryClient.invalidateQueries({ + }, + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: localCourseKeys.allCoursesSettings, }); + await queryClient.invalidateQueries({ + queryKey: localCourseKeys.settings(courseName), + }); }, }); }; diff --git a/nextjs/src/models/local/localCourse.ts b/nextjs/src/models/local/localCourse.ts index 8cf1c27..3858e05 100644 --- a/nextjs/src/models/local/localCourse.ts +++ b/nextjs/src/models/local/localCourse.ts @@ -25,8 +25,9 @@ export interface LocalCourseSettings { defaultAssignmentSubmissionTypes: AssignmentSubmissionType[]; defaultFileUploadTypes: string[]; holidays: { - [key: string]: string[]; // e.g. "spring break": ["datestring", "datestring", "datestring", "datestring"] - }; + name: string; + days: string[]; + }[]; } export enum DayOfWeek { @@ -46,7 +47,7 @@ export function getDayOfWeek(date: Date): DayOfWeek { export const localCourseYamlUtils = { parseSettingYaml: (settingsString: string): LocalCourseSettings => { - const settings = parse(settingsString); + const settings = parse(settingsString, {}); return lowercaseFirstLetter(settings); }, settingsToYaml: (settings: Omit) => { diff --git a/nextjs/src/models/local/settingsUtils.tsx b/nextjs/src/models/local/settingsUtils.tsx index 4f813ca..570e3bb 100644 --- a/nextjs/src/models/local/settingsUtils.tsx +++ b/nextjs/src/models/local/settingsUtils.tsx @@ -7,8 +7,14 @@ import { export const parseHolidays = ( inputText: string -): { [holidayName: string]: string[] } => { - const holidays: { [holidayName: string]: string[] } = {}; +): { + name: string; + days: string[]; +}[] => { + let holidays: { + name: string; + days: string[]; + }[] = []; const lines = inputText.split("\n").filter((line) => line.trim() !== ""); let currentHoliday: string | null = null; @@ -17,24 +23,30 @@ export const parseHolidays = ( if (line.includes(":")) { const holidayName = line.split(":")[0].trim(); currentHoliday = holidayName; - holidays[currentHoliday] = []; + holidays = [...holidays, { name: holidayName, days: [] }]; } else if (currentHoliday && line.startsWith("-")) { const date = line.replace("-", "").trim(); const dateObject = getDateFromStringOrThrow(date, "parsing holiday text"); - holidays[currentHoliday].push(getDateOnlyMarkdownString(dateObject)); + + const holiday = holidays.find((h) => h.name == currentHoliday); + holiday?.days.push(getDateOnlyMarkdownString(dateObject)); } }); return holidays; }; +export const holidaysToString = ( + holidays: { + name: string; + days: string[]; + }[] +) => { + const entries = holidays.map((holiday) => { + const title = holiday.name + ":\n"; + const days = holiday.days.map((d) => `- ${d}\n`); + return title + days.join(""); + }); -export const holidaysToString = (holidays: { [holidayName: string]: string[] })=> { - const entries = Object.keys(holidays).map(holiday => { - const title = holiday + ":\n" - const days = holidays[holiday].map(d => `- ${d}\n`) - return title + days.join("") - }) - - return entries.join("") -} \ No newline at end of file + return entries.join(""); +}; diff --git a/nextjs/src/services/fileStorage/settingsFileStorageService.ts b/nextjs/src/services/fileStorage/settingsFileStorageService.ts index 521e695..a90895c 100644 --- a/nextjs/src/services/fileStorage/settingsFileStorageService.ts +++ b/nextjs/src/services/fileStorage/settingsFileStorageService.ts @@ -47,7 +47,7 @@ const populateDefaultValues = (settingsFromFile: LocalCourseSettings) => { defaultSubmissionType, defaultFileUploadTypes: settingsFromFile.defaultFileUploadTypes || defaultFileUploadTypes, - holidays: !!settingsFromFile.holidays ? settingsFromFile.holidays : {}, + holidays: !!settingsFromFile.holidays ? settingsFromFile.holidays : [], }; return settings; };