mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 15:18:32 -06:00
day of linting judgement
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
30
eslint.config.mjs
Normal file
30
eslint.config.mjs
Normal file
@@ -0,0 +1,30 @@
|
||||
import { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.config({
|
||||
extends: ["next/core-web-vitals", "next/typescript", "prettier"],
|
||||
ignorePatterns: ["**/node_modules/**", "**/.next/**", "storage/**"],
|
||||
rules: {
|
||||
"react-refresh/only-export-components": "off", // Disabled the rule
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
argsIgnorePattern: "^_|error",
|
||||
varsIgnorePattern: "^_|error",
|
||||
},
|
||||
],
|
||||
"jsx-a11y/no-access-key": "off",
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
@@ -9,7 +9,7 @@
|
||||
"build": "next build",
|
||||
"startNoSocket": "next start",
|
||||
"start": "NODE_ENV=production node src/websocket.js",
|
||||
"lint": "tsc && next lint",
|
||||
"lint": "eslint . --config eslint.config.mjs && tsc && next lint",
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -23,6 +23,7 @@
|
||||
"@trpc/server": "11.4.3",
|
||||
"@trpc/tanstack-react-query": "^11.4.3",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript-eslint/parser": "^8.37.0",
|
||||
"chokidar": "^4.0.3",
|
||||
"dotenv": "^17.0.1",
|
||||
"form-data": "^4.0.3",
|
||||
@@ -37,8 +38,10 @@
|
||||
"zod": "^3.25.75"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@monaco-editor/loader": "^1.5.0",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@next/eslint-plugin-next": "^15.3.5",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/node": "^24.0.10",
|
||||
@@ -48,6 +51,8 @@
|
||||
"axios": "^1.10.0",
|
||||
"eslint": "^9.30.1",
|
||||
"eslint-config-next": "^15.3.5",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"isomorphic-dompurify": "^2.26.0",
|
||||
"marked": "^16.0.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
|
||||
1306
pnpm-lock.yaml
generated
1306
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
plugins: { "@tailwindcss/postcss": {},
|
||||
},
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -34,11 +34,11 @@ function getTermName(startDate: string) {
|
||||
}
|
||||
|
||||
export default function CourseList() {
|
||||
const [allSettings] = useLocalCoursesSettingsQuery();
|
||||
const { data: allSettings } = useLocalCoursesSettingsQuery();
|
||||
|
||||
const coursesByStartDate = groupByStartDate(allSettings);
|
||||
|
||||
const sortedDates = Object.keys(coursesByStartDate).sort()
|
||||
const sortedDates = Object.keys(coursesByStartDate).sort();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row ">
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { Toaster, ToastBar, useToaster } from "react-hot-toast";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
export const MyToaster = () => {
|
||||
const { toasts, handlers } = useToaster({ duration: Infinity });
|
||||
const { startPause, endPause } = handlers;
|
||||
|
||||
return (
|
||||
// <Toaster />
|
||||
|
||||
@@ -17,9 +17,10 @@ const getUrl = (params: { rest: string[] }, req: NextRequest) => {
|
||||
|
||||
appendQueryParams(url, req);
|
||||
|
||||
return url;``
|
||||
return url;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const proxyResponseHeaders = (response: any) => {
|
||||
const headers = new Headers();
|
||||
Object.entries(response.headers).forEach(([key, value]) => {
|
||||
@@ -39,6 +40,7 @@ export async function GET(
|
||||
const response = await axiosClient.get(url.toString());
|
||||
const headers = proxyResponseHeaders(response);
|
||||
return NextResponse.json(response.data, { headers });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
console.log("canvas get error", error, error?.message);
|
||||
return NextResponse.json(
|
||||
@@ -61,6 +63,7 @@ export async function POST(
|
||||
|
||||
const headers = proxyResponseHeaders(response);
|
||||
return new NextResponse(JSON.stringify(response.data), { headers });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
if (isAxiosError(error)) {
|
||||
console.log(url.toString(), body);
|
||||
@@ -89,6 +92,7 @@ export async function PUT(
|
||||
|
||||
const headers = proxyResponseHeaders(response);
|
||||
return new NextResponse(JSON.stringify(response.data), { headers });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
if (isAxiosError(error)) {
|
||||
console.log(url.toString(), body);
|
||||
@@ -123,6 +127,7 @@ export async function DELETE(
|
||||
|
||||
const headers = proxyResponseHeaders(response);
|
||||
return new NextResponse(JSON.stringify(response.data), { headers });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
import Link from "next/link";
|
||||
|
||||
export function CourseNavigation() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const canvasAssignmentsQuery = useCanvasAssignmentsQuery();
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getCourseSettingsUrl } from "@/services/urlUtils";
|
||||
|
||||
export default function CourseSettingsLink() {
|
||||
const { courseName } = useCourseContext();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
return (
|
||||
<div>
|
||||
{settings.name}
|
||||
|
||||
@@ -11,7 +11,7 @@ import DownChevron from "@/components/icons/DownChevron";
|
||||
export const CalendarMonth = ({ month }: { month: CalendarMonthModel }) => {
|
||||
// const weekInMilliseconds = 604_800_000;
|
||||
const four_days_in_milliseconds = 345_600_000;
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const startDate = getDateFromStringOrThrow(
|
||||
settings.startDate,
|
||||
"week calculation start date"
|
||||
@@ -50,7 +50,9 @@ export const CalendarMonth = ({ month }: { month: CalendarMonthModel }) => {
|
||||
role="button"
|
||||
>
|
||||
{monthName}
|
||||
<div className="my-auto">{isExpanded ? <UpChevron /> : <DownChevron />}</div>
|
||||
<div className="my-auto">
|
||||
{isExpanded ? <UpChevron /> : <DownChevron />}
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function CalendarWeek({
|
||||
week: string[]; //date strings
|
||||
monthNumber: number;
|
||||
}) {
|
||||
const [settings]= useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const startDate = getDateFromStringOrThrow(
|
||||
settings.startDate,
|
||||
"week calculation start date"
|
||||
|
||||
@@ -5,10 +5,9 @@ import { CalendarMonth } from "./CalendarMonth";
|
||||
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import CalendarItemsContextProvider from "../context/CalendarItemsContextProvider";
|
||||
import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling";
|
||||
|
||||
export default function CourseCalendar() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
|
||||
const startDateTime = useMemo(
|
||||
() => getDateFromStringOrThrow(settings.startDate, "course start date"),
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function Day({ day, month }: { day: string; month: number }) {
|
||||
getDateOnlyMarkdownString(new Date()) ===
|
||||
getDateOnlyMarkdownString(dayAsDate);
|
||||
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { itemDropOnDay } = useDraggingContext();
|
||||
|
||||
const { todaysAssignments, todaysQuizzes, todaysPages } = useTodaysItems(day);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useRef, useState } from "react";
|
||||
|
||||
export function DayTitle({ day, dayAsDate }: { day: string; dayAsDate: Date }) {
|
||||
const { courseName } = useCourseContext();
|
||||
const [weeks] = useLecturesSuspenseQuery();
|
||||
const { data: weeks } = useLecturesSuspenseQuery();
|
||||
const { setIsDragging } = useDragStyleContext();
|
||||
const todaysLecture = getLectureForDay(weeks, dayAsDate);
|
||||
const modal = useModal();
|
||||
|
||||
@@ -35,7 +35,6 @@ export const getStatus = ({
|
||||
|
||||
if (type === "page") {
|
||||
const canvasPage = canvasItem as CanvasPage;
|
||||
const page = item as LocalCoursePage;
|
||||
|
||||
if (!canvasPage.published)
|
||||
return { status: "incomplete", message: "canvas page not published" };
|
||||
|
||||
@@ -15,7 +15,7 @@ import { getStatus } from "./getStatus";
|
||||
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
||||
|
||||
export function useTodaysItems(day: string) {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const dayAsDate = getDateFromStringOrThrow(
|
||||
day,
|
||||
"calculating same month in day items"
|
||||
|
||||
@@ -35,10 +35,9 @@ export function useItemDropOnDay({
|
||||
setIsLoading: Dispatch<SetStateAction<boolean>>;
|
||||
modal: { isOpen: boolean; openModal: () => void; closeModal: () => void };
|
||||
}) {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { courseName } = useCourseContext();
|
||||
// const { data: weeks } = useLecturesByWeekQuery();
|
||||
const [weeks] = useLecturesSuspenseQuery();
|
||||
const { data: weeks } = useLecturesSuspenseQuery();
|
||||
const updateQuizMutation = useUpdateQuizMutation();
|
||||
const updateLectureMutation = useLectureUpdateMutation();
|
||||
const updateAssignmentMutation = useUpdateAssignmentMutation();
|
||||
@@ -72,7 +71,7 @@ export function useItemDropOnDay({
|
||||
return dayAsDate;
|
||||
}
|
||||
function updateLecture(dayAsDate: Date) {
|
||||
const { dueAt, ...lecture } = itemBeingDragged.item as Lecture & {
|
||||
const { dueAt: _, ...lecture } = itemBeingDragged.item as Lecture & {
|
||||
dueAt: string;
|
||||
};
|
||||
console.log("dropped lecture on day");
|
||||
|
||||
@@ -19,9 +19,12 @@ import { useAuthoritativeUpdates } from "../../utils/useAuthoritativeUpdates";
|
||||
|
||||
export default function EditLecture({ lectureDay }: { lectureDay: string }) {
|
||||
const { courseName } = useCourseContext();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const [weeks, { dataUpdatedAt: serverDataUpdatedAt, isFetching }] =
|
||||
useLecturesSuspenseQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const {
|
||||
data: weeks,
|
||||
dataUpdatedAt: serverDataUpdatedAt,
|
||||
isFetching,
|
||||
} = useLecturesSuspenseQuery();
|
||||
const updateLecture = useLectureUpdateMutation();
|
||||
|
||||
const lecture = weeks
|
||||
@@ -63,6 +66,7 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) {
|
||||
}
|
||||
}
|
||||
setError("");
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
setError(e.toString());
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function EditLectureTitle({
|
||||
}: {
|
||||
lectureDay: string;
|
||||
}) {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { courseName } = useCourseContext();
|
||||
const lectureDate = getDateFromString(lectureDay);
|
||||
const lectureWeekName = getLectureWeekName(settings.startDate, lectureDay);
|
||||
|
||||
@@ -12,7 +12,7 @@ import Link from "next/link";
|
||||
|
||||
export default function LectureButtons({ lectureDay }: { lectureDay: string }) {
|
||||
const { courseName } = useCourseContext();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const router = useRouter();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const modal = useModal();
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import MarkdownDisplay from "@/components/MarkdownDisplay";
|
||||
import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling";
|
||||
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
||||
import { Lecture } from "@/models/local/lecture";
|
||||
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
|
||||
|
||||
export default function LecturePreview({ lecture }: { lecture: Lecture }) {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
return (
|
||||
<>
|
||||
<section className="border-b-slate-700 border-b-4">
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async function LectureLayout({
|
||||
children: React.ReactNode;
|
||||
params: Promise<{ courseName: string; lectureDay: string }>;
|
||||
}) {
|
||||
const { courseName, lectureDay } = await params;
|
||||
const { courseName } = await params;
|
||||
const decodedCourseName = decodeURIComponent(courseName);
|
||||
if (courseName.includes(".js.map")) {
|
||||
console.log("cannot load course that is .js.map " + decodedCourseName);
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function LecturePreviewPage({
|
||||
lectureDay: string;
|
||||
}) {
|
||||
const { courseName } = useCourseContext();
|
||||
const [weeks] = useLecturesSuspenseQuery();
|
||||
const { data: weeks } = useLecturesSuspenseQuery();
|
||||
const lecture = weeks
|
||||
.flatMap(({ lectures }) => lectures.map((lecture) => lecture))
|
||||
.find((l) => l.date === lectureDay);
|
||||
|
||||
@@ -32,15 +32,15 @@ export default function ExpandableModule({
|
||||
}) {
|
||||
const { itemDropOnModule } = useDraggingContext();
|
||||
const { courseName } = useCourseContext();
|
||||
const [assignmentNames] = useAssignmentNamesQuery(moduleName);
|
||||
const { data: assignmentNames } = useAssignmentNamesQuery(moduleName);
|
||||
|
||||
const [assignments] = trpc.useSuspenseQueries((t) =>
|
||||
assignmentNames.map((assignmentName) =>
|
||||
t.assignment.getAssignment({ courseName, moduleName, assignmentName })
|
||||
)
|
||||
);
|
||||
const [quizzes] = useQuizzesQueries(moduleName);
|
||||
const [pages] = usePagesQueries(moduleName);
|
||||
const { data: quizzes } = useQuizzesQueries(moduleName);
|
||||
const { data: pages } = usePagesQueries(moduleName);
|
||||
const modal = useModal();
|
||||
|
||||
const moduleItems: {
|
||||
|
||||
@@ -4,7 +4,7 @@ import ExpandableModule from "./ExpandableModule";
|
||||
import CreateModule from "./CreateModule";
|
||||
|
||||
export default function ModuleList() {
|
||||
const [moduleNames] = useModuleNamesQuery();
|
||||
const { data: moduleNames } = useModuleNamesQuery();
|
||||
return (
|
||||
<div>
|
||||
{moduleNames.map((m) => (
|
||||
|
||||
@@ -27,9 +27,9 @@ export default function NewItemForm({
|
||||
creationDate?: string;
|
||||
onCreate?: () => void;
|
||||
}) {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { courseName } = useCourseContext();
|
||||
const [modules] = useModuleNamesQuery();
|
||||
const { data: modules } = useModuleNamesQuery();
|
||||
const [type, setType] = useState<"Assignment" | "Quiz" | "Page">(
|
||||
"Assignment"
|
||||
);
|
||||
|
||||
@@ -29,10 +29,10 @@ export function AssignmentFooterButtons({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { courseName } = useCourseContext();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { data: canvasAssignments, isFetching: canvasIsFetching } =
|
||||
useCanvasAssignmentsQuery();
|
||||
const [assignment, { isFetching }] = useAssignmentQuery(
|
||||
const { data: assignment, isFetching } = useAssignmentQuery(
|
||||
moduleName,
|
||||
assignmentName
|
||||
);
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import ClientOnly from "@/components/ClientOnly";
|
||||
import MarkdownDisplay from "@/components/MarkdownDisplay";
|
||||
import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling";
|
||||
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
||||
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
|
||||
import { rubricItemIsExtraCredit } from "@/models/local/assignment/rubricItem";
|
||||
import { assignmentPoints } from "@/models/local/assignment/utils/assignmentPointsUtils";
|
||||
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
|
||||
import React, { Fragment } from "react";
|
||||
|
||||
export default function AssignmentPreview({
|
||||
@@ -13,8 +9,7 @@ export default function AssignmentPreview({
|
||||
}: {
|
||||
assignment: LocalAssignment;
|
||||
}) {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const totalPoints = assignmentPoints(assignment.rubric)
|
||||
const totalPoints = assignmentPoints(assignment.rubric);
|
||||
const extraPoints = assignment.rubric.reduce(
|
||||
(sum, cur) => (rubricItemIsExtraCredit(cur) ? sum + cur.points : sum),
|
||||
0
|
||||
|
||||
@@ -31,11 +31,12 @@ export default function EditAssignment({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { courseName } = useCourseContext();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const [
|
||||
assignment,
|
||||
{ dataUpdatedAt: serverDataUpdatedAt, isFetching: assignmentIsFetching },
|
||||
] = useAssignmentQuery(moduleName, assignmentName);
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const {
|
||||
data: assignment,
|
||||
dataUpdatedAt: serverDataUpdatedAt,
|
||||
isFetching: assignmentIsFetching,
|
||||
} = useAssignmentQuery(moduleName, assignmentName);
|
||||
const updateAssignment = useUpdateAssignmentMutation();
|
||||
const { isPending: imageUpdateIsPending } =
|
||||
useUpdateImageSettingsForAssignment({ moduleName, assignmentName });
|
||||
@@ -94,6 +95,7 @@ export default function EditAssignment({
|
||||
}
|
||||
}
|
||||
setError("");
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
setError(e.toString());
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export function UpdateAssignmentName({
|
||||
const modal = useModal();
|
||||
const { courseName } = useCourseContext();
|
||||
const router = useRouter();
|
||||
const [assignment] = useAssignmentQuery(moduleName, assignmentName);
|
||||
const { data: assignment } = useAssignmentQuery(moduleName, assignmentName);
|
||||
const updateAssignment = useUpdateAssignmentMutation();
|
||||
const [name, setName] = useState(assignment.name);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@@ -24,10 +24,11 @@ export default function EditPage({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { courseName } = useCourseContext();
|
||||
const [page, { dataUpdatedAt, isFetching }] = usePageQuery(
|
||||
moduleName,
|
||||
pageName
|
||||
);
|
||||
const {
|
||||
data: page,
|
||||
dataUpdatedAt,
|
||||
isFetching,
|
||||
} = usePageQuery(moduleName, pageName);
|
||||
const updatePage = useUpdatePageMutation();
|
||||
|
||||
const { clientIsAuthoritative, text, textUpdate, monacoKey } =
|
||||
@@ -37,7 +38,7 @@ export default function EditPage({
|
||||
});
|
||||
|
||||
const [error, setError] = useState("");
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
|
||||
useEffect(() => {
|
||||
const delay = 500;
|
||||
@@ -85,6 +86,7 @@ export default function EditPage({
|
||||
}
|
||||
}
|
||||
setError("");
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
setError(e.toString());
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ export default function EditPageButtons({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { courseName } = useCourseContext();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const [page] = usePageQuery(moduleName, pageName);
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { data: page } = usePageQuery(moduleName, pageName);
|
||||
const { data: canvasPages } = useCanvasPagesQuery();
|
||||
const createPageInCanvas = useCreateCanvasPageMutation();
|
||||
const updatePageInCanvas = useUpdateCanvasPageMutation();
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import MarkdownDisplay from "@/components/MarkdownDisplay";
|
||||
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
||||
import { LocalCoursePage } from "@/models/local/page/localCoursePage";
|
||||
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
|
||||
import React from "react";
|
||||
|
||||
export default function PagePreview({ page }: { page: LocalCoursePage }) {
|
||||
|
||||
@@ -2,7 +2,10 @@ import { useCourseContext } from "@/app/course/[courseName]/context/courseContex
|
||||
import TextInput from "@/components/form/TextInput";
|
||||
import Modal, { useModal } from "@/components/Modal";
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
import { usePageQuery, useUpdatePageMutation } from "@/hooks/localCourse/pageHooks";
|
||||
import {
|
||||
usePageQuery,
|
||||
useUpdatePageMutation,
|
||||
} from "@/hooks/localCourse/pageHooks";
|
||||
import { getModuleItemUrl } from "@/services/urlUtils";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
@@ -17,7 +20,7 @@ export function UpdatePageName({
|
||||
const modal = useModal();
|
||||
const { courseName } = useCourseContext();
|
||||
const router = useRouter();
|
||||
const [page] = usePageQuery(moduleName, pageName);
|
||||
const { data: page } = usePageQuery(moduleName, pageName);
|
||||
const updatePage = useUpdatePageMutation();
|
||||
const [name, setName] = useState(page.name);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -53,11 +56,7 @@ export function UpdatePageName({
|
||||
);
|
||||
}}
|
||||
>
|
||||
<TextInput
|
||||
value={name}
|
||||
setValue={setName}
|
||||
label={"Rename Page"}
|
||||
/>
|
||||
<TextInput value={name} setValue={setName} label={"Rename Page"} />
|
||||
<button className="w-full my-3">Save New Name</button>
|
||||
{isLoading && <Spinner />}
|
||||
</form>
|
||||
|
||||
@@ -75,10 +75,13 @@ export default function EditQuiz({
|
||||
moduleName: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { courseName } = useCourseContext();
|
||||
const [quiz, { dataUpdatedAt: serverDataUpdatedAt, isFetching }] =
|
||||
useQuizQuery(moduleName, quizName);
|
||||
const {
|
||||
data: quiz,
|
||||
dataUpdatedAt: serverDataUpdatedAt,
|
||||
isFetching,
|
||||
} = useQuizQuery(moduleName, quizName);
|
||||
const updateQuizMutation = useUpdateQuizMutation();
|
||||
const { clientIsAuthoritative, text, textUpdate, monacoKey } =
|
||||
useAuthoritativeUpdates({
|
||||
@@ -122,6 +125,7 @@ export default function EditQuiz({
|
||||
}
|
||||
}
|
||||
setError("");
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
setError(e.toString());
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@ export function QuizButtons({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { courseName } = useCourseContext();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { data: canvasQuizzes } = useCanvasQuizzesQuery();
|
||||
|
||||
const [quiz] = useQuizQuery(moduleName, quizName);
|
||||
const { data: quiz } = useQuizQuery(moduleName, quizName);
|
||||
const addToCanvas = useAddQuizToCanvasMutation();
|
||||
const deleteFromCanvas = useDeleteQuizFromCanvasMutation();
|
||||
const deleteLocal = useDeleteQuizMutation();
|
||||
@@ -90,7 +90,11 @@ export function QuizButtons({
|
||||
<div className="flex justify-around gap-3">
|
||||
<button
|
||||
onClick={async () => {
|
||||
await deleteLocal.mutateAsync({ moduleName, quizName, courseName });
|
||||
await deleteLocal.mutateAsync({
|
||||
moduleName,
|
||||
quizName,
|
||||
courseName,
|
||||
});
|
||||
router.push(getCourseUrl(courseName));
|
||||
}}
|
||||
className="btn-danger"
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import CheckIcon from "@/components/icons/CheckIcon";
|
||||
import MarkdownDisplay from "@/components/MarkdownDisplay";
|
||||
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
||||
import { useQuizQuery } from "@/hooks/localCourse/quizHooks";
|
||||
import {
|
||||
LocalQuizQuestion,
|
||||
QuestionType,
|
||||
} from "@/models/local/quiz/localQuizQuestion";
|
||||
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
|
||||
import { escapeMatchingText } from "@/services/utils/questionHtmlUtils";
|
||||
|
||||
export default function QuizPreview({
|
||||
@@ -16,8 +14,7 @@ export default function QuizPreview({
|
||||
quizName: string;
|
||||
moduleName: string;
|
||||
}) {
|
||||
const [quiz] = useQuizQuery(moduleName, quizName);
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: quiz } = useQuizQuery(moduleName, quizName);
|
||||
return (
|
||||
<div style={{ overflow: "scroll", height: "100%" }}>
|
||||
<div className="columns-2">
|
||||
@@ -74,8 +71,6 @@ export default function QuizPreview({
|
||||
}
|
||||
|
||||
function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
|
||||
return (
|
||||
<div className="rounded bg-slate-900 px-2">
|
||||
<div className="flex flex-row justify-between text-slate-400">
|
||||
@@ -124,7 +119,10 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
|
||||
<div></div>
|
||||
)}
|
||||
</div>
|
||||
<MarkdownDisplay markdown={answer.text} className="markdownQuizAnswerPreview" />
|
||||
<MarkdownDisplay
|
||||
markdown={answer.text}
|
||||
className="markdownQuizAnswerPreview"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -3,10 +3,9 @@ import TextInput from "@/components/form/TextInput";
|
||||
import Modal, { useModal } from "@/components/Modal";
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
import {
|
||||
useAssignmentQuery,
|
||||
useUpdateAssignmentMutation,
|
||||
} from "@/hooks/localCourse/assignmentHooks";
|
||||
import { useQuizQuery, useUpdateQuizMutation } from "@/hooks/localCourse/quizHooks";
|
||||
useQuizQuery,
|
||||
useUpdateQuizMutation,
|
||||
} from "@/hooks/localCourse/quizHooks";
|
||||
import { getModuleItemUrl } from "@/services/urlUtils";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
@@ -21,7 +20,7 @@ export function UpdateQuizName({
|
||||
const modal = useModal();
|
||||
const { courseName } = useCourseContext();
|
||||
const router = useRouter();
|
||||
const [quiz] = useQuizQuery(moduleName, quizName);
|
||||
const { data: quiz } = useQuizQuery(moduleName, quizName);
|
||||
const updateQuiz = useUpdateQuizMutation();
|
||||
const [name, setName] = useState(quiz.name);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -57,11 +56,7 @@ export function UpdateQuizName({
|
||||
);
|
||||
}}
|
||||
>
|
||||
<TextInput
|
||||
value={name}
|
||||
setValue={setName}
|
||||
label={"Rename Quiz"}
|
||||
/>
|
||||
<TextInput value={name} setValue={setName} label={"Rename Quiz"} />
|
||||
<button className="w-full my-3">Save New Name</button>
|
||||
{isLoading && <Spinner />}
|
||||
</form>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DragStyleContextProvider } from "./context/drag/dragStyleContext";
|
||||
import CollapsableSidebar from "./CollapsableSidebar";
|
||||
|
||||
|
||||
export default async function CoursePage({}: {}) {
|
||||
export default async function CoursePage() {
|
||||
return (
|
||||
<>
|
||||
<div className="h-full flex flex-col">
|
||||
|
||||
@@ -14,7 +14,7 @@ import { baseCanvasUrl } from "@/services/canvas/canvasServiceUtils";
|
||||
import MeatballIcon from "./MeatballIcon";
|
||||
|
||||
export default function AssignmentGroupManagement() {
|
||||
const [settings, { isPending }] = useLocalCourseSettingsQuery();
|
||||
const { data: settings, isPending } = useLocalCourseSettingsQuery();
|
||||
const updateSettings = useUpdateLocalCourseSettingsMutation();
|
||||
const applyInCanvas = useSetAssignmentGroupsMutation(settings.canvasId);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import React from "react";
|
||||
|
||||
export default function DaysOfWeekSettings() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const updateSettings = useUpdateLocalCourseSettingsMutation();
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,7 +10,7 @@ import DefaultLockOffset from "./DefaultLockOffset";
|
||||
import { settingsBox } from "./sharedSettings";
|
||||
|
||||
export default function DefaultDueTime() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const updateSettings = useUpdateLocalCourseSettingsMutation();
|
||||
const [haveLockOffset, setHaveLockOffset] = useState(
|
||||
typeof settings.defaultLockHoursOffset !== "undefined"
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useState, useEffect } from "react";
|
||||
import { settingsBox } from "./sharedSettings";
|
||||
|
||||
export default function DefaultFileUploadTypes() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const [defaultFileUploadTypes, setDefaultFileUploadTypes] = useState<
|
||||
string[]
|
||||
>(settings.defaultFileUploadTypes);
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function DefaultLockOffset() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const updateSettings = useUpdateLocalCourseSettingsMutation();
|
||||
const [hoursOffset, setHoursOffset] = useState(
|
||||
settings.defaultLockHoursOffset?.toString() ?? "0"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useCourseStudentsQuery } from "@/hooks/canvas/canvasCourseHooks";
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
|
||||
export default function GithubClassroomList() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const enrollmentsQuery = useCourseStudentsQuery(settings.canvasId);
|
||||
|
||||
if (enrollmentsQuery.isLoading)
|
||||
|
||||
@@ -33,8 +33,12 @@ export const holidaysAreEqual = (
|
||||
): boolean => {
|
||||
if (holidays1.length !== holidays2.length) return false;
|
||||
|
||||
const sortedObj1 = [...holidays1].sort((a, b) => a.name.localeCompare(b.name));
|
||||
const sortedObj2 = [...holidays2].sort((a, b) => a.name.localeCompare(b.name));
|
||||
const sortedObj1 = [...holidays1].sort((a, b) =>
|
||||
a.name.localeCompare(b.name)
|
||||
);
|
||||
const sortedObj2 = [...holidays2].sort((a, b) =>
|
||||
a.name.localeCompare(b.name)
|
||||
);
|
||||
|
||||
for (let i = 0; i < sortedObj1.length; i++) {
|
||||
const holiday1 = sortedObj1[i];
|
||||
@@ -63,7 +67,7 @@ export default function HolidayConfig() {
|
||||
);
|
||||
}
|
||||
function InnerHolidayConfig() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const updateSettings = useUpdateLocalCourseSettingsMutation();
|
||||
|
||||
const [rawText, setRawText] = useState(holidaysToString(settings.holidays));
|
||||
@@ -83,7 +87,7 @@ function InnerHolidayConfig() {
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error: any) {}
|
||||
} catch {}
|
||||
}, 500);
|
||||
return () => clearTimeout(id);
|
||||
}, [rawText, settings.holidays, settings, updateSettings]);
|
||||
@@ -127,7 +131,7 @@ function ParsedHolidaysDisplay({ value }: { value: string }) {
|
||||
const parsed = parseHolidays(value);
|
||||
setParsedHolidays(parsed);
|
||||
setError("");
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
setError(error + "");
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useCourseContext } from "../context/courseContext";
|
||||
|
||||
export default function SettingsHeader() {
|
||||
const { courseName } = useCourseContext();
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row justify-between">
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from "react";
|
||||
import { settingsBox } from "./sharedSettings";
|
||||
|
||||
export default function StartAndEndDate() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const startDate = new Date(settings.startDate);
|
||||
const endDate = new Date(settings.endDate);
|
||||
return (
|
||||
|
||||
@@ -12,7 +12,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { settingsBox } from "./sharedSettings";
|
||||
|
||||
export default function SubmissionDefaults() {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const [defaultSubmissionTypes, setDefaultSubmissionTypes] = useState<
|
||||
AssignmentSubmissionType[]
|
||||
>(settings.defaultAssignmentSubmissionTypes);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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";
|
||||
|
||||
@@ -9,7 +9,7 @@ export const NavTabListItem: FC<{
|
||||
onDragStart: () => void;
|
||||
onDragOver: (e: React.DragEvent) => void;
|
||||
onDrop: () => void;
|
||||
}> = ({ tab, idx, onDragStart, onDragOver, onDrop }) => {
|
||||
}> = ({ tab, onDragStart, onDrop }) => {
|
||||
const updateTab = useUpdateCanvasTabMutation();
|
||||
const [isDragOver, setIsDragOver] = React.useState(false);
|
||||
const handleToggleVisibility = () => {
|
||||
@@ -32,7 +32,7 @@ export const NavTabListItem: FC<{
|
||||
setIsDragOver(true);
|
||||
}}
|
||||
onDragLeave={() => setIsDragOver(false)}
|
||||
onDrop={(e) => {
|
||||
onDrop={() => {
|
||||
setIsDragOver(false);
|
||||
onDrop();
|
||||
}}
|
||||
|
||||
@@ -99,7 +99,7 @@ export default function NewCourseForm() {
|
||||
holidays: [],
|
||||
assignmentGroups: courseToImport.assignmentGroups.map(
|
||||
(assignmentGroup) => {
|
||||
const { canvasId, ...groupWithoutCanvas } =
|
||||
const { canvasId: _, ...groupWithoutCanvas } =
|
||||
assignmentGroup;
|
||||
return { ...groupWithoutCanvas, canvasId: undefined };
|
||||
}
|
||||
@@ -172,10 +172,10 @@ function OtherSettings({
|
||||
>;
|
||||
}) {
|
||||
const { data: canvasCourses } = useCourseListInTermQuery(selectedTerm.id);
|
||||
const [allSettings] = useLocalCoursesSettingsQuery();
|
||||
const [emptyDirectories] = useEmptyDirectoriesQuery();
|
||||
const { data: allSettings } = useLocalCoursesSettingsQuery();
|
||||
const { data: emptyDirectories } = useEmptyDirectoriesQuery();
|
||||
|
||||
const populatedCanvasCourseIds = allSettings.map((s) => s.canvasId);
|
||||
const populatedCanvasCourseIds = allSettings?.map((s) => s.canvasId) ?? [];
|
||||
const availableCourses =
|
||||
canvasCourses?.filter(
|
||||
(canvas: CanvasCourseModel) =>
|
||||
|
||||
@@ -15,12 +15,12 @@ export default function Providers({ children }: { children: ReactNode }) {
|
||||
|
||||
return (
|
||||
<SuspenseAndErrorHandling>
|
||||
<TrpcProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<TrpcProvider>
|
||||
{/* <ReactQueryDevtools initialIsOpen={false} /> */}
|
||||
{children}
|
||||
</QueryClientProvider>
|
||||
</TrpcProvider>
|
||||
</QueryClientProvider>
|
||||
</SuspenseAndErrorHandling>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getDateOnlyMarkdownString } from "@/models/local/utils/timeUtils";
|
||||
|
||||
export default function OneCourseLectures() {
|
||||
const { courseName } = useCourseContext();
|
||||
const [weeks] = useLecturesQuery();
|
||||
const {data: weeks} = useLecturesQuery();
|
||||
|
||||
const dayAsDate = new Date();
|
||||
const dayAsString = getDateOnlyMarkdownString(dayAsDate);
|
||||
|
||||
@@ -7,7 +7,7 @@ import CourseContextProvider from "../course/[courseName]/context/CourseContextP
|
||||
import { Fragment } from "react";
|
||||
|
||||
export default function TodaysLectures() {
|
||||
const [allSettings] = useLocalCoursesSettingsQuery();
|
||||
const { data: allSettings } = useLocalCoursesSettingsQuery();
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex justify-around w-full">
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function MarkdownDisplay({
|
||||
markdown: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
return (
|
||||
<SuspenseAndErrorHandling>
|
||||
<DangerousInnerMarkdown
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
import { SimpleTimeOnly } from "@/models/local/localCourseSettings";
|
||||
import { FC, useState, useEffect } from "react";
|
||||
import { FC } from "react";
|
||||
|
||||
export const TimePicker: FC<{
|
||||
setChosenTime: (simpleTime: SimpleTimeOnly) => void;
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function InnerMonacoEditor({
|
||||
};
|
||||
|
||||
editorRef.current = monaco.editor.create(divRef.current, properties);
|
||||
editorRef.current.onDidChangeModelContent((e) => {
|
||||
editorRef.current.onDidChangeModelContent(() => {
|
||||
console.log("in on change", onChange);
|
||||
onChange(editorRef.current?.getModel()?.getValue() ?? "");
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function InnerMonacoEditorOther({
|
||||
|
||||
function handleEditorDidMount(editor: editor.IStandaloneCodeEditor) {
|
||||
editorRef.current = editor;
|
||||
editor.onDidChangeModelContent((e) => {
|
||||
editor.onDidChangeModelContent(() => {
|
||||
onChange(editorRef.current?.getModel()?.getValue() ?? "");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { trpc } from "@/services/serverFunctions/trpcClient";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { io, Socket } from "socket.io-client";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
interface ServerToClientEvents {
|
||||
message: (data: string) => void;
|
||||
@@ -59,7 +60,8 @@ export function ClientCacheInvalidation() {
|
||||
}
|
||||
|
||||
const useFilePathInvalidation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useCallback(
|
||||
(filePath: string) => {
|
||||
const [courseName, moduleOrLectures, itemType, itemFile] =
|
||||
@@ -69,69 +71,88 @@ const useFilePathInvalidation = () => {
|
||||
const allParts = [courseName, moduleOrLectures, itemType, itemName];
|
||||
|
||||
if (moduleOrLectures === "settings.yml") {
|
||||
utils.settings.allCoursesSettings.invalidate();
|
||||
utils.settings.courseSettings.invalidate({ courseName });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.settings.allCoursesSettings.queryKey(),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.settings.courseSettings.queryKey({ courseName }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (moduleOrLectures === "00 - lectures") {
|
||||
console.log("lecture updated on FS ", allParts);
|
||||
utils.lectures.getLectures.invalidate({ courseName });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.lectures.getLectures.queryKey({ courseName }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemType === "assignments") {
|
||||
console.log("assignment updated on FS ", allParts);
|
||||
utils.assignment.getAllAssignments.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAllAssignments.queryKey({
|
||||
courseName,
|
||||
moduleName: moduleOrLectures,
|
||||
}),
|
||||
});
|
||||
utils.assignment.getAssignment.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAssignment.queryKey({
|
||||
courseName,
|
||||
moduleName: moduleOrLectures,
|
||||
assignmentName: itemName,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemType === "quizzes") {
|
||||
console.log("quiz updated on FS ", allParts);
|
||||
utils.quiz.getAllQuizzes.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.quiz.getAllQuizzes.queryKey({
|
||||
courseName,
|
||||
moduleName: moduleOrLectures,
|
||||
}),
|
||||
});
|
||||
utils.quiz.getQuiz.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.quiz.getQuiz.queryKey({
|
||||
courseName,
|
||||
moduleName: moduleOrLectures,
|
||||
quizName: itemName,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemType === "pages") {
|
||||
console.log("page updated on FS ", allParts);
|
||||
utils.page.getAllPages.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.page.getAllPages.queryKey({
|
||||
courseName,
|
||||
moduleName: moduleOrLectures,
|
||||
}),
|
||||
});
|
||||
utils.page.getPage.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.page.getPage.queryKey({
|
||||
courseName,
|
||||
moduleName: moduleOrLectures,
|
||||
pageName: itemName,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
},
|
||||
[
|
||||
utils.assignment.getAllAssignments,
|
||||
utils.assignment.getAssignment,
|
||||
utils.lectures.getLectures,
|
||||
utils.page.getAllPages,
|
||||
utils.page.getPage,
|
||||
utils.quiz.getAllQuizzes,
|
||||
utils.quiz.getQuiz,
|
||||
utils.settings.allCoursesSettings,
|
||||
utils.settings.courseSettings,
|
||||
queryClient,
|
||||
trpc.assignment.getAllAssignments,
|
||||
trpc.assignment.getAssignment,
|
||||
trpc.lectures.getLectures,
|
||||
trpc.page.getAllPages,
|
||||
trpc.page.getPage,
|
||||
trpc.quiz.getAllQuizzes,
|
||||
trpc.quiz.getQuiz,
|
||||
trpc.settings.allCoursesSettings,
|
||||
trpc.settings.courseSettings,
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ export const canvasAssignmentKeys = {
|
||||
};
|
||||
|
||||
export const useCanvasAssignmentsQuery = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
|
||||
return useQuery({
|
||||
queryKey: canvasAssignmentKeys.assignments(settings.canvasId),
|
||||
@@ -22,9 +22,8 @@ export const useCanvasAssignmentsQuery = () => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const useAddAssignmentToCanvasMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const { data: canvasModules } = useCanvasModulesQuery();
|
||||
const addModule = useAddCanvasModuleMutation();
|
||||
const queryClient = useQueryClient();
|
||||
@@ -74,7 +73,7 @@ export const useAddAssignmentToCanvasMutation = () => {
|
||||
};
|
||||
|
||||
export const useUpdateAssignmentInCanvasMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
@@ -105,7 +104,7 @@ export const useUpdateAssignmentInCanvasMutation = () => {
|
||||
};
|
||||
|
||||
export const useDeleteAssignmentFromCanvasMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { canvasModuleService } from "@/services/canvas/canvasModuleService";
|
||||
import {
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useLocalCourseSettingsQuery } from "../localCourse/localCoursesHooks";
|
||||
|
||||
export const canvasCourseModuleKeys = {
|
||||
@@ -11,7 +7,7 @@ export const canvasCourseModuleKeys = {
|
||||
};
|
||||
|
||||
export const useCanvasModulesQuery = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
return useQuery({
|
||||
queryKey: canvasCourseModuleKeys.modules(settings.canvasId),
|
||||
queryFn: async () =>
|
||||
@@ -20,7 +16,7 @@ export const useCanvasModulesQuery = () => {
|
||||
};
|
||||
|
||||
export const useAddCanvasModuleMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (moduleName: string) =>
|
||||
@@ -32,4 +28,3 @@ export const useAddCanvasModuleMutation = () => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export const canvasCourseTabKeys = {
|
||||
};
|
||||
|
||||
export const useCanvasTabsQuery = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
return useQuery({
|
||||
queryKey: canvasCourseTabKeys.tabs(settings.canvasId),
|
||||
queryFn: async () =>
|
||||
@@ -16,7 +16,7 @@ export const useCanvasTabsQuery = () => {
|
||||
};
|
||||
|
||||
export const useUpdateCanvasTabMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
@@ -35,7 +35,7 @@ export const useUpdateCanvasTabMutation = () => {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: canvasCourseTabKeys.tabs(settings.canvasId),
|
||||
refetchType: "all"
|
||||
refetchType: "all",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ export const canvasPageKeys = {
|
||||
};
|
||||
|
||||
export const useCanvasPagesQuery = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
return useQuery({
|
||||
queryKey: canvasPageKeys.pagesInCourse(settings.canvasId),
|
||||
queryFn: async () => await canvasPageService.getAll(settings.canvasId),
|
||||
@@ -25,7 +25,7 @@ export const useCanvasPagesQuery = () => {
|
||||
};
|
||||
|
||||
export const useCreateCanvasPageMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
const { data: canvasModules } = useCanvasModulesQuery();
|
||||
const addModule = useAddCanvasModuleMutation();
|
||||
@@ -44,7 +44,8 @@ export const useCreateCanvasPageMutation = () => {
|
||||
}
|
||||
const canvasPage = await canvasPageService.create(
|
||||
settings.canvasId,
|
||||
page,settings
|
||||
page,
|
||||
settings
|
||||
);
|
||||
|
||||
const canvasModule = canvasModules.find((c) => c.name === moduleName);
|
||||
@@ -69,7 +70,7 @@ export const useCreateCanvasPageMutation = () => {
|
||||
};
|
||||
|
||||
export const useUpdateCanvasPageMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
@@ -78,7 +79,8 @@ export const useUpdateCanvasPageMutation = () => {
|
||||
}: {
|
||||
page: LocalCoursePage;
|
||||
canvasPageId: number;
|
||||
}) => canvasPageService.update(settings.canvasId, canvasPageId, page, settings),
|
||||
}) =>
|
||||
canvasPageService.update(settings.canvasId, canvasPageId, page, settings),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: canvasPageKeys.pagesInCourse(settings.canvasId),
|
||||
@@ -88,7 +90,7 @@ export const useUpdateCanvasPageMutation = () => {
|
||||
};
|
||||
|
||||
export const useDeleteCanvasPageMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (canvasPageId: number) =>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useLocalCourseSettingsQuery } from "../localCourse/localCoursesHooks";
|
||||
import { canvasQuizService } from "@/services/canvas/canvasQuizService";
|
||||
import { LocalQuiz } from "@/models/local/quiz/localQuiz";
|
||||
@@ -18,7 +14,7 @@ export const canvasQuizKeys = {
|
||||
};
|
||||
|
||||
export const useCanvasQuizzesQuery = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
|
||||
return useQuery({
|
||||
queryKey: canvasQuizKeys.quizzes(settings.canvasId),
|
||||
@@ -27,7 +23,7 @@ export const useCanvasQuizzesQuery = () => {
|
||||
};
|
||||
|
||||
export const useAddQuizToCanvasMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
const { data: canvasModules } = useCanvasModulesQuery();
|
||||
const addModule = useAddCanvasModuleMutation();
|
||||
@@ -76,7 +72,7 @@ export const useAddQuizToCanvasMutation = () => {
|
||||
};
|
||||
|
||||
export const useDeleteQuizFromCanvasMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (canvasQuizId: number) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use client";
|
||||
import { trpc } from "@/services/serverFunctions/trpcClient";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||
import {
|
||||
useLocalCourseSettingsQuery,
|
||||
@@ -10,18 +10,25 @@ import {
|
||||
markdownToHtmlNoImages,
|
||||
} from "@/services/htmlMarkdownUtils";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import {
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
useSuspenseQuery,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
export const useAssignmentQuery = (
|
||||
moduleName: string,
|
||||
assignmentName: string
|
||||
) => {
|
||||
const { courseName } = useCourseContext();
|
||||
return trpc.assignment.getAssignment.useSuspenseQuery({
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(
|
||||
trpc.assignment.getAssignment.queryOptions({
|
||||
moduleName,
|
||||
courseName,
|
||||
assignmentName,
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const enable_images = process.env.NEXT_PUBLIC_ENABLE_FILE_SYNC === "true";
|
||||
@@ -33,13 +40,15 @@ export const useUpdateImageSettingsForAssignment = ({
|
||||
moduleName: string;
|
||||
assignmentName: string;
|
||||
}) => {
|
||||
const [assignment] = useAssignmentQuery(moduleName, assignmentName);
|
||||
const { data: assignment } = useAssignmentQuery(moduleName, assignmentName);
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
const addNewImagesToCanvasMutation = useAddNewImagesToCanvasMutation();
|
||||
|
||||
useEffect(() => {
|
||||
if (!enable_images) {
|
||||
console.log("not uploading images, NEXT_PUBLIC_ENABLE_FILE_SYNC is not set to true");
|
||||
console.log(
|
||||
"not uploading images, NEXT_PUBLIC_ENABLE_FILE_SYNC is not set to true"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,9 +71,11 @@ export const useUpdateImageSettingsForAssignment = ({
|
||||
};
|
||||
|
||||
export const useAddNewImagesToCanvasMutation = () => {
|
||||
const [settings] = useLocalCourseSettingsQuery();
|
||||
const createCanvasUrlMutation =
|
||||
trpc.canvasFile.getCanvasFileUrl.useMutation();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const trpc = useTRPC();
|
||||
const createCanvasUrlMutation = useMutation(
|
||||
trpc.canvasFile.getCanvasFileUrl.mutationOptions()
|
||||
);
|
||||
const updateSettings = useUpdateLocalCourseSettingsMutation();
|
||||
|
||||
return useMutation({
|
||||
@@ -103,20 +114,21 @@ export const useAddNewImagesToCanvasMutation = () => {
|
||||
|
||||
export const useAssignmentNamesQuery = (moduleName: string) => {
|
||||
const { courseName } = useCourseContext();
|
||||
return trpc.assignment.getAllAssignments.useSuspenseQuery(
|
||||
{
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery({
|
||||
...trpc.assignment.getAllAssignments.queryOptions({
|
||||
moduleName,
|
||||
courseName,
|
||||
},
|
||||
{
|
||||
}),
|
||||
select: (assignments) => assignments.map((a) => a.name),
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateAssignmentMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.assignment.updateAssignment.useMutation({
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.assignment.updateAssignment.mutationOptions({
|
||||
onSuccess: (
|
||||
_,
|
||||
{
|
||||
@@ -128,56 +140,75 @@ export const useUpdateAssignmentMutation = () => {
|
||||
}
|
||||
) => {
|
||||
if (moduleName !== previousModuleName) {
|
||||
utils.assignment.getAllAssignments.invalidate(
|
||||
{
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAllAssignments.queryKey({
|
||||
courseName,
|
||||
moduleName: previousModuleName,
|
||||
},
|
||||
{ refetchType: "all" }
|
||||
);
|
||||
}),
|
||||
});
|
||||
}
|
||||
utils.assignment.getAllAssignments.invalidate(
|
||||
{ courseName, moduleName },
|
||||
{ refetchType: "all" }
|
||||
);
|
||||
utils.assignment.getAssignment.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAllAssignments.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
}),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAssignment.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
assignmentName,
|
||||
}),
|
||||
});
|
||||
utils.assignment.getAssignment.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAssignment.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
assignmentName: previousAssignmentName,
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useCreateAssignmentMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.assignment.createAssignment.useMutation({
|
||||
onSuccess: (_, { courseName, moduleName }) => {
|
||||
utils.assignment.getAllAssignments.invalidate({ courseName, moduleName });
|
||||
},
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.assignment.createAssignment.mutationOptions({
|
||||
onSuccess: (_result, { courseName, moduleName }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAllAssignments.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
}),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useDeleteAssignmentMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.assignment.deleteAssignment.useMutation({
|
||||
onSuccess: (_, { courseName, moduleName, assignmentName }) => {
|
||||
utils.assignment.getAllAssignments.invalidate({ courseName, moduleName });
|
||||
utils.assignment.getAssignment.invalidate(
|
||||
{
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.assignment.deleteAssignment.mutationOptions({
|
||||
onSuccess: (_result, { courseName, moduleName, assignmentName }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAllAssignments.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
}),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.assignment.getAssignment.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
assignmentName,
|
||||
},
|
||||
{
|
||||
refetchType: "all",
|
||||
}
|
||||
);
|
||||
},
|
||||
}),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||
import { trpc } from "@/services/serverFunctions/trpcClient";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import {
|
||||
useSuspenseQuery,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
export const useLecturesSuspenseQuery = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
return trpc.lectures.getLectures.useSuspenseQuery({ courseName });
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(
|
||||
trpc.lectures.getLectures.queryOptions({ courseName })
|
||||
);
|
||||
};
|
||||
|
||||
export const useLectureUpdateMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.lectures.updateLecture.useMutation({
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.lectures.updateLecture.mutationOptions({
|
||||
onSuccess: () => {
|
||||
utils.lectures.getLectures.invalidate();
|
||||
},
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.lectures.getLectures.queryKey(),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useDeleteLectureMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.lectures.deleteLecture.useMutation({
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.lectures.deleteLecture.mutationOptions({
|
||||
onSuccess: () => {
|
||||
utils.lectures.getLectures.invalidate();
|
||||
},
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.lectures.getLectures.queryKey(),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,41 +1,55 @@
|
||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||
import { trpc } from "@/services/serverFunctions/trpcClient";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import { CalendarItemsInterface } from "@/app/course/[courseName]/context/calendarItemsContext";
|
||||
import {
|
||||
getDateOnlyMarkdownString,
|
||||
getDateFromStringOrThrow,
|
||||
} from "@/models/local/utils/timeUtils";
|
||||
import {
|
||||
useSuspenseQuery,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
useSuspenseQueries,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
export const useModuleNamesQuery = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
return trpc.module.getModuleNames.useSuspenseQuery({ courseName });
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(
|
||||
trpc.module.getModuleNames.queryOptions({ courseName })
|
||||
);
|
||||
};
|
||||
|
||||
export const useCreateModuleMutation = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
return trpc.module.createModule.useMutation({
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.module.createModule.mutationOptions({
|
||||
onSuccess: () => {
|
||||
utils.module.getModuleNames.invalidate({ courseName });
|
||||
},
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.module.getModuleNames.queryKey({ courseName }),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useCourseQuizzesByModuleByDateQuery = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
const [moduleNames] = useModuleNamesQuery();
|
||||
|
||||
const [quizzes] = trpc.useSuspenseQueries((t) =>
|
||||
moduleNames.map((moduleName) =>
|
||||
t.quiz.getAllQuizzes({ courseName, moduleName })
|
||||
)
|
||||
);
|
||||
|
||||
const quizzesAndModules = moduleNames.flatMap((moduleName, index) => {
|
||||
return quizzes[index].map((quiz) => ({ moduleName, quiz }));
|
||||
const { data: moduleNames } = useModuleNamesQuery();
|
||||
const trpc = useTRPC();
|
||||
const quizzesResults = useSuspenseQueries({
|
||||
queries: moduleNames.map((moduleName: string) =>
|
||||
trpc.quiz.getAllQuizzes.queryOptions({ courseName, moduleName })
|
||||
),
|
||||
});
|
||||
|
||||
const quizzes = quizzesResults.map((result) => result.data ?? []);
|
||||
const quizzesAndModules = moduleNames.flatMap(
|
||||
(moduleName: string, index: number) => {
|
||||
return quizzes[index].map((quiz) => ({ moduleName, quiz }));
|
||||
}
|
||||
);
|
||||
const quizzesByModuleByDate = quizzesAndModules.reduce(
|
||||
(previous, { quiz, moduleName }) => {
|
||||
const dueDay = getDateOnlyMarkdownString(
|
||||
@@ -45,12 +59,10 @@ export const useCourseQuizzesByModuleByDateQuery = () => {
|
||||
const previousModule = previousModules[moduleName] ?? {
|
||||
quizzes: [],
|
||||
};
|
||||
|
||||
const updatedModule = {
|
||||
...previousModule,
|
||||
quizzes: [...previousModule.quizzes, quiz],
|
||||
};
|
||||
|
||||
return {
|
||||
...previous,
|
||||
[dueDay]: {
|
||||
@@ -66,19 +78,24 @@ export const useCourseQuizzesByModuleByDateQuery = () => {
|
||||
|
||||
export const useCoursePagesByModuleByDateQuery = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
const [moduleNames] = useModuleNamesQuery();
|
||||
const [pages] = trpc.useSuspenseQueries((t) =>
|
||||
moduleNames.map((moduleName) =>
|
||||
t.page.getAllPages({ courseName, moduleName })
|
||||
)
|
||||
);
|
||||
|
||||
const pagesAndModules = moduleNames.flatMap((moduleName, index) => {
|
||||
return pages[index].map((page) => ({ moduleName, page }));
|
||||
const { data: moduleNames } = useModuleNamesQuery();
|
||||
const trpc = useTRPC();
|
||||
const pagesResults = useSuspenseQueries({
|
||||
queries: moduleNames.map((moduleName: string) =>
|
||||
trpc.page.getAllPages.queryOptions({ courseName, moduleName })
|
||||
),
|
||||
});
|
||||
|
||||
const pages = pagesResults.map((result) => result.data ?? []);
|
||||
const pagesAndModules = moduleNames.flatMap(
|
||||
(moduleName: string, index: number) => {
|
||||
return pages[index].map((page) => ({ moduleName, page }));
|
||||
}
|
||||
);
|
||||
const pagesByModuleByDate = pagesAndModules.reduce(
|
||||
(previous, { page, moduleName }) => {
|
||||
(
|
||||
previous,
|
||||
{ page, moduleName }
|
||||
) => {
|
||||
const dueDay = getDateOnlyMarkdownString(
|
||||
getDateFromStringOrThrow(page.dueAt, "due at for page in items context")
|
||||
);
|
||||
@@ -86,12 +103,10 @@ export const useCoursePagesByModuleByDateQuery = () => {
|
||||
const previousModule = previousModules[moduleName] ?? {
|
||||
pages: [],
|
||||
};
|
||||
|
||||
const updatedModule = {
|
||||
...previousModule,
|
||||
pages: [...previousModule.pages, page],
|
||||
};
|
||||
|
||||
return {
|
||||
...previous,
|
||||
[dueDay]: {
|
||||
@@ -107,17 +122,29 @@ export const useCoursePagesByModuleByDateQuery = () => {
|
||||
|
||||
export const useCourseAssignmentsByModuleByDateQuery = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
const [moduleNames] = useModuleNamesQuery();
|
||||
const [assignments] = trpc.useSuspenseQueries((t) =>
|
||||
moduleNames.map((moduleName) =>
|
||||
t.assignment.getAllAssignments({ courseName, moduleName })
|
||||
)
|
||||
);
|
||||
const assignmentsAndModules = moduleNames.flatMap((moduleName, index) => {
|
||||
return assignments[index].map((assignment) => ({ moduleName, assignment }));
|
||||
const { data: moduleNames } = useModuleNamesQuery();
|
||||
const trpc = useTRPC();
|
||||
const assignmentsResults = useSuspenseQueries({
|
||||
queries: moduleNames.map((moduleName: string) =>
|
||||
trpc.assignment.getAllAssignments.queryOptions({ courseName, moduleName })
|
||||
),
|
||||
});
|
||||
const assignments = assignmentsResults.map(
|
||||
(result) => result.data
|
||||
);
|
||||
const assignmentsAndModules = moduleNames.flatMap(
|
||||
(moduleName: string, index: number) => {
|
||||
return assignments[index].map((assignment) => ({
|
||||
moduleName,
|
||||
assignment,
|
||||
}));
|
||||
}
|
||||
);
|
||||
const assignmentsByModuleByDate = assignmentsAndModules.reduce(
|
||||
(previous, { assignment, moduleName }) => {
|
||||
(
|
||||
previous,
|
||||
{ assignment, moduleName }
|
||||
) => {
|
||||
const dueDay = getDateOnlyMarkdownString(
|
||||
getDateFromStringOrThrow(
|
||||
assignment.dueAt,
|
||||
@@ -128,12 +155,10 @@ export const useCourseAssignmentsByModuleByDateQuery = () => {
|
||||
const previousModule = previousModules[moduleName] ?? {
|
||||
assignments: [],
|
||||
};
|
||||
|
||||
const updatedModule = {
|
||||
...previousModule,
|
||||
assignments: [...previousModule.assignments, assignment],
|
||||
};
|
||||
|
||||
return {
|
||||
...previous,
|
||||
[dueDay]: {
|
||||
|
||||
@@ -1,33 +1,56 @@
|
||||
"use client";
|
||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||
import { trpc } from "@/services/serverFunctions/trpcClient";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import {
|
||||
useSuspenseQuery,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
export const useLocalCoursesSettingsQuery = () =>
|
||||
trpc.settings.allCoursesSettings.useSuspenseQuery();
|
||||
export const useLocalCoursesSettingsQuery = () => {
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(trpc.settings.allCoursesSettings.queryOptions());
|
||||
};
|
||||
|
||||
export const useLocalCourseSettingsQuery = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
return trpc.settings.courseSettings.useSuspenseQuery({ courseName });
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(
|
||||
trpc.settings.courseSettings.queryOptions({ courseName })
|
||||
);
|
||||
};
|
||||
|
||||
export const useCreateLocalCourseMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.settings.createCourse.useMutation({
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.settings.createCourse.mutationOptions({
|
||||
onSuccess: () => {
|
||||
utils.settings.allCoursesSettings.invalidate();
|
||||
utils.directories.getEmptyDirectories.invalidate();
|
||||
},
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.settings.allCoursesSettings.queryKey(),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.directories.getEmptyDirectories.queryKey(),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useUpdateLocalCourseSettingsMutation = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
return trpc.settings.updateSettings.useMutation({
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.settings.updateSettings.mutationOptions({
|
||||
onSuccess: () => {
|
||||
utils.settings.allCoursesSettings.invalidate();
|
||||
utils.settings.courseSettings.invalidate({ courseName });
|
||||
},
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.settings.allCoursesSettings.queryKey(),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.settings.courseSettings.queryKey({ courseName }),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,64 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||
import { trpc } from "@/services/serverFunctions/trpcClient";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import {
|
||||
useSuspenseQuery,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
export const usePageQuery = (moduleName: string, pageName: string) => {
|
||||
const { courseName } = useCourseContext();
|
||||
return trpc.page.getPage.useSuspenseQuery({
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(
|
||||
trpc.page.getPage.queryOptions({
|
||||
courseName,
|
||||
moduleName,
|
||||
pageName,
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const usePagesQueries = (moduleName: string) => {
|
||||
const { courseName } = useCourseContext();
|
||||
return trpc.page.getAllPages.useSuspenseQuery({
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(
|
||||
trpc.page.getAllPages.queryOptions({
|
||||
courseName,
|
||||
moduleName,
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useUpdatePageMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.page.updatePage.useMutation({
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.page.updatePage.mutationOptions({
|
||||
onSuccess: (
|
||||
_,
|
||||
{ courseName, moduleName, pageName, previousModuleName }
|
||||
) => {
|
||||
utils.page.getAllPages.invalidate({ courseName, moduleName },
|
||||
{ refetchType: "all" });
|
||||
utils.page.getPage.invalidate({ courseName, moduleName, pageName });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.page.getAllPages.queryKey({ courseName, moduleName }),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.page.getPage.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
pageName,
|
||||
}),
|
||||
});
|
||||
if (moduleName !== previousModuleName) {
|
||||
utils.page.getAllPages.invalidate({
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.page.getAllPages.queryKey({
|
||||
courseName,
|
||||
moduleName: previousModuleName,
|
||||
},
|
||||
{ refetchType: "all" });
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
export const useCreatePageMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.page.createPage.useMutation({
|
||||
onSuccess: (_, { courseName, moduleName }) => {
|
||||
utils.page.getAllPages.invalidate({ courseName, moduleName });
|
||||
},
|
||||
});
|
||||
};
|
||||
export const useDeletePageMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.page.deletePage.useMutation({
|
||||
onSuccess: (_, { courseName, moduleName, pageName }) => {
|
||||
utils.page.getAllPages.invalidate(
|
||||
{ courseName, moduleName },
|
||||
{
|
||||
refetchType: "all",
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useCreatePageMutation = () => {
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.page.createPage.mutationOptions({
|
||||
onSuccess: (_, { courseName, moduleName }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.page.getAllPages.queryKey({ courseName, moduleName }),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useDeletePageMutation = () => {
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.page.deletePage.mutationOptions({
|
||||
onSuccess: (_, { courseName, moduleName, pageName }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.page.getAllPages.queryKey({ courseName, moduleName }),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.page.getPage.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
pageName,
|
||||
}),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
utils.page.getPage.invalidate({ courseName, moduleName, pageName });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,62 +1,107 @@
|
||||
"use client";
|
||||
|
||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||
import { trpc } from "@/services/serverFunctions/trpcClient";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import {
|
||||
useSuspenseQuery,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
export const useQuizQuery = (moduleName: string, quizName: string) => {
|
||||
const { courseName } = useCourseContext();
|
||||
return trpc.quiz.getQuiz.useSuspenseQuery({
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(
|
||||
trpc.quiz.getQuiz.queryOptions({
|
||||
courseName,
|
||||
moduleName,
|
||||
quizName,
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useQuizzesQueries = (moduleName: string) => {
|
||||
const { courseName } = useCourseContext();
|
||||
// const trpc = usetrpc();
|
||||
return trpc.quiz.getAllQuizzes.useSuspenseQuery({
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(
|
||||
trpc.quiz.getAllQuizzes.queryOptions({
|
||||
courseName,
|
||||
moduleName,
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useUpdateQuizMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.quiz.updateQuiz.useMutation({
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.quiz.updateQuiz.mutationOptions({
|
||||
onSuccess: (
|
||||
_,
|
||||
{ courseName, moduleName, quizName, previousModuleName }
|
||||
) => {
|
||||
if (moduleName !== previousModuleName)
|
||||
utils.quiz.getAllQuizzes.invalidate({
|
||||
if (moduleName !== previousModuleName) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.quiz.getAllQuizzes.queryKey({
|
||||
courseName,
|
||||
moduleName: previousModuleName,
|
||||
},
|
||||
{ refetchType: "all" });
|
||||
utils.quiz.getAllQuizzes.invalidate({ courseName, moduleName },
|
||||
{ refetchType: "all" });
|
||||
utils.quiz.getQuiz.invalidate({ courseName, moduleName, quizName });
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
export const useCreateQuizMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.quiz.createQuiz.useMutation({
|
||||
onSuccess: (_, { courseName, moduleName }) => {
|
||||
utils.quiz.getAllQuizzes.invalidate({ courseName, moduleName });
|
||||
},
|
||||
}
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.quiz.getAllQuizzes.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
}),
|
||||
});
|
||||
};
|
||||
export const useDeleteQuizMutation = () => {
|
||||
const utils = trpc.useUtils();
|
||||
return trpc.quiz.deleteQuiz.useMutation({
|
||||
onSuccess: (_, { courseName, moduleName, quizName }) => {
|
||||
utils.quiz.getAllQuizzes.invalidate(
|
||||
{ courseName, moduleName },
|
||||
{ refetchType: "all" }
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.quiz.getQuiz.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
quizName,
|
||||
}),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useCreateQuizMutation = () => {
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.quiz.createQuiz.mutationOptions({
|
||||
onSuccess: (_, { courseName, moduleName }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.quiz.getAllQuizzes.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
}),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useDeleteQuizMutation = () => {
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
trpc.quiz.deleteQuiz.mutationOptions({
|
||||
onSuccess: (_, { courseName, moduleName, quizName }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.quiz.getAllQuizzes.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
}),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: trpc.quiz.getQuiz.queryKey({
|
||||
courseName,
|
||||
moduleName,
|
||||
quizName,
|
||||
}),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
utils.quiz.getQuiz.invalidate({ courseName, moduleName, quizName });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { trpc } from "@/services/serverFunctions/trpcClient";
|
||||
import { useTRPC } from "@/services/serverFunctions/trpcClient";
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
|
||||
export const directoryKeys = {
|
||||
emptyFolders: ["empty folders"] as const,
|
||||
};
|
||||
|
||||
export const useEmptyDirectoriesQuery = () =>
|
||||
trpc.directories.getEmptyDirectories.useSuspenseQuery();
|
||||
export const useEmptyDirectoriesQuery = () => {
|
||||
const trpc = useTRPC();
|
||||
return useSuspenseQuery(trpc.directories.getEmptyDirectories.queryOptions());
|
||||
};
|
||||
|
||||
@@ -54,7 +54,7 @@ export interface CanvasAssignment {
|
||||
}[];
|
||||
post_to_sis?: boolean;
|
||||
integration_id?: string;
|
||||
integration_data?: any;
|
||||
integration_data?: unknown;
|
||||
muted?: boolean;
|
||||
points_possible?: number;
|
||||
has_submitted_submissions?: boolean;
|
||||
@@ -69,7 +69,7 @@ export interface CanvasAssignment {
|
||||
frozen_attributes?: string[];
|
||||
submission?: CanvasSubmissionModel;
|
||||
use_rubric_for_grading?: boolean;
|
||||
rubric_settings?: any;
|
||||
rubric_settings?: unknown;
|
||||
rubric?: CanvasRubricCriteria[];
|
||||
assignment_visibility?: number[];
|
||||
overrides?: CanvasAssignmentOverride[];
|
||||
|
||||
@@ -2,6 +2,6 @@ export interface CanvasLockInfo {
|
||||
asset_string: string;
|
||||
unlock_at?: string; // ISO 8601 date string
|
||||
lock_at?: string; // ISO 8601 date string
|
||||
context_module?: any;
|
||||
context_module?: unknown;
|
||||
manually_locked?: boolean;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ export interface CanvasDiscussionTopicModel {
|
||||
locked?: boolean;
|
||||
pinned?: boolean;
|
||||
locked_for_user?: boolean;
|
||||
lock_info?: any;
|
||||
group_topic_children?: any;
|
||||
lock_info?: unknown;
|
||||
group_topic_children?: unknown;
|
||||
root_topic_id?: number;
|
||||
group_category_id?: number;
|
||||
allow_rating?: boolean;
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface CanvasQuiz {
|
||||
speedgrader_url?: string;
|
||||
quiz_extensions_url?: string;
|
||||
permissions: CanvasQuizPermissions;
|
||||
all_dates?: any; // Depending on the structure of the dates, this could be further specified
|
||||
all_dates?: unknown; // Depending on the structure of the dates, this could be further specified
|
||||
version_number?: number;
|
||||
question_types?: string[];
|
||||
anonymous_submissions?: boolean;
|
||||
|
||||
@@ -9,11 +9,6 @@ import {
|
||||
} from "./assignment/localAssignmentGroup";
|
||||
import { parse, stringify } from "yaml";
|
||||
|
||||
// export interface LocalCourse {
|
||||
// modules: LocalModule[];
|
||||
// settings: LocalCourseSettings;
|
||||
// }
|
||||
|
||||
export interface SimpleTimeOnly {
|
||||
hour: number;
|
||||
minute: number;
|
||||
@@ -109,9 +104,9 @@ function lowercaseFirstLetter<T>(obj: T): T {
|
||||
|
||||
if (Array.isArray(obj)) return obj.map(lowercaseFirstLetter) as unknown as T;
|
||||
|
||||
const result: Record<string, any> = {};
|
||||
const result: Record<string, unknown> = {};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const value = (obj as Record<string, any>)[key];
|
||||
const value = (obj as Record<string, unknown>)[key];
|
||||
const newKey = key.charAt(0).toLowerCase() + key.slice(1);
|
||||
|
||||
if (value && typeof value === "object") {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { LocalQuiz } from "../localQuiz";
|
||||
import { LocalQuizQuestion, QuestionType } from "../localQuizQuestion";
|
||||
import { LocalQuizQuestionAnswer } from "../localQuizQuestionAnswer";
|
||||
import { quizQuestionAnswerMarkdownUtils } from "./quizQuestionAnswerMarkdownUtils";
|
||||
@@ -112,7 +111,7 @@ const getAnswers = (
|
||||
questionIndex
|
||||
);
|
||||
|
||||
const answers = answerLines.map((a, i) =>
|
||||
const answers = answerLines.map((a) =>
|
||||
quizQuestionAnswerMarkdownUtils.parseMarkdown(a, questionType)
|
||||
);
|
||||
return answers;
|
||||
@@ -215,7 +214,7 @@ export const quizQuestionMarkdownUtils = {
|
||||
? linesWithoutAnswers
|
||||
.slice(0, linesWithoutPoints.length)
|
||||
.filter(
|
||||
(line, index) =>
|
||||
(line) =>
|
||||
!questionTypesWithoutAnswers.includes(line.toLowerCase())
|
||||
)
|
||||
: linesWithoutAnswers;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { LocalQuiz } from "../../quiz/localQuiz";
|
||||
import { LocalQuizQuestion, QuestionType } from "../../quiz/localQuizQuestion";
|
||||
import { LocalQuizQuestionAnswer } from "../../quiz/localQuizQuestionAnswer";
|
||||
import { QuestionType } from "../../quiz/localQuizQuestion";
|
||||
import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils";
|
||||
import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils";
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { LocalQuiz } from "../../quiz/localQuiz";
|
||||
import { quizMarkdownUtils } from "../../quiz/utils/quizMarkdownUtils";
|
||||
import { QuestionType } from "@/models/local/quiz/localQuizQuestion";
|
||||
import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils";
|
||||
import { markdownToHtmlNoImages, markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
|
||||
import { markdownToHtmlNoImages } from "@/services/htmlMarkdownUtils";
|
||||
|
||||
// Test suite for QuizMarkdown
|
||||
describe("QuizMarkdownTests", () => {
|
||||
@@ -256,7 +256,6 @@ short answer
|
||||
});
|
||||
|
||||
it("can parse quiz with latex in a question", () => {
|
||||
const name = "Test Quiz";
|
||||
const rawMarkdownQuiz = `
|
||||
ShuffleAnswers: true
|
||||
OneQuestionAtATime: false
|
||||
|
||||
@@ -3,7 +3,6 @@ import { QuestionType, zodQuestionType } from "../../quiz/localQuizQuestion";
|
||||
import { quizMarkdownUtils } from "../../quiz/utils/quizMarkdownUtils";
|
||||
import { quizQuestionMarkdownUtils } from "../../quiz/utils/quizQuestionMarkdownUtils";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { LocalCourseSettings } from "../../localCourseSettings";
|
||||
|
||||
describe("TextAnswerTests", () => {
|
||||
it("can parse essay", () => {
|
||||
|
||||
@@ -43,7 +43,8 @@ export function getAxiosErrorMessage(error: AxiosError) {
|
||||
console.log("response error", error.response);
|
||||
const responseErrorText =
|
||||
typeof error.response.data === "object"
|
||||
? (error.response.data as any).error
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(error.response.data as any).error
|
||||
: error.response.data;
|
||||
|
||||
if (
|
||||
|
||||
@@ -15,6 +15,7 @@ export const canvasPageService = {
|
||||
url,
|
||||
});
|
||||
return pages.flatMap((pageList) => pageList);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
if (error?.response?.status === 403) {
|
||||
console.log(
|
||||
|
||||
@@ -72,6 +72,7 @@ const createQuestionOnly = async (
|
||||
const hackFixQuestionOrdering = async (
|
||||
canvasCourseId: number,
|
||||
canvasQuizId: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
questionAndPositions: Array<{ question: any; position: number }>
|
||||
) => {
|
||||
console.log("Fixing question order");
|
||||
@@ -149,6 +150,7 @@ export const canvasQuizService = {
|
||||
? new Date(quiz.lock_at).toLocaleString()
|
||||
: undefined,
|
||||
}));
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
if (error?.response?.status === 403) {
|
||||
console.log(
|
||||
|
||||
@@ -33,10 +33,10 @@ const getNextUrl = (
|
||||
return nextUrl;
|
||||
};
|
||||
|
||||
export async function paginatedRequest<T extends any[]>(request: {
|
||||
export async function paginatedRequest<T extends unknown[]>(request: {
|
||||
url: string;
|
||||
}): Promise<T> {
|
||||
var requestCount = 1;
|
||||
let requestCount = 1;
|
||||
const url = new URL(request.url);
|
||||
url.searchParams.set("per_page", "100");
|
||||
|
||||
@@ -44,8 +44,8 @@ export async function paginatedRequest<T extends any[]>(request: {
|
||||
url.toString()
|
||||
);
|
||||
|
||||
var returnData = Array.isArray(firstData) ? [...firstData] : [firstData]; // terms come across as nested objects {enrolmentTerms: terms[]}
|
||||
var nextUrl = getNextUrl(firstHeaders);
|
||||
let returnData = Array.isArray(firstData) ? [...firstData] : [firstData]; // terms come across as nested objects {enrolmentTerms: terms[]}
|
||||
let nextUrl = getNextUrl(firstHeaders);
|
||||
|
||||
while (nextUrl) {
|
||||
requestCount += 1;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { AxiosResponse } from "axios";
|
||||
import { axiosClient } from "../axiosUtils";
|
||||
|
||||
type FetchOptions = Omit<RequestInit, "method">;
|
||||
|
||||
const rateLimitRetryCount = 6;
|
||||
const rateLimitSleepInterval = 1000;
|
||||
|
||||
@@ -18,21 +16,21 @@ export const isRateLimited = async (
|
||||
);
|
||||
};
|
||||
|
||||
const rateLimitAwarePost = async (url: string, body: any, retryCount = 0) => {
|
||||
const response = await axiosClient.post(url, body);
|
||||
// const rateLimitAwarePost = async (url: string, body: unknown, retryCount = 0) => {
|
||||
// const response = await axiosClient.post(url, body);
|
||||
|
||||
if (await isRateLimited(response)) {
|
||||
if (retryCount < rateLimitRetryCount) {
|
||||
console.info(
|
||||
`Hit rate limit on post, retry count is ${retryCount} / ${rateLimitRetryCount}, retrying`
|
||||
);
|
||||
await sleep(rateLimitSleepInterval);
|
||||
return await rateLimitAwarePost(url, body, retryCount + 1);
|
||||
}
|
||||
}
|
||||
// if (await isRateLimited(response)) {
|
||||
// if (retryCount < rateLimitRetryCount) {
|
||||
// console.info(
|
||||
// `Hit rate limit on post, retry count is ${retryCount} / ${rateLimitRetryCount}, retrying`
|
||||
// );
|
||||
// await sleep(rateLimitSleepInterval);
|
||||
// return await rateLimitAwarePost(url, body, retryCount + 1);
|
||||
// }
|
||||
// }
|
||||
|
||||
return response;
|
||||
};
|
||||
// return response;
|
||||
// };
|
||||
|
||||
export const rateLimitAwareDelete = async (
|
||||
url: string,
|
||||
|
||||
@@ -120,7 +120,7 @@ export const courseItemFileStorageService = {
|
||||
);
|
||||
|
||||
const markdownDictionary: {
|
||||
[key in CourseItemType]: () => string;
|
||||
[_key in CourseItemType]: () => string;
|
||||
} = {
|
||||
Assignment: () =>
|
||||
assignmentMarkdownSerializer.toMarkdown(item as LocalAssignment),
|
||||
|
||||
@@ -102,6 +102,7 @@ export async function deleteLecture(
|
||||
await fs.access(lecturePath); // throws error if no file
|
||||
await fs.unlink(lecturePath);
|
||||
console.log(`File deleted: ${lecturePath}`);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
if (error?.code === "ENOENT") {
|
||||
console.log(`Cannot delete lecture, file does not exist: ${lecturePath}`);
|
||||
|
||||
@@ -75,7 +75,7 @@ export const settingsFileStorageService = {
|
||||
const courseDirectory = path.join(basePath, courseName);
|
||||
const settingsPath = path.join(courseDirectory, "settings.yml");
|
||||
|
||||
const { name, ...settingsWithoutName } = settings;
|
||||
const { name: _, ...settingsWithoutName } = settings;
|
||||
|
||||
const settingsMarkdown =
|
||||
localCourseYamlUtils.settingsToYaml(settingsWithoutName);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { marked } from "marked";
|
||||
import DOMPurify from "isomorphic-dompurify";
|
||||
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
|
||||
import markedKatex from "marked-katex-extension";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
marked.use(
|
||||
markedKatex({
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useState } from "react";
|
||||
import superjson from "superjson";
|
||||
import { httpBatchLink } from "@trpc/client";
|
||||
import { trpc } from "./trpcClient";
|
||||
import { trpc, TRPCProvider } from "./trpcClient";
|
||||
import { getQueryClient } from "@/app/providersQueryClientUtils";
|
||||
import { isServer } from "@tanstack/react-query";
|
||||
|
||||
@@ -13,6 +13,7 @@ export default function TrpcProvider({
|
||||
}) {
|
||||
const url = isServer ? "http://localhost:3000/api/trpc/" : "/api/trpc";
|
||||
|
||||
const queryClient = getQueryClient();
|
||||
const [trpcClient] = useState(() =>
|
||||
trpc.createClient({
|
||||
links: [
|
||||
@@ -25,9 +26,14 @@ export default function TrpcProvider({
|
||||
})
|
||||
);
|
||||
|
||||
// return (
|
||||
// <trpc.Provider client={trpcClient} queryClient={getQueryClient()}>
|
||||
// {children}
|
||||
// </trpc.Provider>
|
||||
// );
|
||||
return (
|
||||
<trpc.Provider client={trpcClient} queryClient={getQueryClient()}>
|
||||
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
|
||||
{children}
|
||||
</trpc.Provider>
|
||||
</TRPCProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import publicProcedure from "../procedures/public";
|
||||
import { z } from "zod";
|
||||
import { router } from "../trpcSetup";
|
||||
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
|
||||
import { zodLocalAssignment } from "@/models/local/assignment/localAssignment";
|
||||
|
||||
export const directoriesRouter = router({
|
||||
getEmptyDirectories: publicProcedure.query(async () => {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { z } from "zod";
|
||||
import { router } from "../trpcSetup";
|
||||
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
|
||||
import { zodLocalCourseSettings } from "@/models/local/localCourseSettings";
|
||||
import { trpc } from "../trpcClient";
|
||||
import {
|
||||
getLectures,
|
||||
updateLecture,
|
||||
|
||||
@@ -13,7 +13,7 @@ describe("FileStorageTests", () => {
|
||||
try {
|
||||
await fs.access(storageDirectory);
|
||||
await fs.rm(storageDirectory, { recursive: true });
|
||||
} catch (error) {}
|
||||
} catch {}
|
||||
await fs.mkdir(storageDirectory, { recursive: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ describe("FileStorageTests", () => {
|
||||
try {
|
||||
await fs.access(storageDirectory);
|
||||
await fs.rm(storageDirectory, { recursive: true });
|
||||
} catch (error) {}
|
||||
} catch {}
|
||||
await fs.mkdir(storageDirectory, { recursive: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
import toast, { ErrorIcon, CheckmarkIcon } from "react-hot-toast";
|
||||
import toast, { CheckmarkIcon } from "react-hot-toast";
|
||||
import { ReactNode } from "react";
|
||||
import { MutationCache, QueryCache, QueryClient } from "@tanstack/react-query";
|
||||
|
||||
const addErrorAsToast = async (error: any) => {
|
||||
console.error("error from toast", error);
|
||||
const message = getErrorMessage(error);
|
||||
// const addErrorAsToast = async (error: unknown) => {
|
||||
// console.error("error from toast", error);
|
||||
// const message = getErrorMessage(error);
|
||||
|
||||
toast(
|
||||
(t: any) => (
|
||||
<div className="row">
|
||||
<div className="col-auto my-auto">
|
||||
<ErrorIcon />
|
||||
</div>
|
||||
<div className="col my-auto">
|
||||
<div className="white-space">{message}</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://snow.kualibuild.com/app/651eeebc32976c013a4c4739/run"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Report Bug
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-auto my-auto">
|
||||
<button
|
||||
onClick={() => toast.dismiss(t.id)}
|
||||
className="btn btn-outline-secondary btn-sm"
|
||||
>
|
||||
<i className="bi bi-x"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
duration: Infinity,
|
||||
}
|
||||
);
|
||||
};
|
||||
// toast(
|
||||
// (t) => (
|
||||
// <div className="row">
|
||||
// <div className="col-auto my-auto">
|
||||
// <ErrorIcon />
|
||||
// </div>
|
||||
// <div className="col my-auto">
|
||||
// <div className="white-space">{message}</div>
|
||||
// <div>
|
||||
// <a
|
||||
// href="https://snow.kualibuild.com/app/651eeebc32976c013a4c4739/run"
|
||||
// target="_blank"
|
||||
// rel="noreferrer"
|
||||
// >
|
||||
// Report Bug
|
||||
// </a>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="col-auto my-auto">
|
||||
// <button
|
||||
// onClick={() => toast.dismiss(t.id)}
|
||||
// className="btn btn-outline-secondary btn-sm"
|
||||
// >
|
||||
// <i className="bi bi-x"></i>
|
||||
// </button>
|
||||
// </div>
|
||||
// </div>
|
||||
// ),
|
||||
// {
|
||||
// duration: Infinity,
|
||||
// }
|
||||
// );
|
||||
// };
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function getErrorMessage(error: any) {
|
||||
if (error?.response?.status === 422) {
|
||||
console.log(error.response.data.detail);
|
||||
const serializationMessages = error.response.data.detail.map(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(d: any) => `${d.type} - ${d.loc[1]}`
|
||||
);
|
||||
return `Deserialization error on request:\n${serializationMessages.join(
|
||||
@@ -66,12 +67,8 @@ export function createInfoToast(
|
||||
children: ReactNode,
|
||||
onClose: () => void = () => {}
|
||||
) {
|
||||
const closeHandler = (t: any) => {
|
||||
toast.dismiss(t.id);
|
||||
onClose();
|
||||
};
|
||||
toast(
|
||||
(t: any) => (
|
||||
(t) => (
|
||||
<div className="row">
|
||||
<div className="col-auto my-auto">
|
||||
<i className="bi bi-info-circle-fill"></i>
|
||||
@@ -79,7 +76,10 @@ export function createInfoToast(
|
||||
<div className="col my-auto">{children}</div>
|
||||
<div className="col-auto my-auto">
|
||||
<button
|
||||
onClick={() => closeHandler(t)}
|
||||
onClick={() => {
|
||||
toast.dismiss(t.id);
|
||||
onClose();
|
||||
}}
|
||||
className="btn btn-outline-secondary py-1"
|
||||
>
|
||||
<i className="bi-x-lg" />
|
||||
@@ -98,7 +98,7 @@ export function createInfoToast(
|
||||
|
||||
export function createSuccessToast(message: string) {
|
||||
toast(
|
||||
(t: any) => (
|
||||
(t) => (
|
||||
<div className="row">
|
||||
<div className="col-auto my-auto">
|
||||
<CheckmarkIcon />
|
||||
|
||||
Reference in New Issue
Block a user