mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 15:18:32 -06:00
pointer
This commit is contained in:
@@ -9,23 +9,15 @@ courses:
|
||||
name: UX
|
||||
- path: ./1425/2025-fall-alex/modules/
|
||||
name: "1425"
|
||||
- path: ./3840_Telemetry/2025_spring_alex/modules/
|
||||
name: Telem and Ops
|
||||
- path: ./4850_AdvancedFE/2026-spring-alex/modules
|
||||
name: Adv Frontend Spring
|
||||
- path: ./1400/2025_spring_alex/modules/
|
||||
name: 1400-old
|
||||
- path: ./1400/2026_spring_alex/modules
|
||||
name: "1400"
|
||||
- path: ./1405/2025_spring_alex/
|
||||
name: 1405 old
|
||||
- path: ./1405/2026_spring_alex
|
||||
name: "1405"
|
||||
- path: ./1810/2026-spring-alex/modules
|
||||
name: Web Intro Spring
|
||||
- path: ./3840_Telemetry/2026_spring_alex
|
||||
name: Telem and Ops New
|
||||
- path: ./4620_Distributed/2025Spring/modules/
|
||||
name: distributed-old
|
||||
- path: ./4620_Distributed/2026-spring-alex/modules
|
||||
name: Distributed
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from "@/features/local/course/localCourseSettings";
|
||||
import { useCourseListInTermQuery } from "@/features/canvas/hooks/canvasCourseHooks";
|
||||
import { useCanvasTermsQuery } from "@/features/canvas/hooks/canvasHooks";
|
||||
import { useDirectoryExistsQuery } from "@/features/local/utils/storageDirectoryHooks";
|
||||
|
||||
const sampleCompose = `services:
|
||||
canvas_manager:
|
||||
@@ -62,7 +63,6 @@ export default function AddNewCourseToGlobalSettingsForm() {
|
||||
const formIsComplete =
|
||||
selectedTerm && selectedCanvasCourse && selectedDirectory;
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ButtonSelect
|
||||
@@ -95,7 +95,12 @@ export default function AddNewCourseToGlobalSettingsForm() {
|
||||
disabled={!formIsComplete || createCourse.isPending}
|
||||
onClick={async () => {
|
||||
if (formIsComplete) {
|
||||
console.log("Creating course with settings:", selectedDirectory, "old course", courseToImport);
|
||||
console.log(
|
||||
"Creating course with settings:",
|
||||
selectedDirectory,
|
||||
"old course",
|
||||
courseToImport
|
||||
);
|
||||
const newSettings: LocalCourseSettings = courseToImport
|
||||
? {
|
||||
...courseToImport,
|
||||
@@ -145,11 +150,6 @@ export default function AddNewCourseToGlobalSettingsForm() {
|
||||
</button>
|
||||
</div>
|
||||
{createCourse.isPending && <Spinner />}
|
||||
|
||||
{/* <pre>
|
||||
<div>Example docker compose</div>
|
||||
<code className="language-yml">{sampleCompose}</code>
|
||||
</pre> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -185,6 +185,8 @@ function OtherSettings({
|
||||
useCourseListInTermQuery(selectedTerm.id);
|
||||
const { data: allSettings } = useLocalCoursesSettingsQuery();
|
||||
const [directory, setDirectory] = useState("./");
|
||||
const { data: directoryExists, isLoading: directoryExistsLoading } =
|
||||
useDirectoryExistsQuery(directory);
|
||||
|
||||
const populatedCanvasCourseIds = allSettings?.map((s) => s.canvasId) ?? [];
|
||||
const availableCourses =
|
||||
@@ -224,6 +226,15 @@ function OtherSettings({
|
||||
setLastTypedValue={setSelectedDirectory}
|
||||
label={"Storage Folder"}
|
||||
/>
|
||||
<div className="text-center mt-2 min-h-6">
|
||||
{directoryExistsLoading && <Spinner />}
|
||||
{!directoryExistsLoading && directoryExists && (
|
||||
<div className="text-red-300">Directory must be a new folder</div>
|
||||
)}
|
||||
{!directoryExistsLoading && directoryExists === false && (
|
||||
<div className="text-green-300">✓ New folder</div>
|
||||
)}
|
||||
</div>
|
||||
<br />
|
||||
<div className="flex justify-center">
|
||||
<DayOfWeekInput
|
||||
|
||||
@@ -47,7 +47,7 @@ export const CalendarMonth = ({ month }: { month: CalendarMonthModel }) => {
|
||||
className={
|
||||
"text-2xl transition-all duration-500 " +
|
||||
"hover:text-slate-50 underline hover:scale-105 " +
|
||||
"flex "
|
||||
"flex cursor-pointer"
|
||||
}
|
||||
onClick={() => setIsExpanded((e) => !e)}
|
||||
role="button"
|
||||
|
||||
@@ -25,4 +25,13 @@ export const directoriesRouter = router({
|
||||
.query(async ({ input: { folderPath } }) => {
|
||||
return await fileStorageService.settings.folderIsCourse(folderPath);
|
||||
}),
|
||||
directoryExists: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
relativePath: z.string(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { relativePath } }) => {
|
||||
return await fileStorageService.directoryExists(relativePath);
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -72,4 +72,15 @@ export const fileStorageService = {
|
||||
}
|
||||
return { files, folders };
|
||||
},
|
||||
|
||||
async directoryExists(relativePath: string): Promise<boolean> {
|
||||
const fullPath = path.join(basePath, relativePath);
|
||||
// Security: ensure fullPath is inside basePath
|
||||
const resolvedBase = path.resolve(basePath);
|
||||
const resolvedFull = path.resolve(fullPath);
|
||||
if (!resolvedFull.startsWith(resolvedBase)) {
|
||||
return false;
|
||||
}
|
||||
return await directoryOrFileExists(fullPath);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -23,3 +23,10 @@ export const useDirectoryIsCourseQuery = (folderPath: string) => {
|
||||
trpc.directories.directoryIsCourse.queryOptions({ folderPath })
|
||||
);
|
||||
};
|
||||
|
||||
export const useDirectoryExistsQuery = (relativePath: string) => {
|
||||
const trpc = useTRPC();
|
||||
return useQuery(
|
||||
trpc.directories.directoryExists.queryOptions({ relativePath })
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user