@medusajs/draft-order 2.10.4-preview-20250928060202 → 2.10.4-preview-20250928120204

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import { Tooltip, DropdownMenu, clx, IconButton, useDataTable, DataTable as Data
4
4
  import { useQuery, useQueryClient, useMutation, keepPreviousData, useInfiniteQuery } from "@tanstack/react-query";
5
5
  import React, { useState, useCallback, useMemo, Fragment, createContext, forwardRef, useId, useContext, useTransition, useRef, useImperativeHandle, useDeferredValue, useEffect, Suspense } from "react";
6
6
  import { useSearchParams, Link, useNavigate, Outlet, useBlocker, useLocation, useParams } from "react-router-dom";
7
- import { EllipsisHorizontal, XMark, InformationCircleSolid, XMarkMini, TrianglesMini, CheckMini, EllipseMiniSolid, PlusMini, ExclamationCircleSolid, ArrowPath, FlyingBox, CurrencyDollar, Envelope, Channels, Trash, ArrowUpRightOnBox, TriangleDownMini, Check, SquareTwoStack, Photo, TriangleRightMini, Shopping, Buildings, TruckFast, Plus, ReceiptPercent, EllipsisVertical, ArrowUpMini, ArrowDownMini, Minus, PencilSquare } from "@medusajs/icons";
7
+ import { EllipsisHorizontal, XMark, InformationCircleSolid, XMarkMini, TrianglesMini, CheckMini, EllipseMiniSolid, PlusMini, ExclamationCircleSolid, ArrowPath, FlyingBox, CurrencyDollar, Envelope, Channels, Trash, ArrowUpRightOnBox, TriangleDownMini, Check, SquareTwoStack, Photo, TriangleRightMini, Shopping, Buildings, TruckFast, Plus, ReceiptPercent, Minus, PencilSquare, EllipsisVertical, ArrowUpMini, ArrowDownMini } from "@medusajs/icons";
8
8
  import Medusa from "@medusajs/js-sdk";
9
9
  import { format, formatDistance, sub, subDays, subMonths } from "date-fns";
10
10
  import { enUS } from "date-fns/locale";
@@ -9757,6 +9757,27 @@ const BillingAddressForm = ({ order }) => {
9757
9757
  ) });
9758
9758
  };
9759
9759
  const schema$5 = addressSchema;
9760
+ const CustomItems = () => {
9761
+ return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
9762
+ /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Custom Items" }) }) }),
9763
+ /* @__PURE__ */ jsx(CustomItemsForm, {})
9764
+ ] });
9765
+ };
9766
+ const CustomItemsForm = () => {
9767
+ const form = useForm({
9768
+ resolver: zodResolver(schema$4)
9769
+ });
9770
+ return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
9771
+ /* @__PURE__ */ jsx(RouteDrawer.Body, {}),
9772
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
9773
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
9774
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", children: "Save" })
9775
+ ] }) })
9776
+ ] }) });
9777
+ };
9778
+ const schema$4 = objectType({
9779
+ email: stringType().email()
9780
+ });
9760
9781
  const Email = () => {
9761
9782
  const { id } = useParams();
9762
9783
  const { order, isPending, isError, error } = useOrder(id, {
@@ -9779,7 +9800,7 @@ const EmailForm = ({ order }) => {
9779
9800
  defaultValues: {
9780
9801
  email: order.email ?? ""
9781
9802
  },
9782
- resolver: zodResolver(schema$4)
9803
+ resolver: zodResolver(schema$3)
9783
9804
  });
9784
9805
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
9785
9806
  const { handleSuccess } = useRouteModal();
@@ -9822,581 +9843,210 @@ const EmailForm = ({ order }) => {
9822
9843
  }
9823
9844
  ) });
9824
9845
  };
9825
- const schema$4 = objectType({
9826
- email: stringType().email()
9827
- });
9828
- const CustomItems = () => {
9829
- return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
9830
- /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Custom Items" }) }) }),
9831
- /* @__PURE__ */ jsx(CustomItemsForm, {})
9832
- ] });
9833
- };
9834
- const CustomItemsForm = () => {
9835
- const form = useForm({
9836
- resolver: zodResolver(schema$3)
9837
- });
9838
- return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
9839
- /* @__PURE__ */ jsx(RouteDrawer.Body, {}),
9840
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
9841
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
9842
- /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", children: "Save" })
9843
- ] }) })
9844
- ] }) });
9845
- };
9846
9846
  const schema$3 = objectType({
9847
9847
  email: stringType().email()
9848
9848
  });
9849
- const InlineTip = forwardRef(
9850
- ({ variant = "tip", label, className, children, ...props }, ref) => {
9851
- const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
9849
+ const NumberInput = forwardRef(
9850
+ ({
9851
+ value,
9852
+ onChange,
9853
+ size = "base",
9854
+ min = 0,
9855
+ max = 100,
9856
+ step = 1,
9857
+ className,
9858
+ disabled,
9859
+ ...props
9860
+ }, ref) => {
9861
+ const handleChange = (event) => {
9862
+ const newValue = event.target.value === "" ? min : Number(event.target.value);
9863
+ if (!isNaN(newValue) && (max === void 0 || newValue <= max) && (min === void 0 || newValue >= min)) {
9864
+ onChange(newValue);
9865
+ }
9866
+ };
9867
+ const handleIncrement = () => {
9868
+ const newValue = value + step;
9869
+ if (max === void 0 || newValue <= max) {
9870
+ onChange(newValue);
9871
+ }
9872
+ };
9873
+ const handleDecrement = () => {
9874
+ const newValue = value - step;
9875
+ if (min === void 0 || newValue >= min) {
9876
+ onChange(newValue);
9877
+ }
9878
+ };
9852
9879
  return /* @__PURE__ */ jsxs(
9853
9880
  "div",
9854
9881
  {
9855
- ref,
9856
9882
  className: clx(
9857
- "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
9883
+ "inline-flex rounded-md bg-ui-bg-field shadow-borders-base overflow-hidden divide-x transition-fg",
9884
+ "[&:has(input:focus)]:shadow-borders-interactive-with-active",
9885
+ {
9886
+ "h-7": size === "small",
9887
+ "h-8": size === "base"
9888
+ },
9858
9889
  className
9859
9890
  ),
9860
- ...props,
9861
9891
  children: [
9862
9892
  /* @__PURE__ */ jsx(
9863
- "div",
9893
+ "input",
9864
9894
  {
9865
- role: "presentation",
9866
- className: clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
9867
- "bg-ui-tag-orange-icon": variant === "warning"
9868
- })
9895
+ ref,
9896
+ type: "number",
9897
+ value,
9898
+ onChange: handleChange,
9899
+ min,
9900
+ max,
9901
+ step,
9902
+ className: clx(
9903
+ "flex-1 px-2 py-1 bg-transparent txt-compact-small text-ui-fg-base outline-none [appearance:textfield]",
9904
+ "[&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
9905
+ "placeholder:text-ui-fg-muted"
9906
+ ),
9907
+ ...props
9869
9908
  }
9870
9909
  ),
9871
- /* @__PURE__ */ jsxs("div", { className: "text-pretty", children: [
9872
- /* @__PURE__ */ jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
9873
- labelValue,
9874
- ":"
9875
- ] }),
9876
- " ",
9877
- children
9878
- ] })
9910
+ /* @__PURE__ */ jsxs(
9911
+ "button",
9912
+ {
9913
+ className: clx(
9914
+ "flex items-center justify-center outline-none transition-fg",
9915
+ "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
9916
+ "focus:bg-ui-bg-field-component-hover",
9917
+ "hover:bg-ui-bg-field-component-hover",
9918
+ {
9919
+ "size-7": size === "small",
9920
+ "size-8": size === "base"
9921
+ }
9922
+ ),
9923
+ type: "button",
9924
+ onClick: handleDecrement,
9925
+ disabled: min !== void 0 && value <= min || disabled,
9926
+ children: [
9927
+ /* @__PURE__ */ jsx(Minus, {}),
9928
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: `Decrease by ${step}` })
9929
+ ]
9930
+ }
9931
+ ),
9932
+ /* @__PURE__ */ jsxs(
9933
+ "button",
9934
+ {
9935
+ className: clx(
9936
+ "flex items-center justify-center outline-none transition-fg",
9937
+ "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
9938
+ "focus:bg-ui-bg-field-hover",
9939
+ "hover:bg-ui-bg-field-hover",
9940
+ {
9941
+ "size-7": size === "small",
9942
+ "size-8": size === "base"
9943
+ }
9944
+ ),
9945
+ type: "button",
9946
+ onClick: handleIncrement,
9947
+ disabled: max !== void 0 && value >= max || disabled,
9948
+ children: [
9949
+ /* @__PURE__ */ jsx(Plus, {}),
9950
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: `Increase by ${step}` })
9951
+ ]
9952
+ }
9953
+ )
9879
9954
  ]
9880
9955
  }
9881
9956
  );
9882
9957
  }
9883
9958
  );
9884
- InlineTip.displayName = "InlineTip";
9885
- const MetadataFieldSchema = objectType({
9886
- key: stringType(),
9887
- disabled: booleanType().optional(),
9888
- value: anyType()
9889
- });
9890
- const MetadataSchema = objectType({
9891
- metadata: arrayType(MetadataFieldSchema)
9892
- });
9893
- const Metadata = () => {
9894
- const { id } = useParams();
9895
- const { order, isPending, isError, error } = useOrder(id, {
9896
- fields: "metadata"
9897
- });
9898
- if (isError) {
9899
- throw error;
9900
- }
9901
- const isReady = !isPending && !!order;
9902
- return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
9903
- /* @__PURE__ */ jsxs(RouteDrawer.Header, { children: [
9904
- /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Metadata" }) }),
9905
- /* @__PURE__ */ jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
9906
- ] }),
9907
- !isReady ? /* @__PURE__ */ jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
9908
- ] });
9959
+ const PRODUCT_VARIANTS_QUERY_KEY = "product-variants";
9960
+ const productVariantsQueryKeys = {
9961
+ list: (query2) => [
9962
+ PRODUCT_VARIANTS_QUERY_KEY,
9963
+ query2 ? query2 : void 0
9964
+ ]
9909
9965
  };
9910
- const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
9911
- const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
9912
- const MetadataForm = ({ orderId, metadata }) => {
9913
- const { handleSuccess } = useRouteModal();
9914
- const hasUneditableRows = getHasUneditableRows(metadata);
9915
- const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
9916
- const form = useForm({
9917
- defaultValues: {
9918
- metadata: getDefaultValues(metadata)
9919
- },
9920
- resolver: zodResolver(MetadataSchema)
9966
+ const useProductVariants = (query2, options) => {
9967
+ const { data, ...rest } = useQuery({
9968
+ queryKey: productVariantsQueryKeys.list(query2),
9969
+ queryFn: async () => await sdk.admin.productVariant.list(query2),
9970
+ ...options
9921
9971
  });
9922
- const handleSubmit = form.handleSubmit(async (data) => {
9923
- const parsedData = parseValues(data);
9924
- await mutateAsync(
9925
- {
9926
- metadata: parsedData
9972
+ return { ...data, ...rest };
9973
+ };
9974
+ const useCancelOrderEdit = ({ preview }) => {
9975
+ const { mutateAsync: cancelOrderEdit } = useDraftOrderCancelEdit(preview == null ? void 0 : preview.id);
9976
+ const onCancel = useCallback(async () => {
9977
+ if (!preview) {
9978
+ return true;
9979
+ }
9980
+ let res = false;
9981
+ await cancelOrderEdit(void 0, {
9982
+ onError: (e) => {
9983
+ toast.error(e.message);
9927
9984
  },
9928
- {
9929
- onSuccess: () => {
9930
- toast.success("Metadata updated");
9931
- handleSuccess();
9932
- },
9933
- onError: (error) => {
9934
- toast.error(error.message);
9935
- }
9985
+ onSuccess: () => {
9986
+ res = true;
9936
9987
  }
9937
- );
9938
- });
9939
- const { fields, insert, remove } = useFieldArray({
9940
- control: form.control,
9941
- name: "metadata"
9942
- });
9943
- function deleteRow(index) {
9944
- remove(index);
9945
- if (fields.length === 1) {
9946
- insert(0, {
9947
- key: "",
9948
- value: "",
9949
- disabled: false
9950
- });
9951
- }
9952
- }
9953
- function insertRow(index, position) {
9954
- insert(index + (position === "above" ? 0 : 1), {
9955
- key: "",
9956
- value: "",
9957
- disabled: false
9958
9988
  });
9959
- }
9960
- return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(
9961
- KeyboundForm,
9962
- {
9963
- onSubmit: handleSubmit,
9964
- className: "flex flex-1 flex-col overflow-hidden",
9965
- children: [
9966
- /* @__PURE__ */ jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
9967
- /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
9968
- /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
9969
- /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_KEY_LABEL_ID, children: "Key" }) }),
9970
- /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_VALUE_LABEL_ID, children: "Value" }) })
9971
- ] }),
9972
- fields.map((field, index) => {
9973
- const isDisabled = field.disabled || false;
9974
- let placeholder = "-";
9975
- if (typeof field.value === "object") {
9976
- placeholder = "{ ... }";
9977
- }
9978
- if (Array.isArray(field.value)) {
9979
- placeholder = "[ ... ]";
9980
- }
9981
- return /* @__PURE__ */ jsx(
9982
- ConditionalTooltip,
9983
- {
9984
- showTooltip: isDisabled,
9985
- content: "This row is disabled because it contains non-primitive data.",
9986
- children: /* @__PURE__ */ jsxs("div", { className: "group/table relative", children: [
9987
- /* @__PURE__ */ jsxs(
9988
- "div",
9989
- {
9990
- className: clx("grid grid-cols-2 divide-x", {
9991
- "overflow-hidden rounded-b-lg": index === fields.length - 1
9992
- }),
9993
- children: [
9994
- /* @__PURE__ */ jsx(
9995
- Form$2.Field,
9996
- {
9997
- control: form.control,
9998
- name: `metadata.${index}.key`,
9999
- render: ({ field: field2 }) => {
10000
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10001
- GridInput,
10002
- {
10003
- "aria-labelledby": METADATA_KEY_LABEL_ID,
10004
- ...field2,
10005
- disabled: isDisabled,
10006
- placeholder: "Key"
10007
- }
10008
- ) }) });
10009
- }
10010
- }
10011
- ),
10012
- /* @__PURE__ */ jsx(
10013
- Form$2.Field,
10014
- {
10015
- control: form.control,
10016
- name: `metadata.${index}.value`,
10017
- render: ({ field: { value, ...field2 } }) => {
10018
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10019
- GridInput,
10020
- {
10021
- "aria-labelledby": METADATA_VALUE_LABEL_ID,
10022
- ...field2,
10023
- value: isDisabled ? placeholder : value,
10024
- disabled: isDisabled,
10025
- placeholder: "Value"
10026
- }
10027
- ) }) });
10028
- }
10029
- }
10030
- )
10031
- ]
10032
- }
10033
- ),
10034
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
10035
- /* @__PURE__ */ jsx(
10036
- DropdownMenu.Trigger,
10037
- {
10038
- className: clx(
10039
- "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
10040
- {
10041
- hidden: isDisabled
10042
- }
10043
- ),
10044
- disabled: isDisabled,
10045
- asChild: true,
10046
- children: /* @__PURE__ */ jsx(IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsx(EllipsisVertical, {}) })
10047
- }
10048
- ),
10049
- /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [
10050
- /* @__PURE__ */ jsxs(
10051
- DropdownMenu.Item,
10052
- {
10053
- className: "gap-x-2",
10054
- onClick: () => insertRow(index, "above"),
10055
- children: [
10056
- /* @__PURE__ */ jsx(ArrowUpMini, { className: "text-ui-fg-subtle" }),
10057
- "Insert row above"
10058
- ]
10059
- }
10060
- ),
10061
- /* @__PURE__ */ jsxs(
10062
- DropdownMenu.Item,
10063
- {
10064
- className: "gap-x-2",
10065
- onClick: () => insertRow(index, "below"),
10066
- children: [
10067
- /* @__PURE__ */ jsx(ArrowDownMini, { className: "text-ui-fg-subtle" }),
10068
- "Insert row below"
10069
- ]
10070
- }
10071
- ),
10072
- /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
10073
- /* @__PURE__ */ jsxs(
10074
- DropdownMenu.Item,
10075
- {
10076
- className: "gap-x-2",
10077
- onClick: () => deleteRow(index),
10078
- children: [
10079
- /* @__PURE__ */ jsx(Trash, { className: "text-ui-fg-subtle" }),
10080
- "Delete row"
10081
- ]
10082
- }
10083
- )
10084
- ] })
10085
- ] })
10086
- ] })
10087
- },
10088
- field.id
10089
- );
10090
- })
10091
- ] }),
10092
- hasUneditableRows && /* @__PURE__ */ jsx(InlineTip, { variant: "warning", label: "Some rows are disabled", children: "This object contains non-primitive metadata, such as arrays or objects, that can't be edited here. To edit the disabled rows, use the API directly." })
10093
- ] }),
10094
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10095
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10096
- /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
10097
- ] }) })
10098
- ]
9989
+ return res;
9990
+ }, [preview, cancelOrderEdit]);
9991
+ return { onCancel };
9992
+ };
9993
+ let IS_REQUEST_RUNNING = false;
9994
+ const useInitiateOrderEdit = ({
9995
+ preview
9996
+ }) => {
9997
+ const navigate = useNavigate();
9998
+ const { mutateAsync } = useDraftOrderBeginEdit(preview == null ? void 0 : preview.id);
9999
+ useEffect(() => {
10000
+ async function run() {
10001
+ if (IS_REQUEST_RUNNING || !preview) {
10002
+ return;
10003
+ }
10004
+ if (preview.order_change) {
10005
+ return;
10006
+ }
10007
+ IS_REQUEST_RUNNING = true;
10008
+ await mutateAsync(void 0, {
10009
+ onError: (e) => {
10010
+ toast.error(e.message);
10011
+ navigate(`/draft-orders/${preview.id}`, { replace: true });
10012
+ return;
10013
+ }
10014
+ });
10015
+ IS_REQUEST_RUNNING = false;
10099
10016
  }
10100
- ) });
10017
+ run();
10018
+ }, [preview, navigate, mutateAsync]);
10101
10019
  };
10102
- const GridInput = forwardRef(({ className, ...props }, ref) => {
10103
- return /* @__PURE__ */ jsx(
10104
- "input",
10020
+ function convertNumber(value) {
10021
+ return typeof value === "string" ? Number(value.replace(",", ".")) : value;
10022
+ }
10023
+ const STACKED_MODAL_ID = "items_stacked_modal";
10024
+ const Items = () => {
10025
+ const { id } = useParams();
10026
+ const {
10027
+ order: preview,
10028
+ isPending: isPreviewPending,
10029
+ isError: isPreviewError,
10030
+ error: previewError
10031
+ } = useOrderPreview(id, void 0, {
10032
+ placeholderData: keepPreviousData
10033
+ });
10034
+ useInitiateOrderEdit({ preview });
10035
+ const { draft_order, isPending, isError, error } = useDraftOrder(
10036
+ id,
10105
10037
  {
10106
- ref,
10107
- ...props,
10108
- autoComplete: "off",
10109
- className: clx(
10110
- "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",
10111
- className
10112
- )
10038
+ fields: "currency_code"
10039
+ },
10040
+ {
10041
+ enabled: !!id
10113
10042
  }
10114
10043
  );
10115
- });
10116
- GridInput.displayName = "MetadataForm.GridInput";
10117
- const PlaceholderInner = () => {
10118
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
10119
- /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
10120
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10121
- /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" }),
10122
- /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" })
10123
- ] }) })
10124
- ] });
10125
- };
10126
- const EDITABLE_TYPES = ["string", "number", "boolean"];
10127
- function getDefaultValues(metadata) {
10128
- if (!metadata || !Object.keys(metadata).length) {
10129
- return [
10130
- {
10131
- key: "",
10132
- value: "",
10133
- disabled: false
10134
- }
10135
- ];
10044
+ const { onCancel } = useCancelOrderEdit({ preview });
10045
+ if (isError) {
10046
+ throw error;
10136
10047
  }
10137
- return Object.entries(metadata).map(([key, value]) => {
10138
- if (!EDITABLE_TYPES.includes(typeof value)) {
10139
- return {
10140
- key,
10141
- value,
10142
- disabled: true
10143
- };
10144
- }
10145
- let stringValue = value;
10146
- if (typeof value !== "string") {
10147
- stringValue = JSON.stringify(value);
10148
- }
10149
- return {
10150
- key,
10151
- value: stringValue,
10152
- original_key: key
10153
- };
10154
- });
10155
- }
10156
- function parseValues(values) {
10157
- const metadata = values.metadata;
10158
- const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
10159
- if (isEmpty) {
10160
- return null;
10161
- }
10162
- const update = {};
10163
- metadata.forEach((field) => {
10164
- let key = field.key;
10165
- let value = field.value;
10166
- const disabled = field.disabled;
10167
- if (!key || !value) {
10168
- return;
10169
- }
10170
- if (disabled) {
10171
- update[key] = value;
10172
- return;
10173
- }
10174
- key = key.trim();
10175
- value = value.trim();
10176
- if (value === "true") {
10177
- update[key] = true;
10178
- } else if (value === "false") {
10179
- update[key] = false;
10180
- } else {
10181
- const parsedNumber = parseFloat(value);
10182
- if (!isNaN(parsedNumber)) {
10183
- update[key] = parsedNumber;
10184
- } else {
10185
- update[key] = value;
10186
- }
10187
- }
10188
- });
10189
- return update;
10190
- }
10191
- function getHasUneditableRows(metadata) {
10192
- if (!metadata) {
10193
- return false;
10194
- }
10195
- return Object.values(metadata).some(
10196
- (value) => !EDITABLE_TYPES.includes(typeof value)
10197
- );
10198
- }
10199
- const NumberInput = forwardRef(
10200
- ({
10201
- value,
10202
- onChange,
10203
- size = "base",
10204
- min = 0,
10205
- max = 100,
10206
- step = 1,
10207
- className,
10208
- disabled,
10209
- ...props
10210
- }, ref) => {
10211
- const handleChange = (event) => {
10212
- const newValue = event.target.value === "" ? min : Number(event.target.value);
10213
- if (!isNaN(newValue) && (max === void 0 || newValue <= max) && (min === void 0 || newValue >= min)) {
10214
- onChange(newValue);
10215
- }
10216
- };
10217
- const handleIncrement = () => {
10218
- const newValue = value + step;
10219
- if (max === void 0 || newValue <= max) {
10220
- onChange(newValue);
10221
- }
10222
- };
10223
- const handleDecrement = () => {
10224
- const newValue = value - step;
10225
- if (min === void 0 || newValue >= min) {
10226
- onChange(newValue);
10227
- }
10228
- };
10229
- return /* @__PURE__ */ jsxs(
10230
- "div",
10231
- {
10232
- className: clx(
10233
- "inline-flex rounded-md bg-ui-bg-field shadow-borders-base overflow-hidden divide-x transition-fg",
10234
- "[&:has(input:focus)]:shadow-borders-interactive-with-active",
10235
- {
10236
- "h-7": size === "small",
10237
- "h-8": size === "base"
10238
- },
10239
- className
10240
- ),
10241
- children: [
10242
- /* @__PURE__ */ jsx(
10243
- "input",
10244
- {
10245
- ref,
10246
- type: "number",
10247
- value,
10248
- onChange: handleChange,
10249
- min,
10250
- max,
10251
- step,
10252
- className: clx(
10253
- "flex-1 px-2 py-1 bg-transparent txt-compact-small text-ui-fg-base outline-none [appearance:textfield]",
10254
- "[&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
10255
- "placeholder:text-ui-fg-muted"
10256
- ),
10257
- ...props
10258
- }
10259
- ),
10260
- /* @__PURE__ */ jsxs(
10261
- "button",
10262
- {
10263
- className: clx(
10264
- "flex items-center justify-center outline-none transition-fg",
10265
- "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
10266
- "focus:bg-ui-bg-field-component-hover",
10267
- "hover:bg-ui-bg-field-component-hover",
10268
- {
10269
- "size-7": size === "small",
10270
- "size-8": size === "base"
10271
- }
10272
- ),
10273
- type: "button",
10274
- onClick: handleDecrement,
10275
- disabled: min !== void 0 && value <= min || disabled,
10276
- children: [
10277
- /* @__PURE__ */ jsx(Minus, {}),
10278
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: `Decrease by ${step}` })
10279
- ]
10280
- }
10281
- ),
10282
- /* @__PURE__ */ jsxs(
10283
- "button",
10284
- {
10285
- className: clx(
10286
- "flex items-center justify-center outline-none transition-fg",
10287
- "disabled:cursor-not-allowed disabled:text-ui-fg-muted",
10288
- "focus:bg-ui-bg-field-hover",
10289
- "hover:bg-ui-bg-field-hover",
10290
- {
10291
- "size-7": size === "small",
10292
- "size-8": size === "base"
10293
- }
10294
- ),
10295
- type: "button",
10296
- onClick: handleIncrement,
10297
- disabled: max !== void 0 && value >= max || disabled,
10298
- children: [
10299
- /* @__PURE__ */ jsx(Plus, {}),
10300
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: `Increase by ${step}` })
10301
- ]
10302
- }
10303
- )
10304
- ]
10305
- }
10306
- );
10307
- }
10308
- );
10309
- const PRODUCT_VARIANTS_QUERY_KEY = "product-variants";
10310
- const productVariantsQueryKeys = {
10311
- list: (query2) => [
10312
- PRODUCT_VARIANTS_QUERY_KEY,
10313
- query2 ? query2 : void 0
10314
- ]
10315
- };
10316
- const useProductVariants = (query2, options) => {
10317
- const { data, ...rest } = useQuery({
10318
- queryKey: productVariantsQueryKeys.list(query2),
10319
- queryFn: async () => await sdk.admin.productVariant.list(query2),
10320
- ...options
10321
- });
10322
- return { ...data, ...rest };
10323
- };
10324
- const useCancelOrderEdit = ({ preview }) => {
10325
- const { mutateAsync: cancelOrderEdit } = useDraftOrderCancelEdit(preview == null ? void 0 : preview.id);
10326
- const onCancel = useCallback(async () => {
10327
- if (!preview) {
10328
- return true;
10329
- }
10330
- let res = false;
10331
- await cancelOrderEdit(void 0, {
10332
- onError: (e) => {
10333
- toast.error(e.message);
10334
- },
10335
- onSuccess: () => {
10336
- res = true;
10337
- }
10338
- });
10339
- return res;
10340
- }, [preview, cancelOrderEdit]);
10341
- return { onCancel };
10342
- };
10343
- let IS_REQUEST_RUNNING = false;
10344
- const useInitiateOrderEdit = ({
10345
- preview
10346
- }) => {
10347
- const navigate = useNavigate();
10348
- const { mutateAsync } = useDraftOrderBeginEdit(preview == null ? void 0 : preview.id);
10349
- useEffect(() => {
10350
- async function run() {
10351
- if (IS_REQUEST_RUNNING || !preview) {
10352
- return;
10353
- }
10354
- if (preview.order_change) {
10355
- return;
10356
- }
10357
- IS_REQUEST_RUNNING = true;
10358
- await mutateAsync(void 0, {
10359
- onError: (e) => {
10360
- toast.error(e.message);
10361
- navigate(`/draft-orders/${preview.id}`, { replace: true });
10362
- return;
10363
- }
10364
- });
10365
- IS_REQUEST_RUNNING = false;
10366
- }
10367
- run();
10368
- }, [preview, navigate, mutateAsync]);
10369
- };
10370
- function convertNumber(value) {
10371
- return typeof value === "string" ? Number(value.replace(",", ".")) : value;
10372
- }
10373
- const STACKED_MODAL_ID = "items_stacked_modal";
10374
- const Items = () => {
10375
- const { id } = useParams();
10376
- const {
10377
- order: preview,
10378
- isPending: isPreviewPending,
10379
- isError: isPreviewError,
10380
- error: previewError
10381
- } = useOrderPreview(id, void 0, {
10382
- placeholderData: keepPreviousData
10383
- });
10384
- useInitiateOrderEdit({ preview });
10385
- const { draft_order, isPending, isError, error } = useDraftOrder(
10386
- id,
10387
- {
10388
- fields: "currency_code"
10389
- },
10390
- {
10391
- enabled: !!id
10392
- }
10393
- );
10394
- const { onCancel } = useCancelOrderEdit({ preview });
10395
- if (isError) {
10396
- throw error;
10397
- }
10398
- if (isPreviewError) {
10399
- throw previewError;
10048
+ if (isPreviewError) {
10049
+ throw previewError;
10400
10050
  }
10401
10051
  const ready = !!preview && !isPreviewPending && !!draft_order && !isPending;
10402
10052
  return /* @__PURE__ */ jsx(RouteFocusModal, { onClose: onCancel, children: ready ? /* @__PURE__ */ jsx(ItemsForm, { preview, currencyCode: draft_order.currency_code }) : /* @__PURE__ */ jsxs("div", { children: [
@@ -10916,366 +10566,887 @@ const ExistingItemsForm = ({ orderId, items }) => {
10916
10566
  limit: LIMIT
10917
10567
  },
10918
10568
  {
10919
- placeholderData: keepPreviousData
10569
+ placeholderData: keepPreviousData
10570
+ }
10571
+ );
10572
+ const columns = useColumns();
10573
+ const { mutateAsync } = useDraftOrderAddItems(orderId);
10574
+ const onSubmit = async () => {
10575
+ const ids = Object.keys(rowSelection).filter(
10576
+ (id) => !items.find((i) => i.variant_id === id)
10577
+ );
10578
+ await mutateAsync(
10579
+ {
10580
+ items: ids.map((id) => ({
10581
+ variant_id: id,
10582
+ quantity: 1
10583
+ }))
10584
+ },
10585
+ {
10586
+ onSuccess: () => {
10587
+ setRowSelection({});
10588
+ setIsOpen(STACKED_MODAL_ID, false);
10589
+ },
10590
+ onError: (e) => {
10591
+ toast.error(e.message);
10592
+ }
10593
+ }
10594
+ );
10595
+ };
10596
+ if (isError) {
10597
+ throw error;
10598
+ }
10599
+ return /* @__PURE__ */ jsxs(
10600
+ StackedFocusModal.Content,
10601
+ {
10602
+ onOpenAutoFocus: (e) => {
10603
+ e.preventDefault();
10604
+ const searchInput = document.querySelector(
10605
+ "[data-modal-id='modal-search-input']"
10606
+ );
10607
+ if (searchInput) {
10608
+ searchInput.focus();
10609
+ }
10610
+ },
10611
+ children: [
10612
+ /* @__PURE__ */ jsxs(StackedFocusModal.Header, { children: [
10613
+ /* @__PURE__ */ jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Product Variants" }) }),
10614
+ /* @__PURE__ */ jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Choose product variants to add to the order." }) })
10615
+ ] }),
10616
+ /* @__PURE__ */ jsx(StackedFocusModal.Body, { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
10617
+ DataTable,
10618
+ {
10619
+ data: variants,
10620
+ columns,
10621
+ isLoading: isPending,
10622
+ getRowId: (row) => row.id,
10623
+ rowCount: count,
10624
+ prefix: VARIANT_PREFIX,
10625
+ layout: "fill",
10626
+ rowSelection: {
10627
+ state: rowSelection,
10628
+ onRowSelectionChange: setRowSelection,
10629
+ enableRowSelection: (row) => {
10630
+ return !items.find((i) => i.variant_id === row.original.id);
10631
+ }
10632
+ },
10633
+ autoFocusSearch: true
10634
+ }
10635
+ ) }),
10636
+ /* @__PURE__ */ jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10637
+ /* @__PURE__ */ jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10638
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "button", onClick: onSubmit, children: "Update items" })
10639
+ ] }) })
10640
+ ]
10641
+ }
10642
+ );
10643
+ };
10644
+ const columnHelper = createDataTableColumnHelper();
10645
+ const useColumns = () => {
10646
+ return useMemo(() => {
10647
+ return [
10648
+ columnHelper.select(),
10649
+ columnHelper.accessor("product.title", {
10650
+ header: "Product",
10651
+ cell: ({ row }) => {
10652
+ var _a, _b, _c;
10653
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
10654
+ /* @__PURE__ */ jsx(
10655
+ Thumbnail,
10656
+ {
10657
+ thumbnail: (_a = row.original.product) == null ? void 0 : _a.thumbnail,
10658
+ alt: (_b = row.original.product) == null ? void 0 : _b.title
10659
+ }
10660
+ ),
10661
+ /* @__PURE__ */ jsx("span", { children: (_c = row.original.product) == null ? void 0 : _c.title })
10662
+ ] });
10663
+ },
10664
+ enableSorting: true
10665
+ }),
10666
+ columnHelper.accessor("title", {
10667
+ header: "Variant",
10668
+ enableSorting: true
10669
+ }),
10670
+ columnHelper.accessor("sku", {
10671
+ header: "SKU",
10672
+ cell: ({ getValue }) => {
10673
+ return getValue() ?? "-";
10674
+ },
10675
+ enableSorting: true
10676
+ }),
10677
+ columnHelper.accessor("updated_at", {
10678
+ header: "Updated",
10679
+ cell: ({ getValue }) => {
10680
+ return /* @__PURE__ */ jsx(
10681
+ Tooltip,
10682
+ {
10683
+ content: getFullDate({ date: getValue(), includeTime: true }),
10684
+ children: /* @__PURE__ */ jsx("span", { children: getFullDate({ date: getValue() }) })
10685
+ }
10686
+ );
10687
+ },
10688
+ enableSorting: true,
10689
+ sortAscLabel: "Oldest first",
10690
+ sortDescLabel: "Newest first"
10691
+ }),
10692
+ columnHelper.accessor("created_at", {
10693
+ header: "Created",
10694
+ cell: ({ getValue }) => {
10695
+ return /* @__PURE__ */ jsx(
10696
+ Tooltip,
10697
+ {
10698
+ content: getFullDate({ date: getValue(), includeTime: true }),
10699
+ children: /* @__PURE__ */ jsx("span", { children: getFullDate({ date: getValue() }) })
10700
+ }
10701
+ );
10702
+ },
10703
+ enableSorting: true,
10704
+ sortAscLabel: "Oldest first",
10705
+ sortDescLabel: "Newest first"
10706
+ })
10707
+ ];
10708
+ }, []);
10709
+ };
10710
+ const CustomItemForm = ({ orderId, currencyCode }) => {
10711
+ const { setIsOpen } = useStackedModal();
10712
+ const { mutateAsync: addItems } = useDraftOrderAddItems(orderId);
10713
+ const form = useForm({
10714
+ defaultValues: {
10715
+ title: "",
10716
+ quantity: 1,
10717
+ unit_price: ""
10718
+ },
10719
+ resolver: zodResolver(customItemSchema)
10720
+ });
10721
+ const onSubmit = form.handleSubmit(async (data) => {
10722
+ await addItems(
10723
+ {
10724
+ items: [
10725
+ {
10726
+ title: data.title,
10727
+ quantity: data.quantity,
10728
+ unit_price: convertNumber(data.unit_price)
10729
+ }
10730
+ ]
10731
+ },
10732
+ {
10733
+ onSuccess: () => {
10734
+ setIsOpen(STACKED_MODAL_ID, false);
10735
+ },
10736
+ onError: (e) => {
10737
+ toast.error(e.message);
10738
+ }
10739
+ }
10740
+ );
10741
+ });
10742
+ return /* @__PURE__ */ jsx(Form$2, { ...form, children: /* @__PURE__ */ jsx(KeyboundForm, { onSubmit, children: /* @__PURE__ */ jsxs(StackedFocusModal.Content, { children: [
10743
+ /* @__PURE__ */ jsx(StackedFocusModal.Header, {}),
10744
+ /* @__PURE__ */ jsx(StackedFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col items-center overflow-y-auto", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full max-w-[720px] flex-col gap-y-6 px-2 py-16", children: [
10745
+ /* @__PURE__ */ jsxs("div", { children: [
10746
+ /* @__PURE__ */ jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Add custom item" }) }),
10747
+ /* @__PURE__ */ jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Add a custom item to the order. This will add a new line item that is not associated with an existing product." }) })
10748
+ ] }),
10749
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10750
+ /* @__PURE__ */ jsx(
10751
+ Form$2.Field,
10752
+ {
10753
+ control: form.control,
10754
+ name: "title",
10755
+ render: ({ field }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
10756
+ /* @__PURE__ */ jsxs("div", { children: [
10757
+ /* @__PURE__ */ jsx(Form$2.Label, { children: "Title" }),
10758
+ /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the title of the item" })
10759
+ ] }),
10760
+ /* @__PURE__ */ jsxs("div", { children: [
10761
+ /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(Input, { ...field }) }),
10762
+ /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
10763
+ ] })
10764
+ ] }) })
10765
+ }
10766
+ ),
10767
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10768
+ /* @__PURE__ */ jsx(
10769
+ Form$2.Field,
10770
+ {
10771
+ control: form.control,
10772
+ name: "unit_price",
10773
+ render: ({ field: { onChange, ...field } }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
10774
+ /* @__PURE__ */ jsxs("div", { children: [
10775
+ /* @__PURE__ */ jsx(Form$2.Label, { children: "Unit price" }),
10776
+ /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the unit price of the item" })
10777
+ ] }),
10778
+ /* @__PURE__ */ jsxs("div", { children: [
10779
+ /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10780
+ CurrencyInput,
10781
+ {
10782
+ symbol: getNativeSymbol(currencyCode),
10783
+ code: currencyCode,
10784
+ onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value),
10785
+ ...field
10786
+ }
10787
+ ) }),
10788
+ /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
10789
+ ] })
10790
+ ] }) })
10791
+ }
10792
+ ),
10793
+ /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
10794
+ /* @__PURE__ */ jsx(
10795
+ Form$2.Field,
10796
+ {
10797
+ control: form.control,
10798
+ name: "quantity",
10799
+ render: ({ field }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
10800
+ /* @__PURE__ */ jsxs("div", { children: [
10801
+ /* @__PURE__ */ jsx(Form$2.Label, { children: "Quantity" }),
10802
+ /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the quantity of the item" })
10803
+ ] }),
10804
+ /* @__PURE__ */ jsxs("div", { className: "w-full flex-1", children: [
10805
+ /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(NumberInput, { ...field, className: "w-full" }) }) }),
10806
+ /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
10807
+ ] })
10808
+ ] }) })
10809
+ }
10810
+ )
10811
+ ] }) }) }),
10812
+ /* @__PURE__ */ jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10813
+ /* @__PURE__ */ jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10814
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "button", onClick: onSubmit, children: "Add item" })
10815
+ ] }) })
10816
+ ] }) }) });
10817
+ };
10818
+ const customItemSchema = objectType({
10819
+ title: stringType().min(1),
10820
+ quantity: numberType(),
10821
+ unit_price: unionType([numberType(), stringType()])
10822
+ });
10823
+ const InlineTip = forwardRef(
10824
+ ({ variant = "tip", label, className, children, ...props }, ref) => {
10825
+ const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
10826
+ return /* @__PURE__ */ jsxs(
10827
+ "div",
10828
+ {
10829
+ ref,
10830
+ className: clx(
10831
+ "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
10832
+ className
10833
+ ),
10834
+ ...props,
10835
+ children: [
10836
+ /* @__PURE__ */ jsx(
10837
+ "div",
10838
+ {
10839
+ role: "presentation",
10840
+ className: clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
10841
+ "bg-ui-tag-orange-icon": variant === "warning"
10842
+ })
10843
+ }
10844
+ ),
10845
+ /* @__PURE__ */ jsxs("div", { className: "text-pretty", children: [
10846
+ /* @__PURE__ */ jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
10847
+ labelValue,
10848
+ ":"
10849
+ ] }),
10850
+ " ",
10851
+ children
10852
+ ] })
10853
+ ]
10854
+ }
10855
+ );
10856
+ }
10857
+ );
10858
+ InlineTip.displayName = "InlineTip";
10859
+ const MetadataFieldSchema = objectType({
10860
+ key: stringType(),
10861
+ disabled: booleanType().optional(),
10862
+ value: anyType()
10863
+ });
10864
+ const MetadataSchema = objectType({
10865
+ metadata: arrayType(MetadataFieldSchema)
10866
+ });
10867
+ const Metadata = () => {
10868
+ const { id } = useParams();
10869
+ const { order, isPending, isError, error } = useOrder(id, {
10870
+ fields: "metadata"
10871
+ });
10872
+ if (isError) {
10873
+ throw error;
10874
+ }
10875
+ const isReady = !isPending && !!order;
10876
+ return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
10877
+ /* @__PURE__ */ jsxs(RouteDrawer.Header, { children: [
10878
+ /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Metadata" }) }),
10879
+ /* @__PURE__ */ jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
10880
+ ] }),
10881
+ !isReady ? /* @__PURE__ */ jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
10882
+ ] });
10883
+ };
10884
+ const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
10885
+ const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
10886
+ const MetadataForm = ({ orderId, metadata }) => {
10887
+ const { handleSuccess } = useRouteModal();
10888
+ const hasUneditableRows = getHasUneditableRows(metadata);
10889
+ const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
10890
+ const form = useForm({
10891
+ defaultValues: {
10892
+ metadata: getDefaultValues(metadata)
10893
+ },
10894
+ resolver: zodResolver(MetadataSchema)
10895
+ });
10896
+ const handleSubmit = form.handleSubmit(async (data) => {
10897
+ const parsedData = parseValues(data);
10898
+ await mutateAsync(
10899
+ {
10900
+ metadata: parsedData
10901
+ },
10902
+ {
10903
+ onSuccess: () => {
10904
+ toast.success("Metadata updated");
10905
+ handleSuccess();
10906
+ },
10907
+ onError: (error) => {
10908
+ toast.error(error.message);
10909
+ }
10910
+ }
10911
+ );
10912
+ });
10913
+ const { fields, insert, remove } = useFieldArray({
10914
+ control: form.control,
10915
+ name: "metadata"
10916
+ });
10917
+ function deleteRow(index) {
10918
+ remove(index);
10919
+ if (fields.length === 1) {
10920
+ insert(0, {
10921
+ key: "",
10922
+ value: "",
10923
+ disabled: false
10924
+ });
10925
+ }
10926
+ }
10927
+ function insertRow(index, position) {
10928
+ insert(index + (position === "above" ? 0 : 1), {
10929
+ key: "",
10930
+ value: "",
10931
+ disabled: false
10932
+ });
10933
+ }
10934
+ return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(
10935
+ KeyboundForm,
10936
+ {
10937
+ onSubmit: handleSubmit,
10938
+ className: "flex flex-1 flex-col overflow-hidden",
10939
+ children: [
10940
+ /* @__PURE__ */ jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
10941
+ /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
10942
+ /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
10943
+ /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_KEY_LABEL_ID, children: "Key" }) }),
10944
+ /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_VALUE_LABEL_ID, children: "Value" }) })
10945
+ ] }),
10946
+ fields.map((field, index) => {
10947
+ const isDisabled = field.disabled || false;
10948
+ let placeholder = "-";
10949
+ if (typeof field.value === "object") {
10950
+ placeholder = "{ ... }";
10951
+ }
10952
+ if (Array.isArray(field.value)) {
10953
+ placeholder = "[ ... ]";
10954
+ }
10955
+ return /* @__PURE__ */ jsx(
10956
+ ConditionalTooltip,
10957
+ {
10958
+ showTooltip: isDisabled,
10959
+ content: "This row is disabled because it contains non-primitive data.",
10960
+ children: /* @__PURE__ */ jsxs("div", { className: "group/table relative", children: [
10961
+ /* @__PURE__ */ jsxs(
10962
+ "div",
10963
+ {
10964
+ className: clx("grid grid-cols-2 divide-x", {
10965
+ "overflow-hidden rounded-b-lg": index === fields.length - 1
10966
+ }),
10967
+ children: [
10968
+ /* @__PURE__ */ jsx(
10969
+ Form$2.Field,
10970
+ {
10971
+ control: form.control,
10972
+ name: `metadata.${index}.key`,
10973
+ render: ({ field: field2 }) => {
10974
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10975
+ GridInput,
10976
+ {
10977
+ "aria-labelledby": METADATA_KEY_LABEL_ID,
10978
+ ...field2,
10979
+ disabled: isDisabled,
10980
+ placeholder: "Key"
10981
+ }
10982
+ ) }) });
10983
+ }
10984
+ }
10985
+ ),
10986
+ /* @__PURE__ */ jsx(
10987
+ Form$2.Field,
10988
+ {
10989
+ control: form.control,
10990
+ name: `metadata.${index}.value`,
10991
+ render: ({ field: { value, ...field2 } }) => {
10992
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10993
+ GridInput,
10994
+ {
10995
+ "aria-labelledby": METADATA_VALUE_LABEL_ID,
10996
+ ...field2,
10997
+ value: isDisabled ? placeholder : value,
10998
+ disabled: isDisabled,
10999
+ placeholder: "Value"
11000
+ }
11001
+ ) }) });
11002
+ }
11003
+ }
11004
+ )
11005
+ ]
11006
+ }
11007
+ ),
11008
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
11009
+ /* @__PURE__ */ jsx(
11010
+ DropdownMenu.Trigger,
11011
+ {
11012
+ className: clx(
11013
+ "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
11014
+ {
11015
+ hidden: isDisabled
11016
+ }
11017
+ ),
11018
+ disabled: isDisabled,
11019
+ asChild: true,
11020
+ children: /* @__PURE__ */ jsx(IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsx(EllipsisVertical, {}) })
11021
+ }
11022
+ ),
11023
+ /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [
11024
+ /* @__PURE__ */ jsxs(
11025
+ DropdownMenu.Item,
11026
+ {
11027
+ className: "gap-x-2",
11028
+ onClick: () => insertRow(index, "above"),
11029
+ children: [
11030
+ /* @__PURE__ */ jsx(ArrowUpMini, { className: "text-ui-fg-subtle" }),
11031
+ "Insert row above"
11032
+ ]
11033
+ }
11034
+ ),
11035
+ /* @__PURE__ */ jsxs(
11036
+ DropdownMenu.Item,
11037
+ {
11038
+ className: "gap-x-2",
11039
+ onClick: () => insertRow(index, "below"),
11040
+ children: [
11041
+ /* @__PURE__ */ jsx(ArrowDownMini, { className: "text-ui-fg-subtle" }),
11042
+ "Insert row below"
11043
+ ]
11044
+ }
11045
+ ),
11046
+ /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
11047
+ /* @__PURE__ */ jsxs(
11048
+ DropdownMenu.Item,
11049
+ {
11050
+ className: "gap-x-2",
11051
+ onClick: () => deleteRow(index),
11052
+ children: [
11053
+ /* @__PURE__ */ jsx(Trash, { className: "text-ui-fg-subtle" }),
11054
+ "Delete row"
11055
+ ]
11056
+ }
11057
+ )
11058
+ ] })
11059
+ ] })
11060
+ ] })
11061
+ },
11062
+ field.id
11063
+ );
11064
+ })
11065
+ ] }),
11066
+ hasUneditableRows && /* @__PURE__ */ jsx(InlineTip, { variant: "warning", label: "Some rows are disabled", children: "This object contains non-primitive metadata, such as arrays or objects, that can't be edited here. To edit the disabled rows, use the API directly." })
11067
+ ] }),
11068
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11069
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
11070
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
11071
+ ] }) })
11072
+ ]
11073
+ }
11074
+ ) });
11075
+ };
11076
+ const GridInput = forwardRef(({ className, ...props }, ref) => {
11077
+ return /* @__PURE__ */ jsx(
11078
+ "input",
11079
+ {
11080
+ ref,
11081
+ ...props,
11082
+ autoComplete: "off",
11083
+ className: clx(
11084
+ "txt-compact-small text-ui-fg-base placeholder:text-ui-fg-muted disabled:text-ui-fg-disabled disabled:bg-ui-bg-base bg-transparent px-2 py-1.5 outline-none",
11085
+ className
11086
+ )
10920
11087
  }
10921
11088
  );
10922
- const columns = useColumns();
10923
- const { mutateAsync } = useDraftOrderAddItems(orderId);
10924
- const onSubmit = async () => {
10925
- const ids = Object.keys(rowSelection).filter(
10926
- (id) => !items.find((i) => i.variant_id === id)
10927
- );
10928
- await mutateAsync(
10929
- {
10930
- items: ids.map((id) => ({
10931
- variant_id: id,
10932
- quantity: 1
10933
- }))
10934
- },
11089
+ });
11090
+ GridInput.displayName = "MetadataForm.GridInput";
11091
+ const PlaceholderInner = () => {
11092
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
11093
+ /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
11094
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11095
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" }),
11096
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" })
11097
+ ] }) })
11098
+ ] });
11099
+ };
11100
+ const EDITABLE_TYPES = ["string", "number", "boolean"];
11101
+ function getDefaultValues(metadata) {
11102
+ if (!metadata || !Object.keys(metadata).length) {
11103
+ return [
10935
11104
  {
10936
- onSuccess: () => {
10937
- setRowSelection({});
10938
- setIsOpen(STACKED_MODAL_ID, false);
10939
- },
10940
- onError: (e) => {
10941
- toast.error(e.message);
10942
- }
11105
+ key: "",
11106
+ value: "",
11107
+ disabled: false
10943
11108
  }
10944
- );
10945
- };
10946
- if (isError) {
10947
- throw error;
11109
+ ];
10948
11110
  }
10949
- return /* @__PURE__ */ jsxs(
10950
- StackedFocusModal.Content,
10951
- {
10952
- onOpenAutoFocus: (e) => {
10953
- e.preventDefault();
10954
- const searchInput = document.querySelector(
10955
- "[data-modal-id='modal-search-input']"
10956
- );
10957
- if (searchInput) {
10958
- searchInput.focus();
10959
- }
10960
- },
10961
- children: [
10962
- /* @__PURE__ */ jsxs(StackedFocusModal.Header, { children: [
10963
- /* @__PURE__ */ jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Product Variants" }) }),
10964
- /* @__PURE__ */ jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Choose product variants to add to the order." }) })
10965
- ] }),
10966
- /* @__PURE__ */ jsx(StackedFocusModal.Body, { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
10967
- DataTable,
10968
- {
10969
- data: variants,
10970
- columns,
10971
- isLoading: isPending,
10972
- getRowId: (row) => row.id,
10973
- rowCount: count,
10974
- prefix: VARIANT_PREFIX,
10975
- layout: "fill",
10976
- rowSelection: {
10977
- state: rowSelection,
10978
- onRowSelectionChange: setRowSelection,
10979
- enableRowSelection: (row) => {
10980
- return !items.find((i) => i.variant_id === row.original.id);
10981
- }
10982
- },
10983
- autoFocusSearch: true
10984
- }
10985
- ) }),
10986
- /* @__PURE__ */ jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
10987
- /* @__PURE__ */ jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
10988
- /* @__PURE__ */ jsx(Button, { size: "small", type: "button", onClick: onSubmit, children: "Update items" })
10989
- ] }) })
10990
- ]
11111
+ return Object.entries(metadata).map(([key, value]) => {
11112
+ if (!EDITABLE_TYPES.includes(typeof value)) {
11113
+ return {
11114
+ key,
11115
+ value,
11116
+ disabled: true
11117
+ };
11118
+ }
11119
+ let stringValue = value;
11120
+ if (typeof value !== "string") {
11121
+ stringValue = JSON.stringify(value);
11122
+ }
11123
+ return {
11124
+ key,
11125
+ value: stringValue,
11126
+ original_key: key
11127
+ };
11128
+ });
11129
+ }
11130
+ function parseValues(values) {
11131
+ const metadata = values.metadata;
11132
+ const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
11133
+ if (isEmpty) {
11134
+ return null;
11135
+ }
11136
+ const update = {};
11137
+ metadata.forEach((field) => {
11138
+ let key = field.key;
11139
+ let value = field.value;
11140
+ const disabled = field.disabled;
11141
+ if (!key || !value) {
11142
+ return;
11143
+ }
11144
+ if (disabled) {
11145
+ update[key] = value;
11146
+ return;
11147
+ }
11148
+ key = key.trim();
11149
+ value = value.trim();
11150
+ if (value === "true") {
11151
+ update[key] = true;
11152
+ } else if (value === "false") {
11153
+ update[key] = false;
11154
+ } else {
11155
+ const parsedNumber = parseFloat(value);
11156
+ if (!isNaN(parsedNumber)) {
11157
+ update[key] = parsedNumber;
11158
+ } else {
11159
+ update[key] = value;
11160
+ }
10991
11161
  }
11162
+ });
11163
+ return update;
11164
+ }
11165
+ function getHasUneditableRows(metadata) {
11166
+ if (!metadata) {
11167
+ return false;
11168
+ }
11169
+ return Object.values(metadata).some(
11170
+ (value) => !EDITABLE_TYPES.includes(typeof value)
10992
11171
  );
11172
+ }
11173
+ const PROMOTION_QUERY_KEY = "promotions";
11174
+ const promotionsQueryKeys = {
11175
+ list: (query2) => [
11176
+ PROMOTION_QUERY_KEY,
11177
+ query2 ? query2 : void 0
11178
+ ],
11179
+ detail: (id, query2) => [
11180
+ PROMOTION_QUERY_KEY,
11181
+ id,
11182
+ query2 ? query2 : void 0
11183
+ ]
10993
11184
  };
10994
- const columnHelper = createDataTableColumnHelper();
10995
- const useColumns = () => {
10996
- return useMemo(() => {
10997
- return [
10998
- columnHelper.select(),
10999
- columnHelper.accessor("product.title", {
11000
- header: "Product",
11001
- cell: ({ row }) => {
11002
- var _a, _b, _c;
11003
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
11004
- /* @__PURE__ */ jsx(
11005
- Thumbnail,
11006
- {
11007
- thumbnail: (_a = row.original.product) == null ? void 0 : _a.thumbnail,
11008
- alt: (_b = row.original.product) == null ? void 0 : _b.title
11009
- }
11010
- ),
11011
- /* @__PURE__ */ jsx("span", { children: (_c = row.original.product) == null ? void 0 : _c.title })
11012
- ] });
11013
- },
11014
- enableSorting: true
11015
- }),
11016
- columnHelper.accessor("title", {
11017
- header: "Variant",
11018
- enableSorting: true
11019
- }),
11020
- columnHelper.accessor("sku", {
11021
- header: "SKU",
11022
- cell: ({ getValue }) => {
11023
- return getValue() ?? "-";
11024
- },
11025
- enableSorting: true
11026
- }),
11027
- columnHelper.accessor("updated_at", {
11028
- header: "Updated",
11029
- cell: ({ getValue }) => {
11030
- return /* @__PURE__ */ jsx(
11031
- Tooltip,
11032
- {
11033
- content: getFullDate({ date: getValue(), includeTime: true }),
11034
- children: /* @__PURE__ */ jsx("span", { children: getFullDate({ date: getValue() }) })
11035
- }
11036
- );
11037
- },
11038
- enableSorting: true,
11039
- sortAscLabel: "Oldest first",
11040
- sortDescLabel: "Newest first"
11041
- }),
11042
- columnHelper.accessor("created_at", {
11043
- header: "Created",
11044
- cell: ({ getValue }) => {
11045
- return /* @__PURE__ */ jsx(
11046
- Tooltip,
11047
- {
11048
- content: getFullDate({ date: getValue(), includeTime: true }),
11049
- children: /* @__PURE__ */ jsx("span", { children: getFullDate({ date: getValue() }) })
11050
- }
11051
- );
11052
- },
11053
- enableSorting: true,
11054
- sortAscLabel: "Oldest first",
11055
- sortDescLabel: "Newest first"
11056
- })
11057
- ];
11058
- }, []);
11185
+ const usePromotions = (query2, options) => {
11186
+ const { data, ...rest } = useQuery({
11187
+ queryKey: promotionsQueryKeys.list(query2),
11188
+ queryFn: async () => sdk.admin.promotion.list(query2),
11189
+ ...options
11190
+ });
11191
+ return { ...data, ...rest };
11192
+ };
11193
+ const Promotions = () => {
11194
+ const { id } = useParams();
11195
+ const {
11196
+ order: preview,
11197
+ isError: isPreviewError,
11198
+ error: previewError
11199
+ } = useOrderPreview(id, void 0);
11200
+ useInitiateOrderEdit({ preview });
11201
+ const { onCancel } = useCancelOrderEdit({ preview });
11202
+ if (isPreviewError) {
11203
+ throw previewError;
11204
+ }
11205
+ const isReady = !!preview;
11206
+ return /* @__PURE__ */ jsxs(RouteDrawer, { onClose: onCancel, children: [
11207
+ /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Promotions" }) }) }),
11208
+ isReady && /* @__PURE__ */ jsx(PromotionForm, { preview })
11209
+ ] });
11059
11210
  };
11060
- const CustomItemForm = ({ orderId, currencyCode }) => {
11061
- const { setIsOpen } = useStackedModal();
11062
- const { mutateAsync: addItems } = useDraftOrderAddItems(orderId);
11063
- const form = useForm({
11064
- defaultValues: {
11065
- title: "",
11066
- quantity: 1,
11067
- unit_price: ""
11211
+ const PromotionForm = ({ preview }) => {
11212
+ const { items, shipping_methods } = preview;
11213
+ const [isSubmitting, setIsSubmitting] = useState(false);
11214
+ const [comboboxValue, setComboboxValue] = useState("");
11215
+ const { handleSuccess } = useRouteModal();
11216
+ const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
11217
+ const promoIds = getPromotionIds(items, shipping_methods);
11218
+ const { promotions, isPending, isError, error } = usePromotions(
11219
+ {
11220
+ id: promoIds
11068
11221
  },
11069
- resolver: zodResolver(customItemSchema)
11222
+ {
11223
+ enabled: !!promoIds.length
11224
+ }
11225
+ );
11226
+ const comboboxData = useComboboxData({
11227
+ queryKey: ["promotions", "combobox", promoIds],
11228
+ queryFn: async (params) => {
11229
+ return await sdk.admin.promotion.list({
11230
+ ...params,
11231
+ id: {
11232
+ $nin: promoIds
11233
+ }
11234
+ });
11235
+ },
11236
+ getOptions: (data) => {
11237
+ return data.promotions.map((promotion) => ({
11238
+ label: promotion.code,
11239
+ value: promotion.code
11240
+ }));
11241
+ }
11070
11242
  });
11071
- const onSubmit = form.handleSubmit(async (data) => {
11072
- await addItems(
11243
+ const add = async (value) => {
11244
+ if (!value) {
11245
+ return;
11246
+ }
11247
+ addPromotions(
11073
11248
  {
11074
- items: [
11075
- {
11076
- title: data.title,
11077
- quantity: data.quantity,
11078
- unit_price: convertNumber(data.unit_price)
11079
- }
11080
- ]
11249
+ promo_codes: [value]
11081
11250
  },
11082
11251
  {
11083
- onSuccess: () => {
11084
- setIsOpen(STACKED_MODAL_ID, false);
11085
- },
11086
11252
  onError: (e) => {
11087
11253
  toast.error(e.message);
11254
+ comboboxData.onSearchValueChange("");
11255
+ setComboboxValue("");
11256
+ },
11257
+ onSuccess: () => {
11258
+ comboboxData.onSearchValueChange("");
11259
+ setComboboxValue("");
11088
11260
  }
11089
11261
  }
11090
11262
  );
11091
- });
11092
- return /* @__PURE__ */ jsx(Form$2, { ...form, children: /* @__PURE__ */ jsx(KeyboundForm, { onSubmit, children: /* @__PURE__ */ jsxs(StackedFocusModal.Content, { children: [
11093
- /* @__PURE__ */ jsx(StackedFocusModal.Header, {}),
11094
- /* @__PURE__ */ jsx(StackedFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col items-center overflow-y-auto", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full max-w-[720px] flex-col gap-y-6 px-2 py-16", children: [
11095
- /* @__PURE__ */ jsxs("div", { children: [
11096
- /* @__PURE__ */ jsx(StackedFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Add custom item" }) }),
11097
- /* @__PURE__ */ jsx(StackedFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Add a custom item to the order. This will add a new line item that is not associated with an existing product." }) })
11263
+ };
11264
+ const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
11265
+ const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
11266
+ const onSubmit = async () => {
11267
+ setIsSubmitting(true);
11268
+ let requestSucceeded = false;
11269
+ await requestOrderEdit(void 0, {
11270
+ onError: (e) => {
11271
+ toast.error(e.message);
11272
+ },
11273
+ onSuccess: () => {
11274
+ requestSucceeded = true;
11275
+ }
11276
+ });
11277
+ if (!requestSucceeded) {
11278
+ setIsSubmitting(false);
11279
+ return;
11280
+ }
11281
+ await confirmOrderEdit(void 0, {
11282
+ onError: (e) => {
11283
+ toast.error(e.message);
11284
+ },
11285
+ onSuccess: () => {
11286
+ handleSuccess();
11287
+ },
11288
+ onSettled: () => {
11289
+ setIsSubmitting(false);
11290
+ }
11291
+ });
11292
+ };
11293
+ if (isError) {
11294
+ throw error;
11295
+ }
11296
+ return /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
11297
+ /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
11298
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
11299
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
11300
+ /* @__PURE__ */ jsx(Label$1, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
11301
+ /* @__PURE__ */ jsx(Hint$1, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
11302
+ ] }),
11303
+ /* @__PURE__ */ jsx(
11304
+ Combobox,
11305
+ {
11306
+ id: "promotion-combobox",
11307
+ "aria-describedby": "promotion-combobox-hint",
11308
+ isFetchingNextPage: comboboxData.isFetchingNextPage,
11309
+ fetchNextPage: comboboxData.fetchNextPage,
11310
+ options: comboboxData.options,
11311
+ onSearchValueChange: comboboxData.onSearchValueChange,
11312
+ searchValue: comboboxData.searchValue,
11313
+ disabled: comboboxData.disabled || isAddingPromotions,
11314
+ onChange: add,
11315
+ value: comboboxValue
11316
+ }
11317
+ )
11098
11318
  ] }),
11099
11319
  /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
11100
- /* @__PURE__ */ jsx(
11101
- Form$2.Field,
11102
- {
11103
- control: form.control,
11104
- name: "title",
11105
- render: ({ field }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
11106
- /* @__PURE__ */ jsxs("div", { children: [
11107
- /* @__PURE__ */ jsx(Form$2.Label, { children: "Title" }),
11108
- /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the title of the item" })
11109
- ] }),
11110
- /* @__PURE__ */ jsxs("div", { children: [
11111
- /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(Input, { ...field }) }),
11112
- /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
11113
- ] })
11114
- ] }) })
11115
- }
11116
- ),
11117
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
11118
- /* @__PURE__ */ jsx(
11119
- Form$2.Field,
11320
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: promotions == null ? void 0 : promotions.map((promotion) => /* @__PURE__ */ jsx(
11321
+ PromotionItem,
11120
11322
  {
11121
- control: form.control,
11122
- name: "unit_price",
11123
- render: ({ field: { onChange, ...field } }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
11124
- /* @__PURE__ */ jsxs("div", { children: [
11125
- /* @__PURE__ */ jsx(Form$2.Label, { children: "Unit price" }),
11126
- /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the unit price of the item" })
11127
- ] }),
11128
- /* @__PURE__ */ jsxs("div", { children: [
11129
- /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
11130
- CurrencyInput,
11131
- {
11132
- symbol: getNativeSymbol(currencyCode),
11133
- code: currencyCode,
11134
- onValueChange: (_value, _name, values) => onChange(values == null ? void 0 : values.value),
11135
- ...field
11136
- }
11137
- ) }),
11138
- /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
11139
- ] })
11140
- ] }) })
11141
- }
11142
- ),
11143
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
11323
+ promotion,
11324
+ orderId: preview.id,
11325
+ isLoading: isPending
11326
+ },
11327
+ promotion.id
11328
+ )) })
11329
+ ] }) }),
11330
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
11331
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
11144
11332
  /* @__PURE__ */ jsx(
11145
- Form$2.Field,
11333
+ Button,
11146
11334
  {
11147
- control: form.control,
11148
- name: "quantity",
11149
- render: ({ field }) => /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-3", children: [
11150
- /* @__PURE__ */ jsxs("div", { children: [
11151
- /* @__PURE__ */ jsx(Form$2.Label, { children: "Quantity" }),
11152
- /* @__PURE__ */ jsx(Form$2.Hint, { children: "Enter the quantity of the item" })
11153
- ] }),
11154
- /* @__PURE__ */ jsxs("div", { className: "w-full flex-1", children: [
11155
- /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx("div", { className: "w-full flex-1", children: /* @__PURE__ */ jsx(NumberInput, { ...field, className: "w-full" }) }) }),
11156
- /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
11157
- ] })
11158
- ] }) })
11335
+ size: "small",
11336
+ type: "submit",
11337
+ isLoading: isSubmitting || isAddingPromotions,
11338
+ children: "Save"
11159
11339
  }
11160
11340
  )
11161
- ] }) }) }),
11162
- /* @__PURE__ */ jsx(StackedFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11163
- /* @__PURE__ */ jsx(StackedFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
11164
- /* @__PURE__ */ jsx(Button, { size: "small", type: "button", onClick: onSubmit, children: "Add item" })
11165
11341
  ] }) })
11166
- ] }) }) });
11167
- };
11168
- const customItemSchema = objectType({
11169
- title: stringType().min(1),
11170
- quantity: numberType(),
11171
- unit_price: unionType([numberType(), stringType()])
11172
- });
11173
- const SalesChannel = () => {
11174
- const { id } = useParams();
11175
- const { draft_order, isPending, isError, error } = useDraftOrder(
11176
- id,
11177
- {
11178
- fields: "+sales_channel_id"
11179
- },
11180
- {
11181
- enabled: !!id
11182
- }
11183
- );
11184
- if (isError) {
11185
- throw error;
11186
- }
11187
- const ISrEADY = !!draft_order && !isPending;
11188
- return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
11189
- /* @__PURE__ */ jsxs(RouteDrawer.Header, { children: [
11190
- /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Sales Channel" }) }),
11191
- /* @__PURE__ */ jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Update which sales channel the draft order is associated with" }) })
11192
- ] }),
11193
- ISrEADY && /* @__PURE__ */ jsx(SalesChannelForm, { order: draft_order })
11194
11342
  ] });
11195
11343
  };
11196
- const SalesChannelForm = ({ order }) => {
11197
- const form = useForm({
11198
- defaultValues: {
11199
- sales_channel_id: order.sales_channel_id || ""
11200
- },
11201
- resolver: zodResolver(schema$2)
11202
- });
11203
- const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
11204
- const { handleSuccess } = useRouteModal();
11205
- const onSubmit = form.handleSubmit(async (data) => {
11206
- await mutateAsync(
11344
+ const PromotionItem = ({
11345
+ promotion,
11346
+ orderId,
11347
+ isLoading
11348
+ }) => {
11349
+ var _a;
11350
+ const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
11351
+ const onRemove = async () => {
11352
+ removePromotions(
11207
11353
  {
11208
- sales_channel_id: data.sales_channel_id
11354
+ promo_codes: [promotion.code]
11209
11355
  },
11210
11356
  {
11211
- onSuccess: () => {
11212
- toast.success("Sales channel updated");
11213
- handleSuccess();
11214
- },
11215
- onError: (error) => {
11216
- toast.error(error.message);
11357
+ onError: (e) => {
11358
+ toast.error(e.message);
11217
11359
  }
11218
11360
  }
11219
11361
  );
11220
- });
11221
- return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(
11222
- KeyboundForm,
11362
+ };
11363
+ const displayValue = getDisplayValue(promotion);
11364
+ return /* @__PURE__ */ jsxs(
11365
+ "div",
11223
11366
  {
11224
- className: "flex flex-1 flex-col overflow-hidden",
11225
- onSubmit,
11367
+ className: clx(
11368
+ "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
11369
+ {
11370
+ "animate-pulse": isLoading
11371
+ }
11372
+ ),
11226
11373
  children: [
11227
- /* @__PURE__ */ jsx(RouteDrawer.Body, { className: "flex flex-col gap-y-6 overflow-y-auto", children: /* @__PURE__ */ jsx(SalesChannelField, { control: form.control, order }) }),
11228
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
11229
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
11230
- /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
11231
- ] }) })
11374
+ /* @__PURE__ */ jsxs("div", { children: [
11375
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
11376
+ /* @__PURE__ */ jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
11377
+ displayValue && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
11378
+ /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", children: displayValue }),
11379
+ /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", children: "·" })
11380
+ ] }),
11381
+ /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", className: "capitalize", children: (_a = promotion.application_method) == null ? void 0 : _a.allocation })
11382
+ ] })
11383
+ ] }),
11384
+ /* @__PURE__ */ jsx(
11385
+ IconButton,
11386
+ {
11387
+ size: "small",
11388
+ type: "button",
11389
+ variant: "transparent",
11390
+ onClick: onRemove,
11391
+ isLoading: isPending || isLoading,
11392
+ children: /* @__PURE__ */ jsx(XMark, {})
11393
+ }
11394
+ )
11232
11395
  ]
11233
- }
11234
- ) });
11235
- };
11236
- const SalesChannelField = ({ control, order }) => {
11237
- const salesChannels = useComboboxData({
11238
- queryFn: async (params) => {
11239
- return await sdk.admin.salesChannel.list(params);
11240
- },
11241
- queryKey: ["sales-channels"],
11242
- getOptions: (data) => {
11243
- return data.sales_channels.map((salesChannel) => ({
11244
- label: salesChannel.name,
11245
- value: salesChannel.id
11246
- }));
11247
11396
  },
11248
- defaultValue: order.sales_channel_id || void 0
11249
- });
11250
- return /* @__PURE__ */ jsx(
11251
- Form$2.Field,
11252
- {
11253
- control,
11254
- name: "sales_channel_id",
11255
- render: ({ field }) => {
11256
- return /* @__PURE__ */ jsxs(Form$2.Item, { children: [
11257
- /* @__PURE__ */ jsx(Form$2.Label, { children: "Sales Channel" }),
11258
- /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
11259
- Combobox,
11260
- {
11261
- options: salesChannels.options,
11262
- fetchNextPage: salesChannels.fetchNextPage,
11263
- isFetchingNextPage: salesChannels.isFetchingNextPage,
11264
- searchValue: salesChannels.searchValue,
11265
- onSearchValueChange: salesChannels.onSearchValueChange,
11266
- placeholder: "Select sales channel",
11267
- ...field
11268
- }
11269
- ) }),
11270
- /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
11271
- ] });
11272
- }
11273
- }
11397
+ promotion.id
11274
11398
  );
11275
11399
  };
11276
- const schema$2 = objectType({
11277
- sales_channel_id: stringType().min(1)
11400
+ function getDisplayValue(promotion) {
11401
+ var _a, _b, _c, _d;
11402
+ const value = (_a = promotion.application_method) == null ? void 0 : _a.value;
11403
+ if (!value) {
11404
+ return null;
11405
+ }
11406
+ if (((_b = promotion.application_method) == null ? void 0 : _b.type) === "fixed") {
11407
+ const currency = (_c = promotion.application_method) == null ? void 0 : _c.currency_code;
11408
+ if (!currency) {
11409
+ return null;
11410
+ }
11411
+ return getLocaleAmount(value, currency);
11412
+ } else if (((_d = promotion.application_method) == null ? void 0 : _d.type) === "percentage") {
11413
+ return formatPercentage(value);
11414
+ }
11415
+ return null;
11416
+ }
11417
+ const formatter = new Intl.NumberFormat([], {
11418
+ style: "percent",
11419
+ minimumFractionDigits: 2
11278
11420
  });
11421
+ const formatPercentage = (value, isPercentageValue = false) => {
11422
+ let val = value || 0;
11423
+ if (!isPercentageValue) {
11424
+ val = val / 100;
11425
+ }
11426
+ return formatter.format(val);
11427
+ };
11428
+ function getPromotionIds(items, shippingMethods) {
11429
+ const promotionIds = /* @__PURE__ */ new Set();
11430
+ for (const item of items) {
11431
+ if (item.adjustments) {
11432
+ for (const adjustment of item.adjustments) {
11433
+ if (adjustment.promotion_id) {
11434
+ promotionIds.add(adjustment.promotion_id);
11435
+ }
11436
+ }
11437
+ }
11438
+ }
11439
+ for (const shippingMethod of shippingMethods) {
11440
+ if (shippingMethod.adjustments) {
11441
+ for (const adjustment of shippingMethod.adjustments) {
11442
+ if (adjustment.promotion_id) {
11443
+ promotionIds.add(adjustment.promotion_id);
11444
+ }
11445
+ }
11446
+ }
11447
+ }
11448
+ return Array.from(promotionIds);
11449
+ }
11279
11450
  const STACKED_FOCUS_MODAL_ID = "shipping-form";
11280
11451
  const Shipping = () => {
11281
11452
  var _a;
@@ -12115,7 +12286,7 @@ const ShippingAddressForm = ({ order }) => {
12115
12286
  postal_code: ((_i = order.shipping_address) == null ? void 0 : _i.postal_code) ?? "",
12116
12287
  phone: ((_j = order.shipping_address) == null ? void 0 : _j.phone) ?? ""
12117
12288
  },
12118
- resolver: zodResolver(schema$1)
12289
+ resolver: zodResolver(schema$2)
12119
12290
  });
12120
12291
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
12121
12292
  const { handleSuccess } = useRouteModal();
@@ -12285,7 +12456,7 @@ const ShippingAddressForm = ({ order }) => {
12285
12456
  }
12286
12457
  ) });
12287
12458
  };
12288
- const schema$1 = addressSchema;
12459
+ const schema$2 = addressSchema;
12289
12460
  const TransferOwnership = () => {
12290
12461
  const { id } = useParams();
12291
12462
  const { draft_order, isPending, isError, error } = useDraftOrder(id, {
@@ -12309,7 +12480,7 @@ const TransferOwnershipForm = ({ order }) => {
12309
12480
  defaultValues: {
12310
12481
  customer_id: order.customer_id || ""
12311
12482
  },
12312
- resolver: zodResolver(schema)
12483
+ resolver: zodResolver(schema$1)
12313
12484
  });
12314
12485
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
12315
12486
  const { handleSuccess } = useRouteModal();
@@ -12759,286 +12930,115 @@ const Illustration = () => {
12759
12930
  }
12760
12931
  );
12761
12932
  };
12762
- const schema = objectType({
12933
+ const schema$1 = objectType({
12763
12934
  customer_id: stringType().min(1)
12764
12935
  });
12765
- const PROMOTION_QUERY_KEY = "promotions";
12766
- const promotionsQueryKeys = {
12767
- list: (query2) => [
12768
- PROMOTION_QUERY_KEY,
12769
- query2 ? query2 : void 0
12770
- ],
12771
- detail: (id, query2) => [
12772
- PROMOTION_QUERY_KEY,
12773
- id,
12774
- query2 ? query2 : void 0
12775
- ]
12776
- };
12777
- const usePromotions = (query2, options) => {
12778
- const { data, ...rest } = useQuery({
12779
- queryKey: promotionsQueryKeys.list(query2),
12780
- queryFn: async () => sdk.admin.promotion.list(query2),
12781
- ...options
12782
- });
12783
- return { ...data, ...rest };
12784
- };
12785
- const Promotions = () => {
12936
+ const SalesChannel = () => {
12786
12937
  const { id } = useParams();
12787
- const {
12788
- order: preview,
12789
- isError: isPreviewError,
12790
- error: previewError
12791
- } = useOrderPreview(id, void 0);
12792
- useInitiateOrderEdit({ preview });
12793
- const { onCancel } = useCancelOrderEdit({ preview });
12794
- if (isPreviewError) {
12795
- throw previewError;
12796
- }
12797
- const isReady = !!preview;
12798
- return /* @__PURE__ */ jsxs(RouteDrawer, { onClose: onCancel, children: [
12799
- /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Promotions" }) }) }),
12800
- isReady && /* @__PURE__ */ jsx(PromotionForm, { preview })
12801
- ] });
12802
- };
12803
- const PromotionForm = ({ preview }) => {
12804
- const { items, shipping_methods } = preview;
12805
- const [isSubmitting, setIsSubmitting] = useState(false);
12806
- const [comboboxValue, setComboboxValue] = useState("");
12807
- const { handleSuccess } = useRouteModal();
12808
- const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
12809
- const promoIds = getPromotionIds(items, shipping_methods);
12810
- const { promotions, isPending, isError, error } = usePromotions(
12811
- {
12812
- id: promoIds
12813
- },
12814
- {
12815
- enabled: !!promoIds.length
12816
- }
12817
- );
12818
- const comboboxData = useComboboxData({
12819
- queryKey: ["promotions", "combobox", promoIds],
12820
- queryFn: async (params) => {
12821
- return await sdk.admin.promotion.list({
12822
- ...params,
12823
- id: {
12824
- $nin: promoIds
12825
- }
12826
- });
12827
- },
12828
- getOptions: (data) => {
12829
- return data.promotions.map((promotion) => ({
12830
- label: promotion.code,
12831
- value: promotion.code
12832
- }));
12833
- }
12834
- });
12835
- const add = async (value) => {
12836
- if (!value) {
12837
- return;
12838
- }
12839
- addPromotions(
12840
- {
12841
- promo_codes: [value]
12842
- },
12843
- {
12844
- onError: (e) => {
12845
- toast.error(e.message);
12846
- comboboxData.onSearchValueChange("");
12847
- setComboboxValue("");
12848
- },
12849
- onSuccess: () => {
12850
- comboboxData.onSearchValueChange("");
12851
- setComboboxValue("");
12852
- }
12853
- }
12854
- );
12855
- };
12856
- const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
12857
- const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
12858
- const onSubmit = async () => {
12859
- setIsSubmitting(true);
12860
- let requestSucceeded = false;
12861
- await requestOrderEdit(void 0, {
12862
- onError: (e) => {
12863
- toast.error(e.message);
12864
- },
12865
- onSuccess: () => {
12866
- requestSucceeded = true;
12867
- }
12868
- });
12869
- if (!requestSucceeded) {
12870
- setIsSubmitting(false);
12871
- return;
12938
+ const { draft_order, isPending, isError, error } = useDraftOrder(
12939
+ id,
12940
+ {
12941
+ fields: "+sales_channel_id"
12942
+ },
12943
+ {
12944
+ enabled: !!id
12872
12945
  }
12873
- await confirmOrderEdit(void 0, {
12874
- onError: (e) => {
12875
- toast.error(e.message);
12876
- },
12877
- onSuccess: () => {
12878
- handleSuccess();
12879
- },
12880
- onSettled: () => {
12881
- setIsSubmitting(false);
12882
- }
12883
- });
12884
- };
12946
+ );
12885
12947
  if (isError) {
12886
12948
  throw error;
12887
12949
  }
12888
- return /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
12889
- /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
12890
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
12891
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
12892
- /* @__PURE__ */ jsx(Label$1, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
12893
- /* @__PURE__ */ jsx(Hint$1, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
12894
- ] }),
12895
- /* @__PURE__ */ jsx(
12896
- Combobox,
12897
- {
12898
- id: "promotion-combobox",
12899
- "aria-describedby": "promotion-combobox-hint",
12900
- isFetchingNextPage: comboboxData.isFetchingNextPage,
12901
- fetchNextPage: comboboxData.fetchNextPage,
12902
- options: comboboxData.options,
12903
- onSearchValueChange: comboboxData.onSearchValueChange,
12904
- searchValue: comboboxData.searchValue,
12905
- disabled: comboboxData.disabled || isAddingPromotions,
12906
- onChange: add,
12907
- value: comboboxValue
12908
- }
12909
- )
12910
- ] }),
12911
- /* @__PURE__ */ jsx(Divider, { variant: "dashed" }),
12912
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: promotions == null ? void 0 : promotions.map((promotion) => /* @__PURE__ */ jsx(
12913
- PromotionItem,
12914
- {
12915
- promotion,
12916
- orderId: preview.id,
12917
- isLoading: isPending
12918
- },
12919
- promotion.id
12920
- )) })
12921
- ] }) }),
12922
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
12923
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
12924
- /* @__PURE__ */ jsx(
12925
- Button,
12926
- {
12927
- size: "small",
12928
- type: "submit",
12929
- isLoading: isSubmitting || isAddingPromotions,
12930
- children: "Save"
12931
- }
12932
- )
12933
- ] }) })
12950
+ const ISrEADY = !!draft_order && !isPending;
12951
+ return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
12952
+ /* @__PURE__ */ jsxs(RouteDrawer.Header, { children: [
12953
+ /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Sales Channel" }) }),
12954
+ /* @__PURE__ */ jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Update which sales channel the draft order is associated with" }) })
12955
+ ] }),
12956
+ ISrEADY && /* @__PURE__ */ jsx(SalesChannelForm, { order: draft_order })
12934
12957
  ] });
12935
12958
  };
12936
- const PromotionItem = ({
12937
- promotion,
12938
- orderId,
12939
- isLoading
12940
- }) => {
12941
- var _a;
12942
- const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
12943
- const onRemove = async () => {
12944
- removePromotions(
12959
+ const SalesChannelForm = ({ order }) => {
12960
+ const form = useForm({
12961
+ defaultValues: {
12962
+ sales_channel_id: order.sales_channel_id || ""
12963
+ },
12964
+ resolver: zodResolver(schema)
12965
+ });
12966
+ const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
12967
+ const { handleSuccess } = useRouteModal();
12968
+ const onSubmit = form.handleSubmit(async (data) => {
12969
+ await mutateAsync(
12945
12970
  {
12946
- promo_codes: [promotion.code]
12971
+ sales_channel_id: data.sales_channel_id
12947
12972
  },
12948
12973
  {
12949
- onError: (e) => {
12950
- toast.error(e.message);
12974
+ onSuccess: () => {
12975
+ toast.success("Sales channel updated");
12976
+ handleSuccess();
12977
+ },
12978
+ onError: (error) => {
12979
+ toast.error(error.message);
12951
12980
  }
12952
12981
  }
12953
12982
  );
12954
- };
12955
- const displayValue = getDisplayValue(promotion);
12956
- return /* @__PURE__ */ jsxs(
12957
- "div",
12983
+ });
12984
+ return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(
12985
+ KeyboundForm,
12958
12986
  {
12959
- className: clx(
12960
- "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
12961
- {
12962
- "animate-pulse": isLoading
12963
- }
12964
- ),
12987
+ className: "flex flex-1 flex-col overflow-hidden",
12988
+ onSubmit,
12965
12989
  children: [
12966
- /* @__PURE__ */ jsxs("div", { children: [
12967
- /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
12968
- /* @__PURE__ */ jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
12969
- displayValue && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
12970
- /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", children: displayValue }),
12971
- /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", children: "·" })
12972
- ] }),
12973
- /* @__PURE__ */ jsx(Text, { size: "small", leading: "compact", className: "capitalize", children: (_a = promotion.application_method) == null ? void 0 : _a.allocation })
12974
- ] })
12975
- ] }),
12976
- /* @__PURE__ */ jsx(
12977
- IconButton,
12978
- {
12979
- size: "small",
12980
- type: "button",
12981
- variant: "transparent",
12982
- onClick: onRemove,
12983
- isLoading: isPending || isLoading,
12984
- children: /* @__PURE__ */ jsx(XMark, {})
12985
- }
12986
- )
12990
+ /* @__PURE__ */ jsx(RouteDrawer.Body, { className: "flex flex-col gap-y-6 overflow-y-auto", children: /* @__PURE__ */ jsx(SalesChannelField, { control: form.control, order }) }),
12991
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
12992
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
12993
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
12994
+ ] }) })
12987
12995
  ]
12988
- },
12989
- promotion.id
12990
- );
12991
- };
12992
- function getDisplayValue(promotion) {
12993
- var _a, _b, _c, _d;
12994
- const value = (_a = promotion.application_method) == null ? void 0 : _a.value;
12995
- if (!value) {
12996
- return null;
12997
- }
12998
- if (((_b = promotion.application_method) == null ? void 0 : _b.type) === "fixed") {
12999
- const currency = (_c = promotion.application_method) == null ? void 0 : _c.currency_code;
13000
- if (!currency) {
13001
- return null;
13002
12996
  }
13003
- return getLocaleAmount(value, currency);
13004
- } else if (((_d = promotion.application_method) == null ? void 0 : _d.type) === "percentage") {
13005
- return formatPercentage(value);
13006
- }
13007
- return null;
13008
- }
13009
- const formatter = new Intl.NumberFormat([], {
13010
- style: "percent",
13011
- minimumFractionDigits: 2
13012
- });
13013
- const formatPercentage = (value, isPercentageValue = false) => {
13014
- let val = value || 0;
13015
- if (!isPercentageValue) {
13016
- val = val / 100;
13017
- }
13018
- return formatter.format(val);
12997
+ ) });
13019
12998
  };
13020
- function getPromotionIds(items, shippingMethods) {
13021
- const promotionIds = /* @__PURE__ */ new Set();
13022
- for (const item of items) {
13023
- if (item.adjustments) {
13024
- for (const adjustment of item.adjustments) {
13025
- if (adjustment.promotion_id) {
13026
- promotionIds.add(adjustment.promotion_id);
13027
- }
13028
- }
13029
- }
13030
- }
13031
- for (const shippingMethod of shippingMethods) {
13032
- if (shippingMethod.adjustments) {
13033
- for (const adjustment of shippingMethod.adjustments) {
13034
- if (adjustment.promotion_id) {
13035
- promotionIds.add(adjustment.promotion_id);
13036
- }
12999
+ const SalesChannelField = ({ control, order }) => {
13000
+ const salesChannels = useComboboxData({
13001
+ queryFn: async (params) => {
13002
+ return await sdk.admin.salesChannel.list(params);
13003
+ },
13004
+ queryKey: ["sales-channels"],
13005
+ getOptions: (data) => {
13006
+ return data.sales_channels.map((salesChannel) => ({
13007
+ label: salesChannel.name,
13008
+ value: salesChannel.id
13009
+ }));
13010
+ },
13011
+ defaultValue: order.sales_channel_id || void 0
13012
+ });
13013
+ return /* @__PURE__ */ jsx(
13014
+ Form$2.Field,
13015
+ {
13016
+ control,
13017
+ name: "sales_channel_id",
13018
+ render: ({ field }) => {
13019
+ return /* @__PURE__ */ jsxs(Form$2.Item, { children: [
13020
+ /* @__PURE__ */ jsx(Form$2.Label, { children: "Sales Channel" }),
13021
+ /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
13022
+ Combobox,
13023
+ {
13024
+ options: salesChannels.options,
13025
+ fetchNextPage: salesChannels.fetchNextPage,
13026
+ isFetchingNextPage: salesChannels.isFetchingNextPage,
13027
+ searchValue: salesChannels.searchValue,
13028
+ onSearchValueChange: salesChannels.onSearchValueChange,
13029
+ placeholder: "Select sales channel",
13030
+ ...field
13031
+ }
13032
+ ) }),
13033
+ /* @__PURE__ */ jsx(Form$2.ErrorMessage, {})
13034
+ ] });
13037
13035
  }
13038
13036
  }
13039
- }
13040
- return Array.from(promotionIds);
13041
- }
13037
+ );
13038
+ };
13039
+ const schema = objectType({
13040
+ sales_channel_id: stringType().min(1)
13041
+ });
13042
13042
  const widgetModule = { widgets: [] };
13043
13043
  const routeModule = {
13044
13044
  routes: [
@@ -13063,25 +13063,25 @@ const routeModule = {
13063
13063
  Component: BillingAddress,
13064
13064
  path: "/draft-orders/:id/billing-address"
13065
13065
  },
13066
- {
13067
- Component: Email,
13068
- path: "/draft-orders/:id/email"
13069
- },
13070
13066
  {
13071
13067
  Component: CustomItems,
13072
13068
  path: "/draft-orders/:id/custom-items"
13073
13069
  },
13074
13070
  {
13075
- Component: Metadata,
13076
- path: "/draft-orders/:id/metadata"
13071
+ Component: Email,
13072
+ path: "/draft-orders/:id/email"
13077
13073
  },
13078
13074
  {
13079
13075
  Component: Items,
13080
13076
  path: "/draft-orders/:id/items"
13081
13077
  },
13082
13078
  {
13083
- Component: SalesChannel,
13084
- path: "/draft-orders/:id/sales-channel"
13079
+ Component: Metadata,
13080
+ path: "/draft-orders/:id/metadata"
13081
+ },
13082
+ {
13083
+ Component: Promotions,
13084
+ path: "/draft-orders/:id/promotions"
13085
13085
  },
13086
13086
  {
13087
13087
  Component: Shipping,
@@ -13096,8 +13096,8 @@ const routeModule = {
13096
13096
  path: "/draft-orders/:id/transfer-ownership"
13097
13097
  },
13098
13098
  {
13099
- Component: Promotions,
13100
- path: "/draft-orders/:id/promotions"
13099
+ Component: SalesChannel,
13100
+ path: "/draft-orders/:id/sales-channel"
13101
13101
  }
13102
13102
  ]
13103
13103
  }