Extract vendor page subcomponents; structured Directus errors
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.
This commit is contained in:
53
frontend/src/components/vendors/vendor-grid.tsx
vendored
Normal file
53
frontend/src/components/vendors/vendor-grid.tsx
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user