@medusajs/draft-order 2.11.4-preview-20251120180140 → 2.11.4-preview-20251120210144

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