kaafil-react-uikit 0.1.0-beta.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/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/OfflineFallbackScreen-B5dfaJBN.d.ts +37 -0
- package/dist/OfflineFallbackScreen-Ctm7nBa-.d.cts +37 -0
- package/dist/SessionExpiredScreen-CytQNuak.d.cts +50 -0
- package/dist/SessionExpiredScreen-D8GHyZc3.d.ts +50 -0
- package/dist/admin/index.cjs +23779 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.d.cts +1157 -0
- package/dist/admin/index.d.ts +1157 -0
- package/dist/admin/index.js +23724 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/chunk-6FPAGGFL.js +6028 -0
- package/dist/chunk-6FPAGGFL.js.map +1 -0
- package/dist/chunk-ACVU55PZ.cjs +294 -0
- package/dist/chunk-ACVU55PZ.cjs.map +1 -0
- package/dist/chunk-EM7JV3D7.cjs +6051 -0
- package/dist/chunk-EM7JV3D7.cjs.map +1 -0
- package/dist/chunk-IIPQ4JNC.js +287 -0
- package/dist/chunk-IIPQ4JNC.js.map +1 -0
- package/dist/chunk-LL243STJ.cjs +2195 -0
- package/dist/chunk-LL243STJ.cjs.map +1 -0
- package/dist/chunk-PJRZCOFF.cjs +958 -0
- package/dist/chunk-PJRZCOFF.cjs.map +1 -0
- package/dist/chunk-PTQ2CJKE.cjs +40 -0
- package/dist/chunk-PTQ2CJKE.cjs.map +1 -0
- package/dist/chunk-PZKXLQSU.js +10 -0
- package/dist/chunk-PZKXLQSU.js.map +1 -0
- package/dist/chunk-QHUDOVKW.cjs +111 -0
- package/dist/chunk-QHUDOVKW.cjs.map +1 -0
- package/dist/chunk-TOBZFADB.cjs +12 -0
- package/dist/chunk-TOBZFADB.cjs.map +1 -0
- package/dist/chunk-UUGMNP27.js +936 -0
- package/dist/chunk-UUGMNP27.js.map +1 -0
- package/dist/chunk-UYJ6LIWP.js +2154 -0
- package/dist/chunk-UYJ6LIWP.js.map +1 -0
- package/dist/chunk-VHTSTUCJ.cjs +346 -0
- package/dist/chunk-VHTSTUCJ.cjs.map +1 -0
- package/dist/chunk-WUAFL77X.js +340 -0
- package/dist/chunk-WUAFL77X.js.map +1 -0
- package/dist/chunk-XI7MK4Y7.js +38 -0
- package/dist/chunk-XI7MK4Y7.js.map +1 -0
- package/dist/chunk-YDGR7JSY.js +107 -0
- package/dist/chunk-YDGR7JSY.js.map +1 -0
- package/dist/core/index.cjs +1254 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +1267 -0
- package/dist/core/index.d.ts +1267 -0
- package/dist/core/index.js +995 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.css +1 -0
- package/dist/manager/index.cjs +13562 -0
- package/dist/manager/index.cjs.map +1 -0
- package/dist/manager/index.d.cts +1054 -0
- package/dist/manager/index.d.ts +1054 -0
- package/dist/manager/index.js +13518 -0
- package/dist/manager/index.js.map +1 -0
- package/dist/testing/index.cjs +221 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +269 -0
- package/dist/testing/index.d.ts +269 -0
- package/dist/testing/index.js +208 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tokenStyle-CToG5oOS.d.cts +3 -0
- package/dist/tokenStyle-CToG5oOS.d.ts +3 -0
- package/dist/traveller/index.cjs +1799 -0
- package/dist/traveller/index.cjs.map +1 -0
- package/dist/traveller/index.d.cts +58 -0
- package/dist/traveller/index.d.ts +58 -0
- package/dist/traveller/index.js +1789 -0
- package/dist/traveller/index.js.map +1 -0
- package/dist/useAgencyTrip-BUYSIaC2.d.cts +454 -0
- package/dist/useAgencyTrip-BzyJB1D9.d.ts +454 -0
- package/dist/useNotifications-BZW1Z_ur.d.cts +356 -0
- package/dist/useNotifications-D262i5BQ.d.ts +356 -0
- package/dist/useVendors-DXOuZWhZ.d.cts +784 -0
- package/dist/useVendors-DXOuZWhZ.d.ts +784 -0
- package/package.json +95 -0
|
@@ -0,0 +1,1157 @@
|
|
|
1
|
+
export { B as BootErrorScreen, a as BootErrorScreenProps, b as BootFailureMode, O as OfflineFallbackScreen, c as OfflineFallbackScreenProps } from '../OfflineFallbackScreen-Ctm7nBa-.cjs';
|
|
2
|
+
export { L as LockedTripScreen, a as LockedTripScreenProps, N as NotFoundScreen, b as NotFoundScreenProps, S as SessionExpiredScreen, c as SessionExpiredScreenProps } from '../SessionExpiredScreen-CytQNuak.cjs';
|
|
3
|
+
import { ReactNode, ComponentType } from 'react';
|
|
4
|
+
import { K as KaafilTokenOverrides } from '../tokenStyle-CToG5oOS.cjs';
|
|
5
|
+
import { C as ChecklistTemplateRow, a as ChecklistTemplateItemRow, F as FormDetailRow, b as FormFieldRow, c as FormRow, M as ManagerListRowResponse, T as TravellerDirectoryItemResponse, J as JourneyTriggerRow, A as AgencyTripRow, d as TripManifestRow, e as CollectionDeltaRow, f as AgencyTripRead } from '../useAgencyTrip-BUYSIaC2.cjs';
|
|
6
|
+
export { g as AgencySettingsDocument, B as FormBuilderBindingRow, h as FormSectionRow } from '../useAgencyTrip-BUYSIaC2.cjs';
|
|
7
|
+
import { ManagerAgencyProfileResponse, TravellerAgencyProfileResponse, BookingListRow, PickupStopDeltaRow, RoomingRoomDeltaRow, SeatingVehicleDeltaRow } from 'kaafil-js/client';
|
|
8
|
+
export { BookingListRow, ManagerAgencyProfileResponse, SeatingOccupant, TravellerAgencyProfileResponse, AgencyTripSegmentCounts as TripDirectorySegmentCounts, TripManifestFacets } from 'kaafil-js/client';
|
|
9
|
+
import { C as ChecklistSectionRow, a as ChecklistsReady, F as FileListItem, E as ExpenseDeltaRow, b as FloatMovementRow, T as TripFormRow, c as TripFormResponseRow, I as ItineraryDayRow, d as ItineraryItemRow, R as RoomingStayWindowRow, e as TripVendorRow } from '../useVendors-DXOuZWhZ.cjs';
|
|
10
|
+
export { f as FloatSummaryRow } from '../useVendors-DXOuZWhZ.cjs';
|
|
11
|
+
|
|
12
|
+
type PlanLockedReason = 'agency' | 'partner-ceiling';
|
|
13
|
+
interface PlanLockedScreenProps {
|
|
14
|
+
/** The capability this route was headed for. Read for its own triple
|
|
15
|
+
* only — never exhaustively switched on (`§6` point 5's open-set rule). */
|
|
16
|
+
readonly capabilityKey: string;
|
|
17
|
+
/** The host's own upsell/support flow — Kaafil has no in-product upgrade
|
|
18
|
+
* path to hand off to. `undefined` means the wire did not say which
|
|
19
|
+
* queue this belongs in; see this file's header. */
|
|
20
|
+
readonly onUpgradeRequest: (reason: PlanLockedReason | undefined) => void;
|
|
21
|
+
/** Supplied by the caller when it saw a refusal that named one. */
|
|
22
|
+
readonly reason?: PlanLockedReason;
|
|
23
|
+
readonly components?: {
|
|
24
|
+
readonly PlanLockedScreenUI?: () => ReactNode;
|
|
25
|
+
};
|
|
26
|
+
readonly style?: KaafilTokenOverrides;
|
|
27
|
+
}
|
|
28
|
+
declare function PlanLockedScreen({ capabilityKey, onUpgradeRequest, reason, components, style, }: PlanLockedScreenProps): ReactNode;
|
|
29
|
+
|
|
30
|
+
/** One field's current draft state. `set: false` means this field is
|
|
31
|
+
* omitted from the outgoing PATCH entirely (inherits the Kaafil default) —
|
|
32
|
+
* `value` is meaningless in that case, kept only so re-enabling the field
|
|
33
|
+
* restores the last value the admin had entered rather than resetting to
|
|
34
|
+
* blank. `value: null` is itself a real, explicit state (`set: true`) for
|
|
35
|
+
* the one field where the wire distinguishes "no default" from "explicitly
|
|
36
|
+
* unlimited" (`files.ttlDays`). */
|
|
37
|
+
interface FieldEntry {
|
|
38
|
+
readonly set: boolean;
|
|
39
|
+
readonly value: boolean | number | string | readonly string[] | null;
|
|
40
|
+
}
|
|
41
|
+
type SectionDraft = Record<string, FieldEntry>;
|
|
42
|
+
interface FieldOption {
|
|
43
|
+
readonly value: string;
|
|
44
|
+
readonly labelKey: string;
|
|
45
|
+
}
|
|
46
|
+
interface ToggleFieldDef {
|
|
47
|
+
readonly kind: 'toggle';
|
|
48
|
+
readonly key: string;
|
|
49
|
+
readonly labelKey: string;
|
|
50
|
+
readonly hintKey?: string;
|
|
51
|
+
}
|
|
52
|
+
interface NumberFieldDef {
|
|
53
|
+
readonly kind: 'number';
|
|
54
|
+
readonly key: string;
|
|
55
|
+
readonly labelKey: string;
|
|
56
|
+
readonly hintKey?: string;
|
|
57
|
+
readonly min?: number;
|
|
58
|
+
/** When true, this field's wire type is `number | null` and the UI offers
|
|
59
|
+
* a distinct explicit-null state (a checkbox), not just blank-vs-number. */
|
|
60
|
+
readonly nullable?: boolean;
|
|
61
|
+
readonly nullLabelKey?: string;
|
|
62
|
+
readonly visibleWhen?: (draft: SectionDraft) => boolean;
|
|
63
|
+
}
|
|
64
|
+
interface SelectFieldDef {
|
|
65
|
+
readonly kind: 'select';
|
|
66
|
+
readonly key: string;
|
|
67
|
+
readonly labelKey: string;
|
|
68
|
+
readonly options: readonly FieldOption[];
|
|
69
|
+
readonly visibleWhen?: (draft: SectionDraft) => boolean;
|
|
70
|
+
}
|
|
71
|
+
interface ArrayFieldDef {
|
|
72
|
+
readonly kind: 'array';
|
|
73
|
+
readonly key: string;
|
|
74
|
+
readonly labelKey: string;
|
|
75
|
+
readonly options: readonly FieldOption[];
|
|
76
|
+
/** Order carries real meaning on the wire (e.g. `rooming.strategyOrder` —
|
|
77
|
+
* the solver applies strategies in this order) — show move-up/move-down
|
|
78
|
+
* controls only when true. */
|
|
79
|
+
readonly reorderable?: boolean;
|
|
80
|
+
}
|
|
81
|
+
type FieldDef = ToggleFieldDef | NumberFieldDef | SelectFieldDef | ArrayFieldDef;
|
|
82
|
+
interface SectionDef {
|
|
83
|
+
readonly id: string;
|
|
84
|
+
/** A presentation-only grouping label (`settings-shell.tsx`'s own
|
|
85
|
+
* "Workspace" / "Your account" / "Advanced" grouping is the identical
|
|
86
|
+
* device) — purely a nav-rail organizing aid, never a real category on
|
|
87
|
+
* the wire; `Agency.settings` itself has no concept of a section group. */
|
|
88
|
+
readonly groupTitleKey: string;
|
|
89
|
+
readonly titleKey: string;
|
|
90
|
+
readonly descriptionKey: string;
|
|
91
|
+
readonly fields: readonly FieldDef[];
|
|
92
|
+
}
|
|
93
|
+
/** `'conflict'` — another write landed first (`CONFLICT_VERSION`); the only
|
|
94
|
+
* safe recovery is discarding this card's local draft and re-reading the
|
|
95
|
+
* server's value, never silently overwriting it. `'failed'` — any other
|
|
96
|
+
* save failure (network, validation); the local draft is still valid, so
|
|
97
|
+
* the recovery is just "try again", no reseed. */
|
|
98
|
+
type SectionSaveErrorKind = 'conflict' | 'failed';
|
|
99
|
+
interface SectionCardProps {
|
|
100
|
+
readonly section: SectionDef;
|
|
101
|
+
/** The stored section object (`document.settings[section.id]`), or
|
|
102
|
+
* `undefined` if this agency has never set anything in this section. */
|
|
103
|
+
readonly stored: Record<string, unknown> | undefined;
|
|
104
|
+
readonly saving: boolean;
|
|
105
|
+
readonly errorKind: SectionSaveErrorKind | undefined;
|
|
106
|
+
readonly onSave: (sectionId: string, patch: Record<string, unknown>) => void;
|
|
107
|
+
/** Fired when the admin dismisses the error banner — for `'conflict'` the
|
|
108
|
+
* parent refreshes and this card reseeds its draft from the fresh value;
|
|
109
|
+
* for `'failed'` the parent just clears the error and the draft is left
|
|
110
|
+
* untouched so the admin can retry the same edit. */
|
|
111
|
+
readonly onDismissError: () => void;
|
|
112
|
+
readonly style?: KaafilTokenOverrides;
|
|
113
|
+
}
|
|
114
|
+
interface AgencySettingsScreenProps {
|
|
115
|
+
readonly title?: string;
|
|
116
|
+
readonly subtitle?: string;
|
|
117
|
+
readonly components?: {
|
|
118
|
+
readonly SectionCard?: ComponentType<SectionCardProps>;
|
|
119
|
+
};
|
|
120
|
+
readonly style?: KaafilTokenOverrides;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
declare function AgencySettingsScreen({ title, subtitle, components, style, }: AgencySettingsScreenProps): ReactNode;
|
|
124
|
+
|
|
125
|
+
/** A locally-edited draft of one item — the same shape `items[]` needs to
|
|
126
|
+
* become on save (`PatchChecklistTemplateRequest`'s item shape: `key`,
|
|
127
|
+
* `title`, `subLine`, `isMandatory`, `gate`, `dayOffset` — no `id`/
|
|
128
|
+
* `sortOrder`, array order IS sort order), plus a local-only `draftId` so
|
|
129
|
+
* React can key a not-yet-saved row before the server assigns a real `id`. */
|
|
130
|
+
interface ChecklistTemplateItemDraft {
|
|
131
|
+
readonly draftId: string;
|
|
132
|
+
readonly key: string;
|
|
133
|
+
readonly title: string;
|
|
134
|
+
readonly subLine: string;
|
|
135
|
+
readonly isMandatory: boolean;
|
|
136
|
+
readonly gate: ChecklistTemplateItemRow['gate'];
|
|
137
|
+
readonly dayOffset: string;
|
|
138
|
+
}
|
|
139
|
+
interface TemplateEditorHeaderProps {
|
|
140
|
+
readonly template: ChecklistTemplateRow;
|
|
141
|
+
readonly title: string;
|
|
142
|
+
readonly onTitleChange: (title: string) => void;
|
|
143
|
+
readonly hasChanges: boolean;
|
|
144
|
+
readonly saving: boolean;
|
|
145
|
+
readonly onSave: () => void;
|
|
146
|
+
readonly canPublish: boolean;
|
|
147
|
+
readonly publishing: boolean;
|
|
148
|
+
readonly onPublish: () => void;
|
|
149
|
+
readonly style?: KaafilTokenOverrides;
|
|
150
|
+
}
|
|
151
|
+
interface TemplateItemRowProps {
|
|
152
|
+
readonly item: ChecklistTemplateItemDraft;
|
|
153
|
+
readonly index: number;
|
|
154
|
+
readonly total: number;
|
|
155
|
+
readonly onEdit: (draftId: string) => void;
|
|
156
|
+
readonly onRemove: (draftId: string) => void;
|
|
157
|
+
readonly onReorder: (draggedDraftId: string, targetDraftId: string) => void;
|
|
158
|
+
readonly style?: KaafilTokenOverrides;
|
|
159
|
+
}
|
|
160
|
+
interface ChecklistTemplateEditorProps {
|
|
161
|
+
readonly templateId: string;
|
|
162
|
+
readonly onSaved?: (template: ChecklistTemplateRow) => void;
|
|
163
|
+
readonly onPublished?: (template: ChecklistTemplateRow) => void;
|
|
164
|
+
readonly components?: {
|
|
165
|
+
readonly TemplateEditorHeader?: ComponentType<TemplateEditorHeaderProps>;
|
|
166
|
+
readonly TemplateItemRow?: ComponentType<TemplateItemRowProps>;
|
|
167
|
+
};
|
|
168
|
+
readonly style?: KaafilTokenOverrides;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
declare function ChecklistTemplateEditor({ templateId, onSaved, onPublished, components, style, }: ChecklistTemplateEditorProps): ReactNode;
|
|
172
|
+
|
|
173
|
+
interface TemplateRowProps {
|
|
174
|
+
readonly template: ChecklistTemplateRow;
|
|
175
|
+
readonly selected?: boolean;
|
|
176
|
+
readonly onSelect?: (template: ChecklistTemplateRow) => void;
|
|
177
|
+
readonly onDelete?: (template: ChecklistTemplateRow) => void;
|
|
178
|
+
readonly style?: KaafilTokenOverrides;
|
|
179
|
+
}
|
|
180
|
+
interface ChecklistTemplateLibraryProps {
|
|
181
|
+
/** Optional page-head title — rendered above the toolbar. Omit when this
|
|
182
|
+
* composite is mounted under a shell that already renders its own page
|
|
183
|
+
* title (e.g. `TemplateAuthoringFlow`'s own master column), same
|
|
184
|
+
* convention `ManagerDirectoryFlow` establishes. */
|
|
185
|
+
readonly title?: string;
|
|
186
|
+
/** Optional supporting copy under `title` — omitted whenever `title` is. */
|
|
187
|
+
readonly subtitle?: string;
|
|
188
|
+
/** The currently-selected template, if any — highlights that row. Purely
|
|
189
|
+
* presentational; this composite never owns selection state itself. */
|
|
190
|
+
readonly selectedTemplateId?: string;
|
|
191
|
+
readonly onSelectTemplate?: (template: ChecklistTemplateRow) => void;
|
|
192
|
+
/** Fires after a successful delete — `templateId` of the removed row. */
|
|
193
|
+
readonly onDeleted?: (templateId: string) => void;
|
|
194
|
+
/** Controlled create-dialog visibility — set when a host shell (e.g.
|
|
195
|
+
* `KaafilAgencyWorkspace`) renders the "New template" action as its own
|
|
196
|
+
* page-level primary CTA rather than duplicating a create button inside
|
|
197
|
+
* this composite's own card header (the mockup's own generic page-header
|
|
198
|
+
* pattern: every pane's `cta` sits beside its `<h1>`, never inside the
|
|
199
|
+
* card below it). Omit both to fall back to this composite's own
|
|
200
|
+
* internal button + state, for standalone/story usage. */
|
|
201
|
+
readonly creating?: boolean;
|
|
202
|
+
readonly onCreatingChange?: (next: boolean) => void;
|
|
203
|
+
readonly components?: {
|
|
204
|
+
readonly TemplateRow?: ComponentType<TemplateRowProps>;
|
|
205
|
+
};
|
|
206
|
+
readonly style?: KaafilTokenOverrides;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
declare function ChecklistTemplateLibrary({ title, subtitle, selectedTemplateId, onSelectTemplate, onDeleted, creating: controlledCreating, onCreatingChange, components, style, }: ChecklistTemplateLibraryProps): ReactNode;
|
|
210
|
+
|
|
211
|
+
type FormFieldKind = 'HEADING' | 'INFO_BLOCK' | 'SHORT_TEXT' | 'LONG_TEXT' | 'EMAIL' | 'PHONE' | 'URL' | 'NUMBER' | 'CURRENCY' | 'RATING' | 'NPS' | 'DATE' | 'DATETIME' | 'TIME' | 'SELECT' | 'MULTISELECT' | 'CHECKBOX' | 'CONSENT' | 'SIGNATURE' | 'FILE_UPLOAD' | 'ADDRESS';
|
|
212
|
+
|
|
213
|
+
interface BuilderHeaderProps {
|
|
214
|
+
readonly form: FormDetailRow;
|
|
215
|
+
readonly title: string;
|
|
216
|
+
readonly onTitleChange: (title: string) => void;
|
|
217
|
+
readonly onTitleCommit: () => void;
|
|
218
|
+
readonly onDelete?: () => void;
|
|
219
|
+
readonly onArchive: () => void;
|
|
220
|
+
readonly onUnarchive: () => void;
|
|
221
|
+
readonly onPublish: () => void;
|
|
222
|
+
readonly onClose: () => void;
|
|
223
|
+
readonly onReopen: () => void;
|
|
224
|
+
readonly pending: boolean;
|
|
225
|
+
readonly style?: KaafilTokenOverrides;
|
|
226
|
+
}
|
|
227
|
+
interface FieldRowProps {
|
|
228
|
+
readonly field: FormFieldRow;
|
|
229
|
+
readonly kind: FormFieldKind;
|
|
230
|
+
/** `true` while THIS row is the one being dragged — dims it in place. */
|
|
231
|
+
readonly dragging: boolean;
|
|
232
|
+
/** `true` while a drag is hovering over this row — shows the drop-target
|
|
233
|
+
* indicator. */
|
|
234
|
+
readonly dragOver: boolean;
|
|
235
|
+
/** Opens `FieldEditDialog` for this field — a read-only summary row,
|
|
236
|
+
* mockup's own `f.edit` anatomy (`TemplateItemRow.tsx`'s established
|
|
237
|
+
* split: rows summarize, a dialog edits). */
|
|
238
|
+
readonly onOpenSettings: () => void;
|
|
239
|
+
readonly onDragStart: () => void;
|
|
240
|
+
readonly onDragOverRow: () => void;
|
|
241
|
+
readonly onDrop: () => void;
|
|
242
|
+
readonly onDragEnd: () => void;
|
|
243
|
+
readonly style?: KaafilTokenOverrides;
|
|
244
|
+
}
|
|
245
|
+
interface FormBuilderProps {
|
|
246
|
+
readonly formId: string;
|
|
247
|
+
readonly onBack?: () => void;
|
|
248
|
+
readonly components?: {
|
|
249
|
+
readonly BuilderHeader?: ComponentType<BuilderHeaderProps>;
|
|
250
|
+
readonly FieldRow?: ComponentType<FieldRowProps>;
|
|
251
|
+
};
|
|
252
|
+
readonly style?: KaafilTokenOverrides;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
declare function FormBuilder({ formId, onBack, components, style }: FormBuilderProps): ReactNode;
|
|
256
|
+
|
|
257
|
+
interface FormsAuthoringFlowProps {
|
|
258
|
+
/** Deep-link: skip the library and open this form's builder directly on
|
|
259
|
+
* mount. Omit to start on the library. */
|
|
260
|
+
readonly initialFormId?: string;
|
|
261
|
+
readonly style?: KaafilTokenOverrides;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
declare function FormsAuthoringFlow({ initialFormId, style }: FormsAuthoringFlowProps): ReactNode;
|
|
265
|
+
|
|
266
|
+
interface FormRowProps$1 {
|
|
267
|
+
readonly form: FormRow;
|
|
268
|
+
readonly selected?: boolean;
|
|
269
|
+
readonly onSelect?: (form: FormRow) => void;
|
|
270
|
+
readonly onArchive?: (form: FormRow) => void;
|
|
271
|
+
readonly onUnarchive?: (form: FormRow) => void;
|
|
272
|
+
readonly onPublish?: (form: FormRow) => void;
|
|
273
|
+
readonly onClose?: (form: FormRow) => void;
|
|
274
|
+
readonly onReopen?: (form: FormRow) => void;
|
|
275
|
+
readonly onDelete?: (form: FormRow) => void;
|
|
276
|
+
readonly style?: KaafilTokenOverrides;
|
|
277
|
+
}
|
|
278
|
+
interface FormsLibraryProps {
|
|
279
|
+
/** Optional page-head title — rendered above the toolbar. Omit when this
|
|
280
|
+
* composite is mounted under a shell that already renders its own page
|
|
281
|
+
* title, same convention `ChecklistTemplateLibrary` establishes. */
|
|
282
|
+
readonly title?: string;
|
|
283
|
+
/** Optional supporting copy under `title` — omitted whenever `title` is. */
|
|
284
|
+
readonly subtitle?: string;
|
|
285
|
+
/** The currently-selected form, if any — highlights that row. Purely
|
|
286
|
+
* presentational; this composite never owns selection state itself. */
|
|
287
|
+
readonly selectedFormId?: string;
|
|
288
|
+
readonly onSelectForm?: (form: FormRow) => void;
|
|
289
|
+
/** Controlled create-dialog visibility — set when a host shell (e.g.
|
|
290
|
+
* `KaafilAgencyWorkspace`) renders the "New form" action as its own
|
|
291
|
+
* page-level primary CTA rather than duplicating a create button inside
|
|
292
|
+
* this composite's own card header (the mockup's own generic page-header
|
|
293
|
+
* pattern: every pane's `cta` sits beside its `<h1>`, never inside the
|
|
294
|
+
* card below it). Omit both to fall back to this composite's own
|
|
295
|
+
* internal button + state, for standalone/story usage. */
|
|
296
|
+
readonly creating?: boolean;
|
|
297
|
+
readonly onCreatingChange?: (next: boolean) => void;
|
|
298
|
+
readonly components?: {
|
|
299
|
+
readonly FormRow?: ComponentType<FormRowProps$1>;
|
|
300
|
+
};
|
|
301
|
+
readonly style?: KaafilTokenOverrides;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
declare function FormsLibrary({ title, subtitle, selectedFormId, onSelectForm, creating: controlledCreating, onCreatingChange, components, style, }: FormsLibraryProps): ReactNode;
|
|
305
|
+
|
|
306
|
+
interface ManagerResultRowProps {
|
|
307
|
+
readonly manager: ManagerListRowResponse;
|
|
308
|
+
/** This row's position in the current page of results — cycles the
|
|
309
|
+
* shared identity-avatar palette (`AdminAvatar`), offset by one from the
|
|
310
|
+
* traveller directory's own start so the two lists never coincidentally
|
|
311
|
+
* colour-match at the same row position. */
|
|
312
|
+
readonly index: number;
|
|
313
|
+
readonly onSelect?: (manager: ManagerListRowResponse) => void;
|
|
314
|
+
readonly style?: KaafilTokenOverrides;
|
|
315
|
+
}
|
|
316
|
+
interface ManagerDirectoryFlowProps {
|
|
317
|
+
/** Optional page-head title — rendered above the search bar. Omit when
|
|
318
|
+
* this flow is mounted under a shell that already renders its own page
|
|
319
|
+
* title (e.g. `KaafilAgencyWorkspace`'s "Managers" nav destination), same
|
|
320
|
+
* convention `TravellerDirectoryFlow` establishes. */
|
|
321
|
+
readonly title?: string;
|
|
322
|
+
/** Optional supporting copy under `title` — omitted whenever `title` is. */
|
|
323
|
+
readonly subtitle?: string;
|
|
324
|
+
readonly onSelectManager?: (manager: ManagerListRowResponse) => void;
|
|
325
|
+
readonly components?: {
|
|
326
|
+
readonly ManagerResultRow?: ComponentType<ManagerResultRowProps>;
|
|
327
|
+
};
|
|
328
|
+
readonly style?: KaafilTokenOverrides;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
declare function ManagerDirectoryFlow({ title, subtitle, onSelectManager, components, style, }: ManagerDirectoryFlowProps): ReactNode;
|
|
332
|
+
|
|
333
|
+
type ManagerAgencyProfileTripRow = ManagerAgencyProfileResponse['trips'][number];
|
|
334
|
+
interface ManagerProfileSectionProps {
|
|
335
|
+
readonly managerRef: string;
|
|
336
|
+
/** Renders a back affordance in the hero when supplied — omitted, the
|
|
337
|
+
* hero shows no back control (a host embedding this without its own
|
|
338
|
+
* "back to directory" concept, e.g. a modal). */
|
|
339
|
+
readonly onBack?: () => void;
|
|
340
|
+
readonly components?: {
|
|
341
|
+
readonly Hero?: ComponentType<{
|
|
342
|
+
readonly profile: ManagerAgencyProfileResponse;
|
|
343
|
+
readonly onBack?: () => void;
|
|
344
|
+
}>;
|
|
345
|
+
readonly AssignmentsCard?: ComponentType<{
|
|
346
|
+
readonly trips: readonly ManagerAgencyProfileTripRow[];
|
|
347
|
+
}>;
|
|
348
|
+
};
|
|
349
|
+
readonly style?: KaafilTokenOverrides;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
declare function ManagerProfileSection({ managerRef, onBack, components, style, }: ManagerProfileSectionProps): ReactNode;
|
|
353
|
+
|
|
354
|
+
interface TemplateAuthoringFlowProps {
|
|
355
|
+
/** Deep-link: skip the library and open this template's editor directly
|
|
356
|
+
* on mount. Omit to start on the library. */
|
|
357
|
+
readonly initialTemplateId?: string;
|
|
358
|
+
readonly style?: KaafilTokenOverrides;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
declare function TemplateAuthoringFlow({ initialTemplateId, style, }: TemplateAuthoringFlowProps): ReactNode;
|
|
362
|
+
|
|
363
|
+
interface TravellerSearchBoxProps {
|
|
364
|
+
/** An optional client-side floor — defaults to `0` (no floor; the server
|
|
365
|
+
* itself no longer requires a `q` at all). Set this when a host serving an
|
|
366
|
+
* unusually large agency wants to hold off issuing a request until the
|
|
367
|
+
* caller has typed a few characters. */
|
|
368
|
+
readonly minQueryLength?: number;
|
|
369
|
+
readonly debounceMs?: number;
|
|
370
|
+
readonly onSelectTraveller?: (traveller: TravellerDirectoryItemResponse) => void;
|
|
371
|
+
/** Rendered in the same row as the search input, right-aligned —
|
|
372
|
+
* `TravellerDirectoryFlow`'s own "Add traveller" button uses this so the
|
|
373
|
+
* two sit on one row instead of the search input stacking underneath a
|
|
374
|
+
* separate toolbar. Omit for a bare search box with nothing beside it. */
|
|
375
|
+
readonly trailingAction?: ReactNode;
|
|
376
|
+
readonly components?: {
|
|
377
|
+
readonly TravellerSearchBoxUI?: ComponentType<Record<string, never>>;
|
|
378
|
+
};
|
|
379
|
+
readonly style?: KaafilTokenOverrides;
|
|
380
|
+
}
|
|
381
|
+
interface TravellerDirectoryFlowProps {
|
|
382
|
+
/** Optional page-head title — rendered above the search bar, boilerplate's
|
|
383
|
+
* own "Trips" `<h1>` rhythm (`--kf-type-heading-xl-*`). Omit when this flow
|
|
384
|
+
* is mounted under a shell that already renders its own page title (e.g.
|
|
385
|
+
* `AdminShell`'s "Traveller" nav destination) so the two never double up —
|
|
386
|
+
* pass one only when this flow owns its own page head. */
|
|
387
|
+
readonly title?: string;
|
|
388
|
+
/** Optional supporting copy under `title` — omitted whenever `title` is. */
|
|
389
|
+
readonly subtitle?: string;
|
|
390
|
+
/** Omits the "Add traveller" affordance — for a host whose own CRM UI is
|
|
391
|
+
* the sole traveller-intake surface. */
|
|
392
|
+
readonly hideAddTraveller?: boolean;
|
|
393
|
+
/** Lifts the "Add traveller" affordance OUT of this card and into the
|
|
394
|
+
* host's own page header, where the design puts it. Supplying this pair
|
|
395
|
+
* makes the flow controlled: it renders no button of its own and opens
|
|
396
|
+
* the modal from `adding` instead. `ChecklistTemplateLibrary`/
|
|
397
|
+
* `FormsLibrary` expose the identical pair for the same reason. */
|
|
398
|
+
readonly adding?: boolean;
|
|
399
|
+
readonly onAddingChange?: (adding: boolean) => void;
|
|
400
|
+
readonly minQueryLength?: number;
|
|
401
|
+
readonly debounceMs?: number;
|
|
402
|
+
readonly onSelectTraveller?: (traveller: TravellerDirectoryItemResponse) => void;
|
|
403
|
+
/** Fires once a traveller is created via `AddTravellerModal`, with the
|
|
404
|
+
* real row the server returned. */
|
|
405
|
+
readonly onTravellerAdded?: (traveller: TravellerDirectoryItemResponse) => void;
|
|
406
|
+
readonly components?: {
|
|
407
|
+
readonly TravellerSearchBox?: ComponentType<TravellerSearchBoxProps>;
|
|
408
|
+
readonly AddTravellerModal?: ComponentType<{
|
|
409
|
+
readonly onClose: () => void;
|
|
410
|
+
readonly onCreated: (traveller: TravellerDirectoryItemResponse) => void;
|
|
411
|
+
}>;
|
|
412
|
+
};
|
|
413
|
+
readonly style?: KaafilTokenOverrides;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
declare function TravellerSearchBox({ minQueryLength, debounceMs, onSelectTraveller, trailingAction, style, }: TravellerSearchBoxProps): ReactNode;
|
|
417
|
+
declare function TravellerDirectoryFlow({ title, subtitle, hideAddTraveller, adding, onAddingChange, minQueryLength, debounceMs, onSelectTraveller, onTravellerAdded, components, style, }: TravellerDirectoryFlowProps): ReactNode;
|
|
418
|
+
|
|
419
|
+
type TravellerAgencyProfileTripRow = TravellerAgencyProfileResponse['trips'][number];
|
|
420
|
+
type TravellerAgencyProfileNoteRow = TravellerAgencyProfileResponse['notes'][number];
|
|
421
|
+
type TravellerAgencyProfileLifetime = TravellerAgencyProfileResponse['lifetime'];
|
|
422
|
+
interface TravellerProfileSectionProps {
|
|
423
|
+
readonly travellerRef: string;
|
|
424
|
+
/** Renders a back affordance in the hero when supplied — omitted, the
|
|
425
|
+
* hero shows no back control (a host embedding this without its own
|
|
426
|
+
* "back to directory" concept, e.g. a modal). */
|
|
427
|
+
readonly onBack?: () => void;
|
|
428
|
+
readonly components?: {
|
|
429
|
+
readonly Hero?: ComponentType<{
|
|
430
|
+
readonly profile: TravellerAgencyProfileResponse;
|
|
431
|
+
readonly onBack?: () => void;
|
|
432
|
+
}>;
|
|
433
|
+
readonly PersonalCard?: ComponentType<{
|
|
434
|
+
readonly profile: TravellerAgencyProfileResponse;
|
|
435
|
+
}>;
|
|
436
|
+
readonly LifetimeCard?: ComponentType<{
|
|
437
|
+
readonly lifetime: TravellerAgencyProfileLifetime;
|
|
438
|
+
}>;
|
|
439
|
+
readonly TripHistoryTable?: ComponentType<{
|
|
440
|
+
readonly trips: readonly TravellerAgencyProfileTripRow[];
|
|
441
|
+
}>;
|
|
442
|
+
readonly NotesList?: ComponentType<{
|
|
443
|
+
readonly notes: readonly TravellerAgencyProfileNoteRow[];
|
|
444
|
+
}>;
|
|
445
|
+
};
|
|
446
|
+
readonly style?: KaafilTokenOverrides;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
declare function TravellerProfileSection({ travellerRef, onBack, components, style, }: TravellerProfileSectionProps): ReactNode;
|
|
450
|
+
|
|
451
|
+
interface TriggerDetailPanelProps {
|
|
452
|
+
readonly trigger: JourneyTriggerRow;
|
|
453
|
+
readonly saving: boolean;
|
|
454
|
+
/** The header toggle — `enabled` is always live, never gated behind the
|
|
455
|
+
* edit dialog (matches the mockup's own `selToggle` sitting in the
|
|
456
|
+
* "SELECTED" card head, outside `dlgOpen`'s own scope). */
|
|
457
|
+
readonly onToggle: (next: boolean) => void;
|
|
458
|
+
/** Fired by `TriggerEditDialog`'s own Save — `anchor`/`offsetHours` only,
|
|
459
|
+
* `enabled` never travels through this path. */
|
|
460
|
+
readonly onSave: (input: {
|
|
461
|
+
readonly anchor: 'START' | 'END';
|
|
462
|
+
readonly offsetHours: number;
|
|
463
|
+
}) => void;
|
|
464
|
+
readonly phaseLabel: (phase: string) => string;
|
|
465
|
+
readonly style?: KaafilTokenOverrides;
|
|
466
|
+
}
|
|
467
|
+
interface TriggerTemplateEditorProps {
|
|
468
|
+
readonly title?: string;
|
|
469
|
+
readonly subtitle?: string;
|
|
470
|
+
/** Show only this trip mode's triggers. Omit to show both. */
|
|
471
|
+
readonly tripModeFilter?: 'GROUP' | 'PERSONALIZED';
|
|
472
|
+
readonly onTriggerPatched?: (trigger: JourneyTriggerRow) => void;
|
|
473
|
+
readonly style?: KaafilTokenOverrides;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
declare function TriggerTemplateEditor({ title, subtitle, tripModeFilter, onTriggerPatched, style, }: TriggerTemplateEditorProps): ReactNode;
|
|
477
|
+
|
|
478
|
+
/** A booking that is not a tombstone. `useBookings` hands both through so a
|
|
479
|
+
* caller can tell the difference; this tab only ever renders the live
|
|
480
|
+
* ones. */
|
|
481
|
+
type BookingLiveRow = Exclude<BookingListRow, {
|
|
482
|
+
readonly _tombstone: true;
|
|
483
|
+
}>;
|
|
484
|
+
type BookingKind = 'ACCOMMODATION' | 'TRANSPORT' | 'ACTIVITY';
|
|
485
|
+
interface TripBookingsSectionProps {
|
|
486
|
+
readonly tripRef: string;
|
|
487
|
+
/** Offered on a row that has vouchers, so an operator can get to the
|
|
488
|
+
* files themselves. Navigation is a callback — the UIKit ships no
|
|
489
|
+
* router (hard rule #9). Absent means the affordance is not offered. */
|
|
490
|
+
readonly onOpenDocuments?: () => void;
|
|
491
|
+
readonly style?: KaafilTokenOverrides;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
declare function TripBookingsSection({ tripRef, onOpenDocuments, style, }: TripBookingsSectionProps): ReactNode;
|
|
495
|
+
|
|
496
|
+
interface ChecklistItemRowProps {
|
|
497
|
+
readonly item: ChecklistItemLiveRow;
|
|
498
|
+
readonly readOnly: boolean;
|
|
499
|
+
readonly busy: boolean;
|
|
500
|
+
readonly onToggle: () => void;
|
|
501
|
+
readonly onEdit: () => void;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
interface PhasePanelProps {
|
|
505
|
+
readonly phase: ChecklistPhase;
|
|
506
|
+
readonly sections: readonly ChecklistSectionRow[];
|
|
507
|
+
readonly items: readonly ChecklistItemLiveRow[];
|
|
508
|
+
readonly readOnly: boolean;
|
|
509
|
+
readonly busyItemId: string | null;
|
|
510
|
+
readonly onToggle: (item: ChecklistItemLiveRow) => void;
|
|
511
|
+
readonly onEdit: (item: ChecklistItemLiveRow) => void;
|
|
512
|
+
readonly onAddToSection: (section: ChecklistSectionRow) => void;
|
|
513
|
+
readonly onAddToPhase: (phase: ChecklistPhase) => void;
|
|
514
|
+
readonly ItemRow?: ComponentType<ChecklistItemRowProps>;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** One row of `useChecklists()`'s `items[]` — a live item or a tombstone. */
|
|
518
|
+
type ChecklistItemDeltaRow = ChecklistsReady['items'][number];
|
|
519
|
+
/** `ChecklistItemDeltaRow`, narrowed to the live shape — a
|
|
520
|
+
* deleted-since-last-cursor row carries no `title`/`status`/etc. at all. */
|
|
521
|
+
type ChecklistItemLiveRow = Exclude<ChecklistItemDeltaRow, {
|
|
522
|
+
readonly _tombstone: true;
|
|
523
|
+
}>;
|
|
524
|
+
type ChecklistPhase = ChecklistSectionRow['phase'];
|
|
525
|
+
type ChecklistGate = ChecklistItemLiveRow['gate'];
|
|
526
|
+
interface TripChecklistSectionProps {
|
|
527
|
+
/** The trip this section reads `useChecklists(tripRef)` for. */
|
|
528
|
+
readonly tripRef: string;
|
|
529
|
+
/** Forces every toggle/add/edit/delete affordance inert. The `423`
|
|
530
|
+
* close-out lock drives this internally too. */
|
|
531
|
+
readonly readOnly?: boolean;
|
|
532
|
+
/** Opens with one phase focused rather than all three equal-width. */
|
|
533
|
+
readonly initialPhase?: ChecklistPhase;
|
|
534
|
+
/** Multi-select bulk toggle. Gate-carrying items are excluded from a
|
|
535
|
+
* selection regardless — see this file's header. */
|
|
536
|
+
readonly enableBulkToggle?: boolean;
|
|
537
|
+
/** Fires after a toggle LANDS, once per item even for a bulk toggle. */
|
|
538
|
+
readonly onItemToggled?: (itemId: string) => void;
|
|
539
|
+
/** Tier-4 named-component overrides. Each receives exactly the props this
|
|
540
|
+
* composite's own default renders it with. */
|
|
541
|
+
readonly components?: {
|
|
542
|
+
readonly PhasePanel?: ComponentType<PhasePanelProps>;
|
|
543
|
+
readonly ChecklistItemRow?: ComponentType<ChecklistItemRowProps>;
|
|
544
|
+
};
|
|
545
|
+
readonly style?: KaafilTokenOverrides;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
declare function TripChecklistSection({ tripRef, readOnly, initialPhase, onItemToggled, components, style, }: TripChecklistSectionProps): ReactNode;
|
|
549
|
+
|
|
550
|
+
/** `Trip.eventType`, derived off the row so it cannot drift from the wire. */
|
|
551
|
+
type TripEventType = AgencyTripRow['eventType'];
|
|
552
|
+
interface TripRowProps {
|
|
553
|
+
readonly trip: AgencyTripRow;
|
|
554
|
+
/** This row's position in the current page — cycles the shared
|
|
555
|
+
* identity-avatar palette so adjacent rows never colour-match. */
|
|
556
|
+
readonly index: number;
|
|
557
|
+
readonly onOpen: () => void;
|
|
558
|
+
readonly style?: KaafilTokenOverrides;
|
|
559
|
+
}
|
|
560
|
+
interface TripDirectoryFlowProps {
|
|
561
|
+
/** Opens one trip. Prop-presence gates the affordance: without it rows are
|
|
562
|
+
* inert text rather than buttons, for a host that embeds this list purely
|
|
563
|
+
* as a read-only overview. */
|
|
564
|
+
readonly onOpenTrip?: (trip: AgencyTripRow) => void;
|
|
565
|
+
/** Seeds the Tours/Treks chip — for a host deep-linking into the trek view.
|
|
566
|
+
* Uncontrolled after mount. */
|
|
567
|
+
readonly initialEventType?: TripEventType;
|
|
568
|
+
readonly components?: {
|
|
569
|
+
readonly TripRow?: ComponentType<TripRowProps>;
|
|
570
|
+
};
|
|
571
|
+
readonly style?: KaafilTokenOverrides;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
declare function TripDirectoryFlow({ onOpenTrip, initialEventType, components, style, }: TripDirectoryFlowProps): ReactNode;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* The spend facts a receipt row borrows. Deliberately narrow — this is what
|
|
578
|
+
* an expense row actually carries that helps NAME a document. Note the
|
|
579
|
+
* absence of a person: an expense has `category`/`amountMinor`/`currency`/
|
|
580
|
+
* `description`/`vendorName`/`spentAt` and no spender, so no "uploaded by"
|
|
581
|
+
* is claimed anywhere on this surface.
|
|
582
|
+
*/
|
|
583
|
+
interface DocumentSpend {
|
|
584
|
+
readonly description: string;
|
|
585
|
+
readonly vendorName: string | null;
|
|
586
|
+
readonly amountMinor: number;
|
|
587
|
+
readonly currency: string;
|
|
588
|
+
readonly spentAt: string;
|
|
589
|
+
readonly voidedAt: string | null;
|
|
590
|
+
}
|
|
591
|
+
/** One table row: the file, plus the spend it belongs to when one is known. */
|
|
592
|
+
interface DocumentRowModel {
|
|
593
|
+
readonly file: FileListItem;
|
|
594
|
+
/** `undefined` for every non-receipt, and for a receipt whose spend is not
|
|
595
|
+
* in the ledger this page read. Never a reason to drop the row. */
|
|
596
|
+
readonly spend: DocumentSpend | undefined;
|
|
597
|
+
}
|
|
598
|
+
interface TripDocumentsSectionProps {
|
|
599
|
+
readonly tripRef: string;
|
|
600
|
+
readonly style?: KaafilTokenOverrides;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
declare function TripDocumentsSection({ tripRef, style }: TripDocumentsSectionProps): ReactNode;
|
|
604
|
+
|
|
605
|
+
/** An expense that is not a tombstone. A VOID is not a tombstone — the row
|
|
606
|
+
* stays live with `voidedAt` set. */
|
|
607
|
+
type ExpenseLiveRow = Exclude<ExpenseDeltaRow, {
|
|
608
|
+
readonly _tombstone: true;
|
|
609
|
+
}>;
|
|
610
|
+
type ExpenseCategory = ExpenseLiveRow['category'];
|
|
611
|
+
type ExpensePaymentMode = ExpenseLiveRow['paymentMode'];
|
|
612
|
+
type ExpenseClaimStatus = NonNullable<ExpenseLiveRow['claimStatus']>;
|
|
613
|
+
/** What the ledger is narrowed to. All client-side: `listExpenses` takes
|
|
614
|
+
* `?since=` only, so there is no server filter to pass these to. */
|
|
615
|
+
interface ExpenseFilters {
|
|
616
|
+
readonly category: ExpenseCategory | null;
|
|
617
|
+
readonly paymentMode: ExpensePaymentMode | null;
|
|
618
|
+
readonly missingReceiptOnly: boolean;
|
|
619
|
+
}
|
|
620
|
+
interface TripExpensesSectionProps {
|
|
621
|
+
readonly tripRef: string;
|
|
622
|
+
readonly style?: KaafilTokenOverrides;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
declare function TripExpensesSection({ tripRef, style }: TripExpensesSectionProps): ReactNode;
|
|
626
|
+
|
|
627
|
+
type FloatMovementType = FloatMovementRow['type'];
|
|
628
|
+
type FloatDirection = FloatMovementRow['direction'];
|
|
629
|
+
/** Which write a dialog is collecting. `adjust` carries a direction; the
|
|
630
|
+
* other two are fixed by their route. */
|
|
631
|
+
type FloatAction = 'issue' | 'return' | 'adjust';
|
|
632
|
+
interface TripFloatSectionProps {
|
|
633
|
+
readonly tripRef: string;
|
|
634
|
+
readonly style?: KaafilTokenOverrides;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
declare function TripFloatSection({ tripRef, style }: TripFloatSectionProps): ReactNode;
|
|
638
|
+
|
|
639
|
+
interface FormRowProps {
|
|
640
|
+
readonly form: TripFormRow;
|
|
641
|
+
/** From `completion()`. `undefined` when that read has not landed or
|
|
642
|
+
* failed — the row then shows the submitted count ALONE rather than a
|
|
643
|
+
* fraction with an invented denominator. */
|
|
644
|
+
readonly total: number | undefined;
|
|
645
|
+
readonly readOnly: boolean;
|
|
646
|
+
readonly busy: boolean;
|
|
647
|
+
readonly onDispatch: () => void;
|
|
648
|
+
readonly onExport: () => void;
|
|
649
|
+
readonly onOpenMissing: () => void;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/** One line of the who-is-missing join: everyone on the manifest, with
|
|
653
|
+
* their response if they have one. */
|
|
654
|
+
interface FormParticipantRow {
|
|
655
|
+
readonly travellerId: string;
|
|
656
|
+
readonly fullName: string;
|
|
657
|
+
/** `undefined` for a traveller with NO response row — the point of the
|
|
658
|
+
* whole view, so it renders as a person who has not started, never as an
|
|
659
|
+
* absence from the list. */
|
|
660
|
+
readonly response: TripFormResponseRow | undefined;
|
|
661
|
+
}
|
|
662
|
+
interface TripFormsSectionProps {
|
|
663
|
+
readonly tripRef: string;
|
|
664
|
+
/** Forces every dispatch/capture affordance inert. */
|
|
665
|
+
readonly readOnly?: boolean;
|
|
666
|
+
readonly components?: {
|
|
667
|
+
readonly FormRow?: ComponentType<FormRowProps>;
|
|
668
|
+
};
|
|
669
|
+
readonly style?: KaafilTokenOverrides;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
declare function TripFormsSection({ tripRef, readOnly, components, style, }: TripFormsSectionProps): ReactNode;
|
|
673
|
+
|
|
674
|
+
type ItineraryViewMode = 'days' | 'timeline';
|
|
675
|
+
interface TripItinerarySectionProps {
|
|
676
|
+
readonly tripRef: string;
|
|
677
|
+
/**
|
|
678
|
+
* Host-owned navigation to a vendor. Omitted when the host has no vendor
|
|
679
|
+
* route — the item's vendor then renders as plain text rather than as a
|
|
680
|
+
* link that goes nowhere (hard rule #9: this package ships no router).
|
|
681
|
+
*/
|
|
682
|
+
readonly onOpenVendor?: (vendorId: string) => void;
|
|
683
|
+
readonly style?: KaafilTokenOverrides;
|
|
684
|
+
}
|
|
685
|
+
/** A day plus the items that belong to it, in `sortOrder`. */
|
|
686
|
+
interface ItineraryDayGroup {
|
|
687
|
+
readonly day: ItineraryDayRow;
|
|
688
|
+
readonly dayNumber: number;
|
|
689
|
+
readonly items: readonly ItineraryItemRow[];
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
declare function TripItinerarySection({ tripRef, onOpenVendor, style, }: TripItinerarySectionProps): ReactNode;
|
|
693
|
+
|
|
694
|
+
/** How the body is arranged. The reference's own two modes. */
|
|
695
|
+
type ManifestGroupBy = 'booking_group' | 'none';
|
|
696
|
+
/**
|
|
697
|
+
* Everything the filter panel can narrow by — a subset of what the endpoint
|
|
698
|
+
* accepts, holding only the axes a real control exists for.
|
|
699
|
+
*
|
|
700
|
+
* `partyId` and `sort` are endpoint parameters this panel drives from
|
|
701
|
+
* elsewhere (a group card's own "only this group" affordance, and the column
|
|
702
|
+
* head), so they are not in the panel's own state.
|
|
703
|
+
*/
|
|
704
|
+
interface ManifestFilterState {
|
|
705
|
+
readonly bookingStatus?: 'CONFIRMED' | 'TENTATIVE' | 'CANCELLED';
|
|
706
|
+
readonly balanceState?: 'paid' | 'due' | 'unbilled';
|
|
707
|
+
readonly gender?: 'MALE' | 'FEMALE' | 'OTHER' | 'UNKNOWN';
|
|
708
|
+
/** `true` narrows to travellers carrying a medical flag. Never `false` —
|
|
709
|
+
* "not flagged" is not a filter an operator asks for. */
|
|
710
|
+
readonly medical?: true;
|
|
711
|
+
}
|
|
712
|
+
interface TripManifestSectionProps {
|
|
713
|
+
readonly tripRef: string;
|
|
714
|
+
/**
|
|
715
|
+
* Opens the agency-wide traveller profile — a DIFFERENT screen from this
|
|
716
|
+
* tab's own trip-scoped detail dialog (`TravellerProfileSection` reads the
|
|
717
|
+
* cross-trip directory profile; the dialog reads this trip's). The
|
|
718
|
+
* reference's row kebab links to it in a new tab; this Surface ships no
|
|
719
|
+
* router (hard rule #9), so it is a callback the host wires to its own
|
|
720
|
+
* travellers pane. Absent means the affordance is not offered.
|
|
721
|
+
*/
|
|
722
|
+
readonly onOpenTravellerProfile?: (travellerId: string, fullName: string) => void;
|
|
723
|
+
readonly components?: {
|
|
724
|
+
readonly ManifestRow?: ComponentType<ManifestRowProps>;
|
|
725
|
+
};
|
|
726
|
+
readonly style?: KaafilTokenOverrides;
|
|
727
|
+
}
|
|
728
|
+
interface ManifestRowProps {
|
|
729
|
+
readonly row: TripManifestRow;
|
|
730
|
+
/** Row position, for the avatar palette cycle. */
|
|
731
|
+
readonly index: number;
|
|
732
|
+
/** The server's own "now", for deriving age from a date of birth. Threaded
|
|
733
|
+
* in rather than read from a local clock — hard rule #6's posture. */
|
|
734
|
+
readonly serverTime: string | undefined;
|
|
735
|
+
readonly onOpen: () => void;
|
|
736
|
+
readonly onRemove: () => void;
|
|
737
|
+
readonly onShareLink: () => void;
|
|
738
|
+
readonly onOpenProfile?: () => void;
|
|
739
|
+
readonly style?: KaafilTokenOverrides;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
declare function TripManifestSection({ tripRef, onOpenTravellerProfile, components, style, }: TripManifestSectionProps): ReactNode;
|
|
743
|
+
|
|
744
|
+
/** A collection that is not a tombstone. A VOID is not a tombstone — the row
|
|
745
|
+
* stays live with `voidedAt` set. A tombstone means the row was deleted at
|
|
746
|
+
* the source, which this API has no path to cause. */
|
|
747
|
+
type CollectionLiveRow = Exclude<CollectionDeltaRow, {
|
|
748
|
+
readonly _tombstone: true;
|
|
749
|
+
}>;
|
|
750
|
+
type CollectionMode = CollectionLiveRow['mode'];
|
|
751
|
+
type EligibleStatus = 'due' | 'settled' | 'all';
|
|
752
|
+
|
|
753
|
+
interface TripPaymentsSectionProps {
|
|
754
|
+
readonly tripRef: string;
|
|
755
|
+
/**
|
|
756
|
+
* The trip's own money rollup, straight from the trip read
|
|
757
|
+
* (`balance.{collectedMinor, totalMinor, dueMinor, currency}`). A prop
|
|
758
|
+
* rather than a second fetch: `TripWorkspace` already holds it, and two
|
|
759
|
+
* reads of the same figure is how two surfaces come to disagree about it.
|
|
760
|
+
*
|
|
761
|
+
* `undefined` when the trip has no balance rows at all — rendered as
|
|
762
|
+
* absent, never as ₹0 (`deriveTripHead.ts`'s own rule: "a trip with no
|
|
763
|
+
* balance rows has not collected ₹0, it has not been billed").
|
|
764
|
+
*/
|
|
765
|
+
readonly balance: {
|
|
766
|
+
readonly collectedMinor: number;
|
|
767
|
+
readonly totalMinor: number;
|
|
768
|
+
readonly dueMinor: number;
|
|
769
|
+
readonly currency: string;
|
|
770
|
+
} | undefined;
|
|
771
|
+
readonly style?: KaafilTokenOverrides;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
declare function TripPaymentsSection({ tripRef, balance, style, }: TripPaymentsSectionProps): ReactNode;
|
|
775
|
+
|
|
776
|
+
/** A stop that is not a tombstone. */
|
|
777
|
+
type PickupStopLiveRow = Exclude<PickupStopDeltaRow, {
|
|
778
|
+
readonly _tombstone: true;
|
|
779
|
+
}>;
|
|
780
|
+
type PickupKind = PickupStopLiveRow['kind'];
|
|
781
|
+
type PickupRollup = PickupStopLiveRow['rollup'];
|
|
782
|
+
/** Which of the two runs the switcher is showing. */
|
|
783
|
+
type PickupView = 'stops' | 'boarding';
|
|
784
|
+
interface TripPickupsSectionProps {
|
|
785
|
+
readonly tripRef: string;
|
|
786
|
+
/**
|
|
787
|
+
* The TRIP's IANA zone. `scheduledTime` is an instant and the DTO says to
|
|
788
|
+
* "render in `trip.timezone` — never assume the caller's own zone", and
|
|
789
|
+
* `pickups.list` does not carry the trip. Passed down by `TripWorkspace`,
|
|
790
|
+
* which already holds the trip read, rather than fetched a second time.
|
|
791
|
+
*/
|
|
792
|
+
readonly timeZone: string;
|
|
793
|
+
/**
|
|
794
|
+
* The trip's own start date, used to anchor a NEW stop's wall clock to a
|
|
795
|
+
* calendar day when the run is still empty and there is no sibling stop to
|
|
796
|
+
* take the day from.
|
|
797
|
+
*
|
|
798
|
+
* A prop rather than `new Date()`: hard rule #6 forbids a local clock in
|
|
799
|
+
* `/core` and the same reasoning holds one layer out — "today" on the
|
|
800
|
+
* operator's laptop is not a fact about this trip, and for a trip that has
|
|
801
|
+
* not departed it is the wrong day outright.
|
|
802
|
+
*/
|
|
803
|
+
readonly tripStartIso: string;
|
|
804
|
+
readonly style?: KaafilTokenOverrides;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
declare function TripPickupsSection({ tripRef, timeZone, tripStartIso, style, }: TripPickupsSectionProps): ReactNode;
|
|
808
|
+
|
|
809
|
+
/** A room that is not a tombstone. */
|
|
810
|
+
type RoomingRoomLiveRow = Exclude<RoomingRoomDeltaRow, {
|
|
811
|
+
readonly _tombstone: true;
|
|
812
|
+
}>;
|
|
813
|
+
type RoomingBed = RoomingRoomLiveRow['beds'][number];
|
|
814
|
+
type RoomingOccupant = NonNullable<RoomingBed['occupant']>;
|
|
815
|
+
/** A stay window that is not a tombstone. `stayWindows.list` is a DELTA feed
|
|
816
|
+
* like the rooms — with `?since=` it carries drops, so the row type is a
|
|
817
|
+
* union and the tombstone branch has to be narrowed away before anything
|
|
818
|
+
* reads a `label`. */
|
|
819
|
+
type StayWindowRow = Exclude<RoomingStayWindowRow, {
|
|
820
|
+
readonly _tombstone: true;
|
|
821
|
+
}>;
|
|
822
|
+
/** One stay window with the rooms that belong to it. */
|
|
823
|
+
interface RoomingWindowGroup {
|
|
824
|
+
readonly window: StayWindowRow;
|
|
825
|
+
readonly rooms: readonly RoomingRoomLiveRow[];
|
|
826
|
+
readonly bedCount: number;
|
|
827
|
+
readonly occupiedCount: number;
|
|
828
|
+
}
|
|
829
|
+
interface TripRoomingSectionProps {
|
|
830
|
+
readonly tripRef: string;
|
|
831
|
+
/**
|
|
832
|
+
* The TRIP's IANA zone. A stay window's `startDate`/`endDate` are instants,
|
|
833
|
+
* and the same rule the itinerary and pickups tabs follow applies: render
|
|
834
|
+
* in the trip's zone, never the reader's.
|
|
835
|
+
*/
|
|
836
|
+
readonly timeZone: string;
|
|
837
|
+
/**
|
|
838
|
+
* The trip's own first and last day. A stay window is a stretch of nights
|
|
839
|
+
* WITHIN a trip, so its date pickers are bounded to these — an unbounded
|
|
840
|
+
* calendar lets an operator pick a night the trip does not have, which the
|
|
841
|
+
* engine would then hold as a real window nobody is on.
|
|
842
|
+
*
|
|
843
|
+
* Props rather than a second read: `TripWorkspace` already holds the trip.
|
|
844
|
+
*/
|
|
845
|
+
readonly tripStartIso: string;
|
|
846
|
+
readonly tripEndIso: string;
|
|
847
|
+
readonly style?: KaafilTokenOverrides;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
declare function TripRoomingSection({ tripRef, timeZone, tripStartIso, tripEndIso, style, }: TripRoomingSectionProps): ReactNode;
|
|
851
|
+
|
|
852
|
+
type VendorCategory = TripVendorRow['category'];
|
|
853
|
+
/** Where one supplier is used on this trip. A count is `undefined`, never 0,
|
|
854
|
+
* when its capability is dark or its read has not landed — "we cannot see"
|
|
855
|
+
* and "there are none" are different facts and must not render alike. */
|
|
856
|
+
interface SupplierUsage {
|
|
857
|
+
readonly vehicles: number | undefined;
|
|
858
|
+
readonly itineraryItems: number | undefined;
|
|
859
|
+
readonly expenses: number | undefined;
|
|
860
|
+
}
|
|
861
|
+
/** The rows the usage scan reads, kept as one bag so `deriveSuppliers` takes
|
|
862
|
+
* a single argument rather than six. */
|
|
863
|
+
interface UsageSources {
|
|
864
|
+
readonly vehicles: readonly SeatingVehicleDeltaRow[] | undefined;
|
|
865
|
+
readonly itineraryItems: readonly ItineraryItemRow[] | undefined;
|
|
866
|
+
readonly expenses: readonly ExpenseDeltaRow[] | undefined;
|
|
867
|
+
}
|
|
868
|
+
type SupplierSortKey = 'name' | 'category' | 'city';
|
|
869
|
+
interface TripSuppliersSectionProps {
|
|
870
|
+
readonly tripRef: string;
|
|
871
|
+
readonly style?: KaafilTokenOverrides;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
declare function TripSuppliersSection({ tripRef, style }: TripSuppliersSectionProps): ReactNode;
|
|
875
|
+
|
|
876
|
+
/** A vehicle that is not a tombstone. */
|
|
877
|
+
type SeatingVehicleLiveRow = Exclude<SeatingVehicleDeltaRow, {
|
|
878
|
+
readonly _tombstone: true;
|
|
879
|
+
}>;
|
|
880
|
+
type SeatingSeat = SeatingVehicleLiveRow['seats'][number];
|
|
881
|
+
type VehicleType = SeatingVehicleLiveRow['type'];
|
|
882
|
+
type VehicleLayout = SeatingVehicleLiveRow['layout'];
|
|
883
|
+
|
|
884
|
+
/** One rendered row of a seat grid: the seats at one `rowIndex`, in `side`
|
|
885
|
+
* order, with the aisle position the layout implies. */
|
|
886
|
+
interface SeatRow {
|
|
887
|
+
readonly rowIndex: number;
|
|
888
|
+
readonly seats: readonly SeatingSeat[];
|
|
889
|
+
/** How many seats sit before the aisle. `0` means no aisle in this row —
|
|
890
|
+
* a bench seat spanning it, or a `FLAT` layout. */
|
|
891
|
+
readonly aisleAfter: number;
|
|
892
|
+
}
|
|
893
|
+
interface TripVehiclesSectionProps {
|
|
894
|
+
readonly tripRef: string;
|
|
895
|
+
/**
|
|
896
|
+
* The trip's day count, so `days[]` indexes can be named ("days 2–3") and
|
|
897
|
+
* the day picker can offer the days that exist. A prop rather than a second
|
|
898
|
+
* read: `TripWorkspace` already holds the trip.
|
|
899
|
+
*/
|
|
900
|
+
readonly durationDays: number;
|
|
901
|
+
readonly style?: KaafilTokenOverrides;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
declare function TripVehiclesSection({ tripRef, durationDays, style, }: TripVehiclesSectionProps): ReactNode;
|
|
905
|
+
|
|
906
|
+
/** Every panel this surface can show. Ordered by `TAB_ORDER` in
|
|
907
|
+
* `internal/tripTabRegistry.tsx`, never by this union's declaration order. */
|
|
908
|
+
type TripTabKey = 'overview' | 'manifest' | 'itinerary' | 'pickups' | 'rooming' | 'vehicles' | 'vendors' | 'payments' | 'expenses' | 'float' | 'checklist' | 'bookings' | 'forms' | 'feedback' | 'documents' | 'treks';
|
|
909
|
+
/** Why a tab is not showing a real panel. Drives the placeholder's copy, so
|
|
910
|
+
* the operator is told the truth rather than a generic "coming soon". */
|
|
911
|
+
type TripTabBlocker =
|
|
912
|
+
/** The module is deferred to a later pass. The API is reachable. */
|
|
913
|
+
'deferred'
|
|
914
|
+
/** No admin-reachable endpoint exists — the read is manager-only. */
|
|
915
|
+
| 'managerOnly'
|
|
916
|
+
/** The server resolved this capability as unavailable for this trip. */
|
|
917
|
+
| 'capabilityDark';
|
|
918
|
+
interface TripWorkspaceProps {
|
|
919
|
+
/** Kaafil's id for the trip, or the agency's own external id — either
|
|
920
|
+
* resolves (`trip-directory.validation.ts`'s `tripRef` param). */
|
|
921
|
+
readonly tripRef: string;
|
|
922
|
+
/** Which tab to open on mount. An unknown or now-dark key falls back to
|
|
923
|
+
* `overview` rather than rendering an empty rail. */
|
|
924
|
+
readonly initialTab?: TripTabKey;
|
|
925
|
+
/** Navigation is a callback, never a route — the UIKit ships no router
|
|
926
|
+
* (hard rule #9). */
|
|
927
|
+
readonly onBack?: () => void;
|
|
928
|
+
/**
|
|
929
|
+
* Forwarded to the People-going tab, whose row kebab offers "Open full
|
|
930
|
+
* profile". That profile is the agency-wide `TravellerProfileSection`, a
|
|
931
|
+
* different screen from this tab's own trip-scoped detail dialog — so it
|
|
932
|
+
* belongs to whichever surface owns the travellers pane, not to this one.
|
|
933
|
+
* Absent means the affordance is not offered.
|
|
934
|
+
*/
|
|
935
|
+
readonly onOpenTravellerProfile?: (travellerId: string, fullName: string) => void;
|
|
936
|
+
/** Host panel overrides, merged over the registry. A host that has built
|
|
937
|
+
* its own Rooming panel can drop it in without forking this composite. */
|
|
938
|
+
readonly panels?: Partial<Record<TripTabKey, ReactNode>>;
|
|
939
|
+
readonly components?: {
|
|
940
|
+
readonly TripWorkspaceHead?: ComponentType<TripWorkspaceHeadProps>;
|
|
941
|
+
};
|
|
942
|
+
readonly style?: KaafilTokenOverrides;
|
|
943
|
+
}
|
|
944
|
+
interface TripWorkspaceHeadProps {
|
|
945
|
+
readonly trip: AgencyTripRead;
|
|
946
|
+
readonly onBack?: () => void;
|
|
947
|
+
readonly style?: KaafilTokenOverrides;
|
|
948
|
+
}
|
|
949
|
+
interface TripKpiCell {
|
|
950
|
+
readonly key: string;
|
|
951
|
+
readonly labelKey: string;
|
|
952
|
+
readonly value: string;
|
|
953
|
+
/** Absent when the underlying number is genuinely absent rather than zero
|
|
954
|
+
* — the cell then renders a worded dash, never a fabricated `0`. */
|
|
955
|
+
readonly subLabel?: string;
|
|
956
|
+
readonly tone?: 'pos' | 'warn' | 'mute';
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
declare function TripWorkspace({ tripRef, initialTab, onBack, onOpenTravellerProfile, panels, components, style, }: TripWorkspaceProps): ReactNode;
|
|
960
|
+
|
|
961
|
+
/** A registry key (hard rule #8), never an inline SVG passed per-item —
|
|
962
|
+
* `AdminShell` owns its own icon set, matching `ManagerShell`'s own fixed
|
|
963
|
+
* icon registry; a host wanting a different glyph for one of these seven
|
|
964
|
+
* uses `components.NavIcon`. Extended this wave (`forms`/`templates`/
|
|
965
|
+
* `journey` added) to cover every real `KaafilAgencyWorkspace` pane —
|
|
966
|
+
* extending this union is not a breaking change since it is additive. */
|
|
967
|
+
type AdminShellNavIconKey = 'trips' | 'traveller' | 'manager' | 'settings' | 'forms' | 'templates' | 'journey';
|
|
968
|
+
interface AdminShellNavItem {
|
|
969
|
+
readonly key: string;
|
|
970
|
+
readonly label: string;
|
|
971
|
+
readonly icon: AdminShellNavIconKey;
|
|
972
|
+
/** Already-resolved group heading text (never a translation key —
|
|
973
|
+
* `AdminShell` itself reads no i18n context, same as `label`) — items
|
|
974
|
+
* sharing the same `group` string render together under one eyebrow
|
|
975
|
+
* header, in the order groups first appear in `items`. Omit on every
|
|
976
|
+
* item to render one flat, ungrouped list (fully backward compatible);
|
|
977
|
+
* mixing grouped and ungrouped items renders the ungrouped ones with no
|
|
978
|
+
* header, in their own relative position. */
|
|
979
|
+
readonly group?: string;
|
|
980
|
+
/** Real count only — never a guessed or placeholder number. Omit to
|
|
981
|
+
* render no badge at all. */
|
|
982
|
+
readonly badge?: number;
|
|
983
|
+
/** `'warn'` gets the warn-token treatment (e.g. an overdue/blocked
|
|
984
|
+
* count) — omit for the default neutral pill. */
|
|
985
|
+
readonly badgeVariant?: 'default' | 'warn';
|
|
986
|
+
/** Renders this item visibly inert — muted, `aria-disabled`, never fires
|
|
987
|
+
* `onSelectItem` — for a real, spec'd destination whose composite doesn't
|
|
988
|
+
* exist yet (`KaafilAgencyWorkspace`'s own progressive-linking posture:
|
|
989
|
+
* the item is present from day one, wired for real the moment its
|
|
990
|
+
* composite ships, never removed and re-added). Omit for a live item. */
|
|
991
|
+
readonly disabled?: boolean;
|
|
992
|
+
}
|
|
993
|
+
interface AdminShellBreadcrumbEntry {
|
|
994
|
+
readonly label: string;
|
|
995
|
+
/** Optional even on the last (current-page) crumb — `AdminShell` itself
|
|
996
|
+
* decides the last entry is never interactive and always carries
|
|
997
|
+
* `aria-current="page"`, ignoring any `onSelect` supplied on it, so a
|
|
998
|
+
* caller that (accidentally, or out of habit) supplies one on every
|
|
999
|
+
* entry can never make the current page read as a clickable link. */
|
|
1000
|
+
readonly onSelect?: () => void;
|
|
1001
|
+
}
|
|
1002
|
+
interface AdminShellHeaderConfig {
|
|
1003
|
+
/** Small uppercase label above `title` (e.g. "AUTOMATION", "AGENCY") —
|
|
1004
|
+
* omit along with `title` for the "content owns its own page head" case;
|
|
1005
|
+
* never shown with a `breadcrumb`-only header. */
|
|
1006
|
+
readonly eyebrow?: string;
|
|
1007
|
+
/** Omit when the mounted content already owns its own page head (e.g.
|
|
1008
|
+
* `TravellerProfileSection`'s own hero renders the avatar/name) — showing
|
|
1009
|
+
* `AdminShell`'s own `<h1>` above it would just duplicate that name.
|
|
1010
|
+
* `breadcrumb` still renders without a `title`. */
|
|
1011
|
+
readonly title?: string;
|
|
1012
|
+
/** Optional supporting copy directly under `title`, tightly paired with
|
|
1013
|
+
* it (its own small top margin, `title`'s own bottom margin shrinks to
|
|
1014
|
+
* match) — never the larger title-to-content gap a title-only page uses.
|
|
1015
|
+
* A composite mounted below (e.g. `TravellerDirectoryFlow`) should NOT
|
|
1016
|
+
* also render its own `title`/`subtitle` in this case — one page head per
|
|
1017
|
+
* screen, owned by whichever component is closest to the root; here,
|
|
1018
|
+
* `AdminShell` itself. */
|
|
1019
|
+
readonly subtitle?: string;
|
|
1020
|
+
readonly breadcrumb?: readonly AdminShellBreadcrumbEntry[];
|
|
1021
|
+
/** Collapses a longer trail to `first … lastN` (a non-interactive
|
|
1022
|
+
* ellipsis marker, never a caller-supplied entry) once `breadcrumb.length`
|
|
1023
|
+
* exceeds this — mirrors the real `maxItems` prop on this package's own
|
|
1024
|
+
* `Breadcrumb` primitive spec (`atomic/11-navigation.md`). Defaults to 4. */
|
|
1025
|
+
readonly breadcrumbMaxItems?: number;
|
|
1026
|
+
/** The page-level primary action (e.g. "New template", "Add traveller")
|
|
1027
|
+
* — rendered beside `title`, never duplicated inside the mounted
|
|
1028
|
+
* content's own card header. Verified against the mockup's own generic
|
|
1029
|
+
* page-header pattern: every pane's fixture carries a `cta` field
|
|
1030
|
+
* alongside `title`/`eyebrow`/`sub`, rendered in the SAME flex row as the
|
|
1031
|
+
* heading, not inside whatever card sits below it. */
|
|
1032
|
+
readonly primaryCta?: {
|
|
1033
|
+
readonly label: string;
|
|
1034
|
+
readonly onClick: () => void;
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
interface AdminShellBrand {
|
|
1038
|
+
readonly name: string;
|
|
1039
|
+
/** The host CRM's own brand mark — this package never renders a
|
|
1040
|
+
* Kaafil-branded default here, matching `BrandShell`'s "host controls
|
|
1041
|
+
* branding" posture elsewhere in this package. */
|
|
1042
|
+
readonly logo?: ReactNode;
|
|
1043
|
+
}
|
|
1044
|
+
interface AdminShellUser {
|
|
1045
|
+
readonly name: string;
|
|
1046
|
+
readonly roleLabel?: string;
|
|
1047
|
+
/** 2-letter avatar initials — omit renders a person-less initial block
|
|
1048
|
+
* derived from `name`, never a "?" (same reasoning `ManagerShell`'s own
|
|
1049
|
+
* header documents for its avatar). */
|
|
1050
|
+
readonly avatarInitials?: string;
|
|
1051
|
+
/** Renders a sign-out affordance only when supplied — prop-presence
|
|
1052
|
+
* gates the button, same convention `ManagerMeSection`/`KaafilManagerApp`
|
|
1053
|
+
* already establish. */
|
|
1054
|
+
readonly onSignOut?: () => void;
|
|
1055
|
+
}
|
|
1056
|
+
interface AdminShellProps {
|
|
1057
|
+
readonly header: AdminShellHeaderConfig;
|
|
1058
|
+
/** Caller-supplied, never hardcoded (hard rule #9's own "host owns
|
|
1059
|
+
* navigation" extended to what destinations even exist) — this wave's
|
|
1060
|
+
* real value is the 4-item Trips/Traveller/Manager/Settings set, but
|
|
1061
|
+
* `AdminShell` itself has no opinion on that list's contents. */
|
|
1062
|
+
readonly items: readonly AdminShellNavItem[];
|
|
1063
|
+
readonly activeItemKey: string;
|
|
1064
|
+
readonly onSelectItem: (key: string) => void;
|
|
1065
|
+
readonly brand?: AdminShellBrand;
|
|
1066
|
+
readonly user?: AdminShellUser;
|
|
1067
|
+
/** The content region — `AdminShell` renders chrome only, exactly as
|
|
1068
|
+
* `ManagerShell` does for its own `main` region. */
|
|
1069
|
+
readonly children?: ReactNode;
|
|
1070
|
+
readonly components?: {
|
|
1071
|
+
readonly NavIcon?: ComponentType<{
|
|
1072
|
+
readonly iconKey: AdminShellNavIconKey;
|
|
1073
|
+
}>;
|
|
1074
|
+
};
|
|
1075
|
+
readonly style?: KaafilTokenOverrides;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/** The spec's own `initialPane` union (`01-surface.md`), plus `settings` —
|
|
1079
|
+
* see this file's header for why. */
|
|
1080
|
+
type KaafilAgencyWorkspacePaneKey = 'trips' | 'managers' | 'travellers' | 'forms' | 'templates' | 'journey' | 'settings';
|
|
1081
|
+
interface KaafilAgencyWorkspaceProps {
|
|
1082
|
+
/** Seeds which pane is selected on first mount. Ignored once `selectedPane`
|
|
1083
|
+
* (below) is supplied. Defaults to `'trips'`, per spec. */
|
|
1084
|
+
readonly initialPane?: KaafilAgencyWorkspacePaneKey;
|
|
1085
|
+
/** Fully controls which pane is selected — omit to let this component own
|
|
1086
|
+
* its own selection state. `onPaneChange` still fires so the host can
|
|
1087
|
+
* react. */
|
|
1088
|
+
readonly selectedPane?: KaafilAgencyWorkspacePaneKey;
|
|
1089
|
+
/**
|
|
1090
|
+
* Fired alongside the internal trip-workspace push opening — the same
|
|
1091
|
+
* analytics/URL-sync mirror every other `onOpen*` below is, paired with
|
|
1092
|
+
* `onCloseTrip`.
|
|
1093
|
+
*
|
|
1094
|
+
* ITS MEANING CHANGED, AND DELIBERATELY KEEPS ITS SPEC'D NAME. It used to
|
|
1095
|
+
* be the ONLY way a trip opened: this Surface had no trip detail of its
|
|
1096
|
+
* own, so the spec had the host respond by "navigating to its own
|
|
1097
|
+
* `KaafilTripWorkspace`-mounting route". This Surface now mounts
|
|
1098
|
+
* `TripWorkspace` itself, so opening a trip no longer depends on the host
|
|
1099
|
+
* doing anything — a host that ignores this callback still gets a working
|
|
1100
|
+
* drill-down. Kept under the spec's own name rather than joined by a
|
|
1101
|
+
* second `onOpenTrip` prop meaning the identical thing: two doors to one
|
|
1102
|
+
* event is exactly the duplication the rest of this Surface avoids.
|
|
1103
|
+
*
|
|
1104
|
+
* The second argument is new. Existing handlers that take only `tripRef`
|
|
1105
|
+
* keep working unchanged.
|
|
1106
|
+
*/
|
|
1107
|
+
readonly onSelectTrip?: (tripRef: string, name: string) => void;
|
|
1108
|
+
/** Fired alongside the internal trip-workspace push closing — the mirror
|
|
1109
|
+
* of `onSelectTrip`, matching every other pane's own pair. */
|
|
1110
|
+
readonly onCloseTrip?: () => void;
|
|
1111
|
+
/** Fires whenever the active pane changes — per spec, T2. */
|
|
1112
|
+
readonly onPaneChange?: (pane: KaafilAgencyWorkspacePaneKey) => void;
|
|
1113
|
+
/** Fired alongside the internal traveller-profile push opening/closing —
|
|
1114
|
+
* an analytics/URL-sync hook, never the only way this navigation happens
|
|
1115
|
+
* (the real trigger is always a row tap inside `TravellerDirectoryFlow`).
|
|
1116
|
+
* `KaafilManagerApp`'s own exact `onOpenTraveller`/`onCloseTraveller`
|
|
1117
|
+
* precedent. */
|
|
1118
|
+
readonly onOpenTraveller?: (travellerId: string, fullName: string) => void;
|
|
1119
|
+
readonly onCloseTraveller?: () => void;
|
|
1120
|
+
/** Fired alongside the internal manager-profile push opening/closing — the
|
|
1121
|
+
* identical `onOpenTraveller`/`onCloseTraveller` shape, for the Managers
|
|
1122
|
+
* pane's own directory → profile journey. */
|
|
1123
|
+
readonly onOpenManager?: (managerId: string, fullName: string) => void;
|
|
1124
|
+
readonly onCloseManager?: () => void;
|
|
1125
|
+
/** Fired alongside the internal template-editor push opening/closing — the
|
|
1126
|
+
* identical `onOpenTraveller`/`onCloseTraveller` shape, for the Checklist
|
|
1127
|
+
* templates pane's own library → editor journey. */
|
|
1128
|
+
readonly onOpenTemplate?: (templateId: string, title: string) => void;
|
|
1129
|
+
readonly onCloseTemplate?: () => void;
|
|
1130
|
+
/** Fired alongside the internal form-builder push opening/closing — the
|
|
1131
|
+
* identical `onOpenTraveller`/`onCloseTraveller` shape, for the Forms
|
|
1132
|
+
* pane's own library → builder journey. */
|
|
1133
|
+
readonly onOpenForm?: (formId: string, title: string) => void;
|
|
1134
|
+
readonly onCloseForm?: () => void;
|
|
1135
|
+
/** Renders "Sign out" on `AdminShell`'s own user footer only when
|
|
1136
|
+
* supplied (prop-presence gates the affordance, same convention
|
|
1137
|
+
* `KaafilManagerApp`/`ManagerMeSection` already establish) — this Surface
|
|
1138
|
+
* has no credential/session-teardown method of its own to call first. */
|
|
1139
|
+
readonly onSignOut?: () => void;
|
|
1140
|
+
readonly components?: {
|
|
1141
|
+
/** Maps onto `AdminShell`'s own existing `components.NavIcon` slot — the
|
|
1142
|
+
* only real nav-chrome customization point that exists today. Narrower
|
|
1143
|
+
* than the spec's "TabBar" name implies, the identical mapping
|
|
1144
|
+
* `KaafilManagerApp.types.ts` already documents for its own
|
|
1145
|
+
* `TabBar`→`ManagerShell.components.TabIcon`. */
|
|
1146
|
+
readonly TabBar?: ComponentType<{
|
|
1147
|
+
readonly iconKey: AdminShellNavIconKey;
|
|
1148
|
+
}>;
|
|
1149
|
+
};
|
|
1150
|
+
readonly style?: KaafilTokenOverrides;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
declare function KaafilAgencyWorkspace(props: KaafilAgencyWorkspaceProps): ReactNode;
|
|
1154
|
+
|
|
1155
|
+
declare function AdminShell({ header, items, activeItemKey, onSelectItem, brand, user, children, components, style, }: AdminShellProps): ReactNode;
|
|
1156
|
+
|
|
1157
|
+
export { AdminShell, type AdminShellBrand, type AdminShellBreadcrumbEntry, type AdminShellHeaderConfig, type AdminShellNavIconKey, type AdminShellNavItem, type AdminShellProps, type AdminShellUser, AgencySettingsScreen, type AgencySettingsScreenProps, type BookingKind, type BookingLiveRow, type BuilderHeaderProps, type ChecklistGate, type ChecklistPhase, ChecklistTemplateEditor, type ChecklistTemplateEditorProps, type ChecklistTemplateItemDraft, ChecklistTemplateLibrary, type ChecklistTemplateLibraryProps, ChecklistTemplateRow, type CollectionLiveRow, type CollectionMode, type DocumentRowModel, type DocumentSpend, type EligibleStatus, type ExpenseCategory, type ExpenseClaimStatus, type ExpenseFilters, type ExpenseLiveRow, type ExpensePaymentMode, type FieldDef, type FieldRowProps, type FloatAction, type FloatDirection, FloatMovementRow, type FloatMovementType, FormBuilder, type FormBuilderProps, FormDetailRow, FormFieldRow, type FormParticipantRow, FormRow, type FormRowProps$1 as FormRowProps, FormsAuthoringFlow, type FormsAuthoringFlowProps, FormsLibrary, type FormsLibraryProps, type ItineraryDayGroup, type ItineraryViewMode, JourneyTriggerRow, KaafilAgencyWorkspace, type KaafilAgencyWorkspacePaneKey, type KaafilAgencyWorkspaceProps, type ManagerAgencyProfileTripRow, ManagerDirectoryFlow, type ManagerDirectoryFlowProps, ManagerListRowResponse, ManagerProfileSection, type ManagerProfileSectionProps, type ManagerResultRowProps, type ManifestFilterState, type ManifestGroupBy, type ManifestRowProps, type PickupKind, type PickupRollup, type PickupStopLiveRow, type PickupView, type PlanLockedReason, PlanLockedScreen, type PlanLockedScreenProps, type RoomingBed, type RoomingOccupant, type RoomingRoomLiveRow, type RoomingWindowGroup, type SeatRow, type SeatingSeat, type SeatingVehicleLiveRow, type SectionCardProps, type SectionDef, type StayWindowRow, type SupplierSortKey, type SupplierUsage, TemplateAuthoringFlow, type TemplateAuthoringFlowProps, type TemplateRowProps, type TravellerAgencyProfileLifetime, type TravellerAgencyProfileNoteRow, type TravellerAgencyProfileTripRow, TravellerDirectoryFlow, type TravellerDirectoryFlowProps, TravellerDirectoryItemResponse, TravellerProfileSection, type TravellerProfileSectionProps, TravellerSearchBox, type TravellerSearchBoxProps, type TriggerDetailPanelProps, TriggerTemplateEditor, type TriggerTemplateEditorProps, TripBookingsSection, type TripBookingsSectionProps, TripChecklistSection, type TripChecklistSectionProps, TripDirectoryFlow, type TripDirectoryFlowProps, AgencyTripRow as TripDirectoryRow, type TripRowProps as TripDirectoryRowProps, TripDocumentsSection, type TripDocumentsSectionProps, TripExpensesSection, type TripExpensesSectionProps, TripFloatSection, type TripFloatSectionProps, TripFormsSection, type TripFormsSectionProps, TripItinerarySection, type TripItinerarySectionProps, type TripKpiCell, TripManifestRow, TripManifestSection, type TripManifestSectionProps, TripPaymentsSection, type TripPaymentsSectionProps, TripPickupsSection, type TripPickupsSectionProps, TripRoomingSection, type TripRoomingSectionProps, TripSuppliersSection, type TripSuppliersSectionProps, type TripTabBlocker, type TripTabKey, TripVehiclesSection, type TripVehiclesSectionProps, TripVendorRow, TripWorkspace, type TripWorkspaceHeadProps, type TripWorkspaceProps, AgencyTripRead as TripWorkspaceTrip, type UsageSources, type VehicleLayout, type VehicleType, type VendorCategory };
|