diff --git a/nextjs/src/app/course/[courseName]/calendar/calendarMonthUtils.ts b/nextjs/src/app/course/[courseName]/calendar/calendarMonthUtils.ts index 6b08958..65ffdee 100644 --- a/nextjs/src/app/course/[courseName]/calendar/calendarMonthUtils.ts +++ b/nextjs/src/app/course/[courseName]/calendar/calendarMonthUtils.ts @@ -30,24 +30,32 @@ function createCalendarMonth(year: number, month: number): CalendarMonthModel { let currentDay = 1; const firstDayOfMonth = new Date(year, month - 1, 1).getDay(); - const daysByWeek = Array.from({ length: weeksNumber }).map((_, weekIndex) => - Array.from({ length: 7 }).map((_, dayIndex) => { - if (weekIndex === 0 && dayIndex < firstDayOfMonth) { - return dateToMarkdownString( - new Date(year, month - 1, dayIndex - firstDayOfMonth + 1, 12, 0, 0) - ); - } else if (currentDay <= daysInMonth) { - return dateToMarkdownString( - new Date(year, month - 1, currentDay++, 12, 0, 0) - ); - } else { - currentDay++; - return dateToMarkdownString( - new Date(year, month, currentDay - daysInMonth - 1, 12, 0, 0) - ); - } - }) - ); + const daysByWeek = Array.from({ length: weeksNumber }) + .map((_, weekIndex) => + Array.from({ length: 7 }).map((_, dayIndex) => { + if (weekIndex === 0 && dayIndex < firstDayOfMonth) { + return dateToMarkdownString( + new Date(year, month - 1, dayIndex - firstDayOfMonth + 1, 12, 0, 0) + ); + } else if (currentDay <= daysInMonth) { + return dateToMarkdownString( + new Date(year, month - 1, currentDay++, 12, 0, 0) + ); + } else { + currentDay++; + return dateToMarkdownString( + new Date(year, month, currentDay - daysInMonth - 1, 12, 0, 0) + ); + } + }) + ) + .filter((week) => { + const lastDate = getDateFromStringOrThrow( + week.at(-1)!, + "filtering out last week of month" + ); + return lastDate.getMonth() <= month - 1; + }); const weeks = daysByWeek.map((week) => week.map((day) => diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx index b1fee39..fcbbb00 100644 --- a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx @@ -33,7 +33,6 @@ export default function EditAssignment({ const [assignmentText, setAssignmentText] = useState( localAssignmentMarkdown.toMarkdown(assignment) ); - console.log("assignment text render"); const [error, setError] = useState(""); const [showHelp, setShowHelp] = useState(false); diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/QuizPreview.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/QuizPreview.tsx index 363f472..8d08cd9 100644 --- a/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/QuizPreview.tsx +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/QuizPreview.tsx @@ -22,6 +22,12 @@ export default function QuizPreview({
Name
{quiz.name}
+
+
Points
+
+ {quiz.questions.reduce((sum, question) => sum + question.points, 0)} +
+
Due Date
{quiz.dueAt}
@@ -46,9 +52,12 @@ export default function QuizPreview({
Assignment Group Name
{quiz.localAssignmentGroupName}
-
- {quiz.description} -
+
{quiz.questions.map((question, i) => ( diff --git a/nextjs/src/app/course/[courseName]/settings/AssignmentGroupManagement.tsx b/nextjs/src/app/course/[courseName]/settings/AssignmentGroupManagement.tsx index 5ffc339..88f37ae 100644 --- a/nextjs/src/app/course/[courseName]/settings/AssignmentGroupManagement.tsx +++ b/nextjs/src/app/course/[courseName]/settings/AssignmentGroupManagement.tsx @@ -70,7 +70,7 @@ export default function AssignmentGroupManagement() { + +
+ + ); +} diff --git a/nextjs/src/app/course/[courseName]/settings/SubmissionDefaults.tsx b/nextjs/src/app/course/[courseName]/settings/SubmissionDefaults.tsx new file mode 100644 index 0000000..8593c2c --- /dev/null +++ b/nextjs/src/app/course/[courseName]/settings/SubmissionDefaults.tsx @@ -0,0 +1,76 @@ +"use client"; +import SelectInput from "@/components/form/SelectInput"; +import { + useLocalCourseSettingsQuery, + useUpdateLocalCourseSettingsMutation, +} from "@/hooks/localCourse/localCoursesHooks"; +import { + AssignmentSubmissionType, + AssignmentSubmissionTypeList, +} from "@/models/local/assignment/assignmentSubmissionType"; +import React, { useEffect, useState } from "react"; + +export default function SubmissionDefaults() { + const { data: settings } = useLocalCourseSettingsQuery(); + const [defaultSubmissionTypes, setDefaultSubmissionTypes] = useState< + AssignmentSubmissionType[] + >(settings.defaultAssignmentSubmissionTypes); + const updateSettings = useUpdateLocalCourseSettingsMutation(); + + useEffect(() => { + if ( + JSON.stringify(settings.defaultAssignmentSubmissionTypes) !== + JSON.stringify(defaultSubmissionTypes) + ) { + updateSettings.mutate({ + ...settings, + defaultAssignmentSubmissionTypes: defaultSubmissionTypes, + }); + } + }, [defaultSubmissionTypes, settings, updateSettings]); + + + + return ( +
+
Default Assignment Submission Type
+ + {defaultSubmissionTypes.map((type, index) => ( +
+ { + if (newType) + setDefaultSubmissionTypes((oldTypes) => + oldTypes.map((t, i) => (i === index ? newType : t)) + ); + }} + label={""} + options={AssignmentSubmissionTypeList} + getOptionName={(t) => t} + /> +
+ ))} +
+ + +
+
+ ); +} diff --git a/nextjs/src/app/course/[courseName]/settings/page.tsx b/nextjs/src/app/course/[courseName]/settings/page.tsx index 9feb7d8..ba23a46 100644 --- a/nextjs/src/app/course/[courseName]/settings/page.tsx +++ b/nextjs/src/app/course/[courseName]/settings/page.tsx @@ -5,6 +5,8 @@ import SettingsHeader from "./SettingsHeader"; import DefaultDueTime from "./DefaultDueTime"; import DaysOfWeekSettings from "./DaysOfWeekSettings"; import AssignmentGroupManagement from "./AssignmentGroupManagement"; +import SubmissionDefaults from "./SubmissionDefaults"; +import DefaultFileUploadTypes from "./DefaultFileUploadTypes"; export default function page() { return ( @@ -13,6 +15,8 @@ export default function page() { + + diff --git a/nextjs/src/app/newCourse/NewCourseForm.tsx b/nextjs/src/app/newCourse/NewCourseForm.tsx index af7a1ac..517b27f 100644 --- a/nextjs/src/app/newCourse/NewCourseForm.tsx +++ b/nextjs/src/app/newCourse/NewCourseForm.tsx @@ -12,6 +12,7 @@ import { import { useEmptyDirectoriesQuery } from "@/hooks/localCourse/storageDirectoryHooks"; import { CanvasCourseModel } from "@/models/canvas/courses/canvasCourseModel"; import { CanvasEnrollmentTermModel } from "@/models/canvas/enrollmentTerms/canvasEnrollmentTermModel"; +import { AssignmentSubmissionType } from "@/models/local/assignment/assignmentSubmissionType"; import { DayOfWeek } from "@/models/local/localCourse"; import { getCourseUrl } from "@/services/urlUtils"; import { useRouter } from "next/navigation"; @@ -89,6 +90,10 @@ export default function NewCourseForm() { startDate: selectedTerm.start_at ?? "", endDate: selectedTerm.end_at ?? "", defaultDueTime: { hour: 23, minute: 59 }, + defaultAssignmentSubmissionTypes: [ + AssignmentSubmissionType.ONLINE_TEXT_ENTRY, + AssignmentSubmissionType.ONLINE_UPLOAD, + ], }, }) .then(() => { diff --git a/nextjs/src/components/form/DayOfWeekInput.tsx b/nextjs/src/components/form/DayOfWeekInput.tsx index 016818a..01c62f4 100644 --- a/nextjs/src/components/form/DayOfWeekInput.tsx +++ b/nextjs/src/components/form/DayOfWeekInput.tsx @@ -17,8 +17,8 @@ export function DayOfWeekInput({ key={day} className={ hasDay - ? "bg-blue-300 text-blue-950 border-blue-500 border" - : "bg-slate-900 border-blue-900 border " + ? "" + : "unstyled btn-outline " } onClick={() => updateSettings(day)} > diff --git a/nextjs/src/models/local/assignment/assignmentSubmissionType.ts b/nextjs/src/models/local/assignment/assignmentSubmissionType.ts index 79aa64a..aaa5c1b 100644 --- a/nextjs/src/models/local/assignment/assignmentSubmissionType.ts +++ b/nextjs/src/models/local/assignment/assignmentSubmissionType.ts @@ -6,3 +6,12 @@ export enum AssignmentSubmissionType { ONLINE_URL = "online_url", NONE = "none", } + +export const AssignmentSubmissionTypeList: AssignmentSubmissionType[] = [ + AssignmentSubmissionType.ONLINE_TEXT_ENTRY, + AssignmentSubmissionType.ONLINE_UPLOAD, + AssignmentSubmissionType.ONLINE_QUIZ, + AssignmentSubmissionType.DISCUSSION_TOPIC, + AssignmentSubmissionType.ONLINE_URL, + AssignmentSubmissionType.NONE, +] as const; diff --git a/nextjs/src/models/local/localCourse.ts b/nextjs/src/models/local/localCourse.ts index fc317af..d326e0b 100644 --- a/nextjs/src/models/local/localCourse.ts +++ b/nextjs/src/models/local/localCourse.ts @@ -1,3 +1,4 @@ +import { AssignmentSubmissionType } from "./assignment/assignmentSubmissionType"; import { LocalAssignmentGroup } from "./assignment/localAssignmentGroup"; import { LocalModule } from "./localModules"; import { parse, stringify } from "yaml"; @@ -20,6 +21,8 @@ export interface LocalCourseSettings { startDate: string; endDate: string; defaultDueTime: SimpleTimeOnly; + defaultAssignmentSubmissionTypes: AssignmentSubmissionType[]; + defaultFileUploadTypes: string[]; } export enum DayOfWeek { diff --git a/nextjs/src/services/fileStorage/settingsFileStorageService.ts b/nextjs/src/services/fileStorage/settingsFileStorageService.ts index 38141e6..e9dba98 100644 --- a/nextjs/src/services/fileStorage/settingsFileStorageService.ts +++ b/nextjs/src/services/fileStorage/settingsFileStorageService.ts @@ -9,6 +9,7 @@ import { directoryOrFileExists, getCourseNames, } from "./utils/fileSystemUtils"; +import { AssignmentSubmissionType } from "@/models/local/assignment/assignmentSubmissionType"; const getCourseSettings = async ( courseName: string @@ -22,7 +23,24 @@ const getCourseSettings = async ( } const settingsString = await fs.readFile(settingsPath, "utf-8"); - const settings = localCourseYamlUtils.parseSettingYaml(settingsString); + + const settingsFromFile = + localCourseYamlUtils.parseSettingYaml(settingsString); + + const defaultSubmissionType = [ + AssignmentSubmissionType.ONLINE_TEXT_ENTRY, + AssignmentSubmissionType.ONLINE_UPLOAD, + ]; + const defaultFileUploadTypes = ["pdf", "jpg", "jpeg", "pdf"]; + + const settings: LocalCourseSettings = { + ...settingsFromFile, + defaultAssignmentSubmissionTypes: + settingsFromFile.defaultAssignmentSubmissionTypes || + defaultSubmissionType, + defaultFileUploadTypes: + settingsFromFile.defaultFileUploadTypes || defaultFileUploadTypes, + }; const folderName = path.basename(courseDirectory); return { ...settings, name: folderName }; @@ -54,4 +72,3 @@ export const settingsFileStorageService = { await fs.writeFile(settingsPath, settingsMarkdown); }, }; -