@lodashventure/medusa-quotation 1.5.28 → 1.5.30
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.
|
@@ -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, CheckCircle, Clock } from "lucide-react";
|
|
@@ -2764,77 +2764,160 @@ 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
|
-
|
|
2767
|
+
function ManageItem({
|
|
2768
|
+
originalItem,
|
|
2769
|
+
item,
|
|
2770
|
+
currencyCode,
|
|
2771
|
+
orderId
|
|
2772
|
+
}) {
|
|
2773
|
+
const { mutateAsync: updateItem } = useUpdateQuoteItem(orderId);
|
|
2774
|
+
const { mutateAsync: removeItem } = useRemoveQuoteItem(orderId);
|
|
2775
|
+
const isItemUpdated = useMemo(
|
|
2776
|
+
() => {
|
|
2777
|
+
var _a;
|
|
2778
|
+
return !!((_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_UPDATE"));
|
|
2779
|
+
},
|
|
2780
|
+
[item]
|
|
2781
|
+
);
|
|
2782
|
+
const onUpdate = async ({
|
|
2783
|
+
quantity,
|
|
2784
|
+
unit_price
|
|
2785
|
+
}) => {
|
|
2786
|
+
if (typeof quantity === "number" && quantity <= item.detail.fulfilled_quantity) {
|
|
2787
|
+
toast.warning("Quantity should be greater than the fulfilled quantity");
|
|
2788
|
+
return;
|
|
2789
|
+
}
|
|
2790
|
+
try {
|
|
2791
|
+
await updateItem({
|
|
2792
|
+
quantity,
|
|
2793
|
+
unit_price,
|
|
2794
|
+
itemId: item.id
|
|
2795
|
+
});
|
|
2796
|
+
} catch (e) {
|
|
2797
|
+
toast.error(e.message);
|
|
2798
|
+
}
|
|
2799
|
+
};
|
|
2800
|
+
const onRemove = async () => {
|
|
2801
|
+
var _a;
|
|
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
|
|
2802
2813
|
});
|
|
2803
|
-
setData(response);
|
|
2804
|
-
} catch (error) {
|
|
2805
|
-
console.error("Error fetching customers:", error);
|
|
2806
|
-
} finally {
|
|
2807
|
-
setIsLoading(false);
|
|
2808
2814
|
}
|
|
2809
|
-
}
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2815
|
+
} catch (e) {
|
|
2816
|
+
toast.error(e.message);
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
return /* @__PURE__ */ jsxs(
|
|
2820
|
+
"div",
|
|
2821
|
+
{
|
|
2822
|
+
className: "bg-ui-bg-subtle shadow-elevation-card-rest my-2 rounded-xl ",
|
|
2823
|
+
children: [
|
|
2824
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-x-2 gap-y-2 p-3 text-sm md:flex-row", children: [
|
|
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
|
+
]
|
|
2823
2917
|
},
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
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
|
-
};
|
|
2918
|
+
item.quantity
|
|
2919
|
+
);
|
|
2920
|
+
}
|
|
2838
2921
|
const useProductSearch = (search, selectedProductIds) => {
|
|
2839
2922
|
const [data, setData] = useState({ variants: [] });
|
|
2840
2923
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -2886,21 +2969,21 @@ const useProductSearch = (search, selectedProductIds) => {
|
|
|
2886
2969
|
}, [search, selectedProductIds.join(",")]);
|
|
2887
2970
|
return { data, isLoading, error };
|
|
2888
2971
|
};
|
|
2889
|
-
const columnHelper = createDataTableColumnHelper();
|
|
2890
|
-
const columns = [
|
|
2891
|
-
columnHelper.accessor("product.title", {
|
|
2972
|
+
const columnHelper$1 = createDataTableColumnHelper();
|
|
2973
|
+
const columns$1 = [
|
|
2974
|
+
columnHelper$1.accessor("product.title", {
|
|
2892
2975
|
header: "Product",
|
|
2893
2976
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2894
2977
|
}),
|
|
2895
|
-
columnHelper.accessor("title", {
|
|
2978
|
+
columnHelper$1.accessor("title", {
|
|
2896
2979
|
header: "Variant",
|
|
2897
2980
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2898
2981
|
}),
|
|
2899
|
-
columnHelper.accessor("sku", {
|
|
2982
|
+
columnHelper$1.accessor("sku", {
|
|
2900
2983
|
header: "SKU",
|
|
2901
2984
|
cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2902
2985
|
}),
|
|
2903
|
-
columnHelper.accessor("prices", {
|
|
2986
|
+
columnHelper$1.accessor("prices", {
|
|
2904
2987
|
header: "Price",
|
|
2905
2988
|
cell: (info) => {
|
|
2906
2989
|
var _a, _b;
|
|
@@ -2928,7 +3011,7 @@ const ProductSelector = ({
|
|
|
2928
3011
|
}, [products == null ? void 0 : products.variants, selectedProductIds]);
|
|
2929
3012
|
const table = useDataTable({
|
|
2930
3013
|
data: selectableProducts || [],
|
|
2931
|
-
columns,
|
|
3014
|
+
columns: columns$1,
|
|
2932
3015
|
search: {
|
|
2933
3016
|
state: search,
|
|
2934
3017
|
onSearchChange: setSearch
|
|
@@ -2950,29 +3033,223 @@ const ProductSelector = ({
|
|
|
2950
3033
|
/* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
|
|
2951
3034
|
] });
|
|
2952
3035
|
};
|
|
2953
|
-
const
|
|
3036
|
+
const ManageQuoteForm = ({ order }) => {
|
|
3037
|
+
const { order: preview } = useOrderPreview(order.id);
|
|
2954
3038
|
const navigate = useNavigate();
|
|
2955
|
-
const
|
|
2956
|
-
const [items, setItems] = useState([]);
|
|
2957
|
-
const [openCustomerModal, setOpenCustomerModal] = useState(false);
|
|
3039
|
+
const { id: quoteId } = useParams();
|
|
2958
3040
|
const [openProductModal, setOpenProductModal] = useState(false);
|
|
2959
|
-
const { mutateAsync:
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
3041
|
+
const { mutateAsync: addItem } = useAddQuoteItem(order.id);
|
|
3042
|
+
const { mutateAsync: confirmQuote, isPending: isRequesting } = useConfirmQuote(order.id);
|
|
3043
|
+
const handleSubmit = async (e) => {
|
|
3044
|
+
e.preventDefault();
|
|
3045
|
+
try {
|
|
3046
|
+
await confirmQuote();
|
|
3047
|
+
navigate(`/quotes/${quoteId}`);
|
|
3048
|
+
toast.success("Successfully updated quote");
|
|
3049
|
+
} catch (e2) {
|
|
3050
|
+
toast.error("Error", {
|
|
3051
|
+
description: e2.message
|
|
3052
|
+
});
|
|
2966
3053
|
}
|
|
2967
|
-
}
|
|
2968
|
-
const
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
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}`);
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
3245
|
+
const handleAddProduct = (variant) => {
|
|
3246
|
+
var _a, _b;
|
|
3247
|
+
const newItem = {
|
|
3248
|
+
variant_id: variant.id,
|
|
3249
|
+
variant,
|
|
3250
|
+
quantity: 1,
|
|
3251
|
+
unit_price: ((_b = (_a = variant.prices) == null ? void 0 : _a[0]) == null ? void 0 : _b.amount) || 0
|
|
3252
|
+
};
|
|
2976
3253
|
setItems([...items, newItem]);
|
|
2977
3254
|
setOpenProductModal(false);
|
|
2978
3255
|
};
|
|
@@ -3195,283 +3472,6 @@ const CreateQuote = () => {
|
|
|
3195
3472
|
/* @__PURE__ */ jsx(Toaster, {})
|
|
3196
3473
|
] });
|
|
3197
3474
|
};
|
|
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: CreateQuote,
|
|
3488
|
-
path: "/quotes/create"
|
|
3489
|
-
},
|
|
3490
3486
|
{
|
|
3491
3487
|
Component: QuoteManage,
|
|
3492
3488
|
path: "/quotes/:id/manage"
|
|
3489
|
+
},
|
|
3490
|
+
{
|
|
3491
|
+
Component: CreateQuote,
|
|
3492
|
+
path: "/quotes/create"
|
|
3493
3493
|
}
|
|
3494
3494
|
]
|
|
3495
3495
|
};
|
|
@@ -3499,7 +3499,9 @@ const menuItemModule = {
|
|
|
3499
3499
|
label: config.label,
|
|
3500
3500
|
icon: config.icon,
|
|
3501
3501
|
path: "/quotes",
|
|
3502
|
-
nested: void 0
|
|
3502
|
+
nested: void 0,
|
|
3503
|
+
rank: void 0,
|
|
3504
|
+
translationNs: void 0
|
|
3503
3505
|
}
|
|
3504
3506
|
]
|
|
3505
3507
|
};
|