mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 07:38:33 -06:00
updating settings
This commit is contained in:
@@ -12,3 +12,14 @@ export const GET = async (
|
||||
const settings = await fileStorageService.getCourseSettings(courseName);
|
||||
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({});
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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() {
|
||||
return (
|
||||
<div>page</div>
|
||||
)
|
||||
<div>
|
||||
<SettingsHeader />
|
||||
<StartAndEndDate />
|
||||
<DefaultDueTime />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ ol {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
hr {
|
||||
@apply border-t border-gray-200 my-4;
|
||||
@apply border-t border-gray-500 my-4;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
@@ -76,3 +76,9 @@ p {
|
||||
button {
|
||||
@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;
|
||||
}
|
||||
|
||||
99
nextjs/src/components/TimePicker.tsx
Normal file
99
nextjs/src/components/TimePicker.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
"use client";
|
||||
import { LocalCourseSettings } from "@/models/local/localCourse";
|
||||
import {
|
||||
useMutation,
|
||||
useQueries,
|
||||
useQueryClient,
|
||||
useSuspenseQueries,
|
||||
useSuspenseQuery,
|
||||
} from "@tanstack/react-query";
|
||||
@@ -14,9 +16,18 @@ import {
|
||||
useAssignmentNamesQuery,
|
||||
useAssignmentsQueries,
|
||||
} from "./assignmentHooks";
|
||||
import { getPageNamesQueryConfig, getPageQueryConfig, usePageNamesQuery, usePagesQueries } from "./pageHooks";
|
||||
import { getQuizNamesQueryConfig, getQuizQueryConfig, useQuizNamesQuery, useQuizzesQueries } from "./quizHooks";
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
getPageNamesQueryConfig,
|
||||
getPageQueryConfig,
|
||||
usePageNamesQuery,
|
||||
usePagesQueries,
|
||||
} from "./pageHooks";
|
||||
import {
|
||||
getQuizNamesQueryConfig,
|
||||
getQuizQueryConfig,
|
||||
useQuizNamesQuery,
|
||||
useQuizzesQueries,
|
||||
} from "./quizHooks";
|
||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||
|
||||
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 = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
return useSuspenseQuery({
|
||||
@@ -116,7 +143,7 @@ export const useAllCourseDataQuery = () => {
|
||||
}),
|
||||
});
|
||||
|
||||
const {data: quizNamesAndModules } = useSuspenseQueries({
|
||||
const { data: quizNamesAndModules } = useSuspenseQueries({
|
||||
queries: moduleNames.map((moduleName) =>
|
||||
getQuizNamesQueryConfig(courseName, moduleName)
|
||||
),
|
||||
@@ -132,9 +159,8 @@ export const useAllCourseDataQuery = () => {
|
||||
});
|
||||
|
||||
const { data: quizzesAndModules } = useSuspenseQueries({
|
||||
queries: quizNamesAndModules.map(
|
||||
({ moduleName, quizName }, i) =>
|
||||
getQuizQueryConfig(courseName, moduleName, quizName)
|
||||
queries: quizNamesAndModules.map(({ moduleName, quizName }, i) =>
|
||||
getQuizQueryConfig(courseName, moduleName, quizName)
|
||||
),
|
||||
combine: (results) => ({
|
||||
data: results.flatMap((r, i) => ({
|
||||
@@ -145,7 +171,7 @@ export const useAllCourseDataQuery = () => {
|
||||
}),
|
||||
});
|
||||
|
||||
const {data: pageNamesAndModules } = useSuspenseQueries({
|
||||
const { data: pageNamesAndModules } = useSuspenseQueries({
|
||||
queries: moduleNames.map((moduleName) =>
|
||||
getPageNamesQueryConfig(courseName, moduleName)
|
||||
),
|
||||
@@ -161,9 +187,8 @@ export const useAllCourseDataQuery = () => {
|
||||
});
|
||||
|
||||
const { data: pagesAndModules } = useSuspenseQueries({
|
||||
queries: pageNamesAndModules.map(
|
||||
({ moduleName, pageName }, i) =>
|
||||
getPageQueryConfig(courseName, moduleName, pageName)
|
||||
queries: pageNamesAndModules.map(({ moduleName, pageName }, i) =>
|
||||
getPageQueryConfig(courseName, moduleName, pageName)
|
||||
),
|
||||
combine: (results) => ({
|
||||
data: results.flatMap((r, i) => ({
|
||||
@@ -174,8 +199,7 @@ export const useAllCourseDataQuery = () => {
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
return { assignmentsAndModules, quizzesAndModules, pagesAndModules };
|
||||
return { assignmentsAndModules, quizzesAndModules, pagesAndModules };
|
||||
};
|
||||
|
||||
// export const useUpdateCourseMutation = (courseName: string) => {
|
||||
|
||||
@@ -34,26 +34,24 @@ export enum DayOfWeek {
|
||||
export const localCourseYamlUtils = {
|
||||
parseSettingYaml: (settingsString: string): LocalCourseSettings => {
|
||||
const settings = parse(settingsString);
|
||||
return lowercaseFirstLetter(settings)
|
||||
return lowercaseFirstLetter(settings);
|
||||
},
|
||||
settingsToYaml: (settings: LocalCourseSettings) => {
|
||||
settingsToYaml: (settings: Omit<LocalCourseSettings, "name">) => {
|
||||
return stringify(settings);
|
||||
},
|
||||
};
|
||||
|
||||
function lowercaseFirstLetter<T>(obj: T): T {
|
||||
if (obj === null || typeof obj !== 'object')
|
||||
return obj as T;
|
||||
if (obj === null || typeof obj !== "object") return obj as 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> = {};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const value = (obj as Record<string, any>)[key];
|
||||
const newKey = key.charAt(0).toLowerCase() + key.slice(1);
|
||||
|
||||
if (value && typeof value === 'object') {
|
||||
if (value && typeof value === "object") {
|
||||
result[newKey] = lowercaseFirstLetter(value);
|
||||
} else {
|
||||
result[newKey] = value;
|
||||
@@ -61,4 +59,4 @@ function lowercaseFirstLetter<T>(obj: T): T {
|
||||
});
|
||||
|
||||
return result as T;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,21 @@ export const fileStorageService = {
|
||||
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) {
|
||||
const courseDirectory = path.join(basePath, courseName);
|
||||
const moduleDirectories = await fs.readdir(courseDirectory, {
|
||||
|
||||
Reference in New Issue
Block a user