@lodashventure/medusa-quotation 1.5.29 → 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, IconButton, Hint, CurrencyInput } from "@medusajs/ui";
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
- 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
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
- 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
2819
- },
2820
- getRowId: (customer) => customer.id,
2821
- onRowClick: (_, row) => {
2822
- onSelectCustomer == null ? void 0 : onSelectCustomer(row.original);
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
- 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
- };
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 CreateQuoteForm = () => {
3036
+ const ManageQuoteForm = ({ order }) => {
3037
+ const { order: preview } = useOrderPreview(order.id);
2954
3038
  const navigate = useNavigate();
2955
- const [selectedCustomer, setSelectedCustomer] = useState(null);
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: 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}`);
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 handleAddProduct = (variant) => {
2969
- var _a, _b;
2970
- const newItem = {
2971
- variant_id: variant.id,
2972
- variant,
2973
- quantity: 1,
2974
- unit_price: ((_b = (_a = variant.prices) == null ? void 0 : _a[0]) == null ? void 0 : _b.amount) || 0
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
  };