@medusajs/draft-order 2.10.1 → 2.10.2-preview-20250829210146

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