@medusajs/draft-order 2.11.4-preview-20251118150138 → 2.11.4-preview-20251118180132

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.
@@ -9755,27 +9755,6 @@ const BillingAddressForm = ({ order }) => {
9755
9755
  ) });
9756
9756
  };
9757
9757
  const schema$5 = addressSchema;
9758
- const CustomItems = () => {
9759
- return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
9760
- /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Custom Items" }) }) }),
9761
- /* @__PURE__ */ jsx(CustomItemsForm, {})
9762
- ] });
9763
- };
9764
- const CustomItemsForm = () => {
9765
- const form = useForm({
9766
- resolver: zodResolver(schema$4)
9767
- });
9768
- return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
9769
- /* @__PURE__ */ jsx(RouteDrawer.Body, {}),
9770
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
9771
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
9772
- /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", children: "Save" })
9773
- ] }) })
9774
- ] }) });
9775
- };
9776
- const schema$4 = objectType({
9777
- email: stringType().email()
9778
- });
9779
9758
  const Email = () => {
9780
9759
  const { id } = useParams();
9781
9760
  const { order, isPending, isError, error } = useOrder(id, {
@@ -9798,7 +9777,7 @@ const EmailForm = ({ order }) => {
9798
9777
  defaultValues: {
9799
9778
  email: order.email ?? ""
9800
9779
  },
9801
- resolver: zodResolver(schema$3)
9780
+ resolver: zodResolver(schema$4)
9802
9781
  });
9803
9782
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
9804
9783
  const { handleSuccess } = useRouteModal();
@@ -9841,6 +9820,27 @@ const EmailForm = ({ order }) => {
9841
9820
  }
9842
9821
  ) });
9843
9822
  };
9823
+ const schema$4 = objectType({
9824
+ email: stringType().email()
9825
+ });
9826
+ const CustomItems = () => {
9827
+ return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
9828
+ /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Custom Items" }) }) }),
9829
+ /* @__PURE__ */ jsx(CustomItemsForm, {})
9830
+ ] });
9831
+ };
9832
+ const CustomItemsForm = () => {
9833
+ const form = useForm({
9834
+ resolver: zodResolver(schema$3)
9835
+ });
9836
+ return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
9837
+ /* @__PURE__ */ jsx(RouteDrawer.Body, {}),
9838
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
9839
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
9840
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", children: "Save" })
9841
+ ] }) })
9842
+ ] }) });
9843
+ };
9844
9844
  const schema$3 = objectType({
9845
9845
  email: stringType().email()
9846
9846
  });
@@ -10818,478 +10818,128 @@ const customItemSchema = objectType({
10818
10818
  quantity: numberType(),
10819
10819
  unit_price: unionType([numberType(), stringType()])
10820
10820
  });
10821
- const InlineTip = forwardRef(
10822
- ({ variant = "tip", label, className, children, ...props }, ref) => {
10823
- const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
10824
- return /* @__PURE__ */ jsxs(
10825
- "div",
10826
- {
10827
- ref,
10828
- className: clx(
10829
- "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
10830
- className
10831
- ),
10832
- ...props,
10833
- children: [
10834
- /* @__PURE__ */ jsx(
10835
- "div",
10836
- {
10837
- role: "presentation",
10838
- className: clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
10839
- "bg-ui-tag-orange-icon": variant === "warning"
10840
- })
10841
- }
10842
- ),
10843
- /* @__PURE__ */ jsxs("div", { className: "text-pretty", children: [
10844
- /* @__PURE__ */ jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
10845
- labelValue,
10846
- ":"
10847
- ] }),
10848
- " ",
10849
- children
10850
- ] })
10851
- ]
10852
- }
10853
- );
10854
- }
10855
- );
10856
- InlineTip.displayName = "InlineTip";
10857
- const MetadataFieldSchema = objectType({
10858
- key: stringType(),
10859
- disabled: booleanType().optional(),
10860
- value: anyType()
10861
- });
10862
- const MetadataSchema = objectType({
10863
- metadata: arrayType(MetadataFieldSchema)
10864
- });
10865
- const Metadata = () => {
10866
- const { id } = useParams();
10867
- const { order, isPending, isError, error } = useOrder(id, {
10868
- fields: "metadata"
10821
+ const PROMOTION_QUERY_KEY = "promotions";
10822
+ const promotionsQueryKeys = {
10823
+ list: (query2) => [
10824
+ PROMOTION_QUERY_KEY,
10825
+ query2 ? query2 : void 0
10826
+ ],
10827
+ detail: (id, query2) => [
10828
+ PROMOTION_QUERY_KEY,
10829
+ id,
10830
+ query2 ? query2 : void 0
10831
+ ]
10832
+ };
10833
+ const usePromotions = (query2, options) => {
10834
+ const { data, ...rest } = useQuery({
10835
+ queryKey: promotionsQueryKeys.list(query2),
10836
+ queryFn: async () => sdk.admin.promotion.list(query2),
10837
+ ...options
10869
10838
  });
10870
- if (isError) {
10871
- throw error;
10839
+ return { ...data, ...rest };
10840
+ };
10841
+ const Promotions = () => {
10842
+ const { id } = useParams();
10843
+ const {
10844
+ order: preview,
10845
+ isError: isPreviewError,
10846
+ error: previewError
10847
+ } = useOrderPreview(id, void 0);
10848
+ useInitiateOrderEdit({ preview });
10849
+ const { onCancel } = useCancelOrderEdit({ preview });
10850
+ if (isPreviewError) {
10851
+ throw previewError;
10872
10852
  }
10873
- const isReady = !isPending && !!order;
10874
- return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
10875
- /* @__PURE__ */ jsxs(RouteDrawer.Header, { children: [
10876
- /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Metadata" }) }),
10877
- /* @__PURE__ */ jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
10878
- ] }),
10879
- !isReady ? /* @__PURE__ */ jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
10853
+ const isReady = !!preview;
10854
+ return /* @__PURE__ */ jsxs(RouteDrawer, { onClose: onCancel, children: [
10855
+ /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Promotions" }) }) }),
10856
+ isReady && /* @__PURE__ */ jsx(PromotionForm, { preview })
10880
10857
  ] });
10881
10858
  };
10882
- const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
10883
- const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
10884
- const MetadataForm = ({ orderId, metadata }) => {
10859
+ const PromotionForm = ({ preview }) => {
10860
+ const { items, shipping_methods } = preview;
10861
+ const [isSubmitting, setIsSubmitting] = useState(false);
10862
+ const [comboboxValue, setComboboxValue] = useState("");
10885
10863
  const { handleSuccess } = useRouteModal();
10886
- const hasUneditableRows = getHasUneditableRows(metadata);
10887
- const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
10888
- const form = useForm({
10889
- defaultValues: {
10890
- metadata: getDefaultValues(metadata)
10864
+ const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
10865
+ const promoIds = getPromotionIds(items, shipping_methods);
10866
+ const { promotions, isPending, isError, error } = usePromotions(
10867
+ {
10868
+ id: promoIds
10891
10869
  },
10892
- resolver: zodResolver(MetadataSchema)
10870
+ {
10871
+ enabled: !!promoIds.length
10872
+ }
10873
+ );
10874
+ const comboboxData = useComboboxData({
10875
+ queryKey: ["promotions", "combobox", promoIds],
10876
+ queryFn: async (params) => {
10877
+ return await sdk.admin.promotion.list({
10878
+ ...params,
10879
+ id: {
10880
+ $nin: promoIds
10881
+ }
10882
+ });
10883
+ },
10884
+ getOptions: (data) => {
10885
+ return data.promotions.map((promotion) => ({
10886
+ label: promotion.code,
10887
+ value: promotion.code
10888
+ }));
10889
+ }
10893
10890
  });
10894
- const handleSubmit = form.handleSubmit(async (data) => {
10895
- const parsedData = parseValues(data);
10896
- await mutateAsync(
10891
+ const add = async (value) => {
10892
+ if (!value) {
10893
+ return;
10894
+ }
10895
+ addPromotions(
10897
10896
  {
10898
- metadata: parsedData
10897
+ promo_codes: [value]
10899
10898
  },
10900
10899
  {
10901
- onSuccess: () => {
10902
- toast.success("Metadata updated");
10903
- handleSuccess();
10900
+ onError: (e) => {
10901
+ toast.error(e.message);
10902
+ comboboxData.onSearchValueChange("");
10903
+ setComboboxValue("");
10904
10904
  },
10905
- onError: (error) => {
10906
- toast.error(error.message);
10905
+ onSuccess: () => {
10906
+ comboboxData.onSearchValueChange("");
10907
+ setComboboxValue("");
10907
10908
  }
10908
10909
  }
10909
10910
  );
10910
- });
10911
- const { fields, insert, remove } = useFieldArray({
10912
- control: form.control,
10913
- name: "metadata"
10914
- });
10915
- function deleteRow(index) {
10916
- remove(index);
10917
- if (fields.length === 1) {
10918
- insert(0, {
10919
- key: "",
10920
- value: "",
10921
- disabled: false
10922
- });
10911
+ };
10912
+ const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
10913
+ const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
10914
+ const onSubmit = async () => {
10915
+ setIsSubmitting(true);
10916
+ let requestSucceeded = false;
10917
+ await requestOrderEdit(void 0, {
10918
+ onError: (e) => {
10919
+ toast.error(e.message);
10920
+ },
10921
+ onSuccess: () => {
10922
+ requestSucceeded = true;
10923
+ }
10924
+ });
10925
+ if (!requestSucceeded) {
10926
+ setIsSubmitting(false);
10927
+ return;
10923
10928
  }
10924
- }
10925
- function insertRow(index, position) {
10926
- insert(index + (position === "above" ? 0 : 1), {
10927
- key: "",
10928
- value: "",
10929
- disabled: false
10929
+ await confirmOrderEdit(void 0, {
10930
+ onError: (e) => {
10931
+ toast.error(e.message);
10932
+ },
10933
+ onSuccess: () => {
10934
+ handleSuccess();
10935
+ },
10936
+ onSettled: () => {
10937
+ setIsSubmitting(false);
10938
+ }
10930
10939
  });
10931
- }
10932
- return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(
10933
- KeyboundForm,
10934
- {
10935
- onSubmit: handleSubmit,
10936
- className: "flex flex-1 flex-col overflow-hidden",
10937
- children: [
10938
- /* @__PURE__ */ jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
10939
- /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
10940
- /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
10941
- /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_KEY_LABEL_ID, children: "Key" }) }),
10942
- /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_VALUE_LABEL_ID, children: "Value" }) })
10943
- ] }),
10944
- fields.map((field, index) => {
10945
- const isDisabled = field.disabled || false;
10946
- let placeholder = "-";
10947
- if (typeof field.value === "object") {
10948
- placeholder = "{ ... }";
10949
- }
10950
- if (Array.isArray(field.value)) {
10951
- placeholder = "[ ... ]";
10952
- }
10953
- return /* @__PURE__ */ jsx(
10954
- ConditionalTooltip,
10955
- {
10956
- showTooltip: isDisabled,
10957
- content: "This row is disabled because it contains non-primitive data.",
10958
- children: /* @__PURE__ */ jsxs("div", { className: "group/table relative", children: [
10959
- /* @__PURE__ */ jsxs(
10960
- "div",
10961
- {
10962
- className: clx("grid grid-cols-2 divide-x", {
10963
- "overflow-hidden rounded-b-lg": index === fields.length - 1
10964
- }),
10965
- children: [
10966
- /* @__PURE__ */ jsx(
10967
- Form$2.Field,
10968
- {
10969
- control: form.control,
10970
- name: `metadata.${index}.key`,
10971
- render: ({ field: field2 }) => {
10972
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10973
- GridInput,
10974
- {
10975
- "aria-labelledby": METADATA_KEY_LABEL_ID,
10976
- ...field2,
10977
- disabled: isDisabled,
10978
- placeholder: "Key"
10979
- }
10980
- ) }) });
10981
- }
10982
- }
10983
- ),
10984
- /* @__PURE__ */ jsx(
10985
- Form$2.Field,
10986
- {
10987
- control: form.control,
10988
- name: `metadata.${index}.value`,
10989
- render: ({ field: { value, ...field2 } }) => {
10990
- return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
10991
- GridInput,
10992
- {
10993
- "aria-labelledby": METADATA_VALUE_LABEL_ID,
10994
- ...field2,
10995
- value: isDisabled ? placeholder : value,
10996
- disabled: isDisabled,
10997
- placeholder: "Value"
10998
- }
10999
- ) }) });
11000
- }
11001
- }
11002
- )
11003
- ]
11004
- }
11005
- ),
11006
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
11007
- /* @__PURE__ */ jsx(
11008
- DropdownMenu.Trigger,
11009
- {
11010
- className: clx(
11011
- "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
11012
- {
11013
- hidden: isDisabled
11014
- }
11015
- ),
11016
- disabled: isDisabled,
11017
- asChild: true,
11018
- children: /* @__PURE__ */ jsx(IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsx(EllipsisVertical, {}) })
11019
- }
11020
- ),
11021
- /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [
11022
- /* @__PURE__ */ jsxs(
11023
- DropdownMenu.Item,
11024
- {
11025
- className: "gap-x-2",
11026
- onClick: () => insertRow(index, "above"),
11027
- children: [
11028
- /* @__PURE__ */ jsx(ArrowUpMini, { className: "text-ui-fg-subtle" }),
11029
- "Insert row above"
11030
- ]
11031
- }
11032
- ),
11033
- /* @__PURE__ */ jsxs(
11034
- DropdownMenu.Item,
11035
- {
11036
- className: "gap-x-2",
11037
- onClick: () => insertRow(index, "below"),
11038
- children: [
11039
- /* @__PURE__ */ jsx(ArrowDownMini, { className: "text-ui-fg-subtle" }),
11040
- "Insert row below"
11041
- ]
11042
- }
11043
- ),
11044
- /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
11045
- /* @__PURE__ */ jsxs(
11046
- DropdownMenu.Item,
11047
- {
11048
- className: "gap-x-2",
11049
- onClick: () => deleteRow(index),
11050
- children: [
11051
- /* @__PURE__ */ jsx(Trash, { className: "text-ui-fg-subtle" }),
11052
- "Delete row"
11053
- ]
11054
- }
11055
- )
11056
- ] })
11057
- ] })
11058
- ] })
11059
- },
11060
- field.id
11061
- );
11062
- })
11063
- ] }),
11064
- hasUneditableRows && /* @__PURE__ */ jsx(InlineTip, { variant: "warning", label: "Some rows are disabled", children: "This object contains non-primitive metadata, such as arrays or objects, that can't be edited here. To edit the disabled rows, use the API directly." })
11065
- ] }),
11066
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11067
- /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
11068
- /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
11069
- ] }) })
11070
- ]
11071
- }
11072
- ) });
11073
- };
11074
- const GridInput = forwardRef(({ className, ...props }, ref) => {
11075
- return /* @__PURE__ */ jsx(
11076
- "input",
11077
- {
11078
- ref,
11079
- ...props,
11080
- autoComplete: "off",
11081
- className: clx(
11082
- "txt-compact-small text-ui-fg-base placeholder:text-ui-fg-muted disabled:text-ui-fg-disabled disabled:bg-ui-bg-base bg-transparent px-2 py-1.5 outline-none",
11083
- className
11084
- )
11085
- }
11086
- );
11087
- });
11088
- GridInput.displayName = "MetadataForm.GridInput";
11089
- const PlaceholderInner = () => {
11090
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
11091
- /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
11092
- /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
11093
- /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" }),
11094
- /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" })
11095
- ] }) })
11096
- ] });
11097
- };
11098
- const EDITABLE_TYPES = ["string", "number", "boolean"];
11099
- function getDefaultValues(metadata) {
11100
- if (!metadata || !Object.keys(metadata).length) {
11101
- return [
11102
- {
11103
- key: "",
11104
- value: "",
11105
- disabled: false
11106
- }
11107
- ];
11108
- }
11109
- return Object.entries(metadata).map(([key, value]) => {
11110
- if (!EDITABLE_TYPES.includes(typeof value)) {
11111
- return {
11112
- key,
11113
- value,
11114
- disabled: true
11115
- };
11116
- }
11117
- let stringValue = value;
11118
- if (typeof value !== "string") {
11119
- stringValue = JSON.stringify(value);
11120
- }
11121
- return {
11122
- key,
11123
- value: stringValue,
11124
- original_key: key
11125
- };
11126
- });
11127
- }
11128
- function parseValues(values) {
11129
- const metadata = values.metadata;
11130
- const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
11131
- if (isEmpty) {
11132
- return null;
11133
- }
11134
- const update = {};
11135
- metadata.forEach((field) => {
11136
- let key = field.key;
11137
- let value = field.value;
11138
- const disabled = field.disabled;
11139
- if (!key || !value) {
11140
- return;
11141
- }
11142
- if (disabled) {
11143
- update[key] = value;
11144
- return;
11145
- }
11146
- key = key.trim();
11147
- value = value.trim();
11148
- if (value === "true") {
11149
- update[key] = true;
11150
- } else if (value === "false") {
11151
- update[key] = false;
11152
- } else {
11153
- const parsedNumber = parseFloat(value);
11154
- if (!isNaN(parsedNumber)) {
11155
- update[key] = parsedNumber;
11156
- } else {
11157
- update[key] = value;
11158
- }
11159
- }
11160
- });
11161
- return update;
11162
- }
11163
- function getHasUneditableRows(metadata) {
11164
- if (!metadata) {
11165
- return false;
11166
- }
11167
- return Object.values(metadata).some(
11168
- (value) => !EDITABLE_TYPES.includes(typeof value)
11169
- );
11170
- }
11171
- const PROMOTION_QUERY_KEY = "promotions";
11172
- const promotionsQueryKeys = {
11173
- list: (query2) => [
11174
- PROMOTION_QUERY_KEY,
11175
- query2 ? query2 : void 0
11176
- ],
11177
- detail: (id, query2) => [
11178
- PROMOTION_QUERY_KEY,
11179
- id,
11180
- query2 ? query2 : void 0
11181
- ]
11182
- };
11183
- const usePromotions = (query2, options) => {
11184
- const { data, ...rest } = useQuery({
11185
- queryKey: promotionsQueryKeys.list(query2),
11186
- queryFn: async () => sdk.admin.promotion.list(query2),
11187
- ...options
11188
- });
11189
- return { ...data, ...rest };
11190
- };
11191
- const Promotions = () => {
11192
- const { id } = useParams();
11193
- const {
11194
- order: preview,
11195
- isError: isPreviewError,
11196
- error: previewError
11197
- } = useOrderPreview(id, void 0);
11198
- useInitiateOrderEdit({ preview });
11199
- const { onCancel } = useCancelOrderEdit({ preview });
11200
- if (isPreviewError) {
11201
- throw previewError;
11202
- }
11203
- const isReady = !!preview;
11204
- return /* @__PURE__ */ jsxs(RouteDrawer, { onClose: onCancel, children: [
11205
- /* @__PURE__ */ jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Edit Promotions" }) }) }),
11206
- isReady && /* @__PURE__ */ jsx(PromotionForm, { preview })
11207
- ] });
11208
- };
11209
- const PromotionForm = ({ preview }) => {
11210
- const { items, shipping_methods } = preview;
11211
- const [isSubmitting, setIsSubmitting] = useState(false);
11212
- const [comboboxValue, setComboboxValue] = useState("");
11213
- const { handleSuccess } = useRouteModal();
11214
- const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
11215
- const promoIds = getPromotionIds(items, shipping_methods);
11216
- const { promotions, isPending, isError, error } = usePromotions(
11217
- {
11218
- id: promoIds
11219
- },
11220
- {
11221
- enabled: !!promoIds.length
11222
- }
11223
- );
11224
- const comboboxData = useComboboxData({
11225
- queryKey: ["promotions", "combobox", promoIds],
11226
- queryFn: async (params) => {
11227
- return await sdk.admin.promotion.list({
11228
- ...params,
11229
- id: {
11230
- $nin: promoIds
11231
- }
11232
- });
11233
- },
11234
- getOptions: (data) => {
11235
- return data.promotions.map((promotion) => ({
11236
- label: promotion.code,
11237
- value: promotion.code
11238
- }));
11239
- }
11240
- });
11241
- const add = async (value) => {
11242
- if (!value) {
11243
- return;
11244
- }
11245
- addPromotions(
11246
- {
11247
- promo_codes: [value]
11248
- },
11249
- {
11250
- onError: (e) => {
11251
- toast.error(e.message);
11252
- comboboxData.onSearchValueChange("");
11253
- setComboboxValue("");
11254
- },
11255
- onSuccess: () => {
11256
- comboboxData.onSearchValueChange("");
11257
- setComboboxValue("");
11258
- }
11259
- }
11260
- );
11261
- };
11262
- const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
11263
- const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
11264
- const onSubmit = async () => {
11265
- setIsSubmitting(true);
11266
- let requestSucceeded = false;
11267
- await requestOrderEdit(void 0, {
11268
- onError: (e) => {
11269
- toast.error(e.message);
11270
- },
11271
- onSuccess: () => {
11272
- requestSucceeded = true;
11273
- }
11274
- });
11275
- if (!requestSucceeded) {
11276
- setIsSubmitting(false);
11277
- return;
11278
- }
11279
- await confirmOrderEdit(void 0, {
11280
- onError: (e) => {
11281
- toast.error(e.message);
11282
- },
11283
- onSuccess: () => {
11284
- handleSuccess();
11285
- },
11286
- onSettled: () => {
11287
- setIsSubmitting(false);
11288
- }
11289
- });
11290
- };
11291
- if (isError) {
11292
- throw error;
10940
+ };
10941
+ if (isError) {
10942
+ throw error;
11293
10943
  }
11294
10944
  return /* @__PURE__ */ jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
11295
10945
  /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
@@ -13032,11 +12682,361 @@ const Illustration = () => {
13032
12682
  ] })
13033
12683
  ]
13034
12684
  }
13035
- );
12685
+ );
12686
+ };
12687
+ const schema = objectType({
12688
+ customer_id: stringType().min(1)
12689
+ });
12690
+ const InlineTip = forwardRef(
12691
+ ({ variant = "tip", label, className, children, ...props }, ref) => {
12692
+ const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
12693
+ return /* @__PURE__ */ jsxs(
12694
+ "div",
12695
+ {
12696
+ ref,
12697
+ className: clx(
12698
+ "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
12699
+ className
12700
+ ),
12701
+ ...props,
12702
+ children: [
12703
+ /* @__PURE__ */ jsx(
12704
+ "div",
12705
+ {
12706
+ role: "presentation",
12707
+ className: clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
12708
+ "bg-ui-tag-orange-icon": variant === "warning"
12709
+ })
12710
+ }
12711
+ ),
12712
+ /* @__PURE__ */ jsxs("div", { className: "text-pretty", children: [
12713
+ /* @__PURE__ */ jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
12714
+ labelValue,
12715
+ ":"
12716
+ ] }),
12717
+ " ",
12718
+ children
12719
+ ] })
12720
+ ]
12721
+ }
12722
+ );
12723
+ }
12724
+ );
12725
+ InlineTip.displayName = "InlineTip";
12726
+ const MetadataFieldSchema = objectType({
12727
+ key: stringType(),
12728
+ disabled: booleanType().optional(),
12729
+ value: anyType()
12730
+ });
12731
+ const MetadataSchema = objectType({
12732
+ metadata: arrayType(MetadataFieldSchema)
12733
+ });
12734
+ const Metadata = () => {
12735
+ const { id } = useParams();
12736
+ const { order, isPending, isError, error } = useOrder(id, {
12737
+ fields: "metadata"
12738
+ });
12739
+ if (isError) {
12740
+ throw error;
12741
+ }
12742
+ const isReady = !isPending && !!order;
12743
+ return /* @__PURE__ */ jsxs(RouteDrawer, { children: [
12744
+ /* @__PURE__ */ jsxs(RouteDrawer.Header, { children: [
12745
+ /* @__PURE__ */ jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsx(Heading, { children: "Metadata" }) }),
12746
+ /* @__PURE__ */ jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
12747
+ ] }),
12748
+ !isReady ? /* @__PURE__ */ jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsx(MetadataForm, { orderId: id, metadata: order == null ? void 0 : order.metadata })
12749
+ ] });
12750
+ };
12751
+ const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
12752
+ const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
12753
+ const MetadataForm = ({ orderId, metadata }) => {
12754
+ const { handleSuccess } = useRouteModal();
12755
+ const hasUneditableRows = getHasUneditableRows(metadata);
12756
+ const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
12757
+ const form = useForm({
12758
+ defaultValues: {
12759
+ metadata: getDefaultValues(metadata)
12760
+ },
12761
+ resolver: zodResolver(MetadataSchema)
12762
+ });
12763
+ const handleSubmit = form.handleSubmit(async (data) => {
12764
+ const parsedData = parseValues(data);
12765
+ await mutateAsync(
12766
+ {
12767
+ metadata: parsedData
12768
+ },
12769
+ {
12770
+ onSuccess: () => {
12771
+ toast.success("Metadata updated");
12772
+ handleSuccess();
12773
+ },
12774
+ onError: (error) => {
12775
+ toast.error(error.message);
12776
+ }
12777
+ }
12778
+ );
12779
+ });
12780
+ const { fields, insert, remove } = useFieldArray({
12781
+ control: form.control,
12782
+ name: "metadata"
12783
+ });
12784
+ function deleteRow(index) {
12785
+ remove(index);
12786
+ if (fields.length === 1) {
12787
+ insert(0, {
12788
+ key: "",
12789
+ value: "",
12790
+ disabled: false
12791
+ });
12792
+ }
12793
+ }
12794
+ function insertRow(index, position) {
12795
+ insert(index + (position === "above" ? 0 : 1), {
12796
+ key: "",
12797
+ value: "",
12798
+ disabled: false
12799
+ });
12800
+ }
12801
+ return /* @__PURE__ */ jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxs(
12802
+ KeyboundForm,
12803
+ {
12804
+ onSubmit: handleSubmit,
12805
+ className: "flex flex-1 flex-col overflow-hidden",
12806
+ children: [
12807
+ /* @__PURE__ */ jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
12808
+ /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
12809
+ /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
12810
+ /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_KEY_LABEL_ID, children: "Key" }) }),
12811
+ /* @__PURE__ */ jsx("div", { className: "txt-compact-small-plus text-ui-fg-subtle px-2 py-1.5", children: /* @__PURE__ */ jsx("label", { id: METADATA_VALUE_LABEL_ID, children: "Value" }) })
12812
+ ] }),
12813
+ fields.map((field, index) => {
12814
+ const isDisabled = field.disabled || false;
12815
+ let placeholder = "-";
12816
+ if (typeof field.value === "object") {
12817
+ placeholder = "{ ... }";
12818
+ }
12819
+ if (Array.isArray(field.value)) {
12820
+ placeholder = "[ ... ]";
12821
+ }
12822
+ return /* @__PURE__ */ jsx(
12823
+ ConditionalTooltip,
12824
+ {
12825
+ showTooltip: isDisabled,
12826
+ content: "This row is disabled because it contains non-primitive data.",
12827
+ children: /* @__PURE__ */ jsxs("div", { className: "group/table relative", children: [
12828
+ /* @__PURE__ */ jsxs(
12829
+ "div",
12830
+ {
12831
+ className: clx("grid grid-cols-2 divide-x", {
12832
+ "overflow-hidden rounded-b-lg": index === fields.length - 1
12833
+ }),
12834
+ children: [
12835
+ /* @__PURE__ */ jsx(
12836
+ Form$2.Field,
12837
+ {
12838
+ control: form.control,
12839
+ name: `metadata.${index}.key`,
12840
+ render: ({ field: field2 }) => {
12841
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
12842
+ GridInput,
12843
+ {
12844
+ "aria-labelledby": METADATA_KEY_LABEL_ID,
12845
+ ...field2,
12846
+ disabled: isDisabled,
12847
+ placeholder: "Key"
12848
+ }
12849
+ ) }) });
12850
+ }
12851
+ }
12852
+ ),
12853
+ /* @__PURE__ */ jsx(
12854
+ Form$2.Field,
12855
+ {
12856
+ control: form.control,
12857
+ name: `metadata.${index}.value`,
12858
+ render: ({ field: { value, ...field2 } }) => {
12859
+ return /* @__PURE__ */ jsx(Form$2.Item, { children: /* @__PURE__ */ jsx(Form$2.Control, { children: /* @__PURE__ */ jsx(
12860
+ GridInput,
12861
+ {
12862
+ "aria-labelledby": METADATA_VALUE_LABEL_ID,
12863
+ ...field2,
12864
+ value: isDisabled ? placeholder : value,
12865
+ disabled: isDisabled,
12866
+ placeholder: "Value"
12867
+ }
12868
+ ) }) });
12869
+ }
12870
+ }
12871
+ )
12872
+ ]
12873
+ }
12874
+ ),
12875
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
12876
+ /* @__PURE__ */ jsx(
12877
+ DropdownMenu.Trigger,
12878
+ {
12879
+ className: clx(
12880
+ "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
12881
+ {
12882
+ hidden: isDisabled
12883
+ }
12884
+ ),
12885
+ disabled: isDisabled,
12886
+ asChild: true,
12887
+ children: /* @__PURE__ */ jsx(IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsx(EllipsisVertical, {}) })
12888
+ }
12889
+ ),
12890
+ /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [
12891
+ /* @__PURE__ */ jsxs(
12892
+ DropdownMenu.Item,
12893
+ {
12894
+ className: "gap-x-2",
12895
+ onClick: () => insertRow(index, "above"),
12896
+ children: [
12897
+ /* @__PURE__ */ jsx(ArrowUpMini, { className: "text-ui-fg-subtle" }),
12898
+ "Insert row above"
12899
+ ]
12900
+ }
12901
+ ),
12902
+ /* @__PURE__ */ jsxs(
12903
+ DropdownMenu.Item,
12904
+ {
12905
+ className: "gap-x-2",
12906
+ onClick: () => insertRow(index, "below"),
12907
+ children: [
12908
+ /* @__PURE__ */ jsx(ArrowDownMini, { className: "text-ui-fg-subtle" }),
12909
+ "Insert row below"
12910
+ ]
12911
+ }
12912
+ ),
12913
+ /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
12914
+ /* @__PURE__ */ jsxs(
12915
+ DropdownMenu.Item,
12916
+ {
12917
+ className: "gap-x-2",
12918
+ onClick: () => deleteRow(index),
12919
+ children: [
12920
+ /* @__PURE__ */ jsx(Trash, { className: "text-ui-fg-subtle" }),
12921
+ "Delete row"
12922
+ ]
12923
+ }
12924
+ )
12925
+ ] })
12926
+ ] })
12927
+ ] })
12928
+ },
12929
+ field.id
12930
+ );
12931
+ })
12932
+ ] }),
12933
+ hasUneditableRows && /* @__PURE__ */ jsx(InlineTip, { variant: "warning", label: "Some rows are disabled", children: "This object contains non-primitive metadata, such as arrays or objects, that can't be edited here. To edit the disabled rows, use the API directly." })
12934
+ ] }),
12935
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
12936
+ /* @__PURE__ */ jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
12937
+ /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
12938
+ ] }) })
12939
+ ]
12940
+ }
12941
+ ) });
13036
12942
  };
13037
- const schema = objectType({
13038
- customer_id: stringType().min(1)
12943
+ const GridInput = forwardRef(({ className, ...props }, ref) => {
12944
+ return /* @__PURE__ */ jsx(
12945
+ "input",
12946
+ {
12947
+ ref,
12948
+ ...props,
12949
+ autoComplete: "off",
12950
+ className: clx(
12951
+ "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",
12952
+ className
12953
+ )
12954
+ }
12955
+ );
13039
12956
  });
12957
+ GridInput.displayName = "MetadataForm.GridInput";
12958
+ const PlaceholderInner = () => {
12959
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
12960
+ /* @__PURE__ */ jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
12961
+ /* @__PURE__ */ jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
12962
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" }),
12963
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" })
12964
+ ] }) })
12965
+ ] });
12966
+ };
12967
+ const EDITABLE_TYPES = ["string", "number", "boolean"];
12968
+ function getDefaultValues(metadata) {
12969
+ if (!metadata || !Object.keys(metadata).length) {
12970
+ return [
12971
+ {
12972
+ key: "",
12973
+ value: "",
12974
+ disabled: false
12975
+ }
12976
+ ];
12977
+ }
12978
+ return Object.entries(metadata).map(([key, value]) => {
12979
+ if (!EDITABLE_TYPES.includes(typeof value)) {
12980
+ return {
12981
+ key,
12982
+ value,
12983
+ disabled: true
12984
+ };
12985
+ }
12986
+ let stringValue = value;
12987
+ if (typeof value !== "string") {
12988
+ stringValue = JSON.stringify(value);
12989
+ }
12990
+ return {
12991
+ key,
12992
+ value: stringValue,
12993
+ original_key: key
12994
+ };
12995
+ });
12996
+ }
12997
+ function parseValues(values) {
12998
+ const metadata = values.metadata;
12999
+ const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
13000
+ if (isEmpty) {
13001
+ return null;
13002
+ }
13003
+ const update = {};
13004
+ metadata.forEach((field) => {
13005
+ let key = field.key;
13006
+ let value = field.value;
13007
+ const disabled = field.disabled;
13008
+ if (!key || !value) {
13009
+ return;
13010
+ }
13011
+ if (disabled) {
13012
+ update[key] = value;
13013
+ return;
13014
+ }
13015
+ key = key.trim();
13016
+ value = value.trim();
13017
+ if (value === "true") {
13018
+ update[key] = true;
13019
+ } else if (value === "false") {
13020
+ update[key] = false;
13021
+ } else {
13022
+ const parsedNumber = parseFloat(value);
13023
+ if (!isNaN(parsedNumber)) {
13024
+ update[key] = parsedNumber;
13025
+ } else {
13026
+ update[key] = value;
13027
+ }
13028
+ }
13029
+ });
13030
+ return update;
13031
+ }
13032
+ function getHasUneditableRows(metadata) {
13033
+ if (!metadata) {
13034
+ return false;
13035
+ }
13036
+ return Object.values(metadata).some(
13037
+ (value) => !EDITABLE_TYPES.includes(typeof value)
13038
+ );
13039
+ }
13040
13040
  const widgetModule = { widgets: [] };
13041
13041
  const routeModule = {
13042
13042
  routes: [
@@ -13061,21 +13061,17 @@ const routeModule = {
13061
13061
  Component: BillingAddress,
13062
13062
  path: "/draft-orders/:id/billing-address"
13063
13063
  },
13064
- {
13065
- Component: CustomItems,
13066
- path: "/draft-orders/:id/custom-items"
13067
- },
13068
13064
  {
13069
13065
  Component: Email,
13070
13066
  path: "/draft-orders/:id/email"
13071
13067
  },
13072
13068
  {
13073
- Component: Items,
13074
- path: "/draft-orders/:id/items"
13069
+ Component: CustomItems,
13070
+ path: "/draft-orders/:id/custom-items"
13075
13071
  },
13076
13072
  {
13077
- Component: Metadata,
13078
- path: "/draft-orders/:id/metadata"
13073
+ Component: Items,
13074
+ path: "/draft-orders/:id/items"
13079
13075
  },
13080
13076
  {
13081
13077
  Component: Promotions,
@@ -13096,6 +13092,10 @@ const routeModule = {
13096
13092
  {
13097
13093
  Component: TransferOwnership,
13098
13094
  path: "/draft-orders/:id/transfer-ownership"
13095
+ },
13096
+ {
13097
+ Component: Metadata,
13098
+ path: "/draft-orders/:id/metadata"
13099
13099
  }
13100
13100
  ]
13101
13101
  }