@voyantjs/allocation-ui 0.52.0 → 0.52.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"slot-allocation-page.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-page.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,0BAA0B,EAE/B,KAAK,sBAAsB,EAQ5B,MAAM,8BAA8B,CAAA;AAsBrC,OAAO,EAAkB,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAA;AAmBzE,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,sBAAsB,CAAA;IAChC,SAAS,EAAE,0BAA0B,EAAE,CAAA;IACvC,eAAe,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,SAAS,CAAA;IAChB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,MAAM,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,SAAS,CAAA;CAChE;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAA;IAC7E,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,SAAS,CAAA;IAC3E,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,SAAS,CAAA;IACzE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,SAAS,CAAA;IACtE,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,SAAS,CAAA;IACrE,SAAS,CAAC,EAAE,0BAA0B,EAAE,CAAA;IACxC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,SAAS,EACT,MAAM,EACN,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,SAAc,EACd,KAAa,GACd,EAAE,uBAAuB,2CAybzB"}
1
+ {"version":3,"file":"slot-allocation-page.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-page.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,0BAA0B,EAE/B,KAAK,sBAAsB,EAO5B,MAAM,8BAA8B,CAAA;AAuBrC,OAAO,EAAkB,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAA;AAiBzE,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,sBAAsB,CAAA;IAChC,SAAS,EAAE,0BAA0B,EAAE,CAAA;IACvC,eAAe,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,SAAS,CAAA;IAChB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,MAAM,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,SAAS,CAAA;CAChE;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAA;IAC7E,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,SAAS,CAAA;IAC3E,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,SAAS,CAAA;IACzE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,SAAS,CAAA;IACtE,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,SAAS,CAAA;IACrE,SAAS,CAAC,EAAE,0BAA0B,EAAE,CAAA;IACxC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,SAAS,EACT,MAAM,EACN,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,SAAc,EACd,KAAa,GACd,EAAE,uBAAuB,2CAmdzB"}
@@ -1,22 +1,20 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useQuery } from "@tanstack/react-query";
4
- import { getSlotQueryOptions, useAllocationAutomationMutation, useAllocationResourceMutation, useAssignTravelerAllocationMutation, useProductResourceTemplates, useSlotAllocation, useSlotAllocationAuditLog, useVoyantAvailabilityContext, } from "@voyantjs/availability-react";
5
- import { Badge, Button, cn, Input, Label, Tabs, TabsList, TabsTrigger, } from "@voyantjs/ui/components";
6
- import { AlertTriangle, Armchair, ArrowLeft, Bed, Download, Plus, Sparkles, Users, Wand2, } from "lucide-react";
4
+ import { getSlotQueryOptions, useAllocationAutomationMutation, useAllocationResourceMutation, useAssignTravelerAllocationMutation, useProductResourceTemplates, useSlotAllocation, useVoyantAvailabilityContext, } from "@voyantjs/availability-react";
5
+ import { Badge, Button, cn, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsList, TabsTrigger, } from "@voyantjs/ui/components";
6
+ import { AlertTriangle, Armchair, ArrowLeft, Bed, Plus, Sparkles, Users, Wand2 } from "lucide-react";
7
7
  import { useMemo, useState } from "react";
8
8
  import { useAllocationUiMessagesOrDefault } from "../i18n/index.js";
9
- import { buildValidationIssues, collectOccupants, defaultCapacityFor, kindLabel, PARENT_ONLY_KINDS, parentKindFor, ROOM_KIND, summarizeResourceCapacity, VEHICLE_SEAT_KIND, } from "./slot-allocation-model.js";
9
+ import { collectOccupants, defaultCapacityFor, kindLabel, PARENT_ONLY_KINDS, parentKindFor, ROOM_KIND, summarizeResourceCapacity, VEHICLE_SEAT_KIND, } from "./slot-allocation-model.js";
10
10
  import { ResourceColumnsView } from "./slot-allocation-resource-view.js";
11
11
  import { VehicleSeatsView } from "./slot-allocation-seat-view.js";
12
- import { AuditLogCard, ValidationSummary } from "./slot-allocation-shared.js";
13
12
  export function SlotAllocationPage({ slotId, className, onBack, renderExtraActions, renderTravelerActions, renderHeaderEnd, renderBefore, renderAfter, extraTabs = [], embed = false, }) {
14
13
  const messages = useAllocationUiMessagesOrDefault();
15
14
  const availabilityClient = useVoyantAvailabilityContext();
16
15
  const allocation = useSlotAllocation({ slotId });
17
16
  const slotRowQuery = useQuery(getSlotQueryOptions(availabilityClient, slotId));
18
17
  const slotRow = slotRowQuery.data?.data;
19
- const auditLog = useSlotAllocationAuditLog({ slotId });
20
18
  const resourceMutation = useAllocationResourceMutation(slotId);
21
19
  const assignMutation = useAssignTravelerAllocationMutation(slotId);
22
20
  const automationMutation = useAllocationAutomationMutation(slotId);
@@ -24,6 +22,7 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
24
22
  const [addingResource, setAddingResource] = useState(false);
25
23
  const [resourceLabel, setResourceLabel] = useState("");
26
24
  const [resourceCapacity, setResourceCapacity] = useState(2);
25
+ const [resourceOptionId, setResourceOptionId] = useState(null);
27
26
  const [error, setError] = useState(null);
28
27
  const data = allocation.data?.data;
29
28
  const templates = useProductResourceTemplates({
@@ -55,6 +54,15 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
55
54
  }
56
55
  return kinds;
57
56
  }, [data?.resources, templates.data?.data]);
57
+ // option_id → option name, used by ResourceColumnsView to badge each
58
+ // resource row with the option it's tied to (Standard double, etc.).
59
+ const optionNamesById = useMemo(() => {
60
+ const map = new Map();
61
+ for (const option of templates.data?.data ?? []) {
62
+ map.set(option.id, option.name);
63
+ }
64
+ return map;
65
+ }, [templates.data?.data]);
58
66
  const activeKind = allocationKinds.includes(selectedKind)
59
67
  ? selectedKind
60
68
  : (allocationKinds[0] ?? ROOM_KIND);
@@ -66,7 +74,6 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
66
74
  const resources = useMemo(() => (data?.resources ?? []).filter((resource) => resource.kind === activeKind), [data?.resources, activeKind]);
67
75
  const parentResources = useMemo(() => (data?.resources ?? []).filter((resource) => resource.kind === parentKindFor(activeKind)), [data?.resources, activeKind]);
68
76
  const occupants = useMemo(() => collectOccupants(travelers, resources, activeKind), [travelers, resources, activeKind]);
69
- const validationIssues = useMemo(() => buildValidationIssues({ travelers, resources, occupants, kind: activeKind, messages }), [travelers, resources, occupants, activeKind, messages]);
70
77
  const capacitySummary = useMemo(() => summarizeResourceCapacity({
71
78
  resources,
72
79
  slotInitialPax: slotRow?.initialPax ?? null,
@@ -109,9 +116,6 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
109
116
  slotRow?.remainingPax,
110
117
  slotRow?.unlimited,
111
118
  ]);
112
- function downloadExport(kind) {
113
- globalThis.location.assign(`/v1/admin/availability/slots/${encodeURIComponent(slotId)}/allocation/export-${kind}`);
114
- }
115
119
  async function assignTraveler(travelerId, resourceId) {
116
120
  setError(null);
117
121
  try {
@@ -129,9 +133,12 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
129
133
  kind: activeKind,
130
134
  label: resourceLabel.trim() || null,
131
135
  capacity: resourceCapacity,
136
+ refType: resourceOptionId ? "option" : null,
137
+ refId: resourceOptionId,
132
138
  });
133
139
  setResourceLabel("");
134
140
  setResourceCapacity(defaultCapacityFor(activeKind));
141
+ setResourceOptionId(null);
135
142
  setAddingResource(false);
136
143
  }
137
144
  catch (err) {
@@ -190,39 +197,42 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
190
197
  travelers,
191
198
  allocationKinds,
192
199
  };
193
- const summaryLine = (_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm text-muted-foreground", children: [_jsxs("span", { children: [data.summary.travelerCount, " ", messages.travelers] }), selectedExtraTab ? null : (_jsxs("span", { children: [resources.length, " ", kindLabel(activeKind, messages).toLowerCase()] })), selectedExtraTab ? null : (_jsx(CapacitySummaryBadges, { summary: capacitySummary, messages: messages, kind: activeKind }))] }));
194
- const actionsCluster = (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [selectedExtraTab ? null : renderExtraActions?.({ slotId, kind: activeKind }), _jsxs(Button, { variant: "outline", onClick: () => downloadExport("passengers"), children: [_jsx(Download, { "data-icon": "inline-start", "aria-hidden": "true" }), messages.exportPassengers] }), _jsxs(Button, { variant: "outline", onClick: () => downloadExport("rooming-list"), children: [_jsx(Download, { "data-icon": "inline-start", "aria-hidden": "true" }), messages.exportRooming] }), selectedExtraTab ? null : resources.length === 0 ? (_jsxs(Button, { variant: "outline", onClick: () => void generateResources(), disabled: automationMutation.autoMaterialize.isPending, children: [_jsx(Sparkles, { "data-icon": "inline-start", "aria-hidden": "true" }), automationMutation.autoMaterialize.isPending
200
+ const summaryLine = (_jsx("div", { className: "flex flex-wrap items-center gap-2 text-sm text-muted-foreground", children: selectedExtraTab ? null : (_jsx(CapacitySummaryBadges, { summary: capacitySummary, messages: messages, kind: activeKind })) }));
201
+ const actionsCluster = (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [selectedExtraTab ? null : renderExtraActions?.({ slotId, kind: activeKind }), selectedExtraTab ? null : resources.length === 0 ? (_jsxs(Button, { variant: "outline", onClick: () => void generateResources(), disabled: automationMutation.autoMaterialize.isPending, children: [_jsx(Sparkles, { "data-icon": "inline-start", "aria-hidden": "true" }), automationMutation.autoMaterialize.isPending
195
202
  ? messages.generatingResources
196
203
  : messages.generateResources] })) : (_jsxs(Button, { variant: "outline", onClick: () => void autoAllocate(), disabled: automationMutation.autoAllocate.isPending, children: [_jsx(Wand2, { "data-icon": "inline-start", "aria-hidden": "true" }), automationMutation.autoAllocate.isPending
197
204
  ? messages.autoAllocating
198
205
  : messages.autoAllocate] })), !selectedExtraTab && canManuallyAddResource ? (_jsxs(Button, { variant: "outline", onClick: () => {
206
+ setResourceLabel("");
199
207
  setResourceCapacity(defaultCapacityFor(activeKind));
200
- setAddingResource((value) => !value);
208
+ setResourceOptionId(null);
209
+ setError(null);
210
+ setAddingResource(true);
201
211
  }, children: [_jsx(Plus, { "data-icon": "inline-start", "aria-hidden": "true" }), messages.addResource] })) : null] }));
202
- return (_jsxs("div", { className: cn("flex flex-col gap-4", embed ? null : "p-6", className), children: [embed ? (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [summaryLine, actionsCluster] })) : (_jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-start md:justify-between", children: [_jsxs("div", { className: "flex items-start gap-3", children: [onBack ? (_jsx(Button, { variant: "ghost", size: "icon", onClick: onBack, "aria-label": messages.back, children: _jsx(ArrowLeft, { "data-icon": true, "aria-hidden": "true" }) })) : null, _jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: messages.pageTitle }), _jsx("div", { className: "mt-1", children: summaryLine })] })] }), _jsxs("div", { className: "flex flex-col gap-3 md:items-end", children: [renderHeaderEnd?.(context), actionsCluster] })] })), renderBefore?.(context), _jsx(Tabs, { value: activeTabId, onValueChange: setSelectedKind, children: _jsxs(TabsList, { className: "flex h-auto w-fit flex-wrap justify-start", children: [allocationKinds.map((kind) => (_jsxs(TabsTrigger, { value: kind, className: "gap-2", children: [kind === VEHICLE_SEAT_KIND ? (_jsx(Armchair, { className: "size-4", "aria-hidden": "true" })) : (_jsx(Bed, { className: "size-4", "aria-hidden": "true" })), kindLabel(kind, messages)] }, kind))), visibleExtraTabs.map((tab) => (_jsxs(TabsTrigger, { value: tab.id, className: "gap-2", children: [tab.icon, tab.label] }, tab.id)))] }) }), selectedExtraTab ? (selectedExtraTab.render(context)) : (_jsxs(_Fragment, { children: [error ? (_jsx("div", { className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error })) : null, addingResource && canManuallyAddResource ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("form", { className: "grid gap-3 rounded-md border bg-muted/30 p-3 sm:grid-cols-[1fr_8rem_auto_auto]", onSubmit: createResource, children: [_jsxs("div", { className: "grid gap-1", children: [_jsx(Label, { htmlFor: "allocation-resource-label", children: messages.resourceLabel }), _jsx(Input, { id: "allocation-resource-label", value: resourceLabel, onChange: (event) => setResourceLabel(event.target.value), placeholder: activeKind === ROOM_KIND ? "102" : kindLabel(activeKind, messages) })] }), _jsxs("div", { className: "grid gap-1", children: [_jsx(Label, { htmlFor: "allocation-resource-capacity", children: messages.resourceCapacity }), _jsx(Input, { id: "allocation-resource-capacity", type: "number", min: 1, value: resourceCapacity, onChange: (event) => setResourceCapacity(Number(event.target.value) || 1) })] }), _jsx(Button, { type: "submit", className: "self-end", disabled: resourceMutation.create.isPending, children: messages.createResource }), _jsx(Button, { type: "button", variant: "ghost", className: "self-end", onClick: () => setAddingResource(false), children: messages.cancel })] }), projectedSummary?.status === "over" && projectedSummary.delta != null ? (_jsxs("div", { className: "flex items-start gap-2 rounded-md border border-amber-500/40 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:bg-amber-950 dark:text-amber-100", role: "status", children: [_jsx(AlertTriangle, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": "true" }), _jsxs("span", { children: [messages.overCapacityWarning, " ", projectedSummary.resourceCapacity, "/", projectedSummary.slotPax ?? "", " (", messages.resourceCapacityOver, ":", " ", projectedSummary.delta, ")"] })] })) : null] })) : null, _jsx(ValidationSummary, { issues: validationIssues, resources: resources, unallocatedCount: occupants.unallocated.length }), isSeatMap ? (_jsx(VehicleSeatsView, { seats: resources, vehicles: parentResources, occupants: occupants, sharingGroupLabels: data.sharingGroupLabels, onAssignTraveler: (travelerId, resourceId) => void assignTraveler(travelerId, resourceId), onUnassignTraveler: (travelerId) => void assignTraveler(travelerId, null), renderTravelerActions: renderTravelerActions })) : (_jsx(ResourceColumnsView, { kind: activeKind, resources: resources, travelers: travelers, occupants: occupants, sharingGroupLabels: data.sharingGroupLabels, onAssignTraveler: (travelerId, resourceId) => void assignTraveler(travelerId, resourceId), onUnassignTraveler: (travelerId) => void assignTraveler(travelerId, null), onRemoveResource: (resourceId) => void resourceMutation.remove.mutateAsync(resourceId), onEditResource: editResource, renderTravelerActions: renderTravelerActions }))] })), renderAfter?.(context), _jsx(AuditLogCard, { entries: auditLog.data?.data ?? [] })] }));
212
+ return (_jsxs("div", { className: cn("flex flex-col gap-4", embed ? null : "p-6", className), children: [embed ? (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [summaryLine, actionsCluster] })) : (_jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-start md:justify-between", children: [_jsxs("div", { className: "flex items-start gap-3", children: [onBack ? (_jsx(Button, { variant: "ghost", size: "icon", onClick: onBack, "aria-label": messages.back, children: _jsx(ArrowLeft, { "data-icon": true, "aria-hidden": "true" }) })) : null, _jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: messages.pageTitle }), _jsx("div", { className: "mt-1", children: summaryLine })] })] }), _jsxs("div", { className: "flex flex-col gap-3 md:items-end", children: [renderHeaderEnd?.(context), actionsCluster] })] })), renderBefore?.(context), _jsx(Tabs, { value: activeTabId, onValueChange: setSelectedKind, children: _jsxs(TabsList, { className: "flex h-auto w-fit flex-wrap justify-start", children: [allocationKinds.map((kind) => (_jsxs(TabsTrigger, { value: kind, className: "gap-2", children: [kind === VEHICLE_SEAT_KIND ? (_jsx(Armchair, { className: "size-4", "aria-hidden": "true" })) : (_jsx(Bed, { className: "size-4", "aria-hidden": "true" })), kindLabel(kind, messages)] }, kind))), visibleExtraTabs.map((tab) => (_jsxs(TabsTrigger, { value: tab.id, className: "gap-2", children: [tab.icon, tab.label] }, tab.id)))] }) }), selectedExtraTab ? (selectedExtraTab.render(context)) : (_jsxs(_Fragment, { children: [error ? (_jsx("div", { className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error })) : null, canManuallyAddResource ? (_jsx(Dialog, { open: addingResource, onOpenChange: setAddingResource, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: messages.addResource }) }), _jsxs("form", { onSubmit: createResource, children: [_jsxs(DialogBody, { className: "grid gap-4", children: [(templates.data?.data ?? []).length > 0 ? (_jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: "allocation-resource-option", children: messages.resourceOption }), _jsxs(Select, { value: resourceOptionId ?? "__none__", onValueChange: (value) => {
213
+ const next = value === "__none__" ? null : value;
214
+ setResourceOptionId(next);
215
+ // Default capacity from the option's matching template
216
+ // when one exists. Operators can still override.
217
+ if (next) {
218
+ const option = (templates.data?.data ?? []).find((o) => o.id === next);
219
+ const template = option?.templates.find((t) => t.kind === activeKind);
220
+ if (template?.capacity)
221
+ setResourceCapacity(template.capacity);
222
+ }
223
+ }, children: [_jsx(SelectTrigger, { id: "allocation-resource-option", className: "w-full", children: _jsx(SelectValue, { placeholder: messages.resourceOptionPlaceholder, children: (value) => value === "__none__"
224
+ ? messages.resourceOptionNone
225
+ : ((templates.data?.data ?? []).find((option) => option.id === value)?.name ?? value) }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: messages.resourceOptionNone }), (templates.data?.data ?? []).map((option) => (_jsx(SelectItem, { value: option.id, children: option.name }, option.id)))] })] })] })) : null, _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: "allocation-resource-label", children: messages.resourceLabel }), _jsx(Input, { id: "allocation-resource-label", value: resourceLabel, onChange: (event) => setResourceLabel(event.target.value), placeholder: activeKind === ROOM_KIND ? "102" : kindLabel(activeKind, messages), autoFocus: true })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: "allocation-resource-capacity", children: messages.resourceCapacity }), _jsx(Input, { id: "allocation-resource-capacity", type: "number", min: 1, value: resourceCapacity, onChange: (event) => setResourceCapacity(Number(event.target.value) || 1) })] }), projectedSummary?.status === "over" && projectedSummary.delta != null ? (_jsxs("div", { className: "flex items-start gap-2 rounded-md border border-amber-300/60 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:border-amber-500/40 dark:bg-amber-500/10 dark:text-amber-200", role: "status", children: [_jsx(AlertTriangle, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": "true" }), _jsxs("span", { children: [messages.overCapacityWarning, " ", projectedSummary.resourceCapacity, "/", projectedSummary.slotPax ?? "—", " (", messages.resourceCapacityOver, ":", " ", projectedSummary.delta, ")"] })] })) : null] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => setAddingResource(false), children: messages.cancel }), _jsx(Button, { type: "submit", disabled: resourceMutation.create.isPending, children: messages.createResource })] })] })] }) })) : null, isSeatMap ? (_jsx(VehicleSeatsView, { seats: resources, vehicles: parentResources, occupants: occupants, sharingGroupLabels: data.sharingGroupLabels, onAssignTraveler: (travelerId, resourceId) => void assignTraveler(travelerId, resourceId), onUnassignTraveler: (travelerId) => void assignTraveler(travelerId, null), renderTravelerActions: renderTravelerActions })) : (_jsx(ResourceColumnsView, { kind: activeKind, resources: resources, travelers: travelers, occupants: occupants, sharingGroupLabels: data.sharingGroupLabels, optionNamesById: optionNamesById, onAssignTraveler: (travelerId, resourceId) => void assignTraveler(travelerId, resourceId), onUnassignTraveler: (travelerId) => void assignTraveler(travelerId, null), onRemoveResource: (resourceId) => void resourceMutation.remove.mutateAsync(resourceId), onEditResource: editResource, renderTravelerActions: renderTravelerActions }))] })), renderAfter?.(context)] }));
203
226
  }
204
227
  function CapacitySummaryBadges({ summary, messages, kind, }) {
205
228
  if (summary.resourceCount === 0 && summary.slotPax == null)
206
229
  return null;
230
+ // i18n-literal-ok numeric layout with separator
207
231
  const slotLabel = summary.slotPax == null
208
232
  ? messages.slotCapacityUnlimited
209
- : `${summary.slotRemainingPax ?? 0}/${summary.slotPax}`;
233
+ : `${summary.slotRemainingPax ?? 0} of ${summary.slotPax}`;
210
234
  const resourceLabel = summary.slotPax == null
211
235
  ? String(summary.resourceCapacity)
212
- : `${summary.resourceCapacity}/${summary.slotPax}`;
213
- const deltaVariant = summary.status === "over"
214
- ? "destructive"
215
- : summary.status === "exact"
216
- ? "default"
217
- : summary.status === "fits"
218
- ? "secondary"
219
- : "outline";
220
- const deltaLabel = summary.status === "over"
221
- ? `${messages.resourceCapacityOver}: ${summary.delta ?? 0}`
222
- : summary.status === "exact"
223
- ? messages.resourceCapacityExact
224
- : summary.status === "fits"
225
- ? messages.resourceCapacityFits
226
- : null;
227
- return (_jsxs("span", { className: "contents", "data-kind": kind, title: kindLabel(kind, messages), children: [_jsxs(Badge, { variant: "outline", className: "gap-1", children: [_jsx(Users, { className: "size-3", "aria-hidden": "true" }), messages.slotCapacityLabel, ": ", slotLabel] }), _jsxs(Badge, { variant: "outline", className: "gap-1", children: [messages.resourceCapacityLabel, ": ", resourceLabel] }), deltaLabel ? _jsx(Badge, { variant: deltaVariant, children: deltaLabel }) : null] }));
236
+ : `${summary.resourceCapacity} of ${summary.slotPax}`;
237
+ return (_jsxs("span", { className: "contents", "data-kind": kind, title: kindLabel(kind, messages), children: [_jsxs(Badge, { variant: "outline", className: "gap-1", children: [_jsx(Users, { className: "size-3", "aria-hidden": "true" }), messages.slotCapacityLabel, ": ", slotLabel] }), _jsxs(Badge, { variant: "outline", className: "gap-1", children: [messages.resourceCapacityLabel, ": ", resourceLabel] })] }));
228
238
  }
@@ -5,12 +5,19 @@ export interface EditResourceInput {
5
5
  label: string | null;
6
6
  capacity: number;
7
7
  }
8
- export declare function ResourceColumnsView({ kind, resources, travelers, occupants, sharingGroupLabels, onAssignTraveler, onUnassignTraveler, onRemoveResource, onEditResource, renderTravelerActions, }: {
8
+ export declare function ResourceColumnsView({ kind, resources, travelers, occupants, sharingGroupLabels, optionNamesById, onAssignTraveler, onUnassignTraveler, onRemoveResource, onEditResource, renderTravelerActions, }: {
9
9
  kind: string;
10
10
  resources: AllocationResource[];
11
11
  travelers: AllocationManifestTraveler[];
12
12
  occupants: AllocationOccupants;
13
13
  sharingGroupLabels: Record<string, string>;
14
+ /**
15
+ * Map from product_option_id → option name, used to badge each resource
16
+ * row with the option it's tied to (when refType === "option"). Lets
17
+ * operators distinguish "Standard double" rooms from "Junior suite"
18
+ * rooms at a glance. Omit when the host doesn't have option data.
19
+ */
20
+ optionNamesById?: ReadonlyMap<string, string>;
14
21
  /** Assign a single unallocated traveler to a specific resource. */
15
22
  onAssignTraveler: (travelerId: string, resourceId: string) => void;
16
23
  /** Remove a single traveler from their current resource. */
@@ -1 +1 @@
1
- {"version":3,"file":"slot-allocation-resource-view.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-resource-view.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AAuBlG,OAAO,EAAkB,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAA;AAG3E,OAAO,EACL,KAAK,mBAAmB,EAGzB,MAAM,4BAA4B,CAAA;AAGnC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,SAAS,EACT,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,GACtB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,kBAAkB,EAAE,CAAA;IAC/B,SAAS,EAAE,0BAA0B,EAAE,CAAA;IACvC,SAAS,EAAE,mBAAmB,CAAA;IAC9B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,mEAAmE;IACnE,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IAClE,4DAA4D;IAC5D,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9C,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACvF,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,SAAS,CAAA;CAC5E,2CAgEA"}
1
+ {"version":3,"file":"slot-allocation-resource-view.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-resource-view.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AAkClG,OAAO,EAAkB,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAA;AAG3E,OAAO,EACL,KAAK,mBAAmB,EAGzB,MAAM,4BAA4B,CAAA;AAGnC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,SAAS,EACT,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,GACtB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,kBAAkB,EAAE,CAAA;IAC/B,SAAS,EAAE,0BAA0B,EAAE,CAAA;IACvC,SAAS,EAAE,mBAAmB,CAAA;IAC9B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C;;;;;OAKG;IACH,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7C,mEAAmE;IACnE,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IAClE,4DAA4D;IAC5D,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9C,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACvF,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,SAAS,CAAA;CAC5E,2CA8DA"}
@@ -1,53 +1,66 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Badge, Button, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Input, Label, Popover, PopoverContent, PopoverTrigger, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@voyantjs/ui/components";
4
- import { Bed, Pencil, Plus, Trash2, UserMinus, Users, X } from "lucide-react";
4
+ import { Accessibility, Bed, Crown, Pencil, Plus, Trash2, UserMinus, Users, UtensilsCrossed, X, } from "lucide-react";
5
5
  import { useEffect, useState } from "react";
6
6
  import { useAllocationUiMessagesOrDefault } from "../i18n/index.js";
7
7
  import { groupResourcesBySubType, kindLabel, } from "./slot-allocation-model.js";
8
- import { AllocationColumn, TravelerTile } from "./slot-allocation-shared.js";
9
- export function ResourceColumnsView({ kind, resources, travelers, occupants, sharingGroupLabels, onAssignTraveler, onUnassignTraveler, onRemoveResource, onEditResource, renderTravelerActions, }) {
8
+ import { AllocationColumn } from "./slot-allocation-shared.js";
9
+ export function ResourceColumnsView({ kind, resources, travelers, occupants, sharingGroupLabels, optionNamesById, onAssignTraveler, onUnassignTraveler, onRemoveResource, onEditResource, renderTravelerActions, }) {
10
10
  const messages = useAllocationUiMessagesOrDefault();
11
- return (_jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(18rem,22rem)_1fr]", children: [_jsx(AllocationColumn, { id: "unallocated", icon: _jsx(Users, { className: "size-4", "aria-hidden": "true" }), title: messages.unallocated, description: messages.unallocatedDescription, count: occupants.unallocated.length, capacity: travelers.length, children: occupants.unallocated.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: messages.unallocatedEmpty })) : (occupants.unallocated.map((traveler) => (_jsx(TravelerTile, { traveler: traveler, sharingGroupLabel: traveler.sharingGroupId ? sharingGroupLabels[traveler.sharingGroupId] : null, renderActions: renderTravelerActions }, traveler.id)))) }), _jsx("div", { className: "flex min-w-0 flex-col gap-6", children: resources.length === 0 ? (_jsx("div", { className: "rounded-md border border-dashed p-6 text-sm text-muted-foreground", children: messages.noResources })) : (groupResourcesBySubType(resources).map((group) => {
12
- const groupLabel = group.label ?? messages.resourceOtherGroup;
13
- return (_jsxs("section", { "aria-label": groupLabel, className: "flex flex-col gap-2", children: [_jsxs("header", { className: "flex items-baseline justify-between gap-2 border-b pb-1", children: [_jsx("h3", { className: "text-sm font-semibold tracking-wide uppercase text-muted-foreground", children: groupLabel }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [group.count, " \u00B7 ", messages.capacity.toLowerCase(), " ", group.capacity] })] }), _jsx(ResourceGroupTable, { kind: kind, resources: group.resources, occupants: occupants, unallocated: occupants.unallocated, sharingGroupLabels: sharingGroupLabels, onAssignTraveler: onAssignTraveler, onUnassignTraveler: onUnassignTraveler, onRemoveResource: onRemoveResource, onEditResource: onEditResource })] }, group.key));
11
+ return (_jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(18rem,22rem)_1fr]", children: [_jsx(AllocationColumn, { id: "unallocated", icon: _jsx(Users, { className: "size-4", "aria-hidden": "true" }), title: messages.unallocated, description: messages.unallocatedDescription, count: occupants.unallocated.length, capacity: travelers.length, children: occupants.unallocated.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: messages.unallocatedEmpty })) : (_jsx(UnallocatedTravelersTable, { travelers: occupants.unallocated, sharingGroupLabels: sharingGroupLabels, renderActions: renderTravelerActions })) }), _jsx("div", { className: "flex min-w-0 flex-col gap-6", children: resources.length === 0 ? (_jsx("div", { className: "rounded-md border border-dashed p-6 text-sm text-muted-foreground", children: messages.noResources })) : (groupResourcesBySubType(resources).map((group) => {
12
+ // The grouping function uses the raw `refId` as the label fallback.
13
+ // Resolve product-option ids back to human names so the section
14
+ // header reads "Standard double" instead of "POPT_01KRS8…".
15
+ const resolvedLabel = (group.label ? optionNamesById?.get(group.label) : null) ?? group.label;
16
+ const groupLabel = resolvedLabel ?? messages.resourceOtherGroup;
17
+ return (_jsxs("section", { "aria-label": groupLabel, className: "flex flex-col gap-2", children: [_jsx("header", { className: "flex items-baseline gap-2 border-b pb-1", children: _jsx("h3", { className: "text-sm font-semibold tracking-wide uppercase text-muted-foreground", children: groupLabel }) }), _jsx(ResourceGroupTable, { kind: kind, resources: group.resources, occupants: occupants, unallocated: occupants.unallocated, sharingGroupLabels: sharingGroupLabels, optionNamesById: optionNamesById, onAssignTraveler: onAssignTraveler, onUnassignTraveler: onUnassignTraveler, onRemoveResource: onRemoveResource, onEditResource: onEditResource })] }, group.key));
14
18
  })) })] }));
15
19
  }
16
- function ResourceGroupTable({ kind, resources, occupants, unallocated, sharingGroupLabels, onAssignTraveler, onUnassignTraveler, onRemoveResource, onEditResource, }) {
20
+ function ResourceGroupTable({ kind, resources, occupants, unallocated, sharingGroupLabels, optionNamesById, onAssignTraveler, onUnassignTraveler, onRemoveResource, onEditResource, }) {
17
21
  const messages = useAllocationUiMessagesOrDefault();
18
22
  const [editingId, setEditingId] = useState(null);
19
23
  return (_jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "bg-muted/40", children: [_jsx(TableHead, { className: "w-48", children: messages.resourceLabel }), _jsx(TableHead, { className: "w-20 text-center", children: messages.capacity }), _jsx(TableHead, { children: messages.travelers }), _jsx(TableHead, { className: "w-40 text-right", children: "\u00A0" })] }) }), _jsx(TableBody, { children: resources.map((resource) => {
20
24
  const seated = occupants.byResource.get(resource.id) ?? [];
21
25
  const isEditing = editingId === resource.id;
22
26
  const isFull = seated.length >= resource.capacity;
23
- return (_jsx(ResourceRow, { kind: kind, resource: resource, seated: seated, unallocated: unallocated, sharingGroupLabels: sharingGroupLabels, isEditing: isEditing, isFull: isFull, canEdit: Boolean(onEditResource), onBeginEdit: () => setEditingId(resource.id), onCancelEdit: () => setEditingId(null), onSaveEdit: async (input) => {
27
+ return (_jsx(ResourceRow, { kind: kind, resource: resource, seated: seated, unallocated: unallocated, sharingGroupLabels: sharingGroupLabels, optionName: resource.refType === "option" && resource.refId
28
+ ? (optionNamesById?.get(resource.refId) ?? null)
29
+ : null, isEditing: isEditing, isFull: isFull, canEdit: Boolean(onEditResource), onBeginEdit: () => setEditingId(resource.id), onCancelEdit: () => setEditingId(null), onSaveEdit: async (input) => {
24
30
  await Promise.resolve(onEditResource?.(resource.id, input));
25
31
  setEditingId(null);
26
32
  }, onAssignTraveler: (travelerId) => onAssignTraveler(travelerId, resource.id), onUnassignTraveler: onUnassignTraveler, onRemoveResource: () => onRemoveResource(resource.id) }, resource.id));
27
33
  }) })] }) }));
28
34
  }
29
- function ResourceRow({ kind, resource, seated, unallocated, sharingGroupLabels, isEditing, isFull, canEdit, onBeginEdit, onCancelEdit, onSaveEdit, onAssignTraveler, onUnassignTraveler, onRemoveResource, }) {
35
+ function ResourceRow({ kind, resource, seated, unallocated, sharingGroupLabels, optionName, isEditing, isFull, canEdit, onBeginEdit, onCancelEdit, onSaveEdit, onAssignTraveler, onUnassignTraveler, onRemoveResource, }) {
30
36
  const messages = useAllocationUiMessagesOrDefault();
31
37
  const overCapacity = seated.length > resource.capacity;
32
38
  if (isEditing && canEdit) {
33
39
  return (_jsx(TableRow, { className: "bg-muted/30", children: _jsx(TableCell, { colSpan: 4, className: "whitespace-normal", children: _jsx(ResourceEditForm, { kind: kind, resource: resource, minCapacity: Math.max(1, seated.length), onCancel: onCancelEdit, onSave: onSaveEdit }) }) }));
34
40
  }
35
- return (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: _jsxs("span", { className: "inline-flex items-center gap-2", children: [_jsx(Bed, { className: "size-4 text-muted-foreground", "aria-hidden": "true" }), resource.label ?? kindLabel(kind, messages)] }) }), _jsx(TableCell, { className: "text-center", children: _jsxs(Badge, { variant: overCapacity ? "destructive" : "outline", children: [seated.length, "/", resource.capacity] }) }), _jsx(TableCell, { className: "whitespace-normal py-2", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [seated.map((traveler) => (_jsx(TravelerChip, { traveler: traveler, sharingGroupLabel: traveler.sharingGroupId
41
+ return (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: _jsxs("span", { className: "inline-flex items-center gap-2", children: [_jsx(Bed, { className: "size-4 text-muted-foreground", "aria-hidden": "true" }), resource.label ?? kindLabel(kind, messages), optionName ? (_jsx(Badge, { variant: "secondary", className: "text-[10px] font-normal", children: optionName })) : null] }) }), _jsx(TableCell, { className: "text-center", children: _jsxs(Badge, { variant: overCapacity ? "destructive" : "outline", children: [seated.length, "/", resource.capacity] }) }), _jsx(TableCell, { className: "whitespace-normal py-2", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [seated.map((traveler) => (_jsx(TravelerChip, { traveler: traveler, sharingGroupLabel: traveler.sharingGroupId
36
42
  ? (sharingGroupLabels[traveler.sharingGroupId] ?? null)
37
- : null, onUnassign: () => onUnassignTraveler(traveler.id) }, traveler.id))), !isFull ? (_jsx(AssignTravelerPopover, { unallocated: unallocated, onSelect: (travelerId) => onAssignTraveler(travelerId) })) : null] }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "inline-flex items-center justify-end gap-1", children: [canEdit ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onBeginEdit, "aria-label": messages.editResource, children: _jsx(Pencil, { className: "size-4", "aria-hidden": "true" }) })) : null, _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onRemoveResource, "aria-label": messages.remove, children: _jsx(Trash2, { className: "size-4", "aria-hidden": "true" }) })] }) })] }));
43
+ : null, onUnassign: () => onUnassignTraveler(traveler.id) }, traveler.id))), !isFull ? (_jsx(AssignTravelerPopover, { unallocated: unallocated, seatedBookingIds: new Set(seated.map((t) => t.bookingId)), onSelect: (travelerId) => onAssignTraveler(travelerId) })) : null] }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "inline-flex items-center justify-end gap-1", children: [canEdit ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onBeginEdit, "aria-label": messages.editResource, children: _jsx(Pencil, { className: "size-4", "aria-hidden": "true" }) })) : null, _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onRemoveResource, "aria-label": messages.remove, children: _jsx(Trash2, { className: "size-4", "aria-hidden": "true" }) })] }) })] }));
38
44
  }
39
45
  function TravelerChip({ traveler, sharingGroupLabel, onUnassign, }) {
40
46
  const messages = useAllocationUiMessagesOrDefault();
41
47
  return (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full border bg-background px-2 py-0.5 text-xs", children: [_jsx("span", { className: "truncate font-medium", title: sharingGroupLabel ?? undefined, children: traveler.fullName }), _jsx("span", { className: "text-muted-foreground", children: traveler.bookingNumber }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-4 rounded-full", onClick: onUnassign, "aria-label": `${messages.remove}: ${traveler.fullName}`, children: _jsx(X, { className: "size-3", "aria-hidden": "true" }) })] }));
42
48
  }
43
- function AssignTravelerPopover({ unallocated, onSelect, }) {
49
+ function AssignTravelerPopover({ unallocated, seatedBookingIds, onSelect, }) {
44
50
  const messages = useAllocationUiMessagesOrDefault();
45
51
  const [open, setOpen] = useState(false);
46
52
  const disabled = unallocated.length === 0;
47
- return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { render: _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-7 gap-1 px-2 text-xs", disabled: disabled, "aria-label": messages.assignTraveler, title: disabled ? messages.assignTravelerEmpty : messages.assignTraveler, children: [_jsx(Plus, { className: "size-3.5", "aria-hidden": "true" }), messages.assignTraveler] }) }), _jsx(PopoverContent, { className: "w-72 p-0", align: "start", children: _jsxs(Command, { children: [_jsx(CommandInput, { placeholder: messages.assignTravelerSearch }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: messages.assignTravelerEmpty }), _jsx(CommandGroup, { children: unallocated.map((traveler) => (_jsxs(CommandItem, { value: `${traveler.fullName} ${traveler.bookingNumber}`, onSelect: () => {
48
- onSelect(traveler.id);
49
- setOpen(false);
50
- }, children: [_jsx(UserMinus, { className: "mr-2 size-4 text-muted-foreground", "aria-hidden": "true" }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "font-medium", children: traveler.fullName }), _jsx("span", { className: "text-xs text-muted-foreground", children: traveler.bookingNumber })] })] }, traveler.id))) })] })] }) })] }));
53
+ const sameBooking = seatedBookingIds
54
+ ? unallocated.filter((t) => seatedBookingIds.has(t.bookingId))
55
+ : [];
56
+ const others = seatedBookingIds
57
+ ? unallocated.filter((t) => !seatedBookingIds.has(t.bookingId))
58
+ : unallocated;
59
+ const renderItem = (traveler) => (_jsxs(CommandItem, { value: `${traveler.fullName} ${traveler.bookingNumber}`, onSelect: () => {
60
+ onSelect(traveler.id);
61
+ setOpen(false);
62
+ }, children: [_jsx(UserMinus, { className: "mr-2 size-4 text-muted-foreground", "aria-hidden": "true" }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "font-medium", children: traveler.fullName }), _jsx("span", { className: "text-xs text-muted-foreground", children: traveler.bookingNumber })] })] }, traveler.id));
63
+ return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { render: _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-7 gap-1 px-2 text-xs", disabled: disabled, "aria-label": messages.assignTraveler, title: disabled ? messages.assignTravelerEmpty : messages.assignTraveler, children: [_jsx(Plus, { className: "size-3.5", "aria-hidden": "true" }), messages.assignTraveler] }) }), _jsx(PopoverContent, { className: "w-72 p-0", align: "start", children: _jsxs(Command, { children: [_jsx(CommandInput, { placeholder: messages.assignTravelerSearch }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: messages.assignTravelerEmpty }), sameBooking.length > 0 ? (_jsx(CommandGroup, { heading: messages.assignTravelerSameBooking, children: sameBooking.map(renderItem) })) : null, others.length > 0 ? (_jsx(CommandGroup, { heading: sameBooking.length > 0 ? messages.assignTravelerOthers : undefined, children: others.map(renderItem) })) : null] })] }) })] }));
51
64
  }
52
65
  function ResourceEditForm({ kind, resource, minCapacity, onCancel, onSave, }) {
53
66
  const messages = useAllocationUiMessagesOrDefault();
@@ -75,3 +88,14 @@ function ResourceEditForm({ kind, resource, minCapacity, onCancel, onSave, }) {
75
88
  }
76
89
  return (_jsxs("form", { className: "grid gap-2 sm:grid-cols-[1fr_8rem_auto_auto] sm:items-end", onSubmit: submit, "aria-label": messages.editResource, children: [_jsxs("div", { className: "grid gap-1", children: [_jsx(Label, { htmlFor: `edit-${resource.id}-label`, className: "text-xs", children: messages.resourceLabel }), _jsx(Input, { id: `edit-${resource.id}-label`, value: label, onChange: (event) => setLabel(event.target.value), placeholder: kindLabel(kind, messages) })] }), _jsxs("div", { className: "grid gap-1", children: [_jsx(Label, { htmlFor: `edit-${resource.id}-capacity`, className: "text-xs", children: messages.resourceCapacity }), _jsx(Input, { id: `edit-${resource.id}-capacity`, type: "number", min: minCapacity, value: capacity, onChange: (event) => setCapacity(Number(event.target.value) || minCapacity) })] }), _jsx(Button, { type: "submit", size: "sm", disabled: saving, children: messages.saveResource }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancel, disabled: saving, children: messages.cancel })] }));
77
90
  }
91
+ /**
92
+ * Dense table view of unallocated travelers. Replaces the per-row Card
93
+ * tiles, which took multiple line-heights per traveler and made even a
94
+ * small slot scroll. Keeps the same metadata (lead flag, sharing group,
95
+ * accessibility / dietary icons, booking number) but in a single row.
96
+ */
97
+ function UnallocatedTravelersTable({ travelers, sharingGroupLabels, renderActions, }) {
98
+ const messages = useAllocationUiMessagesOrDefault();
99
+ const hasActions = Boolean(renderActions);
100
+ return (_jsx("div", { className: "overflow-hidden rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "bg-muted/40", children: [_jsx(TableHead, { className: "px-3 py-1.5 text-xs", children: messages.travelers }), _jsx(TableHead, { className: "px-3 py-1.5 text-xs", children: "\u00A0" }), hasActions ? _jsx(TableHead, { className: "w-12 px-3 py-1.5" }) : null] }) }), _jsx(TableBody, { children: travelers.map((traveler) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "px-3 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [traveler.isLeadTraveler ? (_jsx(Crown, { className: "size-3.5 shrink-0 text-amber-500", "aria-label": messages.lead })) : null, _jsx("span", { className: "truncate font-medium text-sm", children: traveler.fullName }), traveler.sharingGroupId ? (_jsx(Badge, { variant: "secondary", className: "max-w-full truncate text-[10px]", children: sharingGroupLabels[traveler.sharingGroupId] ?? messages.sharingGroup })) : null, traveler.hasAccessibilityNeeds ? (_jsx(Accessibility, { className: "size-3.5 shrink-0 text-muted-foreground", "aria-label": messages.accessibility })) : null, traveler.hasDietaryRequirements ? (_jsx(UtensilsCrossed, { className: "size-3.5 shrink-0 text-muted-foreground", "aria-label": messages.dietary })) : null] }) }), _jsx(TableCell, { className: "px-3 py-1.5 text-muted-foreground text-xs", children: traveler.bookingNumber }), hasActions ? (_jsx(TableCell, { className: "px-3 py-1.5 text-right", children: renderActions?.(traveler) })) : null] }, traveler.id))) })] }) }));
101
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"slot-allocation-shared.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-shared.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EACnB,MAAM,8BAA8B,CAAA;AAGrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAE7E;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,EAAE,EACF,IAAI,EACJ,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,GACT,EAAE;IACD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,2CAqBA;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,iBAAiB,EACjB,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,0BAA0B,CAAA;IACpC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,SAAS,CAAA;CACpE,2CAgCA;AAED,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,SAAS,EACT,gBAAgB,GACjB,EAAE;IACD,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,SAAS,EAAE,kBAAkB,EAAE,CAAA;IAC/B,gBAAgB,EAAE,MAAM,CAAA;CACzB,2CA8BA;AAED,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,kBAAkB,CAAA;CAAE,kDAoBhF;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,kDAmBvE;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,uBAAuB,EAAE,CAAA;CAAE,kDA4B/E"}
1
+ {"version":3,"file":"slot-allocation-shared.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-shared.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EACnB,MAAM,8BAA8B,CAAA;AAGrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAE7E;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,EAAE,EACF,IAAI,EACJ,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,GACT,EAAE;IACD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,2CAqBA;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,iBAAiB,EACjB,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,0BAA0B,CAAA;IACpC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,SAAS,CAAA;CACpE,2CAgCA;AAED,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,SAAS,EACT,gBAAgB,GACjB,EAAE;IACD,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,SAAS,EAAE,kBAAkB,EAAE,CAAA;IAC/B,gBAAgB,EAAE,MAAM,CAAA;CACzB,2CAkCA;AAED,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,kBAAkB,CAAA;CAAE,kDAoBhF;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,kDAmBvE;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,uBAAuB,EAAE,CAAA;CAAE,kDA4B/E"}
@@ -23,7 +23,7 @@ export function ValidationSummary({ issues, resources, unallocatedCount, }) {
23
23
  if (issues.length === 0) {
24
24
  return (_jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-md border bg-muted/30 px-3 py-2 text-sm text-muted-foreground", children: [_jsx(Badge, { variant: "outline", children: messages.validationClear }), _jsxs("span", { children: [resources.length, " ", messages.resources.toLowerCase(), " \u00B7 ", unallocatedCount, " ", messages.unallocated.toLowerCase()] })] }));
25
25
  }
26
- return (_jsxs("div", { className: "rounded-md border border-amber-300/60 bg-amber-50 px-3 py-2 text-sm text-amber-950 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-100", children: [_jsxs("div", { className: "flex items-center gap-2 font-medium", children: [_jsx(CircleAlert, { className: "size-4", "aria-hidden": "true" }), messages.validationTitle] }), _jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: issues.map((issue) => (_jsx(Badge, { variant: "outline", className: "border-amber-300 bg-background/70", children: issue.label }, issue.id))) })] }));
26
+ return (_jsxs("div", { className: "rounded-md border border-amber-300/60 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:border-amber-500/40 dark:bg-amber-500/10 dark:text-amber-200", children: [_jsxs("div", { className: "flex items-center gap-2 font-medium", children: [_jsx(CircleAlert, { className: "size-4", "aria-hidden": "true" }), messages.validationTitle] }), _jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: issues.map((issue) => (_jsx(Badge, { variant: "outline", className: "border-amber-300 bg-background/70 text-amber-900 dark:border-amber-500/40 dark:bg-amber-500/15 dark:text-amber-100", children: issue.label }, issue.id))) })] }));
27
27
  }
28
28
  export function ResourceFlagBadges({ resource }) {
29
29
  const messages = useAllocationUiMessagesOrDefault();
@@ -23,6 +23,9 @@ export type AllocationUiMessages = Record<string, unknown> & {
23
23
  createRoom: string;
24
24
  resourceLabel: string;
25
25
  resourceCapacity: string;
26
+ resourceOption: string;
27
+ resourceOptionPlaceholder: string;
28
+ resourceOptionNone: string;
26
29
  createResource: string;
27
30
  editResource: string;
28
31
  saveResource: string;
@@ -34,6 +37,8 @@ export type AllocationUiMessages = Record<string, unknown> & {
34
37
  assignTraveler: string;
35
38
  assignTravelerSearch: string;
36
39
  assignTravelerEmpty: string;
40
+ assignTravelerSameBooking: string;
41
+ assignTravelerOthers: string;
37
42
  resourceOtherGroup: string;
38
43
  rooms: string;
39
44
  resources: string;
@@ -112,6 +117,9 @@ export declare const allocationUiEn: {
112
117
  createRoom: string;
113
118
  resourceLabel: string;
114
119
  resourceCapacity: string;
120
+ resourceOption: string;
121
+ resourceOptionPlaceholder: string;
122
+ resourceOptionNone: string;
115
123
  createResource: string;
116
124
  editResource: string;
117
125
  saveResource: string;
@@ -123,6 +131,8 @@ export declare const allocationUiEn: {
123
131
  assignTraveler: string;
124
132
  assignTravelerSearch: string;
125
133
  assignTravelerEmpty: string;
134
+ assignTravelerSameBooking: string;
135
+ assignTravelerOthers: string;
126
136
  resourceOtherGroup: string;
127
137
  rooms: string;
128
138
  resources: string;
@@ -201,6 +211,9 @@ export declare const allocationUiRo: {
201
211
  createRoom: string;
202
212
  resourceLabel: string;
203
213
  resourceCapacity: string;
214
+ resourceOption: string;
215
+ resourceOptionPlaceholder: string;
216
+ resourceOptionNone: string;
204
217
  createResource: string;
205
218
  editResource: string;
206
219
  saveResource: string;
@@ -212,6 +225,8 @@ export declare const allocationUiRo: {
212
225
  assignTraveler: string;
213
226
  assignTravelerSearch: string;
214
227
  assignTravelerEmpty: string;
228
+ assignTravelerSameBooking: string;
229
+ assignTravelerOthers: string;
215
230
  resourceOtherGroup: string;
216
231
  rooms: string;
217
232
  resources: string;
@@ -291,6 +306,9 @@ export declare const allocationUiMessageDefinitions: {
291
306
  createRoom: string;
292
307
  resourceLabel: string;
293
308
  resourceCapacity: string;
309
+ resourceOption: string;
310
+ resourceOptionPlaceholder: string;
311
+ resourceOptionNone: string;
294
312
  createResource: string;
295
313
  editResource: string;
296
314
  saveResource: string;
@@ -302,6 +320,8 @@ export declare const allocationUiMessageDefinitions: {
302
320
  assignTraveler: string;
303
321
  assignTravelerSearch: string;
304
322
  assignTravelerEmpty: string;
323
+ assignTravelerSameBooking: string;
324
+ assignTravelerOthers: string;
305
325
  resourceOtherGroup: string;
306
326
  rooms: string;
307
327
  resources: string;
@@ -380,6 +400,9 @@ export declare const allocationUiMessageDefinitions: {
380
400
  createRoom: string;
381
401
  resourceLabel: string;
382
402
  resourceCapacity: string;
403
+ resourceOption: string;
404
+ resourceOptionPlaceholder: string;
405
+ resourceOptionNone: string;
383
406
  createResource: string;
384
407
  editResource: string;
385
408
  saveResource: string;
@@ -391,6 +414,8 @@ export declare const allocationUiMessageDefinitions: {
391
414
  assignTraveler: string;
392
415
  assignTravelerSearch: string;
393
416
  assignTravelerEmpty: string;
417
+ assignTravelerSameBooking: string;
418
+ assignTravelerOthers: string;
394
419
  resourceOtherGroup: string;
395
420
  rooms: string;
396
421
  resources: string;
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/i18n/provider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/i18n/provider.tsx"],"names":[],"mappings":"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"}
@@ -36,6 +36,9 @@ export const allocationUiEn = {
36
36
  createRoom: "Create room",
37
37
  resourceLabel: "Resource label",
38
38
  resourceCapacity: "Capacity",
39
+ resourceOption: "Tied to option",
40
+ resourceOptionPlaceholder: "Select an option…",
41
+ resourceOptionNone: "Unassigned (manual)",
39
42
  createResource: "Create resource",
40
43
  editResource: "Edit resource",
41
44
  saveResource: "Save",
@@ -47,6 +50,8 @@ export const allocationUiEn = {
47
50
  assignTraveler: "Assign",
48
51
  assignTravelerSearch: "Search traveler...",
49
52
  assignTravelerEmpty: "No unallocated travelers.",
53
+ assignTravelerSameBooking: "Same booking",
54
+ assignTravelerOthers: "Other bookings",
50
55
  resourceOtherGroup: "Other",
51
56
  rooms: "Rooms",
52
57
  resources: "Resources",
@@ -55,7 +60,7 @@ export const allocationUiEn = {
55
60
  seat: "Seat",
56
61
  cabins: "Cabins",
57
62
  flightSeats: "Flight seats",
58
- travelers: "travelers",
63
+ travelers: "Travelers",
59
64
  capacity: "Capacity",
60
65
  lead: "Lead",
61
66
  sharingGroup: "Sharing group",
@@ -65,9 +70,9 @@ export const allocationUiEn = {
65
70
  remove: "Remove",
66
71
  overCapacity: "Resource is full",
67
72
  dropHere: "Drop traveler here",
68
- slotCapacityLabel: "Slot pax",
73
+ slotCapacityLabel: "Free seats",
69
74
  slotCapacityUnlimited: "Unlimited",
70
- resourceCapacityLabel: "Resource capacity",
75
+ resourceCapacityLabel: "Provisioned",
71
76
  resourceCapacityFits: "fits in slot",
72
77
  resourceCapacityExact: "matches slot",
73
78
  resourceCapacityOver: "over slot cap",
@@ -125,6 +130,9 @@ export const allocationUiRo = {
125
130
  createRoom: "Creeaza camera",
126
131
  resourceLabel: "Eticheta resursa",
127
132
  resourceCapacity: "Capacitate",
133
+ resourceOption: "Optiune asociata",
134
+ resourceOptionPlaceholder: "Alege o optiune…",
135
+ resourceOptionNone: "Neasociata (manual)",
128
136
  createResource: "Creeaza resursa",
129
137
  editResource: "Editeaza resursa",
130
138
  saveResource: "Salveaza",
@@ -136,6 +144,8 @@ export const allocationUiRo = {
136
144
  assignTraveler: "Aloca",
137
145
  assignTravelerSearch: "Cauta calator...",
138
146
  assignTravelerEmpty: "Nu exista calatori nealocati.",
147
+ assignTravelerSameBooking: "Aceeasi rezervare",
148
+ assignTravelerOthers: "Alte rezervari",
139
149
  resourceOtherGroup: "Altele",
140
150
  rooms: "Camere",
141
151
  resources: "Resurse",
@@ -144,7 +154,7 @@ export const allocationUiRo = {
144
154
  seat: "Loc",
145
155
  cabins: "Cabine",
146
156
  flightSeats: "Locuri zbor",
147
- travelers: "calatori",
157
+ travelers: "Calatori",
148
158
  capacity: "Capacitate",
149
159
  lead: "Lead",
150
160
  sharingGroup: "Grup partaj",
@@ -154,9 +164,9 @@ export const allocationUiRo = {
154
164
  remove: "Scoate",
155
165
  overCapacity: "Resursa este plina",
156
166
  dropHere: "Trage calatorul aici",
157
- slotCapacityLabel: "Pax slot",
167
+ slotCapacityLabel: "Locuri libere",
158
168
  slotCapacityUnlimited: "Nelimitat",
159
- resourceCapacityLabel: "Capacitate resurse",
169
+ resourceCapacityLabel: "Provizionate",
160
170
  resourceCapacityFits: "incape in slot",
161
171
  resourceCapacityExact: "egal cu slotul",
162
172
  resourceCapacityOver: "depaseste slotul",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/allocation-ui",
3
- "version": "0.52.0",
3
+ "version": "0.52.2",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,9 +34,9 @@
34
34
  "lucide-react": "^0.475.0",
35
35
  "react": "^19.0.0",
36
36
  "react-dom": "^19.0.0",
37
- "@voyantjs/availability-react": "0.52.0",
38
- "@voyantjs/i18n": "0.52.0",
39
- "@voyantjs/ui": "0.52.0"
37
+ "@voyantjs/availability-react": "0.52.2",
38
+ "@voyantjs/i18n": "0.52.2",
39
+ "@voyantjs/ui": "0.52.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@tanstack/react-query": "^5.90.12",
@@ -47,10 +47,10 @@
47
47
  "react-dom": "^19.2.4",
48
48
  "typescript": "^6.0.2",
49
49
  "vitest": "^4.1.2",
50
- "@voyantjs/availability-react": "0.52.0",
51
- "@voyantjs/voyant-typescript-config": "0.1.0",
52
- "@voyantjs/ui": "0.52.0",
53
- "@voyantjs/i18n": "0.52.0"
50
+ "@voyantjs/availability-react": "0.52.2",
51
+ "@voyantjs/i18n": "0.52.2",
52
+ "@voyantjs/ui": "0.52.2",
53
+ "@voyantjs/voyant-typescript-config": "0.1.0"
54
54
  },
55
55
  "files": [
56
56
  "dist",