@voyant-travel/bookings-react 0.138.2 → 0.138.4

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 (66) hide show
  1. package/dist/admin/booking-contract-dialog.d.ts.map +1 -1
  2. package/dist/admin/booking-contract-dialog.js +11 -1
  3. package/dist/admin/use-booking-action-ledger-events.d.ts.map +1 -1
  4. package/dist/admin/use-booking-action-ledger-events.js +18 -3
  5. package/dist/components/booking-activity-timeline.d.ts.map +1 -1
  6. package/dist/components/booking-activity-timeline.js +4 -1
  7. package/dist/components/booking-document-dialog.d.ts.map +1 -1
  8. package/dist/components/booking-document-dialog.js +10 -2
  9. package/dist/components/booking-payment-reconciliation-banner.d.ts.map +1 -1
  10. package/dist/components/booking-payments-summary.d.ts +9 -1
  11. package/dist/components/booking-payments-summary.d.ts.map +1 -1
  12. package/dist/components/booking-payments-summary.js +57 -32
  13. package/dist/components/file-dropzone.d.ts +3 -1
  14. package/dist/components/file-dropzone.d.ts.map +1 -1
  15. package/dist/components/file-dropzone.js +2 -1
  16. package/dist/components/traveler-dialog.d.ts.map +1 -1
  17. package/dist/components/traveler-dialog.js +15 -1
  18. package/dist/components/traveler-list.d.ts.map +1 -1
  19. package/dist/components/traveler-list.js +30 -12
  20. package/dist/extras/hooks/use-slot-extra-manifest-mutation.d.ts +3 -3
  21. package/dist/extras/hooks/use-slot-extra-manifest.d.ts +1 -1
  22. package/dist/extras/query-options.d.ts +4 -4
  23. package/dist/extras/schemas.d.ts +3 -3
  24. package/dist/hooks/use-booking-action-ledger.d.ts +1 -1
  25. package/dist/hooks/use-booking-documents.d.ts.map +1 -1
  26. package/dist/hooks/use-booking-documents.js +3 -0
  27. package/dist/hooks/use-booking-note-mutation.d.ts.map +1 -1
  28. package/dist/hooks/use-booking-note-mutation.js +3 -0
  29. package/dist/hooks/use-supplier-status-mutation.d.ts +2 -2
  30. package/dist/hooks/use-supplier-status-mutation.d.ts.map +1 -1
  31. package/dist/hooks/use-supplier-status-mutation.js +10 -12
  32. package/dist/hooks/use-supplier-statuses.d.ts +1 -1
  33. package/dist/hooks/use-traveler-mutation.d.ts.map +1 -1
  34. package/dist/hooks/use-traveler-mutation.js +10 -12
  35. package/dist/hooks/use-traveler-with-travel-details-mutation.d.ts.map +1 -1
  36. package/dist/hooks/use-traveler-with-travel-details-mutation.js +3 -0
  37. package/dist/i18n/en-base.d.ts +9 -0
  38. package/dist/i18n/en-base.d.ts.map +1 -1
  39. package/dist/i18n/en-base.js +9 -0
  40. package/dist/i18n/en-sections.d.ts +1 -0
  41. package/dist/i18n/en-sections.d.ts.map +1 -1
  42. package/dist/i18n/en-sections.js +1 -0
  43. package/dist/i18n/en.d.ts +10 -0
  44. package/dist/i18n/en.d.ts.map +1 -1
  45. package/dist/i18n/messages-base.d.ts +3 -0
  46. package/dist/i18n/messages-base.d.ts.map +1 -1
  47. package/dist/i18n/messages-sections.d.ts +1 -0
  48. package/dist/i18n/messages-sections.d.ts.map +1 -1
  49. package/dist/i18n/provider.d.ts +20 -0
  50. package/dist/i18n/provider.d.ts.map +1 -1
  51. package/dist/i18n/ro-base.d.ts +9 -0
  52. package/dist/i18n/ro-base.d.ts.map +1 -1
  53. package/dist/i18n/ro-base.js +9 -0
  54. package/dist/i18n/ro-sections.d.ts +1 -0
  55. package/dist/i18n/ro-sections.d.ts.map +1 -1
  56. package/dist/i18n/ro-sections.js +1 -0
  57. package/dist/i18n/ro.d.ts +10 -0
  58. package/dist/i18n/ro.d.ts.map +1 -1
  59. package/dist/journey/components/booking-journey-rules.d.ts.map +1 -1
  60. package/dist/journey/components/booking-journey-rules.js +2 -2
  61. package/dist/query-options.d.ts +4 -4
  62. package/dist/requirements/hooks/use-booking-questions.d.ts +1 -1
  63. package/dist/requirements/query-options.d.ts +4 -4
  64. package/dist/requirements/schemas.d.ts +3 -3
  65. package/dist/schemas.d.ts +6 -6
  66. package/package.json +16 -16
@@ -1 +1 @@
1
- {"version":3,"file":"booking-contract-dialog.d.ts","sourceRoot":"","sources":["../../src/admin/booking-contract-dialog.tsx"],"names":[],"mappings":"AA0BA,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,aAAa,EACb,SAAS,GACV,EAAE,0BAA0B,+BA8N5B"}
1
+ {"version":3,"file":"booking-contract-dialog.d.ts","sourceRoot":"","sources":["../../src/admin/booking-contract-dialog.tsx"],"names":[],"mappings":"AA2BA,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,aAAa,EACb,SAAS,GACV,EAAE,0BAA0B,+BA8O5B"}
@@ -6,6 +6,7 @@ import { legalQueryKeys, useLegalContractAttachmentMutation, useLegalContractMut
6
6
  import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, Input, Label, Skeleton, } from "@voyant-travel/ui/components";
7
7
  import { FileText, Loader2, Paperclip, X } from "lucide-react";
8
8
  import { useEffect, useState } from "react";
9
+ import { VoyantApiError } from "../client.js";
9
10
  import { useBookingContractGenerationMutation } from "../index.js";
10
11
  /**
11
12
  * "Add contract" dialog for a booking. Two modes:
@@ -29,6 +30,7 @@ export function BookingContractDialog({ open, onOpenChange, bookingId, bookingNu
29
30
  const [mode, setMode] = useState("generate");
30
31
  const [uploading, setUploading] = useState(false);
31
32
  const [error, setError] = useState(null);
33
+ const [previewRequested, setPreviewRequested] = useState(false);
32
34
  // Upload form state
33
35
  const [title, setTitle] = useState("");
34
36
  const [file, setFile] = useState(null);
@@ -43,6 +45,7 @@ export function BookingContractDialog({ open, onOpenChange, bookingId, bookingNu
43
45
  setFile(null);
44
46
  setError(null);
45
47
  setUploading(false);
48
+ setPreviewRequested(false);
46
49
  resetPreview();
47
50
  }, [open, resetPreview]);
48
51
  // Fetch preview HTML every time the dialog opens (or the mode flips
@@ -52,6 +55,7 @@ export function BookingContractDialog({ open, onOpenChange, bookingId, bookingNu
52
55
  useEffect(() => {
53
56
  if (!open || mode !== "generate")
54
57
  return;
58
+ setPreviewRequested(true);
55
59
  fetchPreview();
56
60
  }, [open, mode, fetchPreview]);
57
61
  const handleGenerate = async () => {
@@ -102,10 +106,16 @@ export function BookingContractDialog({ open, onOpenChange, bookingId, bookingNu
102
106
  const submitting = generate.isPending || uploading;
103
107
  const previewReady = preview.data != null;
104
108
  const canSubmit = mode === "generate" ? previewReady && !submitting : file != null && !submitting;
109
+ const previewSetupMissing = preview.error instanceof VoyantApiError && preview.error.status === 404;
110
+ const showPreviewSetupHint = mode === "generate" &&
111
+ previewRequested &&
112
+ !preview.isPending &&
113
+ !preview.data &&
114
+ (!preview.isError || previewSetupMissing);
105
115
  return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "w-full! max-w-4xl! gap-0 p-0", children: [_jsxs(DialogHeader, { className: "shrink-0 border-b px-6 py-4", children: [_jsx(DialogTitle, { children: t.title }), _jsx(DialogDescription, { children: t.description })] }), _jsx("div", { className: "max-h-[70vh] overflow-y-auto", children: _jsxs("div", { className: "flex flex-col gap-4 px-6 py-5", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: t.modeLabel }), _jsx(SegmentedChoice, { value: mode, onChange: setMode, options: [
106
116
  { value: "generate", label: t.modeGenerate },
107
117
  { value: "upload", label: t.modeUpload },
108
- ] })] }), mode === "generate" ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: t.previewLabel }), _jsx("div", { className: "overflow-hidden rounded-md border bg-muted/30", children: preview.isPending ? (_jsxs("div", { className: "flex flex-col gap-3 p-6", children: [_jsx(Skeleton, { className: "h-6 w-1/2" }), _jsx(Skeleton, { className: "h-4 w-full" }), _jsx(Skeleton, { className: "h-4 w-5/6" }), _jsx(Skeleton, { className: "h-4 w-4/5" }), _jsx(Skeleton, { className: "h-4 w-full" })] })) : preview.isError ? (_jsxs("p", { className: "p-6 text-destructive text-sm", children: [t.previewErrorPrefix, " ", preview.error instanceof Error ? preview.error.message : t.previewFailed] })) : preview.data ? (_jsx("iframe", { title: preview.data.templateName || t.previewIframeFallback, srcDoc: wrapPreviewHtml(preview.data.html), sandbox: "", className: "h-[60vh] w-full border-0 bg-white" })) : null }), preview.data?.templateName ? (_jsxs("p", { className: "text-muted-foreground text-xs", children: [t.previewTemplateLabel, " ", preview.data.templateName] })) : null] })) : (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: t.uploadTitleLabel }), _jsx(Input, { value: title, onChange: (e) => setTitle(e.target.value), placeholder: bookingNumber ? `Contract ${bookingNumber}` : t.uploadTitlePlaceholder }), _jsx("p", { className: "text-muted-foreground text-xs", children: t.uploadTitleHint })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: t.uploadFileLabel }), _jsx("input", { type: "file", accept: "application/pdf", onChange: (e) => {
118
+ ] })] }), mode === "generate" ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: t.previewLabel }), _jsx("div", { className: "overflow-hidden rounded-md border bg-muted/30", children: preview.isPending ? (_jsxs("div", { className: "flex flex-col gap-3 p-6", children: [_jsx(Skeleton, { className: "h-6 w-1/2" }), _jsx(Skeleton, { className: "h-4 w-full" }), _jsx(Skeleton, { className: "h-4 w-5/6" }), _jsx(Skeleton, { className: "h-4 w-4/5" }), _jsx(Skeleton, { className: "h-4 w-full" })] })) : preview.isError && !previewSetupMissing ? (_jsxs("p", { className: "p-6 text-destructive text-sm", children: [t.previewErrorPrefix, " ", preview.error instanceof Error ? preview.error.message : t.previewFailed] })) : preview.data ? (_jsx("iframe", { title: preview.data.templateName || t.previewIframeFallback, srcDoc: wrapPreviewHtml(preview.data.html), sandbox: "", className: "h-[60vh] w-full border-0 bg-white" })) : (_jsx("div", { className: "p-6 text-muted-foreground text-sm", children: t.previewUnavailable })) }), preview.data?.templateName ? (_jsxs("p", { className: "text-muted-foreground text-xs", children: [t.previewTemplateLabel, " ", preview.data.templateName] })) : null, showPreviewSetupHint ? (_jsx("p", { className: "text-muted-foreground text-xs", children: t.previewSetupHint })) : null] })) : (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: t.uploadTitleLabel }), _jsx(Input, { value: title, onChange: (e) => setTitle(e.target.value), placeholder: bookingNumber ? `Contract ${bookingNumber}` : t.uploadTitlePlaceholder }), _jsx("p", { className: "text-muted-foreground text-xs", children: t.uploadTitleHint })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: t.uploadFileLabel }), _jsx("input", { type: "file", accept: "application/pdf", onChange: (e) => {
109
119
  const next = e.target.files?.[0] ?? null;
110
120
  setFile(next);
111
121
  }, className: "block w-full text-sm file:mr-3 file:rounded-md file:border file:bg-muted file:px-3 file:py-1.5 file:text-sm file:font-medium hover:file:bg-muted/70" }), file ? (_jsxs("div", { className: "flex items-center justify-between gap-2 rounded-md border bg-background px-3 py-1.5 text-sm", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx(Paperclip, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate", children: file.name })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: () => setFile(null), children: _jsx(X, { className: "h-3.5 w-3.5" }) })] })) : null] })] })), error ? _jsx("p", { className: "text-sm text-destructive", children: error }) : null] }) }), _jsxs("div", { className: "flex shrink-0 items-center justify-end gap-2 border-t px-6 py-4", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: submitting, children: t.cancel }), _jsxs(Button, { type: "button", disabled: !canSubmit, onClick: () => {
@@ -1 +1 @@
1
- {"version":3,"file":"use-booking-action-ledger-events.d.ts","sourceRoot":"","sources":["../../src/admin/use-booking-action-ledger-events.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAA;AAO/E;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG;IAC/D,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,MAAM,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI,CAAA;CAC/B,CAgDA"}
1
+ {"version":3,"file":"use-booking-action-ledger-events.d.ts","sourceRoot":"","sources":["../../src/admin/use-booking-action-ledger-events.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,4CAA4C,CAAA;AAO/F;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG;IAC/D,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,MAAM,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI,CAAA;CAC/B,CAiDA"}
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useOperatorAdminMessages } from "@voyant-travel/admin";
4
4
  import { Button } from "@voyant-travel/ui/components/button";
5
- import { ScrollText } from "lucide-react";
5
+ import { CreditCard, FileText, ScrollText } from "lucide-react";
6
6
  import { useMemo } from "react";
7
7
  import { useBookingActionLedger, } from "../index.js";
8
8
  /**
@@ -25,14 +25,15 @@ export function useBookingActionLedgerEvents(bookingId) {
25
25
  for (const page of pages) {
26
26
  for (const entry of page.data) {
27
27
  const traveler = travelersById.get(entry.targetId) ?? null;
28
+ const presentation = getLedgerTimelinePresentation(entry.actionName);
28
29
  all.push({
29
30
  id: `action:${entry.id}`,
30
- source: "action",
31
+ source: presentation.source,
31
32
  title: formatActionName(entry.actionName),
32
33
  description: formatLedgerDescription(entry, traveler, t.travelerFallback, t.targetBooking),
33
34
  actorId: entry.principalId,
34
35
  timestamp: entry.occurredAt,
35
- icon: ScrollText,
36
+ icon: presentation.icon,
36
37
  });
37
38
  }
38
39
  }
@@ -44,6 +45,20 @@ export function useBookingActionLedgerEvents(bookingId) {
44
45
  function formatActionName(value) {
45
46
  return value.replaceAll(".", " / ").replaceAll("_", " ");
46
47
  }
48
+ function getLedgerTimelinePresentation(actionName) {
49
+ if (actionName.startsWith("finance.invoice.") ||
50
+ actionName.startsWith("finance.invoice_line_item.") ||
51
+ actionName.startsWith("finance.credit_note.") ||
52
+ actionName.startsWith("finance.credit_note_line_item.")) {
53
+ return { source: "document", icon: FileText };
54
+ }
55
+ if (actionName.startsWith("finance.payment") ||
56
+ actionName.startsWith("finance.booking_payment") ||
57
+ actionName.startsWith("finance.booking_guarantee")) {
58
+ return { source: "payment", icon: CreditCard };
59
+ }
60
+ return { source: "action", icon: ScrollText };
61
+ }
47
62
  function formatTarget(entry, traveler, travelerFallback, bookingFallback) {
48
63
  if (traveler) {
49
64
  return [traveler.firstName, traveler.lastName].filter(Boolean).join(" ") || travelerFallback;
@@ -1 +1 @@
1
- {"version":3,"file":"booking-activity-timeline.d.ts","sourceRoot":"","sources":["../../src/components/booking-activity-timeline.tsx"],"names":[],"mappings":"AAYA,OAAO,EAML,KAAK,UAAU,EAKhB,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAA;IACpC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;IAC3C,0FAA0F;IAC1F,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE3E,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,cAAc,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,UAAU,CAAA;IAChB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACvC,CAAA;AA6BD,wBAAgB,uBAAuB,CAAC,EACtC,SAAS,EACT,eAA0B,EAC1B,gBAAgB,EAChB,MAAM,EACN,QAAa,GACd,EAAE,4BAA4B,qBAuJ9B"}
1
+ {"version":3,"file":"booking-activity-timeline.d.ts","sourceRoot":"","sources":["../../src/components/booking-activity-timeline.tsx"],"names":[],"mappings":"AAYA,OAAO,EAML,KAAK,UAAU,EAKhB,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAA;IACpC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;IAC3C,0FAA0F;IAC1F,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE3E,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,cAAc,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,UAAU,CAAA;IAChB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACvC,CAAA;AA6BD,wBAAgB,uBAAuB,CAAC,EACtC,SAAS,EACT,eAA0B,EAC1B,gBAAgB,EAChB,MAAM,EACN,QAAa,GACd,EAAE,4BAA4B,qBA0J9B"}
@@ -56,7 +56,10 @@ export function BookingActivityTimeline({ bookingId, paymentsVariant = "public",
56
56
  merged.push({
57
57
  id: `activity:${entry.id}`,
58
58
  source: "activity",
59
- title: messages.bookingActivityTimeline.activityTitles[entry.activityType] ?? entry.description,
59
+ title: entry.activityType === "note_added"
60
+ ? entry.description
61
+ : (messages.bookingActivityTimeline.activityTitles[entry.activityType] ??
62
+ entry.description),
60
63
  description: entry.description,
61
64
  // Prefer the hydrated display name; fall back to email then raw
62
65
  // id (system actors stay null and skip the "By …" render).
@@ -1 +1 @@
1
- {"version":3,"file":"booking-document-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-document-dialog.tsx"],"names":[],"mappings":"AAkDA,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,SAAS,GACV,EAAE,0BAA0B,+BAyK5B"}
1
+ {"version":3,"file":"booking-document-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-document-dialog.tsx"],"names":[],"mappings":"AAqDA,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,SAAS,GACV,EAAE,0BAA0B,+BA+K5B"}
@@ -19,7 +19,10 @@ function createDocumentFormSchema(messages) {
19
19
  .string()
20
20
  .min(1, messages.bookingDocumentDialog.validation.fileNameRequired)
21
21
  .max(500),
22
- fileUrl: z.string().url(messages.bookingDocumentDialog.validation.fileUrlInvalid),
22
+ fileUrl: z
23
+ .string()
24
+ .min(1, messages.bookingDocumentDialog.validation.fileRequired)
25
+ .url(messages.bookingDocumentDialog.validation.fileUrlInvalid),
23
26
  travelerId: z.string().optional().nullable(),
24
27
  expiresAt: z.string().optional().nullable(),
25
28
  notes: z.string().optional().nullable(),
@@ -73,11 +76,16 @@ export function BookingDocumentDialog({ open, onOpenChange, bookingId, onSuccess
73
76
  onOpenChange(false);
74
77
  onSuccess?.();
75
78
  };
79
+ const uploadedFileUrl = form.watch("fileUrl");
80
+ const canSubmit = Boolean(uploadedFileUrl) && !create.isPending;
76
81
  return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: messages.bookingDocumentDialog.title }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-1 flex-col overflow-hidden", children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingDocumentDialog.fields.type }), _jsxs(Select, { items: typeItems, value: form.watch("type"), onValueChange: (v) => form.setValue("type", (v ?? "other")), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: documentTypes.map((t) => (_jsx(SelectItem, { value: t, children: messages.bookingDocumentDialog.documentTypeLabels[t] }, t))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingDocumentDialog.fields.traveler }), _jsxs(Select, { items: travelerItems, value: form.watch("travelerId") ?? UNASSIGNED, onValueChange: (v) => form.setValue("travelerId", v ?? UNASSIGNED), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: UNASSIGNED, children: messages.bookingDocumentDialog.placeholders.travelerUnassigned }), travelers.map((traveler) => (_jsxs(SelectItem, { value: traveler.id, children: [traveler.firstName, " ", traveler.lastName] }, traveler.id)))] })] })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingDocumentDialog.fields.file }), _jsx(FileDropzone, { accept: "application/pdf,image/*", maxSize: 10 * 1024 * 1024, onUploaded: (upload) => {
77
82
  form.setValue("fileUrl", upload.url, { shouldValidate: true });
78
83
  form.setValue("fileName", upload.name, { shouldValidate: true });
84
+ }, onCleared: () => {
85
+ form.setValue("fileUrl", "", { shouldDirty: true, shouldValidate: true });
86
+ form.setValue("fileName", "", { shouldDirty: true, shouldValidate: true });
79
87
  }, helperText: messages.bookingDocumentDialog.placeholders.helperText }), form.formState.errors.fileUrl && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.fileUrl.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingDocumentDialog.fields.expiresAt }), _jsx(DatePicker, { value: form.watch("expiresAt") || null, onChange: (next) => form.setValue("expiresAt", next ?? "", {
80
88
  shouldValidate: true,
81
89
  shouldDirty: true,
82
- }), placeholder: messages.bookingDocumentDialog.placeholders.expiresAt, className: "w-full" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingDocumentDialog.fields.notes }), _jsx(Textarea, { ...form.register("notes"), placeholder: messages.bookingDocumentDialog.placeholders.notes })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", size: "sm", disabled: create.isPending, children: [create.isPending && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), messages.bookingDocumentDialog.actions.addDocument] })] })] })] }) }));
90
+ }), placeholder: messages.bookingDocumentDialog.placeholders.expiresAt, className: "w-full" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingDocumentDialog.fields.notes }), _jsx(Textarea, { ...form.register("notes"), placeholder: messages.bookingDocumentDialog.placeholders.notes })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", size: "sm", disabled: !canSubmit, children: [create.isPending && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), messages.bookingDocumentDialog.actions.addDocument] })] })] })] }) }));
83
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"booking-payment-reconciliation-banner.d.ts","sourceRoot":"","sources":["../../src/components/booking-payment-reconciliation-banner.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,uCAAuC;IACtD,SAAS,EAAE,MAAM,CAAA;CAClB;AAID,wBAAgB,kCAAkC,CAAC,EACjD,SAAS,GACV,EAAE,uCAAuC,+BA4FzC"}
1
+ {"version":3,"file":"booking-payment-reconciliation-banner.d.ts","sourceRoot":"","sources":["../../src/components/booking-payment-reconciliation-banner.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,uCAAuC;IACtD,SAAS,EAAE,MAAM,CAAA;CAClB;AASD,wBAAgB,kCAAkC,CAAC,EACjD,SAAS,GACV,EAAE,uCAAuC,+BA4FzC"}
@@ -1,7 +1,8 @@
1
1
  import { type PaymentMethod, type PaymentStatus } from "@voyant-travel/finance-react";
2
2
  import * as React from "react";
3
- export interface BookingPaymentsSummaryRow {
3
+ export interface BookingPaymentSummaryPaymentRow {
4
4
  id: string;
5
+ source: "payment";
5
6
  invoiceId: string;
6
7
  invoiceNumber: string;
7
8
  invoiceType?: "invoice" | "proforma" | "credit_note";
@@ -16,6 +17,13 @@ export interface BookingPaymentsSummaryRow {
16
17
  referenceNumber: string | null;
17
18
  notes: string | null;
18
19
  }
20
+ export interface BookingPaymentSummaryVoucherRedemptionRow extends Omit<BookingPaymentSummaryPaymentRow, "source" | "invoiceId" | "invoiceNumber" | "invoiceType"> {
21
+ source: "voucher_redemption";
22
+ invoiceId: null;
23
+ invoiceNumber: null;
24
+ invoiceType: null;
25
+ }
26
+ export type BookingPaymentsSummaryRow = BookingPaymentSummaryPaymentRow | BookingPaymentSummaryVoucherRedemptionRow;
19
27
  export interface BookingPaymentsSummaryProps {
20
28
  bookingId: string;
21
29
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"booking-payments-summary.d.ts","sourceRoot":"","sources":["../../src/components/booking-payments-summary.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,aAAa,EAGnB,MAAM,8BAA8B,CAAA;AAarC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;IACpD,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,MAAM,EAAE,aAAa,CAAA;IACrB,aAAa,EAAE,aAAa,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IAC3E;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,MAAM,CAAA;IAC3D;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACxD,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;IAClF,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACxD;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC1E;;;;OAIG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,OAAkB,EAClB,aAAa,EACb,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,YAAY,GACb,EAAE,2BAA2B,qBAwQ7B"}
1
+ {"version":3,"file":"booking-payments-summary.d.ts","sourceRoot":"","sources":["../../src/components/booking-payments-summary.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,aAAa,EAGnB,MAAM,8BAA8B,CAAA;AAarC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,WAAW,+BAA+B;IAC9C,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,SAAS,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;IACpD,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,MAAM,EAAE,aAAa,CAAA;IACrB,aAAa,EAAE,aAAa,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,yCACf,SAAQ,IAAI,CACV,+BAA+B,EAC/B,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,aAAa,CACzD;IACD,MAAM,EAAE,oBAAoB,CAAA;IAC5B,SAAS,EAAE,IAAI,CAAA;IACf,aAAa,EAAE,IAAI,CAAA;IACnB,WAAW,EAAE,IAAI,CAAA;CAClB;AAED,MAAM,MAAM,yBAAyB,GACjC,+BAA+B,GAC/B,yCAAyC,CAAA;AAmB7C,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IAC3E;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,MAAM,CAAA;IAC3D;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACxD,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;IAClF,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACxD;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC1E;;;;OAIG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,OAAkB,EAClB,aAAa,EACb,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,YAAY,GACb,EAAE,2BAA2B,qBAqS7B"}
@@ -50,21 +50,37 @@ export function BookingPaymentsSummary({ bookingId, variant = "public", onInvoic
50
50
  setDeletePending(false);
51
51
  }
52
52
  };
53
- const paymentRows = React.useMemo(() => payments.map((payment) => ({
54
- id: payment.id,
55
- invoiceId: payment.invoiceId,
56
- invoiceNumber: payment.invoiceNumber,
57
- invoiceType: payment.invoiceType,
58
- amountCents: payment.amountCents,
59
- currency: payment.currency,
60
- baseCurrency: payment.baseCurrency ?? null,
61
- baseAmountCents: payment.baseAmountCents ?? null,
62
- status: payment.status,
63
- paymentMethod: payment.paymentMethod,
64
- paymentDate: payment.paymentDate,
65
- referenceNumber: payment.referenceNumber,
66
- notes: payment.notes,
67
- })), [payments]);
53
+ const paymentRows = React.useMemo(() => payments.map((payment) => {
54
+ const base = {
55
+ id: payment.id,
56
+ amountCents: payment.amountCents,
57
+ currency: payment.currency,
58
+ baseCurrency: payment.baseCurrency ?? null,
59
+ baseAmountCents: payment.baseAmountCents ?? null,
60
+ status: payment.status,
61
+ paymentMethod: payment.paymentMethod,
62
+ paymentDate: payment.paymentDate,
63
+ referenceNumber: payment.referenceNumber,
64
+ notes: payment.notes,
65
+ };
66
+ const source = payment.source ?? "payment";
67
+ if (source === "voucher_redemption") {
68
+ return {
69
+ ...base,
70
+ source: "voucher_redemption",
71
+ invoiceId: null,
72
+ invoiceNumber: null,
73
+ invoiceType: null,
74
+ };
75
+ }
76
+ return {
77
+ ...base,
78
+ source: "payment",
79
+ invoiceId: payment.invoiceId ?? "",
80
+ invoiceNumber: payment.invoiceNumber ?? "",
81
+ invoiceType: payment.invoiceType ?? undefined,
82
+ };
83
+ }), [payments]);
68
84
  const columns = React.useMemo(() => {
69
85
  const cols = [
70
86
  {
@@ -109,16 +125,20 @@ export function BookingPaymentsSummary({ bookingId, variant = "public", onInvoic
109
125
  accessorKey: "invoiceNumber",
110
126
  header: card.columns.invoice,
111
127
  cell: ({ row }) => {
128
+ const payment = row.original;
129
+ if (payment.source !== "payment") {
130
+ return _jsx("span", { className: "text-muted-foreground", children: "\u2014" });
131
+ }
112
132
  if (onInvoiceOpen) {
113
133
  return (_jsxs("button", { type: "button", onClick: (e) => {
114
134
  e.stopPropagation();
115
- onInvoiceOpen(row.original.invoiceId, row.original);
116
- }, className: "inline-flex items-center gap-1 font-mono text-primary text-xs hover:underline", children: [row.original.invoiceNumber, _jsx(ArrowUpRight, { className: "h-3 w-3" })] }));
135
+ onInvoiceOpen(payment.invoiceId, payment);
136
+ }, className: "inline-flex items-center gap-1 font-mono text-primary text-xs hover:underline", children: [payment.invoiceNumber, _jsx(ArrowUpRight, { className: "h-3 w-3" })] }));
117
137
  }
118
138
  if (getInvoiceHref) {
119
- return (_jsxs("a", { href: getInvoiceHref(row.original), onClick: (e) => e.stopPropagation(), className: "inline-flex items-center gap-1 font-mono text-primary text-xs hover:underline", children: [row.original.invoiceNumber, _jsx(ArrowUpRight, { className: "h-3 w-3" })] }));
139
+ return (_jsxs("a", { href: getInvoiceHref(payment), onClick: (e) => e.stopPropagation(), className: "inline-flex items-center gap-1 font-mono text-primary text-xs hover:underline", children: [payment.invoiceNumber, _jsx(ArrowUpRight, { className: "h-3 w-3" })] }));
120
140
  }
121
- return _jsx("span", { className: "font-mono text-xs", children: row.original.invoiceNumber });
141
+ return _jsx("span", { className: "font-mono text-xs", children: payment.invoiceNumber });
122
142
  },
123
143
  },
124
144
  ];
@@ -126,19 +146,24 @@ export function BookingPaymentsSummary({ bookingId, variant = "public", onInvoic
126
146
  cols.push({
127
147
  id: "actions",
128
148
  header: () => _jsx("span", { className: "sr-only", children: card.columns.actions }),
129
- cell: ({ row }) => (_jsxs("div", { className: "flex items-center justify-end gap-1", children: [onViewPayment ? (_jsx(IconActionButton, { label: card.actions.view, icon: _jsx(Eye, { className: "h-3.5 w-3.5" }), onClick: (e) => {
130
- e.stopPropagation();
131
- onViewPayment(row.original);
132
- } })) : null, onConvertProforma && row.original.invoiceType === "proforma" ? (_jsx(IconActionButton, { label: card.actions.convertToInvoice, icon: _jsx(ArrowUpRight, { className: "h-3.5 w-3.5" }), onClick: (e) => {
133
- e.stopPropagation();
134
- void onConvertProforma(row.original);
135
- } })) : null, onEditPayment ? (_jsx(IconActionButton, { label: card.actions.edit, icon: _jsx(Pencil, { className: "h-3.5 w-3.5" }), onClick: (e) => {
136
- e.stopPropagation();
137
- onEditPayment(row.original);
138
- } })) : null, onDeletePayment ? (_jsx(IconActionButton, { label: card.actions.delete, icon: _jsx(Trash2, { className: "h-3.5 w-3.5" }), className: "text-muted-foreground hover:bg-destructive/10 hover:text-destructive", onClick: (e) => {
139
- e.stopPropagation();
140
- setDeleteTarget(row.original);
141
- } })) : null] })),
149
+ cell: ({ row }) => {
150
+ if (row.original.source !== "payment") {
151
+ return null;
152
+ }
153
+ return (_jsxs("div", { className: "flex items-center justify-end gap-1", children: [onViewPayment ? (_jsx(IconActionButton, { label: card.actions.view, icon: _jsx(Eye, { className: "h-3.5 w-3.5" }), onClick: (e) => {
154
+ e.stopPropagation();
155
+ onViewPayment(row.original);
156
+ } })) : null, onConvertProforma && row.original.invoiceType === "proforma" ? (_jsx(IconActionButton, { label: card.actions.convertToInvoice, icon: _jsx(ArrowUpRight, { className: "h-3.5 w-3.5" }), onClick: (e) => {
157
+ e.stopPropagation();
158
+ void onConvertProforma(row.original);
159
+ } })) : null, onEditPayment ? (_jsx(IconActionButton, { label: card.actions.edit, icon: _jsx(Pencil, { className: "h-3.5 w-3.5" }), onClick: (e) => {
160
+ e.stopPropagation();
161
+ onEditPayment(row.original);
162
+ } })) : null, onDeletePayment ? (_jsx(IconActionButton, { label: card.actions.delete, icon: _jsx(Trash2, { className: "h-3.5 w-3.5" }), className: "text-muted-foreground hover:bg-destructive/10 hover:text-destructive", onClick: (e) => {
163
+ e.stopPropagation();
164
+ setDeleteTarget(row.original);
165
+ } })) : null] }));
166
+ },
142
167
  });
143
168
  }
144
169
  return cols;
@@ -15,6 +15,8 @@ export interface FileDropzoneProps {
15
15
  maxSize?: number;
16
16
  /** Called after a successful upload. */
17
17
  onUploaded: (file: UploadedFile) => void;
18
+ /** Called after a previously uploaded file is cleared from the dropzone. */
19
+ onCleared?: () => void;
18
20
  /** Called when an error occurs (validation, upload failure). */
19
21
  onError?: (message: string) => void;
20
22
  /** Helper text shown in the idle state. */
@@ -22,5 +24,5 @@ export interface FileDropzoneProps {
22
24
  /** Disable interaction. */
23
25
  disabled?: boolean;
24
26
  }
25
- export declare function FileDropzone({ uploadUrl, accept, maxSize, onUploaded, onError, helperText, disabled, }: FileDropzoneProps): React.JSX.Element;
27
+ export declare function FileDropzone({ uploadUrl, accept, maxSize, onUploaded, onCleared, onError, helperText, disabled, }: FileDropzoneProps): React.JSX.Element;
26
28
  //# sourceMappingURL=file-dropzone.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-dropzone.d.ts","sourceRoot":"","sources":["../../src/components/file-dropzone.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wCAAwC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;IACxC,gEAAgE;IAChE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAmC,EACnC,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,EACP,UAAU,EACV,QAAQ,GACT,EAAE,iBAAiB,qBA8KnB"}
1
+ {"version":3,"file":"file-dropzone.d.ts","sourceRoot":"","sources":["../../src/components/file-dropzone.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wCAAwC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;IACxC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,gEAAgE;IAChE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAmC,EACnC,MAAM,EACN,OAAO,EACP,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,QAAQ,GACT,EAAE,iBAAiB,qBA+KnB"}
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { File as FileIcon, Loader2, Upload, X } from "lucide-react";
4
4
  import * as React from "react";
5
5
  import { formatMessage, useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault, } from "../i18n/provider.js";
6
- export function FileDropzone({ uploadUrl = "/api/v1/admin/uploads", accept, maxSize, onUploaded, onError, helperText, disabled, }) {
6
+ export function FileDropzone({ uploadUrl = "/api/v1/admin/uploads", accept, maxSize, onUploaded, onCleared, onError, helperText, disabled, }) {
7
7
  const inputRef = React.useRef(null);
8
8
  const [isDragging, setIsDragging] = React.useState(false);
9
9
  const [isUploading, setIsUploading] = React.useState(false);
@@ -94,6 +94,7 @@ export function FileDropzone({ uploadUrl = "/api/v1/admin/uploads", accept, maxS
94
94
  const reset = () => {
95
95
  setUploaded(null);
96
96
  setError(null);
97
+ onCleared?.();
97
98
  };
98
99
  if (uploaded) {
99
100
  return (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border bg-muted/30 p-3", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(FileIcon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: uploaded.name }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [formatSize(uploaded.size), " \u00B7 ", uploaded.mimeType] })] })] }), _jsx("button", { type: "button", onClick: reset, className: "text-muted-foreground hover:text-destructive", "aria-label": messages.fileDropzone.removeFileAriaLabel, children: _jsx(X, { className: "h-4 w-4" }) })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"traveler-dialog.d.ts","sourceRoot":"","sources":["../../src/components/traveler-dialog.tsx"],"names":[],"mappings":"AAoCA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,aAAa,CAAA;AAoCpB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,GACV,EAAE,mBAAmB,+BA0brB"}
1
+ {"version":3,"file":"traveler-dialog.d.ts","sourceRoot":"","sources":["../../src/components/traveler-dialog.tsx"],"names":[],"mappings":"AAoCA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,aAAa,CAAA;AA4CpB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,GACV,EAAE,mBAAmB,+BA0drB"}
@@ -14,10 +14,12 @@ import { z } from "zod/v4";
14
14
  import { useBookingsUiMessagesOrDefault } from "../i18n/provider.js";
15
15
  import { useRevealTraveler, useTravelerWithTravelDetailsMutation, } from "../index.js";
16
16
  const identityDocumentTypes = ["passport", "id_card", "driver_license", "visa", "other"];
17
+ const travelerCategories = ["adult", "child", "infant", "senior", "other"];
17
18
  function createTravelerFormSchema(messages) {
18
19
  return z.object({
19
20
  firstName: z.string().min(1, messages.travelerDialog.validation.firstNameRequired),
20
21
  lastName: z.string().min(1, messages.travelerDialog.validation.lastNameRequired),
22
+ travelerCategory: z.enum(travelerCategories).default("adult"),
21
23
  email: z.string().email().optional().or(z.literal("")).nullable(),
22
24
  phone: z.string().optional().nullable(),
23
25
  specialRequests: z.string().optional().nullable(),
@@ -41,6 +43,11 @@ const EMPTY_PII_FORM = {
41
43
  dietaryRequirements: "",
42
44
  accessibilityNeeds: "",
43
45
  };
46
+ function normalizeTravelerCategory(value) {
47
+ return travelerCategories.includes(value)
48
+ ? value
49
+ : "adult";
50
+ }
44
51
  export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSuccess, }) {
45
52
  const isEditing = Boolean(traveler);
46
53
  const personId = traveler?.personId ?? null;
@@ -65,6 +72,7 @@ export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSucc
65
72
  defaultValues: {
66
73
  firstName: "",
67
74
  lastName: "",
75
+ travelerCategory: "adult",
68
76
  email: "",
69
77
  phone: "",
70
78
  specialRequests: "",
@@ -82,6 +90,7 @@ export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSucc
82
90
  form.reset({
83
91
  firstName: traveler.firstName,
84
92
  lastName: traveler.lastName,
93
+ travelerCategory: normalizeTravelerCategory(traveler.travelerCategory),
85
94
  email: traveler.email ?? "",
86
95
  phone: traveler.phone ?? "",
87
96
  specialRequests: traveler.specialRequests ?? "",
@@ -99,6 +108,7 @@ export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSucc
99
108
  form.reset({
100
109
  firstName: "",
101
110
  lastName: "",
111
+ travelerCategory: "adult",
102
112
  email: "",
103
113
  phone: "",
104
114
  specialRequests: "",
@@ -135,6 +145,7 @@ export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSucc
135
145
  specialRequests: values.specialRequests || null,
136
146
  isPrimary: traveler?.isPrimary ?? false,
137
147
  participantType: "traveler",
148
+ travelerCategory: values.travelerCategory,
138
149
  documentType: values.documentType,
139
150
  documentNumber: trimOrNull(values.documentNumber),
140
151
  documentExpiry: trimOrNull(values.documentExpiry),
@@ -238,7 +249,10 @@ export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSucc
238
249
  });
239
250
  return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
240
251
  ? messages.travelerDialog.titles.edit
241
- : messages.travelerDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-1 flex-col overflow-hidden", children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.firstName }), _jsx(Input, { ...form.register("firstName"), placeholder: messages.travelerDialog.placeholders.firstName }), form.formState.errors.firstName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.firstName.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.lastName }), _jsx(Input, { ...form.register("lastName"), placeholder: messages.travelerDialog.placeholders.lastName }), form.formState.errors.lastName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.lastName.message }))] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.email }), _jsx(Input, { ...form.register("email"), type: "email", placeholder: messages.travelerDialog.placeholders.email })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.phone }), _jsx(PhoneInput, { value: form.watch("phone") ?? "", onChange: (next) => form.setValue("phone", next, { shouldDirty: true }) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.specialRequests }), _jsx(Textarea, { ...form.register("specialRequests"), placeholder: messages.travelerDialog.placeholders.specialRequests })] }), _jsxs("div", { className: "flex flex-col gap-3 border-t pt-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: messages.travelerDialog.fields.travelDetailsHeading }), personId ? (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: !snapshot || snapshotQuery.isLoading, onClick: prefillFromProfile, children: [_jsx(Sparkles, { className: "mr-2 h-3.5 w-3.5" }), messages.travelerDialog.actions.prefillFromProfile] })) : null] }), prefilledNotice ? (_jsx("p", { className: "text-xs text-muted-foreground", children: messages.travelerDialog.hints.prefilledFromProfile })) : null, _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.documentType }), _jsxs(Select, { value: form.watch("documentType") ?? "passport", onValueChange: (nextValue) => form.setValue("documentType", nextValue, {
252
+ : messages.travelerDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-1 flex-col overflow-hidden", children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.firstName }), _jsx(Input, { ...form.register("firstName"), placeholder: messages.travelerDialog.placeholders.firstName }), form.formState.errors.firstName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.firstName.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.lastName }), _jsx(Input, { ...form.register("lastName"), placeholder: messages.travelerDialog.placeholders.lastName }), form.formState.errors.lastName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.lastName.message }))] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.email }), _jsx(Input, { ...form.register("email"), type: "email", placeholder: messages.travelerDialog.placeholders.email })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.phone }), _jsx(PhoneInput, { value: form.watch("phone") ?? "", onChange: (next) => form.setValue("phone", next, { shouldDirty: true }) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.travelerCategory }), _jsxs(Select, { value: form.watch("travelerCategory") ?? "adult", onValueChange: (nextValue) => form.setValue("travelerCategory", nextValue, {
253
+ shouldDirty: true,
254
+ shouldValidate: true,
255
+ }), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: travelerCategories.map((category) => (_jsx(SelectItem, { value: category, children: messages.travelerDialog.travelerCategoryLabels[category] }, category))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.specialRequests }), _jsx(Textarea, { ...form.register("specialRequests"), placeholder: messages.travelerDialog.placeholders.specialRequests })] }), _jsxs("div", { className: "flex flex-col gap-3 border-t pt-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: messages.travelerDialog.fields.travelDetailsHeading }), personId ? (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: !snapshot || snapshotQuery.isLoading, onClick: prefillFromProfile, children: [_jsx(Sparkles, { className: "mr-2 h-3.5 w-3.5" }), messages.travelerDialog.actions.prefillFromProfile] })) : null] }), prefilledNotice ? (_jsx("p", { className: "text-xs text-muted-foreground", children: messages.travelerDialog.hints.prefilledFromProfile })) : null, _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.documentType }), _jsxs(Select, { value: form.watch("documentType") ?? "passport", onValueChange: (nextValue) => form.setValue("documentType", nextValue, {
242
256
  shouldDirty: true,
243
257
  shouldValidate: true,
244
258
  }), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: identityDocumentTypes.map((type) => (_jsx(SelectItem, { value: type, children: messages.travelerDialog.documentTypeLabels[type] }, type))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.documentNumber }), _jsx(Input, { ...form.register("documentNumber"), placeholder: messages.travelerDialog.placeholders.documentNumber })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.documentExpiry }), _jsx(DatePicker, { value: form.watch("documentExpiry") || null, onChange: (nextValue) => form.setValue("documentExpiry", nextValue ?? "", {
@@ -1 +1 @@
1
- {"version":3,"file":"traveler-list.d.ts","sourceRoot":"","sources":["../../src/components/traveler-list.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,UAAkB,EAAE,EAAE,iBAAiB,qBA2RhF"}
1
+ {"version":3,"file":"traveler-list.d.ts","sourceRoot":"","sources":["../../src/components/traveler-list.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,UAAkB,EAAE,EAAE,iBAAiB,qBAiRhF"}
@@ -51,7 +51,7 @@ export function TravelerList({ bookingId, autoReveal = false }) {
51
51
  return next;
52
52
  });
53
53
  }, []);
54
- const isRevealed = React.useCallback((travelerId) => autoReveal || revealedIds.has(travelerId), [autoReveal, revealedIds]);
54
+ const isRevealed = React.useCallback((travelerId) => autoReveal || allAlreadyRevealed || revealedIds.has(travelerId), [allAlreadyRevealed, autoReveal, revealedIds]);
55
55
  const deleteMessages = messages.travelerList.actions.deleteConfirm;
56
56
  const handleConfirmDelete = async () => {
57
57
  if (!deleteTarget)
@@ -88,13 +88,7 @@ export function TravelerList({ bookingId, autoReveal = false }) {
88
88
  {
89
89
  id: "documents",
90
90
  header: messages.travelerList.columns.documents,
91
- cell: ({ row }) => {
92
- const documents = documentsByTraveler.get(row.original.id) ?? [];
93
- if (documents.length === 0) {
94
- return (_jsx("span", { className: "text-muted-foreground text-xs", children: messages.travelerList.values.documentsUnavailable }));
95
- }
96
- return (_jsxs("div", { className: "flex flex-wrap gap-1.5", children: [documents.slice(0, 2).map((document) => (_jsx(MiniPill, { children: document.type.replaceAll("_", " ") }, document.id))), documents.length > 2 ? _jsxs(MiniPill, { children: ["+", documents.length - 2] }) : null] }));
97
- },
91
+ cell: ({ row }) => (_jsx(TravelerDocumentsCell, { bookingId: bookingId, traveler: row.original, revealed: isRevealed(row.original.id), documents: documentsByTraveler.get(row.original.id) ?? [] })),
98
92
  },
99
93
  {
100
94
  id: "actions",
@@ -196,12 +190,35 @@ function RolePills({ traveler }) {
196
190
  const pills = [];
197
191
  if (traveler.isPrimary)
198
192
  pills.push(messages.travelerList.roles.primary);
199
- if (traveler.travelerCategory)
200
- pills.push(traveler.travelerCategory);
193
+ if (traveler.travelerCategory) {
194
+ pills.push(messages.travelerDialog.travelerCategoryLabels[traveler.travelerCategory] ?? traveler.travelerCategory);
195
+ }
201
196
  if (pills.length === 0)
202
197
  return _jsx("span", { className: "text-muted-foreground text-xs", children: "\u2014" });
203
198
  return (_jsx("div", { className: "flex flex-wrap gap-1.5", children: pills.map((label) => (_jsx(MiniPill, { children: label }, label))) }));
204
199
  }
200
+ function TravelerDocumentsCell({ bookingId, traveler, revealed, documents, }) {
201
+ const { travelDetails, loading } = useRevealed(bookingId, traveler, revealed);
202
+ const messages = useBookingsUiMessagesOrDefault();
203
+ const travelDocumentLabel = getTravelDocumentLabel(travelDetails, messages);
204
+ if (loading)
205
+ return _jsx(RowLoading, {});
206
+ if (!revealed && documents.length === 0) {
207
+ return (_jsx("span", { className: "text-muted-foreground text-xs", children: messages.travelerList.values.documentsHidden }));
208
+ }
209
+ if (documents.length === 0 && !travelDocumentLabel) {
210
+ return (_jsx("span", { className: "text-muted-foreground text-xs", children: messages.travelerList.values.documentsUnavailable }));
211
+ }
212
+ return (_jsxs("div", { className: "flex flex-wrap gap-1.5", children: [travelDocumentLabel ? _jsx(MiniPill, { children: travelDocumentLabel }) : null, documents.slice(0, travelDocumentLabel ? 1 : 2).map((document) => (_jsx(MiniPill, { children: document.type.replaceAll("_", " ") }, document.id))), documents.length > (travelDocumentLabel ? 1 : 2) ? (_jsxs(MiniPill, { children: ["+", documents.length - (travelDocumentLabel ? 1 : 2)] })) : null] }));
213
+ }
214
+ function getTravelDocumentLabel(travelDetails, messages) {
215
+ if (!travelDetails)
216
+ return null;
217
+ if (!travelDetails.documentNumber && !travelDetails.documentExpiry)
218
+ return null;
219
+ const documentType = travelDetails.documentType ?? "passport";
220
+ return messages.travelerDialog.documentTypeLabels[documentType];
221
+ }
205
222
  function TravelerSnapshotBody({ bookingId, traveler, documents, }) {
206
223
  const messages = useBookingsUiMessagesOrDefault();
207
224
  const labels = messages.travelerList.snapshot;
@@ -223,8 +240,9 @@ function TravelerSnapshotBody({ bookingId, traveler, documents, }) {
223
240
  roles.push(messages.travelerList.roles.primary);
224
241
  if (travelDetails?.isLeadTraveler)
225
242
  roles.push(messages.travelerList.roles.lead);
226
- if (display.travelerCategory)
227
- roles.push(display.travelerCategory);
243
+ if (display.travelerCategory) {
244
+ roles.push(messages.travelerDialog.travelerCategoryLabels[display.travelerCategory] ?? display.travelerCategory);
245
+ }
228
246
  return (_jsxs("div", { className: "flex-1 overflow-y-auto px-4 pb-4", children: [loading ? (_jsx("div", { className: "py-4", children: _jsx(RowLoading, {}) })) : null, _jsxs(SnapshotSection, { title: labels.sectionContact, children: [_jsx(SnapshotRow, { label: labels.nameLabel, value: fullName }), _jsx(SnapshotRow, { label: labels.emailLabel, value: email }), _jsx(SnapshotRow, { label: labels.phoneLabel, value: phone }), _jsx(SnapshotRow, { label: labels.languageLabel, value: display.preferredLanguage || empty }), _jsx(SnapshotRow, { label: labels.roleLabel, value: roles.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: roles.map((label) => (_jsx(MiniPill, { children: label }, label))) })) : (empty) })] }), _jsxs(SnapshotSection, { title: labels.sectionTravel, children: [_jsx(SnapshotRow, { label: labels.dobLabel, value: formatDobAge(dob, empty) }), _jsx(SnapshotRow, { label: labels.nationalityLabel, value: travelDetails?.nationality || empty }), _jsx(SnapshotRow, { label: labels.documentLabel, value: documentValue }), _jsx(SnapshotRow, { label: labels.documentExpiryLabel, value: formatDateValue(travelDetails?.documentExpiry) ?? empty }), _jsx(SnapshotRow, { label: labels.dietaryLabel, value: travelDetails?.dietaryRequirements || empty, multiline: true }), _jsx(SnapshotRow, { label: labels.accessibilityLabel, value: travelDetails?.accessibilityNeeds || empty, multiline: true }), _jsx(SnapshotRow, { label: labels.specialRequestsLabel, value: display.specialRequests || empty, multiline: true }), _jsx(SnapshotRow, { label: labels.notesLabel, value: display.notes || empty, multiline: true })] }), _jsx(SnapshotSection, { title: labels.sectionDocuments, children: documents.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: labels.noDocuments })) : (documents.map((document) => (_jsx(SnapshotRow, { label: formatMessage(messages.travelerList.context.documentLabel, {
229
247
  type: document.type.replaceAll("_", " "),
230
248
  }), value: document.fileName }, document.id)))) }), _jsxs(SnapshotSection, { title: labels.sectionMeta, children: [_jsx(SnapshotRow, { label: labels.createdAtLabel, value: formatTimestamp(traveler.createdAt) ?? empty }), _jsx(SnapshotRow, { label: labels.updatedAtLabel, value: formatTimestamp(traveler.updatedAt) ?? empty })] })] }));
@@ -12,7 +12,7 @@ export declare function useSlotExtraManifestMutation(slotId: string): {
12
12
  productExtraId: string;
13
13
  optionExtraConfigId?: string | null | undefined;
14
14
  status?: "selected" | "cancelled" | "fulfilled" | "no_show" | undefined;
15
- collectionStatus?: "pending" | "not_required" | "collected" | "waived" | "refunded" | undefined;
15
+ collectionStatus?: "not_required" | "pending" | "collected" | "waived" | "refunded" | undefined;
16
16
  collectionCurrency?: string | null | undefined;
17
17
  collectionAmountCents?: number | null | undefined;
18
18
  notes?: string | null | undefined;
@@ -27,7 +27,7 @@ export declare function useSlotExtraManifestMutation(slotId: string): {
27
27
  productExtraId: string;
28
28
  optionExtraConfigId?: string | null | undefined;
29
29
  status?: "selected" | "cancelled" | "fulfilled" | "no_show" | undefined;
30
- collectionStatus?: "pending" | "not_required" | "collected" | "waived" | "refunded" | undefined;
30
+ collectionStatus?: "not_required" | "pending" | "collected" | "waived" | "refunded" | undefined;
31
31
  collectionCurrency?: string | null | undefined;
32
32
  collectionAmountCents?: number | null | undefined;
33
33
  notes?: string | null | undefined;
@@ -39,7 +39,7 @@ export declare function useSlotExtraManifestMutation(slotId: string): {
39
39
  }, Error, {
40
40
  productExtraId: string;
41
41
  travelerIds: string[];
42
- collectionStatus: "pending" | "not_required" | "collected" | "waived" | "refunded";
42
+ collectionStatus: "not_required" | "pending" | "collected" | "waived" | "refunded";
43
43
  collectionCurrency?: string | null | undefined;
44
44
  collectionAmountCents?: number | null | undefined;
45
45
  notes?: string | null | undefined;
@@ -55,7 +55,7 @@ export declare function useSlotExtraManifest(slotId: string | null | undefined,
55
55
  status: "selected" | "cancelled" | "fulfilled" | "no_show";
56
56
  selected: boolean;
57
57
  collectionMode: "cash_on_trip" | "external" | "included" | "none" | "booking_total";
58
- collectionStatus: "pending" | "not_required" | "collected" | "waived" | "refunded";
58
+ collectionStatus: "not_required" | "pending" | "collected" | "waived" | "refunded";
59
59
  collectionCurrency: string | null;
60
60
  collectionAmountCents: number | null;
61
61
  collectedAt: string | null;