@medusajs/draft-order 2.10.4-preview-20250928000328 → 2.10.4-snapshot-20250918173105

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,6 +9763,27 @@ 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
+ });
9766
9787
  const Email = () => {
9767
9788
  const { id } = reactRouterDom.useParams();
9768
9789
  const { order, isPending, isError, error } = useOrder(id, {
@@ -9785,7 +9806,7 @@ const EmailForm = ({ order }) => {
9785
9806
  defaultValues: {
9786
9807
  email: order.email ?? ""
9787
9808
  },
9788
- resolver: zod.zodResolver(schema$4)
9809
+ resolver: zod.zodResolver(schema$3)
9789
9810
  });
9790
9811
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
9791
9812
  const { handleSuccess } = useRouteModal();
@@ -9828,1631 +9849,1610 @@ const EmailForm = ({ order }) => {
9828
9849
  }
9829
9850
  ) });
9830
9851
  };
9831
- const schema$4 = objectType({
9852
+ const schema$3 = objectType({
9832
9853
  email: stringType().email()
9833
9854
  });
9834
- const NumberInput = React.forwardRef(
9835
- ({
9836
- value,
9837
- onChange,
9838
- size = "base",
9839
- min = 0,
9840
- max = 100,
9841
- step = 1,
9842
- className,
9843
- disabled,
9844
- ...props
9845
- }, ref) => {
9846
- const handleChange = (event) => {
9847
- const newValue = event.target.value === "" ? min : Number(event.target.value);
9848
- if (!isNaN(newValue) && (max === void 0 || newValue <= max) && (min === void 0 || newValue >= min)) {
9849
- onChange(newValue);
9850
- }
9851
- };
9852
- const handleIncrement = () => {
9853
- const newValue = value + step;
9854
- if (max === void 0 || newValue <= max) {
9855
- onChange(newValue);
9856
- }
9857
- };
9858
- const handleDecrement = () => {
9859
- const newValue = value - step;
9860
- if (min === void 0 || newValue >= min) {
9861
- onChange(newValue);
9862
- }
9863
- };
9855
+ const InlineTip = React.forwardRef(
9856
+ ({ variant = "tip", label, className, children, ...props }, ref) => {
9857
+ const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
9864
9858
  return /* @__PURE__ */ jsxRuntime.jsxs(
9865
9859
  "div",
9866
9860
  {
9861
+ ref,
9867
9862
  className: ui.clx(
9868
- "inline-flex rounded-md bg-ui-bg-field shadow-borders-base overflow-hidden divide-x transition-fg",
9869
- "[&:has(input:focus)]:shadow-borders-interactive-with-active",
9870
- {
9871
- "h-7": size === "small",
9872
- "h-8": size === "base"
9873
- },
9863
+ "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
9874
9864
  className
9875
9865
  ),
9866
+ ...props,
9876
9867
  children: [
9877
9868
  /* @__PURE__ */ jsxRuntime.jsx(
9878
- "input",
9879
- {
9880
- ref,
9881
- type: "number",
9882
- value,
9883
- onChange: handleChange,
9884
- min,
9885
- max,
9886
- step,
9887
- className: ui.clx(
9888
- "flex-1 px-2 py-1 bg-transparent txt-compact-small text-ui-fg-base outline-none [appearance:textfield]",
9889
- "[&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
9890
- "placeholder:text-ui-fg-muted"
9891
- ),
9892
- ...props
9893
- }
9894
- ),
9895
- /* @__PURE__ */ jsxRuntime.jsxs(
9896
- "button",
9869
+ "div",
9897
9870
  {
9898
- className: ui.clx(
9899
- "flex items-center justify-center outline-none transition-fg",
9900
- "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
9901
- "focus:bg-ui-bg-field-component-hover",
9902
- "hover:bg-ui-bg-field-component-hover",
9903
- {
9904
- "size-7": size === "small",
9905
- "size-8": size === "base"
9906
- }
9907
- ),
9908
- type: "button",
9909
- onClick: handleDecrement,
9910
- disabled: min !== void 0 && value <= min || disabled,
9911
- children: [
9912
- /* @__PURE__ */ jsxRuntime.jsx(icons.Minus, {}),
9913
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: `Decrease by ${step}` })
9914
- ]
9871
+ role: "presentation",
9872
+ className: ui.clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
9873
+ "bg-ui-tag-orange-icon": variant === "warning"
9874
+ })
9915
9875
  }
9916
9876
  ),
9917
- /* @__PURE__ */ jsxRuntime.jsxs(
9918
- "button",
9919
- {
9920
- className: ui.clx(
9921
- "flex items-center justify-center outline-none transition-fg",
9922
- "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
9923
- "focus:bg-ui-bg-field-hover",
9924
- "hover:bg-ui-bg-field-hover",
9925
- {
9926
- "size-7": size === "small",
9927
- "size-8": size === "base"
9928
- }
9929
- ),
9930
- type: "button",
9931
- onClick: handleIncrement,
9932
- disabled: max !== void 0 && value >= max || disabled,
9933
- children: [
9934
- /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
9935
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: `Increase by ${step}` })
9936
- ]
9937
- }
9938
- )
9877
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-pretty", children: [
9878
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
9879
+ labelValue,
9880
+ ":"
9881
+ ] }),
9882
+ " ",
9883
+ children
9884
+ ] })
9939
9885
  ]
9940
9886
  }
9941
9887
  );
9942
9888
  }
9943
9889
  );
9944
- const PRODUCT_VARIANTS_QUERY_KEY = "product-variants";
9945
- const productVariantsQueryKeys = {
9946
- list: (query2) => [
9947
- PRODUCT_VARIANTS_QUERY_KEY,
9948
- query2 ? query2 : void 0
9949
- ]
9950
- };
9951
- const useProductVariants = (query2, options) => {
9952
- const { data, ...rest } = reactQuery.useQuery({
9953
- queryKey: productVariantsQueryKeys.list(query2),
9954
- queryFn: async () => await sdk.admin.productVariant.list(query2),
9955
- ...options
9956
- });
9957
- return { ...data, ...rest };
9958
- };
9959
- const useCancelOrderEdit = ({ preview }) => {
9960
- const { mutateAsync: cancelOrderEdit } = useDraftOrderCancelEdit(preview == null ? void 0 : preview.id);
9961
- const onCancel = React.useCallback(async () => {
9962
- if (!preview) {
9963
- return true;
9964
- }
9965
- let res = false;
9966
- await cancelOrderEdit(void 0, {
9967
- onError: (e) => {
9968
- ui.toast.error(e.message);
9969
- },
9970
- onSuccess: () => {
9971
- res = true;
9972
- }
9973
- });
9974
- return res;
9975
- }, [preview, cancelOrderEdit]);
9976
- return { onCancel };
9977
- };
9978
- let IS_REQUEST_RUNNING = false;
9979
- const useInitiateOrderEdit = ({
9980
- preview
9981
- }) => {
9982
- const navigate = reactRouterDom.useNavigate();
9983
- const { mutateAsync } = useDraftOrderBeginEdit(preview == null ? void 0 : preview.id);
9984
- React.useEffect(() => {
9985
- async function run() {
9986
- if (IS_REQUEST_RUNNING || !preview) {
9987
- return;
9988
- }
9989
- if (preview.order_change) {
9990
- return;
9991
- }
9992
- IS_REQUEST_RUNNING = true;
9993
- await mutateAsync(void 0, {
9994
- onError: (e) => {
9995
- ui.toast.error(e.message);
9996
- navigate(`/draft-orders/${preview.id}`, { replace: true });
9997
- return;
9998
- }
9999
- });
10000
- IS_REQUEST_RUNNING = false;
10001
- }
10002
- run();
10003
- }, [preview, navigate, mutateAsync]);
10004
- };
10005
- function convertNumber(value) {
10006
- return typeof value === "string" ? Number(value.replace(",", ".")) : value;
10007
- }
10008
- const STACKED_MODAL_ID = "items_stacked_modal";
10009
- const Items = () => {
9890
+ InlineTip.displayName = "InlineTip";
9891
+ const MetadataFieldSchema = objectType({
9892
+ key: stringType(),
9893
+ disabled: booleanType().optional(),
9894
+ value: anyType()
9895
+ });
9896
+ const MetadataSchema = objectType({
9897
+ metadata: arrayType(MetadataFieldSchema)
9898
+ });
9899
+ const Metadata = () => {
10010
9900
  const { id } = reactRouterDom.useParams();
10011
- const {
10012
- order: preview,
10013
- isPending: isPreviewPending,
10014
- isError: isPreviewError,
10015
- error: previewError
10016
- } = useOrderPreview(id, void 0, {
10017
- placeholderData: reactQuery.keepPreviousData
9901
+ const { order, isPending, isError, error } = useOrder(id, {
9902
+ fields: "metadata"
10018
9903
  });
10019
- useInitiateOrderEdit({ preview });
10020
- const { draft_order, isPending, isError, error } = useDraftOrder(
10021
- id,
10022
- {
10023
- fields: "currency_code"
10024
- },
10025
- {
10026
- enabled: !!id
10027
- }
10028
- );
10029
- const { onCancel } = useCancelOrderEdit({ preview });
10030
9904
  if (isError) {
10031
9905
  throw error;
10032
9906
  }
10033
- if (isPreviewError) {
10034
- throw previewError;
10035
- }
10036
- const ready = !!preview && !isPreviewPending && !!draft_order && !isPending;
10037
- return /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal, { onClose: onCancel, children: ready ? /* @__PURE__ */ jsxRuntime.jsx(ItemsForm, { preview, currencyCode: draft_order.currency_code }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10038
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Edit Items" }) }),
10039
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Loading data for the draft order, please wait..." }) })
10040
- ] }) });
9907
+ const isReady = !isPending && !!order;
9908
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
9909
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
9910
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Metadata" }) }),
9911
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
9912
+ ] }),
9913
+ !isReady ? /* @__PURE__ */ jsxRuntime.jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsxRuntime.jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
9914
+ ] });
10041
9915
  };
10042
- const ItemsForm = ({ preview, currencyCode }) => {
10043
- var _a;
10044
- const [isSubmitting, setIsSubmitting] = React.useState(false);
10045
- const [modalContent, setModalContent] = React.useState(
10046
- null
10047
- );
9916
+ const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
9917
+ const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
9918
+ const MetadataForm = ({ orderId, metadata }) => {
10048
9919
  const { handleSuccess } = useRouteModal();
10049
- const { searchValue, onSearchValueChange, query: query2 } = useDebouncedSearch();
10050
- const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
10051
- const { mutateAsync: requestOrderEdit } = useDraftOrderRequestEdit(preview.id);
10052
- const itemCount = ((_a = preview.items) == null ? void 0 : _a.reduce((acc, item) => acc + item.quantity, 0)) || 0;
10053
- const matches = React.useMemo(() => {
10054
- return matchSorter.matchSorter(preview.items, query2, {
10055
- keys: ["product_title", "variant_title", "variant_sku", "title"]
10056
- });
10057
- }, [preview.items, query2]);
10058
- const onSubmit = async () => {
10059
- setIsSubmitting(true);
10060
- let requestSucceeded = false;
10061
- await requestOrderEdit(void 0, {
10062
- onError: (e) => {
10063
- ui.toast.error(`Failed to request order edit: ${e.message}`);
10064
- },
10065
- onSuccess: () => {
10066
- requestSucceeded = true;
10067
- }
10068
- });
10069
- if (!requestSucceeded) {
10070
- setIsSubmitting(false);
10071
- return;
10072
- }
10073
- await confirmOrderEdit(void 0, {
10074
- onError: (e) => {
10075
- ui.toast.error(`Failed to confirm order edit: ${e.message}`);
10076
- },
10077
- onSuccess: () => {
10078
- handleSuccess();
10079
- },
10080
- onSettled: () => {
10081
- setIsSubmitting(false);
10082
- }
10083
- });
10084
- };
10085
- const onKeyDown = React.useCallback(
10086
- (e) => {
10087
- if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
10088
- if (modalContent || isSubmitting) {
10089
- return;
10090
- }
10091
- onSubmit();
10092
- }
10093
- },
10094
- [modalContent, isSubmitting, onSubmit]
10095
- );
10096
- React.useEffect(() => {
10097
- document.addEventListener("keydown", onKeyDown);
10098
- return () => {
10099
- document.removeEventListener("keydown", onKeyDown);
10100
- };
10101
- }, [onKeyDown]);
10102
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col overflow-hidden", children: [
10103
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Header, {}),
10104
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs(
10105
- StackedFocusModal,
10106
- {
10107
- id: STACKED_MODAL_ID,
10108
- onOpenChangeCallback: (open) => {
10109
- if (!open) {
10110
- setModalContent(null);
10111
- }
10112
- },
10113
- children: [
10114
- /* @__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: [
10115
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10116
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Items" }) }),
10117
- /* @__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" }) })
10118
- ] }),
10119
- /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
10120
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-6", children: [
10121
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 items-center gap-3", children: [
10122
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
10123
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: "Items" }),
10124
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Choose items from the product catalog." })
10125
- ] }),
10126
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
10127
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
10128
- ui.Input,
10129
- {
10130
- type: "search",
10131
- placeholder: "Search items",
10132
- value: searchValue,
10133
- onChange: (e) => onSearchValueChange(e.target.value)
10134
- }
10135
- ) }),
10136
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
10137
- /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { type: "button", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}) }) }),
10138
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
10139
- /* @__PURE__ */ jsxRuntime.jsx(
10140
- StackedModalTrigger$1,
10141
- {
10142
- type: "add-items",
10143
- setModalContent
10144
- }
10145
- ),
10146
- /* @__PURE__ */ jsxRuntime.jsx(
10147
- StackedModalTrigger$1,
10148
- {
10149
- type: "add-custom-item",
10150
- setModalContent
10151
- }
10152
- )
10153
- ] })
10154
- ] })
10155
- ] })
10156
- ] }),
10157
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle shadow-elevation-card-rest rounded-xl", children: [
10158
- /* @__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: [
10159
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Item" }) }),
10160
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Quantity" }) }),
10161
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Price" }) }),
10162
- /* @__PURE__ */ jsxRuntime.jsx("div", {})
10163
- ] }) }),
10164
- /* @__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: [
10165
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: "There are no items in this order" }),
10166
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Add items to the order to get started." })
10167
- ] }) : matches.length > 0 ? matches == null ? void 0 : matches.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
10168
- Item,
10169
- {
10170
- item,
10171
- preview,
10172
- currencyCode
10173
- },
10174
- item.id
10175
- )) : /* @__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: [
10176
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: "No items found" }),
10177
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
10178
- 'No items found for "',
10179
- query2,
10180
- '".'
10181
- ] })
10182
- ] }) })
10183
- ] })
10184
- ] }),
10185
- /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
10186
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-[1fr_0.5fr_0.5fr] gap-3", children: [
10187
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: "Subtotal" }) }),
10188
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(
10189
- ui.Text,
10190
- {
10191
- size: "small",
10192
- leading: "compact",
10193
- className: "text-ui-fg-subtle",
10194
- children: [
10195
- itemCount,
10196
- " ",
10197
- itemCount === 1 ? "item" : "items"
10198
- ]
10199
- }
10200
- ) }),
10201
- /* @__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) }) })
10202
- ] })
10203
- ] }) }),
10204
- modalContent && (modalContent === "add-items" ? /* @__PURE__ */ jsxRuntime.jsx(ExistingItemsForm, { orderId: preview.id, items: preview.items }) : modalContent === "add-custom-item" ? /* @__PURE__ */ jsxRuntime.jsx(
10205
- CustomItemForm,
10206
- {
10207
- orderId: preview.id,
10208
- currencyCode
10209
- }
10210
- ) : null)
10211
- ]
10212
- }
10213
- ) }),
10214
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10215
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10216
- /* @__PURE__ */ jsxRuntime.jsx(
10217
- ui.Button,
10218
- {
10219
- size: "small",
10220
- type: "button",
10221
- onClick: onSubmit,
10222
- isLoading: isSubmitting,
10223
- children: "Save"
10224
- }
10225
- )
10226
- ] }) })
10227
- ] });
10228
- };
10229
- const Item = ({ item, preview, currencyCode }) => {
10230
- if (item.variant_id) {
10231
- return /* @__PURE__ */ jsxRuntime.jsx(VariantItem, { item, preview, currencyCode });
10232
- }
10233
- return /* @__PURE__ */ jsxRuntime.jsx(CustomItem, { item, preview, currencyCode });
10234
- };
10235
- const VariantItem = ({ item, preview, currencyCode }) => {
10236
- const [editing, setEditing] = React.useState(false);
9920
+ const hasUneditableRows = getHasUneditableRows(metadata);
9921
+ const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
10237
9922
  const form = reactHookForm.useForm({
10238
9923
  defaultValues: {
10239
- quantity: item.quantity,
10240
- unit_price: item.unit_price
9924
+ metadata: getDefaultValues(metadata)
10241
9925
  },
10242
- resolver: zod.zodResolver(variantItemSchema)
9926
+ resolver: zod.zodResolver(MetadataSchema)
10243
9927
  });
10244
- const actionId = React.useMemo(() => {
10245
- var _a, _b;
10246
- return (_b = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD")) == null ? void 0 : _b.id;
10247
- }, [item]);
10248
- const { mutateAsync: updateActionItem, isPending: isUpdatingActionItem } = useDraftOrderUpdateActionItem(preview.id);
10249
- const { mutateAsync: updateOriginalItem, isPending: isUpdatingOriginalItem } = useDraftOrderUpdateItem(preview.id);
10250
- const isPending = isUpdatingActionItem || isUpdatingOriginalItem;
10251
- const onSubmit = form.handleSubmit(async (data) => {
10252
- if (convertNumber(data.unit_price) === item.unit_price && data.quantity === item.quantity) {
10253
- setEditing(false);
10254
- return;
10255
- }
10256
- if (!actionId) {
10257
- await updateOriginalItem(
10258
- {
10259
- item_id: item.id,
10260
- quantity: data.quantity,
10261
- unit_price: convertNumber(data.unit_price)
10262
- },
10263
- {
10264
- onSuccess: () => {
10265
- setEditing(false);
10266
- },
10267
- onError: (e) => {
10268
- ui.toast.error(e.message);
10269
- }
10270
- }
10271
- );
10272
- return;
10273
- }
10274
- await updateActionItem(
9928
+ const handleSubmit = form.handleSubmit(async (data) => {
9929
+ const parsedData = parseValues(data);
9930
+ await mutateAsync(
10275
9931
  {
10276
- action_id: actionId,
10277
- quantity: data.quantity,
10278
- unit_price: convertNumber(data.unit_price)
9932
+ metadata: parsedData
10279
9933
  },
10280
9934
  {
10281
9935
  onSuccess: () => {
10282
- setEditing(false);
9936
+ ui.toast.success("Metadata updated");
9937
+ handleSuccess();
10283
9938
  },
10284
- onError: (e) => {
10285
- ui.toast.error(e.message);
9939
+ onError: (error) => {
9940
+ ui.toast.error(error.message);
10286
9941
  }
10287
9942
  }
10288
9943
  );
10289
9944
  });
10290
- 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: [
10291
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-x-3", children: [
10292
- /* @__PURE__ */ jsxRuntime.jsx(
10293
- Thumbnail,
10294
- {
10295
- thumbnail: item.thumbnail,
10296
- alt: item.product_title ?? void 0
10297
- }
10298
- ),
10299
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
10300
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-1", children: [
10301
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: item.product_title }),
10302
- /* @__PURE__ */ jsxRuntime.jsxs(
10303
- ui.Text,
10304
- {
10305
- size: "small",
10306
- leading: "compact",
10307
- className: "text-ui-fg-subtle",
10308
- children: [
10309
- "(",
10310
- item.variant_title,
10311
- ")"
10312
- ]
10313
- }
10314
- )
9945
+ const { fields, insert, remove } = reactHookForm.useFieldArray({
9946
+ control: form.control,
9947
+ name: "metadata"
9948
+ });
9949
+ function deleteRow(index) {
9950
+ remove(index);
9951
+ if (fields.length === 1) {
9952
+ insert(0, {
9953
+ key: "",
9954
+ value: "",
9955
+ disabled: false
9956
+ });
9957
+ }
9958
+ }
9959
+ function insertRow(index, position) {
9960
+ insert(index + (position === "above" ? 0 : 1), {
9961
+ key: "",
9962
+ value: "",
9963
+ disabled: false
9964
+ });
9965
+ }
9966
+ return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
9967
+ KeyboundForm,
9968
+ {
9969
+ onSubmit: handleSubmit,
9970
+ className: "flex flex-1 flex-col overflow-hidden",
9971
+ children: [
9972
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
9973
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
9974
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
9975
+ /* @__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" }) }),
9976
+ /* @__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" }) })
9977
+ ] }),
9978
+ fields.map((field, index) => {
9979
+ const isDisabled = field.disabled || false;
9980
+ let placeholder = "-";
9981
+ if (typeof field.value === "object") {
9982
+ placeholder = "{ ... }";
9983
+ }
9984
+ if (Array.isArray(field.value)) {
9985
+ placeholder = "[ ... ]";
9986
+ }
9987
+ return /* @__PURE__ */ jsxRuntime.jsx(
9988
+ ConditionalTooltip,
9989
+ {
9990
+ showTooltip: isDisabled,
9991
+ content: "This row is disabled because it contains non-primitive data.",
9992
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group/table relative", children: [
9993
+ /* @__PURE__ */ jsxRuntime.jsxs(
9994
+ "div",
9995
+ {
9996
+ className: ui.clx("grid grid-cols-2 divide-x", {
9997
+ "overflow-hidden rounded-b-lg": index === fields.length - 1
9998
+ }),
9999
+ children: [
10000
+ /* @__PURE__ */ jsxRuntime.jsx(
10001
+ Form$2.Field,
10002
+ {
10003
+ control: form.control,
10004
+ name: `metadata.${index}.key`,
10005
+ render: ({ field: field2 }) => {
10006
+ return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
10007
+ GridInput,
10008
+ {
10009
+ "aria-labelledby": METADATA_KEY_LABEL_ID,
10010
+ ...field2,
10011
+ disabled: isDisabled,
10012
+ placeholder: "Key"
10013
+ }
10014
+ ) }) });
10015
+ }
10016
+ }
10017
+ ),
10018
+ /* @__PURE__ */ jsxRuntime.jsx(
10019
+ Form$2.Field,
10020
+ {
10021
+ control: form.control,
10022
+ name: `metadata.${index}.value`,
10023
+ render: ({ field: { value, ...field2 } }) => {
10024
+ return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
10025
+ GridInput,
10026
+ {
10027
+ "aria-labelledby": METADATA_VALUE_LABEL_ID,
10028
+ ...field2,
10029
+ value: isDisabled ? placeholder : value,
10030
+ disabled: isDisabled,
10031
+ placeholder: "Value"
10032
+ }
10033
+ ) }) });
10034
+ }
10035
+ }
10036
+ )
10037
+ ]
10038
+ }
10039
+ ),
10040
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
10041
+ /* @__PURE__ */ jsxRuntime.jsx(
10042
+ ui.DropdownMenu.Trigger,
10043
+ {
10044
+ className: ui.clx(
10045
+ "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
10046
+ {
10047
+ hidden: isDisabled
10048
+ }
10049
+ ),
10050
+ disabled: isDisabled,
10051
+ asChild: true,
10052
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisVertical, {}) })
10053
+ }
10054
+ ),
10055
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
10056
+ /* @__PURE__ */ jsxRuntime.jsxs(
10057
+ ui.DropdownMenu.Item,
10058
+ {
10059
+ className: "gap-x-2",
10060
+ onClick: () => insertRow(index, "above"),
10061
+ children: [
10062
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpMini, { className: "text-ui-fg-subtle" }),
10063
+ "Insert row above"
10064
+ ]
10065
+ }
10066
+ ),
10067
+ /* @__PURE__ */ jsxRuntime.jsxs(
10068
+ ui.DropdownMenu.Item,
10069
+ {
10070
+ className: "gap-x-2",
10071
+ onClick: () => insertRow(index, "below"),
10072
+ children: [
10073
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownMini, { className: "text-ui-fg-subtle" }),
10074
+ "Insert row below"
10075
+ ]
10076
+ }
10077
+ ),
10078
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
10079
+ /* @__PURE__ */ jsxRuntime.jsxs(
10080
+ ui.DropdownMenu.Item,
10081
+ {
10082
+ className: "gap-x-2",
10083
+ onClick: () => deleteRow(index),
10084
+ children: [
10085
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
10086
+ "Delete row"
10087
+ ]
10088
+ }
10089
+ )
10090
+ ] })
10091
+ ] })
10092
+ ] })
10093
+ },
10094
+ field.id
10095
+ );
10096
+ })
10097
+ ] }),
10098
+ 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." })
10315
10099
  ] }),
10316
- /* @__PURE__ */ jsxRuntime.jsx(
10317
- ui.Text,
10318
- {
10319
- size: "small",
10320
- leading: "compact",
10321
- className: "text-ui-fg-subtle",
10322
- children: item.variant_sku
10323
- }
10324
- )
10325
- ] })
10326
- ] }),
10327
- editing ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
10328
- Form$2.Field,
10100
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10101
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10102
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
10103
+ ] }) })
10104
+ ]
10105
+ }
10106
+ ) });
10107
+ };
10108
+ const GridInput = React.forwardRef(({ className, ...props }, ref) => {
10109
+ return /* @__PURE__ */ jsxRuntime.jsx(
10110
+ "input",
10111
+ {
10112
+ ref,
10113
+ ...props,
10114
+ autoComplete: "off",
10115
+ className: ui.clx(
10116
+ "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",
10117
+ className
10118
+ )
10119
+ }
10120
+ );
10121
+ });
10122
+ GridInput.displayName = "MetadataForm.GridInput";
10123
+ const PlaceholderInner = () => {
10124
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
10125
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
10126
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10127
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }),
10128
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" })
10129
+ ] }) })
10130
+ ] });
10131
+ };
10132
+ const EDITABLE_TYPES = ["string", "number", "boolean"];
10133
+ function getDefaultValues(metadata) {
10134
+ if (!metadata || !Object.keys(metadata).length) {
10135
+ return [
10136
+ {
10137
+ key: "",
10138
+ value: "",
10139
+ disabled: false
10140
+ }
10141
+ ];
10142
+ }
10143
+ return Object.entries(metadata).map(([key, value]) => {
10144
+ if (!EDITABLE_TYPES.includes(typeof value)) {
10145
+ return {
10146
+ key,
10147
+ value,
10148
+ disabled: true
10149
+ };
10150
+ }
10151
+ let stringValue = value;
10152
+ if (typeof value !== "string") {
10153
+ stringValue = JSON.stringify(value);
10154
+ }
10155
+ return {
10156
+ key,
10157
+ value: stringValue,
10158
+ original_key: key
10159
+ };
10160
+ });
10161
+ }
10162
+ function parseValues(values) {
10163
+ const metadata = values.metadata;
10164
+ const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
10165
+ if (isEmpty) {
10166
+ return null;
10167
+ }
10168
+ const update = {};
10169
+ metadata.forEach((field) => {
10170
+ let key = field.key;
10171
+ let value = field.value;
10172
+ const disabled = field.disabled;
10173
+ if (!key || !value) {
10174
+ return;
10175
+ }
10176
+ if (disabled) {
10177
+ update[key] = value;
10178
+ return;
10179
+ }
10180
+ key = key.trim();
10181
+ value = value.trim();
10182
+ if (value === "true") {
10183
+ update[key] = true;
10184
+ } else if (value === "false") {
10185
+ update[key] = false;
10186
+ } else {
10187
+ const parsedNumber = parseFloat(value);
10188
+ if (!isNaN(parsedNumber)) {
10189
+ update[key] = parsedNumber;
10190
+ } else {
10191
+ update[key] = value;
10192
+ }
10193
+ }
10194
+ });
10195
+ return update;
10196
+ }
10197
+ function getHasUneditableRows(metadata) {
10198
+ if (!metadata) {
10199
+ return false;
10200
+ }
10201
+ return Object.values(metadata).some(
10202
+ (value) => !EDITABLE_TYPES.includes(typeof value)
10203
+ );
10204
+ }
10205
+ const PROMOTION_QUERY_KEY = "promotions";
10206
+ const promotionsQueryKeys = {
10207
+ list: (query2) => [
10208
+ PROMOTION_QUERY_KEY,
10209
+ query2 ? query2 : void 0
10210
+ ],
10211
+ detail: (id, query2) => [
10212
+ PROMOTION_QUERY_KEY,
10213
+ id,
10214
+ query2 ? query2 : void 0
10215
+ ]
10216
+ };
10217
+ const usePromotions = (query2, options) => {
10218
+ const { data, ...rest } = reactQuery.useQuery({
10219
+ queryKey: promotionsQueryKeys.list(query2),
10220
+ queryFn: async () => sdk.admin.promotion.list(query2),
10221
+ ...options
10222
+ });
10223
+ return { ...data, ...rest };
10224
+ };
10225
+ const useCancelOrderEdit = ({ preview }) => {
10226
+ const { mutateAsync: cancelOrderEdit } = useDraftOrderCancelEdit(preview == null ? void 0 : preview.id);
10227
+ const onCancel = React.useCallback(async () => {
10228
+ if (!preview) {
10229
+ return true;
10230
+ }
10231
+ let res = false;
10232
+ await cancelOrderEdit(void 0, {
10233
+ onError: (e) => {
10234
+ ui.toast.error(e.message);
10235
+ },
10236
+ onSuccess: () => {
10237
+ res = true;
10238
+ }
10239
+ });
10240
+ return res;
10241
+ }, [preview, cancelOrderEdit]);
10242
+ return { onCancel };
10243
+ };
10244
+ let IS_REQUEST_RUNNING = false;
10245
+ const useInitiateOrderEdit = ({
10246
+ preview
10247
+ }) => {
10248
+ const navigate = reactRouterDom.useNavigate();
10249
+ const { mutateAsync } = useDraftOrderBeginEdit(preview == null ? void 0 : preview.id);
10250
+ React.useEffect(() => {
10251
+ async function run() {
10252
+ if (IS_REQUEST_RUNNING || !preview) {
10253
+ return;
10254
+ }
10255
+ if (preview.order_change) {
10256
+ return;
10257
+ }
10258
+ IS_REQUEST_RUNNING = true;
10259
+ await mutateAsync(void 0, {
10260
+ onError: (e) => {
10261
+ ui.toast.error(e.message);
10262
+ navigate(`/draft-orders/${preview.id}`, { replace: true });
10263
+ return;
10264
+ }
10265
+ });
10266
+ IS_REQUEST_RUNNING = false;
10267
+ }
10268
+ run();
10269
+ }, [preview, navigate, mutateAsync]);
10270
+ };
10271
+ const Promotions = () => {
10272
+ const { id } = reactRouterDom.useParams();
10273
+ const {
10274
+ order: preview,
10275
+ isError: isPreviewError,
10276
+ error: previewError
10277
+ } = useOrderPreview(id, void 0);
10278
+ useInitiateOrderEdit({ preview });
10279
+ const { onCancel } = useCancelOrderEdit({ preview });
10280
+ if (isPreviewError) {
10281
+ throw previewError;
10282
+ }
10283
+ const isReady = !!preview;
10284
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { onClose: onCancel, children: [
10285
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Promotions" }) }) }),
10286
+ isReady && /* @__PURE__ */ jsxRuntime.jsx(PromotionForm, { preview })
10287
+ ] });
10288
+ };
10289
+ const PromotionForm = ({ preview }) => {
10290
+ const { items, shipping_methods } = preview;
10291
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
10292
+ const [comboboxValue, setComboboxValue] = React.useState("");
10293
+ const { handleSuccess } = useRouteModal();
10294
+ const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
10295
+ const promoIds = getPromotionIds(items, shipping_methods);
10296
+ const { promotions, isPending, isError, error } = usePromotions(
10297
+ {
10298
+ id: promoIds
10299
+ },
10300
+ {
10301
+ enabled: !!promoIds.length
10302
+ }
10303
+ );
10304
+ const comboboxData = useComboboxData({
10305
+ queryKey: ["promotions", "combobox", promoIds],
10306
+ queryFn: async (params) => {
10307
+ return await sdk.admin.promotion.list({
10308
+ ...params,
10309
+ id: {
10310
+ $nin: promoIds
10311
+ }
10312
+ });
10313
+ },
10314
+ getOptions: (data) => {
10315
+ return data.promotions.map((promotion) => ({
10316
+ label: promotion.code,
10317
+ value: promotion.code
10318
+ }));
10319
+ }
10320
+ });
10321
+ const add = async (value) => {
10322
+ if (!value) {
10323
+ return;
10324
+ }
10325
+ addPromotions(
10329
10326
  {
10330
- control: form.control,
10331
- name: "quantity",
10332
- render: ({ field }) => {
10333
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(NumberInput, { ...field }) }) });
10334
- }
10335
- }
10336
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: item.quantity }) }),
10337
- editing ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
10338
- Form$2.Field,
10327
+ promo_codes: [value]
10328
+ },
10339
10329
  {
10340
- control: form.control,
10341
- name: "unit_price",
10342
- render: ({ field: { onChange, ...field } }) => {
10343
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
10344
- ui.CurrencyInput,
10345
- {
10346
- ...field,
10347
- symbol: getNativeSymbol(currencyCode),
10348
- code: currencyCode,
10349
- onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value)
10350
- }
10351
- ) }) });
10330
+ onError: (e) => {
10331
+ ui.toast.error(e.message);
10332
+ comboboxData.onSearchValueChange("");
10333
+ setComboboxValue("");
10334
+ },
10335
+ onSuccess: () => {
10336
+ comboboxData.onSearchValueChange("");
10337
+ setComboboxValue("");
10352
10338
  }
10353
10339
  }
10354
- ) }) : /* @__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) }) }),
10355
- /* @__PURE__ */ jsxRuntime.jsx(
10356
- ui.IconButton,
10357
- {
10358
- type: "button",
10359
- size: "small",
10360
- onClick: editing ? onSubmit : () => {
10361
- setEditing(true);
10362
- },
10363
- disabled: isPending,
10364
- children: editing ? /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}) : /* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, {})
10340
+ );
10341
+ };
10342
+ const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
10343
+ const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
10344
+ const onSubmit = async () => {
10345
+ setIsSubmitting(true);
10346
+ let requestSucceeded = false;
10347
+ await requestOrderEdit(void 0, {
10348
+ onError: (e) => {
10349
+ ui.toast.error(e.message);
10350
+ },
10351
+ onSuccess: () => {
10352
+ requestSucceeded = true;
10365
10353
  }
10366
- )
10367
- ] }) }) });
10368
- };
10369
- const variantItemSchema = objectType({
10370
- quantity: numberType(),
10371
- unit_price: unionType([numberType(), stringType()])
10372
- });
10373
- const CustomItem = ({ item, preview, currencyCode }) => {
10374
- const [editing, setEditing] = React.useState(false);
10375
- const { quantity, unit_price, title } = item;
10376
- const form = reactHookForm.useForm({
10377
- defaultValues: {
10378
- title,
10379
- quantity,
10380
- unit_price
10381
- },
10382
- resolver: zod.zodResolver(customItemSchema)
10383
- });
10384
- React.useEffect(() => {
10385
- form.reset({
10386
- title,
10387
- quantity,
10388
- unit_price
10389
10354
  });
10390
- }, [form, title, quantity, unit_price]);
10391
- const actionId = React.useMemo(() => {
10392
- var _a, _b;
10393
- return (_b = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD")) == null ? void 0 : _b.id;
10394
- }, [item]);
10395
- const { mutateAsync: updateActionItem, isPending: isUpdatingActionItem } = useDraftOrderUpdateActionItem(preview.id);
10396
- const { mutateAsync: removeActionItem, isPending: isRemovingActionItem } = useDraftOrderRemoveActionItem(preview.id);
10397
- const { mutateAsync: updateOriginalItem, isPending: isUpdatingOriginalItem } = useDraftOrderUpdateItem(preview.id);
10398
- const isPending = isUpdatingActionItem || isUpdatingOriginalItem;
10399
- const onSubmit = form.handleSubmit(async (data) => {
10400
- if (convertNumber(data.unit_price) === item.unit_price && data.quantity === item.quantity && data.title === item.title) {
10401
- setEditing(false);
10355
+ if (!requestSucceeded) {
10356
+ setIsSubmitting(false);
10402
10357
  return;
10403
10358
  }
10404
- if (!actionId) {
10405
- await updateOriginalItem(
10359
+ await confirmOrderEdit(void 0, {
10360
+ onError: (e) => {
10361
+ ui.toast.error(e.message);
10362
+ },
10363
+ onSuccess: () => {
10364
+ handleSuccess();
10365
+ },
10366
+ onSettled: () => {
10367
+ setIsSubmitting(false);
10368
+ }
10369
+ });
10370
+ };
10371
+ if (isError) {
10372
+ throw error;
10373
+ }
10374
+ return /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
10375
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
10376
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
10377
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
10378
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
10379
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Hint, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
10380
+ ] }),
10381
+ /* @__PURE__ */ jsxRuntime.jsx(
10382
+ Combobox,
10383
+ {
10384
+ id: "promotion-combobox",
10385
+ "aria-describedby": "promotion-combobox-hint",
10386
+ isFetchingNextPage: comboboxData.isFetchingNextPage,
10387
+ fetchNextPage: comboboxData.fetchNextPage,
10388
+ options: comboboxData.options,
10389
+ onSearchValueChange: comboboxData.onSearchValueChange,
10390
+ searchValue: comboboxData.searchValue,
10391
+ disabled: comboboxData.disabled || isAddingPromotions,
10392
+ onChange: add,
10393
+ value: comboboxValue
10394
+ }
10395
+ )
10396
+ ] }),
10397
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
10398
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: promotions == null ? void 0 : promotions.map((promotion) => /* @__PURE__ */ jsxRuntime.jsx(
10399
+ PromotionItem,
10406
10400
  {
10407
- item_id: item.id,
10408
- quantity: data.quantity,
10409
- unit_price: convertNumber(data.unit_price)
10401
+ promotion,
10402
+ orderId: preview.id,
10403
+ isLoading: isPending
10410
10404
  },
10405
+ promotion.id
10406
+ )) })
10407
+ ] }) }),
10408
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
10409
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
10410
+ /* @__PURE__ */ jsxRuntime.jsx(
10411
+ ui.Button,
10411
10412
  {
10412
- onSuccess: () => {
10413
- setEditing(false);
10414
- },
10415
- onError: (e) => {
10416
- ui.toast.error(e.message);
10417
- }
10413
+ size: "small",
10414
+ type: "submit",
10415
+ isLoading: isSubmitting || isAddingPromotions,
10416
+ children: "Save"
10418
10417
  }
10419
- );
10420
- return;
10421
- }
10422
- if (data.quantity === 0) {
10423
- await removeActionItem(actionId, {
10424
- onSuccess: () => {
10425
- setEditing(false);
10426
- },
10418
+ )
10419
+ ] }) })
10420
+ ] });
10421
+ };
10422
+ const PromotionItem = ({
10423
+ promotion,
10424
+ orderId,
10425
+ isLoading
10426
+ }) => {
10427
+ var _a;
10428
+ const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
10429
+ const onRemove = async () => {
10430
+ removePromotions(
10431
+ {
10432
+ promo_codes: [promotion.code]
10433
+ },
10434
+ {
10427
10435
  onError: (e) => {
10428
10436
  ui.toast.error(e.message);
10429
10437
  }
10430
- });
10431
- return;
10438
+ }
10439
+ );
10440
+ };
10441
+ const displayValue = getDisplayValue(promotion);
10442
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10443
+ "div",
10444
+ {
10445
+ className: ui.clx(
10446
+ "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
10447
+ {
10448
+ "animate-pulse": isLoading
10449
+ }
10450
+ ),
10451
+ children: [
10452
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10453
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
10454
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
10455
+ displayValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
10456
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: displayValue }),
10457
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "·" })
10458
+ ] }),
10459
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "capitalize", children: (_a = promotion.application_method) == null ? void 0 : _a.allocation })
10460
+ ] })
10461
+ ] }),
10462
+ /* @__PURE__ */ jsxRuntime.jsx(
10463
+ ui.IconButton,
10464
+ {
10465
+ size: "small",
10466
+ type: "button",
10467
+ variant: "transparent",
10468
+ onClick: onRemove,
10469
+ isLoading: isPending || isLoading,
10470
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, {})
10471
+ }
10472
+ )
10473
+ ]
10474
+ },
10475
+ promotion.id
10476
+ );
10477
+ };
10478
+ function getDisplayValue(promotion) {
10479
+ var _a, _b, _c, _d;
10480
+ const value = (_a = promotion.application_method) == null ? void 0 : _a.value;
10481
+ if (!value) {
10482
+ return null;
10483
+ }
10484
+ if (((_b = promotion.application_method) == null ? void 0 : _b.type) === "fixed") {
10485
+ const currency = (_c = promotion.application_method) == null ? void 0 : _c.currency_code;
10486
+ if (!currency) {
10487
+ return null;
10488
+ }
10489
+ return getLocaleAmount(value, currency);
10490
+ } else if (((_d = promotion.application_method) == null ? void 0 : _d.type) === "percentage") {
10491
+ return formatPercentage(value);
10492
+ }
10493
+ return null;
10494
+ }
10495
+ const formatter = new Intl.NumberFormat([], {
10496
+ style: "percent",
10497
+ minimumFractionDigits: 2
10498
+ });
10499
+ const formatPercentage = (value, isPercentageValue = false) => {
10500
+ let val = value || 0;
10501
+ if (!isPercentageValue) {
10502
+ val = val / 100;
10503
+ }
10504
+ return formatter.format(val);
10505
+ };
10506
+ function getPromotionIds(items, shippingMethods) {
10507
+ const promotionIds = /* @__PURE__ */ new Set();
10508
+ for (const item of items) {
10509
+ if (item.adjustments) {
10510
+ for (const adjustment of item.adjustments) {
10511
+ if (adjustment.promotion_id) {
10512
+ promotionIds.add(adjustment.promotion_id);
10513
+ }
10514
+ }
10432
10515
  }
10433
- await updateActionItem(
10434
- {
10435
- action_id: actionId,
10436
- quantity: data.quantity,
10437
- unit_price: convertNumber(data.unit_price)
10438
- },
10439
- {
10440
- onSuccess: () => {
10441
- setEditing(false);
10442
- },
10443
- onError: (e) => {
10444
- ui.toast.error(e.message);
10516
+ }
10517
+ for (const shippingMethod of shippingMethods) {
10518
+ if (shippingMethod.adjustments) {
10519
+ for (const adjustment of shippingMethod.adjustments) {
10520
+ if (adjustment.promotion_id) {
10521
+ promotionIds.add(adjustment.promotion_id);
10445
10522
  }
10446
10523
  }
10447
- );
10448
- });
10449
- 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: [
10450
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
10451
- /* @__PURE__ */ jsxRuntime.jsx(
10452
- Thumbnail,
10453
- {
10454
- thumbnail: item.thumbnail,
10455
- alt: item.title ?? void 0
10456
- }
10457
- ),
10458
- editing ? /* @__PURE__ */ jsxRuntime.jsx(
10459
- Form$2.Field,
10460
- {
10461
- control: form.control,
10462
- name: "title",
10463
- render: ({ field }) => {
10464
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...field }) }) });
10465
- }
10466
- }
10467
- ) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: item.title })
10468
- ] }),
10469
- editing ? /* @__PURE__ */ jsxRuntime.jsx(
10470
- Form$2.Field,
10471
- {
10472
- control: form.control,
10473
- name: "quantity",
10474
- render: ({ field }) => {
10475
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(NumberInput, { ...field }) }) });
10476
- }
10524
+ }
10525
+ }
10526
+ return Array.from(promotionIds);
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);
10477
10544
  }
10478
- ) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: item.quantity }),
10479
- editing ? /* @__PURE__ */ jsxRuntime.jsx(
10480
- Form$2.Field,
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",
10481
10560
  {
10482
- control: form.control,
10483
- name: "unit_price",
10484
- render: ({ field: { onChange, ...field } }) => {
10485
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
10486
- ui.CurrencyInput,
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",
10564
+ {
10565
+ "h-7": size === "small",
10566
+ "h-8": size === "base"
10567
+ },
10568
+ className
10569
+ ),
10570
+ children: [
10571
+ /* @__PURE__ */ jsxRuntime.jsx(
10572
+ "input",
10487
10573
  {
10488
- ...field,
10489
- symbol: getNativeSymbol(currencyCode),
10490
- code: currencyCode,
10491
- onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value)
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
10492
10587
  }
10493
- ) }) });
10494
- }
10495
- }
10496
- ) : /* @__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) }) }),
10497
- /* @__PURE__ */ jsxRuntime.jsx(
10498
- ui.IconButton,
10499
- {
10500
- type: "button",
10501
- size: "small",
10502
- onClick: editing ? onSubmit : () => {
10503
- setEditing(true);
10504
- },
10505
- disabled: isPending,
10506
- children: editing ? /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}) : /* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, {})
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
+ ]
10507
10634
  }
10508
- )
10509
- ] }) }) });
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
+ ]
10510
10644
  };
10511
- const StackedModalTrigger$1 = ({
10512
- type,
10513
- setModalContent
10514
- }) => {
10515
- const { setIsOpen } = useStackedModal();
10516
- const onClick = React.useCallback(() => {
10517
- setModalContent(type);
10518
- setIsOpen(STACKED_MODAL_ID, true);
10519
- }, [setModalContent, setIsOpen, type]);
10520
- 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" }) });
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 };
10521
10652
  };
10522
- const VARIANT_PREFIX = "items";
10523
- const LIMIT = 50;
10524
- const ExistingItemsForm = ({ orderId, items }) => {
10525
- const { setIsOpen } = useStackedModal();
10526
- const [rowSelection, setRowSelection] = React.useState(
10527
- items.reduce((acc, item) => {
10528
- acc[item.variant_id] = true;
10529
- return acc;
10530
- }, {})
10531
- );
10532
- React.useEffect(() => {
10533
- setRowSelection(
10534
- items.reduce((acc, item) => {
10535
- if (item.variant_id) {
10536
- acc[item.variant_id] = true;
10537
- }
10538
- return acc;
10539
- }, {})
10540
- );
10541
- }, [items]);
10542
- const { q, order, offset } = useQueryParams(
10543
- ["q", "order", "offset"],
10544
- VARIANT_PREFIX
10545
- );
10546
- const { variants, count, isPending, isError, error } = useProductVariants(
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,
10547
10670
  {
10548
- q,
10549
- order,
10550
- offset: offset ? parseInt(offset) : void 0,
10551
- limit: LIMIT
10671
+ fields: "currency_code"
10552
10672
  },
10553
10673
  {
10554
- placeholderData: reactQuery.keepPreviousData
10674
+ enabled: !!id
10555
10675
  }
10556
10676
  );
10557
- const columns = useColumns();
10558
- const { mutateAsync } = useDraftOrderAddItems(orderId);
10559
- const onSubmit = async () => {
10560
- const ids = Object.keys(rowSelection).filter(
10561
- (id) => !items.find((i) => i.variant_id === id)
10562
- );
10563
- await mutateAsync(
10564
- {
10565
- items: ids.map((id) => ({
10566
- variant_id: id,
10567
- quantity: 1
10568
- }))
10569
- },
10570
- {
10571
- onSuccess: () => {
10572
- setRowSelection({});
10573
- setIsOpen(STACKED_MODAL_ID, false);
10574
- },
10575
- onError: (e) => {
10576
- ui.toast.error(e.message);
10577
- }
10578
- }
10579
- );
10580
- };
10677
+ const { onCancel } = useCancelOrderEdit({ preview });
10581
10678
  if (isError) {
10582
10679
  throw error;
10583
10680
  }
10584
- return /* @__PURE__ */ jsxRuntime.jsxs(
10585
- StackedFocusModal.Content,
10586
- {
10587
- onOpenAutoFocus: (e) => {
10588
- e.preventDefault();
10589
- const searchInput = document.querySelector(
10590
- "[data-modal-id='modal-search-input']"
10591
- );
10592
- if (searchInput) {
10593
- searchInput.focus();
10594
- }
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}`);
10595
10712
  },
10596
- children: [
10597
- /* @__PURE__ */ jsxRuntime.jsxs(StackedFocusModal.Header, { children: [
10598
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Product Variants" }) }),
10599
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Choose product variants to add to the order." }) })
10600
- ] }),
10601
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Body, { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
10602
- DataTable,
10603
- {
10604
- data: variants,
10605
- columns,
10606
- isLoading: isPending,
10607
- getRowId: (row) => row.id,
10608
- rowCount: count,
10609
- prefix: VARIANT_PREFIX,
10610
- layout: "fill",
10611
- rowSelection: {
10612
- state: rowSelection,
10613
- onRowSelectionChange: setRowSelection,
10614
- enableRowSelection: (row) => {
10615
- return !items.find((i) => i.variant_id === row.original.id);
10616
- }
10617
- },
10618
- autoFocusSearch: true
10619
- }
10620
- ) }),
10621
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10622
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10623
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "button", onClick: onSubmit, children: "Update items" })
10624
- ] }) })
10625
- ]
10713
+ onSuccess: () => {
10714
+ requestSucceeded = true;
10715
+ }
10716
+ });
10717
+ if (!requestSucceeded) {
10718
+ setIsSubmitting(false);
10719
+ return;
10626
10720
  }
10627
- );
10628
- };
10629
- const columnHelper = ui.createDataTableColumnHelper();
10630
- const useColumns = () => {
10631
- return React.useMemo(() => {
10632
- return [
10633
- columnHelper.select(),
10634
- columnHelper.accessor("product.title", {
10635
- header: "Product",
10636
- cell: ({ row }) => {
10637
- var _a, _b, _c;
10638
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
10639
- /* @__PURE__ */ jsxRuntime.jsx(
10640
- Thumbnail,
10641
- {
10642
- thumbnail: (_a = row.original.product) == null ? void 0 : _a.thumbnail,
10643
- alt: (_b = row.original.product) == null ? void 0 : _b.title
10644
- }
10645
- ),
10646
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: (_c = row.original.product) == null ? void 0 : _c.title })
10647
- ] });
10648
- },
10649
- enableSorting: true
10650
- }),
10651
- columnHelper.accessor("title", {
10652
- header: "Variant",
10653
- enableSorting: true
10654
- }),
10655
- columnHelper.accessor("sku", {
10656
- header: "SKU",
10657
- cell: ({ getValue }) => {
10658
- return getValue() ?? "-";
10659
- },
10660
- enableSorting: true
10661
- }),
10662
- columnHelper.accessor("updated_at", {
10663
- header: "Updated",
10664
- cell: ({ getValue }) => {
10665
- return /* @__PURE__ */ jsxRuntime.jsx(
10666
- ui.Tooltip,
10667
- {
10668
- content: getFullDate({ date: getValue(), includeTime: true }),
10669
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: getFullDate({ date: getValue() }) })
10670
- }
10671
- );
10672
- },
10673
- enableSorting: true,
10674
- sortAscLabel: "Oldest first",
10675
- sortDescLabel: "Newest first"
10676
- }),
10677
- columnHelper.accessor("created_at", {
10678
- header: "Created",
10679
- cell: ({ getValue }) => {
10680
- return /* @__PURE__ */ jsxRuntime.jsx(
10681
- ui.Tooltip,
10682
- {
10683
- content: getFullDate({ date: getValue(), includeTime: true }),
10684
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: getFullDate({ date: getValue() }) })
10685
- }
10686
- );
10687
- },
10688
- enableSorting: true,
10689
- sortAscLabel: "Oldest first",
10690
- sortDescLabel: "Newest first"
10691
- })
10692
- ];
10693
- }, []);
10694
- };
10695
- const CustomItemForm = ({ orderId, currencyCode }) => {
10696
- const { setIsOpen } = useStackedModal();
10697
- const { mutateAsync: addItems } = useDraftOrderAddItems(orderId);
10698
- const form = reactHookForm.useForm({
10699
- defaultValues: {
10700
- title: "",
10701
- quantity: 1,
10702
- unit_price: ""
10703
- },
10704
- resolver: zod.zodResolver(customItemSchema)
10705
- });
10706
- const onSubmit = form.handleSubmit(async (data) => {
10707
- await addItems(
10708
- {
10709
- items: [
10710
- {
10711
- title: data.title,
10712
- quantity: data.quantity,
10713
- unit_price: convertNumber(data.unit_price)
10714
- }
10715
- ]
10721
+ await confirmOrderEdit(void 0, {
10722
+ onError: (e) => {
10723
+ ui.toast.error(`Failed to confirm order edit: ${e.message}`);
10716
10724
  },
10717
- {
10718
- onSuccess: () => {
10719
- setIsOpen(STACKED_MODAL_ID, false);
10720
- },
10721
- onError: (e) => {
10722
- ui.toast.error(e.message);
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;
10723
10738
  }
10739
+ onSubmit();
10724
10740
  }
10725
- );
10726
- });
10727
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2, { ...form, children: /* @__PURE__ */ jsxRuntime.jsx(KeyboundForm, { onSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(StackedFocusModal.Content, { children: [
10728
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Header, {}),
10729
- /* @__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: [
10730
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10731
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Add custom item" }) }),
10732
- /* @__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." }) })
10733
- ] }),
10734
- /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
10735
- /* @__PURE__ */ jsxRuntime.jsx(
10736
- Form$2.Field,
10737
- {
10738
- control: form.control,
10739
- name: "title",
10740
- render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
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);
10759
+ }
10760
+ },
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: [
10741
10763
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10742
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Label, { children: "Title" }),
10743
- /* @__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" }) })
10744
10766
  ] }),
10745
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10746
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...field }) }),
10747
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.ErrorMessage, {})
10748
- ] })
10749
- ] }) })
10750
- }
10751
- ),
10752
- /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
10753
- /* @__PURE__ */ jsxRuntime.jsx(
10754
- Form$2.Field,
10755
- {
10756
- control: form.control,
10757
- name: "unit_price",
10758
- render: ({ field: { onChange, ...field } }) => /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
10759
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10760
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Label, { children: "Unit price" }),
10761
- /* @__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
+ ] })
10762
10832
  ] }),
10763
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10764
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
10765
- 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,
10766
10838
  {
10767
- symbol: getNativeSymbol(currencyCode),
10768
- code: currencyCode,
10769
- onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value),
10770
- ...field
10839
+ size: "small",
10840
+ leading: "compact",
10841
+ className: "text-ui-fg-subtle",
10842
+ children: [
10843
+ itemCount,
10844
+ " ",
10845
+ itemCount === 1 ? "item" : "items"
10846
+ ]
10771
10847
  }
10772
10848
  ) }),
10773
- /* @__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) }) })
10774
10850
  ] })
10775
- ] }) })
10776
- }
10777
- ),
10778
- /* @__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" }) }),
10779
10864
  /* @__PURE__ */ jsxRuntime.jsx(
10780
- Form$2.Field,
10865
+ ui.Button,
10781
10866
  {
10782
- control: form.control,
10783
- name: "quantity",
10784
- render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
10785
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10786
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Label, { children: "Quantity" }),
10787
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Hint, { children: "Enter the quantity of the item" })
10788
- ] }),
10789
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex-1", children: [
10790
- /* @__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" }) }) }),
10791
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.ErrorMessage, {})
10792
- ] })
10793
- ] }) })
10867
+ size: "small",
10868
+ type: "button",
10869
+ onClick: onSubmit,
10870
+ isLoading: isSubmitting,
10871
+ children: "Save"
10794
10872
  }
10795
10873
  )
10796
- ] }) }) }),
10797
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10798
- /* @__PURE__ */ jsxRuntime.jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10799
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "button", onClick: onSubmit, children: "Add item" })
10800
10874
  ] }) })
10801
- ] }) }) });
10802
- };
10803
- const customItemSchema = objectType({
10804
- title: stringType().min(1),
10805
- quantity: numberType(),
10806
- unit_price: unionType([numberType(), stringType()])
10807
- });
10808
- const CustomItems = () => {
10809
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
10810
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Custom Items" }) }) }),
10811
- /* @__PURE__ */ jsxRuntime.jsx(CustomItemsForm, {})
10812
10875
  ] });
10813
10876
  };
10814
- const CustomItemsForm = () => {
10815
- const form = reactHookForm.useForm({
10816
- resolver: zod.zodResolver(schema$3)
10817
- });
10818
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
10819
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, {}),
10820
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
10821
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
10822
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", children: "Save" })
10823
- ] }) })
10824
- ] }) });
10825
- };
10826
- const schema$3 = objectType({
10827
- email: stringType().email()
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,18 +13069,14 @@ const routeModule = {
13069
13069
  Component: BillingAddress,
13070
13070
  path: "/draft-orders/:id/billing-address"
13071
13071
  },
13072
- {
13073
- Component: Email,
13074
- path: "/draft-orders/:id/email"
13075
- },
13076
- {
13077
- Component: Items,
13078
- path: "/draft-orders/:id/items"
13079
- },
13080
13072
  {
13081
13073
  Component: CustomItems,
13082
13074
  path: "/draft-orders/:id/custom-items"
13083
13075
  },
13076
+ {
13077
+ Component: Email,
13078
+ path: "/draft-orders/:id/email"
13079
+ },
13084
13080
  {
13085
13081
  Component: Metadata,
13086
13082
  path: "/draft-orders/:id/metadata"
@@ -13089,6 +13085,10 @@ const routeModule = {
13089
13085
  Component: Promotions,
13090
13086
  path: "/draft-orders/:id/promotions"
13091
13087
  },
13088
+ {
13089
+ Component: Items,
13090
+ path: "/draft-orders/:id/items"
13091
+ },
13092
13092
  {
13093
13093
  Component: SalesChannel,
13094
13094
  path: "/draft-orders/:id/sales-channel"