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