diff --git a/app/docs/[[...slug]]/page.tsx b/app/docs/[[...slug]]/page.tsx index 62af165..c82af89 100644 --- a/app/docs/[[...slug]]/page.tsx +++ b/app/docs/[[...slug]]/page.tsx @@ -11,6 +11,7 @@ import type { Metadata } from "next"; import { createRelativeLink } from "fumadocs-ui/mdx"; import { LLMCopyButton, ViewOptions } from "@/components/ai/page-actions"; import { ImageZoom } from "fumadocs-ui/components/image-zoom"; +import ContactFooter, { type Contacts } from "@/lib/contact-footer"; export default async function Page(props: PageProps<"/docs/[[...slug]]">) { const params = await props.params; @@ -47,10 +48,11 @@ export default async function Page(props: PageProps<"/docs/[[...slug]]">) { components={getMDXComponents({ // this allows you to link to other pages with relative file paths a: createRelativeLink(source, page), - img: ImageZoom + img: ImageZoom, })} /> + ); } @@ -74,3 +76,20 @@ export async function generateMetadata( }, }; } + +function GetContacts(slug: string[] | undefined): Contacts { + if (slug == undefined || slug.length == 0) { + return undefined; + } + + const page = source.getPage(slug); + if (!page) { + return undefined; + } + + if (page.data.contacts) { + return page.data.contacts; + } + + return GetContacts(slug?.slice(0, -1)); +} diff --git a/lib/contact-footer.tsx b/lib/contact-footer.tsx new file mode 100644 index 0000000..c60dbf7 --- /dev/null +++ b/lib/contact-footer.tsx @@ -0,0 +1,79 @@ +import { z } from "zod"; +const ContactSchema = z + .object({ + name: z.string(), + role: z.string().optional(), + email: z.email().optional(), + url: z.url().optional(), + }) + .refine( + (x) => { + return EnsureMutuallyExclusive(x, ["email", "url"]); + }, + { + error: "URL and email are mutually exclusive properties", + } + ); + +function EnsureMutuallyExclusive(x: { [key: string]: any }, keys: string[]) { + let keyCount = 0; + for (let i = 0; i < keys.length; i++) { + if (x[keys[i]] != undefined) { + keyCount++; + } + } + return keyCount <= 1; +} + +export const ContactsSchema = z.array(ContactSchema).optional(); + +export type Contacts = z.infer; +export type Contact = z.infer; + +export default function ContactFooter(params: { contacts: Contacts }) { + if (params.contacts == undefined) { + return <>; + } + + return ( +
+

+ {params.contacts.length > 1 ? "Contacts" : "Contact"}: +

+
    + {params.contacts.map((contact, index) => Contact(contact, index))} +
+
+ ); +} + +function Contact(contact: Contact, index: number) { + let contactLink: string | undefined = undefined; + if (contact.email) { + contactLink = "mailto:" + contact.email; + } + if (contact.url) { + contactLink = contact.url; + } + + let textContainer; + if (contactLink) { + textContainer = ( + + {contact.name} + + ); + } else { + textContainer = {contact.name}; + } + + return ( +
  • + {textContainer}{" "} + {contact.role &&
    {contact.role}
    } +
  • + ); +} diff --git a/source.config.ts b/source.config.ts index 1965eca..668279a 100644 --- a/source.config.ts +++ b/source.config.ts @@ -4,13 +4,16 @@ import { frontmatterSchema, metaSchema, } from "fumadocs-mdx/config"; +import { ContactsSchema } from "./lib/contact-footer"; // You can customise Zod schemas for frontmatter and `meta.json` here // see https://fumadocs.dev/docs/mdx/collections export const docs = defineDocs({ dir: "content/docs", docs: { - schema: frontmatterSchema, + schema: frontmatterSchema.extend({ + contacts: ContactsSchema, + }), postprocess: { includeProcessedMarkdown: true, },