medusajs-documents-plugin 1.1.32 → 1.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -211,27 +211,23 @@ const CUSTOM_DOC_PRESETS = [
211
211
  badge: "90-Day Coverage",
212
212
  title: "Warranty Certificate",
213
213
  subject: "Your Official Warranty Certificate - Urban Device Care",
214
- body_text: `This certificate verifies that your device has undergone professional repairs with genuine OEM-specification components and has passed our multi-point quality assurance inspection.
214
+ body_text: `This official Warranty Certificate certifies that the serviced hardware specified below has undergone complete technical overhaul, component replacement, and bench testing in accordance with manufacturer specifications.
215
215
 
216
- All hardware replacements and associated workshop labor are covered under our comprehensive 90-day warranty against defects in parts and workmanship. Please retain this document for warranty verification.`,
216
+ All genuine replacement components and associated technical workshop labor are guaranteed against functional defects for a period of 90 days from the date of issue. Urban Device Care warrants that all installed modules have passed rigorous electrical load and multi-point quality assurance protocols.`,
217
217
  items: [
218
- { title: "OEM Component & Hardware Warranty", description: "90-Day Coverage from Date of Collection", quantity: 1, unit_price: 0 },
219
- { title: "Workshop Diagnostic & Quality Clearance", description: "Multi-Point Hardware & Bench Testing", quantity: 1, unit_price: 0 }
220
- ]
221
- },
222
- {
223
- id: "diagnostic",
224
- name: "Diagnostic & Inspection Report",
225
- icon: "🔍",
226
- badge: "Hardware Findings",
227
- title: "Diagnostic & Hardware Inspection Report",
228
- subject: "Device Diagnostic & Inspection Findings",
229
- body_text: `Our certified repair specialists have completed a hardware and firmware inspection of your equipment. Below are the confirmed technical findings, component test measurements, and technician notes regarding the state of your device.`,
230
- items: [
231
- { title: "Logic Board & Power Rail Inspection", description: "Voltage drop and circuit continuity tested", quantity: 1, unit_price: 0 },
232
- { title: "Display & Touch Digitizer Diagnostic", description: "Digitizer touch grid, TrueTone, and Ambient Sensor test", quantity: 1, unit_price: 0 },
233
- { title: "Battery Health & Thermal Profiling", description: "Cycle count, capacity retention, and charge testing", quantity: 1, unit_price: 0 }
234
- ]
218
+ { title: "OEM Screen Assembly & Digitizer Replacement", description: "90-Day Comprehensive Warranty Coverage against touch and display anomalies", quantity: 1, unit_price: 0 },
219
+ { title: "Internal Power Management & Battery Calibration", description: "Warranty against capacity degradation and voltage fluctuations", quantity: 1, unit_price: 0 },
220
+ { title: "Clean-Room Workshop Installation & Bench Clearance", description: "Certified labor coverage and water/dust ingress seal integrity", quantity: 1, unit_price: 0 }
221
+ ],
222
+ custom_fields: {
223
+ "Device Model": "Apple iPhone 15 Pro Max (256GB - Natural Titanium)",
224
+ "Serial / IMEI": "F2LZ90K8MD6R",
225
+ "Ticket Reference": "RT-2024-8841",
226
+ "Technician": "Bonifencia A. (Lead Certified Engineer)",
227
+ "Coverage Period": "90 Days from Date of Collection",
228
+ "Warranty Status": "Active & Validated"
229
+ },
230
+ notes: "Warranty excludes accidental damage, liquid ingress, unauthorized third-party tampering, or software jailbreaking. Please retain this document and quote your ticket reference number for any warranty claims."
235
231
  },
236
232
  {
237
233
  id: "service_completion",
@@ -239,12 +235,72 @@ All hardware replacements and associated workshop labor are covered under our co
239
235
  icon: "📜",
240
236
  badge: "QC Cleared",
241
237
  title: "Certificate of Service Completion",
242
- subject: "Service Completion & Quality Assurance Sign-off",
243
- body_text: `This document certifies that the requested overhaul, maintenance, and repair services have been executed in full accordance with industry service standards. The device has cleared bench testing and is ready for customer collection.`,
238
+ subject: "Certificate of Service Completion & Quality Sign-Off",
239
+ body_text: `This Certificate of Service Completion verifies that all diagnostic, repair, and maintenance procedures authorized under the referenced work order have been satisfactorily executed in full adherence to ISO/IPC hardware rework standards.
240
+
241
+ The equipment has successfully cleared all pre-delivery functional verifications, thermal load evaluations, and electrical safety diagnostics, and is officially released as fully operational.`,
242
+ items: [
243
+ { title: "Hardware Overhaul & Micro-Soldering Service", description: "Replaced degraded SMD power filtering capacitors and restored logic board rails", quantity: 1, unit_price: 0 },
244
+ { title: "Firmware Diagnostics & Peripheral Bench Testing", description: "100% operational clearance on audio codecs, cameras, biometric sensors, and radios", quantity: 1, unit_price: 0 },
245
+ { title: "Acoustic Chamber & Thermal Stress Verification", description: "Pass: Maximum load temperature maintained within normal threshold (38°C)", quantity: 1, unit_price: 0 }
246
+ ],
247
+ custom_fields: {
248
+ "Device Model": "MacBook Pro 16-inch (M2 Max - Space Grey)",
249
+ "Serial / IMEI": "C02G8027MD6T",
250
+ "Ticket Reference": "RT-2024-9104",
251
+ "Technician": "Bonifencia A. (Lead Certified Engineer)",
252
+ "Quality Control Inspector": "Kevin M. (Senior QA Specialist)",
253
+ "Final Testing Result": "PASSED - All Benchmarks Satisfied"
254
+ },
255
+ notes: "All original customer data was preserved intact. Device firmware updated to current stable release. The client has 7 days from collection to report any cosmetic or configuration discrepancies."
256
+ },
257
+ {
258
+ id: "destruction",
259
+ name: "Certificate of Destruction & Disposal",
260
+ icon: "♻️",
261
+ badge: "Eco & Compliance",
262
+ title: "Certificate of Destruction & Eco-Disposal",
263
+ subject: "Certificate of Secure Hardware Destruction & Eco-Disposal",
264
+ body_text: `This Certificate of Destruction legally certifies that the decommissioned computing equipment and electronic media detailed herein have been securely and irrevocably destroyed in compliance with NIST SP 800-88 Rev. 1 guidelines and WEEE environmental disposal regulations.
265
+
266
+ All storage media have undergone physical de-manufacturing, mechanical shredding, and complete demagnetization, ensuring zero residual data retrievability. Non-hazardous materials have been transferred to certified green-reclamation facilities.`,
267
+ items: [
268
+ { title: "Physical Demanufacturing & High-Torque Shredding", description: "Particle size reduced below DIN 66399 Level H-5 security standards", quantity: 1, unit_price: 0 },
269
+ { title: "Certified NIST 800-88 Cryptographic & Magnetic Sanitization", description: "Complete data eradication across all solid-state and magnetic platters", quantity: 1, unit_price: 0 },
270
+ { title: "Hazardous Materials & Lithium Battery Eco-Reclamation", description: "Zero-landfill certified chemical neutralization and precious metal recovery", quantity: 1, unit_price: 0 }
271
+ ],
272
+ custom_fields: {
273
+ "Device Model": "Dell PowerEdge R740 Server / 4x 1.92TB NVMe",
274
+ "Serial / IMEI": "SN-DELL-882194-X",
275
+ "Ticket Reference": "RT-2024-7712",
276
+ "Destruction Method": "Physical Shear Shredding & Degaussing",
277
+ "Compliance Standard": "NIST SP 800-88 Rev. 1 & WEEE Directive",
278
+ "Authorized Disposal Officer": "Bonifencia A. (Certified Waste Handler)"
279
+ },
280
+ notes: "Urban Device Care Ltd indemnifies the client against data breach liabilities originating from the specified serialized assets following the verified execution of destruction protocols."
281
+ },
282
+ {
283
+ id: "diagnostic",
284
+ name: "Diagnostic & Inspection Report",
285
+ icon: "🔍",
286
+ badge: "Hardware Findings",
287
+ title: "Diagnostic & Hardware Inspection Report",
288
+ subject: "Device Diagnostic & Inspection Findings - Urban Device Care",
289
+ body_text: `Our certified repair specialists have concluded an in-depth hardware, firmware, and electrical diagnostic examination of your device. The comprehensive technical findings, voltage line readings, and component health metrics are detailed below alongside our certified remediation roadmap.`,
244
290
  items: [
245
- { title: "Hardware Servicing & Component Replacement", description: "Executed per work order specifications", quantity: 1, unit_price: 0 },
246
- { title: "Final Bench Testing & Quality Control", description: "Passed 100% functional test criteria", quantity: 1, unit_price: 0 }
247
- ]
291
+ { title: "Logic Board Primary Power Rail Diagnostic", description: "PP_VDD_MAIN measured at 3.82V (Nominal). No ground short-circuits detected on primary PMIC", quantity: 1, unit_price: 0 },
292
+ { title: "Display Digitizer & Touch Array Profiling", description: "Matrix micro-fracture identified in bottom-left corner causing erratic capacitive touch feedback", quantity: 1, unit_price: 0 },
293
+ { title: "Battery Health & Internal Impedance Evaluation", description: "Current capacity: 74% of design capacity (842 cycles). Service battery recommended", quantity: 1, unit_price: 0 }
294
+ ],
295
+ custom_fields: {
296
+ "Device Model": "Samsung Galaxy S23 Ultra (512GB - Phantom Black)",
297
+ "Serial / IMEI": "358941092847120",
298
+ "Ticket Reference": "RT-2024-6523",
299
+ "Technician": "Bonifencia A. (Lead Certified Engineer)",
300
+ "Diagnostic Verdict": "Component Replacement Feasible",
301
+ "Estimated Labor Time": "2.5 Workshop Hours"
302
+ },
303
+ notes: "Diagnostic findings remain valid for 14 calendar days. Internal safety seals have been inspected and logged."
248
304
  },
249
305
  {
250
306
  id: "handover",
@@ -253,11 +309,23 @@ All hardware replacements and associated workshop labor are covered under our co
253
309
  badge: "Custody Form",
254
310
  title: "Equipment Handover & Custody Release Agreement",
255
311
  subject: "Equipment Handover & Custody Release Form",
256
- body_text: `This handover document certifies the return and physical custody release of customer equipment and associated accessories from Urban Device Care. The customer acknowledges receipt of equipment in verified working order.`,
312
+ body_text: `This Handover Agreement legally documents the transfer of custody of the repaired equipment and accessories listed below from Urban Device Care to the client or authorized corporate representative.
313
+
314
+ The recipient confirms that the device has been inspected, tested in their presence (including display, camera, audio, and network connectivity), and accepted in verified good working condition.`,
257
315
  items: [
258
- { title: "Device Handover & Physical Release", description: "Customer custody confirmed", quantity: 1, unit_price: 0 },
259
- { title: "Accessories Handover", description: "Chargers, cases, and cables returned in verified condition", quantity: 1, unit_price: 0 }
260
- ]
316
+ { title: "Primary Computing Device Release", description: "Serial verified, cosmetic inspection performed, customer account signed in", quantity: 1, unit_price: 0 },
317
+ { title: "OEM 96W USB-C Power Adapter & Braided Cable", description: "Tested and verified delivering 20V/4.8A under load", quantity: 1, unit_price: 0 },
318
+ { title: "Protective Hard Case & Accessories", description: "Returned in clean, intact custody state", quantity: 1, unit_price: 0 }
319
+ ],
320
+ custom_fields: {
321
+ "Device Model": "Lenovo ThinkPad X1 Carbon Gen 10",
322
+ "Serial / IMEI": "PF-29X810-22",
323
+ "Ticket Reference": "RT-2024-5390",
324
+ "Released By": "Bonifencia A. (Urban Device Care)",
325
+ "Received By": "Authorized Client Representative",
326
+ "Handover Condition": "Inspected & Approved in Workshop"
327
+ },
328
+ notes: "By accepting handover, the client acknowledges satisfactory return of all custody items and confirms device operability."
261
329
  },
262
330
  {
263
331
  id: "proforma",
@@ -265,12 +333,24 @@ All hardware replacements and associated workshop labor are covered under our co
265
333
  icon: "💼",
266
334
  badge: "Cost Estimate",
267
335
  title: "Commercial Quotation (Proforma)",
268
- subject: "Commercial Quotation & Scope of Work",
269
- body_text: `Thank you for choosing Urban Device Care. Below is an itemized breakdown of estimated hardware parts, workshop labor, and turnaround schedule for your project. This quote is valid for 14 calendar days.`,
336
+ subject: "Commercial Quotation & Scope of Work - Urban Device Care",
337
+ body_text: `Thank you for choosing Urban Device Care. Below is an itemized commercial quotation detailing required OEM components, workshop labor, precision calibration, and expected turnaround schedule for your equipment overhaul.
338
+
339
+ This quotation is valid for 14 calendar days from the date of issue. Upon formal approval, component procurement and bench allocation will commence immediately.`,
270
340
  items: [
271
- { title: "OEM Replacement Hardware Assembly", description: "Brand new replacement module", quantity: 1, unit_price: 150 },
272
- { title: "Workshop Labor & Clean-Room Installation", description: "Certified technician installation & calibration", quantity: 1, unit_price: 45 }
273
- ]
341
+ { title: "OEM Liquid Retina XDR Display Assembly", description: "Brand new original factory panel with factory color calibration", quantity: 1, unit_price: 42e3 },
342
+ { title: "Clean-Room Workshop Installation & Sensor Alignment", description: "Certified clean-room installation, gasket replacement & TrueTone calibration", quantity: 1, unit_price: 6500 },
343
+ { title: "Hydrophobic Dust & Water Ingress Seal Restoration", description: "OEM adhesive barrier pressure cured to IP68 rating", quantity: 1, unit_price: 2500 }
344
+ ],
345
+ custom_fields: {
346
+ "Device Model": "Apple iPad Pro 12.9-inch (6th Gen - Wi-Fi + Cellular)",
347
+ "Serial / IMEI": "DLXW9201M93K",
348
+ "Ticket Reference": "RT-2024-4190",
349
+ "Estimator / Technician": "Bonifencia A.",
350
+ "Validity Period": "14 Calendar Days",
351
+ "Estimated Turnaround": "24 - 48 Hours"
352
+ },
353
+ notes: "All replaced hardware carries our standard 90-day warranty coverage. Payment accepted via M-Pesa, Bank Transfer, or Credit/Debit Card."
274
354
  },
275
355
  {
276
356
  id: "trade_in",
@@ -279,24 +359,22 @@ All hardware replacements and associated workshop labor are covered under our co
279
359
  badge: "Trade-In & Transfer",
280
360
  title: "Device Trade-In & Buyback Agreement",
281
361
  subject: "Device Trade-In & Buyback Agreement - Urban Device Care",
282
- body_text: `This agreement confirms the valuation, transfer of title, and trade-in credit for the listed customer hardware. Customer verifies legal ownership and consents to complete data sanitation and ownership transfer.`,
283
- items: [
284
- { title: "Trade-In Device Valuation & Credit", description: "Assessed Grade A condition allowance", quantity: 1, unit_price: 25e3 },
285
- { title: "Data Sanitization & Ownership Transfer", description: "Certified NIST 800-88 cryptographic wipe", quantity: 1, unit_price: 0 }
286
- ]
287
- },
288
- {
289
- id: "destruction",
290
- name: "Certificate of Destruction & Disposal",
291
- icon: "♻️",
292
- badge: "Eco & Compliance",
293
- title: "Certificate of Destruction & Eco-Disposal",
294
- subject: "Certificate of Hardware Destruction & Eco-Disposal",
295
- body_text: `This certificate confirms that the decommissioned hardware listed below has been safely demanufactured, shredded, or recycled in full compliance with WEEE and environmental data destruction standards.`,
362
+ body_text: `This Agreement formalizes the title transfer, functional evaluation, and agreed trade-in credit valuation for the customer-owned equipment listed below.
363
+
364
+ The seller warrants that they are the sole lawful owner of the equipment, that it is free of all liens, financial encumbrances, iCloud/Google activation locks, and corporate MDM profiles. Urban Device Care agrees to apply the confirmed credit towards service fees or disbursement.`,
296
365
  items: [
297
- { title: "Hardware Demanufacturing & Shredding", description: "Certified environmental destruction", quantity: 1, unit_price: 0 },
298
- { title: "Hazardous Materials Recycling", description: "Zero-landfill lithium battery reclamation", quantity: 1, unit_price: 0 }
299
- ]
366
+ { title: "Trade-In Device Valuation Allowance (Grade A)", description: "Full functional clearance: battery health 91%, minor bezel wear, pristine screen", quantity: 1, unit_price: 35e3 },
367
+ { title: "NIST 800-88 Cryptographic Data Sanitation Protocol", description: "Certified factory reset and firmware wipe with zero data retention guarantee", quantity: 1, unit_price: 0 }
368
+ ],
369
+ custom_fields: {
370
+ "Device Model": "Apple iPhone 13 Pro (128GB - Sierra Blue)",
371
+ "Serial / IMEI": "G6TD9021ML8R",
372
+ "Ticket Reference": "RT-2024-3829",
373
+ "Activation Lock": "Removed & Verified Off",
374
+ "Assessed Grade": "Grade A (Excellent)",
375
+ "Evaluation Specialist": "Bonifencia A."
376
+ },
377
+ notes: "Ownership transfers immediately upon document execution. Valuation is binding and irrevocable once trade-in payout or store credit is issued."
300
378
  },
301
379
  {
302
380
  id: "blank",
@@ -305,13 +383,21 @@ All hardware replacements and associated workshop labor are covered under our co
305
383
  badge: "Custom",
306
384
  title: "Official Document",
307
385
  subject: "Official Document from Urban Device Care",
308
- body_text: "",
386
+ body_text: "This official technical document confirms the specifications, service terms, and verified conditions documented by Urban Device Care for the specified customer asset.",
309
387
  items: [
310
- { title: "", description: "", quantity: 1, unit_price: 0 }
311
- ]
388
+ { title: "Professional Hardware Inspection & Maintenance Service", description: "Certified technical evaluation and service clearance", quantity: 1, unit_price: 0 }
389
+ ],
390
+ custom_fields: {
391
+ "Device Model": "Computing Asset / Hardware Module",
392
+ "Serial / IMEI": "SN-XXXX-XXXX",
393
+ "Ticket Reference": "RT-2024-0001",
394
+ "Technician": "Bonifencia A."
395
+ },
396
+ notes: "Standard terms and conditions apply. For inquiries, contact info@urbandevicecare.co.uk."
312
397
  }
313
398
  ];
314
399
  const DocumentsPage = () => {
400
+ var _a, _b;
315
401
  const [activeTab, setActiveTab] = react.useState("hub");
316
402
  const [documents, setDocuments] = react.useState([]);
317
403
  const [loading, setLoading] = react.useState(true);
@@ -412,32 +498,203 @@ const DocumentsPage = () => {
412
498
  });
413
499
  const [customDocForm, setCustomDocForm] = react.useState({
414
500
  preset_id: "warranty",
415
- title: "Warranty Certificate",
501
+ title: CUSTOM_DOC_PRESETS[0].title,
416
502
  company_name: "",
417
- customer_name: "",
418
- customer_email: "",
419
- customer_phone: "",
420
- body_text: "This certificate verifies that your device has undergone professional repairs with genuine OEM-specification components and has passed our multi-point quality assurance inspection.\n\nAll hardware replacements and associated workshop labor are covered under our comprehensive 90-day warranty against defects in parts and workmanship. Please retain this document for warranty verification.",
421
- items: [
422
- { title: "OEM Component & Hardware Warranty", description: "90-Day Coverage from Date of Collection", quantity: 1, unit_price: 0 },
423
- { title: "Workshop Diagnostic & Quality Clearance", description: "Multi-Point Hardware & Bench Testing", quantity: 1, unit_price: 0 }
424
- ],
503
+ customer_name: "Jane Doe",
504
+ customer_email: "jane.doe@example.com",
505
+ customer_phone: "+254 712 345 678",
506
+ body_text: CUSTOM_DOC_PRESETS[0].body_text,
507
+ items: CUSTOM_DOC_PRESETS[0].items.map((it) => ({ ...it })),
508
+ custom_fields: { ...CUSTOM_DOC_PRESETS[0].custom_fields || {} },
509
+ notes: CUSTOM_DOC_PRESETS[0].notes || "",
425
510
  currency_code: "kes",
426
511
  send_email_now: true,
427
- email_subject: "Your Official Warranty Certificate - Urban Device Care"
512
+ email_subject: CUSTOM_DOC_PRESETS[0].subject
428
513
  });
429
- const selectCustomDocPreset = (presetId) => {
430
- const preset = CUSTOM_DOC_PRESETS.find((p) => p.id === presetId);
431
- if (!preset) return;
514
+ const [repairTickets, setRepairTickets] = react.useState([]);
515
+ const [loadingRepairs, setLoadingRepairs] = react.useState(false);
516
+ const [selectedTicketId, setSelectedTicketId] = react.useState("");
517
+ const [newCustomFieldKey, setNewCustomFieldKey] = react.useState("");
518
+ const [newCustomFieldValue, setNewCustomFieldValue] = react.useState("");
519
+ const loadRepairTickets = async () => {
520
+ setLoadingRepairs(true);
521
+ try {
522
+ const res = await fetch("/admin/repairs", { credentials: "include" });
523
+ if (res.ok) {
524
+ const data = await res.json().catch(() => ({}));
525
+ if (data.repair_tickets && Array.isArray(data.repair_tickets)) {
526
+ setRepairTickets(data.repair_tickets);
527
+ return;
528
+ }
529
+ }
530
+ } catch (e) {
531
+ console.warn("Could not fetch /admin/repairs:", e);
532
+ } finally {
533
+ setLoadingRepairs(false);
534
+ }
535
+ };
536
+ react.useEffect(() => {
537
+ if (customDocModalOpen) {
538
+ loadRepairTickets();
539
+ }
540
+ }, [customDocModalOpen]);
541
+ const availableTickets = react.useMemo(() => {
542
+ var _a2, _b2;
543
+ const map = /* @__PURE__ */ new Map();
544
+ for (const t of repairTickets) {
545
+ if (t.id) map.set(t.id, t);
546
+ }
547
+ for (const d of documents) {
548
+ const ticketId = d.repair_ticket_id;
549
+ const displayId = d.display_id || "";
550
+ const isRepairDoc = displayId.startsWith("RT-") || displayId.startsWith("JOB-") || Boolean(ticketId);
551
+ const idKey = ticketId || displayId;
552
+ if (isRepairDoc && idKey && !map.has(idKey)) {
553
+ const customFields = d.custom_fields || ((_a2 = d.metadata) == null ? void 0 : _a2.custom_fields) || {};
554
+ map.set(idKey, {
555
+ id: idKey,
556
+ ticket_number: String(displayId).replace(/^(RT-|JOB-)+/i, "") || idKey,
557
+ customer_name: d.customer_name,
558
+ customer_email: d.customer_email,
559
+ customer_phone: d.customer_phone,
560
+ device: {
561
+ brand: customFields["Brand"] || "",
562
+ model_name: customFields["Device Model"] || customFields["Model"] || "Device",
563
+ serial_number: customFields["Serial / IMEI"] || customFields["Serial Number"] || "N/A",
564
+ condition: customFields["Condition"] || "Good condition"
565
+ },
566
+ accessories: customFields["Accessories"] || "None",
567
+ technician_name: customFields["Technician"] || "Bonifencia A.",
568
+ issue_description: customFields["Reported Issue"] || d.notes || "Standard Service",
569
+ custom_fields: customFields,
570
+ items: ((_b2 = d.metadata) == null ? void 0 : _b2.items) || []
571
+ });
572
+ }
573
+ }
574
+ return Array.from(map.values());
575
+ }, [repairTickets, documents]);
576
+ const handleSelectRepairTicket = (ticketId) => {
577
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
578
+ setSelectedTicketId(ticketId);
579
+ if (!ticketId) return;
580
+ const ticket = availableTickets.find((t) => t.id === ticketId || t.ticket_number === ticketId);
581
+ if (!ticket) return;
582
+ const brandModel = [(_a2 = ticket.device) == null ? void 0 : _a2.brand, (_b2 = ticket.device) == null ? void 0 : _b2.model_name].filter(Boolean).join(" ").trim() || ((_c = ticket.custom_fields) == null ? void 0 : _c["Device Model"]) || "Apple iPhone 15 Pro Max";
583
+ const serialImei = ((_d = ticket.device) == null ? void 0 : _d.serial_number) || ((_e = ticket.device) == null ? void 0 : _e.imei) || ((_f = ticket.custom_fields) == null ? void 0 : _f["Serial / IMEI"]) || "N/A";
584
+ const technician = ticket.technician_name || ((_g = ticket.custom_fields) == null ? void 0 : _g["Technician"]) || "Bonifencia A. (Lead Certified Engineer)";
585
+ const accessories = ticket.accessories || ((_h = ticket.custom_fields) == null ? void 0 : _h["Accessories"]) || "None (Unit only)";
586
+ const condition = ((_i = ticket.device) == null ? void 0 : _i.condition) || ((_j = ticket.custom_fields) == null ? void 0 : _j["Condition"]) || "Good (Normal wear)";
587
+ const cleanNum = String(ticket.ticket_number || ticket.id).replace(/^(RT-|JOB-)+/i, "");
588
+ const ticketRef = `RT-${cleanNum}`;
589
+ const customerName = ((_k = ticket.customer) == null ? void 0 : _k.company_name) || ticket.company_name || ticket.customer_name || (((_l = ticket.customer) == null ? void 0 : _l.first_name) ? `${ticket.customer.first_name} ${ticket.customer.last_name || ""}`.trim() : "") || "Valued Customer";
590
+ const customerEmail = ((_m = ticket.customer) == null ? void 0 : _m.email) || ticket.customer_email || "";
591
+ const customerPhone = ((_n = ticket.customer) == null ? void 0 : _n.phone) || ticket.customer_phone || "";
592
+ const companyName = ((_o = ticket.customer) == null ? void 0 : _o.company_name) || ticket.company_name || "";
593
+ const ticketItems = [];
594
+ if (ticket.product_variants && Array.isArray(ticket.product_variants) && ticket.product_variants.length > 0) {
595
+ for (const pv of ticket.product_variants) {
596
+ const qty = Number(pv.quantity || ((_p = pv.pivot) == null ? void 0 : _p.quantity) || 1);
597
+ const unitPrice = Number(pv.unit_price ?? pv.price ?? ((_r = (_q = pv.prices) == null ? void 0 : _q[0]) == null ? void 0 : _r.amount) ?? 0);
598
+ const partTitle = ((_s = pv.product) == null ? void 0 : _s.title) ? `${pv.product.title}${pv.title && pv.title !== "Default Variant" ? ` (${pv.title})` : ""}` : pv.title || ((_t = pv.product) == null ? void 0 : _t.title) || "Inventory Part";
599
+ ticketItems.push({
600
+ title: partTitle,
601
+ description: pv.sku ? `SKU: ${pv.sku}` : "",
602
+ quantity: qty,
603
+ unit_price: unitPrice
604
+ });
605
+ }
606
+ }
607
+ if (ticket.custom_parts && Array.isArray(ticket.custom_parts) && ticket.custom_parts.length > 0) {
608
+ for (const cp of ticket.custom_parts) {
609
+ ticketItems.push({
610
+ title: cp.name || cp.title || "Custom Repair Part / Service",
611
+ description: cp.description || "",
612
+ quantity: Number(cp.quantity || 1),
613
+ unit_price: Number(cp.price || cp.unit_price || 0)
614
+ });
615
+ }
616
+ }
617
+ if (((_u = ticket.device) == null ? void 0 : _u.parts_used) && Array.isArray(ticket.device.parts_used)) {
618
+ for (const p of ticket.device.parts_used) {
619
+ ticketItems.push({
620
+ title: p.name || p.title || "Replacement Part",
621
+ description: p.sku ? `SKU: ${p.sku}` : "",
622
+ quantity: Number(p.quantity || 1),
623
+ unit_price: Number(p.price || 0)
624
+ });
625
+ }
626
+ }
627
+ if (Number(ticket.labor_estimate || 0) > 0) {
628
+ ticketItems.push({
629
+ title: "Workshop Labor & Clean-Room Installation",
630
+ description: "Certified hardware diagnostic and component installation",
631
+ quantity: 1,
632
+ unit_price: Number(ticket.labor_estimate)
633
+ });
634
+ }
635
+ if (ticketItems.length === 0 && ticket.items && Array.isArray(ticket.items) && ticket.items.length > 0) {
636
+ for (const it of ticket.items) {
637
+ ticketItems.push({
638
+ title: it.title || "Service Item",
639
+ description: it.description || "",
640
+ quantity: Number(it.quantity || 1),
641
+ unit_price: Number(it.unit_price || 0)
642
+ });
643
+ }
644
+ }
432
645
  setCustomDocForm((prev) => ({
433
646
  ...prev,
434
- preset_id: preset.id,
435
- title: preset.title,
436
- email_subject: preset.subject,
437
- body_text: preset.body_text,
438
- items: preset.items.map((it) => ({ ...it }))
647
+ customer_name: customerName,
648
+ customer_email: customerEmail,
649
+ customer_phone: customerPhone,
650
+ company_name: companyName,
651
+ custom_fields: {
652
+ ...prev.custom_fields,
653
+ "Device Model": brandModel,
654
+ "Serial / IMEI": serialImei,
655
+ "Ticket Reference": ticketRef,
656
+ "Technician": technician,
657
+ "Accessories": accessories,
658
+ "Condition": condition,
659
+ "Reported Issue": ticket.issue_description || ticket.reported_issue || "Standard Service"
660
+ },
661
+ items: ticketItems.length > 0 ? ticketItems : prev.items,
662
+ email_subject: `${prev.title} - ${brandModel} (${ticketRef})`,
663
+ notes: prev.notes || `Linked to Repair Ticket ${ticketRef}. Device: ${brandModel} (S/N: ${serialImei}).`
439
664
  }));
440
665
  };
666
+ const handleClearRepairLink = () => {
667
+ setSelectedTicketId("");
668
+ };
669
+ const selectCustomDocPreset = (presetId) => {
670
+ const preset = CUSTOM_DOC_PRESETS.find((p) => p.id === presetId);
671
+ if (!preset) return;
672
+ setCustomDocForm((prev) => {
673
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
674
+ const updatedFields = {
675
+ ...preset.custom_fields || {},
676
+ ...selectedTicketId ? {
677
+ "Device Model": ((_a2 = prev.custom_fields) == null ? void 0 : _a2["Device Model"]) || ((_b2 = preset.custom_fields) == null ? void 0 : _b2["Device Model"]),
678
+ "Serial / IMEI": ((_c = prev.custom_fields) == null ? void 0 : _c["Serial / IMEI"]) || ((_d = preset.custom_fields) == null ? void 0 : _d["Serial / IMEI"]),
679
+ "Ticket Reference": ((_e = prev.custom_fields) == null ? void 0 : _e["Ticket Reference"]) || ((_f = preset.custom_fields) == null ? void 0 : _f["Ticket Reference"]),
680
+ "Technician": ((_g = prev.custom_fields) == null ? void 0 : _g["Technician"]) || ((_h = preset.custom_fields) == null ? void 0 : _h["Technician"]),
681
+ "Accessories": ((_i = prev.custom_fields) == null ? void 0 : _i["Accessories"]) || ((_j = preset.custom_fields) == null ? void 0 : _j["Accessories"]),
682
+ "Condition": ((_k = prev.custom_fields) == null ? void 0 : _k["Condition"]) || ((_l = preset.custom_fields) == null ? void 0 : _l["Condition"]),
683
+ "Reported Issue": ((_m = prev.custom_fields) == null ? void 0 : _m["Reported Issue"]) || ((_n = preset.custom_fields) == null ? void 0 : _n["Reported Issue"])
684
+ } : {}
685
+ };
686
+ return {
687
+ ...prev,
688
+ preset_id: preset.id,
689
+ title: preset.title,
690
+ email_subject: ((_o = prev.custom_fields) == null ? void 0 : _o["Device Model"]) ? `${preset.title} - ${prev.custom_fields["Device Model"]}` : preset.subject,
691
+ body_text: preset.body_text,
692
+ items: preset.items.map((it) => ({ ...it })),
693
+ custom_fields: updatedFields,
694
+ notes: preset.notes != null ? preset.notes : prev.notes
695
+ };
696
+ });
697
+ };
441
698
  const [sendForm, setSendForm] = react.useState({
442
699
  channels: ["email"],
443
700
  custom_message: "",
@@ -499,8 +756,8 @@ const DocumentsPage = () => {
499
756
  setSettings((prev) => ({ ...prev, [key]: val }));
500
757
  };
501
758
  const handleCreateManual = async () => {
502
- var _a;
503
- if (!((_a = manualForm.customer_name) == null ? void 0 : _a.trim())) {
759
+ var _a2;
760
+ if (!((_a2 = manualForm.customer_name) == null ? void 0 : _a2.trim())) {
504
761
  alert("Please provide a Customer Name.");
505
762
  return;
506
763
  }
@@ -561,12 +818,12 @@ const DocumentsPage = () => {
561
818
  }
562
819
  };
563
820
  const handleCreateCustomDoc = async () => {
564
- var _a, _b;
565
- if (!((_a = customDocForm.title) == null ? void 0 : _a.trim())) {
821
+ var _a2, _b2;
822
+ if (!((_a2 = customDocForm.title) == null ? void 0 : _a2.trim())) {
566
823
  alert("Please provide a Document Title.");
567
824
  return;
568
825
  }
569
- if (!((_b = customDocForm.customer_name) == null ? void 0 : _b.trim())) {
826
+ if (!((_b2 = customDocForm.customer_name) == null ? void 0 : _b2.trim())) {
570
827
  alert("Please provide a Customer / Recipient Name.");
571
828
  return;
572
829
  }
@@ -585,6 +842,8 @@ const DocumentsPage = () => {
585
842
  phone: customDocForm.customer_phone
586
843
  },
587
844
  body_text: customDocForm.body_text,
845
+ notes: customDocForm.notes,
846
+ custom_fields: customDocForm.custom_fields,
588
847
  items: customDocForm.items.filter((it) => it.title.trim() !== ""),
589
848
  currency_code: customDocForm.currency_code,
590
849
  send_email_now: customDocForm.send_email_now,
@@ -742,7 +1001,7 @@ const DocumentsPage = () => {
742
1001
  }
743
1002
  };
744
1003
  const handleDeleteRepairDocuments = async (repairTicketId) => {
745
- var _a, _b, _c;
1004
+ var _a2, _b2, _c;
746
1005
  if (!confirm(
747
1006
  "Are you sure you want to delete ALL generated documents for this repair ticket (including in Zoho Books)? This will allow documents to be cleanly regenerated with updated ticket information."
748
1007
  )) {
@@ -755,7 +1014,7 @@ const DocumentsPage = () => {
755
1014
  });
756
1015
  const data = await res.json();
757
1016
  if (res.ok && data.success) {
758
- const zohoCount = (((_a = data.deleted_zoho_invoices) == null ? void 0 : _a.length) || 0) + (((_b = data.deleted_zoho_estimates) == null ? void 0 : _b.length) || 0) + (((_c = data.deleted_zoho_payments) == null ? void 0 : _c.length) || 0);
1017
+ const zohoCount = (((_a2 = data.deleted_zoho_invoices) == null ? void 0 : _a2.length) || 0) + (((_b2 = data.deleted_zoho_estimates) == null ? void 0 : _b2.length) || 0) + (((_c = data.deleted_zoho_payments) == null ? void 0 : _c.length) || 0);
759
1018
  alert(`Successfully deleted ${data.deleted_records || 0} document record(s)${zohoCount > 0 ? ` and ${zohoCount} Zoho document(s)` : ""}!`);
760
1019
  loadDocuments();
761
1020
  } else {
@@ -766,19 +1025,19 @@ const DocumentsPage = () => {
766
1025
  }
767
1026
  };
768
1027
  const filteredDocs = documents.filter((doc) => {
769
- var _a, _b, _c, _d;
1028
+ var _a2, _b2, _c, _d;
770
1029
  if (typeFilter !== "all" && doc.type !== typeFilter) return false;
771
1030
  if (sourceFilter !== "all" && doc.source !== sourceFilter) return false;
772
1031
  if (searchTerm) {
773
1032
  const q = searchTerm.toLowerCase();
774
- return ((_a = doc.display_id) == null ? void 0 : _a.toLowerCase().includes(q)) || ((_b = doc.customer_name) == null ? void 0 : _b.toLowerCase().includes(q)) || ((_c = doc.customer_email) == null ? void 0 : _c.toLowerCase().includes(q)) || ((_d = doc.title) == null ? void 0 : _d.toLowerCase().includes(q));
1033
+ return ((_a2 = doc.display_id) == null ? void 0 : _a2.toLowerCase().includes(q)) || ((_b2 = doc.customer_name) == null ? void 0 : _b2.toLowerCase().includes(q)) || ((_c = doc.customer_email) == null ? void 0 : _c.toLowerCase().includes(q)) || ((_d = doc.title) == null ? void 0 : _d.toLowerCase().includes(q));
775
1034
  }
776
1035
  return true;
777
1036
  });
778
1037
  const documentGroups = react.useMemo(() => {
779
1038
  const allGroups = groupDocuments(documents);
780
1039
  return allGroups.filter((group) => {
781
- var _a, _b, _c, _d;
1040
+ var _a2, _b2, _c, _d;
782
1041
  if (sourceFilter !== "all") {
783
1042
  const matchesSource = group.documents.some((d) => d.source === sourceFilter);
784
1043
  if (!matchesSource) return false;
@@ -789,10 +1048,10 @@ const DocumentsPage = () => {
789
1048
  }
790
1049
  if (searchTerm) {
791
1050
  const q = searchTerm.toLowerCase();
792
- const matchesSearch = ((_a = group.groupKey) == null ? void 0 : _a.toLowerCase().includes(q)) || ((_b = group.customerName) == null ? void 0 : _b.toLowerCase().includes(q)) || ((_c = group.customerEmail) == null ? void 0 : _c.toLowerCase().includes(q)) || ((_d = group.customerPhone) == null ? void 0 : _d.toLowerCase().includes(q)) || group.documents.some(
1051
+ const matchesSearch = ((_a2 = group.groupKey) == null ? void 0 : _a2.toLowerCase().includes(q)) || ((_b2 = group.customerName) == null ? void 0 : _b2.toLowerCase().includes(q)) || ((_c = group.customerEmail) == null ? void 0 : _c.toLowerCase().includes(q)) || ((_d = group.customerPhone) == null ? void 0 : _d.toLowerCase().includes(q)) || group.documents.some(
793
1052
  (d) => {
794
- var _a2, _b2, _c2;
795
- return ((_a2 = d.display_id) == null ? void 0 : _a2.toLowerCase().includes(q)) || ((_b2 = d.title) == null ? void 0 : _b2.toLowerCase().includes(q)) || ((_c2 = d.customer_name) == null ? void 0 : _c2.toLowerCase().includes(q));
1053
+ var _a3, _b3, _c2;
1054
+ return ((_a3 = d.display_id) == null ? void 0 : _a3.toLowerCase().includes(q)) || ((_b3 = d.title) == null ? void 0 : _b3.toLowerCase().includes(q)) || ((_c2 = d.customer_name) == null ? void 0 : _c2.toLowerCase().includes(q));
796
1055
  }
797
1056
  );
798
1057
  if (!matchesSearch) return false;
@@ -3054,6 +3313,74 @@ Your *{document_title} #{display_id}* has been generated.
3054
3313
  /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Create Custom Document & Send Email" }) }),
3055
3314
  /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Body, { className: "p-6 flex flex-col gap-6 max-w-2xl mx-auto overflow-y-auto max-h-[80vh]", children: [
3056
3315
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-subtle", children: "Create a customized document (e.g. Warranty Certificate, Inspection Report, Service Clearance) and directly dispatch via email." }),
3316
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3.5 border border-ui-border-base rounded-lg bg-ui-bg-subtle flex flex-col gap-2.5", children: [
3317
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
3318
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3319
+ /* @__PURE__ */ jsxRuntime.jsx(icons.DocumentText, { className: "w-4 h-4 text-ui-fg-interactive" }),
3320
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs font-semibold text-ui-fg-base", children: "Link / Load from Repair Ticket (Optional)" })
3321
+ ] }),
3322
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
3323
+ selectedTicketId && /* @__PURE__ */ jsxRuntime.jsx(
3324
+ ui.Button,
3325
+ {
3326
+ variant: "secondary",
3327
+ size: "small",
3328
+ className: "text-[11px] h-6 px-2 text-ui-fg-muted hover:text-red-600",
3329
+ onClick: handleClearRepairLink,
3330
+ children: "Clear Ticket Link"
3331
+ }
3332
+ ),
3333
+ /* @__PURE__ */ jsxRuntime.jsxs(
3334
+ ui.Button,
3335
+ {
3336
+ variant: "secondary",
3337
+ size: "small",
3338
+ className: "text-[11px] h-6 px-2",
3339
+ onClick: loadRepairTickets,
3340
+ isLoading: loadingRepairs,
3341
+ children: [
3342
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "w-3 h-3 mr-1" }),
3343
+ " Refresh Tickets"
3344
+ ]
3345
+ }
3346
+ )
3347
+ ] })
3348
+ ] }),
3349
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-[11px] text-ui-fg-subtle", children: "Automatically autofills customer details, device specifications (model, serial/IMEI, accessories), technician notes, and services from active repairs." }),
3350
+ /* @__PURE__ */ jsxRuntime.jsxs(
3351
+ ui.Select,
3352
+ {
3353
+ value: selectedTicketId || void 0,
3354
+ onValueChange: handleSelectRepairTicket,
3355
+ children: [
3356
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { size: "small", className: "w-full bg-ui-bg-base", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "-- Select a Repair Ticket to autofill fields --" }) }),
3357
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: availableTickets.map((t) => {
3358
+ var _a2, _b2, _c, _d, _e;
3359
+ const brandModel = [(_a2 = t.device) == null ? void 0 : _a2.brand, (_b2 = t.device) == null ? void 0 : _b2.model_name].filter(Boolean).join(" ").trim() || ((_c = t.custom_fields) == null ? void 0 : _c["Device Model"]) || "Device";
3360
+ const cleanNum = String(t.ticket_number || t.id).replace(/^(RT-|JOB-)+/i, "");
3361
+ const displayRef = `RT-${cleanNum}`;
3362
+ const custName = ((_d = t.customer) == null ? void 0 : _d.company_name) || t.company_name || t.customer_name || (((_e = t.customer) == null ? void 0 : _e.first_name) ? `${t.customer.first_name} ${t.customer.last_name || ""}`.trim() : "") || "Customer";
3363
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Item, { value: t.id, children: [
3364
+ displayRef,
3365
+ " — ",
3366
+ custName,
3367
+ " (",
3368
+ brandModel,
3369
+ ")"
3370
+ ] }, t.id);
3371
+ }) })
3372
+ ]
3373
+ }
3374
+ ),
3375
+ selectedTicketId && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-2.5 py-1.5 rounded bg-ui-bg-interactive-subtle border border-ui-border-interactive text-[11px] text-ui-fg-interactive font-medium", children: [
3376
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "✓ Loaded details from ticket" }),
3377
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold underline", children: ((_a = customDocForm.custom_fields) == null ? void 0 : _a["Ticket Reference"]) || selectedTicketId }),
3378
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3379
+ "• ",
3380
+ (_b = customDocForm.custom_fields) == null ? void 0 : _b["Device Model"]
3381
+ ] })
3382
+ ] })
3383
+ ] }),
3057
3384
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
3058
3385
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs font-semibold", children: "Select a Document Preset (Optional)" }),
3059
3386
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-[11px] text-ui-fg-subtle", children: "Pick a pre-configured template below to automatically populate title, certificate text, and itemized points." }),
@@ -3315,6 +3642,105 @@ Your *{document_title} #{display_id}* has been generated.
3315
3642
  ] })
3316
3643
  ] })
3317
3644
  ] }),
3645
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 p-3.5 border border-ui-border-base rounded-lg bg-ui-bg-subtle", children: [
3646
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3647
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs font-semibold text-ui-fg-base", children: "Equipment & Record Specifications (Custom Fields)" }),
3648
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-[11px] text-ui-fg-subtle", children: "Key-value metadata printed prominently in the client & equipment specifications summary box." })
3649
+ ] }) }),
3650
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 mt-1", children: [
3651
+ Object.entries(customDocForm.custom_fields || {}).map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 gap-2 items-center bg-ui-bg-base p-2 rounded border border-ui-border-base", children: [
3652
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-[11px] text-ui-fg-subtle mb-0.5", children: key }) }),
3653
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-7", children: /* @__PURE__ */ jsxRuntime.jsx(
3654
+ ui.Input,
3655
+ {
3656
+ size: "small",
3657
+ value,
3658
+ onChange: (e) => {
3659
+ setCustomDocForm({
3660
+ ...customDocForm,
3661
+ custom_fields: {
3662
+ ...customDocForm.custom_fields || {},
3663
+ [key]: e.target.value
3664
+ }
3665
+ });
3666
+ },
3667
+ placeholder: `Enter ${key}...`
3668
+ }
3669
+ ) }),
3670
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-1 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
3671
+ "button",
3672
+ {
3673
+ type: "button",
3674
+ onClick: () => {
3675
+ const updated = { ...customDocForm.custom_fields || {} };
3676
+ delete updated[key];
3677
+ setCustomDocForm({ ...customDocForm, custom_fields: updated });
3678
+ },
3679
+ className: "text-ui-fg-muted hover:text-red-500 p-1 rounded hover:bg-ui-bg-subtle transition-colors cursor-pointer",
3680
+ title: "Delete Field",
3681
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "w-3.5 h-3.5" })
3682
+ }
3683
+ ) })
3684
+ ] }, key)),
3685
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 gap-2 items-center pt-1", children: [
3686
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-4", children: /* @__PURE__ */ jsxRuntime.jsx(
3687
+ ui.Input,
3688
+ {
3689
+ size: "small",
3690
+ placeholder: "New Field Name (e.g. Battery Health)",
3691
+ value: newCustomFieldKey,
3692
+ onChange: (e) => setNewCustomFieldKey(e.target.value)
3693
+ }
3694
+ ) }),
3695
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-6", children: /* @__PURE__ */ jsxRuntime.jsx(
3696
+ ui.Input,
3697
+ {
3698
+ size: "small",
3699
+ placeholder: "Field Value (e.g. 98% Peak Performance)",
3700
+ value: newCustomFieldValue,
3701
+ onChange: (e) => setNewCustomFieldValue(e.target.value)
3702
+ }
3703
+ ) }),
3704
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
3705
+ ui.Button,
3706
+ {
3707
+ type: "button",
3708
+ variant: "secondary",
3709
+ size: "small",
3710
+ className: "w-full text-xs",
3711
+ onClick: () => {
3712
+ if (!newCustomFieldKey.trim()) return;
3713
+ setCustomDocForm({
3714
+ ...customDocForm,
3715
+ custom_fields: {
3716
+ ...customDocForm.custom_fields || {},
3717
+ [newCustomFieldKey.trim()]: newCustomFieldValue.trim()
3718
+ }
3719
+ });
3720
+ setNewCustomFieldKey("");
3721
+ setNewCustomFieldValue("");
3722
+ },
3723
+ children: [
3724
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, { className: "w-3 h-3 mr-1" }),
3725
+ " Add Field"
3726
+ ]
3727
+ }
3728
+ ) })
3729
+ ] })
3730
+ ] })
3731
+ ] }),
3732
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3733
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs font-semibold", children: "Terms & Conditions / Special Notes (Printed at bottom)" }),
3734
+ /* @__PURE__ */ jsxRuntime.jsx(
3735
+ ui.Textarea,
3736
+ {
3737
+ value: customDocForm.notes || "",
3738
+ onChange: (e) => setCustomDocForm({ ...customDocForm, notes: e.target.value }),
3739
+ placeholder: "Official warranty terms, legal compliance notes, or inspection disclaimers...",
3740
+ rows: 3
3741
+ }
3742
+ )
3743
+ ] }),
3318
3744
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3319
3745
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs font-semibold", children: "Email Subject" }),
3320
3746
  /* @__PURE__ */ jsxRuntime.jsx(