prevent lectures from dropping on each other

This commit is contained in:
2024-11-02 13:17:36 -06:00
parent b5dc579411
commit 14003d52c9
9 changed files with 212 additions and 67 deletions

View File

@@ -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 }) {
</div>
);
}
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 (
<div className="flex justify-between">
<Link
className="ms-1 me-1 truncate text-nowrap transition-all hover:font-bold hover:text-slate-300"
href={getLectureUrl(courseName, day)}
>
{dayAsDate.getDate()} {todaysLecture?.name}
</Link>
<Modal
buttonText="+"
buttonClass="unstyled hover:font-bold hover:scale-125 px-1 mb-auto mt-0 pt-0"
>
{({ closeModal }) => (
<div>
<NewItemForm creationDate={day} onCreate={closeModal} />
<br />
<button onClick={closeModal}>close</button>
</div>
)}
</Modal>
</div>
);
}

View File

@@ -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 (
<div className="flex justify-between">
<Link
className="ms-1 me-1 truncate text-nowrap transition-all hover:font-bold hover:text-slate-300"
href={getLectureUrl(courseName, day)}
draggable={true}
onDragStart={(e) => {
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}
</Link>
<Modal
buttonText="+"
buttonClass="unstyled hover:font-bold hover:scale-125 px-1 mb-auto mt-0 pt-0"
>
{({ closeModal }) => (
<div>
<NewItemForm creationDate={day} onCreate={closeModal} />
<br />
<button onClick={closeModal}>close</button>
</div>
)}
</Modal>
</div>
);
}

View File

@@ -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,13 +64,16 @@ 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;
if (itemBeingDragged.sourceModuleName) {
updateQuizMutation.mutate({
item: quiz,
itemName: quiz.name,
@@ -69,9 +81,16 @@ export default function DraggingContextProvider({
previousModuleName: itemBeingDragged.sourceModuleName,
previousItemName: quiz.name,
});
} else {
console.error(
`error dropping quiz, sourceModuleName is undefined `,
quiz
);
}
}
function updateAssignment() {
const assignment = itemBeingDragged.item as LocalAssignment;
if (itemBeingDragged.sourceModuleName) {
updateAssignmentMutation.mutate({
item: assignment,
previousModuleName: itemBeingDragged.sourceModuleName,
@@ -79,9 +98,16 @@ export default function DraggingContextProvider({
itemName: assignment.name,
previousItemName: assignment.name,
});
} else {
console.error(
`error dropping assignment, sourceModuleName is undefined `,
assignment
);
}
}
function updatePage() {
const page = itemBeingDragged.item as LocalCoursePage;
if (itemBeingDragged.sourceModuleName) {
updatePageMutation.mutate({
item: page,
moduleName: dropModuleName,
@@ -89,6 +115,12 @@ export default function DraggingContextProvider({
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({
</DraggingContext.Provider>
);
}
function getNewLockDate(
originalDueDate: string,
originalLockDate: string | undefined,

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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 (
<Link

View File

@@ -6,6 +6,7 @@ import {
import { lectureKeys } from "./lectureKeys";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
import {
deleteLecture,
getLectures,
updateLecture,
} from "@/services/fileStorage/lectureFileStorageService";
@@ -28,8 +29,18 @@ export const useLectureUpdateMutation = () => {
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({

View File

@@ -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));
}

View File

@@ -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<boolean> => {
try {
const stat = await fs.stat(path);