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) {
|
function formatDate(timestamp: string) {
|
||||||
const date = new Date(timestamp)
|
const date = new Date(timestamp)
|
||||||
return date.toLocaleDateString('en-US', {
|
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 } }) {
|
export default async function BlogPostPage({ params }: { params: { slug: string } }) {
|
||||||
const post = await getBlogPost(params.slug)
|
const post = await getBlogPost(params.slug)
|
||||||
|
|
||||||
|
|
@ -88,6 +118,10 @@ export default async function BlogPostPage({ params }: { params: { slug: string
|
||||||
notFound()
|
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)
|
const heroImage = resolveMediaUrl(post.previewImage)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -98,38 +132,41 @@ export default async function BlogPostPage({ params }: { params: { slug: string
|
||||||
width: '100%',
|
width: '100%',
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
padding: '48px 20px',
|
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
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
backgroundColor: '#F7F1E1',
|
backgroundColor: '#F7F1E1',
|
||||||
border: '2px solid #8B7D6B',
|
border: '2px solid #8B7D6B',
|
||||||
padding: '32px',
|
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 && (
|
{heroImage && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -230,7 +267,138 @@ export default async function BlogPostPage({ params }: { params: { slug: string
|
||||||
</footer>
|
</footer>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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>
|
</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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue