diff --git a/nextjs/src/app/course/[courseName]/calendar/CourseCalendar.tsx b/nextjs/src/app/course/[courseName]/calendar/CourseCalendar.tsx
index c8d8d15..c11581d 100644
--- a/nextjs/src/app/course/[courseName]/calendar/CourseCalendar.tsx
+++ b/nextjs/src/app/course/[courseName]/calendar/CourseCalendar.tsx
@@ -4,6 +4,7 @@ import { getMonthsBetweenDates } from "./calendarMonthUtils";
import { CalendarMonth } from "./CalendarMonth";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { useMemo } from "react";
+import CalendarItemsContextProvider from "../context/CalendarItemsContextProvider";
export default function CourseCalendar() {
const { data: settings } = useLocalCourseSettingsQuery();
@@ -32,9 +33,11 @@ export default function CourseCalendar() {
bg-slate-950
"
>
- {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 ec5af6d..e297aa4 100644
--- a/nextjs/src/app/course/[courseName]/calendar/Day.tsx
+++ b/nextjs/src/app/course/[courseName]/calendar/Day.tsx
@@ -1,19 +1,27 @@
"use client";
-import { useModuleNamesQuery } from "@/hooks/localCourse/localCoursesHooks";
-import DayItemsInModule from "./DayItemsInModule";
-import { getDateFromStringOrThrow } from "@/models/local/timeUtils";
+import {
+ getDateFromStringOrThrow,
+ getDateOnlyMarkdownString,
+} from "@/models/local/timeUtils";
import { useDraggingContext } from "../context/draggingContext";
+import { useCalendarItemsContext } from "../context/calendarItemsContext";
+import { useCourseContext } from "../context/courseContext";
+import Link from "next/link";
export default function Day({ day, month }: { day: string; month: number }) {
- const { data: moduleNames } = useModuleNamesQuery();
-
const dayAsDate = getDateFromStringOrThrow(
day,
"calculating same month in day"
);
const isInSameMonth = dayAsDate.getMonth() + 1 != month;
const backgroundClass = isInSameMonth ? "" : "bg-slate-900";
+
+ const itemsContext = useCalendarItemsContext();
const { itemDrop } = useDraggingContext();
+ const { courseName } = useCourseContext();
+
+ const dateKey = getDateOnlyMarkdownString(dayAsDate);
+ const todaysModules = itemsContext[dateKey];
return (
e.preventDefault()}
>
{dayAsDate.getDate()}
- {moduleNames.map((moduleName) => (
-
- ))}
+ {todaysModules &&
+ Object.keys(todaysModules).flatMap((moduleName) =>
+ todaysModules[moduleName].assignments.map((a) => (
+
{
+ e.dataTransfer.setData(
+ "draggableItem",
+ JSON.stringify({
+ type: "assignment",
+ item: a,
+ sourceModuleName: moduleName,
+ })
+ );
+ }}
+ >
+
+ {a.name}
+
+
+ ))
+ )}
+ {todaysModules &&
+ Object.keys(todaysModules).flatMap((moduleName) =>
+ todaysModules[moduleName].quizzes.map((q) => (
+ {
+ e.dataTransfer.setData(
+ "draggableItem",
+ JSON.stringify({
+ type: "quiz",
+ item: q,
+ sourceModuleName: moduleName,
+ })
+ );
+ }}
+ >
+
+ {q.name}
+
+
+ ))
+ )}
+ {todaysModules &&
+ Object.keys(todaysModules).flatMap((moduleName) =>
+ todaysModules[moduleName].pages.map((p) => (
+ {
+ e.dataTransfer.setData(
+ "draggableItem",
+ JSON.stringify({
+ type: "page",
+ item: p,
+ sourceModuleName: moduleName,
+ })
+ );
+ }}
+ >
+
+ {p.name}
+
+
+ ))
+ )}
);
}
+
+// export default function Day({ day, month }: { day: string; month: number }) {
+// const { data: moduleNames } = useModuleNamesQuery();
+
+// const dayAsDate = getDateFromStringOrThrow(
+// day,
+// "calculating same month in day"
+// );
+// const isInSameMonth = dayAsDate.getMonth() + 1 != month;
+// const backgroundClass = isInSameMonth ? "" : "bg-slate-900";
+// const { itemDrop } = useDraggingContext();
+
+// return (
+// {
+// itemDrop(e, day);
+// }}
+// onDragOver={(e) => e.preventDefault()}
+// >
+// {dayAsDate.getDate()}
+// {moduleNames.map((moduleName) => (
+//
+// ))}
+//
+// );
+// }
diff --git a/nextjs/src/app/course/[courseName]/context/CalendarItemsContextProvider.tsx b/nextjs/src/app/course/[courseName]/context/CalendarItemsContextProvider.tsx
new file mode 100644
index 0000000..962d281
--- /dev/null
+++ b/nextjs/src/app/course/[courseName]/context/CalendarItemsContextProvider.tsx
@@ -0,0 +1,155 @@
+import { ReactNode } from "react";
+import { useCourseContext } from "./courseContext";
+import {
+ useAllCourseDataQuery,
+ useModuleDataQuery,
+} from "@/hooks/localCourse/localCoursesHooks";
+import {
+ CalendarItemsContext,
+ CalendarItemsInterface,
+} from "./calendarItemsContext";
+import {
+ dateToMarkdownString,
+ getDateFromStringOrThrow,
+ getDateOnlyMarkdownString,
+} from "@/models/local/timeUtils";
+import { LocalAssignment } from "@/models/local/assignment/localAssignment";
+import { LocalQuiz } from "@/models/local/quiz/localQuiz";
+import { LocalCoursePage } from "@/models/local/page/localCoursePage";
+
+export default function CalendarItemsContextProvider({
+ children,
+}: {
+ children: ReactNode;
+}) {
+ const { assignmentsAndModules, quizzesAndModules, pagesAndModules } =
+ useAllCourseDataQuery();
+
+ const assignmentsByModuleByDate = assignmentsAndModules.reduce(
+ (previous, { assignment, moduleName }) => {
+ const dueDay = getDateOnlyMarkdownString(
+ getDateFromStringOrThrow(
+ assignment.dueAt,
+ "due at for assignment in items context"
+ )
+ );
+ const previousModules = previous[dueDay] ?? {};
+ const previousModule = previousModules[moduleName] ?? {
+ assignments: [],
+ };
+
+ const updatedModule = {
+ ...previousModule,
+ assignments: [...previousModule.assignments, assignment],
+ };
+
+ return {
+ ...previous,
+ [dueDay]: {
+ ...previousModules,
+ [moduleName]: updatedModule,
+ },
+ };
+ },
+ {} as CalendarItemsInterface
+ );
+
+ const quizzesByModuleByDate = quizzesAndModules.reduce(
+ (previous, { quiz, moduleName }) => {
+ const dueDay = getDateOnlyMarkdownString(
+ getDateFromStringOrThrow(quiz.dueAt, "due at for quiz in items context")
+ );
+ const previousModules = previous[dueDay] ?? {};
+ const previousModule = previousModules[moduleName] ?? {
+ quizzes: [],
+ };
+
+ const updatedModule = {
+ ...previousModule,
+ quizzes: [...previousModule.quizzes, quiz],
+ };
+
+ return {
+ ...previous,
+ [dueDay]: {
+ ...previousModules,
+ [moduleName]: updatedModule,
+ },
+ };
+ },
+ {} as CalendarItemsInterface
+ );
+
+ const pagesByModuleByDate = pagesAndModules.reduce(
+ (previous, { page, moduleName }) => {
+ const dueDay = getDateOnlyMarkdownString(
+ getDateFromStringOrThrow(page.dueAt, "due at for quiz in items context")
+ );
+ const previousModules = previous[dueDay] ?? {};
+ const previousModule = previousModules[moduleName] ?? {
+ pages: [],
+ };
+
+ const updatedModule = {
+ ...previousModule,
+ pages: [...previousModule.pages, page],
+ };
+
+ return {
+ ...previous,
+ [dueDay]: {
+ ...previousModules,
+ [moduleName]: updatedModule,
+ },
+ };
+ },
+ {} as CalendarItemsInterface
+ );
+
+ const allDays = [
+ ...new Set([
+ ...Object.keys(assignmentsByModuleByDate),
+ ...Object.keys(quizzesByModuleByDate),
+ ...Object.keys(pagesByModuleByDate),
+ ]),
+ ];
+
+ const allItemsByModuleByDate = allDays.reduce((prev, day) => {
+ const assignmentModulesInDay = assignmentsByModuleByDate[day] ?? {};
+ const quizModulesInDay = quizzesByModuleByDate[day] ?? {};
+ const pageModulesInDay = pagesByModuleByDate[day] ?? {};
+
+ const allModules = [
+ ...new Set([
+ ...Object.keys(assignmentModulesInDay),
+ ...Object.keys(quizModulesInDay),
+ ...Object.keys(pageModulesInDay),
+ ]),
+ ];
+
+ const modulesInDate = allModules.reduce((prev, moduleName) => {
+ return {
+ ...prev,
+ [moduleName]: {
+ assignments: assignmentModulesInDay[moduleName]
+ ? assignmentModulesInDay[moduleName].assignments
+ : [],
+ quizzes: quizModulesInDay[moduleName]
+ ? quizModulesInDay[moduleName].quizzes
+ : [],
+ pages: pageModulesInDay[moduleName]
+ ? pageModulesInDay[moduleName].pages
+ : [],
+ },
+ };
+ }, {});
+
+ return { ...prev, [day]: modulesInDate };
+ }, {} as CalendarItemsInterface);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/nextjs/src/app/course/[courseName]/context/calendarItemsContext.ts b/nextjs/src/app/course/[courseName]/context/calendarItemsContext.ts
new file mode 100644
index 0000000..168a948
--- /dev/null
+++ b/nextjs/src/app/course/[courseName]/context/calendarItemsContext.ts
@@ -0,0 +1,22 @@
+import { LocalAssignment } from "@/models/local/assignment/localAssignment";
+import { LocalCoursePage } from "@/models/local/page/localCoursePage";
+import { LocalQuiz } from "@/models/local/quiz/localQuiz";
+import { createContext, useContext } from "react";
+
+export interface CalendarItemsInterface {
+ [
+ key: string // representing a date
+ ]: {
+ [moduleName: string]: {
+ assignments: LocalAssignment[];
+ quizzes: LocalQuiz[];
+ pages: LocalCoursePage[];
+ };
+ };
+}
+
+export const CalendarItemsContext = createContext({});
+
+export function useCalendarItemsContext() {
+ return useContext(CalendarItemsContext);
+}
diff --git a/nextjs/src/hooks/cavnasCouresHooks.ts b/nextjs/src/hooks/canvasCourseKeys.ts
similarity index 100%
rename from nextjs/src/hooks/cavnasCouresHooks.ts
rename to nextjs/src/hooks/canvasCourseKeys.ts
diff --git a/nextjs/src/hooks/localCourse/assignmentHooks.ts b/nextjs/src/hooks/localCourse/assignmentHooks.ts
index 1d80e5f..1687285 100644
--- a/nextjs/src/hooks/localCourse/assignmentHooks.ts
+++ b/nextjs/src/hooks/localCourse/assignmentHooks.ts
@@ -1,28 +1,40 @@
-"use client"
+"use client";
import axios from "axios";
import { localCourseKeys } from "./localCourseKeys";
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
-import { useSuspenseQuery, useSuspenseQueries, useQueryClient, useMutation } from "@tanstack/react-query";
+import {
+ useSuspenseQuery,
+ useSuspenseQueries,
+ useQueryClient,
+ useMutation,
+} from "@tanstack/react-query";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
+export const getAssignmentNamesQueryConfig = (
+ courseName: string,
+ moduleName: string
+) => ({
+ queryKey: localCourseKeys.assignmentNames(courseName, moduleName),
+ queryFn: async (): Promise => {
+ const url =
+ "/api/courses/" +
+ encodeURIComponent(courseName) +
+ "/modules/" +
+ encodeURIComponent(moduleName) +
+ "/assignments";
+ const response = await axios.get(url);
+ return response.data;
+ },
+});
+
export const useAssignmentNamesQuery = (moduleName: string) => {
const { courseName } = useCourseContext();
- return useSuspenseQuery({
- queryKey: localCourseKeys.assignmentNames(courseName, moduleName),
- queryFn: async (): Promise => {
- const url =
- "/api/courses/" +
- encodeURIComponent(courseName) +
- "/modules/" +
- encodeURIComponent(moduleName) +
- "/assignments";
- const response = await axios.get(url);
- return response.data;
- },
- });
+ return useSuspenseQuery(
+ getAssignmentNamesQueryConfig(courseName, moduleName)
+ );
};
-const getAssignmentQueryConfig = (
+export const getAssignmentQueryConfig = (
courseName: string,
moduleName: string,
assignmentName: string
@@ -46,6 +58,7 @@ const getAssignmentQueryConfig = (
},
};
};
+
export const useAssignmentQuery = (
moduleName: string,
assignmentName: string
@@ -101,7 +114,11 @@ export const useUpdateAssignmentMutation = () => {
},
onSuccess: (_, { moduleName, assignmentName }) => {
queryClient.invalidateQueries({
- queryKey: localCourseKeys.assignment(courseName, moduleName, assignmentName),
+ queryKey: localCourseKeys.assignment(
+ courseName,
+ moduleName,
+ assignmentName
+ ),
});
queryClient.invalidateQueries({
queryKey: localCourseKeys.assignmentNames(courseName, moduleName),
diff --git a/nextjs/src/hooks/localCourse/localCoursesHooks.ts b/nextjs/src/hooks/localCourse/localCoursesHooks.ts
index 9dbc070..8641711 100644
--- a/nextjs/src/hooks/localCourse/localCoursesHooks.ts
+++ b/nextjs/src/hooks/localCourse/localCoursesHooks.ts
@@ -1,14 +1,20 @@
"use client";
import { LocalCourseSettings } from "@/models/local/localCourse";
-import { useSuspenseQuery } from "@tanstack/react-query";
+import {
+ useQueries,
+ useSuspenseQueries,
+ useSuspenseQuery,
+} from "@tanstack/react-query";
import axios from "axios";
import { localCourseKeys } from "./localCourseKeys";
import {
+ getAssignmentNamesQueryConfig,
+ getAssignmentQueryConfig,
useAssignmentNamesQuery,
useAssignmentsQueries,
} from "./assignmentHooks";
-import { usePageNamesQuery, usePagesQueries } from "./pageHooks";
-import { useQuizNamesQuery, useQuizzesQueries } from "./quizHooks";
+import { getPageNamesQueryConfig, getPageQueryConfig, usePageNamesQuery, usePagesQueries } from "./pageHooks";
+import { getQuizNamesQueryConfig, getQuizQueryConfig, useQuizNamesQuery, useQuizzesQueries } from "./quizHooks";
import { useMemo } from "react";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
@@ -48,33 +54,128 @@ export const useModuleNamesQuery = () => {
// dangerous? really slowed down page...
// maybe it only slowed down with react query devtools...
-// export const useModuleDataQuery = (moduleName: string) => {
-// console.log("running");
-// const { data: assignmentNames } = useAssignmentNamesQuery(moduleName);
-// const { data: quizNames } = useQuizNamesQuery(moduleName);
-// const { data: pageNames } = usePageNamesQuery(moduleName);
+export const useModuleDataQuery = (moduleName: string) => {
+ console.log("running");
+ const { data: assignmentNames } = useAssignmentNamesQuery(moduleName);
+ const { data: quizNames } = useQuizNamesQuery(moduleName);
+ const { data: pageNames } = usePageNamesQuery(moduleName);
-// const { data: assignments } = useAssignmentsQueries(
-// moduleName,
-// assignmentNames
-// );
-// const { data: quizzes } = useQuizzesQueries(moduleName, quizNames);
-// const { data: pages } = usePagesQueries(moduleName, pageNames);
+ const { data: assignments } = useAssignmentsQueries(
+ moduleName,
+ assignmentNames
+ );
+ const { data: quizzes } = useQuizzesQueries(moduleName, quizNames);
+ const { data: pages } = usePagesQueries(moduleName, pageNames);
-// return {
-// assignments,
-// quizzes,
-// pages,
-// };
-// // return useMemo(
-// // () => ({
-// // assignments,
-// // quizzes,
-// // pages,
-// // }),
-// // [assignments, pages, quizzes]
-// // );
-// };
+ return {
+ assignments,
+ quizzes,
+ pages,
+ };
+ // return useMemo(
+ // () => ({
+ // assignments,
+ // quizzes,
+ // pages,
+ // }),
+ // [assignments, pages, quizzes]
+ // );
+};
+
+export const useAllCourseDataQuery = () => {
+ const { courseName } = useCourseContext();
+ const { data: moduleNames } = useModuleNamesQuery();
+
+ const { data: assignmentNamesAndModules } = useSuspenseQueries({
+ queries: moduleNames.map((moduleName) =>
+ getAssignmentNamesQueryConfig(courseName, moduleName)
+ ),
+ combine: (results) => ({
+ data: results.flatMap((r, i) =>
+ r.data.map((assignmentName) => ({
+ moduleName: moduleNames[i],
+ assignmentName,
+ }))
+ ),
+ pending: results.some((r) => r.isPending),
+ }),
+ });
+
+ const { data: assignmentsAndModules } = useSuspenseQueries({
+ queries: assignmentNamesAndModules.map(
+ ({ moduleName, assignmentName }, i) =>
+ getAssignmentQueryConfig(courseName, moduleName, assignmentName)
+ ),
+ combine: (results) => ({
+ data: results.flatMap((r, i) => ({
+ moduleName: assignmentNamesAndModules[i].moduleName,
+ assignment: r.data,
+ })),
+ pending: results.some((r) => r.isPending),
+ }),
+ });
+
+ const {data: quizNamesAndModules } = useSuspenseQueries({
+ queries: moduleNames.map((moduleName) =>
+ getQuizNamesQueryConfig(courseName, moduleName)
+ ),
+ combine: (results) => ({
+ data: results.flatMap((r, i) =>
+ r.data.map((quizName) => ({
+ moduleName: moduleNames[i],
+ quizName: quizName,
+ }))
+ ),
+ pending: results.some((r) => r.isPending),
+ }),
+ });
+
+ const { data: quizzesAndModules } = useSuspenseQueries({
+ queries: quizNamesAndModules.map(
+ ({ moduleName, quizName }, i) =>
+ getQuizQueryConfig(courseName, moduleName, quizName)
+ ),
+ combine: (results) => ({
+ data: results.flatMap((r, i) => ({
+ moduleName: quizNamesAndModules[i].moduleName,
+ quiz: r.data,
+ })),
+ pending: results.some((r) => r.isPending),
+ }),
+ });
+
+ const {data: pageNamesAndModules } = useSuspenseQueries({
+ queries: moduleNames.map((moduleName) =>
+ getPageNamesQueryConfig(courseName, moduleName)
+ ),
+ combine: (results) => ({
+ data: results.flatMap((r, i) =>
+ r.data.map((pageName) => ({
+ moduleName: moduleNames[i],
+ pageName,
+ }))
+ ),
+ pending: results.some((r) => r.isPending),
+ }),
+ });
+
+ const { data: pagesAndModules } = useSuspenseQueries({
+ queries: pageNamesAndModules.map(
+ ({ moduleName, pageName }, i) =>
+ getPageQueryConfig(courseName, moduleName, pageName)
+ ),
+ combine: (results) => ({
+ data: results.flatMap((r, i) => ({
+ moduleName: pageNamesAndModules[i].moduleName,
+ page: r.data,
+ })),
+ pending: results.some((r) => r.isPending),
+ }),
+ });
+
+
+ return { assignmentsAndModules, quizzesAndModules, pagesAndModules };
+};
// 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 bdbf00a..4c65e73 100644
--- a/nextjs/src/hooks/localCourse/pageHooks.ts
+++ b/nextjs/src/hooks/localCourse/pageHooks.ts
@@ -10,9 +10,11 @@ import axios from "axios";
import { localCourseKeys } from "./localCourseKeys";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
-export const usePageNamesQuery = (moduleName: string) => {
- const { courseName } = useCourseContext();
- return useSuspenseQuery({
+export function getPageNamesQueryConfig(
+ courseName: string,
+ moduleName: string
+) {
+ return {
queryKey: localCourseKeys.pageNames(courseName, moduleName),
queryFn: async (): Promise => {
const url =
@@ -24,8 +26,14 @@ export const usePageNamesQuery = (moduleName: string) => {
const response = await axios.get(url);
return response.data;
},
- });
+ };
+}
+
+export const usePageNamesQuery = (moduleName: string) => {
+ const { courseName } = useCourseContext();
+ return useSuspenseQuery(getPageNamesQueryConfig(courseName, moduleName));
};
+
export const usePageQuery = (moduleName: string, pageName: string) => {
const { courseName } = useCourseContext();
return useSuspenseQuery(getPageQueryConfig(courseName, moduleName, pageName));
@@ -44,7 +52,7 @@ export const usePagesQueries = (moduleName: string, pageNames: string[]) => {
});
};
-function getPageQueryConfig(
+export function getPageQueryConfig(
courseName: string,
moduleName: string,
pageName: string
diff --git a/nextjs/src/hooks/localCourse/quizHooks.ts b/nextjs/src/hooks/localCourse/quizHooks.ts
index f306f43..e4e6ff2 100644
--- a/nextjs/src/hooks/localCourse/quizHooks.ts
+++ b/nextjs/src/hooks/localCourse/quizHooks.ts
@@ -10,13 +10,12 @@ import axios from "axios";
import { localCourseKeys } from "./localCourseKeys";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
-export const useQuizNamesQuery = (moduleName: string) => {
- const { courseName } = useCourseContext();
- return useSuspenseQuery({
+
+export function getQuizNamesQueryConfig(courseName: string, moduleName: string) {
+ return {
queryKey: localCourseKeys.quizNames(courseName, moduleName),
queryFn: async (): Promise => {
- const url =
- "/api/courses/" +
+ const url = "/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
@@ -24,7 +23,11 @@ export const useQuizNamesQuery = (moduleName: string) => {
const response = await axios.get(url);
return response.data;
},
- });
+ };
+}
+export const useQuizNamesQuery = (moduleName: string) => {
+ const { courseName } = useCourseContext();
+ return useSuspenseQuery(getQuizNamesQueryConfig(courseName, moduleName));
};
export const useQuizQuery = (moduleName: string, quizName: string) => {
@@ -45,7 +48,7 @@ export const useQuizzesQueries = (moduleName: string, quizNames: string[]) => {
});
};
-function getQuizQueryConfig(
+export function getQuizQueryConfig(
courseName: string,
moduleName: string,
quizName: string
diff --git a/nextjs/src/models/local/timeUtils.ts b/nextjs/src/models/local/timeUtils.ts
index c32b936..70e5c2d 100644
--- a/nextjs/src/models/local/timeUtils.ts
+++ b/nextjs/src/models/local/timeUtils.ts
@@ -91,3 +91,8 @@ export const dateToMarkdownString = (date: Date) => {
return `${stringMonth}/${stringDay}/${stringYear} ${stringHours}:${stringMinutes}:${stringSeconds}`;
};
+
+
+export const getDateOnlyMarkdownString = (date: Date) => {
+ return dateToMarkdownString(date).split(" ")[0]
+}
\ No newline at end of file