updating settings

This commit is contained in:
2024-09-10 14:20:46 -06:00
parent 5d82102a75
commit ff520233d6
10 changed files with 238 additions and 25 deletions

View File

@@ -12,3 +12,14 @@ export const GET = async (
const settings = await fileStorageService.getCourseSettings(courseName); const settings = await fileStorageService.getCourseSettings(courseName);
return Response.json(settings); return Response.json(settings);
}); });
export const PUT = async (
request: Request,
{ params: { courseName } }: { params: { courseName: string } }
) =>
await withErrorHandling(async () => {
const settings = await request.json();
await fileStorageService.updateCourseSettings(courseName, settings);
return Response.json({});
});

View File

@@ -0,0 +1,28 @@
"use client";
import {
useLocalCourseSettingsQuery,
useUpdateLocalCourseSettingsMutation,
} from "@/hooks/localCourse/localCoursesHooks";
import { TimePicker } from "../../../../components/TimePicker";
export default function DefaultDueTime() {
const { data: settings } = useLocalCourseSettingsQuery();
const updateSettings = useUpdateLocalCourseSettingsMutation();
return (
<div className="border w-fit p-3 m-3 rounded-md">
<div className="text-center">Default Assignment Due Time</div>
<hr className="m-1 p-0" />
<TimePicker
time={settings.defaultDueTime}
setChosenTime={(simpleTime) => {
console.log(simpleTime);
updateSettings.mutate({
...settings,
defaultDueTime: simpleTime,
});
}}
/>
</div>
);
}

View File

@@ -0,0 +1,8 @@
"use client"
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import React from "react";
export default function SettingsHeader() {
const { data: settings } = useLocalCourseSettingsQuery();
return <div>Settings for {settings.name}</div>;
}

View File

@@ -0,0 +1,16 @@
"use client";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { getDateOnlyMarkdownString } from "@/models/local/timeUtils";
import React from "react";
export default function StartAndEndDate() {
const { data: settings } = useLocalCourseSettingsQuery();
const startDate = new Date(settings.startDate);
const endDate = new Date(settings.endDate);
return (
<div>
<div>Start: {getDateOnlyMarkdownString(startDate)}</div>
<div>End: {getDateOnlyMarkdownString(endDate)}</div>
</div>
);
}

View File

@@ -1,7 +1,15 @@
import React from 'react' import React from "react";
import { useCourseContext } from "../context/courseContext";
import StartAndEndDate from "./StartAndEndDate";
import SettingsHeader from "./SettingsHeader";
import DefaultDueTime from "./DefaultDueTime";
export default function page() { export default function page() {
return ( return (
<div>page</div> <div>
) <SettingsHeader />
<StartAndEndDate />
<DefaultDueTime />
</div>
);
} }

View File

@@ -59,7 +59,7 @@ ol {
padding-left: 1.5rem; padding-left: 1.5rem;
} }
hr { hr {
@apply border-t border-gray-200 my-4; @apply border-t border-gray-500 my-4;
} }
blockquote { blockquote {
@@ -76,3 +76,9 @@ p {
button { button {
@apply bg-blue-900 hover:bg-blue-700 text-blue-50 font-bold py-1 px-3 rounded transition-all duration-200; @apply bg-blue-900 hover:bg-blue-700 text-blue-50 font-bold py-1 px-3 rounded transition-all duration-200;
} }
select {
@apply block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm sm:text-sm;
@apply focus:outline-none focus:ring-blue-500 focus:border-blue-500 ;
@apply bg-slate-800;
}

View File

@@ -0,0 +1,99 @@
"use client";
import { SimpleTimeOnly } from "@/models/local/localCourse";
import { FC, useState, useEffect } from "react";
export const TimePicker: FC<{
setChosenTime: (simpleTime: SimpleTimeOnly) => void;
time: SimpleTimeOnly;
}> = ({ setChosenTime, time }) => {
const adjustedHour = time.hour % 12 === 0 ? 12 : time.hour % 12;
const partOfDay = time.hour < 12 ? "AM" : "PM";
// useEffect(() => {
// const newtime = {
// hour: partOfDay === "PM" ? hour + 12 : hour,
// minute: minute,
// };
// if (
// newtime.hour != startingTime.hour ||
// newtime.minute != startingTime.minute
// ) {
// setChosenTime(newtime);
// }
// }, [
// hour,
// minute,
// partOfDay,
// setChosenTime,
// startingTime.hour,
// startingTime.minute,
// ]);
return (
<>
<form
onSubmit={(e) => e.preventDefault()}
className="flex flex-row gap-3"
>
<div className="">
<label>
Hour
<select
value={adjustedHour}
onChange={(e) => {
const newHours = parseInt(e.target.value);
setChosenTime({
...time,
hour: partOfDay === "PM" ? newHours + 12 : newHours,
});
}}
>
{Array.from({ length: 12 }, (_, i) => i).map((o) => (
<option key={o.toString()}>{o}</option>
))}
</select>
</label>
</div>
<div className="">
<label>
Minute
<select
value={time.minute}
onChange={(e) => {
const newMinute = parseInt(e.target.value);
setChosenTime({
...time,
minute: newMinute,
});
}}
>
{[0, 15, 30, 45, 59].map((o) => (
<option key={o.toString()}>{o}</option>
))}
</select>
</label>
</div>
<div className="">
<label>
Part of Day
<select
value={partOfDay}
onChange={(e) => {
const newPartOfDay = e.target.value;
setChosenTime({
...time,
hour: newPartOfDay === "PM" ? time.hour + 12 : time.hour,
});
}}
>
{["AM", "PM"].map((o) => (
<option key={o.toString()}>{o}</option>
))}
</select>
</label>
</div>
</form>
</>
);
};

View File

@@ -1,7 +1,9 @@
"use client"; "use client";
import { LocalCourseSettings } from "@/models/local/localCourse"; import { LocalCourseSettings } from "@/models/local/localCourse";
import { import {
useMutation,
useQueries, useQueries,
useQueryClient,
useSuspenseQueries, useSuspenseQueries,
useSuspenseQuery, useSuspenseQuery,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
@@ -14,9 +16,18 @@ import {
useAssignmentNamesQuery, useAssignmentNamesQuery,
useAssignmentsQueries, useAssignmentsQueries,
} from "./assignmentHooks"; } from "./assignmentHooks";
import { getPageNamesQueryConfig, getPageQueryConfig, usePageNamesQuery, usePagesQueries } from "./pageHooks"; import {
import { getQuizNamesQueryConfig, getQuizQueryConfig, useQuizNamesQuery, useQuizzesQueries } from "./quizHooks"; getPageNamesQueryConfig,
import { useMemo } from "react"; getPageQueryConfig,
usePageNamesQuery,
usePagesQueries,
} from "./pageHooks";
import {
getQuizNamesQueryConfig,
getQuizQueryConfig,
useQuizNamesQuery,
useQuizzesQueries,
} from "./quizHooks";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
export const useLocalCourseNamesQuery = () => export const useLocalCourseNamesQuery = () =>
@@ -41,6 +52,22 @@ export const useLocalCourseSettingsQuery = () => {
}); });
}; };
export const useUpdateLocalCourseSettingsMutation = () => {
const { courseName } = useCourseContext();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (updatedSettings: LocalCourseSettings) => {
const url = `/api/courses/${courseName}/settings`;
await axiosClient.put(url, updatedSettings);
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: localCourseKeys.settings(courseName),
});
},
});
};
export const useModuleNamesQuery = () => { export const useModuleNamesQuery = () => {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
return useSuspenseQuery({ return useSuspenseQuery({
@@ -132,8 +159,7 @@ export const useAllCourseDataQuery = () => {
}); });
const { data: quizzesAndModules } = useSuspenseQueries({ const { data: quizzesAndModules } = useSuspenseQueries({
queries: quizNamesAndModules.map( queries: quizNamesAndModules.map(({ moduleName, quizName }, i) =>
({ moduleName, quizName }, i) =>
getQuizQueryConfig(courseName, moduleName, quizName) getQuizQueryConfig(courseName, moduleName, quizName)
), ),
combine: (results) => ({ combine: (results) => ({
@@ -161,8 +187,7 @@ export const useAllCourseDataQuery = () => {
}); });
const { data: pagesAndModules } = useSuspenseQueries({ const { data: pagesAndModules } = useSuspenseQueries({
queries: pageNamesAndModules.map( queries: pageNamesAndModules.map(({ moduleName, pageName }, i) =>
({ moduleName, pageName }, i) =>
getPageQueryConfig(courseName, moduleName, pageName) getPageQueryConfig(courseName, moduleName, pageName)
), ),
combine: (results) => ({ combine: (results) => ({
@@ -174,7 +199,6 @@ export const useAllCourseDataQuery = () => {
}), }),
}); });
return { assignmentsAndModules, quizzesAndModules, pagesAndModules }; return { assignmentsAndModules, quizzesAndModules, pagesAndModules };
}; };

View File

@@ -34,26 +34,24 @@ export enum DayOfWeek {
export const localCourseYamlUtils = { export const localCourseYamlUtils = {
parseSettingYaml: (settingsString: string): LocalCourseSettings => { parseSettingYaml: (settingsString: string): LocalCourseSettings => {
const settings = parse(settingsString); const settings = parse(settingsString);
return lowercaseFirstLetter(settings) return lowercaseFirstLetter(settings);
}, },
settingsToYaml: (settings: LocalCourseSettings) => { settingsToYaml: (settings: Omit<LocalCourseSettings, "name">) => {
return stringify(settings); return stringify(settings);
}, },
}; };
function lowercaseFirstLetter<T>(obj: T): T { function lowercaseFirstLetter<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') if (obj === null || typeof obj !== "object") return obj as T;
return obj as T;
if (Array.isArray(obj)) if (Array.isArray(obj)) return obj.map(lowercaseFirstLetter) as unknown as T;
return obj.map(lowercaseFirstLetter) as unknown as T;
const result: Record<string, any> = {}; const result: Record<string, any> = {};
Object.keys(obj).forEach((key) => { Object.keys(obj).forEach((key) => {
const value = (obj as Record<string, any>)[key]; const value = (obj as Record<string, any>)[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") {
result[newKey] = lowercaseFirstLetter(value); result[newKey] = lowercaseFirstLetter(value);
} else { } else {
result[newKey] = value; result[newKey] = value;

View File

@@ -75,6 +75,21 @@ export const fileStorageService = {
return { ...settings, name: folderName }; return { ...settings, name: folderName };
}, },
async updateCourseSettings(
courseName: string,
settings: LocalCourseSettings
) {
const courseDirectory = path.join(basePath, courseName);
const settingsPath = path.join(courseDirectory, "settings.yml");
const { name, ...settingsWithoutName } = settings;
const settingsMarkdown =
localCourseYamlUtils.settingsToYaml(settingsWithoutName);
console.log(`Saving settings ${settingsPath}`);
await fs.writeFile(settingsPath, settingsMarkdown);
},
async getModuleNames(courseName: string) { async getModuleNames(courseName: string) {
const courseDirectory = path.join(basePath, courseName); const courseDirectory = path.join(basePath, courseName);
const moduleDirectories = await fs.readdir(courseDirectory, { const moduleDirectories = await fs.readdir(courseDirectory, {