Kategorien
This commit is contained in:
@@ -1,9 +1,21 @@
|
||||
'use client'
|
||||
|
||||
import { FormEvent, useEffect, useMemo, useState } from 'react'
|
||||
import { BlogPost, BlogPostSection } from '@/types/blog'
|
||||
import { BlogPost, BlogPostSection, CATEGORY_LABELS, BlogCategory } from '@/types/blog'
|
||||
import { resolveMediaUrl } from '@/lib/media'
|
||||
|
||||
function getCategoryLabel(category: string | null | undefined): string {
|
||||
if (!category) return 'None'
|
||||
|
||||
// Try as slug first
|
||||
if (category in CATEGORY_LABELS) {
|
||||
return CATEGORY_LABELS[category as BlogCategory]
|
||||
}
|
||||
|
||||
// Return as-is if it's already a display name
|
||||
return category
|
||||
}
|
||||
|
||||
const MAX_SECTIONS = 5
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:4005'
|
||||
|
||||
@@ -63,6 +75,7 @@ export default function AdminPage() {
|
||||
const [linkUrl, setLinkUrl] = useState('')
|
||||
const [footer, setFooter] = useState('')
|
||||
const [isEditorsPick, setIsEditorsPick] = useState(false)
|
||||
const [category, setCategory] = useState<string>('')
|
||||
const [mainImage, setMainImage] = useState<MainImageState>({
|
||||
file: null,
|
||||
existing: null,
|
||||
@@ -103,6 +116,7 @@ export default function AdminPage() {
|
||||
setLinkUrl('')
|
||||
setFooter('')
|
||||
setIsEditorsPick(false)
|
||||
setCategory('')
|
||||
setMainImage({ file: null, existing: null, previewUrl: null, removed: false })
|
||||
setSections(ensureSectionSlots([]))
|
||||
setEditingPost(null)
|
||||
@@ -114,6 +128,16 @@ export default function AdminPage() {
|
||||
setLinkUrl(post.linkUrl || '')
|
||||
setFooter(post.footer || '')
|
||||
setIsEditorsPick(post.isEditorsPick)
|
||||
|
||||
// Convert old format to new format
|
||||
const categoryMap: Record<string, string> = {
|
||||
'Books & Magazine': 'books-magazine',
|
||||
'Clothing & Shoes': 'clothing-shoes',
|
||||
'Collectibles & Art': 'collectibles-art',
|
||||
'Toys & Hobbies': 'toys-hobbies'
|
||||
}
|
||||
const normalizedCategory = post.category ? (categoryMap[post.category] || post.category) : ''
|
||||
setCategory(normalizedCategory)
|
||||
setMainImage({
|
||||
file: null,
|
||||
existing: post.previewImage || null,
|
||||
@@ -252,6 +276,7 @@ export default function AdminPage() {
|
||||
linkUrl: normaliseText(linkUrl) || undefined,
|
||||
footer: normaliseText(footer) || undefined,
|
||||
isEditorsPick,
|
||||
category: category || undefined,
|
||||
existingMainImage: mainImage.file || mainImage.removed ? undefined : mainImage.existing,
|
||||
removeMainImage: mainImage.removed,
|
||||
sections: sections.map(section => ({
|
||||
@@ -635,6 +660,23 @@ export default function AdminPage() {
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label style={{ display: 'grid', gap: '8px' }}>
|
||||
<span style={{ fontFamily: 'Space Mono, monospace', fontSize: '12px', letterSpacing: '0.15em' }}>
|
||||
Category (optional)
|
||||
</span>
|
||||
<select
|
||||
value={category}
|
||||
onChange={(event) => setCategory(event.target.value)}
|
||||
style={{ padding: '12px', border: '1px solid #8B7D6B', backgroundColor: 'white' }}
|
||||
>
|
||||
<option value="">-- Select Category --</option>
|
||||
<option value="books-magazine">Books & Magazine</option>
|
||||
<option value="clothing-shoes">Clothing & Shoes</option>
|
||||
<option value="collectibles-art">Collectibles & Art</option>
|
||||
<option value="toys-hobbies">Toys & Hobbies</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { HomePageClient } from '@/components/HomePageClient'
|
||||
import { BlogPost } from '@/types/blog'
|
||||
|
||||
async function getBlogPosts(): Promise<BlogPost[]> {
|
||||
async function getBlogPosts(category?: string): 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`, {
|
||||
let url = `${baseUrl}/posts`
|
||||
if (category) {
|
||||
url += `?category=${encodeURIComponent(category)}`
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
cache: 'no-store',
|
||||
next: { revalidate: 0 }
|
||||
})
|
||||
@@ -23,7 +28,7 @@ async function getBlogPosts(): Promise<BlogPost[]> {
|
||||
}
|
||||
}
|
||||
|
||||
export default async function HomePage() {
|
||||
const posts = await getBlogPosts()
|
||||
return <HomePageClient posts={posts} />
|
||||
export default async function HomePage({ searchParams }: { searchParams: { category?: string } }) {
|
||||
const posts = await getBlogPosts(searchParams.category)
|
||||
return <HomePageClient posts={posts} selectedCategory={searchParams.category} />
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import { BlogPost } from '@/types/blog'
|
||||
import { BlogPost, CATEGORY_LABELS, BlogCategory } from '@/types/blog'
|
||||
import { ScrollEffects } from '@/components/ScrollEffects'
|
||||
import { FloatingElements } from '@/components/FloatingElements'
|
||||
import { ClientOnly } from '@/components/ClientOnly'
|
||||
@@ -9,9 +9,10 @@ import { BlogPostCard } from '@/components/BlogPost'
|
||||
|
||||
type HomePageClientProps = {
|
||||
posts: BlogPost[]
|
||||
selectedCategory?: string
|
||||
}
|
||||
|
||||
export function HomePageClient({ posts }: HomePageClientProps) {
|
||||
export function HomePageClient({ posts, selectedCategory }: HomePageClientProps) {
|
||||
const { editorsPicks, regularPosts, latestPostId } = useMemo(() => {
|
||||
if (!posts.length) {
|
||||
return { editorsPicks: [], regularPosts: [], latestPostId: null }
|
||||
@@ -53,6 +54,48 @@ export function HomePageClient({ posts }: HomePageClientProps) {
|
||||
<p className="tagline">
|
||||
Handpicked Treasures from the eBay Universe - Est. 2024
|
||||
</p>
|
||||
<div style={{ marginTop: '24px' }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
gap: '24px',
|
||||
flexWrap: 'wrap'
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
style={{
|
||||
fontFamily: 'Space Mono, monospace',
|
||||
fontSize: '12px',
|
||||
letterSpacing: '0.1em',
|
||||
textTransform: 'uppercase',
|
||||
color: !selectedCategory ? '#1E1A17' : '#8B7D6B',
|
||||
textDecoration: 'none',
|
||||
fontWeight: !selectedCategory ? 'bold' : 'normal'
|
||||
}}
|
||||
>
|
||||
All
|
||||
</a>
|
||||
{(Object.keys(CATEGORY_LABELS) as BlogCategory[]).map((categoryKey) => (
|
||||
<a
|
||||
key={categoryKey}
|
||||
href={`/?category=${categoryKey}`}
|
||||
style={{
|
||||
fontFamily: 'Space Mono, monospace',
|
||||
fontSize: '12px',
|
||||
letterSpacing: '0.1em',
|
||||
textTransform: 'uppercase',
|
||||
color: selectedCategory === categoryKey ? '#1E1A17' : '#8B7D6B',
|
||||
textDecoration: 'none',
|
||||
fontWeight: selectedCategory === categoryKey ? 'bold' : 'normal'
|
||||
}}
|
||||
>
|
||||
{CATEGORY_LABELS[categoryKey]}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -211,6 +254,17 @@ export function HomePageClient({ posts }: HomePageClientProps) {
|
||||
>
|
||||
Handpicked treasures. Honest descriptions. Careful packaging.
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
fontFamily: 'Space Mono, monospace',
|
||||
fontSize: '11px',
|
||||
color: '#8B7D6B',
|
||||
marginTop: '12px',
|
||||
letterSpacing: '0.05em'
|
||||
}}
|
||||
>
|
||||
Handpicked Treasures from the eBay Universe - Est. 2024
|
||||
</p>
|
||||
<div
|
||||
className="newspaper-rule"
|
||||
style={{ margin: '16px auto', width: '120px' }}
|
||||
|
||||
@@ -4,6 +4,19 @@
|
||||
image?: string | null
|
||||
}
|
||||
|
||||
export type BlogCategory =
|
||||
| 'books-magazine'
|
||||
| 'clothing-shoes'
|
||||
| 'collectibles-art'
|
||||
| 'toys-hobbies'
|
||||
|
||||
export const CATEGORY_LABELS: Record<BlogCategory, string> = {
|
||||
'books-magazine': 'Books & Magazine',
|
||||
'clothing-shoes': 'Clothing & Shoes',
|
||||
'collectibles-art': 'Collectibles & Art',
|
||||
'toys-hobbies': 'Toys & Hobbies'
|
||||
}
|
||||
|
||||
export interface BlogPost {
|
||||
id: number
|
||||
title: string
|
||||
@@ -13,6 +26,7 @@ export interface BlogPost {
|
||||
sections: BlogPostSection[]
|
||||
footer?: string | null
|
||||
isEditorsPick: boolean
|
||||
category?: BlogCategory | null
|
||||
excerpt?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
|
||||
Reference in New Issue
Block a user