@infuro/cms-core 1.0.66 → 1.0.68
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.
- package/dist/admin.cjs +214 -32
- package/dist/admin.js +214 -32
- package/dist/api.cjs +45 -42
- package/dist/api.js +12 -9
- package/dist/auth.cjs +51 -51
- package/dist/auth.js +3 -3
- package/dist/{chunk-QYRXLURF.cjs → chunk-22XKONB5.cjs} +640 -145
- package/dist/{chunk-MVHDC3XF.js → chunk-5NOZSG6J.js} +22 -11
- package/dist/{chunk-TU23GJPJ.js → chunk-7JCCPQEF.js} +1 -1
- package/dist/{chunk-WMMZZRDJ.cjs → chunk-GN4LKG4V.cjs} +1 -1
- package/dist/{chunk-KM2I6AFP.cjs → chunk-HBUV73RB.cjs} +9 -9
- package/dist/chunk-IKTBZJHT.js +16 -0
- package/dist/chunk-M5GPOPGX.cjs +49 -0
- package/dist/{chunk-T33KU5G5.js → chunk-MYUGQHU4.js} +548 -53
- package/dist/{chunk-NV5IRHI3.js → chunk-N4LBZXBG.js} +2 -2
- package/dist/{chunk-J7FMZK66.cjs → chunk-O4AFPDQ4.cjs} +22 -11
- package/dist/{chunk-4U6DATGZ.cjs → chunk-OV5TRZET.cjs} +7 -7
- package/dist/chunk-P6S4XFZO.cjs +18 -0
- package/dist/{chunk-RCE5SJBA.js → chunk-SKJH7PX4.js} +1 -1
- package/dist/{chunk-5MZFQRDW.cjs → chunk-SMHQV3WL.cjs} +4 -4
- package/dist/chunk-SUUD7OUG.cjs +455 -0
- package/dist/{chunk-XFDIIVZB.cjs → chunk-UMC7W5G3.cjs} +3 -3
- package/dist/chunk-WKDBMDUX.js +45 -0
- package/dist/{chunk-6DBDJ4VD.js → chunk-Z5G54EGD.js} +2 -2
- package/dist/chunk-Z5OB5QGN.js +465 -0
- package/dist/chunk-ZIT4IP6N.js +445 -0
- package/dist/chunk-ZX5U3VCB.cjs +471 -0
- package/dist/{chunk-NR44CK5E.js → chunk-ZY64VJMS.js} +1 -1
- package/dist/email-queue-3QBQ63F7.js +8 -0
- package/dist/{email-queue-4OY45JIU.cjs → email-queue-K4E4OFI7.cjs} +9 -6
- package/dist/{emit-order-notification-trigger-ZD5YN3JM.cjs → emit-order-notification-trigger-ROASTYQP.cjs} +3 -3
- package/dist/{emit-order-notification-trigger-EJJZ6XTS.js → emit-order-notification-trigger-YUQTPELC.js} +1 -1
- package/dist/{erp-order-invoice-YUSPZ7CE.cjs → erp-order-invoice-AZ5MX2JB.cjs} +8 -5
- package/dist/{erp-order-invoice-H6HCJ7YS.js → erp-order-invoice-XSYHAMBS.js} +4 -1
- package/dist/generate-local-invoice-pdf-BQTCL5MW.cjs +12 -0
- package/dist/generate-local-invoice-pdf-OCFG47SE.js +3 -0
- package/dist/index.cjs +337 -330
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +18 -15
- package/dist/invoice-templates-GZ33OENC.js +2 -0
- package/dist/invoice-templates-RB462AQN.cjs +19 -0
- package/dist/migrations/1783300000000-CreateOrderAssigneesTable.ts +43 -0
- package/dist/{order-assignees-handlers-NPKD64P6.cjs → order-assignees-handlers-KF7RMTV5.cjs} +77 -8
- package/dist/{order-assignees-handlers-LKMAGLG6.js → order-assignees-handlers-L3RVKUBS.js} +77 -8
- package/dist/{order-completion-otp-handlers-NK65LOLV.cjs → order-completion-otp-handlers-DDQREULW.cjs} +75 -18
- package/dist/{order-completion-otp-handlers-LFRHXWK4.js → order-completion-otp-handlers-NROWBGXN.js} +73 -16
- package/dist/{order-notification-dispatcher-LL7ETKLU.cjs → order-notification-dispatcher-6HCJ6Q75.cjs} +12 -9
- package/dist/{order-notification-dispatcher-ECPFI7CD.js → order-notification-dispatcher-KM2SY7TX.js} +7 -4
- package/dist/{rbac-debug-db-5R6XFPSV.js → rbac-debug-db-DFZWZ6GV.js} +1 -1
- package/dist/{rbac-debug-db-JKK2MCWF.cjs → rbac-debug-db-KWV7D5PQ.cjs} +2 -2
- package/dist/resolve-invoice-asset-url-JEZRENJW.cjs +11 -0
- package/dist/resolve-invoice-asset-url-QZ2FRDHI.js +2 -0
- package/package.json +1 -1
- package/dist/chunk-GLD263KW.cjs +0 -862
- package/dist/chunk-OPJEV44S.js +0 -847
- package/dist/email-queue-HGXB2FIS.js +0 -5
package/dist/admin.js
CHANGED
|
@@ -861,7 +861,7 @@ var init_admin_config_context = __esm({
|
|
|
861
861
|
var CMS_VERSION;
|
|
862
862
|
var init_cms_version = __esm({
|
|
863
863
|
"src/lib/cms-version.ts"() {
|
|
864
|
-
CMS_VERSION = "1.0.
|
|
864
|
+
CMS_VERSION = "1.0.68" ;
|
|
865
865
|
}
|
|
866
866
|
});
|
|
867
867
|
function useCatalogCategories(enabled = true) {
|
|
@@ -13622,6 +13622,39 @@ __export(SettingsPage_exports, {
|
|
|
13622
13622
|
default: () => SettingsPage
|
|
13623
13623
|
});
|
|
13624
13624
|
function InvoiceTemplatePreview({ id }) {
|
|
13625
|
+
if (id === "service_detailed") {
|
|
13626
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
13627
|
+
className: "h-28 rounded-md border border-sky-200 bg-white overflow-hidden shadow-sm p-1.5 space-y-1"
|
|
13628
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13629
|
+
className: "h-4 bg-sky-50 rounded px-1 flex items-center justify-between border border-sky-100"
|
|
13630
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13631
|
+
className: "h-1.5 w-10 bg-sky-700 rounded"
|
|
13632
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13633
|
+
className: "h-1 w-6 bg-slate-400 rounded"
|
|
13634
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
13635
|
+
className: "rounded bg-sky-50/70 border border-sky-100 p-1 space-y-0.5"
|
|
13636
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13637
|
+
className: "grid grid-cols-2 gap-1"
|
|
13638
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13639
|
+
className: "h-1 w-full bg-sky-600/70 rounded"
|
|
13640
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13641
|
+
className: "h-1 w-full bg-sky-600/70 rounded"
|
|
13642
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
13643
|
+
className: "grid grid-cols-2 gap-1"
|
|
13644
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13645
|
+
className: "h-1 w-full bg-slate-400 rounded"
|
|
13646
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13647
|
+
className: "h-1 w-full bg-slate-400 rounded"
|
|
13648
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
13649
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
13650
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13651
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
13652
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13653
|
+
className: "flex justify-end pt-0.5"
|
|
13654
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13655
|
+
className: "h-1.5 w-12 bg-sky-700 rounded"
|
|
13656
|
+
})));
|
|
13657
|
+
}
|
|
13625
13658
|
if (id === "modern") {
|
|
13626
13659
|
return /* @__PURE__ */ React.createElement("div", {
|
|
13627
13660
|
className: "h-28 rounded-md border border-slate-200 bg-white overflow-hidden shadow-sm"
|
|
@@ -13800,6 +13833,7 @@ function SettingsPage() {
|
|
|
13800
13833
|
const [storeSettingsLoading, setStoreSettingsLoading] = useState(true);
|
|
13801
13834
|
const [currency, setCurrency] = useState(DEFAULT_CURRENCY);
|
|
13802
13835
|
const [invoiceTemplate, setInvoiceTemplate] = useState("classic");
|
|
13836
|
+
const [invoiceIncludeQr, setInvoiceIncludeQr] = useState(false);
|
|
13803
13837
|
const [companyLogo, setCompanyLogo] = useState("");
|
|
13804
13838
|
const [companyName, setCompanyName] = useState("");
|
|
13805
13839
|
const [companyEmail, setCompanyEmail] = useState("");
|
|
@@ -13877,6 +13911,7 @@ function SettingsPage() {
|
|
|
13877
13911
|
setStoreEnabled(data.enabled === "true");
|
|
13878
13912
|
setCurrency(data.currency || DEFAULT_CURRENCY);
|
|
13879
13913
|
setInvoiceTemplate(data.invoiceTemplate || "classic");
|
|
13914
|
+
setInvoiceIncludeQr(data.invoice_include_qr === "true" || data.invoiceIncludeQr === "true");
|
|
13880
13915
|
const logo = data.invoiceLogoUrl || data.companyLogo || data.logo || "";
|
|
13881
13916
|
setCompanyLogo((prev) => logo || prev);
|
|
13882
13917
|
setCompanyName(data.companyName || data.invoiceCompanyName || data.name || "");
|
|
@@ -14042,6 +14077,10 @@ function SettingsPage() {
|
|
|
14042
14077
|
value: invoiceTemplate || "classic",
|
|
14043
14078
|
type: "public"
|
|
14044
14079
|
},
|
|
14080
|
+
invoice_include_qr: {
|
|
14081
|
+
value: invoiceIncludeQr ? "true" : "false",
|
|
14082
|
+
type: "public"
|
|
14083
|
+
},
|
|
14045
14084
|
invoiceLogoUrl: {
|
|
14046
14085
|
value: companyLogo,
|
|
14047
14086
|
type: "public"
|
|
@@ -14510,24 +14549,43 @@ function SettingsPage() {
|
|
|
14510
14549
|
placeholder: "e.g. 123 Business Rd, Suite 100\nNew York, NY 10001",
|
|
14511
14550
|
rows: 3,
|
|
14512
14551
|
className: "text-sm bg-white"
|
|
14513
|
-
})))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("
|
|
14514
|
-
className: "
|
|
14515
|
-
},
|
|
14552
|
+
})))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
14553
|
+
className: "flex items-center justify-between mb-1"
|
|
14554
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
14555
|
+
className: "block text-sm font-semibold text-gray-700"
|
|
14556
|
+
}, "Invoice PDF template"), /* @__PURE__ */ React.createElement("label", {
|
|
14557
|
+
className: "inline-flex items-center gap-2 cursor-pointer select-none text-sm font-semibold text-gray-800 hover:text-gray-950"
|
|
14558
|
+
}, /* @__PURE__ */ React.createElement("input", {
|
|
14559
|
+
type: "checkbox",
|
|
14560
|
+
checked: invoiceIncludeQr,
|
|
14561
|
+
onChange: /* @__PURE__ */ __name((e) => setInvoiceIncludeQr(e.target.checked), "onChange"),
|
|
14562
|
+
className: "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500 accent-blue-600 cursor-pointer"
|
|
14563
|
+
}), /* @__PURE__ */ React.createElement("span", null, "Add QR"))), /* @__PURE__ */ React.createElement("p", {
|
|
14516
14564
|
className: "text-xs text-gray-500 mb-3"
|
|
14517
14565
|
}, "Used when generating invoices in the app (and as fallback when ERP invoice is unavailable). Company logo, name, email, phone, and address set above are shown in the invoice header."), /* @__PURE__ */ React.createElement("div", {
|
|
14518
14566
|
className: "grid gap-3 sm:grid-cols-2"
|
|
14519
14567
|
}, INVOICE_TEMPLATE_CARDS.map((opt) => {
|
|
14520
14568
|
const selected = invoiceTemplate === opt.id;
|
|
14521
|
-
return /* @__PURE__ */ React.createElement("
|
|
14569
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
14522
14570
|
key: opt.id,
|
|
14523
|
-
type: "button",
|
|
14524
14571
|
onClick: /* @__PURE__ */ __name(() => setInvoiceTemplate(opt.id), "onClick"),
|
|
14525
|
-
className: `text-left rounded-lg border p-3 transition ${selected ? "border-blue-500 bg-blue-50 ring-1 ring-blue-500" : "border-gray-200 bg-white hover:border-gray-300"}`
|
|
14572
|
+
className: `text-left rounded-lg border p-3 transition cursor-pointer ${selected ? "border-blue-500 bg-blue-50/70 ring-1 ring-blue-500" : "border-gray-200 bg-white hover:border-gray-300"}`
|
|
14526
14573
|
}, /* @__PURE__ */ React.createElement(InvoiceTemplatePreview, {
|
|
14527
14574
|
id: opt.id
|
|
14528
14575
|
}), /* @__PURE__ */ React.createElement("div", {
|
|
14529
|
-
className: "
|
|
14530
|
-
},
|
|
14576
|
+
className: "flex items-center justify-between mt-2"
|
|
14577
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
14578
|
+
className: "text-sm font-semibold text-gray-800"
|
|
14579
|
+
}, opt.label), /* @__PURE__ */ React.createElement("button", {
|
|
14580
|
+
type: "button",
|
|
14581
|
+
onClick: /* @__PURE__ */ __name((e) => {
|
|
14582
|
+
e.stopPropagation();
|
|
14583
|
+
window.open(`/api/settings/invoice-preview?template=${encodeURIComponent(opt.id)}&qr=${invoiceIncludeQr ? "1" : "0"}`, "_blank");
|
|
14584
|
+
}, "onClick"),
|
|
14585
|
+
className: "text-[11px] font-medium text-blue-600 hover:text-blue-800 hover:underline inline-flex items-center gap-1 py-0.5 px-1.5 rounded bg-blue-50 hover:bg-blue-100/80 transition"
|
|
14586
|
+
}, /* @__PURE__ */ React.createElement(Eye, {
|
|
14587
|
+
className: "w-3 h-3"
|
|
14588
|
+
}), "Preview")), /* @__PURE__ */ React.createElement("div", {
|
|
14531
14589
|
className: "text-xs text-gray-500 mt-0.5"
|
|
14532
14590
|
}, opt.description));
|
|
14533
14591
|
}))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
@@ -14756,6 +14814,11 @@ var init_SettingsPage = __esm({
|
|
|
14756
14814
|
id: "bold",
|
|
14757
14815
|
label: "Bold",
|
|
14758
14816
|
description: "Strong header band and clear payment summary."
|
|
14817
|
+
},
|
|
14818
|
+
{
|
|
14819
|
+
id: "service_detailed",
|
|
14820
|
+
label: "Service Detailed",
|
|
14821
|
+
description: "Includes order date, service date, slot, and assigned technician."
|
|
14759
14822
|
}
|
|
14760
14823
|
];
|
|
14761
14824
|
__name(InvoiceTemplatePreview, "InvoiceTemplatePreview");
|
|
@@ -23245,6 +23308,7 @@ function VendorTeamPage() {
|
|
|
23245
23308
|
const [inviteDialog, setInviteDialog] = useState(null);
|
|
23246
23309
|
const [sendingInviteEmail, setSendingInviteEmail] = useState(false);
|
|
23247
23310
|
const [linkCopied, setLinkCopied] = useState(false);
|
|
23311
|
+
const [resendingUserId, setResendingUserId] = useState(null);
|
|
23248
23312
|
const assignableRoles = roles.filter((r) => !r.isOwnerRole);
|
|
23249
23313
|
const load = useCallback(async () => {
|
|
23250
23314
|
setLoading(true);
|
|
@@ -23323,6 +23387,31 @@ function VendorTeamPage() {
|
|
|
23323
23387
|
setInviteDialog(null);
|
|
23324
23388
|
setLinkCopied(false);
|
|
23325
23389
|
}, "closeInviteDialog");
|
|
23390
|
+
const handleResendInvite = /* @__PURE__ */ __name(async (userId) => {
|
|
23391
|
+
setResendingUserId(userId);
|
|
23392
|
+
try {
|
|
23393
|
+
const res = await fetch("/api/admin/vendor/team/send-invite", {
|
|
23394
|
+
method: "POST",
|
|
23395
|
+
headers: {
|
|
23396
|
+
"Content-Type": "application/json"
|
|
23397
|
+
},
|
|
23398
|
+
body: JSON.stringify({
|
|
23399
|
+
userId
|
|
23400
|
+
})
|
|
23401
|
+
});
|
|
23402
|
+
const data = await res.json();
|
|
23403
|
+
if (!res.ok) {
|
|
23404
|
+
toast.error(data.error || data.message || "Failed to resend invite");
|
|
23405
|
+
return;
|
|
23406
|
+
}
|
|
23407
|
+
toast.success(data.message || "Invite email sent");
|
|
23408
|
+
await load();
|
|
23409
|
+
} catch {
|
|
23410
|
+
toast.error("Failed to resend invite");
|
|
23411
|
+
} finally {
|
|
23412
|
+
setResendingUserId(null);
|
|
23413
|
+
}
|
|
23414
|
+
}, "handleResendInvite");
|
|
23326
23415
|
if (!canManage) {
|
|
23327
23416
|
return /* @__PURE__ */ React27__default.createElement("div", {
|
|
23328
23417
|
className: "min-w-0 rounded-lg bg-white shadow-md"
|
|
@@ -23494,12 +23583,26 @@ function VendorTeamPage() {
|
|
|
23494
23583
|
value: r.id
|
|
23495
23584
|
}, r.name)))), /* @__PURE__ */ React27__default.createElement("td", {
|
|
23496
23585
|
className: "px-3 py-2 text-right"
|
|
23497
|
-
},
|
|
23586
|
+
}, /* @__PURE__ */ React27__default.createElement("div", {
|
|
23587
|
+
className: "flex items-center justify-end gap-1.5"
|
|
23588
|
+
}, !m.isOwnerRole && String(m.inviteStatus ?? "") === "pending" && /* @__PURE__ */ React27__default.createElement(Button, {
|
|
23589
|
+
type: "button",
|
|
23590
|
+
variant: "outline",
|
|
23591
|
+
size: "icon",
|
|
23592
|
+
className: "h-7 w-7",
|
|
23593
|
+
disabled: resendingUserId === m.userId,
|
|
23594
|
+
onClick: /* @__PURE__ */ __name(() => void handleResendInvite(m.userId), "onClick"),
|
|
23595
|
+
title: "Resend invite"
|
|
23596
|
+
}, /* @__PURE__ */ React27__default.createElement(RefreshCw, {
|
|
23597
|
+
className: `h-3.5 w-3.5 ${resendingUserId === m.userId ? "animate-spin text-blue-600" : ""}`
|
|
23598
|
+
}), /* @__PURE__ */ React27__default.createElement("span", {
|
|
23599
|
+
className: "sr-only"
|
|
23600
|
+
}, "Resend invite")), !m.isOwnerRole && /* @__PURE__ */ React27__default.createElement(Button, {
|
|
23498
23601
|
type: "button",
|
|
23499
23602
|
variant: "ghost",
|
|
23500
23603
|
size: "sm",
|
|
23501
23604
|
onClick: /* @__PURE__ */ __name(() => handleRemove(m.userId), "onClick")
|
|
23502
|
-
}, "Remove"))))))), /* @__PURE__ */ React27__default.createElement("form", {
|
|
23605
|
+
}, "Remove")))))))), /* @__PURE__ */ React27__default.createElement("form", {
|
|
23503
23606
|
onSubmit: handleAdd,
|
|
23504
23607
|
className: "space-y-4 rounded-lg border border-gray-200 p-4"
|
|
23505
23608
|
}, /* @__PURE__ */ React27__default.createElement("h2", {
|
|
@@ -24564,7 +24667,10 @@ function OrderDetailPage({ orderId }) {
|
|
|
24564
24667
|
const [cancelModal, setCancelModal] = useState(null);
|
|
24565
24668
|
const [assignModalOpen, setAssignModalOpen] = useState(false);
|
|
24566
24669
|
const [assigneesList, setAssigneesList] = useState([]);
|
|
24670
|
+
const [vendorsList, setVendorsList] = useState([]);
|
|
24671
|
+
const [isPlatformAdmin, setIsPlatformAdmin] = useState(false);
|
|
24567
24672
|
const [loadingAssignees, setLoadingAssignees] = useState(false);
|
|
24673
|
+
const [selectedVendorId, setSelectedVendorId] = useState(null);
|
|
24568
24674
|
const [selectedAssigneeId, setSelectedAssigneeId] = useState(null);
|
|
24569
24675
|
const [savingAssignee, setSavingAssignee] = useState(false);
|
|
24570
24676
|
const [completeModalOpen, setCompleteModalOpen] = useState(false);
|
|
@@ -24664,8 +24770,9 @@ function OrderDetailPage({ orderId }) {
|
|
|
24664
24770
|
async function handleStartProcessing() {
|
|
24665
24771
|
if (!order || startingProcessing) return;
|
|
24666
24772
|
const currentAssigneeId = order.assignedUserId ?? order.assignedUser?.id ?? null;
|
|
24667
|
-
|
|
24668
|
-
|
|
24773
|
+
const currentVendorId = order.vendorId ?? order.vendor?.id ?? null;
|
|
24774
|
+
if (!currentAssigneeId && !currentVendorId) {
|
|
24775
|
+
toast.info("Please assign a vendor or teammate before starting order processing.");
|
|
24669
24776
|
handleOpenAssignModal();
|
|
24670
24777
|
return;
|
|
24671
24778
|
}
|
|
@@ -24678,7 +24785,9 @@ function OrderDetailPage({ orderId }) {
|
|
|
24678
24785
|
},
|
|
24679
24786
|
body: JSON.stringify({
|
|
24680
24787
|
status: "processing",
|
|
24681
|
-
|
|
24788
|
+
...currentAssigneeId ? {
|
|
24789
|
+
assignedUserId: currentAssigneeId
|
|
24790
|
+
} : {}
|
|
24682
24791
|
})
|
|
24683
24792
|
});
|
|
24684
24793
|
const data = await res.json().catch(() => ({}));
|
|
@@ -24700,14 +24809,19 @@ function OrderDetailPage({ orderId }) {
|
|
|
24700
24809
|
setAssignModalOpen(true);
|
|
24701
24810
|
setLoadingAssignees(true);
|
|
24702
24811
|
setSelectedAssigneeId(order?.assignedUserId ?? null);
|
|
24812
|
+
setSelectedVendorId(order?.vendorId ?? null);
|
|
24703
24813
|
try {
|
|
24704
24814
|
const res = await fetch(`/api/orders/${orderId}/eligible-assignees`);
|
|
24705
24815
|
if (res.ok) {
|
|
24706
24816
|
const data = await res.json();
|
|
24707
24817
|
setAssigneesList(data.assignees ?? []);
|
|
24818
|
+
setVendorsList(data.vendors ?? []);
|
|
24819
|
+
setIsPlatformAdmin(Boolean(data.isPlatformAdmin));
|
|
24820
|
+
if (data.currentVendorId != null) setSelectedVendorId(data.currentVendorId);
|
|
24821
|
+
if (data.currentAssignedUserId != null) setSelectedAssigneeId(data.currentAssignedUserId);
|
|
24708
24822
|
}
|
|
24709
24823
|
} catch {
|
|
24710
|
-
toast.error("Failed to load eligible
|
|
24824
|
+
toast.error("Failed to load eligible assignees");
|
|
24711
24825
|
} finally {
|
|
24712
24826
|
setLoadingAssignees(false);
|
|
24713
24827
|
}
|
|
@@ -24720,6 +24834,14 @@ function OrderDetailPage({ orderId }) {
|
|
|
24720
24834
|
const bodyPayload = {
|
|
24721
24835
|
assignedUserId: selectedAssigneeId
|
|
24722
24836
|
};
|
|
24837
|
+
if (isPlatformAdmin) {
|
|
24838
|
+
const chosenAssignee = assigneesList.find((a) => a.id === selectedAssigneeId);
|
|
24839
|
+
if (chosenAssignee && chosenAssignee.vendorId === null) {
|
|
24840
|
+
bodyPayload.vendorId = 1;
|
|
24841
|
+
} else {
|
|
24842
|
+
bodyPayload.vendorId = chosenAssignee?.vendorId ?? (selectedVendorId || 1);
|
|
24843
|
+
}
|
|
24844
|
+
}
|
|
24723
24845
|
const res = await fetch(`/api/orders/${orderId}`, {
|
|
24724
24846
|
method: "PUT",
|
|
24725
24847
|
headers: {
|
|
@@ -24729,15 +24851,15 @@ function OrderDetailPage({ orderId }) {
|
|
|
24729
24851
|
});
|
|
24730
24852
|
if (!res.ok) {
|
|
24731
24853
|
const err = await res.json().catch(() => ({}));
|
|
24732
|
-
toast.error(err.error ?? "Failed to update
|
|
24854
|
+
toast.error(err.error ?? "Failed to update assignment");
|
|
24733
24855
|
return;
|
|
24734
24856
|
}
|
|
24735
24857
|
const updated = await fetch(`/api/orders/${orderId}`);
|
|
24736
24858
|
if (updated.ok) setOrder(await updated.json());
|
|
24737
24859
|
setAssignModalOpen(false);
|
|
24738
|
-
toast.success(selectedAssigneeId ? "Order assigned successfully" : "
|
|
24860
|
+
toast.success(selectedAssigneeId || selectedVendorId ? "Order assigned successfully" : "Assignment removed");
|
|
24739
24861
|
} catch {
|
|
24740
|
-
toast.error("Failed to update
|
|
24862
|
+
toast.error("Failed to update assignment");
|
|
24741
24863
|
} finally {
|
|
24742
24864
|
setSavingAssignee(false);
|
|
24743
24865
|
}
|
|
@@ -25122,7 +25244,19 @@ function OrderDetailPage({ orderId }) {
|
|
|
25122
25244
|
className: "min-w-0"
|
|
25123
25245
|
}, /* @__PURE__ */ React.createElement("dt", {
|
|
25124
25246
|
className: "text-gray-500"
|
|
25125
|
-
}, "
|
|
25247
|
+
}, "Vendor"), /* @__PURE__ */ React.createElement("dd", {
|
|
25248
|
+
className: "font-medium text-gray-900 flex flex-wrap items-center gap-1.5 mt-0.5"
|
|
25249
|
+
}, order.vendor ? /* @__PURE__ */ React.createElement("span", {
|
|
25250
|
+
className: "inline-flex items-center gap-1 text-xs bg-purple-50 text-purple-800 font-medium px-2 py-0.5 rounded-full border border-purple-200"
|
|
25251
|
+
}, order.vendor.name || `Vendor #${order.vendor.id}`) : order.vendorId ? /* @__PURE__ */ React.createElement("span", {
|
|
25252
|
+
className: "inline-flex items-center gap-1 text-xs bg-purple-50 text-purple-800 font-medium px-2 py-0.5 rounded-full border border-purple-200"
|
|
25253
|
+
}, `Vendor #${order.vendorId}`) : /* @__PURE__ */ React.createElement("span", {
|
|
25254
|
+
className: "text-gray-400 text-xs italic"
|
|
25255
|
+
}, "Platform Default"))), /* @__PURE__ */ React.createElement("div", {
|
|
25256
|
+
className: "min-w-0"
|
|
25257
|
+
}, /* @__PURE__ */ React.createElement("dt", {
|
|
25258
|
+
className: "text-gray-500"
|
|
25259
|
+
}, "Assigned Teammate"), /* @__PURE__ */ React.createElement("dd", {
|
|
25126
25260
|
className: "font-medium text-gray-900 flex flex-wrap items-center gap-1.5 mt-0.5"
|
|
25127
25261
|
}, order.assignedUser ? /* @__PURE__ */ React.createElement("span", {
|
|
25128
25262
|
className: "inline-flex items-center gap-1 text-xs bg-blue-50 text-blue-800 font-medium px-2 py-0.5 rounded-full border border-blue-200"
|
|
@@ -25391,7 +25525,7 @@ function OrderDetailPage({ orderId }) {
|
|
|
25391
25525
|
className: "text-base font-semibold text-gray-900 flex items-center gap-2"
|
|
25392
25526
|
}, /* @__PURE__ */ React.createElement(UserCheck, {
|
|
25393
25527
|
className: "h-5 w-5 text-blue-600"
|
|
25394
|
-
}), "Assign Order to Teammate"), /* @__PURE__ */ React.createElement("button", {
|
|
25528
|
+
}), isPlatformAdmin ? "Assign Order (Vendor & Team)" : "Assign Order to Teammate"), /* @__PURE__ */ React.createElement("button", {
|
|
25395
25529
|
type: "button",
|
|
25396
25530
|
onClick: /* @__PURE__ */ __name(() => setAssignModalOpen(false), "onClick"),
|
|
25397
25531
|
className: "text-gray-400 hover:text-gray-600"
|
|
@@ -25399,14 +25533,42 @@ function OrderDetailPage({ orderId }) {
|
|
|
25399
25533
|
className: "h-5 w-5"
|
|
25400
25534
|
}))), /* @__PURE__ */ React.createElement("p", {
|
|
25401
25535
|
className: "text-xs text-gray-500"
|
|
25402
|
-
}, "Select a team
|
|
25536
|
+
}, isPlatformAdmin ? "Assign this order to a Vendor or directly to any Vendor's Team Member." : "Select a member of your team to handle this service, or keep it unassigned to your general store."), loadingAssignees ? /* @__PURE__ */ React.createElement("div", {
|
|
25403
25537
|
className: "flex justify-center py-8"
|
|
25404
25538
|
}, /* @__PURE__ */ React.createElement(Loader2, {
|
|
25405
25539
|
className: "h-6 w-6 animate-spin text-blue-600"
|
|
25406
25540
|
})) : /* @__PURE__ */ React.createElement("div", {
|
|
25407
|
-
className: "space-y-
|
|
25541
|
+
className: "space-y-4"
|
|
25542
|
+
}, isPlatformAdmin && vendorsList.length > 0 && /* @__PURE__ */ React.createElement("div", {
|
|
25543
|
+
className: "space-y-1.5"
|
|
25544
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
25545
|
+
className: "block text-xs font-semibold text-gray-700"
|
|
25546
|
+
}, "Assigned Vendor"), /* @__PURE__ */ React.createElement("select", {
|
|
25547
|
+
value: selectedVendorId ?? "",
|
|
25548
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
25549
|
+
const val = e.target.value ? Number(e.target.value) : null;
|
|
25550
|
+
setSelectedVendorId(val);
|
|
25551
|
+
if (val && selectedAssigneeId) {
|
|
25552
|
+
const assignee = assigneesList.find((a) => a.id === selectedAssigneeId);
|
|
25553
|
+
if (assignee && assignee.vendorId !== val) {
|
|
25554
|
+
setSelectedAssigneeId(null);
|
|
25555
|
+
}
|
|
25556
|
+
}
|
|
25557
|
+
}, "onChange"),
|
|
25558
|
+
className: "w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
25559
|
+
}, /* @__PURE__ */ React.createElement("option", {
|
|
25560
|
+
value: ""
|
|
25561
|
+
}, "All Vendors / Any Vendor"), vendorsList.map((v) => /* @__PURE__ */ React.createElement("option", {
|
|
25562
|
+
key: v.id,
|
|
25563
|
+
value: v.id
|
|
25564
|
+
}, v.name)))), /* @__PURE__ */ React.createElement("div", {
|
|
25565
|
+
className: "space-y-1.5"
|
|
25408
25566
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
25409
|
-
className:
|
|
25567
|
+
className: "block text-xs font-semibold text-gray-700"
|
|
25568
|
+
}, isPlatformAdmin ? "Assigned Team Member" : "Select Team Member"), /* @__PURE__ */ React.createElement("div", {
|
|
25569
|
+
className: "space-y-2 max-h-56 overflow-y-auto pr-1"
|
|
25570
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
25571
|
+
className: `flex items-center justify-between p-2.5 rounded-lg border cursor-pointer transition ${selectedAssigneeId === null ? "border-blue-600 bg-blue-50/50" : "border-gray-200 hover:bg-gray-50"}`
|
|
25410
25572
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
25411
25573
|
className: "flex items-center gap-3"
|
|
25412
25574
|
}, /* @__PURE__ */ React.createElement("input", {
|
|
@@ -25417,26 +25579,40 @@ function OrderDetailPage({ orderId }) {
|
|
|
25417
25579
|
className: "text-blue-600"
|
|
25418
25580
|
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", {
|
|
25419
25581
|
className: "text-sm font-medium text-gray-800"
|
|
25420
|
-
}, "Unassigned"), /* @__PURE__ */ React.createElement("p", {
|
|
25582
|
+
}, "Unassigned to specific person"), /* @__PURE__ */ React.createElement("p", {
|
|
25421
25583
|
className: "text-xs text-gray-400"
|
|
25422
|
-
}, "
|
|
25584
|
+
}, selectedVendorId ? "Vendor general queue" : "General queue")))), assigneesList.filter((a) => {
|
|
25585
|
+
if (!isPlatformAdmin) return true;
|
|
25586
|
+
if (selectedVendorId && selectedVendorId !== 1) {
|
|
25587
|
+
return a.vendorId === selectedVendorId;
|
|
25588
|
+
}
|
|
25589
|
+
if (selectedVendorId === 1) {
|
|
25590
|
+
return a.vendorId === 1 || a.vendorId === null;
|
|
25591
|
+
}
|
|
25592
|
+
return true;
|
|
25593
|
+
}).map((a) => /* @__PURE__ */ React.createElement("label", {
|
|
25423
25594
|
key: a.id,
|
|
25424
|
-
className: `flex items-center justify-between p-
|
|
25595
|
+
className: `flex items-center justify-between p-2.5 rounded-lg border cursor-pointer transition ${selectedAssigneeId === a.id ? "border-blue-600 bg-blue-50/50" : "border-gray-200 hover:bg-gray-50"}`
|
|
25425
25596
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
25426
25597
|
className: "flex items-center gap-3"
|
|
25427
25598
|
}, /* @__PURE__ */ React.createElement("input", {
|
|
25428
25599
|
type: "radio",
|
|
25429
25600
|
name: "assignee",
|
|
25430
25601
|
checked: selectedAssigneeId === a.id,
|
|
25431
|
-
onChange: /* @__PURE__ */ __name(() =>
|
|
25602
|
+
onChange: /* @__PURE__ */ __name(() => {
|
|
25603
|
+
setSelectedAssigneeId(a.id);
|
|
25604
|
+
if (isPlatformAdmin) {
|
|
25605
|
+
setSelectedVendorId(a.vendorId || 1);
|
|
25606
|
+
}
|
|
25607
|
+
}, "onChange"),
|
|
25432
25608
|
className: "text-blue-600"
|
|
25433
25609
|
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", {
|
|
25434
25610
|
className: "text-sm font-medium text-gray-900"
|
|
25435
25611
|
}, a.name), /* @__PURE__ */ React.createElement("p", {
|
|
25436
25612
|
className: "text-xs text-gray-500"
|
|
25437
|
-
}, a.email))), /* @__PURE__ */ React.createElement("span", {
|
|
25438
|
-
className: "text-[11px] font-medium text-gray-
|
|
25439
|
-
}, a.role)))), /* @__PURE__ */ React.createElement("div", {
|
|
25613
|
+
}, a.email, isPlatformAdmin && a.vendorName ? ` \u2022 ${a.vendorName}` : ""))), /* @__PURE__ */ React.createElement("span", {
|
|
25614
|
+
className: "text-[11px] font-medium text-gray-600 bg-gray-100 px-2 py-0.5 rounded"
|
|
25615
|
+
}, a.role)))))), /* @__PURE__ */ React.createElement("div", {
|
|
25440
25616
|
className: "flex justify-end gap-2 pt-2 border-t border-gray-100"
|
|
25441
25617
|
}, /* @__PURE__ */ React.createElement(Button, {
|
|
25442
25618
|
variant: "outline",
|
|
@@ -25446,7 +25622,7 @@ function OrderDetailPage({ orderId }) {
|
|
|
25446
25622
|
onClick: handleSaveAssignee,
|
|
25447
25623
|
disabled: savingAssignee,
|
|
25448
25624
|
className: "bg-blue-600 hover:bg-blue-700 text-white"
|
|
25449
|
-
}, savingAssignee ? "Saving..." : "Save
|
|
25625
|
+
}, savingAssignee ? "Saving..." : "Save Assignment")))), completeModalOpen && /* @__PURE__ */ React.createElement("div", {
|
|
25450
25626
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4 animate-in fade-in duration-150"
|
|
25451
25627
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
25452
25628
|
className: "bg-white rounded-xl shadow-2xl w-full max-w-md p-6 space-y-4 border border-gray-100"
|
|
@@ -27010,6 +27186,10 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27010
27186
|
toast.error(placeForSomeoneElse ? "Enter the contact's email address" : "Select a customer or enter an email address");
|
|
27011
27187
|
return;
|
|
27012
27188
|
}
|
|
27189
|
+
if (!effectivePhone) {
|
|
27190
|
+
toast.error(placeForSomeoneElse ? "Enter the recipient's phone number" : "Please enter the customer's phone number");
|
|
27191
|
+
return;
|
|
27192
|
+
}
|
|
27013
27193
|
if (lines.length === 0) {
|
|
27014
27194
|
toast.error("Add at least one product to the cart");
|
|
27015
27195
|
return;
|
|
@@ -27241,7 +27421,8 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27241
27421
|
id: "c-phone",
|
|
27242
27422
|
value: contactPhone,
|
|
27243
27423
|
onChange: /* @__PURE__ */ __name((e) => setContactPhone(e.target.value), "onChange"),
|
|
27244
|
-
placeholder: "phone number"
|
|
27424
|
+
placeholder: "phone number",
|
|
27425
|
+
required: contactId == null && !placeForSomeoneElse
|
|
27245
27426
|
}))), /* @__PURE__ */ React.createElement("div", {
|
|
27246
27427
|
className: "flex items-start gap-3 rounded-lg border border-dashed border-gray-300 bg-gray-50/60 px-4 py-3 mt-1"
|
|
27247
27428
|
}, /* @__PURE__ */ React.createElement(Checkbox, {
|
|
@@ -27317,6 +27498,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27317
27498
|
value: subContactPhone,
|
|
27318
27499
|
onChange: /* @__PURE__ */ __name((e) => setSubContactPhone(e.target.value), "onChange"),
|
|
27319
27500
|
placeholder: "phone number",
|
|
27501
|
+
required: placeForSomeoneElse && subContactId == null,
|
|
27320
27502
|
readOnly: subContactId != null,
|
|
27321
27503
|
className: subContactId != null ? "bg-gray-100 cursor-not-allowed" : ""
|
|
27322
27504
|
})))), /* @__PURE__ */ React.createElement("div", {
|