@lodashventure/medusa-quotation 1.4.16 → 1.4.18
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 +376 -376
- package/.medusa/server/src/admin/index.mjs +377 -377
- 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,
|
|
4
|
+
import { clx, FocusModal, Heading, Text, Button, Table, Badge, Label, DatePicker, Input, toast, Container, Select, Textarea, createDataTableColumnHelper, useDataTable, DataTable, Toaster, usePrompt, Hint, CurrencyInput, IconButton } 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";
|
|
@@ -2440,77 +2440,160 @@ 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
|
-
|
|
2443
|
+
function ManageItem({
|
|
2444
|
+
originalItem,
|
|
2445
|
+
item,
|
|
2446
|
+
currencyCode,
|
|
2447
|
+
orderId
|
|
2448
|
+
}) {
|
|
2449
|
+
const { mutateAsync: updateItem } = useUpdateQuoteItem(orderId);
|
|
2450
|
+
const { mutateAsync: removeItem } = useRemoveQuoteItem(orderId);
|
|
2451
|
+
const isItemUpdated = useMemo(
|
|
2452
|
+
() => {
|
|
2453
|
+
var _a;
|
|
2454
|
+
return !!((_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_UPDATE"));
|
|
2455
|
+
},
|
|
2456
|
+
[item]
|
|
2457
|
+
);
|
|
2458
|
+
const onUpdate = async ({
|
|
2459
|
+
quantity,
|
|
2460
|
+
unit_price
|
|
2461
|
+
}) => {
|
|
2462
|
+
if (typeof quantity === "number" && quantity <= item.detail.fulfilled_quantity) {
|
|
2463
|
+
toast.warning("Quantity should be greater than the fulfilled quantity");
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
try {
|
|
2467
|
+
await updateItem({
|
|
2468
|
+
quantity,
|
|
2469
|
+
unit_price,
|
|
2470
|
+
itemId: item.id
|
|
2471
|
+
});
|
|
2472
|
+
} catch (e) {
|
|
2473
|
+
toast.error(e.message);
|
|
2474
|
+
}
|
|
2475
|
+
};
|
|
2476
|
+
const onRemove = async () => {
|
|
2477
|
+
var _a;
|
|
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
|
|
2478
2489
|
});
|
|
2479
|
-
setData(response);
|
|
2480
|
-
} catch (error) {
|
|
2481
|
-
console.error("Error fetching customers:", error);
|
|
2482
|
-
} finally {
|
|
2483
|
-
setIsLoading(false);
|
|
2484
2490
|
}
|
|
2485
|
-
}
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2491
|
+
} catch (e) {
|
|
2492
|
+
toast.error(e.message);
|
|
2493
|
+
}
|
|
2494
|
+
};
|
|
2495
|
+
return /* @__PURE__ */ jsxs(
|
|
2496
|
+
"div",
|
|
2497
|
+
{
|
|
2498
|
+
className: "bg-ui-bg-subtle shadow-elevation-card-rest my-2 rounded-xl ",
|
|
2499
|
+
children: [
|
|
2500
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-x-2 gap-y-2 p-3 text-sm md:flex-row", children: [
|
|
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
|
+
]
|
|
2499
2593
|
},
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
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
|
-
};
|
|
2594
|
+
item.quantity
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2514
2597
|
const useProductSearch = (search, selectedProductIds) => {
|
|
2515
2598
|
const [data, setData] = useState({ variants: [] });
|
|
2516
2599
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -2562,21 +2645,21 @@ const useProductSearch = (search, selectedProductIds) => {
|
|
|
2562
2645
|
}, [search, selectedProductIds.join(",")]);
|
|
2563
2646
|
return { data, isLoading, error };
|
|
2564
2647
|
};
|
|
2565
|
-
const columnHelper = createDataTableColumnHelper();
|
|
2566
|
-
const columns = [
|
|
2567
|
-
columnHelper.accessor("product.title", {
|
|
2648
|
+
const columnHelper$1 = createDataTableColumnHelper();
|
|
2649
|
+
const columns$1 = [
|
|
2650
|
+
columnHelper$1.accessor("product.title", {
|
|
2568
2651
|
header: "Product",
|
|
2569
2652
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2570
2653
|
}),
|
|
2571
|
-
columnHelper.accessor("title", {
|
|
2654
|
+
columnHelper$1.accessor("title", {
|
|
2572
2655
|
header: "Variant",
|
|
2573
2656
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2574
2657
|
}),
|
|
2575
|
-
columnHelper.accessor("sku", {
|
|
2658
|
+
columnHelper$1.accessor("sku", {
|
|
2576
2659
|
header: "SKU",
|
|
2577
2660
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2578
2661
|
}),
|
|
2579
|
-
columnHelper.accessor("prices", {
|
|
2662
|
+
columnHelper$1.accessor("prices", {
|
|
2580
2663
|
header: "Price",
|
|
2581
2664
|
cell: (info) => {
|
|
2582
2665
|
var _a, _b;
|
|
@@ -2604,7 +2687,7 @@ const ProductSelector = ({
|
|
|
2604
2687
|
}, [products == null ? void 0 : products.variants, selectedProductIds]);
|
|
2605
2688
|
const table = useDataTable({
|
|
2606
2689
|
data: selectableProducts || [],
|
|
2607
|
-
columns,
|
|
2690
|
+
columns: columns$1,
|
|
2608
2691
|
search: {
|
|
2609
2692
|
state: search,
|
|
2610
2693
|
onSearchChange: setSearch
|
|
@@ -2626,29 +2709,223 @@ const ProductSelector = ({
|
|
|
2626
2709
|
/* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
|
|
2627
2710
|
] });
|
|
2628
2711
|
};
|
|
2629
|
-
const
|
|
2712
|
+
const ManageQuoteForm = ({ order }) => {
|
|
2713
|
+
const { order: preview } = useOrderPreview(order.id);
|
|
2630
2714
|
const navigate = useNavigate();
|
|
2631
|
-
const
|
|
2632
|
-
const [items, setItems] = useState([]);
|
|
2633
|
-
const [openCustomerModal, setOpenCustomerModal] = useState(false);
|
|
2715
|
+
const { id: quoteId } = useParams();
|
|
2634
2716
|
const [openProductModal, setOpenProductModal] = useState(false);
|
|
2635
|
-
const { mutateAsync:
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2717
|
+
const { mutateAsync: addItem } = useAddQuoteItem(order.id);
|
|
2718
|
+
const { mutateAsync: confirmQuote, isPending: isRequesting } = useConfirmQuote(order.id);
|
|
2719
|
+
const handleSubmit = async (e) => {
|
|
2720
|
+
e.preventDefault();
|
|
2721
|
+
try {
|
|
2722
|
+
await confirmQuote();
|
|
2723
|
+
navigate(`/quotes/${quoteId}`);
|
|
2724
|
+
toast.success("Successfully updated quote");
|
|
2725
|
+
} catch (e2) {
|
|
2726
|
+
toast.error("Error", {
|
|
2727
|
+
description: e2.message
|
|
2728
|
+
});
|
|
2642
2729
|
}
|
|
2643
|
-
}
|
|
2644
|
-
const
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
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}`);
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2921
|
+
const handleAddProduct = (variant) => {
|
|
2922
|
+
var _a, _b;
|
|
2923
|
+
const newItem = {
|
|
2924
|
+
variant_id: variant.id,
|
|
2925
|
+
variant,
|
|
2926
|
+
quantity: 1,
|
|
2927
|
+
unit_price: ((_b = (_a = variant.prices) == null ? void 0 : _a[0]) == null ? void 0 : _b.amount) || 0
|
|
2928
|
+
};
|
|
2652
2929
|
setItems([...items, newItem]);
|
|
2653
2930
|
setOpenProductModal(false);
|
|
2654
2931
|
};
|
|
@@ -2871,283 +3148,6 @@ const CreateQuote = () => {
|
|
|
2871
3148
|
/* @__PURE__ */ jsx(Toaster, {})
|
|
2872
3149
|
] });
|
|
2873
3150
|
};
|
|
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: CreateQuote,
|
|
3164
|
-
path: "/quotes/create"
|
|
3165
|
-
},
|
|
3166
3162
|
{
|
|
3167
3163
|
Component: QuoteManage,
|
|
3168
3164
|
path: "/quotes/:id/manage"
|
|
3165
|
+
},
|
|
3166
|
+
{
|
|
3167
|
+
Component: CreateQuote,
|
|
3168
|
+
path: "/quotes/create"
|
|
3169
3169
|
}
|
|
3170
3170
|
]
|
|
3171
3171
|
};
|