'use client'; import { useState, useEffect, useRef } from 'react'; import { apiClient } from '@/app/utils/apiClient'; import './Product.scss'; interface Category { id: number; product_category: string; } interface AdditionalDetails { product_color?: string; product_size?: string; product_weight?: number; product_dimensions?: string; product_material?: string; product_manufacturer?: string; product_origin?: string; } interface Product { id?: number; product_name: string; product_description: string; product_price: number; product_brand: string; product_stock: number; product_category_id: number; product_primary_image?: string; product_rating?: number; product_discount?: boolean; product_discount_price?: number; product_discount_start?: string; product_discount_end?: string; product_discount_active?: boolean; product_discount_percentage?: number; product_discount_code?: string; warranty?: string; additional_details?: AdditionalDetails; category?: Category; createdAt?: string; updatedAt?: string; } interface ProductMedia { id: number; product_id: number; media_type: string; is_primary?: boolean; media_data: string; createdAt: string; updatedAt: string; } interface FilterOptions { category: number | ''; minPrice: number | ''; maxPrice: number | ''; stockStatus: 'all' | 'in-stock' | 'out-of-stock'; dateAdded: string | ''; } interface PaymentMethod { id: number; name: string; icon: string | null; is_active: boolean; } export default function ProductManagement() { const [products, setProducts] = useState([]); const [categories, setCategories] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [loading, setLoading] = useState(false); const [showForm, setShowForm] = useState(false); const [selectedProduct, setSelectedProduct] = useState(null); const [productMedia, setProductMedia] = useState([]); const fileInputRef = useRef(null); const [uploading, setUploading] = useState(false); const [uploadError, setUploadError] = useState(''); const [showFilters, setShowFilters] = useState(false); const [filters, setFilters] = useState({ category: '', minPrice: '', maxPrice: '', stockStatus: 'all', dateAdded: '' }); const [availablePaymentMethods, setAvailablePaymentMethods] = useState([]); const [selectedPaymentMethods, setSelectedPaymentMethods] = useState([]); const [loadingPaymentMethods, setLoadingPaymentMethods] = useState(false); const [formData, setFormData] = useState({ product_name: '', product_description: '', product_price: 0, product_brand: '', product_stock: 0, product_category_id: 0, product_discount: false, additional_details: { product_color: '', product_size: '', product_weight: 0, product_dimensions: '', product_material: '', product_manufacturer: '', product_origin: '' } }); const [additionalDetails, setAdditionalDetails] = useState({ product_color: '', product_size: '', product_weight: 0, product_dimensions: '', product_material: '', product_manufacturer: '', product_origin: '' }); useEffect(() => { const adminToken = localStorage.getItem('adminToken'); if (adminToken) { fetchProducts(); fetchCategories(); } }, []); useEffect(() => { fetchAvailablePaymentMethods(); }, []); useEffect(() => { if (selectedProduct?.id) { fetchProductMedia(selectedProduct.id); fetchProductPaymentMethods(selectedProduct.id); } }, [selectedProduct]); const fetchProducts = async () => { try { setLoading(true); const response = await apiClient('/product/products'); setProducts(response.data); } catch (error) { console.error('Error fetching products:', error); } finally { setLoading(false); } }; const fetchCategories = async () => { try { const response = await apiClient('/product/categories'); setCategories(response.data); } catch (error) { console.error('Error fetching categories:', error); } }; const fetchProductMedia = async (productId: number) => { try { const response = await apiClient(`/product/products/${productId}/media`); setProductMedia(response.data || []); } catch (error) { console.error('Error fetching product media:', error); } }; const fetchAvailablePaymentMethods = async () => { try { setLoadingPaymentMethods(true); const response = await apiClient('/payment-methods'); if (response.success) { setAvailablePaymentMethods(response.data.filter((method: PaymentMethod) => method.is_active)); } } catch (error) { console.error('Error fetching payment methods:', error); } finally { setLoadingPaymentMethods(false); } }; const fetchProductPaymentMethods = async (productId: number) => { try { setLoadingPaymentMethods(true); const response = await apiClient(`/payment-methods/product/${productId}`); if (response.success && Array.isArray(response.data)) { const methodIds = response.data.map((method: PaymentMethod) => method.id); setSelectedPaymentMethods(methodIds); } } catch (error) { console.error('Error fetching product payment methods:', error); } finally { setLoadingPaymentMethods(false); } }; const handleInputChange = (e: React.ChangeEvent) => { const { name, value, type } = e.target as HTMLInputElement; if (name.startsWith('additional_')) { const detailName = name.replace('additional_', '') as keyof AdditionalDetails; setAdditionalDetails(prev => ({ ...prev, [detailName]: type === 'number' ? parseFloat(value) : value })); } else { const newValue = type === 'checkbox' ? (e.target as HTMLInputElement).checked : type === 'number' ? parseFloat(value) : value; if (name === 'product_discount_price' && formData.product_price > 0) { const originalPrice = formData.product_price; const discountPrice = parseFloat(value); if (!isNaN(discountPrice) && discountPrice > 0 && discountPrice < originalPrice) { const percentage = ((originalPrice - discountPrice) / originalPrice) * 100; setFormData(prev => ({ ...prev, [name]: discountPrice, product_discount_percentage: Math.round(percentage) })); return; } } else if (name === 'product_discount_percentage' && formData.product_price > 0) { const originalPrice = formData.product_price; const discountPercentage = parseFloat(value); if (!isNaN(discountPercentage) && discountPercentage > 0 && discountPercentage < 100) { const discountPrice = originalPrice - (originalPrice * (discountPercentage / 100)); setFormData(prev => ({ ...prev, [name]: discountPercentage, product_discount_price: Math.round(discountPrice * 100) / 100 })); return; } } else if (name === 'product_price' && formData.product_discount) { const newPrice = parseFloat(value); if (!isNaN(newPrice) && newPrice > 0) { if (formData.product_discount_percentage && formData.product_discount_percentage > 0) { const discountPercentage = formData.product_discount_percentage; const discountPrice = newPrice - (newPrice * (discountPercentage / 100)); setFormData(prev => ({ ...prev, [name]: newPrice, product_discount_price: Math.round(discountPrice * 100) / 100 })); return; } else if (formData.product_discount_price && formData.product_discount_price > 0) { const discountPrice = formData.product_discount_price; if (discountPrice < newPrice) { const percentage = ((newPrice - discountPrice) / newPrice) * 100; setFormData(prev => ({ ...prev, [name]: newPrice, product_discount_percentage: Math.round(percentage) })); return; } } } } setFormData(prev => ({ ...prev, [name]: newValue })); } }; const handleFilterChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setFilters(prev => ({ ...prev, [name]: value })); }; const clearFilters = () => { setFilters({ category: '', minPrice: '', maxPrice: '', stockStatus: 'all', dateAdded: '' }); }; const resetForm = () => { setFormData({ product_name: '', product_description: '', product_price: 0, product_brand: '', product_stock: 0, product_category_id: 0, product_discount: false }); setAdditionalDetails({ product_color: '', product_size: '', product_weight: 0, product_dimensions: '', product_material: '', product_manufacturer: '', product_origin: '' }); setSelectedProduct(null); setSelectedPaymentMethods([]); }; const handleCreateOrUpdate = async (e: React.FormEvent) => { e.preventDefault(); try { setLoading(true); const productData = { product: formData, additionalDetails }; let productId; if (selectedProduct?.id) { const response = await apiClient(`/product/products/${selectedProduct.id}`, { method: 'PUT', body: JSON.stringify(productData), }); productId = selectedProduct.id; } else { const response = await apiClient('/product/products', { method: 'POST', body: JSON.stringify(productData), }); productId = response.data.id; } // Save payment methods if we have a product ID if (productId) { await apiClient(`/payment-methods/product/${productId}`, { method: 'POST', body: JSON.stringify({ paymentMethodIds: selectedPaymentMethods }) }); } setShowForm(false); resetForm(); fetchProducts(); } catch (error) { console.error('Error saving product:', error); } finally { setLoading(false); } }; const handleDelete = async (id: number) => { if (!window.confirm('Are you sure you want to delete this product?')) return; try { setLoading(true); await apiClient(`/product/products/${id}`, { method: 'DELETE' }); fetchProducts(); } catch (error) { console.error('Error deleting product:', error); } finally { setLoading(false); } }; const handleEdit = (product: Product) => { setSelectedProduct(product); setFormData({ ...product, product_category_id: product.product_category_id }); if (product.additional_details) { setAdditionalDetails(product.additional_details); } setShowForm(true); }; const handleUploadImage = async (e: React.ChangeEvent, isPrimary: boolean = false) => { if (!e.target.files || !e.target.files[0] || !selectedProduct?.id) return; try { setUploading(true); setUploadError(''); const formData = new FormData(); formData.append('image', e.target.files[0]); formData.append('isPrimary', isPrimary ? 'true' : 'false'); await apiClient(`/product/products/${selectedProduct.id}/media`, { method: 'POST', body: formData, headers: {}, }); fetchProductMedia(selectedProduct.id); if (fileInputRef.current) { fileInputRef.current.value = ''; } } catch (error: any) { console.error('Error uploading image:', error); setUploadError(error.message || 'Failed to upload image'); } finally { setUploading(false); } }; const handleDeleteImage = async (mediaId: number) => { if (!selectedProduct?.id) return; if (!window.confirm('Are you sure you want to delete this image?')) return; try { await apiClient(`/product/products/${selectedProduct.id}/media/${mediaId}`, { method: 'DELETE' }); fetchProductMedia(selectedProduct.id); } catch (error) { console.error('Error deleting image:', error); } }; const handlePaymentMethodToggle = (methodId: number) => { setSelectedPaymentMethods(prev => { if (prev.includes(methodId)) { return prev.filter(id => id !== methodId); } else { return [...prev, methodId]; } }); }; const filteredProducts = products.filter(product => { if (searchTerm && !product.product_name.toLowerCase().includes(searchTerm.toLowerCase()) && !product.product_brand.toLowerCase().includes(searchTerm.toLowerCase())) { return false; } if (filters.category !== '' && product.product_category_id !== Number(filters.category)) { return false; } if (filters.minPrice !== '' && product.product_price < Number(filters.minPrice)) { return false; } if (filters.maxPrice !== '' && product.product_price > Number(filters.maxPrice)) { return false; } if (filters.stockStatus === 'in-stock' && product.product_stock <= 0) { return false; } if (filters.stockStatus === 'out-of-stock' && product.product_stock > 0) { return false; } if (filters.dateAdded && product.createdAt) { const filterDate = new Date(filters.dateAdded); const productDate = new Date(product.createdAt); filterDate.setHours(0, 0, 0, 0); productDate.setHours(0, 0, 0, 0); if (productDate < filterDate) { return false; } } return true; }); const formatPrice = (price: any): string => { if (price === null || price === undefined || isNaN(Number(price))) { return '$0.00'; } return `$${Number(price).toFixed(2)}`; }; const formatDate = (dateString?: string) => { if (!dateString) return '-'; const date = new Date(dateString); return date.toLocaleString('en-US', { year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }; return (

{selectedProduct ? 'Edit Product' : 'Product Management'}

{!showForm && (
)}
{!showForm && ( <>
🔍 setSearchTerm(e.target.value)} />
{filteredProducts.length} product{filteredProducts.length !== 1 ? 's' : ''} found
{showFilters && (

Filters

to
)}
{loading ? (

Loading products...

) : (
{filteredProducts.length > 0 ? ( filteredProducts.map(product => ( )) ) : ( )}
Name Brand Price Stock Category Created Updated Actions
{product.product_name} {product.product_brand} {formatPrice(product.product_price)} 0 ? 'in-stock' : 'out-of-stock'}`}> {product.product_stock > 0 ? product.product_stock : 'Out of stock'} {product.category?.product_category || ''} {formatDate(product.createdAt)} {formatDate(product.updatedAt)}
No products found. Try adjusting your search or add a new product.
)}
)} {showForm && (

Basic Information