@medusajs/draft-order 2.20.0-snapshot-20260901131559 → 2.20.0

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.
@@ -5979,27 +5979,6 @@ const ID = () => {
5979
5979
  }
5980
5980
  );
5981
5981
  };
5982
- const CustomItems = () => {
5983
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
5984
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Custom Items" }) }) }),
5985
- /* @__PURE__ */ jsxRuntime.jsx(CustomItemsForm, {})
5986
- ] });
5987
- };
5988
- const CustomItemsForm = () => {
5989
- const form = reactHookForm.useForm({
5990
- resolver: zod$1.zodResolver(schema$5)
5991
- });
5992
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
5993
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, {}),
5994
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
5995
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
5996
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", children: "Save" })
5997
- ] }) })
5998
- ] }) });
5999
- };
6000
- const schema$5 = zod.z.object({
6001
- email: zod.z.string().email()
6002
- });
6003
5982
  const BillingAddress = () => {
6004
5983
  const { id } = reactRouterDom.useParams();
6005
5984
  const { order, isPending, isError, error } = useOrder(id, {
@@ -6031,7 +6010,7 @@ const BillingAddressForm = ({ order }) => {
6031
6010
  postal_code: order.billing_address?.postal_code ?? "",
6032
6011
  phone: order.billing_address?.phone ?? ""
6033
6012
  },
6034
- resolver: zod$1.zodResolver(schema$4)
6013
+ resolver: zod$1.zodResolver(schema$5)
6035
6014
  });
6036
6015
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
6037
6016
  const { handleSuccess } = useRouteModal();
@@ -6188,7 +6167,28 @@ const BillingAddressForm = ({ order }) => {
6188
6167
  }
6189
6168
  ) });
6190
6169
  };
6191
- const schema$4 = addressSchema;
6170
+ const schema$5 = addressSchema;
6171
+ const CustomItems = () => {
6172
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
6173
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Custom Items" }) }) }),
6174
+ /* @__PURE__ */ jsxRuntime.jsx(CustomItemsForm, {})
6175
+ ] });
6176
+ };
6177
+ const CustomItemsForm = () => {
6178
+ const form = reactHookForm.useForm({
6179
+ resolver: zod$1.zodResolver(schema$4)
6180
+ });
6181
+ return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
6182
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, {}),
6183
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
6184
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
6185
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", children: "Save" })
6186
+ ] }) })
6187
+ ] }) });
6188
+ };
6189
+ const schema$4 = zod.z.object({
6190
+ email: zod.z.string().email()
6191
+ });
6192
6192
  const Email = () => {
6193
6193
  const { id } = reactRouterDom.useParams();
6194
6194
  const { order, isPending, isError, error } = useOrder(id, {
@@ -6983,7 +6983,8 @@ const ExistingItemsForm = ({ orderId, items }) => {
6983
6983
  q,
6984
6984
  order,
6985
6985
  offset: offset ? parseInt(offset) : void 0,
6986
- limit: LIMIT
6986
+ limit: LIMIT,
6987
+ fields: "*inventory_items.inventory.location_levels,+inventory_quantity,+manage_inventory"
6987
6988
  },
6988
6989
  {
6989
6990
  placeholderData: reactQuery.keepPreviousData
@@ -7062,6 +7063,23 @@ const ExistingItemsForm = ({ orderId, items }) => {
7062
7063
  );
7063
7064
  };
7064
7065
  const columnHelper = ui.createDataTableColumnHelper();
7066
+ const getVariantInventory = (variant) => {
7067
+ if (!variant.manage_inventory) {
7068
+ return { text: "Not managed", hasInventoryKit: false, notManaged: true };
7069
+ }
7070
+ const quantity = variant.inventory_quantity;
7071
+ const inventoryItems = variant.inventory_items?.map((i) => i.inventory).filter(Boolean) ?? [];
7072
+ const hasInventoryKit = inventoryItems.length > 1;
7073
+ const locations = {};
7074
+ inventoryItems.forEach((i) => {
7075
+ i.location_levels?.forEach((l) => {
7076
+ locations[l.id] = true;
7077
+ });
7078
+ });
7079
+ const locationCount = Object.keys(locations).length;
7080
+ const text = hasInventoryKit ? `${quantity} available` : `${quantity} available at ${locationCount} location${locationCount === 1 ? "" : "s"}`;
7081
+ return { text, hasInventoryKit, quantity, notManaged: false };
7082
+ };
7065
7083
  const useColumns = () => {
7066
7084
  return React.useMemo(() => {
7067
7085
  return [
@@ -7122,6 +7140,25 @@ const useColumns = () => {
7122
7140
  enableSorting: true,
7123
7141
  sortAscLabel: "Oldest first",
7124
7142
  sortDescLabel: "Newest first"
7143
+ }),
7144
+ columnHelper.display({
7145
+ id: "inventory",
7146
+ header: "Stock",
7147
+ cell: ({ row }) => {
7148
+ const { text, hasInventoryKit, quantity, notManaged } = getVariantInventory(row.original);
7149
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { content: text, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full w-full items-center gap-2 overflow-hidden", children: [
7150
+ hasInventoryKit && /* @__PURE__ */ jsxRuntime.jsx(icons.Component, {}),
7151
+ /* @__PURE__ */ jsxRuntime.jsx(
7152
+ "span",
7153
+ {
7154
+ className: ui.clx("truncate", {
7155
+ "text-ui-fg-error": !quantity && !notManaged
7156
+ }),
7157
+ children: text
7158
+ }
7159
+ )
7160
+ ] }) });
7161
+ }
7125
7162
  })
7126
7163
  ];
7127
7164
  }, []);
@@ -7239,478 +7276,128 @@ const customItemSchema = zod.z.object({
7239
7276
  quantity: zod.z.number(),
7240
7277
  unit_price: zod.z.union([zod.z.number(), zod.z.string()])
7241
7278
  });
7242
- const InlineTip = React.forwardRef(
7243
- ({ variant = "tip", label, className, children, ...props }, ref) => {
7244
- const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
7245
- return /* @__PURE__ */ jsxRuntime.jsxs(
7246
- "div",
7247
- {
7248
- ref,
7249
- className: ui.clx(
7250
- "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
7251
- className
7252
- ),
7253
- ...props,
7254
- children: [
7255
- /* @__PURE__ */ jsxRuntime.jsx(
7256
- "div",
7257
- {
7258
- role: "presentation",
7259
- className: ui.clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
7260
- "bg-ui-tag-orange-icon": variant === "warning"
7261
- })
7262
- }
7263
- ),
7264
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-pretty", children: [
7265
- /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
7266
- labelValue,
7267
- ":"
7268
- ] }),
7269
- " ",
7270
- children
7271
- ] })
7272
- ]
7273
- }
7274
- );
7275
- }
7276
- );
7277
- InlineTip.displayName = "InlineTip";
7278
- const MetadataFieldSchema = zod.z.object({
7279
- key: zod.z.string(),
7280
- disabled: zod.z.boolean().optional(),
7281
- value: zod.z.any()
7282
- });
7283
- const MetadataSchema = zod.z.object({
7284
- metadata: zod.z.array(MetadataFieldSchema)
7285
- });
7286
- const Metadata = () => {
7287
- const { id } = reactRouterDom.useParams();
7288
- const { order, isPending, isError, error } = useOrder(id, {
7289
- fields: "metadata"
7279
+ const PROMOTION_QUERY_KEY = "promotions";
7280
+ const promotionsQueryKeys = {
7281
+ list: (query2) => [
7282
+ PROMOTION_QUERY_KEY,
7283
+ query2 ? query2 : void 0
7284
+ ],
7285
+ detail: (id, query2) => [
7286
+ PROMOTION_QUERY_KEY,
7287
+ id,
7288
+ query2 ? query2 : void 0
7289
+ ]
7290
+ };
7291
+ const usePromotions = (query2, options) => {
7292
+ const { data, ...rest } = reactQuery.useQuery({
7293
+ queryKey: promotionsQueryKeys.list(query2),
7294
+ queryFn: async () => sdk.admin.promotion.list(query2),
7295
+ ...options
7290
7296
  });
7291
- if (isError) {
7292
- throw error;
7297
+ return { ...data, ...rest };
7298
+ };
7299
+ const Promotions = () => {
7300
+ const { id } = reactRouterDom.useParams();
7301
+ const {
7302
+ order: preview,
7303
+ isError: isPreviewError,
7304
+ error: previewError
7305
+ } = useOrderPreview(id, void 0);
7306
+ useInitiateOrderEdit({ preview });
7307
+ const { onCancel } = useCancelOrderEdit({ preview });
7308
+ if (isPreviewError) {
7309
+ throw previewError;
7293
7310
  }
7294
- const isReady = !isPending && !!order;
7295
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
7296
- /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
7297
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Metadata" }) }),
7298
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
7299
- ] }),
7300
- !isReady ? /* @__PURE__ */ jsxRuntime.jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsxRuntime.jsx(MetadataForm, { orderId: id, metadata: order?.metadata })
7311
+ const isReady = !!preview;
7312
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { onClose: onCancel, children: [
7313
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Promotions" }) }) }),
7314
+ isReady && /* @__PURE__ */ jsxRuntime.jsx(PromotionForm, { preview })
7301
7315
  ] });
7302
7316
  };
7303
- const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
7304
- const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
7305
- const MetadataForm = ({ orderId, metadata }) => {
7317
+ const PromotionForm = ({ preview }) => {
7318
+ const { items, shipping_methods } = preview;
7319
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
7320
+ const [comboboxValue, setComboboxValue] = React.useState("");
7306
7321
  const { handleSuccess } = useRouteModal();
7307
- const hasUneditableRows = getHasUneditableRows(metadata);
7308
- const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
7309
- const form = reactHookForm.useForm({
7310
- defaultValues: {
7311
- metadata: getDefaultValues(metadata)
7322
+ const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
7323
+ const promoIds = getPromotionIds(items, shipping_methods);
7324
+ const { promotions, isPending, isError, error } = usePromotions(
7325
+ {
7326
+ id: promoIds
7312
7327
  },
7313
- resolver: zod$1.zodResolver(MetadataSchema)
7328
+ {
7329
+ enabled: !!promoIds.length
7330
+ }
7331
+ );
7332
+ const comboboxData = useComboboxData({
7333
+ queryKey: ["promotions", "combobox", promoIds],
7334
+ queryFn: async (params) => {
7335
+ return await sdk.admin.promotion.list({
7336
+ ...params,
7337
+ id: {
7338
+ $nin: promoIds
7339
+ }
7340
+ });
7341
+ },
7342
+ getOptions: (data) => {
7343
+ return data.promotions.map((promotion) => ({
7344
+ label: promotion.code,
7345
+ value: promotion.code
7346
+ }));
7347
+ }
7314
7348
  });
7315
- const handleSubmit = form.handleSubmit(async (data) => {
7316
- const parsedData = parseValues(data);
7317
- await mutateAsync(
7349
+ const add = async (value) => {
7350
+ if (!value) {
7351
+ return;
7352
+ }
7353
+ addPromotions(
7318
7354
  {
7319
- metadata: parsedData
7355
+ promo_codes: [value]
7320
7356
  },
7321
7357
  {
7322
- onSuccess: () => {
7323
- ui.toast.success("Metadata updated");
7324
- handleSuccess();
7358
+ onError: (e) => {
7359
+ ui.toast.error(e.message);
7360
+ comboboxData.onSearchValueChange("");
7361
+ setComboboxValue("");
7325
7362
  },
7326
- onError: (error) => {
7327
- ui.toast.error(error.message);
7363
+ onSuccess: () => {
7364
+ comboboxData.onSearchValueChange("");
7365
+ setComboboxValue("");
7328
7366
  }
7329
7367
  }
7330
7368
  );
7331
- });
7332
- const { fields, insert, remove } = reactHookForm.useFieldArray({
7333
- control: form.control,
7334
- name: "metadata"
7335
- });
7336
- function deleteRow(index) {
7337
- remove(index);
7338
- if (fields.length === 1) {
7339
- insert(0, {
7340
- key: "",
7341
- value: "",
7342
- disabled: false
7343
- });
7369
+ };
7370
+ const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
7371
+ const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
7372
+ const onSubmit = async () => {
7373
+ setIsSubmitting(true);
7374
+ let requestSucceeded = false;
7375
+ await requestOrderEdit(void 0, {
7376
+ onError: (e) => {
7377
+ ui.toast.error(e.message);
7378
+ },
7379
+ onSuccess: () => {
7380
+ requestSucceeded = true;
7381
+ }
7382
+ });
7383
+ if (!requestSucceeded) {
7384
+ setIsSubmitting(false);
7385
+ return;
7344
7386
  }
7345
- }
7346
- function insertRow(index, position) {
7347
- insert(index + (position === "above" ? 0 : 1), {
7348
- key: "",
7349
- value: "",
7350
- disabled: false
7387
+ await confirmOrderEdit(void 0, {
7388
+ onError: (e) => {
7389
+ ui.toast.error(e.message);
7390
+ },
7391
+ onSuccess: () => {
7392
+ handleSuccess();
7393
+ },
7394
+ onSettled: () => {
7395
+ setIsSubmitting(false);
7396
+ }
7351
7397
  });
7352
- }
7353
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
7354
- KeyboundForm,
7355
- {
7356
- onSubmit: handleSubmit,
7357
- className: "flex flex-1 flex-col overflow-hidden",
7358
- children: [
7359
- /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
7360
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
7361
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
7362
- /* @__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" }) }),
7363
- /* @__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" }) })
7364
- ] }),
7365
- fields.map((field, index) => {
7366
- const isDisabled = field.disabled || false;
7367
- let placeholder = "-";
7368
- if (typeof field.value === "object") {
7369
- placeholder = "{ ... }";
7370
- }
7371
- if (Array.isArray(field.value)) {
7372
- placeholder = "[ ... ]";
7373
- }
7374
- return /* @__PURE__ */ jsxRuntime.jsx(
7375
- ConditionalTooltip,
7376
- {
7377
- showTooltip: isDisabled,
7378
- content: "This row is disabled because it contains non-primitive data.",
7379
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group/table relative", children: [
7380
- /* @__PURE__ */ jsxRuntime.jsxs(
7381
- "div",
7382
- {
7383
- className: ui.clx("grid grid-cols-2 divide-x", {
7384
- "overflow-hidden rounded-b-lg": index === fields.length - 1
7385
- }),
7386
- children: [
7387
- /* @__PURE__ */ jsxRuntime.jsx(
7388
- Form$2.Field,
7389
- {
7390
- control: form.control,
7391
- name: `metadata.${index}.key`,
7392
- render: ({ field: field2 }) => {
7393
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7394
- GridInput,
7395
- {
7396
- "aria-labelledby": METADATA_KEY_LABEL_ID,
7397
- ...field2,
7398
- disabled: isDisabled,
7399
- placeholder: "Key"
7400
- }
7401
- ) }) });
7402
- }
7403
- }
7404
- ),
7405
- /* @__PURE__ */ jsxRuntime.jsx(
7406
- Form$2.Field,
7407
- {
7408
- control: form.control,
7409
- name: `metadata.${index}.value`,
7410
- render: ({ field: { value, ...field2 } }) => {
7411
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7412
- GridInput,
7413
- {
7414
- "aria-labelledby": METADATA_VALUE_LABEL_ID,
7415
- ...field2,
7416
- value: isDisabled ? placeholder : value,
7417
- disabled: isDisabled,
7418
- placeholder: "Value"
7419
- }
7420
- ) }) });
7421
- }
7422
- }
7423
- )
7424
- ]
7425
- }
7426
- ),
7427
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
7428
- /* @__PURE__ */ jsxRuntime.jsx(
7429
- ui.DropdownMenu.Trigger,
7430
- {
7431
- className: ui.clx(
7432
- "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
7433
- {
7434
- hidden: isDisabled
7435
- }
7436
- ),
7437
- disabled: isDisabled,
7438
- asChild: true,
7439
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisVertical, {}) })
7440
- }
7441
- ),
7442
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
7443
- /* @__PURE__ */ jsxRuntime.jsxs(
7444
- ui.DropdownMenu.Item,
7445
- {
7446
- className: "gap-x-2",
7447
- onClick: () => insertRow(index, "above"),
7448
- children: [
7449
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpMini, { className: "text-ui-fg-subtle" }),
7450
- "Insert row above"
7451
- ]
7452
- }
7453
- ),
7454
- /* @__PURE__ */ jsxRuntime.jsxs(
7455
- ui.DropdownMenu.Item,
7456
- {
7457
- className: "gap-x-2",
7458
- onClick: () => insertRow(index, "below"),
7459
- children: [
7460
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownMini, { className: "text-ui-fg-subtle" }),
7461
- "Insert row below"
7462
- ]
7463
- }
7464
- ),
7465
- /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
7466
- /* @__PURE__ */ jsxRuntime.jsxs(
7467
- ui.DropdownMenu.Item,
7468
- {
7469
- className: "gap-x-2",
7470
- onClick: () => deleteRow(index),
7471
- children: [
7472
- /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
7473
- "Delete row"
7474
- ]
7475
- }
7476
- )
7477
- ] })
7478
- ] })
7479
- ] })
7480
- },
7481
- field.id
7482
- );
7483
- })
7484
- ] }),
7485
- 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." })
7486
- ] }),
7487
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
7488
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
7489
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
7490
- ] }) })
7491
- ]
7492
- }
7493
- ) });
7494
- };
7495
- const GridInput = React.forwardRef(({ className, ...props }, ref) => {
7496
- return /* @__PURE__ */ jsxRuntime.jsx(
7497
- "input",
7498
- {
7499
- ref,
7500
- ...props,
7501
- autoComplete: "off",
7502
- className: ui.clx(
7503
- "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",
7504
- className
7505
- )
7506
- }
7507
- );
7508
- });
7509
- GridInput.displayName = "MetadataForm.GridInput";
7510
- const PlaceholderInner = () => {
7511
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
7512
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
7513
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
7514
- /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }),
7515
- /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" })
7516
- ] }) })
7517
- ] });
7518
- };
7519
- const EDITABLE_TYPES = ["string", "number", "boolean"];
7520
- function getDefaultValues(metadata) {
7521
- if (!metadata || !Object.keys(metadata).length) {
7522
- return [
7523
- {
7524
- key: "",
7525
- value: "",
7526
- disabled: false
7527
- }
7528
- ];
7529
- }
7530
- return Object.entries(metadata).map(([key, value]) => {
7531
- if (!EDITABLE_TYPES.includes(typeof value)) {
7532
- return {
7533
- key,
7534
- value,
7535
- disabled: true
7536
- };
7537
- }
7538
- let stringValue = value;
7539
- if (typeof value !== "string") {
7540
- stringValue = JSON.stringify(value);
7541
- }
7542
- return {
7543
- key,
7544
- value: stringValue,
7545
- original_key: key
7546
- };
7547
- });
7548
- }
7549
- function parseValues(values) {
7550
- const metadata = values.metadata;
7551
- const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
7552
- if (isEmpty) {
7553
- return null;
7554
- }
7555
- const update = {};
7556
- metadata.forEach((field) => {
7557
- let key = field.key;
7558
- let value = field.value;
7559
- const disabled = field.disabled;
7560
- if (!key || !value) {
7561
- return;
7562
- }
7563
- if (disabled) {
7564
- update[key] = value;
7565
- return;
7566
- }
7567
- key = key.trim();
7568
- value = value.trim();
7569
- if (value === "true") {
7570
- update[key] = true;
7571
- } else if (value === "false") {
7572
- update[key] = false;
7573
- } else {
7574
- const parsedNumber = parseFloat(value);
7575
- if (!isNaN(parsedNumber)) {
7576
- update[key] = parsedNumber;
7577
- } else {
7578
- update[key] = value;
7579
- }
7580
- }
7581
- });
7582
- return update;
7583
- }
7584
- function getHasUneditableRows(metadata) {
7585
- if (!metadata) {
7586
- return false;
7587
- }
7588
- return Object.values(metadata).some(
7589
- (value) => !EDITABLE_TYPES.includes(typeof value)
7590
- );
7591
- }
7592
- const PROMOTION_QUERY_KEY = "promotions";
7593
- const promotionsQueryKeys = {
7594
- list: (query2) => [
7595
- PROMOTION_QUERY_KEY,
7596
- query2 ? query2 : void 0
7597
- ],
7598
- detail: (id, query2) => [
7599
- PROMOTION_QUERY_KEY,
7600
- id,
7601
- query2 ? query2 : void 0
7602
- ]
7603
- };
7604
- const usePromotions = (query2, options) => {
7605
- const { data, ...rest } = reactQuery.useQuery({
7606
- queryKey: promotionsQueryKeys.list(query2),
7607
- queryFn: async () => sdk.admin.promotion.list(query2),
7608
- ...options
7609
- });
7610
- return { ...data, ...rest };
7611
- };
7612
- const Promotions = () => {
7613
- const { id } = reactRouterDom.useParams();
7614
- const {
7615
- order: preview,
7616
- isError: isPreviewError,
7617
- error: previewError
7618
- } = useOrderPreview(id, void 0);
7619
- useInitiateOrderEdit({ preview });
7620
- const { onCancel } = useCancelOrderEdit({ preview });
7621
- if (isPreviewError) {
7622
- throw previewError;
7623
- }
7624
- const isReady = !!preview;
7625
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { onClose: onCancel, children: [
7626
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Promotions" }) }) }),
7627
- isReady && /* @__PURE__ */ jsxRuntime.jsx(PromotionForm, { preview })
7628
- ] });
7629
- };
7630
- const PromotionForm = ({ preview }) => {
7631
- const { items, shipping_methods } = preview;
7632
- const [isSubmitting, setIsSubmitting] = React.useState(false);
7633
- const [comboboxValue, setComboboxValue] = React.useState("");
7634
- const { handleSuccess } = useRouteModal();
7635
- const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
7636
- const promoIds = getPromotionIds(items, shipping_methods);
7637
- const { promotions, isPending, isError, error } = usePromotions(
7638
- {
7639
- id: promoIds
7640
- },
7641
- {
7642
- enabled: !!promoIds.length
7643
- }
7644
- );
7645
- const comboboxData = useComboboxData({
7646
- queryKey: ["promotions", "combobox", promoIds],
7647
- queryFn: async (params) => {
7648
- return await sdk.admin.promotion.list({
7649
- ...params,
7650
- id: {
7651
- $nin: promoIds
7652
- }
7653
- });
7654
- },
7655
- getOptions: (data) => {
7656
- return data.promotions.map((promotion) => ({
7657
- label: promotion.code,
7658
- value: promotion.code
7659
- }));
7660
- }
7661
- });
7662
- const add = async (value) => {
7663
- if (!value) {
7664
- return;
7665
- }
7666
- addPromotions(
7667
- {
7668
- promo_codes: [value]
7669
- },
7670
- {
7671
- onError: (e) => {
7672
- ui.toast.error(e.message);
7673
- comboboxData.onSearchValueChange("");
7674
- setComboboxValue("");
7675
- },
7676
- onSuccess: () => {
7677
- comboboxData.onSearchValueChange("");
7678
- setComboboxValue("");
7679
- }
7680
- }
7681
- );
7682
- };
7683
- const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
7684
- const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
7685
- const onSubmit = async () => {
7686
- setIsSubmitting(true);
7687
- let requestSucceeded = false;
7688
- await requestOrderEdit(void 0, {
7689
- onError: (e) => {
7690
- ui.toast.error(e.message);
7691
- },
7692
- onSuccess: () => {
7693
- requestSucceeded = true;
7694
- }
7695
- });
7696
- if (!requestSucceeded) {
7697
- setIsSubmitting(false);
7698
- return;
7699
- }
7700
- await confirmOrderEdit(void 0, {
7701
- onError: (e) => {
7702
- ui.toast.error(e.message);
7703
- },
7704
- onSuccess: () => {
7705
- handleSuccess();
7706
- },
7707
- onSettled: () => {
7708
- setIsSubmitting(false);
7709
- }
7710
- });
7711
- };
7712
- if (isError) {
7713
- throw error;
7398
+ };
7399
+ if (isError) {
7400
+ throw error;
7714
7401
  }
7715
7402
  return /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
7716
7403
  /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
@@ -7760,109 +7447,565 @@ const PromotionForm = ({ preview }) => {
7760
7447
  ] }) })
7761
7448
  ] });
7762
7449
  };
7763
- const PromotionItem = ({
7764
- promotion,
7765
- orderId,
7766
- isLoading
7767
- }) => {
7768
- const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
7769
- const onRemove = async () => {
7770
- removePromotions(
7450
+ const PromotionItem = ({
7451
+ promotion,
7452
+ orderId,
7453
+ isLoading
7454
+ }) => {
7455
+ const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
7456
+ const onRemove = async () => {
7457
+ removePromotions(
7458
+ {
7459
+ promo_codes: [promotion.code]
7460
+ },
7461
+ {
7462
+ onError: (e) => {
7463
+ ui.toast.error(e.message);
7464
+ }
7465
+ }
7466
+ );
7467
+ };
7468
+ const displayValue = getDisplayValue(promotion);
7469
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7470
+ "div",
7471
+ {
7472
+ className: ui.clx(
7473
+ "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
7474
+ {
7475
+ "animate-pulse": isLoading
7476
+ }
7477
+ ),
7478
+ children: [
7479
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7480
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
7481
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
7482
+ displayValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
7483
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: displayValue }),
7484
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "·" })
7485
+ ] }),
7486
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "capitalize", children: promotion.application_method?.allocation })
7487
+ ] })
7488
+ ] }),
7489
+ /* @__PURE__ */ jsxRuntime.jsx(
7490
+ ui.IconButton,
7491
+ {
7492
+ size: "small",
7493
+ type: "button",
7494
+ variant: "transparent",
7495
+ onClick: onRemove,
7496
+ isLoading: isPending || isLoading,
7497
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, {})
7498
+ }
7499
+ )
7500
+ ]
7501
+ },
7502
+ promotion.id
7503
+ );
7504
+ };
7505
+ function getDisplayValue(promotion) {
7506
+ const value = promotion.application_method?.value;
7507
+ if (!value) {
7508
+ return null;
7509
+ }
7510
+ if (promotion.application_method?.type === "fixed") {
7511
+ const currency = promotion.application_method?.currency_code;
7512
+ if (!currency) {
7513
+ return null;
7514
+ }
7515
+ return getLocaleAmount(value, currency);
7516
+ } else if (promotion.application_method?.type === "percentage") {
7517
+ return formatPercentage(value);
7518
+ }
7519
+ return null;
7520
+ }
7521
+ const formatter = new Intl.NumberFormat([], {
7522
+ style: "percent",
7523
+ minimumFractionDigits: 2
7524
+ });
7525
+ const formatPercentage = (value, isPercentageValue = false) => {
7526
+ let val = value || 0;
7527
+ if (!isPercentageValue) {
7528
+ val = val / 100;
7529
+ }
7530
+ return formatter.format(val);
7531
+ };
7532
+ function getPromotionIds(items, shippingMethods) {
7533
+ const promotionIds = /* @__PURE__ */ new Set();
7534
+ for (const item of items) {
7535
+ if (item.adjustments) {
7536
+ for (const adjustment of item.adjustments) {
7537
+ if (adjustment.promotion_id) {
7538
+ promotionIds.add(adjustment.promotion_id);
7539
+ }
7540
+ }
7541
+ }
7542
+ }
7543
+ for (const shippingMethod of shippingMethods) {
7544
+ if (shippingMethod.adjustments) {
7545
+ for (const adjustment of shippingMethod.adjustments) {
7546
+ if (adjustment.promotion_id) {
7547
+ promotionIds.add(adjustment.promotion_id);
7548
+ }
7549
+ }
7550
+ }
7551
+ }
7552
+ return Array.from(promotionIds);
7553
+ }
7554
+ const SalesChannel = () => {
7555
+ const { id } = reactRouterDom.useParams();
7556
+ const { draft_order, isPending, isError, error } = useDraftOrder(
7557
+ id,
7558
+ {
7559
+ fields: "+sales_channel_id"
7560
+ },
7561
+ {
7562
+ enabled: !!id
7563
+ }
7564
+ );
7565
+ if (isError) {
7566
+ throw error;
7567
+ }
7568
+ const ISrEADY = !!draft_order && !isPending;
7569
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
7570
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
7571
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Sales Channel" }) }),
7572
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Update which sales channel the draft order is associated with" }) })
7573
+ ] }),
7574
+ ISrEADY && /* @__PURE__ */ jsxRuntime.jsx(SalesChannelForm, { order: draft_order })
7575
+ ] });
7576
+ };
7577
+ const SalesChannelForm = ({ order }) => {
7578
+ const form = reactHookForm.useForm({
7579
+ defaultValues: {
7580
+ sales_channel_id: order.sales_channel_id || ""
7581
+ },
7582
+ resolver: zod$1.zodResolver(schema$2)
7583
+ });
7584
+ const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
7585
+ const { handleSuccess } = useRouteModal();
7586
+ const onSubmit = form.handleSubmit(async (data) => {
7587
+ await mutateAsync(
7588
+ {
7589
+ sales_channel_id: data.sales_channel_id
7590
+ },
7591
+ {
7592
+ onSuccess: () => {
7593
+ ui.toast.success("Sales channel updated");
7594
+ handleSuccess();
7595
+ },
7596
+ onError: (error) => {
7597
+ ui.toast.error(error.message);
7598
+ }
7599
+ }
7600
+ );
7601
+ });
7602
+ return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
7603
+ KeyboundForm,
7604
+ {
7605
+ className: "flex flex-1 flex-col overflow-hidden",
7606
+ onSubmit,
7607
+ children: [
7608
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { className: "flex flex-col gap-y-6 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(SalesChannelField, { control: form.control, order }) }),
7609
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
7610
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
7611
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
7612
+ ] }) })
7613
+ ]
7614
+ }
7615
+ ) });
7616
+ };
7617
+ const SalesChannelField = ({ control, order }) => {
7618
+ const salesChannels = useComboboxData({
7619
+ queryFn: async (params) => {
7620
+ return await sdk.admin.salesChannel.list(params);
7621
+ },
7622
+ queryKey: ["sales-channels"],
7623
+ getOptions: (data) => {
7624
+ return data.sales_channels.map((salesChannel) => ({
7625
+ label: salesChannel.name,
7626
+ value: salesChannel.id
7627
+ }));
7628
+ },
7629
+ defaultValue: order.sales_channel_id || void 0
7630
+ });
7631
+ return /* @__PURE__ */ jsxRuntime.jsx(
7632
+ Form$2.Field,
7633
+ {
7634
+ control,
7635
+ name: "sales_channel_id",
7636
+ render: ({ field }) => {
7637
+ return /* @__PURE__ */ jsxRuntime.jsxs(Form$2.Item, { children: [
7638
+ /* @__PURE__ */ jsxRuntime.jsx(Form$2.Label, { children: "Sales Channel" }),
7639
+ /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7640
+ Combobox,
7641
+ {
7642
+ options: salesChannels.options,
7643
+ fetchNextPage: salesChannels.fetchNextPage,
7644
+ isFetchingNextPage: salesChannels.isFetchingNextPage,
7645
+ searchValue: salesChannels.searchValue,
7646
+ onSearchValueChange: salesChannels.onSearchValueChange,
7647
+ placeholder: "Select sales channel",
7648
+ ...field
7649
+ }
7650
+ ) }),
7651
+ /* @__PURE__ */ jsxRuntime.jsx(Form$2.ErrorMessage, {})
7652
+ ] });
7653
+ }
7654
+ }
7655
+ );
7656
+ };
7657
+ const schema$2 = zod.z.object({
7658
+ sales_channel_id: zod.z.string().min(1)
7659
+ });
7660
+ const InlineTip = React.forwardRef(
7661
+ ({ variant = "tip", label, className, children, ...props }, ref) => {
7662
+ const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
7663
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7664
+ "div",
7665
+ {
7666
+ ref,
7667
+ className: ui.clx(
7668
+ "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
7669
+ className
7670
+ ),
7671
+ ...props,
7672
+ children: [
7673
+ /* @__PURE__ */ jsxRuntime.jsx(
7674
+ "div",
7675
+ {
7676
+ role: "presentation",
7677
+ className: ui.clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
7678
+ "bg-ui-tag-orange-icon": variant === "warning"
7679
+ })
7680
+ }
7681
+ ),
7682
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-pretty", children: [
7683
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
7684
+ labelValue,
7685
+ ":"
7686
+ ] }),
7687
+ " ",
7688
+ children
7689
+ ] })
7690
+ ]
7691
+ }
7692
+ );
7693
+ }
7694
+ );
7695
+ InlineTip.displayName = "InlineTip";
7696
+ const MetadataFieldSchema = zod.z.object({
7697
+ key: zod.z.string(),
7698
+ disabled: zod.z.boolean().optional(),
7699
+ value: zod.z.any()
7700
+ });
7701
+ const MetadataSchema = zod.z.object({
7702
+ metadata: zod.z.array(MetadataFieldSchema)
7703
+ });
7704
+ const Metadata = () => {
7705
+ const { id } = reactRouterDom.useParams();
7706
+ const { order, isPending, isError, error } = useOrder(id, {
7707
+ fields: "metadata"
7708
+ });
7709
+ if (isError) {
7710
+ throw error;
7711
+ }
7712
+ const isReady = !isPending && !!order;
7713
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
7714
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
7715
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Metadata" }) }),
7716
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
7717
+ ] }),
7718
+ !isReady ? /* @__PURE__ */ jsxRuntime.jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsxRuntime.jsx(MetadataForm, { orderId: id, metadata: order?.metadata })
7719
+ ] });
7720
+ };
7721
+ const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
7722
+ const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
7723
+ const MetadataForm = ({ orderId, metadata }) => {
7724
+ const { handleSuccess } = useRouteModal();
7725
+ const hasUneditableRows = getHasUneditableRows(metadata);
7726
+ const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
7727
+ const form = reactHookForm.useForm({
7728
+ defaultValues: {
7729
+ metadata: getDefaultValues(metadata)
7730
+ },
7731
+ resolver: zod$1.zodResolver(MetadataSchema)
7732
+ });
7733
+ const handleSubmit = form.handleSubmit(async (data) => {
7734
+ const parsedData = parseValues(data);
7735
+ await mutateAsync(
7771
7736
  {
7772
- promo_codes: [promotion.code]
7737
+ metadata: parsedData
7773
7738
  },
7774
7739
  {
7775
- onError: (e) => {
7776
- ui.toast.error(e.message);
7740
+ onSuccess: () => {
7741
+ ui.toast.success("Metadata updated");
7742
+ handleSuccess();
7743
+ },
7744
+ onError: (error) => {
7745
+ ui.toast.error(error.message);
7777
7746
  }
7778
7747
  }
7779
7748
  );
7780
- };
7781
- const displayValue = getDisplayValue(promotion);
7782
- return /* @__PURE__ */ jsxRuntime.jsxs(
7783
- "div",
7749
+ });
7750
+ const { fields, insert, remove } = reactHookForm.useFieldArray({
7751
+ control: form.control,
7752
+ name: "metadata"
7753
+ });
7754
+ function deleteRow(index) {
7755
+ remove(index);
7756
+ if (fields.length === 1) {
7757
+ insert(0, {
7758
+ key: "",
7759
+ value: "",
7760
+ disabled: false
7761
+ });
7762
+ }
7763
+ }
7764
+ function insertRow(index, position) {
7765
+ insert(index + (position === "above" ? 0 : 1), {
7766
+ key: "",
7767
+ value: "",
7768
+ disabled: false
7769
+ });
7770
+ }
7771
+ return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
7772
+ KeyboundForm,
7784
7773
  {
7785
- className: ui.clx(
7786
- "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
7787
- {
7788
- "animate-pulse": isLoading
7789
- }
7790
- ),
7774
+ onSubmit: handleSubmit,
7775
+ className: "flex flex-1 flex-col overflow-hidden",
7791
7776
  children: [
7792
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7793
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
7794
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
7795
- displayValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
7796
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: displayValue }),
7797
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "·" })
7777
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
7778
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
7779
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
7780
+ /* @__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" }) }),
7781
+ /* @__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" }) })
7798
7782
  ] }),
7799
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "capitalize", children: promotion.application_method?.allocation })
7800
- ] })
7783
+ fields.map((field, index) => {
7784
+ const isDisabled = field.disabled || false;
7785
+ let placeholder = "-";
7786
+ if (typeof field.value === "object") {
7787
+ placeholder = "{ ... }";
7788
+ }
7789
+ if (Array.isArray(field.value)) {
7790
+ placeholder = "[ ... ]";
7791
+ }
7792
+ return /* @__PURE__ */ jsxRuntime.jsx(
7793
+ ConditionalTooltip,
7794
+ {
7795
+ showTooltip: isDisabled,
7796
+ content: "This row is disabled because it contains non-primitive data.",
7797
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group/table relative", children: [
7798
+ /* @__PURE__ */ jsxRuntime.jsxs(
7799
+ "div",
7800
+ {
7801
+ className: ui.clx("grid grid-cols-2 divide-x", {
7802
+ "overflow-hidden rounded-b-lg": index === fields.length - 1
7803
+ }),
7804
+ children: [
7805
+ /* @__PURE__ */ jsxRuntime.jsx(
7806
+ Form$2.Field,
7807
+ {
7808
+ control: form.control,
7809
+ name: `metadata.${index}.key`,
7810
+ render: ({ field: field2 }) => {
7811
+ return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7812
+ GridInput,
7813
+ {
7814
+ "aria-labelledby": METADATA_KEY_LABEL_ID,
7815
+ ...field2,
7816
+ disabled: isDisabled,
7817
+ placeholder: "Key"
7818
+ }
7819
+ ) }) });
7820
+ }
7821
+ }
7822
+ ),
7823
+ /* @__PURE__ */ jsxRuntime.jsx(
7824
+ Form$2.Field,
7825
+ {
7826
+ control: form.control,
7827
+ name: `metadata.${index}.value`,
7828
+ render: ({ field: { value, ...field2 } }) => {
7829
+ return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7830
+ GridInput,
7831
+ {
7832
+ "aria-labelledby": METADATA_VALUE_LABEL_ID,
7833
+ ...field2,
7834
+ value: isDisabled ? placeholder : value,
7835
+ disabled: isDisabled,
7836
+ placeholder: "Value"
7837
+ }
7838
+ ) }) });
7839
+ }
7840
+ }
7841
+ )
7842
+ ]
7843
+ }
7844
+ ),
7845
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
7846
+ /* @__PURE__ */ jsxRuntime.jsx(
7847
+ ui.DropdownMenu.Trigger,
7848
+ {
7849
+ className: ui.clx(
7850
+ "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
7851
+ {
7852
+ hidden: isDisabled
7853
+ }
7854
+ ),
7855
+ disabled: isDisabled,
7856
+ asChild: true,
7857
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisVertical, {}) })
7858
+ }
7859
+ ),
7860
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
7861
+ /* @__PURE__ */ jsxRuntime.jsxs(
7862
+ ui.DropdownMenu.Item,
7863
+ {
7864
+ className: "gap-x-2",
7865
+ onClick: () => insertRow(index, "above"),
7866
+ children: [
7867
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpMini, { className: "text-ui-fg-subtle" }),
7868
+ "Insert row above"
7869
+ ]
7870
+ }
7871
+ ),
7872
+ /* @__PURE__ */ jsxRuntime.jsxs(
7873
+ ui.DropdownMenu.Item,
7874
+ {
7875
+ className: "gap-x-2",
7876
+ onClick: () => insertRow(index, "below"),
7877
+ children: [
7878
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownMini, { className: "text-ui-fg-subtle" }),
7879
+ "Insert row below"
7880
+ ]
7881
+ }
7882
+ ),
7883
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
7884
+ /* @__PURE__ */ jsxRuntime.jsxs(
7885
+ ui.DropdownMenu.Item,
7886
+ {
7887
+ className: "gap-x-2",
7888
+ onClick: () => deleteRow(index),
7889
+ children: [
7890
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
7891
+ "Delete row"
7892
+ ]
7893
+ }
7894
+ )
7895
+ ] })
7896
+ ] })
7897
+ ] })
7898
+ },
7899
+ field.id
7900
+ );
7901
+ })
7902
+ ] }),
7903
+ 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." })
7801
7904
  ] }),
7802
- /* @__PURE__ */ jsxRuntime.jsx(
7803
- ui.IconButton,
7804
- {
7805
- size: "small",
7806
- type: "button",
7807
- variant: "transparent",
7808
- onClick: onRemove,
7809
- isLoading: isPending || isLoading,
7810
- children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, {})
7811
- }
7812
- )
7905
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
7906
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
7907
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
7908
+ ] }) })
7813
7909
  ]
7814
- },
7815
- promotion.id
7816
- );
7910
+ }
7911
+ ) });
7817
7912
  };
7818
- function getDisplayValue(promotion) {
7819
- const value = promotion.application_method?.value;
7820
- if (!value) {
7821
- return null;
7822
- }
7823
- if (promotion.application_method?.type === "fixed") {
7824
- const currency = promotion.application_method?.currency_code;
7825
- if (!currency) {
7826
- return null;
7913
+ const GridInput = React.forwardRef(({ className, ...props }, ref) => {
7914
+ return /* @__PURE__ */ jsxRuntime.jsx(
7915
+ "input",
7916
+ {
7917
+ ref,
7918
+ ...props,
7919
+ autoComplete: "off",
7920
+ className: ui.clx(
7921
+ "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",
7922
+ className
7923
+ )
7827
7924
  }
7828
- return getLocaleAmount(value, currency);
7829
- } else if (promotion.application_method?.type === "percentage") {
7830
- return formatPercentage(value);
7831
- }
7832
- return null;
7833
- }
7834
- const formatter = new Intl.NumberFormat([], {
7835
- style: "percent",
7836
- minimumFractionDigits: 2
7925
+ );
7837
7926
  });
7838
- const formatPercentage = (value, isPercentageValue = false) => {
7839
- let val = value || 0;
7840
- if (!isPercentageValue) {
7841
- val = val / 100;
7842
- }
7843
- return formatter.format(val);
7927
+ GridInput.displayName = "MetadataForm.GridInput";
7928
+ const PlaceholderInner = () => {
7929
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
7930
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
7931
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
7932
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }),
7933
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" })
7934
+ ] }) })
7935
+ ] });
7844
7936
  };
7845
- function getPromotionIds(items, shippingMethods) {
7846
- const promotionIds = /* @__PURE__ */ new Set();
7847
- for (const item of items) {
7848
- if (item.adjustments) {
7849
- for (const adjustment of item.adjustments) {
7850
- if (adjustment.promotion_id) {
7851
- promotionIds.add(adjustment.promotion_id);
7852
- }
7937
+ const EDITABLE_TYPES = ["string", "number", "boolean"];
7938
+ function getDefaultValues(metadata) {
7939
+ if (!metadata || !Object.keys(metadata).length) {
7940
+ return [
7941
+ {
7942
+ key: "",
7943
+ value: "",
7944
+ disabled: false
7853
7945
  }
7946
+ ];
7947
+ }
7948
+ return Object.entries(metadata).map(([key, value]) => {
7949
+ if (!EDITABLE_TYPES.includes(typeof value)) {
7950
+ return {
7951
+ key,
7952
+ value,
7953
+ disabled: true
7954
+ };
7955
+ }
7956
+ let stringValue = value;
7957
+ if (typeof value !== "string") {
7958
+ stringValue = JSON.stringify(value);
7854
7959
  }
7960
+ return {
7961
+ key,
7962
+ value: stringValue,
7963
+ original_key: key
7964
+ };
7965
+ });
7966
+ }
7967
+ function parseValues(values) {
7968
+ const metadata = values.metadata;
7969
+ const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
7970
+ if (isEmpty) {
7971
+ return null;
7855
7972
  }
7856
- for (const shippingMethod of shippingMethods) {
7857
- if (shippingMethod.adjustments) {
7858
- for (const adjustment of shippingMethod.adjustments) {
7859
- if (adjustment.promotion_id) {
7860
- promotionIds.add(adjustment.promotion_id);
7861
- }
7973
+ const update = {};
7974
+ metadata.forEach((field) => {
7975
+ let key = field.key;
7976
+ let value = field.value;
7977
+ const disabled = field.disabled;
7978
+ if (!key || !value) {
7979
+ return;
7980
+ }
7981
+ if (disabled) {
7982
+ update[key] = value;
7983
+ return;
7984
+ }
7985
+ key = key.trim();
7986
+ value = value.trim();
7987
+ if (value === "true") {
7988
+ update[key] = true;
7989
+ } else if (value === "false") {
7990
+ update[key] = false;
7991
+ } else {
7992
+ const parsedNumber = parseFloat(value);
7993
+ if (!isNaN(parsedNumber)) {
7994
+ update[key] = parsedNumber;
7995
+ } else {
7996
+ update[key] = value;
7862
7997
  }
7863
7998
  }
7999
+ });
8000
+ return update;
8001
+ }
8002
+ function getHasUneditableRows(metadata) {
8003
+ if (!metadata) {
8004
+ return false;
7864
8005
  }
7865
- return Array.from(promotionIds);
8006
+ return Object.values(metadata).some(
8007
+ (value) => !EDITABLE_TYPES.includes(typeof value)
8008
+ );
7866
8009
  }
7867
8010
  const STACKED_FOCUS_MODAL_ID = "shipping-form";
7868
8011
  const Shipping = () => {
@@ -8658,112 +8801,6 @@ const CustomAmountField = ({
8658
8801
  }
8659
8802
  );
8660
8803
  };
8661
- const SalesChannel = () => {
8662
- const { id } = reactRouterDom.useParams();
8663
- const { draft_order, isPending, isError, error } = useDraftOrder(
8664
- id,
8665
- {
8666
- fields: "+sales_channel_id"
8667
- },
8668
- {
8669
- enabled: !!id
8670
- }
8671
- );
8672
- if (isError) {
8673
- throw error;
8674
- }
8675
- const ISrEADY = !!draft_order && !isPending;
8676
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
8677
- /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
8678
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Sales Channel" }) }),
8679
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Update which sales channel the draft order is associated with" }) })
8680
- ] }),
8681
- ISrEADY && /* @__PURE__ */ jsxRuntime.jsx(SalesChannelForm, { order: draft_order })
8682
- ] });
8683
- };
8684
- const SalesChannelForm = ({ order }) => {
8685
- const form = reactHookForm.useForm({
8686
- defaultValues: {
8687
- sales_channel_id: order.sales_channel_id || ""
8688
- },
8689
- resolver: zod$1.zodResolver(schema$2)
8690
- });
8691
- const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
8692
- const { handleSuccess } = useRouteModal();
8693
- const onSubmit = form.handleSubmit(async (data) => {
8694
- await mutateAsync(
8695
- {
8696
- sales_channel_id: data.sales_channel_id
8697
- },
8698
- {
8699
- onSuccess: () => {
8700
- ui.toast.success("Sales channel updated");
8701
- handleSuccess();
8702
- },
8703
- onError: (error) => {
8704
- ui.toast.error(error.message);
8705
- }
8706
- }
8707
- );
8708
- });
8709
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
8710
- KeyboundForm,
8711
- {
8712
- className: "flex flex-1 flex-col overflow-hidden",
8713
- onSubmit,
8714
- children: [
8715
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { className: "flex flex-col gap-y-6 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(SalesChannelField, { control: form.control, order }) }),
8716
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
8717
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
8718
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
8719
- ] }) })
8720
- ]
8721
- }
8722
- ) });
8723
- };
8724
- const SalesChannelField = ({ control, order }) => {
8725
- const salesChannels = useComboboxData({
8726
- queryFn: async (params) => {
8727
- return await sdk.admin.salesChannel.list(params);
8728
- },
8729
- queryKey: ["sales-channels"],
8730
- getOptions: (data) => {
8731
- return data.sales_channels.map((salesChannel) => ({
8732
- label: salesChannel.name,
8733
- value: salesChannel.id
8734
- }));
8735
- },
8736
- defaultValue: order.sales_channel_id || void 0
8737
- });
8738
- return /* @__PURE__ */ jsxRuntime.jsx(
8739
- Form$2.Field,
8740
- {
8741
- control,
8742
- name: "sales_channel_id",
8743
- render: ({ field }) => {
8744
- return /* @__PURE__ */ jsxRuntime.jsxs(Form$2.Item, { children: [
8745
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Label, { children: "Sales Channel" }),
8746
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
8747
- Combobox,
8748
- {
8749
- options: salesChannels.options,
8750
- fetchNextPage: salesChannels.fetchNextPage,
8751
- isFetchingNextPage: salesChannels.isFetchingNextPage,
8752
- searchValue: salesChannels.searchValue,
8753
- onSearchValueChange: salesChannels.onSearchValueChange,
8754
- placeholder: "Select sales channel",
8755
- ...field
8756
- }
8757
- ) }),
8758
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.ErrorMessage, {})
8759
- ] });
8760
- }
8761
- }
8762
- );
8763
- };
8764
- const schema$2 = zod.z.object({
8765
- sales_channel_id: zod.z.string().min(1)
8766
- });
8767
8804
  const ShippingAddress = () => {
8768
8805
  const { id } = reactRouterDom.useParams();
8769
8806
  const { order, isPending, isError, error } = useOrder(id, {
@@ -9461,14 +9498,14 @@ const routeModule = {
9461
9498
  handle,
9462
9499
  loader,
9463
9500
  children: [
9464
- {
9465
- Component: CustomItems,
9466
- path: "/draft-orders/:id/custom-items"
9467
- },
9468
9501
  {
9469
9502
  Component: BillingAddress,
9470
9503
  path: "/draft-orders/:id/billing-address"
9471
9504
  },
9505
+ {
9506
+ Component: CustomItems,
9507
+ path: "/draft-orders/:id/custom-items"
9508
+ },
9472
9509
  {
9473
9510
  Component: Email,
9474
9511
  path: "/draft-orders/:id/email"
@@ -9477,22 +9514,22 @@ const routeModule = {
9477
9514
  Component: Items,
9478
9515
  path: "/draft-orders/:id/items"
9479
9516
  },
9480
- {
9481
- Component: Metadata,
9482
- path: "/draft-orders/:id/metadata"
9483
- },
9484
9517
  {
9485
9518
  Component: Promotions,
9486
9519
  path: "/draft-orders/:id/promotions"
9487
9520
  },
9488
- {
9489
- Component: Shipping,
9490
- path: "/draft-orders/:id/shipping"
9491
- },
9492
9521
  {
9493
9522
  Component: SalesChannel,
9494
9523
  path: "/draft-orders/:id/sales-channel"
9495
9524
  },
9525
+ {
9526
+ Component: Metadata,
9527
+ path: "/draft-orders/:id/metadata"
9528
+ },
9529
+ {
9530
+ Component: Shipping,
9531
+ path: "/draft-orders/:id/shipping"
9532
+ },
9496
9533
  {
9497
9534
  Component: ShippingAddress,
9498
9535
  path: "/draft-orders/:id/shipping-address"