This commit is contained in:
Vula Builder
2026-10-11 14:20:28 +00:00
parent 5d524eb23b
commit 2e08c7d390
@@ -0,0 +1,164 @@
'use client'
import Image from 'next/image'
import Link from 'next/link'
import { ArrowRight, UtensilsCrossed } from 'lucide-react'
import Badge from '@/components/ui/Badge'
import { useVulaContent } from '@/hooks/useVulaContent'
type PostFields = {
title?: string
excerpt?: string
body?: string
author?: string
category?: string
image?: string
url?: string
}
const trim = (text: string, max: number) => {
if (text.length <= max) return text
return `${text.slice(0, max).trimEnd()}…`
}
export default function WokStoriesBlogSection() {
const { items, loading } = useVulaContent('posts')
if (loading) {
return (
<div className="flex justify-center py-16">
<div className="w-8 h-8 rounded-full border-2 border-current border-t-transparent animate-spin" />
</div>
)
}
if (items.length === 0) return null
const posts = items.map((item) => {
const d = item.data as PostFields
return {
id: item.id,
title: d.title ?? '',
excerpt: d.excerpt ?? '',
body: d.body ?? '',
author: d.author ?? '',
category: d.category ?? '',
image: d.image ?? '',
href: d.url ?? '',
}
})
const lead = posts[0]
const rest = posts.slice(1)
return (
<section
id="wok-stories"
data-vula-section="wokstoriesblog"
data-vula-cms="posts"
className="bg-card text-card-foreground py-24 sm:py-32"
>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex flex-col gap-6 sm:flex-row sm:items-end sm:justify-between">
<div className="max-w-2xl">
<div className="flex items-center gap-4">
<span className="h-px w-14 bg-accent" aria-hidden="true" />
<span className="text-[11px] uppercase tracking-[0.32em] text-muted-foreground">Wok Stories</span>
</div>
<h2 className="mt-6 text-4xl font-light leading-tight tracking-tight sm:text-5xl">
What happens between <span className="italic text-accent">order and box.</span>
</h2>
</div>
<p className="max-w-md text-sm leading-relaxed text-muted-foreground">
Notes from the corner — new things going into the wok, what the street asked us to try next, and the people who keep coming back.
</p>
</div>
<article className="group relative mt-14 overflow-hidden rounded-lg border border-foreground/20 bg-foreground/10 backdrop-blur-md">
<div className="grid gap-0 lg:grid-cols-2">
<div className="relative aspect-[4/3] w-full overflow-hidden lg:aspect-auto lg:h-full lg:min-h-[380px]">
{lead.image ? (
<Image
src={lead.image}
alt={lead.title || 'Wok Stories'}
fill
sizes="(max-width: 1024px) 100vw, 50vw"
className="object-cover transition-transform duration-500 ease-out group-hover:scale-105"
/>
) : (
<div className="flex h-full w-full items-center justify-center bg-secondary">
<UtensilsCrossed className="h-8 w-8 text-muted-foreground" aria-hidden="true" />
</div>
)}
</div>
<div className="flex flex-col justify-center gap-5 p-8 sm:p-12">
{lead.category ? <Badge variant="accent">{lead.category}</Badge> : null}
<h3 className="text-2xl font-light leading-snug sm:text-3xl">{lead.title}</h3>
<p className="text-sm leading-relaxed text-muted-foreground sm:text-base">
{trim(lead.excerpt || lead.body, 280)}
</p>
<div className="flex flex-wrap items-center gap-6 text-[11px] uppercase tracking-[0.28em] text-muted-foreground">
{lead.author ? <span>{lead.author}</span> : null}
{lead.href ? (
<Link
href={lead.href}
className="inline-flex items-center gap-2 text-accent transition-all duration-200 ease-out hover:gap-3"
>
Read the story
<ArrowRight className="h-3.5 w-3.5" aria-hidden="true" />
</Link>
) : null}
</div>
</div>
</div>
</article>
{rest.length > 0 ? (
<div className="mt-8 grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{rest.map((post) => (
<article
key={post.id}
className="group flex flex-col overflow-hidden rounded-lg border border-foreground/20 bg-foreground/10 backdrop-blur-md"
>
<div className="relative aspect-video w-full overflow-hidden">
{post.image ? (
<Image
src={post.image}
alt={post.title || 'Wok Stories'}
fill
sizes="(max-width: 768px) 100vw, 33vw"
className="object-cover transition-transform duration-500 ease-out group-hover:scale-105"
/>
) : (
<div className="flex h-full w-full items-center justify-center bg-secondary">
<UtensilsCrossed className="h-7 w-7 text-muted-foreground" aria-hidden="true" />
</div>
)}
</div>
<div className="flex flex-1 flex-col gap-4 p-6">
{post.category ? <Badge variant="outline">{post.category}</Badge> : null}
<h3 className="text-lg font-light leading-snug">{post.title}</h3>
<p className="text-sm leading-relaxed text-muted-foreground">
{trim(post.excerpt || post.body, 160)}
</p>
<div className="mt-auto flex items-center justify-between gap-4 border-t border-foreground/15 pt-5 text-[11px] uppercase tracking-[0.28em] text-muted-foreground">
<span>{post.author}</span>
{post.href ? (
<Link
href={post.href}
aria-label={`Read ${post.title}`}
className="text-accent transition-all duration-200 ease-out hover:text-foreground"
>
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
) : null}
</div>
</div>
</article>
))}
</div>
) : null}
</div>
</section>
)
}