Skip to content

Commit 8b883e8

Browse files
committed
feat: add tag filtering to resources index page
1 parent 248286d commit 8b883e8

File tree

2 files changed

+96
-3
lines changed

2 files changed

+96
-3
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import Link from 'next/link';
5+
import type { MdxFile } from '@/util/loadMdx.server';
6+
7+
type Props = {
8+
resources: MdxFile[];
9+
};
10+
11+
export default function ResourceIndex({ resources }: Props) {
12+
const [selectedTag, setSelectedTag] = useState<string | null>(null);
13+
14+
// Collect all unique tags
15+
const allTags = Array.from(
16+
new Set(resources.flatMap((r) => r.meta?.tags ?? [])),
17+
).sort();
18+
19+
// Filter logic
20+
const filteredResources = selectedTag
21+
? resources.filter((r) => r.meta?.tags?.includes(selectedTag))
22+
: resources;
23+
24+
return (
25+
<div className="container py-6">
26+
{/* Tag filter */}
27+
{allTags.length > 0 && (
28+
<div className="mb-6 flex flex-wrap gap-2">
29+
<button
30+
onClick={() => setSelectedTag(null)}
31+
className={`btn btn-sm ${
32+
selectedTag === null ? 'btn-primary' : 'btn-outline'
33+
}`}
34+
>
35+
All
36+
</button>
37+
38+
{allTags.map((tag) => (
39+
<button
40+
key={tag}
41+
onClick={() => setSelectedTag(tag)}
42+
aria-pressed={selectedTag === tag}
43+
className={`btn btn-sm ${
44+
selectedTag === tag ? 'btn-primary' : 'btn-outline'
45+
}`}
46+
>
47+
{tag}
48+
</button>
49+
))}
50+
</div>
51+
)}
52+
53+
{/* Resource list */}
54+
{filteredResources.length === 0 ? (
55+
<p>No resources found for this tag.</p>
56+
) : (
57+
<ul className="grid gap-4">
58+
{filteredResources.map((file) => (
59+
<li key={file.slug}>
60+
<Link
61+
href={`/${file.slug.replace('content/', '')}`}
62+
className="text-lg font-semibold underline"
63+
>
64+
{file.meta.title}
65+
</Link>
66+
67+
{file.meta.description && (
68+
<p className="text-muted">{file.meta.description}</p>
69+
)}
70+
</li>
71+
))}
72+
</ul>
73+
)}
74+
</div>
75+
);
76+
}

src/app/resources/[[...slug]]/page.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import DefaultLayout from '@/components/layouts/DefaultLayout';
2+
import ResourceIndex from '@/app/resources/[[...slug]]/ResourceIndex.client';
23
import { createMetaData } from '@/util/createMetaData.server';
34
import {
45
loadMdxDirectory,
@@ -108,15 +109,31 @@ export default async function Page({
108109
params,
109110
}: NextPageProps<'slug', false, true>) {
110111
const uri = ((await params).slug ?? []).join('/');
112+
113+
// ✅ Load all resources first
114+
const allFiles = await loadMdxDirectory({
115+
baseDirectory: 'content/resources',
116+
});
117+
118+
// ✅ INDEX PAGE (/resources)
119+
if (!uri) {
120+
return (
121+
<DefaultLayout
122+
heroHeader="Resources"
123+
heroSubheader="A collection of resources for the Virtual Coffee community"
124+
>
125+
<ResourceIndex resources={allFiles} />
126+
</DefaultLayout>
127+
);
128+
}
129+
130+
// ✅ INDIVIDUAL RESOURCE PAGE
111131
const file = await getFile(uri);
112132

113133
if (!file) {
114134
notFound();
115135
}
116136

117-
const allFiles = await loadMdxDirectory({
118-
baseDirectory: 'content/resources',
119-
});
120137
const breadCrumbs = findBreadcrumbs(allFiles, 'resources/' + uri);
121138

122139
return (

0 commit comments

Comments
 (0)