Skip to content

Commit c7eb0f4

Browse files
authored
feat: custom 404 and docs nav fixes (#394)
* fix: 404 on docs * feat: add custom 404 page * feat: add utility for slugs array
1 parent 1c301b9 commit c7eb0f4

File tree

31 files changed

+194
-134
lines changed

31 files changed

+194
-134
lines changed

pwa/api/contributors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export async function getContributorBySlug(slug: string): Promise<Contributor> {
169169
name: data.name ? data.name : undefined,
170170
};
171171
}
172-
return notFound();
172+
notFound();
173173
}
174174

175175
// TODO: replace the method when events pages will be done

pwa/api/doc/index.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { cache } from "react";
1212
import { current } from "consts";
1313
import { load as parseHtml } from "cheerio";
1414
import { Chapters } from "types";
15+
import { notFound } from "next/navigation";
1516

1617
export const MyOctokit = Octokit.plugin(throttling);
1718
const sidebarMemoryCache = new Map();
@@ -87,16 +88,21 @@ export async function loadMarkdownBySlugArray(slug: string[]) {
8788
}
8889

8990
export const getDocTitle = async (version: string, slug: string[]) => {
90-
const key = slug.join("");
91-
if (sidebarMemoryCache.has(key)) {
91+
try {
92+
const key = slug.join("");
93+
if (sidebarMemoryCache.has(key)) {
94+
return sidebarMemoryCache.get(key);
95+
}
96+
const { data } = await getDocContentFromSlug(version, slug);
97+
const md = Buffer.from((data as any).content, "base64").toString();
98+
const title = extractHeadingsFromMarkdown(md, 1)?.[0];
99+
100+
sidebarMemoryCache.set(key, title || slug.shift());
92101
return sidebarMemoryCache.get(key);
102+
} catch (err) {
103+
console.error(err);
104+
return null;
93105
}
94-
const { data, path } = await getDocContentFromSlug(version, slug);
95-
const md = Buffer.from((data as any).content, "base64").toString();
96-
const title = extractHeadingsFromMarkdown(md, 1)?.[0];
97-
98-
sidebarMemoryCache.set(key, title || slug.shift());
99-
return sidebarMemoryCache.get(key);
100106
};
101107

102108
export const loadV2DocumentationNav = cache(async (branch: string) => {
@@ -149,7 +155,6 @@ const indexes = [
149155
"schema-generator",
150156
"client-generator",
151157
];
152-
153158
export const getDocContentFromSlug = async (
154159
version: string,
155160
slug: string[]
@@ -168,9 +173,9 @@ export const getDocContentFromSlug = async (
168173

169174
return { data, path: p };
170175
} catch (error) {
171-
console.error("An error occured while fetching %s", p);
176+
console.error(`An error occured while fetching ${p}`);
172177
console.error(error);
173-
return { data: { content: "error" }, path: p };
178+
throw error;
174179
}
175180
};
176181

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { notFound } from "next/navigation";
2+
3+
export default function NotFoundCatchAll() {
4+
notFound();
5+
}
Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import React from "react";
2-
import Sidebar from "components/docs/Sidebar";
32
import { loadV2DocumentationNav } from "api/doc";
43
import { versions, current } from "consts";
5-
import { DocProvider } from "contexts/DocContext";
6-
import MobileSideBar from "components/docs/MobileSidebar";
4+
import DocLayout from "../components/DocLayout";
75

8-
async function DocLayout({
6+
async function Layout({
97
params: { slug },
108
children,
119
}: {
@@ -17,17 +15,7 @@ async function DocLayout({
1715
const version = versions.includes(slug[0]) ? slug[0] : current;
1816
const nav = await loadV2DocumentationNav(version);
1917

20-
return (
21-
<div className="max-w-8xl mx-auto overflow-x-clip">
22-
<DocProvider>
23-
<MobileSideBar docMenuParts={nav} />
24-
<div className="flex flex-row flex-wrap items-start justify-start">
25-
<Sidebar docMenuParts={nav} />
26-
<div className="flex-1 overflow-clip">{children}</div>
27-
</div>
28-
</DocProvider>
29-
</div>
30-
);
18+
return <DocLayout nav={nav}>{children}</DocLayout>;
3119
}
3220

33-
export default DocLayout;
21+
export default Layout;

pwa/app/(common)/docs/[...slug]/page.tsx

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,27 @@ import {
77
import classNames from "classnames";
88
import { current, versions } from "consts";
99
import Script from "next/script";
10-
import { Metadata, ResolvingMetadata } from "next";
10+
import { Metadata } from "next";
1111
import BreadCrumbs from "components/docs/BreadCrumbs";
12+
import { notFound } from "next/navigation";
13+
import { getVersionAndSlugFromSlugs } from "utils";
1214

1315
export async function generateStaticParams() {
1416
const slugs: { slug: string[] }[] = [];
1517
const navs = await loadV2DocumentationNav(current);
1618
for (const nav of navs) {
1719
for (const link of nav.links) {
18-
slugs.push({ slug: link.link.replace("/docs/", "").split("/") });
20+
slugs.push({
21+
slug: link.link
22+
.replace("/docs/", "")
23+
.split("/")
24+
.filter((p) => p !== ""),
25+
});
1926
}
2027
}
21-
28+
for (const version of versions) {
29+
slugs.push({ slug: [version] });
30+
}
2231
return slugs;
2332
}
2433

@@ -41,44 +50,38 @@ export default async function Page({
4150
});
4251
}
4352
}
53+
try {
54+
const { version, slugs } = getVersionAndSlugFromSlugs(slug);
55+
const { data, path } = await getDocContentFromSlug(version, slugs);
4456

45-
const version = versions.includes(slug[0]) ? slug[0] : current;
46-
const contentSlug = versions.includes(slug[0])
47-
? slug.slice(1, slug.length)
48-
: slug;
49-
const { data, path } = await getDocContentFromSlug(
50-
version,
51-
contentSlug.length ? contentSlug : ["distribution"]
52-
);
53-
54-
const html = await getHtmlFromGithubContent({ data, path });
55-
const title = await getDocTitle(version, slug);
57+
const html = await getHtmlFromGithubContent({ data, path });
58+
const title = await getDocTitle(version, slugs);
5659

57-
const breadCrumbs = [
58-
{
59-
title: version,
60-
link: version === current ? `/docs/${version}` : "/docs",
61-
},
62-
{ title },
63-
];
60+
const breadCrumbs = [
61+
{
62+
title: version,
63+
link: version === current ? `/docs/${version}` : "/docs",
64+
},
65+
{ title },
66+
];
6467

65-
if (slug.length > 2 || (slug.length === 2 && !versions.includes(slug[0]))) {
66-
breadCrumbs.splice(1, 0, { title: "..." });
67-
}
68+
if (slug.length > 2 || (slug.length === 2 && !versions.includes(slug[0]))) {
69+
breadCrumbs.splice(1, 0, { title: "..." });
70+
}
6871

69-
return (
70-
<div
71-
className={classNames(
72-
"px-6 md:px-10 py-6 leading-loose text-blue-black/80 ",
73-
"dark:text-white/80"
74-
)}
75-
>
76-
<BreadCrumbs breadCrumbs={breadCrumbs} />
77-
<div className="prose max-w-none dark:prose-invert prose-img:max-w-full prose-headings:font-title prose-h1:font-bold prose-code:after:hidden prose-code:before:hidden prose-code:py-1 prose-code:px-1.5 prose-code:bg-gray-100 prose-code:dark:bg-blue-darkest prose-h1:border-b-px prose-h1:border-b-gray-300 prose-h1:pb-2 max-md:prose-tr:flex max-md:prose-tr:flex-col max-md:prose-td:px-0 max-md:prose-td:py-1">
78-
<div className="doc" dangerouslySetInnerHTML={{ __html: html }}></div>
79-
</div>
80-
<Script id="codeselector-switch">
81-
{`function switchCode(event) {
72+
return (
73+
<div
74+
className={classNames(
75+
"px-6 md:px-10 py-6 leading-loose text-blue-black/80 ",
76+
"dark:text-white/80"
77+
)}
78+
>
79+
<BreadCrumbs breadCrumbs={breadCrumbs} />
80+
<div className="prose max-w-none dark:prose-invert prose-img:max-w-full prose-headings:font-title prose-h1:font-bold prose-code:after:hidden prose-code:before:hidden prose-code:py-1 prose-code:px-1.5 prose-code:bg-gray-100 prose-code:dark:bg-blue-darkest prose-h1:border-b-px prose-h1:border-b-gray-300 prose-h1:pb-2 max-md:prose-tr:flex max-md:prose-tr:flex-col max-md:prose-td:px-0 max-md:prose-td:py-1">
81+
<div className="doc" dangerouslySetInnerHTML={{ __html: html }}></div>
82+
</div>
83+
<Script id="codeselector-switch">
84+
{`function switchCode(event) {
8285
const k = event.target.getAttribute('key')
8386
const p = event.target.parentNode.parentNode.parentNode
8487
;[].slice.call(p.querySelectorAll('div[key]')).forEach(e => e.classList.add('hidden'))
@@ -93,9 +96,13 @@ export default async function Page({
9396
event.target.classList.add(...selectedClasses)
9497
9598
}`}
96-
</Script>
97-
</div>
98-
);
99+
</Script>
100+
</div>
101+
);
102+
} catch (err) {
103+
console.error(err);
104+
notFound();
105+
}
99106
}
100107

101108
export async function generateMetadata({
@@ -105,10 +112,16 @@ export async function generateMetadata({
105112
slug: string[];
106113
};
107114
}): Promise<Metadata> {
108-
const version = versions.includes(slug[0]) ? slug[0] : current;
109-
const title = await getDocTitle(version, slug);
115+
try {
116+
const { version, slugs } = getVersionAndSlugFromSlugs(slug);
117+
const title = await getDocTitle(version, slugs);
110118

111-
return {
112-
title: `${title} - API Platform`,
113-
};
119+
return {
120+
title: `${title} - API Platform`,
121+
};
122+
} catch {
123+
return {
124+
title: "API Platform Documentation - API Platform",
125+
};
126+
}
114127
}

0 commit comments

Comments
 (0)