diff --git a/nextjs/src/app/course/[courseName]/calendar/day/Day.tsx b/nextjs/src/app/course/[courseName]/calendar/day/Day.tsx
index 577a35a..5c23b46 100644
--- a/nextjs/src/app/course/[courseName]/calendar/day/Day.tsx
+++ b/nextjs/src/app/course/[courseName]/calendar/day/Day.tsx
@@ -1,20 +1,14 @@
"use client";
import {
- dateToMarkdownString,
getDateFromStringOrThrow,
getDateOnlyMarkdownString,
} from "@/models/local/timeUtils";
import { useDraggingContext } from "../../context/draggingContext";
-import { useCourseContext } from "../../context/courseContext";
-import Link from "next/link";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { getDayOfWeek } from "@/models/local/localCourse";
-import { getLectureUrl } from "@/services/urlUtils";
import { ItemInDay } from "./ItemInDay";
import { useTodaysItems } from "./useTodaysItems";
-import Modal from "@/components/Modal";
-import NewItemForm from "../../modules/NewItemForm";
-import { useLecturesByWeekQuery } from "@/hooks/localCourse/lectureHooks";
+import { DayTitle } from "./DayTitle";
export default function Day({ day, month }: { day: string; month: number }) {
const dayAsDate = getDateFromStringOrThrow(
@@ -130,33 +124,3 @@ export default function Day({ day, month }: { day: string; month: number }) {
);
}
-
-function DayTitle({ day, dayAsDate }: { day: string; dayAsDate: Date }) {
- const { courseName } = useCourseContext();
- const { data: weeks } = useLecturesByWeekQuery();
- const todaysLecture = weeks
- .flatMap((w) => w.lectures)
- .find((l) => l.date == getDateOnlyMarkdownString(dayAsDate));
- return (
-
-
- {dayAsDate.getDate()} {todaysLecture?.name}
-
-
- {({ closeModal }) => (
-
-
-
-
-
- )}
-
-
- );
-}
diff --git a/nextjs/src/app/course/[courseName]/calendar/day/DayTitle.tsx b/nextjs/src/app/course/[courseName]/calendar/day/DayTitle.tsx
new file mode 100644
index 0000000..0211136
--- /dev/null
+++ b/nextjs/src/app/course/[courseName]/calendar/day/DayTitle.tsx
@@ -0,0 +1,53 @@
+import Modal from "@/components/Modal";
+import { useLecturesByWeekQuery } from "@/hooks/localCourse/lectureHooks";
+import { getLectureUrl } from "@/services/urlUtils";
+import Link from "next/link";
+import { useCourseContext } from "../../context/courseContext";
+import NewItemForm from "../../modules/NewItemForm";
+import { DraggableItem } from "../../context/draggingContext";
+import { useDragStyleContext } from "../../context/dragStyleContext";
+import { getLectureForDay } from "@/models/local/lectureUtils";
+
+export function DayTitle({ day, dayAsDate }: { day: string; dayAsDate: Date }) {
+ const { courseName } = useCourseContext();
+ const { data: weeks } = useLecturesByWeekQuery();
+ const { setIsDragging } = useDragStyleContext();
+ const todaysLecture = getLectureForDay(weeks, dayAsDate);
+ return (
+
+
{
+ if (todaysLecture) {
+ const draggableItem: DraggableItem = {
+ type: "lecture",
+ item: { ...todaysLecture, dueAt: todaysLecture.date },
+ sourceModuleName: undefined,
+ };
+ e.dataTransfer.setData(
+ "draggableItem",
+ JSON.stringify(draggableItem)
+ );
+ setIsDragging(true);
+ }
+ }}
+ >
+ {dayAsDate.getDate()} {todaysLecture?.name}
+
+
+ {({ closeModal }) => (
+
+
+
+
+
+ )}
+
+
+ );
+}
diff --git a/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx b/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx
index 6fb47d3..fa85135 100644
--- a/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx
+++ b/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx
@@ -7,12 +7,19 @@ import { LocalQuiz } from "@/models/local/quiz/localQuiz";
import {
getDateFromStringOrThrow,
dateToMarkdownString,
+ getDateOnlyMarkdownString,
} from "@/models/local/timeUtils";
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
import { useUpdateAssignmentMutation } from "@/hooks/localCourse/assignmentHooks";
import { useUpdatePageMutation } from "@/hooks/localCourse/pageHooks";
import { LocalCoursePage } from "@/models/local/page/localCoursePage";
import { useDragStyleContext } from "./dragStyleContext";
+import { Lecture } from "@/models/local/lecture";
+import {
+ useLecturesByWeekQuery,
+ useLectureUpdateMutation,
+} from "@/hooks/localCourse/lectureHooks";
+import { getLectureForDay } from "@/models/local/lectureUtils";
export default function DraggingContextProvider({
children,
@@ -21,9 +28,11 @@ export default function DraggingContextProvider({
}) {
const { setIsDragging } = useDragStyleContext();
const updateQuizMutation = useUpdateQuizMutation();
+ const updateLectureMutation = useLectureUpdateMutation();
const updateAssignmentMutation = useUpdateAssignmentMutation();
const updatePageMutation = useUpdatePageMutation();
const { data: settings } = useLocalCourseSettingsQuery();
+ const { data: weeks } = useLecturesByWeekQuery();
useEffect(() => {
const handleDrop = () => {
@@ -55,40 +64,63 @@ export default function DraggingContextProvider({
updateAssignment();
} else if (itemBeingDragged.type === "page") {
updatePage();
+ } else if (itemBeingDragged.type === "lecture") {
+ // const lecture = itemBeingDragged.item as Lecture & { dueAt: string };
+ console.log("cannot drop lecture on module, only on days");
}
}
setIsDragging(false);
function updateQuiz() {
const quiz = itemBeingDragged.item as LocalQuiz;
-
- updateQuizMutation.mutate({
- item: quiz,
- itemName: quiz.name,
- moduleName: dropModuleName,
- previousModuleName: itemBeingDragged.sourceModuleName,
- previousItemName: quiz.name,
- });
+ if (itemBeingDragged.sourceModuleName) {
+ updateQuizMutation.mutate({
+ item: quiz,
+ itemName: quiz.name,
+ moduleName: dropModuleName,
+ previousModuleName: itemBeingDragged.sourceModuleName,
+ previousItemName: quiz.name,
+ });
+ } else {
+ console.error(
+ `error dropping quiz, sourceModuleName is undefined `,
+ quiz
+ );
+ }
}
function updateAssignment() {
const assignment = itemBeingDragged.item as LocalAssignment;
- updateAssignmentMutation.mutate({
- item: assignment,
- previousModuleName: itemBeingDragged.sourceModuleName,
- moduleName: dropModuleName,
- itemName: assignment.name,
- previousItemName: assignment.name,
- });
+ if (itemBeingDragged.sourceModuleName) {
+ updateAssignmentMutation.mutate({
+ item: assignment,
+ previousModuleName: itemBeingDragged.sourceModuleName,
+ moduleName: dropModuleName,
+ itemName: assignment.name,
+ previousItemName: assignment.name,
+ });
+ } else {
+ console.error(
+ `error dropping assignment, sourceModuleName is undefined `,
+ assignment
+ );
+ }
}
function updatePage() {
const page = itemBeingDragged.item as LocalCoursePage;
- updatePageMutation.mutate({
- item: page,
- moduleName: dropModuleName,
- itemName: page.name,
- previousItemName: page.name,
- previousModuleName: itemBeingDragged.sourceModuleName,
- });
+ if (itemBeingDragged.sourceModuleName) {
+ updatePageMutation.mutate({
+ item: page,
+ moduleName: dropModuleName,
+ itemName: page.name,
+ previousItemName: page.name,
+ previousModuleName: itemBeingDragged.sourceModuleName,
+ });
+ } else {
+ console.error(
+ `error dropping page, sourceModuleName is undefined `,
+ page
+ );
+ }
}
},
[
@@ -113,6 +145,8 @@ export default function DraggingContextProvider({
updateAssignment(dayAsDate);
} else if (itemBeingDragged.type === "page") {
updatePage(dayAsDate);
+ } else if (itemBeingDragged.type === "lecture") {
+ updateLecture(dayAsDate);
}
}
setIsDragging(false);
@@ -124,8 +158,32 @@ export default function DraggingContextProvider({
dayAsDate.setSeconds(0);
return dayAsDate;
}
+ function updateLecture(dayAsDate: Date) {
+ const { dueAt, ...lecture } = itemBeingDragged.item as Lecture & {
+ dueAt: string;
+ };
+ console.log("dropped lecture on day");
+ const existingLecture = getLectureForDay(weeks, dayAsDate);
+ if (existingLecture) {
+ console.log("attempting to drop on existing lecture");
+ } else {
+ updateLectureMutation.mutate({
+ previousDay: lecture.date,
+ lecture: {
+ ...lecture,
+ date: getDateOnlyMarkdownString(dayAsDate),
+ },
+ });
+ }
+ }
function updateQuiz(dayAsDate: Date) {
const previousQuiz = itemBeingDragged.item as LocalQuiz;
+ if (!itemBeingDragged.sourceModuleName) {
+ console.error(
+ "error dropping quiz on day, sourceModuleName is undefined"
+ );
+ return;
+ }
const quiz: LocalQuiz = {
...previousQuiz,
@@ -146,6 +204,12 @@ export default function DraggingContextProvider({
}
function updatePage(dayAsDate: Date) {
const previousPage = itemBeingDragged.item as LocalCoursePage;
+ if (!itemBeingDragged.sourceModuleName) {
+ console.error(
+ "error dropping page on day, sourceModuleName is undefined"
+ );
+ return;
+ }
const page: LocalCoursePage = {
...previousPage,
dueAt: dateToMarkdownString(dayAsDate),
@@ -159,6 +223,12 @@ export default function DraggingContextProvider({
});
}
function updateAssignment(dayAsDate: Date) {
+ if (!itemBeingDragged.sourceModuleName) {
+ console.error(
+ "error dropping assignment on day, sourceModuleName is undefined"
+ );
+ return;
+ }
const previousAssignment = itemBeingDragged.item as LocalAssignment;
const assignment: LocalAssignment = {
...previousAssignment,
@@ -199,6 +269,7 @@ export default function DraggingContextProvider({
);
}
+
function getNewLockDate(
originalDueDate: string,
originalLockDate: string | undefined,
diff --git a/nextjs/src/app/course/[courseName]/context/draggingContext.tsx b/nextjs/src/app/course/[courseName]/context/draggingContext.tsx
index 4b208f3..6fa64b8 100644
--- a/nextjs/src/app/course/[courseName]/context/draggingContext.tsx
+++ b/nextjs/src/app/course/[courseName]/context/draggingContext.tsx
@@ -4,8 +4,8 @@ import { createContext, useContext, DragEvent } from "react";
export interface DraggableItem {
item: IModuleItem;
- sourceModuleName: string;
- type: "quiz" | "assignment" | "page";
+ sourceModuleName: string | undefined; // undefined for lectures
+ type: "quiz" | "assignment" | "page" | "lecture";
}
export interface DraggingContextInterface {
diff --git a/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx b/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx
index d010b88..d9add61 100644
--- a/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx
+++ b/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx
@@ -38,7 +38,7 @@ Date: ${lectureDay}
const parsed = parseLecture(text);
if (!lecture || lectureToString(parsed) !== lectureToString(lecture)) {
console.log("updating lecture");
- updateLecture.mutate(parsed);
+ updateLecture.mutate({ lecture: parsed });
}
setError("");
} catch (e: any) {
diff --git a/nextjs/src/app/todaysLectures/OneCourseLectures.tsx b/nextjs/src/app/todaysLectures/OneCourseLectures.tsx
index 2e8ca6e..2718a4d 100644
--- a/nextjs/src/app/todaysLectures/OneCourseLectures.tsx
+++ b/nextjs/src/app/todaysLectures/OneCourseLectures.tsx
@@ -5,6 +5,7 @@ import { getDateOnlyMarkdownString } from "@/models/local/timeUtils";
import { getLecturePreviewUrl } from "@/services/urlUtils";
import Link from "next/link";
import { useCourseContext } from "../course/[courseName]/context/courseContext";
+import { getLectureForDay } from "@/models/local/lectureUtils";
export default function OneCourseLectures() {
const { courseName } = useCourseContext();
@@ -12,9 +13,7 @@ export default function OneCourseLectures() {
const dayAsDate = new Date();
const dayAsString = getDateOnlyMarkdownString(dayAsDate);
- const todaysLecture = weeks
- .flatMap((w) => w.lectures)
- .find((l) => l.date == dayAsString);
+ const todaysLecture = getLectureForDay(weeks, dayAsDate);
if (!todaysLecture) return <>>;
return (
{
const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient();
return useMutation({
- mutationFn: async (lecture: Lecture) => {
+ mutationFn: async ({
+ lecture,
+ previousDay,
+ }: {
+ lecture: Lecture;
+ previousDay?: string;
+ }) => {
await updateLecture(courseName, settings, lecture);
+
+ if (previousDay && previousDay !== lecture.date) {
+ await deleteLecture(courseName, settings, previousDay);
+ }
},
onSuccess: () => {
queryClient.invalidateQueries({
diff --git a/nextjs/src/models/local/lectureUtils.ts b/nextjs/src/models/local/lectureUtils.ts
new file mode 100644
index 0000000..15bdbcd
--- /dev/null
+++ b/nextjs/src/models/local/lectureUtils.ts
@@ -0,0 +1,9 @@
+import { Lecture } from "./lecture";
+import { getDateOnlyMarkdownString } from "./timeUtils";
+
+export function getLectureForDay(weeks: { weekName: string; lectures: Lecture[]; }[], dayAsDate: Date) {
+ return weeks
+ .flatMap((w) => w.lectures)
+ .find((l) => l.date == getDateOnlyMarkdownString(dayAsDate));
+}
+
diff --git a/nextjs/src/services/fileStorage/lectureFileStorageService.ts b/nextjs/src/services/fileStorage/lectureFileStorageService.ts
index b679215..884c680 100644
--- a/nextjs/src/services/fileStorage/lectureFileStorageService.ts
+++ b/nextjs/src/services/fileStorage/lectureFileStorageService.ts
@@ -57,7 +57,10 @@ export async function updateLecture(
"lecture start date in update lecture"
);
- const weekFolderName = getLectureWeekName(courseSettings.startDate, lecture.date);
+ const weekFolderName = getLectureWeekName(
+ courseSettings.startDate,
+ lecture.date
+ );
const weekPath = path.join(courseLectureRoot, weekFolderName);
if (!(await directoryExists(weekPath))) {
await fs.mkdir(weekPath, { recursive: true });
@@ -71,6 +74,41 @@ export async function updateLecture(
await fs.writeFile(lecturePath, lectureContents);
}
+export async function deleteLecture(
+ courseName: string,
+ courseSettings: LocalCourseSettings,
+ dayAsString: string
+) {
+ console.log("deleting lecture", courseName, dayAsString);
+ const lectureDate = getDateFromStringOrThrow(
+ dayAsString,
+ "lecture start date in update lecture"
+ );
+
+ const weekFolderName = getLectureWeekName(
+ courseSettings.startDate,
+ dayAsString
+ );
+
+ const courseLectureRoot = path.join(basePath, courseName, lectureFolderName);
+ const weekPath = path.join(courseLectureRoot, weekFolderName);
+ const lecturePath = path.join(
+ weekPath,
+ `${lectureDate.getDay()}-${getDayOfWeek(lectureDate)}.md`
+ );
+ try {
+ await fs.access(lecturePath); // throws error if no file
+ await fs.unlink(lecturePath);
+ console.log(`File deleted: ${lecturePath}`);
+ } catch (error: any) {
+ if (error?.code === "ENOENT") {
+ console.log(`Cannot delete lecture, file does not exist: ${lecturePath}`);
+ } else {
+ throw error;
+ }
+ }
+}
+
const directoryExists = async (path: string): Promise => {
try {
const stat = await fs.stat(path);