test
This commit is contained in:
parent
3149b881ff
commit
9db958743b
1 changed files with 1 additions and 141 deletions
|
|
@ -1,141 +1 @@
|
|||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
import { Container } from '@/components/ui/Container'
|
||||
import type { Media as MediaType } from '@/payload-types'
|
||||
|
||||
// Updated type to include category for the 15-project Lab structure
|
||||
type ShowcaseItem = {
|
||||
image?: MediaType | null
|
||||
imageUrl?: string
|
||||
title: string
|
||||
description?: string
|
||||
category: 'engineering' | 'design'
|
||||
tags?: { tag: string }[]
|
||||
links?: { label: string; url: string; newTab?: boolean }[]
|
||||
}
|
||||
|
||||
type ShowcaseBlockProps = {
|
||||
heading?: string
|
||||
subheading?: string
|
||||
items?: ShowcaseItem[]
|
||||
}
|
||||
|
||||
function ShowcaseImage({ item }: { item: ShowcaseItem }): React.ReactElement {
|
||||
const image = item.image
|
||||
const imageUrl = image != null && typeof image === 'object' && 'url' in image ? image.url : null
|
||||
const imageAlt =
|
||||
image != null && typeof image === 'object' && 'alt' in image ? image.alt : item.title
|
||||
|
||||
const imgContent = (
|
||||
<div className="relative w-full aspect-video overflow-hidden">
|
||||
{imageUrl ? (
|
||||
<Image src={imageUrl} alt={imageAlt ?? item.title} fill className="object-cover" />
|
||||
) : (
|
||||
<div className="w-full h-full bg-muted/30 flex flex-col items-center justify-center gap-2">
|
||||
<i className="ti ti-photo-off text-foreground/15 text-2xl" aria-hidden="true" />
|
||||
<span className="text-xs text-foreground/20">Coming soon</span>
|
||||
</div>
|
||||
)}
|
||||
{imageUrl && (
|
||||
<>
|
||||
<div className="absolute inset-0 bg-background/0 group-hover:bg-background/20 transition-colors duration-200" />
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<i className="ti ti-external-link text-foreground/80 text-xl" aria-hidden="true" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
return item.imageUrl ? (
|
||||
<a href={item.imageUrl} target="_blank" rel="noopener noreferrer" className="block group">
|
||||
{imgContent}
|
||||
</a>
|
||||
) : (
|
||||
imgContent
|
||||
)
|
||||
}
|
||||
|
||||
export function ShowcaseBlock(props: ShowcaseBlockProps): React.ReactElement | null {
|
||||
const { heading, subheading, items } = props
|
||||
const [filter, setFilter] = useState<'all' | 'engineering' | 'design'>('all')
|
||||
|
||||
if (!Array.isArray(items) || items.length === 0) return null
|
||||
|
||||
const filteredItems = filter === 'all' ? items : items.filter((item) => item.category === filter)
|
||||
|
||||
return (
|
||||
<Container className="py-12">
|
||||
<div className="mb-8 flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div>
|
||||
{heading && <h2 className="text-2xl font-medium text-foreground mb-2">{heading}</h2>}
|
||||
{subheading && <p className="text-sm text-foreground/40">{subheading}</p>}
|
||||
</div>
|
||||
|
||||
{/* Filter Navigation */}
|
||||
<div className="flex gap-2">
|
||||
{(['all', 'engineering', 'design'] as const).map((f) => (
|
||||
<button
|
||||
key={f}
|
||||
onClick={() => setFilter(f)}
|
||||
className={`px-3 py-1 text-xs rounded-full border transition-colors ${
|
||||
filter === f
|
||||
? 'bg-foreground text-background border-foreground'
|
||||
: 'bg-transparent border-foreground/8 text-foreground/60 hover:border-foreground/30'
|
||||
}`}
|
||||
>
|
||||
{f.charAt(0).toUpperCase() + f.slice(1)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{filteredItems.map((item, i) => (
|
||||
<div
|
||||
key={`${item.title}-${i}`}
|
||||
className="bg-muted/50 border border-foreground/8 rounded-xl overflow-hidden flex flex-col"
|
||||
>
|
||||
<ShowcaseImage item={item} />
|
||||
<div className="p-4 flex flex-col gap-3 flex-1">
|
||||
<h3 className="text-sm font-medium text-foreground/85">{item.title}</h3>
|
||||
{item.description && (
|
||||
<p className="text-xs text-foreground/40 leading-relaxed">{item.description}</p>
|
||||
)}
|
||||
{item.tags && item.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{item.tags.map((t, j) => (
|
||||
<span
|
||||
key={j}
|
||||
className="text-xs text-foreground/40 bg-background/50 border border-foreground/8 rounded-full px-2.5 py-0.5"
|
||||
>
|
||||
{t.tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{item.links && item.links.length > 0 && (
|
||||
<div className="flex flex-wrap gap-3 mt-auto pt-3 border-t border-foreground/8">
|
||||
{item.links.map((link, k) => (
|
||||
<a
|
||||
key={k}
|
||||
href={link.url}
|
||||
target={link.newTab ? '_blank' : '_self'}
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1.5 text-xs text-foreground/40 hover:text-foreground/70 transition-colors"
|
||||
>
|
||||
<i className="ti ti-external-link text-[12px]" aria-hidden="true" />
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
2
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue