diff --git a/nextjs/src/app/course/[courseName]/calendar/day/ItemInDay.tsx b/nextjs/src/app/course/[courseName]/calendar/day/ItemInDay.tsx index d900ba1..9d49616 100644 --- a/nextjs/src/app/course/[courseName]/calendar/day/ItemInDay.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/day/ItemInDay.tsx @@ -1,12 +1,13 @@ import { IModuleItem } from "@/models/local/IModuleItem"; import { getModuleItemUrl } from "@/services/urlUtils"; import Link from "next/link"; -import { ReactNode } from "react"; +import { ReactNode, useEffect, useRef, useState } from "react"; import { useCourseContext } from "../../context/courseContext"; import { useDraggingContext, DraggableItem, } from "../../context/draggingContext"; +import { createPortal } from "react-dom"; export function ItemInDay({ type, @@ -23,6 +24,8 @@ export function ItemInDay({ }) { const { courseName } = useCourseContext(); const { dragStart } = useDraggingContext(); + const linkRef = useRef(null); + const [tooltipVisible, setTooltipVisible] = useState(false); return (
setTooltipVisible(true)} + onMouseLeave={() => setTooltipVisible(false)} + ref={linkRef} > {item.name} - {status === "incomplete" && ( + + {/* {status === "incomplete" && ( - )} + )} */}
); } + +const Tooltip: React.FC<{ + message: ReactNode; + targetRef: React.RefObject; + visible: boolean; +}> = ({ message, targetRef, visible }) => { + const [position, setPosition] = useState({ top: 0, left: 0 }); + + useEffect(() => { + if (targetRef.current && visible) { + const rect = targetRef.current.getBoundingClientRect(); + // Calculate tooltip position relative to the target + setPosition({ + top: rect.bottom + window.scrollY + 10, // Adjust based on your needs + left: rect.left + window.scrollX + rect.width / 2, // Center tooltip + }); + } + }, [targetRef, visible]); + + // if (!visible) return null; + + return createPortal( + , + document.body // Render outside of overflow-hidden parent + ); +}; diff --git a/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx b/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx index f6ea7df..de1adba 100644 --- a/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx +++ b/nextjs/src/app/course/[courseName]/modules/ExpandableModule.tsx @@ -34,14 +34,12 @@ export default function ExpandableModule({ }: { moduleName: string; }) { - const { data: assignmentNames } = useAssignmentNamesQuery(moduleName); const { data: quizNames } = useQuizNamesQuery(moduleName); const { data: pageNames } = usePageNamesQuery(moduleName); const { itemDropOnModule } = useDraggingContext(); const { data: assignments } = useAssignmentsQueries( moduleName, - assignmentNames ); const { data: quizzes } = useQuizzesQueries(moduleName, quizNames); const { data: pages } = usePagesQueries(moduleName, pageNames); diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentButtons.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentButtons.tsx new file mode 100644 index 0000000..4938e26 --- /dev/null +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentButtons.tsx @@ -0,0 +1,152 @@ +import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; +import Modal from "@/components/Modal"; +import { Spinner } from "@/components/Spinner"; +import { + useCanvasAssignmentsQuery, + useAddAssignmentToCanvasMutation, + useDeleteAssignmentFromCanvasMutation, + useUpdateAssignmentInCanvasMutation, +} from "@/hooks/canvas/canvasAssignmentHooks"; +import { + useAssignmentQuery, + useDeleteAssignmentMutation, +} from "@/hooks/localCourse/assignmentHooks"; +import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; +import { baseCanvasUrl } from "@/services/canvas/canvasServiceUtils"; +import { getCourseUrl } from "@/services/urlUtils"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; + +export function AssignmentButtons({ + moduleName, + assignmentName, + toggleHelp, +}: { + assignmentName: string; + moduleName: string; + toggleHelp: () => void; +}) { + const router = useRouter(); + const { courseName } = useCourseContext(); + const { data: settings } = useLocalCourseSettingsQuery(); + const { + data: canvasAssignments, + isPending: canvasIsPending, + isRefetching: canvasIsRefetching, + } = useCanvasAssignmentsQuery(); + const { + data: assignment, + isPending: assignmentIsPending, + isRefetching, + } = useAssignmentQuery(moduleName, assignmentName); + const addToCanvas = useAddAssignmentToCanvasMutation(); + const deleteFromCanvas = useDeleteAssignmentFromCanvasMutation(); + const updateAssignment = useUpdateAssignmentInCanvasMutation(); + const deleteLocal = useDeleteAssignmentMutation(); + + const assignmentInCanvas = canvasAssignments.find( + (a) => a.name === assignmentName + ); + + const anythingIsLoading = + addToCanvas.isPending || + canvasIsPending || + assignmentIsPending || + isRefetching || + canvasIsRefetching || + deleteFromCanvas.isPending || + updateAssignment.isPending; + + return ( +
+
+ +
+
+ {anythingIsLoading && } + {assignmentInCanvas && !assignmentInCanvas.published && ( +
Not Published
+ )} + {!assignmentInCanvas && ( + + )} + {assignmentInCanvas && ( + + View in Canvas + + )} + {assignmentInCanvas && ( + + )} + {assignmentInCanvas && ( + + )} + {!assignmentInCanvas && ( + + {({ closeModal }) => ( +
+
+ Are you sure you want to delete this quiz locally? +
+
+
+ + +
+
+ )} +
+ )} + + Go Back + +
+
+ ); +} diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx index 919c874..1442f6b 100644 --- a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx @@ -24,6 +24,7 @@ import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling" import { AssignmentSubmissionType } from "@/models/local/assignment/assignmentSubmissionType"; import { LocalCourseSettings } from "@/models/local/localCourse"; import { useRouter } from "next/navigation"; +import { AssignmentButtons } from "./AssignmentButtons"; export default function EditAssignment({ moduleName, @@ -141,104 +142,3 @@ Assignment Group Names: - ${groupNames}`; return helpString; } - -function AssignmentButtons({ - moduleName, - assignmentName, - toggleHelp, -}: { - assignmentName: string; - moduleName: string; - toggleHelp: () => void; -}) { - const { courseName } = useCourseContext(); - const { data: settings } = useLocalCourseSettingsQuery(); - const { - data: canvasAssignments, - isPending: canvasIsPending, - isRefetching: canvasIsRefetching, - } = useCanvasAssignmentsQuery(); - const { - data: assignment, - isPending: assignmentIsPending, - isRefetching, - } = useAssignmentQuery(moduleName, assignmentName); - const addToCanvas = useAddAssignmentToCanvasMutation(); - const deleteFromCanvas = useDeleteAssignmentFromCanvasMutation(); - const updateAssignment = useUpdateAssignmentInCanvasMutation(); - - const assignmentInCanvas = canvasAssignments.find( - (a) => a.name === assignmentName - ); - - const anythingIsLoading = - addToCanvas.isPending || - canvasIsPending || - assignmentIsPending || - isRefetching || - canvasIsRefetching || - deleteFromCanvas.isPending || - updateAssignment.isPending; - - return ( -
-
- -
-
- {anythingIsLoading && } - {assignmentInCanvas && !assignmentInCanvas.published && ( -
Not Published
- )} - {!assignmentInCanvas && ( - - )} - {assignmentInCanvas && ( - - View in Canvas - - )} - {assignmentInCanvas && ( - - )} - {assignmentInCanvas && ( - - )} - - Go Back - -
-
- ); -} diff --git a/nextjs/src/hooks/localCourse/assignmentHooks.ts b/nextjs/src/hooks/localCourse/assignmentHooks.ts index 42699ec..cd4708d 100644 --- a/nextjs/src/hooks/localCourse/assignmentHooks.ts +++ b/nextjs/src/hooks/localCourse/assignmentHooks.ts @@ -46,7 +46,7 @@ export const getAssignmentQueryConfig = ( moduleName, assignmentName ), - queryFn: async (): Promise => { + queryFn: async () => { const url = "/api/courses/" + encodeURIComponent(courseName) + @@ -54,7 +54,7 @@ export const getAssignmentQueryConfig = ( encodeURIComponent(moduleName) + "/assignments/" + encodeURIComponent(assignmentName); - const response = await axiosClient.get(url); + const response = await axiosClient.get(url); return response.data; }, }; @@ -71,10 +71,8 @@ export const useAssignmentQuery = ( ); }; -export const useAssignmentsQueries = ( - moduleName: string, - assignmentNames: string[] -) => { +export const useAssignmentsQueries = (moduleName: string) => { + const { data: assignmentNames } = useAssignmentNamesQuery(moduleName); const { courseName } = useCourseContext(); return useSuspenseQueries({ queries: assignmentNames.map((name) => @@ -195,3 +193,45 @@ export const useCreateAssignmentMutation = () => { }, }); }; + +export const useDeleteAssignmentMutation = () => { + const { courseName } = useCourseContext(); + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + moduleName, + assignmentName, + }: { + moduleName: string; + assignmentName: string; + }) => { + + queryClient.removeQueries({ + queryKey: localCourseKeys.assignment( + courseName, + moduleName, + assignmentName + ), + }); + queryClient.removeQueries({ + queryKey: localCourseKeys.assignmentNames( + courseName, + moduleName + ), + }); + const url = + "/api/courses/" + + encodeURIComponent(courseName) + + "/modules/" + + encodeURIComponent(moduleName) + + "/assignments/" + + encodeURIComponent(assignmentName); + await axiosClient.delete(url); + }, + onSuccess: async (_, { moduleName, assignmentName }) => { + queryClient.invalidateQueries({ + queryKey: localCourseKeys.assignmentNames(courseName, moduleName), + }); + }, + }); +};