'use client' import { useState, useEffect, useCallback } from 'react' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Checkbox } from '@/components/ui/checkbox' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import CsvImport from '@/components/ui/csv-import' import CsvExport from '@/components/ui/csv-export' import { Search, Plus, Edit, Trash2, MoreHorizontal, Download, Save, Upload } from 'lucide-react' import { toast } from 'sonner' import Link from 'next/link' import Image from 'next/image' interface Product { id: string name: string description: string | null price: number discount: number stock: number isActive: boolean images: string[] sku: string weight: string | null categoryId: string category: { id: string name: string } } interface QuickEditFormData { name: string description: string price: number discount: number stock: number weight: string } export default function AdminProductsPage() { const [products, setProducts] = useState([]) const [loading, setLoading] = useState(true) const [search, setSearch] = useState('') const [selectedProducts, setSelectedProducts] = useState([]) const [bulkActionLoading, setBulkActionLoading] = useState(false) const [quickEditOpen, setQuickEditOpen] = useState(false) const [editingProduct, setEditingProduct] = useState(null) const [quickEditForm, setQuickEditForm] = useState({ name: '', description: '', price: 0, discount: 0, stock: 0, weight: '' }) const [quickEditLoading, setQuickEditLoading] = useState(false) const fetchProducts = useCallback(async () => { try { setLoading(true) const params = new URLSearchParams({ page: '1', limit: '20', admin: 'true' // Add admin flag to get all products }) if (search) params.append('search', search) const response = await fetch(`/api/products?${params}`) const data = await response.json() setProducts(data.products || []) } catch (error) { console.error('Error fetching products:', error) toast.error('Failed to load products') } finally { setLoading(false) } }, [search]) useEffect(() => { fetchProducts() }, [fetchProducts]) const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedProducts(products.map(p => p.id)) } else { setSelectedProducts([]) } } const handleSelectProduct = (productId: string, checked: boolean) => { if (checked) { setSelectedProducts(prev => [...prev, productId]) } else { setSelectedProducts(prev => prev.filter(id => id !== productId)) } } const handleBulkAction = async (action: 'activate' | 'deactivate' | 'delete') => { if (selectedProducts.length === 0) { toast.error('Please select at least one product') return } setBulkActionLoading(true) try { const response = await fetch('/api/admin/products/bulk', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ productIds: selectedProducts, action }) }) if (!response.ok) throw new Error('Bulk action failed') toast.success(`Successfully ${action}d ${selectedProducts.length} products`) setSelectedProducts([]) fetchProducts() } catch (error) { toast.error(`Failed to ${action} products`) } finally { setBulkActionLoading(false) } } const handleDeleteProduct = async (productId: string) => { if (!confirm('Are you sure you want to delete this product?')) return try { const response = await fetch(`/api/admin/products/${productId}`, { method: 'DELETE' }) if (!response.ok) throw new Error('Delete failed') toast.success('Product deleted successfully') fetchProducts() } catch (error) { toast.error('Failed to delete product') } } const handleQuickEdit = (product: Product) => { setEditingProduct(product) setQuickEditForm({ name: product.name, description: product.description || '', price: product.price, discount: product.discount, stock: product.stock, weight: product.weight || '' }) setQuickEditOpen(true) } const handleQuickEditSave = async () => { if (!editingProduct) return setQuickEditLoading(true) try { const response = await fetch(`/api/admin/products/${editingProduct.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: quickEditForm.name, description: quickEditForm.description, price: quickEditForm.price, discount: quickEditForm.discount, stock: quickEditForm.stock, weight: quickEditForm.weight, // Keep existing values that aren't being edited images: editingProduct.images, sku: editingProduct.sku, isActive: editingProduct.isActive, categoryId: editingProduct.categoryId || editingProduct.category.id }) }) if (!response.ok) { const errorData = await response.json() throw new Error(errorData.error || 'Update failed') } toast.success('Product updated successfully') setQuickEditOpen(false) setEditingProduct(null) fetchProducts() } catch (error) { console.error('Quick edit error:', error) toast.error(error instanceof Error ? error.message : 'Failed to update product') } finally { setQuickEditLoading(false) } } const handleQuickEditInputChange = (field: keyof QuickEditFormData, value: string | number) => { setQuickEditForm(prev => ({ ...prev, [field]: value })) } return (

Products

Manage your product catalog

{ try { const response = await fetch('/api/admin/products/export', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ columns: selectedColumns, filters }), }) if (!response.ok) { throw new Error('Export failed') } const result = await response.json() return { success: true, data: result.data || products, message: 'Export completed successfully' } } catch (error) { return { success: false, message: error instanceof Error ? error.message : 'Export failed', } } }} > { try { const response = await fetch('/api/admin/products/import', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ data }), }) if (!response.ok) { throw new Error('Import failed') } const result = await response.json() fetchProducts() // Refresh the products list return result } catch (error) { return { success: false, message: error instanceof Error ? error.message : 'Import failed', } } }} >
All Products
{selectedProducts.length > 0 && (
{selectedProducts.length} selected
)}
setSearch(e.target.value)} className="pl-10 w-64" />
{loading ? (
Loading...
) : (
0} onCheckedChange={handleSelectAll} /> Product SKU Category Price Stock Status Actions
{products.map((product) => ( handleSelectProduct(product.id, !!checked)} />

{product.name}

{product.discount > 0 && ( {product.discount}% OFF )}
{product.sku} {product.category.name}
{product.discount > 0 ? ( <> ₹{(product.price - (product.price * product.discount / 100)).toFixed(2)} ₹{product.price.toFixed(2)} ) : ( ₹{product.price.toFixed(2)} )}
10 ? 'default' : product.stock > 0 ? 'secondary' : 'destructive'}> {product.stock} {product.isActive ? 'Active' : 'Inactive'}
Full Edit handleDeleteProduct(product.id)} className="text-red-600" > Delete
))}
)}
{/* Quick Edit Dialog */} Quick Edit Product {editingProduct && (
{/* Product Image and Basic Info */}
{editingProduct.name}

SKU: {editingProduct.sku}

Category: {editingProduct.category.name}

{/* Form Fields */}
handleQuickEditInputChange('name', e.target.value)} placeholder="Product name" />
handleQuickEditInputChange('weight', e.target.value)} placeholder="e.g., 1kg, 500g" />
handleQuickEditInputChange('price', parseFloat(e.target.value) || 0)} placeholder="0.00" />
handleQuickEditInputChange('discount', parseFloat(e.target.value) || 0)} placeholder="0" />
handleQuickEditInputChange('stock', parseInt(e.target.value) || 0)} placeholder="0" />
₹{(quickEditForm.price - (quickEditForm.price * quickEditForm.discount / 100)).toFixed(2)}