@medusajs/draft-order 2.21.1 → 2.21.2-snapshot-20260925150426

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.
@@ -3032,7 +3032,7 @@ const LoadingState = () => {
3032
3032
  };
3033
3033
  const CustomerInfo = ({ customer }) => {
3034
3034
  const name = [customer.first_name, customer.last_name].filter(Boolean).join(" ");
3035
- const fallback = name ? name[0] : customer.email[0];
3035
+ const fallback = name ? name[0] : customer.email?.[0] || "-";
3036
3036
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 flex-1", children: [
3037
3037
  /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { size: "small", fallback, className: "w-6 h-6" }),
3038
3038
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 overflow-hidden", children: [
@@ -6168,27 +6168,6 @@ 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
- });
6192
6171
  const Email = () => {
6193
6172
  const { id } = reactRouterDom.useParams();
6194
6173
  const { order, isPending, isError, error } = useOrder(id, {
@@ -6211,7 +6190,7 @@ const EmailForm = ({ order }) => {
6211
6190
  defaultValues: {
6212
6191
  email: order.email ?? ""
6213
6192
  },
6214
- resolver: zod$1.zodResolver(schema$3)
6193
+ resolver: zod$1.zodResolver(schema$4)
6215
6194
  });
6216
6195
  const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
6217
6196
  const { handleSuccess } = useRouteModal();
@@ -6254,7 +6233,7 @@ const EmailForm = ({ order }) => {
6254
6233
  }
6255
6234
  ) });
6256
6235
  };
6257
- const schema$3 = zod.z.object({
6236
+ const schema$4 = zod.z.object({
6258
6237
  email: zod.z.string().email()
6259
6238
  });
6260
6239
  const NumberInput = React.forwardRef(
@@ -7276,88 +7255,67 @@ const customItemSchema = zod.z.object({
7276
7255
  quantity: zod.z.number(),
7277
7256
  unit_price: zod.z.union([zod.z.number(), zod.z.string()])
7278
7257
  });
7279
- const InlineTip = React.forwardRef(
7280
- ({ variant = "tip", label, className, children, ...props }, ref) => {
7281
- const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
7282
- return /* @__PURE__ */ jsxRuntime.jsxs(
7283
- "div",
7284
- {
7285
- ref,
7286
- className: ui.clx(
7287
- "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
7288
- className
7289
- ),
7290
- ...props,
7291
- children: [
7292
- /* @__PURE__ */ jsxRuntime.jsx(
7293
- "div",
7294
- {
7295
- role: "presentation",
7296
- className: ui.clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
7297
- "bg-ui-tag-orange-icon": variant === "warning"
7298
- })
7299
- }
7300
- ),
7301
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-pretty", children: [
7302
- /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
7303
- labelValue,
7304
- ":"
7305
- ] }),
7306
- " ",
7307
- children
7308
- ] })
7309
- ]
7310
- }
7311
- );
7312
- }
7313
- );
7314
- InlineTip.displayName = "InlineTip";
7315
- const MetadataFieldSchema = zod.z.object({
7316
- key: zod.z.string(),
7317
- disabled: zod.z.boolean().optional(),
7318
- value: zod.z.any()
7319
- });
7320
- const MetadataSchema = zod.z.object({
7321
- metadata: zod.z.array(MetadataFieldSchema)
7258
+ const CustomItems = () => {
7259
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
7260
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Custom Items" }) }) }),
7261
+ /* @__PURE__ */ jsxRuntime.jsx(CustomItemsForm, {})
7262
+ ] });
7263
+ };
7264
+ const CustomItemsForm = () => {
7265
+ const form = reactHookForm.useForm({
7266
+ resolver: zod$1.zodResolver(schema$3)
7267
+ });
7268
+ return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", children: [
7269
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, {}),
7270
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
7271
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
7272
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", children: "Save" })
7273
+ ] }) })
7274
+ ] }) });
7275
+ };
7276
+ const schema$3 = zod.z.object({
7277
+ email: zod.z.string().email()
7322
7278
  });
7323
- const Metadata = () => {
7279
+ const SalesChannel = () => {
7324
7280
  const { id } = reactRouterDom.useParams();
7325
- const { order, isPending, isError, error } = useOrder(id, {
7326
- fields: "metadata"
7327
- });
7281
+ const { draft_order, isPending, isError, error } = useDraftOrder(
7282
+ id,
7283
+ {
7284
+ fields: "+sales_channel_id"
7285
+ },
7286
+ {
7287
+ enabled: !!id
7288
+ }
7289
+ );
7328
7290
  if (isError) {
7329
7291
  throw error;
7330
7292
  }
7331
- const isReady = !isPending && !!order;
7293
+ const ISrEADY = !!draft_order && !isPending;
7332
7294
  return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
7333
7295
  /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
7334
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Metadata" }) }),
7335
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
7296
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Sales Channel" }) }),
7297
+ /* @__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" }) })
7336
7298
  ] }),
7337
- !isReady ? /* @__PURE__ */ jsxRuntime.jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsxRuntime.jsx(MetadataForm, { orderId: id, metadata: order?.metadata })
7299
+ ISrEADY && /* @__PURE__ */ jsxRuntime.jsx(SalesChannelForm, { order: draft_order })
7338
7300
  ] });
7339
7301
  };
7340
- const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
7341
- const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
7342
- const MetadataForm = ({ orderId, metadata }) => {
7343
- const { handleSuccess } = useRouteModal();
7344
- const hasUneditableRows = getHasUneditableRows(metadata);
7345
- const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
7302
+ const SalesChannelForm = ({ order }) => {
7346
7303
  const form = reactHookForm.useForm({
7347
7304
  defaultValues: {
7348
- metadata: getDefaultValues(metadata)
7305
+ sales_channel_id: order.sales_channel_id || ""
7349
7306
  },
7350
- resolver: zod$1.zodResolver(MetadataSchema)
7307
+ resolver: zod$1.zodResolver(schema$2)
7351
7308
  });
7352
- const handleSubmit = form.handleSubmit(async (data) => {
7353
- const parsedData = parseValues(data);
7309
+ const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
7310
+ const { handleSuccess } = useRouteModal();
7311
+ const onSubmit = form.handleSubmit(async (data) => {
7354
7312
  await mutateAsync(
7355
7313
  {
7356
- metadata: parsedData
7314
+ sales_channel_id: data.sales_channel_id
7357
7315
  },
7358
7316
  {
7359
7317
  onSuccess: () => {
7360
- ui.toast.success("Metadata updated");
7318
+ ui.toast.success("Sales channel updated");
7361
7319
  handleSuccess();
7362
7320
  },
7363
7321
  onError: (error) => {
@@ -7366,365 +7324,133 @@ const MetadataForm = ({ orderId, metadata }) => {
7366
7324
  }
7367
7325
  );
7368
7326
  });
7369
- const { fields, insert, remove } = reactHookForm.useFieldArray({
7370
- control: form.control,
7371
- name: "metadata"
7372
- });
7373
- function deleteRow(index) {
7374
- remove(index);
7375
- if (fields.length === 1) {
7376
- insert(0, {
7377
- key: "",
7378
- value: "",
7379
- disabled: false
7380
- });
7381
- }
7382
- }
7383
- function insertRow(index, position) {
7384
- insert(index + (position === "above" ? 0 : 1), {
7385
- key: "",
7386
- value: "",
7387
- disabled: false
7388
- });
7389
- }
7390
7327
  return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
7391
7328
  KeyboundForm,
7392
7329
  {
7393
- onSubmit: handleSubmit,
7394
7330
  className: "flex flex-1 flex-col overflow-hidden",
7331
+ onSubmit,
7395
7332
  children: [
7396
- /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
7397
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
7398
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
7399
- /* @__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" }) }),
7400
- /* @__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" }) })
7401
- ] }),
7402
- fields.map((field, index) => {
7403
- const isDisabled = field.disabled || false;
7404
- let placeholder = "-";
7405
- if (typeof field.value === "object") {
7406
- placeholder = "{ ... }";
7407
- }
7408
- if (Array.isArray(field.value)) {
7409
- placeholder = "[ ... ]";
7410
- }
7411
- return /* @__PURE__ */ jsxRuntime.jsx(
7412
- ConditionalTooltip,
7413
- {
7414
- showTooltip: isDisabled,
7415
- content: "This row is disabled because it contains non-primitive data.",
7416
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group/table relative", children: [
7417
- /* @__PURE__ */ jsxRuntime.jsxs(
7418
- "div",
7419
- {
7420
- className: ui.clx("grid grid-cols-2 divide-x", {
7421
- "overflow-hidden rounded-b-lg": index === fields.length - 1
7422
- }),
7423
- children: [
7424
- /* @__PURE__ */ jsxRuntime.jsx(
7425
- Form$2.Field,
7426
- {
7427
- control: form.control,
7428
- name: `metadata.${index}.key`,
7429
- render: ({ field: field2 }) => {
7430
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7431
- GridInput,
7432
- {
7433
- "aria-labelledby": METADATA_KEY_LABEL_ID,
7434
- ...field2,
7435
- disabled: isDisabled,
7436
- placeholder: "Key"
7437
- }
7438
- ) }) });
7439
- }
7440
- }
7441
- ),
7442
- /* @__PURE__ */ jsxRuntime.jsx(
7443
- Form$2.Field,
7444
- {
7445
- control: form.control,
7446
- name: `metadata.${index}.value`,
7447
- render: ({ field: { value, ...field2 } }) => {
7448
- return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7449
- GridInput,
7450
- {
7451
- "aria-labelledby": METADATA_VALUE_LABEL_ID,
7452
- ...field2,
7453
- value: isDisabled ? placeholder : value,
7454
- disabled: isDisabled,
7455
- placeholder: "Value"
7456
- }
7457
- ) }) });
7458
- }
7459
- }
7460
- )
7461
- ]
7462
- }
7463
- ),
7464
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
7465
- /* @__PURE__ */ jsxRuntime.jsx(
7466
- ui.DropdownMenu.Trigger,
7467
- {
7468
- className: ui.clx(
7469
- "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
7470
- {
7471
- hidden: isDisabled
7472
- }
7473
- ),
7474
- disabled: isDisabled,
7475
- asChild: true,
7476
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisVertical, {}) })
7477
- }
7478
- ),
7479
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
7480
- /* @__PURE__ */ jsxRuntime.jsxs(
7481
- ui.DropdownMenu.Item,
7482
- {
7483
- className: "gap-x-2",
7484
- onClick: () => insertRow(index, "above"),
7485
- children: [
7486
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpMini, { className: "text-ui-fg-subtle" }),
7487
- "Insert row above"
7488
- ]
7489
- }
7490
- ),
7491
- /* @__PURE__ */ jsxRuntime.jsxs(
7492
- ui.DropdownMenu.Item,
7493
- {
7494
- className: "gap-x-2",
7495
- onClick: () => insertRow(index, "below"),
7496
- children: [
7497
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownMini, { className: "text-ui-fg-subtle" }),
7498
- "Insert row below"
7499
- ]
7500
- }
7501
- ),
7502
- /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
7503
- /* @__PURE__ */ jsxRuntime.jsxs(
7504
- ui.DropdownMenu.Item,
7505
- {
7506
- className: "gap-x-2",
7507
- onClick: () => deleteRow(index),
7508
- children: [
7509
- /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
7510
- "Delete row"
7511
- ]
7512
- }
7513
- )
7514
- ] })
7515
- ] })
7516
- ] })
7517
- },
7518
- field.id
7519
- );
7520
- })
7521
- ] }),
7522
- 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." })
7523
- ] }),
7524
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
7525
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
7333
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { className: "flex flex-col gap-y-6 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(SalesChannelField, { control: form.control, order }) }),
7334
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
7335
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
7526
7336
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
7527
7337
  ] }) })
7528
7338
  ]
7529
7339
  }
7530
7340
  ) });
7531
7341
  };
7532
- const GridInput = React.forwardRef(({ className, ...props }, ref) => {
7342
+ const SalesChannelField = ({ control, order }) => {
7343
+ const salesChannels = useComboboxData({
7344
+ queryFn: async (params) => {
7345
+ return await sdk.admin.salesChannel.list(params);
7346
+ },
7347
+ queryKey: ["sales-channels"],
7348
+ getOptions: (data) => {
7349
+ return data.sales_channels.map((salesChannel) => ({
7350
+ label: salesChannel.name,
7351
+ value: salesChannel.id
7352
+ }));
7353
+ },
7354
+ defaultValue: order.sales_channel_id || void 0
7355
+ });
7533
7356
  return /* @__PURE__ */ jsxRuntime.jsx(
7534
- "input",
7357
+ Form$2.Field,
7535
7358
  {
7536
- ref,
7537
- ...props,
7538
- autoComplete: "off",
7539
- className: ui.clx(
7540
- "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",
7541
- className
7542
- )
7543
- }
7544
- );
7545
- });
7546
- GridInput.displayName = "MetadataForm.GridInput";
7547
- const PlaceholderInner = () => {
7548
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
7549
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
7550
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
7551
- /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }),
7552
- /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" })
7553
- ] }) })
7554
- ] });
7555
- };
7556
- const EDITABLE_TYPES = ["string", "number", "boolean"];
7557
- function getDefaultValues(metadata) {
7558
- if (!metadata || !Object.keys(metadata).length) {
7559
- return [
7560
- {
7561
- key: "",
7562
- value: "",
7563
- disabled: false
7564
- }
7565
- ];
7566
- }
7567
- return Object.entries(metadata).map(([key, value]) => {
7568
- if (!EDITABLE_TYPES.includes(typeof value)) {
7569
- return {
7570
- key,
7571
- value,
7572
- disabled: true
7573
- };
7574
- }
7575
- let stringValue = value;
7576
- if (typeof value !== "string") {
7577
- stringValue = JSON.stringify(value);
7578
- }
7579
- return {
7580
- key,
7581
- value: stringValue,
7582
- original_key: key
7583
- };
7584
- });
7585
- }
7586
- function parseValues(values) {
7587
- const metadata = values.metadata;
7588
- const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
7589
- if (isEmpty) {
7590
- return null;
7591
- }
7592
- const update = {};
7593
- metadata.forEach((field) => {
7594
- let key = field.key;
7595
- let value = field.value;
7596
- const disabled = field.disabled;
7597
- if (!key || !value) {
7598
- return;
7599
- }
7600
- if (disabled) {
7601
- update[key] = value;
7602
- return;
7603
- }
7604
- key = key.trim();
7605
- value = value.trim();
7606
- if (value === "true") {
7607
- update[key] = true;
7608
- } else if (value === "false") {
7609
- update[key] = false;
7610
- } else {
7611
- const parsedNumber = parseFloat(value);
7612
- if (!isNaN(parsedNumber)) {
7613
- update[key] = parsedNumber;
7614
- } else {
7615
- update[key] = value;
7359
+ control,
7360
+ name: "sales_channel_id",
7361
+ render: ({ field }) => {
7362
+ return /* @__PURE__ */ jsxRuntime.jsxs(Form$2.Item, { children: [
7363
+ /* @__PURE__ */ jsxRuntime.jsx(Form$2.Label, { children: "Sales Channel" }),
7364
+ /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7365
+ Combobox,
7366
+ {
7367
+ options: salesChannels.options,
7368
+ fetchNextPage: salesChannels.fetchNextPage,
7369
+ isFetchingNextPage: salesChannels.isFetchingNextPage,
7370
+ searchValue: salesChannels.searchValue,
7371
+ onSearchValueChange: salesChannels.onSearchValueChange,
7372
+ placeholder: "Select sales channel",
7373
+ ...field
7374
+ }
7375
+ ) }),
7376
+ /* @__PURE__ */ jsxRuntime.jsx(Form$2.ErrorMessage, {})
7377
+ ] });
7616
7378
  }
7617
7379
  }
7618
- });
7619
- return update;
7620
- }
7621
- function getHasUneditableRows(metadata) {
7622
- if (!metadata) {
7623
- return false;
7624
- }
7625
- return Object.values(metadata).some(
7626
- (value) => !EDITABLE_TYPES.includes(typeof value)
7627
7380
  );
7628
- }
7629
- const PROMOTION_QUERY_KEY = "promotions";
7630
- const promotionsQueryKeys = {
7631
- list: (query2) => [
7632
- PROMOTION_QUERY_KEY,
7633
- query2 ? query2 : void 0
7634
- ],
7635
- detail: (id, query2) => [
7636
- PROMOTION_QUERY_KEY,
7637
- id,
7638
- query2 ? query2 : void 0
7639
- ]
7640
- };
7641
- const usePromotions = (query2, options) => {
7642
- const { data, ...rest } = reactQuery.useQuery({
7643
- queryKey: promotionsQueryKeys.list(query2),
7644
- queryFn: async () => sdk.admin.promotion.list(query2),
7645
- ...options
7646
- });
7647
- return { ...data, ...rest };
7648
7381
  };
7649
- const Promotions = () => {
7382
+ const schema$2 = zod.z.object({
7383
+ sales_channel_id: zod.z.string().min(1)
7384
+ });
7385
+ const STACKED_FOCUS_MODAL_ID = "shipping-form";
7386
+ const Shipping = () => {
7650
7387
  const { id } = reactRouterDom.useParams();
7388
+ const { order, isPending, isError, error } = useOrder(id, {
7389
+ fields: "+items.*,+items.variant.*,+items.variant.product.*,+items.variant.product.shipping_profile.*,+currency_code"
7390
+ });
7651
7391
  const {
7652
7392
  order: preview,
7393
+ isPending: isPreviewPending,
7653
7394
  isError: isPreviewError,
7654
7395
  error: previewError
7655
- } = useOrderPreview(id, void 0);
7396
+ } = useOrderPreview(id);
7656
7397
  useInitiateOrderEdit({ preview });
7657
7398
  const { onCancel } = useCancelOrderEdit({ preview });
7399
+ if (isError) {
7400
+ throw error;
7401
+ }
7658
7402
  if (isPreviewError) {
7659
7403
  throw previewError;
7660
7404
  }
7661
- const isReady = !!preview;
7662
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { onClose: onCancel, children: [
7663
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Promotions" }) }) }),
7664
- isReady && /* @__PURE__ */ jsxRuntime.jsx(PromotionForm, { preview })
7665
- ] });
7405
+ const orderHasItems = (order?.items?.length || 0) > 0;
7406
+ const isReady = preview && !isPreviewPending && order && !isPending;
7407
+ return /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal, { onClose: onCancel, children: !orderHasItems ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col overflow-hidden ", children: [
7408
+ /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Header, {}),
7409
+ /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col items-center overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full max-w-[720px] flex-col gap-y-6 px-6 py-16", children: [
7410
+ /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Shipping" }) }),
7411
+ /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "This draft order currently has no items. Add items to the order before adding shipping." }) })
7412
+ ] }) }) }),
7413
+ /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }) })
7414
+ ] }) : isReady ? /* @__PURE__ */ jsxRuntime.jsx(ShippingForm, { preview, order }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7415
+ /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Edit Shipping" }) }),
7416
+ /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Loading data for the draft order, please wait..." }) })
7417
+ ] }) });
7666
7418
  };
7667
- const PromotionForm = ({ preview }) => {
7668
- const { items, shipping_methods } = preview;
7419
+ const ShippingForm = ({ preview, order }) => {
7420
+ const { setIsOpen } = useStackedModal();
7669
7421
  const [isSubmitting, setIsSubmitting] = React.useState(false);
7670
- const [comboboxValue, setComboboxValue] = React.useState("");
7671
- const { handleSuccess } = useRouteModal();
7672
- const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
7673
- const promoIds = getPromotionIds(items, shipping_methods);
7674
- const { promotions, isPending, isError, error } = usePromotions(
7422
+ const [data, setData] = React.useState(null);
7423
+ const appliedShippingOptionIds = preview.shipping_methods?.map((method) => method.shipping_option_id).filter(Boolean);
7424
+ const { shipping_options } = useShippingOptions(
7675
7425
  {
7676
- id: promoIds
7426
+ id: appliedShippingOptionIds,
7427
+ fields: "+service_zone.*,+service_zone.fulfillment_set.*,+service_zone.fulfillment_set.location.*"
7677
7428
  },
7678
7429
  {
7679
- enabled: !!promoIds.length
7430
+ enabled: appliedShippingOptionIds.length > 0
7680
7431
  }
7681
7432
  );
7682
- const comboboxData = useComboboxData({
7683
- queryKey: ["promotions", "combobox", promoIds],
7684
- queryFn: async (params) => {
7685
- return await sdk.admin.promotion.list({
7686
- ...params,
7687
- id: {
7688
- $nin: promoIds
7689
- }
7690
- });
7691
- },
7692
- getOptions: (data) => {
7693
- return data.promotions.map((promotion) => ({
7694
- label: promotion.code,
7695
- value: promotion.code
7696
- }));
7697
- }
7698
- });
7699
- const add = async (value) => {
7700
- if (!value) {
7701
- return;
7702
- }
7703
- addPromotions(
7704
- {
7705
- promo_codes: [value]
7706
- },
7707
- {
7708
- onError: (e) => {
7709
- ui.toast.error(e.message);
7710
- comboboxData.onSearchValueChange("");
7711
- setComboboxValue("");
7712
- },
7713
- onSuccess: () => {
7714
- comboboxData.onSearchValueChange("");
7715
- setComboboxValue("");
7716
- }
7717
- }
7718
- );
7719
- };
7433
+ const uniqueShippingProfiles = React.useMemo(() => {
7434
+ const profiles = /* @__PURE__ */ new Map();
7435
+ getUniqueShippingProfiles(order.items).forEach((profile) => {
7436
+ profiles.set(profile.id, profile);
7437
+ });
7438
+ shipping_options?.forEach((option) => {
7439
+ profiles.set(option.shipping_profile_id, option.shipping_profile);
7440
+ });
7441
+ return Array.from(profiles.values());
7442
+ }, [order.items, shipping_options]);
7443
+ const { handleSuccess } = useRouteModal();
7720
7444
  const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
7721
- const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
7445
+ const { mutateAsync: requestOrderEdit } = useDraftOrderRequestEdit(preview.id);
7446
+ const { mutateAsync: removeShippingMethod } = useDraftOrderRemoveShippingMethod(preview.id);
7447
+ const { mutateAsync: removeActionShippingMethod } = useDraftOrderRemoveActionShippingMethod(preview.id);
7722
7448
  const onSubmit = async () => {
7723
7449
  setIsSubmitting(true);
7724
7450
  let requestSucceeded = false;
7725
7451
  await requestOrderEdit(void 0, {
7726
7452
  onError: (e) => {
7727
- ui.toast.error(e.message);
7453
+ ui.toast.error(`Failed to request order edit: ${e.message}`);
7728
7454
  },
7729
7455
  onSuccess: () => {
7730
7456
  requestSucceeded = true;
@@ -7736,7 +7462,7 @@ const PromotionForm = ({ preview }) => {
7736
7462
  }
7737
7463
  await confirmOrderEdit(void 0, {
7738
7464
  onError: (e) => {
7739
- ui.toast.error(e.message);
7465
+ ui.toast.error(`Failed to confirm order edit: ${e.message}`);
7740
7466
  },
7741
7467
  onSuccess: () => {
7742
7468
  handleSuccess();
@@ -7746,367 +7472,16 @@ const PromotionForm = ({ preview }) => {
7746
7472
  }
7747
7473
  });
7748
7474
  };
7749
- if (isError) {
7750
- throw error;
7751
- }
7752
- return /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
7753
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
7754
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
7755
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
7756
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
7757
- /* @__PURE__ */ jsxRuntime.jsx(ui.Hint, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
7758
- ] }),
7759
- /* @__PURE__ */ jsxRuntime.jsx(
7760
- Combobox,
7761
- {
7762
- id: "promotion-combobox",
7763
- "aria-describedby": "promotion-combobox-hint",
7764
- isFetchingNextPage: comboboxData.isFetchingNextPage,
7765
- fetchNextPage: comboboxData.fetchNextPage,
7766
- options: comboboxData.options,
7767
- onSearchValueChange: comboboxData.onSearchValueChange,
7768
- searchValue: comboboxData.searchValue,
7769
- disabled: comboboxData.disabled || isAddingPromotions,
7770
- onChange: add,
7771
- value: comboboxValue
7772
- }
7773
- )
7774
- ] }),
7775
- /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
7776
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: promotions?.map((promotion) => /* @__PURE__ */ jsxRuntime.jsx(
7777
- PromotionItem,
7778
- {
7779
- promotion,
7780
- orderId: preview.id,
7781
- isLoading: isPending
7782
- },
7783
- promotion.id
7784
- )) })
7785
- ] }) }),
7786
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
7787
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
7788
- /* @__PURE__ */ jsxRuntime.jsx(
7789
- ui.Button,
7790
- {
7791
- size: "small",
7792
- type: "submit",
7793
- isLoading: isSubmitting || isAddingPromotions,
7794
- children: "Save"
7795
- }
7796
- )
7797
- ] }) })
7798
- ] });
7799
- };
7800
- const PromotionItem = ({
7801
- promotion,
7802
- orderId,
7803
- isLoading
7804
- }) => {
7805
- const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
7806
- const onRemove = async () => {
7807
- removePromotions(
7808
- {
7809
- promo_codes: [promotion.code]
7810
- },
7811
- {
7812
- onError: (e) => {
7813
- ui.toast.error(e.message);
7814
- }
7815
- }
7816
- );
7817
- };
7818
- const displayValue = getDisplayValue(promotion);
7819
- return /* @__PURE__ */ jsxRuntime.jsxs(
7820
- "div",
7821
- {
7822
- className: ui.clx(
7823
- "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
7824
- {
7825
- "animate-pulse": isLoading
7826
- }
7827
- ),
7828
- children: [
7829
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7830
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
7831
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
7832
- displayValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
7833
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: displayValue }),
7834
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "·" })
7835
- ] }),
7836
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "capitalize", children: promotion.application_method?.allocation })
7837
- ] })
7838
- ] }),
7839
- /* @__PURE__ */ jsxRuntime.jsx(
7840
- ui.IconButton,
7841
- {
7842
- size: "small",
7843
- type: "button",
7844
- variant: "transparent",
7845
- onClick: onRemove,
7846
- isLoading: isPending || isLoading,
7847
- children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, {})
7848
- }
7849
- )
7850
- ]
7851
- },
7852
- promotion.id
7853
- );
7854
- };
7855
- function getDisplayValue(promotion) {
7856
- const value = promotion.application_method?.value;
7857
- if (!value) {
7858
- return null;
7859
- }
7860
- if (promotion.application_method?.type === "fixed") {
7861
- const currency = promotion.application_method?.currency_code;
7862
- if (!currency) {
7863
- return null;
7864
- }
7865
- return getLocaleAmount(value, currency);
7866
- } else if (promotion.application_method?.type === "percentage") {
7867
- return formatPercentage(value);
7868
- }
7869
- return null;
7870
- }
7871
- const formatter = new Intl.NumberFormat([], {
7872
- style: "percent",
7873
- minimumFractionDigits: 2
7874
- });
7875
- const formatPercentage = (value, isPercentageValue = false) => {
7876
- let val = value || 0;
7877
- if (!isPercentageValue) {
7878
- val = val / 100;
7879
- }
7880
- return formatter.format(val);
7881
- };
7882
- function getPromotionIds(items, shippingMethods) {
7883
- const promotionIds = /* @__PURE__ */ new Set();
7884
- for (const item of items) {
7885
- if (item.adjustments) {
7886
- for (const adjustment of item.adjustments) {
7887
- if (adjustment.promotion_id) {
7888
- promotionIds.add(adjustment.promotion_id);
7889
- }
7890
- }
7891
- }
7892
- }
7893
- for (const shippingMethod of shippingMethods) {
7894
- if (shippingMethod.adjustments) {
7895
- for (const adjustment of shippingMethod.adjustments) {
7896
- if (adjustment.promotion_id) {
7897
- promotionIds.add(adjustment.promotion_id);
7475
+ const onKeydown = React.useCallback(
7476
+ (e) => {
7477
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
7478
+ if (data || isSubmitting) {
7479
+ return;
7898
7480
  }
7481
+ onSubmit();
7899
7482
  }
7900
- }
7901
- }
7902
- return Array.from(promotionIds);
7903
- }
7904
- const SalesChannel = () => {
7905
- const { id } = reactRouterDom.useParams();
7906
- const { draft_order, isPending, isError, error } = useDraftOrder(
7907
- id,
7908
- {
7909
- fields: "+sales_channel_id"
7910
7483
  },
7911
- {
7912
- enabled: !!id
7913
- }
7914
- );
7915
- if (isError) {
7916
- throw error;
7917
- }
7918
- const ISrEADY = !!draft_order && !isPending;
7919
- return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
7920
- /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
7921
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Sales Channel" }) }),
7922
- /* @__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" }) })
7923
- ] }),
7924
- ISrEADY && /* @__PURE__ */ jsxRuntime.jsx(SalesChannelForm, { order: draft_order })
7925
- ] });
7926
- };
7927
- const SalesChannelForm = ({ order }) => {
7928
- const form = reactHookForm.useForm({
7929
- defaultValues: {
7930
- sales_channel_id: order.sales_channel_id || ""
7931
- },
7932
- resolver: zod$1.zodResolver(schema$2)
7933
- });
7934
- const { mutateAsync, isPending } = useUpdateDraftOrder(order.id);
7935
- const { handleSuccess } = useRouteModal();
7936
- const onSubmit = form.handleSubmit(async (data) => {
7937
- await mutateAsync(
7938
- {
7939
- sales_channel_id: data.sales_channel_id
7940
- },
7941
- {
7942
- onSuccess: () => {
7943
- ui.toast.success("Sales channel updated");
7944
- handleSuccess();
7945
- },
7946
- onError: (error) => {
7947
- ui.toast.error(error.message);
7948
- }
7949
- }
7950
- );
7951
- });
7952
- return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
7953
- KeyboundForm,
7954
- {
7955
- className: "flex flex-1 flex-col overflow-hidden",
7956
- onSubmit,
7957
- children: [
7958
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { className: "flex flex-col gap-y-6 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(SalesChannelField, { control: form.control, order }) }),
7959
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
7960
- /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
7961
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
7962
- ] }) })
7963
- ]
7964
- }
7965
- ) });
7966
- };
7967
- const SalesChannelField = ({ control, order }) => {
7968
- const salesChannels = useComboboxData({
7969
- queryFn: async (params) => {
7970
- return await sdk.admin.salesChannel.list(params);
7971
- },
7972
- queryKey: ["sales-channels"],
7973
- getOptions: (data) => {
7974
- return data.sales_channels.map((salesChannel) => ({
7975
- label: salesChannel.name,
7976
- value: salesChannel.id
7977
- }));
7978
- },
7979
- defaultValue: order.sales_channel_id || void 0
7980
- });
7981
- return /* @__PURE__ */ jsxRuntime.jsx(
7982
- Form$2.Field,
7983
- {
7984
- control,
7985
- name: "sales_channel_id",
7986
- render: ({ field }) => {
7987
- return /* @__PURE__ */ jsxRuntime.jsxs(Form$2.Item, { children: [
7988
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Label, { children: "Sales Channel" }),
7989
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
7990
- Combobox,
7991
- {
7992
- options: salesChannels.options,
7993
- fetchNextPage: salesChannels.fetchNextPage,
7994
- isFetchingNextPage: salesChannels.isFetchingNextPage,
7995
- searchValue: salesChannels.searchValue,
7996
- onSearchValueChange: salesChannels.onSearchValueChange,
7997
- placeholder: "Select sales channel",
7998
- ...field
7999
- }
8000
- ) }),
8001
- /* @__PURE__ */ jsxRuntime.jsx(Form$2.ErrorMessage, {})
8002
- ] });
8003
- }
8004
- }
8005
- );
8006
- };
8007
- const schema$2 = zod.z.object({
8008
- sales_channel_id: zod.z.string().min(1)
8009
- });
8010
- const STACKED_FOCUS_MODAL_ID = "shipping-form";
8011
- const Shipping = () => {
8012
- const { id } = reactRouterDom.useParams();
8013
- const { order, isPending, isError, error } = useOrder(id, {
8014
- fields: "+items.*,+items.variant.*,+items.variant.product.*,+items.variant.product.shipping_profile.*,+currency_code"
8015
- });
8016
- const {
8017
- order: preview,
8018
- isPending: isPreviewPending,
8019
- isError: isPreviewError,
8020
- error: previewError
8021
- } = useOrderPreview(id);
8022
- useInitiateOrderEdit({ preview });
8023
- const { onCancel } = useCancelOrderEdit({ preview });
8024
- if (isError) {
8025
- throw error;
8026
- }
8027
- if (isPreviewError) {
8028
- throw previewError;
8029
- }
8030
- const orderHasItems = (order?.items?.length || 0) > 0;
8031
- const isReady = preview && !isPreviewPending && order && !isPending;
8032
- return /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal, { onClose: onCancel, children: !orderHasItems ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col overflow-hidden ", children: [
8033
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Header, {}),
8034
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col items-center overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full max-w-[720px] flex-col gap-y-6 px-6 py-16", children: [
8035
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Shipping" }) }),
8036
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "This draft order currently has no items. Add items to the order before adding shipping." }) })
8037
- ] }) }) }),
8038
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }) })
8039
- ] }) : isReady ? /* @__PURE__ */ jsxRuntime.jsx(ShippingForm, { preview, order }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8040
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Edit Shipping" }) }),
8041
- /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Loading data for the draft order, please wait..." }) })
8042
- ] }) });
8043
- };
8044
- const ShippingForm = ({ preview, order }) => {
8045
- const { setIsOpen } = useStackedModal();
8046
- const [isSubmitting, setIsSubmitting] = React.useState(false);
8047
- const [data, setData] = React.useState(null);
8048
- const appliedShippingOptionIds = preview.shipping_methods?.map((method) => method.shipping_option_id).filter(Boolean);
8049
- const { shipping_options } = useShippingOptions(
8050
- {
8051
- id: appliedShippingOptionIds,
8052
- fields: "+service_zone.*,+service_zone.fulfillment_set.*,+service_zone.fulfillment_set.location.*"
8053
- },
8054
- {
8055
- enabled: appliedShippingOptionIds.length > 0
8056
- }
8057
- );
8058
- const uniqueShippingProfiles = React.useMemo(() => {
8059
- const profiles = /* @__PURE__ */ new Map();
8060
- getUniqueShippingProfiles(order.items).forEach((profile) => {
8061
- profiles.set(profile.id, profile);
8062
- });
8063
- shipping_options?.forEach((option) => {
8064
- profiles.set(option.shipping_profile_id, option.shipping_profile);
8065
- });
8066
- return Array.from(profiles.values());
8067
- }, [order.items, shipping_options]);
8068
- const { handleSuccess } = useRouteModal();
8069
- const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
8070
- const { mutateAsync: requestOrderEdit } = useDraftOrderRequestEdit(preview.id);
8071
- const { mutateAsync: removeShippingMethod } = useDraftOrderRemoveShippingMethod(preview.id);
8072
- const { mutateAsync: removeActionShippingMethod } = useDraftOrderRemoveActionShippingMethod(preview.id);
8073
- const onSubmit = async () => {
8074
- setIsSubmitting(true);
8075
- let requestSucceeded = false;
8076
- await requestOrderEdit(void 0, {
8077
- onError: (e) => {
8078
- ui.toast.error(`Failed to request order edit: ${e.message}`);
8079
- },
8080
- onSuccess: () => {
8081
- requestSucceeded = true;
8082
- }
8083
- });
8084
- if (!requestSucceeded) {
8085
- setIsSubmitting(false);
8086
- return;
8087
- }
8088
- await confirmOrderEdit(void 0, {
8089
- onError: (e) => {
8090
- ui.toast.error(`Failed to confirm order edit: ${e.message}`);
8091
- },
8092
- onSuccess: () => {
8093
- handleSuccess();
8094
- },
8095
- onSettled: () => {
8096
- setIsSubmitting(false);
8097
- }
8098
- });
8099
- };
8100
- const onKeydown = React.useCallback(
8101
- (e) => {
8102
- if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
8103
- if (data || isSubmitting) {
8104
- return;
8105
- }
8106
- onSubmit();
8107
- }
8108
- },
8109
- [data, isSubmitting, onSubmit]
7484
+ [data, isSubmitting, onSubmit]
8110
7485
  );
8111
7486
  React.useEffect(() => {
8112
7487
  document.addEventListener("keydown", onKeydown);
@@ -9398,86 +8773,711 @@ const Illustration = () => {
9398
8773
  /* @__PURE__ */ jsxRuntime.jsx("g", { clipPath: "url(#clip4_20915_38670)", children: /* @__PURE__ */ jsxRuntime.jsx(
9399
8774
  "path",
9400
8775
  {
9401
- d: "M136.504 95.1945L129.12 95.1519L129.095 99.3617",
9402
- stroke: "#A1A1AA",
9403
- strokeWidth: "1.5",
9404
- strokeLinecap: "round",
9405
- strokeLinejoin: "round"
8776
+ d: "M136.504 95.1945L129.12 95.1519L129.095 99.3617",
8777
+ stroke: "#A1A1AA",
8778
+ strokeWidth: "1.5",
8779
+ strokeLinecap: "round",
8780
+ strokeLinejoin: "round"
8781
+ }
8782
+ ) }),
8783
+ /* @__PURE__ */ jsxRuntime.jsx("g", { clipPath: "url(#clip5_20915_38670)", children: /* @__PURE__ */ jsxRuntime.jsx(
8784
+ "path",
8785
+ {
8786
+ d: "M146.894 101.198L139.51 101.155L139.486 105.365",
8787
+ stroke: "#A1A1AA",
8788
+ strokeWidth: "1.5",
8789
+ strokeLinecap: "round",
8790
+ strokeLinejoin: "round"
8791
+ }
8792
+ ) }),
8793
+ /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
8794
+ /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip0_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
8795
+ "rect",
8796
+ {
8797
+ width: "12",
8798
+ height: "12",
8799
+ fill: "white",
8800
+ transform: "matrix(0.865865 0.500278 -0.871576 0.490261 138.36 74.6508)"
8801
+ }
8802
+ ) }),
8803
+ /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip1_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
8804
+ "rect",
8805
+ {
8806
+ width: "12",
8807
+ height: "12",
8808
+ fill: "white",
8809
+ transform: "matrix(0.865865 0.500278 -0.871576 0.490261 148.75 80.6541)"
8810
+ }
8811
+ ) }),
8812
+ /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip2_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
8813
+ "rect",
8814
+ {
8815
+ width: "12",
8816
+ height: "12",
8817
+ fill: "white",
8818
+ transform: "matrix(0.865865 0.500278 -0.871576 0.490261 159.141 86.6575)"
8819
+ }
8820
+ ) }),
8821
+ /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip3_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
8822
+ "rect",
8823
+ {
8824
+ width: "12",
8825
+ height: "12",
8826
+ fill: "white",
8827
+ transform: "matrix(0.865865 0.500278 -0.871576 0.490261 120.928 84.4561)"
8828
+ }
8829
+ ) }),
8830
+ /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip4_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
8831
+ "rect",
8832
+ {
8833
+ width: "12",
8834
+ height: "12",
8835
+ fill: "white",
8836
+ transform: "matrix(0.865865 0.500278 -0.871576 0.490261 131.318 90.4594)"
8837
+ }
8838
+ ) }),
8839
+ /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip5_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
8840
+ "rect",
8841
+ {
8842
+ width: "12",
8843
+ height: "12",
8844
+ fill: "white",
8845
+ transform: "matrix(0.865865 0.500278 -0.871576 0.490261 141.709 96.4627)"
8846
+ }
8847
+ ) })
8848
+ ] })
8849
+ ]
8850
+ }
8851
+ );
8852
+ };
8853
+ const schema = zod.z.object({
8854
+ customer_id: zod.z.string().min(1)
8855
+ });
8856
+ const InlineTip = React.forwardRef(
8857
+ ({ variant = "tip", label, className, children, ...props }, ref) => {
8858
+ const labelValue = label || (variant === "warning" ? "Warning" : "Tip");
8859
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8860
+ "div",
8861
+ {
8862
+ ref,
8863
+ className: ui.clx(
8864
+ "bg-ui-bg-component txt-small text-ui-fg-subtle grid grid-cols-[4px_1fr] items-start gap-3 rounded-lg border p-3",
8865
+ className
8866
+ ),
8867
+ ...props,
8868
+ children: [
8869
+ /* @__PURE__ */ jsxRuntime.jsx(
8870
+ "div",
8871
+ {
8872
+ role: "presentation",
8873
+ className: ui.clx("w-4px bg-ui-tag-neutral-icon h-full rounded-full", {
8874
+ "bg-ui-tag-orange-icon": variant === "warning"
8875
+ })
8876
+ }
8877
+ ),
8878
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-pretty", children: [
8879
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "txt-small-plus text-ui-fg-base", children: [
8880
+ labelValue,
8881
+ ":"
8882
+ ] }),
8883
+ " ",
8884
+ children
8885
+ ] })
8886
+ ]
8887
+ }
8888
+ );
8889
+ }
8890
+ );
8891
+ InlineTip.displayName = "InlineTip";
8892
+ const MetadataFieldSchema = zod.z.object({
8893
+ key: zod.z.string(),
8894
+ disabled: zod.z.boolean().optional(),
8895
+ value: zod.z.any()
8896
+ });
8897
+ const MetadataSchema = zod.z.object({
8898
+ metadata: zod.z.array(MetadataFieldSchema)
8899
+ });
8900
+ const Metadata = () => {
8901
+ const { id } = reactRouterDom.useParams();
8902
+ const { order, isPending, isError, error } = useOrder(id, {
8903
+ fields: "metadata"
8904
+ });
8905
+ if (isError) {
8906
+ throw error;
8907
+ }
8908
+ const isReady = !isPending && !!order;
8909
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { children: [
8910
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Header, { children: [
8911
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Metadata" }) }),
8912
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Description, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Add metadata to the draft order." }) })
8913
+ ] }),
8914
+ !isReady ? /* @__PURE__ */ jsxRuntime.jsx(PlaceholderInner, {}) : /* @__PURE__ */ jsxRuntime.jsx(MetadataForm, { orderId: id, metadata: order?.metadata })
8915
+ ] });
8916
+ };
8917
+ const METADATA_KEY_LABEL_ID = "metadata-form-key-label";
8918
+ const METADATA_VALUE_LABEL_ID = "metadata-form-value-label";
8919
+ const MetadataForm = ({ orderId, metadata }) => {
8920
+ const { handleSuccess } = useRouteModal();
8921
+ const hasUneditableRows = getHasUneditableRows(metadata);
8922
+ const { mutateAsync, isPending } = useUpdateDraftOrder(orderId);
8923
+ const form = reactHookForm.useForm({
8924
+ defaultValues: {
8925
+ metadata: getDefaultValues(metadata)
8926
+ },
8927
+ resolver: zod$1.zodResolver(MetadataSchema)
8928
+ });
8929
+ const handleSubmit = form.handleSubmit(async (data) => {
8930
+ const parsedData = parseValues(data);
8931
+ await mutateAsync(
8932
+ {
8933
+ metadata: parsedData
8934
+ },
8935
+ {
8936
+ onSuccess: () => {
8937
+ ui.toast.success("Metadata updated");
8938
+ handleSuccess();
8939
+ },
8940
+ onError: (error) => {
8941
+ ui.toast.error(error.message);
8942
+ }
8943
+ }
8944
+ );
8945
+ });
8946
+ const { fields, insert, remove } = reactHookForm.useFieldArray({
8947
+ control: form.control,
8948
+ name: "metadata"
8949
+ });
8950
+ function deleteRow(index) {
8951
+ remove(index);
8952
+ if (fields.length === 1) {
8953
+ insert(0, {
8954
+ key: "",
8955
+ value: "",
8956
+ disabled: false
8957
+ });
8958
+ }
8959
+ }
8960
+ function insertRow(index, position) {
8961
+ insert(index + (position === "above" ? 0 : 1), {
8962
+ key: "",
8963
+ value: "",
8964
+ disabled: false
8965
+ });
8966
+ }
8967
+ return /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Form, { form, children: /* @__PURE__ */ jsxRuntime.jsxs(
8968
+ KeyboundForm,
8969
+ {
8970
+ onSubmit: handleSubmit,
8971
+ className: "flex flex-1 flex-col overflow-hidden",
8972
+ children: [
8973
+ /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer.Body, { className: "flex flex-1 flex-col gap-y-8 overflow-y-auto", children: [
8974
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-1 divide-y rounded-lg", children: [
8975
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle grid grid-cols-2 divide-x rounded-t-lg", children: [
8976
+ /* @__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" }) }),
8977
+ /* @__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" }) })
8978
+ ] }),
8979
+ fields.map((field, index) => {
8980
+ const isDisabled = field.disabled || false;
8981
+ let placeholder = "-";
8982
+ if (typeof field.value === "object") {
8983
+ placeholder = "{ ... }";
8984
+ }
8985
+ if (Array.isArray(field.value)) {
8986
+ placeholder = "[ ... ]";
8987
+ }
8988
+ return /* @__PURE__ */ jsxRuntime.jsx(
8989
+ ConditionalTooltip,
8990
+ {
8991
+ showTooltip: isDisabled,
8992
+ content: "This row is disabled because it contains non-primitive data.",
8993
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group/table relative", children: [
8994
+ /* @__PURE__ */ jsxRuntime.jsxs(
8995
+ "div",
8996
+ {
8997
+ className: ui.clx("grid grid-cols-2 divide-x", {
8998
+ "overflow-hidden rounded-b-lg": index === fields.length - 1
8999
+ }),
9000
+ children: [
9001
+ /* @__PURE__ */ jsxRuntime.jsx(
9002
+ Form$2.Field,
9003
+ {
9004
+ control: form.control,
9005
+ name: `metadata.${index}.key`,
9006
+ render: ({ field: field2 }) => {
9007
+ return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
9008
+ GridInput,
9009
+ {
9010
+ "aria-labelledby": METADATA_KEY_LABEL_ID,
9011
+ ...field2,
9012
+ disabled: isDisabled,
9013
+ placeholder: "Key"
9014
+ }
9015
+ ) }) });
9016
+ }
9017
+ }
9018
+ ),
9019
+ /* @__PURE__ */ jsxRuntime.jsx(
9020
+ Form$2.Field,
9021
+ {
9022
+ control: form.control,
9023
+ name: `metadata.${index}.value`,
9024
+ render: ({ field: { value, ...field2 } }) => {
9025
+ return /* @__PURE__ */ jsxRuntime.jsx(Form$2.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Form$2.Control, { children: /* @__PURE__ */ jsxRuntime.jsx(
9026
+ GridInput,
9027
+ {
9028
+ "aria-labelledby": METADATA_VALUE_LABEL_ID,
9029
+ ...field2,
9030
+ value: isDisabled ? placeholder : value,
9031
+ disabled: isDisabled,
9032
+ placeholder: "Value"
9033
+ }
9034
+ ) }) });
9035
+ }
9036
+ }
9037
+ )
9038
+ ]
9039
+ }
9040
+ ),
9041
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
9042
+ /* @__PURE__ */ jsxRuntime.jsx(
9043
+ ui.DropdownMenu.Trigger,
9044
+ {
9045
+ className: ui.clx(
9046
+ "invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
9047
+ {
9048
+ hidden: isDisabled
9049
+ }
9050
+ ),
9051
+ disabled: isDisabled,
9052
+ asChild: true,
9053
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "2xsmall", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisVertical, {}) })
9054
+ }
9055
+ ),
9056
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
9057
+ /* @__PURE__ */ jsxRuntime.jsxs(
9058
+ ui.DropdownMenu.Item,
9059
+ {
9060
+ className: "gap-x-2",
9061
+ onClick: () => insertRow(index, "above"),
9062
+ children: [
9063
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpMini, { className: "text-ui-fg-subtle" }),
9064
+ "Insert row above"
9065
+ ]
9066
+ }
9067
+ ),
9068
+ /* @__PURE__ */ jsxRuntime.jsxs(
9069
+ ui.DropdownMenu.Item,
9070
+ {
9071
+ className: "gap-x-2",
9072
+ onClick: () => insertRow(index, "below"),
9073
+ children: [
9074
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownMini, { className: "text-ui-fg-subtle" }),
9075
+ "Insert row below"
9076
+ ]
9077
+ }
9078
+ ),
9079
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
9080
+ /* @__PURE__ */ jsxRuntime.jsxs(
9081
+ ui.DropdownMenu.Item,
9082
+ {
9083
+ className: "gap-x-2",
9084
+ onClick: () => deleteRow(index),
9085
+ children: [
9086
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
9087
+ "Delete row"
9088
+ ]
9089
+ }
9090
+ )
9091
+ ] })
9092
+ ] })
9093
+ ] })
9094
+ },
9095
+ field.id
9096
+ );
9097
+ })
9098
+ ] }),
9099
+ 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." })
9100
+ ] }),
9101
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
9102
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", type: "button", children: "Cancel" }) }),
9103
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", type: "submit", isLoading: isPending, children: "Save" })
9104
+ ] }) })
9105
+ ]
9106
+ }
9107
+ ) });
9108
+ };
9109
+ const GridInput = React.forwardRef(({ className, ...props }, ref) => {
9110
+ return /* @__PURE__ */ jsxRuntime.jsx(
9111
+ "input",
9112
+ {
9113
+ ref,
9114
+ ...props,
9115
+ autoComplete: "off",
9116
+ className: ui.clx(
9117
+ "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",
9118
+ className
9119
+ )
9120
+ }
9121
+ );
9122
+ });
9123
+ GridInput.displayName = "MetadataForm.GridInput";
9124
+ const PlaceholderInner = () => {
9125
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
9126
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[148ox] w-full rounded-lg" }) }),
9127
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
9128
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }),
9129
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" })
9130
+ ] }) })
9131
+ ] });
9132
+ };
9133
+ const EDITABLE_TYPES = ["string", "number", "boolean"];
9134
+ function getDefaultValues(metadata) {
9135
+ if (!metadata || !Object.keys(metadata).length) {
9136
+ return [
9137
+ {
9138
+ key: "",
9139
+ value: "",
9140
+ disabled: false
9141
+ }
9142
+ ];
9143
+ }
9144
+ return Object.entries(metadata).map(([key, value]) => {
9145
+ if (!EDITABLE_TYPES.includes(typeof value)) {
9146
+ return {
9147
+ key,
9148
+ value,
9149
+ disabled: true
9150
+ };
9151
+ }
9152
+ let stringValue = value;
9153
+ if (typeof value !== "string") {
9154
+ stringValue = JSON.stringify(value);
9155
+ }
9156
+ return {
9157
+ key,
9158
+ value: stringValue,
9159
+ original_key: key
9160
+ };
9161
+ });
9162
+ }
9163
+ function parseValues(values) {
9164
+ const metadata = values.metadata;
9165
+ const isEmpty = !metadata.length || metadata.length === 1 && !metadata[0].key && !metadata[0].value;
9166
+ if (isEmpty) {
9167
+ return null;
9168
+ }
9169
+ const update = {};
9170
+ metadata.forEach((field) => {
9171
+ let key = field.key;
9172
+ let value = field.value;
9173
+ const disabled = field.disabled;
9174
+ if (!key || !value) {
9175
+ return;
9176
+ }
9177
+ if (disabled) {
9178
+ update[key] = value;
9179
+ return;
9180
+ }
9181
+ key = key.trim();
9182
+ value = value.trim();
9183
+ if (value === "true") {
9184
+ update[key] = true;
9185
+ } else if (value === "false") {
9186
+ update[key] = false;
9187
+ } else {
9188
+ const parsedNumber = parseFloat(value);
9189
+ if (!isNaN(parsedNumber)) {
9190
+ update[key] = parsedNumber;
9191
+ } else {
9192
+ update[key] = value;
9193
+ }
9194
+ }
9195
+ });
9196
+ return update;
9197
+ }
9198
+ function getHasUneditableRows(metadata) {
9199
+ if (!metadata) {
9200
+ return false;
9201
+ }
9202
+ return Object.values(metadata).some(
9203
+ (value) => !EDITABLE_TYPES.includes(typeof value)
9204
+ );
9205
+ }
9206
+ const PROMOTION_QUERY_KEY = "promotions";
9207
+ const promotionsQueryKeys = {
9208
+ list: (query2) => [
9209
+ PROMOTION_QUERY_KEY,
9210
+ query2 ? query2 : void 0
9211
+ ],
9212
+ detail: (id, query2) => [
9213
+ PROMOTION_QUERY_KEY,
9214
+ id,
9215
+ query2 ? query2 : void 0
9216
+ ]
9217
+ };
9218
+ const usePromotions = (query2, options) => {
9219
+ const { data, ...rest } = reactQuery.useQuery({
9220
+ queryKey: promotionsQueryKeys.list(query2),
9221
+ queryFn: async () => sdk.admin.promotion.list(query2),
9222
+ ...options
9223
+ });
9224
+ return { ...data, ...rest };
9225
+ };
9226
+ const Promotions = () => {
9227
+ const { id } = reactRouterDom.useParams();
9228
+ const {
9229
+ order: preview,
9230
+ isError: isPreviewError,
9231
+ error: previewError
9232
+ } = useOrderPreview(id, void 0);
9233
+ useInitiateOrderEdit({ preview });
9234
+ const { onCancel } = useCancelOrderEdit({ preview });
9235
+ if (isPreviewError) {
9236
+ throw previewError;
9237
+ }
9238
+ const isReady = !!preview;
9239
+ return /* @__PURE__ */ jsxRuntime.jsxs(RouteDrawer, { onClose: onCancel, children: [
9240
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Edit Promotions" }) }) }),
9241
+ isReady && /* @__PURE__ */ jsxRuntime.jsx(PromotionForm, { preview })
9242
+ ] });
9243
+ };
9244
+ const PromotionForm = ({ preview }) => {
9245
+ const { items, shipping_methods } = preview;
9246
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
9247
+ const [comboboxValue, setComboboxValue] = React.useState("");
9248
+ const { handleSuccess } = useRouteModal();
9249
+ const { mutateAsync: addPromotions, isPending: isAddingPromotions } = useDraftOrderAddPromotions(preview.id);
9250
+ const promoIds = getPromotionIds(items, shipping_methods);
9251
+ const { promotions, isPending, isError, error } = usePromotions(
9252
+ {
9253
+ id: promoIds
9254
+ },
9255
+ {
9256
+ enabled: !!promoIds.length
9257
+ }
9258
+ );
9259
+ const comboboxData = useComboboxData({
9260
+ queryKey: ["promotions", "combobox", promoIds],
9261
+ queryFn: async (params) => {
9262
+ return await sdk.admin.promotion.list({
9263
+ ...params,
9264
+ id: {
9265
+ $nin: promoIds
9266
+ }
9267
+ });
9268
+ },
9269
+ getOptions: (data) => {
9270
+ return data.promotions.map((promotion) => ({
9271
+ label: promotion.code,
9272
+ value: promotion.code
9273
+ }));
9274
+ }
9275
+ });
9276
+ const add = async (value) => {
9277
+ if (!value) {
9278
+ return;
9279
+ }
9280
+ addPromotions(
9281
+ {
9282
+ promo_codes: [value]
9283
+ },
9284
+ {
9285
+ onError: (e) => {
9286
+ ui.toast.error(e.message);
9287
+ comboboxData.onSearchValueChange("");
9288
+ setComboboxValue("");
9289
+ },
9290
+ onSuccess: () => {
9291
+ comboboxData.onSearchValueChange("");
9292
+ setComboboxValue("");
9293
+ }
9294
+ }
9295
+ );
9296
+ };
9297
+ const { mutateAsync: confirmOrderEdit } = useDraftOrderConfirmEdit(preview.id);
9298
+ const { mutateAsync: requestOrderEdit } = useOrderEditRequest(preview.id);
9299
+ const onSubmit = async () => {
9300
+ setIsSubmitting(true);
9301
+ let requestSucceeded = false;
9302
+ await requestOrderEdit(void 0, {
9303
+ onError: (e) => {
9304
+ ui.toast.error(e.message);
9305
+ },
9306
+ onSuccess: () => {
9307
+ requestSucceeded = true;
9308
+ }
9309
+ });
9310
+ if (!requestSucceeded) {
9311
+ setIsSubmitting(false);
9312
+ return;
9313
+ }
9314
+ await confirmOrderEdit(void 0, {
9315
+ onError: (e) => {
9316
+ ui.toast.error(e.message);
9317
+ },
9318
+ onSuccess: () => {
9319
+ handleSuccess();
9320
+ },
9321
+ onSettled: () => {
9322
+ setIsSubmitting(false);
9323
+ }
9324
+ });
9325
+ };
9326
+ if (isError) {
9327
+ throw error;
9328
+ }
9329
+ return /* @__PURE__ */ jsxRuntime.jsxs(KeyboundForm, { className: "flex flex-1 flex-col", onSubmit, children: [
9330
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
9331
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
9332
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
9333
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", htmlFor: "promotion-combobox", children: "Apply promotions" }),
9334
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Hint, { id: "promotion-combobox-hint", children: "Manage promotions that should be applied to the order." })
9335
+ ] }),
9336
+ /* @__PURE__ */ jsxRuntime.jsx(
9337
+ Combobox,
9338
+ {
9339
+ id: "promotion-combobox",
9340
+ "aria-describedby": "promotion-combobox-hint",
9341
+ isFetchingNextPage: comboboxData.isFetchingNextPage,
9342
+ fetchNextPage: comboboxData.fetchNextPage,
9343
+ options: comboboxData.options,
9344
+ onSearchValueChange: comboboxData.onSearchValueChange,
9345
+ searchValue: comboboxData.searchValue,
9346
+ disabled: comboboxData.disabled || isAddingPromotions,
9347
+ onChange: add,
9348
+ value: comboboxValue
9406
9349
  }
9407
- ) }),
9408
- /* @__PURE__ */ jsxRuntime.jsx("g", { clipPath: "url(#clip5_20915_38670)", children: /* @__PURE__ */ jsxRuntime.jsx(
9409
- "path",
9350
+ )
9351
+ ] }),
9352
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { variant: "dashed" }),
9353
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: promotions?.map((promotion) => /* @__PURE__ */ jsxRuntime.jsx(
9354
+ PromotionItem,
9355
+ {
9356
+ promotion,
9357
+ orderId: preview.id,
9358
+ isLoading: isPending
9359
+ },
9360
+ promotion.id
9361
+ )) })
9362
+ ] }) }),
9363
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
9364
+ /* @__PURE__ */ jsxRuntime.jsx(RouteDrawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "Cancel" }) }),
9365
+ /* @__PURE__ */ jsxRuntime.jsx(
9366
+ ui.Button,
9367
+ {
9368
+ size: "small",
9369
+ type: "submit",
9370
+ isLoading: isSubmitting || isAddingPromotions,
9371
+ children: "Save"
9372
+ }
9373
+ )
9374
+ ] }) })
9375
+ ] });
9376
+ };
9377
+ const PromotionItem = ({
9378
+ promotion,
9379
+ orderId,
9380
+ isLoading
9381
+ }) => {
9382
+ const { mutateAsync: removePromotions, isPending } = useDraftOrderRemovePromotions(orderId);
9383
+ const onRemove = async () => {
9384
+ removePromotions(
9385
+ {
9386
+ promo_codes: [promotion.code]
9387
+ },
9388
+ {
9389
+ onError: (e) => {
9390
+ ui.toast.error(e.message);
9391
+ }
9392
+ }
9393
+ );
9394
+ };
9395
+ const displayValue = getDisplayValue(promotion);
9396
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9397
+ "div",
9398
+ {
9399
+ className: ui.clx(
9400
+ "bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
9401
+ {
9402
+ "animate-pulse": isLoading
9403
+ }
9404
+ ),
9405
+ children: [
9406
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9407
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: promotion.code }),
9408
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle flex items-center gap-1.5", children: [
9409
+ displayValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
9410
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: displayValue }),
9411
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "·" })
9412
+ ] }),
9413
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "capitalize", children: promotion.application_method?.allocation })
9414
+ ] })
9415
+ ] }),
9416
+ /* @__PURE__ */ jsxRuntime.jsx(
9417
+ ui.IconButton,
9410
9418
  {
9411
- d: "M146.894 101.198L139.51 101.155L139.486 105.365",
9412
- stroke: "#A1A1AA",
9413
- strokeWidth: "1.5",
9414
- strokeLinecap: "round",
9415
- strokeLinejoin: "round"
9419
+ size: "small",
9420
+ type: "button",
9421
+ variant: "transparent",
9422
+ onClick: onRemove,
9423
+ isLoading: isPending || isLoading,
9424
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, {})
9416
9425
  }
9417
- ) }),
9418
- /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
9419
- /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip0_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
9420
- "rect",
9421
- {
9422
- width: "12",
9423
- height: "12",
9424
- fill: "white",
9425
- transform: "matrix(0.865865 0.500278 -0.871576 0.490261 138.36 74.6508)"
9426
- }
9427
- ) }),
9428
- /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip1_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
9429
- "rect",
9430
- {
9431
- width: "12",
9432
- height: "12",
9433
- fill: "white",
9434
- transform: "matrix(0.865865 0.500278 -0.871576 0.490261 148.75 80.6541)"
9435
- }
9436
- ) }),
9437
- /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip2_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
9438
- "rect",
9439
- {
9440
- width: "12",
9441
- height: "12",
9442
- fill: "white",
9443
- transform: "matrix(0.865865 0.500278 -0.871576 0.490261 159.141 86.6575)"
9444
- }
9445
- ) }),
9446
- /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip3_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
9447
- "rect",
9448
- {
9449
- width: "12",
9450
- height: "12",
9451
- fill: "white",
9452
- transform: "matrix(0.865865 0.500278 -0.871576 0.490261 120.928 84.4561)"
9453
- }
9454
- ) }),
9455
- /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip4_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
9456
- "rect",
9457
- {
9458
- width: "12",
9459
- height: "12",
9460
- fill: "white",
9461
- transform: "matrix(0.865865 0.500278 -0.871576 0.490261 131.318 90.4594)"
9462
- }
9463
- ) }),
9464
- /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip5_20915_38670", children: /* @__PURE__ */ jsxRuntime.jsx(
9465
- "rect",
9466
- {
9467
- width: "12",
9468
- height: "12",
9469
- fill: "white",
9470
- transform: "matrix(0.865865 0.500278 -0.871576 0.490261 141.709 96.4627)"
9471
- }
9472
- ) })
9473
- ] })
9426
+ )
9474
9427
  ]
9475
- }
9428
+ },
9429
+ promotion.id
9476
9430
  );
9477
9431
  };
9478
- const schema = zod.z.object({
9479
- customer_id: zod.z.string().min(1)
9432
+ function getDisplayValue(promotion) {
9433
+ const value = promotion.application_method?.value;
9434
+ if (!value) {
9435
+ return null;
9436
+ }
9437
+ if (promotion.application_method?.type === "fixed") {
9438
+ const currency = promotion.application_method?.currency_code;
9439
+ if (!currency) {
9440
+ return null;
9441
+ }
9442
+ return getLocaleAmount(value, currency);
9443
+ } else if (promotion.application_method?.type === "percentage") {
9444
+ return formatPercentage(value);
9445
+ }
9446
+ return null;
9447
+ }
9448
+ const formatter = new Intl.NumberFormat([], {
9449
+ style: "percent",
9450
+ minimumFractionDigits: 2
9480
9451
  });
9452
+ const formatPercentage = (value, isPercentageValue = false) => {
9453
+ let val = value || 0;
9454
+ if (!isPercentageValue) {
9455
+ val = val / 100;
9456
+ }
9457
+ return formatter.format(val);
9458
+ };
9459
+ function getPromotionIds(items, shippingMethods) {
9460
+ const promotionIds = /* @__PURE__ */ new Set();
9461
+ for (const item of items) {
9462
+ if (item.adjustments) {
9463
+ for (const adjustment of item.adjustments) {
9464
+ if (adjustment.promotion_id) {
9465
+ promotionIds.add(adjustment.promotion_id);
9466
+ }
9467
+ }
9468
+ }
9469
+ }
9470
+ for (const shippingMethod of shippingMethods) {
9471
+ if (shippingMethod.adjustments) {
9472
+ for (const adjustment of shippingMethod.adjustments) {
9473
+ if (adjustment.promotion_id) {
9474
+ promotionIds.add(adjustment.promotion_id);
9475
+ }
9476
+ }
9477
+ }
9478
+ }
9479
+ return Array.from(promotionIds);
9480
+ }
9481
9481
  const widgetModule = { widgets: [] };
9482
9482
  const routeModule = {
9483
9483
  routes: [
@@ -9502,10 +9502,6 @@ const routeModule = {
9502
9502
  Component: BillingAddress,
9503
9503
  path: "/draft-orders/:id/billing-address"
9504
9504
  },
9505
- {
9506
- Component: CustomItems,
9507
- path: "/draft-orders/:id/custom-items"
9508
- },
9509
9505
  {
9510
9506
  Component: Email,
9511
9507
  path: "/draft-orders/:id/email"
@@ -9515,12 +9511,8 @@ const routeModule = {
9515
9511
  path: "/draft-orders/:id/items"
9516
9512
  },
9517
9513
  {
9518
- Component: Metadata,
9519
- path: "/draft-orders/:id/metadata"
9520
- },
9521
- {
9522
- Component: Promotions,
9523
- path: "/draft-orders/:id/promotions"
9514
+ Component: CustomItems,
9515
+ path: "/draft-orders/:id/custom-items"
9524
9516
  },
9525
9517
  {
9526
9518
  Component: SalesChannel,
@@ -9537,6 +9529,14 @@ const routeModule = {
9537
9529
  {
9538
9530
  Component: TransferOwnership,
9539
9531
  path: "/draft-orders/:id/transfer-ownership"
9532
+ },
9533
+ {
9534
+ Component: Metadata,
9535
+ path: "/draft-orders/:id/metadata"
9536
+ },
9537
+ {
9538
+ Component: Promotions,
9539
+ path: "/draft-orders/:id/promotions"
9540
9540
  }
9541
9541
  ]
9542
9542
  }