@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
@@ -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"}
@@ -8,30 +8,39 @@ import { Loader2 } from "lucide-react";
8
8
  import { useEffect } from "react";
9
9
  import { useForm } from "react-hook-form";
10
10
  import { z } from "zod/v4";
11
- const supplierStatusFormSchema = z.object({
12
- serviceName: z.string().min(1, "Service name is required"),
13
- status: z.enum(["pending", "confirmed", "rejected", "cancelled"]),
14
- supplierReference: z.string().optional().nullable(),
15
- costCurrency: z.string().min(3).max(3, "Use 3-letter ISO code"),
16
- costAmountCents: z.coerce.number().int().min(0),
17
- notes: z.string().optional().nullable(),
18
- });
11
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
12
+ function createSupplierStatusFormSchema(messages) {
13
+ return z.object({
14
+ serviceName: z.string().min(1, messages.supplierStatusDialog.validation.serviceNameRequired),
15
+ status: z.enum(["pending", "confirmed", "rejected", "cancelled"]),
16
+ supplierReference: z.string().optional().nullable(),
17
+ costCurrency: z
18
+ .string()
19
+ .min(3)
20
+ .max(3, messages.supplierStatusDialog.validation.costCurrencyInvalid),
21
+ costAmountCents: z.coerce.number().int().min(0),
22
+ notes: z.string().optional().nullable(),
23
+ });
24
+ }
19
25
  const CONFIRMATION_STATUSES = [
20
- { value: "pending", label: "Pending" },
21
- { value: "confirmed", label: "Confirmed" },
22
- { value: "rejected", label: "Rejected" },
23
- { value: "cancelled", label: "Cancelled" },
26
+ { value: "pending" },
27
+ { value: "confirmed" },
28
+ { value: "rejected" },
29
+ { value: "cancelled" },
24
30
  ];
31
+ const DEFAULT_CURRENCY = "EUR"; // i18n-literal-ok ISO default currency
25
32
  export function SupplierStatusDialog({ open, onOpenChange, bookingId, supplierStatus, onSuccess, }) {
26
33
  const isEditing = Boolean(supplierStatus);
27
34
  const { create, update } = useSupplierStatusMutation(bookingId);
35
+ const messages = useBookingsUiMessagesOrDefault();
36
+ const supplierStatusFormSchema = createSupplierStatusFormSchema(messages);
28
37
  const form = useForm({
29
38
  resolver: zodResolver(supplierStatusFormSchema),
30
39
  defaultValues: {
31
40
  serviceName: "",
32
41
  status: "pending",
33
42
  supplierReference: "",
34
- costCurrency: "EUR",
43
+ costCurrency: DEFAULT_CURRENCY,
35
44
  costAmountCents: 0,
36
45
  notes: "",
37
46
  },
@@ -70,8 +79,15 @@ export function SupplierStatusDialog({ open, onOpenChange, bookingId, supplierSt
70
79
  onSuccess?.();
71
80
  };
72
81
  const isSubmitting = create.isPending || update.isPending;
73
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Update Supplier Status" : "Add Supplier 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: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Service Name" }), _jsx(Input, { ...form.register("serviceName"), placeholder: "Hotel Dubrovnik Palace", disabled: isEditing }), form.formState.errors.serviceName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.serviceName.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Status" }), _jsxs(Select, { value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), items: CONFIRMATION_STATUSES, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: CONFIRMATION_STATUSES.map((status) => (_jsx(SelectItem, { value: status.value, children: status.label }, status.value))) })] })] })] }), _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", {
82
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
83
+ ? messages.supplierStatusDialog.titles.edit
84
+ : messages.supplierStatusDialog.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.supplierStatusDialog.fields.serviceName }), _jsx(Input, { ...form.register("serviceName"), placeholder: messages.supplierStatusDialog.placeholders.serviceName, disabled: isEditing }), form.formState.errors.serviceName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.serviceName.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.status }), _jsxs(Select, { value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), items: CONFIRMATION_STATUSES.map((status) => ({
85
+ ...status,
86
+ label: messages.common.supplierStatusLabels[status.value],
87
+ })), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: CONFIRMATION_STATUSES.map((status) => (_jsx(SelectItem, { value: status.value, children: messages.common.supplierStatusLabels[status.value] }, status.value))) })] })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.costCurrency }), _jsx(CurrencyCombobox, { value: form.watch("costCurrency") || null, onChange: (next) => form.setValue("costCurrency", next ?? DEFAULT_CURRENCY, {
74
88
  shouldValidate: true,
75
89
  shouldDirty: true,
76
- }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Cost Amount (cents)" }), _jsx(Input, { ...form.register("costAmountCents", { valueAsNumber: true }), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Supplier Reference" }), _jsx(Input, { ...form.register("supplierReference"), placeholder: "CONF-12345" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Notes" }), _jsx(Textarea, { ...form.register("notes"), placeholder: "Additional 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"] })] })] })] }) }));
90
+ }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.costAmountCents }), _jsx(Input, { ...form.register("costAmountCents", { valueAsNumber: true }), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.supplierReference }), _jsx(Input, { ...form.register("supplierReference"), placeholder: messages.supplierStatusDialog.placeholders.supplierReference })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.notes }), _jsx(Textarea, { ...form.register("notes"), placeholder: messages.supplierStatusDialog.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
91
+ ? messages.common.saveChanges
92
+ : messages.supplierStatusDialog.actions.addSupplierStatus] })] })] })] }) }));
77
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"supplier-status-list.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-list.tsx"],"names":[],"mappings":"AASA,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAA;CAClB;AASD,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,EAAE,EAAE,uBAAuB,2CA+FxE"}
1
+ {"version":3,"file":"supplier-status-list.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-list.tsx"],"names":[],"mappings":"AAUA,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAA;CAClB;AASD,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,EAAE,EAAE,uBAAuB,2CA+GxE"}
@@ -4,6 +4,7 @@ import { useSupplierStatuses } from "@voyantjs/bookings-react";
4
4
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
5
  import { Pencil, Plus } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  import { SupplierStatusDialog } from "./supplier-status-dialog";
8
9
  const supplierStatusVariant = {
9
10
  pending: "outline",
@@ -15,11 +16,18 @@ export function SupplierStatusList({ bookingId }) {
15
16
  const [dialogOpen, setDialogOpen] = React.useState(false);
16
17
  const [editing, setEditing] = React.useState(undefined);
17
18
  const { data } = useSupplierStatuses(bookingId);
19
+ const { formatCurrency, formatDate } = useBookingsUiI18nOrDefault();
20
+ const messages = useBookingsUiMessagesOrDefault();
18
21
  const statuses = data?.data ?? [];
19
- return (_jsxs(Card, { "data-slot": "supplier-status-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { children: "Supplier Confirmations" }), _jsxs(Button, { size: "sm", onClick: () => {
22
+ return (_jsxs(Card, { "data-slot": "supplier-status-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { children: messages.supplierStatusList.title }), _jsxs(Button, { size: "sm", onClick: () => {
20
23
  setEditing(undefined);
21
24
  setDialogOpen(true);
22
- }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Add Supplier"] })] }), _jsx(CardContent, { children: statuses.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "No supplier statuses 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: "Service" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Status" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Cost" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Reference" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Confirmed" }), _jsx("th", { className: "w-12 p-2" })] }) }), _jsx("tbody", { children: statuses.map((status) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: status.serviceName }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: supplierStatusVariant[status.status] ?? "secondary", className: "capitalize text-xs", children: status.status }) }), _jsxs("td", { className: "p-2 font-mono", children: [(status.costAmountCents / 100).toFixed(2), " ", status.costCurrency] }), _jsx("td", { className: "p-2", children: status.supplierReference ?? "-" }), _jsx("td", { className: "p-2", children: status.confirmedAt ? new Date(status.confirmedAt).toLocaleDateString() : "-" }), _jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => {
25
+ }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.supplierStatusList.addSupplier] })] }), _jsx(CardContent, { children: statuses.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.supplierStatusList.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.supplierStatusList.columns.service }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.status }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.cost }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.reference }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.confirmed }), _jsx("th", { className: "w-12 p-2" })] }) }), _jsx("tbody", { children: statuses.map((status) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: status.serviceName }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: supplierStatusVariant[status.status] ?? "secondary", children: messages.common.supplierStatusLabels[status.status] }) }), _jsx("td", { className: "p-2 font-mono", children: status.costAmountCents == null || !status.costCurrency
26
+ ? messages.supplierStatusList.values.costUnavailable
27
+ : formatCurrency(status.costAmountCents / 100, status.costCurrency) }), _jsx("td", { className: "p-2", children: status.supplierReference ??
28
+ messages.supplierStatusList.values.referenceUnavailable }), _jsx("td", { className: "p-2", children: status.confirmedAt
29
+ ? formatDate(status.confirmedAt)
30
+ : messages.supplierStatusList.values.confirmedUnavailable }), _jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => {
23
31
  setEditing(status);
24
32
  setDialogOpen(true);
25
33
  }, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }) })] }, status.id))) })] }) })) }), _jsx(SupplierStatusDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
@@ -1 +1 @@
1
- {"version":3,"file":"traveler-dialog.d.ts","sourceRoot":"","sources":["../../src/components/traveler-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAAuB,MAAM,0BAA0B,CAAA;AA8B1F,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,GACV,EAAE,mBAAmB,2CAmHrB"}
1
+ {"version":3,"file":"traveler-dialog.d.ts","sourceRoot":"","sources":["../../src/components/traveler-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAAuB,MAAM,0BAA0B,CAAA;AAkC1F,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,GACV,EAAE,mBAAmB,2CAwIrB"}
@@ -7,16 +7,21 @@ 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
- const travelerFormSchema = z.object({
11
- firstName: z.string().min(1, "First name is required"),
12
- lastName: z.string().min(1, "Last name is required"),
13
- email: z.string().email().optional().or(z.literal("")).nullable(),
14
- phone: z.string().optional().nullable(),
15
- specialRequests: z.string().optional().nullable(),
16
- });
10
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
11
+ function createTravelerFormSchema(messages) {
12
+ return z.object({
13
+ firstName: z.string().min(1, messages.travelerDialog.validation.firstNameRequired),
14
+ lastName: z.string().min(1, messages.travelerDialog.validation.lastNameRequired),
15
+ email: z.string().email().optional().or(z.literal("")).nullable(),
16
+ phone: z.string().optional().nullable(),
17
+ specialRequests: z.string().optional().nullable(),
18
+ });
19
+ }
17
20
  export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSuccess, }) {
18
21
  const isEditing = Boolean(traveler);
19
22
  const { create, update } = useTravelerMutation(bookingId);
23
+ const messages = useBookingsUiMessagesOrDefault();
24
+ const travelerFormSchema = createTravelerFormSchema(messages);
20
25
  const form = useForm({
21
26
  resolver: zodResolver(travelerFormSchema),
22
27
  defaultValues: {
@@ -60,5 +65,9 @@ export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSucc
60
65
  onSuccess?.();
61
66
  };
62
67
  const isSubmitting = create.isPending || update.isPending;
63
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Traveler" : "Add Traveler" }) }), _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: "First Name" }), _jsx(Input, { ...form.register("firstName"), placeholder: "John" }), form.formState.errors.firstName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.firstName.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Last Name" }), _jsx(Input, { ...form.register("lastName"), placeholder: "Smith" }), form.formState.errors.lastName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.lastName.message }))] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Email" }), _jsx(Input, { ...form.register("email"), type: "email", placeholder: "john@example.com" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Phone" }), _jsx(Input, { ...form.register("phone"), placeholder: "+44 7911 123456" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Special Requests" }), _jsx(Textarea, { ...form.register("specialRequests"), placeholder: "Any special requests..." })] })] }), _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 Traveler"] })] })] })] }) }));
68
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
69
+ ? messages.travelerDialog.titles.edit
70
+ : messages.travelerDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-1 flex-col overflow-hidden", children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.firstName }), _jsx(Input, { ...form.register("firstName"), placeholder: messages.travelerDialog.placeholders.firstName }), form.formState.errors.firstName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.firstName.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.lastName }), _jsx(Input, { ...form.register("lastName"), placeholder: messages.travelerDialog.placeholders.lastName }), form.formState.errors.lastName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.lastName.message }))] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.email }), _jsx(Input, { ...form.register("email"), type: "email", placeholder: messages.travelerDialog.placeholders.email })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.phone }), _jsx(Input, { ...form.register("phone"), placeholder: messages.travelerDialog.placeholders.phone })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.specialRequests }), _jsx(Textarea, { ...form.register("specialRequests"), placeholder: messages.travelerDialog.placeholders.specialRequests })] })] }), _jsxs(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
71
+ ? messages.common.saveChanges
72
+ : messages.travelerDialog.actions.addTraveler] })] })] })] }) }));
64
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"traveler-list.d.ts","sourceRoot":"","sources":["../../src/components/traveler-list.tsx"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CAiG5D"}
1
+ {"version":3,"file":"traveler-list.d.ts","sourceRoot":"","sources":["../../src/components/traveler-list.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CA8G5D"}
@@ -4,21 +4,23 @@ import { useTravelerMutation, useTravelers, } from "@voyantjs/bookings-react";
4
4
  import { Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
5
  import { Pencil, Plus, Trash2, Users } from "lucide-react";
6
6
  import * as React from "react";
7
+ import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
7
8
  import { TravelerDialog } from "./traveler-dialog";
8
9
  export function TravelerList({ bookingId }) {
9
10
  const [dialogOpen, setDialogOpen] = React.useState(false);
10
11
  const [editing, setEditing] = React.useState(undefined);
11
12
  const { data } = useTravelers(bookingId);
12
13
  const { remove } = useTravelerMutation(bookingId);
14
+ const messages = useBookingsUiMessagesOrDefault();
13
15
  const travelers = data?.data ?? [];
14
- return (_jsxs(Card, { "data-slot": "traveler-list", 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" }), "Travelers"] }), _jsxs(Button, { size: "sm", onClick: () => {
16
+ return (_jsxs(Card, { "data-slot": "traveler-list", 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.travelerList.title] }), _jsxs(Button, { size: "sm", onClick: () => {
15
17
  setEditing(undefined);
16
18
  setDialogOpen(true);
17
- }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Add Traveler"] })] }), _jsx(CardContent, { children: travelers.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "No travelers 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: "Name" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Email" }), _jsx("th", { className: "p-2 text-left font-medium", children: "Phone" }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: travelers.map((traveler) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsxs("td", { className: "p-2", children: [traveler.firstName, " ", traveler.lastName] }), _jsx("td", { className: "p-2", children: traveler.email ?? "-" }), _jsx("td", { className: "p-2", children: traveler.phone ?? "-" }), _jsx("td", { className: "p-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
19
+ }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.travelerList.addTraveler] })] }), _jsx(CardContent, { children: travelers.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.travelerList.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.travelerList.columns.name }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.travelerList.columns.email }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.travelerList.columns.phone }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: travelers.map((traveler) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsxs("td", { className: "p-2", children: [traveler.firstName, " ", traveler.lastName] }), _jsx("td", { className: "p-2", children: traveler.email ?? messages.travelerList.values.emailUnavailable }), _jsx("td", { className: "p-2", children: traveler.phone ?? messages.travelerList.values.phoneUnavailable }), _jsx("td", { className: "p-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
18
20
  setEditing(traveler);
19
21
  setDialogOpen(true);
20
22
  }, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => {
21
- if (confirm("Delete this traveler?")) {
23
+ if (confirm(messages.travelerList.actions.deleteConfirm)) {
22
24
  remove.mutate(traveler.id);
23
25
  }
24
26
  }, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }, traveler.id))) })] }) })) }), _jsx(TravelerDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
@@ -1 +1 @@
1
- {"version":3,"file":"voucher-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/voucher-picker-section.tsx"],"names":[],"mappings":"AAMA,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IAC5B,mFAAmF;IACnF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAIrC,CAAA;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;CACF;AA0BD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,MAAM,GACP,EAAE,yBAAyB,2CAuG3B"}
1
+ {"version":3,"file":"voucher-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/voucher-picker-section.tsx"],"names":[],"mappings":"AAOA,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IAC5B,mFAAmF;IACnF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAIrC,CAAA;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;CACF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,MAAM,GACP,EAAE,yBAAyB,2CAiH3B"}
@@ -3,33 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { usePublicVoucherValidationMutation } from "@voyantjs/finance-react";
4
4
  import { Button, Input, Label } from "@voyantjs/ui/components";
5
5
  import { CheckCircle2, Loader2, XCircle } from "lucide-react";
6
+ import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
6
7
  export const emptyVoucherPickerValue = {
7
8
  code: "",
8
9
  picked: null,
9
10
  error: null,
10
11
  };
11
- const DEFAULT_LABELS = {
12
- heading: "Voucher (optional)",
13
- codePlaceholder: "Enter voucher code...",
14
- apply: "Apply",
15
- clear: "Clear",
16
- remainingLabel: "Remaining balance:",
17
- invalidLabel: "This voucher can't be applied:",
18
- };
19
- const REASON_MESSAGES = {
20
- not_found: "Voucher code not found.",
21
- inactive: "Voucher is not active.",
22
- not_started: "Voucher is not yet valid.",
23
- expired: "Voucher has expired.",
24
- booking_mismatch: "Voucher is assigned to a different booking.",
25
- currency_mismatch: "Voucher currency does not match the booking.",
26
- insufficient_balance: "Voucher balance is too low for the selected amount.",
27
- };
28
- function formatAmount(cents, currency) {
29
- if (cents == null)
30
- return "—";
31
- return `${(cents / 100).toFixed(2)}${currency ? ` ${currency}` : ""}`;
32
- }
33
12
  /**
34
13
  * Voucher picker for booking-create flows. Operator enters a code, clicks
35
14
  * Apply, and the server-side `/v1/public/vouchers/validate` runs all the
@@ -42,7 +21,9 @@ function formatAmount(cents, currency) {
42
21
  * again without leaving a trail.
43
22
  */
44
23
  export function VoucherPickerSection({ value, onChange, bookingId, currency, amountCents, labels, }) {
45
- const merged = { ...DEFAULT_LABELS, ...labels };
24
+ const { formatCurrency } = useBookingsUiI18nOrDefault();
25
+ const messages = useBookingsUiMessagesOrDefault();
26
+ const merged = { ...messages.voucherPickerSection.labels, ...labels };
46
27
  const validate = usePublicVoucherValidationMutation();
47
28
  const handleApply = async () => {
48
29
  const code = value.code.trim();
@@ -73,14 +54,16 @@ export function VoucherPickerSection({ value, onChange, bookingId, currency, amo
73
54
  onChange({
74
55
  code,
75
56
  picked: null,
76
- error: REASON_MESSAGES[data.reason ?? ""] ?? "Voucher is not valid.",
57
+ error: messages.voucherPickerSection.reasonMessages[data.reason] ?? messages.voucherPickerSection.validation.invalid,
77
58
  });
78
59
  }
79
60
  catch (err) {
80
61
  onChange({
81
62
  code,
82
63
  picked: null,
83
- error: err instanceof Error ? err.message : "Voucher lookup failed.",
64
+ error: err instanceof Error
65
+ ? err.message
66
+ : messages.voucherPickerSection.validation.lookupFailed,
84
67
  });
85
68
  }
86
69
  };
@@ -90,5 +73,7 @@ export function VoucherPickerSection({ value, onChange, bookingId, currency, amo
90
73
  e.preventDefault();
91
74
  void handleApply();
92
75
  }
93
- }, placeholder: merged.codePlaceholder, disabled: validate.isPending || Boolean(value.picked) }), value.picked ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleClear, children: merged.clear })) : (_jsxs(Button, { type: "button", size: "sm", onClick: () => void handleApply(), disabled: validate.isPending || !value.code.trim(), children: [validate.isPending && _jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }), merged.apply] }))] }), value.picked && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), _jsxs("span", { children: [merged.remainingLabel, " ", _jsx("strong", { children: formatAmount(value.picked.remainingAmountCents, value.picked.currency) })] })] })), value.error && (_jsxs("div", { className: "flex items-start gap-2 text-sm text-destructive", children: [_jsx(XCircle, { className: "mt-0.5 h-4 w-4" }), _jsxs("span", { children: [merged.invalidLabel, " ", value.error] })] }))] }));
76
+ }, placeholder: merged.codePlaceholder, disabled: validate.isPending || Boolean(value.picked) }), value.picked ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleClear, children: merged.clear })) : (_jsxs(Button, { type: "button", size: "sm", onClick: () => void handleApply(), disabled: validate.isPending || !value.code.trim(), children: [validate.isPending && _jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }), merged.apply] }))] }), value.picked && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), _jsxs("span", { children: [merged.remainingLabel, " ", _jsx("strong", { children: value.picked.remainingAmountCents == null || !value.picked.currency
77
+ ? messages.voucherPickerSection.validation.amountUnavailable
78
+ : formatCurrency(value.picked.remainingAmountCents / 100, value.picked.currency) })] })] })), value.error && (_jsxs("div", { className: "flex items-start gap-2 text-sm text-destructive", children: [_jsx(XCircle, { className: "mt-0.5 h-4 w-4" }), _jsxs("span", { children: [merged.invalidLabel, " ", value.error] })] }))] }));
94
79
  }