@medusajs/draft-order 2.10.1-snapshot-20250828204656 → 2.10.1

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