@voyantjs/allocation-ui 0.50.8 → 0.51.1
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.
- package/dist/components/slot-allocation-model.d.ts +43 -0
- package/dist/components/slot-allocation-model.d.ts.map +1 -1
- package/dist/components/slot-allocation-model.js +59 -0
- package/dist/components/slot-allocation-page.d.ts +8 -1
- package/dist/components/slot-allocation-page.d.ts.map +1 -1
- package/dist/components/slot-allocation-page.js +104 -38
- package/dist/components/slot-allocation-resource-view.d.ts +11 -3
- package/dist/components/slot-allocation-resource-view.d.ts.map +1 -1
- package/dist/components/slot-allocation-resource-view.js +70 -9
- package/dist/components/slot-allocation-seat-view.d.ts +4 -2
- package/dist/components/slot-allocation-seat-view.d.ts.map +1 -1
- package/dist/components/slot-allocation-seat-view.js +17 -21
- package/dist/components/slot-allocation-shared.d.ts +9 -4
- package/dist/components/slot-allocation-shared.d.ts.map +1 -1
- package/dist/components/slot-allocation-shared.js +11 -24
- package/dist/i18n/provider.d.ts +75 -0
- package/dist/i18n/provider.d.ts.map +1 -1
- package/dist/i18n/provider.js +32 -2
- package/package.json +7 -7
|
@@ -62,5 +62,48 @@ export declare function seatName(seat: AllocationResource, messages: AllocationU
|
|
|
62
62
|
export declare function parentKindFor(kind: string): "" | "vehicle";
|
|
63
63
|
export declare function defaultCapacityFor(kind: string): 1 | 2;
|
|
64
64
|
export declare function kindLabel(kind: string, messages: AllocationUiMessages): string;
|
|
65
|
+
/**
|
|
66
|
+
* Group resources for display by a "sub-type" — `refId` when present,
|
|
67
|
+
* otherwise the leading alphabetic token of `label` ("DBL" from
|
|
68
|
+
* "DBL 1"), otherwise a single "other" bucket. Lets the operator scan
|
|
69
|
+
* all DBLs together instead of an alphabetic interleave of mixed
|
|
70
|
+
* sub-types. Each group also exposes the count and the total person-
|
|
71
|
+
* capacity so the header summary stays consistent with the grid.
|
|
72
|
+
*/
|
|
73
|
+
export interface ResourceSubTypeGroup {
|
|
74
|
+
key: string;
|
|
75
|
+
/**
|
|
76
|
+
* Human-readable group label derived from the resource's refId or
|
|
77
|
+
* label prefix. `null` for the catch-all bucket (unlabeled, refId-
|
|
78
|
+
* less resources) — consumers should fall back to a localized
|
|
79
|
+
* "Other" string.
|
|
80
|
+
*/
|
|
81
|
+
label: string | null;
|
|
82
|
+
resources: AllocationResource[];
|
|
83
|
+
count: number;
|
|
84
|
+
capacity: number;
|
|
85
|
+
}
|
|
86
|
+
export declare function groupResourcesBySubType(resources: AllocationResource[]): ResourceSubTypeGroup[];
|
|
87
|
+
export interface ResourceCapacitySummary {
|
|
88
|
+
resourceCount: number;
|
|
89
|
+
resourceCapacity: number;
|
|
90
|
+
slotPax: number | null;
|
|
91
|
+
slotRemainingPax: number | null;
|
|
92
|
+
delta: number | null;
|
|
93
|
+
status: "fits" | "exact" | "over" | "unbounded";
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Roll up slot pax vs. resource person-capacity into a single summary
|
|
97
|
+
* the header can render as a coloured pill. `slotPax` is the slot's
|
|
98
|
+
* `initialPax` when finite; if the slot is `unlimited` or has no
|
|
99
|
+
* initial pax we treat it as unbounded and the operator only sees the
|
|
100
|
+
* total resource capacity without a delta.
|
|
101
|
+
*/
|
|
102
|
+
export declare function summarizeResourceCapacity(input: {
|
|
103
|
+
resources: AllocationResource[];
|
|
104
|
+
slotInitialPax: number | null | undefined;
|
|
105
|
+
slotRemainingPax: number | null | undefined;
|
|
106
|
+
unlimited: boolean;
|
|
107
|
+
}): ResourceCapacitySummary;
|
|
65
108
|
export declare function flagString(value: unknown): string | null;
|
|
66
109
|
//# sourceMappingURL=slot-allocation-model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot-allocation-model.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AAElG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,eAAO,MAAM,SAAS,SAAS,CAAA;AAC/B,eAAO,MAAM,YAAY,YAAY,CAAA;AACrC,eAAO,MAAM,iBAAiB,iBAAiB,CAAA;AAC/C,eAAO,MAAM,iBAAiB,aAA0B,CAAA;AAExD,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,0BAA0B,EAAE,CAAC,CAAA;IACrD,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAA;IACrD,WAAW,EAAE,0BAA0B,EAAE,CAAA;CAC1C,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,0BAA0B,EAAE,EACvC,SAAS,EAAE,kBAAkB,EAAE,EAC/B,IAAI,EAAE,MAAM,GACX,mBAAmB,CAmBrB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,SAAS,EACT,SAAS,EACT,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,0BAA0B,EAAE,CAAA;IACvC,SAAS,EAAE,kBAAkB,EAAE,CAAA;IAC/B,SAAS,EAAE,mBAAmB,CAAA;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,oBAAoB,CAAA;CAC/B,qBA4BA;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,0BAA0B,EAAE,EAAE,IAAI,EAAE,MAAM,YAevF;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,EAAE,EAC3B,QAAQ,EAAE,kBAAkB,EAAE,EAC9B,QAAQ,EAAE,oBAAoB;;;;;;;;;;;;;;;QAKtB,MAAM;WAAS,MAAM;eAAa,MAAM;IA0BjD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,kBAAkB,EAAE;;;;;;;;;;;;;;;;IAenD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,oBAAoB,UAKhF;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,kBAEzC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,SAE9C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,UAMrE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,iBAExC"}
|
|
1
|
+
{"version":3,"file":"slot-allocation-model.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -125,6 +125,65 @@ export function kindLabel(kind, messages) {
|
|
|
125
125
|
return messages.flightSeats;
|
|
126
126
|
return titleCaseKind(kind);
|
|
127
127
|
}
|
|
128
|
+
export function groupResourcesBySubType(resources) {
|
|
129
|
+
const buckets = new Map();
|
|
130
|
+
for (const resource of resources) {
|
|
131
|
+
const { key, label } = resourceSubTypeKey(resource);
|
|
132
|
+
const bucket = buckets.get(key) ?? { key, label, resources: [] };
|
|
133
|
+
bucket.resources.push(resource);
|
|
134
|
+
buckets.set(key, bucket);
|
|
135
|
+
}
|
|
136
|
+
return Array.from(buckets.values())
|
|
137
|
+
.map((bucket) => ({
|
|
138
|
+
key: bucket.key,
|
|
139
|
+
label: bucket.label,
|
|
140
|
+
resources: bucket.resources,
|
|
141
|
+
count: bucket.resources.length,
|
|
142
|
+
capacity: bucket.resources.reduce((sum, resource) => sum + resource.capacity, 0),
|
|
143
|
+
}))
|
|
144
|
+
.sort((a, b) => (a.label ?? "").localeCompare(b.label ?? "") || a.key.localeCompare(b.key));
|
|
145
|
+
}
|
|
146
|
+
function resourceSubTypeKey(resource) {
|
|
147
|
+
if (resource.refId)
|
|
148
|
+
return { key: `ref:${resource.refId}`, label: resource.refId };
|
|
149
|
+
const label = (resource.label ?? "").trim();
|
|
150
|
+
if (label.length > 0) {
|
|
151
|
+
const prefix = label.match(/^[A-Za-z]+/)?.[0];
|
|
152
|
+
if (prefix)
|
|
153
|
+
return { key: `prefix:${prefix.toUpperCase()}`, label: prefix.toUpperCase() };
|
|
154
|
+
}
|
|
155
|
+
return { key: "other", label: null };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Roll up slot pax vs. resource person-capacity into a single summary
|
|
159
|
+
* the header can render as a coloured pill. `slotPax` is the slot's
|
|
160
|
+
* `initialPax` when finite; if the slot is `unlimited` or has no
|
|
161
|
+
* initial pax we treat it as unbounded and the operator only sees the
|
|
162
|
+
* total resource capacity without a delta.
|
|
163
|
+
*/
|
|
164
|
+
export function summarizeResourceCapacity(input) {
|
|
165
|
+
const resourceCapacity = input.resources.reduce((sum, resource) => sum + resource.capacity, 0);
|
|
166
|
+
const slotPax = input.unlimited ? null : (input.slotInitialPax ?? null);
|
|
167
|
+
if (slotPax == null) {
|
|
168
|
+
return {
|
|
169
|
+
resourceCount: input.resources.length,
|
|
170
|
+
resourceCapacity,
|
|
171
|
+
slotPax: null,
|
|
172
|
+
slotRemainingPax: input.unlimited ? null : (input.slotRemainingPax ?? null),
|
|
173
|
+
delta: null,
|
|
174
|
+
status: "unbounded",
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const delta = resourceCapacity - slotPax;
|
|
178
|
+
return {
|
|
179
|
+
resourceCount: input.resources.length,
|
|
180
|
+
resourceCapacity,
|
|
181
|
+
slotPax,
|
|
182
|
+
slotRemainingPax: input.slotRemainingPax ?? null,
|
|
183
|
+
delta,
|
|
184
|
+
status: delta > 0 ? "over" : delta === 0 ? "exact" : "fits",
|
|
185
|
+
};
|
|
186
|
+
}
|
|
128
187
|
export function flagString(value) {
|
|
129
188
|
return typeof value === "string" ? value : null;
|
|
130
189
|
}
|
|
@@ -28,6 +28,13 @@ export interface SlotAllocationPageProps {
|
|
|
28
28
|
renderBefore?: (context: SlotAllocationPageRenderContext) => ReactNode;
|
|
29
29
|
renderAfter?: (context: SlotAllocationPageRenderContext) => ReactNode;
|
|
30
30
|
extraTabs?: SlotAllocationPageExtraTab[];
|
|
31
|
+
/**
|
|
32
|
+
* Drop the top-level page header (title + back arrow). The host is
|
|
33
|
+
* expected to render its own. Capacity badges + the actions cluster
|
|
34
|
+
* stay as an inline toolbar above the kind tabs so the body is
|
|
35
|
+
* still self-sufficient when embedded.
|
|
36
|
+
*/
|
|
37
|
+
embed?: boolean;
|
|
31
38
|
}
|
|
32
|
-
export declare function SlotAllocationPage({ slotId, className, onBack, renderExtraActions, renderTravelerActions, renderHeaderEnd, renderBefore, renderAfter, extraTabs, }: SlotAllocationPageProps): import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
export declare function SlotAllocationPage({ slotId, className, onBack, renderExtraActions, renderTravelerActions, renderHeaderEnd, renderBefore, renderAfter, extraTabs, embed, }: SlotAllocationPageProps): import("react/jsx-runtime").JSX.Element;
|
|
33
40
|
//# sourceMappingURL=slot-allocation-page.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot-allocation-page.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-page.tsx"],"names":[],"mappings":"
|
|
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,17 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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";
|
|
6
7
|
import { useMemo, useState } from "react";
|
|
7
8
|
import { useAllocationUiMessagesOrDefault } from "../i18n/index.js";
|
|
8
|
-
import { buildValidationIssues, collectOccupants, defaultCapacityFor, kindLabel, PARENT_ONLY_KINDS, parentKindFor, ROOM_KIND, VEHICLE_SEAT_KIND, } from "./slot-allocation-model.js";
|
|
9
|
+
import { buildValidationIssues, collectOccupants, defaultCapacityFor, kindLabel, PARENT_ONLY_KINDS, parentKindFor, ROOM_KIND, summarizeResourceCapacity, VEHICLE_SEAT_KIND, } from "./slot-allocation-model.js";
|
|
9
10
|
import { ResourceColumnsView } from "./slot-allocation-resource-view.js";
|
|
10
11
|
import { VehicleSeatsView } from "./slot-allocation-seat-view.js";
|
|
11
12
|
import { AuditLogCard, ValidationSummary } from "./slot-allocation-shared.js";
|
|
12
|
-
export function SlotAllocationPage({ slotId, className, onBack, renderExtraActions, renderTravelerActions, renderHeaderEnd, renderBefore, renderAfter, extraTabs = [], }) {
|
|
13
|
+
export function SlotAllocationPage({ slotId, className, onBack, renderExtraActions, renderTravelerActions, renderHeaderEnd, renderBefore, renderAfter, extraTabs = [], embed = false, }) {
|
|
13
14
|
const messages = useAllocationUiMessagesOrDefault();
|
|
15
|
+
const availabilityClient = useVoyantAvailabilityContext();
|
|
14
16
|
const allocation = useSlotAllocation({ slotId });
|
|
17
|
+
const slotRowQuery = useQuery(getSlotQueryOptions(availabilityClient, slotId));
|
|
18
|
+
const slotRow = slotRowQuery.data?.data;
|
|
15
19
|
const auditLog = useSlotAllocationAuditLog({ slotId });
|
|
16
20
|
const resourceMutation = useAllocationResourceMutation(slotId);
|
|
17
21
|
const assignMutation = useAssignTravelerAllocationMutation(slotId);
|
|
@@ -21,7 +25,6 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
|
|
|
21
25
|
const [resourceLabel, setResourceLabel] = useState("");
|
|
22
26
|
const [resourceCapacity, setResourceCapacity] = useState(2);
|
|
23
27
|
const [error, setError] = useState(null);
|
|
24
|
-
const hasPageExtensions = Boolean(renderHeaderEnd || renderBefore || renderAfter || extraTabs.length);
|
|
25
28
|
const data = allocation.data?.data;
|
|
26
29
|
const templates = useProductResourceTemplates({
|
|
27
30
|
productId: data?.slot.productId,
|
|
@@ -64,6 +67,48 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
|
|
|
64
67
|
const parentResources = useMemo(() => (data?.resources ?? []).filter((resource) => resource.kind === parentKindFor(activeKind)), [data?.resources, activeKind]);
|
|
65
68
|
const occupants = useMemo(() => collectOccupants(travelers, resources, activeKind), [travelers, resources, activeKind]);
|
|
66
69
|
const validationIssues = useMemo(() => buildValidationIssues({ travelers, resources, occupants, kind: activeKind, messages }), [travelers, resources, occupants, activeKind, messages]);
|
|
70
|
+
const capacitySummary = useMemo(() => summarizeResourceCapacity({
|
|
71
|
+
resources,
|
|
72
|
+
slotInitialPax: slotRow?.initialPax ?? null,
|
|
73
|
+
slotRemainingPax: slotRow?.remainingPax ?? null,
|
|
74
|
+
unlimited: slotRow?.unlimited ?? false,
|
|
75
|
+
}), [resources, slotRow?.initialPax, slotRow?.remainingPax, slotRow?.unlimited]);
|
|
76
|
+
const projectedSummary = useMemo(() => {
|
|
77
|
+
if (!addingResource)
|
|
78
|
+
return null;
|
|
79
|
+
const capacityNumber = Number.isFinite(resourceCapacity) ? Math.max(0, resourceCapacity) : 0;
|
|
80
|
+
return summarizeResourceCapacity({
|
|
81
|
+
resources: [
|
|
82
|
+
...resources,
|
|
83
|
+
{
|
|
84
|
+
id: "__projected__",
|
|
85
|
+
slotId,
|
|
86
|
+
kind: activeKind,
|
|
87
|
+
label: null,
|
|
88
|
+
refType: null,
|
|
89
|
+
refId: null,
|
|
90
|
+
capacity: capacityNumber,
|
|
91
|
+
flags: {},
|
|
92
|
+
parentId: null,
|
|
93
|
+
sortOrder: 0,
|
|
94
|
+
createdAt: new Date().toISOString(),
|
|
95
|
+
updatedAt: new Date().toISOString(),
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
slotInitialPax: slotRow?.initialPax ?? null,
|
|
99
|
+
slotRemainingPax: slotRow?.remainingPax ?? null,
|
|
100
|
+
unlimited: slotRow?.unlimited ?? false,
|
|
101
|
+
});
|
|
102
|
+
}, [
|
|
103
|
+
addingResource,
|
|
104
|
+
resourceCapacity,
|
|
105
|
+
resources,
|
|
106
|
+
slotId,
|
|
107
|
+
activeKind,
|
|
108
|
+
slotRow?.initialPax,
|
|
109
|
+
slotRow?.remainingPax,
|
|
110
|
+
slotRow?.unlimited,
|
|
111
|
+
]);
|
|
67
112
|
function downloadExport(kind) {
|
|
68
113
|
globalThis.location.assign(`/v1/admin/availability/slots/${encodeURIComponent(slotId)}/allocation/export-${kind}`);
|
|
69
114
|
}
|
|
@@ -76,29 +121,6 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
|
|
|
76
121
|
setError(err instanceof Error ? err.message : messages.allocationFailed);
|
|
77
122
|
}
|
|
78
123
|
}
|
|
79
|
-
async function swapOrAssignSeat(travelerId, resourceId) {
|
|
80
|
-
const traveler = occupants.byTravelerId.get(travelerId);
|
|
81
|
-
if (!traveler)
|
|
82
|
-
return;
|
|
83
|
-
const currentResourceId = traveler.allocations[activeKind] ?? null;
|
|
84
|
-
if (currentResourceId === resourceId)
|
|
85
|
-
return;
|
|
86
|
-
setError(null);
|
|
87
|
-
try {
|
|
88
|
-
const targetOccupant = (occupants.byResource.get(resourceId) ?? []).find((occupant) => occupant.id !== travelerId);
|
|
89
|
-
if (targetOccupant) {
|
|
90
|
-
await assignMutation.mutateAsync({
|
|
91
|
-
travelerId: targetOccupant.id,
|
|
92
|
-
kind: activeKind,
|
|
93
|
-
resourceId: currentResourceId,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
await assignMutation.mutateAsync({ travelerId, kind: activeKind, resourceId });
|
|
97
|
-
}
|
|
98
|
-
catch (err) {
|
|
99
|
-
setError(err instanceof Error ? err.message : messages.allocationFailed);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
124
|
async function createResource(event) {
|
|
103
125
|
event.preventDefault();
|
|
104
126
|
setError(null);
|
|
@@ -116,6 +138,16 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
|
|
|
116
138
|
setError(err instanceof Error ? err.message : messages.createResourceFailed);
|
|
117
139
|
}
|
|
118
140
|
}
|
|
141
|
+
async function editResource(resourceId, input) {
|
|
142
|
+
setError(null);
|
|
143
|
+
try {
|
|
144
|
+
await resourceMutation.update.mutateAsync({ resourceId, input });
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
setError(err instanceof Error ? err.message : messages.updateResourceFailed);
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
119
151
|
async function generateResources() {
|
|
120
152
|
setError(null);
|
|
121
153
|
try {
|
|
@@ -137,7 +169,14 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
|
|
|
137
169
|
if (allocation.isPending) {
|
|
138
170
|
return (_jsx("div", { className: cn("p-6 text-sm text-muted-foreground", className), children: messages.loading }));
|
|
139
171
|
}
|
|
140
|
-
|
|
172
|
+
// Only short-circuit when we genuinely have no data to render
|
|
173
|
+
// against. The page intentionally renders even when both resources
|
|
174
|
+
// and travelers are empty so operators can seed the per-departure
|
|
175
|
+
// resource block before any bookings exist — setting up the room
|
|
176
|
+
// block before selling is the canonical flow. The per-kind resource
|
|
177
|
+
// view handles its own empty state (and the "Add resource" /
|
|
178
|
+
// "Generate resources" affordances stay reachable).
|
|
179
|
+
if (!data) {
|
|
141
180
|
return (_jsxs("div", { className: cn("flex flex-col items-center justify-center gap-3 p-8", className), children: [_jsx(Users, { className: "size-6 text-muted-foreground", "aria-hidden": "true" }), _jsx("p", { className: "text-sm text-muted-foreground", children: messages.empty })] }));
|
|
142
181
|
}
|
|
143
182
|
const isSeatMap = activeKind === VEHICLE_SEAT_KIND;
|
|
@@ -151,12 +190,39 @@ export function SlotAllocationPage({ slotId, className, onBack, renderExtraActio
|
|
|
151
190
|
travelers,
|
|
152
191
|
allocationKinds,
|
|
153
192
|
};
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
|
195
|
+
? messages.generatingResources
|
|
196
|
+
: 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
|
+
? messages.autoAllocating
|
|
198
|
+
: messages.autoAllocate] })), !selectedExtraTab && canManuallyAddResource ? (_jsxs(Button, { variant: "outline", onClick: () => {
|
|
199
|
+
setResourceCapacity(defaultCapacityFor(activeKind));
|
|
200
|
+
setAddingResource((value) => !value);
|
|
201
|
+
}, 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 ?? [] })] }));
|
|
203
|
+
}
|
|
204
|
+
function CapacitySummaryBadges({ summary, messages, kind, }) {
|
|
205
|
+
if (summary.resourceCount === 0 && summary.slotPax == null)
|
|
206
|
+
return null;
|
|
207
|
+
const slotLabel = summary.slotPax == null
|
|
208
|
+
? messages.slotCapacityUnlimited
|
|
209
|
+
: `${summary.slotRemainingPax ?? 0}/${summary.slotPax}`;
|
|
210
|
+
const resourceLabel = summary.slotPax == null
|
|
211
|
+
? 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] }));
|
|
162
228
|
}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import type { AllocationManifestTraveler, AllocationResource } from "@voyantjs/availability-react";
|
|
2
|
-
import type
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
3
|
import { type AllocationOccupants } from "./slot-allocation-model.js";
|
|
4
|
-
export
|
|
4
|
+
export interface EditResourceInput {
|
|
5
|
+
label: string | null;
|
|
6
|
+
capacity: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function ResourceColumnsView({ kind, resources, travelers, occupants, sharingGroupLabels, onAssignTraveler, onUnassignTraveler, onRemoveResource, onEditResource, renderTravelerActions, }: {
|
|
5
9
|
kind: string;
|
|
6
10
|
resources: AllocationResource[];
|
|
7
11
|
travelers: AllocationManifestTraveler[];
|
|
8
12
|
occupants: AllocationOccupants;
|
|
9
13
|
sharingGroupLabels: Record<string, string>;
|
|
10
|
-
|
|
14
|
+
/** Assign a single unallocated traveler to a specific resource. */
|
|
15
|
+
onAssignTraveler: (travelerId: string, resourceId: string) => void;
|
|
16
|
+
/** Remove a single traveler from their current resource. */
|
|
17
|
+
onUnassignTraveler: (travelerId: string) => void;
|
|
11
18
|
onRemoveResource: (resourceId: string) => void;
|
|
19
|
+
onEditResource?: (resourceId: string, input: EditResourceInput) => Promise<void> | void;
|
|
12
20
|
renderTravelerActions?: (traveler: AllocationManifestTraveler) => ReactNode;
|
|
13
21
|
}): import("react/jsx-runtime").JSX.Element;
|
|
14
22
|
//# sourceMappingURL=slot-allocation-resource-view.d.ts.map
|
|
@@ -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;
|
|
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,16 +1,77 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Badge, Button } from "@voyantjs/ui/components";
|
|
4
|
-
import {
|
|
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";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
5
6
|
import { useAllocationUiMessagesOrDefault } from "../i18n/index.js";
|
|
6
|
-
import { kindLabel,
|
|
7
|
-
import {
|
|
8
|
-
export function ResourceColumnsView({ kind, resources, travelers, occupants, sharingGroupLabels,
|
|
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, }) {
|
|
9
10
|
const messages = useAllocationUiMessagesOrDefault();
|
|
10
|
-
return (_jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(18rem,22rem)_1fr]", children: [_jsx(
|
|
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));
|
|
14
|
+
})) })] }));
|
|
11
15
|
}
|
|
12
|
-
function
|
|
16
|
+
function ResourceGroupTable({ kind, resources, occupants, unallocated, sharingGroupLabels, onAssignTraveler, onUnassignTraveler, onRemoveResource, onEditResource, }) {
|
|
13
17
|
const messages = useAllocationUiMessagesOrDefault();
|
|
14
|
-
const
|
|
15
|
-
return (
|
|
18
|
+
const [editingId, setEditingId] = useState(null);
|
|
19
|
+
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
|
+
const seated = occupants.byResource.get(resource.id) ?? [];
|
|
21
|
+
const isEditing = editingId === resource.id;
|
|
22
|
+
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) => {
|
|
24
|
+
await Promise.resolve(onEditResource?.(resource.id, input));
|
|
25
|
+
setEditingId(null);
|
|
26
|
+
}, onAssignTraveler: (travelerId) => onAssignTraveler(travelerId, resource.id), onUnassignTraveler: onUnassignTraveler, onRemoveResource: () => onRemoveResource(resource.id) }, resource.id));
|
|
27
|
+
}) })] }) }));
|
|
28
|
+
}
|
|
29
|
+
function ResourceRow({ kind, resource, seated, unallocated, sharingGroupLabels, isEditing, isFull, canEdit, onBeginEdit, onCancelEdit, onSaveEdit, onAssignTraveler, onUnassignTraveler, onRemoveResource, }) {
|
|
30
|
+
const messages = useAllocationUiMessagesOrDefault();
|
|
31
|
+
const overCapacity = seated.length > resource.capacity;
|
|
32
|
+
if (isEditing && canEdit) {
|
|
33
|
+
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
|
+
}
|
|
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
|
|
36
|
+
? (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" }) })] }) })] }));
|
|
38
|
+
}
|
|
39
|
+
function TravelerChip({ traveler, sharingGroupLabel, onUnassign, }) {
|
|
40
|
+
const messages = useAllocationUiMessagesOrDefault();
|
|
41
|
+
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
|
+
}
|
|
43
|
+
function AssignTravelerPopover({ unallocated, onSelect, }) {
|
|
44
|
+
const messages = useAllocationUiMessagesOrDefault();
|
|
45
|
+
const [open, setOpen] = useState(false);
|
|
46
|
+
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))) })] })] }) })] }));
|
|
51
|
+
}
|
|
52
|
+
function ResourceEditForm({ kind, resource, minCapacity, onCancel, onSave, }) {
|
|
53
|
+
const messages = useAllocationUiMessagesOrDefault();
|
|
54
|
+
const [label, setLabel] = useState(resource.label ?? "");
|
|
55
|
+
const [capacity, setCapacity] = useState(resource.capacity);
|
|
56
|
+
const [saving, setSaving] = useState(false);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
setLabel(resource.label ?? "");
|
|
59
|
+
setCapacity(resource.capacity);
|
|
60
|
+
}, [resource.label, resource.capacity]);
|
|
61
|
+
async function submit(event) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
const trimmed = label.trim();
|
|
64
|
+
const nextCapacity = Math.max(minCapacity, Math.floor(capacity) || minCapacity);
|
|
65
|
+
setSaving(true);
|
|
66
|
+
try {
|
|
67
|
+
await onSave({
|
|
68
|
+
label: trimmed.length === 0 ? null : trimmed,
|
|
69
|
+
capacity: nextCapacity,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
setSaving(false);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
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 })] }));
|
|
16
77
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { AllocationManifestTraveler, AllocationResource } from "@voyantjs/availability-react";
|
|
2
2
|
import { type ReactNode } from "react";
|
|
3
3
|
import { type AllocationOccupants } from "./slot-allocation-model.js";
|
|
4
|
-
export declare function VehicleSeatsView({ seats, vehicles, occupants, sharingGroupLabels,
|
|
4
|
+
export declare function VehicleSeatsView({ seats, vehicles, occupants, sharingGroupLabels, onAssignTraveler, onUnassignTraveler, renderTravelerActions, }: {
|
|
5
5
|
seats: AllocationResource[];
|
|
6
6
|
vehicles: AllocationResource[];
|
|
7
7
|
occupants: AllocationOccupants;
|
|
8
8
|
sharingGroupLabels: Record<string, string>;
|
|
9
|
-
|
|
9
|
+
/** Assign an unallocated traveler to a specific seat resource. */
|
|
10
|
+
onAssignTraveler: (travelerId: string, resourceId: string) => void;
|
|
11
|
+
/** Remove a traveler from their current seat (no resource id required). */
|
|
10
12
|
onUnassignTraveler: (travelerId: string) => void;
|
|
11
13
|
renderTravelerActions?: (traveler: AllocationManifestTraveler) => ReactNode;
|
|
12
14
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot-allocation-seat-view.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-seat-view.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"slot-allocation-seat-view.d.ts","sourceRoot":"","sources":["../../src/components/slot-allocation-seat-view.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AAoBlG,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAGhD,OAAO,EACL,KAAK,mBAAmB,EAIzB,MAAM,4BAA4B,CAAA;AAGnC,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,SAAS,EACT,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,GACtB,EAAE;IACD,KAAK,EAAE,kBAAkB,EAAE,CAAA;IAC3B,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,SAAS,EAAE,mBAAmB,CAAA;IAC9B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,kEAAkE;IAClE,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IAClE,2EAA2E;IAC3E,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,SAAS,CAAA;CAC5E,2CA2FA"}
|
|
@@ -1,37 +1,33 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Badge, Card, CardContent, CardHeader, CardTitle, cn } from "@voyantjs/ui/components";
|
|
4
|
-
import { Armchair, Crown, Users } from "lucide-react";
|
|
3
|
+
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, cn, Popover, PopoverContent, PopoverTrigger, } from "@voyantjs/ui/components";
|
|
4
|
+
import { Armchair, Crown, Users, X } from "lucide-react";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { useAllocationUiMessagesOrDefault } from "../i18n/index.js";
|
|
7
7
|
import { groupSeatsByVehicle, seatName, seatRows, } from "./slot-allocation-model.js";
|
|
8
|
-
import {
|
|
9
|
-
export function VehicleSeatsView({ seats, vehicles, occupants, sharingGroupLabels,
|
|
8
|
+
import { AllocationColumn, SeatPositionBadge, TravelerTile } from "./slot-allocation-shared.js";
|
|
9
|
+
export function VehicleSeatsView({ seats, vehicles, occupants, sharingGroupLabels, onAssignTraveler, onUnassignTraveler, renderTravelerActions, }) {
|
|
10
10
|
const messages = useAllocationUiMessagesOrDefault();
|
|
11
11
|
const groups = groupSeatsByVehicle(seats, vehicles, messages);
|
|
12
|
-
return (_jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(18rem,22rem)_1fr]", children: [_jsx(
|
|
12
|
+
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: occupants.byTravelerId.size, 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: "grid min-w-0 gap-4", children: seats.length === 0 ? (_jsx("div", { className: "rounded-md border border-dashed p-6 text-sm text-muted-foreground", children: messages.noSeats })) : (groups.map((group) => (_jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs(CardTitle, { className: "flex flex-wrap items-center gap-2 text-base", children: [_jsx(Armchair, { className: "size-4", "aria-hidden": "true" }), _jsx("span", { children: group.label }), _jsxs(Badge, { variant: "outline", children: [group.seats.filter((seat) => (occupants.byResource.get(seat.id) ?? []).length > 0).length, "/", group.seats.length] })] }) }), _jsx(CardContent, { children: _jsx("div", { className: "grid gap-2", children: seatRows(group.seats).map((row) => (_jsx("div", { className: "grid items-stretch gap-2", style: {
|
|
13
13
|
gridTemplateColumns: `repeat(${row.seats.length}, minmax(4.25rem, 1fr))`,
|
|
14
14
|
}, children: row.seats.map((seat) => {
|
|
15
15
|
const seatOccupants = occupants.byResource.get(seat.id) ?? [];
|
|
16
|
-
return (_jsx(VehicleSeatCell, { seat: seat, occupant: seatOccupants[0] ?? null, overflow: seatOccupants.length > 1, sharingGroupLabel: seatOccupants[0]?.sharingGroupId
|
|
16
|
+
return (_jsx(VehicleSeatCell, { seat: seat, occupant: seatOccupants[0] ?? null, overflow: seatOccupants.length > 1, unallocated: occupants.unallocated, sharingGroupLabel: seatOccupants[0]?.sharingGroupId
|
|
17
17
|
? sharingGroupLabels[seatOccupants[0].sharingGroupId]
|
|
18
|
-
: null,
|
|
18
|
+
: null, onAssignTraveler: (travelerId) => onAssignTraveler(travelerId, seat.id), onUnassignTraveler: onUnassignTraveler }, seat.id));
|
|
19
19
|
}) }, row.rowKey))) }) })] }, group.id)))) })] }));
|
|
20
20
|
}
|
|
21
|
-
function VehicleSeatCell({ seat, occupant, overflow, sharingGroupLabel,
|
|
21
|
+
function VehicleSeatCell({ seat, occupant, overflow, unallocated, sharingGroupLabel, onAssignTraveler, onUnassignTraveler, }) {
|
|
22
22
|
const messages = useAllocationUiMessagesOrDefault();
|
|
23
|
-
const [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const travelerId = event.dataTransfer.getData("text/plain");
|
|
28
|
-
if (travelerId)
|
|
29
|
-
onDropTraveler(travelerId);
|
|
23
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
24
|
+
const cellClasses = cn("flex min-h-24 flex-col rounded-md border bg-background p-2 text-left text-xs", overflow ? "border-destructive bg-destructive/5" /* i18n-literal-ok CSS class token */ : null);
|
|
25
|
+
if (occupant) {
|
|
26
|
+
return (_jsxs("div", { id: `seat:${seat.id}`, className: cellClasses, children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx("span", { className: "font-medium", children: seat.label ?? seatName(seat, messages) }), _jsx(SeatPositionBadge, { seat: seat })] }), _jsxs("div", { className: "mt-2 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-1", children: [occupant.isLeadTraveler ? (_jsx(Crown, { className: "size-3 text-amber-500", "aria-label": messages.lead })) : null, _jsx("span", { className: "truncate font-medium", children: occupant.fullName })] }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1 text-muted-foreground", children: [_jsx("span", { children: occupant.bookingNumber }), sharingGroupLabel ? (_jsx(Badge, { variant: "secondary", className: "max-w-full truncate text-[10px]", children: sharingGroupLabel })) : null] })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "mt-auto h-6 self-end px-1 text-xs", onClick: () => onUnassignTraveler(occupant.id), "aria-label": `${messages.remove}: ${occupant.fullName}`, children: _jsx(X, { className: "size-3", "aria-hidden": "true" }) })] }));
|
|
30
27
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}, onDragLeave: () => setOver(false), onDrop: onDrop, children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx("span", { className: "font-medium", children: seat.label ?? seatName(seat, messages) }), _jsx(SeatPositionBadge, { seat: seat })] }), occupant ? (_jsxs("div", { className: "mt-2 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-1", children: [occupant.isLeadTraveler ? (_jsx(Crown, { className: "size-3 text-amber-500", "aria-label": messages.lead })) : null, _jsx("span", { className: "truncate font-medium", children: occupant.fullName })] }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1 text-muted-foreground", children: [_jsx("span", { children: occupant.bookingNumber }), sharingGroupLabel ? (_jsx(Badge, { variant: "secondary", className: "max-w-full truncate text-[10px]", children: sharingGroupLabel })) : null] })] })) : (_jsx("div", { className: "mt-3 rounded border border-dashed p-2 text-muted-foreground", children: messages.dropHere }))] }));
|
|
28
|
+
const disabled = unallocated.length === 0;
|
|
29
|
+
return (_jsxs("div", { id: `seat:${seat.id}`, className: cellClasses, children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx("span", { className: "font-medium", children: seat.label ?? seatName(seat, messages) }), _jsx(SeatPositionBadge, { seat: seat })] }), _jsxs(Popover, { open: pickerOpen, onOpenChange: setPickerOpen, children: [_jsx(PopoverTrigger, { render: _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "mt-auto h-7 w-full justify-center rounded border border-dashed text-muted-foreground", disabled: disabled, "aria-label": messages.assignTraveler, children: disabled ? messages.assignTravelerEmpty : 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) => (_jsx(CommandItem, { value: `${traveler.fullName} ${traveler.bookingNumber}`, onSelect: () => {
|
|
30
|
+
onAssignTraveler(traveler.id);
|
|
31
|
+
setPickerOpen(false);
|
|
32
|
+
}, children: _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))) })] })] }) })] })] }));
|
|
37
33
|
}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import type { AllocationAuditLogEntry, AllocationManifestTraveler, AllocationResource } from "@voyantjs/availability-react";
|
|
2
|
-
import {
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
3
|
import { type ValidationIssue } from "./slot-allocation-model.js";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Passive grouping container used by the unallocated column and other
|
|
6
|
+
* card-shaped sections. Used to be a drop target during the drag-and-
|
|
7
|
+
* drop era; switched to click-to-allocate, so it's now a plain layout
|
|
8
|
+
* primitive. Kept as a named export so render-prop consumers can
|
|
9
|
+
* compose new sections without re-exporting Card pieces themselves.
|
|
10
|
+
*/
|
|
11
|
+
export declare function AllocationColumn({ id, icon, title, description, count, capacity, action, children, }: {
|
|
5
12
|
id: string;
|
|
6
13
|
icon: ReactNode;
|
|
7
14
|
title: string;
|
|
8
15
|
description: string;
|
|
9
16
|
count: number;
|
|
10
17
|
capacity: number;
|
|
11
|
-
disabled?: boolean;
|
|
12
18
|
action?: ReactNode;
|
|
13
19
|
children: ReactNode;
|
|
14
|
-
onDropTraveler: (travelerId: string) => void;
|
|
15
20
|
}): import("react/jsx-runtime").JSX.Element;
|
|
16
21
|
export declare function TravelerTile({ traveler, sharingGroupLabel, renderActions, }: {
|
|
17
22
|
traveler: AllocationManifestTraveler;
|
|
@@ -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,
|
|
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,35 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Badge, Card, CardContent, CardHeader, CardTitle
|
|
3
|
+
import { Badge, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
|
|
4
4
|
import { Accessibility, CircleAlert, Crown, History, UtensilsCrossed } from "lucide-react";
|
|
5
|
-
import { useState } from "react";
|
|
6
5
|
import { useAllocationUiMessagesOrDefault } from "../i18n/index.js";
|
|
7
6
|
import { flagString } from "./slot-allocation-model.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
onDropTraveler(travelerId);
|
|
18
|
-
}
|
|
19
|
-
return (_jsxs(Card, { id: id, className: cn("min-h-40 transition-colors", over && !disabled
|
|
20
|
-
? "border-primary bg-primary/5" /* i18n-literal-ok CSS class token */
|
|
21
|
-
: null), onDragOver: (event) => {
|
|
22
|
-
event.preventDefault();
|
|
23
|
-
if (!disabled)
|
|
24
|
-
setOver(true);
|
|
25
|
-
}, onDragLeave: () => setOver(false), onDrop: onDrop, children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-3 space-y-0", children: [_jsxs("div", { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [icon, title] }), _jsx("p", { className: "text-xs text-muted-foreground", children: description })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Badge, { variant: count > capacity ? "destructive" : "outline", children: [count, "/", capacity] }), action] })] }), _jsx(CardContent, { className: "flex flex-col gap-2", children: children })] }));
|
|
7
|
+
/**
|
|
8
|
+
* Passive grouping container used by the unallocated column and other
|
|
9
|
+
* card-shaped sections. Used to be a drop target during the drag-and-
|
|
10
|
+
* drop era; switched to click-to-allocate, so it's now a plain layout
|
|
11
|
+
* primitive. Kept as a named export so render-prop consumers can
|
|
12
|
+
* compose new sections without re-exporting Card pieces themselves.
|
|
13
|
+
*/
|
|
14
|
+
export function AllocationColumn({ id, icon, title, description, count, capacity, action, children, }) {
|
|
15
|
+
return (_jsxs(Card, { id: id, className: "min-h-40", children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-3 space-y-0", children: [_jsxs("div", { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [icon, title] }), _jsx("p", { className: "text-xs text-muted-foreground", children: description })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Badge, { variant: count > capacity ? "destructive" : "outline", children: [count, "/", capacity] }), action] })] }), _jsx(CardContent, { className: "flex flex-col gap-2", children: children })] }));
|
|
26
16
|
}
|
|
27
17
|
export function TravelerTile({ traveler, sharingGroupLabel, renderActions, }) {
|
|
28
18
|
const messages = useAllocationUiMessagesOrDefault();
|
|
29
|
-
return (_jsxs("div", {
|
|
30
|
-
event.dataTransfer.effectAllowed = "move";
|
|
31
|
-
event.dataTransfer.setData("text/plain", traveler.id);
|
|
32
|
-
}, className: "group flex cursor-grab items-start justify-between gap-3 rounded-md border bg-background p-3 text-sm shadow-sm active:cursor-grabbing", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [traveler.isLeadTraveler ? (_jsx(Crown, { className: "size-3.5 text-amber-500", "aria-label": messages.lead })) : null, _jsx("span", { className: "truncate font-medium", children: traveler.fullName }), traveler.sharingGroupId ? (_jsx(Badge, { variant: "secondary", className: "max-w-full truncate text-[10px]", children: sharingGroupLabel ?? messages.sharingGroup })) : null] }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: traveler.bookingNumber }), traveler.roomTypeId ? _jsx("span", { children: traveler.roomTypeId }) : null, traveler.bedPreference ? _jsx("span", { children: traveler.bedPreference }) : null, traveler.hasAccessibilityNeeds ? (_jsx(Accessibility, { className: "size-3.5", "aria-label": messages.accessibility })) : null, traveler.hasDietaryRequirements ? (_jsx(UtensilsCrossed, { className: "size-3.5", "aria-label": messages.dietary })) : null] })] }), renderActions ? _jsx("div", { className: "shrink-0", children: renderActions(traveler) }) : null] }));
|
|
19
|
+
return (_jsxs("div", { className: "group flex items-start justify-between gap-3 rounded-md border bg-background p-3 text-sm shadow-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [traveler.isLeadTraveler ? (_jsx(Crown, { className: "size-3.5 text-amber-500", "aria-label": messages.lead })) : null, _jsx("span", { className: "truncate font-medium", children: traveler.fullName }), traveler.sharingGroupId ? (_jsx(Badge, { variant: "secondary", className: "max-w-full truncate text-[10px]", children: sharingGroupLabel ?? messages.sharingGroup })) : null] }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: traveler.bookingNumber }), traveler.roomTypeId ? _jsx("span", { children: traveler.roomTypeId }) : null, traveler.bedPreference ? _jsx("span", { children: traveler.bedPreference }) : null, traveler.hasAccessibilityNeeds ? (_jsx(Accessibility, { className: "size-3.5", "aria-label": messages.accessibility })) : null, traveler.hasDietaryRequirements ? (_jsx(UtensilsCrossed, { className: "size-3.5", "aria-label": messages.dietary })) : null] })] }), renderActions ? _jsx("div", { className: "shrink-0", children: renderActions(traveler) }) : null] }));
|
|
33
20
|
}
|
|
34
21
|
export function ValidationSummary({ issues, resources, unallocatedCount, }) {
|
|
35
22
|
const messages = useAllocationUiMessagesOrDefault();
|
package/dist/i18n/provider.d.ts
CHANGED
|
@@ -24,9 +24,17 @@ export type AllocationUiMessages = Record<string, unknown> & {
|
|
|
24
24
|
resourceLabel: string;
|
|
25
25
|
resourceCapacity: string;
|
|
26
26
|
createResource: string;
|
|
27
|
+
editResource: string;
|
|
28
|
+
saveResource: string;
|
|
29
|
+
updateResourceFailed: string;
|
|
27
30
|
cancel: string;
|
|
28
31
|
unallocated: string;
|
|
29
32
|
unallocatedDescription: string;
|
|
33
|
+
unallocatedEmpty: string;
|
|
34
|
+
assignTraveler: string;
|
|
35
|
+
assignTravelerSearch: string;
|
|
36
|
+
assignTravelerEmpty: string;
|
|
37
|
+
resourceOtherGroup: string;
|
|
30
38
|
rooms: string;
|
|
31
39
|
resources: string;
|
|
32
40
|
vehicleSeats: string;
|
|
@@ -44,6 +52,13 @@ export type AllocationUiMessages = Record<string, unknown> & {
|
|
|
44
52
|
remove: string;
|
|
45
53
|
overCapacity: string;
|
|
46
54
|
dropHere: string;
|
|
55
|
+
slotCapacityLabel: string;
|
|
56
|
+
slotCapacityUnlimited: string;
|
|
57
|
+
resourceCapacityLabel: string;
|
|
58
|
+
resourceCapacityFits: string;
|
|
59
|
+
resourceCapacityExact: string;
|
|
60
|
+
resourceCapacityOver: string;
|
|
61
|
+
overCapacityWarning: string;
|
|
47
62
|
noRooms: string;
|
|
48
63
|
noResources: string;
|
|
49
64
|
noSeats: string;
|
|
@@ -98,9 +113,17 @@ export declare const allocationUiEn: {
|
|
|
98
113
|
resourceLabel: string;
|
|
99
114
|
resourceCapacity: string;
|
|
100
115
|
createResource: string;
|
|
116
|
+
editResource: string;
|
|
117
|
+
saveResource: string;
|
|
118
|
+
updateResourceFailed: string;
|
|
101
119
|
cancel: string;
|
|
102
120
|
unallocated: string;
|
|
103
121
|
unallocatedDescription: string;
|
|
122
|
+
unallocatedEmpty: string;
|
|
123
|
+
assignTraveler: string;
|
|
124
|
+
assignTravelerSearch: string;
|
|
125
|
+
assignTravelerEmpty: string;
|
|
126
|
+
resourceOtherGroup: string;
|
|
104
127
|
rooms: string;
|
|
105
128
|
resources: string;
|
|
106
129
|
vehicleSeats: string;
|
|
@@ -118,6 +141,13 @@ export declare const allocationUiEn: {
|
|
|
118
141
|
remove: string;
|
|
119
142
|
overCapacity: string;
|
|
120
143
|
dropHere: string;
|
|
144
|
+
slotCapacityLabel: string;
|
|
145
|
+
slotCapacityUnlimited: string;
|
|
146
|
+
resourceCapacityLabel: string;
|
|
147
|
+
resourceCapacityFits: string;
|
|
148
|
+
resourceCapacityExact: string;
|
|
149
|
+
resourceCapacityOver: string;
|
|
150
|
+
overCapacityWarning: string;
|
|
121
151
|
noRooms: string;
|
|
122
152
|
noResources: string;
|
|
123
153
|
noSeats: string;
|
|
@@ -172,9 +202,17 @@ export declare const allocationUiRo: {
|
|
|
172
202
|
resourceLabel: string;
|
|
173
203
|
resourceCapacity: string;
|
|
174
204
|
createResource: string;
|
|
205
|
+
editResource: string;
|
|
206
|
+
saveResource: string;
|
|
207
|
+
updateResourceFailed: string;
|
|
175
208
|
cancel: string;
|
|
176
209
|
unallocated: string;
|
|
177
210
|
unallocatedDescription: string;
|
|
211
|
+
unallocatedEmpty: string;
|
|
212
|
+
assignTraveler: string;
|
|
213
|
+
assignTravelerSearch: string;
|
|
214
|
+
assignTravelerEmpty: string;
|
|
215
|
+
resourceOtherGroup: string;
|
|
178
216
|
rooms: string;
|
|
179
217
|
resources: string;
|
|
180
218
|
vehicleSeats: string;
|
|
@@ -192,6 +230,13 @@ export declare const allocationUiRo: {
|
|
|
192
230
|
remove: string;
|
|
193
231
|
overCapacity: string;
|
|
194
232
|
dropHere: string;
|
|
233
|
+
slotCapacityLabel: string;
|
|
234
|
+
slotCapacityUnlimited: string;
|
|
235
|
+
resourceCapacityLabel: string;
|
|
236
|
+
resourceCapacityFits: string;
|
|
237
|
+
resourceCapacityExact: string;
|
|
238
|
+
resourceCapacityOver: string;
|
|
239
|
+
overCapacityWarning: string;
|
|
195
240
|
noRooms: string;
|
|
196
241
|
noResources: string;
|
|
197
242
|
noSeats: string;
|
|
@@ -247,9 +292,17 @@ export declare const allocationUiMessageDefinitions: {
|
|
|
247
292
|
resourceLabel: string;
|
|
248
293
|
resourceCapacity: string;
|
|
249
294
|
createResource: string;
|
|
295
|
+
editResource: string;
|
|
296
|
+
saveResource: string;
|
|
297
|
+
updateResourceFailed: string;
|
|
250
298
|
cancel: string;
|
|
251
299
|
unallocated: string;
|
|
252
300
|
unallocatedDescription: string;
|
|
301
|
+
unallocatedEmpty: string;
|
|
302
|
+
assignTraveler: string;
|
|
303
|
+
assignTravelerSearch: string;
|
|
304
|
+
assignTravelerEmpty: string;
|
|
305
|
+
resourceOtherGroup: string;
|
|
253
306
|
rooms: string;
|
|
254
307
|
resources: string;
|
|
255
308
|
vehicleSeats: string;
|
|
@@ -267,6 +320,13 @@ export declare const allocationUiMessageDefinitions: {
|
|
|
267
320
|
remove: string;
|
|
268
321
|
overCapacity: string;
|
|
269
322
|
dropHere: string;
|
|
323
|
+
slotCapacityLabel: string;
|
|
324
|
+
slotCapacityUnlimited: string;
|
|
325
|
+
resourceCapacityLabel: string;
|
|
326
|
+
resourceCapacityFits: string;
|
|
327
|
+
resourceCapacityExact: string;
|
|
328
|
+
resourceCapacityOver: string;
|
|
329
|
+
overCapacityWarning: string;
|
|
270
330
|
noRooms: string;
|
|
271
331
|
noResources: string;
|
|
272
332
|
noSeats: string;
|
|
@@ -321,9 +381,17 @@ export declare const allocationUiMessageDefinitions: {
|
|
|
321
381
|
resourceLabel: string;
|
|
322
382
|
resourceCapacity: string;
|
|
323
383
|
createResource: string;
|
|
384
|
+
editResource: string;
|
|
385
|
+
saveResource: string;
|
|
386
|
+
updateResourceFailed: string;
|
|
324
387
|
cancel: string;
|
|
325
388
|
unallocated: string;
|
|
326
389
|
unallocatedDescription: string;
|
|
390
|
+
unallocatedEmpty: string;
|
|
391
|
+
assignTraveler: string;
|
|
392
|
+
assignTravelerSearch: string;
|
|
393
|
+
assignTravelerEmpty: string;
|
|
394
|
+
resourceOtherGroup: string;
|
|
327
395
|
rooms: string;
|
|
328
396
|
resources: string;
|
|
329
397
|
vehicleSeats: string;
|
|
@@ -341,6 +409,13 @@ export declare const allocationUiMessageDefinitions: {
|
|
|
341
409
|
remove: string;
|
|
342
410
|
overCapacity: string;
|
|
343
411
|
dropHere: string;
|
|
412
|
+
slotCapacityLabel: string;
|
|
413
|
+
slotCapacityUnlimited: string;
|
|
414
|
+
resourceCapacityLabel: string;
|
|
415
|
+
resourceCapacityFits: string;
|
|
416
|
+
resourceCapacityExact: string;
|
|
417
|
+
resourceCapacityOver: string;
|
|
418
|
+
overCapacityWarning: string;
|
|
344
419
|
noRooms: string;
|
|
345
420
|
noResources: string;
|
|
346
421
|
noSeats: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/i18n/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EAEtB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC3D,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,kBAAkB,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/i18n/provider.tsx"],"names":[],"mappings":"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"}
|
package/dist/i18n/provider.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createLocaleFormatters, createPackageMessagesContext, resolvePackageMes
|
|
|
4
4
|
export const allocationUiEn = {
|
|
5
5
|
pageTitle: "Allocation",
|
|
6
6
|
loading: "Loading allocation...",
|
|
7
|
-
empty: "
|
|
7
|
+
empty: "Allocation data unavailable for this departure.",
|
|
8
8
|
back: "Back",
|
|
9
9
|
addRoom: "Add room",
|
|
10
10
|
addResource: "Add resource",
|
|
@@ -37,9 +37,17 @@ export const allocationUiEn = {
|
|
|
37
37
|
resourceLabel: "Resource label",
|
|
38
38
|
resourceCapacity: "Capacity",
|
|
39
39
|
createResource: "Create resource",
|
|
40
|
+
editResource: "Edit resource",
|
|
41
|
+
saveResource: "Save",
|
|
42
|
+
updateResourceFailed: "Could not update resource.",
|
|
40
43
|
cancel: "Cancel",
|
|
41
44
|
unallocated: "Unallocated",
|
|
42
45
|
unallocatedDescription: "Travelers not assigned to this resource kind.",
|
|
46
|
+
unallocatedEmpty: "Everyone has been assigned.",
|
|
47
|
+
assignTraveler: "Assign",
|
|
48
|
+
assignTravelerSearch: "Search traveler...",
|
|
49
|
+
assignTravelerEmpty: "No unallocated travelers.",
|
|
50
|
+
resourceOtherGroup: "Other",
|
|
43
51
|
rooms: "Rooms",
|
|
44
52
|
resources: "Resources",
|
|
45
53
|
vehicleSeats: "Vehicle seats",
|
|
@@ -57,6 +65,13 @@ export const allocationUiEn = {
|
|
|
57
65
|
remove: "Remove",
|
|
58
66
|
overCapacity: "Resource is full",
|
|
59
67
|
dropHere: "Drop traveler here",
|
|
68
|
+
slotCapacityLabel: "Slot pax",
|
|
69
|
+
slotCapacityUnlimited: "Unlimited",
|
|
70
|
+
resourceCapacityLabel: "Resource capacity",
|
|
71
|
+
resourceCapacityFits: "fits in slot",
|
|
72
|
+
resourceCapacityExact: "matches slot",
|
|
73
|
+
resourceCapacityOver: "over slot cap",
|
|
74
|
+
overCapacityWarning: "Resource capacity exceeds the slot's pax cap.",
|
|
60
75
|
noRooms: "No rooms have been added for this slot.",
|
|
61
76
|
noResources: "No resources have been added for this slot.",
|
|
62
77
|
noSeats: "No vehicle seats have been generated for this slot.",
|
|
@@ -78,7 +93,7 @@ export const allocationUiEn = {
|
|
|
78
93
|
export const allocationUiRo = {
|
|
79
94
|
pageTitle: "Alocare",
|
|
80
95
|
loading: "Se incarca alocarea...",
|
|
81
|
-
empty: "
|
|
96
|
+
empty: "Datele de alocare nu sunt disponibile pentru aceasta plecare.",
|
|
82
97
|
back: "Inapoi",
|
|
83
98
|
addRoom: "Adauga camera",
|
|
84
99
|
addResource: "Adauga resursa",
|
|
@@ -111,9 +126,17 @@ export const allocationUiRo = {
|
|
|
111
126
|
resourceLabel: "Eticheta resursa",
|
|
112
127
|
resourceCapacity: "Capacitate",
|
|
113
128
|
createResource: "Creeaza resursa",
|
|
129
|
+
editResource: "Editeaza resursa",
|
|
130
|
+
saveResource: "Salveaza",
|
|
131
|
+
updateResourceFailed: "Resursa nu a putut fi actualizata.",
|
|
114
132
|
cancel: "Anuleaza",
|
|
115
133
|
unallocated: "Nealocati",
|
|
116
134
|
unallocatedDescription: "Calatori fara acest tip de resursa alocat.",
|
|
135
|
+
unallocatedEmpty: "Toti calatorii sunt alocati.",
|
|
136
|
+
assignTraveler: "Aloca",
|
|
137
|
+
assignTravelerSearch: "Cauta calator...",
|
|
138
|
+
assignTravelerEmpty: "Nu exista calatori nealocati.",
|
|
139
|
+
resourceOtherGroup: "Altele",
|
|
117
140
|
rooms: "Camere",
|
|
118
141
|
resources: "Resurse",
|
|
119
142
|
vehicleSeats: "Locuri vehicul",
|
|
@@ -131,6 +154,13 @@ export const allocationUiRo = {
|
|
|
131
154
|
remove: "Scoate",
|
|
132
155
|
overCapacity: "Resursa este plina",
|
|
133
156
|
dropHere: "Trage calatorul aici",
|
|
157
|
+
slotCapacityLabel: "Pax slot",
|
|
158
|
+
slotCapacityUnlimited: "Nelimitat",
|
|
159
|
+
resourceCapacityLabel: "Capacitate resurse",
|
|
160
|
+
resourceCapacityFits: "incape in slot",
|
|
161
|
+
resourceCapacityExact: "egal cu slotul",
|
|
162
|
+
resourceCapacityOver: "depaseste slotul",
|
|
163
|
+
overCapacityWarning: "Capacitatea resurselor depaseste limita de pax a slotului.",
|
|
134
164
|
noRooms: "Nu exista camere adaugate pentru acest slot.",
|
|
135
165
|
noResources: "Nu exista resurse adaugate pentru acest slot.",
|
|
136
166
|
noSeats: "Nu exista locuri generate pentru acest slot.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/allocation-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.1",
|
|
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.
|
|
38
|
-
"@voyantjs/i18n": "0.
|
|
39
|
-
"@voyantjs/ui": "0.
|
|
37
|
+
"@voyantjs/availability-react": "0.51.1",
|
|
38
|
+
"@voyantjs/i18n": "0.51.1",
|
|
39
|
+
"@voyantjs/ui": "0.51.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@tanstack/react-query": "^5.90.12",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"react-dom": "^19.2.4",
|
|
48
48
|
"typescript": "^6.0.2",
|
|
49
49
|
"vitest": "^4.1.2",
|
|
50
|
-
"@voyantjs/availability-react": "0.
|
|
51
|
-
"@voyantjs/i18n": "0.
|
|
52
|
-
"@voyantjs/ui": "0.
|
|
50
|
+
"@voyantjs/availability-react": "0.51.1",
|
|
51
|
+
"@voyantjs/i18n": "0.51.1",
|
|
52
|
+
"@voyantjs/ui": "0.51.1",
|
|
53
53
|
"@voyantjs/voyant-typescript-config": "0.1.0"
|
|
54
54
|
},
|
|
55
55
|
"files": [
|