mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
better matching
This commit is contained in:
@@ -97,7 +97,7 @@ Description:
|
|||||||
Match the following terms & definitions
|
Match the following terms & definitions
|
||||||
|
|
||||||
^statement - a single command to be executed
|
^statement - a single command to be executed
|
||||||
^ - this is the distractor
|
^ - this is the distractor
|
||||||
";
|
";
|
||||||
|
|
||||||
var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz);
|
var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz);
|
||||||
@@ -127,4 +127,33 @@ Match the following terms & definitions
|
|||||||
|
|
||||||
quizMarkdown.Should().Contain("^ statement - a single command to be executed\n^ - this is the distractor");
|
quizMarkdown.Should().Contain("^ statement - a single command to be executed\n^ - this is the distractor");
|
||||||
}
|
}
|
||||||
|
[Fact]
|
||||||
|
public void DistractorsDoNotAddDelimiterOntheEnd()
|
||||||
|
{
|
||||||
|
var rawMarkdownQuiz = @"
|
||||||
|
Name: Test Quiz
|
||||||
|
ShuffleAnswers: true
|
||||||
|
OneQuestionAtATime: false
|
||||||
|
DueAt: 2023-08-21T23:59:00
|
||||||
|
LockAt: 2023-08-21T23:59:00
|
||||||
|
AssignmentGroup: Assignments
|
||||||
|
AllowedAttempts: -1
|
||||||
|
Description:
|
||||||
|
---
|
||||||
|
|
||||||
|
Points: 2
|
||||||
|
|
||||||
|
Match up the term with the best possible answer.
|
||||||
|
^ - a variable name
|
||||||
|
^ - A reserved word with special meaning to the compiler
|
||||||
|
";
|
||||||
|
|
||||||
|
var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz);
|
||||||
|
var quizMarkdown = quiz.ToMarkdown();
|
||||||
|
|
||||||
|
quizMarkdown.Should().Contain(@"Match up the term with the best possible answer.
|
||||||
|
|
||||||
|
^ - a variable name
|
||||||
|
^ - A reserved word with special meaning to the compiler");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,21 @@ public record LocalQuizQuestionAnswer
|
|||||||
if (questionType == QuestionType.MATCHING)
|
if (questionType == QuestionType.MATCHING)
|
||||||
{
|
{
|
||||||
|
|
||||||
string matchingPattern = @"^\^ ?";
|
string matchingPattern = @"^\^";
|
||||||
var textWithoutMatchDelimiter = Regex.Replace(input, matchingPattern, string.Empty).Trim();
|
var textWithoutMatchDelimiter = Regex.Replace(input, matchingPattern, string.Empty);
|
||||||
|
|
||||||
|
var leftRightDelimiter = " - ";
|
||||||
return new LocalQuizQuestionAnswer()
|
return new LocalQuizQuestionAnswer()
|
||||||
{
|
{
|
||||||
Correct = true,
|
Correct = true,
|
||||||
Text = textWithoutMatchDelimiter.Split('-')[0].Trim(),
|
Text = textWithoutMatchDelimiter.Split(leftRightDelimiter)[0].Trim(),
|
||||||
MatchedText = string.Join("-", textWithoutMatchDelimiter.Split('-')[1..]).Trim(),
|
MatchedText = string.Join(
|
||||||
|
leftRightDelimiter,
|
||||||
|
textWithoutMatchDelimiter
|
||||||
|
.Split(leftRightDelimiter)[1..]
|
||||||
|
.Select(a => a.Trim())
|
||||||
|
.Where(a => a != "")
|
||||||
|
).Trim(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
59
nextjs/package-lock.json
generated
59
nextjs/package-lock.json
generated
@@ -9,9 +9,11 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/react-query": "^5.52.0",
|
"@tanstack/react-query": "^5.52.0",
|
||||||
|
"axios": "^1.7.5",
|
||||||
"next": "14.2.5",
|
"next": "14.2.5",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
"react-error-boundary": "^4.0.13",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"yaml": "^2.5.0"
|
"yaml": "^2.5.0"
|
||||||
},
|
},
|
||||||
@@ -406,7 +408,6 @@
|
|||||||
"version": "7.25.0",
|
"version": "7.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz",
|
||||||
"integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==",
|
"integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerator-runtime": "^0.14.0"
|
"regenerator-runtime": "^0.14.0"
|
||||||
},
|
},
|
||||||
@@ -2137,8 +2138,7 @@
|
|||||||
"node_modules/asynckit": {
|
"node_modules/asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/available-typed-arrays": {
|
"node_modules/available-typed-arrays": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
@@ -2164,6 +2164,16 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz",
|
||||||
|
"integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/axobject-query": {
|
"node_modules/axobject-query": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
|
||||||
@@ -2425,7 +2435,6 @@
|
|||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"delayed-stream": "~1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
},
|
},
|
||||||
@@ -2681,7 +2690,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
@@ -3576,6 +3584,25 @@
|
|||||||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||||
|
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||||
@@ -3605,7 +3632,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
@@ -4816,7 +4842,6 @@
|
|||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -4825,7 +4850,6 @@
|
|||||||
"version": "2.1.35",
|
"version": "2.1.35",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": "1.52.0"
|
"mime-db": "1.52.0"
|
||||||
},
|
},
|
||||||
@@ -5594,6 +5618,11 @@
|
|||||||
"react-is": "^16.13.1"
|
"react-is": "^16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"node_modules/psl": {
|
"node_modules/psl": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||||
@@ -5658,6 +5687,17 @@
|
|||||||
"react": "^18.3.1"
|
"react": "^18.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-error-boundary": {
|
||||||
|
"version": "4.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz",
|
||||||
|
"integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-hot-toast": {
|
"node_modules/react-hot-toast": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
|
||||||
@@ -5733,8 +5773,7 @@
|
|||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.14.1",
|
"version": "0.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/regexp.prototype.flags": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.2",
|
"version": "1.5.2",
|
||||||
|
|||||||
@@ -11,9 +11,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/react-query": "^5.52.0",
|
"@tanstack/react-query": "^5.52.0",
|
||||||
|
"axios": "^1.7.5",
|
||||||
"next": "14.2.5",
|
"next": "14.2.5",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
"react-error-boundary": "^4.0.13",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"yaml": "^2.5.0"
|
"yaml": "^2.5.0"
|
||||||
},
|
},
|
||||||
|
|||||||
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 type { Metadata } from "next";
|
||||||
import { Inter } from "next/font/google";
|
import { Inter } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { createQueryClient } from "@/services/utils/queryClient";
|
|
||||||
import { dehydrate } from "@tanstack/react-query";
|
import { dehydrate } from "@tanstack/react-query";
|
||||||
import { MyQueryClientProvider } from "@/services/utils/MyQueryClientProvider";
|
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"] });
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
@@ -12,9 +14,9 @@ export const metadata: Metadata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function getDehydratedClient() {
|
export async function getDehydratedClient() {
|
||||||
const queryClient = createQueryClient();
|
const queryClient = createQueryClientForServer();
|
||||||
|
|
||||||
// await hydrateOpenSections(queryClient);
|
await hydrateCourses(queryClient);
|
||||||
const dehydratedState = dehydrate(queryClient);
|
const dehydratedState = dehydrate(queryClient);
|
||||||
return dehydratedState;
|
return dehydratedState;
|
||||||
}
|
}
|
||||||
@@ -25,10 +27,13 @@ export default async function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
const dehydratedState = await getDehydratedClient();
|
const dehydratedState = await getDehydratedClient();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<MyQueryClientProvider dehydratedState={dehydratedState}>
|
<MyQueryClientProvider dehydratedState={dehydratedState}>
|
||||||
<body className={inter.className}>{children}</body>
|
<LoadingAndErrorHandling>
|
||||||
|
<body className={inter.className}>{children}</body>
|
||||||
|
</LoadingAndErrorHandling>
|
||||||
</MyQueryClientProvider>
|
</MyQueryClientProvider>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { canvasAssignmentService } from "@/services/canvas/canvasAssignmentService";
|
"use client"
|
||||||
|
import { useLocalCoursesQuery } from "@/hooks/localCoursesHooks";
|
||||||
export default async function Home() {
|
|
||||||
const assignments = await canvasAssignmentService.getAll(960410);
|
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const { data: courses } = useLocalCoursesQuery();
|
||||||
return (
|
return (
|
||||||
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||||
{assignments.map((assignment) => (
|
{courses.map((c) => (
|
||||||
<div key={assignment.id}>{assignment.name}</div>
|
<div key={c.settings.name}>{c.settings.name} </div>
|
||||||
))}
|
))}
|
||||||
</main>
|
</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) => {
|
export const getDateFromString = (value: string) => {
|
||||||
// may need to check for other formats
|
// Updated regex to match both formats: "MM/DD/YYYY HH:mm:ss" and "M/D/YYYY h:mm:ss AM/PM"
|
||||||
const validDateRegex =
|
const validDateRegex = /^\d{1,2}\/\d{1,2}\/\d{4} \d{1,2}:\d{2}:\d{2}(?:\s?[APap][Mm])?$/;
|
||||||
/\d{2}\/\d{2}\/\d{4} [0-2][0-9]:[0-5][0-9]:[0-5][0-9]/;
|
|
||||||
if (!validDateRegex.test(value)) {
|
if (!validDateRegex.test(value)) {
|
||||||
|
console.log("invalid date format", value);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [datePart, timePart] = value.split(" ");
|
const [datePart, timePartWithMeridian] = value.split(" ");
|
||||||
const [day, month, year] = datePart.split("/").map(Number);
|
const [day, month, year] = datePart.split("/").map(Number);
|
||||||
|
let [timePart, meridian] = timePartWithMeridian.split(" ");
|
||||||
const [hours, minutes, seconds] = timePart.split(":").map(Number);
|
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())) {
|
if (isNaN(date.getTime())) {
|
||||||
|
console.log("could not parse time out of value", value);
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const verifyDateStringOrUndefined = (
|
export const verifyDateStringOrUndefined = (
|
||||||
value: string
|
value: string
|
||||||
): string | undefined => {
|
): string | undefined => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { courseMarkdownLoader } from "./utils/couresMarkdownLoader";
|
|||||||
import { courseMarkdownSaver } from "./utils/courseMarkdownSaver";
|
import { courseMarkdownSaver } from "./utils/courseMarkdownSaver";
|
||||||
|
|
||||||
const basePath = process.env.STORAGE_DIRECTORY ?? "./storage";
|
const basePath = process.env.STORAGE_DIRECTORY ?? "./storage";
|
||||||
|
console.log("base path", basePath);
|
||||||
|
|
||||||
export const fileStorageService = {
|
export const fileStorageService = {
|
||||||
async saveCourseAsync(
|
async saveCourseAsync(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import {
|
import {
|
||||||
DehydratedState,
|
DehydratedState,
|
||||||
hydrate,
|
hydrate,
|
||||||
|
HydrationBoundary,
|
||||||
QueryClientProvider,
|
QueryClientProvider,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -15,9 +16,9 @@ export const MyQueryClientProvider: FC<{
|
|||||||
}> = ({ children, dehydratedState }) => {
|
}> = ({ children, dehydratedState }) => {
|
||||||
const [queryClient] = useState(createQueryClient());
|
const [queryClient] = useState(createQueryClient());
|
||||||
|
|
||||||
hydrate(queryClient, dehydratedState);
|
|
||||||
|
|
||||||
return (
|
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