EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
page.tsx
Go to the documentation of this file.
1"use client";
2
3import React, { useState, useEffect } from "react";
4import { useParams, useRouter } from "next/navigation";
5import { apiClient } from "@/lib/client/apiClient";
6import { Icon } from '@/contexts/IconContext';
7
8interface ProductData {
9 // Basic Identity
10 pid: number;
11 name: string;
12 description: string;
13 shortName?: string;
14 barcode?: string;
15 plu?: string;
16
17 // Pricing
18 sellPrice: number;
19 costPrice?: number;
20 freight?: number;
21 packaging?: number;
22 confirmPrice?: boolean;
23 franchisePrice?: number;
24 priceBand1?: number;
25 priceBand2?: number;
26 priceBand3?: number;
27 lowestPrice?: number;
28 discountLimit1?: number;
29 discountLimit2?: number;
30 discountLimit3?: number;
31 discountLimit4?: number;
32 discountLimit5?: number;
33 disableDiscounts?: boolean;
34 decimalPlaces?: number;
35
36 // Department Hierarchy (8 levels)
37 department?: number;
38 departmentName?: string;
39 department2?: number;
40 department3?: number;
41 department4?: number;
42 department5?: number;
43 department6?: number;
44 department7?: number;
45 department8?: number;
46
47 // Supplier
48 supplierId?: number;
49 supplierName?: string;
50 supplierPartCode?: string;
51 manufacturerPartCode?: string;
52
53 // Stock
54 stockLevel?: number;
55 stockMin?: number;
56 stockMax?: number;
57 supplySource?: number;
58 ignoreStockMovements?: boolean;
59 recordStockMovements?: boolean;
60 placement?: number;
61 partCount?: number;
62 warehouseLabelName?: string;
63
64 // Core Attributes
65 brand?: string;
66 weightGm?: number;
67 size?: string;
68 mainColour?: number;
69 measurement?: number;
70
71 // Flags
72 taxFlag?: number;
73 sellFlags?: number;
74 noSell?: boolean;
75 hidden?: boolean;
76
77 // Dates
78 releaseDate?: string;
79 availableDate?: string;
80
81 // Shelf Labels
82 shelfLabelFormat?: string;
83 shelfLabelLine1?: string;
84 shelfLabelLine2?: string;
85 shelfLabelLine3?: string;
86
87 // Optical/Eyewear
88 alternateFit?: boolean;
89 lensSize?: string;
90 bridgeSize?: string;
91 templeLength?: string;
92 lensBaseCurve?: string;
93 lensMaterial?: number;
94 frameMaterial?: number;
95
96 // Measurements
97 measurementId?: number;
98 stockMeasurementId?: number;
99 displayMeasurementId?: number;
100 packagingWeight?: number;
101 lengthMm?: number;
102 widthMm?: number;
103 heightMm?: number;
104 shippingCubic?: number;
105
106 // Website/Marketing
107 publishToWebsite?: boolean;
108 websiteHandling?: number;
109 webDescription?: string;
110 webKeywords?: string;
111 webBlurb?: string;
112 searchRank?: number;
113 displayRank?: number;
114 websiteControl?: string;
115 onSpecial?: boolean;
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;
130 upsellUrl?: string;
131 marketingUrl?: string;
132 webGroup?: string;
133
134 // Supply/Ordering
135 orderCategory?: number;
136 messageWhenOrdering?: string;
137 disableOrdering?: boolean;
138 minUnitsPerOrder?: number;
139 maxUnitsPerOrder?: number;
140 priorityColorCode?: number;
141 importanceLevel?: number;
142
143 // System/Debug (read-only)
144 physkey?: number;
145 globalKey?: number;
146 rve?: number;
147 createDateTime?: string;
148 processFlags?: number;
149 shopifyId?: number;
150 bigcommerceId?: number;
151 rmSystem?: number;
152 extractFlags?: number;
153 comments?: string;
154
155 // Additional Barcodes
156 barcode512?: string;
157 barcode513?: string;
158 barcode514?: string;
159
160 // Food Attributes (11 fields)
161 isGlutenFree?: boolean;
162 isOrganic?: boolean;
163 isDairyFree?: boolean;
164 isLowSugar?: boolean;
165 isVegan?: boolean;
166 isLowSalt?: boolean;
167 isYeastFree?: boolean;
168 isLowFat?: boolean;
169 isRaw?: boolean;
170 isKosher?: boolean;
171 isEdible?: boolean;
172
173 // Physical Product Attributes
174 lensType?: string;
175 madeFrom?: string;
176 designShape?: string;
177 polarised?: boolean;
178 mirrored?: boolean;
179 perishable?: boolean;
180 blueLight?: boolean;
181
182 // Supply Chain
183 fairTrade?: boolean;
184 countryMade?: string;
185 nzMade?: boolean;
186
187 // eCommerce
188 gender?: string;
189 asnzsCompliance?: string;
190
191 // Extended Website Flags
192 websiteBackgroundItem?: boolean;
193 websitePositionTop?: boolean;
194 websitePositionLeft?: boolean;
195 websitePositionBottom?: boolean;
196 websitePositionRight?: boolean;
197 websiteTag?: string;
198
199 // Extended Product Properties
200 model?: string;
201 itemColor?: string;
202 cartridge?: string;
203 refillable?: boolean;
204 internalSupportUrl?: string;
205 rxAble?: number;
206 productCase?: string;
207 warranty?: string;
208 productManufacturer?: string;
209
210 // Printing/Hardware
211 paperSize?: string;
212 printerConnectivity?: string;
213 printingTechnology?: string;
214 printerAdfCapacity?: string;
215 printSpeed?: string;
216 scanResolution?: string;
217 printerPaperCapacity?: string;
218
219 // Product Types
220 nonTangibleItem?: boolean;
221 oneOffItem?: boolean;
222
223 // Prompting Controls
224 promptRegno?: boolean;
225 promptLTNZNo?: boolean;
226 promptDocument?: boolean;
227 promptCustomer?: boolean;
228}
229
230type TabType = "main" | "pricing" | "optical" | "measurements" | "website" | "supply" | "system" | "history";
231
232// Reusable Card Component
233interface CardProps {
234 title: string;
235 icon: string;
236 children: React.ReactNode;
237 className?: string;
238}
239
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" />
245 </div>
246 <h2 className="text-xl font-bold text-text">{title}</h2>
247 </div>
248 <div className="space-y-4">
249 {children}
250 </div>
251 </div>
252);
253
254// Reusable Field Component
255interface FieldProps {
256 label: string;
257 value: any;
258 editValue: any;
259 isEditing: boolean;
260 type?: "text" | "number" | "textarea" | "select" | "checkbox" | "date";
261 options?: { value: any; label: string }[];
262 onChange: (value: any) => void;
263 className?: string;
264 placeholder?: string;
265}
266
267const Field: React.FC<FieldProps> = ({
268 label,
269 value,
270 editValue,
271 isEditing,
272 type = "text",
273 options = [],
274 onChange,
275 className = "",
276 placeholder
277}) => (
278 <div className={className}>
279 <p className="text-sm text-muted mb-1">{label}</p>
280 {isEditing ? (
281 <>
282 {type === "checkbox" ? (
283 <label className="flex items-center gap-2 cursor-pointer">
284 <input
285 type="checkbox"
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"
289 />
290 <span className="text-text">{editValue ? "Yes" : "No"}</span>
291 </label>
292 ) : type === "select" ? (
293 <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"
297 >
298 <option value="">Select...</option>
299 {options.map(opt => (
300 <option key={opt.value} value={opt.value}>{opt.label}</option>
301 ))}
302 </select>
303 ) : type === "textarea" ? (
304 <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"
308 rows={3}
309 placeholder={placeholder}
310 />
311 ) : (
312 <input
313 type={type}
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}
318 />
319 )}
320 </>
321 ) : (
322 <p className="text-text font-medium">
323 {type === "checkbox" ? (value ? "Yes" : "No") : (value !== null && value !== undefined ? value : "N/A")}
324 </p>
325 )}
326 </div>
327);
328
329export default function ProductDetailPage() {
330 const params = useParams();
331 const router = useRouter();
332 const productId = params.id as string;
333
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[]>>({});
341
342 useEffect(() => {
343 loadProductData();
344 loadDepartments();
345 loadHierarchy();
346 }, [productId]);
347
348 const loadHierarchy = async () => {
349 try {
350 const result = await apiClient.getDepartmentHierarchy();
351 if (result.success && result.data) {
352 setDepartmentHierarchy(result.data);
353 }
354 } catch (error) {
355 console.error('Failed to load department hierarchy:', error);
356 }
357 };
358
359 const loadProductData = async () => {
360 try {
361 setLoading(true);
362 const result = await apiClient.get(`/v1/openapi/products/${productId}`);
363
364 if (result?.success && result?.data) {
365 const p = result.data;
366 const productData: ProductData = {
367 pid: p.Pid || p.pid,
368 name: p.Name || p.name || '',
369 description: p.Description || p.description || '',
370 shortName: p.ShortName || p.shortName,
371 barcode: p.Barcode || p.barcode,
372 plu: p.Plu || p.plu,
373 sellPrice: p.SellPrice || p.sellPrice || 0,
374 costPrice: p.CostPrice || p.costPrice,
375
376 // Pricing extras
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,
392
393 // Departments
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,
403
404 // Supplier
405 supplierId: p.SupplierId || p.supplierId,
406 supplierName: p.SupplierName || p.supplierName,
407 supplierPartCode: p.SupplierPartCode || p.supplierPartCode,
408 manufacturerPartCode: p.ManufacturerPartCode || p.manufacturerPartCode,
409
410 // Stock
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,
420
421 // Core attributes
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,
427
428 // Flags
429 taxFlag: p.TaxFlag || p.taxFlag,
430 sellFlags: p.SellFlags || p.sellFlags,
431 noSell: p.NoSell || p.noSell,
432 hidden: p.Hidden || p.hidden,
433
434 // Dates
435 releaseDate: p.ReleaseDate || p.releaseDate,
436 availableDate: p.AvailableDate || p.availableDate,
437
438 // Shelf Labels
439 shelfLabelFormat: p.ShelfLabelFormat || p.shelfLabelFormat,
440 shelfLabelLine1: p.ShelfLabelLine1 || p.shelfLabelLine1,
441 shelfLabelLine2: p.ShelfLabelLine2 || p.shelfLabelLine2,
442 shelfLabelLine3: p.ShelfLabelLine3 || p.shelfLabelLine3,
443
444 // Optical
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,
452
453 // Measurements
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,
462
463 // Website/Marketing
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,
490
491 // Supply/Ordering
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,
499
500 // System/Debug
501 physkey: p.Physkey || p.physkey,
502 globalKey: p.GlobalKey || p.globalKey,
503 rve: p.Rve || p.rve,
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,
511
512 // Additional Barcodes
513 barcode512: p.Barcode512 || p.barcode512,
514 barcode513: p.Barcode513 || p.barcode513,
515 barcode514: p.Barcode514 || p.barcode514,
516
517 // Food Attributes
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,
529
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,
538
539 // Supply Chain
540 fairTrade: p.FairTrade || p.fairTrade,
541 countryMade: p.CountryMade || p.countryMade,
542 nzMade: p.NZMade || p.nzMade,
543
544 // eCommerce
545 gender: p.Gender || p.gender,
546 asnzsCompliance: p.ASNZSCompliance || p.asnzsCompliance,
547
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,
555
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,
566
567 // Printing/Hardware
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,
575
576 // Product Types
577 nonTangibleItem: p.NonTangibleItem || p.nonTangibleItem,
578 oneOffItem: p.OneOffItem || p.oneOffItem,
579
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,
585 };
586
587 setProduct(productData);
588 setEditForm(productData);
589 }
590 } catch (error) {
591 console.error("Error loading product:", error);
592 } finally {
593 setLoading(false);
594 }
595 };
596
597 const loadDepartments = async () => {
598 try {
599 const result = await apiClient.getDepartments();
600
601 if (result.success && result.data) {
602 const deptMap: Record<number, string> = {};
603 let deptArray: any[] = [];
604
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;
609 }
610
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;
614 if (id && name) {
615 deptMap[id] = name;
616 }
617 });
618
619 setDepartments(deptMap);
620 }
621 } catch (error) {
622 console.error('Error loading departments:', error);
623 }
624 };
625
626 const getDepartmentName = (deptId?: number): string => {
627 if (!deptId) return 'N/A';
628 return departments[deptId] || `Department ${deptId}`;
629 };
630
631 const handleEditToggle = () => {
632 if (isEditing) {
633 setEditForm(product || {});
634 }
635 setIsEditing(!isEditing);
636 };
637
638 const handleSave = async () => {
639 try {
640 setLoading(true);
641
642 const response = await fetch(`/api/v1/openapi/products/${productId}`, {
643 method: 'PUT',
644 headers: { 'Content-Type': 'application/json' },
645 body: JSON.stringify(editForm),
646 });
647
648 const result = await response.json();
649
650 if (response.ok && result.success) {
651 setProduct(editForm as ProductData);
652 setIsEditing(false);
653 await loadProductData();
654 } else {
655 alert(`Failed to update product: ${result.error || 'Unknown error'}`);
656 }
657 } catch (error) {
658 console.error('Error saving product:', error);
659 alert('Error saving product. Please try again.');
660 } finally {
661 setLoading(false);
662 }
663 };
664
665 if (loading) {
666 return (
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>
672 </div>
673 </div>
674 </div>
675 );
676 }
677
678 if (!product) {
679 return (
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>
685 <button
686 onClick={() => router.push("/pages/products")}
687 className="px-6 py-2 bg-brand text-white rounded-lg hover:bg-brand-2 transition-colors"
688 >
689 Back to Products
690 </button>
691 </div>
692 </div>
693 </div>
694 );
695 }
696
697 return (
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">
700 {/* Header */}
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">
704 <button
705 onClick={() => router.push("/pages/products")}
706 className="text-brand hover:text-brand-2 mb-3 flex items-center gap-2 transition-colors group"
707 >
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" />
710 </svg>
711 Back to Products
712 </button>
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" />
716 </div>
717 <div>
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 && (
722 <>
723 <span className="text-muted">•</span>
724 <span className="text-muted text-sm font-mono">{product.barcode}</span>
725 </>
726 )}
727 </div>
728 </div>
729 </div>
730 </div>
731 <div className="flex gap-2">
732 {isEditing ? (
733 <>
734 <button
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"
737 >
738 <Icon name="close" size={20} />
739 Cancel
740 </button>
741 <button
742 onClick={handleSave}
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"
744 >
745 <Icon name="save" size={20} />
746 Save Changes
747 </button>
748 </>
749 ) : (
750 <button
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"
753 >
754 <Icon name="edit" size={20} />
755 Edit Product
756 </button>
757 )}
758 </div>
759 </div>
760 </div>
761
762 {/* Tabs */}
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">
765 {[
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" }
774 ].map(tab => (
775 <button
776 key={tab.id}
777 onClick={() => setActiveTab(tab.id as TabType)}
778 className={`py-3 px-4 font-semibold text-sm rounded-lg transition-all ${
779 activeTab === tab.id
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"
782 }`}
783 >
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>
787 </div>
788 </button>
789 ))}
790 </nav>
791 </div>
792
793 {/* Tab Content */}
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">
798 <Field
799 label="Product Name"
800 value={product.name}
801 editValue={editForm.name}
802 isEditing={isEditing}
803 onChange={(v) => setEditForm({...editForm, name: v})}
804 />
805 <Field
806 label="Description"
807 value={product.description}
808 editValue={editForm.description}
809 isEditing={isEditing}
810 type="textarea"
811 onChange={(v) => setEditForm({...editForm, description: v})}
812 />
813 <Field
814 label="Short Name"
815 value={product.shortName}
816 editValue={editForm.shortName}
817 isEditing={isEditing}
818 onChange={(v) => setEditForm({...editForm, shortName: v})}
819 />
820 <Field
821 label="Brand"
822 value={product.brand}
823 editValue={editForm.brand}
824 isEditing={isEditing}
825 onChange={(v) => setEditForm({...editForm, brand: v})}
826 />
827 <Field
828 label="Size"
829 value={product.size}
830 editValue={editForm.size}
831 isEditing={isEditing}
832 onChange={(v) => setEditForm({...editForm, size: v})}
833 />
834 <Field
835 label="Weight (g)"
836 value={product.weightGm}
837 editValue={editForm.weightGm}
838 isEditing={isEditing}
839 type="number"
840 onChange={(v) => setEditForm({...editForm, weightGm: v})}
841 />
842 </Card>
843
844 {/* Barcodes Card */}
845 <Card title="Barcodes & Identifiers" icon="qr_code_2">
846 <Field
847 label="Primary Barcode"
848 value={product.barcode}
849 editValue={editForm.barcode}
850 isEditing={isEditing}
851 onChange={(v) => setEditForm({...editForm, barcode: v})}
852 />
853 <Field
854 label="PLU"
855 value={product.plu}
856 editValue={editForm.plu}
857 isEditing={isEditing}
858 onChange={(v) => setEditForm({...editForm, plu: v})}
859 />
860 <Field
861 label="Alternate Barcode (512)"
862 value={product.barcode512}
863 editValue={editForm.barcode512}
864 isEditing={isEditing}
865 onChange={(v) => setEditForm({...editForm, barcode512: v})}
866 />
867 <Field
868 label="Alternate Barcode (513)"
869 value={product.barcode513}
870 editValue={editForm.barcode513}
871 isEditing={isEditing}
872 onChange={(v) => setEditForm({...editForm, barcode513: v})}
873 />
874 <Field
875 label="Alternate Barcode (514)"
876 value={product.barcode514}
877 editValue={editForm.barcode514}
878 isEditing={isEditing}
879 onChange={(v) => setEditForm({...editForm, barcode514: v})}
880 />
881 </Card>
882
883 {/* Product Types & Special Handling Card */}
884 <Card title="Product Types" icon="category">
885 <Field
886 label="Non-Tangible Item"
887 value={product.nonTangibleItem}
888 editValue={editForm.nonTangibleItem}
889 isEditing={isEditing}
890 type="checkbox"
891 onChange={(v) => setEditForm({...editForm, nonTangibleItem: v})}
892 />
893 <Field
894 label="One-Off Item"
895 value={product.oneOffItem}
896 editValue={editForm.oneOffItem}
897 isEditing={isEditing}
898 type="checkbox"
899 onChange={(v) => setEditForm({...editForm, oneOffItem: v})}
900 />
901 <Field
902 label="Cartridge/Refillable"
903 value={product.cartridge}
904 editValue={editForm.cartridge}
905 isEditing={isEditing}
906 onChange={(v) => setEditForm({...editForm, cartridge: v})}
907 />
908 <Field
909 label="Refillable"
910 value={product.refillable}
911 editValue={editForm.refillable}
912 isEditing={isEditing}
913 type="checkbox"
914 onChange={(v) => setEditForm({...editForm, refillable: v})}
915 />
916 <Field
917 label="Prompt Vehicle Registration"
918 value={product.promptRegno}
919 editValue={editForm.promptRegno}
920 isEditing={isEditing}
921 type="checkbox"
922 onChange={(v) => setEditForm({...editForm, promptRegno: v})}
923 />
924 <Field
925 label="Prompt LTNZ Number"
926 value={product.promptLTNZNo}
927 editValue={editForm.promptLTNZNo}
928 isEditing={isEditing}
929 type="checkbox"
930 onChange={(v) => setEditForm({...editForm, promptLTNZNo: v})}
931 />
932 <Field
933 label="Prompt Document"
934 value={product.promptDocument}
935 editValue={editForm.promptDocument}
936 isEditing={isEditing}
937 type="checkbox"
938 onChange={(v) => setEditForm({...editForm, promptDocument: v})}
939 />
940 <Field
941 label="Prompt Customer"
942 value={product.promptCustomer}
943 editValue={editForm.promptCustomer}
944 isEditing={isEditing}
945 type="checkbox"
946 onChange={(v) => setEditForm({...editForm, promptCustomer: v})}
947 />
948 </Card>
949
950 {/* Department Hierarchy Card */}
951 <Card title="Department Hierarchy" icon="account_tree">
952 <Field
953 label="Department (1)"
954 value={getDepartmentName(product.department)}
955 editValue={editForm.department}
956 isEditing={isEditing}
957 type="select"
958 options={Object.entries(departments).map(([id, name]) => ({ value: id, label: name }))}
959 onChange={(v) => setEditForm({...editForm, department: parseInt(v) || undefined})}
960 />
961 {(product.department2 || isEditing) && (
962 <Field
963 label="Department (2)"
964 value={getDepartmentName(product.department2)}
965 editValue={editForm.department2}
966 isEditing={isEditing}
967 type="select"
968 options={Object.entries(departments).map(([id, name]) => ({ value: id, label: name }))}
969 onChange={(v) => setEditForm({...editForm, department2: parseInt(v) || undefined})}
970 />
971 )}
972 {(product.department3 || isEditing) && (
973 <Field
974 label="Department (3)"
975 value={getDepartmentName(product.department3)}
976 editValue={editForm.department3}
977 isEditing={isEditing}
978 type="select"
979 options={Object.entries(departments).map(([id, name]) => ({ value: id, label: name }))}
980 onChange={(v) => setEditForm({...editForm, department3: parseInt(v) || undefined})}
981 />
982 )}
983 {(product.department4 || isEditing) && (
984 <Field
985 label="Department (4)"
986 value={getDepartmentName(product.department4)}
987 editValue={editForm.department4}
988 isEditing={isEditing}
989 type="select"
990 options={Object.entries(departments).map(([id, name]) => ({ value: id, label: name }))}
991 onChange={(v) => setEditForm({...editForm, department4: parseInt(v) || undefined})}
992 />
993 )}
994 </Card>
995
996 {/* Supplier Card */}
997 <Card title="Supplier Information" icon="store">
998 <div>
999 <p className="text-sm text-muted mb-1">Supplier Name</p>
1000 <p className="text-text font-medium">{product.supplierName || 'N/A'}</p>
1001 </div>
1002 <Field
1003 label="Supplier Part Code"
1004 value={product.supplierPartCode}
1005 editValue={editForm.supplierPartCode}
1006 isEditing={isEditing}
1007 onChange={(v) => setEditForm({...editForm, supplierPartCode: v})}
1008 />
1009 <Field
1010 label="Manufacturer Part Code"
1011 value={product.manufacturerPartCode}
1012 editValue={editForm.manufacturerPartCode}
1013 isEditing={isEditing}
1014 onChange={(v) => setEditForm({...editForm, manufacturerPartCode: v})}
1015 />
1016 </Card>
1017
1018 {/* Product Flags Card */}
1019 <Card title="Product Flags & Dates" icon="flag">
1020 <Field
1021 label="No Sell"
1022 value={product.noSell}
1023 editValue={editForm.noSell}
1024 isEditing={isEditing}
1025 type="checkbox"
1026 onChange={(v) => setEditForm({...editForm, noSell: v})}
1027 />
1028 <Field
1029 label="Hidden"
1030 value={product.hidden}
1031 editValue={editForm.hidden}
1032 isEditing={isEditing}
1033 type="checkbox"
1034 onChange={(v) => setEditForm({...editForm, hidden: v})}
1035 />
1036 <Field
1037 label="Release Date"
1038 value={product.releaseDate}
1039 editValue={editForm.releaseDate}
1040 isEditing={isEditing}
1041 type="date"
1042 onChange={(v) => setEditForm({...editForm, releaseDate: v})}
1043 />
1044 <Field
1045 label="Available Date"
1046 value={product.availableDate}
1047 editValue={editForm.availableDate}
1048 isEditing={isEditing}
1049 type="date"
1050 onChange={(v) => setEditForm({...editForm, availableDate: v})}
1051 />
1052 </Card>
1053 </div>
1054 )}
1055
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">
1060 <Field
1061 label="Sell Price"
1062 value={product.sellPrice}
1063 editValue={editForm.sellPrice}
1064 isEditing={isEditing}
1065 type="number"
1066 onChange={(v) => setEditForm({...editForm, sellPrice: v})}
1067 />
1068 <Field
1069 label="Cost Price"
1070 value={product.costPrice}
1071 editValue={editForm.costPrice}
1072 isEditing={isEditing}
1073 type="number"
1074 onChange={(v) => setEditForm({...editForm, costPrice: v})}
1075 />
1076 <Field
1077 label="Freight"
1078 value={product.freight}
1079 editValue={editForm.freight}
1080 isEditing={isEditing}
1081 type="number"
1082 onChange={(v) => setEditForm({...editForm, freight: v})}
1083 />
1084 <Field
1085 label="Packaging"
1086 value={product.packaging}
1087 editValue={editForm.packaging}
1088 isEditing={isEditing}
1089 type="number"
1090 onChange={(v) => setEditForm({...editForm, packaging: v})}
1091 />
1092 <Field
1093 label="Decimal Places"
1094 value={product.decimalPlaces}
1095 editValue={editForm.decimalPlaces}
1096 isEditing={isEditing}
1097 type="number"
1098 onChange={(v) => setEditForm({...editForm, decimalPlaces: v})}
1099 />
1100 </Card>
1101
1102 {/* Advanced Pricing Card */}
1103 <Card title="Advanced Pricing" icon="trending_up">
1104 <Field
1105 label="Confirm Price"
1106 value={product.confirmPrice}
1107 editValue={editForm.confirmPrice}
1108 isEditing={isEditing}
1109 type="checkbox"
1110 onChange={(v) => setEditForm({...editForm, confirmPrice: v})}
1111 />
1112 <Field
1113 label="Franchise Price"
1114 value={product.franchisePrice}
1115 editValue={editForm.franchisePrice}
1116 isEditing={isEditing}
1117 type="number"
1118 onChange={(v) => setEditForm({...editForm, franchisePrice: v})}
1119 />
1120 <Field
1121 label="Price Band 1"
1122 value={product.priceBand1}
1123 editValue={editForm.priceBand1}
1124 isEditing={isEditing}
1125 type="number"
1126 onChange={(v) => setEditForm({...editForm, priceBand1: v})}
1127 />
1128 <Field
1129 label="Price Band 2"
1130 value={product.priceBand2}
1131 editValue={editForm.priceBand2}
1132 isEditing={isEditing}
1133 type="number"
1134 onChange={(v) => setEditForm({...editForm, priceBand2: v})}
1135 />
1136 <Field
1137 label="Price Band 3"
1138 value={product.priceBand3}
1139 editValue={editForm.priceBand3}
1140 isEditing={isEditing}
1141 type="number"
1142 onChange={(v) => setEditForm({...editForm, priceBand3: v})}
1143 />
1144 </Card>
1145
1146 {/* Discount Limits Card */}
1147 <Card title="Discount Limits" icon="discount">
1148 <Field
1149 label="Disable Discounts"
1150 value={product.disableDiscounts}
1151 editValue={editForm.disableDiscounts}
1152 isEditing={isEditing}
1153 type="checkbox"
1154 onChange={(v) => setEditForm({...editForm, disableDiscounts: v})}
1155 />
1156 <Field
1157 label="Lowest Price"
1158 value={product.lowestPrice}
1159 editValue={editForm.lowestPrice}
1160 isEditing={isEditing}
1161 type="number"
1162 onChange={(v) => setEditForm({...editForm, lowestPrice: v})}
1163 />
1164 <Field
1165 label="Discount Limit 1"
1166 value={product.discountLimit1}
1167 editValue={editForm.discountLimit1}
1168 isEditing={isEditing}
1169 type="number"
1170 onChange={(v) => setEditForm({...editForm, discountLimit1: v})}
1171 />
1172 <Field
1173 label="Discount Limit 2"
1174 value={product.discountLimit2}
1175 editValue={editForm.discountLimit2}
1176 isEditing={isEditing}
1177 type="number"
1178 onChange={(v) => setEditForm({...editForm, discountLimit2: v})}
1179 />
1180 </Card>
1181
1182 {/* Stock Control Card */}
1183 <Card title="Stock Control" icon="inventory">
1184 <Field
1185 label="Current Stock"
1186 value={product.stockLevel}
1187 editValue={editForm.stockLevel}
1188 isEditing={isEditing}
1189 type="number"
1190 onChange={(v) => setEditForm({...editForm, stockLevel: v})}
1191 />
1192 <Field
1193 label="Stock Min"
1194 value={product.stockMin}
1195 editValue={editForm.stockMin}
1196 isEditing={isEditing}
1197 type="number"
1198 onChange={(v) => setEditForm({...editForm, stockMin: v})}
1199 />
1200 <Field
1201 label="Stock Max"
1202 value={product.stockMax}
1203 editValue={editForm.stockMax}
1204 isEditing={isEditing}
1205 type="number"
1206 onChange={(v) => setEditForm({...editForm, stockMax: v})}
1207 />
1208 <Field
1209 label="Ignore Stock Movements"
1210 value={product.ignoreStockMovements}
1211 editValue={editForm.ignoreStockMovements}
1212 isEditing={isEditing}
1213 type="checkbox"
1214 onChange={(v) => setEditForm({...editForm, ignoreStockMovements: v})}
1215 />
1216 <Field
1217 label="Record Stock Movements"
1218 value={product.recordStockMovements}
1219 editValue={editForm.recordStockMovements}
1220 isEditing={isEditing}
1221 type="checkbox"
1222 onChange={(v) => setEditForm({...editForm, recordStockMovements: v})}
1223 />
1224 </Card>
1225 </div>
1226 )}
1227
1228 {activeTab === "optical" && (
1229 <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
1230 <Card title="Optical/Eyewear" icon="visibility">
1231 <Field
1232 label="Alternate Fit"
1233 value={product.alternateFit}
1234 editValue={editForm.alternateFit}
1235 isEditing={isEditing}
1236 type="checkbox"
1237 onChange={(v) => setEditForm({...editForm, alternateFit: v})}
1238 />
1239 <Field
1240 label="Lens Size"
1241 value={product.lensSize}
1242 editValue={editForm.lensSize}
1243 isEditing={isEditing}
1244 onChange={(v) => setEditForm({...editForm, lensSize: v})}
1245 />
1246 <Field
1247 label="Bridge Size"
1248 value={product.bridgeSize}
1249 editValue={editForm.bridgeSize}
1250 isEditing={isEditing}
1251 onChange={(v) => setEditForm({...editForm, bridgeSize: v})}
1252 />
1253 <Field
1254 label="Temple Length"
1255 value={product.templeLength}
1256 editValue={editForm.templeLength}
1257 isEditing={isEditing}
1258 onChange={(v) => setEditForm({...editForm, templeLength: v})}
1259 />
1260 <Field
1261 label="Lens Base Curve"
1262 value={product.lensBaseCurve}
1263 editValue={editForm.lensBaseCurve}
1264 isEditing={isEditing}
1265 onChange={(v) => setEditForm({...editForm, lensBaseCurve: v})}
1266 />
1267 <Field
1268 label="Lens Type"
1269 value={product.lensType}
1270 editValue={editForm.lensType}
1271 isEditing={isEditing}
1272 onChange={(v) => setEditForm({...editForm, lensType: v})}
1273 />
1274 <Field
1275 label="Polarised"
1276 value={product.polarised}
1277 editValue={editForm.polarised}
1278 isEditing={isEditing}
1279 type="checkbox"
1280 onChange={(v) => setEditForm({...editForm, polarised: v})}
1281 />
1282 <Field
1283 label="Mirrored"
1284 value={product.mirrored}
1285 editValue={editForm.mirrored}
1286 isEditing={isEditing}
1287 type="checkbox"
1288 onChange={(v) => setEditForm({...editForm, mirrored: v})}
1289 />
1290 <Field
1291 label="Blue Light Protection"
1292 value={product.blueLight}
1293 editValue={editForm.blueLight}
1294 isEditing={isEditing}
1295 type="checkbox"
1296 onChange={(v) => setEditForm({...editForm, blueLight: v})}
1297 />
1298 <Field
1299 label="RxAble (Prescription Capable)"
1300 value={product.rxAble}
1301 editValue={editForm.rxAble}
1302 isEditing={isEditing}
1303 type="number"
1304 onChange={(v) => setEditForm({...editForm, rxAble: v})}
1305 />
1306 </Card>
1307
1308 <Card title="Physical Attributes" icon="category">
1309 <Field
1310 label="Model"
1311 value={product.model}
1312 editValue={editForm.model}
1313 isEditing={isEditing}
1314 onChange={(v) => setEditForm({...editForm, model: v})}
1315 />
1316 <Field
1317 label="Item Color"
1318 value={product.itemColor}
1319 editValue={editForm.itemColor}
1320 isEditing={isEditing}
1321 onChange={(v) => setEditForm({...editForm, itemColor: v})}
1322 />
1323 <Field
1324 label="Design Shape"
1325 value={product.designShape}
1326 editValue={editForm.designShape}
1327 isEditing={isEditing}
1328 onChange={(v) => setEditForm({...editForm, designShape: v})}
1329 />
1330 <Field
1331 label="Made From"
1332 value={product.madeFrom}
1333 editValue={editForm.madeFrom}
1334 isEditing={isEditing}
1335 onChange={(v) => setEditForm({...editForm, madeFrom: v})}
1336 />
1337 <Field
1338 label="Product Case"
1339 value={product.productCase}
1340 editValue={editForm.productCase}
1341 isEditing={isEditing}
1342 onChange={(v) => setEditForm({...editForm, productCase: v})}
1343 />
1344 <Field
1345 label="Warranty"
1346 value={product.warranty}
1347 editValue={editForm.warranty}
1348 isEditing={isEditing}
1349 onChange={(v) => setEditForm({...editForm, warranty: v})}
1350 />
1351 <Field
1352 label="Product Manufacturer"
1353 value={product.productManufacturer}
1354 editValue={editForm.productManufacturer}
1355 isEditing={isEditing}
1356 onChange={(v) => setEditForm({...editForm, productManufacturer: v})}
1357 />
1358 </Card>
1359
1360 <Card title="Food & Supply Chain" icon="restaurant">
1361 <Field
1362 label="Gluten Free"
1363 value={product.isGlutenFree}
1364 editValue={editForm.isGlutenFree}
1365 isEditing={isEditing}
1366 type="checkbox"
1367 onChange={(v) => setEditForm({...editForm, isGlutenFree: v})}
1368 />
1369 <Field
1370 label="Organic"
1371 value={product.isOrganic}
1372 editValue={editForm.isOrganic}
1373 isEditing={isEditing}
1374 type="checkbox"
1375 onChange={(v) => setEditForm({...editForm, isOrganic: v})}
1376 />
1377 <Field
1378 label="Dairy Free"
1379 value={product.isDairyFree}
1380 editValue={editForm.isDairyFree}
1381 isEditing={isEditing}
1382 type="checkbox"
1383 onChange={(v) => setEditForm({...editForm, isDairyFree: v})}
1384 />
1385 <Field
1386 label="Vegan"
1387 value={product.isVegan}
1388 editValue={editForm.isVegan}
1389 isEditing={isEditing}
1390 type="checkbox"
1391 onChange={(v) => setEditForm({...editForm, isVegan: v})}
1392 />
1393 <Field
1394 label="Kosher"
1395 value={product.isKosher}
1396 editValue={editForm.isKosher}
1397 isEditing={isEditing}
1398 type="checkbox"
1399 onChange={(v) => setEditForm({...editForm, isKosher: v})}
1400 />
1401 <Field
1402 label="Perishable"
1403 value={product.perishable}
1404 editValue={editForm.perishable}
1405 isEditing={isEditing}
1406 type="checkbox"
1407 onChange={(v) => setEditForm({...editForm, perishable: v})}
1408 />
1409 <Field
1410 label="Fair Trade"
1411 value={product.fairTrade}
1412 editValue={editForm.fairTrade}
1413 isEditing={isEditing}
1414 type="checkbox"
1415 onChange={(v) => setEditForm({...editForm, fairTrade: v})}
1416 />
1417 <Field
1418 label="NZ Made"
1419 value={product.nzMade}
1420 editValue={editForm.nzMade}
1421 isEditing={isEditing}
1422 type="checkbox"
1423 onChange={(v) => setEditForm({...editForm, nzMade: v})}
1424 />
1425 <Field
1426 label="Country Made"
1427 value={product.countryMade}
1428 editValue={editForm.countryMade}
1429 isEditing={isEditing}
1430 onChange={(v) => setEditForm({...editForm, countryMade: v})}
1431 />
1432 </Card>
1433 </div>
1434 )}
1435
1436 {activeTab === "measurements" && (
1437 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
1438 <Card title="Measurements" icon="straighten">
1439 <Field
1440 label="Length (mm)"
1441 value={product.lengthMm}
1442 editValue={editForm.lengthMm}
1443 isEditing={isEditing}
1444 type="number"
1445 onChange={(v) => setEditForm({...editForm, lengthMm: v})}
1446 />
1447 <Field
1448 label="Width (mm)"
1449 value={product.widthMm}
1450 editValue={editForm.widthMm}
1451 isEditing={isEditing}
1452 type="number"
1453 onChange={(v) => setEditForm({...editForm, widthMm: v})}
1454 />
1455 <Field
1456 label="Height (mm)"
1457 value={product.heightMm}
1458 editValue={editForm.heightMm}
1459 isEditing={isEditing}
1460 type="number"
1461 onChange={(v) => setEditForm({...editForm, heightMm: v})}
1462 />
1463 <Field
1464 label="Packaging Weight"
1465 value={product.packagingWeight}
1466 editValue={editForm.packagingWeight}
1467 isEditing={isEditing}
1468 type="number"
1469 onChange={(v) => setEditForm({...editForm, packagingWeight: v})}
1470 />
1471 <Field
1472 label="Shipping Cubic"
1473 value={product.shippingCubic}
1474 editValue={editForm.shippingCubic}
1475 isEditing={isEditing}
1476 type="number"
1477 onChange={(v) => setEditForm({...editForm, shippingCubic: v})}
1478 />
1479 </Card>
1480 </div>
1481 )}
1482
1483 {activeTab === "website" && (
1484 <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
1485 <Card title="Website Configuration" icon="language">
1486 <Field
1487 label="Publish to Website"
1488 value={product.publishToWebsite}
1489 editValue={editForm.publishToWebsite}
1490 isEditing={isEditing}
1491 type="checkbox"
1492 onChange={(v) => setEditForm({...editForm, publishToWebsite: v})}
1493 />
1494 <Field
1495 label="Web Description"
1496 value={product.webDescription}
1497 editValue={editForm.webDescription}
1498 isEditing={isEditing}
1499 type="textarea"
1500 onChange={(v) => setEditForm({...editForm, webDescription: v})}
1501 />
1502 <Field
1503 label="Web Keywords"
1504 value={product.webKeywords}
1505 editValue={editForm.webKeywords}
1506 isEditing={isEditing}
1507 onChange={(v) => setEditForm({...editForm, webKeywords: v})}
1508 />
1509 <Field
1510 label="Web Blurb"
1511 value={product.webBlurb}
1512 editValue={editForm.webBlurb}
1513 isEditing={isEditing}
1514 type="textarea"
1515 onChange={(v) => setEditForm({...editForm, webBlurb: v})}
1516 />
1517 <Field
1518 label="Website Tag"
1519 value={product.websiteTag}
1520 editValue={editForm.websiteTag}
1521 isEditing={isEditing}
1522 onChange={(v) => setEditForm({...editForm, websiteTag: v})}
1523 />
1524 <Field
1525 label="Gender"
1526 value={product.gender}
1527 editValue={editForm.gender}
1528 isEditing={isEditing}
1529 onChange={(v) => setEditForm({...editForm, gender: v})}
1530 />
1531 <Field
1532 label="AS/NZS Compliance"
1533 value={product.asnzsCompliance}
1534 editValue={editForm.asnzsCompliance}
1535 isEditing={isEditing}
1536 onChange={(v) => setEditForm({...editForm, asnzsCompliance: v})}
1537 />
1538 </Card>
1539
1540 <Card title="Marketing Flags" icon="sell">
1541 <Field
1542 label="On Special"
1543 value={product.onSpecial}
1544 editValue={editForm.onSpecial}
1545 isEditing={isEditing}
1546 type="checkbox"
1547 onChange={(v) => setEditForm({...editForm, onSpecial: v})}
1548 />
1549 <Field
1550 label="New Product"
1551 value={product.newProduct}
1552 editValue={editForm.newProduct}
1553 isEditing={isEditing}
1554 type="checkbox"
1555 onChange={(v) => setEditForm({...editForm, newProduct: v})}
1556 />
1557 <Field
1558 label="Featured Product"
1559 value={product.featuredProduct}
1560 editValue={editForm.featuredProduct}
1561 isEditing={isEditing}
1562 type="checkbox"
1563 onChange={(v) => setEditForm({...editForm, featuredProduct: v})}
1564 />
1565 <Field
1566 label="Highlight Product"
1567 value={product.highlightProduct}
1568 editValue={editForm.highlightProduct}
1569 isEditing={isEditing}
1570 type="checkbox"
1571 onChange={(v) => setEditForm({...editForm, highlightProduct: v})}
1572 />
1573 <Field
1574 label="Promote Product"
1575 value={product.promoteProduct}
1576 editValue={editForm.promoteProduct}
1577 isEditing={isEditing}
1578 type="checkbox"
1579 onChange={(v) => setEditForm({...editForm, promoteProduct: v})}
1580 />
1581 <Field
1582 label="Out of Stock"
1583 value={product.outOfStock}
1584 editValue={editForm.outOfStock}
1585 isEditing={isEditing}
1586 type="checkbox"
1587 onChange={(v) => setEditForm({...editForm, outOfStock: v})}
1588 />
1589 <Field
1590 label="Out of Season"
1591 value={product.outOfSeason}
1592 editValue={editForm.outOfSeason}
1593 isEditing={isEditing}
1594 type="checkbox"
1595 onChange={(v) => setEditForm({...editForm, outOfSeason: v})}
1596 />
1597 <Field
1598 label="In-store Only"
1599 value={product.instoreOnly}
1600 editValue={editForm.instoreOnly}
1601 isEditing={isEditing}
1602 type="checkbox"
1603 onChange={(v) => setEditForm({...editForm, instoreOnly: v})}
1604 />
1605 </Card>
1606
1607 <Card title="Website Positioning" icon="web">
1608 <Field
1609 label="Background Item"
1610 value={product.websiteBackgroundItem}
1611 editValue={editForm.websiteBackgroundItem}
1612 isEditing={isEditing}
1613 type="checkbox"
1614 onChange={(v) => setEditForm({...editForm, websiteBackgroundItem: v})}
1615 />
1616 <Field
1617 label="Position Top"
1618 value={product.websitePositionTop}
1619 editValue={editForm.websitePositionTop}
1620 isEditing={isEditing}
1621 type="checkbox"
1622 onChange={(v) => setEditForm({...editForm, websitePositionTop: v})}
1623 />
1624 <Field
1625 label="Position Left"
1626 value={product.websitePositionLeft}
1627 editValue={editForm.websitePositionLeft}
1628 isEditing={isEditing}
1629 type="checkbox"
1630 onChange={(v) => setEditForm({...editForm, websitePositionLeft: v})}
1631 />
1632 <Field
1633 label="Position Bottom"
1634 value={product.websitePositionBottom}
1635 editValue={editForm.websitePositionBottom}
1636 isEditing={isEditing}
1637 type="checkbox"
1638 onChange={(v) => setEditForm({...editForm, websitePositionBottom: v})}
1639 />
1640 <Field
1641 label="Position Right"
1642 value={product.websitePositionRight}
1643 editValue={editForm.websitePositionRight}
1644 isEditing={isEditing}
1645 type="checkbox"
1646 onChange={(v) => setEditForm({...editForm, websitePositionRight: v})}
1647 />
1648 </Card>
1649 </div>
1650 )}
1651
1652 {activeTab === "supply" && (
1653 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
1654 <Card title="Supply & Ordering" icon="local_shipping">
1655 <Field
1656 label="Disable Ordering"
1657 value={product.disableOrdering}
1658 editValue={editForm.disableOrdering}
1659 isEditing={isEditing}
1660 type="checkbox"
1661 onChange={(v) => setEditForm({...editForm, disableOrdering: v})}
1662 />
1663 <Field
1664 label="Min Units Per Order"
1665 value={product.minUnitsPerOrder}
1666 editValue={editForm.minUnitsPerOrder}
1667 isEditing={isEditing}
1668 type="number"
1669 onChange={(v) => setEditForm({...editForm, minUnitsPerOrder: v})}
1670 />
1671 <Field
1672 label="Max Units Per Order"
1673 value={product.maxUnitsPerOrder}
1674 editValue={editForm.maxUnitsPerOrder}
1675 isEditing={isEditing}
1676 type="number"
1677 onChange={(v) => setEditForm({...editForm, maxUnitsPerOrder: v})}
1678 />
1679 <Field
1680 label="Message When Ordering"
1681 value={product.messageWhenOrdering}
1682 editValue={editForm.messageWhenOrdering}
1683 isEditing={isEditing}
1684 type="textarea"
1685 placeholder="Communication notes for ordering staff"
1686 onChange={(v) => setEditForm({...editForm, messageWhenOrdering: v})}
1687 />
1688 </Card>
1689 </div>
1690 )}
1691
1692 {activeTab === "system" && (
1693 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
1694 <Card title="System Information" icon="settings">
1695 <div>
1696 <p className="text-sm text-muted mb-1">Physical Key</p>
1697 <p className="text-text font-mono">{product.physkey || 'N/A'}</p>
1698 </div>
1699 <div>
1700 <p className="text-sm text-muted mb-1">Global Key</p>
1701 <p className="text-text font-mono">{product.globalKey || 'N/A'}</p>
1702 </div>
1703 <div>
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>
1706 </div>
1707 <div>
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>
1710 </div>
1711 <div>
1712 <p className="text-sm text-muted mb-1">Shopify ID</p>
1713 <p className="text-text font-mono">{product.shopifyId || 'N/A'}</p>
1714 </div>
1715 <div>
1716 <p className="text-sm text-muted mb-1">BigCommerce ID</p>
1717 <p className="text-text font-mono">{product.bigcommerceId || 'N/A'}</p>
1718 </div>
1719 </Card>
1720
1721 <Card title="Comments" icon="comment">
1722 <Field
1723 label="Internal Comments"
1724 value={product.comments}
1725 editValue={editForm.comments}
1726 isEditing={isEditing}
1727 type="textarea"
1728 placeholder="Internal notes about this product"
1729 onChange={(v) => setEditForm({...editForm, comments: v})}
1730 />
1731 </Card>
1732 </div>
1733 )}
1734
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>
1739 </Card>
1740 </div>
1741 )}
1742 </div>
1743 </div>
1744 );
1745}