@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.cjs
CHANGED
|
@@ -895,7 +895,7 @@ var init_admin_config_context = __esm({
|
|
|
895
895
|
var CMS_VERSION;
|
|
896
896
|
var init_cms_version = __esm({
|
|
897
897
|
"src/lib/cms-version.ts"() {
|
|
898
|
-
CMS_VERSION = "1.0.
|
|
898
|
+
CMS_VERSION = "1.0.68" ;
|
|
899
899
|
}
|
|
900
900
|
});
|
|
901
901
|
function useCatalogCategories(enabled = true) {
|
|
@@ -13656,6 +13656,39 @@ __export(SettingsPage_exports, {
|
|
|
13656
13656
|
default: () => SettingsPage
|
|
13657
13657
|
});
|
|
13658
13658
|
function InvoiceTemplatePreview({ id }) {
|
|
13659
|
+
if (id === "service_detailed") {
|
|
13660
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
13661
|
+
className: "h-28 rounded-md border border-sky-200 bg-white overflow-hidden shadow-sm p-1.5 space-y-1"
|
|
13662
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13663
|
+
className: "h-4 bg-sky-50 rounded px-1 flex items-center justify-between border border-sky-100"
|
|
13664
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13665
|
+
className: "h-1.5 w-10 bg-sky-700 rounded"
|
|
13666
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13667
|
+
className: "h-1 w-6 bg-slate-400 rounded"
|
|
13668
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
13669
|
+
className: "rounded bg-sky-50/70 border border-sky-100 p-1 space-y-0.5"
|
|
13670
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13671
|
+
className: "grid grid-cols-2 gap-1"
|
|
13672
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13673
|
+
className: "h-1 w-full bg-sky-600/70 rounded"
|
|
13674
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13675
|
+
className: "h-1 w-full bg-sky-600/70 rounded"
|
|
13676
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
13677
|
+
className: "grid grid-cols-2 gap-1"
|
|
13678
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13679
|
+
className: "h-1 w-full bg-slate-400 rounded"
|
|
13680
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13681
|
+
className: "h-1 w-full bg-slate-400 rounded"
|
|
13682
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
13683
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
13684
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13685
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
13686
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13687
|
+
className: "flex justify-end pt-0.5"
|
|
13688
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13689
|
+
className: "h-1.5 w-12 bg-sky-700 rounded"
|
|
13690
|
+
})));
|
|
13691
|
+
}
|
|
13659
13692
|
if (id === "modern") {
|
|
13660
13693
|
return /* @__PURE__ */ React.createElement("div", {
|
|
13661
13694
|
className: "h-28 rounded-md border border-slate-200 bg-white overflow-hidden shadow-sm"
|
|
@@ -13834,6 +13867,7 @@ function SettingsPage() {
|
|
|
13834
13867
|
const [storeSettingsLoading, setStoreSettingsLoading] = React27.useState(true);
|
|
13835
13868
|
const [currency, setCurrency] = React27.useState(DEFAULT_CURRENCY);
|
|
13836
13869
|
const [invoiceTemplate, setInvoiceTemplate] = React27.useState("classic");
|
|
13870
|
+
const [invoiceIncludeQr, setInvoiceIncludeQr] = React27.useState(false);
|
|
13837
13871
|
const [companyLogo, setCompanyLogo] = React27.useState("");
|
|
13838
13872
|
const [companyName, setCompanyName] = React27.useState("");
|
|
13839
13873
|
const [companyEmail, setCompanyEmail] = React27.useState("");
|
|
@@ -13911,6 +13945,7 @@ function SettingsPage() {
|
|
|
13911
13945
|
setStoreEnabled(data.enabled === "true");
|
|
13912
13946
|
setCurrency(data.currency || DEFAULT_CURRENCY);
|
|
13913
13947
|
setInvoiceTemplate(data.invoiceTemplate || "classic");
|
|
13948
|
+
setInvoiceIncludeQr(data.invoice_include_qr === "true" || data.invoiceIncludeQr === "true");
|
|
13914
13949
|
const logo = data.invoiceLogoUrl || data.companyLogo || data.logo || "";
|
|
13915
13950
|
setCompanyLogo((prev) => logo || prev);
|
|
13916
13951
|
setCompanyName(data.companyName || data.invoiceCompanyName || data.name || "");
|
|
@@ -14076,6 +14111,10 @@ function SettingsPage() {
|
|
|
14076
14111
|
value: invoiceTemplate || "classic",
|
|
14077
14112
|
type: "public"
|
|
14078
14113
|
},
|
|
14114
|
+
invoice_include_qr: {
|
|
14115
|
+
value: invoiceIncludeQr ? "true" : "false",
|
|
14116
|
+
type: "public"
|
|
14117
|
+
},
|
|
14079
14118
|
invoiceLogoUrl: {
|
|
14080
14119
|
value: companyLogo,
|
|
14081
14120
|
type: "public"
|
|
@@ -14544,24 +14583,43 @@ function SettingsPage() {
|
|
|
14544
14583
|
placeholder: "e.g. 123 Business Rd, Suite 100\nNew York, NY 10001",
|
|
14545
14584
|
rows: 3,
|
|
14546
14585
|
className: "text-sm bg-white"
|
|
14547
|
-
})))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("
|
|
14548
|
-
className: "
|
|
14549
|
-
},
|
|
14586
|
+
})))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
14587
|
+
className: "flex items-center justify-between mb-1"
|
|
14588
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
14589
|
+
className: "block text-sm font-semibold text-gray-700"
|
|
14590
|
+
}, "Invoice PDF template"), /* @__PURE__ */ React.createElement("label", {
|
|
14591
|
+
className: "inline-flex items-center gap-2 cursor-pointer select-none text-sm font-semibold text-gray-800 hover:text-gray-950"
|
|
14592
|
+
}, /* @__PURE__ */ React.createElement("input", {
|
|
14593
|
+
type: "checkbox",
|
|
14594
|
+
checked: invoiceIncludeQr,
|
|
14595
|
+
onChange: /* @__PURE__ */ __name((e) => setInvoiceIncludeQr(e.target.checked), "onChange"),
|
|
14596
|
+
className: "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500 accent-blue-600 cursor-pointer"
|
|
14597
|
+
}), /* @__PURE__ */ React.createElement("span", null, "Add QR"))), /* @__PURE__ */ React.createElement("p", {
|
|
14550
14598
|
className: "text-xs text-gray-500 mb-3"
|
|
14551
14599
|
}, "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", {
|
|
14552
14600
|
className: "grid gap-3 sm:grid-cols-2"
|
|
14553
14601
|
}, INVOICE_TEMPLATE_CARDS.map((opt) => {
|
|
14554
14602
|
const selected = invoiceTemplate === opt.id;
|
|
14555
|
-
return /* @__PURE__ */ React.createElement("
|
|
14603
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
14556
14604
|
key: opt.id,
|
|
14557
|
-
type: "button",
|
|
14558
14605
|
onClick: /* @__PURE__ */ __name(() => setInvoiceTemplate(opt.id), "onClick"),
|
|
14559
|
-
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"}`
|
|
14606
|
+
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"}`
|
|
14560
14607
|
}, /* @__PURE__ */ React.createElement(InvoiceTemplatePreview, {
|
|
14561
14608
|
id: opt.id
|
|
14562
14609
|
}), /* @__PURE__ */ React.createElement("div", {
|
|
14563
|
-
className: "
|
|
14564
|
-
},
|
|
14610
|
+
className: "flex items-center justify-between mt-2"
|
|
14611
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
14612
|
+
className: "text-sm font-semibold text-gray-800"
|
|
14613
|
+
}, opt.label), /* @__PURE__ */ React.createElement("button", {
|
|
14614
|
+
type: "button",
|
|
14615
|
+
onClick: /* @__PURE__ */ __name((e) => {
|
|
14616
|
+
e.stopPropagation();
|
|
14617
|
+
window.open(`/api/settings/invoice-preview?template=${encodeURIComponent(opt.id)}&qr=${invoiceIncludeQr ? "1" : "0"}`, "_blank");
|
|
14618
|
+
}, "onClick"),
|
|
14619
|
+
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"
|
|
14620
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.Eye, {
|
|
14621
|
+
className: "w-3 h-3"
|
|
14622
|
+
}), "Preview")), /* @__PURE__ */ React.createElement("div", {
|
|
14565
14623
|
className: "text-xs text-gray-500 mt-0.5"
|
|
14566
14624
|
}, opt.description));
|
|
14567
14625
|
}))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
@@ -14790,6 +14848,11 @@ var init_SettingsPage = __esm({
|
|
|
14790
14848
|
id: "bold",
|
|
14791
14849
|
label: "Bold",
|
|
14792
14850
|
description: "Strong header band and clear payment summary."
|
|
14851
|
+
},
|
|
14852
|
+
{
|
|
14853
|
+
id: "service_detailed",
|
|
14854
|
+
label: "Service Detailed",
|
|
14855
|
+
description: "Includes order date, service date, slot, and assigned technician."
|
|
14793
14856
|
}
|
|
14794
14857
|
];
|
|
14795
14858
|
__name(InvoiceTemplatePreview, "InvoiceTemplatePreview");
|
|
@@ -23279,6 +23342,7 @@ function VendorTeamPage() {
|
|
|
23279
23342
|
const [inviteDialog, setInviteDialog] = React27.useState(null);
|
|
23280
23343
|
const [sendingInviteEmail, setSendingInviteEmail] = React27.useState(false);
|
|
23281
23344
|
const [linkCopied, setLinkCopied] = React27.useState(false);
|
|
23345
|
+
const [resendingUserId, setResendingUserId] = React27.useState(null);
|
|
23282
23346
|
const assignableRoles = roles.filter((r) => !r.isOwnerRole);
|
|
23283
23347
|
const load = React27.useCallback(async () => {
|
|
23284
23348
|
setLoading(true);
|
|
@@ -23357,6 +23421,31 @@ function VendorTeamPage() {
|
|
|
23357
23421
|
setInviteDialog(null);
|
|
23358
23422
|
setLinkCopied(false);
|
|
23359
23423
|
}, "closeInviteDialog");
|
|
23424
|
+
const handleResendInvite = /* @__PURE__ */ __name(async (userId) => {
|
|
23425
|
+
setResendingUserId(userId);
|
|
23426
|
+
try {
|
|
23427
|
+
const res = await fetch("/api/admin/vendor/team/send-invite", {
|
|
23428
|
+
method: "POST",
|
|
23429
|
+
headers: {
|
|
23430
|
+
"Content-Type": "application/json"
|
|
23431
|
+
},
|
|
23432
|
+
body: JSON.stringify({
|
|
23433
|
+
userId
|
|
23434
|
+
})
|
|
23435
|
+
});
|
|
23436
|
+
const data = await res.json();
|
|
23437
|
+
if (!res.ok) {
|
|
23438
|
+
sonner.toast.error(data.error || data.message || "Failed to resend invite");
|
|
23439
|
+
return;
|
|
23440
|
+
}
|
|
23441
|
+
sonner.toast.success(data.message || "Invite email sent");
|
|
23442
|
+
await load();
|
|
23443
|
+
} catch {
|
|
23444
|
+
sonner.toast.error("Failed to resend invite");
|
|
23445
|
+
} finally {
|
|
23446
|
+
setResendingUserId(null);
|
|
23447
|
+
}
|
|
23448
|
+
}, "handleResendInvite");
|
|
23360
23449
|
if (!canManage) {
|
|
23361
23450
|
return /* @__PURE__ */ React27__namespace.default.createElement("div", {
|
|
23362
23451
|
className: "min-w-0 rounded-lg bg-white shadow-md"
|
|
@@ -23528,12 +23617,26 @@ function VendorTeamPage() {
|
|
|
23528
23617
|
value: r.id
|
|
23529
23618
|
}, r.name)))), /* @__PURE__ */ React27__namespace.default.createElement("td", {
|
|
23530
23619
|
className: "px-3 py-2 text-right"
|
|
23531
|
-
},
|
|
23620
|
+
}, /* @__PURE__ */ React27__namespace.default.createElement("div", {
|
|
23621
|
+
className: "flex items-center justify-end gap-1.5"
|
|
23622
|
+
}, !m.isOwnerRole && String(m.inviteStatus ?? "") === "pending" && /* @__PURE__ */ React27__namespace.default.createElement(Button, {
|
|
23623
|
+
type: "button",
|
|
23624
|
+
variant: "outline",
|
|
23625
|
+
size: "icon",
|
|
23626
|
+
className: "h-7 w-7",
|
|
23627
|
+
disabled: resendingUserId === m.userId,
|
|
23628
|
+
onClick: /* @__PURE__ */ __name(() => void handleResendInvite(m.userId), "onClick"),
|
|
23629
|
+
title: "Resend invite"
|
|
23630
|
+
}, /* @__PURE__ */ React27__namespace.default.createElement(LucideIcons.RefreshCw, {
|
|
23631
|
+
className: `h-3.5 w-3.5 ${resendingUserId === m.userId ? "animate-spin text-blue-600" : ""}`
|
|
23632
|
+
}), /* @__PURE__ */ React27__namespace.default.createElement("span", {
|
|
23633
|
+
className: "sr-only"
|
|
23634
|
+
}, "Resend invite")), !m.isOwnerRole && /* @__PURE__ */ React27__namespace.default.createElement(Button, {
|
|
23532
23635
|
type: "button",
|
|
23533
23636
|
variant: "ghost",
|
|
23534
23637
|
size: "sm",
|
|
23535
23638
|
onClick: /* @__PURE__ */ __name(() => handleRemove(m.userId), "onClick")
|
|
23536
|
-
}, "Remove"))))))), /* @__PURE__ */ React27__namespace.default.createElement("form", {
|
|
23639
|
+
}, "Remove")))))))), /* @__PURE__ */ React27__namespace.default.createElement("form", {
|
|
23537
23640
|
onSubmit: handleAdd,
|
|
23538
23641
|
className: "space-y-4 rounded-lg border border-gray-200 p-4"
|
|
23539
23642
|
}, /* @__PURE__ */ React27__namespace.default.createElement("h2", {
|
|
@@ -24598,7 +24701,10 @@ function OrderDetailPage({ orderId }) {
|
|
|
24598
24701
|
const [cancelModal, setCancelModal] = React27.useState(null);
|
|
24599
24702
|
const [assignModalOpen, setAssignModalOpen] = React27.useState(false);
|
|
24600
24703
|
const [assigneesList, setAssigneesList] = React27.useState([]);
|
|
24704
|
+
const [vendorsList, setVendorsList] = React27.useState([]);
|
|
24705
|
+
const [isPlatformAdmin, setIsPlatformAdmin] = React27.useState(false);
|
|
24601
24706
|
const [loadingAssignees, setLoadingAssignees] = React27.useState(false);
|
|
24707
|
+
const [selectedVendorId, setSelectedVendorId] = React27.useState(null);
|
|
24602
24708
|
const [selectedAssigneeId, setSelectedAssigneeId] = React27.useState(null);
|
|
24603
24709
|
const [savingAssignee, setSavingAssignee] = React27.useState(false);
|
|
24604
24710
|
const [completeModalOpen, setCompleteModalOpen] = React27.useState(false);
|
|
@@ -24698,8 +24804,9 @@ function OrderDetailPage({ orderId }) {
|
|
|
24698
24804
|
async function handleStartProcessing() {
|
|
24699
24805
|
if (!order || startingProcessing) return;
|
|
24700
24806
|
const currentAssigneeId = order.assignedUserId ?? order.assignedUser?.id ?? null;
|
|
24701
|
-
|
|
24702
|
-
|
|
24807
|
+
const currentVendorId = order.vendorId ?? order.vendor?.id ?? null;
|
|
24808
|
+
if (!currentAssigneeId && !currentVendorId) {
|
|
24809
|
+
sonner.toast.info("Please assign a vendor or teammate before starting order processing.");
|
|
24703
24810
|
handleOpenAssignModal();
|
|
24704
24811
|
return;
|
|
24705
24812
|
}
|
|
@@ -24712,7 +24819,9 @@ function OrderDetailPage({ orderId }) {
|
|
|
24712
24819
|
},
|
|
24713
24820
|
body: JSON.stringify({
|
|
24714
24821
|
status: "processing",
|
|
24715
|
-
|
|
24822
|
+
...currentAssigneeId ? {
|
|
24823
|
+
assignedUserId: currentAssigneeId
|
|
24824
|
+
} : {}
|
|
24716
24825
|
})
|
|
24717
24826
|
});
|
|
24718
24827
|
const data = await res.json().catch(() => ({}));
|
|
@@ -24734,14 +24843,19 @@ function OrderDetailPage({ orderId }) {
|
|
|
24734
24843
|
setAssignModalOpen(true);
|
|
24735
24844
|
setLoadingAssignees(true);
|
|
24736
24845
|
setSelectedAssigneeId(order?.assignedUserId ?? null);
|
|
24846
|
+
setSelectedVendorId(order?.vendorId ?? null);
|
|
24737
24847
|
try {
|
|
24738
24848
|
const res = await fetch(`/api/orders/${orderId}/eligible-assignees`);
|
|
24739
24849
|
if (res.ok) {
|
|
24740
24850
|
const data = await res.json();
|
|
24741
24851
|
setAssigneesList(data.assignees ?? []);
|
|
24852
|
+
setVendorsList(data.vendors ?? []);
|
|
24853
|
+
setIsPlatformAdmin(Boolean(data.isPlatformAdmin));
|
|
24854
|
+
if (data.currentVendorId != null) setSelectedVendorId(data.currentVendorId);
|
|
24855
|
+
if (data.currentAssignedUserId != null) setSelectedAssigneeId(data.currentAssignedUserId);
|
|
24742
24856
|
}
|
|
24743
24857
|
} catch {
|
|
24744
|
-
sonner.toast.error("Failed to load eligible
|
|
24858
|
+
sonner.toast.error("Failed to load eligible assignees");
|
|
24745
24859
|
} finally {
|
|
24746
24860
|
setLoadingAssignees(false);
|
|
24747
24861
|
}
|
|
@@ -24754,6 +24868,14 @@ function OrderDetailPage({ orderId }) {
|
|
|
24754
24868
|
const bodyPayload = {
|
|
24755
24869
|
assignedUserId: selectedAssigneeId
|
|
24756
24870
|
};
|
|
24871
|
+
if (isPlatformAdmin) {
|
|
24872
|
+
const chosenAssignee = assigneesList.find((a) => a.id === selectedAssigneeId);
|
|
24873
|
+
if (chosenAssignee && chosenAssignee.vendorId === null) {
|
|
24874
|
+
bodyPayload.vendorId = 1;
|
|
24875
|
+
} else {
|
|
24876
|
+
bodyPayload.vendorId = chosenAssignee?.vendorId ?? (selectedVendorId || 1);
|
|
24877
|
+
}
|
|
24878
|
+
}
|
|
24757
24879
|
const res = await fetch(`/api/orders/${orderId}`, {
|
|
24758
24880
|
method: "PUT",
|
|
24759
24881
|
headers: {
|
|
@@ -24763,15 +24885,15 @@ function OrderDetailPage({ orderId }) {
|
|
|
24763
24885
|
});
|
|
24764
24886
|
if (!res.ok) {
|
|
24765
24887
|
const err = await res.json().catch(() => ({}));
|
|
24766
|
-
sonner.toast.error(err.error ?? "Failed to update
|
|
24888
|
+
sonner.toast.error(err.error ?? "Failed to update assignment");
|
|
24767
24889
|
return;
|
|
24768
24890
|
}
|
|
24769
24891
|
const updated = await fetch(`/api/orders/${orderId}`);
|
|
24770
24892
|
if (updated.ok) setOrder(await updated.json());
|
|
24771
24893
|
setAssignModalOpen(false);
|
|
24772
|
-
sonner.toast.success(selectedAssigneeId ? "Order assigned successfully" : "
|
|
24894
|
+
sonner.toast.success(selectedAssigneeId || selectedVendorId ? "Order assigned successfully" : "Assignment removed");
|
|
24773
24895
|
} catch {
|
|
24774
|
-
sonner.toast.error("Failed to update
|
|
24896
|
+
sonner.toast.error("Failed to update assignment");
|
|
24775
24897
|
} finally {
|
|
24776
24898
|
setSavingAssignee(false);
|
|
24777
24899
|
}
|
|
@@ -25156,7 +25278,19 @@ function OrderDetailPage({ orderId }) {
|
|
|
25156
25278
|
className: "min-w-0"
|
|
25157
25279
|
}, /* @__PURE__ */ React.createElement("dt", {
|
|
25158
25280
|
className: "text-gray-500"
|
|
25159
|
-
}, "
|
|
25281
|
+
}, "Vendor"), /* @__PURE__ */ React.createElement("dd", {
|
|
25282
|
+
className: "font-medium text-gray-900 flex flex-wrap items-center gap-1.5 mt-0.5"
|
|
25283
|
+
}, order.vendor ? /* @__PURE__ */ React.createElement("span", {
|
|
25284
|
+
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"
|
|
25285
|
+
}, order.vendor.name || `Vendor #${order.vendor.id}`) : order.vendorId ? /* @__PURE__ */ React.createElement("span", {
|
|
25286
|
+
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"
|
|
25287
|
+
}, `Vendor #${order.vendorId}`) : /* @__PURE__ */ React.createElement("span", {
|
|
25288
|
+
className: "text-gray-400 text-xs italic"
|
|
25289
|
+
}, "Platform Default"))), /* @__PURE__ */ React.createElement("div", {
|
|
25290
|
+
className: "min-w-0"
|
|
25291
|
+
}, /* @__PURE__ */ React.createElement("dt", {
|
|
25292
|
+
className: "text-gray-500"
|
|
25293
|
+
}, "Assigned Teammate"), /* @__PURE__ */ React.createElement("dd", {
|
|
25160
25294
|
className: "font-medium text-gray-900 flex flex-wrap items-center gap-1.5 mt-0.5"
|
|
25161
25295
|
}, order.assignedUser ? /* @__PURE__ */ React.createElement("span", {
|
|
25162
25296
|
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"
|
|
@@ -25425,7 +25559,7 @@ function OrderDetailPage({ orderId }) {
|
|
|
25425
25559
|
className: "text-base font-semibold text-gray-900 flex items-center gap-2"
|
|
25426
25560
|
}, /* @__PURE__ */ React.createElement(LucideIcons.UserCheck, {
|
|
25427
25561
|
className: "h-5 w-5 text-blue-600"
|
|
25428
|
-
}), "Assign Order to Teammate"), /* @__PURE__ */ React.createElement("button", {
|
|
25562
|
+
}), isPlatformAdmin ? "Assign Order (Vendor & Team)" : "Assign Order to Teammate"), /* @__PURE__ */ React.createElement("button", {
|
|
25429
25563
|
type: "button",
|
|
25430
25564
|
onClick: /* @__PURE__ */ __name(() => setAssignModalOpen(false), "onClick"),
|
|
25431
25565
|
className: "text-gray-400 hover:text-gray-600"
|
|
@@ -25433,14 +25567,42 @@ function OrderDetailPage({ orderId }) {
|
|
|
25433
25567
|
className: "h-5 w-5"
|
|
25434
25568
|
}))), /* @__PURE__ */ React.createElement("p", {
|
|
25435
25569
|
className: "text-xs text-gray-500"
|
|
25436
|
-
}, "Select a team
|
|
25570
|
+
}, 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", {
|
|
25437
25571
|
className: "flex justify-center py-8"
|
|
25438
25572
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
|
|
25439
25573
|
className: "h-6 w-6 animate-spin text-blue-600"
|
|
25440
25574
|
})) : /* @__PURE__ */ React.createElement("div", {
|
|
25441
|
-
className: "space-y-
|
|
25575
|
+
className: "space-y-4"
|
|
25576
|
+
}, isPlatformAdmin && vendorsList.length > 0 && /* @__PURE__ */ React.createElement("div", {
|
|
25577
|
+
className: "space-y-1.5"
|
|
25578
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
25579
|
+
className: "block text-xs font-semibold text-gray-700"
|
|
25580
|
+
}, "Assigned Vendor"), /* @__PURE__ */ React.createElement("select", {
|
|
25581
|
+
value: selectedVendorId ?? "",
|
|
25582
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
25583
|
+
const val = e.target.value ? Number(e.target.value) : null;
|
|
25584
|
+
setSelectedVendorId(val);
|
|
25585
|
+
if (val && selectedAssigneeId) {
|
|
25586
|
+
const assignee = assigneesList.find((a) => a.id === selectedAssigneeId);
|
|
25587
|
+
if (assignee && assignee.vendorId !== val) {
|
|
25588
|
+
setSelectedAssigneeId(null);
|
|
25589
|
+
}
|
|
25590
|
+
}
|
|
25591
|
+
}, "onChange"),
|
|
25592
|
+
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"
|
|
25593
|
+
}, /* @__PURE__ */ React.createElement("option", {
|
|
25594
|
+
value: ""
|
|
25595
|
+
}, "All Vendors / Any Vendor"), vendorsList.map((v) => /* @__PURE__ */ React.createElement("option", {
|
|
25596
|
+
key: v.id,
|
|
25597
|
+
value: v.id
|
|
25598
|
+
}, v.name)))), /* @__PURE__ */ React.createElement("div", {
|
|
25599
|
+
className: "space-y-1.5"
|
|
25442
25600
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
25443
|
-
className:
|
|
25601
|
+
className: "block text-xs font-semibold text-gray-700"
|
|
25602
|
+
}, isPlatformAdmin ? "Assigned Team Member" : "Select Team Member"), /* @__PURE__ */ React.createElement("div", {
|
|
25603
|
+
className: "space-y-2 max-h-56 overflow-y-auto pr-1"
|
|
25604
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
25605
|
+
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"}`
|
|
25444
25606
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
25445
25607
|
className: "flex items-center gap-3"
|
|
25446
25608
|
}, /* @__PURE__ */ React.createElement("input", {
|
|
@@ -25451,26 +25613,40 @@ function OrderDetailPage({ orderId }) {
|
|
|
25451
25613
|
className: "text-blue-600"
|
|
25452
25614
|
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", {
|
|
25453
25615
|
className: "text-sm font-medium text-gray-800"
|
|
25454
|
-
}, "Unassigned"), /* @__PURE__ */ React.createElement("p", {
|
|
25616
|
+
}, "Unassigned to specific person"), /* @__PURE__ */ React.createElement("p", {
|
|
25455
25617
|
className: "text-xs text-gray-400"
|
|
25456
|
-
}, "
|
|
25618
|
+
}, selectedVendorId ? "Vendor general queue" : "General queue")))), assigneesList.filter((a) => {
|
|
25619
|
+
if (!isPlatformAdmin) return true;
|
|
25620
|
+
if (selectedVendorId && selectedVendorId !== 1) {
|
|
25621
|
+
return a.vendorId === selectedVendorId;
|
|
25622
|
+
}
|
|
25623
|
+
if (selectedVendorId === 1) {
|
|
25624
|
+
return a.vendorId === 1 || a.vendorId === null;
|
|
25625
|
+
}
|
|
25626
|
+
return true;
|
|
25627
|
+
}).map((a) => /* @__PURE__ */ React.createElement("label", {
|
|
25457
25628
|
key: a.id,
|
|
25458
|
-
className: `flex items-center justify-between p-
|
|
25629
|
+
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"}`
|
|
25459
25630
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
25460
25631
|
className: "flex items-center gap-3"
|
|
25461
25632
|
}, /* @__PURE__ */ React.createElement("input", {
|
|
25462
25633
|
type: "radio",
|
|
25463
25634
|
name: "assignee",
|
|
25464
25635
|
checked: selectedAssigneeId === a.id,
|
|
25465
|
-
onChange: /* @__PURE__ */ __name(() =>
|
|
25636
|
+
onChange: /* @__PURE__ */ __name(() => {
|
|
25637
|
+
setSelectedAssigneeId(a.id);
|
|
25638
|
+
if (isPlatformAdmin) {
|
|
25639
|
+
setSelectedVendorId(a.vendorId || 1);
|
|
25640
|
+
}
|
|
25641
|
+
}, "onChange"),
|
|
25466
25642
|
className: "text-blue-600"
|
|
25467
25643
|
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", {
|
|
25468
25644
|
className: "text-sm font-medium text-gray-900"
|
|
25469
25645
|
}, a.name), /* @__PURE__ */ React.createElement("p", {
|
|
25470
25646
|
className: "text-xs text-gray-500"
|
|
25471
|
-
}, a.email))), /* @__PURE__ */ React.createElement("span", {
|
|
25472
|
-
className: "text-[11px] font-medium text-gray-
|
|
25473
|
-
}, a.role)))), /* @__PURE__ */ React.createElement("div", {
|
|
25647
|
+
}, a.email, isPlatformAdmin && a.vendorName ? ` \u2022 ${a.vendorName}` : ""))), /* @__PURE__ */ React.createElement("span", {
|
|
25648
|
+
className: "text-[11px] font-medium text-gray-600 bg-gray-100 px-2 py-0.5 rounded"
|
|
25649
|
+
}, a.role)))))), /* @__PURE__ */ React.createElement("div", {
|
|
25474
25650
|
className: "flex justify-end gap-2 pt-2 border-t border-gray-100"
|
|
25475
25651
|
}, /* @__PURE__ */ React.createElement(Button, {
|
|
25476
25652
|
variant: "outline",
|
|
@@ -25480,7 +25656,7 @@ function OrderDetailPage({ orderId }) {
|
|
|
25480
25656
|
onClick: handleSaveAssignee,
|
|
25481
25657
|
disabled: savingAssignee,
|
|
25482
25658
|
className: "bg-blue-600 hover:bg-blue-700 text-white"
|
|
25483
|
-
}, savingAssignee ? "Saving..." : "Save
|
|
25659
|
+
}, savingAssignee ? "Saving..." : "Save Assignment")))), completeModalOpen && /* @__PURE__ */ React.createElement("div", {
|
|
25484
25660
|
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"
|
|
25485
25661
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
25486
25662
|
className: "bg-white rounded-xl shadow-2xl w-full max-w-md p-6 space-y-4 border border-gray-100"
|
|
@@ -27044,6 +27220,10 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27044
27220
|
sonner.toast.error(placeForSomeoneElse ? "Enter the contact's email address" : "Select a customer or enter an email address");
|
|
27045
27221
|
return;
|
|
27046
27222
|
}
|
|
27223
|
+
if (!effectivePhone) {
|
|
27224
|
+
sonner.toast.error(placeForSomeoneElse ? "Enter the recipient's phone number" : "Please enter the customer's phone number");
|
|
27225
|
+
return;
|
|
27226
|
+
}
|
|
27047
27227
|
if (lines.length === 0) {
|
|
27048
27228
|
sonner.toast.error("Add at least one product to the cart");
|
|
27049
27229
|
return;
|
|
@@ -27275,7 +27455,8 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27275
27455
|
id: "c-phone",
|
|
27276
27456
|
value: contactPhone,
|
|
27277
27457
|
onChange: /* @__PURE__ */ __name((e) => setContactPhone(e.target.value), "onChange"),
|
|
27278
|
-
placeholder: "phone number"
|
|
27458
|
+
placeholder: "phone number",
|
|
27459
|
+
required: contactId == null && !placeForSomeoneElse
|
|
27279
27460
|
}))), /* @__PURE__ */ React.createElement("div", {
|
|
27280
27461
|
className: "flex items-start gap-3 rounded-lg border border-dashed border-gray-300 bg-gray-50/60 px-4 py-3 mt-1"
|
|
27281
27462
|
}, /* @__PURE__ */ React.createElement(Checkbox, {
|
|
@@ -27351,6 +27532,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27351
27532
|
value: subContactPhone,
|
|
27352
27533
|
onChange: /* @__PURE__ */ __name((e) => setSubContactPhone(e.target.value), "onChange"),
|
|
27353
27534
|
placeholder: "phone number",
|
|
27535
|
+
required: placeForSomeoneElse && subContactId == null,
|
|
27354
27536
|
readOnly: subContactId != null,
|
|
27355
27537
|
className: subContactId != null ? "bg-gray-100 cursor-not-allowed" : ""
|
|
27356
27538
|
})))), /* @__PURE__ */ React.createElement("div", {
|