mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 15:18:32 -06:00
sort module button
This commit is contained in:
@@ -15,6 +15,7 @@ services:
|
||||
- NEXT_PUBLIC_ENABLE_FILE_SYNC=true
|
||||
- REDIS_URL=redis://redis:6379
|
||||
volumes:
|
||||
# - ./globalSettings.dev.yml:/app/globalSettings.yml
|
||||
- ./globalSettings.yml:/app/globalSettings.yml
|
||||
- .:/app
|
||||
- ~/projects/faculty:/app/storage
|
||||
|
||||
@@ -14,30 +14,8 @@ services:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
# - FILE_POLLING=true
|
||||
volumes:
|
||||
# - ~/projects/faculty/3840_Telemetry/2024Spring_alex/modules:/app/storage/spring_2024_telemetry
|
||||
|
||||
# - ~/projects/faculty/1400/2025_spring_alex/modules:/app/storage/1400
|
||||
# - ~/projects/faculty/1405/2025_spring_alex:/app/storage/1405
|
||||
# - ~/projects/faculty/3840_Telemetry/2025_spring_alex/modules:/app/storage/telemetry
|
||||
# - ~/projects/faculty/4620_Distributed/2025Spring/modules:/app/storage/distributed
|
||||
# - ~/projects/faculty/4620_Distributed/2024Spring/modules:/app/storage/distributed_old
|
||||
|
||||
|
||||
- ~/projects/faculty/1810/2025-spring-alex/in-person:/app/storage/intro_to_web_old
|
||||
- ~/projects/faculty/1810/2025-fall-alex/modules:/app/storage/intro_to_web
|
||||
|
||||
- ~/projects/faculty/4850_AdvancedFE/2025-fall-alex/modules:/app/storage/advanced_frontend
|
||||
- ~/projects/faculty/4850_AdvancedFE/2024-fall-alex/modules:/app/storage/advanced_frontend_old
|
||||
|
||||
- ~/projects/faculty/1430/2024-fall-alex/modules:/app/storage/ux_old
|
||||
- ~/projects/faculty/1430/2025-fall-alex/modules:/app/storage/ux
|
||||
|
||||
- ~/projects/faculty/1420/2024-fall/Modules:/app/storage/1420_old
|
||||
- ~/projects/faculty/1420/2025-fall-alex/modules:/app/storage/1420
|
||||
|
||||
- ~/projects/faculty/1425/2024-fall/Modules:/app/storage/1425_old
|
||||
- ~/projects/faculty/1425/2025-fall-alex/modules:/app/storage/1425
|
||||
|
||||
- ./globalSettings.yml:/app/globalSettings.yml
|
||||
- ~/projects/faculty:/app/storage
|
||||
- ~/projects/facultyFiles:/app/public/images/facultyFiles
|
||||
|
||||
|
||||
|
||||
5
globalSettings.dev.yml
Normal file
5
globalSettings.dev.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
courses:
|
||||
- path: ./3820_BackEnd/2025-fall/Modules/
|
||||
name: Back-End
|
||||
- path: ./3820_BackEnd/2024-fall/Modules/
|
||||
name: Back-End
|
||||
12
requests/module.http
Normal file
12
requests/module.http
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
### doesn't work, there is a form request that does thi son the site, but this isn't it
|
||||
POST https://snow.instructure.com/api/v1/courses/1154759/modules/3965250/reorder
|
||||
Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"order": [
|
||||
29273428, 29274455, 29272498, 29274867, 29272743, 29273425
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ export default function CourseList() {
|
||||
|
||||
const sortedDates = Object.keys(coursesByStartDate).sort();
|
||||
|
||||
console.log(allSettings, coursesByStartDate);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row ">
|
||||
{sortedDates.map((startDate) => (
|
||||
@@ -29,10 +31,10 @@ export default function CourseList() {
|
||||
href={getCourseUrl(settings.name)}
|
||||
shallow={true}
|
||||
className="
|
||||
font-bold text-xl block
|
||||
transition-all hover:scale-105 hover:underline hover:text-slate-200
|
||||
mb-3
|
||||
"
|
||||
font-bold text-xl block
|
||||
transition-all hover:scale-105 hover:underline hover:text-slate-200
|
||||
mb-3
|
||||
"
|
||||
>
|
||||
{settings.name}
|
||||
</Link>
|
||||
|
||||
@@ -25,6 +25,9 @@ import { useQuizzesQueries } from "@/features/local/quizzes/quizHooks";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import { useSuspenseQueries } from "@tanstack/react-query";
|
||||
import { useAssignmentNamesQuery } from "@/features/local/assignments/assignmentHooks";
|
||||
import { useReorderCanvasModuleItemsMutation } from "@/features/canvas/hooks/canvasModuleHooks";
|
||||
import { useCanvasModulesQuery } from "@/features/canvas/hooks/canvasModuleHooks";
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
|
||||
export default function ExpandableModule({
|
||||
moduleName,
|
||||
@@ -50,6 +53,8 @@ export default function ExpandableModule({
|
||||
const { data: quizzes } = useQuizzesQueries(moduleName);
|
||||
const { data: pages } = usePagesQueries(moduleName);
|
||||
const modal = useModal();
|
||||
const reorderMutation = useReorderCanvasModuleItemsMutation();
|
||||
const { data: canvasModules } = useCanvasModulesQuery();
|
||||
|
||||
const moduleItems: {
|
||||
type: "assignment" | "quiz" | "page";
|
||||
@@ -110,6 +115,30 @@ export default function ExpandableModule({
|
||||
)}
|
||||
>
|
||||
<>
|
||||
{!reorderMutation.isPending && (
|
||||
<button
|
||||
className=" me-3"
|
||||
onClick={() => {
|
||||
const canvasModuleId = canvasModules?.find(
|
||||
(m) => m.name === moduleName
|
||||
)?.id;
|
||||
if (!canvasModuleId) {
|
||||
console.error(
|
||||
"Canvas module ID not found for",
|
||||
moduleName
|
||||
);
|
||||
return;
|
||||
}
|
||||
reorderMutation.mutate({
|
||||
moduleId: canvasModuleId,
|
||||
items: moduleItems.map((item) => item.item),
|
||||
});
|
||||
}}
|
||||
>
|
||||
Sort by Due Date
|
||||
</button>
|
||||
)}
|
||||
{reorderMutation.isPending && <Spinner />}
|
||||
<Modal
|
||||
modalControl={modal}
|
||||
buttonText="New Item"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { CanvasModuleItem } from "@/features/canvas/models/modules/canvasModuleItems";
|
||||
import { useLocalCourseSettingsQuery } from "@/features/local/course/localCoursesHooks";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { canvasModuleService } from "../services/canvasModuleService";
|
||||
import { IModuleItem } from "@/features/local/modules/IModuleItem";
|
||||
|
||||
export const canvasCourseModuleKeys = {
|
||||
modules: (canvasId: number) => ["canvas", canvasId, "module list"] as const,
|
||||
@@ -28,3 +30,54 @@ export const useAddCanvasModuleMutation = () => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useReorderCanvasModuleItemsMutation = () => {
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
moduleId,
|
||||
items,
|
||||
}: {
|
||||
moduleId: number;
|
||||
items: IModuleItem[];
|
||||
}) => {
|
||||
if (!settings?.canvasId) throw new Error("No canvasId in settings");
|
||||
|
||||
const canvasModule = await canvasModuleService.getModuleWithItems(
|
||||
settings.canvasId,
|
||||
moduleId
|
||||
);
|
||||
if (!canvasModule.items) {
|
||||
throw new Error(
|
||||
"cannot sort canvas module items, no items found in module"
|
||||
);
|
||||
}
|
||||
const canvasItems = canvasModule.items;
|
||||
|
||||
// Sort IModuleItems by dueAt
|
||||
const sorted = [...items].sort((a, b) => {
|
||||
const aDate = a.dueAt ? new Date(a.dueAt).getTime() : 0;
|
||||
const bDate = b.dueAt ? new Date(b.dueAt).getTime() : 0;
|
||||
return aDate - bDate;
|
||||
});
|
||||
|
||||
// Map sorted IModuleItems to CanvasModuleItem ids by matching name/title
|
||||
const orderedIds = sorted
|
||||
.map((localItem) => canvasItems.find((canvasItem) => canvasItem.title === localItem.name)?.id)
|
||||
.filter((id): id is number => typeof id === "number");
|
||||
|
||||
return await canvasModuleService.reorderModuleItems(
|
||||
settings.canvasId,
|
||||
moduleId,
|
||||
orderedIds
|
||||
);
|
||||
},
|
||||
onSuccess: (_data) => {
|
||||
if (!settings?.canvasId) return;
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: canvasCourseModuleKeys.modules(settings.canvasId),
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -20,6 +20,13 @@ export const canvasModuleService = {
|
||||
if (!data) throw new Error("Something went wrong updating module item");
|
||||
},
|
||||
|
||||
async getModuleWithItems(canvasCourseId: number, moduleId: number) {
|
||||
const url = `${canvasApi}/courses/${canvasCourseId}/modules/${moduleId}`;
|
||||
const params = { include: ["items"] };
|
||||
const response = await axiosClient.get<CanvasModule>(url, { params });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async createModuleItem(
|
||||
canvasCourseId: number,
|
||||
canvasModuleId: number,
|
||||
@@ -63,4 +70,21 @@ export const canvasModuleService = {
|
||||
const response = await axiosClient.post<CanvasModule>(url, body);
|
||||
return response.data.id;
|
||||
},
|
||||
|
||||
async reorderModuleItems(
|
||||
canvasCourseId: number,
|
||||
canvasModuleId: number,
|
||||
itemIds: number[]
|
||||
) {
|
||||
for (let i = 0; i < itemIds.length; i++) {
|
||||
const itemId = itemIds[i];
|
||||
const url = `${canvasApi}/courses/${canvasCourseId}/modules/${canvasModuleId}/items/${itemId}`;
|
||||
const body = {
|
||||
module_item: {
|
||||
position: i + 1,
|
||||
},
|
||||
};
|
||||
await axiosClient.put(url, body);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import { GlobalSettingsCourse } from "../globalSettings/globalSettingsModels";
|
||||
const getCourseSettings = async (
|
||||
course: GlobalSettingsCourse
|
||||
): Promise<LocalCourseSettings> => {
|
||||
const courseDirectory = await getCoursePathByName(course.name);
|
||||
const courseDirectory = path.join(basePath, course.path);
|
||||
const settingsPath = path.join(courseDirectory, "settings.yml");
|
||||
if (!(await directoryOrFileExists(settingsPath))) {
|
||||
const errorMessage = `could not find settings for ${course.name}, settings file ${settingsPath}`;
|
||||
|
||||
Reference in New Issue
Block a user