Skip to content

Commit c4a69cf

Browse files
committed
feat(resources): implement resource detail and list components, update home page with filters
1 parent 7ee418c commit c4a69cf

File tree

11 files changed

+104
-278
lines changed

11 files changed

+104
-278
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { getResourceBySlug } from "@/actions/resources";
2+
3+
type Props = {
4+
params: { slug: string };
5+
};
6+
7+
export default async function ResourceDetailPage({ params }: Props) {
8+
const { slug } = await params;
9+
const resource = await getResourceBySlug(slug);
10+
return (
11+
<main className="max-w-screen-xl mx-auto p-4">
12+
<h1>{resource.title}</h1>
13+
<p>{resource.description}</p>
14+
<p>{resource.tags.join(", ")}</p>
15+
<p>{resource.author.name}</p>
16+
<p>{resource.created_at}</p>
17+
<img src={resource.image} alt={resource.title} />
18+
</main>
19+
);
20+
}

src/app/(public)/resources/page.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import type { Metadata } from "next";
2+
3+
import { getResources } from "@/actions/resources";
4+
import { ResourceList } from "@/components/resources";
25
import { PROJECT_NAME } from "@/config/constants";
36

47
export const metadata: Metadata = {
58
title: `Resources - ${PROJECT_NAME}`,
69
description: "Resources page",
710
};
811

9-
export default function ResourcesPage() {
12+
export default async function ResourcesPage() {
13+
const resources = await getResources();
14+
1015
return (
1116
<main className="max-w-screen-xl mx-auto p-4">
12-
<h1>ResourcesPage</h1>
17+
<h1>Resources</h1>
18+
<ResourceList resources={resources} />
1319
</main>
1420
);
1521
}

src/app/page.tsx

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,15 @@
1-
"use client";
1+
import { HeroSection, HomeFilters } from "@/components/home";
2+
import { getResources } from "@/actions/resources";
3+
import { ResourceList } from "@/components/resources";
24

3-
import { Button } from "@heroui/react";
4-
import { Filter } from "lucide-react";
5-
import { HeroSection } from "@/components/home";
6-
import { ResourceCard } from "@/components/resources";
5+
export default async function HomePage() {
6+
const resources = await getResources();
77

8-
import resources from "@/data/resources.json";
9-
10-
export default function HomePage() {
118
return (
129
<main className="min-h-screen bg-white">
1310
<HeroSection />
14-
15-
{/* Categories and Filters */}
16-
<section className="border-t border-gray-100 py-4">
17-
<div className="container mx-auto px-4 flex justify-between items-center">
18-
{/* <Tabs defaultValue="discover" className="w-auto">
19-
<TabsList className="bg-transparent h-auto p-0 gap-6">
20-
<TabsTrigger
21-
value="discover"
22-
className="data-[state=active]:bg-transparent data-[state=active]:shadow-none data-[state=active]:text-black data-[state=active]:font-medium px-0 py-2"
23-
>
24-
Discover
25-
</TabsTrigger>
26-
</TabsList>
27-
</Tabs> */}
28-
<Button variant="ghost" className="gap-2">
29-
<Filter className="h-4 w-4" />
30-
Filters
31-
</Button>
32-
</div>
33-
</section>
34-
35-
{/* Design Grid */}
36-
<section className="py-8">
37-
<div className="container mx-auto px-4">
38-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
39-
{resources.map((resource) => (
40-
<ResourceCard key={resource.id} resource={resource} />
41-
))}
42-
</div>
43-
</div>
44-
</section>
11+
<HomeFilters />
12+
<ResourceList resources={resources} />
4513
</main>
4614
);
4715
}

src/components/home/hero-section.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client";
2+
13
import {
24
Button,
35
Input,

src/components/home/home-filters.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"use client";
2+
3+
import { Button } from "@heroui/react";
4+
import { Filter } from "lucide-react";
5+
6+
export default function HomeFilters() {
7+
return (
8+
<section className="border-t border-gray-100 py-4">
9+
<div className="container mx-auto px-4 flex justify-between items-center">
10+
{/* <Tabs defaultValue="discover" className="w-auto">
11+
<TabsList className="bg-transparent h-auto p-0 gap-6">
12+
<TabsTrigger
13+
value="discover"
14+
className="data-[state=active]:bg-transparent data-[state=active]:shadow-none data-[state=active]:text-black data-[state=active]:font-medium px-0 py-2"
15+
>
16+
Discover
17+
</TabsTrigger>
18+
</TabsList>
19+
</Tabs> */}
20+
<Button variant="ghost" className="gap-2">
21+
<Filter className="h-4 w-4" />
22+
Filters
23+
</Button>
24+
</div>
25+
</section>
26+
);
27+
}

src/components/home/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { default as HeroSection } from "./hero-section";
2+
export { default as HomeFilters } from "./home-filters";

src/components/layout/navigation.tsx

Lines changed: 6 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -1,245 +1,27 @@
11
"use client";
22

3+
import Link from "next/link";
34
import {
45
Navbar,
56
NavbarBrand,
67
NavbarContent,
78
NavbarItem,
8-
Link,
99
Button,
1010
DropdownItem,
1111
DropdownTrigger,
1212
Dropdown,
1313
DropdownMenu,
1414
} from "@heroui/react";
15-
import {
16-
ActivityIcon,
17-
ChevronDown,
18-
Flashlight,
19-
LockIcon,
20-
Origami,
21-
ServerCog,
22-
User,
23-
} from "lucide-react";
24-
25-
export const Lock = ({ fill, size, height, width, ...props }) => {
26-
const color = fill;
27-
28-
return (
29-
<svg
30-
height={size || height || 24}
31-
viewBox="0 0 24 24"
32-
width={size || width || 24}
33-
xmlns="http://www.w3.org/2000/svg"
34-
{...props}
35-
>
36-
<g transform="translate(3.5 2)">
37-
<path
38-
d="M9.121,6.653V4.5A4.561,4.561,0,0,0,0,4.484V6.653"
39-
fill="none"
40-
stroke={color}
41-
strokeLinecap="round"
42-
strokeLinejoin="round"
43-
strokeMiterlimit="10"
44-
strokeWidth={1.5}
45-
transform="translate(3.85 0.75)"
46-
/>
47-
<path
48-
d="M.5,0V2.221"
49-
fill="none"
50-
stroke={color}
51-
strokeLinecap="round"
52-
strokeLinejoin="round"
53-
strokeMiterlimit="10"
54-
strokeWidth={1.5}
55-
transform="translate(7.91 12.156)"
56-
/>
57-
<path
58-
d="M7.66,0C1.915,0,0,1.568,0,6.271s1.915,6.272,7.66,6.272,7.661-1.568,7.661-6.272S13.4,0,7.66,0Z"
59-
fill="none"
60-
stroke={color}
61-
strokeLinecap="round"
62-
strokeLinejoin="round"
63-
strokeMiterlimit="10"
64-
strokeWidth={1.5}
65-
transform="translate(0.75 6.824)"
66-
/>
67-
</g>
68-
</svg>
69-
);
70-
};
71-
72-
export const Activity = ({ fill, size, height, width, ...props }) => {
73-
return (
74-
<svg
75-
height={size || height || 24}
76-
viewBox="0 0 24 24"
77-
width={size || width || 24}
78-
xmlns="http://www.w3.org/2000/svg"
79-
{...props}
80-
>
81-
<g
82-
fill="none"
83-
stroke={fill}
84-
strokeLinecap="round"
85-
strokeLinejoin="round"
86-
strokeMiterlimit={10}
87-
strokeWidth={1.5}
88-
>
89-
<path d="M6.918 14.854l2.993-3.889 3.414 2.68 2.929-3.78" />
90-
<path d="M19.668 2.35a1.922 1.922 0 11-1.922 1.922 1.921 1.921 0 011.922-1.922z" />
91-
<path d="M20.756 9.269a20.809 20.809 0 01.194 3.034c0 6.938-2.312 9.25-9.25 9.25s-9.25-2.312-9.25-9.25 2.313-9.25 9.25-9.25a20.931 20.931 0 012.983.187" />
92-
</g>
93-
</svg>
94-
);
95-
};
96-
97-
export const Flash = ({
98-
fill = "currentColor",
99-
size,
100-
height,
101-
width,
102-
...props
103-
}) => {
104-
return (
105-
<svg
106-
fill="none"
107-
height={size || height}
108-
viewBox="0 0 24 24"
109-
width={size || width}
110-
xmlns="http://www.w3.org/2000/svg"
111-
{...props}
112-
>
113-
<path
114-
d="M6.09 13.28h3.09v7.2c0 1.68.91 2.02 2.02.76l7.57-8.6c.93-1.05.54-1.92-.87-1.92h-3.09v-7.2c0-1.68-.91-2.02-2.02-.76l-7.57 8.6c-.92 1.06-.53 1.92.87 1.92Z"
115-
stroke={fill}
116-
strokeLinecap="round"
117-
strokeLinejoin="round"
118-
strokeMiterlimit={10}
119-
strokeWidth={1.5}
120-
/>
121-
</svg>
122-
);
123-
};
124-
125-
export const Server = ({
126-
fill = "currentColor",
127-
size,
128-
height,
129-
width,
130-
...props
131-
}) => {
132-
return (
133-
<svg
134-
fill="none"
135-
height={size || height}
136-
viewBox="0 0 24 24"
137-
width={size || width}
138-
xmlns="http://www.w3.org/2000/svg"
139-
{...props}
140-
>
141-
<path
142-
d="M19.32 10H4.69c-1.48 0-2.68-1.21-2.68-2.68V4.69c0-1.48 1.21-2.68 2.68-2.68h14.63C20.8 2.01 22 3.22 22 4.69v2.63C22 8.79 20.79 10 19.32 10ZM19.32 22H4.69c-1.48 0-2.68-1.21-2.68-2.68v-2.63c0-1.48 1.21-2.68 2.68-2.68h14.63c1.48 0 2.68 1.21 2.68 2.68v2.63c0 1.47-1.21 2.68-2.68 2.68ZM6 5v2M10 5v2M6 17v2M10 17v2M14 6h4M14 18h4"
143-
stroke={fill}
144-
strokeLinecap="round"
145-
strokeLinejoin="round"
146-
strokeWidth={1.5}
147-
/>
148-
</svg>
149-
);
150-
};
151-
152-
export const TagUser = ({
153-
fill = "currentColor",
154-
size,
155-
height,
156-
width,
157-
...props
158-
}) => {
159-
return (
160-
<svg
161-
fill="none"
162-
height={size || height}
163-
viewBox="0 0 24 24"
164-
width={size || width}
165-
xmlns="http://www.w3.org/2000/svg"
166-
{...props}
167-
>
168-
<path
169-
d="M18 18.86h-.76c-.8 0-1.56.31-2.12.87l-1.71 1.69c-.78.77-2.05.77-2.83 0l-1.71-1.69c-.56-.56-1.33-.87-2.12-.87H6c-1.66 0-3-1.33-3-2.97V4.98c0-1.64 1.34-2.97 3-2.97h12c1.66 0 3 1.33 3 2.97v10.91c0 1.63-1.34 2.97-3 2.97Z"
170-
stroke={fill}
171-
strokeLinecap="round"
172-
strokeLinejoin="round"
173-
strokeMiterlimit={10}
174-
strokeWidth={1.5}
175-
/>
176-
<path
177-
d="M12 10a2.33 2.33 0 1 0 0-4.66A2.33 2.33 0 0 0 12 10ZM16 15.66c0-1.8-1.79-3.26-4-3.26s-4 1.46-4 3.26"
178-
stroke={fill}
179-
strokeLinecap="round"
180-
strokeLinejoin="round"
181-
strokeWidth={1.5}
182-
/>
183-
</svg>
184-
);
185-
};
186-
187-
export const Scale = ({
188-
fill = "currentColor",
189-
size,
190-
height,
191-
width,
192-
...props
193-
}) => {
194-
return (
195-
<svg
196-
fill="none"
197-
height={size || height}
198-
viewBox="0 0 24 24"
199-
width={size || width}
200-
xmlns="http://www.w3.org/2000/svg"
201-
{...props}
202-
>
203-
<path
204-
d="M9 22h6c5 0 7-2 7-7V9c0-5-2-7-7-7H9C4 2 2 4 2 9v6c0 5 2 7 7 7ZM18 6 6 18"
205-
stroke={fill}
206-
strokeLinecap="round"
207-
strokeLinejoin="round"
208-
strokeWidth={1.5}
209-
/>
210-
<path
211-
d="M18 10V6h-4M6 14v4h4"
212-
stroke={fill}
213-
strokeLinecap="round"
214-
strokeLinejoin="round"
215-
strokeWidth={1.5}
216-
/>
217-
</svg>
218-
);
219-
};
15+
import { ChevronDown, Origami, User } from "lucide-react";
22016

22117
export default function App() {
222-
const icons = {
223-
chevron: <ChevronDown fill="currentColor" size={16} />,
224-
// scale: <Scale className="text-warning" fill="currentColor" size={30} />,
225-
lock: <LockIcon className="text-success" fill="currentColor" size={30} />,
226-
activity: (
227-
<ActivityIcon className="text-secondary" fill="currentColor" size={30} />
228-
),
229-
flash: (
230-
<Flashlight className="text-primary" fill="currentColor" size={30} />
231-
),
232-
server: (
233-
<ServerCog className="text-success" fill="currentColor" size={30} />
234-
),
235-
user: <User className="text-danger" fill="currentColor" size={30} />,
236-
};
237-
23818
return (
23919
<Navbar maxWidth="full">
24020
<NavbarBrand>
241-
<Origami size={30} />
242-
<p className="text-2xl font-bold text-inherit ml-2">Code Atlas</p>
21+
<Link href="/" className="flex items-center gap-1">
22+
<Origami size={30} />
23+
<p className="text-2xl font-bold text-inherit">Code Atlas</p>
24+
</Link>
24325
</NavbarBrand>
24426
<NavbarContent className="hidden sm:flex gap-4" justify="center">
24527
<Dropdown>

src/components/resources/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { default as ResourceCard } from "./resource-card";
2+
export { default as ResourceList } from "./resource-list";

0 commit comments

Comments
 (0)