Split the 316-line vendors index into VendorToolbar, VendorGrid, VendorList, and PerPageSelector under components/vendors/. Page now owns only search-param state and query orchestration. Replace generic Error throws in lib/directus.ts with a DirectusError class carrying status, statusText, collection, url, and parsed body. Surface useLocalStorage read/write failures via console.warn instead of swallowing them.
54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
import { Link } from '@tanstack/react-router'
|
|
|
|
import { Card, CardContent } from '~/components/ui/card'
|
|
import { Skeleton } from '~/components/ui/skeleton'
|
|
import { assetUrl } from '~/lib/directus'
|
|
import type { VendorListItem } from '~/lib/types'
|
|
|
|
import { truncateDescription } from './types'
|
|
|
|
type Props = {
|
|
vendors: VendorListItem[]
|
|
isLoading: boolean
|
|
perPage: number
|
|
}
|
|
|
|
export function VendorGrid({ vendors, isLoading, perPage }: Props) {
|
|
return (
|
|
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
|
{isLoading
|
|
? Array.from({ length: perPage }).map((_, k) => (
|
|
<Card key={k}>
|
|
<Skeleton className="h-40 rounded-none" />
|
|
<CardContent className="pt-4">
|
|
<Skeleton className="mb-2 h-6" />
|
|
<Skeleton className="mb-1 h-4" />
|
|
<Skeleton className="h-4" />
|
|
</CardContent>
|
|
</Card>
|
|
))
|
|
: vendors.map((v) => (
|
|
<Card key={v.id} className="group relative hover:border-primary/40">
|
|
<img
|
|
src={assetUrl(v.logo, 'logo-card')}
|
|
alt={v.name}
|
|
width={250}
|
|
height={150}
|
|
className="h-40 w-full rounded-t-lg bg-white object-contain"
|
|
/>
|
|
<CardContent className="pt-4">
|
|
<Link
|
|
to="/vendors/$slug"
|
|
params={{ slug: v.slug }}
|
|
className="mb-2 block text-base font-extrabold after:absolute after:inset-0"
|
|
>
|
|
{v.name}
|
|
</Link>
|
|
<p className="text-sm text-muted-foreground">{truncateDescription(v.description)}</p>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|