Skip to content

Commit ee401d1

Browse files
Move to server components for displaying the documentation menu
1 parent 3b3a959 commit ee401d1

File tree

9 files changed

+195
-118
lines changed

9 files changed

+195
-118
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from "react";
2+
import Navbar from "../components/navbar";
3+
import SidebarMenu from "../components/sidebarMenu";
4+
import InfoView from "../components/infoView";
5+
6+
interface DocumentationPageProps {
7+
selectedEndpoint: string | null;
8+
}
9+
10+
const DocumentationPage: React.FC<DocumentationPageProps> = ({
11+
selectedEndpoint,
12+
}) => {
13+
return (
14+
<>
15+
<Navbar />
16+
<div className="flex flex-row items-start">
17+
<div className="w-64 pl-4 pt-2 bg-gray-100">
18+
<SidebarMenu selectedEndpoint={selectedEndpoint} />
19+
</div>
20+
<main className="flex flex-col flex-grow items-start justify-center rounded-md mt-24">
21+
<div className="w-full p-8 bg-white rounded-md">
22+
<InfoView
23+
selectedEndpoint={selectedEndpoint}
24+
firstFile={null}
25+
fileData={null}
26+
/>
27+
</div>
28+
</main>
29+
<div className="w-64"></div>
30+
</div>
31+
</>
32+
);
33+
};
34+
35+
export default DocumentationPage;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from "react";
2+
3+
interface EndpointViewProps {
4+
endpoint: string;
5+
method: string;
6+
}
7+
8+
const EndpointView: React.FC<EndpointViewProps> = ({ endpoint, method }) => {
9+
const handleOpenEndpoint = () => {
10+
window.open(endpoint, "_blank");
11+
};
12+
13+
const handleCopyEndpoint = () => {
14+
navigator.clipboard.writeText(endpoint);
15+
};
16+
17+
return (
18+
<div className="bg-pokemon-gray text-white p-2 h-12 w-fit rounded flex items-center">
19+
<span>{method}</span>
20+
<span className="w-2"></span>
21+
<span className="underline decoration-pokemon-yellow">{endpoint}</span>
22+
</div>
23+
);
24+
};
25+
26+
export default EndpointView;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import EndpointView from "../components/endpointView";
2+
import React from "react";
3+
4+
interface InfoViewProps {
5+
selectedEndpoint: string | null;
6+
firstFile: string | null;
7+
fileData: any;
8+
}
9+
10+
const InfoView: React.FC<InfoViewProps> = ({
11+
selectedEndpoint,
12+
firstFile,
13+
fileData,
14+
}) => {
15+
return (
16+
<div className="w-full p-8 bg-white rounded-md">
17+
<h1 className="text-2xl font-press-start font-bold mb-4">Hi</h1>
18+
{selectedEndpoint ? (
19+
<EndpointView endpoint={selectedEndpoint} method="GET" />
20+
) : null}
21+
<p>ToDo: Add more info!</p>
22+
</div>
23+
);
24+
};
25+
26+
export default InfoView;

src/app/pokedex/components/responseView.tsx

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import React from "react";
22
import { Code } from "bright";
33
import { draculaAltered } from "../editorTheme";
4-
5-
import fsPromises from "fs/promises";
6-
import path from "path";
4+
import { getFile } from "../utils/fileUtils";
75

86
interface ResponseViewProps {
97
endpoint: string;
@@ -15,8 +13,8 @@ const ResponseView: React.FC<ResponseViewProps> = async ({
1513
method,
1614
}) => {
1715
Code.theme = draculaAltered;
18-
const file = endpoint.replace("/api", ""); // the endpoint looks like /api/pokemon/charizard
19-
const data = await getResponseJSON(file);
16+
const [folder, file] = endpoint.replace("/api/", "").split("/"); // the endpoint looks like /api/pokemon/charizard
17+
const data = await getFile(folder, file);
2018

2119
return (
2220
<>
@@ -33,10 +31,3 @@ const ResponseView: React.FC<ResponseViewProps> = async ({
3331
};
3432

3533
export default ResponseView;
36-
37-
async function getResponseJSON(file: string) {
38-
const jsonFile = await fsPromises.readFile(
39-
path.resolve(__dirname, `../../../../public/data/pokedex/${file}.json`)
40-
);
41-
return JSON.parse(jsonFile.toString());
42-
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React from "react";
2+
import { categories } from "../data/categories";
3+
import Link from "next/link";
4+
5+
interface SidebarMenuProps {
6+
selectedEndpoint: string | null;
7+
}
8+
9+
const formatEndpointId = (id: string) => id.toLowerCase().replace(/\s+/g, "-");
10+
11+
const SidebarMenu: React.FC<SidebarMenuProps> = ({ selectedEndpoint }) => {
12+
const isEndpointAvailable = categories.some((category) =>
13+
category.endpoints?.some(
14+
(endpoint) =>
15+
formatEndpointId(endpoint.id) === selectedEndpoint
16+
)
17+
);
18+
19+
if (!isEndpointAvailable) {
20+
selectedEndpoint = "Information";
21+
}
22+
23+
const selectedCategory =
24+
categories.find((category) =>
25+
category.endpoints?.some(
26+
(endpoint) =>
27+
formatEndpointId(endpoint.id) === selectedEndpoint
28+
)
29+
)?.id || "Information";
30+
31+
return (
32+
<div className="w-fit min-w-48 max-w-80 p-3 text-sm text-pokemon-gray">
33+
{categories.map((category) => (
34+
<div key={category.id} className="mb-1">
35+
<Link
36+
href={`/pokedex/documentation/${category.endpoints ? formatEndpointId(category.endpoints[0].id) : ""}`}
37+
>
38+
<div
39+
className={`cursor-pointer px-2 py-1 mb-1 rounded-lg hover:bg-pokemon-yellow ${selectedCategory === category.id ? "underline decoration-2 underline-offset-2 decoration-pokemon-blue" : ""}`}
40+
>
41+
{category.id}
42+
</div>
43+
</Link>
44+
{selectedCategory === category.id && category.endpoints && (
45+
<div className="pl-2">
46+
{category.endpoints.map((endpoint) => (
47+
<Link
48+
href={`/pokedex/documentation/${formatEndpointId(endpoint.id)}`}
49+
key={`link-${endpoint.id}`}
50+
>
51+
<div
52+
className={`cursor-pointer px-2 py-1 mb-1 rounded-lg hover:bg-pokemon-yellow ${selectedEndpoint === formatEndpointId(endpoint.id) ? "underline decoration-2 underline-offset-2 decoration-pokemon-blue" : ""}`}
53+
>
54+
{endpoint.id}
55+
</div>
56+
</Link>
57+
))}
58+
</div>
59+
)}
60+
</div>
61+
))}
62+
</div>
63+
);
64+
};
65+
66+
export default SidebarMenu;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from "react";
2+
import DocumentationPage from "../../components/documentationPage";
3+
4+
export default function Documentation({
5+
params,
6+
}: {
7+
params: { endpoint: string };
8+
}) {
9+
const { endpoint } = params;
10+
return <DocumentationPage selectedEndpoint={endpoint} />;
11+
}
Lines changed: 5 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,10 @@
1-
"use client";
2-
import React, { useState } from "react";
3-
import Navbar from "../components/navbar";
4-
5-
import { categories } from "../data/categories";
6-
7-
const DocumentationPage = () => {
8-
const [selectedCategory, setSelectedCategory] = useState<string>(
9-
categories[0]?.id
10-
);
11-
const [selectedEndpoint, setselectedEndpoint] = useState<string | null>(
12-
categories[0]?.endpoints?.[0]?.id || null
13-
);
14-
15-
const handleEndpointSelect = (
16-
categoryId: string,
17-
endpointId: string | null
18-
) => {
19-
setSelectedCategory(categoryId);
20-
setselectedEndpoint(endpointId);
21-
};
1+
import React from "react";
2+
import DocumentationPage from "../components/documentationPage";
223

4+
const Documentation = () => {
235
return (
24-
<>
25-
<Navbar />
26-
<div className="flex flex-row items-start">
27-
<div className="w-64 pl-4 pt-2 bg-gray-100">
28-
<SidebarMenu onEndpointSelect={handleEndpointSelect} />
29-
</div>
30-
<main className="flex flex-col flex-grow items-start justify-center rounded-md mt-24">
31-
<div className="w-full p-8 bg-white rounded-md">
32-
<h1 className="text-2xl font-press-start font-bold mb-4">
33-
{selectedCategory}
34-
</h1>
35-
{selectedEndpoint && (
36-
<p>
37-
Selected category: {selectedCategory}
38-
<br />
39-
Selected edndpoint: {selectedEndpoint}
40-
</p>
41-
)}
42-
{!selectedEndpoint && <p>Selected Category: {selectedCategory}</p>}
43-
</div>
44-
</main>
45-
<div className="w-64"></div>
46-
</div>
47-
</>
48-
);
49-
};
50-
51-
interface SidebarMenuProps {
52-
onEndpointSelect?: (categoryId: string, endpointId: string | null) => void;
53-
}
54-
55-
const SidebarMenu: React.FC<SidebarMenuProps> = ({ onEndpointSelect }) => {
56-
const [openCategory, setOpenCategory] = useState<string | null>(null);
57-
const [selectedEndpoint, setselectedEndpoint] = useState<string | null>(null);
58-
59-
const toggleCategory = (id: string) => {
60-
if (openCategory === id) {
61-
return;
62-
}
63-
64-
const isOpening = openCategory !== id;
65-
setOpenCategory(isOpening ? id : null);
66-
67-
const firstendpointId = isOpening
68-
? categories.find((category) => category.id === id)?.endpoints?.[0]?.id ||
69-
null
70-
: null;
71-
setselectedEndpoint(firstendpointId);
72-
73-
onEndpointSelect && onEndpointSelect(id, firstendpointId || null);
74-
};
75-
76-
const selectEndpoint = (id: string) => {
77-
setselectedEndpoint(id);
78-
onEndpointSelect && onEndpointSelect(openCategory || "", id);
79-
};
80-
81-
return (
82-
<div className="w-fit min-w-48 max-w-80 p-3 text-sm text-pokemon-gray">
83-
{categories.map((category) => (
84-
<div key={category.id} className="mb-1">
85-
<div
86-
className={`cursor-pointer px-2 py-1 mb-1 rounded-lg hover:bg-pokemon-yellow ${openCategory === category.id ? "underline decoration-2 underline-offset-2 decoration-pokemon-blue" : ""}`}
87-
onClick={() => toggleCategory(category.id)}
88-
>
89-
{category.id}
90-
</div>
91-
92-
{openCategory === category.id && category.endpoints && (
93-
<div className="pl-2">
94-
{category.endpoints.map((endpoint) => (
95-
<div
96-
key={endpoint.id}
97-
className={`cursor-pointer px-2 py-1 mb-1 rounded-lg hover:bg-pokemon-yellow ${selectedEndpoint === endpoint.id ? "underline decoration-2 underline-offset-2 decoration-pokemon-blue" : ""}`}
98-
onClick={() => selectEndpoint(endpoint.id)}
99-
>
100-
{endpoint.id}
101-
</div>
102-
))}
103-
</div>
104-
)}
105-
</div>
106-
))}
107-
</div>
6+
<DocumentationPage selectedEndpoint={null} />
1087
);
1098
};
1109

111-
export default DocumentationPage;
10+
export default Documentation;

src/app/pokedex/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default function PokemonAPI() {
2424
</p>
2525
<section className="w-11/12 m-auto">
2626
<h2 className="text-3xl font-semibold mt-8 mb-2">Example Request</h2>
27+
2728
<Image
2829
src={`https://raw.githubusercontent.com/getmimo/things-api/main/files/pokedex/sprites/master/sprites/pokemon/6.png`}
2930
alt="Pokemon"

src/app/pokedex/utils/fileUtils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import fsPromises from "fs/promises";
2+
import path from "path";
3+
4+
export async function getFile(folder: string, file: string) {
5+
const filePath = file.endsWith(".json") ? file : `${file}.json`;
6+
const jsonFile = await fsPromises.readFile(
7+
path.resolve(
8+
__dirname,
9+
`../../../../public/data/pokedex/${folder}/${filePath}`
10+
)
11+
);
12+
return JSON.parse(jsonFile.toString());
13+
}
14+
15+
export async function getFirstFileNameInFolder(
16+
folder: string
17+
): Promise<string> {
18+
const files = await fsPromises.readdir(
19+
path.resolve(__dirname, `../../../../public/data/pokedex/${folder}`)
20+
);
21+
return files[0];
22+
}

0 commit comments

Comments
 (0)