diff --git a/app/dashboard/products/page.tsx b/app/dashboard/products/page.tsx index 16bbc47..7ef0d69 100644 --- a/app/dashboard/products/page.tsx +++ b/app/dashboard/products/page.tsx @@ -88,7 +88,7 @@ export default function ProductsPage() { })); }; - // Handle input changes + // Handle input changes const handleChange = ( e: ChangeEvent ) => setProductData({ ...productData, [e.target.name]: e.target.value }); @@ -239,7 +239,6 @@ export default function ProductsPage() { onClick={() => setImportModalOpen(true)} variant="outline" className="gap-2" - disabled={true} > Import Products diff --git a/app/layout.tsx b/app/layout.tsx index 95d1d0e..4176beb 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,7 @@ import { Inter } from "next/font/google" import "./globals.css" import { ThemeProvider } from "@/components/layout/theme-provider" +import { Toaster } from "sonner" import type React from "react" import KeepOnline from "@/components/KeepOnline" @@ -20,7 +21,12 @@ export default function RootLayout({ - + + {children} diff --git a/components/modals/import-products-modal.tsx b/components/modals/import-products-modal.tsx index a026713..de18245 100644 --- a/components/modals/import-products-modal.tsx +++ b/components/modals/import-products-modal.tsx @@ -8,11 +8,31 @@ import { toast } from "sonner"; import { Alert, AlertDescription } from "@/components/ui/alert"; interface ImportProductsModalProps { - open: boolean; + open: boolean; setOpen: (open: boolean) => void; onImportComplete: () => void; } +interface Price { + quantity: number; + unit: string; + pricePerUnit: number; + totalPrice: number; +} + +// Add unit standardization mapping +const unitMatch: Record = { + "cart": "pcs", + "gr": "gr", + "pcs": "pcs", + "unit": "pcs", + "tip": "pcs", + "bar": "pcs", + "vape": "pcs", + "ml": "pcs", + "pill": "pcs", +}; + export default function ImportProductsModal({ open, setOpen, onImportComplete }: ImportProductsModalProps) { const [file, setFile] = useState(null); const [isUploading, setIsUploading] = useState(false); @@ -35,34 +55,71 @@ export default function ImportProductsModal({ open, setOpen, onImportComplete }: try { setIsUploading(true); const fileContent = await file.text(); - - const products = [] - const sections = fileContent.split("----------------------------------------------------------") + const sections = fileContent.split("----------------------------------------------------------"); + const processedProducts = []; for (const section of sections) { - if(!section.trim()) continue + const productInfo = section.split("\n\n")[1]; + if (!productInfo) continue; - const lines = section.trim().split("\n") - const name = lines[0].trim() - const category = lines[1].trim().split("->")[0].trim() - const subcategory = lines[1].trim().split("->")[1].split("•")[0].trim() + const [name] = productInfo.split("\n"); + if (!name) continue; - console.log(`${name} - ${category} - ${subcategory}`) + const [category, subcategory] = productInfo.split("\n")[1].split("•")[0].split("->") + .map(item => item.trim()); - const pricing = lines.slice(3).filter(line => line.includes('@')).map(line => { - const price = line.split('@')[0].trim() - const unit = line.split('@')[1].trim() - return { price, unit } - }) + if (!category || !subcategory) continue; - console.log(pricing) - + // Process pricing + const priceText = section.split("\n\n")[2]; + if (!priceText) continue; + const priceLines = priceText.trim().split("\n"); + const prices = priceLines + .map(line => { + const matches = line.match(/from (\d+\.?\d*) (cart[s]?|gr|grams?|g|pcs|unit[s]?|tip[s]?|bar[s]?|vape[s]?|ml|pill[s]?) @ £(\d+\.?\d*)/); + if (!matches) return null; + + const quantity = parseFloat(matches[1]); + const totalPrice = parseFloat(matches[3]); + const pricePerUnit = totalPrice / quantity; + + const rawUnit = matches[2].replace(/s$/, ''); + return { + quantity, + unit: unitMatch[rawUnit] || rawUnit, + pricePerUnit, + totalPrice + }; + }) + .filter((price): price is Price => price !== null) + .sort((a, b) => a.quantity - b.quantity); + + if (prices.length > 0) { + processedProducts.push({ name, category, subcategory, prices }); + } } + + console.log('Processed products:', processedProducts); + // Get auth token from cookie + const authToken = document.cookie.split("Authorization=")[1]; + + // Send to API + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/products/batch`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${authToken}` + }, + body: JSON.stringify({ products: processedProducts }) + }); + if (!response.ok) { + throw new Error('Failed to upload products'); + } - //toast.success(`Successfully imported ${result.count} products`); + toast.success(`Successfully imported ${processedProducts.length} products`); onImportComplete(); setOpen(false); } catch (error) { diff --git a/components/tables/order-table.tsx b/components/tables/order-table.tsx index 3560806..321db84 100644 --- a/components/tables/order-table.tsx +++ b/components/tables/order-table.tsx @@ -144,7 +144,7 @@ export default function OrderTable() { method: "POST", body: JSON.stringify({ orderIds: Array.from(selectedOrders) }) }); - + setOrders(prev => prev.map(order => selectedOrders.has(order._id)