126 lines
4.2 KiB
TypeScript
126 lines
4.2 KiB
TypeScript
import { createFileRoute, useNavigate } from '@tanstack/react-router'
|
|
import { useState } from 'react'
|
|
import { trpc } from '../lib/trpc-client'
|
|
import { Tag, Loader2, AlertCircle } from 'lucide-react'
|
|
import { ProductCard } from '../components/ProductCard'
|
|
import { AppLayout } from '../components/AppLayout'
|
|
import type { OffersSectionProps } from '@packages/shared'
|
|
|
|
export const Route = createFileRoute('/offers')({ component: OffersPage })
|
|
|
|
const SHOW_MORE_STEP = 6
|
|
|
|
function OffersSection({ title, subtitle, products, expanded, onExpand, onProductPress }: OffersSectionProps) {
|
|
const visible = expanded ? products : products.slice(0, SHOW_MORE_STEP)
|
|
const hasMore = products.length > SHOW_MORE_STEP && !expanded
|
|
|
|
return (
|
|
<section className="mb-10">
|
|
<div className="section-rule mb-5">
|
|
<h2 className="display-2 text-gray-900">{title}</h2>
|
|
<p className="mt-1 text-sm text-gray-500">{subtitle}</p>
|
|
</div>
|
|
|
|
{products.length === 0 ? (
|
|
<div className="flex flex-col items-center justify-center py-10">
|
|
<Tag className="mb-2 h-7 w-7 text-gray-400" />
|
|
<p className="text-gray-500">No {title.toLowerCase()} available right now</p>
|
|
</div>
|
|
) : (
|
|
<>
|
|
<div className="grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
|
|
{visible.map((item: any) => (
|
|
<ProductCard
|
|
key={item.id}
|
|
item={item}
|
|
onPress={() => onProductPress(item.id)}
|
|
showDeliveryInfo={false}
|
|
miniView={true}
|
|
useAddToCartDialog={true}
|
|
/>
|
|
))}
|
|
</div>
|
|
{hasMore && (
|
|
<div className="mt-6 flex justify-center">
|
|
<button
|
|
onClick={onExpand}
|
|
className="rounded-lg border border-gray-200 bg-white px-6 py-2.5 text-sm font-bold text-gray-800 shadow-sm transition-colors hover:border-brand-300 hover:text-brand-700"
|
|
>
|
|
Show More
|
|
</button>
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</section>
|
|
)
|
|
}
|
|
|
|
function OffersPage() {
|
|
const navigate = useNavigate()
|
|
const [expandedOffers, setExpandedOffers] = useState(false)
|
|
const [expandedCombos, setExpandedCombos] = useState(false)
|
|
|
|
const { data, isLoading, error } = trpc.user.product.getOffersPage.useQuery()
|
|
|
|
const combos = data?.combos || []
|
|
const offers = data?.offers || []
|
|
|
|
const handleProductPress = (id: number) => {
|
|
navigate({ to: '/home/product/$id', params: { id: String(id) } })
|
|
}
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<AppLayout>
|
|
<div className="flex min-h-screen items-center justify-center">
|
|
<Loader2 className="h-8 w-8 animate-spin text-brand-500" />
|
|
<p className="ml-3 text-gray-600">Loading offers...</p>
|
|
</div>
|
|
</AppLayout>
|
|
)
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<AppLayout>
|
|
<div className="flex min-h-screen flex-col items-center justify-center p-4">
|
|
<AlertCircle className="mb-2 h-12 w-12 text-red-500" />
|
|
<p className="text-center text-red-600">Failed to load offers. Please try again.</p>
|
|
</div>
|
|
</AppLayout>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<AppLayout>
|
|
<div className="mx-auto min-h-screen w-full max-w-7xl px-4 py-6 pb-24 md:px-8 md:pb-12">
|
|
<div className="mb-6">
|
|
<p className="text-[11px] font-black uppercase tracking-[0.22em] text-brand-600">
|
|
Deals & Bundles
|
|
</p>
|
|
<h1 className="display-2 mt-1 text-gray-900">Offers</h1>
|
|
<p className="mt-1 text-gray-500">Great prices on select items</p>
|
|
</div>
|
|
|
|
<OffersSection
|
|
title="Offers"
|
|
subtitle="Great prices on select items"
|
|
products={offers}
|
|
expanded={expandedOffers}
|
|
onExpand={() => setExpandedOffers(true)}
|
|
onProductPress={handleProductPress}
|
|
/>
|
|
|
|
<OffersSection
|
|
title="Combos"
|
|
subtitle="Bundle your favorites and save"
|
|
products={combos}
|
|
expanded={expandedCombos}
|
|
onExpand={() => setExpandedCombos(true)}
|
|
onProductPress={handleProductPress}
|
|
/>
|
|
</div>
|
|
</AppLayout>
|
|
)
|
|
}
|