@medusajs/draft-order 2.10.4-preview-20250928031806 → 2.10.4-preview-20250928060202

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