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,
},