day of linting judgement

This commit is contained in:
2025-07-14 11:53:13 -06:00
parent a128107094
commit c39d7ca4d7
97 changed files with 1500 additions and 1130 deletions

View File

@@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

30
eslint.config.mjs Normal file
View 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;

View File

@@ -9,7 +9,7 @@
"build": "next build", "build": "next build",
"startNoSocket": "next start", "startNoSocket": "next start",
"start": "NODE_ENV=production node src/websocket.js", "start": "NODE_ENV=production node src/websocket.js",
"lint": "tsc && next lint", "lint": "eslint . --config eslint.config.mjs && tsc && next lint",
"test": "vitest" "test": "vitest"
}, },
"dependencies": { "dependencies": {
@@ -23,6 +23,7 @@
"@trpc/server": "11.4.3", "@trpc/server": "11.4.3",
"@trpc/tanstack-react-query": "^11.4.3", "@trpc/tanstack-react-query": "^11.4.3",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"@typescript-eslint/parser": "^8.37.0",
"chokidar": "^4.0.3", "chokidar": "^4.0.3",
"dotenv": "^17.0.1", "dotenv": "^17.0.1",
"form-data": "^4.0.3", "form-data": "^4.0.3",
@@ -37,8 +38,10 @@
"zod": "^3.25.75" "zod": "^3.25.75"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3",
"@monaco-editor/loader": "^1.5.0", "@monaco-editor/loader": "^1.5.0",
"@monaco-editor/react": "^4.7.0", "@monaco-editor/react": "^4.7.0",
"@next/eslint-plugin-next": "^15.3.5",
"@testing-library/dom": "^10.4.0", "@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.3.0", "@testing-library/react": "^16.3.0",
"@types/node": "^24.0.10", "@types/node": "^24.0.10",
@@ -48,6 +51,8 @@
"axios": "^1.10.0", "axios": "^1.10.0",
"eslint": "^9.30.1", "eslint": "^9.30.1",
"eslint-config-next": "^15.3.5", "eslint-config-next": "^15.3.5",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-react-hooks": "^5.2.0",
"isomorphic-dompurify": "^2.26.0", "isomorphic-dompurify": "^2.26.0",
"marked": "^16.0.0", "marked": "^16.0.0",
"monaco-editor": "^0.52.2", "monaco-editor": "^0.52.2",

1306
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,5 @@
/** @type {import('postcss-load-config').Config} */
const config = { const config = {
plugins: { "@tailwindcss/postcss": {}, plugins: ["@tailwindcss/postcss"],
},
}; };
export default config; export default config;

View File

@@ -34,11 +34,11 @@ function getTermName(startDate: string) {
} }
export default function CourseList() { export default function CourseList() {
const [allSettings] = useLocalCoursesSettingsQuery(); const { data: allSettings } = useLocalCoursesSettingsQuery();
const coursesByStartDate = groupByStartDate(allSettings); const coursesByStartDate = groupByStartDate(allSettings);
const sortedDates = Object.keys(coursesByStartDate).sort() const sortedDates = Object.keys(coursesByStartDate).sort();
return ( return (
<div className="flex flex-row "> <div className="flex flex-row ">

View File

@@ -1,10 +1,8 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { Toaster, ToastBar, useToaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
export const MyToaster = () => { export const MyToaster = () => {
const { toasts, handlers } = useToaster({ duration: Infinity });
const { startPause, endPause } = handlers;
return ( return (
// <Toaster /> // <Toaster />

View File

@@ -17,9 +17,10 @@ const getUrl = (params: { rest: string[] }, req: NextRequest) => {
appendQueryParams(url, req); appendQueryParams(url, req);
return url;`` return url;
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const proxyResponseHeaders = (response: any) => { const proxyResponseHeaders = (response: any) => {
const headers = new Headers(); const headers = new Headers();
Object.entries(response.headers).forEach(([key, value]) => { Object.entries(response.headers).forEach(([key, value]) => {
@@ -39,6 +40,7 @@ export async function GET(
const response = await axiosClient.get(url.toString()); const response = await axiosClient.get(url.toString());
const headers = proxyResponseHeaders(response); const headers = proxyResponseHeaders(response);
return NextResponse.json(response.data, { headers }); return NextResponse.json(response.data, { headers });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
console.log("canvas get error", error, error?.message); console.log("canvas get error", error, error?.message);
return NextResponse.json( return NextResponse.json(
@@ -61,6 +63,7 @@ export async function POST(
const headers = proxyResponseHeaders(response); const headers = proxyResponseHeaders(response);
return new NextResponse(JSON.stringify(response.data), { headers }); return new NextResponse(JSON.stringify(response.data), { headers });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
if (isAxiosError(error)) { if (isAxiosError(error)) {
console.log(url.toString(), body); console.log(url.toString(), body);
@@ -89,6 +92,7 @@ export async function PUT(
const headers = proxyResponseHeaders(response); const headers = proxyResponseHeaders(response);
return new NextResponse(JSON.stringify(response.data), { headers }); return new NextResponse(JSON.stringify(response.data), { headers });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
if (isAxiosError(error)) { if (isAxiosError(error)) {
console.log(url.toString(), body); console.log(url.toString(), body);
@@ -123,6 +127,7 @@ export async function DELETE(
const headers = proxyResponseHeaders(response); const headers = proxyResponseHeaders(response);
return new NextResponse(JSON.stringify(response.data), { headers }); return new NextResponse(JSON.stringify(response.data), { headers });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
return new NextResponse( return new NextResponse(
JSON.stringify({ JSON.stringify({

View File

@@ -22,7 +22,7 @@ import { useQueryClient } from "@tanstack/react-query";
import Link from "next/link"; import Link from "next/link";
export function CourseNavigation() { export function CourseNavigation() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const canvasAssignmentsQuery = useCanvasAssignmentsQuery(); const canvasAssignmentsQuery = useCanvasAssignmentsQuery();

View File

@@ -7,7 +7,7 @@ import { getCourseSettingsUrl } from "@/services/urlUtils";
export default function CourseSettingsLink() { export default function CourseSettingsLink() {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return ( return (
<div> <div>
{settings.name} {settings.name}

View File

@@ -11,7 +11,7 @@ import DownChevron from "@/components/icons/DownChevron";
export const CalendarMonth = ({ month }: { month: CalendarMonthModel }) => { export const CalendarMonth = ({ month }: { month: CalendarMonthModel }) => {
// const weekInMilliseconds = 604_800_000; // const weekInMilliseconds = 604_800_000;
const four_days_in_milliseconds = 345_600_000; const four_days_in_milliseconds = 345_600_000;
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const startDate = getDateFromStringOrThrow( const startDate = getDateFromStringOrThrow(
settings.startDate, settings.startDate,
"week calculation start date" "week calculation start date"
@@ -50,7 +50,9 @@ export const CalendarMonth = ({ month }: { month: CalendarMonthModel }) => {
role="button" role="button"
> >
{monthName} {monthName}
<div className="my-auto">{isExpanded ? <UpChevron /> : <DownChevron />}</div> <div className="my-auto">
{isExpanded ? <UpChevron /> : <DownChevron />}
</div>
</h3> </h3>
</div> </div>
)} )}

View File

@@ -11,7 +11,7 @@ export function CalendarWeek({
week: string[]; //date strings week: string[]; //date strings
monthNumber: number; monthNumber: number;
}) { }) {
const [settings]= useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const startDate = getDateFromStringOrThrow( const startDate = getDateFromStringOrThrow(
settings.startDate, settings.startDate,
"week calculation start date" "week calculation start date"

View File

@@ -5,10 +5,9 @@ import { CalendarMonth } from "./CalendarMonth";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { useEffect, useMemo, useRef } from "react"; import { useEffect, useMemo, useRef } from "react";
import CalendarItemsContextProvider from "../context/CalendarItemsContextProvider"; import CalendarItemsContextProvider from "../context/CalendarItemsContextProvider";
import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling";
export default function CourseCalendar() { export default function CourseCalendar() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const startDateTime = useMemo( const startDateTime = useMemo(
() => getDateFromStringOrThrow(settings.startDate, "course start date"), () => getDateFromStringOrThrow(settings.startDate, "course start date"),

View File

@@ -19,7 +19,7 @@ export default function Day({ day, month }: { day: string; month: number }) {
getDateOnlyMarkdownString(new Date()) === getDateOnlyMarkdownString(new Date()) ===
getDateOnlyMarkdownString(dayAsDate); getDateOnlyMarkdownString(dayAsDate);
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const { itemDropOnDay } = useDraggingContext(); const { itemDropOnDay } = useDraggingContext();
const { todaysAssignments, todaysQuizzes, todaysPages } = useTodaysItems(day); const { todaysAssignments, todaysQuizzes, todaysPages } = useTodaysItems(day);

View File

@@ -13,7 +13,7 @@ import { useRef, useState } from "react";
export function DayTitle({ day, dayAsDate }: { day: string; dayAsDate: Date }) { export function DayTitle({ day, dayAsDate }: { day: string; dayAsDate: Date }) {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [weeks] = useLecturesSuspenseQuery(); const { data: weeks } = useLecturesSuspenseQuery();
const { setIsDragging } = useDragStyleContext(); const { setIsDragging } = useDragStyleContext();
const todaysLecture = getLectureForDay(weeks, dayAsDate); const todaysLecture = getLectureForDay(weeks, dayAsDate);
const modal = useModal(); const modal = useModal();

View File

@@ -35,7 +35,6 @@ export const getStatus = ({
if (type === "page") { if (type === "page") {
const canvasPage = canvasItem as CanvasPage; const canvasPage = canvasItem as CanvasPage;
const page = item as LocalCoursePage;
if (!canvasPage.published) if (!canvasPage.published)
return { status: "incomplete", message: "canvas page not published" }; return { status: "incomplete", message: "canvas page not published" };

View File

@@ -15,7 +15,7 @@ import { getStatus } from "./getStatus";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
export function useTodaysItems(day: string) { export function useTodaysItems(day: string) {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const dayAsDate = getDateFromStringOrThrow( const dayAsDate = getDateFromStringOrThrow(
day, day,
"calculating same month in day items" "calculating same month in day items"

View File

@@ -35,10 +35,9 @@ export function useItemDropOnDay({
setIsLoading: Dispatch<SetStateAction<boolean>>; setIsLoading: Dispatch<SetStateAction<boolean>>;
modal: { isOpen: boolean; openModal: () => void; closeModal: () => void }; modal: { isOpen: boolean; openModal: () => void; closeModal: () => void };
}) { }) {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
// const { data: weeks } = useLecturesByWeekQuery(); const { data: weeks } = useLecturesSuspenseQuery();
const [weeks] = useLecturesSuspenseQuery();
const updateQuizMutation = useUpdateQuizMutation(); const updateQuizMutation = useUpdateQuizMutation();
const updateLectureMutation = useLectureUpdateMutation(); const updateLectureMutation = useLectureUpdateMutation();
const updateAssignmentMutation = useUpdateAssignmentMutation(); const updateAssignmentMutation = useUpdateAssignmentMutation();
@@ -72,7 +71,7 @@ export function useItemDropOnDay({
return dayAsDate; return dayAsDate;
} }
function updateLecture(dayAsDate: Date) { function updateLecture(dayAsDate: Date) {
const { dueAt, ...lecture } = itemBeingDragged.item as Lecture & { const { dueAt: _, ...lecture } = itemBeingDragged.item as Lecture & {
dueAt: string; dueAt: string;
}; };
console.log("dropped lecture on day"); console.log("dropped lecture on day");

View File

@@ -19,9 +19,12 @@ import { useAuthoritativeUpdates } from "../../utils/useAuthoritativeUpdates";
export default function EditLecture({ lectureDay }: { lectureDay: string }) { export default function EditLecture({ lectureDay }: { lectureDay: string }) {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const [weeks, { dataUpdatedAt: serverDataUpdatedAt, isFetching }] = const {
useLecturesSuspenseQuery(); data: weeks,
dataUpdatedAt: serverDataUpdatedAt,
isFetching,
} = useLecturesSuspenseQuery();
const updateLecture = useLectureUpdateMutation(); const updateLecture = useLectureUpdateMutation();
const lecture = weeks const lecture = weeks
@@ -63,6 +66,7 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) {
} }
} }
setError(""); setError("");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) { } catch (e: any) {
setError(e.toString()); setError(e.toString());
} }

View File

@@ -11,7 +11,7 @@ export default function EditLectureTitle({
}: { }: {
lectureDay: string; lectureDay: string;
}) { }) {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const lectureDate = getDateFromString(lectureDay); const lectureDate = getDateFromString(lectureDay);
const lectureWeekName = getLectureWeekName(settings.startDate, lectureDay); const lectureWeekName = getLectureWeekName(settings.startDate, lectureDay);

View File

@@ -12,7 +12,7 @@ import Link from "next/link";
export default function LectureButtons({ lectureDay }: { lectureDay: string }) { export default function LectureButtons({ lectureDay }: { lectureDay: string }) {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const router = useRouter(); const router = useRouter();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const modal = useModal(); const modal = useModal();

View File

@@ -1,11 +1,7 @@
import MarkdownDisplay from "@/components/MarkdownDisplay"; import MarkdownDisplay from "@/components/MarkdownDisplay";
import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { Lecture } from "@/models/local/lecture"; import { Lecture } from "@/models/local/lecture";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
export default function LecturePreview({ lecture }: { lecture: Lecture }) { export default function LecturePreview({ lecture }: { lecture: Lecture }) {
const [settings] = useLocalCourseSettingsQuery();
return ( return (
<> <>
<section className="border-b-slate-700 border-b-4"> <section className="border-b-slate-700 border-b-4">

View File

@@ -23,7 +23,7 @@ export default async function LectureLayout({
children: React.ReactNode; children: React.ReactNode;
params: Promise<{ courseName: string; lectureDay: string }>; params: Promise<{ courseName: string; lectureDay: string }>;
}) { }) {
const { courseName, lectureDay } = await params; const { courseName } = await params;
const decodedCourseName = decodeURIComponent(courseName); const decodedCourseName = decodeURIComponent(courseName);
if (courseName.includes(".js.map")) { if (courseName.includes(".js.map")) {
console.log("cannot load course that is .js.map " + decodedCourseName); console.log("cannot load course that is .js.map " + decodedCourseName);

View File

@@ -12,7 +12,7 @@ export default function LecturePreviewPage({
lectureDay: string; lectureDay: string;
}) { }) {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [weeks] = useLecturesSuspenseQuery(); const { data: weeks } = useLecturesSuspenseQuery();
const lecture = weeks const lecture = weeks
.flatMap(({ lectures }) => lectures.map((lecture) => lecture)) .flatMap(({ lectures }) => lectures.map((lecture) => lecture))
.find((l) => l.date === lectureDay); .find((l) => l.date === lectureDay);

View File

@@ -32,15 +32,15 @@ export default function ExpandableModule({
}) { }) {
const { itemDropOnModule } = useDraggingContext(); const { itemDropOnModule } = useDraggingContext();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [assignmentNames] = useAssignmentNamesQuery(moduleName); const { data: assignmentNames } = useAssignmentNamesQuery(moduleName);
const [assignments] = trpc.useSuspenseQueries((t) => const [assignments] = trpc.useSuspenseQueries((t) =>
assignmentNames.map((assignmentName) => assignmentNames.map((assignmentName) =>
t.assignment.getAssignment({ courseName, moduleName, assignmentName }) t.assignment.getAssignment({ courseName, moduleName, assignmentName })
) )
); );
const [quizzes] = useQuizzesQueries(moduleName); const { data: quizzes } = useQuizzesQueries(moduleName);
const [pages] = usePagesQueries(moduleName); const { data: pages } = usePagesQueries(moduleName);
const modal = useModal(); const modal = useModal();
const moduleItems: { const moduleItems: {

View File

@@ -4,7 +4,7 @@ import ExpandableModule from "./ExpandableModule";
import CreateModule from "./CreateModule"; import CreateModule from "./CreateModule";
export default function ModuleList() { export default function ModuleList() {
const [moduleNames] = useModuleNamesQuery(); const { data: moduleNames } = useModuleNamesQuery();
return ( return (
<div> <div>
{moduleNames.map((m) => ( {moduleNames.map((m) => (

View File

@@ -27,9 +27,9 @@ export default function NewItemForm({
creationDate?: string; creationDate?: string;
onCreate?: () => void; onCreate?: () => void;
}) { }) {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [modules] = useModuleNamesQuery(); const { data: modules } = useModuleNamesQuery();
const [type, setType] = useState<"Assignment" | "Quiz" | "Page">( const [type, setType] = useState<"Assignment" | "Quiz" | "Page">(
"Assignment" "Assignment"
); );

View File

@@ -29,10 +29,10 @@ export function AssignmentFooterButtons({
}) { }) {
const router = useRouter(); const router = useRouter();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const { data: canvasAssignments, isFetching: canvasIsFetching } = const { data: canvasAssignments, isFetching: canvasIsFetching } =
useCanvasAssignmentsQuery(); useCanvasAssignmentsQuery();
const [assignment, { isFetching }] = useAssignmentQuery( const { data: assignment, isFetching } = useAssignmentQuery(
moduleName, moduleName,
assignmentName assignmentName
); );

View File

@@ -1,11 +1,7 @@
import ClientOnly from "@/components/ClientOnly";
import MarkdownDisplay from "@/components/MarkdownDisplay"; import MarkdownDisplay from "@/components/MarkdownDisplay";
import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { LocalAssignment } from "@/models/local/assignment/localAssignment"; import { LocalAssignment } from "@/models/local/assignment/localAssignment";
import { rubricItemIsExtraCredit } from "@/models/local/assignment/rubricItem"; import { rubricItemIsExtraCredit } from "@/models/local/assignment/rubricItem";
import { assignmentPoints } from "@/models/local/assignment/utils/assignmentPointsUtils"; import { assignmentPoints } from "@/models/local/assignment/utils/assignmentPointsUtils";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
import React, { Fragment } from "react"; import React, { Fragment } from "react";
export default function AssignmentPreview({ export default function AssignmentPreview({
@@ -13,8 +9,7 @@ export default function AssignmentPreview({
}: { }: {
assignment: LocalAssignment; assignment: LocalAssignment;
}) { }) {
const [settings] = useLocalCourseSettingsQuery(); const totalPoints = assignmentPoints(assignment.rubric);
const totalPoints = assignmentPoints(assignment.rubric)
const extraPoints = assignment.rubric.reduce( const extraPoints = assignment.rubric.reduce(
(sum, cur) => (rubricItemIsExtraCredit(cur) ? sum + cur.points : sum), (sum, cur) => (rubricItemIsExtraCredit(cur) ? sum + cur.points : sum),
0 0

View File

@@ -31,11 +31,12 @@ export default function EditAssignment({
}) { }) {
const router = useRouter(); const router = useRouter();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const [ const {
assignment, data: assignment,
{ dataUpdatedAt: serverDataUpdatedAt, isFetching: assignmentIsFetching }, dataUpdatedAt: serverDataUpdatedAt,
] = useAssignmentQuery(moduleName, assignmentName); isFetching: assignmentIsFetching,
} = useAssignmentQuery(moduleName, assignmentName);
const updateAssignment = useUpdateAssignmentMutation(); const updateAssignment = useUpdateAssignmentMutation();
const { isPending: imageUpdateIsPending } = const { isPending: imageUpdateIsPending } =
useUpdateImageSettingsForAssignment({ moduleName, assignmentName }); useUpdateImageSettingsForAssignment({ moduleName, assignmentName });
@@ -94,6 +95,7 @@ export default function EditAssignment({
} }
} }
setError(""); setError("");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) { } catch (e: any) {
setError(e.toString()); setError(e.toString());
} }

View File

@@ -20,11 +20,11 @@ export function UpdateAssignmentName({
const modal = useModal(); const modal = useModal();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const router = useRouter(); const router = useRouter();
const [assignment] = useAssignmentQuery(moduleName, assignmentName); const { data: assignment } = useAssignmentQuery(moduleName, assignmentName);
const updateAssignment = useUpdateAssignmentMutation(); const updateAssignment = useUpdateAssignmentMutation();
const [name, setName] = useState(assignment.name); const [name, setName] = useState(assignment.name);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
return ( return (
<div> <div>
<Modal <Modal

View File

@@ -24,10 +24,11 @@ export default function EditPage({
}) { }) {
const router = useRouter(); const router = useRouter();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [page, { dataUpdatedAt, isFetching }] = usePageQuery( const {
moduleName, data: page,
pageName dataUpdatedAt,
); isFetching,
} = usePageQuery(moduleName, pageName);
const updatePage = useUpdatePageMutation(); const updatePage = useUpdatePageMutation();
const { clientIsAuthoritative, text, textUpdate, monacoKey } = const { clientIsAuthoritative, text, textUpdate, monacoKey } =
@@ -37,7 +38,7 @@ export default function EditPage({
}); });
const [error, setError] = useState(""); const [error, setError] = useState("");
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
useEffect(() => { useEffect(() => {
const delay = 500; const delay = 500;
@@ -85,6 +86,7 @@ export default function EditPage({
} }
} }
setError(""); setError("");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) { } catch (e: any) {
setError(e.toString()); setError(e.toString());
} }

View File

@@ -27,8 +27,8 @@ export default function EditPageButtons({
}) { }) {
const router = useRouter(); const router = useRouter();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const [page] = usePageQuery(moduleName, pageName); const { data: page } = usePageQuery(moduleName, pageName);
const { data: canvasPages } = useCanvasPagesQuery(); const { data: canvasPages } = useCanvasPagesQuery();
const createPageInCanvas = useCreateCanvasPageMutation(); const createPageInCanvas = useCreateCanvasPageMutation();
const updatePageInCanvas = useUpdateCanvasPageMutation(); const updatePageInCanvas = useUpdateCanvasPageMutation();

View File

@@ -1,7 +1,5 @@
import MarkdownDisplay from "@/components/MarkdownDisplay"; import MarkdownDisplay from "@/components/MarkdownDisplay";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { LocalCoursePage } from "@/models/local/page/localCoursePage"; import { LocalCoursePage } from "@/models/local/page/localCoursePage";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
import React from "react"; import React from "react";
export default function PagePreview({ page }: { page: LocalCoursePage }) { export default function PagePreview({ page }: { page: LocalCoursePage }) {

View File

@@ -2,7 +2,10 @@ import { useCourseContext } from "@/app/course/[courseName]/context/courseContex
import TextInput from "@/components/form/TextInput"; import TextInput from "@/components/form/TextInput";
import Modal, { useModal } from "@/components/Modal"; import Modal, { useModal } from "@/components/Modal";
import { Spinner } from "@/components/Spinner"; 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 { getModuleItemUrl } from "@/services/urlUtils";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
@@ -17,11 +20,11 @@ export function UpdatePageName({
const modal = useModal(); const modal = useModal();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const router = useRouter(); const router = useRouter();
const [page] = usePageQuery(moduleName, pageName); const { data: page } = usePageQuery(moduleName, pageName);
const updatePage = useUpdatePageMutation(); const updatePage = useUpdatePageMutation();
const [name, setName] = useState(page.name); const [name, setName] = useState(page.name);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
return ( return (
<div> <div>
<Modal <Modal
@@ -53,11 +56,7 @@ export function UpdatePageName({
); );
}} }}
> >
<TextInput <TextInput value={name} setValue={setName} label={"Rename Page"} />
value={name}
setValue={setName}
label={"Rename Page"}
/>
<button className="w-full my-3">Save New Name</button> <button className="w-full my-3">Save New Name</button>
{isLoading && <Spinner />} {isLoading && <Spinner />}
</form> </form>

View File

@@ -75,10 +75,13 @@ export default function EditQuiz({
moduleName: string; moduleName: string;
}) { }) {
const router = useRouter(); const router = useRouter();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [quiz, { dataUpdatedAt: serverDataUpdatedAt, isFetching }] = const {
useQuizQuery(moduleName, quizName); data: quiz,
dataUpdatedAt: serverDataUpdatedAt,
isFetching,
} = useQuizQuery(moduleName, quizName);
const updateQuizMutation = useUpdateQuizMutation(); const updateQuizMutation = useUpdateQuizMutation();
const { clientIsAuthoritative, text, textUpdate, monacoKey } = const { clientIsAuthoritative, text, textUpdate, monacoKey } =
useAuthoritativeUpdates({ useAuthoritativeUpdates({
@@ -122,6 +125,7 @@ export default function EditQuiz({
} }
} }
setError(""); setError("");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) { } catch (e: any) {
setError(e.toString()); setError(e.toString());
} }

View File

@@ -27,10 +27,10 @@ export function QuizButtons({
}) { }) {
const router = useRouter(); const router = useRouter();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const { data: canvasQuizzes } = useCanvasQuizzesQuery(); const { data: canvasQuizzes } = useCanvasQuizzesQuery();
const [quiz] = useQuizQuery(moduleName, quizName); const { data: quiz } = useQuizQuery(moduleName, quizName);
const addToCanvas = useAddQuizToCanvasMutation(); const addToCanvas = useAddQuizToCanvasMutation();
const deleteFromCanvas = useDeleteQuizFromCanvasMutation(); const deleteFromCanvas = useDeleteQuizFromCanvasMutation();
const deleteLocal = useDeleteQuizMutation(); const deleteLocal = useDeleteQuizMutation();
@@ -90,7 +90,11 @@ export function QuizButtons({
<div className="flex justify-around gap-3"> <div className="flex justify-around gap-3">
<button <button
onClick={async () => { onClick={async () => {
await deleteLocal.mutateAsync({ moduleName, quizName, courseName }); await deleteLocal.mutateAsync({
moduleName,
quizName,
courseName,
});
router.push(getCourseUrl(courseName)); router.push(getCourseUrl(courseName));
}} }}
className="btn-danger" className="btn-danger"

View File

@@ -1,12 +1,10 @@
import CheckIcon from "@/components/icons/CheckIcon"; import CheckIcon from "@/components/icons/CheckIcon";
import MarkdownDisplay from "@/components/MarkdownDisplay"; import MarkdownDisplay from "@/components/MarkdownDisplay";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { useQuizQuery } from "@/hooks/localCourse/quizHooks"; import { useQuizQuery } from "@/hooks/localCourse/quizHooks";
import { import {
LocalQuizQuestion, LocalQuizQuestion,
QuestionType, QuestionType,
} from "@/models/local/quiz/localQuizQuestion"; } from "@/models/local/quiz/localQuizQuestion";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
import { escapeMatchingText } from "@/services/utils/questionHtmlUtils"; import { escapeMatchingText } from "@/services/utils/questionHtmlUtils";
export default function QuizPreview({ export default function QuizPreview({
@@ -16,8 +14,7 @@ export default function QuizPreview({
quizName: string; quizName: string;
moduleName: string; moduleName: string;
}) { }) {
const [quiz] = useQuizQuery(moduleName, quizName); const { data: quiz } = useQuizQuery(moduleName, quizName);
const [settings] = useLocalCourseSettingsQuery();
return ( return (
<div style={{ overflow: "scroll", height: "100%" }}> <div style={{ overflow: "scroll", height: "100%" }}>
<div className="columns-2"> <div className="columns-2">
@@ -74,8 +71,6 @@ export default function QuizPreview({
} }
function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) { function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
const [settings] = useLocalCourseSettingsQuery();
return ( return (
<div className="rounded bg-slate-900 px-2"> <div className="rounded bg-slate-900 px-2">
<div className="flex flex-row justify-between text-slate-400"> <div className="flex flex-row justify-between text-slate-400">
@@ -124,7 +119,10 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
<div></div> <div></div>
)} )}
</div> </div>
<MarkdownDisplay markdown={answer.text} className="markdownQuizAnswerPreview" /> <MarkdownDisplay
markdown={answer.text}
className="markdownQuizAnswerPreview"
/>
</div> </div>
))} ))}
</div> </div>

View File

@@ -3,10 +3,9 @@ import TextInput from "@/components/form/TextInput";
import Modal, { useModal } from "@/components/Modal"; import Modal, { useModal } from "@/components/Modal";
import { Spinner } from "@/components/Spinner"; import { Spinner } from "@/components/Spinner";
import { import {
useAssignmentQuery, useQuizQuery,
useUpdateAssignmentMutation, useUpdateQuizMutation,
} from "@/hooks/localCourse/assignmentHooks"; } from "@/hooks/localCourse/quizHooks";
import { useQuizQuery, useUpdateQuizMutation } from "@/hooks/localCourse/quizHooks";
import { getModuleItemUrl } from "@/services/urlUtils"; import { getModuleItemUrl } from "@/services/urlUtils";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
@@ -21,11 +20,11 @@ export function UpdateQuizName({
const modal = useModal(); const modal = useModal();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const router = useRouter(); const router = useRouter();
const [quiz] = useQuizQuery(moduleName, quizName); const { data: quiz } = useQuizQuery(moduleName, quizName);
const updateQuiz = useUpdateQuizMutation(); const updateQuiz = useUpdateQuizMutation();
const [name, setName] = useState(quiz.name); const [name, setName] = useState(quiz.name);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
return ( return (
<div> <div>
<Modal <Modal
@@ -57,11 +56,7 @@ export function UpdateQuizName({
); );
}} }}
> >
<TextInput <TextInput value={name} setValue={setName} label={"Rename Quiz"} />
value={name}
setValue={setName}
label={"Rename Quiz"}
/>
<button className="w-full my-3">Save New Name</button> <button className="w-full my-3">Save New Name</button>
{isLoading && <Spinner />} {isLoading && <Spinner />}
</form> </form>

View File

@@ -5,7 +5,7 @@ import { DragStyleContextProvider } from "./context/drag/dragStyleContext";
import CollapsableSidebar from "./CollapsableSidebar"; import CollapsableSidebar from "./CollapsableSidebar";
export default async function CoursePage({}: {}) { export default async function CoursePage() {
return ( return (
<> <>
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">

View File

@@ -14,7 +14,7 @@ import { baseCanvasUrl } from "@/services/canvas/canvasServiceUtils";
import MeatballIcon from "./MeatballIcon"; import MeatballIcon from "./MeatballIcon";
export default function AssignmentGroupManagement() { export default function AssignmentGroupManagement() {
const [settings, { isPending }] = useLocalCourseSettingsQuery(); const { data: settings, isPending } = useLocalCourseSettingsQuery();
const updateSettings = useUpdateLocalCourseSettingsMutation(); const updateSettings = useUpdateLocalCourseSettingsMutation();
const applyInCanvas = useSetAssignmentGroupsMutation(settings.canvasId); const applyInCanvas = useSetAssignmentGroupsMutation(settings.canvasId);

View File

@@ -8,7 +8,7 @@ import {
import React from "react"; import React from "react";
export default function DaysOfWeekSettings() { export default function DaysOfWeekSettings() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const updateSettings = useUpdateLocalCourseSettingsMutation(); const updateSettings = useUpdateLocalCourseSettingsMutation();
return ( return (

View File

@@ -10,7 +10,7 @@ import DefaultLockOffset from "./DefaultLockOffset";
import { settingsBox } from "./sharedSettings"; import { settingsBox } from "./sharedSettings";
export default function DefaultDueTime() { export default function DefaultDueTime() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const updateSettings = useUpdateLocalCourseSettingsMutation(); const updateSettings = useUpdateLocalCourseSettingsMutation();
const [haveLockOffset, setHaveLockOffset] = useState( const [haveLockOffset, setHaveLockOffset] = useState(
typeof settings.defaultLockHoursOffset !== "undefined" typeof settings.defaultLockHoursOffset !== "undefined"

View File

@@ -8,7 +8,7 @@ import { useState, useEffect } from "react";
import { settingsBox } from "./sharedSettings"; import { settingsBox } from "./sharedSettings";
export default function DefaultFileUploadTypes() { export default function DefaultFileUploadTypes() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const [defaultFileUploadTypes, setDefaultFileUploadTypes] = useState< const [defaultFileUploadTypes, setDefaultFileUploadTypes] = useState<
string[] string[]
>(settings.defaultFileUploadTypes); >(settings.defaultFileUploadTypes);

View File

@@ -8,7 +8,7 @@ import {
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export default function DefaultLockOffset() { export default function DefaultLockOffset() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const updateSettings = useUpdateLocalCourseSettingsMutation(); const updateSettings = useUpdateLocalCourseSettingsMutation();
const [hoursOffset, setHoursOffset] = useState( const [hoursOffset, setHoursOffset] = useState(
settings.defaultLockHoursOffset?.toString() ?? "0" settings.defaultLockHoursOffset?.toString() ?? "0"

View File

@@ -5,7 +5,7 @@ import { useCourseStudentsQuery } from "@/hooks/canvas/canvasCourseHooks";
import { Spinner } from "@/components/Spinner"; import { Spinner } from "@/components/Spinner";
export default function GithubClassroomList() { export default function GithubClassroomList() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const enrollmentsQuery = useCourseStudentsQuery(settings.canvasId); const enrollmentsQuery = useCourseStudentsQuery(settings.canvasId);
if (enrollmentsQuery.isLoading) if (enrollmentsQuery.isLoading)

View File

@@ -33,8 +33,12 @@ export const holidaysAreEqual = (
): boolean => { ): boolean => {
if (holidays1.length !== holidays2.length) return false; if (holidays1.length !== holidays2.length) return false;
const sortedObj1 = [...holidays1].sort((a, b) => a.name.localeCompare(b.name)); const sortedObj1 = [...holidays1].sort((a, b) =>
const sortedObj2 = [...holidays2].sort((a, b) => a.name.localeCompare(b.name)); a.name.localeCompare(b.name)
);
const sortedObj2 = [...holidays2].sort((a, b) =>
a.name.localeCompare(b.name)
);
for (let i = 0; i < sortedObj1.length; i++) { for (let i = 0; i < sortedObj1.length; i++) {
const holiday1 = sortedObj1[i]; const holiday1 = sortedObj1[i];
@@ -63,7 +67,7 @@ export default function HolidayConfig() {
); );
} }
function InnerHolidayConfig() { function InnerHolidayConfig() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const updateSettings = useUpdateLocalCourseSettingsMutation(); const updateSettings = useUpdateLocalCourseSettingsMutation();
const [rawText, setRawText] = useState(holidaysToString(settings.holidays)); const [rawText, setRawText] = useState(holidaysToString(settings.holidays));
@@ -83,7 +87,7 @@ function InnerHolidayConfig() {
}, },
}); });
} }
} catch (error: any) {} } catch {}
}, 500); }, 500);
return () => clearTimeout(id); return () => clearTimeout(id);
}, [rawText, settings.holidays, settings, updateSettings]); }, [rawText, settings.holidays, settings, updateSettings]);
@@ -127,7 +131,7 @@ function ParsedHolidaysDisplay({ value }: { value: string }) {
const parsed = parseHolidays(value); const parsed = parseHolidays(value);
setParsedHolidays(parsed); setParsedHolidays(parsed);
setError(""); setError("");
} catch (error: any) { } catch (error) {
setError(error + ""); setError(error + "");
} }
}, [value]); }, [value]);

View File

@@ -7,7 +7,7 @@ import { useCourseContext } from "../context/courseContext";
export default function SettingsHeader() { export default function SettingsHeader() {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return ( return (
<> <>
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">

View File

@@ -5,7 +5,7 @@ import React from "react";
import { settingsBox } from "./sharedSettings"; import { settingsBox } from "./sharedSettings";
export default function StartAndEndDate() { export default function StartAndEndDate() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const startDate = new Date(settings.startDate); const startDate = new Date(settings.startDate);
const endDate = new Date(settings.endDate); const endDate = new Date(settings.endDate);
return ( return (

View File

@@ -12,7 +12,7 @@ import React, { useEffect, useState } from "react";
import { settingsBox } from "./sharedSettings"; import { settingsBox } from "./sharedSettings";
export default function SubmissionDefaults() { export default function SubmissionDefaults() {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const [defaultSubmissionTypes, setDefaultSubmissionTypes] = useState< const [defaultSubmissionTypes, setDefaultSubmissionTypes] = useState<
AssignmentSubmissionType[] AssignmentSubmissionType[]
>(settings.defaultAssignmentSubmissionTypes); >(settings.defaultAssignmentSubmissionTypes);

View File

@@ -1,6 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useCanvasTabsQuery } from "@/hooks/canvas/canvasNavigationHooks"; import { useCanvasTabsQuery } from "@/hooks/canvas/canvasNavigationHooks";
import { CanvasCourseTab } from "@/services/canvas/canvasNavigationService";
import { useUpdateCanvasTabMutation } from "@/hooks/canvas/canvasNavigationHooks"; import { useUpdateCanvasTabMutation } from "@/hooks/canvas/canvasNavigationHooks";
import { Spinner } from "@/components/Spinner"; import { Spinner } from "@/components/Spinner";
import { NavTabListItem } from "./NavTabListItem"; import { NavTabListItem } from "./NavTabListItem";

View File

@@ -9,7 +9,7 @@ export const NavTabListItem: FC<{
onDragStart: () => void; onDragStart: () => void;
onDragOver: (e: React.DragEvent) => void; onDragOver: (e: React.DragEvent) => void;
onDrop: () => void; onDrop: () => void;
}> = ({ tab, idx, onDragStart, onDragOver, onDrop }) => { }> = ({ tab, onDragStart, onDrop }) => {
const updateTab = useUpdateCanvasTabMutation(); const updateTab = useUpdateCanvasTabMutation();
const [isDragOver, setIsDragOver] = React.useState(false); const [isDragOver, setIsDragOver] = React.useState(false);
const handleToggleVisibility = () => { const handleToggleVisibility = () => {
@@ -32,7 +32,7 @@ export const NavTabListItem: FC<{
setIsDragOver(true); setIsDragOver(true);
}} }}
onDragLeave={() => setIsDragOver(false)} onDragLeave={() => setIsDragOver(false)}
onDrop={(e) => { onDrop={() => {
setIsDragOver(false); setIsDragOver(false);
onDrop(); onDrop();
}} }}

View File

@@ -99,7 +99,7 @@ export default function NewCourseForm() {
holidays: [], holidays: [],
assignmentGroups: courseToImport.assignmentGroups.map( assignmentGroups: courseToImport.assignmentGroups.map(
(assignmentGroup) => { (assignmentGroup) => {
const { canvasId, ...groupWithoutCanvas } = const { canvasId: _, ...groupWithoutCanvas } =
assignmentGroup; assignmentGroup;
return { ...groupWithoutCanvas, canvasId: undefined }; return { ...groupWithoutCanvas, canvasId: undefined };
} }
@@ -172,10 +172,10 @@ function OtherSettings({
>; >;
}) { }) {
const { data: canvasCourses } = useCourseListInTermQuery(selectedTerm.id); const { data: canvasCourses } = useCourseListInTermQuery(selectedTerm.id);
const [allSettings] = useLocalCoursesSettingsQuery(); const { data: allSettings } = useLocalCoursesSettingsQuery();
const [emptyDirectories] = useEmptyDirectoriesQuery(); const { data: emptyDirectories } = useEmptyDirectoriesQuery();
const populatedCanvasCourseIds = allSettings.map((s) => s.canvasId); const populatedCanvasCourseIds = allSettings?.map((s) => s.canvasId) ?? [];
const availableCourses = const availableCourses =
canvasCourses?.filter( canvasCourses?.filter(
(canvas: CanvasCourseModel) => (canvas: CanvasCourseModel) =>

View File

@@ -15,12 +15,12 @@ export default function Providers({ children }: { children: ReactNode }) {
return ( return (
<SuspenseAndErrorHandling> <SuspenseAndErrorHandling>
<TrpcProvider> <QueryClientProvider client={queryClient}>
<QueryClientProvider client={queryClient}> <TrpcProvider>
{/* <ReactQueryDevtools initialIsOpen={false} /> */} {/* <ReactQueryDevtools initialIsOpen={false} /> */}
{children} {children}
</QueryClientProvider> </TrpcProvider>
</TrpcProvider> </QueryClientProvider>
</SuspenseAndErrorHandling> </SuspenseAndErrorHandling>
); );
} }

View File

@@ -9,7 +9,7 @@ import { getDateOnlyMarkdownString } from "@/models/local/utils/timeUtils";
export default function OneCourseLectures() { export default function OneCourseLectures() {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [weeks] = useLecturesQuery(); const {data: weeks} = useLecturesQuery();
const dayAsDate = new Date(); const dayAsDate = new Date();
const dayAsString = getDateOnlyMarkdownString(dayAsDate); const dayAsString = getDateOnlyMarkdownString(dayAsDate);

View File

@@ -7,7 +7,7 @@ import CourseContextProvider from "../course/[courseName]/context/CourseContextP
import { Fragment } from "react"; import { Fragment } from "react";
export default function TodaysLectures() { export default function TodaysLectures() {
const [allSettings] = useLocalCoursesSettingsQuery(); const { data: allSettings } = useLocalCoursesSettingsQuery();
return ( return (
<div className="w-full"> <div className="w-full">
<div className="flex justify-around w-full"> <div className="flex justify-around w-full">

View File

@@ -10,7 +10,7 @@ export default function MarkdownDisplay({
markdown: string; markdown: string;
className?: string; className?: string;
}) { }) {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return ( return (
<SuspenseAndErrorHandling> <SuspenseAndErrorHandling>
<DangerousInnerMarkdown <DangerousInnerMarkdown

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { SimpleTimeOnly } from "@/models/local/localCourseSettings"; import { SimpleTimeOnly } from "@/models/local/localCourseSettings";
import { FC, useState, useEffect } from "react"; import { FC } from "react";
export const TimePicker: FC<{ export const TimePicker: FC<{
setChosenTime: (simpleTime: SimpleTimeOnly) => void; setChosenTime: (simpleTime: SimpleTimeOnly) => void;

View File

@@ -38,7 +38,7 @@ export default function InnerMonacoEditor({
}; };
editorRef.current = monaco.editor.create(divRef.current, properties); editorRef.current = monaco.editor.create(divRef.current, properties);
editorRef.current.onDidChangeModelContent((e) => { editorRef.current.onDidChangeModelContent(() => {
console.log("in on change", onChange); console.log("in on change", onChange);
onChange(editorRef.current?.getModel()?.getValue() ?? ""); onChange(editorRef.current?.getModel()?.getValue() ?? "");
}); });

View File

@@ -15,7 +15,7 @@ export default function InnerMonacoEditorOther({
function handleEditorDidMount(editor: editor.IStandaloneCodeEditor) { function handleEditorDidMount(editor: editor.IStandaloneCodeEditor) {
editorRef.current = editor; editorRef.current = editor;
editor.onDidChangeModelContent((e) => { editor.onDidChangeModelContent(() => {
onChange(editorRef.current?.getModel()?.getValue() ?? ""); onChange(editorRef.current?.getModel()?.getValue() ?? "");
}); });
} }

View File

@@ -1,8 +1,9 @@
"use client"; "use client";
import { trpc } from "@/services/serverFunctions/trpcClient"; import { useTRPC } from "@/services/serverFunctions/trpcClient";
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { io, Socket } from "socket.io-client"; import { io, Socket } from "socket.io-client";
import { useQueryClient } from "@tanstack/react-query";
interface ServerToClientEvents { interface ServerToClientEvents {
message: (data: string) => void; message: (data: string) => void;
@@ -59,7 +60,8 @@ export function ClientCacheInvalidation() {
} }
const useFilePathInvalidation = () => { const useFilePathInvalidation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
const queryClient = useQueryClient();
return useCallback( return useCallback(
(filePath: string) => { (filePath: string) => {
const [courseName, moduleOrLectures, itemType, itemFile] = const [courseName, moduleOrLectures, itemType, itemFile] =
@@ -69,69 +71,88 @@ const useFilePathInvalidation = () => {
const allParts = [courseName, moduleOrLectures, itemType, itemName]; const allParts = [courseName, moduleOrLectures, itemType, itemName];
if (moduleOrLectures === "settings.yml") { if (moduleOrLectures === "settings.yml") {
utils.settings.allCoursesSettings.invalidate(); queryClient.invalidateQueries({
utils.settings.courseSettings.invalidate({ courseName }); queryKey: trpc.settings.allCoursesSettings.queryKey(),
});
queryClient.invalidateQueries({
queryKey: trpc.settings.courseSettings.queryKey({ courseName }),
});
return; return;
} }
if (moduleOrLectures === "00 - lectures") { if (moduleOrLectures === "00 - lectures") {
console.log("lecture updated on FS ", allParts); console.log("lecture updated on FS ", allParts);
utils.lectures.getLectures.invalidate({ courseName }); queryClient.invalidateQueries({
queryKey: trpc.lectures.getLectures.queryKey({ courseName }),
});
return; return;
} }
if (itemType === "assignments") { if (itemType === "assignments") {
console.log("assignment updated on FS ", allParts); console.log("assignment updated on FS ", allParts);
utils.assignment.getAllAssignments.invalidate({ queryClient.invalidateQueries({
courseName, queryKey: trpc.assignment.getAllAssignments.queryKey({
moduleName: moduleOrLectures, courseName,
moduleName: moduleOrLectures,
}),
}); });
utils.assignment.getAssignment.invalidate({ queryClient.invalidateQueries({
courseName, queryKey: trpc.assignment.getAssignment.queryKey({
moduleName: moduleOrLectures, courseName,
assignmentName: itemName, moduleName: moduleOrLectures,
assignmentName: itemName,
}),
}); });
return; return;
} }
if (itemType === "quizzes") { if (itemType === "quizzes") {
console.log("quiz updated on FS ", allParts); console.log("quiz updated on FS ", allParts);
utils.quiz.getAllQuizzes.invalidate({ queryClient.invalidateQueries({
courseName, queryKey: trpc.quiz.getAllQuizzes.queryKey({
moduleName: moduleOrLectures, courseName,
moduleName: moduleOrLectures,
}),
}); });
utils.quiz.getQuiz.invalidate({ queryClient.invalidateQueries({
courseName, queryKey: trpc.quiz.getQuiz.queryKey({
moduleName: moduleOrLectures, courseName,
quizName: itemName, moduleName: moduleOrLectures,
quizName: itemName,
}),
}); });
return; return;
} }
if (itemType === "pages") { if (itemType === "pages") {
console.log("page updated on FS ", allParts); console.log("page updated on FS ", allParts);
utils.page.getAllPages.invalidate({ queryClient.invalidateQueries({
courseName, queryKey: trpc.page.getAllPages.queryKey({
moduleName: moduleOrLectures, courseName,
moduleName: moduleOrLectures,
}),
}); });
utils.page.getPage.invalidate({ queryClient.invalidateQueries({
courseName, queryKey: trpc.page.getPage.queryKey({
moduleName: moduleOrLectures, courseName,
pageName: itemName, moduleName: moduleOrLectures,
pageName: itemName,
}),
}); });
return; return;
} }
}, },
[ [
utils.assignment.getAllAssignments, queryClient,
utils.assignment.getAssignment, trpc.assignment.getAllAssignments,
utils.lectures.getLectures, trpc.assignment.getAssignment,
utils.page.getAllPages, trpc.lectures.getLectures,
utils.page.getPage, trpc.page.getAllPages,
utils.quiz.getAllQuizzes, trpc.page.getPage,
utils.quiz.getQuiz, trpc.quiz.getAllQuizzes,
utils.settings.allCoursesSettings, trpc.quiz.getQuiz,
utils.settings.courseSettings, trpc.settings.allCoursesSettings,
trpc.settings.courseSettings,
] ]
); );
}; };

View File

@@ -14,7 +14,7 @@ export const canvasAssignmentKeys = {
}; };
export const useCanvasAssignmentsQuery = () => { export const useCanvasAssignmentsQuery = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return useQuery({ return useQuery({
queryKey: canvasAssignmentKeys.assignments(settings.canvasId), queryKey: canvasAssignmentKeys.assignments(settings.canvasId),
@@ -22,9 +22,8 @@ export const useCanvasAssignmentsQuery = () => {
}); });
}; };
export const useAddAssignmentToCanvasMutation = () => { export const useAddAssignmentToCanvasMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const { data: canvasModules } = useCanvasModulesQuery(); const { data: canvasModules } = useCanvasModulesQuery();
const addModule = useAddCanvasModuleMutation(); const addModule = useAddCanvasModuleMutation();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -74,7 +73,7 @@ export const useAddAssignmentToCanvasMutation = () => {
}; };
export const useUpdateAssignmentInCanvasMutation = () => { export const useUpdateAssignmentInCanvasMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
@@ -105,7 +104,7 @@ export const useUpdateAssignmentInCanvasMutation = () => {
}; };
export const useDeleteAssignmentFromCanvasMutation = () => { export const useDeleteAssignmentFromCanvasMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ mutationFn: async ({

View File

@@ -1,9 +1,5 @@
import { canvasModuleService } from "@/services/canvas/canvasModuleService"; import { canvasModuleService } from "@/services/canvas/canvasModuleService";
import { import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
useMutation,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import { useLocalCourseSettingsQuery } from "../localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "../localCourse/localCoursesHooks";
export const canvasCourseModuleKeys = { export const canvasCourseModuleKeys = {
@@ -11,7 +7,7 @@ export const canvasCourseModuleKeys = {
}; };
export const useCanvasModulesQuery = () => { export const useCanvasModulesQuery = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return useQuery({ return useQuery({
queryKey: canvasCourseModuleKeys.modules(settings.canvasId), queryKey: canvasCourseModuleKeys.modules(settings.canvasId),
queryFn: async () => queryFn: async () =>
@@ -20,7 +16,7 @@ export const useCanvasModulesQuery = () => {
}; };
export const useAddCanvasModuleMutation = () => { export const useAddCanvasModuleMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async (moduleName: string) => mutationFn: async (moduleName: string) =>
@@ -32,4 +28,3 @@ export const useAddCanvasModuleMutation = () => {
}, },
}); });
}; };

View File

@@ -7,7 +7,7 @@ export const canvasCourseTabKeys = {
}; };
export const useCanvasTabsQuery = () => { export const useCanvasTabsQuery = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return useQuery({ return useQuery({
queryKey: canvasCourseTabKeys.tabs(settings.canvasId), queryKey: canvasCourseTabKeys.tabs(settings.canvasId),
queryFn: async () => queryFn: async () =>
@@ -16,7 +16,7 @@ export const useCanvasTabsQuery = () => {
}; };
export const useUpdateCanvasTabMutation = () => { export const useUpdateCanvasTabMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ mutationFn: async ({
@@ -35,7 +35,7 @@ export const useUpdateCanvasTabMutation = () => {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: canvasCourseTabKeys.tabs(settings.canvasId), queryKey: canvasCourseTabKeys.tabs(settings.canvasId),
refetchType: "all" refetchType: "all",
}); });
}, },
}); });

View File

@@ -17,7 +17,7 @@ export const canvasPageKeys = {
}; };
export const useCanvasPagesQuery = () => { export const useCanvasPagesQuery = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return useQuery({ return useQuery({
queryKey: canvasPageKeys.pagesInCourse(settings.canvasId), queryKey: canvasPageKeys.pagesInCourse(settings.canvasId),
queryFn: async () => await canvasPageService.getAll(settings.canvasId), queryFn: async () => await canvasPageService.getAll(settings.canvasId),
@@ -25,7 +25,7 @@ export const useCanvasPagesQuery = () => {
}; };
export const useCreateCanvasPageMutation = () => { export const useCreateCanvasPageMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { data: canvasModules } = useCanvasModulesQuery(); const { data: canvasModules } = useCanvasModulesQuery();
const addModule = useAddCanvasModuleMutation(); const addModule = useAddCanvasModuleMutation();
@@ -44,7 +44,8 @@ export const useCreateCanvasPageMutation = () => {
} }
const canvasPage = await canvasPageService.create( const canvasPage = await canvasPageService.create(
settings.canvasId, settings.canvasId,
page,settings page,
settings
); );
const canvasModule = canvasModules.find((c) => c.name === moduleName); const canvasModule = canvasModules.find((c) => c.name === moduleName);
@@ -69,7 +70,7 @@ export const useCreateCanvasPageMutation = () => {
}; };
export const useUpdateCanvasPageMutation = () => { export const useUpdateCanvasPageMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ mutationFn: async ({
@@ -78,7 +79,8 @@ export const useUpdateCanvasPageMutation = () => {
}: { }: {
page: LocalCoursePage; page: LocalCoursePage;
canvasPageId: number; canvasPageId: number;
}) => canvasPageService.update(settings.canvasId, canvasPageId, page, settings), }) =>
canvasPageService.update(settings.canvasId, canvasPageId, page, settings),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: canvasPageKeys.pagesInCourse(settings.canvasId), queryKey: canvasPageKeys.pagesInCourse(settings.canvasId),
@@ -88,7 +90,7 @@ export const useUpdateCanvasPageMutation = () => {
}; };
export const useDeleteCanvasPageMutation = () => { export const useDeleteCanvasPageMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async (canvasPageId: number) => mutationFn: async (canvasPageId: number) =>

View File

@@ -1,8 +1,4 @@
import { import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
useMutation,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import { useLocalCourseSettingsQuery } from "../localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "../localCourse/localCoursesHooks";
import { canvasQuizService } from "@/services/canvas/canvasQuizService"; import { canvasQuizService } from "@/services/canvas/canvasQuizService";
import { LocalQuiz } from "@/models/local/quiz/localQuiz"; import { LocalQuiz } from "@/models/local/quiz/localQuiz";
@@ -18,7 +14,7 @@ export const canvasQuizKeys = {
}; };
export const useCanvasQuizzesQuery = () => { export const useCanvasQuizzesQuery = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return useQuery({ return useQuery({
queryKey: canvasQuizKeys.quizzes(settings.canvasId), queryKey: canvasQuizKeys.quizzes(settings.canvasId),
@@ -27,7 +23,7 @@ export const useCanvasQuizzesQuery = () => {
}; };
export const useAddQuizToCanvasMutation = () => { export const useAddQuizToCanvasMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { data: canvasModules } = useCanvasModulesQuery(); const { data: canvasModules } = useCanvasModulesQuery();
const addModule = useAddCanvasModuleMutation(); const addModule = useAddCanvasModuleMutation();
@@ -76,7 +72,7 @@ export const useAddQuizToCanvasMutation = () => {
}; };
export const useDeleteQuizFromCanvasMutation = () => { export const useDeleteQuizFromCanvasMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async (canvasQuizId: number) => { mutationFn: async (canvasQuizId: number) => {

View File

@@ -1,5 +1,5 @@
"use client"; "use client";
import { trpc } from "@/services/serverFunctions/trpcClient"; import { useTRPC } from "@/services/serverFunctions/trpcClient";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
import { import {
useLocalCourseSettingsQuery, useLocalCourseSettingsQuery,
@@ -10,18 +10,25 @@ import {
markdownToHtmlNoImages, markdownToHtmlNoImages,
} from "@/services/htmlMarkdownUtils"; } from "@/services/htmlMarkdownUtils";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useMutation } from "@tanstack/react-query"; import {
useMutation,
useQueryClient,
useSuspenseQuery,
} from "@tanstack/react-query";
export const useAssignmentQuery = ( export const useAssignmentQuery = (
moduleName: string, moduleName: string,
assignmentName: string assignmentName: string
) => { ) => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return trpc.assignment.getAssignment.useSuspenseQuery({ const trpc = useTRPC();
moduleName, return useSuspenseQuery(
courseName, trpc.assignment.getAssignment.queryOptions({
assignmentName, moduleName,
}); courseName,
assignmentName,
})
);
}; };
const enable_images = process.env.NEXT_PUBLIC_ENABLE_FILE_SYNC === "true"; const enable_images = process.env.NEXT_PUBLIC_ENABLE_FILE_SYNC === "true";
@@ -33,13 +40,15 @@ export const useUpdateImageSettingsForAssignment = ({
moduleName: string; moduleName: string;
assignmentName: string; assignmentName: string;
}) => { }) => {
const [assignment] = useAssignmentQuery(moduleName, assignmentName); const { data: assignment } = useAssignmentQuery(moduleName, assignmentName);
const [isPending, setIsPending] = useState(false); const [isPending, setIsPending] = useState(false);
const addNewImagesToCanvasMutation = useAddNewImagesToCanvasMutation(); const addNewImagesToCanvasMutation = useAddNewImagesToCanvasMutation();
useEffect(() => { useEffect(() => {
if (!enable_images) { 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; return;
} }
@@ -62,9 +71,11 @@ export const useUpdateImageSettingsForAssignment = ({
}; };
export const useAddNewImagesToCanvasMutation = () => { export const useAddNewImagesToCanvasMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
const createCanvasUrlMutation = const trpc = useTRPC();
trpc.canvasFile.getCanvasFileUrl.useMutation(); const createCanvasUrlMutation = useMutation(
trpc.canvasFile.getCanvasFileUrl.mutationOptions()
);
const updateSettings = useUpdateLocalCourseSettingsMutation(); const updateSettings = useUpdateLocalCourseSettingsMutation();
return useMutation({ return useMutation({
@@ -103,81 +114,101 @@ export const useAddNewImagesToCanvasMutation = () => {
export const useAssignmentNamesQuery = (moduleName: string) => { export const useAssignmentNamesQuery = (moduleName: string) => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return trpc.assignment.getAllAssignments.useSuspenseQuery( const trpc = useTRPC();
{ return useSuspenseQuery({
...trpc.assignment.getAllAssignments.queryOptions({
moduleName, moduleName,
courseName, courseName,
}, }),
{ select: (assignments) => assignments.map((a) => a.name),
select: (assignments) => assignments.map((a) => a.name), });
}
);
}; };
export const useUpdateAssignmentMutation = () => { export const useUpdateAssignmentMutation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
return trpc.assignment.updateAssignment.useMutation({ const queryClient = useQueryClient();
onSuccess: ( return useMutation(
_, trpc.assignment.updateAssignment.mutationOptions({
{ onSuccess: (
courseName, _,
moduleName,
assignmentName,
previousAssignmentName,
previousModuleName,
}
) => {
if (moduleName !== previousModuleName) {
utils.assignment.getAllAssignments.invalidate(
{
courseName,
moduleName: previousModuleName,
},
{ refetchType: "all" }
);
}
utils.assignment.getAllAssignments.invalidate(
{ courseName, moduleName },
{ refetchType: "all" }
);
utils.assignment.getAssignment.invalidate({
courseName,
moduleName,
assignmentName,
});
utils.assignment.getAssignment.invalidate({
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 });
},
});
};
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(
{ {
courseName, courseName,
moduleName, moduleName,
assignmentName, assignmentName,
}, previousAssignmentName,
{ previousModuleName,
refetchType: "all",
} }
); ) => {
}, if (moduleName !== previousModuleName) {
}); queryClient.invalidateQueries({
queryKey: trpc.assignment.getAllAssignments.queryKey({
courseName,
moduleName: previousModuleName,
}),
});
}
queryClient.invalidateQueries({
queryKey: trpc.assignment.getAllAssignments.queryKey({
courseName,
moduleName,
}),
});
queryClient.invalidateQueries({
queryKey: trpc.assignment.getAssignment.queryKey({
courseName,
moduleName,
assignmentName,
}),
});
queryClient.invalidateQueries({
queryKey: trpc.assignment.getAssignment.queryKey({
courseName,
moduleName,
assignmentName: previousAssignmentName,
}),
});
},
})
);
};
export const useCreateAssignmentMutation = () => {
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 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,
}),
});
},
})
);
}; };

View File

@@ -1,25 +1,43 @@
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; 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 = () => { export const useLecturesSuspenseQuery = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return trpc.lectures.getLectures.useSuspenseQuery({ courseName }); const trpc = useTRPC();
return useSuspenseQuery(
trpc.lectures.getLectures.queryOptions({ courseName })
);
}; };
export const useLectureUpdateMutation = () => { export const useLectureUpdateMutation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
return trpc.lectures.updateLecture.useMutation({ const queryClient = useQueryClient();
onSuccess: () => { return useMutation(
utils.lectures.getLectures.invalidate(); trpc.lectures.updateLecture.mutationOptions({
}, onSuccess: () => {
}); queryClient.invalidateQueries({
queryKey: trpc.lectures.getLectures.queryKey(),
});
},
})
);
}; };
export const useDeleteLectureMutation = () => { export const useDeleteLectureMutation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
return trpc.lectures.deleteLecture.useMutation({ const queryClient = useQueryClient();
onSuccess: () => { return useMutation(
utils.lectures.getLectures.invalidate(); trpc.lectures.deleteLecture.mutationOptions({
}, onSuccess: () => {
}); queryClient.invalidateQueries({
queryKey: trpc.lectures.getLectures.queryKey(),
});
},
})
);
}; };

View File

@@ -1,41 +1,55 @@
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; 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 { CalendarItemsInterface } from "@/app/course/[courseName]/context/calendarItemsContext";
import { import {
getDateOnlyMarkdownString, getDateOnlyMarkdownString,
getDateFromStringOrThrow, getDateFromStringOrThrow,
} from "@/models/local/utils/timeUtils"; } from "@/models/local/utils/timeUtils";
import {
useSuspenseQuery,
useMutation,
useQueryClient,
useSuspenseQueries,
} from "@tanstack/react-query";
export const useModuleNamesQuery = () => { export const useModuleNamesQuery = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return trpc.module.getModuleNames.useSuspenseQuery({ courseName }); const trpc = useTRPC();
return useSuspenseQuery(
trpc.module.getModuleNames.queryOptions({ courseName })
);
}; };
export const useCreateModuleMutation = () => { export const useCreateModuleMutation = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const utils = trpc.useUtils(); const trpc = useTRPC();
const queryClient = useQueryClient();
return trpc.module.createModule.useMutation({ return useMutation(
onSuccess: () => { trpc.module.createModule.mutationOptions({
utils.module.getModuleNames.invalidate({ courseName }); onSuccess: () => {
}, queryClient.invalidateQueries({
}); queryKey: trpc.module.getModuleNames.queryKey({ courseName }),
});
},
})
);
}; };
export const useCourseQuizzesByModuleByDateQuery = () => { export const useCourseQuizzesByModuleByDateQuery = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [moduleNames] = useModuleNamesQuery(); const { data: moduleNames } = useModuleNamesQuery();
const trpc = useTRPC();
const [quizzes] = trpc.useSuspenseQueries((t) => const quizzesResults = useSuspenseQueries({
moduleNames.map((moduleName) => queries: moduleNames.map((moduleName: string) =>
t.quiz.getAllQuizzes({ courseName, moduleName }) trpc.quiz.getAllQuizzes.queryOptions({ courseName, moduleName })
) ),
);
const quizzesAndModules = moduleNames.flatMap((moduleName, index) => {
return quizzes[index].map((quiz) => ({ moduleName, quiz }));
}); });
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( const quizzesByModuleByDate = quizzesAndModules.reduce(
(previous, { quiz, moduleName }) => { (previous, { quiz, moduleName }) => {
const dueDay = getDateOnlyMarkdownString( const dueDay = getDateOnlyMarkdownString(
@@ -45,12 +59,10 @@ export const useCourseQuizzesByModuleByDateQuery = () => {
const previousModule = previousModules[moduleName] ?? { const previousModule = previousModules[moduleName] ?? {
quizzes: [], quizzes: [],
}; };
const updatedModule = { const updatedModule = {
...previousModule, ...previousModule,
quizzes: [...previousModule.quizzes, quiz], quizzes: [...previousModule.quizzes, quiz],
}; };
return { return {
...previous, ...previous,
[dueDay]: { [dueDay]: {
@@ -66,19 +78,24 @@ export const useCourseQuizzesByModuleByDateQuery = () => {
export const useCoursePagesByModuleByDateQuery = () => { export const useCoursePagesByModuleByDateQuery = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [moduleNames] = useModuleNamesQuery(); const { data: moduleNames } = useModuleNamesQuery();
const [pages] = trpc.useSuspenseQueries((t) => const trpc = useTRPC();
moduleNames.map((moduleName) => const pagesResults = useSuspenseQueries({
t.page.getAllPages({ courseName, moduleName }) queries: moduleNames.map((moduleName: string) =>
) trpc.page.getAllPages.queryOptions({ courseName, moduleName })
); ),
const pagesAndModules = moduleNames.flatMap((moduleName, index) => {
return pages[index].map((page) => ({ moduleName, page }));
}); });
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( const pagesByModuleByDate = pagesAndModules.reduce(
(previous, { page, moduleName }) => { (
previous,
{ page, moduleName }
) => {
const dueDay = getDateOnlyMarkdownString( const dueDay = getDateOnlyMarkdownString(
getDateFromStringOrThrow(page.dueAt, "due at for page in items context") getDateFromStringOrThrow(page.dueAt, "due at for page in items context")
); );
@@ -86,12 +103,10 @@ export const useCoursePagesByModuleByDateQuery = () => {
const previousModule = previousModules[moduleName] ?? { const previousModule = previousModules[moduleName] ?? {
pages: [], pages: [],
}; };
const updatedModule = { const updatedModule = {
...previousModule, ...previousModule,
pages: [...previousModule.pages, page], pages: [...previousModule.pages, page],
}; };
return { return {
...previous, ...previous,
[dueDay]: { [dueDay]: {
@@ -107,17 +122,29 @@ export const useCoursePagesByModuleByDateQuery = () => {
export const useCourseAssignmentsByModuleByDateQuery = () => { export const useCourseAssignmentsByModuleByDateQuery = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [moduleNames] = useModuleNamesQuery(); const { data: moduleNames } = useModuleNamesQuery();
const [assignments] = trpc.useSuspenseQueries((t) => const trpc = useTRPC();
moduleNames.map((moduleName) => const assignmentsResults = useSuspenseQueries({
t.assignment.getAllAssignments({ courseName, moduleName }) queries: moduleNames.map((moduleName: string) =>
) trpc.assignment.getAllAssignments.queryOptions({ courseName, moduleName })
); ),
const assignmentsAndModules = moduleNames.flatMap((moduleName, index) => {
return assignments[index].map((assignment) => ({ moduleName, assignment }));
}); });
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( const assignmentsByModuleByDate = assignmentsAndModules.reduce(
(previous, { assignment, moduleName }) => { (
previous,
{ assignment, moduleName }
) => {
const dueDay = getDateOnlyMarkdownString( const dueDay = getDateOnlyMarkdownString(
getDateFromStringOrThrow( getDateFromStringOrThrow(
assignment.dueAt, assignment.dueAt,
@@ -128,12 +155,10 @@ export const useCourseAssignmentsByModuleByDateQuery = () => {
const previousModule = previousModules[moduleName] ?? { const previousModule = previousModules[moduleName] ?? {
assignments: [], assignments: [],
}; };
const updatedModule = { const updatedModule = {
...previousModule, ...previousModule,
assignments: [...previousModule.assignments, assignment], assignments: [...previousModule.assignments, assignment],
}; };
return { return {
...previous, ...previous,
[dueDay]: { [dueDay]: {

View File

@@ -1,33 +1,56 @@
"use client"; "use client";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; 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 = () => export const useLocalCoursesSettingsQuery = () => {
trpc.settings.allCoursesSettings.useSuspenseQuery(); const trpc = useTRPC();
return useSuspenseQuery(trpc.settings.allCoursesSettings.queryOptions());
};
export const useLocalCourseSettingsQuery = () => { export const useLocalCourseSettingsQuery = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return trpc.settings.courseSettings.useSuspenseQuery({ courseName }); const trpc = useTRPC();
return useSuspenseQuery(
trpc.settings.courseSettings.queryOptions({ courseName })
);
}; };
export const useCreateLocalCourseMutation = () => { export const useCreateLocalCourseMutation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
return trpc.settings.createCourse.useMutation({ const queryClient = useQueryClient();
onSuccess: () => { return useMutation(
utils.settings.allCoursesSettings.invalidate(); trpc.settings.createCourse.mutationOptions({
utils.directories.getEmptyDirectories.invalidate(); onSuccess: () => {
}, queryClient.invalidateQueries({
}); queryKey: trpc.settings.allCoursesSettings.queryKey(),
});
queryClient.invalidateQueries({
queryKey: trpc.directories.getEmptyDirectories.queryKey(),
});
},
})
);
}; };
export const useUpdateLocalCourseSettingsMutation = () => { export const useUpdateLocalCourseSettingsMutation = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const utils = trpc.useUtils(); const trpc = useTRPC();
const queryClient = useQueryClient();
return trpc.settings.updateSettings.useMutation({ return useMutation(
onSuccess: () => { trpc.settings.updateSettings.mutationOptions({
utils.settings.allCoursesSettings.invalidate(); onSuccess: () => {
utils.settings.courseSettings.invalidate({ courseName }); queryClient.invalidateQueries({
}, queryKey: trpc.settings.allCoursesSettings.queryKey(),
}); });
queryClient.invalidateQueries({
queryKey: trpc.settings.courseSettings.queryKey({ courseName }),
});
},
})
);
}; };

View File

@@ -1,64 +1,98 @@
"use client"; "use client";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; 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) => { export const usePageQuery = (moduleName: string, pageName: string) => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return trpc.page.getPage.useSuspenseQuery({ const trpc = useTRPC();
courseName, return useSuspenseQuery(
moduleName, trpc.page.getPage.queryOptions({
pageName, courseName,
}); moduleName,
pageName,
})
);
}; };
export const usePagesQueries = (moduleName: string) => { export const usePagesQueries = (moduleName: string) => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return trpc.page.getAllPages.useSuspenseQuery({ const trpc = useTRPC();
courseName, return useSuspenseQuery(
moduleName, trpc.page.getAllPages.queryOptions({
}); courseName,
moduleName,
})
);
}; };
export const useUpdatePageMutation = () => { export const useUpdatePageMutation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
return trpc.page.updatePage.useMutation({ const queryClient = useQueryClient();
onSuccess: ( return useMutation(
_, trpc.page.updatePage.mutationOptions({
{ courseName, moduleName, pageName, previousModuleName } onSuccess: (
) => { _,
utils.page.getAllPages.invalidate({ courseName, moduleName }, { courseName, moduleName, pageName, previousModuleName }
{ refetchType: "all" }); ) => {
utils.page.getPage.invalidate({ courseName, moduleName, pageName }); queryClient.invalidateQueries({
if (moduleName !== previousModuleName) { queryKey: trpc.page.getAllPages.queryKey({ courseName, moduleName }),
utils.page.getAllPages.invalidate({ });
courseName, queryClient.invalidateQueries({
moduleName: previousModuleName, queryKey: trpc.page.getPage.queryKey({
}, courseName,
{ refetchType: "all" }); moduleName,
} pageName,
}, }),
}); });
}; if (moduleName !== previousModuleName) {
export const useCreatePageMutation = () => { queryClient.invalidateQueries({
const utils = trpc.useUtils(); queryKey: trpc.page.getAllPages.queryKey({
return trpc.page.createPage.useMutation({ courseName,
onSuccess: (_, { courseName, moduleName }) => { moduleName: previousModuleName,
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",
} }
); },
utils.page.getPage.invalidate({ courseName, moduleName, pageName }); })
}, );
}); };
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,
}),
});
},
})
);
}; };

View File

@@ -1,62 +1,107 @@
"use client"; "use client";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; 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) => { export const useQuizQuery = (moduleName: string, quizName: string) => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return trpc.quiz.getQuiz.useSuspenseQuery({ const trpc = useTRPC();
courseName, return useSuspenseQuery(
moduleName, trpc.quiz.getQuiz.queryOptions({
quizName, courseName,
}); moduleName,
quizName,
})
);
}; };
export const useQuizzesQueries = (moduleName: string) => { export const useQuizzesQueries = (moduleName: string) => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
// const trpc = usetrpc(); const trpc = useTRPC();
return trpc.quiz.getAllQuizzes.useSuspenseQuery({ return useSuspenseQuery(
courseName, trpc.quiz.getAllQuizzes.queryOptions({
moduleName, courseName,
}); moduleName,
})
);
}; };
export const useUpdateQuizMutation = () => { export const useUpdateQuizMutation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
return trpc.quiz.updateQuiz.useMutation({ const queryClient = useQueryClient();
onSuccess: ( return useMutation(
_, trpc.quiz.updateQuiz.mutationOptions({
{ courseName, moduleName, quizName, previousModuleName } onSuccess: (
) => { _,
if (moduleName !== previousModuleName) { courseName, moduleName, quizName, previousModuleName }
utils.quiz.getAllQuizzes.invalidate({ ) => {
courseName, if (moduleName !== previousModuleName) {
moduleName: previousModuleName, queryClient.invalidateQueries({
}, queryKey: trpc.quiz.getAllQuizzes.queryKey({
{ refetchType: "all" }); courseName,
utils.quiz.getAllQuizzes.invalidate({ courseName, moduleName }, moduleName: previousModuleName,
{ refetchType: "all" }); }),
utils.quiz.getQuiz.invalidate({ courseName, moduleName, quizName }); });
}, }
}); queryClient.invalidateQueries({
queryKey: trpc.quiz.getAllQuizzes.queryKey({
courseName,
moduleName,
}),
});
queryClient.invalidateQueries({
queryKey: trpc.quiz.getQuiz.queryKey({
courseName,
moduleName,
quizName,
}),
});
},
})
);
}; };
export const useCreateQuizMutation = () => { export const useCreateQuizMutation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
return trpc.quiz.createQuiz.useMutation({ const queryClient = useQueryClient();
onSuccess: (_, { courseName, moduleName }) => { return useMutation(
utils.quiz.getAllQuizzes.invalidate({ courseName, moduleName }); trpc.quiz.createQuiz.mutationOptions({
}, onSuccess: (_, { courseName, moduleName }) => {
}); queryClient.invalidateQueries({
queryKey: trpc.quiz.getAllQuizzes.queryKey({
courseName,
moduleName,
}),
});
},
})
);
}; };
export const useDeleteQuizMutation = () => { export const useDeleteQuizMutation = () => {
const utils = trpc.useUtils(); const trpc = useTRPC();
return trpc.quiz.deleteQuiz.useMutation({ const queryClient = useQueryClient();
onSuccess: (_, { courseName, moduleName, quizName }) => { return useMutation(
utils.quiz.getAllQuizzes.invalidate( trpc.quiz.deleteQuiz.mutationOptions({
{ courseName, moduleName }, onSuccess: (_, { courseName, moduleName, quizName }) => {
{ refetchType: "all" } queryClient.invalidateQueries({
); queryKey: trpc.quiz.getAllQuizzes.queryKey({
utils.quiz.getQuiz.invalidate({ courseName, moduleName, quizName }); courseName,
}, moduleName,
}); }),
});
queryClient.invalidateQueries({
queryKey: trpc.quiz.getQuiz.queryKey({
courseName,
moduleName,
quizName,
}),
});
},
})
);
}; };

View File

@@ -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 = { export const directoryKeys = {
emptyFolders: ["empty folders"] as const, emptyFolders: ["empty folders"] as const,
}; };
export const useEmptyDirectoriesQuery = () => export const useEmptyDirectoriesQuery = () => {
trpc.directories.getEmptyDirectories.useSuspenseQuery(); const trpc = useTRPC();
return useSuspenseQuery(trpc.directories.getEmptyDirectories.queryOptions());
};

View File

@@ -54,7 +54,7 @@ export interface CanvasAssignment {
}[]; }[];
post_to_sis?: boolean; post_to_sis?: boolean;
integration_id?: string; integration_id?: string;
integration_data?: any; integration_data?: unknown;
muted?: boolean; muted?: boolean;
points_possible?: number; points_possible?: number;
has_submitted_submissions?: boolean; has_submitted_submissions?: boolean;
@@ -69,7 +69,7 @@ export interface CanvasAssignment {
frozen_attributes?: string[]; frozen_attributes?: string[];
submission?: CanvasSubmissionModel; submission?: CanvasSubmissionModel;
use_rubric_for_grading?: boolean; use_rubric_for_grading?: boolean;
rubric_settings?: any; rubric_settings?: unknown;
rubric?: CanvasRubricCriteria[]; rubric?: CanvasRubricCriteria[];
assignment_visibility?: number[]; assignment_visibility?: number[];
overrides?: CanvasAssignmentOverride[]; overrides?: CanvasAssignmentOverride[];

View File

@@ -2,6 +2,6 @@ export interface CanvasLockInfo {
asset_string: string; asset_string: string;
unlock_at?: string; // ISO 8601 date string unlock_at?: string; // ISO 8601 date string
lock_at?: string; // ISO 8601 date string lock_at?: string; // ISO 8601 date string
context_module?: any; context_module?: unknown;
manually_locked?: boolean; manually_locked?: boolean;
} }

View File

@@ -30,8 +30,8 @@ export interface CanvasDiscussionTopicModel {
locked?: boolean; locked?: boolean;
pinned?: boolean; pinned?: boolean;
locked_for_user?: boolean; locked_for_user?: boolean;
lock_info?: any; lock_info?: unknown;
group_topic_children?: any; group_topic_children?: unknown;
root_topic_id?: number; root_topic_id?: number;
group_category_id?: number; group_category_id?: number;
allow_rating?: boolean; allow_rating?: boolean;

View File

@@ -37,7 +37,7 @@ export interface CanvasQuiz {
speedgrader_url?: string; speedgrader_url?: string;
quiz_extensions_url?: string; quiz_extensions_url?: string;
permissions: CanvasQuizPermissions; 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; version_number?: number;
question_types?: string[]; question_types?: string[];
anonymous_submissions?: boolean; anonymous_submissions?: boolean;

View File

@@ -9,11 +9,6 @@ import {
} from "./assignment/localAssignmentGroup"; } from "./assignment/localAssignmentGroup";
import { parse, stringify } from "yaml"; import { parse, stringify } from "yaml";
// export interface LocalCourse {
// modules: LocalModule[];
// settings: LocalCourseSettings;
// }
export interface SimpleTimeOnly { export interface SimpleTimeOnly {
hour: number; hour: number;
minute: 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; 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) => { 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); const newKey = key.charAt(0).toLowerCase() + key.slice(1);
if (value && typeof value === "object") { if (value && typeof value === "object") {

View File

@@ -1,4 +1,3 @@
import { LocalQuiz } from "../localQuiz";
import { LocalQuizQuestion, QuestionType } from "../localQuizQuestion"; import { LocalQuizQuestion, QuestionType } from "../localQuizQuestion";
import { LocalQuizQuestionAnswer } from "../localQuizQuestionAnswer"; import { LocalQuizQuestionAnswer } from "../localQuizQuestionAnswer";
import { quizQuestionAnswerMarkdownUtils } from "./quizQuestionAnswerMarkdownUtils"; import { quizQuestionAnswerMarkdownUtils } from "./quizQuestionAnswerMarkdownUtils";
@@ -112,7 +111,7 @@ const getAnswers = (
questionIndex questionIndex
); );
const answers = answerLines.map((a, i) => const answers = answerLines.map((a) =>
quizQuestionAnswerMarkdownUtils.parseMarkdown(a, questionType) quizQuestionAnswerMarkdownUtils.parseMarkdown(a, questionType)
); );
return answers; return answers;
@@ -215,7 +214,7 @@ export const quizQuestionMarkdownUtils = {
? linesWithoutAnswers ? linesWithoutAnswers
.slice(0, linesWithoutPoints.length) .slice(0, linesWithoutPoints.length)
.filter( .filter(
(line, index) => (line) =>
!questionTypesWithoutAnswers.includes(line.toLowerCase()) !questionTypesWithoutAnswers.includes(line.toLowerCase())
) )
: linesWithoutAnswers; : linesWithoutAnswers;

View File

@@ -1,7 +1,6 @@
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { LocalQuiz } from "../../quiz/localQuiz"; import { LocalQuiz } from "../../quiz/localQuiz";
import { LocalQuizQuestion, QuestionType } from "../../quiz/localQuizQuestion"; import { QuestionType } from "../../quiz/localQuizQuestion";
import { LocalQuizQuestionAnswer } from "../../quiz/localQuizQuestionAnswer";
import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils"; import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils";
import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils"; import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils";

View File

@@ -3,7 +3,7 @@ import { LocalQuiz } from "../../quiz/localQuiz";
import { quizMarkdownUtils } from "../../quiz/utils/quizMarkdownUtils"; import { quizMarkdownUtils } from "../../quiz/utils/quizMarkdownUtils";
import { QuestionType } from "@/models/local/quiz/localQuizQuestion"; import { QuestionType } from "@/models/local/quiz/localQuizQuestion";
import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils"; import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils";
import { markdownToHtmlNoImages, markdownToHTMLSafe } from "@/services/htmlMarkdownUtils"; import { markdownToHtmlNoImages } from "@/services/htmlMarkdownUtils";
// Test suite for QuizMarkdown // Test suite for QuizMarkdown
describe("QuizMarkdownTests", () => { describe("QuizMarkdownTests", () => {
@@ -256,7 +256,6 @@ short answer
}); });
it("can parse quiz with latex in a question", () => { it("can parse quiz with latex in a question", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false

View File

@@ -3,7 +3,6 @@ import { QuestionType, zodQuestionType } from "../../quiz/localQuizQuestion";
import { quizMarkdownUtils } from "../../quiz/utils/quizMarkdownUtils"; import { quizMarkdownUtils } from "../../quiz/utils/quizMarkdownUtils";
import { quizQuestionMarkdownUtils } from "../../quiz/utils/quizQuestionMarkdownUtils"; import { quizQuestionMarkdownUtils } from "../../quiz/utils/quizQuestionMarkdownUtils";
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { LocalCourseSettings } from "../../localCourseSettings";
describe("TextAnswerTests", () => { describe("TextAnswerTests", () => {
it("can parse essay", () => { it("can parse essay", () => {

View File

@@ -43,7 +43,8 @@ export function getAxiosErrorMessage(error: AxiosError) {
console.log("response error", error.response); console.log("response error", error.response);
const responseErrorText = const responseErrorText =
typeof error.response.data === "object" 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; : error.response.data;
if ( if (

View File

@@ -15,6 +15,7 @@ export const canvasPageService = {
url, url,
}); });
return pages.flatMap((pageList) => pageList); return pages.flatMap((pageList) => pageList);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
if (error?.response?.status === 403) { if (error?.response?.status === 403) {
console.log( console.log(

View File

@@ -72,6 +72,7 @@ const createQuestionOnly = async (
const hackFixQuestionOrdering = async ( const hackFixQuestionOrdering = async (
canvasCourseId: number, canvasCourseId: number,
canvasQuizId: number, canvasQuizId: number,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
questionAndPositions: Array<{ question: any; position: number }> questionAndPositions: Array<{ question: any; position: number }>
) => { ) => {
console.log("Fixing question order"); console.log("Fixing question order");
@@ -149,6 +150,7 @@ export const canvasQuizService = {
? new Date(quiz.lock_at).toLocaleString() ? new Date(quiz.lock_at).toLocaleString()
: undefined, : undefined,
})); }));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
if (error?.response?.status === 403) { if (error?.response?.status === 403) {
console.log( console.log(

View File

@@ -33,10 +33,10 @@ const getNextUrl = (
return nextUrl; return nextUrl;
}; };
export async function paginatedRequest<T extends any[]>(request: { export async function paginatedRequest<T extends unknown[]>(request: {
url: string; url: string;
}): Promise<T> { }): Promise<T> {
var requestCount = 1; let requestCount = 1;
const url = new URL(request.url); const url = new URL(request.url);
url.searchParams.set("per_page", "100"); url.searchParams.set("per_page", "100");
@@ -44,8 +44,8 @@ export async function paginatedRequest<T extends any[]>(request: {
url.toString() url.toString()
); );
var returnData = Array.isArray(firstData) ? [...firstData] : [firstData]; // terms come across as nested objects {enrolmentTerms: terms[]} let returnData = Array.isArray(firstData) ? [...firstData] : [firstData]; // terms come across as nested objects {enrolmentTerms: terms[]}
var nextUrl = getNextUrl(firstHeaders); let nextUrl = getNextUrl(firstHeaders);
while (nextUrl) { while (nextUrl) {
requestCount += 1; requestCount += 1;

View File

@@ -1,8 +1,6 @@
import { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { axiosClient } from "../axiosUtils"; import { axiosClient } from "../axiosUtils";
type FetchOptions = Omit<RequestInit, "method">;
const rateLimitRetryCount = 6; const rateLimitRetryCount = 6;
const rateLimitSleepInterval = 1000; const rateLimitSleepInterval = 1000;
@@ -18,21 +16,21 @@ export const isRateLimited = async (
); );
}; };
const rateLimitAwarePost = async (url: string, body: any, retryCount = 0) => { // const rateLimitAwarePost = async (url: string, body: unknown, retryCount = 0) => {
const response = await axiosClient.post(url, body); // const response = await axiosClient.post(url, body);
if (await isRateLimited(response)) { // if (await isRateLimited(response)) {
if (retryCount < rateLimitRetryCount) { // if (retryCount < rateLimitRetryCount) {
console.info( // console.info(
`Hit rate limit on post, retry count is ${retryCount} / ${rateLimitRetryCount}, retrying` // `Hit rate limit on post, retry count is ${retryCount} / ${rateLimitRetryCount}, retrying`
); // );
await sleep(rateLimitSleepInterval); // await sleep(rateLimitSleepInterval);
return await rateLimitAwarePost(url, body, retryCount + 1); // return await rateLimitAwarePost(url, body, retryCount + 1);
} // }
} // }
return response; // return response;
}; // };
export const rateLimitAwareDelete = async ( export const rateLimitAwareDelete = async (
url: string, url: string,

View File

@@ -120,7 +120,7 @@ export const courseItemFileStorageService = {
); );
const markdownDictionary: { const markdownDictionary: {
[key in CourseItemType]: () => string; [_key in CourseItemType]: () => string;
} = { } = {
Assignment: () => Assignment: () =>
assignmentMarkdownSerializer.toMarkdown(item as LocalAssignment), assignmentMarkdownSerializer.toMarkdown(item as LocalAssignment),

View File

@@ -102,6 +102,7 @@ export async function deleteLecture(
await fs.access(lecturePath); // throws error if no file await fs.access(lecturePath); // throws error if no file
await fs.unlink(lecturePath); await fs.unlink(lecturePath);
console.log(`File deleted: ${lecturePath}`); console.log(`File deleted: ${lecturePath}`);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
if (error?.code === "ENOENT") { if (error?.code === "ENOENT") {
console.log(`Cannot delete lecture, file does not exist: ${lecturePath}`); console.log(`Cannot delete lecture, file does not exist: ${lecturePath}`);

View File

@@ -75,7 +75,7 @@ export const settingsFileStorageService = {
const courseDirectory = path.join(basePath, courseName); const courseDirectory = path.join(basePath, courseName);
const settingsPath = path.join(courseDirectory, "settings.yml"); const settingsPath = path.join(courseDirectory, "settings.yml");
const { name, ...settingsWithoutName } = settings; const { name: _, ...settingsWithoutName } = settings;
const settingsMarkdown = const settingsMarkdown =
localCourseYamlUtils.settingsToYaml(settingsWithoutName); localCourseYamlUtils.settingsToYaml(settingsWithoutName);

View File

@@ -3,7 +3,6 @@ import { marked } from "marked";
import DOMPurify from "isomorphic-dompurify"; import DOMPurify from "isomorphic-dompurify";
import { LocalCourseSettings } from "@/models/local/localCourseSettings"; import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import markedKatex from "marked-katex-extension"; import markedKatex from "marked-katex-extension";
import toast from "react-hot-toast";
marked.use( marked.use(
markedKatex({ markedKatex({

View File

@@ -2,7 +2,7 @@
import { useState } from "react"; import { useState } from "react";
import superjson from "superjson"; import superjson from "superjson";
import { httpBatchLink } from "@trpc/client"; import { httpBatchLink } from "@trpc/client";
import { trpc } from "./trpcClient"; import { trpc, TRPCProvider } from "./trpcClient";
import { getQueryClient } from "@/app/providersQueryClientUtils"; import { getQueryClient } from "@/app/providersQueryClientUtils";
import { isServer } from "@tanstack/react-query"; 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 url = isServer ? "http://localhost:3000/api/trpc/" : "/api/trpc";
const queryClient = getQueryClient();
const [trpcClient] = useState(() => const [trpcClient] = useState(() =>
trpc.createClient({ trpc.createClient({
links: [ links: [
@@ -25,9 +26,14 @@ export default function TrpcProvider({
}) })
); );
// return (
// <trpc.Provider client={trpcClient} queryClient={getQueryClient()}>
// {children}
// </trpc.Provider>
// );
return ( return (
<trpc.Provider client={trpcClient} queryClient={getQueryClient()}> <TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
{children} {children}
</trpc.Provider> </TRPCProvider>
); );
} }

View File

@@ -1,8 +1,6 @@
import publicProcedure from "../procedures/public"; import publicProcedure from "../procedures/public";
import { z } from "zod";
import { router } from "../trpcSetup"; import { router } from "../trpcSetup";
import { fileStorageService } from "@/services/fileStorage/fileStorageService"; import { fileStorageService } from "@/services/fileStorage/fileStorageService";
import { zodLocalAssignment } from "@/models/local/assignment/localAssignment";
export const directoriesRouter = router({ export const directoriesRouter = router({
getEmptyDirectories: publicProcedure.query(async () => { getEmptyDirectories: publicProcedure.query(async () => {

View File

@@ -3,7 +3,6 @@ import { z } from "zod";
import { router } from "../trpcSetup"; import { router } from "../trpcSetup";
import { fileStorageService } from "@/services/fileStorage/fileStorageService"; import { fileStorageService } from "@/services/fileStorage/fileStorageService";
import { zodLocalCourseSettings } from "@/models/local/localCourseSettings"; import { zodLocalCourseSettings } from "@/models/local/localCourseSettings";
import { trpc } from "../trpcClient";
import { import {
getLectures, getLectures,
updateLecture, updateLecture,

View File

@@ -13,7 +13,7 @@ describe("FileStorageTests", () => {
try { try {
await fs.access(storageDirectory); await fs.access(storageDirectory);
await fs.rm(storageDirectory, { recursive: true }); await fs.rm(storageDirectory, { recursive: true });
} catch (error) {} } catch {}
await fs.mkdir(storageDirectory, { recursive: true }); await fs.mkdir(storageDirectory, { recursive: true });
}); });

View File

@@ -10,7 +10,7 @@ describe("FileStorageTests", () => {
try { try {
await fs.access(storageDirectory); await fs.access(storageDirectory);
await fs.rm(storageDirectory, { recursive: true }); await fs.rm(storageDirectory, { recursive: true });
} catch (error) {} } catch {}
await fs.mkdir(storageDirectory, { recursive: true }); await fs.mkdir(storageDirectory, { recursive: true });
}); });
@@ -75,7 +75,7 @@ a) truthy
// `${basePath}/${courseName}/${moduleName}/quizzes/testQuiz.md`, // `${basePath}/${courseName}/${moduleName}/quizzes/testQuiz.md`,
// invalidQuizMarkdown // invalidQuizMarkdown
// ); // );
// const invalidReasons = await fileStorageService.quizzes.getInvalidQuizzes( // const invalidReasons = await fileStorageService.quizzes.getInvalidQuizzes(
// courseName, // courseName,
// moduleName // moduleName

View File

@@ -1,49 +1,50 @@
import toast, { ErrorIcon, CheckmarkIcon } from "react-hot-toast"; import toast, { CheckmarkIcon } from "react-hot-toast";
import { ReactNode } from "react"; import { ReactNode } from "react";
import { MutationCache, QueryCache, QueryClient } from "@tanstack/react-query";
const addErrorAsToast = async (error: any) => { // const addErrorAsToast = async (error: unknown) => {
console.error("error from toast", error); // console.error("error from toast", error);
const message = getErrorMessage(error); // const message = getErrorMessage(error);
toast( // toast(
(t: any) => ( // (t) => (
<div className="row"> // <div className="row">
<div className="col-auto my-auto"> // <div className="col-auto my-auto">
<ErrorIcon /> // <ErrorIcon />
</div> // </div>
<div className="col my-auto"> // <div className="col my-auto">
<div className="white-space">{message}</div> // <div className="white-space">{message}</div>
<div> // <div>
<a // <a
href="https://snow.kualibuild.com/app/651eeebc32976c013a4c4739/run" // href="https://snow.kualibuild.com/app/651eeebc32976c013a4c4739/run"
target="_blank" // target="_blank"
rel="noreferrer" // rel="noreferrer"
> // >
Report Bug // Report Bug
</a> // </a>
</div> // </div>
</div> // </div>
<div className="col-auto my-auto"> // <div className="col-auto my-auto">
<button // <button
onClick={() => toast.dismiss(t.id)} // onClick={() => toast.dismiss(t.id)}
className="btn btn-outline-secondary btn-sm" // className="btn btn-outline-secondary btn-sm"
> // >
<i className="bi bi-x"></i> // <i className="bi bi-x"></i>
</button> // </button>
</div> // </div>
</div> // </div>
), // ),
{ // {
duration: Infinity, // duration: Infinity,
} // }
); // );
}; // };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getErrorMessage(error: any) { export function getErrorMessage(error: any) {
if (error?.response?.status === 422) { if (error?.response?.status === 422) {
console.log(error.response.data.detail); console.log(error.response.data.detail);
const serializationMessages = error.response.data.detail.map( const serializationMessages = error.response.data.detail.map(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(d: any) => `${d.type} - ${d.loc[1]}` (d: any) => `${d.type} - ${d.loc[1]}`
); );
return `Deserialization error on request:\n${serializationMessages.join( return `Deserialization error on request:\n${serializationMessages.join(
@@ -66,12 +67,8 @@ export function createInfoToast(
children: ReactNode, children: ReactNode,
onClose: () => void = () => {} onClose: () => void = () => {}
) { ) {
const closeHandler = (t: any) => {
toast.dismiss(t.id);
onClose();
};
toast( toast(
(t: any) => ( (t) => (
<div className="row"> <div className="row">
<div className="col-auto my-auto"> <div className="col-auto my-auto">
<i className="bi bi-info-circle-fill"></i> <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 my-auto">{children}</div>
<div className="col-auto my-auto"> <div className="col-auto my-auto">
<button <button
onClick={() => closeHandler(t)} onClick={() => {
toast.dismiss(t.id);
onClose();
}}
className="btn btn-outline-secondary py-1" className="btn btn-outline-secondary py-1"
> >
<i className="bi-x-lg" /> <i className="bi-x-lg" />
@@ -98,7 +98,7 @@ export function createInfoToast(
export function createSuccessToast(message: string) { export function createSuccessToast(message: string) {
toast( toast(
(t: any) => ( (t) => (
<div className="row"> <div className="row">
<div className="col-auto my-auto"> <div className="col-auto my-auto">
<CheckmarkIcon /> <CheckmarkIcon />