@lodashventure/medusa-quotation 1.5.30 → 1.5.32
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/.medusa/server/src/api/admin/quotes/query-config.js +6 -39
- package/.medusa/server/src/api/store/customers/me/quotes/query-config.js +8 -43
- package/.medusa/server/src/api/store/customers/me/quotes/route.js +16 -33
- 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, CheckCircle, Clock } from "lucide-react";
|
|
@@ -2764,160 +2764,77 @@ const QuoteDetails = () => {
|
|
|
2764
2764
|
/* @__PURE__ */ jsx(Toaster, {})
|
|
2765
2765
|
] });
|
|
2766
2766
|
};
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
})
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
try {
|
|
2803
|
-
const addAction = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD");
|
|
2804
|
-
if (addAction == null ? void 0 : addAction.id) {
|
|
2805
|
-
await removeItem({
|
|
2806
|
-
itemId: addAction.id
|
|
2807
|
-
});
|
|
2808
|
-
} else {
|
|
2809
|
-
toast.warning("Cannot remove item, set quantity to 0 instead");
|
|
2810
|
-
await updateItem({
|
|
2811
|
-
quantity: 0,
|
|
2812
|
-
itemId: item.id
|
|
2767
|
+
const columnHelper$1 = createDataTableColumnHelper();
|
|
2768
|
+
const columns$1 = [
|
|
2769
|
+
columnHelper$1.accessor("email", {
|
|
2770
|
+
header: "Email",
|
|
2771
|
+
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2772
|
+
}),
|
|
2773
|
+
columnHelper$1.accessor("first_name", {
|
|
2774
|
+
header: "First Name",
|
|
2775
|
+
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2776
|
+
}),
|
|
2777
|
+
columnHelper$1.accessor("last_name", {
|
|
2778
|
+
header: "Last Name",
|
|
2779
|
+
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2780
|
+
}),
|
|
2781
|
+
columnHelper$1.accessor("company_name", {
|
|
2782
|
+
header: "Company",
|
|
2783
|
+
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() || "-" })
|
|
2784
|
+
})
|
|
2785
|
+
];
|
|
2786
|
+
const CustomerSelector = ({
|
|
2787
|
+
onSelectCustomer
|
|
2788
|
+
}) => {
|
|
2789
|
+
const [search, setSearch] = useState("");
|
|
2790
|
+
const [data, setData] = useState(null);
|
|
2791
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
2792
|
+
const debouncedSearchHandler = debounce((value) => {
|
|
2793
|
+
setSearch(value);
|
|
2794
|
+
}, 500);
|
|
2795
|
+
useEffect(() => {
|
|
2796
|
+
const fetchCustomers = async () => {
|
|
2797
|
+
try {
|
|
2798
|
+
setIsLoading(true);
|
|
2799
|
+
const response = await sdk.admin.customer.list({
|
|
2800
|
+
q: search,
|
|
2801
|
+
limit: 20
|
|
2813
2802
|
});
|
|
2803
|
+
setData(response);
|
|
2804
|
+
} catch (error) {
|
|
2805
|
+
console.error("Error fetching customers:", error);
|
|
2806
|
+
} finally {
|
|
2807
|
+
setIsLoading(false);
|
|
2814
2808
|
}
|
|
2815
|
-
}
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center justify-between", children: [
|
|
2826
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-row items-center gap-x-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
2827
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2828
|
-
/* @__PURE__ */ jsxs(Text, { className: "txt-small", as: "span", weight: "plus", children: [
|
|
2829
|
-
item.title,
|
|
2830
|
-
" "
|
|
2831
|
-
] }),
|
|
2832
|
-
item.variant_sku && /* @__PURE__ */ jsxs("span", { children: [
|
|
2833
|
-
"(",
|
|
2834
|
-
item.variant_sku,
|
|
2835
|
-
")"
|
|
2836
|
-
] })
|
|
2837
|
-
] }),
|
|
2838
|
-
/* @__PURE__ */ jsx(Text, { as: "div", className: "text-ui-fg-subtle txt-small", children: item.product_title })
|
|
2839
|
-
] }) }),
|
|
2840
|
-
isItemUpdated && /* @__PURE__ */ jsx(
|
|
2841
|
-
Badge,
|
|
2842
|
-
{
|
|
2843
|
-
size: "2xsmall",
|
|
2844
|
-
rounded: "full",
|
|
2845
|
-
color: "orange",
|
|
2846
|
-
className: "mr-1",
|
|
2847
|
-
children: "Modified"
|
|
2848
|
-
}
|
|
2849
|
-
)
|
|
2850
|
-
] }),
|
|
2851
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 justify-between", children: [
|
|
2852
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-grow items-center gap-2", children: [
|
|
2853
|
-
/* @__PURE__ */ jsx(
|
|
2854
|
-
Input,
|
|
2855
|
-
{
|
|
2856
|
-
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",
|
|
2857
|
-
type: "number",
|
|
2858
|
-
min: item.detail.fulfilled_quantity,
|
|
2859
|
-
defaultValue: item.quantity,
|
|
2860
|
-
onBlur: (e) => {
|
|
2861
|
-
const val = e.target.value;
|
|
2862
|
-
const quantity = val === "" ? null : Number(val);
|
|
2863
|
-
if (quantity) {
|
|
2864
|
-
onUpdate({ quantity });
|
|
2865
|
-
}
|
|
2866
|
-
}
|
|
2867
|
-
}
|
|
2868
|
-
),
|
|
2869
|
-
/* @__PURE__ */ jsx(Text, { className: "txt-small text-ui-fg-subtle", children: "Quantity" })
|
|
2870
|
-
] }),
|
|
2871
|
-
/* @__PURE__ */ jsx("div", { className: "text-ui-fg-subtle txt-small mr-2 flex flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
2872
|
-
Amount,
|
|
2873
|
-
{
|
|
2874
|
-
currencyCode,
|
|
2875
|
-
amount: item.total,
|
|
2876
|
-
originalAmount: originalItem == null ? void 0 : originalItem.total
|
|
2877
|
-
}
|
|
2878
|
-
) })
|
|
2879
|
-
] })
|
|
2880
|
-
] }),
|
|
2881
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-2 p-3 md:grid-cols-2", children: [
|
|
2882
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
2883
|
-
/* @__PURE__ */ jsx(Label, { children: "Price" }),
|
|
2884
|
-
/* @__PURE__ */ jsx(Hint, { className: "!mt-1", children: "Override the unit price of this product" })
|
|
2885
|
-
] }),
|
|
2886
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx("div", { className: "flex-grow", children: /* @__PURE__ */ jsx(
|
|
2887
|
-
CurrencyInput,
|
|
2888
|
-
{
|
|
2889
|
-
symbol: currencyCode,
|
|
2890
|
-
code: currencyCode,
|
|
2891
|
-
defaultValue: item.unit_price,
|
|
2892
|
-
type: "numeric",
|
|
2893
|
-
min: 0,
|
|
2894
|
-
onBlur: (e) => {
|
|
2895
|
-
onUpdate({
|
|
2896
|
-
unit_price: parseFloat(e.target.value),
|
|
2897
|
-
quantity: item.quantity
|
|
2898
|
-
});
|
|
2899
|
-
},
|
|
2900
|
-
className: "bg-ui-bg-field-component hover:bg-ui-bg-field-component-hover"
|
|
2901
|
-
}
|
|
2902
|
-
) }) })
|
|
2903
|
-
] }),
|
|
2904
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center px-3 pb-3", children: /* @__PURE__ */ jsx(
|
|
2905
|
-
Button,
|
|
2906
|
-
{
|
|
2907
|
-
type: "button",
|
|
2908
|
-
variant: "danger",
|
|
2909
|
-
size: "small",
|
|
2910
|
-
onClick: () => {
|
|
2911
|
-
onRemove();
|
|
2912
|
-
},
|
|
2913
|
-
children: "Remove"
|
|
2914
|
-
}
|
|
2915
|
-
) })
|
|
2916
|
-
]
|
|
2809
|
+
};
|
|
2810
|
+
fetchCustomers();
|
|
2811
|
+
}, [search]);
|
|
2812
|
+
const customers = (data == null ? void 0 : data.customers) || [];
|
|
2813
|
+
const table = useDataTable({
|
|
2814
|
+
data: customers,
|
|
2815
|
+
columns: columns$1,
|
|
2816
|
+
search: {
|
|
2817
|
+
state: search,
|
|
2818
|
+
onSearchChange: setSearch
|
|
2917
2819
|
},
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2820
|
+
getRowId: (customer) => customer.id,
|
|
2821
|
+
onRowClick: (_, row) => {
|
|
2822
|
+
onSelectCustomer == null ? void 0 : onSelectCustomer(row.original);
|
|
2823
|
+
},
|
|
2824
|
+
isLoading
|
|
2825
|
+
});
|
|
2826
|
+
return /* @__PURE__ */ jsxs(Container, { children: [
|
|
2827
|
+
/* @__PURE__ */ jsx(
|
|
2828
|
+
Input,
|
|
2829
|
+
{
|
|
2830
|
+
className: "text-lg py-2 mb-4",
|
|
2831
|
+
placeholder: "Search customers by email, name...",
|
|
2832
|
+
onChange: (e) => debouncedSearchHandler(e.target.value)
|
|
2833
|
+
}
|
|
2834
|
+
),
|
|
2835
|
+
/* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
|
|
2836
|
+
] });
|
|
2837
|
+
};
|
|
2921
2838
|
const useProductSearch = (search, selectedProductIds) => {
|
|
2922
2839
|
const [data, setData] = useState({ variants: [] });
|
|
2923
2840
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -2969,21 +2886,21 @@ const useProductSearch = (search, selectedProductIds) => {
|
|
|
2969
2886
|
}, [search, selectedProductIds.join(",")]);
|
|
2970
2887
|
return { data, isLoading, error };
|
|
2971
2888
|
};
|
|
2972
|
-
const columnHelper
|
|
2973
|
-
const columns
|
|
2974
|
-
columnHelper
|
|
2889
|
+
const columnHelper = createDataTableColumnHelper();
|
|
2890
|
+
const columns = [
|
|
2891
|
+
columnHelper.accessor("product.title", {
|
|
2975
2892
|
header: "Product",
|
|
2976
2893
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2977
2894
|
}),
|
|
2978
|
-
columnHelper
|
|
2895
|
+
columnHelper.accessor("title", {
|
|
2979
2896
|
header: "Variant",
|
|
2980
2897
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2981
2898
|
}),
|
|
2982
|
-
columnHelper
|
|
2899
|
+
columnHelper.accessor("sku", {
|
|
2983
2900
|
header: "SKU",
|
|
2984
2901
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2985
2902
|
}),
|
|
2986
|
-
columnHelper
|
|
2903
|
+
columnHelper.accessor("prices", {
|
|
2987
2904
|
header: "Price",
|
|
2988
2905
|
cell: (info) => {
|
|
2989
2906
|
var _a, _b;
|
|
@@ -3011,7 +2928,7 @@ const ProductSelector = ({
|
|
|
3011
2928
|
}, [products == null ? void 0 : products.variants, selectedProductIds]);
|
|
3012
2929
|
const table = useDataTable({
|
|
3013
2930
|
data: selectableProducts || [],
|
|
3014
|
-
columns
|
|
2931
|
+
columns,
|
|
3015
2932
|
search: {
|
|
3016
2933
|
state: search,
|
|
3017
2934
|
onSearchChange: setSearch
|
|
@@ -3033,213 +2950,19 @@ const ProductSelector = ({
|
|
|
3033
2950
|
/* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
|
|
3034
2951
|
] });
|
|
3035
2952
|
};
|
|
3036
|
-
const
|
|
3037
|
-
const { order: preview } = useOrderPreview(order.id);
|
|
2953
|
+
const CreateQuoteForm = () => {
|
|
3038
2954
|
const navigate = useNavigate();
|
|
3039
|
-
const
|
|
2955
|
+
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
|
2956
|
+
const [items, setItems] = useState([]);
|
|
2957
|
+
const [openCustomerModal, setOpenCustomerModal] = useState(false);
|
|
3040
2958
|
const [openProductModal, setOpenProductModal] = useState(false);
|
|
3041
|
-
const { mutateAsync:
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
toast.success("Successfully updated quote");
|
|
3049
|
-
} catch (e2) {
|
|
3050
|
-
toast.error("Error", {
|
|
3051
|
-
description: e2.message
|
|
3052
|
-
});
|
|
3053
|
-
}
|
|
3054
|
-
};
|
|
3055
|
-
const originalItemsMap = useMemo(() => {
|
|
3056
|
-
return new Map(order.items.map((item) => [item.id, item]));
|
|
3057
|
-
}, [order]);
|
|
3058
|
-
if (!preview) {
|
|
3059
|
-
return /* @__PURE__ */ jsx(Fragment, {});
|
|
3060
|
-
}
|
|
3061
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3062
|
-
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex h-full flex-col p-4 gap-2", children: [
|
|
3063
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
3064
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-3 mt-8 flex items-center justify-between", children: [
|
|
3065
|
-
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Items" }),
|
|
3066
|
-
/* @__PURE__ */ jsx(
|
|
3067
|
-
Button,
|
|
3068
|
-
{
|
|
3069
|
-
type: "button",
|
|
3070
|
-
variant: "primary",
|
|
3071
|
-
size: "small",
|
|
3072
|
-
onClick: () => setOpenProductModal(true),
|
|
3073
|
-
children: "Add Item"
|
|
3074
|
-
}
|
|
3075
|
-
)
|
|
3076
|
-
] }),
|
|
3077
|
-
preview.items.map((item) => /* @__PURE__ */ jsx(
|
|
3078
|
-
ManageItem,
|
|
3079
|
-
{
|
|
3080
|
-
originalItem: originalItemsMap.get(item.id),
|
|
3081
|
-
item,
|
|
3082
|
-
orderId: order.id,
|
|
3083
|
-
currencyCode: order.currency_code
|
|
3084
|
-
},
|
|
3085
|
-
item.id
|
|
3086
|
-
))
|
|
3087
|
-
] }),
|
|
3088
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-8 border-y border-dotted py-4", children: [
|
|
3089
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
3090
|
-
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: "Current Total" }),
|
|
3091
|
-
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(order.total, order.currency_code) })
|
|
3092
|
-
] }),
|
|
3093
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
3094
|
-
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: "New Total" }),
|
|
3095
|
-
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(preview.total, order.currency_code) })
|
|
3096
|
-
] })
|
|
3097
|
-
] }),
|
|
3098
|
-
/* @__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(
|
|
3099
|
-
Button,
|
|
3100
|
-
{
|
|
3101
|
-
type: "submit",
|
|
3102
|
-
variant: "primary",
|
|
3103
|
-
size: "small",
|
|
3104
|
-
disabled: isRequesting,
|
|
3105
|
-
children: "Confirm Edit"
|
|
3106
|
-
},
|
|
3107
|
-
"submit-button"
|
|
3108
|
-
) }) })
|
|
3109
|
-
] }),
|
|
3110
|
-
/* @__PURE__ */ jsx(FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxs(FocusModal.Content, { children: [
|
|
3111
|
-
/* @__PURE__ */ jsx(FocusModal.Header, { children: /* @__PURE__ */ jsx(FocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Add Product" }) }) }),
|
|
3112
|
-
/* @__PURE__ */ jsx(FocusModal.Body, { children: /* @__PURE__ */ jsx(
|
|
3113
|
-
ProductSelector,
|
|
3114
|
-
{
|
|
3115
|
-
selectedProductIds: order.items.map(
|
|
3116
|
-
(item) => item.variant_id ?? ""
|
|
3117
|
-
),
|
|
3118
|
-
onSelectProduct: (variant) => {
|
|
3119
|
-
var _a, _b;
|
|
3120
|
-
if (!((_a = variant.prices) == null ? void 0 : _a[0].amount)) {
|
|
3121
|
-
toast.error("Product price not found");
|
|
3122
|
-
return;
|
|
3123
|
-
}
|
|
3124
|
-
addItem({
|
|
3125
|
-
items: [
|
|
3126
|
-
{
|
|
3127
|
-
variant_id: variant.id,
|
|
3128
|
-
quantity: 1,
|
|
3129
|
-
unit_price: (_b = variant.prices) == null ? void 0 : _b[0].amount
|
|
3130
|
-
}
|
|
3131
|
-
]
|
|
3132
|
-
});
|
|
3133
|
-
setOpenProductModal(false);
|
|
3134
|
-
}
|
|
3135
|
-
}
|
|
3136
|
-
) })
|
|
3137
|
-
] }) })
|
|
3138
|
-
] });
|
|
3139
|
-
};
|
|
3140
|
-
const QuoteManage = () => {
|
|
3141
|
-
const { id } = useParams();
|
|
3142
|
-
const { quote, isLoading } = useQuote(id, {
|
|
3143
|
-
fields: "*draft_order.customer"
|
|
3144
|
-
});
|
|
3145
|
-
if (isLoading) {
|
|
3146
|
-
return /* @__PURE__ */ jsx(Fragment, {});
|
|
3147
|
-
}
|
|
3148
|
-
if (!quote) {
|
|
3149
|
-
throw "quote not found";
|
|
3150
|
-
}
|
|
3151
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3152
|
-
/* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
3153
|
-
/* @__PURE__ */ jsx(Heading, { className: "flex items-center justify-between px-6 py-4", children: "Manage Quote" }),
|
|
3154
|
-
/* @__PURE__ */ jsx(ManageQuoteForm, { order: quote.draft_order })
|
|
3155
|
-
] }),
|
|
3156
|
-
/* @__PURE__ */ jsx(Toaster, {})
|
|
3157
|
-
] });
|
|
3158
|
-
};
|
|
3159
|
-
const columnHelper = createDataTableColumnHelper();
|
|
3160
|
-
const columns = [
|
|
3161
|
-
columnHelper.accessor("email", {
|
|
3162
|
-
header: "Email",
|
|
3163
|
-
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
3164
|
-
}),
|
|
3165
|
-
columnHelper.accessor("first_name", {
|
|
3166
|
-
header: "First Name",
|
|
3167
|
-
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
3168
|
-
}),
|
|
3169
|
-
columnHelper.accessor("last_name", {
|
|
3170
|
-
header: "Last Name",
|
|
3171
|
-
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
3172
|
-
}),
|
|
3173
|
-
columnHelper.accessor("company_name", {
|
|
3174
|
-
header: "Company",
|
|
3175
|
-
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() || "-" })
|
|
3176
|
-
})
|
|
3177
|
-
];
|
|
3178
|
-
const CustomerSelector = ({
|
|
3179
|
-
onSelectCustomer
|
|
3180
|
-
}) => {
|
|
3181
|
-
const [search, setSearch] = useState("");
|
|
3182
|
-
const [data, setData] = useState(null);
|
|
3183
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
3184
|
-
const debouncedSearchHandler = debounce((value) => {
|
|
3185
|
-
setSearch(value);
|
|
3186
|
-
}, 500);
|
|
3187
|
-
useEffect(() => {
|
|
3188
|
-
const fetchCustomers = async () => {
|
|
3189
|
-
try {
|
|
3190
|
-
setIsLoading(true);
|
|
3191
|
-
const response = await sdk.admin.customer.list({
|
|
3192
|
-
q: search,
|
|
3193
|
-
limit: 20
|
|
3194
|
-
});
|
|
3195
|
-
setData(response);
|
|
3196
|
-
} catch (error) {
|
|
3197
|
-
console.error("Error fetching customers:", error);
|
|
3198
|
-
} finally {
|
|
3199
|
-
setIsLoading(false);
|
|
3200
|
-
}
|
|
3201
|
-
};
|
|
3202
|
-
fetchCustomers();
|
|
3203
|
-
}, [search]);
|
|
3204
|
-
const customers = (data == null ? void 0 : data.customers) || [];
|
|
3205
|
-
const table = useDataTable({
|
|
3206
|
-
data: customers,
|
|
3207
|
-
columns,
|
|
3208
|
-
search: {
|
|
3209
|
-
state: search,
|
|
3210
|
-
onSearchChange: setSearch
|
|
3211
|
-
},
|
|
3212
|
-
getRowId: (customer) => customer.id,
|
|
3213
|
-
onRowClick: (_, row) => {
|
|
3214
|
-
onSelectCustomer == null ? void 0 : onSelectCustomer(row.original);
|
|
3215
|
-
},
|
|
3216
|
-
isLoading
|
|
3217
|
-
});
|
|
3218
|
-
return /* @__PURE__ */ jsxs(Container, { children: [
|
|
3219
|
-
/* @__PURE__ */ jsx(
|
|
3220
|
-
Input,
|
|
3221
|
-
{
|
|
3222
|
-
className: "text-lg py-2 mb-4",
|
|
3223
|
-
placeholder: "Search customers by email, name...",
|
|
3224
|
-
onChange: (e) => debouncedSearchHandler(e.target.value)
|
|
3225
|
-
}
|
|
3226
|
-
),
|
|
3227
|
-
/* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
|
|
3228
|
-
] });
|
|
3229
|
-
};
|
|
3230
|
-
const CreateQuoteForm = () => {
|
|
3231
|
-
const navigate = useNavigate();
|
|
3232
|
-
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
|
3233
|
-
const [items, setItems] = useState([]);
|
|
3234
|
-
const [openCustomerModal, setOpenCustomerModal] = useState(false);
|
|
3235
|
-
const [openProductModal, setOpenProductModal] = useState(false);
|
|
3236
|
-
const { mutateAsync: createQuote, isPending } = useCreateQuote({
|
|
3237
|
-
onSuccess: (data) => {
|
|
3238
|
-
toast.success("Quote created successfully");
|
|
3239
|
-
navigate(`/quotes/${data.quote.id}`);
|
|
3240
|
-
},
|
|
3241
|
-
onError: (error) => {
|
|
3242
|
-
toast.error(`Failed to create quote: ${error.message}`);
|
|
2959
|
+
const { mutateAsync: createQuote, isPending } = useCreateQuote({
|
|
2960
|
+
onSuccess: (data) => {
|
|
2961
|
+
toast.success("Quote created successfully");
|
|
2962
|
+
navigate(`/quotes/${data.quote.id}`);
|
|
2963
|
+
},
|
|
2964
|
+
onError: (error) => {
|
|
2965
|
+
toast.error(`Failed to create quote: ${error.message}`);
|
|
3243
2966
|
}
|
|
3244
2967
|
});
|
|
3245
2968
|
const handleAddProduct = (variant) => {
|
|
@@ -3472,6 +3195,283 @@ const CreateQuote = () => {
|
|
|
3472
3195
|
/* @__PURE__ */ jsx(Toaster, {})
|
|
3473
3196
|
] });
|
|
3474
3197
|
};
|
|
3198
|
+
function ManageItem({
|
|
3199
|
+
originalItem,
|
|
3200
|
+
item,
|
|
3201
|
+
currencyCode,
|
|
3202
|
+
orderId
|
|
3203
|
+
}) {
|
|
3204
|
+
const { mutateAsync: updateItem } = useUpdateQuoteItem(orderId);
|
|
3205
|
+
const { mutateAsync: removeItem } = useRemoveQuoteItem(orderId);
|
|
3206
|
+
const isItemUpdated = useMemo(
|
|
3207
|
+
() => {
|
|
3208
|
+
var _a;
|
|
3209
|
+
return !!((_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_UPDATE"));
|
|
3210
|
+
},
|
|
3211
|
+
[item]
|
|
3212
|
+
);
|
|
3213
|
+
const onUpdate = async ({
|
|
3214
|
+
quantity,
|
|
3215
|
+
unit_price
|
|
3216
|
+
}) => {
|
|
3217
|
+
if (typeof quantity === "number" && quantity <= item.detail.fulfilled_quantity) {
|
|
3218
|
+
toast.warning("Quantity should be greater than the fulfilled quantity");
|
|
3219
|
+
return;
|
|
3220
|
+
}
|
|
3221
|
+
try {
|
|
3222
|
+
await updateItem({
|
|
3223
|
+
quantity,
|
|
3224
|
+
unit_price,
|
|
3225
|
+
itemId: item.id
|
|
3226
|
+
});
|
|
3227
|
+
} catch (e) {
|
|
3228
|
+
toast.error(e.message);
|
|
3229
|
+
}
|
|
3230
|
+
};
|
|
3231
|
+
const onRemove = async () => {
|
|
3232
|
+
var _a;
|
|
3233
|
+
try {
|
|
3234
|
+
const addAction = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD");
|
|
3235
|
+
if (addAction == null ? void 0 : addAction.id) {
|
|
3236
|
+
await removeItem({
|
|
3237
|
+
itemId: addAction.id
|
|
3238
|
+
});
|
|
3239
|
+
} else {
|
|
3240
|
+
toast.warning("Cannot remove item, set quantity to 0 instead");
|
|
3241
|
+
await updateItem({
|
|
3242
|
+
quantity: 0,
|
|
3243
|
+
itemId: item.id
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
} catch (e) {
|
|
3247
|
+
toast.error(e.message);
|
|
3248
|
+
}
|
|
3249
|
+
};
|
|
3250
|
+
return /* @__PURE__ */ jsxs(
|
|
3251
|
+
"div",
|
|
3252
|
+
{
|
|
3253
|
+
className: "bg-ui-bg-subtle shadow-elevation-card-rest my-2 rounded-xl ",
|
|
3254
|
+
children: [
|
|
3255
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-x-2 gap-y-2 p-3 text-sm md:flex-row", children: [
|
|
3256
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center justify-between", children: [
|
|
3257
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-row items-center gap-x-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
3258
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3259
|
+
/* @__PURE__ */ jsxs(Text, { className: "txt-small", as: "span", weight: "plus", children: [
|
|
3260
|
+
item.title,
|
|
3261
|
+
" "
|
|
3262
|
+
] }),
|
|
3263
|
+
item.variant_sku && /* @__PURE__ */ jsxs("span", { children: [
|
|
3264
|
+
"(",
|
|
3265
|
+
item.variant_sku,
|
|
3266
|
+
")"
|
|
3267
|
+
] })
|
|
3268
|
+
] }),
|
|
3269
|
+
/* @__PURE__ */ jsx(Text, { as: "div", className: "text-ui-fg-subtle txt-small", children: item.product_title })
|
|
3270
|
+
] }) }),
|
|
3271
|
+
isItemUpdated && /* @__PURE__ */ jsx(
|
|
3272
|
+
Badge,
|
|
3273
|
+
{
|
|
3274
|
+
size: "2xsmall",
|
|
3275
|
+
rounded: "full",
|
|
3276
|
+
color: "orange",
|
|
3277
|
+
className: "mr-1",
|
|
3278
|
+
children: "Modified"
|
|
3279
|
+
}
|
|
3280
|
+
)
|
|
3281
|
+
] }),
|
|
3282
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 justify-between", children: [
|
|
3283
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-grow items-center gap-2", children: [
|
|
3284
|
+
/* @__PURE__ */ jsx(
|
|
3285
|
+
Input,
|
|
3286
|
+
{
|
|
3287
|
+
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",
|
|
3288
|
+
type: "number",
|
|
3289
|
+
min: item.detail.fulfilled_quantity,
|
|
3290
|
+
defaultValue: item.quantity,
|
|
3291
|
+
onBlur: (e) => {
|
|
3292
|
+
const val = e.target.value;
|
|
3293
|
+
const quantity = val === "" ? null : Number(val);
|
|
3294
|
+
if (quantity) {
|
|
3295
|
+
onUpdate({ quantity });
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
),
|
|
3300
|
+
/* @__PURE__ */ jsx(Text, { className: "txt-small text-ui-fg-subtle", children: "Quantity" })
|
|
3301
|
+
] }),
|
|
3302
|
+
/* @__PURE__ */ jsx("div", { className: "text-ui-fg-subtle txt-small mr-2 flex flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
3303
|
+
Amount,
|
|
3304
|
+
{
|
|
3305
|
+
currencyCode,
|
|
3306
|
+
amount: item.total,
|
|
3307
|
+
originalAmount: originalItem == null ? void 0 : originalItem.total
|
|
3308
|
+
}
|
|
3309
|
+
) })
|
|
3310
|
+
] })
|
|
3311
|
+
] }),
|
|
3312
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-2 p-3 md:grid-cols-2", children: [
|
|
3313
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
3314
|
+
/* @__PURE__ */ jsx(Label, { children: "Price" }),
|
|
3315
|
+
/* @__PURE__ */ jsx(Hint, { className: "!mt-1", children: "Override the unit price of this product" })
|
|
3316
|
+
] }),
|
|
3317
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx("div", { className: "flex-grow", children: /* @__PURE__ */ jsx(
|
|
3318
|
+
CurrencyInput,
|
|
3319
|
+
{
|
|
3320
|
+
symbol: currencyCode,
|
|
3321
|
+
code: currencyCode,
|
|
3322
|
+
defaultValue: item.unit_price,
|
|
3323
|
+
type: "numeric",
|
|
3324
|
+
min: 0,
|
|
3325
|
+
onBlur: (e) => {
|
|
3326
|
+
onUpdate({
|
|
3327
|
+
unit_price: parseFloat(e.target.value),
|
|
3328
|
+
quantity: item.quantity
|
|
3329
|
+
});
|
|
3330
|
+
},
|
|
3331
|
+
className: "bg-ui-bg-field-component hover:bg-ui-bg-field-component-hover"
|
|
3332
|
+
}
|
|
3333
|
+
) }) })
|
|
3334
|
+
] }),
|
|
3335
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center px-3 pb-3", children: /* @__PURE__ */ jsx(
|
|
3336
|
+
Button,
|
|
3337
|
+
{
|
|
3338
|
+
type: "button",
|
|
3339
|
+
variant: "danger",
|
|
3340
|
+
size: "small",
|
|
3341
|
+
onClick: () => {
|
|
3342
|
+
onRemove();
|
|
3343
|
+
},
|
|
3344
|
+
children: "Remove"
|
|
3345
|
+
}
|
|
3346
|
+
) })
|
|
3347
|
+
]
|
|
3348
|
+
},
|
|
3349
|
+
item.quantity
|
|
3350
|
+
);
|
|
3351
|
+
}
|
|
3352
|
+
const ManageQuoteForm = ({ order }) => {
|
|
3353
|
+
const { order: preview } = useOrderPreview(order.id);
|
|
3354
|
+
const navigate = useNavigate();
|
|
3355
|
+
const { id: quoteId } = useParams();
|
|
3356
|
+
const [openProductModal, setOpenProductModal] = useState(false);
|
|
3357
|
+
const { mutateAsync: addItem } = useAddQuoteItem(order.id);
|
|
3358
|
+
const { mutateAsync: confirmQuote, isPending: isRequesting } = useConfirmQuote(order.id);
|
|
3359
|
+
const handleSubmit = async (e) => {
|
|
3360
|
+
e.preventDefault();
|
|
3361
|
+
try {
|
|
3362
|
+
await confirmQuote();
|
|
3363
|
+
navigate(`/quotes/${quoteId}`);
|
|
3364
|
+
toast.success("Successfully updated quote");
|
|
3365
|
+
} catch (e2) {
|
|
3366
|
+
toast.error("Error", {
|
|
3367
|
+
description: e2.message
|
|
3368
|
+
});
|
|
3369
|
+
}
|
|
3370
|
+
};
|
|
3371
|
+
const originalItemsMap = useMemo(() => {
|
|
3372
|
+
return new Map(order.items.map((item) => [item.id, item]));
|
|
3373
|
+
}, [order]);
|
|
3374
|
+
if (!preview) {
|
|
3375
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
3376
|
+
}
|
|
3377
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3378
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex h-full flex-col p-4 gap-2", children: [
|
|
3379
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3380
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 mt-8 flex items-center justify-between", children: [
|
|
3381
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Items" }),
|
|
3382
|
+
/* @__PURE__ */ jsx(
|
|
3383
|
+
Button,
|
|
3384
|
+
{
|
|
3385
|
+
type: "button",
|
|
3386
|
+
variant: "primary",
|
|
3387
|
+
size: "small",
|
|
3388
|
+
onClick: () => setOpenProductModal(true),
|
|
3389
|
+
children: "Add Item"
|
|
3390
|
+
}
|
|
3391
|
+
)
|
|
3392
|
+
] }),
|
|
3393
|
+
preview.items.map((item) => /* @__PURE__ */ jsx(
|
|
3394
|
+
ManageItem,
|
|
3395
|
+
{
|
|
3396
|
+
originalItem: originalItemsMap.get(item.id),
|
|
3397
|
+
item,
|
|
3398
|
+
orderId: order.id,
|
|
3399
|
+
currencyCode: order.currency_code
|
|
3400
|
+
},
|
|
3401
|
+
item.id
|
|
3402
|
+
))
|
|
3403
|
+
] }),
|
|
3404
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-8 border-y border-dotted py-4", children: [
|
|
3405
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
3406
|
+
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: "Current Total" }),
|
|
3407
|
+
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(order.total, order.currency_code) })
|
|
3408
|
+
] }),
|
|
3409
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
3410
|
+
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: "New Total" }),
|
|
3411
|
+
/* @__PURE__ */ jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(preview.total, order.currency_code) })
|
|
3412
|
+
] })
|
|
3413
|
+
] }),
|
|
3414
|
+
/* @__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(
|
|
3415
|
+
Button,
|
|
3416
|
+
{
|
|
3417
|
+
type: "submit",
|
|
3418
|
+
variant: "primary",
|
|
3419
|
+
size: "small",
|
|
3420
|
+
disabled: isRequesting,
|
|
3421
|
+
children: "Confirm Edit"
|
|
3422
|
+
},
|
|
3423
|
+
"submit-button"
|
|
3424
|
+
) }) })
|
|
3425
|
+
] }),
|
|
3426
|
+
/* @__PURE__ */ jsx(FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxs(FocusModal.Content, { children: [
|
|
3427
|
+
/* @__PURE__ */ jsx(FocusModal.Header, { children: /* @__PURE__ */ jsx(FocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Add Product" }) }) }),
|
|
3428
|
+
/* @__PURE__ */ jsx(FocusModal.Body, { children: /* @__PURE__ */ jsx(
|
|
3429
|
+
ProductSelector,
|
|
3430
|
+
{
|
|
3431
|
+
selectedProductIds: order.items.map(
|
|
3432
|
+
(item) => item.variant_id ?? ""
|
|
3433
|
+
),
|
|
3434
|
+
onSelectProduct: (variant) => {
|
|
3435
|
+
var _a, _b;
|
|
3436
|
+
if (!((_a = variant.prices) == null ? void 0 : _a[0].amount)) {
|
|
3437
|
+
toast.error("Product price not found");
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
addItem({
|
|
3441
|
+
items: [
|
|
3442
|
+
{
|
|
3443
|
+
variant_id: variant.id,
|
|
3444
|
+
quantity: 1,
|
|
3445
|
+
unit_price: (_b = variant.prices) == null ? void 0 : _b[0].amount
|
|
3446
|
+
}
|
|
3447
|
+
]
|
|
3448
|
+
});
|
|
3449
|
+
setOpenProductModal(false);
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
) })
|
|
3453
|
+
] }) })
|
|
3454
|
+
] });
|
|
3455
|
+
};
|
|
3456
|
+
const QuoteManage = () => {
|
|
3457
|
+
const { id } = useParams();
|
|
3458
|
+
const { quote, isLoading } = useQuote(id, {
|
|
3459
|
+
fields: "*draft_order.customer"
|
|
3460
|
+
});
|
|
3461
|
+
if (isLoading) {
|
|
3462
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
3463
|
+
}
|
|
3464
|
+
if (!quote) {
|
|
3465
|
+
throw "quote not found";
|
|
3466
|
+
}
|
|
3467
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3468
|
+
/* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
3469
|
+
/* @__PURE__ */ jsx(Heading, { className: "flex items-center justify-between px-6 py-4", children: "Manage Quote" }),
|
|
3470
|
+
/* @__PURE__ */ jsx(ManageQuoteForm, { order: quote.draft_order })
|
|
3471
|
+
] }),
|
|
3472
|
+
/* @__PURE__ */ jsx(Toaster, {})
|
|
3473
|
+
] });
|
|
3474
|
+
};
|
|
3475
3475
|
const widgetModule = { widgets: [] };
|
|
3476
3476
|
const routeModule = {
|
|
3477
3477
|
routes: [
|
|
@@ -3483,13 +3483,13 @@ const routeModule = {
|
|
|
3483
3483
|
Component: QuoteDetails,
|
|
3484
3484
|
path: "/quotes/:id"
|
|
3485
3485
|
},
|
|
3486
|
-
{
|
|
3487
|
-
Component: QuoteManage,
|
|
3488
|
-
path: "/quotes/:id/manage"
|
|
3489
|
-
},
|
|
3490
3486
|
{
|
|
3491
3487
|
Component: CreateQuote,
|
|
3492
3488
|
path: "/quotes/create"
|
|
3489
|
+
},
|
|
3490
|
+
{
|
|
3491
|
+
Component: QuoteManage,
|
|
3492
|
+
path: "/quotes/:id/manage"
|
|
3493
3493
|
}
|
|
3494
3494
|
]
|
|
3495
3495
|
};
|