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