Add vendor detail page

This commit is contained in:
2026-04-23 21:14:40 +04:00
parent 73a0d24c4f
commit 5b0b0c3430

View File

@@ -1,5 +1,149 @@
import { createFileRoute } from '@tanstack/react-router' import { useSuspenseQuery } from '@tanstack/react-query'
import { createFileRoute, Link, notFound } from '@tanstack/react-router'
import { iso31661 } from 'iso-3166'
import { Facebook, Globe, Linkedin, Twitter } from 'lucide-react'
import { Helmet } from 'react-helmet-async'
import { Button } from '~/components/ui/button'
import { assetUrl } from '~/lib/directus'
import { vendorBySlugQuery } from '~/lib/queries'
export const Route = createFileRoute('/vendors/$slug')({ export const Route = createFileRoute('/vendors/$slug')({
component: () => null, loader: async ({ context: { queryClient }, params: { slug } }) => {
const vendor = await queryClient.ensureQueryData(vendorBySlugQuery(slug))
if (!vendor) throw notFound()
},
component: VendorDetailPage,
}) })
function countryName(alpha3: string | null | undefined): string {
if (!alpha3) return ''
return iso31661.find((iso) => iso.alpha3 === alpha3)?.name ?? ''
}
function VendorDetailPage() {
const { slug } = Route.useParams()
const { data: vendor } = useSuspenseQuery(vendorBySlugQuery(slug))
if (!vendor) return null
const country = countryName(vendor.country)
const hasSocial = vendor.website || vendor.facebook || vendor.linkedin || vendor.twitter
const hasAddress = vendor.address_line_1 || vendor.address_line_2 || vendor.city || vendor.state || country
return (
<article className="vendor">
<Helmet>
<title>{vendor.name}</title>
</Helmet>
<div className="flex flex-col items-start justify-between gap-6 md:flex-row md:items-center">
<h1 className="text-3xl font-bold">{vendor.name}</h1>
{vendor.logo && (
<img
src={assetUrl(vendor.logo, 'logo-page')}
alt={vendor.name}
width={350}
height={150}
className="h-[150px] w-[350px] rounded-md bg-white object-contain"
/>
)}
</div>
{vendor.long_description && (
<div className="prose-cms mt-6" dangerouslySetInnerHTML={{ __html: vendor.long_description }} />
)}
<div className="mt-6 grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-3">
{hasAddress && (
<section>
<h2 className="mt-6 mb-2 text-lg font-semibold">Address</h2>
<p className="text-sm">
{vendor.address_line_1 && (
<>
{vendor.address_line_1}
<br />
</>
)}
{vendor.address_line_2 && (
<>
{vendor.address_line_2}
<br />
</>
)}
{vendor.city && (
<>
{vendor.city}
<br />
</>
)}
{vendor.state && (
<>
{vendor.state}
<br />
</>
)}
{country && <>{country}</>}
</p>
</section>
)}
{hasSocial && (
<section>
<h2 className="mt-6 mb-2 text-lg font-semibold">Social</h2>
<ul className="space-y-2 text-sm">
{vendor.website && (
<li className="flex items-center gap-2">
<Globe className="h-4 w-4" />
<a href={vendor.website} target="_blank" rel="noreferrer" className="hover:underline">
{vendor.website}
</a>
</li>
)}
{vendor.linkedin && (
<li className="flex items-center gap-2">
<Linkedin className="h-4 w-4" />
<a href={vendor.linkedin} target="_blank" rel="noreferrer" className="hover:underline">
{vendor.linkedin}
</a>
</li>
)}
{vendor.twitter && (
<li className="flex items-center gap-2">
<Twitter className="h-4 w-4" />
<a href={vendor.twitter} target="_blank" rel="noreferrer" className="hover:underline">
{vendor.twitter}
</a>
</li>
)}
{vendor.facebook && (
<li className="flex items-center gap-2">
<Facebook className="h-4 w-4" />
<a href={vendor.facebook} target="_blank" rel="noreferrer" className="hover:underline">
{vendor.facebook}
</a>
</li>
)}
</ul>
</section>
)}
{vendor.categories.length > 0 && (
<section>
<h2 className="mt-6 mb-2 text-lg font-semibold">Categories</h2>
<ul className="flex flex-col gap-2">
{vendor.categories.map((cat) => (
<li key={cat.categories_id.slug}>
<Button asChild variant="secondary" size="sm" className="h-6 px-2 text-[11px]">
<Link to="/vendors" search={{ category: cat.categories_id.slug }}>
{cat.categories_id.name}
</Link>
</Button>
</li>
))}
</ul>
</section>
)}
</div>
</article>
)
}