@medusajs/draft-order 2.20.0-snapshot-20260901111536 → 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.
@@ -6168,6 +6168,27 @@ const BillingAddressForm = ({ order }) => {
6168
6168
  ) });
6169
6169
  };
6170
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
+ });
6171
6192
  const Email = () => {
6172
6193
  const { id } = reactRouterDom.useParams();
6173
6194
  const { order, isPending, isError, error } = useOrder(id, {
@@ -6190,7 +6211,7 @@ const EmailForm = ({ order }) => {
6190
6211
  defaultValues: {
6191
6212
  email: order.email ?? ""
6192
6213
  },
6193
- resolver: zod$1.zodResolver(schema$4)
6214
+ resolver: zod$1.zodResolver(schema$3)
6194
6215
  });
6195
6216
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
6196
6217
  const { handleSuccess } = useRouteModal();
@@ -6233,7 +6254,7 @@ const EmailForm = ({ order }) => {
6233
6254
  }
6234
6255
  ) });
6235
6256
  };
6236
- const schema$4 = zod.z.object({
6257
+ const schema$3 = zod.z.object({
6237
6258
  email: zod.z.string().email()
6238
6259
  });
6239
6260
  const NumberInput = React.forwardRef(
@@ -6962,7 +6983,8 @@ const ExistingItemsForm = ({ orderId, items }) => {
6962
6983
  q,
6963
6984
  order,
6964
6985
  offset: offset ? parseInt(offset) : void 0,
6965
- limit: LIMIT
6986
+ limit: LIMIT,
6987
+ fields: "*inventory_items.inventory.location_levels,+inventory_quantity,+manage_inventory"
6966
6988
  },
6967
6989
  {
6968
6990
  placeholderData: reactQuery.keepPreviousData
@@ -7041,6 +7063,23 @@ const ExistingItemsForm = ({ orderId, items }) => {
7041
7063
  );
7042
7064
  };
7043
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
+ };
7044
7083
  const useColumns = () => {
7045
7084
  return React.useMemo(() => {
7046
7085
  return [
@@ -7101,6 +7140,25 @@ const useColumns = () => {
7101
7140
  enableSorting: true,
7102
7141
  sortAscLabel: "Oldest first",
7103
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
+ }
7104
7162
  })
7105
7163
  ];
7106
7164
  }, []);
@@ -7218,499 +7276,128 @@ const customItemSchema = zod.z.object({
7218
7276
  quantity: zod.z.number(),
7219
7277
  unit_price: zod.z.union([zod.z.number(), zod.z.string()])
7220
7278
  });
7221
- const CustomItems = () => {
7222
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
7223
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Custom Items" }) }) }),
7224
- /* @__PURE__ */ jsxRuntime.jsx(CustomItemsForm, {})
7225
- ] });
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
+ ]
7226
7290
  };
7227
- const CustomItemsForm = () => {
7228
- const form = reactHookForm.useForm({
7229
- resolver: zod$1.zodResolver(schema$3)
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
7230
7296
  });
7231
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
7232
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, {}),
7233
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
7234
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
7235
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", children: "Save" })
7236
- ] }) })
7237
- ] }) });
7297
+ return { ...data, ...rest };
7238
7298
  };
7239
- const schema$3 = zod.z.object({
7240
- email: zod.z.string().email()
7241
- });
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 = () => {
7299
+ const Promotions = () => {
7287
7300
  const { id } = reactRouterDom.useParams();
7288
- const { order, isPending, isError, error } = useOrder(id, {
7289
- fields: "metadata"
7290
- });
7291
- if (isError) {
7292
- throw error;
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
7910
+ }
7911
+ ) });
7912
+ };
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
+ )
7924
+ }
7816
7925
  );
7926
+ });
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
+ ] });
7817
7936
  };
7818
- function getDisplayValue(promotion) {
7819
- const value = promotion.application_method?.value;
7820
- if (!value) {
7821
- return null;
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
7945
+ }
7946
+ ];
7822
7947
  }
7823
- if (promotion.application_method?.type === "fixed") {
7824
- const currency = promotion.application_method?.currency_code;
7825
- if (!currency) {
7826
- return null;
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
+ };
7827
7955
  }
7828
- return getLocaleAmount(value, currency);
7829
- } else if (promotion.application_method?.type === "percentage") {
7830
- return formatPercentage(value);
7831
- }
7832
- return null;
7956
+ let stringValue = value;
7957
+ if (typeof value !== "string") {
7958
+ stringValue = JSON.stringify(value);
7959
+ }
7960
+ return {
7961
+ key,
7962
+ value: stringValue,
7963
+ original_key: key
7964
+ };
7965
+ });
7833
7966
  }
7834
- const formatter = new Intl.NumberFormat([], {
7835
- style: "percent",
7836
- minimumFractionDigits: 2
7837
- });
7838
- const formatPercentage = (value, isPercentageValue = false) => {
7839
- let val = value || 0;
7840
- if (!isPercentageValue) {
7841
- val = val / 100;
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;
7842
7972
  }
7843
- return formatter.format(val);
7844
- };
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
- }
7853
- }
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;
7854
7980
  }
7855
- }
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
- }
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 = () => {
@@ -8689,7 +8832,7 @@ const ShippingAddressForm = ({ order }) => {
8689
8832
  postal_code: order.shipping_address?.postal_code ?? "",
8690
8833
  phone: order.shipping_address?.phone ?? ""
8691
8834
  },
8692
- resolver: zod$1.zodResolver(schema$2)
8835
+ resolver: zod$1.zodResolver(schema$1)
8693
8836
  });
8694
8837
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
8695
8838
  const { handleSuccess } = useRouteModal();
@@ -8859,7 +9002,7 @@ const ShippingAddressForm = ({ order }) => {
8859
9002
  }
8860
9003
  ) });
8861
9004
  };
8862
- const schema$2 = addressSchema;
9005
+ const schema$1 = addressSchema;
8863
9006
  const TransferOwnership = () => {
8864
9007
  const { id } = reactRouterDom.useParams();
8865
9008
  const { draft_order, isPending, isError, error } = useDraftOrder(id, {
@@ -8882,7 +9025,7 @@ const TransferOwnershipForm = ({ order }) => {
8882
9025
  defaultValues: {
8883
9026
  customer_id: order.customer_id || ""
8884
9027
  },
8885
- resolver: zod$1.zodResolver(schema$1)
9028
+ resolver: zod$1.zodResolver(schema)
8886
9029
  });
8887
9030
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
8888
9031
  const { handleSuccess } = useRouteModal();
@@ -9332,114 +9475,8 @@ const Illustration = () => {
9332
9475
  }
9333
9476
  );
9334
9477
  };
9335
- const schema$1 = zod.z.object({
9336
- customer_id: zod.z.string().min(1)
9337
- });
9338
- const SalesChannel = () => {
9339
- const { id } = reactRouterDom.useParams();
9340
- const { draft_order, isPending, isError, error } = useDraftOrder(
9341
- id,
9342
- {
9343
- fields: "+sales_channel_id"
9344
- },
9345
- {
9346
- enabled: !!id
9347
- }
9348
- );
9349
- if (isError) {
9350
- throw error;
9351
- }
9352
- const ISrEADY = !!draft_order && !isPending;
9353
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
9354
- /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
9355
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Sales Channel" }) }),
9356
- /* @__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" }) })
9357
- ] }),
9358
- ISrEADY && /* @__PURE__ */ jsxRuntime.jsx(SalesChannelForm, { order: draft_order })
9359
- ] });
9360
- };
9361
- const SalesChannelForm = ({ order }) => {
9362
- const form = reactHookForm.useForm({
9363
- defaultValues: {
9364
- sales_channel_id: order.sales_channel_id || ""
9365
- },
9366
- resolver: zod$1.zodResolver(schema)
9367
- });
9368
- const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
9369
- const { handleSuccess } = useRouteModal();
9370
- const onSubmit = form.handleSubmit(async (data) => {
9371
- await mutateAsync(
9372
- {
9373
- sales_channel_id: data.sales_channel_id
9374
- },
9375
- {
9376
- onSuccess: () => {
9377
- ui.toast.success("Sales channel updated");
9378
- handleSuccess();
9379
- },
9380
- onError: (error) => {
9381
- ui.toast.error(error.message);
9382
- }
9383
- }
9384
- );
9385
- });
9386
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
9387
- KeyboundForm,
9388
- {
9389
- className: "flex flex-1 flex-col overflow-hidden",
9390
- onSubmit,
9391
- children: [
9392
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { className: "flex flex-col gap-y-6 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(SalesChannelField, { control: form.control, order }) }),
9393
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
9394
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
9395
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
9396
- ] }) })
9397
- ]
9398
- }
9399
- ) });
9400
- };
9401
- const SalesChannelField = ({ control, order }) => {
9402
- const salesChannels = useComboboxData({
9403
- queryFn: async (params) => {
9404
- return await sdk.admin.salesChannel.list(params);
9405
- },
9406
- queryKey: ["sales-channels"],
9407
- getOptions: (data) => {
9408
- return data.sales_channels.map((salesChannel) => ({
9409
- label: salesChannel.name,
9410
- value: salesChannel.id
9411
- }));
9412
- },
9413
- defaultValue: order.sales_channel_id || void 0
9414
- });
9415
- return /* @__PURE__ */ jsxRuntime.jsx(
9416
- Form$2.Field,
9417
- {
9418
- control,
9419
- name: "sales_channel_id",
9420
- render: ({ field }) => {
9421
- return /* @__PURE__ */ jsxRuntime.jsxs(Form$2.Item, { children: [
9422
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Label, { children: "Sales Channel" }),
9423
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
9424
- Combobox,
9425
- {
9426
- options: salesChannels.options,
9427
- fetchNextPage: salesChannels.fetchNextPage,
9428
- isFetchingNextPage: salesChannels.isFetchingNextPage,
9429
- searchValue: salesChannels.searchValue,
9430
- onSearchValueChange: salesChannels.onSearchValueChange,
9431
- placeholder: "Select sales channel",
9432
- ...field
9433
- }
9434
- ) }),
9435
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.ErrorMessage, {})
9436
- ] });
9437
- }
9438
- }
9439
- );
9440
- };
9441
9478
  const schema = zod.z.object({
9442
- sales_channel_id: zod.z.string().min(1)
9479
+ customer_id: zod.z.string().min(1)
9443
9480
  });
9444
9481
  const widgetModule = { widgets: [] };
9445
9482
  const routeModule = {
@@ -9465,6 +9502,10 @@ const routeModule = {
9465
9502
  Component: BillingAddress,
9466
9503
  path: "/draft-orders/:id/billing-address"
9467
9504
  },
9505
+ {
9506
+ Component: CustomItems,
9507
+ path: "/draft-orders/:id/custom-items"
9508
+ },
9468
9509
  {
9469
9510
  Component: Email,
9470
9511
  path: "/draft-orders/:id/email"
@@ -9474,16 +9515,16 @@ const routeModule = {
9474
9515
  path: "/draft-orders/:id/items"
9475
9516
  },
9476
9517
  {
9477
- Component: CustomItems,
9478
- path: "/draft-orders/:id/custom-items"
9518
+ Component: Promotions,
9519
+ path: "/draft-orders/:id/promotions"
9479
9520
  },
9480
9521
  {
9481
- Component: Metadata,
9482
- path: "/draft-orders/:id/metadata"
9522
+ Component: SalesChannel,
9523
+ path: "/draft-orders/:id/sales-channel"
9483
9524
  },
9484
9525
  {
9485
- Component: Promotions,
9486
- path: "/draft-orders/:id/promotions"
9526
+ Component: Metadata,
9527
+ path: "/draft-orders/:id/metadata"
9487
9528
  },
9488
9529
  {
9489
9530
  Component: Shipping,
@@ -9496,10 +9537,6 @@ const routeModule = {
9496
9537
  {
9497
9538
  Component: TransferOwnership,
9498
9539
  path: "/draft-orders/:id/transfer-ownership"
9499
- },
9500
- {
9501
- Component: SalesChannel,
9502
- path: "/draft-orders/:id/sales-channel"
9503
9540
  }
9504
9541
  ]
9505
9542
  }