Xianbao QIAN
commited on
Commit
•
31767a7
1
Parent(s):
29c3483
Adapt the heatmap code for AI policy
Browse files- bun.lockb +0 -0
- content/policies/data-safety/en.md +5 -0
- content/policies/data-safety/zh.md +7 -0
- content/policies/global-ai/en.md +5 -0
- content/policies/global-ai/zh.md +3 -0
- package.json +2 -0
- src/components/AIPoliciesTable.tsx +66 -0
- src/pages/_document.tsx +15 -11
- src/pages/index.tsx +118 -78
- src/pages/policies/[slug].tsx +40 -0
- src/pages/policies/[slug]/[fileName].tsx +50 -0
bun.lockb
CHANGED
Binary files a/bun.lockb and b/bun.lockb differ
|
|
content/policies/data-safety/en.md
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: English Policy Title
|
3 |
+
language: English
|
4 |
+
originalLink: https://example.com/policy1/en
|
5 |
+
---
|
content/policies/data-safety/zh.md
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: 中文政策标题
|
3 |
+
language: 中文
|
4 |
+
originalLink: https://abc.com
|
5 |
+
---
|
6 |
+
|
7 |
+
这是中文版的政策内容。
|
content/policies/global-ai/en.md
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Global AI Policy
|
3 |
+
---
|
4 |
+
|
5 |
+
Test global policy
|
content/policies/global-ai/zh.md
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: 全球人工智能治理倡议
|
3 |
+
---
|
package.json
CHANGED
@@ -18,11 +18,13 @@
|
|
18 |
"@radix-ui/react-tooltip": "^1.1.2",
|
19 |
"class-variance-authority": "^0.7.0",
|
20 |
"clsx": "^2.1.1",
|
|
|
21 |
"lucide-react": "^0.427.0",
|
22 |
"next": "14.2.5",
|
23 |
"react": "^18",
|
24 |
"react-activity-calendar": "^2.2.11",
|
25 |
"react-dom": "^18",
|
|
|
26 |
"tailwind-merge": "^2.4.0",
|
27 |
"tailwindcss-animate": "^1.0.7"
|
28 |
},
|
|
|
18 |
"@radix-ui/react-tooltip": "^1.1.2",
|
19 |
"class-variance-authority": "^0.7.0",
|
20 |
"clsx": "^2.1.1",
|
21 |
+
"gray-matter": "^4.0.3",
|
22 |
"lucide-react": "^0.427.0",
|
23 |
"next": "14.2.5",
|
24 |
"react": "^18",
|
25 |
"react-activity-calendar": "^2.2.11",
|
26 |
"react-dom": "^18",
|
27 |
+
"react-markdown": "^9.0.1",
|
28 |
"tailwind-merge": "^2.4.0",
|
29 |
"tailwindcss-animate": "^1.0.7"
|
30 |
},
|
src/components/AIPoliciesTable.tsx
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import Link from 'next/link';
|
3 |
+
|
4 |
+
interface PolicyData {
|
5 |
+
title: string;
|
6 |
+
slug: string;
|
7 |
+
fileName: string;
|
8 |
+
originalLink?: string;
|
9 |
+
}
|
10 |
+
|
11 |
+
interface AIPoliciesTableProps {
|
12 |
+
policies: PolicyData[];
|
13 |
+
}
|
14 |
+
|
15 |
+
const AIPoliciesTable: React.FC<AIPoliciesTableProps> = ({ policies }) => {
|
16 |
+
const groupedPolicies: { [slug: string]: PolicyData[] } = {};
|
17 |
+
|
18 |
+
policies.forEach((policy) => {
|
19 |
+
if (!groupedPolicies[policy.slug]) {
|
20 |
+
groupedPolicies[policy.slug] = [];
|
21 |
+
}
|
22 |
+
groupedPolicies[policy.slug].push(policy);
|
23 |
+
});
|
24 |
+
|
25 |
+
return (
|
26 |
+
<div className="my-8">
|
27 |
+
<h2 className="text-2xl font-bold mb-4 dark:text-white">AI Policies</h2>
|
28 |
+
<div className="overflow-x-auto">
|
29 |
+
<table className="w-full border-collapse table-auto">
|
30 |
+
<tbody>
|
31 |
+
{Object.entries(groupedPolicies).map(([slug, policies], index) => {
|
32 |
+
const zhPolicy = policies.find((policy) => policy.fileName === 'zh.md');
|
33 |
+
const enPolicy = policies.find((policy) => policy.fileName === 'en.md');
|
34 |
+
|
35 |
+
return (
|
36 |
+
<tr key={slug} className={`${index % 2 === 0 ? 'bg-white dark:bg-gray-700' : 'bg-gray-50 dark:bg-gray-800'}`}>
|
37 |
+
<td className="py-2 px-4 dark:text-white">
|
38 |
+
<div>{zhPolicy?.title}</div>
|
39 |
+
<div className="text-sm text-gray-500 dark:text-gray-400">{enPolicy?.title}</div>
|
40 |
+
</td>
|
41 |
+
<td className="py-2 px-4">
|
42 |
+
{policies.map((policy) => (
|
43 |
+
policy.originalLink ? (
|
44 |
+
<a key={policy.fileName} href={policy.originalLink} target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline dark:text-blue-400 mr-4">
|
45 |
+
{policy.fileName === 'zh.md' ? '中文' : 'English'}
|
46 |
+
</a>
|
47 |
+
) : (
|
48 |
+
<Link key={policy.fileName} href={`/policies/${slug}/${policy.fileName}`}>
|
49 |
+
<span className="text-blue-500 hover:underline dark:text-blue-400 mr-4 cursor-pointer">
|
50 |
+
{policy.fileName === 'zh.md' ? '中文' : 'English'}
|
51 |
+
</span>
|
52 |
+
</Link>
|
53 |
+
)
|
54 |
+
))}
|
55 |
+
</td>
|
56 |
+
</tr>
|
57 |
+
);
|
58 |
+
})}
|
59 |
+
</tbody>
|
60 |
+
</table>
|
61 |
+
</div>
|
62 |
+
</div>
|
63 |
+
);
|
64 |
+
};
|
65 |
+
|
66 |
+
export default AIPoliciesTable;
|
src/pages/_document.tsx
CHANGED
@@ -1,13 +1,17 @@
|
|
1 |
-
import { Html, Head, Main, NextScript } from
|
2 |
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
<
|
7 |
-
|
8 |
-
<
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
13 |
}
|
|
|
|
|
|
1 |
+
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
2 |
|
3 |
+
class MyDocument extends Document {
|
4 |
+
render() {
|
5 |
+
return (
|
6 |
+
<Html className="dark">
|
7 |
+
<Head />
|
8 |
+
<body>
|
9 |
+
<Main />
|
10 |
+
<NextScript />
|
11 |
+
</body>
|
12 |
+
</Html>
|
13 |
+
);
|
14 |
+
}
|
15 |
}
|
16 |
+
|
17 |
+
export default MyDocument;
|
src/pages/index.tsx
CHANGED
@@ -1,46 +1,81 @@
|
|
1 |
import React, { useState, useEffect, useMemo } from "react";
|
2 |
-
import { generateCalendarData } from "../utils/calendar";
|
3 |
-
import {
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
} from "../types/heatmap";
|
9 |
-
import Heatmap from "../components/Heatmap";
|
10 |
-
import { fetchAllProvidersData, fetchAllAuthorsData } from "../utils/authors";
|
11 |
-
import UserSearchDialog from "../components/UserSearchDialog";
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
export async function getStaticProps() {
|
31 |
try {
|
32 |
-
const allAuthors = PROVIDERS.flatMap(({ authors }) => authors);
|
33 |
-
const uniqueAuthors = Array.from(new Set(allAuthors));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
-
|
36 |
-
|
37 |
|
38 |
-
|
|
|
|
|
|
|
39 |
|
40 |
return {
|
41 |
props: {
|
42 |
-
calendarData,
|
43 |
-
providers: updatedProviders,
|
|
|
44 |
},
|
45 |
revalidate: 3600, // regenerate every hour
|
46 |
};
|
@@ -48,61 +83,66 @@ export async function getStaticProps() {
|
|
48 |
console.error("Error fetching data:", error);
|
49 |
return {
|
50 |
props: {
|
51 |
-
calendarData: {},
|
52 |
-
providers: PROVIDERS,
|
|
|
53 |
},
|
54 |
revalidate: 60, // retry after 1 minute if there was an error
|
55 |
};
|
56 |
}
|
57 |
}
|
58 |
|
59 |
-
const ProviderHeatmap = React.memo(({ provider, calendarData }: { provider: ProviderInfo, calendarData: CalendarData }) => {
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
});
|
73 |
|
74 |
-
const OpenSourceHeatmap: React.FC<
|
75 |
-
calendarData,
|
76 |
-
providers,
|
|
|
77 |
}) => {
|
78 |
-
const [isLoading, setIsLoading] = useState(true);
|
79 |
|
80 |
-
useEffect(() => {
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
}, [calendarData]);
|
85 |
-
|
86 |
-
const sortedProviders = useMemo(() =>
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
);
|
99 |
|
100 |
return (
|
101 |
-
<div className="w-full max-w-screen-lg mx-auto p-4 py-16">
|
102 |
<h1 className="text-3xl lg:text-5xl mt-16 font-bold text-center mb-2">
|
103 |
Hugging Face Heatmap 🤗
|
104 |
</h1>
|
105 |
-
|
|
|
|
|
|
|
106 |
<p>
|
107 |
Models, Datasets, and Spaces from the top AI labs.
|
108 |
</p>
|
@@ -111,7 +151,7 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
|
|
111 |
</div>
|
112 |
</div>
|
113 |
|
114 |
-
<div className="h-px max-w-lg mx-auto bg-gray-200 my-16" />
|
115 |
|
116 |
{isLoading ? (
|
117 |
<p className="text-center">Loading...</p>
|
@@ -125,9 +165,9 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
|
|
125 |
/>
|
126 |
))}
|
127 |
</div>
|
128 |
-
)}
|
129 |
</div>
|
130 |
);
|
131 |
};
|
132 |
|
133 |
-
export default React.memo(OpenSourceHeatmap);
|
|
|
1 |
import React, { useState, useEffect, useMemo } from "react";
|
2 |
+
// import { generateCalendarData } from "../utils/calendar";
|
3 |
+
// import {
|
4 |
+
// OpenSourceHeatmapProps,
|
5 |
+
// ProviderInfo,
|
6 |
+
// ModelData,
|
7 |
+
// CalendarData,
|
8 |
+
// } from "../types/heatmap";
|
9 |
+
// import Heatmap from "../components/Heatmap";
|
10 |
+
// import { fetchAllProvidersData, fetchAllAuthorsData } from "../utils/authors";
|
11 |
+
// import UserSearchDialog from "../components/UserSearchDialog";
|
12 |
+
import AIPoliciesTable from "../components/AIPoliciesTable";
|
13 |
+
import fs from 'fs';
|
14 |
+
import path from 'path';
|
15 |
+
import matter from 'gray-matter';
|
16 |
+
|
17 |
+
// const PROVIDERS: ProviderInfo[] = [
|
18 |
+
// { color: "#ff7000", authors: ["mistralai"] },
|
19 |
+
// { color: "#1877F2", authors: ["meta-llama", "facebook", ] },
|
20 |
+
// { color: "#10A37F", authors: ["openai"] },
|
21 |
+
// { color: "#cc785c", authors: ["Anthropic"] },
|
22 |
+
// { color: "#DB4437", authors: ["google"] },
|
23 |
+
// { color: "#5E35B1", authors: ["allenai"] },
|
24 |
+
// { color: "#0088cc", authors: ["apple"] },
|
25 |
+
// { color: "#FEB800", authors: ["microsoft"] },
|
26 |
+
// { color: "#76B900", authors: ["nvidia"] },
|
27 |
+
// { color: "#0088cc", authors: ["deepseek-ai"] },
|
28 |
+
// { color: "#0088cc", authors: ["Qwen"] },
|
29 |
+
// { color: "#4C6EE6", authors: ["CohereForAI"] },
|
30 |
+
// { color: "#4C6EE6", authors: ["ibm-granite"] },
|
31 |
+
// { color: "#A020F0", authors: ["stabilityai"] },
|
32 |
+
// ];
|
33 |
+
|
34 |
+
interface PolicyData {
|
35 |
+
title: string;
|
36 |
+
language: string;
|
37 |
+
originalLink: string;
|
38 |
+
slug: string;
|
39 |
+
fileName: string;
|
40 |
+
}
|
41 |
|
42 |
export async function getStaticProps() {
|
43 |
try {
|
44 |
+
// const allAuthors = PROVIDERS.flatMap(({ authors }) => authors);
|
45 |
+
// const uniqueAuthors = Array.from(new Set(allAuthors));
|
46 |
+
|
47 |
+
// const flatData: ModelData[] = await fetchAllAuthorsData(uniqueAuthors);
|
48 |
+
// const updatedProviders = await fetchAllProvidersData(PROVIDERS);
|
49 |
+
|
50 |
+
// const calendarData = generateCalendarData(flatData, updatedProviders);
|
51 |
+
|
52 |
+
// Read policy data
|
53 |
+
const policiesDir = path.join(process.cwd(), 'content', 'policies');
|
54 |
+
const policyFolders = fs.readdirSync(policiesDir);
|
55 |
+
|
56 |
+
const policyData: PolicyData[] = [];
|
57 |
+
|
58 |
+
for (const folder of policyFolders) {
|
59 |
+
const zhFilePath = path.join(policiesDir, folder, 'zh.md');
|
60 |
+
const enFilePath = path.join(policiesDir, folder, 'en.md');
|
61 |
+
|
62 |
+
if (fs.existsSync(zhFilePath) && fs.existsSync(enFilePath)) {
|
63 |
+
const zhContent = fs.readFileSync(zhFilePath, 'utf-8');
|
64 |
+
const enContent = fs.readFileSync(enFilePath, 'utf-8');
|
65 |
|
66 |
+
const { data: zhData } = matter(zhContent);
|
67 |
+
const { data: enData } = matter(enContent);
|
68 |
|
69 |
+
policyData.push({ ...zhData, slug: folder, fileName: 'zh.md' } as PolicyData);
|
70 |
+
policyData.push({ ...enData, slug: folder, fileName: 'en.md' } as PolicyData);
|
71 |
+
}
|
72 |
+
}
|
73 |
|
74 |
return {
|
75 |
props: {
|
76 |
+
// calendarData,
|
77 |
+
// providers: updatedProviders,
|
78 |
+
policyData, // Pass policy data as props
|
79 |
},
|
80 |
revalidate: 3600, // regenerate every hour
|
81 |
};
|
|
|
83 |
console.error("Error fetching data:", error);
|
84 |
return {
|
85 |
props: {
|
86 |
+
// calendarData: {},
|
87 |
+
// providers: PROVIDERS,
|
88 |
+
policyData: [], // Pass empty policy data as props
|
89 |
},
|
90 |
revalidate: 60, // retry after 1 minute if there was an error
|
91 |
};
|
92 |
}
|
93 |
}
|
94 |
|
95 |
+
// const ProviderHeatmap = React.memo(({ provider, calendarData }: { provider: ProviderInfo, calendarData: CalendarData }) => {
|
96 |
+
// const providerName = provider.fullName || provider.authors[0];
|
97 |
+
// return (
|
98 |
+
// <div key={providerName} className="flex flex-col items-center">
|
99 |
+
// <Heatmap
|
100 |
+
// data={calendarData[providerName]}
|
101 |
+
// color={provider.color}
|
102 |
+
// providerName={providerName}
|
103 |
+
// fullName={provider.fullName ?? providerName}
|
104 |
+
// avatarUrl={provider.avatarUrl ?? ''}
|
105 |
+
// />
|
106 |
+
// </div>
|
107 |
+
// );
|
108 |
+
// });
|
109 |
|
110 |
+
const OpenSourceHeatmap: React.FC<{ policyData: PolicyData[] }> = ({
|
111 |
+
// calendarData,
|
112 |
+
// providers,
|
113 |
+
policyData,
|
114 |
}) => {
|
115 |
+
// const [isLoading, setIsLoading] = useState(true);
|
116 |
|
117 |
+
// useEffect(() => {
|
118 |
+
// if (calendarData && Object.keys(calendarData).length > 0) {
|
119 |
+
// setIsLoading(false);
|
120 |
+
// }
|
121 |
+
// }, [calendarData]);
|
122 |
+
|
123 |
+
// const sortedProviders = useMemo(() =>
|
124 |
+
// providers.sort((a, b) =>
|
125 |
+
// calendarData[b.fullName || b.authors[0]].reduce(
|
126 |
+
// (sum, day) => sum + day.count,
|
127 |
+
// 0
|
128 |
+
// ) -
|
129 |
+
// calendarData[a.fullName || a.authors[0]].reduce(
|
130 |
+
// (sum, day) => sum + day.count,
|
131 |
+
// 0
|
132 |
+
// )
|
133 |
+
// ),
|
134 |
+
// [providers, calendarData]
|
135 |
+
// );
|
136 |
|
137 |
return (
|
138 |
+
<div className="w-full max-w-screen-lg mx-auto p-4 py-16 dark:bg-gray-900 dark:text-white">
|
139 |
<h1 className="text-3xl lg:text-5xl mt-16 font-bold text-center mb-2">
|
140 |
Hugging Face Heatmap 🤗
|
141 |
</h1>
|
142 |
+
|
143 |
+
<AIPoliciesTable policies={policyData} />
|
144 |
+
|
145 |
+
{/* <div className="text-center text-sm my-8 space-y-4">
|
146 |
<p>
|
147 |
Models, Datasets, and Spaces from the top AI labs.
|
148 |
</p>
|
|
|
151 |
</div>
|
152 |
</div>
|
153 |
|
154 |
+
<div className="h-px max-w-lg mx-auto bg-gray-200 dark:bg-gray-700 my-16" />
|
155 |
|
156 |
{isLoading ? (
|
157 |
<p className="text-center">Loading...</p>
|
|
|
165 |
/>
|
166 |
))}
|
167 |
</div>
|
168 |
+
)} */}
|
169 |
</div>
|
170 |
);
|
171 |
};
|
172 |
|
173 |
+
export default React.memo(OpenSourceHeatmap);
|
src/pages/policies/[slug].tsx
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import { GetStaticProps, GetStaticPaths } from 'next';
|
3 |
+
import fs from 'fs';
|
4 |
+
import path from 'path';
|
5 |
+
import matter from 'gray-matter';
|
6 |
+
import ReactMarkdown from 'react-markdown';
|
7 |
+
|
8 |
+
interface PolicyProps {
|
9 |
+
content: string;
|
10 |
+
}
|
11 |
+
|
12 |
+
const PolicyPage: React.FC<PolicyProps> = ({ content }) => {
|
13 |
+
return (
|
14 |
+
<div className="container mx-auto py-8">
|
15 |
+
<ReactMarkdown>{content}</ReactMarkdown>
|
16 |
+
</div>
|
17 |
+
);
|
18 |
+
};
|
19 |
+
|
20 |
+
export const getStaticPaths: GetStaticPaths = async () => {
|
21 |
+
return {
|
22 |
+
paths: [],
|
23 |
+
fallback: false,
|
24 |
+
};
|
25 |
+
};
|
26 |
+
|
27 |
+
export const getStaticProps: GetStaticProps<PolicyProps> = async ({ params }) => {
|
28 |
+
const { slug, fileName } = params as { slug: string; fileName: string };
|
29 |
+
const filePath = path.join(process.cwd(), 'content', 'policies', slug, fileName);
|
30 |
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
31 |
+
const { content } = matter(fileContent);
|
32 |
+
|
33 |
+
return {
|
34 |
+
props: {
|
35 |
+
content,
|
36 |
+
},
|
37 |
+
};
|
38 |
+
};
|
39 |
+
|
40 |
+
export default PolicyPage;
|
src/pages/policies/[slug]/[fileName].tsx
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import { GetStaticProps, GetStaticPaths } from 'next';
|
3 |
+
import fs from 'fs';
|
4 |
+
import path from 'path';
|
5 |
+
import matter from 'gray-matter';
|
6 |
+
import ReactMarkdown from 'react-markdown';
|
7 |
+
|
8 |
+
interface PolicyProps {
|
9 |
+
content: string;
|
10 |
+
}
|
11 |
+
|
12 |
+
const PolicyPage: React.FC<PolicyProps> = ({ content }) => {
|
13 |
+
return (
|
14 |
+
<div className="container mx-auto py-8">
|
15 |
+
<ReactMarkdown>{content}</ReactMarkdown>
|
16 |
+
</div>
|
17 |
+
);
|
18 |
+
};
|
19 |
+
|
20 |
+
export const getStaticPaths: GetStaticPaths = async () => {
|
21 |
+
const policiesDir = path.join(process.cwd(), 'content', 'policies');
|
22 |
+
const policyFolders = fs.readdirSync(policiesDir);
|
23 |
+
|
24 |
+
const paths = policyFolders.flatMap((folder) => {
|
25 |
+
const languageFiles = fs.readdirSync(path.join(policiesDir, folder));
|
26 |
+
return languageFiles.map((file) => {
|
27 |
+
return { params: { slug: folder, fileName: file } };
|
28 |
+
});
|
29 |
+
});
|
30 |
+
|
31 |
+
return {
|
32 |
+
paths,
|
33 |
+
fallback: false,
|
34 |
+
};
|
35 |
+
};
|
36 |
+
|
37 |
+
export const getStaticProps: GetStaticProps<PolicyProps> = async ({ params }) => {
|
38 |
+
const { slug, fileName } = params as { slug: string; fileName: string };
|
39 |
+
const filePath = path.join(process.cwd(), 'content', 'policies', slug, fileName);
|
40 |
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
41 |
+
const { content } = matter(fileContent);
|
42 |
+
|
43 |
+
return {
|
44 |
+
props: {
|
45 |
+
content,
|
46 |
+
},
|
47 |
+
};
|
48 |
+
};
|
49 |
+
|
50 |
+
export default PolicyPage;
|