Spaces:
Running
Running
File size: 7,439 Bytes
b89a86e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
import { useState } from "react";
import { useParams, useLocation } from "wouter";
import { useQuery } from "@tanstack/react-query";
import { storesApi } from "@/lib/api";
import Header from "@/components/layout/header";
import SellerProductGrid from "@/components/product/seller-product-grid";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { LoadingSpinner } from "@/components/ui/spinner";
import { ArrowLeft, Star, MapPin, User } from "lucide-react";
export default function StoreDetail() {
const { id } = useParams();
const [, setLocation] = useLocation();
const [searchQuery, setSearchQuery] = useState("");
const { data: storeData, isLoading, error } = useQuery({
queryKey: ['/api/stores', id],
queryFn: () => storesApi.getById(id!),
enabled: !!id,
});
if (isLoading) {
return (
<div className="min-h-screen page-gradient">
<Header searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<LoadingSpinner text="Loading store details..." />
<div className="mt-8">
<Skeleton className="h-32 w-full mb-8 rounded-2xl" />
<div className="flex items-center space-x-4 mb-8">
<Skeleton className="w-16 h-16 rounded-full" />
<div className="flex-1">
<Skeleton className="h-6 w-48 mb-2" />
<Skeleton className="h-4 w-32" />
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{[...Array(8)].map((_, i) => (
<Skeleton key={i} className="aspect-square w-full rounded-2xl" />
))}
</div>
</div>
</div>
</div>
);
}
if (error || !storeData) {
return (
<div className="min-h-screen page-gradient">
<Header searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="text-center">
<h1 className="text-2xl font-bold text-foreground mb-4">Store Not Found</h1>
<Button onClick={() => setLocation('/')}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Home
</Button>
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen page-gradient">
<Header searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
<div className="max-w-7xl mx-auto px-3 sm:px-4 lg:px-6 py-6" data-testid="store-detail">
<Button
variant="ghost"
onClick={() => setLocation('/')}
className="mb-6"
data-testid="back-button"
>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Stores
</Button>
{/* Store Banner */}
<div className="relative h-48 bg-gradient-to-r from-primary via-secondary to-primary rounded-2xl mb-8 overflow-hidden shadow-xl">
{storeData.bannerImage ? (
<img
src={storeData.bannerImage}
alt={`${storeData.name} banner`}
className="w-full h-full object-cover"
data-testid="store-banner"
/>
) : (
<div className="absolute inset-0 bg-gradient-to-r from-primary via-secondary to-primary" />
)}
<div className="absolute inset-0 bg-black/20" />
<div className="absolute bottom-4 left-4 text-white">
<h1 className="text-3xl font-bold" data-testid="store-name">
{storeData.name}
</h1>
</div>
</div>
{/* Store Info */}
<Card className="mb-8 glass-card border-0">
<CardContent className="p-6">
<div className="flex items-start space-x-6">
{/* Store Face Image */}
<div className="w-20 h-20 rounded-full overflow-hidden bg-gray-100 flex-shrink-0">
{storeData.faceImage ? (
<img
src={storeData.faceImage}
alt={`${storeData.name} profile`}
className="w-full h-full object-cover"
data-testid="store-face-image"
/>
) : (
<div className="w-full h-full bg-gray-200 flex items-center justify-center">
<span className="text-gray-400 text-xs">No Image</span>
</div>
)}
</div>
<div className="flex-1">
<div className="flex items-center space-x-4 mb-4">
<h2 className="text-xl font-bold text-foreground" data-testid="store-title">
{storeData.name}
</h2>
<div className="flex items-center">
<div className="flex text-yellow-400 text-sm">
{[...Array(5)].map((_, i) => (
<Star key={i} className="h-4 w-4 fill-current" />
))}
</div>
<span className="text-sm text-muted-foreground ml-2" data-testid="store-rating">
(4.9/5)
</span>
</div>
</div>
{/* Seller Info */}
<div className="flex items-center space-x-2 mb-4">
<User className="h-4 w-4 text-primary" />
<span className="text-sm font-medium text-foreground">Seller:</span>
<span className="text-sm text-muted-foreground" data-testid="seller-name">
{storeData.seller?.username || 'Store Owner'}
</span>
</div>
{storeData.description && (
<div className="mb-4">
<h3 className="font-semibold text-foreground mb-2">About This Store</h3>
<p className="text-muted-foreground leading-relaxed" data-testid="store-description">
{storeData.description}
</p>
</div>
)}
<div className="flex items-center text-sm text-muted-foreground">
<MapPin className="h-4 w-4 mr-1" />
<span>Verified Seller • Bharat</span>
</div>
</div>
</div>
</CardContent>
</Card>
{/* Store Products */}
<div>
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold text-foreground" data-testid="products-title">
Products from {storeData.name}
</h2>
<span className="text-muted-foreground" data-testid="products-count">
{storeData.products?.length || 0} products
</span>
</div>
{storeData.products && storeData.products.length > 0 ? (
<SellerProductGrid products={storeData.products} />
) : (
<div className="text-center py-12" data-testid="no-products">
<p className="text-muted-foreground">This store doesn't have any products yet.</p>
</div>
)}
</div>
</div>
</div>
);
}
|