@voyantjs/bookings-ui 0.16.0 → 0.18.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 (85) hide show
  1. package/LICENSE +201 -109
  2. package/README.md +11 -0
  3. package/dist/components/booking-activity-timeline.d.ts.map +1 -1
  4. package/dist/components/booking-activity-timeline.js +34 -14
  5. package/dist/components/booking-cancellation-dialog.d.ts.map +1 -1
  6. package/dist/components/booking-cancellation-dialog.js +15 -16
  7. package/dist/components/booking-create-dialog.d.ts.map +1 -1
  8. package/dist/components/booking-create-dialog.js +77 -13
  9. package/dist/components/booking-dialog.d.ts.map +1 -1
  10. package/dist/components/booking-dialog.js +27 -21
  11. package/dist/components/booking-document-dialog.d.ts.map +1 -1
  12. package/dist/components/booking-document-dialog.js +27 -13
  13. package/dist/components/booking-document-list.d.ts.map +1 -1
  14. package/dist/components/booking-document-list.js +9 -4
  15. package/dist/components/booking-group-link-dialog.d.ts.map +1 -1
  16. package/dist/components/booking-group-link-dialog.js +17 -6
  17. package/dist/components/booking-group-section.d.ts.map +1 -1
  18. package/dist/components/booking-group-section.js +8 -2
  19. package/dist/components/booking-guarantee-dialog.d.ts.map +1 -1
  20. package/dist/components/booking-guarantee-dialog.js +30 -14
  21. package/dist/components/booking-guarantee-list.d.ts.map +1 -1
  22. package/dist/components/booking-guarantee-list.js +11 -8
  23. package/dist/components/booking-item-dialog.d.ts.map +1 -1
  24. package/dist/components/booking-item-dialog.js +34 -20
  25. package/dist/components/booking-item-list.d.ts.map +1 -1
  26. package/dist/components/booking-item-list.js +10 -9
  27. package/dist/components/booking-item-travelers.d.ts.map +1 -1
  28. package/dist/components/booking-item-travelers.js +9 -4
  29. package/dist/components/booking-list.d.ts.map +1 -1
  30. package/dist/components/booking-list.js +17 -8
  31. package/dist/components/booking-notes.d.ts.map +1 -1
  32. package/dist/components/booking-notes.js +5 -2
  33. package/dist/components/booking-payment-schedule-dialog.d.ts.map +1 -1
  34. package/dist/components/booking-payment-schedule-dialog.js +31 -12
  35. package/dist/components/booking-payment-schedule-list.d.ts.map +1 -1
  36. package/dist/components/booking-payment-schedule-list.js +7 -6
  37. package/dist/components/booking-payments-summary.d.ts.map +1 -1
  38. package/dist/components/booking-payments-summary.js +7 -4
  39. package/dist/components/file-dropzone.d.ts.map +1 -1
  40. package/dist/components/file-dropzone.js +25 -15
  41. package/dist/components/passengers-section.d.ts.map +1 -1
  42. package/dist/components/passengers-section.js +3 -17
  43. package/dist/components/payment-schedule-section.d.ts.map +1 -1
  44. package/dist/components/payment-schedule-section.js +3 -14
  45. package/dist/components/person-picker-section.d.ts.map +1 -1
  46. package/dist/components/person-picker-section.js +3 -19
  47. package/dist/components/price-breakdown-section.d.ts.map +1 -1
  48. package/dist/components/price-breakdown-section.js +15 -18
  49. package/dist/components/product-picker-section.d.ts.map +1 -1
  50. package/dist/components/product-picker-section.js +3 -8
  51. package/dist/components/rooms-stepper-section.d.ts.map +1 -1
  52. package/dist/components/rooms-stepper-section.js +4 -9
  53. package/dist/components/shared-room-section.d.ts.map +1 -1
  54. package/dist/components/shared-room-section.js +3 -9
  55. package/dist/components/status-change-dialog.d.ts.map +1 -1
  56. package/dist/components/status-change-dialog.js +6 -1
  57. package/dist/components/supplier-status-dialog.d.ts.map +1 -1
  58. package/dist/components/supplier-status-dialog.js +31 -15
  59. package/dist/components/supplier-status-list.d.ts.map +1 -1
  60. package/dist/components/supplier-status-list.js +10 -2
  61. package/dist/components/traveler-dialog.d.ts.map +1 -1
  62. package/dist/components/traveler-dialog.js +17 -8
  63. package/dist/components/traveler-list.d.ts.map +1 -1
  64. package/dist/components/traveler-list.js +5 -3
  65. package/dist/components/voucher-picker-section.d.ts.map +1 -1
  66. package/dist/components/voucher-picker-section.js +11 -26
  67. package/dist/i18n/en.d.ts +797 -0
  68. package/dist/i18n/en.d.ts.map +1 -0
  69. package/dist/i18n/en.js +796 -0
  70. package/dist/i18n/index.d.ts +5 -0
  71. package/dist/i18n/index.d.ts.map +1 -0
  72. package/dist/i18n/index.js +3 -0
  73. package/dist/i18n/messages.d.ts +684 -0
  74. package/dist/i18n/messages.d.ts.map +1 -0
  75. package/dist/i18n/messages.js +1 -0
  76. package/dist/i18n/provider.d.ts +1617 -0
  77. package/dist/i18n/provider.d.ts.map +1 -0
  78. package/dist/i18n/provider.js +45 -0
  79. package/dist/i18n/ro.d.ts +797 -0
  80. package/dist/i18n/ro.d.ts.map +1 -0
  81. package/dist/i18n/ro.js +796 -0
  82. package/dist/index.d.ts +1 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +1 -0
  85. package/package.json +40 -20
@@ -4,6 +4,7 @@ import { useBookingPaymentScheduleMutation, useBookingPaymentSchedules, } from "
4
4
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
5
  import { CalendarClock, Pencil, Plus, Trash2 } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  import { BookingPaymentScheduleDialog } from "./booking-payment-schedule-dialog";
8
9
  const statusVariant = {
9
10
  pending: "outline",
@@ -13,23 +14,23 @@ const statusVariant = {
13
14
  cancelled: "destructive",
14
15
  expired: "secondary",
15
16
  };
16
- function formatAmount(cents, currency) {
17
- return `${(cents / 100).toFixed(2)} ${currency}`;
18
- }
19
17
  export function BookingPaymentScheduleList({ bookingId }) {
20
18
  const [dialogOpen, setDialogOpen] = React.useState(false);
21
19
  const [editing, setEditing] = React.useState(undefined);
22
20
  const { data } = useBookingPaymentSchedules(bookingId);
23
21
  const { remove } = useBookingPaymentScheduleMutation(bookingId);
22
+ const { formatCurrency } = useBookingsUiI18nOrDefault();
23
+ const messages = useBookingsUiMessagesOrDefault();
24
24
  const schedules = data?.data ?? [];
25
- return (_jsxs(Card, { "data-slot": "booking-payment-schedule-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-4 w-4" }), "Payment Schedule"] }), _jsxs(Button, { size: "sm", onClick: () => {
25
+ return (_jsxs(Card, { "data-slot": "booking-payment-schedule-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-4 w-4" }), messages.bookingPaymentScheduleList.title] }), _jsxs(Button, { size: "sm", onClick: () => {
26
26
  setEditing(undefined);
27
27
  setDialogOpen(true);
28
- }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Add Schedule"] })] }), _jsx(CardContent, { children: schedules.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "No payment schedules 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-left font-medium", children: "Due Date" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Amount" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Notes" }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: schedules.map((schedule) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2 capitalize", children: schedule.scheduleType.replace("_", " ") }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[schedule.status] ?? "secondary", className: "capitalize", children: schedule.status.replace("_", " ") }) }), _jsx("td", { className: "p-2", children: schedule.dueDate }), _jsx("td", { className: "p-2 text-right font-mono", children: formatAmount(schedule.amountCents, schedule.currency) }), _jsx("td", { className: "max-w-[200px] truncate p-2 text-muted-foreground", children: schedule.notes ?? "-" }), _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.bookingPaymentScheduleList.addSchedule] })] }), _jsx(CardContent, { children: schedules.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.bookingPaymentScheduleList.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.bookingPaymentScheduleList.columns.type }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingPaymentScheduleList.columns.status }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingPaymentScheduleList.columns.dueDate }), _jsx("th", { className: "p-2 text-right font-medium", children: messages.bookingPaymentScheduleList.columns.amount }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingPaymentScheduleList.columns.notes }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: schedules.map((schedule) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: messages.paymentScheduleDialog.scheduleTypeLabels[schedule.scheduleType] }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[schedule.status] ?? "secondary", children: messages.paymentScheduleDialog.scheduleStatusLabels[schedule.status] }) }), _jsx("td", { className: "p-2", children: schedule.dueDate }), _jsx("td", { className: "p-2 text-right font-mono", children: formatCurrency(schedule.amountCents / 100, schedule.currency) }), _jsx("td", { className: "max-w-[200px] truncate p-2 text-muted-foreground", children: schedule.notes ??
29
+ messages.bookingPaymentScheduleList.values.notesUnavailable }), _jsx("td", { className: "p-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
29
30
  setEditing(schedule);
30
31
  setDialogOpen(true);
31
32
  }, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => {
32
- if (confirm("Delete this payment schedule?")) {
33
+ if (confirm(messages.bookingPaymentScheduleList.actions.deleteConfirm)) {
33
34
  remove.mutate(schedule.id);
34
35
  }
35
36
  }, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }, schedule.id))) })] }) })) }), _jsx(BookingPaymentScheduleDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
@@ -1 +1 @@
1
- {"version":3,"file":"booking-payments-summary.d.ts","sourceRoot":"","sources":["../../src/components/booking-payments-summary.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,sBAAsB,CAAC,EAAE,SAAS,EAAE,EAAE,2BAA2B,2CA0DhF"}
1
+ {"version":3,"file":"booking-payments-summary.d.ts","sourceRoot":"","sources":["../../src/components/booking-payments-summary.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,sBAAsB,CAAC,EAAE,SAAS,EAAE,EAAE,2BAA2B,2CAgFhF"}
@@ -9,11 +9,14 @@ const statusVariant = {
9
9
  failed: "destructive",
10
10
  refunded: "secondary",
11
11
  };
12
- function formatAmount(cents, currency) {
13
- return `${(cents / 100).toFixed(2)} ${currency}`;
14
- }
12
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
15
13
  export function BookingPaymentsSummary({ bookingId }) {
16
14
  const { data } = usePublicBookingPayments(bookingId);
15
+ const { formatDate, formatNumber } = useBookingsUiI18nOrDefault();
16
+ const messages = useBookingsUiMessagesOrDefault();
17
17
  const payments = data?.data?.payments ?? [];
18
- return (_jsxs(Card, { "data-slot": "booking-payments-summary", children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "h-4 w-4" }), "Payments"] }) }), _jsx(CardContent, { children: payments.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "No payments recorded." })) : (_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: "Invoice" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Method" }), _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: "Date" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Reference" })] }) }), _jsx("tbody", { children: payments.map((payment) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2 font-mono text-xs", children: payment.invoiceNumber }), _jsx("td", { className: "p-2 capitalize", children: payment.paymentMethod.replace(/_/g, " ") }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[payment.status] ?? "secondary", className: "capitalize", children: payment.status }) }), _jsx("td", { className: "p-2 text-right font-mono", children: formatAmount(payment.amountCents, payment.currency) }), _jsx("td", { className: "p-2", children: new Date(payment.paymentDate).toLocaleDateString() }), _jsx("td", { className: "max-w-[150px] truncate p-2 font-mono text-xs", children: payment.referenceNumber ?? "-" })] }, payment.id))) })] }) })) })] }));
18
+ return (_jsxs(Card, { "data-slot": "booking-payments-summary", children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "h-4 w-4" }), messages.bookingPaymentsSummary.title] }) }), _jsx(CardContent, { children: payments.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.bookingPaymentsSummary.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.bookingPaymentsSummary.columns.invoice }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingPaymentsSummary.columns.method }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingPaymentsSummary.columns.status }), _jsx("th", { className: "p-2 text-right font-medium", children: messages.bookingPaymentsSummary.columns.amount }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingPaymentsSummary.columns.date }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.bookingPaymentsSummary.columns.reference })] }) }), _jsx("tbody", { children: payments.map((payment) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2 font-mono text-xs", children: payment.invoiceNumber }), _jsx("td", { className: "p-2", children: messages.bookingPaymentsSummary.paymentMethodLabels[payment.paymentMethod] ?? payment.paymentMethod }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[payment.status] ?? "secondary", children: messages.bookingPaymentsSummary.paymentStatusLabels[payment.status] ?? payment.status }) }), _jsx("td", { className: "p-2 text-right font-mono", children: `${formatNumber(payment.amountCents / 100, {
19
+ minimumFractionDigits: 2,
20
+ maximumFractionDigits: 2,
21
+ })} ${payment.currency}` }), _jsx("td", { className: "p-2", children: formatDate(payment.paymentDate) }), _jsx("td", { className: "max-w-[150px] truncate p-2 font-mono text-xs", children: payment.referenceNumber ?? "-" })] }, payment.id))) })] }) })) })] }));
19
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file-dropzone.d.ts","sourceRoot":"","sources":["../../src/components/file-dropzone.tsx"],"names":[],"mappings":"AAKA,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,gEAAgE;IAChE,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;AAQD,wBAAgB,YAAY,CAAC,EAC3B,SAA6B,EAC7B,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,EACP,UAA4D,EAC5D,QAAQ,GACT,EAAE,iBAAiB,2CAgJnB"}
1
+ {"version":3,"file":"file-dropzone.d.ts","sourceRoot":"","sources":["../../src/components/file-dropzone.tsx"],"names":[],"mappings":"AAUA,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,gEAAgE;IAChE,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,SAA6B,EAC7B,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,EACP,UAAU,EACV,QAAQ,GACT,EAAE,iBAAiB,2CA8KnB"}
@@ -2,19 +2,24 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { File as FileIcon, Loader2, Upload, X } from "lucide-react";
4
4
  import * as React from "react";
5
- function formatSize(bytes) {
6
- if (bytes < 1024)
7
- return `${bytes} B`;
8
- if (bytes < 1024 * 1024)
9
- return `${(bytes / 1024).toFixed(1)} KB`;
10
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
11
- }
12
- export function FileDropzone({ uploadUrl = "/api/v1/uploads", accept, maxSize, onUploaded, onError, helperText = "Drag and drop a file here, or click to select", disabled, }) {
5
+ import { formatMessage, useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault, } from "../i18n/provider";
6
+ export function FileDropzone({ uploadUrl = "/api/v1/uploads", accept, maxSize, onUploaded, onError, helperText, disabled, }) {
13
7
  const inputRef = React.useRef(null);
14
8
  const [isDragging, setIsDragging] = React.useState(false);
15
9
  const [isUploading, setIsUploading] = React.useState(false);
16
10
  const [uploaded, setUploaded] = React.useState(null);
17
11
  const [error, setError] = React.useState(null);
12
+ const { formatNumber } = useBookingsUiI18nOrDefault();
13
+ const messages = useBookingsUiMessagesOrDefault();
14
+ const resolvedHelperText = helperText ?? messages.fileDropzone.helperText;
15
+ const formatSize = React.useCallback((bytes) => {
16
+ if (bytes < 1024)
17
+ return `${formatNumber(bytes)} B`;
18
+ if (bytes < 1024 * 1024) {
19
+ return `${formatNumber(bytes / 1024, { maximumFractionDigits: 1 })} KB`;
20
+ }
21
+ return `${formatNumber(bytes / (1024 * 1024), { maximumFractionDigits: 1 })} MB`;
22
+ }, [formatNumber]);
18
23
  const reportError = (message) => {
19
24
  setError(message);
20
25
  onError?.(message);
@@ -22,7 +27,9 @@ export function FileDropzone({ uploadUrl = "/api/v1/uploads", accept, maxSize, o
22
27
  const handleFile = async (file) => {
23
28
  setError(null);
24
29
  if (maxSize && file.size > maxSize) {
25
- reportError(`File too large (max ${formatSize(maxSize)})`);
30
+ reportError(formatMessage(messages.fileDropzone.validation.fileTooLarge, {
31
+ maxSize: formatSize(maxSize),
32
+ }));
26
33
  return;
27
34
  }
28
35
  setIsUploading(true);
@@ -30,13 +37,16 @@ export function FileDropzone({ uploadUrl = "/api/v1/uploads", accept, maxSize, o
30
37
  const formData = new FormData();
31
38
  formData.append("file", file);
32
39
  const res = await fetch(uploadUrl, {
33
- method: "POST",
40
+ method: "POST", // i18n-literal-ok HTTP method
34
41
  body: formData,
35
- credentials: "include",
42
+ credentials: "include", // i18n-literal-ok fetch credentials mode
36
43
  });
37
44
  if (!res.ok) {
38
45
  const body = await res.text();
39
- reportError(body || `Upload failed (${res.status})`);
46
+ reportError(body ||
47
+ formatMessage(messages.fileDropzone.validation.uploadFailedWithStatus, {
48
+ status: res.status,
49
+ }));
40
50
  return;
41
51
  }
42
52
  const data = (await res.json());
@@ -45,7 +55,7 @@ export function FileDropzone({ uploadUrl = "/api/v1/uploads", accept, maxSize, o
45
55
  onUploaded(result);
46
56
  }
47
57
  catch (err) {
48
- reportError(err instanceof Error ? err.message : "Upload failed");
58
+ reportError(err instanceof Error ? err.message : messages.fileDropzone.validation.uploadFailed);
49
59
  }
50
60
  finally {
51
61
  setIsUploading(false);
@@ -86,7 +96,7 @@ export function FileDropzone({ uploadUrl = "/api/v1/uploads", accept, maxSize, o
86
96
  setError(null);
87
97
  };
88
98
  if (uploaded) {
89
- 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": "Remove file", children: _jsx(X, { className: "h-4 w-4" }) })] }));
99
+ 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" }) })] }));
90
100
  }
91
- return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("button", { type: "button", onClick: () => inputRef.current?.click(), onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, disabled: disabled || isUploading, "data-dragging": isDragging, className: "flex flex-col items-center justify-center gap-2 rounded-md border border-dashed px-4 py-8 text-center transition-colors hover:border-foreground/30 hover:bg-muted/30 data-[dragging=true]:border-primary data-[dragging=true]:bg-primary/5 disabled:cursor-not-allowed disabled:opacity-60", children: isUploading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Uploading..." })] })) : (_jsxs(_Fragment, { children: [_jsx(Upload, { className: "h-6 w-6 text-muted-foreground" }), _jsx("p", { className: "text-sm text-muted-foreground", children: helperText }), accept && _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Accepted: ", accept] })] })) }), _jsx("input", { ref: inputRef, type: "file", className: "hidden", accept: accept, onChange: handleChange, disabled: disabled || isUploading }), error && _jsx("p", { className: "text-xs text-destructive", children: error })] }));
101
+ return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("button", { type: "button", onClick: () => inputRef.current?.click(), onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, disabled: disabled || isUploading, "data-dragging": isDragging, className: "flex flex-col items-center justify-center gap-2 rounded-md border border-dashed px-4 py-8 text-center transition-colors hover:border-foreground/30 hover:bg-muted/30 data-[dragging=true]:border-primary data-[dragging=true]:bg-primary/5 disabled:cursor-not-allowed disabled:opacity-60", children: isUploading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }), _jsx("p", { className: "text-sm text-muted-foreground", children: messages.fileDropzone.uploading })] })) : (_jsxs(_Fragment, { children: [_jsx(Upload, { className: "h-6 w-6 text-muted-foreground" }), _jsx("p", { className: "text-sm text-muted-foreground", children: resolvedHelperText }), accept && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [messages.fileDropzone.acceptedPrefix, " ", accept] }))] })) }), _jsx("input", { ref: inputRef, type: "file", className: "hidden", accept: accept, onChange: handleChange, disabled: disabled || isUploading }), error && _jsx("p", { className: "text-xs text-destructive", children: error })] }));
92
102
  }
@@ -1 +1 @@
1
- {"version":3,"file":"passengers-section.d.ts","sourceRoot":"","sources":["../../src/components/passengers-section.tsx"],"names":[],"mappings":"AAcA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAA;AAIjE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;IACnB,gFAAgF;IAChF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,cAAc,EAAE,CAAA;CAC7B;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAAuC,CAAA;AAE7E,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,aAAuB,GAAG,cAAc,CAElF;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,EAAE,CAAA;IAC5B,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAqBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,sBAAsB,2CA2I/F"}
1
+ {"version":3,"file":"passengers-section.d.ts","sourceRoot":"","sources":["../../src/components/passengers-section.tsx"],"names":[],"mappings":"AAeA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAA;AAIjE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;IACnB,gFAAgF;IAChF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,cAAc,EAAE,CAAA;CAC7B;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAAuC,CAAA;AAE7E,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,aAAuB,GAAG,cAAc,CAElF;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,EAAE,CAAA;IAC5B,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,sBAAsB,2CA4I/F"}
@@ -2,28 +2,13 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components";
4
4
  import { Trash2 } from "lucide-react";
5
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
5
6
  const ALL_ROLES = ["lead", "adult", "child", "infant"];
6
7
  export const emptyPassengerListValue = { passengers: [] };
7
8
  /** Factory for a blank row — `role` defaults to `adult` unless the list is empty. */
8
9
  export function createBlankPassenger(role = "adult") {
9
10
  return { firstName: "", lastName: "", email: "", role, roomUnitId: null };
10
11
  }
11
- const DEFAULT_LABELS = {
12
- heading: "Passengers",
13
- addPassenger: "Add passenger",
14
- firstName: "First name",
15
- lastName: "Last name",
16
- email: "Email",
17
- role: "Role",
18
- roleLead: "Lead",
19
- roleAdult: "Adult",
20
- roleChild: "Child",
21
- roleInfant: "Infant",
22
- room: "Room",
23
- noRoom: "Unassigned",
24
- remove: "Remove passenger",
25
- empty: "No passengers yet. Add at least one.",
26
- };
27
12
  const NO_ROOM = "__unassigned__";
28
13
  /**
29
14
  * Passenger list for booking-create flows. Each row carries name + optional
@@ -46,7 +31,8 @@ const NO_ROOM = "__unassigned__";
46
31
  * the submit handler errors if the invariant isn't met.
47
32
  */
48
33
  export function PassengersSection({ value, onChange, roomUnits, labels }) {
49
- const merged = { ...DEFAULT_LABELS, ...labels };
34
+ const messages = useBookingsUiMessagesOrDefault();
35
+ const merged = { ...messages.passengersSection.labels, ...labels };
50
36
  const roleLabels = {
51
37
  lead: merged.roleLead,
52
38
  adult: merged.roleAdult,
@@ -1 +1 @@
1
- {"version":3,"file":"payment-schedule-section.d.ts","sourceRoot":"","sources":["../../src/components/payment-schedule-section.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;AAEzE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,mBAAmB,CAAA;IACzB,wEAAwE;IACxE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,wEAAwE;IACxE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,qDAAqD;IACrD,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,eAAO,MAAM,yBAAyB,EAAE,oBASvC,CAAA;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,oBAAoB,CAAA;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC/C;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;QACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAkCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,MAAM,GACP,EAAE,2BAA2B,2CAmI7B"}
1
+ {"version":3,"file":"payment-schedule-section.d.ts","sourceRoot":"","sources":["../../src/components/payment-schedule-section.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;AAEzE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,mBAAmB,CAAA;IACzB,wEAAwE;IACxE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,wEAAwE;IACxE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,qDAAqD;IACrD,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,eAAO,MAAM,yBAAyB,EAAE,oBASvC,CAAA;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,oBAAoB,CAAA;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAC/C;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;QACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAoBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,MAAM,GACP,EAAE,2BAA2B,2CAoI7B"}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Button, Input, Label } from "@voyantjs/ui/components";
4
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
4
5
  export const emptyPaymentScheduleValue = {
5
6
  mode: "unpaid",
6
7
  fullDueDate: null,
@@ -11,19 +12,6 @@ export const emptyPaymentScheduleValue = {
11
12
  splitSecondAmountCents: null,
12
13
  splitSecondDueDate: null,
13
14
  };
14
- const DEFAULT_LABELS = {
15
- heading: "Payment schedule",
16
- modeUnpaid: "Unpaid",
17
- modeFull: "Full",
18
- modeAdvance: "Advance",
19
- modeSplit: "Split",
20
- dueDate: "Due date",
21
- amount: "Amount",
22
- firstInstallment: "First installment",
23
- secondInstallment: "Second installment",
24
- preset5050: "50 / 50",
25
- unpaidHint: "No payment schedule will be created. Operator will invoice manually.",
26
- };
27
15
  /**
28
16
  * Converts an `<input type="number">` string value to minor units (cents).
29
17
  * Accepts `""` / `NaN` → `null`. Multiplies by 100 and rounds to avoid
@@ -64,7 +52,8 @@ function centsToMajorString(cents) {
64
52
  * - `split` → two schedules with `scheduleType: "installment"`.
65
53
  */
66
54
  export function PaymentScheduleSection({ value, onChange, totalAmountCents, currency, labels, }) {
67
- const merged = { ...DEFAULT_LABELS, ...labels };
55
+ const messages = useBookingsUiMessagesOrDefault();
56
+ const merged = { ...messages.paymentScheduleSection.labels, ...labels };
68
57
  const set = (patch) => onChange({ ...value, ...patch });
69
58
  const currencySuffix = currency ? ` ${currency}` : "";
70
59
  const modes = [
@@ -1 +1 @@
1
- {"version":3,"file":"person-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/person-picker-section.tsx"],"names":[],"mappings":"AAkBA,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,KAAK,CAAA;AAEjD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,gBAAgB,CAAA;IACtB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,gCAAgC;IAChC,SAAS,EAAE,cAAc,CAAA;IACzB,yCAAyC;IACzC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,eAAO,MAAM,cAAc,EAAE,cAK5B,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,iBAKpC,CAAA;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,iBAAiB,CAAA;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,oBAAoB,CAAC,EAAE,MAAM,CAAA;QAC7B,uBAAuB,CAAC,EAAE,MAAM,CAAA;QAChC,uBAAuB,CAAC,EAAE,MAAM,CAAA;QAChC,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,oBAAoB,CAAC,EAAE,MAAM,CAAA;QAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;QACzB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;QACzB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,6BAA6B,CAAC,EAAE,MAAM,CAAA;QACtC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAC1B,CAAA;CACF;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,OAAc,EACd,gBAAuB,EACvB,MAAM,GACP,EAAE,wBAAwB,2CA2J1B"}
1
+ {"version":3,"file":"person-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/person-picker-section.tsx"],"names":[],"mappings":"AAmBA,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,KAAK,CAAA;AAEjD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,gBAAgB,CAAA;IACtB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,gCAAgC;IAChC,SAAS,EAAE,cAAc,CAAA;IACzB,yCAAyC;IACzC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,eAAO,MAAM,cAAc,EAAE,cAK5B,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,iBAKpC,CAAA;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,iBAAiB,CAAA;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,oBAAoB,CAAC,EAAE,MAAM,CAAA;QAC7B,uBAAuB,CAAC,EAAE,MAAM,CAAA;QAChC,uBAAuB,CAAC,EAAE,MAAM,CAAA;QAChC,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,oBAAoB,CAAC,EAAE,MAAM,CAAA;QAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,mBAAmB,CAAC,EAAE,MAAM,CAAA;QAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;QACzB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;QACzB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,6BAA6B,CAAC,EAAE,MAAM,CAAA;QACtC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAC1B,CAAA;CACF;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,OAAc,EACd,gBAAuB,EACvB,MAAM,GACP,EAAE,wBAAwB,2CA4J1B"}
@@ -4,6 +4,7 @@ import { useOrganizations, usePeople } from "@voyantjs/crm-react";
4
4
  import { Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components";
5
5
  import { UserPlus } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  const ORG_NONE = "__none__";
8
9
  export const emptyNewPerson = {
9
10
  firstName: "",
@@ -17,24 +18,6 @@ export const emptyPersonPickerValue = {
17
18
  newPerson: emptyNewPerson,
18
19
  organizationId: null,
19
20
  };
20
- const DEFAULT_LABELS = {
21
- person: "Person",
22
- createNewPerson: "Create new",
23
- selectExistingPerson: "Select existing",
24
- personSearchPlaceholder: "Search people by name or email...",
25
- personSelectPlaceholder: "Select a person...",
26
- firstName: "First Name",
27
- firstNamePlaceholder: "John",
28
- lastName: "Last Name",
29
- lastNamePlaceholder: "Smith",
30
- email: "Email",
31
- emailPlaceholder: "john@example.com",
32
- phone: "Phone",
33
- phonePlaceholder: "+44 7911 123456",
34
- organization: "Organization (optional)",
35
- organizationSearchPlaceholder: "Search organizations...",
36
- organizationNone: "No organization",
37
- };
38
21
  /**
39
22
  * Person picker with inline-create + optional organization attachment.
40
23
  *
@@ -47,7 +30,8 @@ const DEFAULT_LABELS = {
47
30
  export function PersonPickerSection({ value, onChange, enabled = true, showOrganization = true, labels, }) {
48
31
  const [personSearch, setPersonSearch] = React.useState("");
49
32
  const [orgSearch, setOrgSearch] = React.useState("");
50
- const merged = { ...DEFAULT_LABELS, ...labels };
33
+ const messages = useBookingsUiMessagesOrDefault();
34
+ const merged = { ...messages.personPickerSection.labels, ...labels };
51
35
  const { data: peopleData } = usePeople({
52
36
  search: personSearch || undefined,
53
37
  limit: 20,
@@ -1 +1 @@
1
- {"version":3,"file":"price-breakdown-section.d.ts","sourceRoot":"","sources":["../../src/components/price-breakdown-section.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,4EAA4E;IAC5E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,uEAAuE;IACvE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF;AAsCD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,QAAQ,EACR,cAAc,EACd,SAAS,EACT,MAAM,GACP,EAAE,0BAA0B,kDAsK5B"}
1
+ {"version":3,"file":"price-breakdown-section.d.ts","sourceRoot":"","sources":["../../src/components/price-breakdown-section.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,4EAA4E;IAC5E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,uEAAuE;IACvE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,QAAQ,EACR,cAAc,EACd,SAAS,EACT,MAAM,GACP,EAAE,0BAA0B,kDAkL5B"}
@@ -3,20 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { usePricingPreview } from "@voyantjs/bookings-react";
4
4
  import { Label } from "@voyantjs/ui/components";
5
5
  import * as React from "react";
6
- const DEFAULT_LABELS = {
7
- heading: "Price breakdown",
8
- total: "Total",
9
- onRequest: "On request",
10
- groupRate: "group rate",
11
- empty: "Pick units above to see the breakdown.",
12
- noPricing: "No pricing catalog available for this product.",
13
- };
14
- function formatCents(cents, currency) {
15
- if (cents === null)
16
- return "";
17
- const major = (cents / 100).toFixed(2);
18
- return currency ? `${major} ${currency}` : major;
19
- }
6
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
20
7
  /**
21
8
  * Picks the tier whose quantity range contains `qty`. Tiers are expected
22
9
  * oldest-to-newest, `minQuantity`-ascending. Ties are broken by first-match —
@@ -25,7 +12,9 @@ function formatCents(cents, currency) {
25
12
  */
26
13
  function matchTier(tiers, qty) {
27
14
  for (const tier of tiers) {
28
- if (qty >= tier.minQuantity && (tier.maxQuantity === null || qty <= tier.maxQuantity)) {
15
+ if (qty >= tier.minQuantity &&
16
+ (tier.maxQuantity === null || qty <= tier.maxQuantity) // i18n-literal-ok numeric bounds
17
+ ) {
29
18
  return tier;
30
19
  }
31
20
  }
@@ -44,7 +33,9 @@ function matchTier(tiers, qty) {
44
33
  * - `on_request` / anything else — render "On request"; total excludes it.
45
34
  */
46
35
  export function PriceBreakdownSection({ productId, optionId, unitQuantities, catalogId, labels, }) {
47
- const merged = { ...DEFAULT_LABELS, ...labels };
36
+ const { formatCurrency, formatNumber } = useBookingsUiI18nOrDefault();
37
+ const messages = useBookingsUiMessagesOrDefault();
38
+ const merged = { ...messages.priceBreakdownSection.labels, ...labels };
48
39
  const preview = usePricingPreview({
49
40
  productId: productId ?? "",
50
41
  optionId: optionId ?? null,
@@ -53,6 +44,12 @@ export function PriceBreakdownSection({ productId, optionId, unitQuantities, cat
53
44
  });
54
45
  const snapshot = preview.data?.data;
55
46
  const currency = snapshot?.catalog.currencyCode ?? null;
47
+ const formatAmount = React.useCallback((cents) => currency
48
+ ? formatCurrency(cents / 100, currency)
49
+ : formatNumber(cents / 100, {
50
+ minimumFractionDigits: 2,
51
+ maximumFractionDigits: 2,
52
+ }), [currency, formatCurrency, formatNumber]);
56
53
  const { lines, total } = React.useMemo(() => {
57
54
  const out = [];
58
55
  let runningTotal = 0;
@@ -159,7 +156,7 @@ export function PriceBreakdownSection({ productId, optionId, unitQuantities, cat
159
156
  if (lines.length === 0) {
160
157
  return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsx(Label, { children: merged.heading }), _jsx("p", { className: "text-xs text-muted-foreground", children: merged.empty })] }));
161
158
  }
162
- return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsx(Label, { children: merged.heading }), _jsx("div", { className: "flex flex-col gap-1.5", children: lines.map((line) => (_jsxs("div", { className: "flex items-baseline justify-between text-sm", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsxs("span", { className: "tabular-nums", children: [line.quantity, "\u00D7"] }), _jsx("span", { children: line.label }), line.tierLabel ? (_jsxs("span", { className: "text-xs text-muted-foreground", children: ["\u00B7 ", line.tierLabel] })) : null] }), _jsx("div", { className: "tabular-nums", children: line.totalAmountCents === null
159
+ return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsx(Label, { children: merged.heading }), _jsx("div", { className: "flex flex-col gap-1.5", children: lines.map((line) => (_jsxs("div", { className: "flex items-baseline justify-between text-sm", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsxs("span", { className: "tabular-nums", children: [formatNumber(line.quantity), "x"] }), _jsx("span", { children: line.label }), line.tierLabel ? (_jsxs("span", { className: "text-xs text-muted-foreground", children: ["\u00B7 ", line.tierLabel] })) : null] }), _jsx("div", { className: "tabular-nums", children: line.totalAmountCents === null
163
160
  ? merged.onRequest
164
- : formatCents(line.totalAmountCents, currency) })] }, line.unitId))) }), _jsxs("div", { className: "mt-1 flex items-baseline justify-between border-t pt-2 text-sm font-medium", children: [_jsx("span", { children: merged.total }), _jsx("span", { className: "tabular-nums", children: total === null ? merged.onRequest : formatCents(total, currency) })] })] }));
161
+ : formatAmount(line.totalAmountCents) })] }, line.unitId))) }), _jsxs("div", { className: "mt-1 flex items-baseline justify-between border-t pt-2 text-sm font-medium", children: [_jsx("span", { children: merged.total }), _jsx("span", { className: "tabular-nums", children: total === null ? merged.onRequest : formatAmount(total) })] })] }));
165
162
  }
@@ -1 +1 @@
1
- {"version":3,"file":"product-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/product-picker-section.tsx"],"names":[],"mappings":"AAgBA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qGAAqG;IACrG,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAUD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,QAAQ,EACR,OAAc,EACd,WAAmB,EACnB,MAAM,GACP,EAAE,yBAAyB,2CAiF3B"}
1
+ {"version":3,"file":"product-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/product-picker-section.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qGAAqG;IACrG,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,QAAQ,EACR,OAAc,EACd,WAAmB,EACnB,MAAM,GACP,EAAE,yBAAyB,2CAkF3B"}
@@ -3,14 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useProductOptions, useProducts } from "@voyantjs/products-react";
4
4
  import { Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components";
5
5
  import * as React from "react";
6
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
6
7
  const OPTION_NONE = "__none__";
7
- const DEFAULT_LABELS = {
8
- product: "Product",
9
- productSearchPlaceholder: "Search products...",
10
- productSelectPlaceholder: "Select a product...",
11
- option: "Option",
12
- optionNone: "No specific option",
13
- };
14
8
  /**
15
9
  * Controlled product + option picker. Splits `value` + `onChange` so apps can
16
10
  * replace the whole section (e.g., with a typeahead against a custom catalog)
@@ -18,7 +12,8 @@ const DEFAULT_LABELS = {
18
12
  */
19
13
  export function ProductPickerSection({ value, onChange, enabled = true, lockProduct = false, labels, }) {
20
14
  const [productSearch, setProductSearch] = React.useState("");
21
- const merged = { ...DEFAULT_LABELS, ...labels };
15
+ const messages = useBookingsUiMessagesOrDefault();
16
+ const merged = { ...messages.productPickerSection.labels, ...labels };
22
17
  const { data: productsData } = useProducts({
23
18
  search: productSearch || undefined,
24
19
  limit: 20,
@@ -1 +1 @@
1
- {"version":3,"file":"rooms-stepper-section.d.ts","sourceRoot":"","sources":["../../src/components/rooms-stepper-section.tsx"],"names":[],"mappings":"AAMA,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC;AAED,eAAO,MAAM,sBAAsB,EAAE,iBAAsC,CAAA;AAE3E,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,iBAAiB,CAAA;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAC5C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF;AAUD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,OAAc,EACd,MAAM,GACP,EAAE,wBAAwB,2CAmF1B"}
1
+ {"version":3,"file":"rooms-stepper-section.d.ts","sourceRoot":"","sources":["../../src/components/rooms-stepper-section.tsx"],"names":[],"mappings":"AAOA,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC;AAED,eAAO,MAAM,sBAAsB,EAAE,iBAAsC,CAAA;AAE3E,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,iBAAiB,CAAA;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAC5C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,OAAc,EACd,MAAM,GACP,EAAE,wBAAwB,2CAoF1B"}
@@ -3,14 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useSlotUnitAvailability } from "@voyantjs/availability-react";
4
4
  import { Button, Label } from "@voyantjs/ui/components";
5
5
  import { Minus, Plus } from "lucide-react";
6
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
6
7
  export const emptyRoomsStepperValue = { quantities: {} };
7
- const DEFAULT_LABELS = {
8
- heading: "Rooms",
9
- noSlot: "Pick a departure first to see available rooms.",
10
- noUnits: "This departure has no per-unit availability configured.",
11
- remaining: "left",
12
- unlimited: "unlimited",
13
- };
14
8
  /**
15
9
  * Rooms / per-unit stepper for booking-create flows. Drives
16
10
  * `GET /v1/availability/slots/:id/unit-availability` from #235 so the
@@ -32,7 +26,8 @@ const DEFAULT_LABELS = {
32
26
  * would 409 at insert time.
33
27
  */
34
28
  export function RoomsStepperSection({ value, onChange, slotId, enabled = true, labels, }) {
35
- const merged = { ...DEFAULT_LABELS, ...labels };
29
+ const messages = useBookingsUiMessagesOrDefault();
30
+ const merged = { ...messages.roomsStepperSection.labels, ...labels };
36
31
  const availability = useSlotUnitAvailability({ slotId, enabled: enabled && Boolean(slotId) });
37
32
  const units = availability.data?.data ?? [];
38
33
  if (!slotId) {
@@ -55,6 +50,6 @@ export function RoomsStepperSection({ value, onChange, slotId, enabled = true, l
55
50
  const qty = value.quantities[unit.optionUnitId] ?? 0;
56
51
  const remainingLabel = unit.remaining === null ? merged.unlimited : `${unit.remaining} ${merged.remaining}`;
57
52
  const atMax = unit.remaining !== null && qty >= unit.remaining;
58
- return (_jsxs("div", { className: "flex items-center gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-sm font-medium", children: unit.unitName }), _jsx("div", { className: "text-xs text-muted-foreground", children: remainingLabel })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => setQuantity(unit.optionUnitId, Math.max(0, qty - 1)), disabled: qty <= 0, "aria-label": `Decrease ${unit.unitName}`, children: _jsx(Minus, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "min-w-[1.5rem] text-center text-sm tabular-nums", children: qty }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => setQuantity(unit.optionUnitId, qty + 1), disabled: atMax, "aria-label": `Increase ${unit.unitName}`, children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })] })] }, unit.optionUnitId));
53
+ return (_jsxs("div", { className: "flex items-center gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-sm font-medium", children: unit.unitName }), _jsx("div", { className: "text-xs text-muted-foreground", children: remainingLabel })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => setQuantity(unit.optionUnitId, Math.max(0, qty - 1)), disabled: qty <= 0, "aria-label": `${merged.decreaseUnitPrefix} ${unit.unitName}`, children: _jsx(Minus, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "min-w-[1.5rem] text-center text-sm tabular-nums", children: qty }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => setQuantity(unit.optionUnitId, qty + 1), disabled: atMax, "aria-label": `${merged.increaseUnitPrefix} ${unit.unitName}`, children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })] })] }, unit.optionUnitId));
59
54
  }) })] }));
60
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"shared-room-section.d.ts","sourceRoot":"","sources":["../../src/components/shared-room-section.tsx"],"names":[],"mappings":"AAgBA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE9C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,cAAc,CAAA;IACpB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,oBAAoB,EAAE,eAIlC,CAAA;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,eAAe,CAAA;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAWD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,OAAc,EACd,MAAM,GACP,EAAE,sBAAsB,2CAmFxB"}
1
+ {"version":3,"file":"shared-room-section.d.ts","sourceRoot":"","sources":["../../src/components/shared-room-section.tsx"],"names":[],"mappings":"AAiBA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE9C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,cAAc,CAAA;IACpB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,oBAAoB,EAAE,eAIlC,CAAA;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,eAAe,CAAA;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,OAAc,EACd,MAAM,GACP,EAAE,sBAAsB,2CAoFxB"}
@@ -2,20 +2,13 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useBookingGroups } from "@voyantjs/bookings-react";
4
4
  import { Button, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components";
5
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
5
6
  const GROUP_NONE = "__none__";
6
7
  export const emptySharedRoomValue = {
7
8
  enabled: false,
8
9
  mode: "create",
9
10
  groupId: "",
10
11
  };
11
- const DEFAULT_LABELS = {
12
- toggle: "Link to a shared-room group",
13
- createMode: "Create new group",
14
- joinMode: "Join existing",
15
- selectPlaceholder: "Select a group...",
16
- noGroups: "No existing groups for this product",
17
- createHint: "A new group will be created with this booking as the primary member.",
18
- };
19
12
  /**
20
13
  * Shared-room (partaj) attachment section. Operators use it to either create a
21
14
  * new `booking_groups` row at booking-create time or join an existing group
@@ -26,7 +19,8 @@ const DEFAULT_LABELS = {
26
19
  * insert (we need the new booking id to attach).
27
20
  */
28
21
  export function SharedRoomSection({ value, onChange, productId, enabled = true, labels, }) {
29
- const merged = { ...DEFAULT_LABELS, ...labels };
22
+ const messages = useBookingsUiMessagesOrDefault();
23
+ const merged = { ...messages.sharedRoomSection.labels, ...labels };
30
24
  const { data: groupsData } = useBookingGroups({
31
25
  productId: productId || undefined,
32
26
  limit: 50,
@@ -1 +1 @@
1
- {"version":3,"file":"status-change-dialog.d.ts","sourceRoot":"","sources":["../../src/components/status-change-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;AA+BjC,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;IACtC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,aAAa,EACb,SAAS,GACV,EAAE,uBAAuB,2CAiFzB"}
1
+ {"version":3,"file":"status-change-dialog.d.ts","sourceRoot":"","sources":["../../src/components/status-change-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;AAgCjC,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;IACtC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,aAAa,EACb,SAAS,GACV,EAAE,uBAAuB,2CA+FzB"}
@@ -7,12 +7,14 @@ import { Loader2 } from "lucide-react";
7
7
  import { useEffect } from "react";
8
8
  import { useForm } from "react-hook-form";
9
9
  import { z } from "zod/v4";
10
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
10
11
  const statusChangeFormSchema = z.object({
11
12
  status: bookingStatusSchema,
12
13
  note: z.string().optional().nullable(),
13
14
  });
14
15
  export function StatusChangeDialog({ open, onOpenChange, bookingId, currentStatus, onSuccess, }) {
15
16
  const mutation = useBookingStatusMutation(bookingId);
17
+ const messages = useBookingsUiMessagesOrDefault();
16
18
  const form = useForm({
17
19
  resolver: zodResolver(statusChangeFormSchema),
18
20
  defaultValues: {
@@ -37,5 +39,8 @@ export function StatusChangeDialog({ open, onOpenChange, bookingId, currentStatu
37
39
  onOpenChange(false);
38
40
  onSuccess?.();
39
41
  };
40
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Change Booking Status" }) }), _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: "New Status" }), _jsxs(Select, { value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), items: bookingStatusOptions, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: bookingStatusOptions.map((status) => (_jsx(SelectItem, { value: status.value, children: status.label }, status.value))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Note (optional)" }), _jsx(Textarea, { ...form.register("note"), placeholder: "Reason for status change..." })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", size: "sm", disabled: mutation.isPending, children: [mutation.isPending && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Update Status"] })] })] })] }) }));
42
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: messages.statusChangeDialog.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: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.statusChangeDialog.fields.status }), _jsxs(Select, { value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), items: bookingStatusOptions.map((status) => ({
43
+ ...status,
44
+ label: messages.common.bookingStatusLabels[status.value],
45
+ })), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: bookingStatusOptions.map((status) => (_jsx(SelectItem, { value: status.value, children: messages.common.bookingStatusLabels[status.value] }, status.value))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.statusChangeDialog.fields.note }), _jsx(Textarea, { ...form.register("note"), placeholder: messages.statusChangeDialog.placeholders.note })] })] }), _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: mutation.isPending, children: [mutation.isPending && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), messages.statusChangeDialog.actions.updateStatus] })] })] })] }) }));
41
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"supplier-status-dialog.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,2BAA2B,EAEjC,MAAM,0BAA0B,CAAA;AAqCjC,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,2BAA2B,CAAA;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AASD,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,cAAc,EACd,SAAS,GACV,EAAE,yBAAyB,2CAmJ3B"}
1
+ {"version":3,"file":"supplier-status-dialog.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,2BAA2B,EAEjC,MAAM,0BAA0B,CAAA;AA6CjC,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,2BAA2B,CAAA;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAUD,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,cAAc,EACd,SAAS,GACV,EAAE,yBAAyB,2CAoK3B"}