diff --git a/Frontend/bun.lockb b/Frontend/bun.lockb deleted file mode 100644 index 14aef67..0000000 Binary files a/Frontend/bun.lockb and /dev/null differ diff --git a/Frontend/docs/bob.md b/Frontend/docs/player/bob.md similarity index 100% rename from Frontend/docs/bob.md rename to Frontend/docs/player/bob.md diff --git a/Frontend/package.json b/Frontend/package.json index 0244a49..d926b6b 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -39,6 +39,7 @@ "react-hook-form": "^7.51.3", "remark-gfm": "^4.0.0", "restfulmc-lib": "^1.1.3", + "shadcn-ui": "0.8.0", "sharp": "^0.33.3", "sonner": "^1.4.41", "tailwind-merge": "^2.2.2", diff --git a/Frontend/src/app/(pages)/docs/[[...slug]]/page.tsx b/Frontend/src/app/(pages)/docs/[[...slug]]/page.tsx index f10e2fa..8cb2d3b 100644 --- a/Frontend/src/app/(pages)/docs/[[...slug]]/page.tsx +++ b/Frontend/src/app/(pages)/docs/[[...slug]]/page.tsx @@ -3,6 +3,15 @@ import { getDocsContent } from "@/lib/mdxUtils"; import { PageProps } from "@/types/page"; import { notFound } from "next/navigation"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb"; +import { capitalize } from "@/lib/stringUtils"; + /** * The page to display content * from an MDX file on the docs. @@ -10,17 +19,47 @@ import { notFound } from "next/navigation"; * @param slug the slug of the mdx file * @return the page jsx */ -const ContentPage = ({ params: { slug } }: PageProps): ReactElement => { +const ContentPage = ({ params }: PageProps): ReactElement => { + const slug: string = ((params.slug as string[]) || undefined)?.join("/"); // The slug of the content + const content: DocsContentMetadata | undefined = getDocsContent().find( (metadata: DocsContentMetadata): boolean => - metadata.slug === (slug ? slug[0] : "home") + metadata.slug === (slug || "home") ); // Get the content to display based on the provided slug // Return a 404 if the content is not found if (!content) { notFound(); } + const splitSlug: string[] = content.slug?.split("/") || []; - return
{content.title}
; + return ( +
+ {/* Breadcrumb */} + + + {["docs", ...splitSlug].map( + (part: string, index: number): ReactElement => ( +
+ + 0 ? "/docs" : "") + + `/${part}` + } + > + {capitalize(part)} + + + {index < splitSlug.length && ( + + )} +
+ ) + )} +
+
+
+ ); }; export default ContentPage; diff --git a/Frontend/src/app/(pages)/docs/layout.tsx b/Frontend/src/app/(pages)/docs/layout.tsx index fb1fb01..017c089 100644 --- a/Frontend/src/app/(pages)/docs/layout.tsx +++ b/Frontend/src/app/(pages)/docs/layout.tsx @@ -12,9 +12,11 @@ const DocumentationLayout = ({ }: Readonly<{ children: ReactNode; }>): ReactElement => ( -
- - {children} +
+
+ + {children} +
); export default DocumentationLayout; diff --git a/Frontend/src/app/common/mdxUtils.ts b/Frontend/src/app/common/mdxUtils.ts index 7ffabc4..ef969db 100644 --- a/Frontend/src/app/common/mdxUtils.ts +++ b/Frontend/src/app/common/mdxUtils.ts @@ -1,25 +1,40 @@ import * as fs from "node:fs"; import path from "node:path"; +import { Stats } from "node:fs"; /** * The regex to match for metadata. */ const METADATA_REGEX: RegExp = /---\s*([\s\S]*?)\s*---/; +/** + * The directory docs are stored in. + */ +const DOCS_DIR: string = path.join(process.cwd(), "docs"); + /** * Get the content to * display in the docs. */ -export const getDocsContent = (): DocsContentMetadata[] => - getMetadata(path.join(process.cwd(), "docs")); +export const getDocsContent = (): DocsContentMetadata[] => { + const content: DocsContentMetadata[] = []; + for (let directory of getRecursiveDirectories(DOCS_DIR)) { + content.push(...getMetadata(DOCS_DIR, directory)); + } + return content; +}; /** * Get the metadata of mdx * files in the given directory. * + * @param parent the parent directory to search * @param directory the directory to search */ -export const getMetadata = (directory: string): T[] => { +const getMetadata = ( + parent: string, + directory: string +): T[] => { const files: string[] = fs .readdirSync(directory) .filter((file: string): boolean => { @@ -29,8 +44,12 @@ export const getMetadata = (directory: string): T[] => { return files.map((file: string): T => { const filePath: string = path.join(directory, file); // The path of the file return { + slug: filePath + .replace(parent, "") + .replace(/\\/g, "/") // Normalize the path + .replace(/\.mdx?$/, "") + .substring(1), ...parseMetadata(fs.readFileSync(filePath, "utf-8")), - slug: path.basename(file, path.extname(file)), }; // Map each file to its metadata }); }; @@ -65,3 +84,24 @@ const parseMetadata = (content: string): T => { // slug is empty, and is defined later on. return { ...metadata, content } as T; }; + +/** + * Get directories recursively + * in the given directory. + * + * @param directory the directory to search + * @return the directories + */ +const getRecursiveDirectories = (directory: string): string[] => { + const directories: string[] = [directory]; // The directories to return + + for (let sub of fs.readdirSync(directory)) { + const subDirPath: string = path.join(directory, sub); // The sub dir path + const stats: Stats = fs.statSync(subDirPath); // Get file stats + if (stats.isDirectory()) { + directories.push(...getRecursiveDirectories(subDirPath)); // Recursively get directories + } + } + + return directories; +}; diff --git a/Frontend/src/app/components/docs/sidebar.tsx b/Frontend/src/app/components/docs/sidebar.tsx index c8ab3af..4c686df 100644 --- a/Frontend/src/app/components/docs/sidebar.tsx +++ b/Frontend/src/app/components/docs/sidebar.tsx @@ -1,4 +1,5 @@ import { ReactElement } from "react"; +import { Input } from "@/components/ui/input"; /** * The sidebar for the docs page. @@ -6,8 +7,14 @@ import { ReactElement } from "react"; * @returns the sidebar jsx */ const Sidebar = (): ReactElement => ( -
-

SIDEBAR

+
+ {/* Search */} +
); export default Sidebar; diff --git a/Frontend/src/app/types/mdx.d.ts b/Frontend/src/app/types/mdx.d.ts index d813282..9f1e4f3 100644 --- a/Frontend/src/app/types/mdx.d.ts +++ b/Frontend/src/app/types/mdx.d.ts @@ -25,7 +25,7 @@ type MDXMetadata = { /** * The slug of the file, defined once read. */ - slug: string; + slug?: string | undefined; /** * The metadata of the file. diff --git a/Frontend/src/app/types/page.d.ts b/Frontend/src/app/types/page.d.ts index 4ba67cd..4f5f02f 100644 --- a/Frontend/src/app/types/page.d.ts +++ b/Frontend/src/app/types/page.d.ts @@ -2,6 +2,6 @@ * Props for a page. */ export type PageProps = { - params: { slug: string }; - searchParams: { [key: string]: string | string[] | undefined }; + params: { slug: string[] }; + searchParams: { [key: string]: string | string[] | undefined }; };