"use client"; import { useEffect, useRef, useState } from "react"; import { X, Upload, CheckCircle2, AlertTriangle, Eye } from "lucide-react"; import { api } from "@/lib/api"; type Mode = "add" | "edit" | "duplicate" | "view"; const CATEGORIES = ["Pain Relief", "Supplement", "Pharma", "Cosmetic", "Food", "Beverage", "Other"]; const UNIT_TYPES = ["Tablets", "Capsules", "ml", "g", "kg", "Strips", "Bottles"]; const FORMS = ["Tablet", "Capsule", "Syrup", "Powder", "Cream", "Gel", "Liquid"]; const empty = { name: "", brand_id: "", category: "", sku: "", description: "", image_url: "", manufacturer: { company: "", plant: "", country: "India", license_number: "", location: "" }, details: { net_weight: "", pack_size: "", unit_type: "", form_type: "", storage_condition: "", shelf_life: "", temperature_range: "", dosage: "" }, recycling_info: "", internal_notes: "", qr_type: "dynamic", display_brand_logo: true, status: "active",website: "", email: "", phone: "", }; export default function ProductDialog({ mode, productId, onClose, onSaved, }: { mode: Mode; productId?: string; onClose: () => void; onSaved: () => void }) { const [brands, setBrands] = useState([]); const [form, setForm] = useState(empty); const [logoUrl, setLogoUrl] = useState(""); const [err, setErr] = useState(""); const [fErr, setFErr] = useState>({}); const [busy, setBusy] = useState(false); const fileRef = useRef(null); const bodyRef = useRef(null); const readOnly = mode === "view"; // required fields — key uses "group.field" for nested const REQUIRED: Record = { name: "Product name is required", brand_id: "Please select a brand", category: "Category is required", sku: "SKU is required", "manufacturer.company": "Manufacturer name is required", "manufacturer.country": "Country of origin is required", }; function validate(): boolean { const e: Record = {}; for (const [key, msg] of Object.entries(REQUIRED)) { const val = key.includes(".") ? form[key.split(".")[0]]?.[key.split(".")[1]] : form[key]; if (!val || !String(val).trim()) e[key] = msg; } setFErr(e); if (Object.keys(e).length) { setErr("Please fill in all required fields highlighted below."); bodyRef.current?.scrollTo({ top: 0, behavior: "smooth" }); return false; } setErr(""); return true; } function validateMinimal(): boolean { const e: Record = {}; if (!form.name.trim()) e.name = REQUIRED.name; if (!form.brand_id) e.brand_id = REQUIRED.brand_id; setFErr(e); if (Object.keys(e).length) { setErr("A draft still needs a product name and brand."); bodyRef.current?.scrollTo({ top: 0, behavior: "smooth" }); return false; } setErr(""); return true; } useEffect(() => { api("/brands").then(setBrands).catch(() => {}); if ((mode === "edit" || mode === "view" || mode === "duplicate") && productId) { api(`/products/${productId}`).then((p) => { setForm({ name: mode === "duplicate" ? `${p.name} (Copy)` : p.name, brand_id: p.brand_id, category: p.category ?? "", sku: mode === "duplicate" ? "" : (p.sku ?? ""), description: p.description ?? "", image_url: p.image_url ?? "", manufacturer: { ...empty.manufacturer, ...(p.manufacturer ?? {}) }, details: { ...empty.details, ...(p.details ?? {}) }, recycling_info: p.recycling_info ?? "", internal_notes: p.internal_notes ?? "", qr_type: p.qr_type ?? "dynamic", display_brand_logo: p.display_brand_logo ?? true, status: p.status ?? "active", }); if (p.image_url) setLogoUrl(p.image_url); }).catch(() => {}); } }, [mode, productId]); const clearErr = (key: string) => setFErr((e) => { if (!e[key]) return e; const n = { ...e }; delete n[key]; return n; }); const set = (k: string, v: any) => { setForm((f: any) => ({ ...f, [k]: v })); clearErr(k); }; const setN = (group: string, k: string, v: any) => { setForm((f: any) => ({ ...f, [group]: { ...f[group], [k]: v } })); clearErr(`${group}.${k}`); }; function onImage(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; if (file.size > 2 * 1024 * 1024) { setErr("Image must be under 2 MB"); return; } const r = new FileReader(); r.onload = () => { set("image_url", r.result as string); setLogoUrl(r.result as string); }; r.readAsDataURL(file); } async function save(asDraft: boolean) { // A draft still needs the identifying fields; publishing needs everything. const ok = asDraft ? (form.name.trim() && form.brand_id ? true : validateMinimal()) : validate(); if (!ok) return; setBusy(true); const payload = { ...form, status: asDraft ? "draft" : form.status }; try { if (mode === "edit" && productId) { await api(`/products/${productId}`, { method: "PUT", body: JSON.stringify(payload) }); } else { // add + duplicate both create a new product await api("/products", { method: "POST", body: JSON.stringify(payload) }); } onSaved(); onClose(); } catch (e: any) { const d = e.detail; setErr(typeof d === "object" ? d.message : d || "Failed to save product"); } finally { setBusy(false); } } const titles: Record = { add: "Add Product", edit: "Edit Product", duplicate: "Duplicate Product", view: "Product Details" }; return (
e.stopPropagation()}> {/* header */}

{titles[mode]}

{err &&
{err}
}
{/* 1. Basic */}
set("name", v)} ro={readOnly} placeholder="e.g. Pain Relief 500mg" error={fErr.name} /> set("brand_id", v)} ro={readOnly} opts={brands.map((b) => ({ value: b.id, label: b.name }))} ph="Select brand" error={fErr.brand_id} />
set("category", v)} ro={readOnly} opts={CATEGORIES.map((c) => ({ value: c, label: c }))} ph="Select" error={fErr.category} /> set("sku", v)} ro={readOnly} placeholder="e.g. PR500" error={fErr.sku} />
set("description", v)} ro={readOnly} placeholder="Short description" />
{logoUrl && }
{/* 2. Manufacturing */}
setN("manufacturer", "company", v)} ro={readOnly} placeholder="e.g. ABC Pharma Pvt Ltd" error={fErr["manufacturer.company"]} /> setN("manufacturer", "plant", v)} ro={readOnly} placeholder="Plant" />
setN("manufacturer", "country", v)} ro={readOnly} error={fErr["manufacturer.country"]} /> setN("manufacturer", "license_number", v)} ro={readOnly} placeholder="e.g. TN/25D/12345" />
setN("manufacturer", "location", v)} ro={readOnly} placeholder="e.g. Bengaluru, Karnataka" />
setN("manufacturer", "email", v)} ro={readOnly} placeholder="support@example.com" /> setN("manufacturer", "phone", v)} ro={readOnly} placeholder="+91 9876543210" />
setN("manufacturer", "website", v)} ro={readOnly} placeholder="https://example.com" />
{/* 3. Details */}
setN("details", "net_weight", v)} ro={readOnly} placeholder="e.g. 10 Tablets" /> setN("details", "pack_size", v)} ro={readOnly} placeholder="e.g. 10/Strip" /> setN("details", "unit_type", v)} ro={readOnly} opts={UNIT_TYPES.map((u) => ({ value: u, label: u }))} ph="Select" /> setN("details", "form_type", v)} ro={readOnly} opts={FORMS.map((f) => ({ value: f, label: f }))} ph="Select" /> setN("details", "storage_condition", v)} ro={readOnly} placeholder="Dry & Cool" /> setN("details", "shelf_life", v)} ro={readOnly} placeholder="24 Months" /> setN("details", "temperature_range", v)} ro={readOnly} placeholder="15-25°C" /> setN("details", "dosage", v)} ro={readOnly} placeholder="As directed" />
{/* 4. QR config */}
set("qr_type", v)} ro={readOnly} opts={[{ value: "dynamic", label: "Dynamic (Recommended)" }, { value: "static", label: "Static" }]} /> set("status", form.status === "active" ? "inactive" : "active")} text={form.status === "active" ? "Active" : "Inactive"} />

Dynamic QR lets you update info anytime without changing the printed code.

{/* 6. Compliance summary */}
{[ ["FSSAI / License", !!form.manufacturer.license_number], ["Country / Origin", !!form.manufacturer.country], ["Product Category", !!form.category], ["Recycling / EPR", !!form.recycling_info], ].map(([label, ok]) => (
{label} {ok ? Complete : Incomplete}
))} set("recycling_info", v)} ro={readOnly} placeholder="Disposal & recycling notes" />
{/* 8. Internal notes + 7 branding */}
set("display_brand_logo", !form.display_brand_logo)} text={form.display_brand_logo ? "Yes" : "No"} /> set("internal_notes", v)} ro={readOnly} placeholder="Internal notes about this product" />
{/* 9. Preview */}
{logoUrl ? :
}
{form.name || "Product name"}
Brand: {brands.find((b) => b.id === form.brand_id)?.name ?? "—"} · SKU: {form.sku || "—"} · {form.category || "—"}
{form.status}
{/* footer */} {!readOnly && (
)}
); } function Section({ n, title, children, full }: any) { return (
{n}

{title}

{children}
); } function Label({ children }: any) { return {children}; } function Inp({ label, value, onChange, ro, req, placeholder, error }: { label: string; value: any; onChange: (v: string) => void; ro?: boolean; req?: boolean; placeholder?: string; error?: string; }) { return ( ); } function Sel({ label, value, onChange, ro, req, opts, ph, error }: { label: string; value: any; onChange: (v: string) => void; ro?: boolean; req?: boolean; opts: { value: string; label: string }[]; ph?: string; error?: string; }) { return ( ); } function ToggleRow({ label, on, onClick, ro, text }: any) { return (
); }