3import React, { useState, useEffect } from "react";
4import { useParams, useRouter } from "next/navigation";
5import { apiClient } from "@/lib/client/apiClient";
6import { Icon } from '@/contexts/IconContext';
22 confirmPrice?: boolean;
23 franchisePrice?: number;
28 discountLimit1?: number;
29 discountLimit2?: number;
30 discountLimit3?: number;
31 discountLimit4?: number;
32 discountLimit5?: number;
33 disableDiscounts?: boolean;
34 decimalPlaces?: number;
36 // Department Hierarchy (8 levels)
38 departmentName?: string;
49 supplierName?: string;
50 supplierPartCode?: string;
51 manufacturerPartCode?: string;
57 supplySource?: number;
58 ignoreStockMovements?: boolean;
59 recordStockMovements?: boolean;
62 warehouseLabelName?: string;
79 availableDate?: string;
82 shelfLabelFormat?: string;
83 shelfLabelLine1?: string;
84 shelfLabelLine2?: string;
85 shelfLabelLine3?: string;
88 alternateFit?: boolean;
91 templeLength?: string;
92 lensBaseCurve?: string;
93 lensMaterial?: number;
94 frameMaterial?: number;
97 measurementId?: number;
98 stockMeasurementId?: number;
99 displayMeasurementId?: number;
100 packagingWeight?: number;
104 shippingCubic?: number;
107 publishToWebsite?: boolean;
108 websiteHandling?: number;
109 webDescription?: string;
110 webKeywords?: string;
113 displayRank?: number;
114 websiteControl?: string;
116 outOfStock?: boolean;
117 outOfSeason?: boolean;
118 instoreOnly?: boolean;
119 newProduct?: boolean;
120 freeLocalDelivery?: boolean;
121 highlightProduct?: boolean;
122 featuredProduct?: boolean;
123 promoteProduct?: boolean;
124 marketingColor1?: string;
125 marketingColor2?: string;
126 marketingColor3?: string;
127 marketingColor4?: string;
128 installationUrl?: string;
129 warrantyUrl?: string;
131 marketingUrl?: string;
135 orderCategory?: number;
136 messageWhenOrdering?: string;
137 disableOrdering?: boolean;
138 minUnitsPerOrder?: number;
139 maxUnitsPerOrder?: number;
140 priorityColorCode?: number;
141 importanceLevel?: number;
143 // System/Debug (read-only)
147 createDateTime?: string;
148 processFlags?: number;
150 bigcommerceId?: number;
152 extractFlags?: number;
155 // Additional Barcodes
160 // Food Attributes (11 fields)
161 isGlutenFree?: boolean;
163 isDairyFree?: boolean;
164 isLowSugar?: boolean;
167 isYeastFree?: boolean;
173 // Physical Product Attributes
176 designShape?: string;
179 perishable?: boolean;
184 countryMade?: string;
189 asnzsCompliance?: string;
191 // Extended Website Flags
192 websiteBackgroundItem?: boolean;
193 websitePositionTop?: boolean;
194 websitePositionLeft?: boolean;
195 websitePositionBottom?: boolean;
196 websitePositionRight?: boolean;
199 // Extended Product Properties
203 refillable?: boolean;
204 internalSupportUrl?: string;
206 productCase?: string;
208 productManufacturer?: string;
212 printerConnectivity?: string;
213 printingTechnology?: string;
214 printerAdfCapacity?: string;
216 scanResolution?: string;
217 printerPaperCapacity?: string;
220 nonTangibleItem?: boolean;
221 oneOffItem?: boolean;
223 // Prompting Controls
224 promptRegno?: boolean;
225 promptLTNZNo?: boolean;
226 promptDocument?: boolean;
227 promptCustomer?: boolean;
230type TabType = "main" | "pricing" | "optical" | "measurements" | "website" | "supply" | "system" | "history";
232// Reusable Card Component
236 children: React.ReactNode;
240const Card: React.FC<CardProps> = ({ title, icon, children, className = "" }) => (
241 <div className={`bg-surface rounded-xl shadow-lg p-6 border border-border hover:shadow-xl transition-shadow ${className}`}>
242 <div className="flex items-center gap-3 mb-4">
243 <div className="w-10 h-10 bg-gradient-to-br from-brand to-brand-2 rounded-lg flex items-center justify-center">
244 <Icon name={icon} size={24} className="text-white" />
246 <h2 className="text-xl font-bold text-text">{title}</h2>
248 <div className="space-y-4">
254// Reusable Field Component
255interface FieldProps {
260 type?: "text" | "number" | "textarea" | "select" | "checkbox" | "date";
261 options?: { value: any; label: string }[];
262 onChange: (value: any) => void;
264 placeholder?: string;
267const Field: React.FC<FieldProps> = ({
278 <div className={className}>
279 <p className="text-sm text-muted mb-1">{label}</p>
282 {type === "checkbox" ? (
283 <label className="flex items-center gap-2 cursor-pointer">
286 checked={editValue || false}
287 onChange={(e) => onChange(e.target.checked)}
288 className="w-5 h-5 text-brand focus:ring-brand border-border rounded"
290 <span className="text-text">{editValue ? "Yes" : "No"}</span>
292 ) : type === "select" ? (
294 value={editValue || ''}
295 onChange={(e) => onChange(e.target.value)}
296 className="w-full px-3 py-2 border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-brand bg-surface text-text"
298 <option value="">Select...</option>
299 {options.map(opt => (
300 <option key={opt.value} value={opt.value}>{opt.label}</option>
303 ) : type === "textarea" ? (
305 value={editValue || ''}
306 onChange={(e) => onChange(e.target.value)}
307 className="w-full px-3 py-2 border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-brand bg-surface text-text"
309 placeholder={placeholder}
314 value={editValue || ''}
315 onChange={(e) => onChange(type === "number" ? parseFloat(e.target.value) || 0 : e.target.value)}
316 className="w-full px-3 py-2 border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-brand bg-surface text-text"
317 placeholder={placeholder}
322 <p className="text-text font-medium">
323 {type === "checkbox" ? (value ? "Yes" : "No") : (value !== null && value !== undefined ? value : "N/A")}
329export default function ProductDetailPage() {
330 const params = useParams();
331 const router = useRouter();
332 const productId = params.id as string;
334 const [activeTab, setActiveTab] = useState<TabType>("main");
335 const [product, setProduct] = useState<ProductData | null>(null);
336 const [departments, setDepartments] = useState<Record<number, string>>({});
337 const [loading, setLoading] = useState(true);
338 const [isEditing, setIsEditing] = useState(false);
339 const [editForm, setEditForm] = useState<Partial<ProductData>>({});
340 const [departmentHierarchy, setDepartmentHierarchy] = useState<Record<number, number[]>>({});
348 const loadHierarchy = async () => {
350 const result = await apiClient.getDepartmentHierarchy();
351 if (result.success && result.data) {
352 setDepartmentHierarchy(result.data);
355 console.error('Failed to load department hierarchy:', error);
359 const loadProductData = async () => {
362 const result = await apiClient.get(`/v1/openapi/products/${productId}`);
364 if (result?.success && result?.data) {
365 const p = result.data;
366 const productData: ProductData = {
368 name: p.Name || p.name || '',
369 description: p.Description || p.description || '',
370 shortName: p.ShortName || p.shortName,
371 barcode: p.Barcode || p.barcode,
373 sellPrice: p.SellPrice || p.sellPrice || 0,
374 costPrice: p.CostPrice || p.costPrice,
377 freight: p.Freight || p.freight,
378 packaging: p.Packaging || p.packaging,
379 confirmPrice: p.ConfirmPrice || p.confirmPrice,
380 franchisePrice: p.FranchisePrice || p.franchisePrice,
381 priceBand1: p.PriceBand1 || p.priceBand1,
382 priceBand2: p.PriceBand2 || p.priceBand2,
383 priceBand3: p.PriceBand3 || p.priceBand3,
384 lowestPrice: p.LowestPrice || p.lowestPrice,
385 discountLimit1: p.DiscountLimit1 || p.discountLimit1,
386 discountLimit2: p.DiscountLimit2 || p.discountLimit2,
387 discountLimit3: p.DiscountLimit3 || p.discountLimit3,
388 discountLimit4: p.DiscountLimit4 || p.discountLimit4,
389 discountLimit5: p.DiscountLimit5 || p.discountLimit5,
390 disableDiscounts: p.DisableDiscounts || p.disableDiscounts,
391 decimalPlaces: p.DecimalPlaces || p.decimalPlaces,
394 department: p.Department || p.department,
395 departmentName: p.DepartmentName || p.departmentName,
396 department2: p.Department2 || p.department2,
397 department3: p.Department3 || p.department3,
398 department4: p.Department4 || p.department4,
399 department5: p.Department5 || p.department5,
400 department6: p.Department6 || p.department6,
401 department7: p.Department7 || p.department7,
402 department8: p.Department8 || p.department8,
405 supplierId: p.SupplierId || p.supplierId,
406 supplierName: p.SupplierName || p.supplierName,
407 supplierPartCode: p.SupplierPartCode || p.supplierPartCode,
408 manufacturerPartCode: p.ManufacturerPartCode || p.manufacturerPartCode,
411 stockLevel: p.StockLevel || p.stockLevel,
412 stockMin: p.StockMin || p.stockMin,
413 stockMax: p.StockMax || p.stockMax,
414 supplySource: p.SupplySource || p.supplySource,
415 ignoreStockMovements: p.IgnoreStockMovements || p.ignoreStockMovements,
416 recordStockMovements: p.RecordStockMovements || p.recordStockMovements,
417 placement: p.Placement || p.placement,
418 partCount: p.PartCount || p.partCount,
419 warehouseLabelName: p.WarehouseLabelName || p.warehouseLabelName,
422 brand: p.Brand || p.brand,
423 weightGm: p.WeightGm || p.weightGm,
424 size: p.Size || p.size,
425 mainColour: p.MainColour || p.mainColour,
426 measurement: p.Measurement || p.measurement,
429 taxFlag: p.TaxFlag || p.taxFlag,
430 sellFlags: p.SellFlags || p.sellFlags,
431 noSell: p.NoSell || p.noSell,
432 hidden: p.Hidden || p.hidden,
435 releaseDate: p.ReleaseDate || p.releaseDate,
436 availableDate: p.AvailableDate || p.availableDate,
439 shelfLabelFormat: p.ShelfLabelFormat || p.shelfLabelFormat,
440 shelfLabelLine1: p.ShelfLabelLine1 || p.shelfLabelLine1,
441 shelfLabelLine2: p.ShelfLabelLine2 || p.shelfLabelLine2,
442 shelfLabelLine3: p.ShelfLabelLine3 || p.shelfLabelLine3,
445 alternateFit: p.AlternateFit || p.alternateFit,
446 lensSize: p.LensSize || p.lensSize,
447 bridgeSize: p.BridgeSize || p.bridgeSize,
448 templeLength: p.TempleLength || p.templeLength,
449 lensBaseCurve: p.LensBaseCurve || p.lensBaseCurve,
450 lensMaterial: p.LensMaterial || p.lensMaterial,
451 frameMaterial: p.FrameMaterial || p.frameMaterial,
454 measurementId: p.MeasurementId || p.measurementId,
455 stockMeasurementId: p.StockMeasurementId || p.stockMeasurementId,
456 displayMeasurementId: p.DisplayMeasurementId || p.displayMeasurementId,
457 packagingWeight: p.PackagingWeight || p.packagingWeight,
458 lengthMm: p.LengthMm || p.lengthMm,
459 widthMm: p.WidthMm || p.widthMm,
460 heightMm: p.HeightMm || p.heightMm,
461 shippingCubic: p.ShippingCubic || p.shippingCubic,
464 publishToWebsite: p.PublishToWebsite || p.publishToWebsite,
465 websiteHandling: p.WebsiteHandling || p.websiteHandling,
466 webDescription: p.WebDescription || p.webDescription,
467 webKeywords: p.WebKeywords || p.webKeywords,
468 webBlurb: p.WebBlurb || p.webBlurb,
469 searchRank: p.SearchRank || p.searchRank,
470 displayRank: p.DisplayRank || p.displayRank,
471 websiteControl: p.WebsiteControl || p.websiteControl,
472 onSpecial: p.OnSpecial || p.onSpecial,
473 outOfStock: p.OutOfStock || p.outOfStock,
474 outOfSeason: p.OutOfSeason || p.outOfSeason,
475 instoreOnly: p.InstoreOnly || p.instoreOnly,
476 newProduct: p.NewProduct || p.newProduct,
477 freeLocalDelivery: p.FreeLocalDelivery || p.freeLocalDelivery,
478 highlightProduct: p.HighlightProduct || p.highlightProduct,
479 featuredProduct: p.FeaturedProduct || p.featuredProduct,
480 promoteProduct: p.PromoteProduct || p.promoteProduct,
481 marketingColor1: p.MarketingColor1 || p.marketingColor1,
482 marketingColor2: p.MarketingColor2 || p.marketingColor2,
483 marketingColor3: p.MarketingColor3 || p.marketingColor3,
484 marketingColor4: p.MarketingColor4 || p.marketingColor4,
485 installationUrl: p.InstallationUrl || p.installationUrl,
486 warrantyUrl: p.WarrantyUrl || p.warrantyUrl,
487 upsellUrl: p.UpsellUrl || p.upsellUrl,
488 marketingUrl: p.MarketingUrl || p.marketingUrl,
489 webGroup: p.WebGroup || p.webGroup,
492 orderCategory: p.OrderCategory || p.orderCategory,
493 messageWhenOrdering: p.MessageWhenOrdering || p.messageWhenOrdering,
494 disableOrdering: p.DisableOrdering || p.disableOrdering,
495 minUnitsPerOrder: p.MinUnitsPerOrder || p.minUnitsPerOrder,
496 maxUnitsPerOrder: p.MaxUnitsPerOrder || p.maxUnitsPerOrder,
497 priorityColorCode: p.PriorityColorCode || p.priorityColorCode,
498 importanceLevel: p.ImportanceLevel || p.importanceLevel,
501 physkey: p.Physkey || p.physkey,
502 globalKey: p.GlobalKey || p.globalKey,
504 createDateTime: p.CreateDateTime || p.createDateTime,
505 processFlags: p.ProcessFlags || p.processFlags,
506 shopifyId: p.ShopifyId || p.shopifyId,
507 bigcommerceId: p.BigcommerceId || p.bigcommerceId,
508 rmSystem: p.RmSystem || p.rmSystem,
509 extractFlags: p.ExtractFlags || p.extractFlags,
510 comments: p.Comments || p.comments,
512 // Additional Barcodes
513 barcode512: p.Barcode512 || p.barcode512,
514 barcode513: p.Barcode513 || p.barcode513,
515 barcode514: p.Barcode514 || p.barcode514,
518 isGlutenFree: p.IsGlutenFree || p.isGlutenFree,
519 isOrganic: p.IsOrganic || p.isOrganic,
520 isDairyFree: p.IsDairyFree || p.isDairyFree,
521 isLowSugar: p.IsLowSugar || p.isLowSugar,
522 isVegan: p.IsVegan || p.isVegan,
523 isLowSalt: p.IsLowSalt || p.isLowSalt,
524 isYeastFree: p.IsYeastFree || p.isYeastFree,
525 isLowFat: p.IsLowFat || p.isLowFat,
526 isRaw: p.IsRaw || p.isRaw,
527 isKosher: p.IsKosher || p.isKosher,
528 isEdible: p.IsEdible || p.isEdible,
530 // Physical Product Attributes
531 lensType: p.LensType || p.lensType,
532 madeFrom: p.MadeFrom || p.madeFrom,
533 designShape: p.DesignShape || p.designShape,
534 polarised: p.Polarised || p.polarised,
535 mirrored: p.Mirrored || p.mirrored,
536 perishable: p.Perishable || p.perishable,
537 blueLight: p.BlueLight || p.blueLight,
540 fairTrade: p.FairTrade || p.fairTrade,
541 countryMade: p.CountryMade || p.countryMade,
542 nzMade: p.NZMade || p.nzMade,
545 gender: p.Gender || p.gender,
546 asnzsCompliance: p.ASNZSCompliance || p.asnzsCompliance,
548 // Extended Website Flags
549 websiteBackgroundItem: p.WebsiteBackgroundItem || p.websiteBackgroundItem,
550 websitePositionTop: p.WebsitePositionTop || p.websitePositionTop,
551 websitePositionLeft: p.WebsitePositionLeft || p.websitePositionLeft,
552 websitePositionBottom: p.WebsitePositionBottom || p.websitePositionBottom,
553 websitePositionRight: p.WebsitePositionRight || p.websitePositionRight,
554 websiteTag: p.WebsiteTag || p.websiteTag,
556 // Extended Product Properties
557 model: p.Model || p.model,
558 itemColor: p.ItemColor || p.itemColor,
559 cartridge: p.Cartridge || p.cartridge,
560 refillable: p.Refillable || p.refillable,
561 internalSupportUrl: p.InternalSupportUrl || p.internalSupportUrl,
562 rxAble: p.RxAble || p.rxAble,
563 productCase: p.ProductCase || p.productCase,
564 warranty: p.Warranty || p.warranty,
565 productManufacturer: p.ProductManufacturer || p.productManufacturer,
568 paperSize: p.PaperSize || p.paperSize,
569 printerConnectivity: p.PrinterConnectivity || p.printerConnectivity,
570 printingTechnology: p.PrintingTechnology || p.printingTechnology,
571 printerAdfCapacity: p.PrinterAdfCapacity || p.printerAdfCapacity,
572 printSpeed: p.PrintSpeed || p.printSpeed,
573 scanResolution: p.ScanResolution || p.scanResolution,
574 printerPaperCapacity: p.PrinterPaperCapacity || p.printerPaperCapacity,
577 nonTangibleItem: p.NonTangibleItem || p.nonTangibleItem,
578 oneOffItem: p.OneOffItem || p.oneOffItem,
580 // Prompting Controls
581 promptRegno: p.PromptRegno || p.promptRegno,
582 promptLTNZNo: p.PromptLTNZNo || p.promptLTNZNo,
583 promptDocument: p.PromptDocument || p.promptDocument,
584 promptCustomer: p.PromptCustomer || p.promptCustomer,
587 setProduct(productData);
588 setEditForm(productData);
591 console.error("Error loading product:", error);
597 const loadDepartments = async () => {
599 const result = await apiClient.getDepartments();
601 if (result.success && result.data) {
602 const deptMap: Record<number, string> = {};
603 let deptArray: any[] = [];
605 if (Array.isArray(result.data)) {
606 deptArray = result.data;
607 } else if ((result.data as any)?.DATS) {
608 deptArray = (result.data as any).DATS;
611 deptArray.forEach((dept: any) => {
612 const id = dept.f100 || dept.Depid || dept.depid || dept.id;
613 const name = dept.f101 || dept.Name || dept.name;
619 setDepartments(deptMap);
622 console.error('Error loading departments:', error);
626 const getDepartmentName = (deptId?: number): string => {
627 if (!deptId) return 'N/A';
628 return departments[deptId] || `Department ${deptId}`;
631 const handleEditToggle = () => {
633 setEditForm(product || {});
635 setIsEditing(!isEditing);
638 const handleSave = async () => {
642 const response = await fetch(`/api/v1/openapi/products/${productId}`, {
644 headers: { 'Content-Type': 'application/json' },
645 body: JSON.stringify(editForm),
648 const result = await response.json();
650 if (response.ok && result.success) {
651 setProduct(editForm as ProductData);
653 await loadProductData();
655 alert(`Failed to update product: ${result.error || 'Unknown error'}`);
658 console.error('Error saving product:', error);
659 alert('Error saving product. Please try again.');
667 <div className="p-6 min-h-screen bg-bg">
668 <div className="flex items-center justify-center h-64">
669 <div className="text-center">
670 <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-brand mx-auto"></div>
671 <p className="mt-4 text-muted">Loading product data...</p>
680 <div className="p-6 min-h-screen bg-bg">
681 <div className="max-w-7xl mx-auto">
682 <div className="bg-surface rounded-lg shadow-md p-8 text-center">
683 <h2 className="text-2xl font-bold text-text mb-4">Product Not Found</h2>
684 <p className="text-muted mb-6">The product with ID {productId} could not be found.</p>
686 onClick={() => router.push("/pages/products")}
687 className="px-6 py-2 bg-brand text-white rounded-lg hover:bg-brand-2 transition-colors"
698 <div className="p-6 min-h-screen bg-gradient-to-br from-bg via-bg to-surface">
699 <div className="max-w-7xl mx-auto">
701 <div className="mb-6 bg-surface rounded-xl shadow-lg p-6 border border-border">
702 <div className="flex items-center justify-between">
703 <div className="flex-1">
705 onClick={() => router.push("/pages/products")}
706 className="text-brand hover:text-brand-2 mb-3 flex items-center gap-2 transition-colors group"
708 <svg className="w-5 h-5 group-hover:-translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
709 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
713 <div className="flex items-center gap-4">
714 <div className="w-16 h-16 bg-gradient-to-br from-brand to-brand-2 rounded-xl flex items-center justify-center shadow-lg">
715 <Icon name="inventory_2" size={36} className="text-white" />
718 <h1 className="text-3xl font-bold text-text">{product.name}</h1>
719 <div className="flex items-center gap-3 mt-1">
720 <span className="text-muted text-sm">Product ID: {product.pid}</span>
721 {product.barcode && (
723 <span className="text-muted">•</span>
724 <span className="text-muted text-sm font-mono">{product.barcode}</span>
731 <div className="flex gap-2">
735 onClick={handleEditToggle}
736 className="px-4 py-2 bg-muted/80 text-white rounded-lg hover:bg-muted transition-colors flex items-center gap-2"
738 <Icon name="close" size={20} />
743 className="px-4 py-2 bg-brand text-white rounded-lg hover:bg-brand-2 transition-colors flex items-center gap-2 shadow-md"
745 <Icon name="save" size={20} />
751 onClick={handleEditToggle}
752 className="px-4 py-2 bg-brand text-white rounded-lg hover:bg-brand-2 transition-colors flex items-center gap-2 shadow-md"
754 <Icon name="edit" size={20} />
763 <div className="mb-6 bg-surface/50 backdrop-blur-sm rounded-xl shadow-lg border border-border p-2">
764 <nav className="grid grid-cols-4 lg:grid-cols-8 gap-2">
766 { id: "main", label: "Main", icon: "info" },
767 { id: "pricing", label: "Pricing", icon: "payments" },
768 { id: "optical", label: "Optical", icon: "visibility" },
769 { id: "measurements", label: "Measure", icon: "straighten" },
770 { id: "website", label: "Website", icon: "language" },
771 { id: "supply", label: "Supply", icon: "local_shipping" },
772 { id: "system", label: "System", icon: "settings" },
773 { id: "history", label: "History", icon: "history" }
777 onClick={() => setActiveTab(tab.id as TabType)}
778 className={`py-3 px-4 font-semibold text-sm rounded-lg transition-all ${
780 ? "bg-gradient-to-r from-brand to-brand-2 text-white shadow-lg shadow-brand/30"
781 : "bg-transparent text-muted hover:bg-surface-2 hover:text-text"
784 <div className="flex flex-col items-center justify-center gap-1">
785 <Icon name={tab.icon} size={20} />
786 <span className="text-xs">{tab.label}</span>
794 {activeTab === "main" && (
795 <div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
796 {/* Basic Information Card */}
797 <Card title="Basic Information" icon="description">
801 editValue={editForm.name}
802 isEditing={isEditing}
803 onChange={(v) => setEditForm({...editForm, name: v})}
807 value={product.description}
808 editValue={editForm.description}
809 isEditing={isEditing}
811 onChange={(v) => setEditForm({...editForm, description: v})}
815 value={product.shortName}
816 editValue={editForm.shortName}
817 isEditing={isEditing}
818 onChange={(v) => setEditForm({...editForm, shortName: v})}
822 value={product.brand}
823 editValue={editForm.brand}
824 isEditing={isEditing}
825 onChange={(v) => setEditForm({...editForm, brand: v})}
830 editValue={editForm.size}
831 isEditing={isEditing}
832 onChange={(v) => setEditForm({...editForm, size: v})}
836 value={product.weightGm}
837 editValue={editForm.weightGm}
838 isEditing={isEditing}
840 onChange={(v) => setEditForm({...editForm, weightGm: v})}
844 {/* Barcodes Card */}
845 <Card title="Barcodes & Identifiers" icon="qr_code_2">
847 label="Primary Barcode"
848 value={product.barcode}
849 editValue={editForm.barcode}
850 isEditing={isEditing}
851 onChange={(v) => setEditForm({...editForm, barcode: v})}
856 editValue={editForm.plu}
857 isEditing={isEditing}
858 onChange={(v) => setEditForm({...editForm, plu: v})}
861 label="Alternate Barcode (512)"
862 value={product.barcode512}
863 editValue={editForm.barcode512}
864 isEditing={isEditing}
865 onChange={(v) => setEditForm({...editForm, barcode512: v})}
868 label="Alternate Barcode (513)"
869 value={product.barcode513}
870 editValue={editForm.barcode513}
871 isEditing={isEditing}
872 onChange={(v) => setEditForm({...editForm, barcode513: v})}
875 label="Alternate Barcode (514)"
876 value={product.barcode514}
877 editValue={editForm.barcode514}
878 isEditing={isEditing}
879 onChange={(v) => setEditForm({...editForm, barcode514: v})}
883 {/* Product Types & Special Handling Card */}
884 <Card title="Product Types" icon="category">
886 label="Non-Tangible Item"
887 value={product.nonTangibleItem}
888 editValue={editForm.nonTangibleItem}
889 isEditing={isEditing}
891 onChange={(v) => setEditForm({...editForm, nonTangibleItem: v})}
895 value={product.oneOffItem}
896 editValue={editForm.oneOffItem}
897 isEditing={isEditing}
899 onChange={(v) => setEditForm({...editForm, oneOffItem: v})}
902 label="Cartridge/Refillable"
903 value={product.cartridge}
904 editValue={editForm.cartridge}
905 isEditing={isEditing}
906 onChange={(v) => setEditForm({...editForm, cartridge: v})}
910 value={product.refillable}
911 editValue={editForm.refillable}
912 isEditing={isEditing}
914 onChange={(v) => setEditForm({...editForm, refillable: v})}
917 label="Prompt Vehicle Registration"
918 value={product.promptRegno}
919 editValue={editForm.promptRegno}
920 isEditing={isEditing}
922 onChange={(v) => setEditForm({...editForm, promptRegno: v})}
925 label="Prompt LTNZ Number"
926 value={product.promptLTNZNo}
927 editValue={editForm.promptLTNZNo}
928 isEditing={isEditing}
930 onChange={(v) => setEditForm({...editForm, promptLTNZNo: v})}
933 label="Prompt Document"
934 value={product.promptDocument}
935 editValue={editForm.promptDocument}
936 isEditing={isEditing}
938 onChange={(v) => setEditForm({...editForm, promptDocument: v})}
941 label="Prompt Customer"
942 value={product.promptCustomer}
943 editValue={editForm.promptCustomer}
944 isEditing={isEditing}
946 onChange={(v) => setEditForm({...editForm, promptCustomer: v})}
950 {/* Department Hierarchy Card */}
951 <Card title="Department Hierarchy" icon="account_tree">
953 label="Department (1)"
954 value={getDepartmentName(product.department)}
955 editValue={editForm.department}
956 isEditing={isEditing}
958 options={Object.entries(departments).map(([id, name]) => ({ value: id, label: name }))}
959 onChange={(v) => setEditForm({...editForm, department: parseInt(v) || undefined})}
961 {(product.department2 || isEditing) && (
963 label="Department (2)"
964 value={getDepartmentName(product.department2)}
965 editValue={editForm.department2}
966 isEditing={isEditing}
968 options={Object.entries(departments).map(([id, name]) => ({ value: id, label: name }))}
969 onChange={(v) => setEditForm({...editForm, department2: parseInt(v) || undefined})}
972 {(product.department3 || isEditing) && (
974 label="Department (3)"
975 value={getDepartmentName(product.department3)}
976 editValue={editForm.department3}
977 isEditing={isEditing}
979 options={Object.entries(departments).map(([id, name]) => ({ value: id, label: name }))}
980 onChange={(v) => setEditForm({...editForm, department3: parseInt(v) || undefined})}
983 {(product.department4 || isEditing) && (
985 label="Department (4)"
986 value={getDepartmentName(product.department4)}
987 editValue={editForm.department4}
988 isEditing={isEditing}
990 options={Object.entries(departments).map(([id, name]) => ({ value: id, label: name }))}
991 onChange={(v) => setEditForm({...editForm, department4: parseInt(v) || undefined})}
996 {/* Supplier Card */}
997 <Card title="Supplier Information" icon="store">
999 <p className="text-sm text-muted mb-1">Supplier Name</p>
1000 <p className="text-text font-medium">{product.supplierName || 'N/A'}</p>
1003 label="Supplier Part Code"
1004 value={product.supplierPartCode}
1005 editValue={editForm.supplierPartCode}
1006 isEditing={isEditing}
1007 onChange={(v) => setEditForm({...editForm, supplierPartCode: v})}
1010 label="Manufacturer Part Code"
1011 value={product.manufacturerPartCode}
1012 editValue={editForm.manufacturerPartCode}
1013 isEditing={isEditing}
1014 onChange={(v) => setEditForm({...editForm, manufacturerPartCode: v})}
1018 {/* Product Flags Card */}
1019 <Card title="Product Flags & Dates" icon="flag">
1022 value={product.noSell}
1023 editValue={editForm.noSell}
1024 isEditing={isEditing}
1026 onChange={(v) => setEditForm({...editForm, noSell: v})}
1030 value={product.hidden}
1031 editValue={editForm.hidden}
1032 isEditing={isEditing}
1034 onChange={(v) => setEditForm({...editForm, hidden: v})}
1037 label="Release Date"
1038 value={product.releaseDate}
1039 editValue={editForm.releaseDate}
1040 isEditing={isEditing}
1042 onChange={(v) => setEditForm({...editForm, releaseDate: v})}
1045 label="Available Date"
1046 value={product.availableDate}
1047 editValue={editForm.availableDate}
1048 isEditing={isEditing}
1050 onChange={(v) => setEditForm({...editForm, availableDate: v})}
1056 {activeTab === "pricing" && (
1057 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
1058 {/* Core Pricing Card */}
1059 <Card title="Core Pricing" icon="attach_money">
1062 value={product.sellPrice}
1063 editValue={editForm.sellPrice}
1064 isEditing={isEditing}
1066 onChange={(v) => setEditForm({...editForm, sellPrice: v})}
1070 value={product.costPrice}
1071 editValue={editForm.costPrice}
1072 isEditing={isEditing}
1074 onChange={(v) => setEditForm({...editForm, costPrice: v})}
1078 value={product.freight}
1079 editValue={editForm.freight}
1080 isEditing={isEditing}
1082 onChange={(v) => setEditForm({...editForm, freight: v})}
1086 value={product.packaging}
1087 editValue={editForm.packaging}
1088 isEditing={isEditing}
1090 onChange={(v) => setEditForm({...editForm, packaging: v})}
1093 label="Decimal Places"
1094 value={product.decimalPlaces}
1095 editValue={editForm.decimalPlaces}
1096 isEditing={isEditing}
1098 onChange={(v) => setEditForm({...editForm, decimalPlaces: v})}
1102 {/* Advanced Pricing Card */}
1103 <Card title="Advanced Pricing" icon="trending_up">
1105 label="Confirm Price"
1106 value={product.confirmPrice}
1107 editValue={editForm.confirmPrice}
1108 isEditing={isEditing}
1110 onChange={(v) => setEditForm({...editForm, confirmPrice: v})}
1113 label="Franchise Price"
1114 value={product.franchisePrice}
1115 editValue={editForm.franchisePrice}
1116 isEditing={isEditing}
1118 onChange={(v) => setEditForm({...editForm, franchisePrice: v})}
1121 label="Price Band 1"
1122 value={product.priceBand1}
1123 editValue={editForm.priceBand1}
1124 isEditing={isEditing}
1126 onChange={(v) => setEditForm({...editForm, priceBand1: v})}
1129 label="Price Band 2"
1130 value={product.priceBand2}
1131 editValue={editForm.priceBand2}
1132 isEditing={isEditing}
1134 onChange={(v) => setEditForm({...editForm, priceBand2: v})}
1137 label="Price Band 3"
1138 value={product.priceBand3}
1139 editValue={editForm.priceBand3}
1140 isEditing={isEditing}
1142 onChange={(v) => setEditForm({...editForm, priceBand3: v})}
1146 {/* Discount Limits Card */}
1147 <Card title="Discount Limits" icon="discount">
1149 label="Disable Discounts"
1150 value={product.disableDiscounts}
1151 editValue={editForm.disableDiscounts}
1152 isEditing={isEditing}
1154 onChange={(v) => setEditForm({...editForm, disableDiscounts: v})}
1157 label="Lowest Price"
1158 value={product.lowestPrice}
1159 editValue={editForm.lowestPrice}
1160 isEditing={isEditing}
1162 onChange={(v) => setEditForm({...editForm, lowestPrice: v})}
1165 label="Discount Limit 1"
1166 value={product.discountLimit1}
1167 editValue={editForm.discountLimit1}
1168 isEditing={isEditing}
1170 onChange={(v) => setEditForm({...editForm, discountLimit1: v})}
1173 label="Discount Limit 2"
1174 value={product.discountLimit2}
1175 editValue={editForm.discountLimit2}
1176 isEditing={isEditing}
1178 onChange={(v) => setEditForm({...editForm, discountLimit2: v})}
1182 {/* Stock Control Card */}
1183 <Card title="Stock Control" icon="inventory">
1185 label="Current Stock"
1186 value={product.stockLevel}
1187 editValue={editForm.stockLevel}
1188 isEditing={isEditing}
1190 onChange={(v) => setEditForm({...editForm, stockLevel: v})}
1194 value={product.stockMin}
1195 editValue={editForm.stockMin}
1196 isEditing={isEditing}
1198 onChange={(v) => setEditForm({...editForm, stockMin: v})}
1202 value={product.stockMax}
1203 editValue={editForm.stockMax}
1204 isEditing={isEditing}
1206 onChange={(v) => setEditForm({...editForm, stockMax: v})}
1209 label="Ignore Stock Movements"
1210 value={product.ignoreStockMovements}
1211 editValue={editForm.ignoreStockMovements}
1212 isEditing={isEditing}
1214 onChange={(v) => setEditForm({...editForm, ignoreStockMovements: v})}
1217 label="Record Stock Movements"
1218 value={product.recordStockMovements}
1219 editValue={editForm.recordStockMovements}
1220 isEditing={isEditing}
1222 onChange={(v) => setEditForm({...editForm, recordStockMovements: v})}
1228 {activeTab === "optical" && (
1229 <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
1230 <Card title="Optical/Eyewear" icon="visibility">
1232 label="Alternate Fit"
1233 value={product.alternateFit}
1234 editValue={editForm.alternateFit}
1235 isEditing={isEditing}
1237 onChange={(v) => setEditForm({...editForm, alternateFit: v})}
1241 value={product.lensSize}
1242 editValue={editForm.lensSize}
1243 isEditing={isEditing}
1244 onChange={(v) => setEditForm({...editForm, lensSize: v})}
1248 value={product.bridgeSize}
1249 editValue={editForm.bridgeSize}
1250 isEditing={isEditing}
1251 onChange={(v) => setEditForm({...editForm, bridgeSize: v})}
1254 label="Temple Length"
1255 value={product.templeLength}
1256 editValue={editForm.templeLength}
1257 isEditing={isEditing}
1258 onChange={(v) => setEditForm({...editForm, templeLength: v})}
1261 label="Lens Base Curve"
1262 value={product.lensBaseCurve}
1263 editValue={editForm.lensBaseCurve}
1264 isEditing={isEditing}
1265 onChange={(v) => setEditForm({...editForm, lensBaseCurve: v})}
1269 value={product.lensType}
1270 editValue={editForm.lensType}
1271 isEditing={isEditing}
1272 onChange={(v) => setEditForm({...editForm, lensType: v})}
1276 value={product.polarised}
1277 editValue={editForm.polarised}
1278 isEditing={isEditing}
1280 onChange={(v) => setEditForm({...editForm, polarised: v})}
1284 value={product.mirrored}
1285 editValue={editForm.mirrored}
1286 isEditing={isEditing}
1288 onChange={(v) => setEditForm({...editForm, mirrored: v})}
1291 label="Blue Light Protection"
1292 value={product.blueLight}
1293 editValue={editForm.blueLight}
1294 isEditing={isEditing}
1296 onChange={(v) => setEditForm({...editForm, blueLight: v})}
1299 label="RxAble (Prescription Capable)"
1300 value={product.rxAble}
1301 editValue={editForm.rxAble}
1302 isEditing={isEditing}
1304 onChange={(v) => setEditForm({...editForm, rxAble: v})}
1308 <Card title="Physical Attributes" icon="category">
1311 value={product.model}
1312 editValue={editForm.model}
1313 isEditing={isEditing}
1314 onChange={(v) => setEditForm({...editForm, model: v})}
1318 value={product.itemColor}
1319 editValue={editForm.itemColor}
1320 isEditing={isEditing}
1321 onChange={(v) => setEditForm({...editForm, itemColor: v})}
1324 label="Design Shape"
1325 value={product.designShape}
1326 editValue={editForm.designShape}
1327 isEditing={isEditing}
1328 onChange={(v) => setEditForm({...editForm, designShape: v})}
1332 value={product.madeFrom}
1333 editValue={editForm.madeFrom}
1334 isEditing={isEditing}
1335 onChange={(v) => setEditForm({...editForm, madeFrom: v})}
1338 label="Product Case"
1339 value={product.productCase}
1340 editValue={editForm.productCase}
1341 isEditing={isEditing}
1342 onChange={(v) => setEditForm({...editForm, productCase: v})}
1346 value={product.warranty}
1347 editValue={editForm.warranty}
1348 isEditing={isEditing}
1349 onChange={(v) => setEditForm({...editForm, warranty: v})}
1352 label="Product Manufacturer"
1353 value={product.productManufacturer}
1354 editValue={editForm.productManufacturer}
1355 isEditing={isEditing}
1356 onChange={(v) => setEditForm({...editForm, productManufacturer: v})}
1360 <Card title="Food & Supply Chain" icon="restaurant">
1363 value={product.isGlutenFree}
1364 editValue={editForm.isGlutenFree}
1365 isEditing={isEditing}
1367 onChange={(v) => setEditForm({...editForm, isGlutenFree: v})}
1371 value={product.isOrganic}
1372 editValue={editForm.isOrganic}
1373 isEditing={isEditing}
1375 onChange={(v) => setEditForm({...editForm, isOrganic: v})}
1379 value={product.isDairyFree}
1380 editValue={editForm.isDairyFree}
1381 isEditing={isEditing}
1383 onChange={(v) => setEditForm({...editForm, isDairyFree: v})}
1387 value={product.isVegan}
1388 editValue={editForm.isVegan}
1389 isEditing={isEditing}
1391 onChange={(v) => setEditForm({...editForm, isVegan: v})}
1395 value={product.isKosher}
1396 editValue={editForm.isKosher}
1397 isEditing={isEditing}
1399 onChange={(v) => setEditForm({...editForm, isKosher: v})}
1403 value={product.perishable}
1404 editValue={editForm.perishable}
1405 isEditing={isEditing}
1407 onChange={(v) => setEditForm({...editForm, perishable: v})}
1411 value={product.fairTrade}
1412 editValue={editForm.fairTrade}
1413 isEditing={isEditing}
1415 onChange={(v) => setEditForm({...editForm, fairTrade: v})}
1419 value={product.nzMade}
1420 editValue={editForm.nzMade}
1421 isEditing={isEditing}
1423 onChange={(v) => setEditForm({...editForm, nzMade: v})}
1426 label="Country Made"
1427 value={product.countryMade}
1428 editValue={editForm.countryMade}
1429 isEditing={isEditing}
1430 onChange={(v) => setEditForm({...editForm, countryMade: v})}
1436 {activeTab === "measurements" && (
1437 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
1438 <Card title="Measurements" icon="straighten">
1441 value={product.lengthMm}
1442 editValue={editForm.lengthMm}
1443 isEditing={isEditing}
1445 onChange={(v) => setEditForm({...editForm, lengthMm: v})}
1449 value={product.widthMm}
1450 editValue={editForm.widthMm}
1451 isEditing={isEditing}
1453 onChange={(v) => setEditForm({...editForm, widthMm: v})}
1457 value={product.heightMm}
1458 editValue={editForm.heightMm}
1459 isEditing={isEditing}
1461 onChange={(v) => setEditForm({...editForm, heightMm: v})}
1464 label="Packaging Weight"
1465 value={product.packagingWeight}
1466 editValue={editForm.packagingWeight}
1467 isEditing={isEditing}
1469 onChange={(v) => setEditForm({...editForm, packagingWeight: v})}
1472 label="Shipping Cubic"
1473 value={product.shippingCubic}
1474 editValue={editForm.shippingCubic}
1475 isEditing={isEditing}
1477 onChange={(v) => setEditForm({...editForm, shippingCubic: v})}
1483 {activeTab === "website" && (
1484 <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
1485 <Card title="Website Configuration" icon="language">
1487 label="Publish to Website"
1488 value={product.publishToWebsite}
1489 editValue={editForm.publishToWebsite}
1490 isEditing={isEditing}
1492 onChange={(v) => setEditForm({...editForm, publishToWebsite: v})}
1495 label="Web Description"
1496 value={product.webDescription}
1497 editValue={editForm.webDescription}
1498 isEditing={isEditing}
1500 onChange={(v) => setEditForm({...editForm, webDescription: v})}
1503 label="Web Keywords"
1504 value={product.webKeywords}
1505 editValue={editForm.webKeywords}
1506 isEditing={isEditing}
1507 onChange={(v) => setEditForm({...editForm, webKeywords: v})}
1511 value={product.webBlurb}
1512 editValue={editForm.webBlurb}
1513 isEditing={isEditing}
1515 onChange={(v) => setEditForm({...editForm, webBlurb: v})}
1519 value={product.websiteTag}
1520 editValue={editForm.websiteTag}
1521 isEditing={isEditing}
1522 onChange={(v) => setEditForm({...editForm, websiteTag: v})}
1526 value={product.gender}
1527 editValue={editForm.gender}
1528 isEditing={isEditing}
1529 onChange={(v) => setEditForm({...editForm, gender: v})}
1532 label="AS/NZS Compliance"
1533 value={product.asnzsCompliance}
1534 editValue={editForm.asnzsCompliance}
1535 isEditing={isEditing}
1536 onChange={(v) => setEditForm({...editForm, asnzsCompliance: v})}
1540 <Card title="Marketing Flags" icon="sell">
1543 value={product.onSpecial}
1544 editValue={editForm.onSpecial}
1545 isEditing={isEditing}
1547 onChange={(v) => setEditForm({...editForm, onSpecial: v})}
1551 value={product.newProduct}
1552 editValue={editForm.newProduct}
1553 isEditing={isEditing}
1555 onChange={(v) => setEditForm({...editForm, newProduct: v})}
1558 label="Featured Product"
1559 value={product.featuredProduct}
1560 editValue={editForm.featuredProduct}
1561 isEditing={isEditing}
1563 onChange={(v) => setEditForm({...editForm, featuredProduct: v})}
1566 label="Highlight Product"
1567 value={product.highlightProduct}
1568 editValue={editForm.highlightProduct}
1569 isEditing={isEditing}
1571 onChange={(v) => setEditForm({...editForm, highlightProduct: v})}
1574 label="Promote Product"
1575 value={product.promoteProduct}
1576 editValue={editForm.promoteProduct}
1577 isEditing={isEditing}
1579 onChange={(v) => setEditForm({...editForm, promoteProduct: v})}
1582 label="Out of Stock"
1583 value={product.outOfStock}
1584 editValue={editForm.outOfStock}
1585 isEditing={isEditing}
1587 onChange={(v) => setEditForm({...editForm, outOfStock: v})}
1590 label="Out of Season"
1591 value={product.outOfSeason}
1592 editValue={editForm.outOfSeason}
1593 isEditing={isEditing}
1595 onChange={(v) => setEditForm({...editForm, outOfSeason: v})}
1598 label="In-store Only"
1599 value={product.instoreOnly}
1600 editValue={editForm.instoreOnly}
1601 isEditing={isEditing}
1603 onChange={(v) => setEditForm({...editForm, instoreOnly: v})}
1607 <Card title="Website Positioning" icon="web">
1609 label="Background Item"
1610 value={product.websiteBackgroundItem}
1611 editValue={editForm.websiteBackgroundItem}
1612 isEditing={isEditing}
1614 onChange={(v) => setEditForm({...editForm, websiteBackgroundItem: v})}
1617 label="Position Top"
1618 value={product.websitePositionTop}
1619 editValue={editForm.websitePositionTop}
1620 isEditing={isEditing}
1622 onChange={(v) => setEditForm({...editForm, websitePositionTop: v})}
1625 label="Position Left"
1626 value={product.websitePositionLeft}
1627 editValue={editForm.websitePositionLeft}
1628 isEditing={isEditing}
1630 onChange={(v) => setEditForm({...editForm, websitePositionLeft: v})}
1633 label="Position Bottom"
1634 value={product.websitePositionBottom}
1635 editValue={editForm.websitePositionBottom}
1636 isEditing={isEditing}
1638 onChange={(v) => setEditForm({...editForm, websitePositionBottom: v})}
1641 label="Position Right"
1642 value={product.websitePositionRight}
1643 editValue={editForm.websitePositionRight}
1644 isEditing={isEditing}
1646 onChange={(v) => setEditForm({...editForm, websitePositionRight: v})}
1652 {activeTab === "supply" && (
1653 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
1654 <Card title="Supply & Ordering" icon="local_shipping">
1656 label="Disable Ordering"
1657 value={product.disableOrdering}
1658 editValue={editForm.disableOrdering}
1659 isEditing={isEditing}
1661 onChange={(v) => setEditForm({...editForm, disableOrdering: v})}
1664 label="Min Units Per Order"
1665 value={product.minUnitsPerOrder}
1666 editValue={editForm.minUnitsPerOrder}
1667 isEditing={isEditing}
1669 onChange={(v) => setEditForm({...editForm, minUnitsPerOrder: v})}
1672 label="Max Units Per Order"
1673 value={product.maxUnitsPerOrder}
1674 editValue={editForm.maxUnitsPerOrder}
1675 isEditing={isEditing}
1677 onChange={(v) => setEditForm({...editForm, maxUnitsPerOrder: v})}
1680 label="Message When Ordering"
1681 value={product.messageWhenOrdering}
1682 editValue={editForm.messageWhenOrdering}
1683 isEditing={isEditing}
1685 placeholder="Communication notes for ordering staff"
1686 onChange={(v) => setEditForm({...editForm, messageWhenOrdering: v})}
1692 {activeTab === "system" && (
1693 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
1694 <Card title="System Information" icon="settings">
1696 <p className="text-sm text-muted mb-1">Physical Key</p>
1697 <p className="text-text font-mono">{product.physkey || 'N/A'}</p>
1700 <p className="text-sm text-muted mb-1">Global Key</p>
1701 <p className="text-text font-mono">{product.globalKey || 'N/A'}</p>
1704 <p className="text-sm text-muted mb-1">RVE (Replication Version)</p>
1705 <p className="text-text font-mono">{product.rve || 'N/A'}</p>
1708 <p className="text-sm text-muted mb-1">Created</p>
1709 <p className="text-text">{product.createDateTime ? new Date(product.createDateTime).toLocaleString() : 'N/A'}</p>
1712 <p className="text-sm text-muted mb-1">Shopify ID</p>
1713 <p className="text-text font-mono">{product.shopifyId || 'N/A'}</p>
1716 <p className="text-sm text-muted mb-1">BigCommerce ID</p>
1717 <p className="text-text font-mono">{product.bigcommerceId || 'N/A'}</p>
1721 <Card title="Comments" icon="comment">
1723 label="Internal Comments"
1724 value={product.comments}
1725 editValue={editForm.comments}
1726 isEditing={isEditing}
1728 placeholder="Internal notes about this product"
1729 onChange={(v) => setEditForm({...editForm, comments: v})}
1735 {activeTab === "history" && (
1736 <div className="grid grid-cols-1 gap-6">
1737 <Card title="Product History" icon="history">
1738 <p className="text-muted">Sales history, stock movements, and price changes will be displayed here.</p>