trpc stuff

This commit is contained in:
2024-11-08 11:41:20 -07:00
parent 9503b208d2
commit 95d758210c
13 changed files with 213 additions and 73 deletions

View File

@@ -1,16 +1,11 @@
"use client";
import { useLocalCoursesSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { trpc } from "@/services/trpc/utils";
import { getCourseUrl } from "@/services/urlUtils";
import Link from "next/link";
import { useEffect } from "react";
export default function CourseList() {
const { data: allSettings } = useLocalCoursesSettingsQuery();
const {data} = trpc.sayHello.useQuery()
console.log(data);
return (
<div>
{allSettings.map((settings) => (

View File

@@ -8,6 +8,7 @@ import {
getDateOnlyMarkdownString,
} from "@/models/local/timeUtils";
import { useAllCourseDataQuery } from "@/hooks/localCourse/localCourseModuleHooks";
import { trpc } from "@/services/trpc/utils";
export default function CalendarItemsContextProvider({
children,
@@ -17,6 +18,9 @@ export default function CalendarItemsContextProvider({
const { assignmentsAndModules, quizzesAndModules, pagesAndModules } =
useAllCourseDataQuery();
const assignmentsByModuleByDate = assignmentsAndModules.reduce(
(previous, { assignment, moduleName }) => {
const dueDay = getDateOnlyMarkdownString(

View File

@@ -1,5 +1,4 @@
"use client";
import { useAssignmentsQueries } from "@/hooks/localCourse/assignmentHooks";
import { usePagesQueries } from "@/hooks/localCourse/pageHooks";
import { IModuleItem } from "@/models/local/IModuleItem";
import {
@@ -13,13 +12,17 @@ import NewItemForm from "./NewItemForm";
import { ModuleCanvasStatus } from "./ModuleCanvasStatus";
import ClientOnly from "@/components/ClientOnly";
import ExpandIcon from "../../../../components/icons/ExpandIcon";
import { DraggableItem, useDraggingContext } from "../context/drag/draggingContext";
import {
DraggableItem,
useDraggingContext,
} from "../context/drag/draggingContext";
import Link from "next/link";
import { getModuleItemUrl } from "@/services/urlUtils";
import { useCourseContext } from "../context/courseContext";
import { Expandable } from "../../../../components/Expandable";
import { useDragStyleContext } from "../context/drag/dragStyleContext";
import { useItemsQueries } from "@/hooks/localCourse/courseItemHooks";
import { useAssignmentsQuery } from "@/hooks/localCourse/assignmentHooks";
export default function ExpandableModule({
moduleName,
@@ -28,7 +31,7 @@ export default function ExpandableModule({
}) {
const { itemDropOnModule } = useDraggingContext();
const { data: assignments } = useAssignmentsQueries(moduleName);
const [assignments] = useAssignmentsQuery(moduleName);
const { data: quizzes } = useItemsQueries(moduleName, "Quiz");
const { data: pages } = usePagesQueries(moduleName);
const modal = useModal();

View File

@@ -14,7 +14,9 @@ import {
getDateFromString,
getDateFromStringOrThrow,
} from "@/models/local/timeUtils";
import { trpc } from "@/services/trpc/utils";
import React, { useState } from "react";
import { useCourseContext } from "../context/courseContext";
export default function NewItemForm({
moduleName: defaultModuleName,
@@ -26,10 +28,15 @@ export default function NewItemForm({
onCreate?: () => void;
}) {
const { data: settings } = useLocalCourseSettingsQuery();
const { courseName } = useCourseContext();
const { data: modules } = useModuleNamesQuery();
const [type, setType] = useState<"Assignment" | "Quiz" | "Page">(
"Assignment"
);
const assignmentCreationMutation = useCreateAssignmentMutation({
courseName,
moduleName: defaultModuleName ?? "",
});
const [moduleName, setModuleName] = useState<string | undefined>(
defaultModuleName
@@ -50,12 +57,13 @@ export default function NewItemForm({
const [assignmentGroup, setAssignmentGroup] =
useState<LocalAssignmentGroup>();
const createAssignment = useCreateAssignmentMutation();
const createPage = useCreatePageMutation();
const createQuiz = useCreateItemMutation("Quiz");
const isPending =
createAssignment.isPending || createPage.isPending || createQuiz.isPending;
assignmentCreationMutation.isPending ||
createPage.isPending ||
createQuiz.isPending;
return (
<form
@@ -84,8 +92,22 @@ export default function NewItemForm({
return;
}
if (type === "Assignment") {
createAssignment.mutate({
item: {
// createAssignment.mutate({
// item: {
// name,
// description: "",
// localAssignmentGroupName: assignmentGroup?.name ?? "",
// dueAt,
// lockAt,
// submissionTypes: settings.defaultAssignmentSubmissionTypes,
// allowedFileUploadExtensions: settings.defaultFileUploadTypes,
// rubric: [],
// },
// moduleName: moduleName,
// itemName: name,
// });
assignmentCreationMutation.mutate({
assignment: {
name,
description: "",
localAssignmentGroupName: assignmentGroup?.name ?? "",
@@ -96,7 +118,8 @@ export default function NewItemForm({
rubric: [],
},
moduleName: moduleName,
itemName: name,
assignmentName: name,
courseName,
});
} else if (type === "Quiz") {
createQuiz.mutate({

View File

@@ -27,7 +27,7 @@ export default function EditAssignment({
const router = useRouter();
const { courseName } = useCourseContext();
const { data: settings } = useLocalCourseSettingsQuery();
const { data: assignment } = useAssignmentQuery(moduleName, assignmentName);
const [assignment] = useAssignmentQuery(moduleName, assignmentName);
const updateAssignment = useUpdateAssignmentMutation();
const [assignmentText, setAssignmentText] = useState(

View File

@@ -12,6 +12,10 @@ import { canvasPageService } from "@/services/canvas/canvasPageService";
import { canvasQuizKeys } from "./canvas/canvasQuizHooks";
import { canvasPageKeys } from "./canvas/canvasPageHooks";
import { getLecturesQueryConfig } from "./localCourse/lectureHooks";
import { createServerSideHelpers } from "@trpc/react-query/server";
import { appRouter } from "@/services/trpc/router/app";
import superjson from "superjson";
import { createContext } from "@/services/trpc/context";
// https://tanstack.com/query/latest/docs/framework/react/guides/ssr
export const hydrateCourses = async (queryClient: QueryClient) => {
@@ -32,10 +36,27 @@ export const hydrateCourse = async (
queryClient: QueryClient,
courseSettings: LocalCourseSettings
) => {
const trpcHelpers = createServerSideHelpers({
router: appRouter,
ctx: createContext(),
transformer: superjson,
});
const courseName = courseSettings.name;
const moduleNames = await fileStorageService.modules.getModuleNames(
courseName
);
await Promise.all(
moduleNames.map(
async (moduleName) =>
await trpcHelpers.assignment.getAllAssignments.fetch({
courseName,
moduleName,
})
)
);
const modulesData = await Promise.all(
moduleNames.map((moduleName) => loadAllModuleData(courseName, moduleName))
);
@@ -77,36 +98,36 @@ export const hydrateCanvasCourse = async (
};
const loadAllModuleData = async (courseName: string, moduleName: string) => {
const [assignmentNames, pages, quizzes] = await Promise.all([
await fileStorageService.assignments.getAssignmentNames(
courseName,
moduleName
),
const [pages, quizzes] = await Promise.all([
// await fileStorageService.assignments.getAssignmentNames(
// courseName,
// moduleName
// ),
await fileStorageService.pages.getPages(courseName, moduleName),
await fileStorageService.quizzes.getQuizzes(courseName, moduleName),
]);
const [assignments] = await Promise.all([
await Promise.all(
assignmentNames.map(async (assignmentName) => {
try {
return await fileStorageService.assignments.getAssignment(
courseName,
moduleName,
assignmentName
);
} catch (error) {
console.error(`Error fetching assignment: ${assignmentName}`, error);
return null; // or any other placeholder value
}
})
),
]);
// const [assignments] = await Promise.all([
// await Promise.all(
// assignmentNames.map(async (assignmentName) => {
// try {
// return await fileStorageService.assignments.getAssignment(
// courseName,
// moduleName,
// assignmentName
// );
// } catch (error) {
// console.error(`Error fetching assignment: ${assignmentName}`, error);
// return null; // or any other placeholder value
// }
// })
// ),
// ]);
const assignmentsLoaded = assignments.filter((a) => a !== null);
// const assignmentsLoaded = assignments.filter((a) => a !== null);
return {
moduleName,
assignments: assignmentsLoaded,
// assignments: assignmentsLoaded,
quizzes,
pages,
};

View File

@@ -1,38 +1,65 @@
"use client";
import { trpc } from "@/services/trpc/utils";
import {
getAllItemsQueryConfig,
getItemQueryConfig,
useCreateItemMutation,
useDeleteItemMutation,
useItemQuery,
useItemsQueries,
useUpdateItemMutation,
} from "./courseItemHooks";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
export const getAllAssignmentsQueryConfig = (
courseName: string,
moduleName: string
) => getAllItemsQueryConfig(courseName, moduleName, "Assignment");
// export const getAllAssignmentsQueryConfig = (
// courseName: string,
// moduleName: string
// ) => getAllItemsQueryConfig(courseName, moduleName, "Assignment");
export const getAssignmentQueryConfig = (
courseName: string,
moduleName: string,
assignmentName: string
) => getItemQueryConfig(courseName, moduleName, assignmentName, "Assignment");
// export const getAssignmentQueryConfig = (
// courseName: string,
// moduleName: string,
// assignmentName: string
// ) => getItemQueryConfig(courseName, moduleName, assignmentName, "Assignment");
export const useAssignmentQuery = (
moduleName: string,
assignmentName: string
) => useItemQuery(moduleName, assignmentName, "Assignment");
) => {
const { courseName } = useCourseContext();
return trpc.assignment.getAssignment.useSuspenseQuery({
moduleName,
courseName,
assignmentName,
});
};
export const useAssignmentsQueries = (moduleName: string) =>
useItemsQueries(moduleName, "Assignment");
export const useAssignmentsQuery = (moduleName: string) => {
const { courseName } = useCourseContext();
return trpc.assignment.getAllAssignments.useSuspenseQuery({
moduleName,
courseName,
});
};
// useItemsQueries(moduleName, "Assignment");
export const useUpdateAssignmentMutation = () =>
useUpdateItemMutation("Assignment");
export const useCreateAssignmentMutation = () =>
useCreateItemMutation("Assignment");
export const useCreateAssignmentMutation = ({
courseName,
moduleName,
}: {
courseName: string;
moduleName: string;
}) => {
const utils = trpc.useUtils();
return trpc.assignment.createAssignment.useMutation({
onSuccess: () => {
utils.assignment.getAllAssignments.invalidate({ courseName, moduleName });
},
});
};
// useCreateItemMutation("Assignment");
export const useDeleteAssignmentMutation = () =>
useDeleteItemMutation("Assignment");

View File

@@ -6,12 +6,12 @@ import {
useSuspenseQuery,
} from "@tanstack/react-query";
import { localCourseKeys } from "./localCourseKeys";
import { getAllAssignmentsQueryConfig } from "./assignmentHooks";
import { getAllItemsQueryConfig } from "./courseItemHooks";
import {
createModuleOnServer,
getModuleNamesFromServer,
} from "./localCourseModuleServerActions";
import { trpc } from "@/services/trpc/utils";
export const useModuleNamesQuery = () => {
const { courseName } = useCourseContext();
@@ -42,21 +42,31 @@ export const useAllCourseDataQuery = () => {
const { courseName } = useCourseContext();
const { data: moduleNames } = useModuleNamesQuery();
const { data: assignmentsAndModules } = useSuspenseQueries({
queries: moduleNames.map((moduleName) =>
getAllAssignmentsQueryConfig(courseName, moduleName)
),
combine: (results) => ({
data: results.flatMap((r, i) =>
r.data.map((assignment) => ({
moduleName: moduleNames[i],
assignment,
}))
),
pending: results.some((r) => r.isPending),
}),
const [assignments] = trpc.useSuspenseQueries((t) =>
moduleNames.map((moduleName) =>
t.assignment.getAllAssignments({ courseName, moduleName })
)
);
const assignmentsAndModules = moduleNames.flatMap((moduleName, index) => {
return assignments[index].map((assignment) => ({ moduleName, assignment }));
});
// const { data: assignmentsAndModules } = useSuspenseQueries({
// queries: moduleNames.map((moduleName) =>
// getAllAssignmentsQueryConfig(courseName, moduleName)
// ),
// combine: (results) => ({
// data: results.flatMap((r, i) =>
// r.data.map((assignment) => ({
// moduleName: moduleNames[i],
// assignment,
// }))
// ),
// pending: results.some((r) => r.isPending),
// }),
// });
const { data: quizzesAndModules } = useSuspenseQueries({
queries: moduleNames.map((moduleName) =>
getAllItemsQueryConfig(courseName, moduleName, "Quiz")

View File

@@ -1,3 +1,4 @@
import { z } from "zod";
export enum AssignmentSubmissionType {
ONLINE_TEXT_ENTRY = "online_text_entry",
ONLINE_UPLOAD = "online_upload",
@@ -6,6 +7,14 @@ export enum AssignmentSubmissionType {
ONLINE_URL = "online_url",
NONE = "none",
}
export const zodAssignmentSubmissionType = z.enum([
AssignmentSubmissionType.ONLINE_TEXT_ENTRY,
AssignmentSubmissionType.ONLINE_UPLOAD,
AssignmentSubmissionType.ONLINE_QUIZ,
AssignmentSubmissionType.DISCUSSION_TOPIC,
AssignmentSubmissionType.ONLINE_URL,
AssignmentSubmissionType.NONE,
]);
export const AssignmentSubmissionTypeList: AssignmentSubmissionType[] = [
AssignmentSubmissionType.ONLINE_TEXT_ENTRY,

View File

@@ -1,8 +1,12 @@
import { IModuleItem } from "../IModuleItem";
import { AssignmentSubmissionType } from "./assignmentSubmissionType";
import { RubricItem } from "./rubricItem";
import {
AssignmentSubmissionType,
zodAssignmentSubmissionType,
} from "./assignmentSubmissionType";
import { RubricItem, zodRubricItem } from "./rubricItem";
import { assignmentMarkdownParser } from "./utils/assignmentMarkdownParser";
import { assignmentMarkdownSerializer } from "./utils/assignmentMarkdownSerializer";
import { z } from "zod";
export interface LocalAssignment extends IModuleItem {
name: string;
@@ -15,6 +19,16 @@ export interface LocalAssignment extends IModuleItem {
rubric: RubricItem[];
}
export const zodLocalAssignment = z.object({
name: z.string(),
description: z.string(),
lockAt: z.string().optional(),
dueAt: z.string(),
localAssignmentGroupName: z.string(),
submissionTypes: zodAssignmentSubmissionType.array(),
allowedFileUploadExtensions: z.string().array(),
rubric: zodRubricItem.array(),
});
export const localAssignmentMarkdown = {
parseMarkdown: assignmentMarkdownParser.parseMarkdown,

View File

@@ -1,10 +1,16 @@
import { z } from "zod";
export interface RubricItem {
label: string;
points: number;
}
export const zodRubricItem = z.object({
label: z.string(),
points: z.number(),
});
export const rubricItemIsExtraCredit = (item: RubricItem) => {
const extraCredit = '(extra credit)';
const extraCredit = "(extra credit)";
return item.label.toLowerCase().includes(extraCredit.toLowerCase());
}
};

View File

@@ -1,6 +1,7 @@
import { createContext } from "../context";
import publicProcedure from "../procedures/public";
import { createCallerFactory, mergeRouters, router } from "../trpc";
import { assignmentRouter } from "./assignmentRouter";
export const helloRouter = router({
sayHello: publicProcedure.query(() => {
@@ -10,8 +11,10 @@ export const helloRouter = router({
}),
});
export const appRouter = mergeRouters(helloRouter);
export const appRouter = router({
hello: helloRouter,
assignment: assignmentRouter,
});
export const createCaller = createCallerFactory(appRouter);

View File

@@ -2,8 +2,9 @@ import publicProcedure from "../procedures/public";
import { z } from "zod";
import { router } from "../trpc";
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
import { zodLocalAssignment } from "@/models/local/assignment/localAssignment";
export const courseItemRouter = router({
export const assignmentRouter = router({
getAssignment: publicProcedure
.input(
z.object({
@@ -32,4 +33,28 @@ export const courseItemRouter = router({
moduleName
);
}),
createAssignment: publicProcedure
.input(
z.object({
courseName: z.string(),
moduleName: z.string(),
assignmentName: z.string(),
assignment: zodLocalAssignment,
})
)
.mutation(
async ({
input: { courseName, moduleName, assignmentName, assignment },
ctx,
}) => {
await fileStorageService.assignments.updateOrCreateAssignment({
courseName,
moduleName,
assignmentName,
assignment,
});
ctx;
}
),
});