This commit is contained in:
parent
f36fe6276c
commit
70991a4345
|
|
@ -27,6 +27,27 @@ async function getBlogPost(slug: string): Promise<BlogPost | null> {
|
|||
}
|
||||
}
|
||||
|
||||
async function getAllBlogPosts(): Promise<BlogPost[]> {
|
||||
const baseUrl = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:4005'
|
||||
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/posts`, {
|
||||
cache: 'no-store',
|
||||
next: { revalidate: 0 }
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
return []
|
||||
}
|
||||
|
||||
const payload = await response.json()
|
||||
return Array.isArray(payload.data) ? payload.data : []
|
||||
} catch (error) {
|
||||
console.error('[frontend] Failed to load posts', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(timestamp: string) {
|
||||
const date = new Date(timestamp)
|
||||
return date.toLocaleDateString('en-US', {
|
||||
|
|
@ -81,6 +102,15 @@ function renderSection(section: BlogPostSection) {
|
|||
)
|
||||
}
|
||||
|
||||
function shuffleArray<T>(array: T[]): T[] {
|
||||
const shuffled = [...array]
|
||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
|
||||
}
|
||||
return shuffled
|
||||
}
|
||||
|
||||
export default async function BlogPostPage({ params }: { params: { slug: string } }) {
|
||||
const post = await getBlogPost(params.slug)
|
||||
|
||||
|
|
@ -88,6 +118,10 @@ export default async function BlogPostPage({ params }: { params: { slug: string
|
|||
notFound()
|
||||
}
|
||||
|
||||
const allPosts = await getAllBlogPosts()
|
||||
const otherPosts = allPosts.filter(p => p.id !== post.id)
|
||||
const randomPosts = shuffleArray(otherPosts).slice(0, 3)
|
||||
|
||||
const heroImage = resolveMediaUrl(post.previewImage)
|
||||
|
||||
return (
|
||||
|
|
@ -98,38 +132,41 @@ export default async function BlogPostPage({ params }: { params: { slug: string
|
|||
width: '100%',
|
||||
margin: '0 auto',
|
||||
padding: '48px 20px',
|
||||
backgroundColor: '#F7F1E1'
|
||||
backgroundColor: '#F7F1E1',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '16px',
|
||||
left: '20px',
|
||||
padding: '8px 12px',
|
||||
border: '1px solid #8B7D6B',
|
||||
backgroundColor: '#F7F1E1',
|
||||
fontFamily: 'Space Mono, monospace',
|
||||
fontSize: '12px',
|
||||
letterSpacing: '0.1em',
|
||||
textTransform: 'uppercase',
|
||||
textDecoration: 'none',
|
||||
color: '#1E1A17',
|
||||
zIndex: 10
|
||||
}}
|
||||
>
|
||||
← Back
|
||||
</a>
|
||||
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
backgroundColor: '#F7F1E1',
|
||||
border: '2px solid #8B7D6B',
|
||||
padding: '32px',
|
||||
boxShadow: '0 20px 60px rgba(0,0,0,0.35)'
|
||||
boxShadow: '0 20px 60px rgba(0,0,0,0.35)',
|
||||
marginTop: '48px'
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '16px',
|
||||
left: '16px',
|
||||
padding: '8px 12px',
|
||||
border: '1px solid #8B7D6B',
|
||||
backgroundColor: 'transparent',
|
||||
fontFamily: 'Space Mono, monospace',
|
||||
fontSize: '12px',
|
||||
letterSpacing: '0.1em',
|
||||
textTransform: 'uppercase',
|
||||
textDecoration: 'none',
|
||||
color: '#1E1A17'
|
||||
}}
|
||||
>
|
||||
← Back
|
||||
</a>
|
||||
|
||||
{heroImage && (
|
||||
<div
|
||||
style={{
|
||||
|
|
@ -230,7 +267,138 @@ export default async function BlogPostPage({ params }: { params: { slug: string
|
|||
</footer>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{randomPosts.length > 0 && (
|
||||
<section style={{ marginTop: '64px' }}>
|
||||
<h2 style={{
|
||||
fontFamily: 'Abril Fatface, serif',
|
||||
fontSize: '32px',
|
||||
color: '#1E1A17',
|
||||
marginBottom: '32px',
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
More Articles
|
||||
</h2>
|
||||
<div style={{ display: 'grid', gap: '24px' }}>
|
||||
{randomPosts.map(article => {
|
||||
const previewUrl = resolveMediaUrl(article.previewImage)
|
||||
return (
|
||||
<a
|
||||
key={article.id}
|
||||
href={`/blog/${article.slug}`}
|
||||
style={{
|
||||
display: 'block',
|
||||
backgroundColor: 'white',
|
||||
border: '2px solid #8B7D6B',
|
||||
padding: '16px',
|
||||
textDecoration: 'none',
|
||||
color: 'inherit',
|
||||
transition: 'transform 0.2s'
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: '16px' }}>
|
||||
{previewUrl && (
|
||||
<div
|
||||
style={{
|
||||
width: '120px',
|
||||
height: '120px',
|
||||
flexShrink: 0,
|
||||
backgroundImage: `url('${previewUrl}')`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
border: '1px solid #8B7D6B'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{
|
||||
fontFamily: 'Abril Fatface, serif',
|
||||
fontSize: '18px',
|
||||
color: '#1E1A17',
|
||||
marginBottom: '8px'
|
||||
}}>
|
||||
{article.title}
|
||||
</h3>
|
||||
{article.excerpt && (
|
||||
<p style={{
|
||||
fontFamily: 'Spectral, serif',
|
||||
fontSize: '14px',
|
||||
color: '#4A4A4A',
|
||||
lineHeight: 1.5,
|
||||
marginBottom: '8px'
|
||||
}}>
|
||||
{article.excerpt.substring(0, 100)}...
|
||||
</p>
|
||||
)}
|
||||
<span style={{
|
||||
fontFamily: 'Space Mono, monospace',
|
||||
fontSize: '10px',
|
||||
color: '#8B7D6B',
|
||||
letterSpacing: '0.1em',
|
||||
textTransform: 'uppercase'
|
||||
}}>
|
||||
Read More →
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<footer
|
||||
style={{
|
||||
backgroundColor: '#F7F1E1',
|
||||
borderTop: '2px solid #8B7D6B',
|
||||
padding: '32px 20px',
|
||||
marginTop: '64px'
|
||||
}}
|
||||
>
|
||||
<div style={{ maxWidth: '820px', margin: '0 auto' }}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<h3
|
||||
style={{
|
||||
fontFamily: 'Abril Fatface, serif',
|
||||
fontSize: '24px',
|
||||
margin: 0,
|
||||
color: '#1E1A17'
|
||||
}}
|
||||
>
|
||||
The Curated Finds
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontFamily: 'Spectral, serif',
|
||||
fontSize: '14px',
|
||||
color: '#4A4A4A',
|
||||
marginTop: '8px'
|
||||
}}
|
||||
>
|
||||
Handpicked treasures. Honest descriptions. Careful packaging.
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
margin: '16px auto',
|
||||
width: '120px',
|
||||
height: '2px',
|
||||
backgroundColor: '#8B7D6B'
|
||||
}}
|
||||
/>
|
||||
<p
|
||||
style={{
|
||||
color: '#8B7D6B',
|
||||
fontFamily: 'Space Mono, monospace',
|
||||
fontSize: '12px'
|
||||
}}
|
||||
>
|
||||
(c) {new Date().getFullYear()} The Curated Finds - All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue