@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.
@@ -2768,77 +2768,160 @@ const QuoteDetails = () => {
2768
2768
  /* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
2769
2769
  ] });
2770
2770
  };
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
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
2806
2817
  });
2807
- setData(response);
2808
- } catch (error) {
2809
- console.error("Error fetching customers:", error);
2810
- } finally {
2811
- setIsLoading(false);
2812
2818
  }
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
2823
- },
2824
- getRowId: (customer) => customer.id,
2825
- onRowClick: (_, row) => {
2826
- onSelectCustomer == null ? void 0 : onSelectCustomer(row.original);
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
+ ]
2827
2921
  },
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
- };
2922
+ item.quantity
2923
+ );
2924
+ }
2842
2925
  const useProductSearch = (search, selectedProductIds) => {
2843
2926
  const [data, setData] = react.useState({ variants: [] });
2844
2927
  const [isLoading, setIsLoading] = react.useState(false);
@@ -2890,21 +2973,21 @@ const useProductSearch = (search, selectedProductIds) => {
2890
2973
  }, [search, selectedProductIds.join(",")]);
2891
2974
  return { data, isLoading, error };
2892
2975
  };
2893
- const columnHelper = ui.createDataTableColumnHelper();
2894
- const columns = [
2895
- columnHelper.accessor("product.title", {
2976
+ const columnHelper$1 = ui.createDataTableColumnHelper();
2977
+ const columns$1 = [
2978
+ columnHelper$1.accessor("product.title", {
2896
2979
  header: "Product",
2897
2980
  cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2898
2981
  }),
2899
- columnHelper.accessor("title", {
2982
+ columnHelper$1.accessor("title", {
2900
2983
  header: "Variant",
2901
2984
  cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2902
2985
  }),
2903
- columnHelper.accessor("sku", {
2986
+ columnHelper$1.accessor("sku", {
2904
2987
  header: "SKU",
2905
2988
  cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2906
2989
  }),
2907
- columnHelper.accessor("prices", {
2990
+ columnHelper$1.accessor("prices", {
2908
2991
  header: "Price",
2909
2992
  cell: (info) => {
2910
2993
  var _a, _b;
@@ -2932,7 +3015,7 @@ const ProductSelector = ({
2932
3015
  }, [products == null ? void 0 : products.variants, selectedProductIds]);
2933
3016
  const table = ui.useDataTable({
2934
3017
  data: selectableProducts || [],
2935
- columns,
3018
+ columns: columns$1,
2936
3019
  search: {
2937
3020
  state: search,
2938
3021
  onSearchChange: setSearch
@@ -2954,29 +3037,223 @@ const ProductSelector = ({
2954
3037
  /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable, { instance: table, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}) })
2955
3038
  ] });
2956
3039
  };
2957
- const CreateQuoteForm = () => {
3040
+ const ManageQuoteForm = ({ order }) => {
3041
+ const { order: preview } = useOrderPreview(order.id);
2958
3042
  const navigate = reactRouterDom.useNavigate();
2959
- const [selectedCustomer, setSelectedCustomer] = react.useState(null);
2960
- const [items, setItems] = react.useState([]);
2961
- const [openCustomerModal, setOpenCustomerModal] = react.useState(false);
3043
+ const { id: quoteId } = reactRouterDom.useParams();
2962
3044
  const [openProductModal, setOpenProductModal] = react.useState(false);
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}`);
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
+ });
2970
3057
  }
2971
- });
2972
- const handleAddProduct = (variant) => {
2973
- var _a, _b;
2974
- const newItem = {
2975
- variant_id: variant.id,
2976
- variant,
2977
- quantity: 1,
2978
- unit_price: ((_b = (_a = variant.prices) == null ? void 0 : _a[0]) == null ? void 0 : _b.amount) || 0
2979
- };
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}`);
3247
+ }
3248
+ });
3249
+ const handleAddProduct = (variant) => {
3250
+ var _a, _b;
3251
+ const newItem = {
3252
+ variant_id: variant.id,
3253
+ variant,
3254
+ quantity: 1,
3255
+ unit_price: ((_b = (_a = variant.prices) == null ? void 0 : _a[0]) == null ? void 0 : _b.amount) || 0
3256
+ };
2980
3257
  setItems([...items, newItem]);
2981
3258
  setOpenProductModal(false);
2982
3259
  };
@@ -3199,283 +3476,6 @@ const CreateQuote = () => {
3199
3476
  /* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
3200
3477
  ] });
3201
3478
  };
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: CreateQuote,
3492
- path: "/quotes/create"
3493
- },
3494
3490
  {
3495
3491
  Component: QuoteManage,
3496
3492
  path: "/quotes/:id/manage"
3493
+ },
3494
+ {
3495
+ Component: CreateQuote,
3496
+ path: "/quotes/create"
3497
3497
  }
3498
3498
  ]
3499
3499
  };
@@ -3503,7 +3503,9 @@ const menuItemModule = {
3503
3503
  label: config.label,
3504
3504
  icon: config.icon,
3505
3505
  path: "/quotes",
3506
- nested: void 0
3506
+ nested: void 0,
3507
+ rank: void 0,
3508
+ translationNs: void 0
3507
3509
  }
3508
3510
  ]
3509
3511
  };