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

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