@medusajs/draft-order 2.10.1 → 2.10.2-snapshot-20250829071156

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