import { useState, useRef } from "react"; import { useLocation } from "wouter"; import { useAuth } from "@/hooks/use-auth"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { sellerApi, categoriesApi, storesApi, productsApi, authApi, ordersApi } from "@/lib/api"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { useToast } from "@/hooks/use-toast"; import { Store, Package, Plus, Edit, Trash2, Upload, LogOut, Loader2, ShoppingCart, Clock, CheckCircle, MapPin, ExternalLink, Camera } from "lucide-react"; const sellerLoginSchema = z.object({ username: z.string().min(1, "Username is required"), password: z.string().min(1, "Password is required"), }); const storeSchema = z.object({ name: z.string().min(1, "Store name is required"), description: z.string().optional(), }); const productSchema = z.object({ title: z.string().min(1, "Product title is required"), description: z.string().min(1, "Product description is required"), price: z.string().min(1, "Price is required"), originalPrice: z.string().optional(), stock: z.string().min(1, "Stock quantity is required"), categoryId: z.string().min(1, "Category is required"), }); type SellerLoginFormData = z.infer; type StoreFormData = z.infer; type ProductFormData = z.infer; export default function AdminSeller() { const [, setLocation] = useLocation(); const { seller, userType, logout, sellerLogin } = useAuth(); const { toast } = useToast(); const queryClient = useQueryClient(); const [isStoreDialogOpen, setIsStoreDialogOpen] = useState(false); const [isProductDialogOpen, setIsProductDialogOpen] = useState(false); const [selectedFiles, setSelectedFiles] = useState(null); const [capturedFiles, setCapturedFiles] = useState([]); const videoRef = useRef(null); const canvasRef = useRef(null); const [isCameraOpen, setIsCameraOpen] = useState(false); const [stream, setStream] = useState(null); const [isLoading, setIsLoading] = useState(false); const [activeTab, setActiveTab] = useState("dashboard"); // All useQuery hooks must be called at the top level - before any conditional returns const { data: store, isLoading: storeLoading } = useQuery({ queryKey: ['/api/seller/store'], queryFn: async () => { const response = await sellerApi.getStore(); return response.json(); }, enabled: userType === 'seller' && !!seller, // Only run query when authenticated }); const { data: products = [], isLoading: productsLoading } = useQuery({ queryKey: ['/api/seller/products'], queryFn: async () => { const response = await sellerApi.getProducts(); return response.json(); }, enabled: userType === 'seller' && !!seller, // Only run query when authenticated }); const { data: categories = [] } = useQuery({ queryKey: ['/api/categories'], queryFn: () => categoriesApi.getAll(), enabled: userType === 'seller' && !!seller, // Only run query when authenticated }); const { data: orders = [], isLoading: ordersLoading } = useQuery({ queryKey: ['/api/seller/orders'], queryFn: async () => { const response = await sellerApi.getOrders(); return response.json(); }, enabled: userType === 'seller' && !!seller, // Only run query when authenticated }); // Query to get user location data const { data: users = [] } = useQuery({ queryKey: ['/api/admin/users'], queryFn: async () => { const response = await fetch('/api/admin/users'); return response.json(); }, enabled: userType === 'seller' && !!seller, }); // All useForm and useMutation hooks must also be at the top level const sellerLoginForm = useForm({ resolver: zodResolver(sellerLoginSchema), defaultValues: { username: "", password: "", }, }); const storeForm = useForm({ resolver: zodResolver(storeSchema), defaultValues: { name: store?.name || "", description: store?.description || "", }, }); const productForm = useForm({ resolver: zodResolver(productSchema), defaultValues: { title: "", description: "", price: "", originalPrice: "", stock: "", categoryId: "", }, }); const createStoreMutation = useMutation({ mutationFn: (data: StoreFormData) => { const formData = new FormData(); formData.append('name', data.name); if (data.description) formData.append('description', data.description); if (selectedFiles) { Array.from(selectedFiles).forEach((file, index) => { if (index === 0) formData.append('bannerImage', file); if (index === 1) formData.append('faceImage', file); }); } return storesApi.create(formData); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/seller/store'] }); setIsStoreDialogOpen(false); storeForm.reset(); setSelectedFiles(null); toast({ title: "Store created", description: "Your store has been created successfully.", }); }, onError: () => { toast({ variant: "destructive", title: "Error", description: "Failed to create store.", }); }, }); const updateStoreMutation = useMutation({ mutationFn: (data: StoreFormData) => { const formData = new FormData(); formData.append('name', data.name); if (data.description) formData.append('description', data.description); if (selectedFiles) { Array.from(selectedFiles).forEach((file, index) => { if (index === 0) formData.append('bannerImage', file); if (index === 1) formData.append('faceImage', file); }); } return storesApi.update(store.id, formData); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/seller/store'] }); setIsStoreDialogOpen(false); setSelectedFiles(null); toast({ title: "Store updated", description: "Your store has been updated successfully.", }); }, onError: () => { toast({ variant: "destructive", title: "Error", description: "Failed to update store.", }); }, }); const createProductMutation = useMutation({ mutationFn: (data: ProductFormData) => { const formData = new FormData(); formData.append('title', data.title); formData.append('description', data.description); formData.append('price', data.price); if (data.originalPrice) formData.append('originalPrice', data.originalPrice); formData.append('stock', data.stock); formData.append('categoryId', data.categoryId); // Append selected files if (selectedFiles) { Array.from(selectedFiles).forEach((file) => { formData.append('images', file); }); } // Append captured files capturedFiles.forEach((file) => { formData.append('images', file); }); return productsApi.create(formData); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/seller/products'] }); setIsProductDialogOpen(false); productForm.reset(); setSelectedFiles(null); setCapturedFiles([]); closeCamera(); toast({ title: "Product created", description: "Your product has been created successfully.", }); }, onError: () => { toast({ variant: "destructive", title: "Error", description: "Failed to create product.", }); }, }); const deleteProductMutation = useMutation({ mutationFn: (id: string) => productsApi.delete(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/seller/products'] }); toast({ title: "Product deleted", description: "Product has been deleted successfully.", }); }, onError: () => { toast({ variant: "destructive", title: "Error", description: "Failed to delete product.", }); }, }); const updateOrderMutation = useMutation({ mutationFn: ({ id, data }: { id: string; data: any }) => sellerApi.updateOrder(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/seller/orders'] }); toast({ title: "Order updated", description: "Order status has been updated successfully.", }); }, onError: () => { toast({ variant: "destructive", title: "Error", description: "Failed to update order status.", }); }, }); const handleUpdateOrderStatus = (id: string, data: any) => { updateOrderMutation.mutate({ id, data }); }; const onSellerLogin = async (data: SellerLoginFormData) => { setIsLoading(true); try { const response = await authApi.sellerLogin(data); const result = await response.json(); sellerLogin(result.token, result.seller); toast({ title: "Welcome back!", description: "You have been successfully logged in as a seller.", }); } catch (error) { toast({ variant: "destructive", title: "Login failed", description: "Invalid credentials. Please check with the administrator.", }); } finally { setIsLoading(false); } }; // Show login form if not authenticated as seller if (userType !== 'seller' || !seller) { return (
Shoposphere Seller Login
( Username )} /> ( Password )} />

Seller accounts are created by administrators only

); } const onStoreSubmit = (data: StoreFormData) => { if (store) { updateStoreMutation.mutate(data); } else { createStoreMutation.mutate(data); } }; const onProductSubmit = (data: ProductFormData) => { createProductMutation.mutate(data); }; // Camera functions const openCamera = async () => { try { const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, // Use back camera on mobile audio: false }); setStream(mediaStream); setIsCameraOpen(true); if (videoRef.current) { videoRef.current.srcObject = mediaStream; } } catch (error) { console.error('Error accessing camera:', error); toast({ variant: "destructive", title: "Camera Error", description: "Unable to access camera. Please check permissions.", }); } }; const closeCamera = () => { if (stream) { stream.getTracks().forEach(track => track.stop()); setStream(null); } setIsCameraOpen(false); }; const capturePhoto = () => { if (videoRef.current && canvasRef.current) { const video = videoRef.current; const canvas = canvasRef.current; const context = canvas.getContext('2d'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; if (context) { context.drawImage(video, 0, 0); canvas.toBlob((blob) => { if (blob) { const file = new File([blob], `captured-${Date.now()}.jpg`, { type: 'image/jpeg' }); setCapturedFiles(prev => [...prev, file]); toast({ title: "Photo captured", description: "Photo added to product images.", }); } }, 'image/jpeg', 0.8); } } }; const removeCapturedPhoto = (index: number) => { setCapturedFiles(prev => prev.filter((_, i) => i !== index)); }; const handleLogout = () => { logout(); setLocation('/'); }; return (

Shoposphere Seller

Welcome, {seller.username}

Dashboard My Store Products Orders ({orders.length})

{store ? 1 : 0}

Store

{products.length}

Products Listed

{orders.length}

Total Orders

{/* Customer Locations */} Customer Locations {users.length > 0 ? (
{users.slice(0, 5).map((user: any) => (

{user.firstName} {user.lastName}

{user.email} • {user.phone}

{/* Location Information */}
{user.locationDetectedAutomatically && user.googleMapsUrl ? (
Auto-detected location
) : user.street && user.city ? (
{user.street}, {user.city}, {user.state} {user.pinCode}
) : (
No location provided
)}
{user.latitude && user.longitude && (

{Number(user.latitude).toFixed(4)}, {Number(user.longitude).toFixed(4)}

)}

Joined: {new Date(user.createdAt).toLocaleDateString()}

))}
) : (

No customer data available yet.

)}
{/* Recent Orders */} Recent Orders {ordersLoading ? (
{[...Array(5)].map((_, i) => ( ))}
) : orders.length === 0 ? (

No orders yet. Start by creating products to sell!

) : (
{orders.slice(0, 5).map((order: any) => (

Order #{order.id.slice(-8)}

₹{parseFloat(order.total).toFixed(2)} • {order.items?.length || 0} items

{order.status}

{new Date(order.createdAt).toLocaleDateString()}

))}
)}
Store Management {store ? 'Edit Store' : 'Create Store'}
( Store Name )} /> ( Description