diff --git a/app/dashboard/stock/page.tsx b/app/dashboard/stock/page.tsx index 6d277c5..73b4451 100644 --- a/app/dashboard/stock/page.tsx +++ b/app/dashboard/stock/page.tsx @@ -26,9 +26,15 @@ import { } from "@/components/ui/alert-dialog"; import { Product } from "@/models/products"; import { Package, RefreshCw, ChevronDown, CheckSquare, XSquare } from "lucide-react"; -import { fetchProductData, updateProductStock, saveProductData } from "@/lib/api"; +import { clientFetch } from "@/lib/api"; import { toast } from "sonner"; +interface StockData { + currentStock: number; + stockTracking: boolean; + lowStockThreshold?: number; +} + export default function StockManagementPage() { const router = useRouter(); const [products, setProducts] = useState([]); @@ -53,8 +59,8 @@ export default function StockManagementPage() { const fetchDataAsync = async () => { try { - const apiUrl = `${process.env.NEXT_PUBLIC_API_URL}/products`; - const fetchedProducts = await fetchProductData(apiUrl, authToken); + const response = await clientFetch('api/products'); + const fetchedProducts = response || []; setProducts(fetchedProducts); // Initialize stock values @@ -85,28 +91,23 @@ export default function StockManagementPage() { const handleSaveStock = async (product: Product) => { if (!product._id) return; - - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - return; - } try { const newStockValue = stockValues[product._id] || 0; - await updateProductStock( - product._id, - { - currentStock: newStockValue, - stockTracking: product.stockTracking + const stockData: StockData = { + currentStock: newStockValue, + stockTracking: product.stockTracking || false, + lowStockThreshold: product.lowStockThreshold + }; + + await clientFetch(`api/stock/${product._id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' }, - authToken - ); + body: JSON.stringify(stockData) + }); // Update local products state setProducts(products.map(p => { @@ -140,44 +141,26 @@ export default function StockManagementPage() { const handleToggleStockTracking = async (product: Product) => { if (!product._id) return; - - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - return; - } try { - const apiUrl = `${process.env.NEXT_PUBLIC_API_URL}/products/${product._id}`; - // Toggle the stock tracking status const newTrackingStatus = !product.stockTracking; // For enabling tracking, we need to ensure there's a stock value - const stockData = { + const stockData: StockData = { stockTracking: newTrackingStatus, currentStock: product.currentStock || 0, lowStockThreshold: product.lowStockThreshold || 10, }; - // Update product with new tracking status - await saveProductData( - apiUrl, - stockData, - authToken, - "PUT" - ); - - // Also update stock API to ensure consistency - await updateProductStock( - product._id, - stockData, - authToken - ); + // Update stock tracking status + await clientFetch(`api/stock/${product._id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(stockData) + }); // Update local state setProducts(products.map(p => { @@ -200,340 +183,228 @@ export default function StockManagementPage() { }; const handleBulkAction = async (action: 'enable' | 'disable') => { - if (selectedProducts.length === 0) { - toast.error("No products selected"); - return; - } - setBulkAction(action); setIsConfirmDialogOpen(true); }; const executeBulkAction = async () => { - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken || !bulkAction) { - setIsConfirmDialogOpen(false); - return; - } - - setLoading(true); + if (!bulkAction) return; try { - const isEnabling = bulkAction === 'enable'; - const updatePromises = selectedProducts.map(async (productId) => { - const product = products.find(p => p._id === productId); - if (!product) return null; + const productsToUpdate = products.filter(p => selectedProducts.includes(p._id || '')); + + await Promise.all(productsToUpdate.map(async (product) => { + if (!product._id) return; - const apiUrl = `${process.env.NEXT_PUBLIC_API_URL}/products/${productId}`; - - const stockData = { - stockTracking: isEnabling, + const stockData: StockData = { + stockTracking: bulkAction === 'enable', currentStock: product.currentStock || 0, lowStockThreshold: product.lowStockThreshold || 10, }; - - // Update product with new tracking status - await saveProductData( - apiUrl, - stockData, - authToken, - "PUT" - ); - - // Also update stock API to ensure consistency - await updateProductStock( - productId, - stockData, - authToken - ); - return productId; - }); - - await Promise.all(updatePromises); + await clientFetch(`api/stock/${product._id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(stockData) + }); + })); // Update local state setProducts(products.map(p => { - if (selectedProducts.includes(p._id as string)) { + if (selectedProducts.includes(p._id || '')) { return { ...p, - stockTracking: isEnabling, + stockTracking: bulkAction === 'enable', }; } return p; })); setSelectedProducts([]); - toast.success(`Stock tracking ${isEnabling ? 'enabled' : 'disabled'} for ${selectedProducts.length} products`); + toast.success(`Stock tracking ${bulkAction}d for selected products`); } catch (error) { console.error(`Error ${bulkAction}ing stock tracking:`, error); toast.error(`Failed to ${bulkAction} stock tracking`); - } finally { - setLoading(false); - setIsConfirmDialogOpen(false); } + + setIsConfirmDialogOpen(false); + setBulkAction(null); }; const toggleSelectProduct = (productId: string) => { setSelectedProducts(prev => - prev.includes(productId) - ? prev.filter(id => id !== productId) + prev.includes(productId) + ? prev.filter(id => id !== productId) : [...prev, productId] ); }; const toggleSelectAll = () => { - if (selectedProducts.length === filteredProducts.length) { - setSelectedProducts([]); - } else { - setSelectedProducts(filteredProducts.map(p => p._id as string)); - } + setSelectedProducts(prev => + prev.length === products.length + ? [] + : products.map(p => p._id || '') + ); }; const getStockStatus = (product: Product) => { - if (!product.stockTracking) return "Not Tracked"; - - if (!product.currentStock || product.currentStock <= 0) { - return "Out of Stock"; - } else if (product.lowStockThreshold && product.currentStock <= product.lowStockThreshold) { - return "Low Stock"; - } else { - return "In Stock"; - } + if (!product.stockTracking) return 'Not tracked'; + if (product.currentStock === undefined) return 'Unknown'; + if (product.currentStock <= 0) return 'Out of stock'; + if (product.lowStockThreshold && product.currentStock <= product.lowStockThreshold) return 'Low stock'; + return 'In stock'; }; const filteredProducts = products.filter(product => { - const matchesSearch = - !searchTerm || - product.name.toLowerCase().includes(searchTerm.toLowerCase()); + if (!searchTerm) return true; - return matchesSearch; + const searchLower = searchTerm.toLowerCase(); + return ( + product.name.toLowerCase().includes(searchLower) || + product.description.toLowerCase().includes(searchLower) || + getStockStatus(product).toLowerCase().includes(searchLower) + ); }); - const statsData = { - total: products.length, - inStock: products.filter(p => p.stockTracking && p.currentStock && p.currentStock > 0 && (!p.lowStockThreshold || p.currentStock > p.lowStockThreshold)).length, - lowStock: products.filter(p => p.stockTracking && p.lowStockThreshold && p.currentStock && p.currentStock <= p.lowStockThreshold && p.currentStock > 0).length, - outOfStock: products.filter(p => p.stockTracking && (!p.currentStock || p.currentStock <= 0)).length, - notTracked: products.filter(p => p.stockTracking === false).length - }; - return ( -
-
-

Stock Management

-
- +
+
+

Stock Management

+
+ setSearchTerm(e.target.value)} + /> + {selectedProducts.length > 0 && ( + + + + + + handleBulkAction('enable')}> + + Enable Stock Tracking + + handleBulkAction('disable')}> + + Disable Stock Tracking + + + + )}
-
-
-
{statsData.total}
-
Total Products
-
- -
-
{statsData.inStock}
-
In Stock
-
- -
-
{statsData.lowStock}
-
Low Stock
-
- -
-
{statsData.outOfStock}
-
Out of Stock
-
- -
-
{statsData.notTracked}
-
Not Tracked
-
-
- -
-
-
-

Inventory List

- {selectedProducts.length > 0 && ( - - - - - - handleBulkAction('enable')}> - - Enable Tracking - - handleBulkAction('disable')}> - - Disable Tracking - - - - )} -
-
- setSearchTerm(e.target.value)} - className="w-64" - /> - -
-
- -
- - +
+
+ + + + + + Product + Stock Status + Current Stock + Track Stock + Actions + + + + {loading ? ( - - 0 && selectedProducts.length === filteredProducts.length} - onChange={toggleSelectAll} - /> - - Product - Unit - Status - Current Stock - Low Stock Threshold - Track Stock - Actions + + + Loading products... + - - - {loading ? ( - - - Loading... + ) : filteredProducts.length === 0 ? ( + + + No products found + + + ) : ( + filteredProducts.map((product) => ( + + + toggleSelectProduct(product._id || '')} + className="rounded border-gray-300" + /> - - ) : filteredProducts.length > 0 ? ( - filteredProducts.map((product) => ( - - - toggleSelectProduct(product._id as string)} - /> - - {product.name} - {product.unitType} - - {getStockStatus(product)} - - - {editingStock[product._id as string] ? ( + {product.name} + {getStockStatus(product)} + + {editingStock[product._id || ''] ? ( +
handleStockChange(product._id as string, parseFloat(e.target.value))} - className="max-w-[100px]" + value={stockValues[product._id || ''] || 0} + onChange={(e) => handleStockChange(product._id || '', parseInt(e.target.value) || 0)} + className="w-24" /> - ) : ( - product.stockTracking ? - `${product.currentStock || 0} ${product.unitType}` : - "N/A" - )} - - - {product.stockTracking ? - `${product.lowStockThreshold || 0} ${product.unitType}` : - "N/A"} - - - handleToggleStockTracking(product)} - /> - - - {product.stockTracking && ( - editingStock[product._id as string] ? ( - - ) : ( - - ) - )} - - - )) - ) : ( - - - No products found. + +
+ ) : ( + {product.currentStock || 0} + )} +
+ + handleToggleStockTracking(product)} + /> + + + {!editingStock[product._id || ''] && ( + + )}
- )} -
-
-
+ )) + )} + +
- - - - - - {bulkAction === 'enable' ? 'Enable' : 'Disable'} Stock Tracking - - - Are you sure you want to {bulkAction === 'enable' ? 'enable' : 'disable'} stock tracking for {selectedProducts.length} selected products? - {bulkAction === 'disable' && ' This will remove any stock tracking for these products.'} - {bulkAction === 'enable' && ' This will enable stock tracking with initial values.'} - - - - Cancel - - Confirm - - - -
+ + + + + Confirm Bulk Action + + Are you sure you want to {bulkAction} stock tracking for {selectedProducts.length} selected products? + + + + setBulkAction(null)}>Cancel + Continue + + + ); } \ No newline at end of file