Skip to content

Commit 2c418b0

Browse files
authored
Merge pull request #449 from 1chooo/feat/portfolio
2 parents 6e4f380 + ab26fd8 commit 2c418b0

File tree

4 files changed

+119
-149
lines changed

4 files changed

+119
-149
lines changed

apps/web/src/app/mock/page.tsx

-113
This file was deleted.

apps/web/src/app/mock/[slug]/page.tsx apps/web/src/app/portfolio/[slug]/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function generateMetadata({
3737
description,
3838
type: 'article',
3939
publishedTime,
40-
url: `https://1chooo.com/mock/${post.slug}`,
40+
url: `https://1chooo.com/portfolio/${post.slug}`,
4141
locale: 'en_US',
4242
images: [
4343
{

apps/web/src/app/portfolio/page.tsx

+106-30
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
'use client';
2-
3-
import React, { useEffect } from 'react';
4-
import { usePathname } from 'next/navigation';
5-
import PageContent from '@/components/page-content';
6-
import Projects from '@/components/portfolio/projects';
7-
import { initializeCustomSelect, filterItemsByCategory } from '@/lib/utils/dom-utils';
8-
import config from '@/config';
9-
10-
const { title } = config;
1+
import Link from "next/link";
2+
import Image from "next/image";
3+
import PageHeader from "@/components/page-header";
4+
import FilterSelectBox from "@/components/portfolio/v2/filter-select-box";
5+
import FilterList from "@/components/portfolio/v2/filter-list";
6+
import MarkdownRenderer from "@/components/markdown/markdown-renderer";
7+
import { getPortfolioPosts } from "@/lib/db/portfolio";
8+
import config from "@/config";
9+
import { LuEye } from "react-icons/lu";
10+
import "react-loading-skeleton/dist/skeleton.css";
1111

1212
/**
1313
* TODO: #257
@@ -17,28 +17,104 @@ const { title } = config;
1717
* };
1818
*/
1919

20-
const Portfolio = () => {
21-
const pathname = usePathname();
20+
const { title } = config;
21+
const POSTS_PER_PAGE = 9;
22+
23+
export const metadata = {
24+
title: `Portfolio | ${title}`,
25+
description: "Read my thoughts on software development, design, and more.",
26+
};
27+
28+
export default function Portfolio({
29+
searchParams,
30+
}: {
31+
searchParams: { tag?: string; page?: string };
32+
}) {
33+
let allBlogs = getPortfolioPosts();
34+
const blogTags = [
35+
"All",
36+
...Array.from(
37+
new Set(allBlogs.map((post) => post.metadata.category ?? ""))
38+
),
39+
];
40+
const selectedTag = searchParams.tag || "All";
41+
const currentPage = parseInt(searchParams.page || "1", 10);
2242

23-
useEffect(() => {
24-
initializeCustomSelect(filterItemsByCategory);
25-
}, []);
43+
// Filter blogs based on the selected tag
44+
const filteredBlogs =
45+
selectedTag === "All"
46+
? allBlogs
47+
: allBlogs.filter((post) => post.metadata.category === selectedTag);
48+
49+
// Sort blogs by date
50+
const sortedBlogs = filteredBlogs.sort((a, b) => {
51+
if (new Date(a.metadata.publishedAt) > new Date(b.metadata.publishedAt)) {
52+
return -1;
53+
}
54+
return 1;
55+
});
56+
57+
// Calculate total pages
58+
const totalPages = Math.ceil(sortedBlogs.length / POSTS_PER_PAGE);
59+
60+
// Get blogs for current page
61+
const paginatedBlogs = sortedBlogs.slice(
62+
(currentPage - 1) * POSTS_PER_PAGE,
63+
currentPage * POSTS_PER_PAGE
64+
);
2665

2766
return (
28-
/**
29-
* TODO: #257
30-
* update the document title see (#341)
31-
*/
32-
<PageContent
33-
documentTitle='Portfolio'
34-
title={title}
35-
header="Hugo's Portfolio"
36-
page="portfolio"
37-
pathName={pathname}
38-
>
39-
<Projects />
40-
</PageContent>
67+
<article>
68+
<PageHeader header="Hugo's Portfolio" />
69+
<section className="projects">
70+
<FilterList selectedTag={selectedTag} blogTags={blogTags} />
71+
<FilterSelectBox selectedTag={selectedTag} blogTags={blogTags} />
72+
<ul className="project-list">
73+
{paginatedBlogs.map((post, index) => (
74+
<li
75+
key={index}
76+
className="project-item active"
77+
data-category={post.metadata.category}
78+
>
79+
<Link href={`/portfolio/${post.slug}`} rel="noopener noreferrer">
80+
<figure className="project-img">
81+
<div className="project-item-icon-box">
82+
<LuEye />
83+
</div>
84+
<Image
85+
src={post.metadata.banner}
86+
alt={post.metadata.alt || "Portfolio post image"}
87+
width={1600}
88+
height={900}
89+
priority={true}
90+
placeholder="empty"
91+
loading="eager"
92+
/>
93+
</figure>
94+
<h3 className="project-title"><MarkdownRenderer content={post.metadata.title} /></h3>
95+
<p className="project-category">{post.metadata.category}</p>
96+
</Link>
97+
</li>
98+
))}
99+
</ul>
100+
<div className="pagination">
101+
{Array.from({ length: totalPages }, (_, i) => i + 1).map(
102+
(pageNum) => (
103+
<Link
104+
key={pageNum}
105+
href={{
106+
pathname: "/portfolio",
107+
query: { ...searchParams, page: pageNum.toString() },
108+
}}
109+
className={`pagination-btn ${pageNum === currentPage ? "active" : ""
110+
}`}
111+
>
112+
{pageNum}
113+
</Link>
114+
)
115+
)}
116+
</div>
117+
</section>
118+
</article>
41119
);
42120
}
43-
44-
export default Portfolio;

apps/web/src/app/sitemap.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1-
/*
2-
* Go to https://github.com/leerob/site/blob/1129b6d81937cef493edb060f87e6f2ac9f335ed/app/sitemap.ts to see details
3-
*/
4-
51
import { getBlogPosts } from '@/lib/db/blog';
2+
import { getPortfolioPosts } from '@/lib/db/portfolio';
63

4+
/**
5+
* This function returns an array of objects with the URL and last modified date
6+
* @see https://github.com/leerob/site/blob/1129b6d81937cef493edb060f87e6f2ac9f335ed/app/sitemap.ts
7+
* @returns [{ url: string, lastModified: string }]
8+
*/
79
export default async function sitemap() {
810
let blogs = getBlogPosts().map((post) => ({
911
url: `https://1chooo.com/blog/${post.slug}`,
1012
lastModified: post.metadata.publishedAt,
1113
}));
1214

15+
let portfolios = getPortfolioPosts().map((post) => ({
16+
url: `https://1chooo.com/portfolio/${post.slug}`,
17+
lastModified: post.metadata.publishedAt,
18+
}));
19+
1320
let routes = ['', '/resume', '/portfolio', '/blog', '/contact'].map((route) => ({
1421
url: `https://1chooo.com${route}`,
1522
lastModified: new Date().toISOString().split('T')[0],
1623
}));
1724

18-
return [...routes, ...blogs];
25+
return [...routes, ...blogs, ...portfolios];
1926
}

0 commit comments

Comments
 (0)