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