@open-mercato/core 0.6.6-develop.6184.1.b7e55f8d61 → 0.6.6-develop.6201.1.8ceb502c4b

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.
Files changed (63) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/frontend/login.js +5 -5
  3. package/dist/modules/auth/frontend/login.js.map +2 -2
  4. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +3 -2
  5. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
  6. package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js +26 -14
  7. package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js.map +2 -2
  8. package/dist/modules/customers/api/deals/[id]/route.js +169 -149
  9. package/dist/modules/customers/api/deals/[id]/route.js.map +3 -3
  10. package/dist/modules/customers/components/detail/InlineEditors.js +9 -4
  11. package/dist/modules/customers/components/detail/InlineEditors.js.map +2 -2
  12. package/dist/modules/integrations/backend/integrations/[id]/page.js +13 -9
  13. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  14. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +81 -37
  15. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  16. package/dist/modules/integrations/backend/integrations/detail-page-refresh.js +17 -0
  17. package/dist/modules/integrations/backend/integrations/detail-page-refresh.js.map +7 -0
  18. package/dist/modules/integrations/backend/integrations/page.js +45 -24
  19. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  20. package/dist/modules/notifications/api/[id]/action/route.js +18 -3
  21. package/dist/modules/notifications/api/[id]/action/route.js.map +2 -2
  22. package/dist/modules/notifications/api/[id]/restore/route.js +19 -3
  23. package/dist/modules/notifications/api/[id]/restore/route.js.map +2 -2
  24. package/dist/modules/notifications/api/mark-all-read/route.js +18 -4
  25. package/dist/modules/notifications/api/mark-all-read/route.js.map +2 -2
  26. package/dist/modules/notifications/api/route.js +17 -4
  27. package/dist/modules/notifications/api/route.js.map +2 -2
  28. package/dist/modules/notifications/api/settings/route.js +26 -5
  29. package/dist/modules/notifications/api/settings/route.js.map +2 -2
  30. package/dist/modules/notifications/lib/routeHelpers.js +64 -5
  31. package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
  32. package/dist/modules/sales/components/documents/SalesDocumentForm.js +299 -297
  33. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  34. package/dist/modules/workflows/components/EdgeEditDialog.js +120 -141
  35. package/dist/modules/workflows/components/EdgeEditDialog.js.map +2 -2
  36. package/dist/modules/workflows/components/NodeEditDialog.js +114 -118
  37. package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
  38. package/dist/modules/workflows/frontend/checkout-demo/page.js +155 -260
  39. package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
  40. package/package.json +7 -7
  41. package/src/modules/auth/frontend/login.tsx +5 -5
  42. package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +4 -3
  43. package/src/modules/catalog/widgets/injection/product-seo/widget.client.tsx +27 -17
  44. package/src/modules/customers/api/deals/[id]/route.ts +208 -174
  45. package/src/modules/customers/components/detail/InlineEditors.tsx +6 -10
  46. package/src/modules/integrations/backend/integrations/[id]/page.tsx +13 -9
  47. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +78 -35
  48. package/src/modules/integrations/backend/integrations/detail-page-refresh.ts +36 -0
  49. package/src/modules/integrations/backend/integrations/page.tsx +42 -21
  50. package/src/modules/notifications/api/[id]/action/route.ts +17 -2
  51. package/src/modules/notifications/api/[id]/restore/route.ts +19 -3
  52. package/src/modules/notifications/api/mark-all-read/route.ts +18 -4
  53. package/src/modules/notifications/api/route.ts +16 -3
  54. package/src/modules/notifications/api/settings/route.ts +26 -5
  55. package/src/modules/notifications/lib/routeHelpers.ts +101 -4
  56. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +351 -327
  57. package/src/modules/workflows/components/EdgeEditDialog.tsx +132 -157
  58. package/src/modules/workflows/components/NodeEditDialog.tsx +116 -121
  59. package/src/modules/workflows/frontend/checkout-demo/page.tsx +172 -239
  60. package/src/modules/workflows/i18n/de.json +2 -0
  61. package/src/modules/workflows/i18n/en.json +2 -0
  62. package/src/modules/workflows/i18n/es.json +2 -0
  63. package/src/modules/workflows/i18n/pl.json +2 -0
@@ -300,6 +300,302 @@ function normalizeAddressDraft(draft) {
300
300
  assign("isPrimary", "isPrimary");
301
301
  return Object.keys(normalized).length ? normalized : null;
302
302
  }
303
+ function DocumentNumberField({ value, setValue, values, t }) {
304
+ const formValues = values ?? {};
305
+ const kind = formValues.documentKind === "order" ? "order" : "quote";
306
+ const [loading, setLoading] = React.useState(false);
307
+ const [error, setError] = React.useState(null);
308
+ const autoValueRef = React.useRef(null);
309
+ const lastKindRef = React.useRef(null);
310
+ const requestNumber = React.useCallback(async () => {
311
+ setLoading(true);
312
+ setError(null);
313
+ try {
314
+ const call = await apiCall("/api/sales/document-numbers", {
315
+ method: "POST",
316
+ headers: { "content-type": "application/json" },
317
+ body: JSON.stringify({ kind })
318
+ });
319
+ const nextNumber = typeof call.result?.number === "string" ? call.result.number : null;
320
+ if (call.ok && nextNumber) {
321
+ autoValueRef.current = nextNumber;
322
+ lastKindRef.current = kind;
323
+ setValue(nextNumber);
324
+ } else {
325
+ setError(call.result?.error || t("sales.documents.form.errors.numberGenerate", "Could not generate a document number."));
326
+ }
327
+ } catch (err) {
328
+ console.error("sales.documents.generateNumber", err);
329
+ setError(t("sales.documents.form.errors.numberGenerate", "Could not generate a document number."));
330
+ } finally {
331
+ setLoading(false);
332
+ }
333
+ }, [kind, setValue, t]);
334
+ React.useEffect(() => {
335
+ const current = typeof value === "string" ? value.trim() : "";
336
+ if (!current.length && !lastKindRef.current) {
337
+ void requestNumber();
338
+ } else {
339
+ lastKindRef.current = kind;
340
+ }
341
+ }, [kind, requestNumber, value]);
342
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
343
+ /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-2 md:flex-row md:items-center md:gap-3", children: [
344
+ /* @__PURE__ */ jsx(
345
+ Input,
346
+ {
347
+ value: typeof value === "string" ? value : "",
348
+ onChange: (event) => setValue(event.target.value),
349
+ disabled: loading,
350
+ spellCheck: false,
351
+ className: "w-full md:flex-1"
352
+ }
353
+ ),
354
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: requestNumber, disabled: loading, children: loading ? t("sales.documents.form.numberLoading", "Generating\u2026") : t("sales.documents.form.numberRefresh", "Generate") })
355
+ ] }),
356
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: kind === "order" ? t("sales.documents.form.numberHintOrder", "Format applies to orders and uses the configured counter.") : t("sales.documents.form.numberHintQuote", "Format applies to quotes and uses the configured counter.") }),
357
+ error ? /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error }) : null
358
+ ] });
359
+ }
360
+ function BillingAddressSectionField({ values, setFormValue, t, addressesLoading, addressOptions, addressFormat }) {
361
+ const formValues = values ?? {};
362
+ const updateValue = setFormValue ?? (() => {
363
+ });
364
+ const useCustom = formValues.useCustomBilling === true;
365
+ const selectedId = typeof formValues.billingAddressId === "string" ? formValues.billingAddressId : "";
366
+ const draft = formValues.billingAddressDraft ?? {};
367
+ const customerRequired = !formValues.customerEntityId;
368
+ const sameAsShipping = formValues.sameAsShipping !== false;
369
+ const shippingId = typeof formValues.shippingAddressId === "string" ? formValues.shippingAddressId : null;
370
+ const shippingDraft = formValues.shippingAddressDraft ?? {};
371
+ const shippingDraftKey = JSON.stringify(shippingDraft);
372
+ const billingDraftKey = JSON.stringify(draft);
373
+ const useCustomShipping = formValues.useCustomShipping === true;
374
+ React.useEffect(() => {
375
+ if (!sameAsShipping) return;
376
+ if ((formValues.billingAddressId ?? null) !== shippingId) {
377
+ updateValue("billingAddressId", shippingId);
378
+ }
379
+ if (useCustomShipping !== (formValues.useCustomBilling === true)) {
380
+ updateValue("useCustomBilling", useCustomShipping);
381
+ }
382
+ if (useCustomShipping && shippingDraftKey !== billingDraftKey) {
383
+ updateValue("billingAddressDraft", shippingDraft);
384
+ }
385
+ }, [
386
+ billingDraftKey,
387
+ sameAsShipping,
388
+ updateValue,
389
+ shippingDraft,
390
+ shippingDraftKey,
391
+ shippingId,
392
+ useCustomShipping,
393
+ formValues.billingAddressId,
394
+ formValues.useCustomBilling
395
+ ]);
396
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
397
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 md:flex-row md:items-center md:justify-between", children: [
398
+ /* @__PURE__ */ jsxs("div", { children: [
399
+ /* @__PURE__ */ jsx("p", { className: "text-base font-semibold", children: t("sales.documents.form.billing.title", "Billing address") }),
400
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: sameAsShipping ? t("sales.documents.form.address.sameAsShippingHint", "Billing will mirror the shipping address. Uncheck to edit.") : t("sales.documents.form.billing.hint", "Select an address or define a new one.") })
401
+ ] }),
402
+ /* @__PURE__ */ jsx(
403
+ SwitchField,
404
+ {
405
+ label: t("sales.documents.form.address.sameAsShipping", "Same as shipping"),
406
+ flip: true,
407
+ checked: sameAsShipping,
408
+ onCheckedChange: (checked) => {
409
+ updateValue("sameAsShipping", checked);
410
+ if (checked) {
411
+ updateValue("useCustomBilling", useCustomShipping);
412
+ updateValue("billingAddressId", shippingId);
413
+ updateValue("billingAddressDraft", shippingDraft);
414
+ }
415
+ }
416
+ }
417
+ )
418
+ ] }),
419
+ !sameAsShipping ? /* @__PURE__ */ jsxs(Fragment, { children: [
420
+ !useCustom ? /* @__PURE__ */ jsxs(
421
+ Select,
422
+ {
423
+ value: selectedId || void 0,
424
+ onValueChange: (value) => updateValue("billingAddressId", value || null),
425
+ disabled: addressesLoading || customerRequired,
426
+ children: [
427
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(
428
+ SelectValue,
429
+ {
430
+ placeholder: addressesLoading ? t("sales.documents.form.address.loading", "Loading addresses\u2026") : t("sales.documents.form.address.placeholder", "Select address")
431
+ }
432
+ ) }),
433
+ /* @__PURE__ */ jsx(SelectContent, { children: addressOptions.map((addr) => {
434
+ const optionLabel = addr.summary ? `${addr.label} \u2014 ${addr.summary}` : addr.label;
435
+ return /* @__PURE__ */ jsx(SelectItem, { value: addr.id, children: optionLabel }, addr.id);
436
+ }) })
437
+ ]
438
+ }
439
+ ) : null,
440
+ /* @__PURE__ */ jsx(
441
+ SwitchField,
442
+ {
443
+ label: t("sales.documents.form.shipping.custom", "Define new address"),
444
+ flip: true,
445
+ checked: useCustom,
446
+ onCheckedChange: (checked) => updateValue("useCustomBilling", checked),
447
+ disabled: false
448
+ }
449
+ ),
450
+ useCustom ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
451
+ /* @__PURE__ */ jsx(
452
+ AddressEditor,
453
+ {
454
+ value: draft,
455
+ format: addressFormat,
456
+ t,
457
+ onChange: (next) => updateValue("billingAddressDraft", next),
458
+ hidePrimaryToggle: true
459
+ }
460
+ ),
461
+ /* @__PURE__ */ jsx(
462
+ SwitchField,
463
+ {
464
+ containerClassName: "col-span-2",
465
+ label: t("sales.documents.form.address.saveToCustomer", "Save this address to the customer"),
466
+ flip: true,
467
+ checked: formValues.saveBillingAddress === true,
468
+ onCheckedChange: (checked) => updateValue("saveBillingAddress", checked)
469
+ }
470
+ )
471
+ ] }) : null
472
+ ] }) : null
473
+ ] });
474
+ }
475
+ function CustomerGroupComponent({ values, setValue, t, customers, setCustomers, customerQuerySetter, loadAddresses, loadCustomers, fetchCustomerEmail, resetAddressFormState }) {
476
+ const emailValue = typeof values.customerEmail === "string" ? values.customerEmail : "";
477
+ const { duplicate, checking } = useEmailDuplicateCheck(emailValue, {
478
+ disabled: false,
479
+ debounceMs: 400,
480
+ matchMode: "prefix"
481
+ });
482
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
483
+ /* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsx(
484
+ LookupSelect,
485
+ {
486
+ value: typeof values.customerEntityId === "string" ? values.customerEntityId : null,
487
+ onChange: (next) => {
488
+ if (next !== values.customerEntityId) {
489
+ resetAddressFormState(setValue);
490
+ }
491
+ setValue("customerEntityId", next);
492
+ loadAddresses(next).then((addrs) => {
493
+ const primary = addrs.find((addr) => addr.isPrimary);
494
+ if (primary) {
495
+ setValue("shippingAddressId", primary.id);
496
+ }
497
+ }).catch((err) => {
498
+ console.error("sales.documents.autoSelectAddress", err);
499
+ });
500
+ if (next) {
501
+ const match = customers.find((entry) => entry.id === next);
502
+ const possibleEmail = typeof match?.primaryEmail === "string" && match.primaryEmail.length ? match.primaryEmail : null;
503
+ if (possibleEmail) {
504
+ setValue("customerEmail", possibleEmail);
505
+ } else {
506
+ fetchCustomerEmail(next, match?.kind).then((email) => {
507
+ if (email) setValue("customerEmail", email);
508
+ }).catch(() => {
509
+ });
510
+ }
511
+ }
512
+ },
513
+ fetchItems: async (query) => {
514
+ const options = await loadCustomers(query);
515
+ return options.map((opt) => ({
516
+ id: opt.id,
517
+ title: opt.label,
518
+ subtitle: opt.subtitle ?? void 0,
519
+ icon: opt.kind === "person" ? /* @__PURE__ */ jsx(UserRound, { className: "h-5 w-5 text-muted-foreground" }) : /* @__PURE__ */ jsx(Building2, { className: "h-5 w-5 text-muted-foreground" })
520
+ }));
521
+ },
522
+ actionSlot: /* @__PURE__ */ jsx(
523
+ CustomerQuickCreate,
524
+ {
525
+ t,
526
+ onCreated: ({ id, email, label, kind, subtitle }) => {
527
+ customerQuerySetter.current?.(label);
528
+ setCustomers((prev) => {
529
+ const exists = prev.some((entry) => entry.id === id);
530
+ if (exists) return prev;
531
+ const next = {
532
+ id,
533
+ label,
534
+ subtitle: subtitle ?? void 0,
535
+ kind,
536
+ primaryEmail: email ?? null
537
+ };
538
+ return [next, ...prev];
539
+ });
540
+ setValue("customerEntityId", id);
541
+ resetAddressFormState(setValue);
542
+ loadAddresses(id).then((addrs) => {
543
+ const primary = addrs.find((addr) => addr.isPrimary);
544
+ if (primary) {
545
+ setValue("shippingAddressId", primary.id);
546
+ }
547
+ }).catch((err) => {
548
+ console.error("sales.documents.autoSelectAddress", err);
549
+ });
550
+ if (email && !values.customerEmail) {
551
+ setValue("customerEmail", email);
552
+ }
553
+ }
554
+ }
555
+ ),
556
+ onReady: ({ setQuery }) => {
557
+ customerQuerySetter.current = setQuery;
558
+ },
559
+ searchPlaceholder: t("sales.documents.form.customer.placeholder", "Search customers\u2026"),
560
+ loadingLabel: t("sales.documents.form.customer.loading", "Loading customers\u2026"),
561
+ emptyLabel: t("sales.documents.form.customer.empty", "No customers found."),
562
+ selectedHintLabel: (id) => t("sales.documents.form.customer.selected", "Selected customer: {{id}}", { id })
563
+ }
564
+ ) }),
565
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
566
+ /* @__PURE__ */ jsx(
567
+ EmailInput,
568
+ {
569
+ value: emailValue,
570
+ onChange: (event) => setValue("customerEmail", event.target.value),
571
+ placeholder: t("sales.documents.form.email.placeholder", "Email used for the document"),
572
+ spellCheck: false
573
+ }
574
+ ),
575
+ duplicate ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded border bg-muted px-3 py-2 text-xs text-muted-foreground", children: [
576
+ /* @__PURE__ */ jsx("span", { children: t("customers.people.form.emailDuplicateNotice", void 0, { name: duplicate.displayName }) }),
577
+ /* @__PURE__ */ jsx(
578
+ Button,
579
+ {
580
+ size: "sm",
581
+ variant: "secondary",
582
+ className: "px-4",
583
+ type: "button",
584
+ disabled: values.customerEntityId === duplicate.id,
585
+ "aria-disabled": values.customerEntityId === duplicate.id,
586
+ onClick: () => {
587
+ setValue("customerEntityId", duplicate.id);
588
+ resetAddressFormState(setValue);
589
+ loadAddresses(duplicate.id);
590
+ },
591
+ children: values.customerEntityId === duplicate.id ? t("sales.documents.form.email.alreadySelected", "Selected customer") : t("sales.documents.form.email.selectCustomer", "Select customer")
592
+ }
593
+ )
594
+ ] }) : null,
595
+ !duplicate && checking ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("customers.people.form.emailChecking") }) : null
596
+ ] })
597
+ ] });
598
+ }
303
599
  function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind, inboxPreFill }) {
304
600
  const t = useT();
305
601
  const [customers, setCustomers] = React.useState([]);
@@ -596,63 +892,6 @@ function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind, inbox
596
892
  },
597
893
  []
598
894
  );
599
- function DocumentNumberField({ value, setValue, values }) {
600
- const formValues = values ?? {};
601
- const kind = formValues.documentKind === "order" ? "order" : "quote";
602
- const [loading, setLoading] = React.useState(false);
603
- const [error, setError] = React.useState(null);
604
- const autoValueRef = React.useRef(null);
605
- const lastKindRef = React.useRef(null);
606
- const requestNumber = React.useCallback(async () => {
607
- setLoading(true);
608
- setError(null);
609
- try {
610
- const call = await apiCall("/api/sales/document-numbers", {
611
- method: "POST",
612
- headers: { "content-type": "application/json" },
613
- body: JSON.stringify({ kind })
614
- });
615
- const nextNumber = typeof call.result?.number === "string" ? call.result.number : null;
616
- if (call.ok && nextNumber) {
617
- autoValueRef.current = nextNumber;
618
- lastKindRef.current = kind;
619
- setValue(nextNumber);
620
- } else {
621
- setError(call.result?.error || t("sales.documents.form.errors.numberGenerate", "Could not generate a document number."));
622
- }
623
- } catch (err) {
624
- console.error("sales.documents.generateNumber", err);
625
- setError(t("sales.documents.form.errors.numberGenerate", "Could not generate a document number."));
626
- } finally {
627
- setLoading(false);
628
- }
629
- }, [kind, setValue, t]);
630
- React.useEffect(() => {
631
- const current = typeof value === "string" ? value.trim() : "";
632
- if (!current.length && !lastKindRef.current) {
633
- void requestNumber();
634
- } else {
635
- lastKindRef.current = kind;
636
- }
637
- }, [kind, requestNumber, value]);
638
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
639
- /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-2 md:flex-row md:items-center md:gap-3", children: [
640
- /* @__PURE__ */ jsx(
641
- Input,
642
- {
643
- value: typeof value === "string" ? value : "",
644
- onChange: (event) => setValue(event.target.value),
645
- disabled: loading,
646
- spellCheck: false,
647
- className: "w-full md:flex-1"
648
- }
649
- ),
650
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: requestNumber, disabled: loading, children: loading ? t("sales.documents.form.numberLoading", "Generating\u2026") : t("sales.documents.form.numberRefresh", "Generate") })
651
- ] }),
652
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: kind === "order" ? t("sales.documents.form.numberHintOrder", "Format applies to orders and uses the configured counter.") : t("sales.documents.form.numberHintQuote", "Format applies to quotes and uses the configured counter.") }),
653
- error ? /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error }) : null
654
- ] });
655
- }
656
895
  const fields = React.useMemo(() => [
657
896
  {
658
897
  id: "documentKind",
@@ -688,7 +927,7 @@ function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind, inbox
688
927
  label: t("sales.documents.form.number", "Document number"),
689
928
  type: "custom",
690
929
  required: true,
691
- component: DocumentNumberField
930
+ component: (props) => /* @__PURE__ */ jsx(DocumentNumberField, { ...props, t })
692
931
  },
693
932
  {
694
933
  id: "currencyCode",
@@ -826,121 +1065,7 @@ function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind, inbox
826
1065
  id: "billingAddressSection",
827
1066
  label: "",
828
1067
  type: "custom",
829
- component: function BillingAddressSectionField({ values, setFormValue }) {
830
- const formValues = values ?? {};
831
- const updateValue = setFormValue ?? (() => {
832
- });
833
- const useCustom = formValues.useCustomBilling === true;
834
- const selectedId = typeof formValues.billingAddressId === "string" ? formValues.billingAddressId : "";
835
- const draft = formValues.billingAddressDraft ?? {};
836
- const customerRequired = !formValues.customerEntityId;
837
- const sameAsShipping = formValues.sameAsShipping !== false;
838
- const shippingId = typeof formValues.shippingAddressId === "string" ? formValues.shippingAddressId : null;
839
- const shippingDraft = formValues.shippingAddressDraft ?? {};
840
- const shippingDraftKey = JSON.stringify(shippingDraft);
841
- const billingDraftKey = JSON.stringify(draft);
842
- const useCustomShipping = formValues.useCustomShipping === true;
843
- React.useEffect(() => {
844
- if (!sameAsShipping) return;
845
- if ((formValues.billingAddressId ?? null) !== shippingId) {
846
- updateValue("billingAddressId", shippingId);
847
- }
848
- if (useCustomShipping !== (formValues.useCustomBilling === true)) {
849
- updateValue("useCustomBilling", useCustomShipping);
850
- }
851
- if (useCustomShipping && shippingDraftKey !== billingDraftKey) {
852
- updateValue("billingAddressDraft", shippingDraft);
853
- }
854
- }, [
855
- billingDraftKey,
856
- sameAsShipping,
857
- updateValue,
858
- shippingDraft,
859
- shippingDraftKey,
860
- shippingId,
861
- useCustomShipping,
862
- formValues.billingAddressId,
863
- formValues.useCustomBilling
864
- ]);
865
- return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
866
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 md:flex-row md:items-center md:justify-between", children: [
867
- /* @__PURE__ */ jsxs("div", { children: [
868
- /* @__PURE__ */ jsx("p", { className: "text-base font-semibold", children: t("sales.documents.form.billing.title", "Billing address") }),
869
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: sameAsShipping ? t("sales.documents.form.address.sameAsShippingHint", "Billing will mirror the shipping address. Uncheck to edit.") : t("sales.documents.form.billing.hint", "Select an address or define a new one.") })
870
- ] }),
871
- /* @__PURE__ */ jsx(
872
- SwitchField,
873
- {
874
- label: t("sales.documents.form.address.sameAsShipping", "Same as shipping"),
875
- flip: true,
876
- checked: sameAsShipping,
877
- onCheckedChange: (checked) => {
878
- updateValue("sameAsShipping", checked);
879
- if (checked) {
880
- updateValue("useCustomBilling", useCustomShipping);
881
- updateValue("billingAddressId", shippingId);
882
- updateValue("billingAddressDraft", shippingDraft);
883
- }
884
- }
885
- }
886
- )
887
- ] }),
888
- !sameAsShipping ? /* @__PURE__ */ jsxs(Fragment, { children: [
889
- !useCustom ? /* @__PURE__ */ jsxs(
890
- Select,
891
- {
892
- value: selectedId || void 0,
893
- onValueChange: (value) => updateValue("billingAddressId", value || null),
894
- disabled: addressesLoading || customerRequired,
895
- children: [
896
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(
897
- SelectValue,
898
- {
899
- placeholder: addressesLoading ? t("sales.documents.form.address.loading", "Loading addresses\u2026") : t("sales.documents.form.address.placeholder", "Select address")
900
- }
901
- ) }),
902
- /* @__PURE__ */ jsx(SelectContent, { children: addressOptions.map((addr) => {
903
- const optionLabel = addr.summary ? `${addr.label} \u2014 ${addr.summary}` : addr.label;
904
- return /* @__PURE__ */ jsx(SelectItem, { value: addr.id, children: optionLabel }, addr.id);
905
- }) })
906
- ]
907
- }
908
- ) : null,
909
- /* @__PURE__ */ jsx(
910
- SwitchField,
911
- {
912
- label: t("sales.documents.form.shipping.custom", "Define new address"),
913
- flip: true,
914
- checked: useCustom,
915
- onCheckedChange: (checked) => updateValue("useCustomBilling", checked),
916
- disabled: false
917
- }
918
- ),
919
- useCustom ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
920
- /* @__PURE__ */ jsx(
921
- AddressEditor,
922
- {
923
- value: draft,
924
- format: addressFormat,
925
- t,
926
- onChange: (next) => updateValue("billingAddressDraft", next),
927
- hidePrimaryToggle: true
928
- }
929
- ),
930
- /* @__PURE__ */ jsx(
931
- SwitchField,
932
- {
933
- containerClassName: "col-span-2",
934
- label: t("sales.documents.form.address.saveToCustomer", "Save this address to the customer"),
935
- flip: true,
936
- checked: formValues.saveBillingAddress === true,
937
- onCheckedChange: (checked) => updateValue("saveBillingAddress", checked)
938
- }
939
- )
940
- ] }) : null
941
- ] }) : null
942
- ] });
943
- }
1068
+ component: (props) => /* @__PURE__ */ jsx(BillingAddressSectionField, { ...props, t, addressesLoading, addressOptions, addressFormat })
944
1069
  },
945
1070
  {
946
1071
  id: "comments",
@@ -973,130 +1098,7 @@ function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind, inbox
973
1098
  title: "",
974
1099
  column: 1,
975
1100
  fields: [],
976
- component: function CustomerGroupComponent({ values, setValue }) {
977
- const emailValue = typeof values.customerEmail === "string" ? values.customerEmail : "";
978
- const { duplicate, checking } = useEmailDuplicateCheck(emailValue, {
979
- disabled: false,
980
- debounceMs: 400,
981
- matchMode: "prefix"
982
- });
983
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
984
- /* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsx(
985
- LookupSelect,
986
- {
987
- value: typeof values.customerEntityId === "string" ? values.customerEntityId : null,
988
- onChange: (next) => {
989
- if (next !== values.customerEntityId) {
990
- resetAddressFormState(setValue);
991
- }
992
- setValue("customerEntityId", next);
993
- loadAddresses(next).then((addrs) => {
994
- const primary = addrs.find((addr) => addr.isPrimary);
995
- if (primary) {
996
- setValue("shippingAddressId", primary.id);
997
- }
998
- }).catch((err) => {
999
- console.error("sales.documents.autoSelectAddress", err);
1000
- });
1001
- if (next) {
1002
- const match = customers.find((entry) => entry.id === next);
1003
- const possibleEmail = typeof match?.primaryEmail === "string" && match.primaryEmail.length ? match.primaryEmail : null;
1004
- if (possibleEmail) {
1005
- setValue("customerEmail", possibleEmail);
1006
- } else {
1007
- fetchCustomerEmail(next, match?.kind).then((email) => {
1008
- if (email) setValue("customerEmail", email);
1009
- }).catch(() => {
1010
- });
1011
- }
1012
- }
1013
- },
1014
- fetchItems: async (query) => {
1015
- const options = await loadCustomers(query);
1016
- return options.map((opt) => ({
1017
- id: opt.id,
1018
- title: opt.label,
1019
- subtitle: opt.subtitle ?? void 0,
1020
- icon: opt.kind === "person" ? /* @__PURE__ */ jsx(UserRound, { className: "h-5 w-5 text-muted-foreground" }) : /* @__PURE__ */ jsx(Building2, { className: "h-5 w-5 text-muted-foreground" })
1021
- }));
1022
- },
1023
- actionSlot: /* @__PURE__ */ jsx(
1024
- CustomerQuickCreate,
1025
- {
1026
- t,
1027
- onCreated: ({ id, email, label, kind, subtitle }) => {
1028
- customerQuerySetter.current?.(label);
1029
- setCustomers((prev) => {
1030
- const exists = prev.some((entry) => entry.id === id);
1031
- if (exists) return prev;
1032
- const next = {
1033
- id,
1034
- label,
1035
- subtitle: subtitle ?? void 0,
1036
- kind,
1037
- primaryEmail: email ?? null
1038
- };
1039
- return [next, ...prev];
1040
- });
1041
- setValue("customerEntityId", id);
1042
- resetAddressFormState(setValue);
1043
- loadAddresses(id).then((addrs) => {
1044
- const primary = addrs.find((addr) => addr.isPrimary);
1045
- if (primary) {
1046
- setValue("shippingAddressId", primary.id);
1047
- }
1048
- }).catch((err) => {
1049
- console.error("sales.documents.autoSelectAddress", err);
1050
- });
1051
- if (email && !values.customerEmail) {
1052
- setValue("customerEmail", email);
1053
- }
1054
- }
1055
- }
1056
- ),
1057
- onReady: ({ setQuery }) => {
1058
- customerQuerySetter.current = setQuery;
1059
- },
1060
- searchPlaceholder: t("sales.documents.form.customer.placeholder", "Search customers\u2026"),
1061
- loadingLabel: t("sales.documents.form.customer.loading", "Loading customers\u2026"),
1062
- emptyLabel: t("sales.documents.form.customer.empty", "No customers found."),
1063
- selectedHintLabel: (id) => t("sales.documents.form.customer.selected", "Selected customer: {{id}}", { id })
1064
- }
1065
- ) }),
1066
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1067
- /* @__PURE__ */ jsx(
1068
- EmailInput,
1069
- {
1070
- value: emailValue,
1071
- onChange: (event) => setValue("customerEmail", event.target.value),
1072
- placeholder: t("sales.documents.form.email.placeholder", "Email used for the document"),
1073
- spellCheck: false
1074
- }
1075
- ),
1076
- duplicate ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded border bg-muted px-3 py-2 text-xs text-muted-foreground", children: [
1077
- /* @__PURE__ */ jsx("span", { children: t("customers.people.form.emailDuplicateNotice", void 0, { name: duplicate.displayName }) }),
1078
- /* @__PURE__ */ jsx(
1079
- Button,
1080
- {
1081
- size: "sm",
1082
- variant: "secondary",
1083
- className: "px-4",
1084
- type: "button",
1085
- disabled: values.customerEntityId === duplicate.id,
1086
- "aria-disabled": values.customerEntityId === duplicate.id,
1087
- onClick: () => {
1088
- setValue("customerEntityId", duplicate.id);
1089
- resetAddressFormState(setValue);
1090
- loadAddresses(duplicate.id);
1091
- },
1092
- children: values.customerEntityId === duplicate.id ? t("sales.documents.form.email.alreadySelected", "Selected customer") : t("sales.documents.form.email.selectCustomer", "Select customer")
1093
- }
1094
- )
1095
- ] }) : null,
1096
- !duplicate && checking ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("customers.people.form.emailChecking") }) : null
1097
- ] })
1098
- ] });
1099
- }
1101
+ component: (ctx) => /* @__PURE__ */ jsx(CustomerGroupComponent, { ...ctx, t, customers, setCustomers, customerQuerySetter, loadAddresses, loadCustomers, fetchCustomerEmail, resetAddressFormState })
1100
1102
  },
1101
1103
  { id: "channels-comments", title: "", column: 1, fields: ["channelId", "comments"] },
1102
1104
  { id: "currency", title: "", column: 2, fields: ["currencyCode"] },