@lodashventure/medusa-quotation 1.4.18 → 1.4.20
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/.medusa/server/src/admin/index.js +368 -368
- package/.medusa/server/src/admin/index.mjs +369 -369
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
3
|
import { DocumentText, CheckCircleSolid, Plus, Trash } from "@medusajs/icons";
|
|
4
|
-
import { clx, FocusModal, Heading, Text, Button, Table, Badge, Label, DatePicker, Input, toast, Container, Select, Textarea, createDataTableColumnHelper, useDataTable, DataTable, Toaster, usePrompt, Hint, CurrencyInput
|
|
4
|
+
import { clx, FocusModal, Heading, Text, Button, Table, Badge, Label, DatePicker, Input, toast, Container, Select, Textarea, createDataTableColumnHelper, useDataTable, DataTable, Toaster, usePrompt, IconButton, Hint, CurrencyInput } from "@medusajs/ui";
|
|
5
5
|
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
6
6
|
import { useNavigate, useParams, Link } from "react-router-dom";
|
|
7
7
|
import { XCircle, Clock, CheckCircle } from "lucide-react";
|
|
@@ -1177,7 +1177,7 @@ const StoreConfig = () => {
|
|
|
1177
1177
|
const onSubmit = async (data) => {
|
|
1178
1178
|
try {
|
|
1179
1179
|
setIsLoading(true);
|
|
1180
|
-
await updateQuoteStore.
|
|
1180
|
+
await updateQuoteStore.mutate(data);
|
|
1181
1181
|
toast.success("Company information saved successfully");
|
|
1182
1182
|
setOpen(false);
|
|
1183
1183
|
} catch (error) {
|
|
@@ -2440,160 +2440,77 @@ const QuoteDetails = () => {
|
|
|
2440
2440
|
/* @__PURE__ */ jsx(Toaster, {})
|
|
2441
2441
|
] });
|
|
2442
2442
|
};
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
})
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
}
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
try {
|
|
2479
|
-
const addAction = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD");
|
|
2480
|
-
if (addAction == null ? void 0 : addAction.id) {
|
|
2481
|
-
await removeItem({
|
|
2482
|
-
itemId: addAction.id
|
|
2483
|
-
});
|
|
2484
|
-
} else {
|
|
2485
|
-
toast.warning("Cannot remove item, set quantity to 0 instead");
|
|
2486
|
-
await updateItem({
|
|
2487
|
-
quantity: 0,
|
|
2488
|
-
itemId: item.id
|
|
2443
|
+
const columnHelper$1 = createDataTableColumnHelper();
|
|
2444
|
+
const columns$1 = [
|
|
2445
|
+
columnHelper$1.accessor("email", {
|
|
2446
|
+
header: "Email",
|
|
2447
|
+
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2448
|
+
}),
|
|
2449
|
+
columnHelper$1.accessor("first_name", {
|
|
2450
|
+
header: "First Name",
|
|
2451
|
+
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2452
|
+
}),
|
|
2453
|
+
columnHelper$1.accessor("last_name", {
|
|
2454
|
+
header: "Last Name",
|
|
2455
|
+
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2456
|
+
}),
|
|
2457
|
+
columnHelper$1.accessor("company_name", {
|
|
2458
|
+
header: "Company",
|
|
2459
|
+
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() || "-" })
|
|
2460
|
+
})
|
|
2461
|
+
];
|
|
2462
|
+
const CustomerSelector = ({
|
|
2463
|
+
onSelectCustomer
|
|
2464
|
+
}) => {
|
|
2465
|
+
const [search, setSearch] = useState("");
|
|
2466
|
+
const [data, setData] = useState(null);
|
|
2467
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
2468
|
+
const debouncedSearchHandler = debounce((value) => {
|
|
2469
|
+
setSearch(value);
|
|
2470
|
+
}, 500);
|
|
2471
|
+
useEffect(() => {
|
|
2472
|
+
const fetchCustomers = async () => {
|
|
2473
|
+
try {
|
|
2474
|
+
setIsLoading(true);
|
|
2475
|
+
const response = await sdk.admin.customer.list({
|
|
2476
|
+
q: search,
|
|
2477
|
+
limit: 20
|
|
2489
2478
|
});
|
|
2479
|
+
setData(response);
|
|
2480
|
+
} catch (error) {
|
|
2481
|
+
console.error("Error fetching customers:", error);
|
|
2482
|
+
} finally {
|
|
2483
|
+
setIsLoading(false);
|
|
2490
2484
|
}
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center justify-between", children: [
|
|
2502
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-row items-center gap-x-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
2503
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2504
|
-
/* @__PURE__ */ jsxs(Text, { className: "txt-small", as: "span", weight: "plus", children: [
|
|
2505
|
-
item.title,
|
|
2506
|
-
" "
|
|
2507
|
-
] }),
|
|
2508
|
-
item.variant_sku && /* @__PURE__ */ jsxs("span", { children: [
|
|
2509
|
-
"(",
|
|
2510
|
-
item.variant_sku,
|
|
2511
|
-
")"
|
|
2512
|
-
] })
|
|
2513
|
-
] }),
|
|
2514
|
-
/* @__PURE__ */ jsx(Text, { as: "div", className: "text-ui-fg-subtle txt-small", children: item.product_title })
|
|
2515
|
-
] }) }),
|
|
2516
|
-
isItemUpdated && /* @__PURE__ */ jsx(
|
|
2517
|
-
Badge,
|
|
2518
|
-
{
|
|
2519
|
-
size: "2xsmall",
|
|
2520
|
-
rounded: "full",
|
|
2521
|
-
color: "orange",
|
|
2522
|
-
className: "mr-1",
|
|
2523
|
-
children: "Modified"
|
|
2524
|
-
}
|
|
2525
|
-
)
|
|
2526
|
-
] }),
|
|
2527
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 justify-between", children: [
|
|
2528
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-grow items-center gap-2", children: [
|
|
2529
|
-
/* @__PURE__ */ jsx(
|
|
2530
|
-
Input,
|
|
2531
|
-
{
|
|
2532
|
-
className: "bg-ui-bg-base txt-small w-[67px] rounded-lg [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
|
|
2533
|
-
type: "number",
|
|
2534
|
-
min: item.detail.fulfilled_quantity,
|
|
2535
|
-
defaultValue: item.quantity,
|
|
2536
|
-
onBlur: (e) => {
|
|
2537
|
-
const val = e.target.value;
|
|
2538
|
-
const quantity = val === "" ? null : Number(val);
|
|
2539
|
-
if (quantity) {
|
|
2540
|
-
onUpdate({ quantity });
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2543
|
-
}
|
|
2544
|
-
),
|
|
2545
|
-
/* @__PURE__ */ jsx(Text, { className: "txt-small text-ui-fg-subtle", children: "Quantity" })
|
|
2546
|
-
] }),
|
|
2547
|
-
/* @__PURE__ */ jsx("div", { className: "text-ui-fg-subtle txt-small mr-2 flex flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
2548
|
-
Amount,
|
|
2549
|
-
{
|
|
2550
|
-
currencyCode,
|
|
2551
|
-
amount: item.total,
|
|
2552
|
-
originalAmount: originalItem == null ? void 0 : originalItem.total
|
|
2553
|
-
}
|
|
2554
|
-
) })
|
|
2555
|
-
] })
|
|
2556
|
-
] }),
|
|
2557
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-2 p-3 md:grid-cols-2", children: [
|
|
2558
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
2559
|
-
/* @__PURE__ */ jsx(Label, { children: "Price" }),
|
|
2560
|
-
/* @__PURE__ */ jsx(Hint, { className: "!mt-1", children: "Override the unit price of this product" })
|
|
2561
|
-
] }),
|
|
2562
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx("div", { className: "flex-grow", children: /* @__PURE__ */ jsx(
|
|
2563
|
-
CurrencyInput,
|
|
2564
|
-
{
|
|
2565
|
-
symbol: currencyCode,
|
|
2566
|
-
code: currencyCode,
|
|
2567
|
-
defaultValue: item.unit_price,
|
|
2568
|
-
type: "numeric",
|
|
2569
|
-
min: 0,
|
|
2570
|
-
onBlur: (e) => {
|
|
2571
|
-
onUpdate({
|
|
2572
|
-
unit_price: parseFloat(e.target.value),
|
|
2573
|
-
quantity: item.quantity
|
|
2574
|
-
});
|
|
2575
|
-
},
|
|
2576
|
-
className: "bg-ui-bg-field-component hover:bg-ui-bg-field-component-hover"
|
|
2577
|
-
}
|
|
2578
|
-
) }) })
|
|
2579
|
-
] }),
|
|
2580
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center px-3 pb-3", children: /* @__PURE__ */ jsx(
|
|
2581
|
-
Button,
|
|
2582
|
-
{
|
|
2583
|
-
type: "button",
|
|
2584
|
-
variant: "danger",
|
|
2585
|
-
size: "small",
|
|
2586
|
-
onClick: () => {
|
|
2587
|
-
onRemove();
|
|
2588
|
-
},
|
|
2589
|
-
children: "Remove"
|
|
2590
|
-
}
|
|
2591
|
-
) })
|
|
2592
|
-
]
|
|
2485
|
+
};
|
|
2486
|
+
fetchCustomers();
|
|
2487
|
+
}, [search]);
|
|
2488
|
+
const customers = (data == null ? void 0 : data.customers) || [];
|
|
2489
|
+
const table = useDataTable({
|
|
2490
|
+
data: customers,
|
|
2491
|
+
columns: columns$1,
|
|
2492
|
+
search: {
|
|
2493
|
+
state: search,
|
|
2494
|
+
onSearchChange: setSearch
|
|
2593
2495
|
},
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2496
|
+
getRowId: (customer) => customer.id,
|
|
2497
|
+
onRowClick: (_, row) => {
|
|
2498
|
+
onSelectCustomer == null ? void 0 : onSelectCustomer(row.original);
|
|
2499
|
+
},
|
|
2500
|
+
isLoading
|
|
2501
|
+
});
|
|
2502
|
+
return /* @__PURE__ */ jsxs(Container, { children: [
|
|
2503
|
+
/* @__PURE__ */ jsx(
|
|
2504
|
+
Input,
|
|
2505
|
+
{
|
|
2506
|
+
className: "text-lg py-2 mb-4",
|
|
2507
|
+
placeholder: "Search customers by email, name...",
|
|
2508
|
+
onChange: (e) => debouncedSearchHandler(e.target.value)
|
|
2509
|
+
}
|
|
2510
|
+
),
|
|
2511
|
+
/* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
|
|
2512
|
+
] });
|
|
2513
|
+
};
|
|
2597
2514
|
const useProductSearch = (search, selectedProductIds) => {
|
|
2598
2515
|
const [data, setData] = useState({ variants: [] });
|
|
2599
2516
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -2645,21 +2562,21 @@ const useProductSearch = (search, selectedProductIds) => {
|
|
|
2645
2562
|
}, [search, selectedProductIds.join(",")]);
|
|
2646
2563
|
return { data, isLoading, error };
|
|
2647
2564
|
};
|
|
2648
|
-
const columnHelper
|
|
2649
|
-
const columns
|
|
2650
|
-
columnHelper
|
|
2565
|
+
const columnHelper = createDataTableColumnHelper();
|
|
2566
|
+
const columns = [
|
|
2567
|
+
columnHelper.accessor("product.title", {
|
|
2651
2568
|
header: "Product",
|
|
2652
2569
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2653
2570
|
}),
|
|
2654
|
-
columnHelper
|
|
2571
|
+
columnHelper.accessor("title", {
|
|
2655
2572
|
header: "Variant",
|
|
2656
2573
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2657
2574
|
}),
|
|
2658
|
-
columnHelper
|
|
2575
|
+
columnHelper.accessor("sku", {
|
|
2659
2576
|
header: "SKU",
|
|
2660
2577
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2661
2578
|
}),
|
|
2662
|
-
columnHelper
|
|
2579
|
+
columnHelper.accessor("prices", {
|
|
2663
2580
|
header: "Price",
|
|
2664
2581
|
cell: (info) => {
|
|
2665
2582
|
var _a, _b;
|
|
@@ -2687,7 +2604,7 @@ const ProductSelector = ({
|
|
|
2687
2604
|
}, [products == null ? void 0 : products.variants, selectedProductIds]);
|
|
2688
2605
|
const table = useDataTable({
|
|
2689
2606
|
data: selectableProducts || [],
|
|
2690
|
-
columns
|
|
2607
|
+
columns,
|
|
2691
2608
|
search: {
|
|
2692
2609
|
state: search,
|
|
2693
2610
|
onSearchChange: setSearch
|
|
@@ -2709,213 +2626,19 @@ const ProductSelector = ({
|
|
|
2709
2626
|
/* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
|
|
2710
2627
|
] });
|
|
2711
2628
|
};
|
|
2712
|
-
const
|
|
2713
|
-
const { order: preview } = useOrderPreview(order.id);
|
|
2629
|
+
const CreateQuoteForm = () => {
|
|
2714
2630
|
const navigate = useNavigate();
|
|
2715
|
-
const
|
|
2631
|
+
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
|
2632
|
+
const [items, setItems] = useState([]);
|
|
2633
|
+
const [openCustomerModal, setOpenCustomerModal] = useState(false);
|
|
2716
2634
|
const [openProductModal, setOpenProductModal] = useState(false);
|
|
2717
|
-
const { mutateAsync:
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
toast.success("Successfully updated quote");
|
|
2725
|
-
} catch (e2) {
|
|
2726
|
-
toast.error("Error", {
|
|
2727
|
-
description: e2.message
|
|
2728
|
-
});
|
|
2729
|
-
}
|
|
2730
|
-
};
|
|
2731
|
-
const originalItemsMap = useMemo(() => {
|
|
2732
|
-
return new Map(order.items.map((item) => [item.id, item]));
|
|
2733
|
-
}, [order]);
|
|
2734
|
-
if (!preview) {
|
|
2735
|
-
return /* @__PURE__ */ jsx(Fragment, {});
|
|
2736
|
-
}
|
|
2737
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2738
|
-
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex h-full flex-col p-4 gap-2", children: [
|
|
2739
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2740
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-3 mt-8 flex items-center justify-between", children: [
|
|
2741
|
-
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Items" }),
|
|
2742
|
-
/* @__PURE__ */ jsx(
|
|
2743
|
-
Button,
|
|
2744
|
-
{
|
|
2745
|
-
type: "button",
|
|
2746
|
-
variant: "primary",
|
|
2747
|
-
size: "small",
|
|
2748
|
-
onClick: () => setOpenProductModal(true),
|
|
2749
|
-
children: "Add Item"
|
|
2750
|
-
}
|
|
2751
|
-
)
|
|
2752
|
-
] }),
|
|
2753
|
-
preview.items.map((item) => /* @__PURE__ */ jsx(
|
|
2754
|
-
ManageItem,
|
|
2755
|
-
{
|
|
2756
|
-
originalItem: originalItemsMap.get(item.id),
|
|
2757
|
-
item,
|
|
2758
|
-
orderId: order.id,
|
|
2759
|
-
currencyCode: order.currency_code
|
|
2760
|
-
},
|
|
2761
|
-
item.id
|
|
2762
|
-
))
|
|
2763
|
-
] }),
|
|
2764
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-8 border-y border-dotted py-4", children: [
|
|
2765
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
2766
|
-
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: "Current Total" }),
|
|
2767
|
-
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(order.total, order.currency_code) })
|
|
2768
|
-
] }),
|
|
2769
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
2770
|
-
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: "New Total" }),
|
|
2771
|
-
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(preview.total, order.currency_code) })
|
|
2772
|
-
] })
|
|
2773
|
-
] }),
|
|
2774
|
-
/* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-end gap-x-4", children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-x-2", children: /* @__PURE__ */ jsx(
|
|
2775
|
-
Button,
|
|
2776
|
-
{
|
|
2777
|
-
type: "submit",
|
|
2778
|
-
variant: "primary",
|
|
2779
|
-
size: "small",
|
|
2780
|
-
disabled: isRequesting,
|
|
2781
|
-
children: "Confirm Edit"
|
|
2782
|
-
},
|
|
2783
|
-
"submit-button"
|
|
2784
|
-
) }) })
|
|
2785
|
-
] }),
|
|
2786
|
-
/* @__PURE__ */ jsx(FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxs(FocusModal.Content, { children: [
|
|
2787
|
-
/* @__PURE__ */ jsx(FocusModal.Header, { title: "Add Product" }),
|
|
2788
|
-
/* @__PURE__ */ jsx(FocusModal.Body, { children: /* @__PURE__ */ jsx(
|
|
2789
|
-
ProductSelector,
|
|
2790
|
-
{
|
|
2791
|
-
selectedProductIds: order.items.map(
|
|
2792
|
-
(item) => item.variant_id ?? ""
|
|
2793
|
-
),
|
|
2794
|
-
onSelectProduct: (variant) => {
|
|
2795
|
-
var _a, _b;
|
|
2796
|
-
if (!((_a = variant.prices) == null ? void 0 : _a[0].amount)) {
|
|
2797
|
-
toast.error("Product price not found");
|
|
2798
|
-
return;
|
|
2799
|
-
}
|
|
2800
|
-
addItem({
|
|
2801
|
-
items: [
|
|
2802
|
-
{
|
|
2803
|
-
variant_id: variant.id,
|
|
2804
|
-
quantity: 1,
|
|
2805
|
-
unit_price: (_b = variant.prices) == null ? void 0 : _b[0].amount
|
|
2806
|
-
}
|
|
2807
|
-
]
|
|
2808
|
-
});
|
|
2809
|
-
setOpenProductModal(false);
|
|
2810
|
-
}
|
|
2811
|
-
}
|
|
2812
|
-
) })
|
|
2813
|
-
] }) })
|
|
2814
|
-
] });
|
|
2815
|
-
};
|
|
2816
|
-
const QuoteManage = () => {
|
|
2817
|
-
const { id } = useParams();
|
|
2818
|
-
const { quote, isLoading } = useQuote(id, {
|
|
2819
|
-
fields: "*draft_order.customer"
|
|
2820
|
-
});
|
|
2821
|
-
if (isLoading) {
|
|
2822
|
-
return /* @__PURE__ */ jsx(Fragment, {});
|
|
2823
|
-
}
|
|
2824
|
-
if (!quote) {
|
|
2825
|
-
throw "quote not found";
|
|
2826
|
-
}
|
|
2827
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2828
|
-
/* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
2829
|
-
/* @__PURE__ */ jsx(Heading, { className: "flex items-center justify-between px-6 py-4", children: "Manage Quote" }),
|
|
2830
|
-
/* @__PURE__ */ jsx(ManageQuoteForm, { order: quote.draft_order })
|
|
2831
|
-
] }),
|
|
2832
|
-
/* @__PURE__ */ jsx(Toaster, {})
|
|
2833
|
-
] });
|
|
2834
|
-
};
|
|
2835
|
-
const columnHelper = createDataTableColumnHelper();
|
|
2836
|
-
const columns = [
|
|
2837
|
-
columnHelper.accessor("email", {
|
|
2838
|
-
header: "Email",
|
|
2839
|
-
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2840
|
-
}),
|
|
2841
|
-
columnHelper.accessor("first_name", {
|
|
2842
|
-
header: "First Name",
|
|
2843
|
-
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2844
|
-
}),
|
|
2845
|
-
columnHelper.accessor("last_name", {
|
|
2846
|
-
header: "Last Name",
|
|
2847
|
-
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2848
|
-
}),
|
|
2849
|
-
columnHelper.accessor("company_name", {
|
|
2850
|
-
header: "Company",
|
|
2851
|
-
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() || "-" })
|
|
2852
|
-
})
|
|
2853
|
-
];
|
|
2854
|
-
const CustomerSelector = ({
|
|
2855
|
-
onSelectCustomer
|
|
2856
|
-
}) => {
|
|
2857
|
-
const [search, setSearch] = useState("");
|
|
2858
|
-
const [data, setData] = useState(null);
|
|
2859
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
2860
|
-
const debouncedSearchHandler = debounce((value) => {
|
|
2861
|
-
setSearch(value);
|
|
2862
|
-
}, 500);
|
|
2863
|
-
useEffect(() => {
|
|
2864
|
-
const fetchCustomers = async () => {
|
|
2865
|
-
try {
|
|
2866
|
-
setIsLoading(true);
|
|
2867
|
-
const response = await sdk.admin.customer.list({
|
|
2868
|
-
q: search,
|
|
2869
|
-
limit: 20
|
|
2870
|
-
});
|
|
2871
|
-
setData(response);
|
|
2872
|
-
} catch (error) {
|
|
2873
|
-
console.error("Error fetching customers:", error);
|
|
2874
|
-
} finally {
|
|
2875
|
-
setIsLoading(false);
|
|
2876
|
-
}
|
|
2877
|
-
};
|
|
2878
|
-
fetchCustomers();
|
|
2879
|
-
}, [search]);
|
|
2880
|
-
const customers = (data == null ? void 0 : data.customers) || [];
|
|
2881
|
-
const table = useDataTable({
|
|
2882
|
-
data: customers,
|
|
2883
|
-
columns,
|
|
2884
|
-
search: {
|
|
2885
|
-
state: search,
|
|
2886
|
-
onSearchChange: setSearch
|
|
2887
|
-
},
|
|
2888
|
-
getRowId: (customer) => customer.id,
|
|
2889
|
-
onRowClick: (_, row) => {
|
|
2890
|
-
onSelectCustomer == null ? void 0 : onSelectCustomer(row.original);
|
|
2891
|
-
},
|
|
2892
|
-
isLoading
|
|
2893
|
-
});
|
|
2894
|
-
return /* @__PURE__ */ jsxs(Container, { children: [
|
|
2895
|
-
/* @__PURE__ */ jsx(
|
|
2896
|
-
Input,
|
|
2897
|
-
{
|
|
2898
|
-
className: "text-lg py-2 mb-4",
|
|
2899
|
-
placeholder: "Search customers by email, name...",
|
|
2900
|
-
onChange: (e) => debouncedSearchHandler(e.target.value)
|
|
2901
|
-
}
|
|
2902
|
-
),
|
|
2903
|
-
/* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
|
|
2904
|
-
] });
|
|
2905
|
-
};
|
|
2906
|
-
const CreateQuoteForm = () => {
|
|
2907
|
-
const navigate = useNavigate();
|
|
2908
|
-
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
|
2909
|
-
const [items, setItems] = useState([]);
|
|
2910
|
-
const [openCustomerModal, setOpenCustomerModal] = useState(false);
|
|
2911
|
-
const [openProductModal, setOpenProductModal] = useState(false);
|
|
2912
|
-
const { mutateAsync: createQuote, isPending } = useCreateQuote({
|
|
2913
|
-
onSuccess: (data) => {
|
|
2914
|
-
toast.success("Quote created successfully");
|
|
2915
|
-
navigate(`/quotes/${data.quote.id}`);
|
|
2916
|
-
},
|
|
2917
|
-
onError: (error) => {
|
|
2918
|
-
toast.error(`Failed to create quote: ${error.message}`);
|
|
2635
|
+
const { mutateAsync: createQuote, isPending } = useCreateQuote({
|
|
2636
|
+
onSuccess: (data) => {
|
|
2637
|
+
toast.success("Quote created successfully");
|
|
2638
|
+
navigate(`/quotes/${data.quote.id}`);
|
|
2639
|
+
},
|
|
2640
|
+
onError: (error) => {
|
|
2641
|
+
toast.error(`Failed to create quote: ${error.message}`);
|
|
2919
2642
|
}
|
|
2920
2643
|
});
|
|
2921
2644
|
const handleAddProduct = (variant) => {
|
|
@@ -3148,6 +2871,283 @@ const CreateQuote = () => {
|
|
|
3148
2871
|
/* @__PURE__ */ jsx(Toaster, {})
|
|
3149
2872
|
] });
|
|
3150
2873
|
};
|
|
2874
|
+
function ManageItem({
|
|
2875
|
+
originalItem,
|
|
2876
|
+
item,
|
|
2877
|
+
currencyCode,
|
|
2878
|
+
orderId
|
|
2879
|
+
}) {
|
|
2880
|
+
const { mutateAsync: updateItem } = useUpdateQuoteItem(orderId);
|
|
2881
|
+
const { mutateAsync: removeItem } = useRemoveQuoteItem(orderId);
|
|
2882
|
+
const isItemUpdated = useMemo(
|
|
2883
|
+
() => {
|
|
2884
|
+
var _a;
|
|
2885
|
+
return !!((_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_UPDATE"));
|
|
2886
|
+
},
|
|
2887
|
+
[item]
|
|
2888
|
+
);
|
|
2889
|
+
const onUpdate = async ({
|
|
2890
|
+
quantity,
|
|
2891
|
+
unit_price
|
|
2892
|
+
}) => {
|
|
2893
|
+
if (typeof quantity === "number" && quantity <= item.detail.fulfilled_quantity) {
|
|
2894
|
+
toast.warning("Quantity should be greater than the fulfilled quantity");
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
2897
|
+
try {
|
|
2898
|
+
await updateItem({
|
|
2899
|
+
quantity,
|
|
2900
|
+
unit_price,
|
|
2901
|
+
itemId: item.id
|
|
2902
|
+
});
|
|
2903
|
+
} catch (e) {
|
|
2904
|
+
toast.error(e.message);
|
|
2905
|
+
}
|
|
2906
|
+
};
|
|
2907
|
+
const onRemove = async () => {
|
|
2908
|
+
var _a;
|
|
2909
|
+
try {
|
|
2910
|
+
const addAction = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD");
|
|
2911
|
+
if (addAction == null ? void 0 : addAction.id) {
|
|
2912
|
+
await removeItem({
|
|
2913
|
+
itemId: addAction.id
|
|
2914
|
+
});
|
|
2915
|
+
} else {
|
|
2916
|
+
toast.warning("Cannot remove item, set quantity to 0 instead");
|
|
2917
|
+
await updateItem({
|
|
2918
|
+
quantity: 0,
|
|
2919
|
+
itemId: item.id
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
} catch (e) {
|
|
2923
|
+
toast.error(e.message);
|
|
2924
|
+
}
|
|
2925
|
+
};
|
|
2926
|
+
return /* @__PURE__ */ jsxs(
|
|
2927
|
+
"div",
|
|
2928
|
+
{
|
|
2929
|
+
className: "bg-ui-bg-subtle shadow-elevation-card-rest my-2 rounded-xl ",
|
|
2930
|
+
children: [
|
|
2931
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-x-2 gap-y-2 p-3 text-sm md:flex-row", children: [
|
|
2932
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center justify-between", children: [
|
|
2933
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-row items-center gap-x-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
2934
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2935
|
+
/* @__PURE__ */ jsxs(Text, { className: "txt-small", as: "span", weight: "plus", children: [
|
|
2936
|
+
item.title,
|
|
2937
|
+
" "
|
|
2938
|
+
] }),
|
|
2939
|
+
item.variant_sku && /* @__PURE__ */ jsxs("span", { children: [
|
|
2940
|
+
"(",
|
|
2941
|
+
item.variant_sku,
|
|
2942
|
+
")"
|
|
2943
|
+
] })
|
|
2944
|
+
] }),
|
|
2945
|
+
/* @__PURE__ */ jsx(Text, { as: "div", className: "text-ui-fg-subtle txt-small", children: item.product_title })
|
|
2946
|
+
] }) }),
|
|
2947
|
+
isItemUpdated && /* @__PURE__ */ jsx(
|
|
2948
|
+
Badge,
|
|
2949
|
+
{
|
|
2950
|
+
size: "2xsmall",
|
|
2951
|
+
rounded: "full",
|
|
2952
|
+
color: "orange",
|
|
2953
|
+
className: "mr-1",
|
|
2954
|
+
children: "Modified"
|
|
2955
|
+
}
|
|
2956
|
+
)
|
|
2957
|
+
] }),
|
|
2958
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 justify-between", children: [
|
|
2959
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-grow items-center gap-2", children: [
|
|
2960
|
+
/* @__PURE__ */ jsx(
|
|
2961
|
+
Input,
|
|
2962
|
+
{
|
|
2963
|
+
className: "bg-ui-bg-base txt-small w-[67px] rounded-lg [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
|
|
2964
|
+
type: "number",
|
|
2965
|
+
min: item.detail.fulfilled_quantity,
|
|
2966
|
+
defaultValue: item.quantity,
|
|
2967
|
+
onBlur: (e) => {
|
|
2968
|
+
const val = e.target.value;
|
|
2969
|
+
const quantity = val === "" ? null : Number(val);
|
|
2970
|
+
if (quantity) {
|
|
2971
|
+
onUpdate({ quantity });
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
),
|
|
2976
|
+
/* @__PURE__ */ jsx(Text, { className: "txt-small text-ui-fg-subtle", children: "Quantity" })
|
|
2977
|
+
] }),
|
|
2978
|
+
/* @__PURE__ */ jsx("div", { className: "text-ui-fg-subtle txt-small mr-2 flex flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
2979
|
+
Amount,
|
|
2980
|
+
{
|
|
2981
|
+
currencyCode,
|
|
2982
|
+
amount: item.total,
|
|
2983
|
+
originalAmount: originalItem == null ? void 0 : originalItem.total
|
|
2984
|
+
}
|
|
2985
|
+
) })
|
|
2986
|
+
] })
|
|
2987
|
+
] }),
|
|
2988
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-2 p-3 md:grid-cols-2", children: [
|
|
2989
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
2990
|
+
/* @__PURE__ */ jsx(Label, { children: "Price" }),
|
|
2991
|
+
/* @__PURE__ */ jsx(Hint, { className: "!mt-1", children: "Override the unit price of this product" })
|
|
2992
|
+
] }),
|
|
2993
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx("div", { className: "flex-grow", children: /* @__PURE__ */ jsx(
|
|
2994
|
+
CurrencyInput,
|
|
2995
|
+
{
|
|
2996
|
+
symbol: currencyCode,
|
|
2997
|
+
code: currencyCode,
|
|
2998
|
+
defaultValue: item.unit_price,
|
|
2999
|
+
type: "numeric",
|
|
3000
|
+
min: 0,
|
|
3001
|
+
onBlur: (e) => {
|
|
3002
|
+
onUpdate({
|
|
3003
|
+
unit_price: parseFloat(e.target.value),
|
|
3004
|
+
quantity: item.quantity
|
|
3005
|
+
});
|
|
3006
|
+
},
|
|
3007
|
+
className: "bg-ui-bg-field-component hover:bg-ui-bg-field-component-hover"
|
|
3008
|
+
}
|
|
3009
|
+
) }) })
|
|
3010
|
+
] }),
|
|
3011
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center px-3 pb-3", children: /* @__PURE__ */ jsx(
|
|
3012
|
+
Button,
|
|
3013
|
+
{
|
|
3014
|
+
type: "button",
|
|
3015
|
+
variant: "danger",
|
|
3016
|
+
size: "small",
|
|
3017
|
+
onClick: () => {
|
|
3018
|
+
onRemove();
|
|
3019
|
+
},
|
|
3020
|
+
children: "Remove"
|
|
3021
|
+
}
|
|
3022
|
+
) })
|
|
3023
|
+
]
|
|
3024
|
+
},
|
|
3025
|
+
item.quantity
|
|
3026
|
+
);
|
|
3027
|
+
}
|
|
3028
|
+
const ManageQuoteForm = ({ order }) => {
|
|
3029
|
+
const { order: preview } = useOrderPreview(order.id);
|
|
3030
|
+
const navigate = useNavigate();
|
|
3031
|
+
const { id: quoteId } = useParams();
|
|
3032
|
+
const [openProductModal, setOpenProductModal] = useState(false);
|
|
3033
|
+
const { mutateAsync: addItem } = useAddQuoteItem(order.id);
|
|
3034
|
+
const { mutateAsync: confirmQuote, isPending: isRequesting } = useConfirmQuote(order.id);
|
|
3035
|
+
const handleSubmit = async (e) => {
|
|
3036
|
+
e.preventDefault();
|
|
3037
|
+
try {
|
|
3038
|
+
await confirmQuote();
|
|
3039
|
+
navigate(`/quotes/${quoteId}`);
|
|
3040
|
+
toast.success("Successfully updated quote");
|
|
3041
|
+
} catch (e2) {
|
|
3042
|
+
toast.error("Error", {
|
|
3043
|
+
description: e2.message
|
|
3044
|
+
});
|
|
3045
|
+
}
|
|
3046
|
+
};
|
|
3047
|
+
const originalItemsMap = useMemo(() => {
|
|
3048
|
+
return new Map(order.items.map((item) => [item.id, item]));
|
|
3049
|
+
}, [order]);
|
|
3050
|
+
if (!preview) {
|
|
3051
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
3052
|
+
}
|
|
3053
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3054
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex h-full flex-col p-4 gap-2", children: [
|
|
3055
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3056
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 mt-8 flex items-center justify-between", children: [
|
|
3057
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Items" }),
|
|
3058
|
+
/* @__PURE__ */ jsx(
|
|
3059
|
+
Button,
|
|
3060
|
+
{
|
|
3061
|
+
type: "button",
|
|
3062
|
+
variant: "primary",
|
|
3063
|
+
size: "small",
|
|
3064
|
+
onClick: () => setOpenProductModal(true),
|
|
3065
|
+
children: "Add Item"
|
|
3066
|
+
}
|
|
3067
|
+
)
|
|
3068
|
+
] }),
|
|
3069
|
+
preview.items.map((item) => /* @__PURE__ */ jsx(
|
|
3070
|
+
ManageItem,
|
|
3071
|
+
{
|
|
3072
|
+
originalItem: originalItemsMap.get(item.id),
|
|
3073
|
+
item,
|
|
3074
|
+
orderId: order.id,
|
|
3075
|
+
currencyCode: order.currency_code
|
|
3076
|
+
},
|
|
3077
|
+
item.id
|
|
3078
|
+
))
|
|
3079
|
+
] }),
|
|
3080
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-8 border-y border-dotted py-4", children: [
|
|
3081
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
3082
|
+
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: "Current Total" }),
|
|
3083
|
+
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(order.total, order.currency_code) })
|
|
3084
|
+
] }),
|
|
3085
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
3086
|
+
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: "New Total" }),
|
|
3087
|
+
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(preview.total, order.currency_code) })
|
|
3088
|
+
] })
|
|
3089
|
+
] }),
|
|
3090
|
+
/* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-end gap-x-4", children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-x-2", children: /* @__PURE__ */ jsx(
|
|
3091
|
+
Button,
|
|
3092
|
+
{
|
|
3093
|
+
type: "submit",
|
|
3094
|
+
variant: "primary",
|
|
3095
|
+
size: "small",
|
|
3096
|
+
disabled: isRequesting,
|
|
3097
|
+
children: "Confirm Edit"
|
|
3098
|
+
},
|
|
3099
|
+
"submit-button"
|
|
3100
|
+
) }) })
|
|
3101
|
+
] }),
|
|
3102
|
+
/* @__PURE__ */ jsx(FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxs(FocusModal.Content, { children: [
|
|
3103
|
+
/* @__PURE__ */ jsx(FocusModal.Header, { title: "Add Product" }),
|
|
3104
|
+
/* @__PURE__ */ jsx(FocusModal.Body, { children: /* @__PURE__ */ jsx(
|
|
3105
|
+
ProductSelector,
|
|
3106
|
+
{
|
|
3107
|
+
selectedProductIds: order.items.map(
|
|
3108
|
+
(item) => item.variant_id ?? ""
|
|
3109
|
+
),
|
|
3110
|
+
onSelectProduct: (variant) => {
|
|
3111
|
+
var _a, _b;
|
|
3112
|
+
if (!((_a = variant.prices) == null ? void 0 : _a[0].amount)) {
|
|
3113
|
+
toast.error("Product price not found");
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
3116
|
+
addItem({
|
|
3117
|
+
items: [
|
|
3118
|
+
{
|
|
3119
|
+
variant_id: variant.id,
|
|
3120
|
+
quantity: 1,
|
|
3121
|
+
unit_price: (_b = variant.prices) == null ? void 0 : _b[0].amount
|
|
3122
|
+
}
|
|
3123
|
+
]
|
|
3124
|
+
});
|
|
3125
|
+
setOpenProductModal(false);
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
) })
|
|
3129
|
+
] }) })
|
|
3130
|
+
] });
|
|
3131
|
+
};
|
|
3132
|
+
const QuoteManage = () => {
|
|
3133
|
+
const { id } = useParams();
|
|
3134
|
+
const { quote, isLoading } = useQuote(id, {
|
|
3135
|
+
fields: "*draft_order.customer"
|
|
3136
|
+
});
|
|
3137
|
+
if (isLoading) {
|
|
3138
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
3139
|
+
}
|
|
3140
|
+
if (!quote) {
|
|
3141
|
+
throw "quote not found";
|
|
3142
|
+
}
|
|
3143
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3144
|
+
/* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
3145
|
+
/* @__PURE__ */ jsx(Heading, { className: "flex items-center justify-between px-6 py-4", children: "Manage Quote" }),
|
|
3146
|
+
/* @__PURE__ */ jsx(ManageQuoteForm, { order: quote.draft_order })
|
|
3147
|
+
] }),
|
|
3148
|
+
/* @__PURE__ */ jsx(Toaster, {})
|
|
3149
|
+
] });
|
|
3150
|
+
};
|
|
3151
3151
|
const widgetModule = { widgets: [] };
|
|
3152
3152
|
const routeModule = {
|
|
3153
3153
|
routes: [
|
|
@@ -3159,13 +3159,13 @@ const routeModule = {
|
|
|
3159
3159
|
Component: QuoteDetails,
|
|
3160
3160
|
path: "/quotes/:id"
|
|
3161
3161
|
},
|
|
3162
|
-
{
|
|
3163
|
-
Component: QuoteManage,
|
|
3164
|
-
path: "/quotes/:id/manage"
|
|
3165
|
-
},
|
|
3166
3162
|
{
|
|
3167
3163
|
Component: CreateQuote,
|
|
3168
3164
|
path: "/quotes/create"
|
|
3165
|
+
},
|
|
3166
|
+
{
|
|
3167
|
+
Component: QuoteManage,
|
|
3168
|
+
path: "/quotes/:id/manage"
|
|
3169
3169
|
}
|
|
3170
3170
|
]
|
|
3171
3171
|
};
|