@voyantjs/bookings-ui 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +11 -0
  2. package/dist/components/booking-activity-timeline.d.ts.map +1 -1
  3. package/dist/components/booking-activity-timeline.js +34 -14
  4. package/dist/components/booking-cancellation-dialog.d.ts.map +1 -1
  5. package/dist/components/booking-cancellation-dialog.js +15 -16
  6. package/dist/components/booking-create-dialog.d.ts.map +1 -1
  7. package/dist/components/booking-create-dialog.js +77 -13
  8. package/dist/components/booking-dialog.d.ts.map +1 -1
  9. package/dist/components/booking-dialog.js +27 -21
  10. package/dist/components/booking-document-dialog.d.ts.map +1 -1
  11. package/dist/components/booking-document-dialog.js +27 -13
  12. package/dist/components/booking-document-list.d.ts.map +1 -1
  13. package/dist/components/booking-document-list.js +9 -4
  14. package/dist/components/booking-group-link-dialog.d.ts.map +1 -1
  15. package/dist/components/booking-group-link-dialog.js +17 -6
  16. package/dist/components/booking-group-section.d.ts.map +1 -1
  17. package/dist/components/booking-group-section.js +8 -2
  18. package/dist/components/booking-guarantee-dialog.d.ts.map +1 -1
  19. package/dist/components/booking-guarantee-dialog.js +30 -14
  20. package/dist/components/booking-guarantee-list.d.ts.map +1 -1
  21. package/dist/components/booking-guarantee-list.js +11 -8
  22. package/dist/components/booking-item-dialog.d.ts.map +1 -1
  23. package/dist/components/booking-item-dialog.js +34 -20
  24. package/dist/components/booking-item-list.d.ts.map +1 -1
  25. package/dist/components/booking-item-list.js +10 -9
  26. package/dist/components/booking-item-travelers.d.ts.map +1 -1
  27. package/dist/components/booking-item-travelers.js +9 -4
  28. package/dist/components/booking-list.d.ts.map +1 -1
  29. package/dist/components/booking-list.js +17 -8
  30. package/dist/components/booking-notes.d.ts.map +1 -1
  31. package/dist/components/booking-notes.js +5 -2
  32. package/dist/components/booking-payment-schedule-dialog.d.ts.map +1 -1
  33. package/dist/components/booking-payment-schedule-dialog.js +31 -12
  34. package/dist/components/booking-payment-schedule-list.d.ts.map +1 -1
  35. package/dist/components/booking-payment-schedule-list.js +7 -6
  36. package/dist/components/booking-payments-summary.d.ts.map +1 -1
  37. package/dist/components/booking-payments-summary.js +7 -4
  38. package/dist/components/file-dropzone.d.ts.map +1 -1
  39. package/dist/components/file-dropzone.js +25 -15
  40. package/dist/components/passengers-section.d.ts.map +1 -1
  41. package/dist/components/passengers-section.js +3 -17
  42. package/dist/components/payment-schedule-section.d.ts.map +1 -1
  43. package/dist/components/payment-schedule-section.js +3 -14
  44. package/dist/components/person-picker-section.d.ts.map +1 -1
  45. package/dist/components/person-picker-section.js +3 -19
  46. package/dist/components/price-breakdown-section.d.ts.map +1 -1
  47. package/dist/components/price-breakdown-section.js +15 -18
  48. package/dist/components/product-picker-section.d.ts.map +1 -1
  49. package/dist/components/product-picker-section.js +3 -8
  50. package/dist/components/rooms-stepper-section.d.ts.map +1 -1
  51. package/dist/components/rooms-stepper-section.js +4 -9
  52. package/dist/components/shared-room-section.d.ts.map +1 -1
  53. package/dist/components/shared-room-section.js +3 -9
  54. package/dist/components/status-change-dialog.d.ts.map +1 -1
  55. package/dist/components/status-change-dialog.js +6 -1
  56. package/dist/components/supplier-status-dialog.d.ts.map +1 -1
  57. package/dist/components/supplier-status-dialog.js +31 -15
  58. package/dist/components/supplier-status-list.d.ts.map +1 -1
  59. package/dist/components/supplier-status-list.js +10 -2
  60. package/dist/components/traveler-dialog.d.ts.map +1 -1
  61. package/dist/components/traveler-dialog.js +17 -8
  62. package/dist/components/traveler-list.d.ts.map +1 -1
  63. package/dist/components/traveler-list.js +5 -3
  64. package/dist/components/voucher-picker-section.d.ts.map +1 -1
  65. package/dist/components/voucher-picker-section.js +11 -26
  66. package/dist/i18n/en.d.ts +797 -0
  67. package/dist/i18n/en.d.ts.map +1 -0
  68. package/dist/i18n/en.js +796 -0
  69. package/dist/i18n/index.d.ts +5 -0
  70. package/dist/i18n/index.d.ts.map +1 -0
  71. package/dist/i18n/index.js +3 -0
  72. package/dist/i18n/messages.d.ts +684 -0
  73. package/dist/i18n/messages.d.ts.map +1 -0
  74. package/dist/i18n/messages.js +1 -0
  75. package/dist/i18n/provider.d.ts +1617 -0
  76. package/dist/i18n/provider.d.ts.map +1 -0
  77. package/dist/i18n/provider.js +45 -0
  78. package/dist/i18n/ro.d.ts +797 -0
  79. package/dist/i18n/ro.d.ts.map +1 -0
  80. package/dist/i18n/ro.js +796 -0
  81. package/dist/index.d.ts +1 -0
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +1 -0
  84. package/package.json +32 -17
@@ -4,6 +4,7 @@ import { useBookingTravelerDocumentMutation, useBookingTravelerDocuments, useTra
4
4
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
5
  import { ExternalLink, FileText, Plus, Trash2 } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  import { BookingDocumentDialog } from "./booking-document-dialog";
8
9
  const typeVariant = {
9
10
  visa: "default",
@@ -17,20 +18,24 @@ export function BookingDocumentList({ bookingId }) {
17
18
  const { data } = useBookingTravelerDocuments(bookingId);
18
19
  const { data: travelersData } = useTravelers(bookingId);
19
20
  const { remove } = useBookingTravelerDocumentMutation(bookingId);
21
+ const { formatDate } = useBookingsUiI18nOrDefault();
22
+ const messages = useBookingsUiMessagesOrDefault();
20
23
  const documents = data?.data ?? [];
21
24
  const travelers = travelersData?.data ?? [];
22
25
  const travelerMap = new Map();
23
26
  for (const traveler of travelers) {
24
27
  travelerMap.set(traveler.id, traveler);
25
28
  }
26
- return (_jsxs(Card, { "data-slot": "booking-document-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(FileText, { className: "h-4 w-4" }), "Documents"] }), _jsxs(Button, { size: "sm", onClick: () => setDialogOpen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Add Document"] })] }), _jsx(CardContent, { children: documents.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "No documents yet." })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "p-2 text-left font-medium", children: "Type" }), _jsx("th", { className: "p-2 text-left font-medium", children: "File" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Traveler" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Expires" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Notes" }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: documents.map((doc) => {
29
+ return (_jsxs(Card, { "data-slot": "booking-document-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(FileText, { className: "h-4 w-4" }), messages.bookingDocumentList.title] }), _jsxs(Button, { size: "sm", onClick: () => setDialogOpen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.bookingDocumentList.addDocument] })] }), _jsx(CardContent, { children: documents.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.bookingDocumentList.empty })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingDocumentList.columns.type }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingDocumentList.columns.file }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingDocumentList.columns.traveler }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingDocumentList.columns.expires }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingDocumentList.columns.notes }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: documents.map((doc) => {
27
30
  const traveler = doc.travelerId ? travelerMap.get(doc.travelerId) : undefined;
28
- return (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: _jsx(Badge, { variant: typeVariant[doc.type] ?? "outline", className: "capitalize", children: doc.type.replace(/_/g, " ") }) }), _jsx("td", { className: "p-2", children: _jsxs("a", { href: doc.fileUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 hover:underline", children: [doc.fileName, _jsx(ExternalLink, { className: "h-3 w-3 text-muted-foreground" })] }) }), _jsx("td", { className: "p-2", children: traveler
31
+ return (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: _jsx(Badge, { variant: typeVariant[doc.type] ?? "outline", children: messages.bookingDocumentDialog.documentTypeLabels[doc.type] }) }), _jsx("td", { className: "p-2", children: _jsxs("a", { href: doc.fileUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 hover:underline", children: [doc.fileName, _jsx(ExternalLink, { className: "h-3 w-3 text-muted-foreground" })] }) }), _jsx("td", { className: "p-2", children: traveler
29
32
  ? `${traveler.firstName} ${traveler.lastName}`
30
33
  : doc.travelerId
31
34
  ? doc.travelerId
32
- : "—" }), _jsx("td", { className: "p-2", children: doc.expiresAt ? new Date(doc.expiresAt).toLocaleDateString() : "-" }), _jsx("td", { className: "max-w-[200px] truncate p-2 text-muted-foreground", children: doc.notes ?? "-" }), _jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => {
33
- if (confirm("Delete this document?")) {
35
+ : messages.bookingDocumentList.values.travelerUnavailable }), _jsx("td", { className: "p-2", children: doc.expiresAt
36
+ ? formatDate(doc.expiresAt)
37
+ : messages.bookingDocumentList.values.expiresUnavailable }), _jsx("td", { className: "max-w-[200px] truncate p-2 text-muted-foreground", children: doc.notes ?? messages.bookingDocumentList.values.notesUnavailable }), _jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => {
38
+ if (confirm(messages.bookingDocumentList.actions.deleteConfirm)) {
34
39
  remove.mutate(doc.id);
35
40
  }
36
41
  }, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) })] }, doc.id));
@@ -1 +1 @@
1
- {"version":3,"file":"booking-group-link-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-group-link-dialog.tsx"],"names":[],"mappings":"AA0BA,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAMD,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,SAAS,EACT,YAAY,EACZ,QAAQ,GACT,EAAE,2BAA2B,2CAuK7B"}
1
+ {"version":3,"file":"booking-group-link-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-group-link-dialog.tsx"],"names":[],"mappings":"AA2BA,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAMD,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,SAAS,EACT,YAAY,EACZ,QAAQ,GACT,EAAE,2BAA2B,2CAqL7B"}
@@ -4,12 +4,15 @@ import { useBookingGroupMemberMutation, useBookingGroupMutation, useBookingGroup
4
4
  import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components";
5
5
  import { Loader2 } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  const JOIN_PLACEHOLDER = "__none__";
8
9
  export function BookingGroupLinkDialog({ open, onOpenChange, bookingId, productId, optionUnitId, onLinked, }) {
9
10
  const [mode, setMode] = React.useState("join");
10
11
  const [selectedGroupId, setSelectedGroupId] = React.useState("");
11
12
  const [newGroupLabel, setNewGroupLabel] = React.useState("");
12
13
  const [error, setError] = React.useState(null);
14
+ const { formatDate } = useBookingsUiI18nOrDefault();
15
+ const messages = useBookingsUiMessagesOrDefault();
13
16
  React.useEffect(() => {
14
17
  if (!open) {
15
18
  setMode("join");
@@ -33,7 +36,8 @@ export function BookingGroupLinkDialog({ open, onOpenChange, bookingId, productI
33
36
  let targetGroupId = selectedGroupId;
34
37
  let role = "shared";
35
38
  if (mode === "create") {
36
- const label = newGroupLabel.trim() || `Shared room — ${new Date().toLocaleDateString()}`;
39
+ const label = newGroupLabel.trim() ||
40
+ `${messages.bookingGroupLinkDialog.labels.generatedLabelPrefix} - ${formatDate(new Date())}`;
37
41
  const group = await createGroup.mutateAsync({
38
42
  kind: "shared_room",
39
43
  label,
@@ -45,7 +49,7 @@ export function BookingGroupLinkDialog({ open, onOpenChange, bookingId, productI
45
49
  role = "primary";
46
50
  }
47
51
  if (!targetGroupId || targetGroupId === JOIN_PLACEHOLDER) {
48
- setError("Select a group to join");
52
+ setError(messages.bookingGroupLinkDialog.validation.selectGroup);
49
53
  return;
50
54
  }
51
55
  await addMember.mutateAsync({
@@ -57,12 +61,19 @@ export function BookingGroupLinkDialog({ open, onOpenChange, bookingId, productI
57
61
  onLinked?.(targetGroupId);
58
62
  }
59
63
  catch (err) {
60
- const message = err instanceof Error ? err.message : "Failed to link booking";
64
+ const message = err instanceof Error ? err.message : messages.bookingGroupLinkDialog.validation.linkFailed;
61
65
  setError(message);
62
66
  }
63
67
  };
64
68
  const isSubmitting = createGroup.isPending || addMember.isPending;
65
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Link Booking to Shared Room" }) }), _jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { type: "button", size: "sm", variant: mode === "join" ? "default" : "ghost", onClick: () => setMode("join"), children: "Join existing" }), _jsx(Button, { type: "button", size: "sm", variant: mode === "create" ? "default" : "ghost", onClick: () => setMode("create"), children: "Create new" })] }), mode === "join" ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Existing groups" }), _jsxs(Select, { items: groups.length === 0
66
- ? [{ label: "No existing groups", value: JOIN_PLACEHOLDER }]
67
- : groups.map((g) => ({ label: g.label, value: g.id })), value: selectedGroupId || JOIN_PLACEHOLDER, onValueChange: (v) => setSelectedGroupId(v === JOIN_PLACEHOLDER ? "" : (v ?? "")), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: "Select a group..." }) }), _jsx(SelectContent, { children: groups.length === 0 ? (_jsx(SelectItem, { value: JOIN_PLACEHOLDER, disabled: true, children: "No existing groups" })) : (groups.map((g) => (_jsx(SelectItem, { value: g.id, children: g.label }, g.id)))) })] }), productId && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Filtered to groups for the booking's product." }))] })) : (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Group label" }), _jsx(Input, { value: newGroupLabel, onChange: (e) => setNewGroupLabel(e.target.value), placeholder: "e.g. Smith + Jones, Room 204" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "This booking will be marked as the primary member." })] })), error && _jsx("p", { className: "text-xs text-destructive", children: error })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), disabled: isSubmitting, children: "Cancel" }), _jsxs(Button, { type: "button", size: "sm", onClick: handleSubmit, disabled: isSubmitting || (mode === "join" && !selectedGroupId), children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), mode === "create" ? "Create & Link" : "Link to Group"] })] })] }) }));
69
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: messages.bookingGroupLinkDialog.title }) }), _jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { type: "button", size: "sm", variant: mode === "join" ? "default" : "ghost", onClick: () => setMode("join"), children: messages.bookingGroupLinkDialog.modes.join }), _jsx(Button, { type: "button", size: "sm", variant: mode === "create" ? "default" : "ghost", onClick: () => setMode("create"), children: messages.bookingGroupLinkDialog.modes.create })] }), mode === "join" ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGroupLinkDialog.fields.existingGroups }), _jsxs(Select, { items: groups.length === 0
70
+ ? [
71
+ {
72
+ label: messages.bookingGroupLinkDialog.placeholders.noExistingGroups,
73
+ value: JOIN_PLACEHOLDER,
74
+ },
75
+ ]
76
+ : groups.map((g) => ({ label: g.label, value: g.id })), value: selectedGroupId || JOIN_PLACEHOLDER, onValueChange: (v) => setSelectedGroupId(v === JOIN_PLACEHOLDER ? "" : (v ?? "")), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: messages.bookingGroupLinkDialog.placeholders.selectGroup }) }), _jsx(SelectContent, { children: groups.length === 0 ? (_jsx(SelectItem, { value: JOIN_PLACEHOLDER, disabled: true, children: messages.bookingGroupLinkDialog.placeholders.noExistingGroups })) : (groups.map((g) => (_jsx(SelectItem, { value: g.id, children: g.label }, g.id)))) })] }), productId && (_jsx("p", { className: "text-xs text-muted-foreground", children: messages.bookingGroupLinkDialog.hints.productFiltered }))] })) : (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGroupLinkDialog.fields.groupLabel }), _jsx(Input, { value: newGroupLabel, onChange: (e) => setNewGroupLabel(e.target.value), placeholder: messages.bookingGroupLinkDialog.placeholders.groupLabel }), _jsx("p", { className: "text-xs text-muted-foreground", children: messages.bookingGroupLinkDialog.hints.primaryMember })] })), error && _jsx("p", { className: "text-xs text-destructive", children: error })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), disabled: isSubmitting, children: messages.common.cancel }), _jsxs(Button, { type: "button", size: "sm", onClick: handleSubmit, disabled: isSubmitting || (mode === "join" && !selectedGroupId), children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), mode === "create"
77
+ ? messages.bookingGroupLinkDialog.actions.createAndLink
78
+ : messages.bookingGroupLinkDialog.actions.linkToGroup] })] })] }) }));
68
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"booking-group-section.d.ts","sourceRoot":"","sources":["../../src/components/booking-group-section.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,SAAS,EACT,YAAY,GACb,EAAE,wBAAwB,2CAsH1B"}
1
+ {"version":3,"file":"booking-group-section.d.ts","sourceRoot":"","sources":["../../src/components/booking-group-section.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,SAAS,EACT,YAAY,GACb,EAAE,wBAAwB,2CA4H1B"}
@@ -4,9 +4,11 @@ import { useBookingGroup, useBookingGroupForBooking, useBookingGroupMemberMutati
4
4
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
5
  import { Link2, Unlink, Users } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { formatMessage, useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  import { BookingGroupLinkDialog } from "./booking-group-link-dialog";
8
9
  export function BookingGroupSection({ bookingId, productId, optionUnitId, }) {
9
10
  const [linkDialogOpen, setLinkDialogOpen] = React.useState(false);
11
+ const messages = useBookingsUiMessagesOrDefault();
10
12
  // Auto-resolve product/option-unit from items when the caller hasn't
11
13
  // supplied them. Explicit `null` is respected as an override.
12
14
  const shouldAutoResolve = productId === undefined || optionUnitId === undefined;
@@ -22,10 +24,14 @@ export function BookingGroupSection({ bookingId, productId, optionUnitId, }) {
22
24
  const handleRemove = async () => {
23
25
  if (!groupId)
24
26
  return;
25
- if (!confirm("Remove this booking from the shared-room group?"))
27
+ if (!confirm(messages.bookingGroupSection.actions.removeConfirm))
26
28
  return;
27
29
  await removeMember.mutateAsync({ groupId, bookingId });
28
30
  };
29
31
  const siblings = members.filter((m) => m.bookingId !== bookingId);
30
- return (_jsxs(Card, { "data-slot": "booking-group-section", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Users, { className: "h-4 w-4" }), "Shared Room"] }), group ? (_jsxs(Button, { size: "sm", variant: "outline", onClick: handleRemove, disabled: removeMember.isPending, children: [_jsx(Unlink, { className: "mr-2 h-4 w-4" }), "Remove from group"] })) : (_jsxs(Button, { size: "sm", onClick: () => setLinkDialogOpen(true), children: [_jsx(Link2, { className: "mr-2 h-4 w-4" }), "Link to shared room"] }))] }), _jsx(CardContent, { children: !group ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "This booking is not linked to a shared-room group." })) : (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between rounded-md border bg-muted/30 p-3 text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Group" }), _jsx("div", { className: "font-medium", children: group.label })] }), _jsx(Badge, { variant: "outline", className: "capitalize", children: group.kind.replace(/_/g, " ") })] }), _jsxs("div", { children: [_jsxs("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: ["Sibling bookings (", siblings.length, ")"] }), siblings.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: "No other bookings linked yet. Share the group id with another booking to link them." })) : (_jsx("ul", { className: "space-y-1", children: siblings.map((m) => (_jsxs("li", { className: "flex items-center justify-between rounded px-2 py-1 text-sm", children: [_jsx("span", { className: "font-mono text-xs", children: m.booking?.bookingNumber ?? m.bookingId }), _jsxs("div", { className: "flex items-center gap-2", children: [m.role === "primary" && (_jsx(Badge, { variant: "default", className: "text-xs", children: "Primary" })), m.booking?.status && (_jsx("span", { className: "text-xs text-muted-foreground capitalize", children: m.booking.status.replace(/_/g, " ") }))] })] }, m.id))) }))] })] })) }), _jsx(BookingGroupLinkDialog, { open: linkDialogOpen, onOpenChange: setLinkDialogOpen, bookingId: bookingId, productId: effectiveProductId, optionUnitId: effectiveOptionUnitId })] }));
32
+ return (_jsxs(Card, { "data-slot": "booking-group-section", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Users, { className: "h-4 w-4" }), messages.bookingGroupSection.title] }), group ? (_jsxs(Button, { size: "sm", variant: "outline", onClick: handleRemove, disabled: removeMember.isPending, children: [_jsx(Unlink, { className: "mr-2 h-4 w-4" }), messages.bookingGroupSection.actions.removeFromGroup] })) : (_jsxs(Button, { size: "sm", onClick: () => setLinkDialogOpen(true), children: [_jsx(Link2, { className: "mr-2 h-4 w-4" }), messages.bookingGroupSection.actions.linkToSharedRoom] }))] }), _jsx(CardContent, { children: !group ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.bookingGroupSection.empty })) : (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between rounded-md border bg-muted/30 p-3 text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: messages.bookingGroupSection.group }), _jsx("div", { className: "font-medium", children: group.label })] }), _jsx(Badge, { variant: "outline", children: group.kind === "shared_room"
33
+ ? messages.bookingGroupSection.sharedRoomKind
34
+ : group.kind.replace(/_/g, " ") })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: formatMessage(messages.bookingGroupSection.siblingBookings, {
35
+ count: siblings.length,
36
+ }) }), siblings.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: messages.bookingGroupSection.noSiblingBookings })) : (_jsx("ul", { className: "space-y-1", children: siblings.map((m) => (_jsxs("li", { className: "flex items-center justify-between rounded px-2 py-1 text-sm", children: [_jsx("span", { className: "font-mono text-xs", children: m.booking?.bookingNumber ?? m.bookingId }), _jsxs("div", { className: "flex items-center gap-2", children: [m.role === "primary" && (_jsx(Badge, { variant: "default", className: "text-xs", children: messages.bookingGroupSection.primaryBadge })), m.booking?.status && (_jsx("span", { className: "text-xs text-muted-foreground", children: messages.common.bookingStatusLabels[m.booking.status] }))] })] }, m.id))) }))] })] })) }), _jsx(BookingGroupLinkDialog, { open: linkDialogOpen, onOpenChange: setLinkDialogOpen, bookingId: bookingId, productId: effectiveProductId, optionUnitId: effectiveOptionUnitId })] }));
31
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"booking-guarantee-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-guarantee-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,sBAAsB,EAA+B,MAAM,yBAAyB,CAAA;AA4DlG,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,sBAAsB,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,SAAS,EACT,SAAS,GACV,EAAE,2BAA2B,2CAqL7B"}
1
+ {"version":3,"file":"booking-guarantee-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-guarantee-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,sBAAsB,EAA+B,MAAM,yBAAyB,CAAA;AAgElG,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,sBAAsB,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,SAAS,EACT,SAAS,GACV,EAAE,2BAA2B,2CA4M7B"}
@@ -9,6 +9,7 @@ import { Loader2 } from "lucide-react";
9
9
  import { useEffect } from "react";
10
10
  import { useForm } from "react-hook-form";
11
11
  import { z } from "zod/v4";
12
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
12
13
  const guaranteeTypes = [
13
14
  "deposit",
14
15
  "credit_card",
@@ -27,25 +28,30 @@ const guaranteeStatuses = [
27
28
  "cancelled",
28
29
  "expired",
29
30
  ];
30
- const guaranteeFormSchema = z.object({
31
- guaranteeType: z.enum(guaranteeTypes),
32
- status: z.enum(guaranteeStatuses).default("pending"),
33
- currency: z.string().min(3).max(3).optional().nullable(),
34
- amountCents: z.coerce.number().int().min(0).optional().nullable(),
35
- provider: z.string().optional().nullable(),
36
- referenceNumber: z.string().optional().nullable(),
37
- expiresAt: z.string().optional().nullable(),
38
- notes: z.string().optional().nullable(),
39
- });
31
+ const DEFAULT_CURRENCY = "EUR"; // i18n-literal-ok ISO default currency
32
+ function createGuaranteeFormSchema() {
33
+ return z.object({
34
+ guaranteeType: z.enum(guaranteeTypes),
35
+ status: z.enum(guaranteeStatuses).default("pending"),
36
+ currency: z.string().min(3).max(3).optional().nullable(),
37
+ amountCents: z.coerce.number().int().min(0).optional().nullable(),
38
+ provider: z.string().optional().nullable(),
39
+ referenceNumber: z.string().optional().nullable(),
40
+ expiresAt: z.string().optional().nullable(),
41
+ notes: z.string().optional().nullable(),
42
+ });
43
+ }
40
44
  export function BookingGuaranteeDialog({ open, onOpenChange, bookingId, guarantee, onSuccess, }) {
41
45
  const isEditing = Boolean(guarantee);
42
46
  const { create, update } = useBookingGuaranteeMutation(bookingId);
47
+ const messages = useBookingsUiMessagesOrDefault();
48
+ const guaranteeFormSchema = createGuaranteeFormSchema();
43
49
  const form = useForm({
44
50
  resolver: zodResolver(guaranteeFormSchema),
45
51
  defaultValues: {
46
52
  guaranteeType: "deposit",
47
53
  status: "pending",
48
- currency: "EUR",
54
+ currency: DEFAULT_CURRENCY,
49
55
  amountCents: null,
50
56
  provider: "",
51
57
  referenceNumber: "",
@@ -91,11 +97,21 @@ export function BookingGuaranteeDialog({ open, onOpenChange, bookingId, guarante
91
97
  onSuccess?.();
92
98
  };
93
99
  const isSubmitting = create.isPending || update.isPending;
94
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Guarantee" : "Add Guarantee" }) }), _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: "Type" }), _jsxs(Select, { items: guaranteeTypes.map((t) => ({ label: t.replace(/_/g, " "), value: t })), value: form.watch("guaranteeType"), onValueChange: (v) => form.setValue("guaranteeType", (v ?? "deposit")), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: guaranteeTypes.map((t) => (_jsx(SelectItem, { value: t, children: t.replace(/_/g, " ") }, t))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Status" }), _jsxs(Select, { items: guaranteeStatuses.map((s) => ({ label: s.replace(/_/g, " "), value: s })), value: form.watch("status"), onValueChange: (v) => form.setValue("status", (v ?? "pending")), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: guaranteeStatuses.map((s) => (_jsx(SelectItem, { value: s, children: s.replace(/_/g, " ") }, s))) })] })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Currency" }), _jsx(CurrencyCombobox, { value: form.watch("currency") || null, onChange: (next) => form.setValue("currency", next ?? "EUR", {
100
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
101
+ ? messages.bookingGuaranteeDialog.titles.edit
102
+ : messages.bookingGuaranteeDialog.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.bookingGuaranteeDialog.fields.type }), _jsxs(Select, { items: guaranteeTypes.map((t) => ({
103
+ label: messages.bookingGuaranteeDialog.guaranteeTypeLabels[t],
104
+ value: t,
105
+ })), value: form.watch("guaranteeType"), onValueChange: (v) => form.setValue("guaranteeType", (v ?? "deposit")), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: guaranteeTypes.map((t) => (_jsx(SelectItem, { value: t, children: messages.bookingGuaranteeDialog.guaranteeTypeLabels[t] }, t))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGuaranteeDialog.fields.status }), _jsxs(Select, { items: guaranteeStatuses.map((s) => ({
106
+ label: messages.bookingGuaranteeDialog.guaranteeStatusLabels[s],
107
+ value: s,
108
+ })), value: form.watch("status"), onValueChange: (v) => form.setValue("status", (v ?? "pending")), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: guaranteeStatuses.map((s) => (_jsx(SelectItem, { value: s, children: messages.bookingGuaranteeDialog.guaranteeStatusLabels[s] }, s))) })] })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGuaranteeDialog.fields.currency }), _jsx(CurrencyCombobox, { value: form.watch("currency") || null, onChange: (next) => form.setValue("currency", next ?? DEFAULT_CURRENCY, {
95
109
  shouldValidate: true,
96
110
  shouldDirty: true,
97
- }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Amount (cents)" }), _jsx(Input, { ...form.register("amountCents"), type: "number", min: 0 })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Provider" }), _jsx(Input, { ...form.register("provider"), placeholder: "Stripe, bank name..." })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Reference Number" }), _jsx(Input, { ...form.register("referenceNumber"), placeholder: "External reference..." })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Expires At" }), _jsx(DateTimePicker, { value: form.watch("expiresAt") || null, onChange: (next) => form.setValue("expiresAt", next ?? "", {
111
+ }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGuaranteeDialog.fields.amountCents }), _jsx(Input, { ...form.register("amountCents"), type: "number", min: 0 })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGuaranteeDialog.fields.provider }), _jsx(Input, { ...form.register("provider"), placeholder: messages.bookingGuaranteeDialog.placeholders.provider })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGuaranteeDialog.fields.referenceNumber }), _jsx(Input, { ...form.register("referenceNumber"), placeholder: messages.bookingGuaranteeDialog.placeholders.referenceNumber })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGuaranteeDialog.fields.expiresAt }), _jsx(DateTimePicker, { value: form.watch("expiresAt") || null, onChange: (next) => form.setValue("expiresAt", next ?? "", {
98
112
  shouldValidate: true,
99
113
  shouldDirty: true,
100
- }), placeholder: "Select expiry date & time", className: "w-full" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Notes" }), _jsx(Textarea, { ...form.register("notes"), placeholder: "Guarantee notes..." })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", size: "sm", disabled: isSubmitting, children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing ? "Save Changes" : "Add Guarantee"] })] })] })] }) }));
114
+ }), placeholder: messages.bookingGuaranteeDialog.placeholders.expiresAt, className: "w-full" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingGuaranteeDialog.fields.notes }), _jsx(Textarea, { ...form.register("notes"), placeholder: messages.bookingGuaranteeDialog.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: isSubmitting, children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing
115
+ ? messages.common.saveChanges
116
+ : messages.bookingGuaranteeDialog.actions.addGuarantee] })] })] })] }) }));
101
117
  }
@@ -1 +1 @@
1
- {"version":3,"file":"booking-guarantee-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-guarantee-list.tsx"],"names":[],"mappings":"AA2BA,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,EAAE,yBAAyB,2CAkH5E"}
1
+ {"version":3,"file":"booking-guarantee-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-guarantee-list.tsx"],"names":[],"mappings":"AAuBA,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,EAAE,yBAAyB,2CAwI5E"}
@@ -4,6 +4,7 @@ import { useBookingGuaranteeMutation, useBookingGuarantees, } from "@voyantjs/fi
4
4
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
5
  import { Pencil, Plus, ShieldCheck, Trash2 } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  import { BookingGuaranteeDialog } from "./booking-guarantee-dialog";
8
9
  const statusVariant = {
9
10
  pending: "outline",
@@ -13,25 +14,27 @@ const statusVariant = {
13
14
  cancelled: "destructive",
14
15
  expired: "secondary",
15
16
  };
16
- function formatAmount(cents, currency) {
17
- if (cents == null || !currency)
18
- return "-";
19
- return `${(cents / 100).toFixed(2)} ${currency}`;
20
- }
21
17
  export function BookingGuaranteeList({ bookingId }) {
22
18
  const [dialogOpen, setDialogOpen] = React.useState(false);
23
19
  const [editing, setEditing] = React.useState(undefined);
24
20
  const { data } = useBookingGuarantees(bookingId);
25
21
  const { remove } = useBookingGuaranteeMutation(bookingId);
22
+ const { formatCurrency, formatDate } = useBookingsUiI18nOrDefault();
23
+ const messages = useBookingsUiMessagesOrDefault();
26
24
  const guarantees = data?.data ?? [];
27
- return (_jsxs(Card, { "data-slot": "booking-guarantee-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(ShieldCheck, { className: "h-4 w-4" }), "Guarantees"] }), _jsxs(Button, { size: "sm", onClick: () => {
25
+ return (_jsxs(Card, { "data-slot": "booking-guarantee-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(ShieldCheck, { className: "h-4 w-4" }), messages.bookingGuaranteeList.title] }), _jsxs(Button, { size: "sm", onClick: () => {
28
26
  setEditing(undefined);
29
27
  setDialogOpen(true);
30
- }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Add Guarantee"] })] }), _jsx(CardContent, { children: guarantees.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "No guarantees yet." })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "p-2 text-left font-medium", children: "Type" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Status" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Amount" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Provider" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Reference" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Expires" }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: guarantees.map((g) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2 capitalize", children: g.guaranteeType.replace(/_/g, " ") }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[g.status] ?? "secondary", className: "capitalize", children: g.status.replace(/_/g, " ") }) }), _jsx("td", { className: "p-2 text-right font-mono", children: formatAmount(g.amountCents, g.currency) }), _jsx("td", { className: "p-2", children: g.provider ?? "-" }), _jsx("td", { className: "max-w-[150px] truncate p-2 font-mono text-xs", children: g.referenceNumber ?? "-" }), _jsx("td", { className: "p-2", children: g.expiresAt ? new Date(g.expiresAt).toLocaleDateString() : "-" }), _jsx("td", { className: "p-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
28
+ }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.bookingGuaranteeList.addGuarantee] })] }), _jsx(CardContent, { children: guarantees.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.bookingGuaranteeList.empty })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingGuaranteeList.columns.type }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingGuaranteeList.columns.status }), _jsx("th", { className: "p-2 text-right font-medium", children: messages.bookingGuaranteeList.columns.amount }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingGuaranteeList.columns.provider }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingGuaranteeList.columns.reference }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingGuaranteeList.columns.expires }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: guarantees.map((g) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: messages.bookingGuaranteeDialog.guaranteeTypeLabels[g.guaranteeType] }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[g.status] ?? "secondary", children: messages.bookingGuaranteeDialog.guaranteeStatusLabels[g.status] }) }), _jsx("td", { className: "p-2 text-right font-mono", children: g.amountCents == null || !g.currency
29
+ ? messages.bookingGuaranteeList.values.amountUnavailable
30
+ : formatCurrency(g.amountCents / 100, g.currency) }), _jsx("td", { className: "p-2", children: g.provider ?? messages.bookingGuaranteeList.values.providerUnavailable }), _jsx("td", { className: "max-w-[150px] truncate p-2 font-mono text-xs", children: g.referenceNumber ??
31
+ messages.bookingGuaranteeList.values.referenceUnavailable }), _jsx("td", { className: "p-2", children: g.expiresAt
32
+ ? formatDate(g.expiresAt)
33
+ : messages.bookingGuaranteeList.values.expiresUnavailable }), _jsx("td", { className: "p-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
31
34
  setEditing(g);
32
35
  setDialogOpen(true);
33
36
  }, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => {
34
- if (confirm("Delete this guarantee?")) {
37
+ if (confirm(messages.bookingGuaranteeList.actions.deleteConfirm)) {
35
38
  remove.mutate(g.id);
36
39
  }
37
40
  }, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }, g.id))) })] }) })) }), _jsx(BookingGuaranteeDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
@@ -1 +1 @@
1
- {"version":3,"file":"booking-item-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-item-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAA0B,MAAM,0BAA0B,CAAA;AA4DzF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,EAAE,sBAAsB,2CA8NxB"}
1
+ {"version":3,"file":"booking-item-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-item-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAA0B,MAAM,0BAA0B,CAAA;AAgEzF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,EAAE,sBAAsB,2CAmQxB"}
@@ -9,6 +9,7 @@ import { Loader2 } from "lucide-react";
9
9
  import { useEffect } from "react";
10
10
  import { useForm } from "react-hook-form";
11
11
  import { z } from "zod/v4";
12
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
12
13
  const itemTypes = [
13
14
  "unit",
14
15
  "extra",
@@ -22,24 +23,29 @@ const itemTypes = [
22
23
  "other",
23
24
  ];
24
25
  const itemStatuses = ["draft", "on_hold", "confirmed", "cancelled", "expired", "fulfilled"];
25
- const bookingItemFormSchema = z.object({
26
- title: z.string().min(1, "Title is required"),
27
- itemType: z.enum(itemTypes).default("unit"),
28
- status: z.enum(itemStatuses).default("draft"),
29
- quantity: z.coerce.number().int().positive().default(1),
30
- sellCurrency: z.string().min(3).max(3).default("EUR"),
31
- unitSellAmountCents: z.coerce.number().int().optional().nullable(),
32
- totalSellAmountCents: z.coerce.number().int().optional().nullable(),
33
- costCurrency: z.string().min(3).max(3).optional().nullable(),
34
- unitCostAmountCents: z.coerce.number().int().optional().nullable(),
35
- totalCostAmountCents: z.coerce.number().int().optional().nullable(),
36
- serviceDate: z.string().optional().nullable(),
37
- description: z.string().optional().nullable(),
38
- notes: z.string().optional().nullable(),
39
- });
26
+ const DEFAULT_CURRENCY = "EUR"; // i18n-literal-ok ISO default currency
27
+ function createBookingItemFormSchema(messages) {
28
+ return z.object({
29
+ title: z.string().min(1, messages.bookingItemDialog.validation.titleRequired),
30
+ itemType: z.enum(itemTypes).default("unit"),
31
+ status: z.enum(itemStatuses).default("draft"),
32
+ quantity: z.coerce.number().int().positive().default(1),
33
+ sellCurrency: z.string().min(3).max(3).default("EUR"),
34
+ unitSellAmountCents: z.coerce.number().int().optional().nullable(),
35
+ totalSellAmountCents: z.coerce.number().int().optional().nullable(),
36
+ costCurrency: z.string().min(3).max(3).optional().nullable(),
37
+ unitCostAmountCents: z.coerce.number().int().optional().nullable(),
38
+ totalCostAmountCents: z.coerce.number().int().optional().nullable(),
39
+ serviceDate: z.string().optional().nullable(),
40
+ description: z.string().optional().nullable(),
41
+ notes: z.string().optional().nullable(),
42
+ });
43
+ }
40
44
  export function BookingItemDialog({ open, onOpenChange, bookingId, item, onSuccess, }) {
41
45
  const isEditing = Boolean(item);
42
46
  const { create, update } = useBookingItemMutation(bookingId);
47
+ const messages = useBookingsUiMessagesOrDefault();
48
+ const bookingItemFormSchema = createBookingItemFormSchema(messages);
43
49
  const form = useForm({
44
50
  resolver: zodResolver(bookingItemFormSchema),
45
51
  defaultValues: {
@@ -47,7 +53,7 @@ export function BookingItemDialog({ open, onOpenChange, bookingId, item, onSucce
47
53
  itemType: "unit",
48
54
  status: "draft",
49
55
  quantity: 1,
50
- sellCurrency: "EUR",
56
+ sellCurrency: DEFAULT_CURRENCY,
51
57
  unitSellAmountCents: null,
52
58
  totalSellAmountCents: null,
53
59
  costCurrency: null,
@@ -106,14 +112,22 @@ export function BookingItemDialog({ open, onOpenChange, bookingId, item, onSucce
106
112
  onSuccess?.();
107
113
  };
108
114
  const isSubmitting = create.isPending || update.isPending;
109
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Item" : "Add Item" }) }), _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: "flex flex-col gap-2", children: [_jsx(Label, { children: "Title" }), _jsx(Input, { ...form.register("title"), placeholder: "Room night, transfer, tour..." }), form.formState.errors.title && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.title.message }))] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Type" }), _jsxs(Select, { items: itemTypes.map((t) => ({ label: t.replace("_", " "), value: t })), value: form.watch("itemType"), onValueChange: (v) => form.setValue("itemType", v), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemTypes.map((t) => (_jsx(SelectItem, { value: t, children: t.replace("_", " ") }, t))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Status" }), _jsxs(Select, { items: itemStatuses.map((s) => ({ label: s.replace("_", " "), value: s })), value: form.watch("status"), onValueChange: (v) => form.setValue("status", v), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemStatuses.map((s) => (_jsx(SelectItem, { value: s, children: s.replace("_", " ") }, s))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Quantity" }), _jsx(Input, { ...form.register("quantity"), type: "number", min: 1 })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sell Currency" }), _jsx(CurrencyCombobox, { value: form.watch("sellCurrency") || null, onChange: (next) => form.setValue("sellCurrency", next ?? "EUR", {
115
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
116
+ ? messages.bookingItemDialog.titles.edit
117
+ : messages.bookingItemDialog.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: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.title }), _jsx(Input, { ...form.register("title"), placeholder: messages.bookingItemDialog.placeholders.title }), form.formState.errors.title && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.title.message }))] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.type }), _jsxs(Select, { items: itemTypes.map((t) => ({
118
+ label: messages.bookingItemDialog.itemTypeLabels[t],
119
+ value: t,
120
+ })), value: form.watch("itemType"), onValueChange: (v) => form.setValue("itemType", v), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemTypes.map((t) => (_jsx(SelectItem, { value: t, children: messages.bookingItemDialog.itemTypeLabels[t] }, t))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.status }), _jsxs(Select, { items: itemStatuses.map((s) => ({
121
+ label: messages.bookingItemDialog.itemStatusLabels[s],
122
+ value: s,
123
+ })), value: form.watch("status"), onValueChange: (v) => form.setValue("status", v), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemStatuses.map((s) => (_jsx(SelectItem, { value: s, children: messages.bookingItemDialog.itemStatusLabels[s] }, s))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.quantity }), _jsx(Input, { ...form.register("quantity"), type: "number", min: 1 })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.sellCurrency }), _jsx(CurrencyCombobox, { value: form.watch("sellCurrency") || null, onChange: (next) => form.setValue("sellCurrency", next ?? DEFAULT_CURRENCY, {
110
124
  shouldValidate: true,
111
125
  shouldDirty: true,
112
- }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Unit Sell (cents)" }), _jsx(Input, { ...form.register("unitSellAmountCents"), type: "number", placeholder: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Total Sell (cents)" }), _jsx(Input, { ...form.register("totalSellAmountCents"), type: "number", placeholder: "0" })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Cost Currency" }), _jsx(CurrencyCombobox, { value: form.watch("costCurrency") || null, onChange: (next) => form.setValue("costCurrency", next ?? "EUR", {
126
+ }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.unitSellAmountCents }), _jsx(Input, { ...form.register("unitSellAmountCents"), type: "number", placeholder: messages.bookingItemDialog.placeholders.unitSellAmountCents })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.totalSellAmountCents }), _jsx(Input, { ...form.register("totalSellAmountCents"), type: "number", placeholder: messages.bookingItemDialog.placeholders.totalSellAmountCents })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.costCurrency }), _jsx(CurrencyCombobox, { value: form.watch("costCurrency") || null, onChange: (next) => form.setValue("costCurrency", next ?? DEFAULT_CURRENCY, {
113
127
  shouldValidate: true,
114
128
  shouldDirty: true,
115
- }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Unit Cost (cents)" }), _jsx(Input, { ...form.register("unitCostAmountCents"), type: "number", placeholder: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Total Cost (cents)" }), _jsx(Input, { ...form.register("totalCostAmountCents"), type: "number", placeholder: "0" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Service Date" }), _jsx(DatePicker, { value: form.watch("serviceDate") || null, onChange: (next) => form.setValue("serviceDate", next ?? "", {
129
+ }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.unitCostAmountCents }), _jsx(Input, { ...form.register("unitCostAmountCents"), type: "number", placeholder: messages.bookingItemDialog.placeholders.unitCostAmountCents })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.totalCostAmountCents }), _jsx(Input, { ...form.register("totalCostAmountCents"), type: "number", placeholder: messages.bookingItemDialog.placeholders.totalCostAmountCents })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.serviceDate }), _jsx(DatePicker, { value: form.watch("serviceDate") || null, onChange: (next) => form.setValue("serviceDate", next ?? "", {
116
130
  shouldValidate: true,
117
131
  shouldDirty: true,
118
- }), placeholder: "Select service date", className: "w-full" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Description" }), _jsx(Textarea, { ...form.register("description"), placeholder: "Item description..." })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Notes" }), _jsx(Textarea, { ...form.register("notes"), placeholder: "Internal notes..." })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", size: "sm", disabled: isSubmitting, children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing ? "Save Changes" : "Add Item"] })] })] })] }) }));
132
+ }), placeholder: messages.bookingItemDialog.placeholders.serviceDate, className: "w-full" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.description }), _jsx(Textarea, { ...form.register("description"), placeholder: messages.bookingItemDialog.placeholders.description })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingItemDialog.fields.notes }), _jsx(Textarea, { ...form.register("notes"), placeholder: messages.bookingItemDialog.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: isSubmitting, children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing ? messages.common.saveChanges : messages.bookingItemDialog.actions.addItem] })] })] })] }) }));
119
133
  }
@@ -1 +1 @@
1
- {"version":3,"file":"booking-item-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-item-list.tsx"],"names":[],"mappings":"AA4BA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,EAAE,oBAAoB,2CAyIlE"}
1
+ {"version":3,"file":"booking-item-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-item-list.tsx"],"names":[],"mappings":"AAwBA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,EAAE,oBAAoB,2CA6JlE"}
@@ -4,6 +4,7 @@ import { useBookingItemMutation, useBookingItems, } from "@voyantjs/bookings-rea
4
4
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
5
  import { ChevronDown, ChevronRight, Package, Pencil, Plus, Trash2 } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  import { BookingItemDialog } from "./booking-item-dialog";
8
9
  import { BookingItemTravelers } from "./booking-item-travelers";
9
10
  const statusVariant = {
@@ -14,28 +15,28 @@ const statusVariant = {
14
15
  expired: "secondary",
15
16
  fulfilled: "default",
16
17
  };
17
- function formatAmount(cents, currency) {
18
- if (cents == null)
19
- return "-";
20
- return `${(cents / 100).toFixed(2)} ${currency}`;
21
- }
22
18
  export function BookingItemList({ bookingId }) {
23
19
  const [dialogOpen, setDialogOpen] = React.useState(false);
24
20
  const [editing, setEditing] = React.useState(undefined);
25
21
  const [expandedItemId, setExpandedItemId] = React.useState(null);
26
22
  const { data } = useBookingItems(bookingId);
27
23
  const { remove } = useBookingItemMutation(bookingId);
24
+ const { formatCurrency } = useBookingsUiI18nOrDefault();
25
+ const messages = useBookingsUiMessagesOrDefault();
28
26
  const items = data?.data ?? [];
29
- return (_jsxs(Card, { "data-slot": "booking-item-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Package, { className: "h-4 w-4" }), "Items"] }), _jsxs(Button, { size: "sm", onClick: () => {
27
+ return (_jsxs(Card, { "data-slot": "booking-item-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Package, { className: "h-4 w-4" }), messages.bookingItemList.title] }), _jsxs(Button, { size: "sm", onClick: () => {
30
28
  setEditing(undefined);
31
29
  setDialogOpen(true);
32
- }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Add Item"] })] }), _jsx(CardContent, { children: items.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "No items yet." })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "w-8 p-2" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Title" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Type" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Status" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Qty" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Total" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Service Date" }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: items.map((item) => {
30
+ }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.bookingItemList.addItem] })] }), _jsx(CardContent, { children: items.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.bookingItemList.empty })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "w-8 p-2" }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingItemList.columns.title }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingItemList.columns.type }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingItemList.columns.status }), _jsx("th", { className: "p-2 text-right font-medium", children: messages.bookingItemList.columns.quantity }), _jsx("th", { className: "p-2 text-right font-medium", children: messages.bookingItemList.columns.total }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingItemList.columns.serviceDate }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: items.map((item) => {
33
31
  const isExpanded = expandedItemId === item.id;
34
- return (_jsxs(React.Fragment, { children: [_jsxs("tr", { className: "border-b", children: [_jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => setExpandedItemId(isExpanded ? null : item.id), className: "text-muted-foreground hover:text-foreground", children: isExpanded ? (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronRight, { className: "h-3.5 w-3.5" })) }) }), _jsx("td", { className: "p-2 font-medium", children: item.title }), _jsx("td", { className: "p-2 capitalize", children: item.itemType.replace("_", " ") }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[item.status] ?? "secondary", className: "capitalize", children: item.status.replace("_", " ") }) }), _jsx("td", { className: "p-2 text-right font-mono", children: item.quantity }), _jsx("td", { className: "p-2 text-right font-mono", children: formatAmount(item.totalSellAmountCents, item.sellCurrency) }), _jsx("td", { className: "p-2", children: item.serviceDate ?? "-" }), _jsx("td", { className: "p-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
32
+ return (_jsxs(React.Fragment, { children: [_jsxs("tr", { className: "border-b", children: [_jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => setExpandedItemId(isExpanded ? null : item.id), className: "text-muted-foreground hover:text-foreground", children: isExpanded ? (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronRight, { className: "h-3.5 w-3.5" })) }) }), _jsx("td", { className: "p-2 font-medium", children: item.title }), _jsx("td", { className: "p-2", children: messages.bookingItemDialog.itemTypeLabels[item.itemType] }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[item.status] ?? "secondary", children: messages.bookingItemDialog.itemStatusLabels[item.status] }) }), _jsx("td", { className: "p-2 text-right font-mono", children: item.quantity }), _jsx("td", { className: "p-2 text-right font-mono", children: item.totalSellAmountCents == null
33
+ ? messages.bookingItemList.values.totalUnavailable
34
+ : formatCurrency(item.totalSellAmountCents / 100, item.sellCurrency) }), _jsx("td", { className: "p-2", children: item.serviceDate ??
35
+ messages.bookingItemList.values.serviceDateUnavailable }), _jsx("td", { className: "p-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
35
36
  setEditing(item);
36
37
  setDialogOpen(true);
37
38
  }, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => {
38
- if (confirm("Delete this item?")) {
39
+ if (confirm(messages.bookingItemList.actions.deleteConfirm)) {
39
40
  remove.mutate(item.id);
40
41
  }
41
42
  }, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }), isExpanded && (_jsx("tr", { className: "border-b last:border-b-0", children: _jsx("td", { colSpan: 8, className: "p-2", children: _jsx(BookingItemTravelers, { bookingId: bookingId, itemId: item.id }) }) }))] }, item.id));
@@ -1 +1 @@
1
- {"version":3,"file":"booking-item-travelers.d.ts","sourceRoot":"","sources":["../../src/components/booking-item-travelers.tsx"],"names":[],"mappings":"AA8BA,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,yBAAyB,2CAuIpF"}
1
+ {"version":3,"file":"booking-item-travelers.d.ts","sourceRoot":"","sources":["../../src/components/booking-item-travelers.tsx"],"names":[],"mappings":"AA+BA,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,yBAAyB,2CA6IpF"}
@@ -4,6 +4,7 @@ import { useBookingItemTravelerMutation, useBookingItemTravelers, useTravelers,
4
4
  import { Badge, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components";
5
5
  import { Plus, Trash2, UserCheck } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  const roles = [
8
9
  "traveler",
9
10
  "occupant",
@@ -16,6 +17,7 @@ export function BookingItemTravelers({ bookingId, itemId }) {
16
17
  const { data: travelerLinksData } = useBookingItemTravelers(bookingId, itemId);
17
18
  const { data: travelersData } = useTravelers(bookingId);
18
19
  const { add, remove } = useBookingItemTravelerMutation(bookingId, itemId);
20
+ const messages = useBookingsUiMessagesOrDefault();
19
21
  const [selectedTravelerId, setSelectedTravelerId] = React.useState("");
20
22
  const [selectedRole, setSelectedRole] = React.useState("traveler");
21
23
  const assignedTravelers = travelerLinksData?.data ?? [];
@@ -36,15 +38,18 @@ export function BookingItemTravelers({ bookingId, itemId }) {
36
38
  },
37
39
  });
38
40
  };
39
- return (_jsxs("div", { className: "space-y-3 rounded-md border bg-muted/30 p-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [_jsx(UserCheck, { className: "h-3.5 w-3.5" }), "Assigned Travelers"] }), assignedTravelers.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: "No travelers assigned to this item." })) : (_jsx("div", { className: "space-y-1", children: assignedTravelers.map((link) => {
41
+ return (_jsxs("div", { className: "space-y-3 rounded-md border bg-muted/30 p-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [_jsx(UserCheck, { className: "h-3.5 w-3.5" }), messages.bookingItemTravelers.title] }), assignedTravelers.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: messages.bookingItemTravelers.empty })) : (_jsx("div", { className: "space-y-1", children: assignedTravelers.map((link) => {
40
42
  const traveler = travelerMap.get(link.travelerId);
41
- return (_jsxs("div", { className: "flex items-center justify-between rounded px-2 py-1 text-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: traveler ? `${traveler.firstName} ${traveler.lastName}` : link.travelerId }), _jsx(Badge, { variant: "outline", className: "text-xs capitalize", children: link.role.replace("_", " ") }), link.isPrimary && (_jsx(Badge, { variant: "default", className: "text-xs", children: "Primary" }))] }), _jsx("button", { type: "button", onClick: () => {
42
- if (confirm("Remove this traveler from the item?")) {
43
+ return (_jsxs("div", { className: "flex items-center justify-between rounded px-2 py-1 text-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: traveler ? `${traveler.firstName} ${traveler.lastName}` : link.travelerId }), _jsx(Badge, { variant: "outline", className: "text-xs", children: messages.bookingItemTravelers.roleLabels[link.role] }), link.isPrimary && (_jsx(Badge, { variant: "default", className: "text-xs", children: messages.bookingItemTravelers.primaryBadge }))] }), _jsx("button", { type: "button", onClick: () => {
44
+ if (confirm(messages.bookingItemTravelers.actions.removeConfirm)) {
43
45
  remove.mutate(link.id);
44
46
  }
45
47
  }, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, link.id));
46
48
  }) })), availableTravelers.length > 0 && (_jsxs("div", { className: "flex items-end gap-2 border-t pt-3", children: [_jsx("div", { className: "flex-1", children: _jsxs(Select, { items: availableTravelers.map((traveler) => ({
47
49
  label: `${traveler.firstName} ${traveler.lastName}`,
48
50
  value: traveler.id,
49
- })), value: selectedTravelerId, onValueChange: (v) => setSelectedTravelerId(v ?? ""), children: [_jsx(SelectTrigger, { className: "w-full h-8 text-xs", children: _jsx(SelectValue, { placeholder: "Select traveler..." }) }), _jsx(SelectContent, { children: availableTravelers.map((traveler) => (_jsxs(SelectItem, { value: traveler.id, children: [traveler.firstName, " ", traveler.lastName] }, traveler.id))) })] }) }), _jsx("div", { className: "w-36", children: _jsxs(Select, { items: roles.map((r) => ({ label: r.replace("_", " "), value: r })), value: selectedRole, onValueChange: (v) => setSelectedRole(v ?? "traveler"), children: [_jsx(SelectTrigger, { className: "w-full h-8 text-xs", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: roles.map((r) => (_jsx(SelectItem, { value: r, children: r.replace("_", " ") }, r))) })] }) }), _jsxs(Button, { size: "sm", variant: "outline", className: "h-8", onClick: handleAssign, disabled: !selectedTravelerId || add.isPending, children: [_jsx(Plus, { className: "mr-1 h-3.5 w-3.5" }), "Assign"] })] }))] }));
51
+ })), value: selectedTravelerId, onValueChange: (v) => setSelectedTravelerId(v ?? ""), children: [_jsx(SelectTrigger, { className: "w-full h-8 text-xs", children: _jsx(SelectValue, { placeholder: messages.bookingItemTravelers.selectTravelerPlaceholder }) }), _jsx(SelectContent, { children: availableTravelers.map((traveler) => (_jsxs(SelectItem, { value: traveler.id, children: [traveler.firstName, " ", traveler.lastName] }, traveler.id))) })] }) }), _jsx("div", { className: "w-36", children: _jsxs(Select, { items: roles.map((r) => ({
52
+ label: messages.bookingItemTravelers.roleLabels[r],
53
+ value: r,
54
+ })), value: selectedRole, onValueChange: (v) => setSelectedRole(v ?? "traveler"), children: [_jsx(SelectTrigger, { className: "w-full h-8 text-xs", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: roles.map((r) => (_jsx(SelectItem, { value: r, children: messages.bookingItemTravelers.roleLabels[r] }, r))) })] }) }), _jsxs(Button, { size: "sm", variant: "outline", className: "h-8", onClick: handleAssign, disabled: !selectedTravelerId || add.isPending, children: [_jsx(Plus, { className: "mr-1 h-3.5 w-3.5" }), messages.bookingItemTravelers.actions.assign] })] }))] }));
50
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"booking-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-list.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;AAiBjC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CACnD;AAOD,wBAAgB,WAAW,CAAC,EAAE,QAAa,EAAE,eAAe,EAAE,GAAE,gBAAqB,2CAkJpF"}
1
+ {"version":3,"file":"booking-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-list.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,0BAA0B,CAAA;AAsBjC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;CACnD;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAa,EAAE,eAAe,EAAE,GAAE,gBAAqB,2CA+JpF"}