Deploy
This commit is contained in:
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user