Skip to content

Commit da568a7

Browse files
committed
feat: add "Recently Updated" section to homepage
1 parent 686847d commit da568a7

File tree

1 file changed

+175
-112
lines changed

1 file changed

+175
-112
lines changed

pages/index.tsx

Lines changed: 175 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -2,139 +2,202 @@ import Link from 'next/link';
22
import Header from '../components/Header';
33
import Footer from '../components/Footer';
44
import { GetStaticProps } from 'next';
5-
import {getPostsMetaOnly} from '../lib/posts';
5+
import { getPostsMetaOnly } from '../lib/posts';
66
import AdSense from '../components/AdSense';
77
import TopNotice from "../components/TopNotice";
8-
import {catImages} from "../lib/mainImage";
8+
import { catImages } from "../lib/mainImage";
99

1010
type Post = {
11-
id: string;
12-
title: string;
13-
summary?: string;
14-
description?: string;
11+
id: string;
12+
title: string;
13+
summary?: string;
14+
description?: string;
15+
date: string;
16+
updated?: string;
1517
};
1618

1719
type HomeProps = {
18-
allPostsData: Post[];
19-
gradientsForPosts: string[];
20+
allPostsData: Post[];
21+
gradientsForPosts: string[];
2022
};
2123

2224
function shuffle<T>(array: T[]): T[] {
23-
const copy = [...array];
24-
for (let i = copy.length - 1; i > 0; i--) {
25-
const j = Math.floor(Math.random() * (i + 1));
26-
[copy[i], copy[j]] = [copy[j], copy[i]];
27-
}
28-
return copy;
25+
const copy = [...array];
26+
for (let i = copy.length - 1; i > 0; i--) {
27+
const j = Math.floor(Math.random() * (i + 1));
28+
[copy[i], copy[j]] = [copy[j], copy[i]];
29+
}
30+
return copy;
2931
}
3032

3133
export const getStaticProps: GetStaticProps<HomeProps> = async () => {
32-
const allPostsData = getPostsMetaOnly();
33-
const [latest, ...restAll] = allPostsData;
34-
35-
const rest = restAll.filter((post) => post.id !== latest.id).slice(0, 3);
36-
37-
const shuffledImages = shuffle(catImages).slice(0, rest.length);
38-
39-
return {
40-
props: {
41-
allPostsData,
42-
gradientsForPosts: shuffledImages,
43-
},
44-
};
34+
const allPostsData = getPostsMetaOnly();
35+
36+
const [latest, ...restAll] = allPostsData;
37+
const rest = restAll.filter((post) => post.id !== latest.id).slice(0, 3);
38+
const shuffledImages = shuffle(catImages).slice(0, rest.length);
39+
40+
return {
41+
props: {
42+
allPostsData,
43+
gradientsForPosts: shuffledImages,
44+
},
45+
};
4546
};
4647

48+
// 날짜 내림차순 비교 (undefined 안전)
49+
const byDesc = (a?: string, b?: string) =>
50+
new Date(b ?? '1970-01-01').getTime() - new Date(a ?? '1970-01-01').getTime();
51+
4752
export default function Home({ allPostsData, gradientsForPosts }: HomeProps) {
48-
const [latest, ...restAll] = allPostsData;
49-
const rest = restAll.filter((post) => post.id !== latest.id).slice(0, 3);
50-
51-
return (
52-
<>
53-
<TopNotice />
54-
<div className="min-h-screen">
55-
<Header isDark={false} />
56-
57-
{/* Hero Section */}
58-
<section className="text-center pt-40 pb-28 px-4">
59-
<h1 className="text-4xl sm:text-5xl font-extrabold mb-6">Hi, I’m a <strong>considerate developer</strong>.</h1>
60-
<p className="text-lg text-gray-600 leading-loose space-y-2">
61-
I believe being considerate means writing <strong>clean, readable code</strong>,<br />
62-
building <strong>predictable and testable systems</strong>,<br />
63-
and delivering <strong>reliable, trustworthy services</strong> that users can depend on.<br /><br />
64-
I’m constantly <strong>learning and growing</strong> to become better at this,<br />
65-
and this blog is where I share my <strong>journey as a learning developer</strong>.
66-
</p>
67-
</section>
68-
69-
70-
<div className="bg-gray-50 pt-32 pb-20 px-4">
71-
<main className="max-w-4xl mx-auto px-4 pb-32 space-y-24">
72-
{/* Latest Post */}
73-
<section>
53+
if (!allPostsData.length) {
54+
return (
55+
<>
56+
<TopNotice />
57+
<Header isDark={false} />
58+
<main className="max-w-3xl mx-auto px-6 py-24">
59+
<h1 className="text-3xl font-bold">No posts yet</h1>
60+
</main>
61+
<Footer />
62+
</>
63+
);
64+
}
65+
66+
// 최신 글(작성일 기준)
67+
const [latest, ...restAll] = allPostsData;
68+
69+
// 최근 업데이트 글(업데이트일 없으면 작성일로 대체)
70+
const updatedSorted = [...allPostsData].sort((a, b) =>
71+
byDesc(a.updated ?? a.date, b.updated ?? b.date)
72+
);
73+
const updatedPost =
74+
updatedSorted.find((p) => p.id !== latest.id) ?? updatedSorted[0];
75+
76+
const rest = restAll
77+
.filter((post) => post.id !== updatedPost.id)
78+
.slice(0, 3);
79+
80+
return (
81+
<>
82+
<TopNotice />
83+
<div className="min-h-screen">
84+
<Header isDark={false} />
85+
86+
{/* Hero */}
87+
<section className="text-center pt-40 pb-28 px-4">
88+
<h1 className="text-4xl sm:text-5xl font-extrabold mb-6">
89+
Hi, I’m a <strong>considerate developer</strong>.
90+
</h1>
91+
<p className="text-lg text-gray-600 leading-loose space-y-2">
92+
I believe being considerate means writing <strong>clean, readable code</strong>,<br />
93+
building <strong>predictable and testable systems</strong>,<br />
94+
and delivering <strong>reliable, trustworthy services</strong> that users can depend on.<br /><br />
95+
I’m constantly <strong>learning and growing</strong> to become better at this,<br />
96+
and this blog is where I share my <strong>journey as a learning developer</strong>.
97+
</p>
98+
</section>
99+
100+
<div className="bg-gray-50 pt-32 pb-20 px-4">
101+
<main className="max-w-4xl mx-auto px-4 pb-32 space-y-24">
102+
{/* Latest Post */}
103+
<section>
74104
<span className="inline-block px-3 py-1 text-xs font-semibold rounded-full bg-white text-gray-500 uppercase tracking-wide">
75105
Latest Post
76106
</span>
77107

78-
<h2 className="mt-4 text-4xl font-extrabold text-gray-900">
79-
{latest.title}
80-
</h2>
81-
82-
<p className="mt-4 text-lg text-gray-600 leading-relaxed">
83-
{latest.summary || 'No summary provided.'}
84-
</p>
85-
86-
{latest.description && (
87-
<p className="mt-2 text-base text-gray-500 leading-relaxed">
88-
{latest.description}
89-
</p>
90-
)}
91-
92-
<Link
93-
href={`/post/${latest.id}`}
94-
className="inline-block mt-6 bg-blue-600 hover:bg-blue-700 text-white font-semibold px-6 py-2 rounded transition"
95-
>
96-
Continue Reading →
97-
</Link>
98-
</section>
99-
100-
<AdSense />
101-
102-
{/* More Posts */}
103-
<section>
104-
<div className="flex justify-between items-center mb-4">
108+
<h2 className="mt-4 text-4xl font-extrabold text-gray-900">
109+
{latest.title}
110+
</h2>
111+
112+
<p className="mt-4 text-lg text-gray-600 leading-relaxed">
113+
{latest.summary || 'No summary provided.'}
114+
</p>
115+
116+
{latest.description && (
117+
<p className="mt-2 text-base text-gray-500 leading-relaxed">
118+
{latest.description}
119+
</p>
120+
)}
121+
122+
<Link
123+
href={`/post/${latest.id}`}
124+
className="inline-block mt-6 bg-blue-600 hover:bg-blue-700 text-white font-semibold px-6 py-2 rounded transition"
125+
>
126+
Continue Reading →
127+
</Link>
128+
</section>
129+
130+
{/* Recently Updated */}
131+
<section>
132+
<span className="inline-block px-3 py-1 text-xs font-semibold rounded-full bg-white text-gray-500 uppercase tracking-wide">
133+
Recently Updated
134+
</span>
135+
136+
<h2 className="mt-4 text-3xl font-bold text-gray-900">
137+
{updatedPost.title}
138+
</h2>
139+
140+
<p className="mt-2 text-sm text-gray-500">
141+
Updated: {updatedPost.updated ?? updatedPost.date}
142+
</p>
143+
144+
<p className="mt-4 text-lg text-gray-600 leading-relaxed">
145+
{updatedPost.summary || 'No summary provided.'}
146+
</p>
147+
148+
{updatedPost.description && (
149+
<p className="mt-2 text-base text-gray-500 leading-relaxed">
150+
{updatedPost.description}
151+
</p>
152+
)}
153+
154+
<Link
155+
href={`/post/${updatedPost.id}`}
156+
className="inline-block mt-6 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold px-6 py-2 rounded transition"
157+
>
158+
Read Updated Post →
159+
</Link>
160+
</section>
161+
162+
<AdSense />
163+
164+
{/* More Posts */}
165+
<section>
166+
<div className="flex justify-between items-center mb-4">
105167
<span className="inline-block px-3 py-1 text-xs font-semibold rounded-full bg-white text-gray-500 uppercase tracking-wide">
106168
More Posts
107169
</span>
108-
<Link
109-
href="/post"
110-
className="text-sm text-gray-500 hover:text-gray-700 flex items-center gap-1"
111-
>
112-
Read all →
113-
</Link>
114-
</div>
115-
<div className="grid md:grid-cols-3 gap-6">
116-
{rest.map(({ id, title }, idx) => (
117-
<Link key={id} href={`/post/${id}`}>
118-
<div
119-
className="flex flex-col justify-end p-6 h-48 sm:h-64 rounded-xl text-white shadow hover:shadow-xl transition"
120-
style={{
121-
backgroundImage: `url(${gradientsForPosts[idx]})`,
122-
backgroundSize: "cover",
123-
backgroundPosition: "center",
124-
}}
125-
>
126-
<h3 className="text-lg font-semibold leading-snug bg-black bg-opacity-50 p-2 rounded">
127-
{title}
128-
</h3>
129-
</div>
130-
</Link>
131-
))}
132-
</div>
133-
</section>
134-
</main>
135-
</div>
136-
</div>
137-
<Footer />
138-
</>
139-
);
170+
<Link
171+
href="/post"
172+
className="text-sm text-gray-500 hover:text-gray-700 flex items-center gap-1"
173+
>
174+
Read all →
175+
</Link>
176+
</div>
177+
<div className="grid md:grid-cols-3 gap-6">
178+
{rest.map(({ id, title }, idx) => (
179+
<Link key={id} href={`/post/${id}`}>
180+
<div
181+
className="flex flex-col justify-end p-6 h-48 sm:h-64 rounded-xl text-white shadow hover:shadow-xl transition"
182+
style={{
183+
backgroundImage: `url(${gradientsForPosts[idx]})`,
184+
backgroundSize: "cover",
185+
backgroundPosition: "center",
186+
}}
187+
>
188+
<h3 className="text-lg font-semibold leading-snug bg-black/50 p-2 rounded">
189+
{title}
190+
</h3>
191+
</div>
192+
</Link>
193+
))}
194+
</div>
195+
</section>
196+
</main>
197+
</div>
198+
</div>
199+
200+
<Footer />
201+
</>
202+
);
140203
}

0 commit comments

Comments
 (0)