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