@medusajs/draft-order 2.10.4-preview-20250922000330 → 2.10.4-preview-20250922031739

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.
@@ -9831,6 +9831,27 @@ const EmailForm = ({ order }) => {
9831
9831
  const schema$4 = objectType({
9832
9832
  email: stringType().email()
9833
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
+ const schema$3 = objectType({
9853
+ email: stringType().email()
9854
+ });
9834
9855
  const NumberInput = React.forwardRef(
9835
9856
  ({
9836
9857
  value,
@@ -10805,632 +10826,632 @@ const customItemSchema = objectType({
10805
10826
  quantity: numberType(),
10806
10827
  unit_price: unionType([numberType(), stringType()])
10807
10828
  });
10808
- const InlineTip = React.forwardRef(
10809
- ({ variant = "tip", label, className, children, ...props }, ref) => {
10810
- const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
10811
- return /* @__PURE__ */ jsxRuntime.jsxs(
10812
- "div",
10813
- {
10814
- ref,
10815
- className: ui.clx(
10816
- "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
10817
- className
10818
- ),
10819
- ...props,
10820
- children: [
10821
- /* @__PURE__ */ jsxRuntime.jsx(
10822
- "div",
10823
- {
10824
- role: "presentation",
10825
- className: ui.clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
10826
- "bg-ui-tag-orange-icon": variant === "warning"
10827
- })
10828
- }
10829
- ),
10830
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-pretty", children: [
10831
- /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
10832
- labelValue,
10833
- ":"
10834
- ] }),
10835
- " ",
10836
- children
10837
- ] })
10838
- ]
10839
- }
10840
- );
10841
- }
10842
- );
10843
- InlineTip.displayName = "InlineTip";
10844
- const MetadataFieldSchema = objectType({
10845
- key: stringType(),
10846
- disabled: booleanType().optional(),
10847
- value: anyType()
10848
- });
10849
- const MetadataSchema = objectType({
10850
- metadata: arrayType(MetadataFieldSchema)
10851
- });
10852
- const Metadata = () => {
10853
- const { id } = reactRouterDom.useParams();
10854
- const { order, isPending, isError, error } = useOrder(id, {
10855
- fields: "metadata"
10829
+ const PROMOTION_QUERY_KEY = "promotions";
10830
+ const promotionsQueryKeys = {
10831
+ list: (query2) => [
10832
+ PROMOTION_QUERY_KEY,
10833
+ query2 ? query2 : void 0
10834
+ ],
10835
+ detail: (id, query2) => [
10836
+ PROMOTION_QUERY_KEY,
10837
+ id,
10838
+ query2 ? query2 : void 0
10839
+ ]
10840
+ };
10841
+ const usePromotions = (query2, options) => {
10842
+ const { data, ...rest } = reactQuery.useQuery({
10843
+ queryKey: promotionsQueryKeys.list(query2),
10844
+ queryFn: async () => sdk.admin.promotion.list(query2),
10845
+ ...options
10856
10846
  });
10857
- if (isError) {
10858
- throw error;
10847
+ return { ...data, ...rest };
10848
+ };
10849
+ const Promotions = () => {
10850
+ const { id } = reactRouterDom.useParams();
10851
+ const {
10852
+ order: preview,
10853
+ isError: isPreviewError,
10854
+ error: previewError
10855
+ } = useOrderPreview(id, void 0);
10856
+ useInitiateOrderEdit({ preview });
10857
+ const { onCancel } = useCancelOrderEdit({ preview });
10858
+ if (isPreviewError) {
10859
+ throw previewError;
10859
10860
  }
10860
- const isReady = !isPending && !!order;
10861
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
10862
- /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
10863
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Metadata" }) }),
10864
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
10865
- ] }),
10866
- !isReady ? /* @__PURE__ */ jsxRuntime.jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsxRuntime.jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
10861
+ const isReady = !!preview;
10862
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { onClose: onCancel, children: [
10863
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Promotions" }) }) }),
10864
+ isReady && /* @__PURE__ */ jsxRuntime.jsx(PromotionForm, { preview })
10867
10865
  ] });
10868
10866
  };
10869
- const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
10870
- const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
10871
- const MetadataForm = ({ orderId, metadata }) => {
10867
+ const PromotionForm = ({ preview }) => {
10868
+ const { items, shipping_methods } = preview;
10869
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
10870
+ const [comboboxValue, setComboboxValue] = React.useState("");
10872
10871
  const { handleSuccess } = useRouteModal();
10873
- const hasUneditableRows = getHasUneditableRows(metadata);
10874
- const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
10875
- const form = reactHookForm.useForm({
10876
- defaultValues: {
10877
- metadata: getDefaultValues(metadata)
10872
+ const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
10873
+ const promoIds = getPromotionIds(items, shipping_methods);
10874
+ const { promotions, isPending, isError, error } = usePromotions(
10875
+ {
10876
+ id: promoIds
10878
10877
  },
10879
- resolver: zod.zodResolver(MetadataSchema)
10878
+ {
10879
+ enabled: !!promoIds.length
10880
+ }
10881
+ );
10882
+ const comboboxData = useComboboxData({
10883
+ queryKey: ["promotions", "combobox", promoIds],
10884
+ queryFn: async (params) => {
10885
+ return await sdk.admin.promotion.list({
10886
+ ...params,
10887
+ id: {
10888
+ $nin: promoIds
10889
+ }
10890
+ });
10891
+ },
10892
+ getOptions: (data) => {
10893
+ return data.promotions.map((promotion) => ({
10894
+ label: promotion.code,
10895
+ value: promotion.code
10896
+ }));
10897
+ }
10880
10898
  });
10881
- const handleSubmit = form.handleSubmit(async (data) => {
10882
- const parsedData = parseValues(data);
10883
- await mutateAsync(
10899
+ const add = async (value) => {
10900
+ if (!value) {
10901
+ return;
10902
+ }
10903
+ addPromotions(
10884
10904
  {
10885
- metadata: parsedData
10905
+ promo_codes: [value]
10886
10906
  },
10887
10907
  {
10888
- onSuccess: () => {
10889
- ui.toast.success("Metadata updated");
10890
- handleSuccess();
10908
+ onError: (e) => {
10909
+ ui.toast.error(e.message);
10910
+ comboboxData.onSearchValueChange("");
10911
+ setComboboxValue("");
10891
10912
  },
10892
- onError: (error) => {
10893
- ui.toast.error(error.message);
10913
+ onSuccess: () => {
10914
+ comboboxData.onSearchValueChange("");
10915
+ setComboboxValue("");
10894
10916
  }
10895
10917
  }
10896
10918
  );
10897
- });
10898
- const { fields, insert, remove } = reactHookForm.useFieldArray({
10899
- control: form.control,
10900
- name: "metadata"
10901
- });
10902
- function deleteRow(index) {
10903
- remove(index);
10904
- if (fields.length === 1) {
10905
- insert(0, {
10906
- key: "",
10907
- value: "",
10908
- disabled: false
10909
- });
10919
+ };
10920
+ const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
10921
+ const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
10922
+ const onSubmit = async () => {
10923
+ setIsSubmitting(true);
10924
+ let requestSucceeded = false;
10925
+ await requestOrderEdit(void 0, {
10926
+ onError: (e) => {
10927
+ ui.toast.error(e.message);
10928
+ },
10929
+ onSuccess: () => {
10930
+ requestSucceeded = true;
10931
+ }
10932
+ });
10933
+ if (!requestSucceeded) {
10934
+ setIsSubmitting(false);
10935
+ return;
10910
10936
  }
10911
- }
10912
- function insertRow(index, position) {
10913
- insert(index + (position === "above" ? 0 : 1), {
10914
- key: "",
10915
- value: "",
10916
- disabled: false
10937
+ await confirmOrderEdit(void 0, {
10938
+ onError: (e) => {
10939
+ ui.toast.error(e.message);
10940
+ },
10941
+ onSuccess: () => {
10942
+ handleSuccess();
10943
+ },
10944
+ onSettled: () => {
10945
+ setIsSubmitting(false);
10946
+ }
10917
10947
  });
10948
+ };
10949
+ if (isError) {
10950
+ throw error;
10918
10951
  }
10919
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
10920
- KeyboundForm,
10921
- {
10922
- onSubmit: handleSubmit,
10923
- className: "flex flex-1 flex-col overflow-hidden",
10924
- children: [
10925
- /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
10926
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
10927
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
10928
- /* @__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" }) }),
10929
- /* @__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" }) })
10930
- ] }),
10931
- fields.map((field, index) => {
10932
- const isDisabled = field.disabled || false;
10933
- let placeholder = "-";
10934
- if (typeof field.value === "object") {
10935
- placeholder = "{ ... }";
10936
- }
10937
- if (Array.isArray(field.value)) {
10938
- placeholder = "[ ... ]";
10939
- }
10940
- return /* @__PURE__ */ jsxRuntime.jsx(
10941
- ConditionalTooltip,
10942
- {
10943
- showTooltip: isDisabled,
10944
- content: "This row is disabled because it contains non-primitive data.",
10945
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group/table relative", children: [
10946
- /* @__PURE__ */ jsxRuntime.jsxs(
10947
- "div",
10948
- {
10949
- className: ui.clx("grid grid-cols-2 divide-x", {
10950
- "overflow-hidden rounded-b-lg": index === fields.length - 1
10951
- }),
10952
- children: [
10953
- /* @__PURE__ */ jsxRuntime.jsx(
10954
- Form$2.Field,
10955
- {
10956
- control: form.control,
10957
- name: `metadata.${index}.key`,
10958
- render: ({ field: field2 }) => {
10959
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
10960
- GridInput,
10961
- {
10962
- "aria-labelledby": METADATA_KEY_LABEL_ID,
10963
- ...field2,
10964
- disabled: isDisabled,
10965
- placeholder: "Key"
10966
- }
10967
- ) }) });
10968
- }
10969
- }
10970
- ),
10971
- /* @__PURE__ */ jsxRuntime.jsx(
10972
- Form$2.Field,
10973
- {
10974
- control: form.control,
10975
- name: `metadata.${index}.value`,
10976
- render: ({ field: { value, ...field2 } }) => {
10977
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
10978
- GridInput,
10979
- {
10980
- "aria-labelledby": METADATA_VALUE_LABEL_ID,
10981
- ...field2,
10982
- value: isDisabled ? placeholder : value,
10983
- disabled: isDisabled,
10984
- placeholder: "Value"
10985
- }
10986
- ) }) });
10987
- }
10988
- }
10989
- )
10990
- ]
10991
- }
10992
- ),
10993
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
10994
- /* @__PURE__ */ jsxRuntime.jsx(
10995
- ui.DropdownMenu.Trigger,
10996
- {
10997
- className: ui.clx(
10998
- "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
10999
- {
11000
- hidden: isDisabled
11001
- }
11002
- ),
11003
- disabled: isDisabled,
11004
- asChild: true,
11005
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisVertical, {}) })
11006
- }
11007
- ),
11008
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
11009
- /* @__PURE__ */ jsxRuntime.jsxs(
11010
- ui.DropdownMenu.Item,
11011
- {
11012
- className: "gap-x-2",
11013
- onClick: () => insertRow(index, "above"),
11014
- children: [
11015
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpMini, { className: "text-ui-fg-subtle" }),
11016
- "Insert row above"
11017
- ]
11018
- }
11019
- ),
11020
- /* @__PURE__ */ jsxRuntime.jsxs(
11021
- ui.DropdownMenu.Item,
11022
- {
11023
- className: "gap-x-2",
11024
- onClick: () => insertRow(index, "below"),
11025
- children: [
11026
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownMini, { className: "text-ui-fg-subtle" }),
11027
- "Insert row below"
11028
- ]
11029
- }
11030
- ),
11031
- /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
11032
- /* @__PURE__ */ jsxRuntime.jsxs(
11033
- ui.DropdownMenu.Item,
11034
- {
11035
- className: "gap-x-2",
11036
- onClick: () => deleteRow(index),
11037
- children: [
11038
- /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
11039
- "Delete row"
11040
- ]
11041
- }
11042
- )
11043
- ] })
11044
- ] })
11045
- ] })
11046
- },
11047
- field.id
11048
- );
11049
- })
11050
- ] }),
11051
- 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." })
10952
+ return /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
10953
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
10954
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
10955
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
10956
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
10957
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Hint, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
11052
10958
  ] }),
11053
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11054
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
11055
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
11056
- ] }) })
11057
- ]
11058
- }
11059
- ) });
11060
- };
11061
- const GridInput = React.forwardRef(({ className, ...props }, ref) => {
11062
- return /* @__PURE__ */ jsxRuntime.jsx(
11063
- "input",
11064
- {
11065
- ref,
11066
- ...props,
11067
- autoComplete: "off",
11068
- className: ui.clx(
11069
- "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",
11070
- className
10959
+ /* @__PURE__ */ jsxRuntime.jsx(
10960
+ Combobox,
10961
+ {
10962
+ id: "promotion-combobox",
10963
+ "aria-describedby": "promotion-combobox-hint",
10964
+ isFetchingNextPage: comboboxData.isFetchingNextPage,
10965
+ fetchNextPage: comboboxData.fetchNextPage,
10966
+ options: comboboxData.options,
10967
+ onSearchValueChange: comboboxData.onSearchValueChange,
10968
+ searchValue: comboboxData.searchValue,
10969
+ disabled: comboboxData.disabled || isAddingPromotions,
10970
+ onChange: add,
10971
+ value: comboboxValue
10972
+ }
10973
+ )
10974
+ ] }),
10975
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
10976
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: promotions == null ? void 0 : promotions.map((promotion) => /* @__PURE__ */ jsxRuntime.jsx(
10977
+ PromotionItem,
10978
+ {
10979
+ promotion,
10980
+ orderId: preview.id,
10981
+ isLoading: isPending
10982
+ },
10983
+ promotion.id
10984
+ )) })
10985
+ ] }) }),
10986
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
10987
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
10988
+ /* @__PURE__ */ jsxRuntime.jsx(
10989
+ ui.Button,
10990
+ {
10991
+ size: "small",
10992
+ type: "submit",
10993
+ isLoading: isSubmitting || isAddingPromotions,
10994
+ children: "Save"
10995
+ }
11071
10996
  )
11072
- }
11073
- );
11074
- });
11075
- GridInput.displayName = "MetadataForm.GridInput";
11076
- const PlaceholderInner = () => {
11077
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
11078
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
11079
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11080
- /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }),
11081
- /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" })
11082
10997
  ] }) })
11083
10998
  ] });
11084
10999
  };
11085
- const EDITABLE_TYPES = ["string", "number", "boolean"];
11086
- function getDefaultValues(metadata) {
11087
- if (!metadata || !Object.keys(metadata).length) {
11088
- return [
11000
+ const PromotionItem = ({
11001
+ promotion,
11002
+ orderId,
11003
+ isLoading
11004
+ }) => {
11005
+ var _a;
11006
+ const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
11007
+ const onRemove = async () => {
11008
+ removePromotions(
11089
11009
  {
11090
- key: "",
11091
- value: "",
11092
- disabled: false
11010
+ promo_codes: [promotion.code]
11011
+ },
11012
+ {
11013
+ onError: (e) => {
11014
+ ui.toast.error(e.message);
11015
+ }
11093
11016
  }
11094
- ];
11017
+ );
11018
+ };
11019
+ const displayValue = getDisplayValue(promotion);
11020
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11021
+ "div",
11022
+ {
11023
+ className: ui.clx(
11024
+ "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
11025
+ {
11026
+ "animate-pulse": isLoading
11027
+ }
11028
+ ),
11029
+ children: [
11030
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
11031
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
11032
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
11033
+ displayValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
11034
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: displayValue }),
11035
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "·" })
11036
+ ] }),
11037
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "capitalize", children: (_a = promotion.application_method) == null ? void 0 : _a.allocation })
11038
+ ] })
11039
+ ] }),
11040
+ /* @__PURE__ */ jsxRuntime.jsx(
11041
+ ui.IconButton,
11042
+ {
11043
+ size: "small",
11044
+ type: "button",
11045
+ variant: "transparent",
11046
+ onClick: onRemove,
11047
+ isLoading: isPending || isLoading,
11048
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, {})
11049
+ }
11050
+ )
11051
+ ]
11052
+ },
11053
+ promotion.id
11054
+ );
11055
+ };
11056
+ function getDisplayValue(promotion) {
11057
+ var _a, _b, _c, _d;
11058
+ const value = (_a = promotion.application_method) == null ? void 0 : _a.value;
11059
+ if (!value) {
11060
+ return null;
11095
11061
  }
11096
- return Object.entries(metadata).map(([key, value]) => {
11097
- if (!EDITABLE_TYPES.includes(typeof value)) {
11098
- return {
11099
- key,
11100
- value,
11101
- disabled: true
11102
- };
11103
- }
11104
- let stringValue = value;
11105
- if (typeof value !== "string") {
11106
- stringValue = JSON.stringify(value);
11062
+ if (((_b = promotion.application_method) == null ? void 0 : _b.type) === "fixed") {
11063
+ const currency = (_c = promotion.application_method) == null ? void 0 : _c.currency_code;
11064
+ if (!currency) {
11065
+ return null;
11107
11066
  }
11108
- return {
11109
- key,
11110
- value: stringValue,
11111
- original_key: key
11112
- };
11113
- });
11067
+ return getLocaleAmount(value, currency);
11068
+ } else if (((_d = promotion.application_method) == null ? void 0 : _d.type) === "percentage") {
11069
+ return formatPercentage(value);
11070
+ }
11071
+ return null;
11114
11072
  }
11115
- function parseValues(values) {
11116
- const metadata = values.metadata;
11117
- const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
11118
- if (isEmpty) {
11119
- return null;
11073
+ const formatter = new Intl.NumberFormat([], {
11074
+ style: "percent",
11075
+ minimumFractionDigits: 2
11076
+ });
11077
+ const formatPercentage = (value, isPercentageValue = false) => {
11078
+ let val = value || 0;
11079
+ if (!isPercentageValue) {
11080
+ val = val / 100;
11120
11081
  }
11121
- const update = {};
11122
- metadata.forEach((field) => {
11123
- let key = field.key;
11124
- let value = field.value;
11125
- const disabled = field.disabled;
11126
- if (!key || !value) {
11127
- return;
11128
- }
11129
- if (disabled) {
11130
- update[key] = value;
11131
- return;
11082
+ return formatter.format(val);
11083
+ };
11084
+ function getPromotionIds(items, shippingMethods) {
11085
+ const promotionIds = /* @__PURE__ */ new Set();
11086
+ for (const item of items) {
11087
+ if (item.adjustments) {
11088
+ for (const adjustment of item.adjustments) {
11089
+ if (adjustment.promotion_id) {
11090
+ promotionIds.add(adjustment.promotion_id);
11091
+ }
11092
+ }
11132
11093
  }
11133
- key = key.trim();
11134
- value = value.trim();
11135
- if (value === "true") {
11136
- update[key] = true;
11137
- } else if (value === "false") {
11138
- update[key] = false;
11139
- } else {
11140
- const parsedNumber = parseFloat(value);
11141
- if (!isNaN(parsedNumber)) {
11142
- update[key] = parsedNumber;
11143
- } else {
11144
- update[key] = value;
11094
+ }
11095
+ for (const shippingMethod of shippingMethods) {
11096
+ if (shippingMethod.adjustments) {
11097
+ for (const adjustment of shippingMethod.adjustments) {
11098
+ if (adjustment.promotion_id) {
11099
+ promotionIds.add(adjustment.promotion_id);
11100
+ }
11145
11101
  }
11146
11102
  }
11147
- });
11148
- return update;
11149
- }
11150
- function getHasUneditableRows(metadata) {
11151
- if (!metadata) {
11152
- return false;
11153
11103
  }
11154
- return Object.values(metadata).some(
11155
- (value) => !EDITABLE_TYPES.includes(typeof value)
11156
- );
11104
+ return Array.from(promotionIds);
11157
11105
  }
11158
- const PROMOTION_QUERY_KEY = "promotions";
11159
- const promotionsQueryKeys = {
11160
- list: (query2) => [
11161
- PROMOTION_QUERY_KEY,
11162
- query2 ? query2 : void 0
11163
- ],
11164
- detail: (id, query2) => [
11165
- PROMOTION_QUERY_KEY,
11166
- id,
11167
- query2 ? query2 : void 0
11168
- ]
11169
- };
11170
- const usePromotions = (query2, options) => {
11171
- const { data, ...rest } = reactQuery.useQuery({
11172
- queryKey: promotionsQueryKeys.list(query2),
11173
- queryFn: async () => sdk.admin.promotion.list(query2),
11174
- ...options
11175
- });
11176
- return { ...data, ...rest };
11177
- };
11178
- const Promotions = () => {
11106
+ const InlineTip = React.forwardRef(
11107
+ ({ variant = "tip", label, className, children, ...props }, ref) => {
11108
+ const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
11109
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11110
+ "div",
11111
+ {
11112
+ ref,
11113
+ className: ui.clx(
11114
+ "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
11115
+ className
11116
+ ),
11117
+ ...props,
11118
+ children: [
11119
+ /* @__PURE__ */ jsxRuntime.jsx(
11120
+ "div",
11121
+ {
11122
+ role: "presentation",
11123
+ className: ui.clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
11124
+ "bg-ui-tag-orange-icon": variant === "warning"
11125
+ })
11126
+ }
11127
+ ),
11128
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-pretty", children: [
11129
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
11130
+ labelValue,
11131
+ ":"
11132
+ ] }),
11133
+ " ",
11134
+ children
11135
+ ] })
11136
+ ]
11137
+ }
11138
+ );
11139
+ }
11140
+ );
11141
+ InlineTip.displayName = "InlineTip";
11142
+ const MetadataFieldSchema = objectType({
11143
+ key: stringType(),
11144
+ disabled: booleanType().optional(),
11145
+ value: anyType()
11146
+ });
11147
+ const MetadataSchema = objectType({
11148
+ metadata: arrayType(MetadataFieldSchema)
11149
+ });
11150
+ const Metadata = () => {
11179
11151
  const { id } = reactRouterDom.useParams();
11180
- const {
11181
- order: preview,
11182
- isError: isPreviewError,
11183
- error: previewError
11184
- } = useOrderPreview(id, void 0);
11185
- useInitiateOrderEdit({ preview });
11186
- const { onCancel } = useCancelOrderEdit({ preview });
11187
- if (isPreviewError) {
11188
- throw previewError;
11152
+ const { order, isPending, isError, error } = useOrder(id, {
11153
+ fields: "metadata"
11154
+ });
11155
+ if (isError) {
11156
+ throw error;
11189
11157
  }
11190
- const isReady = !!preview;
11191
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { onClose: onCancel, children: [
11192
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Promotions" }) }) }),
11193
- isReady && /* @__PURE__ */ jsxRuntime.jsx(PromotionForm, { preview })
11158
+ const isReady = !isPending && !!order;
11159
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
11160
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
11161
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Metadata" }) }),
11162
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
11163
+ ] }),
11164
+ !isReady ? /* @__PURE__ */ jsxRuntime.jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsxRuntime.jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
11194
11165
  ] });
11195
11166
  };
11196
- const PromotionForm = ({ preview }) => {
11197
- const { items, shipping_methods } = preview;
11198
- const [isSubmitting, setIsSubmitting] = React.useState(false);
11199
- const [comboboxValue, setComboboxValue] = React.useState("");
11167
+ const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
11168
+ const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
11169
+ const MetadataForm = ({ orderId, metadata }) => {
11200
11170
  const { handleSuccess } = useRouteModal();
11201
- const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
11202
- const promoIds = getPromotionIds(items, shipping_methods);
11203
- const { promotions, isPending, isError, error } = usePromotions(
11204
- {
11205
- id: promoIds
11206
- },
11207
- {
11208
- enabled: !!promoIds.length
11209
- }
11210
- );
11211
- const comboboxData = useComboboxData({
11212
- queryKey: ["promotions", "combobox", promoIds],
11213
- queryFn: async (params) => {
11214
- return await sdk.admin.promotion.list({
11215
- ...params,
11216
- id: {
11217
- $nin: promoIds
11218
- }
11219
- });
11171
+ const hasUneditableRows = getHasUneditableRows(metadata);
11172
+ const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
11173
+ const form = reactHookForm.useForm({
11174
+ defaultValues: {
11175
+ metadata: getDefaultValues(metadata)
11220
11176
  },
11221
- getOptions: (data) => {
11222
- return data.promotions.map((promotion) => ({
11223
- label: promotion.code,
11224
- value: promotion.code
11225
- }));
11226
- }
11177
+ resolver: zod.zodResolver(MetadataSchema)
11227
11178
  });
11228
- const add = async (value) => {
11229
- if (!value) {
11230
- return;
11231
- }
11232
- addPromotions(
11179
+ const handleSubmit = form.handleSubmit(async (data) => {
11180
+ const parsedData = parseValues(data);
11181
+ await mutateAsync(
11233
11182
  {
11234
- promo_codes: [value]
11183
+ metadata: parsedData
11235
11184
  },
11236
11185
  {
11237
- onError: (e) => {
11238
- ui.toast.error(e.message);
11239
- comboboxData.onSearchValueChange("");
11240
- setComboboxValue("");
11241
- },
11242
11186
  onSuccess: () => {
11243
- comboboxData.onSearchValueChange("");
11244
- setComboboxValue("");
11187
+ ui.toast.success("Metadata updated");
11188
+ handleSuccess();
11189
+ },
11190
+ onError: (error) => {
11191
+ ui.toast.error(error.message);
11245
11192
  }
11246
11193
  }
11247
11194
  );
11248
- };
11249
- const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
11250
- const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
11251
- const onSubmit = async () => {
11252
- setIsSubmitting(true);
11253
- let requestSucceeded = false;
11254
- await requestOrderEdit(void 0, {
11255
- onError: (e) => {
11256
- ui.toast.error(e.message);
11257
- },
11258
- onSuccess: () => {
11259
- requestSucceeded = true;
11260
- }
11261
- });
11262
- if (!requestSucceeded) {
11263
- setIsSubmitting(false);
11264
- return;
11195
+ });
11196
+ const { fields, insert, remove } = reactHookForm.useFieldArray({
11197
+ control: form.control,
11198
+ name: "metadata"
11199
+ });
11200
+ function deleteRow(index) {
11201
+ remove(index);
11202
+ if (fields.length === 1) {
11203
+ insert(0, {
11204
+ key: "",
11205
+ value: "",
11206
+ disabled: false
11207
+ });
11265
11208
  }
11266
- await confirmOrderEdit(void 0, {
11267
- onError: (e) => {
11268
- ui.toast.error(e.message);
11269
- },
11270
- onSuccess: () => {
11271
- handleSuccess();
11272
- },
11273
- onSettled: () => {
11274
- setIsSubmitting(false);
11275
- }
11209
+ }
11210
+ function insertRow(index, position) {
11211
+ insert(index + (position === "above" ? 0 : 1), {
11212
+ key: "",
11213
+ value: "",
11214
+ disabled: false
11276
11215
  });
11277
- };
11278
- if (isError) {
11279
- throw error;
11280
11216
  }
11281
- return /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
11282
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
11283
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
11284
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
11285
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
11286
- /* @__PURE__ */ jsxRuntime.jsx(ui.Hint, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
11287
- ] }),
11288
- /* @__PURE__ */ jsxRuntime.jsx(
11289
- Combobox,
11290
- {
11291
- id: "promotion-combobox",
11292
- "aria-describedby": "promotion-combobox-hint",
11293
- isFetchingNextPage: comboboxData.isFetchingNextPage,
11294
- fetchNextPage: comboboxData.fetchNextPage,
11295
- options: comboboxData.options,
11296
- onSearchValueChange: comboboxData.onSearchValueChange,
11297
- searchValue: comboboxData.searchValue,
11298
- disabled: comboboxData.disabled || isAddingPromotions,
11299
- onChange: add,
11300
- value: comboboxValue
11301
- }
11302
- )
11303
- ] }),
11304
- /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
11305
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: promotions == null ? void 0 : promotions.map((promotion) => /* @__PURE__ */ jsxRuntime.jsx(
11306
- PromotionItem,
11307
- {
11308
- promotion,
11309
- orderId: preview.id,
11310
- isLoading: isPending
11311
- },
11312
- promotion.id
11313
- )) })
11314
- ] }) }),
11315
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
11316
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
11317
- /* @__PURE__ */ jsxRuntime.jsx(
11318
- ui.Button,
11319
- {
11320
- size: "small",
11321
- type: "submit",
11322
- isLoading: isSubmitting || isAddingPromotions,
11323
- children: "Save"
11324
- }
11217
+ return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
11218
+ KeyboundForm,
11219
+ {
11220
+ onSubmit: handleSubmit,
11221
+ className: "flex flex-1 flex-col overflow-hidden",
11222
+ children: [
11223
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
11224
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
11225
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
11226
+ /* @__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" }) }),
11227
+ /* @__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" }) })
11228
+ ] }),
11229
+ fields.map((field, index) => {
11230
+ const isDisabled = field.disabled || false;
11231
+ let placeholder = "-";
11232
+ if (typeof field.value === "object") {
11233
+ placeholder = "{ ... }";
11234
+ }
11235
+ if (Array.isArray(field.value)) {
11236
+ placeholder = "[ ... ]";
11237
+ }
11238
+ return /* @__PURE__ */ jsxRuntime.jsx(
11239
+ ConditionalTooltip,
11240
+ {
11241
+ showTooltip: isDisabled,
11242
+ content: "This row is disabled because it contains non-primitive data.",
11243
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group/table relative", children: [
11244
+ /* @__PURE__ */ jsxRuntime.jsxs(
11245
+ "div",
11246
+ {
11247
+ className: ui.clx("grid grid-cols-2 divide-x", {
11248
+ "overflow-hidden rounded-b-lg": index === fields.length - 1
11249
+ }),
11250
+ children: [
11251
+ /* @__PURE__ */ jsxRuntime.jsx(
11252
+ Form$2.Field,
11253
+ {
11254
+ control: form.control,
11255
+ name: `metadata.${index}.key`,
11256
+ render: ({ field: field2 }) => {
11257
+ return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
11258
+ GridInput,
11259
+ {
11260
+ "aria-labelledby": METADATA_KEY_LABEL_ID,
11261
+ ...field2,
11262
+ disabled: isDisabled,
11263
+ placeholder: "Key"
11264
+ }
11265
+ ) }) });
11266
+ }
11267
+ }
11268
+ ),
11269
+ /* @__PURE__ */ jsxRuntime.jsx(
11270
+ Form$2.Field,
11271
+ {
11272
+ control: form.control,
11273
+ name: `metadata.${index}.value`,
11274
+ render: ({ field: { value, ...field2 } }) => {
11275
+ return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
11276
+ GridInput,
11277
+ {
11278
+ "aria-labelledby": METADATA_VALUE_LABEL_ID,
11279
+ ...field2,
11280
+ value: isDisabled ? placeholder : value,
11281
+ disabled: isDisabled,
11282
+ placeholder: "Value"
11283
+ }
11284
+ ) }) });
11285
+ }
11286
+ }
11287
+ )
11288
+ ]
11289
+ }
11290
+ ),
11291
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
11292
+ /* @__PURE__ */ jsxRuntime.jsx(
11293
+ ui.DropdownMenu.Trigger,
11294
+ {
11295
+ className: ui.clx(
11296
+ "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
11297
+ {
11298
+ hidden: isDisabled
11299
+ }
11300
+ ),
11301
+ disabled: isDisabled,
11302
+ asChild: true,
11303
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisVertical, {}) })
11304
+ }
11305
+ ),
11306
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
11307
+ /* @__PURE__ */ jsxRuntime.jsxs(
11308
+ ui.DropdownMenu.Item,
11309
+ {
11310
+ className: "gap-x-2",
11311
+ onClick: () => insertRow(index, "above"),
11312
+ children: [
11313
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpMini, { className: "text-ui-fg-subtle" }),
11314
+ "Insert row above"
11315
+ ]
11316
+ }
11317
+ ),
11318
+ /* @__PURE__ */ jsxRuntime.jsxs(
11319
+ ui.DropdownMenu.Item,
11320
+ {
11321
+ className: "gap-x-2",
11322
+ onClick: () => insertRow(index, "below"),
11323
+ children: [
11324
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownMini, { className: "text-ui-fg-subtle" }),
11325
+ "Insert row below"
11326
+ ]
11327
+ }
11328
+ ),
11329
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
11330
+ /* @__PURE__ */ jsxRuntime.jsxs(
11331
+ ui.DropdownMenu.Item,
11332
+ {
11333
+ className: "gap-x-2",
11334
+ onClick: () => deleteRow(index),
11335
+ children: [
11336
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
11337
+ "Delete row"
11338
+ ]
11339
+ }
11340
+ )
11341
+ ] })
11342
+ ] })
11343
+ ] })
11344
+ },
11345
+ field.id
11346
+ );
11347
+ })
11348
+ ] }),
11349
+ 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." })
11350
+ ] }),
11351
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11352
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
11353
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
11354
+ ] }) })
11355
+ ]
11356
+ }
11357
+ ) });
11358
+ };
11359
+ const GridInput = React.forwardRef(({ className, ...props }, ref) => {
11360
+ return /* @__PURE__ */ jsxRuntime.jsx(
11361
+ "input",
11362
+ {
11363
+ ref,
11364
+ ...props,
11365
+ autoComplete: "off",
11366
+ className: ui.clx(
11367
+ "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",
11368
+ className
11325
11369
  )
11370
+ }
11371
+ );
11372
+ });
11373
+ GridInput.displayName = "MetadataForm.GridInput";
11374
+ const PlaceholderInner = () => {
11375
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
11376
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
11377
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11378
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }),
11379
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" })
11326
11380
  ] }) })
11327
11381
  ] });
11328
11382
  };
11329
- const PromotionItem = ({
11330
- promotion,
11331
- orderId,
11332
- isLoading
11333
- }) => {
11334
- var _a;
11335
- const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
11336
- const onRemove = async () => {
11337
- removePromotions(
11338
- {
11339
- promo_codes: [promotion.code]
11340
- },
11383
+ const EDITABLE_TYPES = ["string", "number", "boolean"];
11384
+ function getDefaultValues(metadata) {
11385
+ if (!metadata || !Object.keys(metadata).length) {
11386
+ return [
11341
11387
  {
11342
- onError: (e) => {
11343
- ui.toast.error(e.message);
11344
- }
11388
+ key: "",
11389
+ value: "",
11390
+ disabled: false
11345
11391
  }
11346
- );
11347
- };
11348
- const displayValue = getDisplayValue(promotion);
11349
- return /* @__PURE__ */ jsxRuntime.jsxs(
11350
- "div",
11351
- {
11352
- className: ui.clx(
11353
- "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
11354
- {
11355
- "animate-pulse": isLoading
11356
- }
11357
- ),
11358
- children: [
11359
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
11360
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
11361
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
11362
- displayValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
11363
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: displayValue }),
11364
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "·" })
11365
- ] }),
11366
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "capitalize", children: (_a = promotion.application_method) == null ? void 0 : _a.allocation })
11367
- ] })
11368
- ] }),
11369
- /* @__PURE__ */ jsxRuntime.jsx(
11370
- ui.IconButton,
11371
- {
11372
- size: "small",
11373
- type: "button",
11374
- variant: "transparent",
11375
- onClick: onRemove,
11376
- isLoading: isPending || isLoading,
11377
- children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, {})
11378
- }
11379
- )
11380
- ]
11381
- },
11382
- promotion.id
11383
- );
11384
- };
11385
- function getDisplayValue(promotion) {
11386
- var _a, _b, _c, _d;
11387
- const value = (_a = promotion.application_method) == null ? void 0 : _a.value;
11388
- if (!value) {
11389
- return null;
11392
+ ];
11390
11393
  }
11391
- if (((_b = promotion.application_method) == null ? void 0 : _b.type) === "fixed") {
11392
- const currency = (_c = promotion.application_method) == null ? void 0 : _c.currency_code;
11393
- if (!currency) {
11394
- return null;
11394
+ return Object.entries(metadata).map(([key, value]) => {
11395
+ if (!EDITABLE_TYPES.includes(typeof value)) {
11396
+ return {
11397
+ key,
11398
+ value,
11399
+ disabled: true
11400
+ };
11395
11401
  }
11396
- return getLocaleAmount(value, currency);
11397
- } else if (((_d = promotion.application_method) == null ? void 0 : _d.type) === "percentage") {
11398
- return formatPercentage(value);
11399
- }
11400
- return null;
11402
+ let stringValue = value;
11403
+ if (typeof value !== "string") {
11404
+ stringValue = JSON.stringify(value);
11405
+ }
11406
+ return {
11407
+ key,
11408
+ value: stringValue,
11409
+ original_key: key
11410
+ };
11411
+ });
11401
11412
  }
11402
- const formatter = new Intl.NumberFormat([], {
11403
- style: "percent",
11404
- minimumFractionDigits: 2
11405
- });
11406
- const formatPercentage = (value, isPercentageValue = false) => {
11407
- let val = value || 0;
11408
- if (!isPercentageValue) {
11409
- val = val / 100;
11413
+ function parseValues(values) {
11414
+ const metadata = values.metadata;
11415
+ const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
11416
+ if (isEmpty) {
11417
+ return null;
11410
11418
  }
11411
- return formatter.format(val);
11412
- };
11413
- function getPromotionIds(items, shippingMethods) {
11414
- const promotionIds = /* @__PURE__ */ new Set();
11415
- for (const item of items) {
11416
- if (item.adjustments) {
11417
- for (const adjustment of item.adjustments) {
11418
- if (adjustment.promotion_id) {
11419
- promotionIds.add(adjustment.promotion_id);
11420
- }
11421
- }
11419
+ const update = {};
11420
+ metadata.forEach((field) => {
11421
+ let key = field.key;
11422
+ let value = field.value;
11423
+ const disabled = field.disabled;
11424
+ if (!key || !value) {
11425
+ return;
11422
11426
  }
11423
- }
11424
- for (const shippingMethod of shippingMethods) {
11425
- if (shippingMethod.adjustments) {
11426
- for (const adjustment of shippingMethod.adjustments) {
11427
- if (adjustment.promotion_id) {
11428
- promotionIds.add(adjustment.promotion_id);
11429
- }
11427
+ if (disabled) {
11428
+ update[key] = value;
11429
+ return;
11430
+ }
11431
+ key = key.trim();
11432
+ value = value.trim();
11433
+ if (value === "true") {
11434
+ update[key] = true;
11435
+ } else if (value === "false") {
11436
+ update[key] = false;
11437
+ } else {
11438
+ const parsedNumber = parseFloat(value);
11439
+ if (!isNaN(parsedNumber)) {
11440
+ update[key] = parsedNumber;
11441
+ } else {
11442
+ update[key] = value;
11430
11443
  }
11431
11444
  }
11445
+ });
11446
+ return update;
11447
+ }
11448
+ function getHasUneditableRows(metadata) {
11449
+ if (!metadata) {
11450
+ return false;
11432
11451
  }
11433
- return Array.from(promotionIds);
11452
+ return Object.values(metadata).some(
11453
+ (value) => !EDITABLE_TYPES.includes(typeof value)
11454
+ );
11434
11455
  }
11435
11456
  const SalesChannel = () => {
11436
11457
  const { id } = reactRouterDom.useParams();
@@ -11460,7 +11481,7 @@ const SalesChannelForm = ({ order }) => {
11460
11481
  defaultValues: {
11461
11482
  sales_channel_id: order.sales_channel_id || ""
11462
11483
  },
11463
- resolver: zod.zodResolver(schema$3)
11484
+ resolver: zod.zodResolver(schema$2)
11464
11485
  });
11465
11486
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
11466
11487
  const { handleSuccess } = useRouteModal();
@@ -11535,7 +11556,7 @@ const SalesChannelField = ({ control, order }) => {
11535
11556
  }
11536
11557
  );
11537
11558
  };
11538
- const schema$3 = objectType({
11559
+ const schema$2 = objectType({
11539
11560
  sales_channel_id: stringType().min(1)
11540
11561
  });
11541
11562
  const STACKED_FOCUS_MODAL_ID = "shipping-form";
@@ -12377,7 +12398,7 @@ const ShippingAddressForm = ({ order }) => {
12377
12398
  postal_code: ((_i = order.shipping_address) == null ? void 0 : _i.postal_code) ?? "",
12378
12399
  phone: ((_j = order.shipping_address) == null ? void 0 : _j.phone) ?? ""
12379
12400
  },
12380
- resolver: zod.zodResolver(schema$2)
12401
+ resolver: zod.zodResolver(schema$1)
12381
12402
  });
12382
12403
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
12383
12404
  const { handleSuccess } = useRouteModal();
@@ -12547,7 +12568,7 @@ const ShippingAddressForm = ({ order }) => {
12547
12568
  }
12548
12569
  ) });
12549
12570
  };
12550
- const schema$2 = addressSchema;
12571
+ const schema$1 = addressSchema;
12551
12572
  const TransferOwnership = () => {
12552
12573
  const { id } = reactRouterDom.useParams();
12553
12574
  const { draft_order, isPending, isError, error } = useDraftOrder(id, {
@@ -12571,7 +12592,7 @@ const TransferOwnershipForm = ({ order }) => {
12571
12592
  defaultValues: {
12572
12593
  customer_id: order.customer_id || ""
12573
12594
  },
12574
- resolver: zod.zodResolver(schema$1)
12595
+ resolver: zod.zodResolver(schema)
12575
12596
  });
12576
12597
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
12577
12598
  const { handleSuccess } = useRouteModal();
@@ -13021,29 +13042,8 @@ const Illustration = () => {
13021
13042
  }
13022
13043
  );
13023
13044
  };
13024
- const schema$1 = objectType({
13025
- customer_id: stringType().min(1)
13026
- });
13027
- const CustomItems = () => {
13028
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
13029
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Custom Items" }) }) }),
13030
- /* @__PURE__ */ jsxRuntime.jsx(CustomItemsForm, {})
13031
- ] });
13032
- };
13033
- const CustomItemsForm = () => {
13034
- const form = reactHookForm.useForm({
13035
- resolver: zod.zodResolver(schema)
13036
- });
13037
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
13038
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, {}),
13039
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
13040
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
13041
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", children: "Save" })
13042
- ] }) })
13043
- ] }) });
13044
- };
13045
13045
  const schema = objectType({
13046
- email: stringType().email()
13046
+ customer_id: stringType().min(1)
13047
13047
  });
13048
13048
  const widgetModule = { widgets: [] };
13049
13049
  const routeModule = {
@@ -13074,17 +13074,21 @@ const routeModule = {
13074
13074
  path: "/draft-orders/:id/email"
13075
13075
  },
13076
13076
  {
13077
- Component: Items,
13078
- path: "/draft-orders/:id/items"
13077
+ Component: CustomItems,
13078
+ path: "/draft-orders/:id/custom-items"
13079
13079
  },
13080
13080
  {
13081
- Component: Metadata,
13082
- path: "/draft-orders/:id/metadata"
13081
+ Component: Items,
13082
+ path: "/draft-orders/:id/items"
13083
13083
  },
13084
13084
  {
13085
13085
  Component: Promotions,
13086
13086
  path: "/draft-orders/:id/promotions"
13087
13087
  },
13088
+ {
13089
+ Component: Metadata,
13090
+ path: "/draft-orders/:id/metadata"
13091
+ },
13088
13092
  {
13089
13093
  Component: SalesChannel,
13090
13094
  path: "/draft-orders/:id/sales-channel"
@@ -13100,10 +13104,6 @@ const routeModule = {
13100
13104
  {
13101
13105
  Component: TransferOwnership,
13102
13106
  path: "/draft-orders/:id/transfer-ownership"
13103
- },
13104
- {
13105
- Component: CustomItems,
13106
- path: "/draft-orders/:id/custom-items"
13107
13107
  }
13108
13108
  ]
13109
13109
  }