diff --git a/nextjs/package-lock.json b/nextjs/package-lock.json index 0b66818..8be748f 100644 --- a/nextjs/package-lock.json +++ b/nextjs/package-lock.json @@ -8,6 +8,7 @@ "name": "canvas-mangement", "version": "0.1.0", "dependencies": { + "@monaco-editor/react": "^4.6.0", "@tanstack/react-query": "^5.52.0", "axios": "^1.7.5", "next": "14.2.6", @@ -1065,6 +1066,32 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@monaco-editor/loader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", + "integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==", + "license": "MIT", + "dependencies": { + "state-local": "^1.0.6" + }, + "peerDependencies": { + "monaco-editor": ">= 0.21.0 < 1" + } + }, + "node_modules/@monaco-editor/react": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz", + "integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==", + "license": "MIT", + "dependencies": { + "@monaco-editor/loader": "^1.4.0" + }, + "peerDependencies": { + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@next/env": { "version": "14.2.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.6.tgz", @@ -5348,6 +5375,13 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/monaco-editor": { + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.51.0.tgz", + "integrity": "sha512-xaGwVV1fq343cM7aOYB6lVE4Ugf0UyimdD/x5PWcWBMKENwectaEu77FAN7c5sFiyumqeJdX1RPTh1ocioyDjw==", + "license": "MIT", + "peer": true + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -6691,6 +6725,12 @@ "dev": true, "license": "MIT" }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", + "license": "MIT" + }, "node_modules/std-env": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", diff --git a/nextjs/package.json b/nextjs/package.json index 4382c01..2cd5ee4 100644 --- a/nextjs/package.json +++ b/nextjs/package.json @@ -10,6 +10,7 @@ "test": "vitest" }, "dependencies": { + "@monaco-editor/react": "^4.6.0", "@tanstack/react-query": "^5.52.0", "axios": "^1.7.5", "next": "14.2.6", diff --git a/nextjs/src/app/course/[courseName]/calendar/DayItemsInModule.tsx b/nextjs/src/app/course/[courseName]/calendar/DayItemsInModule.tsx index 58ab8b0..6a47558 100644 --- a/nextjs/src/app/course/[courseName]/calendar/DayItemsInModule.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/DayItemsInModule.tsx @@ -2,6 +2,7 @@ import React from "react"; import { useCourseContext } from "../context/courseContext"; import { useModuleDataQuery } from "@/hooks/localCourse/localCoursesHooks"; import { getDateFromStringOrThrow } from "@/models/local/timeUtils"; +import Link from "next/link"; export default function DayItemsInModule({ day, @@ -63,7 +64,7 @@ export default function DayItemsInModule({ } onDragEnd={endItemDrag} > - {q.name} + {q.name} ))} {todaysPages.map((p) => ( diff --git a/nextjs/src/app/course/[courseName]/layout.tsx b/nextjs/src/app/course/[courseName]/layout.tsx new file mode 100644 index 0000000..bba7fd9 --- /dev/null +++ b/nextjs/src/app/course/[courseName]/layout.tsx @@ -0,0 +1,21 @@ +import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query"; +import { hydrateCourse } from "@/hooks/hookHydration"; +import { getQueryClient } from "@/app/providersQueryClientUtils"; + +export default async function CourseLayout({ + children, + params: { courseName }, +}: { + children: React.ReactNode; + params: { courseName: string }; +}) { + const queryClient = getQueryClient(); + + await hydrateCourse(queryClient, courseName); + const dehydratedState = dehydrate(queryClient); + + console.log("hydrated course state", courseName, dehydratedState); + return ( + {children} + ); +} diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiz.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiz.tsx new file mode 100644 index 0000000..a3b5cf6 --- /dev/null +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiz.tsx @@ -0,0 +1,23 @@ +"use client"; +import MonacoEditor from "@/components/MonacoEditor"; +import { useQuizQuery } from "@/hooks/localCourse/quizHooks"; + +export default function EditQuiz({ + courseName, + moduleName, + quizName, +}: { + courseName: string; + quizName: string; + moduleName: string; +}) { + const { data: quiz } = useQuizQuery(courseName, moduleName, quizName); + + return ( +
+ {quiz.name} + + {/* */} +
+ ); +} diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/page.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/page.tsx new file mode 100644 index 0000000..53f011b --- /dev/null +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/page.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import EditQuiz from "./EditQuiz"; + +export default async function Page({ + params: { courseName, moduleName, quizName }, +}: { + params: { courseName: string; quizName: string; moduleName: string }; +}) { + return ( + + ); +} diff --git a/nextjs/src/app/course/[courseName]/page.tsx b/nextjs/src/app/course/[courseName]/page.tsx index 7c75aeb..a86ee1b 100644 --- a/nextjs/src/app/course/[courseName]/page.tsx +++ b/nextjs/src/app/course/[courseName]/page.tsx @@ -1,36 +1,26 @@ import CourseContextProvider from "./context/CourseContextProvider"; import CourseCalendar from "./calendar/CourseCalendar"; -import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; import CourseSettings from "./CourseSettings"; import ModuleList from "./modules/ModuleList"; -import { createQueryClientForServer } from "@/services/utils/queryClientServer"; -import { hydrateCourse } from "@/hooks/hookHydration"; export default async function CoursePage({ params: { courseName }, }: { params: { courseName: string }; }) { - const queryClient = createQueryClientForServer(); - - await hydrateCourse(queryClient, courseName); - const dehydratedState = dehydrate(queryClient); - return ( - - -
- -
-
- -
-
- -
+ +
+ +
+
+ +
+
+
- - +
+
); } diff --git a/nextjs/src/app/page.tsx b/nextjs/src/app/page.tsx index 3f622dc..775af56 100644 --- a/nextjs/src/app/page.tsx +++ b/nextjs/src/app/page.tsx @@ -1,18 +1,14 @@ -import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; +import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query"; import CourseList from "./CourseList"; -import { createQueryClientForServer } from "@/services/utils/queryClientServer"; import { hydrateCourses } from "@/hooks/hookHydration"; +import { getQueryClient } from "./providersQueryClientUtils"; -async function getDehydratedClient() { - const queryClient = createQueryClientForServer(); - - await hydrateCourses(queryClient); - const dehydratedState = dehydrate(queryClient); - return dehydratedState; -} export default async function Home() { - const dehydratedState = await getDehydratedClient(); + const queryClient = getQueryClient(); + await hydrateCourses(queryClient); + const dehydratedState = dehydrate(queryClient); + return (
diff --git a/nextjs/src/app/providers.tsx b/nextjs/src/app/providers.tsx index 727d8b5..01f17c9 100644 --- a/nextjs/src/app/providers.tsx +++ b/nextjs/src/app/providers.tsx @@ -1,39 +1,11 @@ "use client"; import { - isServer, - QueryClient, QueryClientProvider, } from "@tanstack/react-query"; import { ReactNode } from "react"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { getQueryClient } from "./providersQueryClientUtils"; -function makeQueryClient() { - return new QueryClient({ - defaultOptions: { - queries: { - // With SSR, we usually want to set some default staleTime - // above 0 to avoid refetching immediately on the client - staleTime: 1000, - }, - }, - }); -} - -let browserQueryClient: QueryClient | undefined = undefined; - -function getQueryClient() { - if (isServer) { - // Server: always make a new query client - return makeQueryClient(); - } else { - // Browser: make a new query client if we don't already have one - // This is very important, so we don't re-make a new client if React - // suspends during the initial render. This may not be needed if we - // have a suspense boundary BELOW the creation of the query client - if (!browserQueryClient) browserQueryClient = makeQueryClient(); - return browserQueryClient; - } -} export default function Providers({ children }: { children: ReactNode }) { // NOTE: Avoid useState when initializing the query client if you don't diff --git a/nextjs/src/app/providersQueryClientUtils.ts b/nextjs/src/app/providersQueryClientUtils.ts new file mode 100644 index 0000000..8ca631b --- /dev/null +++ b/nextjs/src/app/providersQueryClientUtils.ts @@ -0,0 +1,29 @@ +import { isServer, QueryClient } from "@tanstack/react-query"; + +export function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // With SSR, we usually want to set some default staleTime + // above 0 to avoid refetching immediately on the client + staleTime: 1000, + }, + }, + }); +} + +let browserQueryClient: QueryClient | undefined = undefined; + +export function getQueryClient() { + if (isServer) { + // Server: always make a new query client + return makeQueryClient(); + } else { + // Browser: make a new query client if we don't already have one + // This is very important, so we don't re-make a new client if React + // suspends during the initial render. This may not be needed if we + // have a suspense boundary BELOW the creation of the query client + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; + } +} diff --git a/nextjs/src/components/MonacoEditor.module.css b/nextjs/src/components/MonacoEditor.module.css new file mode 100644 index 0000000..bc41e26 --- /dev/null +++ b/nextjs/src/components/MonacoEditor.module.css @@ -0,0 +1,4 @@ +.Editor { + width: 100vw; + height: 100vh; +} \ No newline at end of file diff --git a/nextjs/src/components/MonacoEditor.tsx b/nextjs/src/components/MonacoEditor.tsx new file mode 100755 index 0000000..29d9086 --- /dev/null +++ b/nextjs/src/components/MonacoEditor.tsx @@ -0,0 +1,13 @@ +"use client"; +import styles from "./MonacoEditor.module.css"; +import Editor from "@monaco-editor/react"; + +export default function MonacoEditor() { + return ( + + ); +} diff --git a/nextjs/src/services/utils/MyQueryClientProvider.tsx b/nextjs/src/services/utils/MyQueryClientProvider.tsx deleted file mode 100644 index 320692d..0000000 --- a/nextjs/src/services/utils/MyQueryClientProvider.tsx +++ /dev/null @@ -1,25 +0,0 @@ -"use client" -import { - DehydratedState, - hydrate, - QueryClientProvider, -} from "@tanstack/react-query"; -import React from "react"; -import { ReactNode, useState } from "react"; -import { createQueryClient } from "./queryClient"; - -export default function MyQueryClientProvider({ - children, - dehydratedState, -}: { - children: ReactNode; - dehydratedState: DehydratedState; -}) { - const [queryClient] = useState(createQueryClient()); - - hydrate(queryClient, dehydratedState); - - return ( - {children} - ); -} diff --git a/nextjs/src/services/utils/queryClient.tsx b/nextjs/src/services/utils/queryClient.tsx index e8f1097..269cf20 100644 --- a/nextjs/src/services/utils/queryClient.tsx +++ b/nextjs/src/services/utils/queryClient.tsx @@ -123,23 +123,23 @@ export function createSuccessToast(message: string) { ); } -export function createQueryClient() { - return new QueryClient({ - defaultOptions: { - queries: { - refetchOnWindowFocus: false, - retry: 0, - }, - mutations: { - onError: addErrorAsToast, - retry: 0, - }, - }, - queryCache: new QueryCache({ - onError: addErrorAsToast, - }), - mutationCache: new MutationCache({ - onError: addErrorAsToast, - }), - }); -} +// export function createQueryClient() { +// return new QueryClient({ +// defaultOptions: { +// queries: { +// refetchOnWindowFocus: false, +// retry: 0, +// }, +// mutations: { +// onError: addErrorAsToast, +// retry: 0, +// }, +// }, +// queryCache: new QueryCache({ +// onError: addErrorAsToast, +// }), +// mutationCache: new MutationCache({ +// onError: addErrorAsToast, +// }), +// }); +// } diff --git a/nextjs/src/services/utils/queryClientServer.tsx b/nextjs/src/services/utils/queryClientServer.tsx deleted file mode 100644 index c7ad271..0000000 --- a/nextjs/src/services/utils/queryClientServer.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { MutationCache, QueryCache, QueryClient } from "@tanstack/react-query"; - -export function createQueryClientForServer() { - return 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), - }), - }); -} diff --git a/nextjs/tsconfig.json b/nextjs/tsconfig.json index 7b28589..cd8a767 100644 --- a/nextjs/tsconfig.json +++ b/nextjs/tsconfig.json @@ -21,6 +21,6 @@ "@/*": ["./src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiztsx"], "exclude": ["node_modules"] }