@medusajs/draft-order 2.11.4-preview-20251106090134 → 2.11.4-preview-20251106150143

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