mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 07:38:33 -06:00
can manage course navigation from settinss
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import AssignmentGroupManagement from "./AssignmentGroupManagement";
|
||||
import { CanvasNavigationManagement } from "./canvasNavigation.tsx/CanvasNavigationManagement";
|
||||
import DaysOfWeekSettings from "./DaysOfWeekSettings";
|
||||
import DefaultDueTime from "./DefaultDueTime";
|
||||
import DefaultFileUploadTypes from "./DefaultFileUploadTypes";
|
||||
@@ -22,6 +23,12 @@ export default function AllSettings() {
|
||||
<DefaultDueTime />
|
||||
<AssignmentGroupManagement />
|
||||
<HolidayConfig />
|
||||
<CanvasNavigationManagement />
|
||||
<div className="p-16"></div>
|
||||
<div className="p-16"></div>
|
||||
<div className="p-16"></div>
|
||||
<div className="p-16"></div>
|
||||
<div className="p-16"></div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import React, { useState } from "react";
|
||||
import { useCanvasTabsQuery } from "@/hooks/canvas/canvasNavigationHooks";
|
||||
import { CanvasCourseTab } from "@/services/canvas/canvasNavigationService";
|
||||
import { useUpdateCanvasTabMutation } from "@/hooks/canvas/canvasNavigationHooks";
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
import { NavTabListItem } from "./NavTabListItem";
|
||||
|
||||
export const CanvasNavigationManagement = () => {
|
||||
const { data: tabs, isLoading, isError } = useCanvasTabsQuery();
|
||||
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
|
||||
const updateTab = useUpdateCanvasTabMutation();
|
||||
|
||||
const handleHideAllExternal = async () => {
|
||||
if (!tabs) return;
|
||||
for (const tab of tabs.filter(
|
||||
(tab) => tab.type.toLowerCase() === "external" && !tab.hidden
|
||||
)) {
|
||||
await updateTab.mutateAsync({
|
||||
tabId: tab.id,
|
||||
hidden: true,
|
||||
position: tab.position,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) return <div>Loading tabs...</div>;
|
||||
if (isError || !tabs) return <div>Error loading tabs.</div>;
|
||||
|
||||
const handleDragStart = (idx: number) => setDraggedIndex(idx);
|
||||
const handleDrop = async (dropIdx: number) => {
|
||||
if (draggedIndex === null || draggedIndex === dropIdx || !tabs) {
|
||||
setDraggedIndex(null);
|
||||
return;
|
||||
}
|
||||
const newTabs = [...tabs].sort((a, b) => a.position - b.position);
|
||||
const [removed] = newTabs.splice(draggedIndex, 1);
|
||||
newTabs.splice(dropIdx, 0, removed);
|
||||
// Persist new order
|
||||
for (let i = 0; i < newTabs.length; i++) {
|
||||
const tab = newTabs[i];
|
||||
if (tab.position !== i + 1) {
|
||||
await updateTab.mutateAsync({
|
||||
tabId: tab.id,
|
||||
position: i + 1,
|
||||
hidden: tab.hidden,
|
||||
});
|
||||
}
|
||||
}
|
||||
setDraggedIndex(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className=" mx-auto p-4">
|
||||
<div className="flex justify-between">
|
||||
<h2 className="text-xl font-bold mb-4">
|
||||
Manage Course Navigation Tabs
|
||||
</h2>
|
||||
<div>
|
||||
{updateTab.isPending ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<button
|
||||
className="mb-4 px-3 py-1 rounded bg-slate-700 hover:bg-slate-600 text-white"
|
||||
onClick={handleHideAllExternal}
|
||||
disabled={updateTab.isPending}
|
||||
>
|
||||
Hide All External
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-center ">
|
||||
<ul className="w-md h-[800px] overflow-y-auto rounded bg-slate-950 p-4 border border-slate-700">
|
||||
{[...tabs]
|
||||
.sort((a, b) => a.position - b.position)
|
||||
.map((tab, idx) => (
|
||||
<NavTabListItem
|
||||
key={tab.id}
|
||||
tab={tab}
|
||||
idx={idx}
|
||||
onDragStart={() => handleDragStart(idx)}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
onDrop={() => handleDrop(idx)}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
import { useUpdateCanvasTabMutation } from "@/hooks/canvas/canvasNavigationHooks";
|
||||
import { CanvasCourseTab } from "@/services/canvas/canvasNavigationService";
|
||||
import React, { FC } from "react";
|
||||
|
||||
export const NavTabListItem: FC<{
|
||||
tab: CanvasCourseTab;
|
||||
idx: number;
|
||||
onDragStart: () => void;
|
||||
onDragOver: (e: React.DragEvent) => void;
|
||||
onDrop: () => void;
|
||||
}> = ({ tab, idx, onDragStart, onDragOver, onDrop }) => {
|
||||
const updateTab = useUpdateCanvasTabMutation();
|
||||
const handleToggleVisibility = () => {
|
||||
updateTab.mutate({
|
||||
tabId: tab.id,
|
||||
hidden: !tab.hidden,
|
||||
position: tab.position,
|
||||
});
|
||||
};
|
||||
return (
|
||||
<li
|
||||
key={tab.id}
|
||||
className={`flex items-center justify-between mb-2 p-1 px-4 rounded bg-slate-800 ${
|
||||
tab.hidden ? "opacity-50" : ""
|
||||
}`}
|
||||
draggable
|
||||
onDragStart={onDragStart}
|
||||
onDragOver={onDragOver}
|
||||
onDrop={onDrop}
|
||||
>
|
||||
<span className="flex-1 cursor-move">{tab.label}</span>
|
||||
{updateTab.isPending && <Spinner />}
|
||||
<span className="text-xs text-slate-400 mr-2">{tab.type}</span>
|
||||
<button
|
||||
className={` py-1 rounded unstyled w-20 ${
|
||||
tab.hidden ? "bg-slate-600" : "bg-blue-900/50"
|
||||
}`}
|
||||
onClick={handleToggleVisibility}
|
||||
disabled={updateTab.isPending}
|
||||
>
|
||||
{tab.hidden ? "Show" : "Hide"}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
@@ -14,7 +14,7 @@ export function makeQueryClient() {
|
||||
// refetchInterval: 7000, // debug only
|
||||
refetchOnWindowFocus: false,
|
||||
retry: 0,
|
||||
refetchOnMount: false,
|
||||
// refetchOnMount: false,
|
||||
},
|
||||
mutations: {
|
||||
onError: (error) => {
|
||||
|
||||
Reference in New Issue
Block a user