@medusajs/draft-order 2.11.0-preview-20251016031628 → 2.11.0-preview-20251016060156

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.
@@ -4,7 +4,7 @@ import { Tooltip, DropdownMenu, clx, IconButton, useDataTable, DataTable as Data
4
4
  import { useQuery, useQueryClient, useMutation, keepPreviousData, useInfiniteQuery } from "@tanstack/react-query";
5
5
  import React, { useState, useCallback, useMemo, Fragment, createContext, forwardRef, useId, useContext, useTransition, useRef, useImperativeHandle, useDeferredValue, useEffect, Suspense } from "react";
6
6
  import { useSearchParams, Link, useNavigate, Outlet, useBlocker, useLocation, useParams } from "react-router-dom";
7
- import { EllipsisHorizontal, XMark, InformationCircleSolid, XMarkMini, TrianglesMini, CheckMini, EllipseMiniSolid, PlusMini, ExclamationCircleSolid, ArrowPath, FlyingBox, CurrencyDollar, Envelope, Channels, Trash, ArrowUpRightOnBox, TriangleDownMini, Check, SquareTwoStack, Photo, TriangleRightMini, Shopping, Buildings, TruckFast, Plus, ReceiptPercent, Minus, PencilSquare, EllipsisVertical, ArrowUpMini, ArrowDownMini } from "@medusajs/icons";
7
+ import { EllipsisHorizontal, XMark, InformationCircleSolid, XMarkMini, TrianglesMini, CheckMini, EllipseMiniSolid, PlusMini, ExclamationCircleSolid, ArrowPath, FlyingBox, CurrencyDollar, Envelope, Channels, Trash, ArrowUpRightOnBox, TriangleDownMini, Check, SquareTwoStack, Photo, TriangleRightMini, Shopping, Buildings, TruckFast, Plus, ReceiptPercent, EllipsisVertical, ArrowUpMini, ArrowDownMini, Minus, PencilSquare } from "@medusajs/icons";
8
8
  import Medusa from "@medusajs/js-sdk";
9
9
  import { format, formatDistance, sub, subDays, subMonths } from "date-fns";
10
10
  import { enUS } from "date-fns/locale";
@@ -9757,27 +9757,6 @@ const BillingAddressForm = ({ order }) => {
9757
9757
  ) });
9758
9758
  };
9759
9759
  const schema$5 = addressSchema;
9760
- const CustomItems = () => {
9761
- return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
9762
- /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Custom Items" }) }) }),
9763
- /* @__PURE__ */ jsx(CustomItemsForm, {})
9764
- ] });
9765
- };
9766
- const CustomItemsForm = () => {
9767
- const form = useForm({
9768
- resolver: zodResolver(schema$4)
9769
- });
9770
- return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
9771
- /* @__PURE__ */ jsx(RouteDrawer.Body, {}),
9772
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
9773
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
9774
- /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", children: "Save" })
9775
- ] }) })
9776
- ] }) });
9777
- };
9778
- const schema$4 = objectType({
9779
- email: stringType().email()
9780
- });
9781
9760
  const Email = () => {
9782
9761
  const { id } = useParams();
9783
9762
  const { order, isPending, isError, error } = useOrder(id, {
@@ -9800,7 +9779,7 @@ const EmailForm = ({ order }) => {
9800
9779
  defaultValues: {
9801
9780
  email: order.email ?? ""
9802
9781
  },
9803
- resolver: zodResolver(schema$3)
9782
+ resolver: zodResolver(schema$4)
9804
9783
  });
9805
9784
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
9806
9785
  const { handleSuccess } = useRouteModal();
@@ -9843,130 +9822,46 @@ const EmailForm = ({ order }) => {
9843
9822
  }
9844
9823
  ) });
9845
9824
  };
9825
+ const schema$4 = objectType({
9826
+ email: stringType().email()
9827
+ });
9828
+ const CustomItems = () => {
9829
+ return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
9830
+ /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Custom Items" }) }) }),
9831
+ /* @__PURE__ */ jsx(CustomItemsForm, {})
9832
+ ] });
9833
+ };
9834
+ const CustomItemsForm = () => {
9835
+ const form = useForm({
9836
+ resolver: zodResolver(schema$3)
9837
+ });
9838
+ return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
9839
+ /* @__PURE__ */ jsx(RouteDrawer.Body, {}),
9840
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
9841
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
9842
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", children: "Save" })
9843
+ ] }) })
9844
+ ] }) });
9845
+ };
9846
9846
  const schema$3 = objectType({
9847
9847
  email: stringType().email()
9848
9848
  });
9849
- const NumberInput = forwardRef(
9850
- ({
9851
- value,
9852
- onChange,
9853
- size = "base",
9854
- min = 0,
9855
- max = 100,
9856
- step = 1,
9857
- className,
9858
- disabled,
9859
- ...props
9860
- }, ref) => {
9861
- const handleChange = (event) => {
9862
- const newValue = event.target.value === "" ? min : Number(event.target.value);
9863
- if (!isNaN(newValue) && (max === void 0 || newValue <= max) && (min === void 0 || newValue >= min)) {
9864
- onChange(newValue);
9865
- }
9866
- };
9867
- const handleIncrement = () => {
9868
- const newValue = value + step;
9869
- if (max === void 0 || newValue <= max) {
9870
- onChange(newValue);
9871
- }
9872
- };
9873
- const handleDecrement = () => {
9874
- const newValue = value - step;
9875
- if (min === void 0 || newValue >= min) {
9876
- onChange(newValue);
9877
- }
9878
- };
9879
- return /* @__PURE__ */ jsxs(
9880
- "div",
9881
- {
9882
- className: clx(
9883
- "inline-flex rounded-md bg-ui-bg-field shadow-borders-base overflow-hidden divide-x transition-fg",
9884
- "[&:has(input:focus)]:shadow-borders-interactive-with-active",
9885
- {
9886
- "h-7": size === "small",
9887
- "h-8": size === "base"
9888
- },
9889
- className
9890
- ),
9891
- children: [
9892
- /* @__PURE__ */ jsx(
9893
- "input",
9894
- {
9895
- ref,
9896
- type: "number",
9897
- value,
9898
- onChange: handleChange,
9899
- min,
9900
- max,
9901
- step,
9902
- className: clx(
9903
- "flex-1 px-2 py-1 bg-transparent txt-compact-small text-ui-fg-base outline-none [appearance:textfield]",
9904
- "[&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
9905
- "placeholder:text-ui-fg-muted"
9906
- ),
9907
- ...props
9908
- }
9909
- ),
9910
- /* @__PURE__ */ jsxs(
9911
- "button",
9912
- {
9913
- className: clx(
9914
- "flex items-center justify-center outline-none transition-fg",
9915
- "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
9916
- "focus:bg-ui-bg-field-component-hover",
9917
- "hover:bg-ui-bg-field-component-hover",
9918
- {
9919
- "size-7": size === "small",
9920
- "size-8": size === "base"
9921
- }
9922
- ),
9923
- type: "button",
9924
- onClick: handleDecrement,
9925
- disabled: min !== void 0 && value <= min || disabled,
9926
- children: [
9927
- /* @__PURE__ */ jsx(Minus, {}),
9928
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: `Decrease by ${step}` })
9929
- ]
9930
- }
9931
- ),
9932
- /* @__PURE__ */ jsxs(
9933
- "button",
9934
- {
9935
- className: clx(
9936
- "flex items-center justify-center outline-none transition-fg",
9937
- "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
9938
- "focus:bg-ui-bg-field-hover",
9939
- "hover:bg-ui-bg-field-hover",
9940
- {
9941
- "size-7": size === "small",
9942
- "size-8": size === "base"
9943
- }
9944
- ),
9945
- type: "button",
9946
- onClick: handleIncrement,
9947
- disabled: max !== void 0 && value >= max || disabled,
9948
- children: [
9949
- /* @__PURE__ */ jsx(Plus, {}),
9950
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: `Increase by ${step}` })
9951
- ]
9952
- }
9953
- )
9954
- ]
9955
- }
9956
- );
9957
- }
9958
- );
9959
- const PRODUCT_VARIANTS_QUERY_KEY = "product-variants";
9960
- const productVariantsQueryKeys = {
9849
+ const PROMOTION_QUERY_KEY = "promotions";
9850
+ const promotionsQueryKeys = {
9961
9851
  list: (query2) => [
9962
- PRODUCT_VARIANTS_QUERY_KEY,
9852
+ PROMOTION_QUERY_KEY,
9853
+ query2 ? query2 : void 0
9854
+ ],
9855
+ detail: (id, query2) => [
9856
+ PROMOTION_QUERY_KEY,
9857
+ id,
9963
9858
  query2 ? query2 : void 0
9964
9859
  ]
9965
9860
  };
9966
- const useProductVariants = (query2, options) => {
9861
+ const usePromotions = (query2, options) => {
9967
9862
  const { data, ...rest } = useQuery({
9968
- queryKey: productVariantsQueryKeys.list(query2),
9969
- queryFn: async () => await sdk.admin.productVariant.list(query2),
9863
+ queryKey: promotionsQueryKeys.list(query2),
9864
+ queryFn: async () => sdk.admin.promotion.list(query2),
9970
9865
  ...options
9971
9866
  });
9972
9867
  return { ...data, ...rest };
@@ -10017,1436 +9912,1541 @@ const useInitiateOrderEdit = ({
10017
9912
  run();
10018
9913
  }, [preview, navigate, mutateAsync]);
10019
9914
  };
10020
- function convertNumber(value) {
10021
- return typeof value === "string" ? Number(value.replace(",", ".")) : value;
10022
- }
10023
- const STACKED_MODAL_ID = "items_stacked_modal";
10024
- const Items = () => {
9915
+ const Promotions = () => {
10025
9916
  const { id } = useParams();
10026
9917
  const {
10027
9918
  order: preview,
10028
- isPending: isPreviewPending,
10029
9919
  isError: isPreviewError,
10030
9920
  error: previewError
10031
- } = useOrderPreview(id, void 0, {
10032
- placeholderData: keepPreviousData
10033
- });
9921
+ } = useOrderPreview(id, void 0);
10034
9922
  useInitiateOrderEdit({ preview });
10035
- const { draft_order, isPending, isError, error } = useDraftOrder(
10036
- id,
10037
- {
10038
- fields: "currency_code"
10039
- },
10040
- {
10041
- enabled: !!id
10042
- }
10043
- );
10044
9923
  const { onCancel } = useCancelOrderEdit({ preview });
10045
- if (isError) {
10046
- throw error;
10047
- }
10048
9924
  if (isPreviewError) {
10049
9925
  throw previewError;
10050
9926
  }
10051
- const ready = !!preview && !isPreviewPending && !!draft_order && !isPending;
10052
- return /* @__PURE__ */ jsx(RouteFocusModal, { onClose: onCancel, children: ready ? /* @__PURE__ */ jsx(ItemsForm, { preview, currencyCode: draft_order.currency_code }) : /* @__PURE__ */ jsxs("div", { children: [
10053
- /* @__PURE__ */ jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Edit Items" }) }),
10054
- /* @__PURE__ */ jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading data for the draft order, please wait..." }) })
10055
- ] }) });
9927
+ const isReady = !!preview;
9928
+ return /* @__PURE__ */ jsxs(RouteDrawer, { onClose: onCancel, children: [
9929
+ /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Promotions" }) }) }),
9930
+ isReady && /* @__PURE__ */ jsx(PromotionForm, { preview })
9931
+ ] });
10056
9932
  };
10057
- const ItemsForm = ({ preview, currencyCode }) => {
10058
- var _a;
9933
+ const PromotionForm = ({ preview }) => {
9934
+ const { items, shipping_methods } = preview;
10059
9935
  const [isSubmitting, setIsSubmitting] = useState(false);
10060
- const [modalContent, setModalContent] = useState(
10061
- null
10062
- );
9936
+ const [comboboxValue, setComboboxValue] = useState("");
10063
9937
  const { handleSuccess } = useRouteModal();
10064
- const { searchValue, onSearchValueChange, query: query2 } = useDebouncedSearch();
10065
- const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
10066
- const { mutateAsync: requestOrderEdit } = useDraftOrderRequestEdit(preview.id);
10067
- const itemCount = ((_a = preview.items) == null ? void 0 : _a.reduce((acc, item) => acc + item.quantity, 0)) || 0;
10068
- const matches = useMemo(() => {
10069
- return matchSorter(preview.items, query2, {
10070
- keys: ["product_title", "variant_title", "variant_sku", "title"]
10071
- });
10072
- }, [preview.items, query2]);
10073
- const onSubmit = async () => {
10074
- setIsSubmitting(true);
10075
- let requestSucceeded = false;
10076
- await requestOrderEdit(void 0, {
10077
- onError: (e) => {
10078
- toast.error(`Failed to request order edit: ${e.message}`);
10079
- },
10080
- onSuccess: () => {
10081
- requestSucceeded = true;
10082
- }
10083
- });
10084
- if (!requestSucceeded) {
10085
- setIsSubmitting(false);
10086
- return;
9938
+ const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
9939
+ const promoIds = getPromotionIds(items, shipping_methods);
9940
+ const { promotions, isPending, isError, error } = usePromotions(
9941
+ {
9942
+ id: promoIds
9943
+ },
9944
+ {
9945
+ enabled: !!promoIds.length
10087
9946
  }
10088
- await confirmOrderEdit(void 0, {
10089
- onError: (e) => {
10090
- toast.error(`Failed to confirm order edit: ${e.message}`);
10091
- },
10092
- onSuccess: () => {
10093
- handleSuccess();
10094
- },
10095
- onSettled: () => {
10096
- setIsSubmitting(false);
10097
- }
10098
- });
10099
- };
10100
- const onKeyDown = useCallback(
10101
- (e) => {
10102
- if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
10103
- if (modalContent || isSubmitting) {
10104
- return;
9947
+ );
9948
+ const comboboxData = useComboboxData({
9949
+ queryKey: ["promotions", "combobox", promoIds],
9950
+ queryFn: async (params) => {
9951
+ return await sdk.admin.promotion.list({
9952
+ ...params,
9953
+ id: {
9954
+ $nin: promoIds
10105
9955
  }
10106
- onSubmit();
10107
- }
9956
+ });
10108
9957
  },
10109
- [modalContent, isSubmitting, onSubmit]
10110
- );
10111
- useEffect(() => {
10112
- document.addEventListener("keydown", onKeyDown);
10113
- return () => {
10114
- document.removeEventListener("keydown", onKeyDown);
10115
- };
10116
- }, [onKeyDown]);
10117
- return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col overflow-hidden", children: [
10118
- /* @__PURE__ */ jsx(RouteFocusModal.Header, {}),
10119
- /* @__PURE__ */ jsx(RouteFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsxs(
10120
- StackedFocusModal,
9958
+ getOptions: (data) => {
9959
+ return data.promotions.map((promotion) => ({
9960
+ label: promotion.code,
9961
+ value: promotion.code
9962
+ }));
9963
+ }
9964
+ });
9965
+ const add = async (value) => {
9966
+ if (!value) {
9967
+ return;
9968
+ }
9969
+ addPromotions(
10121
9970
  {
10122
- id: STACKED_MODAL_ID,
10123
- onOpenChangeCallback: (open) => {
10124
- if (!open) {
10125
- setModalContent(null);
10126
- }
9971
+ promo_codes: [value]
9972
+ },
9973
+ {
9974
+ onError: (e) => {
9975
+ toast.error(e.message);
9976
+ comboboxData.onSearchValueChange("");
9977
+ setComboboxValue("");
10127
9978
  },
10128
- children: [
10129
- /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col items-center overflow-y-auto", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full max-w-[720px] flex-col gap-y-6 px-6 py-16", children: [
10130
- /* @__PURE__ */ jsxs("div", { children: [
10131
- /* @__PURE__ */ jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Items" }) }),
10132
- /* @__PURE__ */ jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Edit the items in the draft order" }) })
10133
- ] }),
10134
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10135
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-6", children: [
10136
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 items-center gap-3", children: [
10137
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
10138
- /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: "Items" }),
10139
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Choose items from the product catalog." })
10140
- ] }),
10141
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
10142
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
10143
- Input,
10144
- {
10145
- type: "search",
10146
- placeholder: "Search items",
10147
- value: searchValue,
10148
- onChange: (e) => onSearchValueChange(e.target.value)
10149
- }
10150
- ) }),
10151
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
10152
- /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { type: "button", children: /* @__PURE__ */ jsx(Plus, {}) }) }),
10153
- /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [
10154
- /* @__PURE__ */ jsx(
10155
- StackedModalTrigger$1,
10156
- {
10157
- type: "add-items",
10158
- setModalContent
10159
- }
10160
- ),
10161
- /* @__PURE__ */ jsx(
10162
- StackedModalTrigger$1,
10163
- {
10164
- type: "add-custom-item",
10165
- setModalContent
10166
- }
10167
- )
10168
- ] })
10169
- ] })
10170
- ] })
10171
- ] }),
10172
- /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle shadow-elevation-card-rest rounded-xl", children: [
10173
- /* @__PURE__ */ jsx("div", { className: "px-[5px]", children: /* @__PURE__ */ jsxs("div", { className: "text-ui-fg-muted grid grid-cols-[2fr_1fr_2fr_28px] gap-3 px-4 py-2", children: [
10174
- /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Item" }) }),
10175
- /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Quantity" }) }),
10176
- /* @__PURE__ */ jsx("div", { className: "text-right", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Price" }) }),
10177
- /* @__PURE__ */ jsx("div", {})
10178
- ] }) }),
10179
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-y-1.5 px-[5px] pb-[5px]", children: itemCount <= 0 ? /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest flex flex-col items-center justify-center gap-1 gap-x-3 rounded-lg p-4", children: [
10180
- /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: "There are no items in this order" }),
10181
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Add items to the order to get started." })
10182
- ] }) : matches.length > 0 ? matches == null ? void 0 : matches.map((item) => /* @__PURE__ */ jsx(
10183
- Item,
10184
- {
10185
- item,
10186
- preview,
10187
- currencyCode
10188
- },
10189
- item.id
10190
- )) : /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest flex flex-col items-center justify-center gap-1 gap-x-3 rounded-lg p-4", children: [
10191
- /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: "No items found" }),
10192
- /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
10193
- 'No items found for "',
10194
- query2,
10195
- '".'
10196
- ] })
10197
- ] }) })
10198
- ] })
10199
- ] }),
10200
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10201
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_0.5fr_0.5fr] gap-3", children: [
10202
- /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: "Subtotal" }) }),
10203
- /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
10204
- Text,
10205
- {
10206
- size: "small",
10207
- leading: "compact",
10208
- className: "text-ui-fg-subtle",
10209
- children: [
10210
- itemCount,
10211
- " ",
10212
- itemCount === 1 ? "item" : "items"
10213
- ]
10214
- }
10215
- ) }),
10216
- /* @__PURE__ */ jsx("div", { className: "text-right", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: getStylizedAmount(preview.item_subtotal, currencyCode) }) })
10217
- ] })
10218
- ] }) }),
10219
- modalContent && (modalContent === "add-items" ? /* @__PURE__ */ jsx(ExistingItemsForm, { orderId: preview.id, items: preview.items }) : modalContent === "add-custom-item" ? /* @__PURE__ */ jsx(
10220
- CustomItemForm,
10221
- {
10222
- orderId: preview.id,
10223
- currencyCode
10224
- }
10225
- ) : null)
10226
- ]
9979
+ onSuccess: () => {
9980
+ comboboxData.onSearchValueChange("");
9981
+ setComboboxValue("");
9982
+ }
10227
9983
  }
10228
- ) }),
10229
- /* @__PURE__ */ jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10230
- /* @__PURE__ */ jsx(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
9984
+ );
9985
+ };
9986
+ const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
9987
+ const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
9988
+ const onSubmit = async () => {
9989
+ setIsSubmitting(true);
9990
+ let requestSucceeded = false;
9991
+ await requestOrderEdit(void 0, {
9992
+ onError: (e) => {
9993
+ toast.error(e.message);
9994
+ },
9995
+ onSuccess: () => {
9996
+ requestSucceeded = true;
9997
+ }
9998
+ });
9999
+ if (!requestSucceeded) {
10000
+ setIsSubmitting(false);
10001
+ return;
10002
+ }
10003
+ await confirmOrderEdit(void 0, {
10004
+ onError: (e) => {
10005
+ toast.error(e.message);
10006
+ },
10007
+ onSuccess: () => {
10008
+ handleSuccess();
10009
+ },
10010
+ onSettled: () => {
10011
+ setIsSubmitting(false);
10012
+ }
10013
+ });
10014
+ };
10015
+ if (isError) {
10016
+ throw error;
10017
+ }
10018
+ return /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
10019
+ /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
10020
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
10021
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
10022
+ /* @__PURE__ */ jsx(Label$1, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
10023
+ /* @__PURE__ */ jsx(Hint$1, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
10024
+ ] }),
10025
+ /* @__PURE__ */ jsx(
10026
+ Combobox,
10027
+ {
10028
+ id: "promotion-combobox",
10029
+ "aria-describedby": "promotion-combobox-hint",
10030
+ isFetchingNextPage: comboboxData.isFetchingNextPage,
10031
+ fetchNextPage: comboboxData.fetchNextPage,
10032
+ options: comboboxData.options,
10033
+ onSearchValueChange: comboboxData.onSearchValueChange,
10034
+ searchValue: comboboxData.searchValue,
10035
+ disabled: comboboxData.disabled || isAddingPromotions,
10036
+ onChange: add,
10037
+ value: comboboxValue
10038
+ }
10039
+ )
10040
+ ] }),
10041
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10042
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: promotions == null ? void 0 : promotions.map((promotion) => /* @__PURE__ */ jsx(
10043
+ PromotionItem,
10044
+ {
10045
+ promotion,
10046
+ orderId: preview.id,
10047
+ isLoading: isPending
10048
+ },
10049
+ promotion.id
10050
+ )) })
10051
+ ] }) }),
10052
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
10053
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
10231
10054
  /* @__PURE__ */ jsx(
10232
10055
  Button,
10233
10056
  {
10234
10057
  size: "small",
10235
- type: "button",
10236
- onClick: onSubmit,
10237
- isLoading: isSubmitting,
10058
+ type: "submit",
10059
+ isLoading: isSubmitting || isAddingPromotions,
10238
10060
  children: "Save"
10239
10061
  }
10240
10062
  )
10241
10063
  ] }) })
10242
10064
  ] });
10243
10065
  };
10244
- const Item = ({ item, preview, currencyCode }) => {
10245
- if (item.variant_id) {
10246
- return /* @__PURE__ */ jsx(VariantItem, { item, preview, currencyCode });
10247
- }
10248
- return /* @__PURE__ */ jsx(CustomItem, { item, preview, currencyCode });
10249
- };
10250
- const VariantItem = ({ item, preview, currencyCode }) => {
10251
- const [editing, setEditing] = useState(false);
10252
- const form = useForm({
10253
- defaultValues: {
10254
- quantity: item.quantity,
10255
- unit_price: item.unit_price
10256
- },
10257
- resolver: zodResolver(variantItemSchema)
10258
- });
10259
- const actionId = useMemo(() => {
10260
- var _a, _b;
10261
- return (_b = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD")) == null ? void 0 : _b.id;
10262
- }, [item]);
10263
- const { mutateAsync: updateActionItem, isPending: isUpdatingActionItem } = useDraftOrderUpdateActionItem(preview.id);
10264
- const { mutateAsync: updateOriginalItem, isPending: isUpdatingOriginalItem } = useDraftOrderUpdateItem(preview.id);
10265
- const isPending = isUpdatingActionItem || isUpdatingOriginalItem;
10266
- const onSubmit = form.handleSubmit(async (data) => {
10267
- if (convertNumber(data.unit_price) === item.unit_price && data.quantity === item.quantity) {
10268
- setEditing(false);
10269
- return;
10270
- }
10271
- if (!actionId) {
10272
- await updateOriginalItem(
10273
- {
10274
- item_id: item.id,
10275
- quantity: data.quantity,
10276
- unit_price: convertNumber(data.unit_price)
10277
- },
10278
- {
10279
- onSuccess: () => {
10280
- setEditing(false);
10281
- },
10282
- onError: (e) => {
10283
- toast.error(e.message);
10284
- }
10285
- }
10286
- );
10287
- return;
10288
- }
10289
- await updateActionItem(
10066
+ const PromotionItem = ({
10067
+ promotion,
10068
+ orderId,
10069
+ isLoading
10070
+ }) => {
10071
+ var _a;
10072
+ const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
10073
+ const onRemove = async () => {
10074
+ removePromotions(
10290
10075
  {
10291
- action_id: actionId,
10292
- quantity: data.quantity,
10293
- unit_price: convertNumber(data.unit_price)
10076
+ promo_codes: [promotion.code]
10294
10077
  },
10295
10078
  {
10296
- onSuccess: () => {
10297
- setEditing(false);
10298
- },
10299
10079
  onError: (e) => {
10300
10080
  toast.error(e.message);
10301
10081
  }
10302
10082
  }
10303
10083
  );
10304
- });
10305
- return /* @__PURE__ */ jsx(Form$2, { ...form, children: /* @__PURE__ */ jsx("form", { onSubmit, children: /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-[minmax(0,2fr)_minmax(0,1fr)_minmax(0,2fr)_28px] items-center gap-3 rounded-lg px-4 py-2", children: [
10306
- /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-x-3", children: [
10307
- /* @__PURE__ */ jsx(
10308
- Thumbnail,
10084
+ };
10085
+ const displayValue = getDisplayValue(promotion);
10086
+ return /* @__PURE__ */ jsxs(
10087
+ "div",
10088
+ {
10089
+ className: clx(
10090
+ "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
10309
10091
  {
10310
- thumbnail: item.thumbnail,
10311
- alt: item.product_title ?? void 0
10092
+ "animate-pulse": isLoading
10312
10093
  }
10313
10094
  ),
10314
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
10315
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-1", children: [
10316
- /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: item.product_title }),
10317
- /* @__PURE__ */ jsxs(
10318
- Text,
10319
- {
10320
- size: "small",
10321
- leading: "compact",
10322
- className: "text-ui-fg-subtle",
10323
- children: [
10324
- "(",
10325
- item.variant_title,
10326
- ")"
10327
- ]
10328
- }
10329
- )
10095
+ children: [
10096
+ /* @__PURE__ */ jsxs("div", { children: [
10097
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
10098
+ /* @__PURE__ */ jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
10099
+ displayValue && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
10100
+ /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", children: displayValue }),
10101
+ /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", children: "·" })
10102
+ ] }),
10103
+ /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", className: "capitalize", children: (_a = promotion.application_method) == null ? void 0 : _a.allocation })
10104
+ ] })
10330
10105
  ] }),
10331
10106
  /* @__PURE__ */ jsx(
10332
- Text,
10107
+ IconButton,
10333
10108
  {
10334
10109
  size: "small",
10335
- leading: "compact",
10336
- className: "text-ui-fg-subtle",
10337
- children: item.variant_sku
10110
+ type: "button",
10111
+ variant: "transparent",
10112
+ onClick: onRemove,
10113
+ isLoading: isPending || isLoading,
10114
+ children: /* @__PURE__ */ jsx(XMark, {})
10338
10115
  }
10339
10116
  )
10340
- ] })
10341
- ] }),
10342
- editing ? /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(
10343
- Form$2.Field,
10344
- {
10345
- control: form.control,
10346
- name: "quantity",
10347
- render: ({ field }) => {
10348
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(NumberInput, { ...field }) }) });
10117
+ ]
10118
+ },
10119
+ promotion.id
10120
+ );
10121
+ };
10122
+ function getDisplayValue(promotion) {
10123
+ var _a, _b, _c, _d;
10124
+ const value = (_a = promotion.application_method) == null ? void 0 : _a.value;
10125
+ if (!value) {
10126
+ return null;
10127
+ }
10128
+ if (((_b = promotion.application_method) == null ? void 0 : _b.type) === "fixed") {
10129
+ const currency = (_c = promotion.application_method) == null ? void 0 : _c.currency_code;
10130
+ if (!currency) {
10131
+ return null;
10132
+ }
10133
+ return getLocaleAmount(value, currency);
10134
+ } else if (((_d = promotion.application_method) == null ? void 0 : _d.type) === "percentage") {
10135
+ return formatPercentage(value);
10136
+ }
10137
+ return null;
10138
+ }
10139
+ const formatter = new Intl.NumberFormat([], {
10140
+ style: "percent",
10141
+ minimumFractionDigits: 2
10142
+ });
10143
+ const formatPercentage = (value, isPercentageValue = false) => {
10144
+ let val = value || 0;
10145
+ if (!isPercentageValue) {
10146
+ val = val / 100;
10147
+ }
10148
+ return formatter.format(val);
10149
+ };
10150
+ function getPromotionIds(items, shippingMethods) {
10151
+ const promotionIds = /* @__PURE__ */ new Set();
10152
+ for (const item of items) {
10153
+ if (item.adjustments) {
10154
+ for (const adjustment of item.adjustments) {
10155
+ if (adjustment.promotion_id) {
10156
+ promotionIds.add(adjustment.promotion_id);
10349
10157
  }
10350
10158
  }
10351
- ) }) : /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: item.quantity }) }),
10352
- editing ? /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(
10353
- Form$2.Field,
10354
- {
10355
- control: form.control,
10356
- name: "unit_price",
10357
- render: ({ field: { onChange, ...field } }) => {
10358
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10359
- CurrencyInput,
10360
- {
10361
- ...field,
10362
- symbol: getNativeSymbol(currencyCode),
10363
- code: currencyCode,
10364
- onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value)
10365
- }
10366
- ) }) });
10159
+ }
10160
+ }
10161
+ for (const shippingMethod of shippingMethods) {
10162
+ if (shippingMethod.adjustments) {
10163
+ for (const adjustment of shippingMethod.adjustments) {
10164
+ if (adjustment.promotion_id) {
10165
+ promotionIds.add(adjustment.promotion_id);
10367
10166
  }
10368
10167
  }
10369
- ) }) : /* @__PURE__ */ jsx("div", { className: "flex w-full flex-1 items-center justify-end", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: getLocaleAmount(item.unit_price, currencyCode) }) }),
10370
- /* @__PURE__ */ jsx(
10371
- IconButton,
10168
+ }
10169
+ }
10170
+ return Array.from(promotionIds);
10171
+ }
10172
+ const InlineTip = forwardRef(
10173
+ ({ variant = "tip", label, className, children, ...props }, ref) => {
10174
+ const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
10175
+ return /* @__PURE__ */ jsxs(
10176
+ "div",
10372
10177
  {
10373
- type: "button",
10374
- size: "small",
10375
- onClick: editing ? onSubmit : () => {
10376
- setEditing(true);
10377
- },
10378
- disabled: isPending,
10379
- children: editing ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(PencilSquare, {})
10178
+ ref,
10179
+ className: clx(
10180
+ "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
10181
+ className
10182
+ ),
10183
+ ...props,
10184
+ children: [
10185
+ /* @__PURE__ */ jsx(
10186
+ "div",
10187
+ {
10188
+ role: "presentation",
10189
+ className: clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
10190
+ "bg-ui-tag-orange-icon": variant === "warning"
10191
+ })
10192
+ }
10193
+ ),
10194
+ /* @__PURE__ */ jsxs("div", { className: "text-pretty", children: [
10195
+ /* @__PURE__ */ jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
10196
+ labelValue,
10197
+ ":"
10198
+ ] }),
10199
+ " ",
10200
+ children
10201
+ ] })
10202
+ ]
10380
10203
  }
10381
- )
10382
- ] }) }) });
10383
- };
10384
- const variantItemSchema = objectType({
10385
- quantity: numberType(),
10386
- unit_price: unionType([numberType(), stringType()])
10204
+ );
10205
+ }
10206
+ );
10207
+ InlineTip.displayName = "InlineTip";
10208
+ const MetadataFieldSchema = objectType({
10209
+ key: stringType(),
10210
+ disabled: booleanType().optional(),
10211
+ value: anyType()
10387
10212
  });
10388
- const CustomItem = ({ item, preview, currencyCode }) => {
10389
- const [editing, setEditing] = useState(false);
10390
- const { quantity, unit_price, title } = item;
10213
+ const MetadataSchema = objectType({
10214
+ metadata: arrayType(MetadataFieldSchema)
10215
+ });
10216
+ const Metadata = () => {
10217
+ const { id } = useParams();
10218
+ const { order, isPending, isError, error } = useOrder(id, {
10219
+ fields: "metadata"
10220
+ });
10221
+ if (isError) {
10222
+ throw error;
10223
+ }
10224
+ const isReady = !isPending && !!order;
10225
+ return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
10226
+ /* @__PURE__ */ jsxs(RouteDrawer.Header, { children: [
10227
+ /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Metadata" }) }),
10228
+ /* @__PURE__ */ jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
10229
+ ] }),
10230
+ !isReady ? /* @__PURE__ */ jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
10231
+ ] });
10232
+ };
10233
+ const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
10234
+ const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
10235
+ const MetadataForm = ({ orderId, metadata }) => {
10236
+ const { handleSuccess } = useRouteModal();
10237
+ const hasUneditableRows = getHasUneditableRows(metadata);
10238
+ const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
10391
10239
  const form = useForm({
10392
10240
  defaultValues: {
10393
- title,
10394
- quantity,
10395
- unit_price
10241
+ metadata: getDefaultValues(metadata)
10396
10242
  },
10397
- resolver: zodResolver(customItemSchema)
10243
+ resolver: zodResolver(MetadataSchema)
10398
10244
  });
10399
- useEffect(() => {
10400
- form.reset({
10401
- title,
10402
- quantity,
10403
- unit_price
10404
- });
10405
- }, [form, title, quantity, unit_price]);
10406
- const actionId = useMemo(() => {
10407
- var _a, _b;
10408
- return (_b = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD")) == null ? void 0 : _b.id;
10409
- }, [item]);
10410
- const { mutateAsync: updateActionItem, isPending: isUpdatingActionItem } = useDraftOrderUpdateActionItem(preview.id);
10411
- const { mutateAsync: removeActionItem, isPending: isRemovingActionItem } = useDraftOrderRemoveActionItem(preview.id);
10412
- const { mutateAsync: updateOriginalItem, isPending: isUpdatingOriginalItem } = useDraftOrderUpdateItem(preview.id);
10413
- const isPending = isUpdatingActionItem || isUpdatingOriginalItem;
10414
- const onSubmit = form.handleSubmit(async (data) => {
10415
- if (convertNumber(data.unit_price) === item.unit_price && data.quantity === item.quantity && data.title === item.title) {
10416
- setEditing(false);
10417
- return;
10418
- }
10419
- if (!actionId) {
10420
- await updateOriginalItem(
10421
- {
10422
- item_id: item.id,
10423
- quantity: data.quantity,
10424
- unit_price: convertNumber(data.unit_price)
10425
- },
10426
- {
10427
- onSuccess: () => {
10428
- setEditing(false);
10429
- },
10430
- onError: (e) => {
10431
- toast.error(e.message);
10432
- }
10433
- }
10434
- );
10435
- return;
10436
- }
10437
- if (data.quantity === 0) {
10438
- await removeActionItem(actionId, {
10439
- onSuccess: () => {
10440
- setEditing(false);
10441
- },
10442
- onError: (e) => {
10443
- toast.error(e.message);
10444
- }
10445
- });
10446
- return;
10447
- }
10448
- await updateActionItem(
10245
+ const handleSubmit = form.handleSubmit(async (data) => {
10246
+ const parsedData = parseValues(data);
10247
+ await mutateAsync(
10449
10248
  {
10450
- action_id: actionId,
10451
- quantity: data.quantity,
10452
- unit_price: convertNumber(data.unit_price)
10249
+ metadata: parsedData
10453
10250
  },
10454
10251
  {
10455
10252
  onSuccess: () => {
10456
- setEditing(false);
10253
+ toast.success("Metadata updated");
10254
+ handleSuccess();
10457
10255
  },
10458
- onError: (e) => {
10459
- toast.error(e.message);
10256
+ onError: (error) => {
10257
+ toast.error(error.message);
10460
10258
  }
10461
10259
  }
10462
10260
  );
10463
10261
  });
10464
- return /* @__PURE__ */ jsx(Form$2, { ...form, children: /* @__PURE__ */ jsx("form", { onSubmit, children: /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-[minmax(0,2fr)_minmax(0,1fr)_minmax(0,2fr)_28px] items-center gap-3 rounded-lg px-4 py-2", children: [
10465
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-3", children: [
10466
- /* @__PURE__ */ jsx(
10467
- Thumbnail,
10468
- {
10469
- thumbnail: item.thumbnail,
10470
- alt: item.title ?? void 0
10471
- }
10472
- ),
10473
- editing ? /* @__PURE__ */ jsx(
10474
- Form$2.Field,
10475
- {
10476
- control: form.control,
10477
- name: "title",
10478
- render: ({ field }) => {
10479
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(Input, { ...field }) }) });
10480
- }
10481
- }
10482
- ) : /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: item.title })
10483
- ] }),
10484
- editing ? /* @__PURE__ */ jsx(
10485
- Form$2.Field,
10486
- {
10487
- control: form.control,
10488
- name: "quantity",
10489
- render: ({ field }) => {
10490
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(NumberInput, { ...field }) }) });
10491
- }
10492
- }
10493
- ) : /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: item.quantity }),
10494
- editing ? /* @__PURE__ */ jsx(
10495
- Form$2.Field,
10496
- {
10497
- control: form.control,
10498
- name: "unit_price",
10499
- render: ({ field: { onChange, ...field } }) => {
10500
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10501
- CurrencyInput,
10502
- {
10503
- ...field,
10504
- symbol: getNativeSymbol(currencyCode),
10505
- code: currencyCode,
10506
- onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value)
10507
- }
10508
- ) }) });
10509
- }
10510
- }
10511
- ) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-end", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: getLocaleAmount(item.unit_price, currencyCode) }) }),
10512
- /* @__PURE__ */ jsx(
10513
- IconButton,
10514
- {
10515
- type: "button",
10516
- size: "small",
10517
- onClick: editing ? onSubmit : () => {
10518
- setEditing(true);
10519
- },
10520
- disabled: isPending,
10521
- children: editing ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(PencilSquare, {})
10522
- }
10523
- )
10524
- ] }) }) });
10525
- };
10526
- const StackedModalTrigger$1 = ({
10527
- type,
10528
- setModalContent
10529
- }) => {
10530
- const { setIsOpen } = useStackedModal();
10531
- const onClick = useCallback(() => {
10532
- setModalContent(type);
10533
- setIsOpen(STACKED_MODAL_ID, true);
10534
- }, [setModalContent, setIsOpen, type]);
10535
- return /* @__PURE__ */ jsx(StackedFocusModal.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(DropdownMenu.Item, { onClick, children: type === "add-items" ? "Add items" : "Add custom item" }) });
10536
- };
10537
- const VARIANT_PREFIX = "items";
10538
- const LIMIT = 50;
10539
- const ExistingItemsForm = ({ orderId, items }) => {
10540
- const { setIsOpen } = useStackedModal();
10541
- const [rowSelection, setRowSelection] = useState(
10542
- items.reduce((acc, item) => {
10543
- acc[item.variant_id] = true;
10544
- return acc;
10545
- }, {})
10546
- );
10547
- useEffect(() => {
10548
- setRowSelection(
10549
- items.reduce((acc, item) => {
10550
- if (item.variant_id) {
10551
- acc[item.variant_id] = true;
10552
- }
10553
- return acc;
10554
- }, {})
10555
- );
10556
- }, [items]);
10557
- const { q, order, offset } = useQueryParams(
10558
- ["q", "order", "offset"],
10559
- VARIANT_PREFIX
10560
- );
10561
- const { variants, count, isPending, isError, error } = useProductVariants(
10562
- {
10563
- q,
10564
- order,
10565
- offset: offset ? parseInt(offset) : void 0,
10566
- limit: LIMIT
10567
- },
10568
- {
10569
- placeholderData: keepPreviousData
10262
+ const { fields, insert, remove } = useFieldArray({
10263
+ control: form.control,
10264
+ name: "metadata"
10265
+ });
10266
+ function deleteRow(index) {
10267
+ remove(index);
10268
+ if (fields.length === 1) {
10269
+ insert(0, {
10270
+ key: "",
10271
+ value: "",
10272
+ disabled: false
10273
+ });
10570
10274
  }
10571
- );
10572
- const columns = useColumns();
10573
- const { mutateAsync } = useDraftOrderAddItems(orderId);
10574
- const onSubmit = async () => {
10575
- const ids = Object.keys(rowSelection).filter(
10576
- (id) => !items.find((i) => i.variant_id === id)
10577
- );
10578
- await mutateAsync(
10579
- {
10580
- items: ids.map((id) => ({
10581
- variant_id: id,
10582
- quantity: 1
10583
- }))
10584
- },
10585
- {
10586
- onSuccess: () => {
10587
- setRowSelection({});
10588
- setIsOpen(STACKED_MODAL_ID, false);
10589
- },
10590
- onError: (e) => {
10591
- toast.error(e.message);
10592
- }
10593
- }
10594
- );
10595
- };
10596
- if (isError) {
10597
- throw error;
10598
10275
  }
10599
- return /* @__PURE__ */ jsxs(
10600
- StackedFocusModal.Content,
10276
+ function insertRow(index, position) {
10277
+ insert(index + (position === "above" ? 0 : 1), {
10278
+ key: "",
10279
+ value: "",
10280
+ disabled: false
10281
+ });
10282
+ }
10283
+ return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(
10284
+ KeyboundForm,
10601
10285
  {
10602
- onOpenAutoFocus: (e) => {
10603
- e.preventDefault();
10604
- const searchInput = document.querySelector(
10605
- "[data-modal-id='modal-search-input']"
10606
- );
10607
- if (searchInput) {
10608
- searchInput.focus();
10609
- }
10610
- },
10286
+ onSubmit: handleSubmit,
10287
+ className: "flex flex-1 flex-col overflow-hidden",
10611
10288
  children: [
10612
- /* @__PURE__ */ jsxs(StackedFocusModal.Header, { children: [
10613
- /* @__PURE__ */ jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Product Variants" }) }),
10614
- /* @__PURE__ */ jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Choose product variants to add to the order." }) })
10615
- ] }),
10616
- /* @__PURE__ */ jsx(StackedFocusModal.Body, { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
10617
- DataTable,
10618
- {
10619
- data: variants,
10620
- columns,
10621
- isLoading: isPending,
10622
- getRowId: (row) => row.id,
10623
- rowCount: count,
10624
- prefix: VARIANT_PREFIX,
10625
- layout: "fill",
10626
- rowSelection: {
10627
- state: rowSelection,
10628
- onRowSelectionChange: setRowSelection,
10629
- enableRowSelection: (row) => {
10630
- return !items.find((i) => i.variant_id === row.original.id);
10631
- }
10632
- },
10633
- autoFocusSearch: true
10634
- }
10635
- ) }),
10636
- /* @__PURE__ */ jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10637
- /* @__PURE__ */ jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10638
- /* @__PURE__ */ jsx(Button, { size: "small", type: "button", onClick: onSubmit, children: "Update items" })
10289
+ /* @__PURE__ */ jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
10290
+ /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
10291
+ /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
10292
+ /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_KEY_LABEL_ID, children: "Key" }) }),
10293
+ /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_VALUE_LABEL_ID, children: "Value" }) })
10294
+ ] }),
10295
+ fields.map((field, index) => {
10296
+ const isDisabled = field.disabled || false;
10297
+ let placeholder = "-";
10298
+ if (typeof field.value === "object") {
10299
+ placeholder = "{ ... }";
10300
+ }
10301
+ if (Array.isArray(field.value)) {
10302
+ placeholder = "[ ... ]";
10303
+ }
10304
+ return /* @__PURE__ */ jsx(
10305
+ ConditionalTooltip,
10306
+ {
10307
+ showTooltip: isDisabled,
10308
+ content: "This row is disabled because it contains non-primitive data.",
10309
+ children: /* @__PURE__ */ jsxs("div", { className: "group/table relative", children: [
10310
+ /* @__PURE__ */ jsxs(
10311
+ "div",
10312
+ {
10313
+ className: clx("grid grid-cols-2 divide-x", {
10314
+ "overflow-hidden rounded-b-lg": index === fields.length - 1
10315
+ }),
10316
+ children: [
10317
+ /* @__PURE__ */ jsx(
10318
+ Form$2.Field,
10319
+ {
10320
+ control: form.control,
10321
+ name: `metadata.${index}.key`,
10322
+ render: ({ field: field2 }) => {
10323
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10324
+ GridInput,
10325
+ {
10326
+ "aria-labelledby": METADATA_KEY_LABEL_ID,
10327
+ ...field2,
10328
+ disabled: isDisabled,
10329
+ placeholder: "Key"
10330
+ }
10331
+ ) }) });
10332
+ }
10333
+ }
10334
+ ),
10335
+ /* @__PURE__ */ jsx(
10336
+ Form$2.Field,
10337
+ {
10338
+ control: form.control,
10339
+ name: `metadata.${index}.value`,
10340
+ render: ({ field: { value, ...field2 } }) => {
10341
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10342
+ GridInput,
10343
+ {
10344
+ "aria-labelledby": METADATA_VALUE_LABEL_ID,
10345
+ ...field2,
10346
+ value: isDisabled ? placeholder : value,
10347
+ disabled: isDisabled,
10348
+ placeholder: "Value"
10349
+ }
10350
+ ) }) });
10351
+ }
10352
+ }
10353
+ )
10354
+ ]
10355
+ }
10356
+ ),
10357
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
10358
+ /* @__PURE__ */ jsx(
10359
+ DropdownMenu.Trigger,
10360
+ {
10361
+ className: clx(
10362
+ "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
10363
+ {
10364
+ hidden: isDisabled
10365
+ }
10366
+ ),
10367
+ disabled: isDisabled,
10368
+ asChild: true,
10369
+ children: /* @__PURE__ */ jsx(IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsx(EllipsisVertical, {}) })
10370
+ }
10371
+ ),
10372
+ /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [
10373
+ /* @__PURE__ */ jsxs(
10374
+ DropdownMenu.Item,
10375
+ {
10376
+ className: "gap-x-2",
10377
+ onClick: () => insertRow(index, "above"),
10378
+ children: [
10379
+ /* @__PURE__ */ jsx(ArrowUpMini, { className: "text-ui-fg-subtle" }),
10380
+ "Insert row above"
10381
+ ]
10382
+ }
10383
+ ),
10384
+ /* @__PURE__ */ jsxs(
10385
+ DropdownMenu.Item,
10386
+ {
10387
+ className: "gap-x-2",
10388
+ onClick: () => insertRow(index, "below"),
10389
+ children: [
10390
+ /* @__PURE__ */ jsx(ArrowDownMini, { className: "text-ui-fg-subtle" }),
10391
+ "Insert row below"
10392
+ ]
10393
+ }
10394
+ ),
10395
+ /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
10396
+ /* @__PURE__ */ jsxs(
10397
+ DropdownMenu.Item,
10398
+ {
10399
+ className: "gap-x-2",
10400
+ onClick: () => deleteRow(index),
10401
+ children: [
10402
+ /* @__PURE__ */ jsx(Trash, { className: "text-ui-fg-subtle" }),
10403
+ "Delete row"
10404
+ ]
10405
+ }
10406
+ )
10407
+ ] })
10408
+ ] })
10409
+ ] })
10410
+ },
10411
+ field.id
10412
+ );
10413
+ })
10414
+ ] }),
10415
+ hasUneditableRows && /* @__PURE__ */ jsx(InlineTip, { variant: "warning", label: "Some rows are disabled", children: "This object contains non-primitive metadata, such as arrays or objects, that can't be edited here. To edit the disabled rows, use the API directly." })
10416
+ ] }),
10417
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10418
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10419
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
10639
10420
  ] }) })
10640
10421
  ]
10641
10422
  }
10423
+ ) });
10424
+ };
10425
+ const GridInput = forwardRef(({ className, ...props }, ref) => {
10426
+ return /* @__PURE__ */ jsx(
10427
+ "input",
10428
+ {
10429
+ ref,
10430
+ ...props,
10431
+ autoComplete: "off",
10432
+ className: clx(
10433
+ "txt-compact-small text-ui-fg-base placeholder:text-ui-fg-muted disabled:text-ui-fg-disabled disabled:bg-ui-bg-base bg-transparent px-2 py-1.5 outline-none",
10434
+ className
10435
+ )
10436
+ }
10642
10437
  );
10438
+ });
10439
+ GridInput.displayName = "MetadataForm.GridInput";
10440
+ const PlaceholderInner = () => {
10441
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
10442
+ /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
10443
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10444
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" }),
10445
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" })
10446
+ ] }) })
10447
+ ] });
10643
10448
  };
10644
- const columnHelper = createDataTableColumnHelper();
10645
- const useColumns = () => {
10646
- return useMemo(() => {
10449
+ const EDITABLE_TYPES = ["string", "number", "boolean"];
10450
+ function getDefaultValues(metadata) {
10451
+ if (!metadata || !Object.keys(metadata).length) {
10647
10452
  return [
10648
- columnHelper.select(),
10649
- columnHelper.accessor("product.title", {
10650
- header: "Product",
10651
- cell: ({ row }) => {
10652
- var _a, _b, _c;
10653
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
10654
- /* @__PURE__ */ jsx(
10655
- Thumbnail,
10656
- {
10657
- thumbnail: (_a = row.original.product) == null ? void 0 : _a.thumbnail,
10658
- alt: (_b = row.original.product) == null ? void 0 : _b.title
10659
- }
10660
- ),
10661
- /* @__PURE__ */ jsx("span", { children: (_c = row.original.product) == null ? void 0 : _c.title })
10662
- ] });
10663
- },
10664
- enableSorting: true
10665
- }),
10666
- columnHelper.accessor("title", {
10667
- header: "Variant",
10668
- enableSorting: true
10669
- }),
10670
- columnHelper.accessor("sku", {
10671
- header: "SKU",
10672
- cell: ({ getValue }) => {
10673
- return getValue() ?? "-";
10674
- },
10675
- enableSorting: true
10676
- }),
10677
- columnHelper.accessor("updated_at", {
10678
- header: "Updated",
10679
- cell: ({ getValue }) => {
10680
- return /* @__PURE__ */ jsx(
10681
- Tooltip,
10682
- {
10683
- content: getFullDate({ date: getValue(), includeTime: true }),
10684
- children: /* @__PURE__ */ jsx("span", { children: getFullDate({ date: getValue() }) })
10685
- }
10686
- );
10687
- },
10688
- enableSorting: true,
10689
- sortAscLabel: "Oldest first",
10690
- sortDescLabel: "Newest first"
10691
- }),
10692
- columnHelper.accessor("created_at", {
10693
- header: "Created",
10694
- cell: ({ getValue }) => {
10695
- return /* @__PURE__ */ jsx(
10696
- Tooltip,
10697
- {
10698
- content: getFullDate({ date: getValue(), includeTime: true }),
10699
- children: /* @__PURE__ */ jsx("span", { children: getFullDate({ date: getValue() }) })
10700
- }
10701
- );
10702
- },
10703
- enableSorting: true,
10704
- sortAscLabel: "Oldest first",
10705
- sortDescLabel: "Newest first"
10706
- })
10453
+ {
10454
+ key: "",
10455
+ value: "",
10456
+ disabled: false
10457
+ }
10707
10458
  ];
10708
- }, []);
10709
- };
10710
- const CustomItemForm = ({ orderId, currencyCode }) => {
10711
- const { setIsOpen } = useStackedModal();
10712
- const { mutateAsync: addItems } = useDraftOrderAddItems(orderId);
10713
- const form = useForm({
10714
- defaultValues: {
10715
- title: "",
10716
- quantity: 1,
10717
- unit_price: ""
10718
- },
10719
- resolver: zodResolver(customItemSchema)
10459
+ }
10460
+ return Object.entries(metadata).map(([key, value]) => {
10461
+ if (!EDITABLE_TYPES.includes(typeof value)) {
10462
+ return {
10463
+ key,
10464
+ value,
10465
+ disabled: true
10466
+ };
10467
+ }
10468
+ let stringValue = value;
10469
+ if (typeof value !== "string") {
10470
+ stringValue = JSON.stringify(value);
10471
+ }
10472
+ return {
10473
+ key,
10474
+ value: stringValue,
10475
+ original_key: key
10476
+ };
10720
10477
  });
10721
- const onSubmit = form.handleSubmit(async (data) => {
10722
- await addItems(
10478
+ }
10479
+ function parseValues(values) {
10480
+ const metadata = values.metadata;
10481
+ const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
10482
+ if (isEmpty) {
10483
+ return null;
10484
+ }
10485
+ const update = {};
10486
+ metadata.forEach((field) => {
10487
+ let key = field.key;
10488
+ let value = field.value;
10489
+ const disabled = field.disabled;
10490
+ if (!key || !value) {
10491
+ return;
10492
+ }
10493
+ if (disabled) {
10494
+ update[key] = value;
10495
+ return;
10496
+ }
10497
+ key = key.trim();
10498
+ value = value.trim();
10499
+ if (value === "true") {
10500
+ update[key] = true;
10501
+ } else if (value === "false") {
10502
+ update[key] = false;
10503
+ } else {
10504
+ const parsedNumber = parseFloat(value);
10505
+ if (!isNaN(parsedNumber)) {
10506
+ update[key] = parsedNumber;
10507
+ } else {
10508
+ update[key] = value;
10509
+ }
10510
+ }
10511
+ });
10512
+ return update;
10513
+ }
10514
+ function getHasUneditableRows(metadata) {
10515
+ if (!metadata) {
10516
+ return false;
10517
+ }
10518
+ return Object.values(metadata).some(
10519
+ (value) => !EDITABLE_TYPES.includes(typeof value)
10520
+ );
10521
+ }
10522
+ const NumberInput = forwardRef(
10523
+ ({
10524
+ value,
10525
+ onChange,
10526
+ size = "base",
10527
+ min = 0,
10528
+ max = 100,
10529
+ step = 1,
10530
+ className,
10531
+ disabled,
10532
+ ...props
10533
+ }, ref) => {
10534
+ const handleChange = (event) => {
10535
+ const newValue = event.target.value === "" ? min : Number(event.target.value);
10536
+ if (!isNaN(newValue) && (max === void 0 || newValue <= max) && (min === void 0 || newValue >= min)) {
10537
+ onChange(newValue);
10538
+ }
10539
+ };
10540
+ const handleIncrement = () => {
10541
+ const newValue = value + step;
10542
+ if (max === void 0 || newValue <= max) {
10543
+ onChange(newValue);
10544
+ }
10545
+ };
10546
+ const handleDecrement = () => {
10547
+ const newValue = value - step;
10548
+ if (min === void 0 || newValue >= min) {
10549
+ onChange(newValue);
10550
+ }
10551
+ };
10552
+ return /* @__PURE__ */ jsxs(
10553
+ "div",
10723
10554
  {
10724
- items: [
10555
+ className: clx(
10556
+ "inline-flex rounded-md bg-ui-bg-field shadow-borders-base overflow-hidden divide-x transition-fg",
10557
+ "[&:has(input:focus)]:shadow-borders-interactive-with-active",
10725
10558
  {
10726
- title: data.title,
10727
- quantity: data.quantity,
10728
- unit_price: convertNumber(data.unit_price)
10559
+ "h-7": size === "small",
10560
+ "h-8": size === "base"
10561
+ },
10562
+ className
10563
+ ),
10564
+ children: [
10565
+ /* @__PURE__ */ jsx(
10566
+ "input",
10567
+ {
10568
+ ref,
10569
+ type: "number",
10570
+ value,
10571
+ onChange: handleChange,
10572
+ min,
10573
+ max,
10574
+ step,
10575
+ className: clx(
10576
+ "flex-1 px-2 py-1 bg-transparent txt-compact-small text-ui-fg-base outline-none [appearance:textfield]",
10577
+ "[&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
10578
+ "placeholder:text-ui-fg-muted"
10579
+ ),
10580
+ ...props
10581
+ }
10582
+ ),
10583
+ /* @__PURE__ */ jsxs(
10584
+ "button",
10585
+ {
10586
+ className: clx(
10587
+ "flex items-center justify-center outline-none transition-fg",
10588
+ "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
10589
+ "focus:bg-ui-bg-field-component-hover",
10590
+ "hover:bg-ui-bg-field-component-hover",
10591
+ {
10592
+ "size-7": size === "small",
10593
+ "size-8": size === "base"
10594
+ }
10595
+ ),
10596
+ type: "button",
10597
+ onClick: handleDecrement,
10598
+ disabled: min !== void 0 && value <= min || disabled,
10599
+ children: [
10600
+ /* @__PURE__ */ jsx(Minus, {}),
10601
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: `Decrease by ${step}` })
10602
+ ]
10603
+ }
10604
+ ),
10605
+ /* @__PURE__ */ jsxs(
10606
+ "button",
10607
+ {
10608
+ className: clx(
10609
+ "flex items-center justify-center outline-none transition-fg",
10610
+ "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
10611
+ "focus:bg-ui-bg-field-hover",
10612
+ "hover:bg-ui-bg-field-hover",
10613
+ {
10614
+ "size-7": size === "small",
10615
+ "size-8": size === "base"
10616
+ }
10617
+ ),
10618
+ type: "button",
10619
+ onClick: handleIncrement,
10620
+ disabled: max !== void 0 && value >= max || disabled,
10621
+ children: [
10622
+ /* @__PURE__ */ jsx(Plus, {}),
10623
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: `Increase by ${step}` })
10624
+ ]
10625
+ }
10626
+ )
10627
+ ]
10628
+ }
10629
+ );
10630
+ }
10631
+ );
10632
+ const PRODUCT_VARIANTS_QUERY_KEY = "product-variants";
10633
+ const productVariantsQueryKeys = {
10634
+ list: (query2) => [
10635
+ PRODUCT_VARIANTS_QUERY_KEY,
10636
+ query2 ? query2 : void 0
10637
+ ]
10638
+ };
10639
+ const useProductVariants = (query2, options) => {
10640
+ const { data, ...rest } = useQuery({
10641
+ queryKey: productVariantsQueryKeys.list(query2),
10642
+ queryFn: async () => await sdk.admin.productVariant.list(query2),
10643
+ ...options
10644
+ });
10645
+ return { ...data, ...rest };
10646
+ };
10647
+ function convertNumber(value) {
10648
+ return typeof value === "string" ? Number(value.replace(",", ".")) : value;
10649
+ }
10650
+ const STACKED_MODAL_ID = "items_stacked_modal";
10651
+ const Items = () => {
10652
+ const { id } = useParams();
10653
+ const {
10654
+ order: preview,
10655
+ isPending: isPreviewPending,
10656
+ isError: isPreviewError,
10657
+ error: previewError
10658
+ } = useOrderPreview(id, void 0, {
10659
+ placeholderData: keepPreviousData
10660
+ });
10661
+ useInitiateOrderEdit({ preview });
10662
+ const { draft_order, isPending, isError, error } = useDraftOrder(
10663
+ id,
10664
+ {
10665
+ fields: "currency_code"
10666
+ },
10667
+ {
10668
+ enabled: !!id
10669
+ }
10670
+ );
10671
+ const { onCancel } = useCancelOrderEdit({ preview });
10672
+ if (isError) {
10673
+ throw error;
10674
+ }
10675
+ if (isPreviewError) {
10676
+ throw previewError;
10677
+ }
10678
+ const ready = !!preview && !isPreviewPending && !!draft_order && !isPending;
10679
+ return /* @__PURE__ */ jsx(RouteFocusModal, { onClose: onCancel, children: ready ? /* @__PURE__ */ jsx(ItemsForm, { preview, currencyCode: draft_order.currency_code }) : /* @__PURE__ */ jsxs("div", { children: [
10680
+ /* @__PURE__ */ jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Edit Items" }) }),
10681
+ /* @__PURE__ */ jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading data for the draft order, please wait..." }) })
10682
+ ] }) });
10683
+ };
10684
+ const ItemsForm = ({ preview, currencyCode }) => {
10685
+ var _a;
10686
+ const [isSubmitting, setIsSubmitting] = useState(false);
10687
+ const [modalContent, setModalContent] = useState(
10688
+ null
10689
+ );
10690
+ const { handleSuccess } = useRouteModal();
10691
+ const { searchValue, onSearchValueChange, query: query2 } = useDebouncedSearch();
10692
+ const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
10693
+ const { mutateAsync: requestOrderEdit } = useDraftOrderRequestEdit(preview.id);
10694
+ const itemCount = ((_a = preview.items) == null ? void 0 : _a.reduce((acc, item) => acc + item.quantity, 0)) || 0;
10695
+ const matches = useMemo(() => {
10696
+ return matchSorter(preview.items, query2, {
10697
+ keys: ["product_title", "variant_title", "variant_sku", "title"]
10698
+ });
10699
+ }, [preview.items, query2]);
10700
+ const onSubmit = async () => {
10701
+ setIsSubmitting(true);
10702
+ let requestSucceeded = false;
10703
+ await requestOrderEdit(void 0, {
10704
+ onError: (e) => {
10705
+ toast.error(`Failed to request order edit: ${e.message}`);
10706
+ },
10707
+ onSuccess: () => {
10708
+ requestSucceeded = true;
10709
+ }
10710
+ });
10711
+ if (!requestSucceeded) {
10712
+ setIsSubmitting(false);
10713
+ return;
10714
+ }
10715
+ await confirmOrderEdit(void 0, {
10716
+ onError: (e) => {
10717
+ toast.error(`Failed to confirm order edit: ${e.message}`);
10718
+ },
10719
+ onSuccess: () => {
10720
+ handleSuccess();
10721
+ },
10722
+ onSettled: () => {
10723
+ setIsSubmitting(false);
10724
+ }
10725
+ });
10726
+ };
10727
+ const onKeyDown = useCallback(
10728
+ (e) => {
10729
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
10730
+ if (modalContent || isSubmitting) {
10731
+ return;
10732
+ }
10733
+ onSubmit();
10734
+ }
10735
+ },
10736
+ [modalContent, isSubmitting, onSubmit]
10737
+ );
10738
+ useEffect(() => {
10739
+ document.addEventListener("keydown", onKeyDown);
10740
+ return () => {
10741
+ document.removeEventListener("keydown", onKeyDown);
10742
+ };
10743
+ }, [onKeyDown]);
10744
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col overflow-hidden", children: [
10745
+ /* @__PURE__ */ jsx(RouteFocusModal.Header, {}),
10746
+ /* @__PURE__ */ jsx(RouteFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsxs(
10747
+ StackedFocusModal,
10748
+ {
10749
+ id: STACKED_MODAL_ID,
10750
+ onOpenChangeCallback: (open) => {
10751
+ if (!open) {
10752
+ setModalContent(null);
10729
10753
  }
10730
- ]
10731
- },
10732
- {
10733
- onSuccess: () => {
10734
- setIsOpen(STACKED_MODAL_ID, false);
10735
10754
  },
10736
- onError: (e) => {
10737
- toast.error(e.message);
10738
- }
10739
- }
10740
- );
10741
- });
10742
- return /* @__PURE__ */ jsx(Form$2, { ...form, children: /* @__PURE__ */ jsx(KeyboundForm, { onSubmit, children: /* @__PURE__ */ jsxs(StackedFocusModal.Content, { children: [
10743
- /* @__PURE__ */ jsx(StackedFocusModal.Header, {}),
10744
- /* @__PURE__ */ jsx(StackedFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col items-center overflow-y-auto", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full max-w-[720px] flex-col gap-y-6 px-2 py-16", children: [
10745
- /* @__PURE__ */ jsxs("div", { children: [
10746
- /* @__PURE__ */ jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Add custom item" }) }),
10747
- /* @__PURE__ */ jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Add a custom item to the order. This will add a new line item that is not associated with an existing product." }) })
10748
- ] }),
10749
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10750
- /* @__PURE__ */ jsx(
10751
- Form$2.Field,
10752
- {
10753
- control: form.control,
10754
- name: "title",
10755
- render: ({ field }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
10755
+ children: [
10756
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col items-center overflow-y-auto", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full max-w-[720px] flex-col gap-y-6 px-6 py-16", children: [
10756
10757
  /* @__PURE__ */ jsxs("div", { children: [
10757
- /* @__PURE__ */ jsx(Form$2.Label, { children: "Title" }),
10758
- /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the title of the item" })
10758
+ /* @__PURE__ */ jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Items" }) }),
10759
+ /* @__PURE__ */ jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Edit the items in the draft order" }) })
10759
10760
  ] }),
10760
- /* @__PURE__ */ jsxs("div", { children: [
10761
- /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(Input, { ...field }) }),
10762
- /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
10763
- ] })
10764
- ] }) })
10765
- }
10766
- ),
10767
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10768
- /* @__PURE__ */ jsx(
10769
- Form$2.Field,
10770
- {
10771
- control: form.control,
10772
- name: "unit_price",
10773
- render: ({ field: { onChange, ...field } }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
10774
- /* @__PURE__ */ jsxs("div", { children: [
10775
- /* @__PURE__ */ jsx(Form$2.Label, { children: "Unit price" }),
10776
- /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the unit price of the item" })
10761
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10762
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-6", children: [
10763
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 items-center gap-3", children: [
10764
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
10765
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: "Items" }),
10766
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Choose items from the product catalog." })
10767
+ ] }),
10768
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
10769
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
10770
+ Input,
10771
+ {
10772
+ type: "search",
10773
+ placeholder: "Search items",
10774
+ value: searchValue,
10775
+ onChange: (e) => onSearchValueChange(e.target.value)
10776
+ }
10777
+ ) }),
10778
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
10779
+ /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { type: "button", children: /* @__PURE__ */ jsx(Plus, {}) }) }),
10780
+ /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [
10781
+ /* @__PURE__ */ jsx(
10782
+ StackedModalTrigger$1,
10783
+ {
10784
+ type: "add-items",
10785
+ setModalContent
10786
+ }
10787
+ ),
10788
+ /* @__PURE__ */ jsx(
10789
+ StackedModalTrigger$1,
10790
+ {
10791
+ type: "add-custom-item",
10792
+ setModalContent
10793
+ }
10794
+ )
10795
+ ] })
10796
+ ] })
10797
+ ] })
10798
+ ] }),
10799
+ /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle shadow-elevation-card-rest rounded-xl", children: [
10800
+ /* @__PURE__ */ jsx("div", { className: "px-[5px]", children: /* @__PURE__ */ jsxs("div", { className: "text-ui-fg-muted grid grid-cols-[2fr_1fr_2fr_28px] gap-3 px-4 py-2", children: [
10801
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Item" }) }),
10802
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Quantity" }) }),
10803
+ /* @__PURE__ */ jsx("div", { className: "text-right", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Price" }) }),
10804
+ /* @__PURE__ */ jsx("div", {})
10805
+ ] }) }),
10806
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-y-1.5 px-[5px] pb-[5px]", children: itemCount <= 0 ? /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest flex flex-col items-center justify-center gap-1 gap-x-3 rounded-lg p-4", children: [
10807
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: "There are no items in this order" }),
10808
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Add items to the order to get started." })
10809
+ ] }) : matches.length > 0 ? matches == null ? void 0 : matches.map((item) => /* @__PURE__ */ jsx(
10810
+ Item,
10811
+ {
10812
+ item,
10813
+ preview,
10814
+ currencyCode
10815
+ },
10816
+ item.id
10817
+ )) : /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest flex flex-col items-center justify-center gap-1 gap-x-3 rounded-lg p-4", children: [
10818
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: "No items found" }),
10819
+ /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
10820
+ 'No items found for "',
10821
+ query2,
10822
+ '".'
10823
+ ] })
10824
+ ] }) })
10825
+ ] })
10777
10826
  ] }),
10778
- /* @__PURE__ */ jsxs("div", { children: [
10779
- /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10780
- CurrencyInput,
10827
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10828
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_0.5fr_0.5fr] gap-3", children: [
10829
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: "Subtotal" }) }),
10830
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
10831
+ Text,
10781
10832
  {
10782
- symbol: getNativeSymbol(currencyCode),
10783
- code: currencyCode,
10784
- onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value),
10785
- ...field
10833
+ size: "small",
10834
+ leading: "compact",
10835
+ className: "text-ui-fg-subtle",
10836
+ children: [
10837
+ itemCount,
10838
+ " ",
10839
+ itemCount === 1 ? "item" : "items"
10840
+ ]
10786
10841
  }
10787
10842
  ) }),
10788
- /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
10843
+ /* @__PURE__ */ jsx("div", { className: "text-right", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: getStylizedAmount(preview.item_subtotal, currencyCode) }) })
10789
10844
  ] })
10790
- ] }) })
10791
- }
10792
- ),
10793
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10845
+ ] }) }),
10846
+ modalContent && (modalContent === "add-items" ? /* @__PURE__ */ jsx(ExistingItemsForm, { orderId: preview.id, items: preview.items }) : modalContent === "add-custom-item" ? /* @__PURE__ */ jsx(
10847
+ CustomItemForm,
10848
+ {
10849
+ orderId: preview.id,
10850
+ currencyCode
10851
+ }
10852
+ ) : null)
10853
+ ]
10854
+ }
10855
+ ) }),
10856
+ /* @__PURE__ */ jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10857
+ /* @__PURE__ */ jsx(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10794
10858
  /* @__PURE__ */ jsx(
10795
- Form$2.Field,
10859
+ Button,
10796
10860
  {
10797
- control: form.control,
10798
- name: "quantity",
10799
- render: ({ field }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
10800
- /* @__PURE__ */ jsxs("div", { children: [
10801
- /* @__PURE__ */ jsx(Form$2.Label, { children: "Quantity" }),
10802
- /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the quantity of the item" })
10803
- ] }),
10804
- /* @__PURE__ */ jsxs("div", { className: "w-full flex-1", children: [
10805
- /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(NumberInput, { ...field, className: "w-full" }) }) }),
10806
- /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
10807
- ] })
10808
- ] }) })
10861
+ size: "small",
10862
+ type: "button",
10863
+ onClick: onSubmit,
10864
+ isLoading: isSubmitting,
10865
+ children: "Save"
10809
10866
  }
10810
10867
  )
10811
- ] }) }) }),
10812
- /* @__PURE__ */ jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10813
- /* @__PURE__ */ jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10814
- /* @__PURE__ */ jsx(Button, { size: "small", type: "button", onClick: onSubmit, children: "Add item" })
10815
10868
  ] }) })
10816
- ] }) }) });
10869
+ ] });
10817
10870
  };
10818
- const customItemSchema = objectType({
10819
- title: stringType().min(1),
10820
- quantity: numberType(),
10821
- unit_price: unionType([numberType(), stringType()])
10822
- });
10823
- const InlineTip = forwardRef(
10824
- ({ variant = "tip", label, className, children, ...props }, ref) => {
10825
- const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
10826
- return /* @__PURE__ */ jsxs(
10827
- "div",
10828
- {
10829
- ref,
10830
- className: clx(
10831
- "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
10832
- className
10833
- ),
10834
- ...props,
10835
- children: [
10836
- /* @__PURE__ */ jsx(
10837
- "div",
10838
- {
10839
- role: "presentation",
10840
- className: clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
10841
- "bg-ui-tag-orange-icon": variant === "warning"
10842
- })
10843
- }
10844
- ),
10845
- /* @__PURE__ */ jsxs("div", { className: "text-pretty", children: [
10846
- /* @__PURE__ */ jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
10847
- labelValue,
10848
- ":"
10849
- ] }),
10850
- " ",
10851
- children
10852
- ] })
10853
- ]
10854
- }
10855
- );
10856
- }
10857
- );
10858
- InlineTip.displayName = "InlineTip";
10859
- const MetadataFieldSchema = objectType({
10860
- key: stringType(),
10861
- disabled: booleanType().optional(),
10862
- value: anyType()
10863
- });
10864
- const MetadataSchema = objectType({
10865
- metadata: arrayType(MetadataFieldSchema)
10866
- });
10867
- const Metadata = () => {
10868
- const { id } = useParams();
10869
- const { order, isPending, isError, error } = useOrder(id, {
10870
- fields: "metadata"
10871
- });
10872
- if (isError) {
10873
- throw error;
10871
+ const Item = ({ item, preview, currencyCode }) => {
10872
+ if (item.variant_id) {
10873
+ return /* @__PURE__ */ jsx(VariantItem, { item, preview, currencyCode });
10874
10874
  }
10875
- const isReady = !isPending && !!order;
10876
- return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
10877
- /* @__PURE__ */ jsxs(RouteDrawer.Header, { children: [
10878
- /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Metadata" }) }),
10879
- /* @__PURE__ */ jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
10880
- ] }),
10881
- !isReady ? /* @__PURE__ */ jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
10882
- ] });
10875
+ return /* @__PURE__ */ jsx(CustomItem, { item, preview, currencyCode });
10883
10876
  };
10884
- const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
10885
- const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
10886
- const MetadataForm = ({ orderId, metadata }) => {
10887
- const { handleSuccess } = useRouteModal();
10888
- const hasUneditableRows = getHasUneditableRows(metadata);
10889
- const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
10877
+ const VariantItem = ({ item, preview, currencyCode }) => {
10878
+ const [editing, setEditing] = useState(false);
10890
10879
  const form = useForm({
10891
10880
  defaultValues: {
10892
- metadata: getDefaultValues(metadata)
10881
+ quantity: item.quantity,
10882
+ unit_price: item.unit_price
10893
10883
  },
10894
- resolver: zodResolver(MetadataSchema)
10884
+ resolver: zodResolver(variantItemSchema)
10895
10885
  });
10896
- const handleSubmit = form.handleSubmit(async (data) => {
10897
- const parsedData = parseValues(data);
10898
- await mutateAsync(
10886
+ const actionId = useMemo(() => {
10887
+ var _a, _b;
10888
+ return (_b = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD")) == null ? void 0 : _b.id;
10889
+ }, [item]);
10890
+ const { mutateAsync: updateActionItem, isPending: isUpdatingActionItem } = useDraftOrderUpdateActionItem(preview.id);
10891
+ const { mutateAsync: updateOriginalItem, isPending: isUpdatingOriginalItem } = useDraftOrderUpdateItem(preview.id);
10892
+ const isPending = isUpdatingActionItem || isUpdatingOriginalItem;
10893
+ const onSubmit = form.handleSubmit(async (data) => {
10894
+ if (convertNumber(data.unit_price) === item.unit_price && data.quantity === item.quantity) {
10895
+ setEditing(false);
10896
+ return;
10897
+ }
10898
+ if (!actionId) {
10899
+ await updateOriginalItem(
10900
+ {
10901
+ item_id: item.id,
10902
+ quantity: data.quantity,
10903
+ unit_price: convertNumber(data.unit_price)
10904
+ },
10905
+ {
10906
+ onSuccess: () => {
10907
+ setEditing(false);
10908
+ },
10909
+ onError: (e) => {
10910
+ toast.error(e.message);
10911
+ }
10912
+ }
10913
+ );
10914
+ return;
10915
+ }
10916
+ await updateActionItem(
10899
10917
  {
10900
- metadata: parsedData
10918
+ action_id: actionId,
10919
+ quantity: data.quantity,
10920
+ unit_price: convertNumber(data.unit_price)
10901
10921
  },
10902
10922
  {
10903
10923
  onSuccess: () => {
10904
- toast.success("Metadata updated");
10905
- handleSuccess();
10924
+ setEditing(false);
10906
10925
  },
10907
- onError: (error) => {
10908
- toast.error(error.message);
10926
+ onError: (e) => {
10927
+ toast.error(e.message);
10909
10928
  }
10910
10929
  }
10911
10930
  );
10912
10931
  });
10913
- const { fields, insert, remove } = useFieldArray({
10914
- control: form.control,
10915
- name: "metadata"
10916
- });
10917
- function deleteRow(index) {
10918
- remove(index);
10919
- if (fields.length === 1) {
10920
- insert(0, {
10921
- key: "",
10922
- value: "",
10923
- disabled: false
10924
- });
10925
- }
10926
- }
10927
- function insertRow(index, position) {
10928
- insert(index + (position === "above" ? 0 : 1), {
10929
- key: "",
10930
- value: "",
10931
- disabled: false
10932
- });
10933
- }
10934
- return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(
10935
- KeyboundForm,
10936
- {
10937
- onSubmit: handleSubmit,
10938
- className: "flex flex-1 flex-col overflow-hidden",
10939
- children: [
10940
- /* @__PURE__ */ jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
10941
- /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
10942
- /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
10943
- /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_KEY_LABEL_ID, children: "Key" }) }),
10944
- /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_VALUE_LABEL_ID, children: "Value" }) })
10945
- ] }),
10946
- fields.map((field, index) => {
10947
- const isDisabled = field.disabled || false;
10948
- let placeholder = "-";
10949
- if (typeof field.value === "object") {
10950
- placeholder = "{ ... }";
10951
- }
10952
- if (Array.isArray(field.value)) {
10953
- placeholder = "[ ... ]";
10954
- }
10955
- return /* @__PURE__ */ jsx(
10956
- ConditionalTooltip,
10957
- {
10958
- showTooltip: isDisabled,
10959
- content: "This row is disabled because it contains non-primitive data.",
10960
- children: /* @__PURE__ */ jsxs("div", { className: "group/table relative", children: [
10961
- /* @__PURE__ */ jsxs(
10962
- "div",
10963
- {
10964
- className: clx("grid grid-cols-2 divide-x", {
10965
- "overflow-hidden rounded-b-lg": index === fields.length - 1
10966
- }),
10967
- children: [
10968
- /* @__PURE__ */ jsx(
10969
- Form$2.Field,
10970
- {
10971
- control: form.control,
10972
- name: `metadata.${index}.key`,
10973
- render: ({ field: field2 }) => {
10974
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10975
- GridInput,
10976
- {
10977
- "aria-labelledby": METADATA_KEY_LABEL_ID,
10978
- ...field2,
10979
- disabled: isDisabled,
10980
- placeholder: "Key"
10981
- }
10982
- ) }) });
10983
- }
10984
- }
10985
- ),
10986
- /* @__PURE__ */ jsx(
10987
- Form$2.Field,
10988
- {
10989
- control: form.control,
10990
- name: `metadata.${index}.value`,
10991
- render: ({ field: { value, ...field2 } }) => {
10992
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10993
- GridInput,
10994
- {
10995
- "aria-labelledby": METADATA_VALUE_LABEL_ID,
10996
- ...field2,
10997
- value: isDisabled ? placeholder : value,
10998
- disabled: isDisabled,
10999
- placeholder: "Value"
11000
- }
11001
- ) }) });
11002
- }
11003
- }
11004
- )
11005
- ]
11006
- }
11007
- ),
11008
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
11009
- /* @__PURE__ */ jsx(
11010
- DropdownMenu.Trigger,
11011
- {
11012
- className: clx(
11013
- "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
11014
- {
11015
- hidden: isDisabled
11016
- }
11017
- ),
11018
- disabled: isDisabled,
11019
- asChild: true,
11020
- children: /* @__PURE__ */ jsx(IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsx(EllipsisVertical, {}) })
11021
- }
11022
- ),
11023
- /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [
11024
- /* @__PURE__ */ jsxs(
11025
- DropdownMenu.Item,
11026
- {
11027
- className: "gap-x-2",
11028
- onClick: () => insertRow(index, "above"),
11029
- children: [
11030
- /* @__PURE__ */ jsx(ArrowUpMini, { className: "text-ui-fg-subtle" }),
11031
- "Insert row above"
11032
- ]
11033
- }
11034
- ),
11035
- /* @__PURE__ */ jsxs(
11036
- DropdownMenu.Item,
11037
- {
11038
- className: "gap-x-2",
11039
- onClick: () => insertRow(index, "below"),
11040
- children: [
11041
- /* @__PURE__ */ jsx(ArrowDownMini, { className: "text-ui-fg-subtle" }),
11042
- "Insert row below"
11043
- ]
11044
- }
11045
- ),
11046
- /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
11047
- /* @__PURE__ */ jsxs(
11048
- DropdownMenu.Item,
11049
- {
11050
- className: "gap-x-2",
11051
- onClick: () => deleteRow(index),
11052
- children: [
11053
- /* @__PURE__ */ jsx(Trash, { className: "text-ui-fg-subtle" }),
11054
- "Delete row"
11055
- ]
11056
- }
11057
- )
11058
- ] })
11059
- ] })
11060
- ] })
11061
- },
11062
- field.id
11063
- );
11064
- })
11065
- ] }),
11066
- hasUneditableRows && /* @__PURE__ */ jsx(InlineTip, { variant: "warning", label: "Some rows are disabled", children: "This object contains non-primitive metadata, such as arrays or objects, that can't be edited here. To edit the disabled rows, use the API directly." })
11067
- ] }),
11068
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11069
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
11070
- /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
11071
- ] }) })
11072
- ]
11073
- }
11074
- ) });
11075
- };
11076
- const GridInput = forwardRef(({ className, ...props }, ref) => {
11077
- return /* @__PURE__ */ jsx(
11078
- "input",
11079
- {
11080
- ref,
11081
- ...props,
11082
- autoComplete: "off",
11083
- className: clx(
11084
- "txt-compact-small text-ui-fg-base placeholder:text-ui-fg-muted disabled:text-ui-fg-disabled disabled:bg-ui-bg-base bg-transparent px-2 py-1.5 outline-none",
11085
- className
11086
- )
11087
- }
11088
- );
11089
- });
11090
- GridInput.displayName = "MetadataForm.GridInput";
11091
- const PlaceholderInner = () => {
11092
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
11093
- /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
11094
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11095
- /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" }),
11096
- /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" })
11097
- ] }) })
11098
- ] });
11099
- };
11100
- const EDITABLE_TYPES = ["string", "number", "boolean"];
11101
- function getDefaultValues(metadata) {
11102
- if (!metadata || !Object.keys(metadata).length) {
11103
- return [
10932
+ return /* @__PURE__ */ jsx(Form$2, { ...form, children: /* @__PURE__ */ jsx("form", { onSubmit, children: /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-[minmax(0,2fr)_minmax(0,1fr)_minmax(0,2fr)_28px] items-center gap-3 rounded-lg px-4 py-2", children: [
10933
+ /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-x-3", children: [
10934
+ /* @__PURE__ */ jsx(
10935
+ Thumbnail,
10936
+ {
10937
+ thumbnail: item.thumbnail,
10938
+ alt: item.product_title ?? void 0
10939
+ }
10940
+ ),
10941
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
10942
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-1", children: [
10943
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: item.product_title }),
10944
+ /* @__PURE__ */ jsxs(
10945
+ Text,
10946
+ {
10947
+ size: "small",
10948
+ leading: "compact",
10949
+ className: "text-ui-fg-subtle",
10950
+ children: [
10951
+ "(",
10952
+ item.variant_title,
10953
+ ")"
10954
+ ]
10955
+ }
10956
+ )
10957
+ ] }),
10958
+ /* @__PURE__ */ jsx(
10959
+ Text,
10960
+ {
10961
+ size: "small",
10962
+ leading: "compact",
10963
+ className: "text-ui-fg-subtle",
10964
+ children: item.variant_sku
10965
+ }
10966
+ )
10967
+ ] })
10968
+ ] }),
10969
+ editing ? /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(
10970
+ Form$2.Field,
11104
10971
  {
11105
- key: "",
11106
- value: "",
11107
- disabled: false
10972
+ control: form.control,
10973
+ name: "quantity",
10974
+ render: ({ field }) => {
10975
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(NumberInput, { ...field }) }) });
10976
+ }
11108
10977
  }
11109
- ];
11110
- }
11111
- return Object.entries(metadata).map(([key, value]) => {
11112
- if (!EDITABLE_TYPES.includes(typeof value)) {
11113
- return {
11114
- key,
11115
- value,
11116
- disabled: true
11117
- };
11118
- }
11119
- let stringValue = value;
11120
- if (typeof value !== "string") {
11121
- stringValue = JSON.stringify(value);
11122
- }
11123
- return {
11124
- key,
11125
- value: stringValue,
11126
- original_key: key
11127
- };
10978
+ ) }) : /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: item.quantity }) }),
10979
+ editing ? /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(
10980
+ Form$2.Field,
10981
+ {
10982
+ control: form.control,
10983
+ name: "unit_price",
10984
+ render: ({ field: { onChange, ...field } }) => {
10985
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10986
+ CurrencyInput,
10987
+ {
10988
+ ...field,
10989
+ symbol: getNativeSymbol(currencyCode),
10990
+ code: currencyCode,
10991
+ onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value)
10992
+ }
10993
+ ) }) });
10994
+ }
10995
+ }
10996
+ ) }) : /* @__PURE__ */ jsx("div", { className: "flex w-full flex-1 items-center justify-end", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: getLocaleAmount(item.unit_price, currencyCode) }) }),
10997
+ /* @__PURE__ */ jsx(
10998
+ IconButton,
10999
+ {
11000
+ type: "button",
11001
+ size: "small",
11002
+ onClick: editing ? onSubmit : () => {
11003
+ setEditing(true);
11004
+ },
11005
+ disabled: isPending,
11006
+ children: editing ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(PencilSquare, {})
11007
+ }
11008
+ )
11009
+ ] }) }) });
11010
+ };
11011
+ const variantItemSchema = objectType({
11012
+ quantity: numberType(),
11013
+ unit_price: unionType([numberType(), stringType()])
11014
+ });
11015
+ const CustomItem = ({ item, preview, currencyCode }) => {
11016
+ const [editing, setEditing] = useState(false);
11017
+ const { quantity, unit_price, title } = item;
11018
+ const form = useForm({
11019
+ defaultValues: {
11020
+ title,
11021
+ quantity,
11022
+ unit_price
11023
+ },
11024
+ resolver: zodResolver(customItemSchema)
11128
11025
  });
11129
- }
11130
- function parseValues(values) {
11131
- const metadata = values.metadata;
11132
- const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
11133
- if (isEmpty) {
11134
- return null;
11135
- }
11136
- const update = {};
11137
- metadata.forEach((field) => {
11138
- let key = field.key;
11139
- let value = field.value;
11140
- const disabled = field.disabled;
11141
- if (!key || !value) {
11026
+ useEffect(() => {
11027
+ form.reset({
11028
+ title,
11029
+ quantity,
11030
+ unit_price
11031
+ });
11032
+ }, [form, title, quantity, unit_price]);
11033
+ const actionId = useMemo(() => {
11034
+ var _a, _b;
11035
+ return (_b = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD")) == null ? void 0 : _b.id;
11036
+ }, [item]);
11037
+ const { mutateAsync: updateActionItem, isPending: isUpdatingActionItem } = useDraftOrderUpdateActionItem(preview.id);
11038
+ const { mutateAsync: removeActionItem, isPending: isRemovingActionItem } = useDraftOrderRemoveActionItem(preview.id);
11039
+ const { mutateAsync: updateOriginalItem, isPending: isUpdatingOriginalItem } = useDraftOrderUpdateItem(preview.id);
11040
+ const isPending = isUpdatingActionItem || isUpdatingOriginalItem;
11041
+ const onSubmit = form.handleSubmit(async (data) => {
11042
+ if (convertNumber(data.unit_price) === item.unit_price && data.quantity === item.quantity && data.title === item.title) {
11043
+ setEditing(false);
11142
11044
  return;
11143
11045
  }
11144
- if (disabled) {
11145
- update[key] = value;
11046
+ if (!actionId) {
11047
+ await updateOriginalItem(
11048
+ {
11049
+ item_id: item.id,
11050
+ quantity: data.quantity,
11051
+ unit_price: convertNumber(data.unit_price)
11052
+ },
11053
+ {
11054
+ onSuccess: () => {
11055
+ setEditing(false);
11056
+ },
11057
+ onError: (e) => {
11058
+ toast.error(e.message);
11059
+ }
11060
+ }
11061
+ );
11146
11062
  return;
11147
11063
  }
11148
- key = key.trim();
11149
- value = value.trim();
11150
- if (value === "true") {
11151
- update[key] = true;
11152
- } else if (value === "false") {
11153
- update[key] = false;
11154
- } else {
11155
- const parsedNumber = parseFloat(value);
11156
- if (!isNaN(parsedNumber)) {
11157
- update[key] = parsedNumber;
11158
- } else {
11159
- update[key] = value;
11160
- }
11064
+ if (data.quantity === 0) {
11065
+ await removeActionItem(actionId, {
11066
+ onSuccess: () => {
11067
+ setEditing(false);
11068
+ },
11069
+ onError: (e) => {
11070
+ toast.error(e.message);
11071
+ }
11072
+ });
11073
+ return;
11161
11074
  }
11075
+ await updateActionItem(
11076
+ {
11077
+ action_id: actionId,
11078
+ quantity: data.quantity,
11079
+ unit_price: convertNumber(data.unit_price)
11080
+ },
11081
+ {
11082
+ onSuccess: () => {
11083
+ setEditing(false);
11084
+ },
11085
+ onError: (e) => {
11086
+ toast.error(e.message);
11087
+ }
11088
+ }
11089
+ );
11162
11090
  });
11163
- return update;
11164
- }
11165
- function getHasUneditableRows(metadata) {
11166
- if (!metadata) {
11167
- return false;
11168
- }
11169
- return Object.values(metadata).some(
11170
- (value) => !EDITABLE_TYPES.includes(typeof value)
11171
- );
11172
- }
11173
- const PROMOTION_QUERY_KEY = "promotions";
11174
- const promotionsQueryKeys = {
11175
- list: (query2) => [
11176
- PROMOTION_QUERY_KEY,
11177
- query2 ? query2 : void 0
11178
- ],
11179
- detail: (id, query2) => [
11180
- PROMOTION_QUERY_KEY,
11181
- id,
11182
- query2 ? query2 : void 0
11183
- ]
11184
- };
11185
- const usePromotions = (query2, options) => {
11186
- const { data, ...rest } = useQuery({
11187
- queryKey: promotionsQueryKeys.list(query2),
11188
- queryFn: async () => sdk.admin.promotion.list(query2),
11189
- ...options
11190
- });
11191
- return { ...data, ...rest };
11091
+ return /* @__PURE__ */ jsx(Form$2, { ...form, children: /* @__PURE__ */ jsx("form", { onSubmit, children: /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-[minmax(0,2fr)_minmax(0,1fr)_minmax(0,2fr)_28px] items-center gap-3 rounded-lg px-4 py-2", children: [
11092
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-3", children: [
11093
+ /* @__PURE__ */ jsx(
11094
+ Thumbnail,
11095
+ {
11096
+ thumbnail: item.thumbnail,
11097
+ alt: item.title ?? void 0
11098
+ }
11099
+ ),
11100
+ editing ? /* @__PURE__ */ jsx(
11101
+ Form$2.Field,
11102
+ {
11103
+ control: form.control,
11104
+ name: "title",
11105
+ render: ({ field }) => {
11106
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(Input, { ...field }) }) });
11107
+ }
11108
+ }
11109
+ ) : /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: item.title })
11110
+ ] }),
11111
+ editing ? /* @__PURE__ */ jsx(
11112
+ Form$2.Field,
11113
+ {
11114
+ control: form.control,
11115
+ name: "quantity",
11116
+ render: ({ field }) => {
11117
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(NumberInput, { ...field }) }) });
11118
+ }
11119
+ }
11120
+ ) : /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: item.quantity }),
11121
+ editing ? /* @__PURE__ */ jsx(
11122
+ Form$2.Field,
11123
+ {
11124
+ control: form.control,
11125
+ name: "unit_price",
11126
+ render: ({ field: { onChange, ...field } }) => {
11127
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
11128
+ CurrencyInput,
11129
+ {
11130
+ ...field,
11131
+ symbol: getNativeSymbol(currencyCode),
11132
+ code: currencyCode,
11133
+ onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value)
11134
+ }
11135
+ ) }) });
11136
+ }
11137
+ }
11138
+ ) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-end", children: /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: getLocaleAmount(item.unit_price, currencyCode) }) }),
11139
+ /* @__PURE__ */ jsx(
11140
+ IconButton,
11141
+ {
11142
+ type: "button",
11143
+ size: "small",
11144
+ onClick: editing ? onSubmit : () => {
11145
+ setEditing(true);
11146
+ },
11147
+ disabled: isPending,
11148
+ children: editing ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(PencilSquare, {})
11149
+ }
11150
+ )
11151
+ ] }) }) });
11192
11152
  };
11193
- const Promotions = () => {
11194
- const { id } = useParams();
11195
- const {
11196
- order: preview,
11197
- isError: isPreviewError,
11198
- error: previewError
11199
- } = useOrderPreview(id, void 0);
11200
- useInitiateOrderEdit({ preview });
11201
- const { onCancel } = useCancelOrderEdit({ preview });
11202
- if (isPreviewError) {
11203
- throw previewError;
11204
- }
11205
- const isReady = !!preview;
11206
- return /* @__PURE__ */ jsxs(RouteDrawer, { onClose: onCancel, children: [
11207
- /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Promotions" }) }) }),
11208
- isReady && /* @__PURE__ */ jsx(PromotionForm, { preview })
11209
- ] });
11153
+ const StackedModalTrigger$1 = ({
11154
+ type,
11155
+ setModalContent
11156
+ }) => {
11157
+ const { setIsOpen } = useStackedModal();
11158
+ const onClick = useCallback(() => {
11159
+ setModalContent(type);
11160
+ setIsOpen(STACKED_MODAL_ID, true);
11161
+ }, [setModalContent, setIsOpen, type]);
11162
+ return /* @__PURE__ */ jsx(StackedFocusModal.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(DropdownMenu.Item, { onClick, children: type === "add-items" ? "Add items" : "Add custom item" }) });
11210
11163
  };
11211
- const PromotionForm = ({ preview }) => {
11212
- const { items, shipping_methods } = preview;
11213
- const [isSubmitting, setIsSubmitting] = useState(false);
11214
- const [comboboxValue, setComboboxValue] = useState("");
11215
- const { handleSuccess } = useRouteModal();
11216
- const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
11217
- const promoIds = getPromotionIds(items, shipping_methods);
11218
- const { promotions, isPending, isError, error } = usePromotions(
11164
+ const VARIANT_PREFIX = "items";
11165
+ const LIMIT = 50;
11166
+ const ExistingItemsForm = ({ orderId, items }) => {
11167
+ const { setIsOpen } = useStackedModal();
11168
+ const [rowSelection, setRowSelection] = useState(
11169
+ items.reduce((acc, item) => {
11170
+ acc[item.variant_id] = true;
11171
+ return acc;
11172
+ }, {})
11173
+ );
11174
+ useEffect(() => {
11175
+ setRowSelection(
11176
+ items.reduce((acc, item) => {
11177
+ if (item.variant_id) {
11178
+ acc[item.variant_id] = true;
11179
+ }
11180
+ return acc;
11181
+ }, {})
11182
+ );
11183
+ }, [items]);
11184
+ const { q, order, offset } = useQueryParams(
11185
+ ["q", "order", "offset"],
11186
+ VARIANT_PREFIX
11187
+ );
11188
+ const { variants, count, isPending, isError, error } = useProductVariants(
11219
11189
  {
11220
- id: promoIds
11190
+ q,
11191
+ order,
11192
+ offset: offset ? parseInt(offset) : void 0,
11193
+ limit: LIMIT
11221
11194
  },
11222
11195
  {
11223
- enabled: !!promoIds.length
11196
+ placeholderData: keepPreviousData
11224
11197
  }
11225
11198
  );
11226
- const comboboxData = useComboboxData({
11227
- queryKey: ["promotions", "combobox", promoIds],
11228
- queryFn: async (params) => {
11229
- return await sdk.admin.promotion.list({
11230
- ...params,
11231
- id: {
11232
- $nin: promoIds
11233
- }
11234
- });
11235
- },
11236
- getOptions: (data) => {
11237
- return data.promotions.map((promotion) => ({
11238
- label: promotion.code,
11239
- value: promotion.code
11240
- }));
11241
- }
11242
- });
11243
- const add = async (value) => {
11244
- if (!value) {
11245
- return;
11246
- }
11247
- addPromotions(
11199
+ const columns = useColumns();
11200
+ const { mutateAsync } = useDraftOrderAddItems(orderId);
11201
+ const onSubmit = async () => {
11202
+ const ids = Object.keys(rowSelection).filter(
11203
+ (id) => !items.find((i) => i.variant_id === id)
11204
+ );
11205
+ await mutateAsync(
11248
11206
  {
11249
- promo_codes: [value]
11207
+ items: ids.map((id) => ({
11208
+ variant_id: id,
11209
+ quantity: 1
11210
+ }))
11250
11211
  },
11251
11212
  {
11213
+ onSuccess: () => {
11214
+ setRowSelection({});
11215
+ setIsOpen(STACKED_MODAL_ID, false);
11216
+ },
11252
11217
  onError: (e) => {
11253
11218
  toast.error(e.message);
11254
- comboboxData.onSearchValueChange("");
11255
- setComboboxValue("");
11256
- },
11257
- onSuccess: () => {
11258
- comboboxData.onSearchValueChange("");
11259
- setComboboxValue("");
11260
11219
  }
11261
11220
  }
11262
11221
  );
11263
11222
  };
11264
- const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
11265
- const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
11266
- const onSubmit = async () => {
11267
- setIsSubmitting(true);
11268
- let requestSucceeded = false;
11269
- await requestOrderEdit(void 0, {
11270
- onError: (e) => {
11271
- toast.error(e.message);
11272
- },
11273
- onSuccess: () => {
11274
- requestSucceeded = true;
11275
- }
11276
- });
11277
- if (!requestSucceeded) {
11278
- setIsSubmitting(false);
11279
- return;
11280
- }
11281
- await confirmOrderEdit(void 0, {
11282
- onError: (e) => {
11283
- toast.error(e.message);
11284
- },
11285
- onSuccess: () => {
11286
- handleSuccess();
11287
- },
11288
- onSettled: () => {
11289
- setIsSubmitting(false);
11290
- }
11291
- });
11292
- };
11293
11223
  if (isError) {
11294
11224
  throw error;
11295
11225
  }
11296
- return /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
11297
- /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
11298
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
11299
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
11300
- /* @__PURE__ */ jsx(Label$1, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
11301
- /* @__PURE__ */ jsx(Hint$1, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
11226
+ return /* @__PURE__ */ jsxs(
11227
+ StackedFocusModal.Content,
11228
+ {
11229
+ onOpenAutoFocus: (e) => {
11230
+ e.preventDefault();
11231
+ const searchInput = document.querySelector(
11232
+ "[data-modal-id='modal-search-input']"
11233
+ );
11234
+ if (searchInput) {
11235
+ searchInput.focus();
11236
+ }
11237
+ },
11238
+ children: [
11239
+ /* @__PURE__ */ jsxs(StackedFocusModal.Header, { children: [
11240
+ /* @__PURE__ */ jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Product Variants" }) }),
11241
+ /* @__PURE__ */ jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Choose product variants to add to the order." }) })
11302
11242
  ] }),
11303
- /* @__PURE__ */ jsx(
11304
- Combobox,
11243
+ /* @__PURE__ */ jsx(StackedFocusModal.Body, { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
11244
+ DataTable,
11305
11245
  {
11306
- id: "promotion-combobox",
11307
- "aria-describedby": "promotion-combobox-hint",
11308
- isFetchingNextPage: comboboxData.isFetchingNextPage,
11309
- fetchNextPage: comboboxData.fetchNextPage,
11310
- options: comboboxData.options,
11311
- onSearchValueChange: comboboxData.onSearchValueChange,
11312
- searchValue: comboboxData.searchValue,
11313
- disabled: comboboxData.disabled || isAddingPromotions,
11314
- onChange: add,
11315
- value: comboboxValue
11246
+ data: variants,
11247
+ columns,
11248
+ isLoading: isPending,
11249
+ getRowId: (row) => row.id,
11250
+ rowCount: count,
11251
+ prefix: VARIANT_PREFIX,
11252
+ layout: "fill",
11253
+ rowSelection: {
11254
+ state: rowSelection,
11255
+ onRowSelectionChange: setRowSelection,
11256
+ enableRowSelection: (row) => {
11257
+ return !items.find((i) => i.variant_id === row.original.id);
11258
+ }
11259
+ },
11260
+ autoFocusSearch: true
11316
11261
  }
11317
- )
11318
- ] }),
11319
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
11320
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: promotions == null ? void 0 : promotions.map((promotion) => /* @__PURE__ */ jsx(
11321
- PromotionItem,
11322
- {
11323
- promotion,
11324
- orderId: preview.id,
11325
- isLoading: isPending
11262
+ ) }),
11263
+ /* @__PURE__ */ jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11264
+ /* @__PURE__ */ jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
11265
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "button", onClick: onSubmit, children: "Update items" })
11266
+ ] }) })
11267
+ ]
11268
+ }
11269
+ );
11270
+ };
11271
+ const columnHelper = createDataTableColumnHelper();
11272
+ const useColumns = () => {
11273
+ return useMemo(() => {
11274
+ return [
11275
+ columnHelper.select(),
11276
+ columnHelper.accessor("product.title", {
11277
+ header: "Product",
11278
+ cell: ({ row }) => {
11279
+ var _a, _b, _c;
11280
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
11281
+ /* @__PURE__ */ jsx(
11282
+ Thumbnail,
11283
+ {
11284
+ thumbnail: (_a = row.original.product) == null ? void 0 : _a.thumbnail,
11285
+ alt: (_b = row.original.product) == null ? void 0 : _b.title
11286
+ }
11287
+ ),
11288
+ /* @__PURE__ */ jsx("span", { children: (_c = row.original.product) == null ? void 0 : _c.title })
11289
+ ] });
11290
+ },
11291
+ enableSorting: true
11292
+ }),
11293
+ columnHelper.accessor("title", {
11294
+ header: "Variant",
11295
+ enableSorting: true
11296
+ }),
11297
+ columnHelper.accessor("sku", {
11298
+ header: "SKU",
11299
+ cell: ({ getValue }) => {
11300
+ return getValue() ?? "-";
11301
+ },
11302
+ enableSorting: true
11303
+ }),
11304
+ columnHelper.accessor("updated_at", {
11305
+ header: "Updated",
11306
+ cell: ({ getValue }) => {
11307
+ return /* @__PURE__ */ jsx(
11308
+ Tooltip,
11309
+ {
11310
+ content: getFullDate({ date: getValue(), includeTime: true }),
11311
+ children: /* @__PURE__ */ jsx("span", { children: getFullDate({ date: getValue() }) })
11312
+ }
11313
+ );
11326
11314
  },
11327
- promotion.id
11328
- )) })
11329
- ] }) }),
11330
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
11331
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
11332
- /* @__PURE__ */ jsx(
11333
- Button,
11334
- {
11335
- size: "small",
11336
- type: "submit",
11337
- isLoading: isSubmitting || isAddingPromotions,
11338
- children: "Save"
11339
- }
11340
- )
11341
- ] }) })
11342
- ] });
11315
+ enableSorting: true,
11316
+ sortAscLabel: "Oldest first",
11317
+ sortDescLabel: "Newest first"
11318
+ }),
11319
+ columnHelper.accessor("created_at", {
11320
+ header: "Created",
11321
+ cell: ({ getValue }) => {
11322
+ return /* @__PURE__ */ jsx(
11323
+ Tooltip,
11324
+ {
11325
+ content: getFullDate({ date: getValue(), includeTime: true }),
11326
+ children: /* @__PURE__ */ jsx("span", { children: getFullDate({ date: getValue() }) })
11327
+ }
11328
+ );
11329
+ },
11330
+ enableSorting: true,
11331
+ sortAscLabel: "Oldest first",
11332
+ sortDescLabel: "Newest first"
11333
+ })
11334
+ ];
11335
+ }, []);
11343
11336
  };
11344
- const PromotionItem = ({
11345
- promotion,
11346
- orderId,
11347
- isLoading
11348
- }) => {
11349
- var _a;
11350
- const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
11351
- const onRemove = async () => {
11352
- removePromotions(
11337
+ const CustomItemForm = ({ orderId, currencyCode }) => {
11338
+ const { setIsOpen } = useStackedModal();
11339
+ const { mutateAsync: addItems } = useDraftOrderAddItems(orderId);
11340
+ const form = useForm({
11341
+ defaultValues: {
11342
+ title: "",
11343
+ quantity: 1,
11344
+ unit_price: ""
11345
+ },
11346
+ resolver: zodResolver(customItemSchema)
11347
+ });
11348
+ const onSubmit = form.handleSubmit(async (data) => {
11349
+ await addItems(
11353
11350
  {
11354
- promo_codes: [promotion.code]
11351
+ items: [
11352
+ {
11353
+ title: data.title,
11354
+ quantity: data.quantity,
11355
+ unit_price: convertNumber(data.unit_price)
11356
+ }
11357
+ ]
11355
11358
  },
11356
11359
  {
11360
+ onSuccess: () => {
11361
+ setIsOpen(STACKED_MODAL_ID, false);
11362
+ },
11357
11363
  onError: (e) => {
11358
11364
  toast.error(e.message);
11359
11365
  }
11360
11366
  }
11361
11367
  );
11362
- };
11363
- const displayValue = getDisplayValue(promotion);
11364
- return /* @__PURE__ */ jsxs(
11365
- "div",
11366
- {
11367
- className: clx(
11368
- "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
11368
+ });
11369
+ return /* @__PURE__ */ jsx(Form$2, { ...form, children: /* @__PURE__ */ jsx(KeyboundForm, { onSubmit, children: /* @__PURE__ */ jsxs(StackedFocusModal.Content, { children: [
11370
+ /* @__PURE__ */ jsx(StackedFocusModal.Header, {}),
11371
+ /* @__PURE__ */ jsx(StackedFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col items-center overflow-y-auto", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full max-w-[720px] flex-col gap-y-6 px-2 py-16", children: [
11372
+ /* @__PURE__ */ jsxs("div", { children: [
11373
+ /* @__PURE__ */ jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Add custom item" }) }),
11374
+ /* @__PURE__ */ jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Add a custom item to the order. This will add a new line item that is not associated with an existing product." }) })
11375
+ ] }),
11376
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
11377
+ /* @__PURE__ */ jsx(
11378
+ Form$2.Field,
11369
11379
  {
11370
- "animate-pulse": isLoading
11380
+ control: form.control,
11381
+ name: "title",
11382
+ render: ({ field }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
11383
+ /* @__PURE__ */ jsxs("div", { children: [
11384
+ /* @__PURE__ */ jsx(Form$2.Label, { children: "Title" }),
11385
+ /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the title of the item" })
11386
+ ] }),
11387
+ /* @__PURE__ */ jsxs("div", { children: [
11388
+ /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(Input, { ...field }) }),
11389
+ /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
11390
+ ] })
11391
+ ] }) })
11371
11392
  }
11372
11393
  ),
11373
- children: [
11374
- /* @__PURE__ */ jsxs("div", { children: [
11375
- /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
11376
- /* @__PURE__ */ jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
11377
- displayValue && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
11378
- /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", children: displayValue }),
11379
- /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", children: "·" })
11394
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
11395
+ /* @__PURE__ */ jsx(
11396
+ Form$2.Field,
11397
+ {
11398
+ control: form.control,
11399
+ name: "unit_price",
11400
+ render: ({ field: { onChange, ...field } }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
11401
+ /* @__PURE__ */ jsxs("div", { children: [
11402
+ /* @__PURE__ */ jsx(Form$2.Label, { children: "Unit price" }),
11403
+ /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the unit price of the item" })
11380
11404
  ] }),
11381
- /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", className: "capitalize", children: (_a = promotion.application_method) == null ? void 0 : _a.allocation })
11382
- ] })
11383
- ] }),
11384
- /* @__PURE__ */ jsx(
11385
- IconButton,
11386
- {
11387
- size: "small",
11388
- type: "button",
11389
- variant: "transparent",
11390
- onClick: onRemove,
11391
- isLoading: isPending || isLoading,
11392
- children: /* @__PURE__ */ jsx(XMark, {})
11393
- }
11394
- )
11395
- ]
11396
- },
11397
- promotion.id
11398
- );
11399
- };
11400
- function getDisplayValue(promotion) {
11401
- var _a, _b, _c, _d;
11402
- const value = (_a = promotion.application_method) == null ? void 0 : _a.value;
11403
- if (!value) {
11404
- return null;
11405
- }
11406
- if (((_b = promotion.application_method) == null ? void 0 : _b.type) === "fixed") {
11407
- const currency = (_c = promotion.application_method) == null ? void 0 : _c.currency_code;
11408
- if (!currency) {
11409
- return null;
11410
- }
11411
- return getLocaleAmount(value, currency);
11412
- } else if (((_d = promotion.application_method) == null ? void 0 : _d.type) === "percentage") {
11413
- return formatPercentage(value);
11414
- }
11415
- return null;
11416
- }
11417
- const formatter = new Intl.NumberFormat([], {
11418
- style: "percent",
11419
- minimumFractionDigits: 2
11420
- });
11421
- const formatPercentage = (value, isPercentageValue = false) => {
11422
- let val = value || 0;
11423
- if (!isPercentageValue) {
11424
- val = val / 100;
11425
- }
11426
- return formatter.format(val);
11427
- };
11428
- function getPromotionIds(items, shippingMethods) {
11429
- const promotionIds = /* @__PURE__ */ new Set();
11430
- for (const item of items) {
11431
- if (item.adjustments) {
11432
- for (const adjustment of item.adjustments) {
11433
- if (adjustment.promotion_id) {
11434
- promotionIds.add(adjustment.promotion_id);
11405
+ /* @__PURE__ */ jsxs("div", { children: [
11406
+ /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
11407
+ CurrencyInput,
11408
+ {
11409
+ symbol: getNativeSymbol(currencyCode),
11410
+ code: currencyCode,
11411
+ onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value),
11412
+ ...field
11413
+ }
11414
+ ) }),
11415
+ /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
11416
+ ] })
11417
+ ] }) })
11435
11418
  }
11436
- }
11437
- }
11438
- }
11439
- for (const shippingMethod of shippingMethods) {
11440
- if (shippingMethod.adjustments) {
11441
- for (const adjustment of shippingMethod.adjustments) {
11442
- if (adjustment.promotion_id) {
11443
- promotionIds.add(adjustment.promotion_id);
11419
+ ),
11420
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
11421
+ /* @__PURE__ */ jsx(
11422
+ Form$2.Field,
11423
+ {
11424
+ control: form.control,
11425
+ name: "quantity",
11426
+ render: ({ field }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
11427
+ /* @__PURE__ */ jsxs("div", { children: [
11428
+ /* @__PURE__ */ jsx(Form$2.Label, { children: "Quantity" }),
11429
+ /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the quantity of the item" })
11430
+ ] }),
11431
+ /* @__PURE__ */ jsxs("div", { className: "w-full flex-1", children: [
11432
+ /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(NumberInput, { ...field, className: "w-full" }) }) }),
11433
+ /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
11434
+ ] })
11435
+ ] }) })
11444
11436
  }
11445
- }
11446
- }
11447
- }
11448
- return Array.from(promotionIds);
11449
- }
11437
+ )
11438
+ ] }) }) }),
11439
+ /* @__PURE__ */ jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11440
+ /* @__PURE__ */ jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
11441
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "button", onClick: onSubmit, children: "Add item" })
11442
+ ] }) })
11443
+ ] }) }) });
11444
+ };
11445
+ const customItemSchema = objectType({
11446
+ title: stringType().min(1),
11447
+ quantity: numberType(),
11448
+ unit_price: unionType([numberType(), stringType()])
11449
+ });
11450
11450
  const SalesChannel = () => {
11451
11451
  const { id } = useParams();
11452
11452
  const { draft_order, isPending, isError, error } = useDraftOrder(
@@ -13063,25 +13063,25 @@ const routeModule = {
13063
13063
  Component: BillingAddress,
13064
13064
  path: "/draft-orders/:id/billing-address"
13065
13065
  },
13066
- {
13067
- Component: CustomItems,
13068
- path: "/draft-orders/:id/custom-items"
13069
- },
13070
13066
  {
13071
13067
  Component: Email,
13072
13068
  path: "/draft-orders/:id/email"
13073
13069
  },
13074
13070
  {
13075
- Component: Items,
13076
- path: "/draft-orders/:id/items"
13071
+ Component: CustomItems,
13072
+ path: "/draft-orders/:id/custom-items"
13073
+ },
13074
+ {
13075
+ Component: Promotions,
13076
+ path: "/draft-orders/:id/promotions"
13077
13077
  },
13078
13078
  {
13079
13079
  Component: Metadata,
13080
13080
  path: "/draft-orders/:id/metadata"
13081
13081
  },
13082
13082
  {
13083
- Component: Promotions,
13084
- path: "/draft-orders/:id/promotions"
13083
+ Component: Items,
13084
+ path: "/draft-orders/:id/items"
13085
13085
  },
13086
13086
  {
13087
13087
  Component: SalesChannel,