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