mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 07:38:33 -06:00
better matching
This commit is contained in:
3
nextjs/src/app/api/courses/route.ts
Normal file
3
nextjs/src/app/api/courses/route.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export async function GET() {
|
||||
return Response.json([]);
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { createQueryClient } from "@/services/utils/queryClient";
|
||||
import { dehydrate } from "@tanstack/react-query";
|
||||
import { MyQueryClientProvider } from "@/services/utils/MyQueryClientProvider";
|
||||
import { hydrateCourses } from "@/hooks/hookHydration";
|
||||
import { LoadingAndErrorHandling } from "@/components/LoadingAndErrorHandling";
|
||||
import { createQueryClientForServer } from "@/services/utils/queryClientServer";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
@@ -12,9 +14,9 @@ export const metadata: Metadata = {
|
||||
};
|
||||
|
||||
export async function getDehydratedClient() {
|
||||
const queryClient = createQueryClient();
|
||||
const queryClient = createQueryClientForServer();
|
||||
|
||||
// await hydrateOpenSections(queryClient);
|
||||
await hydrateCourses(queryClient);
|
||||
const dehydratedState = dehydrate(queryClient);
|
||||
return dehydratedState;
|
||||
}
|
||||
@@ -25,10 +27,13 @@ export default async function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const dehydratedState = await getDehydratedClient();
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<MyQueryClientProvider dehydratedState={dehydratedState}>
|
||||
<body className={inter.className}>{children}</body>
|
||||
<LoadingAndErrorHandling>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</LoadingAndErrorHandling>
|
||||
</MyQueryClientProvider>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { canvasAssignmentService } from "@/services/canvas/canvasAssignmentService";
|
||||
|
||||
export default async function Home() {
|
||||
const assignments = await canvasAssignmentService.getAll(960410);
|
||||
"use client"
|
||||
import { useLocalCoursesQuery } from "@/hooks/localCoursesHooks";
|
||||
|
||||
export default function Home() {
|
||||
const { data: courses } = useLocalCoursesQuery();
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||
{assignments.map((assignment) => (
|
||||
<div key={assignment.id}>{assignment.name}</div>
|
||||
{courses.map((c) => (
|
||||
<div key={c.settings.name}>{c.settings.name} </div>
|
||||
))}
|
||||
</main>
|
||||
);
|
||||
|
||||
30
nextjs/src/components/LoadingAndErrorHandling.tsx
Normal file
30
nextjs/src/components/LoadingAndErrorHandling.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { QueryErrorResetBoundary } from "@tanstack/react-query";
|
||||
import { FC, ReactNode, Suspense } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
|
||||
export const LoadingAndErrorHandling: FC<{ children: ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<QueryErrorResetBoundary>
|
||||
{({ reset }) => (
|
||||
<ErrorBoundary
|
||||
onReset={reset}
|
||||
fallbackRender={(props) => (
|
||||
<div className="text-center">
|
||||
<div className="p-3">{JSON.stringify(props.error)}</div>
|
||||
<button
|
||||
className="btn btn-outline-secondary"
|
||||
onClick={() => props.resetErrorBoundary()}
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<Suspense fallback={<div>loading...</div>}>{children}</Suspense>
|
||||
</ErrorBoundary>
|
||||
)}
|
||||
</QueryErrorResetBoundary>
|
||||
);
|
||||
};
|
||||
10
nextjs/src/hooks/hookHydration.ts
Normal file
10
nextjs/src/hooks/hookHydration.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
import { localCourseKeys } from "./localCoursesHooks";
|
||||
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
|
||||
|
||||
export const hydrateCourses = async (queryClient: QueryClient) => {
|
||||
await queryClient.prefetchQuery({
|
||||
queryKey: localCourseKeys.allCourses,
|
||||
queryFn: async () => await fileStorageService.loadSavedCourses(),
|
||||
});
|
||||
};
|
||||
17
nextjs/src/hooks/localCoursesHooks.ts
Normal file
17
nextjs/src/hooks/localCoursesHooks.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { LocalCourse } from "@/models/local/localCourse";
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export const localCourseKeys = {
|
||||
allCourses: ["all courses"] as const,
|
||||
};
|
||||
|
||||
export const useLocalCoursesQuery = () =>
|
||||
useSuspenseQuery({
|
||||
queryKey: localCourseKeys.allCourses,
|
||||
queryFn: async (): Promise<LocalCourse[]> => {
|
||||
const url = `/api/courses`;
|
||||
const response = await axios.get(url);
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
19
nextjs/src/models/local/tests/timeUtils.test.ts
Normal file
19
nextjs/src/models/local/tests/timeUtils.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { getDateFromString } from "../timeUtils";
|
||||
|
||||
describe("Can properly handle expected date formats", () => {
|
||||
it("can use AM/PM dates", () =>{
|
||||
const dateString = "8/27/2024 1:00:00 AM"
|
||||
const dateObject = getDateFromString(dateString)
|
||||
expect(dateObject).not.toBeUndefined()
|
||||
})
|
||||
it("can use 24 hour dates", () =>{
|
||||
const dateString = "8/27/2024 23:95:00"
|
||||
const dateObject = getDateFromString(dateString)
|
||||
expect(dateObject).not.toBeUndefined()
|
||||
|
||||
})
|
||||
})
|
||||
@@ -1,24 +1,39 @@
|
||||
|
||||
|
||||
export const getDateFromString = (value: string) => {
|
||||
// may need to check for other formats
|
||||
const validDateRegex =
|
||||
/\d{2}\/\d{2}\/\d{4} [0-2][0-9]:[0-5][0-9]:[0-5][0-9]/;
|
||||
// Updated regex to match both formats: "MM/DD/YYYY HH:mm:ss" and "M/D/YYYY h:mm:ss AM/PM"
|
||||
const validDateRegex = /^\d{1,2}\/\d{1,2}\/\d{4} \d{1,2}:\d{2}:\d{2}(?:\s?[APap][Mm])?$/;
|
||||
if (!validDateRegex.test(value)) {
|
||||
console.log("invalid date format", value);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const [datePart, timePart] = value.split(" ");
|
||||
const [datePart, timePartWithMeridian] = value.split(" ");
|
||||
const [day, month, year] = datePart.split("/").map(Number);
|
||||
let [timePart, meridian] = timePartWithMeridian.split(" ");
|
||||
const [hours, minutes, seconds] = timePart.split(":").map(Number);
|
||||
const date = new Date(year, month - 1, day, hours, minutes, seconds);
|
||||
|
||||
let adjustedHours = hours;
|
||||
if (meridian) {
|
||||
meridian = meridian.toUpperCase();
|
||||
if (meridian === "PM" && hours < 12) {
|
||||
adjustedHours += 12;
|
||||
} else if (meridian === "AM" && hours === 12) {
|
||||
adjustedHours = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const date = new Date(year, month - 1, day, adjustedHours, minutes, seconds);
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
console.log("could not parse time out of value", value);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
|
||||
export const verifyDateStringOrUndefined = (
|
||||
value: string
|
||||
): string | undefined => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { courseMarkdownLoader } from "./utils/couresMarkdownLoader";
|
||||
import { courseMarkdownSaver } from "./utils/courseMarkdownSaver";
|
||||
|
||||
const basePath = process.env.STORAGE_DIRECTORY ?? "./storage";
|
||||
console.log("base path", basePath);
|
||||
|
||||
export const fileStorageService = {
|
||||
async saveCourseAsync(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import {
|
||||
DehydratedState,
|
||||
hydrate,
|
||||
HydrationBoundary,
|
||||
QueryClientProvider,
|
||||
} from "@tanstack/react-query";
|
||||
import React from "react";
|
||||
@@ -15,9 +16,9 @@ export const MyQueryClientProvider: FC<{
|
||||
}> = ({ children, dehydratedState }) => {
|
||||
const [queryClient] = useState(createQueryClient());
|
||||
|
||||
hydrate(queryClient, dehydratedState);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<HydrationBoundary state={dehydratedState}>{children}</HydrationBoundary>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
20
nextjs/src/services/utils/queryClientServer.tsx
Normal file
20
nextjs/src/services/utils/queryClientServer.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { MutationCache, QueryCache, QueryClient } from "@tanstack/react-query";
|
||||
|
||||
export const createQueryClientForServer = () => new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
retry: 0,
|
||||
},
|
||||
mutations: {
|
||||
onError: (e) => console.log(e),
|
||||
retry: 0,
|
||||
},
|
||||
},
|
||||
queryCache: new QueryCache({
|
||||
onError: (e) => console.log(e),
|
||||
}),
|
||||
mutationCache: new MutationCache({
|
||||
onError: (e) => console.log(e),
|
||||
}),
|
||||
});
|
||||
Reference in New Issue
Block a user