medusajs-documents-plugin 1.1.61 → 1.1.64

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.
@@ -330,31 +330,6 @@ The recipient confirms that the device has been inspected, tested in their prese
330
330
  },
331
331
  notes: "By accepting handover, the client acknowledges satisfactory return of all custody items and confirms device operability."
332
332
  },
333
- {
334
- id: "proforma",
335
- name: "Commercial Quotation / Proforma",
336
- icon: "💼",
337
- badge: "Cost Estimate",
338
- title: "Commercial Quotation (Proforma)",
339
- subject: "Commercial Quotation & Scope of Work - Urban Device Care",
340
- 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.
341
-
342
- This quotation is valid for 14 calendar days from the date of issue. Upon formal approval, component procurement and bench allocation will commence immediately.`,
343
- items: [
344
- { title: "OEM Liquid Retina XDR Display Assembly", description: "Brand new original factory panel with factory color calibration", quantity: 1, unit_price: 42e3 },
345
- { title: "Clean-Room Workshop Installation & Sensor Alignment", description: "Certified clean-room installation, gasket replacement & TrueTone calibration", quantity: 1, unit_price: 6500 },
346
- { title: "Hydrophobic Dust & Water Ingress Seal Restoration", description: "OEM adhesive barrier pressure cured to IP68 rating", quantity: 1, unit_price: 2500 }
347
- ],
348
- custom_fields: {
349
- "Device Model": "Apple iPad Pro 12.9-inch (6th Gen - Wi-Fi + Cellular)",
350
- "Serial / IMEI": "DLXW9201M93K",
351
- "Ticket Reference": "RT-2024-4190",
352
- "Estimator / Technician": "Bonifencia A.",
353
- "Validity Period": "14 Calendar Days",
354
- "Estimated Turnaround": "24 - 48 Hours"
355
- },
356
- notes: "All replaced hardware carries our standard 90-day warranty coverage. Payment accepted via M-Pesa, Bank Transfer, or Credit/Debit Card."
357
- },
358
333
  {
359
334
  id: "trade_in",
360
335
  name: "Device Trade-In Agreement",
@@ -378,27 +353,358 @@ The seller warrants that they are the sole lawful owner of the equipment, that i
378
353
  "Evaluation Specialist": "Bonifencia A."
379
354
  },
380
355
  notes: "Ownership transfers immediately upon document execution. Valuation is binding and irrevocable once trade-in payout or store credit is issued."
381
- },
382
- {
383
- id: "blank",
384
- name: "Blank Custom Document",
385
- icon: "📝",
386
- badge: "Custom",
387
- title: "Official Document",
388
- subject: "Official Document from Urban Device Care",
389
- body_text: "This official technical document confirms the specifications, service terms, and verified conditions documented by Urban Device Care for the specified customer asset.",
390
- items: [
391
- { title: "Professional Hardware Inspection & Maintenance Service", description: "Certified technical evaluation and service clearance", quantity: 1, unit_price: 0 }
392
- ],
393
- custom_fields: {
394
- "Device Model": "Computing Asset / Hardware Module",
395
- "Serial / IMEI": "SN-XXXX-XXXX",
396
- "Ticket Reference": "RT-2024-0001",
397
- "Technician": "Bonifencia A."
398
- },
399
- notes: "Standard terms and conditions apply. For inquiries, contact info@urbandevicecare.co.uk."
400
356
  }
401
357
  ];
358
+ const CustomerAutofillCombobox = ({
359
+ candidates,
360
+ loading = false,
361
+ onSelectCustomer,
362
+ label = "Quick Autofill Customer Details"
363
+ }) => {
364
+ const [query, setQuery] = react.useState("");
365
+ const [isOpen, setIsOpen] = react.useState(false);
366
+ const [autofilledNotice, setAutofilledNotice] = react.useState(null);
367
+ const [remoteCandidates, setRemoteCandidates] = react.useState([]);
368
+ const [isSearchingRemote, setIsSearchingRemote] = react.useState(false);
369
+ const containerRef = react.useRef(null);
370
+ react.useEffect(() => {
371
+ const handleClickOutside = (e) => {
372
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
373
+ setIsOpen(false);
374
+ }
375
+ };
376
+ document.addEventListener("mousedown", handleClickOutside);
377
+ return () => document.removeEventListener("mousedown", handleClickOutside);
378
+ }, []);
379
+ react.useEffect(() => {
380
+ const q = query.trim();
381
+ if (q.length < 2) {
382
+ setRemoteCandidates([]);
383
+ setIsSearchingRemote(false);
384
+ return;
385
+ }
386
+ const timer = setTimeout(async () => {
387
+ setIsSearchingRemote(true);
388
+ try {
389
+ const res = await fetch(`/admin/customers?q=${encodeURIComponent(q)}&fields=*addresses&limit=25`, {
390
+ credentials: "include"
391
+ });
392
+ if (res.ok) {
393
+ const data = await res.json().catch(() => ({}));
394
+ if (data.customers && Array.isArray(data.customers)) {
395
+ const mapped = data.customers.map((c) => {
396
+ var _a, _b, _c;
397
+ const fullName = [c.first_name, c.last_name].filter(Boolean).join(" ").trim();
398
+ const addr = ((_a = c.addresses) == null ? void 0 : _a[0]) || c.default_billing_address || c.billing_address;
399
+ const formattedAddr = addr ? [addr.address_1, addr.address_2, addr.city, addr.postal_code, addr.province, (_b = addr.country_code) == null ? void 0 : _b.toUpperCase()].filter(Boolean).join(", ") : "";
400
+ const company = c.company_name || (addr == null ? void 0 : addr.company) || ((_c = c.metadata) == null ? void 0 : _c.company_name) || "";
401
+ const name = fullName || company || c.email || "Customer";
402
+ return {
403
+ id: `remote-${c.id}`,
404
+ name,
405
+ company_name: company,
406
+ email: c.email || "",
407
+ phone: c.phone || (addr == null ? void 0 : addr.phone) || "",
408
+ address: formattedAddr,
409
+ source: "customer",
410
+ source_label: "Medusa Customer (Live)"
411
+ };
412
+ });
413
+ setRemoteCandidates(mapped);
414
+ }
415
+ }
416
+ } catch (e) {
417
+ } finally {
418
+ setIsSearchingRemote(false);
419
+ }
420
+ }, 250);
421
+ return () => clearTimeout(timer);
422
+ }, [query]);
423
+ const allCandidates = react.useMemo(() => {
424
+ var _a, _b, _c, _d;
425
+ if (remoteCandidates.length === 0) return candidates;
426
+ const map = /* @__PURE__ */ new Map();
427
+ for (const c of candidates) {
428
+ const key = ((_a = c.email) == null ? void 0 : _a.toLowerCase()) || ((_b = c.phone) == null ? void 0 : _b.replace(/\s+/g, "")) || `${c.name.toLowerCase()}::${c.company_name.toLowerCase()}`;
429
+ if (key) map.set(key, c);
430
+ }
431
+ for (const c of remoteCandidates) {
432
+ const key = ((_c = c.email) == null ? void 0 : _c.toLowerCase()) || ((_d = c.phone) == null ? void 0 : _d.replace(/\s+/g, "")) || `${c.name.toLowerCase()}::${c.company_name.toLowerCase()}`;
433
+ if (key) {
434
+ if (!map.has(key)) {
435
+ map.set(key, c);
436
+ } else {
437
+ const existing = map.get(key);
438
+ if (!existing.address && c.address) existing.address = c.address;
439
+ if (!existing.company_name && c.company_name) existing.company_name = c.company_name;
440
+ }
441
+ }
442
+ }
443
+ return Array.from(map.values());
444
+ }, [candidates, remoteCandidates]);
445
+ const filteredCandidates = react.useMemo(() => {
446
+ const q = query.trim().toLowerCase();
447
+ if (!q) {
448
+ return allCandidates.slice(0, 10);
449
+ }
450
+ const cleanQ = q.replace(/\s+/g, "");
451
+ const digitsOnlyQ = cleanQ.replace(/\D/g, "");
452
+ return allCandidates.filter((c) => {
453
+ const name = c.name.toLowerCase();
454
+ const company = c.company_name.toLowerCase();
455
+ const email = c.email.toLowerCase();
456
+ const phone = c.phone.replace(/\s+/g, "").toLowerCase();
457
+ const digitsOnlyPhone = phone.replace(/\D/g, "");
458
+ const addr = c.address.toLowerCase();
459
+ const src = c.source_label.toLowerCase();
460
+ const phoneMatches = phone.includes(cleanQ) || digitsOnlyQ.length >= 4 && (digitsOnlyPhone.includes(digitsOnlyQ) || digitsOnlyQ.includes(digitsOnlyPhone));
461
+ return name.includes(q) || company.includes(q) || email.includes(q) || phoneMatches || addr.includes(q) || src.includes(q);
462
+ }).slice(0, 20);
463
+ }, [allCandidates, query]);
464
+ const handleSelect = (candidate) => {
465
+ onSelectCustomer(candidate);
466
+ const displayName = candidate.company_name ? `${candidate.name} (${candidate.company_name})` : candidate.name;
467
+ setAutofilledNotice(`✓ Autofilled: ${displayName}`);
468
+ setQuery("");
469
+ setIsOpen(false);
470
+ setTimeout(() => {
471
+ setAutofilledNotice(null);
472
+ }, 4e3);
473
+ };
474
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: "relative flex flex-col gap-1.5 p-3 rounded-lg bg-ui-bg-subtle border border-ui-border-base", children: [
475
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
476
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { className: "text-xs font-semibold flex items-center gap-1.5 text-ui-fg-base", children: [
477
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Sparkles, { className: "w-3.5 h-3.5 text-ui-fg-interactive" }),
478
+ label
479
+ ] }),
480
+ autofilledNotice && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-emerald-600 bg-emerald-50 dark:bg-emerald-950/40 px-2 py-0.5 rounded border border-emerald-200", children: autofilledNotice })
481
+ ] }),
482
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-[11px] text-ui-fg-subtle", children: "Search existing customer records, repair tickets, or recent documents in Medusa to automatically populate customer fields." }),
483
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
484
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none text-ui-fg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(icons.MagnifyingGlass, { className: "w-4 h-4" }) }),
485
+ /* @__PURE__ */ jsxRuntime.jsx(
486
+ "input",
487
+ {
488
+ type: "text",
489
+ value: query,
490
+ onChange: (e) => {
491
+ setQuery(e.target.value);
492
+ setIsOpen(true);
493
+ },
494
+ onFocus: (e) => {
495
+ setIsOpen(true);
496
+ e.target.select();
497
+ },
498
+ placeholder: "Type name, company, email, phone, or ticket # to search & autofill...",
499
+ className: "w-full pl-8.5 pr-8 py-1.5 text-xs bg-ui-bg-base border border-ui-border-base rounded-md text-ui-fg-base placeholder:text-ui-fg-muted focus:outline-none focus:ring-1 focus:ring-ui-border-interactive focus:border-ui-border-interactive"
500
+ }
501
+ ),
502
+ query && /* @__PURE__ */ jsxRuntime.jsx(
503
+ "button",
504
+ {
505
+ type: "button",
506
+ onClick: () => setQuery(""),
507
+ className: "absolute inset-y-0 right-0 pr-2.5 flex items-center text-ui-fg-muted hover:text-ui-fg-base",
508
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, { className: "w-3.5 h-3.5" })
509
+ }
510
+ )
511
+ ] }),
512
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-0 right-0 mt-1 z-50 bg-ui-bg-base border border-ui-border-base rounded-md shadow-lg max-h-64 overflow-y-auto divide-y divide-ui-border-base", children: loading || isSearchingRemote ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 text-xs text-ui-fg-muted text-center flex items-center justify-center gap-2", children: [
513
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "w-3 h-3 animate-spin" }),
514
+ "Searching customer records..."
515
+ ] }) : filteredCandidates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 text-xs text-ui-fg-muted text-center", children: "No matching customer records found" }) : filteredCandidates.map((c) => {
516
+ const badgeColor = c.source === "customer" ? "blue" : c.source === "repair" ? "orange" : "green";
517
+ return /* @__PURE__ */ jsxRuntime.jsxs(
518
+ "div",
519
+ {
520
+ onClick: () => handleSelect(c),
521
+ className: "p-2.5 hover:bg-ui-bg-subtle-hover cursor-pointer transition-colors",
522
+ children: [
523
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 mb-1", children: [
524
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
525
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-xs text-ui-fg-base truncate", children: c.name }),
526
+ c.company_name && c.company_name !== c.name && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-ui-fg-muted truncate", children: [
527
+ "(",
528
+ c.company_name,
529
+ ")"
530
+ ] })
531
+ ] }),
532
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "small", color: badgeColor, className: "shrink-0 text-[10px]", children: c.source_label })
533
+ ] }),
534
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-[11px] text-ui-fg-subtle truncate", children: [
535
+ c.email && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
536
+ "📧 ",
537
+ c.email
538
+ ] }),
539
+ c.phone && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
540
+ "📞 ",
541
+ c.phone
542
+ ] }),
543
+ c.address && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate max-w-[200px]", children: [
544
+ "📍 ",
545
+ c.address
546
+ ] })
547
+ ] })
548
+ ]
549
+ },
550
+ c.id
551
+ );
552
+ }) })
553
+ ] });
554
+ };
555
+ const RepairTicketCombobox = ({
556
+ tickets,
557
+ selectedTicketId,
558
+ onSelectTicket,
559
+ onClear,
560
+ loading = false
561
+ }) => {
562
+ const [query, setQuery] = react.useState("");
563
+ const [isOpen, setIsOpen] = react.useState(false);
564
+ const containerRef = react.useRef(null);
565
+ const getTicketLabel = (t) => {
566
+ var _a, _b, _c, _d, _e;
567
+ const cleanNum = String(t.ticket_number || t.id).replace(/^(RT-|JOB-|CERT-)+/i, "");
568
+ const displayRef = `RT-${cleanNum}`;
569
+ const custName = ((_a = t.customer) == null ? void 0 : _a.company_name) || t.company_name || t.customer_name || (((_b = t.customer) == null ? void 0 : _b.first_name) ? `${t.customer.first_name} ${t.customer.last_name || ""}`.trim() : "") || "Customer";
570
+ const brandModel = [(_c = t.device) == null ? void 0 : _c.brand, (_d = t.device) == null ? void 0 : _d.model_name].filter(Boolean).join(" ").trim() || ((_e = t.custom_fields) == null ? void 0 : _e["Device Model"]) || "Device";
571
+ return `${displayRef} — ${custName} (${brandModel})`;
572
+ };
573
+ react.useEffect(() => {
574
+ if (!selectedTicketId) {
575
+ setQuery("");
576
+ return;
577
+ }
578
+ const t = tickets.find((item) => item.id === selectedTicketId || item.ticket_number === selectedTicketId);
579
+ if (t) {
580
+ setQuery(getTicketLabel(t));
581
+ }
582
+ }, [selectedTicketId, tickets]);
583
+ react.useEffect(() => {
584
+ const handleClickOutside = (e) => {
585
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
586
+ setIsOpen(false);
587
+ }
588
+ };
589
+ document.addEventListener("mousedown", handleClickOutside);
590
+ return () => document.removeEventListener("mousedown", handleClickOutside);
591
+ }, []);
592
+ const filteredTickets = react.useMemo(() => {
593
+ const q = query.trim().toLowerCase();
594
+ const cleanQ = q.replace(/^(?:(?:rt|job|cert)[-–—\s#]*|[#])/i, "").trim().toLowerCase();
595
+ const digitsOnlyQ = cleanQ.replace(/\D/g, "");
596
+ const selectedTicket = tickets.find((item) => item.id === selectedTicketId || item.ticket_number === selectedTicketId);
597
+ const selectedLabel = selectedTicket ? getTicketLabel(selectedTicket).toLowerCase() : "";
598
+ const isUnmodifiedSelection = Boolean(selectedTicket && q === selectedLabel);
599
+ if (!q || isUnmodifiedSelection) {
600
+ return tickets.slice(0, 30);
601
+ }
602
+ return tickets.filter((t) => {
603
+ var _a, _b, _c, _d, _e, _f, _g, _h;
604
+ const cleanNum = String(t.ticket_number || t.id).replace(/^(RT-|JOB-|CERT-)+/i, "").toLowerCase();
605
+ const rawNum = String(t.ticket_number || "").toLowerCase();
606
+ const rawId = String(t.id || "").toLowerCase();
607
+ const displayRef = `rt-${cleanNum}`;
608
+ const custName = (((_a = t.customer) == null ? void 0 : _a.name) || t.customer_name || (((_b = t.customer) == null ? void 0 : _b.first_name) ? `${t.customer.first_name} ${t.customer.last_name || ""}`.trim() : "")).toLowerCase();
609
+ const compName = (((_c = t.customer) == null ? void 0 : _c.company_name) || t.company_name || "").toLowerCase();
610
+ const email = (((_d = t.customer) == null ? void 0 : _d.email) || t.customer_email || "").toLowerCase();
611
+ const phone = (((_e = t.customer) == null ? void 0 : _e.phone) || t.customer_phone || "").replace(/\s+/g, "").toLowerCase();
612
+ const digitsOnlyPhone = phone.replace(/\D/g, "");
613
+ const brandModel = ([(_f = t.device) == null ? void 0 : _f.brand, (_g = t.device) == null ? void 0 : _g.model_name].filter(Boolean).join(" ") || ((_h = t.custom_fields) == null ? void 0 : _h["Device Model"]) || "").toLowerCase();
614
+ const phoneMatches = phone.includes(q.replace(/\s+/g, "")) || digitsOnlyQ.length >= 4 && (digitsOnlyPhone.includes(digitsOnlyQ) || digitsOnlyQ.includes(digitsOnlyPhone));
615
+ return cleanNum.includes(q) || cleanQ && cleanNum.includes(cleanQ) || displayRef.includes(q) || rawNum.includes(q) || cleanQ && rawNum.includes(cleanQ) || rawId.includes(q) || custName.includes(q) || compName.includes(q) || email.includes(q) || phoneMatches || brandModel.includes(q);
616
+ }).slice(0, 30);
617
+ }, [tickets, query, selectedTicketId]);
618
+ const handleSelect = (ticket) => {
619
+ onSelectTicket(ticket.id);
620
+ setQuery(getTicketLabel(ticket));
621
+ setIsOpen(false);
622
+ };
623
+ const handleClear = () => {
624
+ onClear();
625
+ setQuery("");
626
+ };
627
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: "relative w-full", children: [
628
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
629
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none text-ui-fg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(icons.MagnifyingGlass, { className: "w-4 h-4" }) }),
630
+ /* @__PURE__ */ jsxRuntime.jsx(
631
+ "input",
632
+ {
633
+ type: "text",
634
+ value: query,
635
+ onChange: (e) => {
636
+ setQuery(e.target.value);
637
+ setIsOpen(true);
638
+ },
639
+ onFocus: (e) => {
640
+ setIsOpen(true);
641
+ e.target.select();
642
+ },
643
+ placeholder: "Search by customer name, company, or ticket number (e.g. 5390 or RT-5390)...",
644
+ className: "w-full pl-8.5 pr-8 py-1.5 text-xs bg-ui-bg-base border border-ui-border-base rounded-md text-ui-fg-base placeholder:text-ui-fg-muted focus:outline-none focus:ring-1 focus:ring-ui-border-interactive focus:border-ui-border-interactive"
645
+ }
646
+ ),
647
+ (query || selectedTicketId) && /* @__PURE__ */ jsxRuntime.jsx(
648
+ "button",
649
+ {
650
+ type: "button",
651
+ onClick: handleClear,
652
+ className: "absolute inset-y-0 right-0 pr-2.5 flex items-center text-ui-fg-muted hover:text-ui-fg-base",
653
+ title: "Clear ticket link",
654
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, { className: "w-3.5 h-3.5" })
655
+ }
656
+ )
657
+ ] }),
658
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-0 right-0 mt-1 z-50 bg-ui-bg-base border border-ui-border-base rounded-md shadow-lg max-h-64 overflow-y-auto divide-y divide-ui-border-base", children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 text-xs text-ui-fg-muted text-center flex items-center justify-center gap-2", children: [
659
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "w-3 h-3 animate-spin" }),
660
+ "Loading repair tickets..."
661
+ ] }) : filteredTickets.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 text-xs text-ui-fg-muted text-center", children: [
662
+ 'No repair tickets found matching "',
663
+ query,
664
+ '"'
665
+ ] }) : filteredTickets.map((t) => {
666
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
667
+ const cleanNum = String(t.ticket_number || t.id).replace(/^(RT-|JOB-|CERT-)+/i, "");
668
+ const displayRef = `RT-${cleanNum}`;
669
+ const custName = ((_a = t.customer) == null ? void 0 : _a.company_name) || t.company_name || t.customer_name || (((_b = t.customer) == null ? void 0 : _b.first_name) ? `${t.customer.first_name} ${t.customer.last_name || ""}`.trim() : "") || "Customer";
670
+ const compName = ((_c = t.customer) == null ? void 0 : _c.company_name) || t.company_name;
671
+ const brandModel = [(_d = t.device) == null ? void 0 : _d.brand, (_e = t.device) == null ? void 0 : _e.model_name].filter(Boolean).join(" ").trim() || ((_f = t.custom_fields) == null ? void 0 : _f["Device Model"]) || "Device";
672
+ const isSelected = selectedTicketId === t.id;
673
+ return /* @__PURE__ */ jsxRuntime.jsxs(
674
+ "div",
675
+ {
676
+ onClick: () => handleSelect(t),
677
+ className: `p-2.5 hover:bg-ui-bg-subtle-hover cursor-pointer transition-colors ${isSelected ? "bg-ui-bg-interactive-subtle" : ""}`,
678
+ children: [
679
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 mb-1", children: [
680
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
681
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "small", color: isSelected ? "blue" : "grey", className: "shrink-0 font-mono", children: displayRef }),
682
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-xs text-ui-fg-base truncate", children: custName }),
683
+ compName && compName !== custName && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-ui-fg-muted truncate", children: [
684
+ "(",
685
+ compName,
686
+ ")"
687
+ ] })
688
+ ] }),
689
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-ui-fg-subtle truncate shrink-0 font-medium", children: brandModel })
690
+ ] }),
691
+ (((_g = t.customer) == null ? void 0 : _g.email) || t.customer_email || ((_h = t.customer) == null ? void 0 : _h.phone) || t.customer_phone) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-[11px] text-ui-fg-muted truncate", children: [
692
+ (((_i = t.customer) == null ? void 0 : _i.email) || t.customer_email) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
693
+ "📧 ",
694
+ ((_j = t.customer) == null ? void 0 : _j.email) || t.customer_email
695
+ ] }),
696
+ (((_k = t.customer) == null ? void 0 : _k.phone) || t.customer_phone) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
697
+ "📞 ",
698
+ ((_l = t.customer) == null ? void 0 : _l.phone) || t.customer_phone
699
+ ] })
700
+ ] })
701
+ ]
702
+ },
703
+ t.id
704
+ );
705
+ }) })
706
+ ] });
707
+ };
402
708
  const DocumentsPage = () => {
403
709
  var _a, _b;
404
710
  const [activeTab, setActiveTab] = react.useState("hub");
@@ -512,7 +818,7 @@ const DocumentsPage = () => {
512
818
  items: [{ title: "", description: "", quantity: 1, unit_price: 0 }],
513
819
  currency_code: "kes",
514
820
  notes: "",
515
- force_source: "auto",
821
+ force_source: "zoho",
516
822
  auto_send: false
517
823
  });
518
824
  const [customDocForm, setCustomDocForm] = react.useState({
@@ -522,6 +828,7 @@ const DocumentsPage = () => {
522
828
  customer_name: "Jane Doe",
523
829
  customer_email: "jane.doe@example.com",
524
830
  customer_phone: "+254 712 345 678",
831
+ customer_address: "",
525
832
  body_text: CUSTOM_DOC_PRESETS[0].body_text,
526
833
  items: CUSTOM_DOC_PRESETS[0].items.map((it) => ({ ...it })),
527
834
  custom_fields: { ...CUSTOM_DOC_PRESETS[0].custom_fields || {} },
@@ -535,6 +842,8 @@ const DocumentsPage = () => {
535
842
  const [selectedTicketId, setSelectedTicketId] = react.useState("");
536
843
  const [newCustomFieldKey, setNewCustomFieldKey] = react.useState("");
537
844
  const [newCustomFieldValue, setNewCustomFieldValue] = react.useState("");
845
+ const [customersList, setCustomersList] = react.useState([]);
846
+ const [loadingCustomers, setLoadingCustomers] = react.useState(false);
538
847
  const loadRepairTickets = async () => {
539
848
  setLoadingRepairs(true);
540
849
  try {
@@ -552,16 +861,60 @@ const DocumentsPage = () => {
552
861
  setLoadingRepairs(false);
553
862
  }
554
863
  };
864
+ const loadCustomers = async () => {
865
+ setLoadingCustomers(true);
866
+ try {
867
+ const res = await fetch("/admin/customers?limit=100&fields=*addresses", { credentials: "include" });
868
+ if (res.ok) {
869
+ const data = await res.json().catch(() => ({}));
870
+ if (data.customers && Array.isArray(data.customers)) {
871
+ setCustomersList(data.customers);
872
+ return;
873
+ }
874
+ }
875
+ } catch (e) {
876
+ console.warn("Could not fetch /admin/customers:", e);
877
+ } finally {
878
+ setLoadingCustomers(false);
879
+ }
880
+ };
555
881
  react.useEffect(() => {
556
- if (customDocModalOpen) {
882
+ if (customDocModalOpen || manualModalOpen) {
557
883
  loadRepairTickets();
884
+ loadCustomers();
558
885
  }
559
- }, [customDocModalOpen]);
886
+ }, [customDocModalOpen, manualModalOpen]);
560
887
  const availableTickets = react.useMemo(() => {
561
- var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
888
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
562
889
  const map = /* @__PURE__ */ new Map();
563
890
  for (const t of repairTickets) {
564
- if (t.id) map.set(t.id, t);
891
+ if (t.id) {
892
+ const linkedCust = t.customer_id ? customersList.find((c) => c.id === t.customer_id) : null;
893
+ const custName = ((_a2 = t.customer) == null ? void 0 : _a2.name) || (((_b2 = t.customer) == null ? void 0 : _b2.first_name) ? `${t.customer.first_name} ${t.customer.last_name || ""}`.trim() : "") || t.customer_name || ((_c = t.metadata) == null ? void 0 : _c.customer_name) || (linkedCust ? [linkedCust.first_name, linkedCust.last_name].filter(Boolean).join(" ").trim() : "") || "";
894
+ const compName = ((_d = t.customer) == null ? void 0 : _d.company_name) || t.company_name || ((_e = t.metadata) == null ? void 0 : _e.company_name) || (linkedCust == null ? void 0 : linkedCust.company_name) || "";
895
+ const email = ((_f = t.customer) == null ? void 0 : _f.email) || t.customer_email || ((_g = t.metadata) == null ? void 0 : _g.customer_email) || (linkedCust == null ? void 0 : linkedCust.email) || "";
896
+ const phone = ((_h = t.customer) == null ? void 0 : _h.phone) || t.customer_phone || ((_i = t.metadata) == null ? void 0 : _i.customer_phone) || (linkedCust == null ? void 0 : linkedCust.phone) || "";
897
+ const linkedAddr = (_j = linkedCust == null ? void 0 : linkedCust.addresses) == null ? void 0 : _j[0];
898
+ const formattedCustAddr = linkedAddr ? [linkedAddr.address_1, linkedAddr.address_2, linkedAddr.city, linkedAddr.postal_code, linkedAddr.province, (_k = linkedAddr.country_code) == null ? void 0 : _k.toUpperCase()].filter(Boolean).join(", ") : "";
899
+ const address = ((_l = t.customer) == null ? void 0 : _l.address) || t.customer_address || ((_m = t.metadata) == null ? void 0 : _m.address) || ((_n = t.custom_fields) == null ? void 0 : _n["Address"]) || formattedCustAddr || "";
900
+ map.set(t.id, {
901
+ ...t,
902
+ customer_name: custName || t.customer_name,
903
+ company_name: compName || t.company_name,
904
+ customer_email: email || t.customer_email,
905
+ customer_phone: phone || t.customer_phone,
906
+ customer_address: address || t.customer_address,
907
+ customer: {
908
+ name: custName,
909
+ first_name: (linkedCust == null ? void 0 : linkedCust.first_name) || ((_o = t.customer) == null ? void 0 : _o.first_name),
910
+ last_name: (linkedCust == null ? void 0 : linkedCust.last_name) || ((_p = t.customer) == null ? void 0 : _p.last_name),
911
+ company_name: compName,
912
+ email,
913
+ phone,
914
+ address
915
+ }
916
+ });
917
+ }
565
918
  }
566
919
  for (const d of documents) {
567
920
  const ticketId = d.repair_ticket_id;
@@ -570,14 +923,14 @@ const DocumentsPage = () => {
570
923
  const idKey = ticketId || displayId;
571
924
  if (isRepairDoc && idKey) {
572
925
  if (!map.has(idKey)) {
573
- const customFields = d.custom_fields || ((_a2 = d.metadata) == null ? void 0 : _a2.custom_fields) || {};
926
+ const customFields = d.custom_fields || ((_q = d.metadata) == null ? void 0 : _q.custom_fields) || {};
574
927
  map.set(idKey, {
575
928
  id: idKey,
576
929
  ticket_number: String(displayId).replace(/^(RT-|JOB-|CERT-)+/i, "") || idKey,
577
930
  customer_name: d.customer_name,
578
931
  customer_email: d.customer_email,
579
932
  customer_phone: d.customer_phone,
580
- company_name: ((_b2 = d.customer) == null ? void 0 : _b2.company_name) || ((_c = d.metadata) == null ? void 0 : _c.company_name),
933
+ company_name: ((_r = d.customer) == null ? void 0 : _r.company_name) || ((_s = d.metadata) == null ? void 0 : _s.company_name),
581
934
  device: {
582
935
  brand: customFields["Brand"] || "",
583
936
  model_name: customFields["Device Model"] || customFields["Model"] || "Device",
@@ -588,16 +941,16 @@ const DocumentsPage = () => {
588
941
  technician_name: customFields["Technician"] || "Bonifencia A.",
589
942
  issue_description: customFields["Reported Issue"] || d.notes || "Standard Service",
590
943
  custom_fields: customFields,
591
- items: ((_d = d.metadata) == null ? void 0 : _d.items) || []
944
+ items: ((_t = d.metadata) == null ? void 0 : _t.items) || []
592
945
  });
593
946
  } else {
594
947
  const existing = map.get(idKey);
595
948
  if (!existing.customer_name && d.customer_name) existing.customer_name = d.customer_name;
596
949
  if (!existing.customer_email && d.customer_email) existing.customer_email = d.customer_email;
597
950
  if (!existing.customer_phone && d.customer_phone) existing.customer_phone = d.customer_phone;
598
- const compName = ((_e = d.customer) == null ? void 0 : _e.company_name) || ((_f = d.metadata) == null ? void 0 : _f.company_name);
951
+ const compName = ((_u = d.customer) == null ? void 0 : _u.company_name) || ((_v = d.metadata) == null ? void 0 : _v.company_name);
599
952
  if (!existing.company_name && compName) existing.company_name = compName;
600
- if ((!((_g = existing.device) == null ? void 0 : _g.model_name) || ((_h = existing.device) == null ? void 0 : _h.model_name) === "Device") && ((_j = (_i = d.metadata) == null ? void 0 : _i.custom_fields) == null ? void 0 : _j["Device Model"])) {
953
+ if ((!((_w = existing.device) == null ? void 0 : _w.model_name) || ((_x = existing.device) == null ? void 0 : _x.model_name) === "Device") && ((_z = (_y = d.metadata) == null ? void 0 : _y.custom_fields) == null ? void 0 : _z["Device Model"])) {
601
954
  existing.device = existing.device || {};
602
955
  existing.device.model_name = d.metadata.custom_fields["Device Model"];
603
956
  }
@@ -605,9 +958,87 @@ const DocumentsPage = () => {
605
958
  }
606
959
  }
607
960
  return Array.from(map.values());
608
- }, [repairTickets, documents]);
961
+ }, [repairTickets, documents, customersList]);
962
+ const customerAutofillCandidates = react.useMemo(() => {
963
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
964
+ const map = /* @__PURE__ */ new Map();
965
+ const addCandidate = (c) => {
966
+ var _a3, _b3;
967
+ const key = ((_a3 = c.email) == null ? void 0 : _a3.trim().toLowerCase()) || ((_b3 = c.phone) == null ? void 0 : _b3.trim().replace(/\s+/g, "")) || `${c.name.trim().toLowerCase()}::${c.company_name.trim().toLowerCase()}`;
968
+ if (!key || key === "::" || key === "customer::") return;
969
+ if (!map.has(key)) {
970
+ map.set(key, c);
971
+ } else {
972
+ const existing = map.get(key);
973
+ if (!existing.name || existing.name === "Customer") existing.name = c.name;
974
+ if (!existing.company_name && c.company_name) existing.company_name = c.company_name;
975
+ if (!existing.email && c.email) existing.email = c.email;
976
+ if (!existing.phone && c.phone) existing.phone = c.phone;
977
+ if (!existing.address && c.address) existing.address = c.address;
978
+ }
979
+ };
980
+ for (const c of customersList) {
981
+ const fullName = [c.first_name, c.last_name].filter(Boolean).join(" ").trim();
982
+ const addr = ((_a2 = c.addresses) == null ? void 0 : _a2[0]) || c.default_billing_address || c.billing_address;
983
+ const formattedAddr = addr ? [addr.address_1, addr.address_2, addr.city, addr.postal_code, addr.province, (_b2 = addr.country_code) == null ? void 0 : _b2.toUpperCase()].filter(Boolean).join(", ") : "";
984
+ const company = c.company_name || (addr == null ? void 0 : addr.company) || ((_c = c.metadata) == null ? void 0 : _c.company_name) || "";
985
+ const name = fullName || company || c.email || "Customer";
986
+ addCandidate({
987
+ id: c.id || `cust-${Math.random()}`,
988
+ name,
989
+ company_name: company,
990
+ email: c.email || "",
991
+ phone: c.phone || (addr == null ? void 0 : addr.phone) || "",
992
+ address: formattedAddr,
993
+ source: "customer",
994
+ source_label: "Medusa Customer"
995
+ });
996
+ }
997
+ for (const t of availableTickets) {
998
+ const fullName = (((_d = t.customer) == null ? void 0 : _d.first_name) ? `${t.customer.first_name} ${t.customer.last_name || ""}`.trim() : "") || ((_e = t.customer) == null ? void 0 : _e.name) || t.customer_name || "";
999
+ const company = ((_f = t.customer) == null ? void 0 : _f.company_name) || t.company_name || ((_g = t.metadata) == null ? void 0 : _g.company_name) || "";
1000
+ const email = ((_h = t.customer) == null ? void 0 : _h.email) || t.customer_email || ((_i = t.metadata) == null ? void 0 : _i.customer_email) || "";
1001
+ const phone = ((_j = t.customer) == null ? void 0 : _j.phone) || t.customer_phone || ((_k = t.metadata) == null ? void 0 : _k.customer_phone) || "";
1002
+ const address = ((_l = t.customer) == null ? void 0 : _l.address) || t.customer_address || ((_m = t.metadata) == null ? void 0 : _m.address) || ((_n = t.custom_fields) == null ? void 0 : _n["Address"]) || "";
1003
+ const cleanNum = String(t.ticket_number || t.id).replace(/^(RT-|JOB-|CERT-)+/i, "");
1004
+ const name = fullName || company || "Customer";
1005
+ {
1006
+ addCandidate({
1007
+ id: `ticket-${t.id || cleanNum}`,
1008
+ name,
1009
+ company_name: company,
1010
+ email,
1011
+ phone,
1012
+ address,
1013
+ source: "repair",
1014
+ source_label: `Ticket RT-${cleanNum}`
1015
+ });
1016
+ }
1017
+ }
1018
+ for (const d of documents) {
1019
+ const name = d.customer_name || "";
1020
+ const company = ((_o = d.customer) == null ? void 0 : _o.company_name) || ((_p = d.metadata) == null ? void 0 : _p.company_name) || d.company_name || "";
1021
+ const email = d.customer_email || ((_q = d.customer) == null ? void 0 : _q.email) || "";
1022
+ const phone = d.customer_phone || ((_r = d.customer) == null ? void 0 : _r.phone) || "";
1023
+ const address = ((_s = d.customer) == null ? void 0 : _s.address) || d.customer_address || ((_t = d.metadata) == null ? void 0 : _t.customer_address) || ((_v = (_u = d.metadata) == null ? void 0 : _u.customer) == null ? void 0 : _v.address) || "";
1024
+ const docRef = d.display_id || ((_w = d.id) == null ? void 0 : _w.slice(0, 8));
1025
+ if (name || company || email || phone) {
1026
+ addCandidate({
1027
+ id: `doc-${d.id}`,
1028
+ name: name || company || "Customer",
1029
+ company_name: company,
1030
+ email,
1031
+ phone,
1032
+ address,
1033
+ source: "document",
1034
+ source_label: `Doc ${docRef}`
1035
+ });
1036
+ }
1037
+ }
1038
+ return Array.from(map.values());
1039
+ }, [customersList, availableTickets, documents]);
609
1040
  const handleSelectRepairTicket = (ticketId) => {
610
- var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
1041
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C;
611
1042
  setSelectedTicketId(ticketId);
612
1043
  if (!ticketId) return;
613
1044
  const ticket = availableTickets.find((t) => t.id === ticketId || t.ticket_number === ticketId);
@@ -675,12 +1106,14 @@ const DocumentsPage = () => {
675
1106
  });
676
1107
  }
677
1108
  }
1109
+ const customerAddress = ((_A = ticket.customer) == null ? void 0 : _A.address) || ticket.customer_address || ((_B = ticket.metadata) == null ? void 0 : _B.address) || ((_C = ticket.custom_fields) == null ? void 0 : _C["Address"]) || "";
678
1110
  setCustomDocForm((prev) => ({
679
1111
  ...prev,
680
1112
  customer_name: customerName,
681
1113
  customer_email: customerEmail,
682
1114
  customer_phone: customerPhone,
683
1115
  company_name: companyName,
1116
+ customer_address: customerAddress || prev.customer_address,
684
1117
  custom_fields: {
685
1118
  ...prev.custom_fields,
686
1119
  "Device Model": brandModel,
@@ -826,7 +1259,7 @@ const DocumentsPage = () => {
826
1259
  items: validItems,
827
1260
  currency_code: manualForm.currency_code,
828
1261
  notes: manualForm.notes,
829
- force_source: manualForm.force_source === "auto" ? void 0 : manualForm.force_source,
1262
+ force_source: "zoho",
830
1263
  auto_send: manualForm.auto_send
831
1264
  })
832
1265
  });
@@ -845,7 +1278,7 @@ const DocumentsPage = () => {
845
1278
  items: [{ title: "", description: "", quantity: 1, unit_price: 0 }],
846
1279
  currency_code: "kes",
847
1280
  notes: "",
848
- force_source: "auto",
1281
+ force_source: "zoho",
849
1282
  auto_send: false
850
1283
  });
851
1284
  loadDocuments();
@@ -881,7 +1314,8 @@ const DocumentsPage = () => {
881
1314
  name: customDocForm.customer_name,
882
1315
  company_name: customDocForm.company_name || void 0,
883
1316
  email: customDocForm.customer_email,
884
- phone: customDocForm.customer_phone
1317
+ phone: customDocForm.customer_phone,
1318
+ address: customDocForm.customer_address || void 0
885
1319
  },
886
1320
  body_text: customDocForm.body_text,
887
1321
  notes: customDocForm.notes,
@@ -899,6 +1333,7 @@ const DocumentsPage = () => {
899
1333
  customDocForm.send_email_now ? "Custom document generated & emailed successfully!" : "Custom document generated and saved to Documents Hub!"
900
1334
  );
901
1335
  setCustomDocModalOpen(false);
1336
+ setSelectedTicketId("");
902
1337
  loadDocuments();
903
1338
  } else {
904
1339
  alert(`Failed to generate custom document: ${data.message || res.statusText}`);
@@ -2363,52 +2798,6 @@ const DocumentsPage = () => {
2363
2798
  )
2364
2799
  ] })
2365
2800
  ] }),
2366
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 border border-ui-border-base rounded-lg bg-ui-bg-base flex flex-col justify-between shadow-sm hover:border-ui-border-interactive transition-colors", children: [
2367
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2368
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
2369
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2370
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xl", children: "💼" }),
2371
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "font-semibold text-sm", children: "Commercial Proforma Quote" })
2372
- ] }),
2373
- /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "orange", size: "small", children: "Cost Estimate" })
2374
- ] }),
2375
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-subtle mb-4", children: "Itemized corporate repair quotation with workshop turnaround estimate, OEM part warranties, and 14-day validity period." }),
2376
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
2377
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs font-medium", children: "Template Style" }),
2378
- /* @__PURE__ */ jsxRuntime.jsxs(
2379
- ui.Select,
2380
- {
2381
- value: settings.custom_doc_template || "modern",
2382
- onValueChange: (v) => updateSettingField("custom_doc_template", v),
2383
- children: [
2384
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, {}) }),
2385
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
2386
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "modern", children: "Modern Minimalist" }),
2387
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "classic", children: "Classic Corporate" }),
2388
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "minimal", children: "Clean Tech Monospace" })
2389
- ] })
2390
- ]
2391
- }
2392
- )
2393
- ] })
2394
- ] }),
2395
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5 pt-3 border-t border-ui-border-base flex items-center justify-between", children: [
2396
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-[11px] text-ui-fg-muted font-mono", children: "Preset: proforma" }),
2397
- /* @__PURE__ */ jsxRuntime.jsxs(
2398
- ui.Button,
2399
- {
2400
- variant: "secondary",
2401
- size: "small",
2402
- onClick: () => handlePreviewTemplate(settings.custom_doc_template || "modern", "custom", "proforma"),
2403
- isLoading: previewLoading === `${settings.custom_doc_template || "modern"}-custom-proforma`,
2404
- children: [
2405
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownTray, { className: "w-3.5 h-3.5 mr-1" }),
2406
- " Preview Proforma"
2407
- ]
2408
- }
2409
- )
2410
- ] })
2411
- ] }),
2412
2801
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 border border-ui-border-base rounded-lg bg-ui-bg-base flex flex-col justify-between shadow-sm hover:border-ui-border-interactive transition-colors", children: [
2413
2802
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2414
2803
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
@@ -3296,6 +3685,23 @@ Your *{document_title} #{display_id}* has been generated.
3296
3685
  }
3297
3686
  )
3298
3687
  ] }),
3688
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx(
3689
+ CustomerAutofillCombobox,
3690
+ {
3691
+ candidates: customerAutofillCandidates,
3692
+ loading: loadingCustomers || loadingRepairs,
3693
+ onSelectCustomer: (candidate) => {
3694
+ setManualForm((prev) => ({
3695
+ ...prev,
3696
+ customer_name: candidate.name,
3697
+ company_name: candidate.company_name,
3698
+ customer_email: candidate.email,
3699
+ customer_phone: candidate.phone,
3700
+ customer_address: candidate.address || prev.customer_address
3701
+ }));
3702
+ }
3703
+ }
3704
+ ) }),
3299
3705
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3300
3706
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Company Name (Optional)" }),
3301
3707
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3309,7 +3715,7 @@ Your *{document_title} #{display_id}* has been generated.
3309
3715
  )
3310
3716
  ] }),
3311
3717
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3312
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Customer Name" }),
3718
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Customer Name *" }),
3313
3719
  /* @__PURE__ */ jsxRuntime.jsx(
3314
3720
  ui.Input,
3315
3721
  {
@@ -3344,23 +3750,26 @@ Your *{document_title} #{display_id}* has been generated.
3344
3750
  }
3345
3751
  )
3346
3752
  ] }),
3347
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3348
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Generation Source" }),
3349
- /* @__PURE__ */ jsxRuntime.jsxs(
3350
- ui.Select,
3753
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-2", children: [
3754
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Customer Address (Optional)" }),
3755
+ /* @__PURE__ */ jsxRuntime.jsx(
3756
+ ui.Input,
3351
3757
  {
3352
- value: manualForm.force_source,
3353
- onValueChange: (v) => setManualForm({ ...manualForm, force_source: v }),
3354
- children: [
3355
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, {}) }),
3356
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
3357
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "auto", children: "Auto (Zoho if enabled, else PDFKit)" }),
3358
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "zoho", children: "Force Zoho Books" }),
3359
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "pdfkit", children: "Force Local PDFKit" })
3360
- ] })
3361
- ]
3758
+ value: manualForm.customer_address,
3759
+ onChange: (e) => setManualForm({ ...manualForm, customer_address: e.target.value }),
3760
+ placeholder: "Street Address, City, Postal Code",
3761
+ size: "small"
3362
3762
  }
3363
3763
  )
3764
+ ] }),
3765
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-2", children: [
3766
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Generation Source" }),
3767
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1 px-3 py-2 rounded-md border border-ui-border-base bg-ui-bg-subtle text-xs text-ui-fg-base", children: [
3768
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }),
3769
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-ui-fg-base", children: "Zoho Books (Cloud Invoicing)" }),
3770
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "small", color: "blue", className: "ml-auto font-mono", children: "Enforced" })
3771
+ ] }),
3772
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-[11px] text-ui-fg-subtle mt-1", children: "All manual invoices and quotes are generated and synced directly via Zoho Books. Local PDFKit fallback is disabled." })
3364
3773
  ] })
3365
3774
  ] }),
3366
3775
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
@@ -3596,29 +4005,14 @@ Your *{document_title} #{display_id}* has been generated.
3596
4005
  ] })
3597
4006
  ] }),
3598
4007
  /* @__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." }),
3599
- /* @__PURE__ */ jsxRuntime.jsxs(
3600
- ui.Select,
4008
+ /* @__PURE__ */ jsxRuntime.jsx(
4009
+ RepairTicketCombobox,
3601
4010
  {
3602
- value: selectedTicketId || void 0,
3603
- onValueChange: handleSelectRepairTicket,
3604
- children: [
3605
- /* @__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 --" }) }),
3606
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: availableTickets.map((t) => {
3607
- var _a2, _b2, _c, _d, _e;
3608
- 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";
3609
- const cleanNum = String(t.ticket_number || t.id).replace(/^(RT-|JOB-)+/i, "");
3610
- const displayRef = `RT-${cleanNum}`;
3611
- 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";
3612
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Item, { value: t.id, children: [
3613
- displayRef,
3614
- " — ",
3615
- custName,
3616
- " (",
3617
- brandModel,
3618
- ")"
3619
- ] }, t.id);
3620
- }) })
3621
- ]
4011
+ tickets: availableTickets,
4012
+ selectedTicketId,
4013
+ onSelectTicket: handleSelectRepairTicket,
4014
+ onClear: handleClearRepairLink,
4015
+ loading: loadingRepairs
3622
4016
  }
3623
4017
  ),
3624
4018
  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: [
@@ -3666,6 +4060,23 @@ Your *{document_title} #{display_id}* has been generated.
3666
4060
  }
3667
4061
  )
3668
4062
  ] }),
4063
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx(
4064
+ CustomerAutofillCombobox,
4065
+ {
4066
+ candidates: customerAutofillCandidates,
4067
+ loading: loadingCustomers || loadingRepairs,
4068
+ onSelectCustomer: (candidate) => {
4069
+ setCustomDocForm((prev) => ({
4070
+ ...prev,
4071
+ customer_name: candidate.name,
4072
+ company_name: candidate.company_name,
4073
+ customer_email: candidate.email,
4074
+ customer_phone: candidate.phone,
4075
+ customer_address: candidate.address || prev.customer_address
4076
+ }));
4077
+ }
4078
+ }
4079
+ ) }),
3669
4080
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3670
4081
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Company Name (Optional)" }),
3671
4082
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3713,6 +4124,18 @@ Your *{document_title} #{display_id}* has been generated.
3713
4124
  size: "small"
3714
4125
  }
3715
4126
  )
4127
+ ] }),
4128
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-2", children: [
4129
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Customer Address (Optional)" }),
4130
+ /* @__PURE__ */ jsxRuntime.jsx(
4131
+ ui.Input,
4132
+ {
4133
+ value: customDocForm.customer_address,
4134
+ onChange: (e) => setCustomDocForm({ ...customDocForm, customer_address: e.target.value }),
4135
+ placeholder: "Street Address, City, Postal Code",
4136
+ size: "small"
4137
+ }
4138
+ )
3716
4139
  ] })
3717
4140
  ] }),
3718
4141
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [