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,784 @@
|
|
|
1
|
+
import { ChecklistItemDeltaRow, ChecklistsResource, AddChecklistItemOptions, OutboxOp, PatchChecklistItemOptions, DeleteChecklistItemOptions, ToggleChecklistItemOptions, ListChecklistTemplatesOptions, ChecklistTemplatesResource, PullChecklistTemplateOptions, KaafilClient, FilesResource, ExpensesResource, LogExpenseOptions, LinkExpenseReceiptOptions, VoidExpenseOptions, SubmitExpenseClaimOptions, WithdrawExpenseClaimOptions, FloatResource, IssueFloatOptions, ReturnFloatOptions, AdjustFloatOptions, FormsResource, ManagerCreateFormResponseOptions, DispatchFormOptions, ExportFormResponsesOptions, KaafilBinaryResponse, ReadFormConsentReceiptOptions, BoundCreateFormOptions, PatchFormOptions, CloneFormOptions, ReorderFormOptions, CreateFormSectionOptions, PatchFormSectionOptions, DeleteFormSectionOptions, CreateFormFieldOptions, PatchFormFieldOptions, DeleteFormFieldOptions, ItineraryResource, ListItineraryChangeLogOptions, PatchItineraryDayOptions, AddItineraryItemOptions, PatchItineraryItemOptions, DeleteItineraryItemOptions, ReorderItineraryItemOptions, RoomingResource, RoomingRoomDeltaRow, RoomingOccupant, AssignRoomingBedOptions, AutoAssignRoomingOptions, CreateRoomingStayWindowOptions, PatchRoomingStayWindowOptions, DeleteRoomingStayWindowOptions, CreateRoomingRoomOptions, PatchRoomingRoomOptions, DeleteRoomingRoomOptions, VendorsResource, CreateVendorOptions, UpsertVendorOptions, DeleteVendorOptions } from 'kaafil-js/client';
|
|
2
|
+
|
|
3
|
+
/** The four handling paths a `/core` hook ever takes for a classified error. */
|
|
4
|
+
type ErrorHandlingPath = 'retryable' | 'parked' | 'surface-to-user' | 're-auth';
|
|
5
|
+
/**
|
|
6
|
+
* Exactly the 24 codes `architecture/10-conventions.md §7` lists, each
|
|
7
|
+
* mapped to exactly one `ErrorHandlingPath`:
|
|
8
|
+
*
|
|
9
|
+
* - `re-auth` (1 code): `UNAUTHENTICATED` — the SDK's own credential
|
|
10
|
+
* resolver already attempted one silent refresh before this ever reaches
|
|
11
|
+
* `/core` (`10 §7`'s "*SDK attempts one token refresh first"); a hook
|
|
12
|
+
* seeing this classification is seeing a refresh that already failed, so
|
|
13
|
+
* the one correct action left is surfacing `session.expired`.
|
|
14
|
+
* - `retryable` (3 codes): the three the offline table (`06 §7`) marks
|
|
15
|
+
* TRANSIENT — `RATE_LIMITED`, `INTERNAL_ERROR`, `TENANT_SCOPE_MISSING`.
|
|
16
|
+
* - `parked` (3 codes): the closed three-code subset `07-offline-and-sync.md
|
|
17
|
+
* §10` names explicitly as "never retried" — `PLAN_FEATURE_DISABLED`,
|
|
18
|
+
* `FILE_PURGED`, `LOCKED`. (`UnsatisfiableSchemeError` — not a wire code,
|
|
19
|
+
* thrown locally by `kaafil-js`'s credential resolver before any request —
|
|
20
|
+
* is the fourth member of that same closed set; `./classify.ts` handles it
|
|
21
|
+
* by `instanceof`, not through this table, because it never carries a
|
|
22
|
+
* `KaafilErrorCode`.)
|
|
23
|
+
* - `surface-to-user` (the remaining 17 codes): every code that is neither
|
|
24
|
+
* retryable, parked, nor a re-auth signal — a human needs to see it and,
|
|
25
|
+
* in most cases, act on it (fix a validation error, resolve a conflict via
|
|
26
|
+
* `useConflicts()`, request a fresh share link). `CONFLICT_VERSION` and
|
|
27
|
+
* `SECRET_NOT_REPLAYABLE` land here too: both ARE surfaced to a human, just
|
|
28
|
+
* through the one shared `ConflictResolver` shape (`07-offline-and-sync.md
|
|
29
|
+
* §6`) rather than a generic toast — that richer shape is `useConflicts()`'s
|
|
30
|
+
* job, not this classifier's.
|
|
31
|
+
*/
|
|
32
|
+
declare const ERROR_HANDLING_PATH: Readonly<Record<string, ErrorHandlingPath>>;
|
|
33
|
+
|
|
34
|
+
/** The normalised shape every `/core` hook's `'error'` branch carries. */
|
|
35
|
+
interface ErrorClassification {
|
|
36
|
+
readonly path: ErrorHandlingPath;
|
|
37
|
+
/** The catalog code, when the failure reached a real response
|
|
38
|
+
* (`kind: 'api'`). `undefined` for a transport failure (network, timeout,
|
|
39
|
+
* abort) or for `UnsatisfiableSchemeError`, neither of which ever carries
|
|
40
|
+
* one (`kaafil-js/src/http/errors.ts`, verified: `KaafilTransportError`
|
|
41
|
+
* fixes `code: undefined`). */
|
|
42
|
+
readonly code: string | undefined;
|
|
43
|
+
readonly status: number | undefined;
|
|
44
|
+
/** `true` when the SDK's own credential resolver refused the call locally,
|
|
45
|
+
* before any request was built — `UnsatisfiableSchemeError`
|
|
46
|
+
* (`kaafil-js/src/auth/credentials.ts`, verified). A hook surfacing this
|
|
47
|
+
* to a composite renders it as a structural "this persona cannot do that",
|
|
48
|
+
* never a transient failure worth retrying. */
|
|
49
|
+
readonly unsatisfiableScheme: boolean;
|
|
50
|
+
/** The original error, for a caller that needs more than the
|
|
51
|
+
* classification (support escalation, a `details` field a specific hook
|
|
52
|
+
* knows how to read). */
|
|
53
|
+
readonly cause: unknown;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Turns any caught value into the shape every `/core` hook's error branch
|
|
57
|
+
* needs. Never throws — an unrecognised shape (a bare `Error`, a rejected
|
|
58
|
+
* promise from something outside the SDK) still classifies, conservatively,
|
|
59
|
+
* as `surface-to-user` with no code and no status, rather than propagating
|
|
60
|
+
* an uncaught exception up through a hook that promised a `{ status:
|
|
61
|
+
* 'error' }` branch instead.
|
|
62
|
+
*/
|
|
63
|
+
declare function classifyError(error: unknown): ErrorClassification;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Which of the three gates failed, in the priority order the three
|
|
67
|
+
* darkness treatments are picked in (`06-capability-and-personas.md §2`).
|
|
68
|
+
* `'mode'` wins over `'data'` wins over `'flag'` is NOT the rule here — the
|
|
69
|
+
* wire's own gate order is flag-before-data (`06 §1`, "why 402 wins"), but
|
|
70
|
+
* `DarkReason` names which single boolean a *component* should read to pick
|
|
71
|
+
* its treatment, and mode-dark is checked first because it is the one
|
|
72
|
+
* treatment that renders nothing at all (`06 §2`'s table, row 1).
|
|
73
|
+
*/
|
|
74
|
+
type DarkReason = 'mode' | 'data' | 'flag';
|
|
75
|
+
/**
|
|
76
|
+
* The wire shape of one row of `journey.capabilities()`
|
|
77
|
+
* (`06-capability-and-personas.md §6`, verified against
|
|
78
|
+
* `kaafil-js/src/resources/journey.ts`'s `JourneyCapabilitiesResponse`).
|
|
79
|
+
* `capability` is an open string set (§6 point 5) — never exhaustively
|
|
80
|
+
* switched on.
|
|
81
|
+
*/
|
|
82
|
+
interface CapabilityRow {
|
|
83
|
+
readonly capability: string;
|
|
84
|
+
readonly modeOk: boolean;
|
|
85
|
+
readonly dataOk: boolean;
|
|
86
|
+
readonly flagOk: boolean;
|
|
87
|
+
readonly enabled: boolean;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* `useCapability()`'s per-key return shape (`08-core-hooks.md §1.4`).
|
|
91
|
+
* `status` is derived client-side from the triple, once, here — never
|
|
92
|
+
* re-derived by a composite (§6 point 2).
|
|
93
|
+
*/
|
|
94
|
+
interface CapabilityTriple {
|
|
95
|
+
readonly enabled: boolean;
|
|
96
|
+
readonly modeOk: boolean;
|
|
97
|
+
readonly dataOk: boolean;
|
|
98
|
+
readonly flagOk: boolean;
|
|
99
|
+
readonly status: 'lit' | 'mode-dark' | 'data-dark' | 'flag-dark';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* `TReady` is merged into the `'ready'` branch (never nested under a `data`
|
|
104
|
+
* key) so a composite destructures `{ status, rows, mutate }` directly once
|
|
105
|
+
* it has narrowed on `status === 'ready'`, with no extra unwrap.
|
|
106
|
+
*/
|
|
107
|
+
type DomainHookResult<TReady extends object> = {
|
|
108
|
+
readonly status: 'loading';
|
|
109
|
+
} | {
|
|
110
|
+
readonly status: 'dark';
|
|
111
|
+
readonly reason: DarkReason;
|
|
112
|
+
} | ({
|
|
113
|
+
readonly status: 'ready';
|
|
114
|
+
} & TReady) | {
|
|
115
|
+
readonly status: 'error';
|
|
116
|
+
readonly error: ErrorClassification;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** `Awaited<ReturnType<...>>` off the resource methods, never imported by
|
|
120
|
+
* name — `ChecklistAggregateResponse`/`ChecklistItemResponse`/
|
|
121
|
+
* `ToggleResponse`/`TemplatesListResponse`/`DeleteItemResponse` are not
|
|
122
|
+
* individually re-exported from `kaafil-js/client`'s public surface
|
|
123
|
+
* (verified — only `ChecklistItemDeltaRow` and the `*Options` input types
|
|
124
|
+
* are), the identical gap `usePickups.ts`'s own header documents for its
|
|
125
|
+
* resource's response shapes. */
|
|
126
|
+
type ChecklistReadResult = Awaited<ReturnType<ChecklistsResource['read']>>;
|
|
127
|
+
type ChecklistTemplatesListResult = Awaited<ReturnType<ChecklistTemplatesResource['list']>>;
|
|
128
|
+
/** One row of `ChecklistReadResult['sections']` — always the full set,
|
|
129
|
+
* never narrowed by `?since=` (this file's header). */
|
|
130
|
+
type ChecklistSectionRow = ChecklistReadResult['sections'][number];
|
|
131
|
+
/** One row of `ChecklistReadResult['availableTemplates']` — the agency's
|
|
132
|
+
* templates this trip hasn't pulled yet, as returned inline on the
|
|
133
|
+
* aggregate (a smaller shape than `listTemplates()`'s own
|
|
134
|
+
* `TemplatesListResponse`, which additionally carries `itemCount`/
|
|
135
|
+
* `lastUsedAt`). */
|
|
136
|
+
type ChecklistAvailableTemplateRow = ChecklistReadResult['availableTemplates'][number];
|
|
137
|
+
type AddChecklistItemInput = Omit<AddChecklistItemOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
138
|
+
type PatchChecklistItemInput = Omit<PatchChecklistItemOptions, 'tripRef' | 'signal'>;
|
|
139
|
+
type RemoveChecklistItemInput = Omit<DeleteChecklistItemOptions, 'tripRef' | 'signal'>;
|
|
140
|
+
type ToggleChecklistItemInput = Omit<ToggleChecklistItemOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
141
|
+
type ListChecklistTemplatesInput = Omit<ListChecklistTemplatesOptions, 'tripRef' | 'signal'>;
|
|
142
|
+
type PullChecklistTemplateInput = Omit<PullChecklistTemplateOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
143
|
+
type BoundAgencyChecklistTemplates = KaafilClient['checklists']['agencyTemplates'];
|
|
144
|
+
type ListAgencyChecklistTemplatesInput = Parameters<BoundAgencyChecklistTemplates['list']>[0];
|
|
145
|
+
type CreateAgencyChecklistTemplateInput = Parameters<BoundAgencyChecklistTemplates['create']>[0];
|
|
146
|
+
type PatchAgencyChecklistTemplateInput = Parameters<BoundAgencyChecklistTemplates['patch']>[0];
|
|
147
|
+
type RemoveAgencyChecklistTemplateInput = Parameters<BoundAgencyChecklistTemplates['remove']>[0];
|
|
148
|
+
type PublishAgencyChecklistTemplateInput = Parameters<BoundAgencyChecklistTemplates['publish']>[0];
|
|
149
|
+
/**
|
|
150
|
+
* The live-fetched remainder of `ChecklistAggregateResponse` — everything
|
|
151
|
+
* that isn't `items[]` (this file's header explains the split). Same
|
|
152
|
+
* `loaded`/`lastFetchFailed`/`fetchedAt`/`refresh` contract
|
|
153
|
+
* `RoomingUnassignedPool`/`FloatManagerSummaryState` already establish.
|
|
154
|
+
*/
|
|
155
|
+
interface ChecklistAggregateState {
|
|
156
|
+
readonly title: string | undefined;
|
|
157
|
+
readonly subtitle: string | undefined;
|
|
158
|
+
readonly progress: {
|
|
159
|
+
readonly total: number;
|
|
160
|
+
readonly complete: number;
|
|
161
|
+
} | undefined;
|
|
162
|
+
readonly hasOpenMandatoryByPhase: {
|
|
163
|
+
readonly PRE_DEPARTURE: boolean;
|
|
164
|
+
readonly IN_TRIP: boolean;
|
|
165
|
+
readonly POST_TRIP: boolean;
|
|
166
|
+
} | undefined;
|
|
167
|
+
readonly sections: readonly ChecklistSectionRow[];
|
|
168
|
+
/** The aggregate's OWN `items[]`, in the identical live-row-or-tombstone
|
|
169
|
+
* union the snapshot list holds. Kept because a desk console mounts no
|
|
170
|
+
* `OfflineShell`, so the snapshot list is permanently empty there and
|
|
171
|
+
* this fetch is the only place a desk's rows come from — see
|
|
172
|
+
* `ChecklistsReady.items`. */
|
|
173
|
+
readonly items: readonly ChecklistItemDeltaRow[];
|
|
174
|
+
readonly availableTemplates: readonly ChecklistAvailableTemplateRow[];
|
|
175
|
+
/** `true` once at least one `client.checklists.read()` call has settled
|
|
176
|
+
* (success or failure) — distinguishes "never fetched yet" from "fetched
|
|
177
|
+
* and genuinely empty." */
|
|
178
|
+
readonly loaded: boolean;
|
|
179
|
+
/** `true` when the MOST RECENT fetch threw. */
|
|
180
|
+
readonly lastFetchFailed: boolean;
|
|
181
|
+
readonly fetchedAt: string | undefined;
|
|
182
|
+
readonly refresh: () => Promise<void>;
|
|
183
|
+
}
|
|
184
|
+
interface ChecklistsReady {
|
|
185
|
+
/** Cache-first — `SnapshotStore`'s `'checklist'` list. */
|
|
186
|
+
readonly items: readonly ChecklistItemDeltaRow[];
|
|
187
|
+
readonly syncedAt: string | undefined;
|
|
188
|
+
readonly aggregate: ChecklistAggregateState;
|
|
189
|
+
/** `null` rather than an `OutboxOp` when the write took the DESK lane —
|
|
190
|
+
* a direct call has no outbox row to hand back. A field surface with an
|
|
191
|
+
* `OfflineShell` still gets its op. */
|
|
192
|
+
readonly addItem: (input: AddChecklistItemInput) => Promise<OutboxOp | null>;
|
|
193
|
+
readonly patchItem: (input: PatchChecklistItemInput) => Promise<OutboxOp | null>;
|
|
194
|
+
readonly removeItem: (input: RemoveChecklistItemInput) => Promise<OutboxOp | null>;
|
|
195
|
+
readonly toggleItem: (input: ToggleChecklistItemInput) => Promise<OutboxOp | null>;
|
|
196
|
+
/** Live, on-demand — see this file's header for why this is a plain
|
|
197
|
+
* function rather than auto-fetched hook state. */
|
|
198
|
+
readonly listTemplates: (input?: ListChecklistTemplatesInput) => Promise<ChecklistTemplatesListResult>;
|
|
199
|
+
readonly pullTemplate: (input: PullChecklistTemplateInput) => Promise<OutboxOp | null>;
|
|
200
|
+
/** Out of THIS family's own scope (this file's header) — wired for the
|
|
201
|
+
* future admin-family template-authoring composite. */
|
|
202
|
+
readonly listAgencyTemplates: (input: ListAgencyChecklistTemplatesInput) => ReturnType<BoundAgencyChecklistTemplates['list']>;
|
|
203
|
+
readonly createAgencyTemplate: (input: CreateAgencyChecklistTemplateInput) => ReturnType<BoundAgencyChecklistTemplates['create']>;
|
|
204
|
+
readonly patchAgencyTemplate: (input: PatchAgencyChecklistTemplateInput) => ReturnType<BoundAgencyChecklistTemplates['patch']>;
|
|
205
|
+
readonly removeAgencyTemplate: (input: RemoveAgencyChecklistTemplateInput) => ReturnType<BoundAgencyChecklistTemplates['remove']>;
|
|
206
|
+
readonly publishAgencyTemplate: (input: PublishAgencyChecklistTemplateInput) => ReturnType<BoundAgencyChecklistTemplates['publish']>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
|
|
210
|
+
* fetch when the `'checklists'` capability triple isn't `lit`
|
|
211
|
+
* (`06-capability-and-personas.md §3`).
|
|
212
|
+
*/
|
|
213
|
+
declare function useChecklists(tripRef: string): DomainHookResult<ChecklistsReady>;
|
|
214
|
+
|
|
215
|
+
interface FilesContext {
|
|
216
|
+
readonly tripRef: string;
|
|
217
|
+
}
|
|
218
|
+
type RequestOptions = Omit<Parameters<FilesResource['request']>[0], 'tripRef'>;
|
|
219
|
+
type ConfirmOptions = Omit<Parameters<FilesResource['confirm']>[0], 'signal'>;
|
|
220
|
+
type ListOptions = Omit<Parameters<FilesResource['list']>[0], 'tripRef'>;
|
|
221
|
+
type FileMeta = Awaited<ReturnType<FilesResource['meta']>>;
|
|
222
|
+
type FileUrl = Awaited<ReturnType<FilesResource['url']>>;
|
|
223
|
+
type FileListItem = Awaited<ReturnType<FilesResource['list']>>['items'][number];
|
|
224
|
+
type ManagerFileView = Awaited<ReturnType<FilesResource['managerView']>>;
|
|
225
|
+
type FilePurpose = FileMeta['purpose'];
|
|
226
|
+
interface FilesReady {
|
|
227
|
+
/** `POST /api/v1/files` — mints a `pending` row + a presigned `PUT`. */
|
|
228
|
+
readonly request: (input: RequestOptions) => ReturnType<FilesResource['request']>;
|
|
229
|
+
/** `POST /api/v1/files/{id}/confirm` — verifies what landed, flips
|
|
230
|
+
* `pending` → `ready`. `422 UPLOAD_MISMATCH` leaves the file `pending`;
|
|
231
|
+
* the caller re-`PUT`s and re-confirms (`FileAttachmentFlow` does this
|
|
232
|
+
* itself, see its own file). */
|
|
233
|
+
readonly confirm: (input: ConfirmOptions) => ReturnType<FilesResource['confirm']>;
|
|
234
|
+
/** `GET /api/v1/files/{id}` — the metadata skeleton; `200` even once
|
|
235
|
+
* `purged` — the one read that never 404s on a gone file. */
|
|
236
|
+
readonly meta: (fileId: string) => Promise<FileMeta>;
|
|
237
|
+
/** `GET /api/v1/files/{id}/url` — a fresh 5-minute signed GET, minted on
|
|
238
|
+
* every call, never cached across calls. `410 FILE_PURGED` once the
|
|
239
|
+
* bytes are gone. */
|
|
240
|
+
readonly url: (fileId: string) => Promise<FileUrl>;
|
|
241
|
+
/** `GET /api/v1/files?tripRef=…` — every file for this trip, newest
|
|
242
|
+
* first, optionally narrowed by `purpose`/`status`. Needs no `fileId`. */
|
|
243
|
+
readonly list: (query?: ListOptions) => Promise<readonly FileListItem[]>;
|
|
244
|
+
/** `GET /api/v1/files/{id}/manager-view` — `meta()` + `url()` composed
|
|
245
|
+
* server-side into one manager-doc-viewer-shaped response. Never refuses
|
|
246
|
+
* on a non-`ready` file the way `url()` does — `url`/`urlExpiresAt` come
|
|
247
|
+
* back `null` instead. */
|
|
248
|
+
readonly managerView: (fileId: string) => Promise<ManagerFileView>;
|
|
249
|
+
/**
|
|
250
|
+
* The full presign → direct-`PUT` → confirm sequence for one local
|
|
251
|
+
* `File`, run in the foreground. On a `422 UPLOAD_MISMATCH` OR a presign
|
|
252
|
+
* window that lapsed mid-`PUT` (the underlying `PUT`/`confirm` answering
|
|
253
|
+
* a plain network failure once the 15-minute signature has expired) this
|
|
254
|
+
* re-requests a fresh slot and retries silently, up to `maxAttempts` —
|
|
255
|
+
* `07-files.md`'s own "must never surface as an error" contract for the
|
|
256
|
+
* routine case. Bytes never pass through this SDK or the Kaafil API
|
|
257
|
+
* itself; only the presign/confirm calls do.
|
|
258
|
+
*/
|
|
259
|
+
readonly upload: (input: {
|
|
260
|
+
readonly file: File;
|
|
261
|
+
readonly purpose: FilePurpose;
|
|
262
|
+
/** REQUIRED for `trip_document`, refused (422) for every other purpose —
|
|
263
|
+
* nothing else names that file. */
|
|
264
|
+
readonly title?: string;
|
|
265
|
+
/** Only for `trip_document`. Omit and the document belongs to the whole
|
|
266
|
+
* trip; name a traveller and only they see it on their own share link.
|
|
267
|
+
* It also decides retention (scoped → `TRAVELLER_PII`). */
|
|
268
|
+
readonly travellerRef?: string;
|
|
269
|
+
readonly maxAttempts?: number;
|
|
270
|
+
readonly signal?: AbortSignal;
|
|
271
|
+
}) => Promise<FileMeta>;
|
|
272
|
+
}
|
|
273
|
+
declare function useFiles(context: FilesContext): FilesReady;
|
|
274
|
+
|
|
275
|
+
/** One row of `ExpensesReady['expenses']` — a live expense (the full
|
|
276
|
+
* `ExpenseResponse` shape) or the tombstone for one voided-and-purged since
|
|
277
|
+
* the last cursor. Structurally identical split to `RoomingRoomDeltaRow`
|
|
278
|
+
* (`useRooming.ts`) — `ExpenseListResponse.items[]` is `anyOf` the two,
|
|
279
|
+
* verified against `kaafil-js/openapi/openapi.json`. */
|
|
280
|
+
type ExpenseDeltaRow = Awaited<ReturnType<ExpensesResource['list']>>['items'][number];
|
|
281
|
+
/** `ExpenseDeltaRow`, narrowed to the live shape — a voided-and-purged row
|
|
282
|
+
* carries no `category`/`paymentMode`/etc. at all. Composites built on this
|
|
283
|
+
* hook (`manager/composite/expenses/*`) filter to this narrowed type before
|
|
284
|
+
* rendering, the same `isLive*` pattern `useRooming.ts`'s consumers use. */
|
|
285
|
+
type ExpenseLiveRow = Exclude<ExpenseDeltaRow, {
|
|
286
|
+
readonly _tombstone: true;
|
|
287
|
+
}>;
|
|
288
|
+
type LogExpenseInput = Omit<LogExpenseOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
289
|
+
type LinkExpenseReceiptInput = Omit<LinkExpenseReceiptOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
290
|
+
type VoidExpenseInput = Omit<VoidExpenseOptions, 'tripRef' | 'signal'>;
|
|
291
|
+
type SubmitExpenseClaimInput = Omit<SubmitExpenseClaimOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
292
|
+
type WithdrawExpenseClaimInput = Omit<WithdrawExpenseClaimOptions, 'tripRef' | 'signal'>;
|
|
293
|
+
type LogInput = LogExpenseInput;
|
|
294
|
+
type LinkReceiptInput = LinkExpenseReceiptInput;
|
|
295
|
+
type VoidInput = VoidExpenseInput;
|
|
296
|
+
type SubmitClaimInput = SubmitExpenseClaimInput;
|
|
297
|
+
type WithdrawClaimInput = WithdrawExpenseClaimInput;
|
|
298
|
+
interface ExpensesReady {
|
|
299
|
+
readonly expenses: readonly ExpenseDeltaRow[];
|
|
300
|
+
readonly syncedAt: string | undefined;
|
|
301
|
+
/** Re-reads the desk lane after a write. A NO-OP on a field device, where
|
|
302
|
+
* the outbox drain emits `snapshot.updated` and the cache-first read
|
|
303
|
+
* re-renders itself — a composite need not know which lane it is on. */
|
|
304
|
+
readonly refreshLive: () => Promise<void>;
|
|
305
|
+
/** Live read (`client.expenses.read()`), not cache-first — for a detail
|
|
306
|
+
* screen that wants the freshest single row rather than whatever the last
|
|
307
|
+
* pull cached. The list above stays the cache-first source for every list
|
|
308
|
+
* view. */
|
|
309
|
+
readonly read: (expenseId: string) => ReturnType<ExpensesResource['read']>;
|
|
310
|
+
readonly log: (input: LogInput) => Promise<OutboxOp | null>;
|
|
311
|
+
/** FIELD-ONLY — see the mutation's own note: its route requires a
|
|
312
|
+
* client-minted idempotency key, which hard rule #4 forbids this layer
|
|
313
|
+
* from supplying, so there is no desk lane and a desk must not offer it. */
|
|
314
|
+
readonly linkReceipt: (input: LinkReceiptInput) => Promise<OutboxOp>;
|
|
315
|
+
readonly void: (input: VoidInput) => Promise<OutboxOp | null>;
|
|
316
|
+
readonly submitClaim: (input: SubmitClaimInput) => Promise<OutboxOp>;
|
|
317
|
+
readonly withdrawClaim: (input: WithdrawClaimInput) => Promise<OutboxOp>;
|
|
318
|
+
/**
|
|
319
|
+
* The SERVER's own spend rollup for this trip — `spendTotalMinor` and the
|
|
320
|
+
* per-category breakdown, straight off `expenses.list()`'s envelope.
|
|
321
|
+
*
|
|
322
|
+
* Its own live state rather than a field on the snapshot list, for the
|
|
323
|
+
* reason `useFloat.summary` and `useCollections.eligible` are: these are
|
|
324
|
+
* DERIVED figures with no `id`/`version`, so there is nothing for
|
|
325
|
+
* `SnapshotStore` to key on, and `useSnapshotList`'s `live` contract
|
|
326
|
+
* returns `{rows, syncedAt}` with no slot for an envelope.
|
|
327
|
+
*
|
|
328
|
+
* Exposed because summing the rows in the browser is the wrong answer
|
|
329
|
+
* twice over: it would miss anything outside the page and it would put a
|
|
330
|
+
* money figure's correctness in the client. `deriveOverview.ts` wanted
|
|
331
|
+
* exactly this and said so ("wants a server-side rollup … until it lands
|
|
332
|
+
* this card states the two numbers it can stand behind").
|
|
333
|
+
*/
|
|
334
|
+
readonly totals: ExpenseTotalsState;
|
|
335
|
+
}
|
|
336
|
+
/** The server's spend rollup, and whether it could be read at all. */
|
|
337
|
+
interface ExpenseTotalsState {
|
|
338
|
+
/** `undefined` until a read lands — NEVER 0. A money figure the surface
|
|
339
|
+
* could not read must not be drawn as zero. */
|
|
340
|
+
readonly spendTotalMinor: number | undefined;
|
|
341
|
+
readonly categoryTotals: Readonly<Record<string, number>> | undefined;
|
|
342
|
+
readonly loaded: boolean;
|
|
343
|
+
readonly lastFetchFailed: boolean;
|
|
344
|
+
readonly refresh: () => Promise<void>;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
|
|
348
|
+
* fetch when the `'expenses'` capability triple isn't `lit`
|
|
349
|
+
* (`06-capability-and-personas.md §3`).
|
|
350
|
+
*/
|
|
351
|
+
declare function useExpenses(tripRef: string): DomainHookResult<ExpensesReady>;
|
|
352
|
+
|
|
353
|
+
/** One row of the calling manager's own float ledger — verified against
|
|
354
|
+
* `FloatLedgerResponse.data[number]` (`generated/schema.d.ts`). See this
|
|
355
|
+
* file's header for why no tombstone variant is modelled. */
|
|
356
|
+
type FloatMovementRow = Awaited<ReturnType<FloatResource['readLedger']>>['data'][number];
|
|
357
|
+
/** One row of `FloatSummaryResponse.data[]` — entirely derived, never
|
|
358
|
+
* delta-mergeable. See this file's header. */
|
|
359
|
+
type FloatSummaryRow = Awaited<ReturnType<FloatResource['readSummary']>>['data'][number];
|
|
360
|
+
type IssueFloatInput = Omit<IssueFloatOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
361
|
+
type ReturnFloatInput = Omit<ReturnFloatOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
362
|
+
type AdjustFloatInput = Omit<AdjustFloatOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
363
|
+
/**
|
|
364
|
+
* The live-fetched balance/breakdown state — `readSummary()`'s own rows,
|
|
365
|
+
* refreshed explicitly rather than subscribed to `snapshot.updated`. Carries
|
|
366
|
+
* every manager row the calling credential is entitled to see (a manager
|
|
367
|
+
* session is answered its own row only, per `float.ts`'s own header — this
|
|
368
|
+
* hook does not re-filter that, since it has no independent way to verify
|
|
369
|
+
* which row is "self" beyond what the server already scoped the response
|
|
370
|
+
* to); a composite that needs "my own row" specifically matches on the
|
|
371
|
+
* `managerRef`/`managerId` it was itself handed as a prop.
|
|
372
|
+
*/
|
|
373
|
+
interface FloatManagerSummaryState {
|
|
374
|
+
readonly rows: readonly FloatSummaryRow[];
|
|
375
|
+
/** `true` once at least one `client.float.readSummary()` call has settled
|
|
376
|
+
* (success or failure) — distinguishes "never fetched yet" from "fetched
|
|
377
|
+
* and genuinely empty," the same contract `RoomingUnassignedPool.loaded`
|
|
378
|
+
* documents. */
|
|
379
|
+
readonly loaded: boolean;
|
|
380
|
+
/** `true` when the MOST RECENT fetch threw. */
|
|
381
|
+
readonly lastFetchFailed: boolean;
|
|
382
|
+
/** The server time (`meta.serverTime`) of the last successful fetch. */
|
|
383
|
+
readonly fetchedAt: string | undefined;
|
|
384
|
+
readonly refresh: () => Promise<void>;
|
|
385
|
+
}
|
|
386
|
+
interface FloatReady {
|
|
387
|
+
/** The calling manager's own movement history, cache-first — see this
|
|
388
|
+
* file's header for why this is the one cache-first field on this hook. */
|
|
389
|
+
readonly movements: readonly FloatMovementRow[];
|
|
390
|
+
readonly syncedAt: string | undefined;
|
|
391
|
+
readonly summary: FloatManagerSummaryState;
|
|
392
|
+
/**
|
|
393
|
+
* ONE NAMED MANAGER's movement history — `GET .../float/{managerId}/ledger`.
|
|
394
|
+
*
|
|
395
|
+
* Distinct from `movements` above, and the distinction matters on a desk:
|
|
396
|
+
* `movements` is the CALLING MANAGER's own ledger, read from the `'float'`
|
|
397
|
+
* pull section, which is scoped that way. An agency admin has no `Manager`
|
|
398
|
+
* row, so there is no "own ledger" for them to read and no live lane that
|
|
399
|
+
* could sensibly fill one — a desk asks "show me THIS manager's
|
|
400
|
+
* movements", which is what this is for. `summary` names every manager who
|
|
401
|
+
* has ever had a movement on the trip, so a caller always has an id to
|
|
402
|
+
* pass.
|
|
403
|
+
*/
|
|
404
|
+
readonly readLedger: (managerId: string) => ReturnType<FloatResource['readLedger']>;
|
|
405
|
+
/** `POST .../float/issue` — `type=ISSUE`, `direction=IN`, never
|
|
406
|
+
* negative-float guarded. */
|
|
407
|
+
readonly issue: (input: IssueFloatInput) => Promise<OutboxOp | null>;
|
|
408
|
+
/** `POST .../float/return` — `type=RETURN`, `direction=OUT`,
|
|
409
|
+
* negative-float guarded. A manager, an API key OR an agency admin may
|
|
410
|
+
* call it: this comment previously claimed `managerAuth`-only and that an
|
|
411
|
+
* admin "gets refused by the server", which is wrong —
|
|
412
|
+
* `float.routes.ts` declares the admin triple on it and its route
|
|
413
|
+
* description calls the parity a deliberate owner decision. An office
|
|
414
|
+
* reconciling a returned cash box is the case it exists for. */
|
|
415
|
+
readonly return: (input: ReturnFloatInput) => Promise<OutboxOp | null>;
|
|
416
|
+
/** `POST .../float/adjust` — `type=ADJUSTMENT`; `direction: 'OUT'` is
|
|
417
|
+
* negative-float guarded identically to `return`, `direction: 'IN'`
|
|
418
|
+
* never is. `note` is required by the wire request type itself. */
|
|
419
|
+
readonly adjust: (input: AdjustFloatInput) => Promise<OutboxOp | null>;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
|
|
423
|
+
* fetch when the `'float'` capability triple isn't `lit`
|
|
424
|
+
* (`06-capability-and-personas.md §3`).
|
|
425
|
+
*/
|
|
426
|
+
declare function useFloat(tripRef: string): DomainHookResult<FloatReady>;
|
|
427
|
+
|
|
428
|
+
/** One row of `GET /api/v1/trips/{ref}/forms` — `TripFormsListResponse.items[]`
|
|
429
|
+
* (verified, `kaafil-js/src/generated/schema.d.ts`). Carries `id`/`version`
|
|
430
|
+
* like a delta row, but is NOT one — `SYNC_PULL_SECTIONS` has no `forms`
|
|
431
|
+
* entry (this file's own header, corrected this pass), so this is a live
|
|
432
|
+
* fetch (`forms.trip.list`), never a snapshot-list read. */
|
|
433
|
+
type TripFormRow = Awaited<ReturnType<FormsResource['trip']['list']>>['items'][number];
|
|
434
|
+
/** One row of `GET /api/v1/trips/{ref}/forms/completion` — PER FORM, not
|
|
435
|
+
* per traveller. See `completion()`'s own note. */
|
|
436
|
+
type FormCompletionRow = Awaited<ReturnType<FormsResource['trip']['completion']>>['items'][number];
|
|
437
|
+
type DispatchFormInput = Omit<DispatchFormOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
438
|
+
type ExportFormResponsesInput = Omit<ExportFormResponsesOptions, 'signal'>;
|
|
439
|
+
type ConsentReceiptInput = Omit<ReadFormConsentReceiptOptions, 'signal'>;
|
|
440
|
+
/** `GET /api/v1/forms/{formId}` — `FormDetailResponse`: sections + fields +
|
|
441
|
+
* resolved options, the definition `FormFillRenderer` renders. A live read
|
|
442
|
+
* (mirrors `useShareForms.renderForm`'s own "live call, not cached" note) —
|
|
443
|
+
* there is no per-form definition slot in the trip snapshot's `'forms'`
|
|
444
|
+
* section, only the summary row list. */
|
|
445
|
+
type FormDefinitionDetail = Awaited<ReturnType<FormsResource['get']>>;
|
|
446
|
+
/** `GET /api/v1/forms/{formId}/responses/{responseId}` —
|
|
447
|
+
* `ResponseDetailResponse`: the full answer set plus staleness flags against
|
|
448
|
+
* the current form version. */
|
|
449
|
+
type FormResponseDetail = Awaited<ReturnType<FormsResource['responses']['get']>>;
|
|
450
|
+
/** One row of `GET /api/v1/trips/{ref}/travellers/{travellerRef}/forms/
|
|
451
|
+
* responses` — this traveller's `FormResponse` rows across every form on the
|
|
452
|
+
* trip (the per-traveller cross-form read `TripFormResponsesResource.list`,
|
|
453
|
+
* scoped to one `formId`, cannot give). Used to resolve "has this filler
|
|
454
|
+
* already got a response for this form, and what's its id/status" without a
|
|
455
|
+
* second per-form list call per row. */
|
|
456
|
+
type TravellerFormResponseRow = Awaited<ReturnType<FormsResource['trip']['travellerResponses']['list']>>['items'][number];
|
|
457
|
+
/** One row of `GET /api/v1/trips/{ref}/forms/{formId}/responses` — EVERY
|
|
458
|
+
* respondent's row for one form (the per-form cross-traveller read
|
|
459
|
+
* `TripFormResponsesResource.list`, the other axis from
|
|
460
|
+
* `travellerResponses` above — same underlying `ResponsesListResponse`
|
|
461
|
+
* shape, verified identical, just a different URL/fixed axis). `managerAuth`
|
|
462
|
+
* is allowed (`generated/security.ts`, verified) — this is what a manager
|
|
463
|
+
* needs to browse "who has answered this form" for a `TRAVELLER`-audience
|
|
464
|
+
* form, never a browsable-all-travellers index of its own (that stays out
|
|
465
|
+
* of scope, `manager/05-forms.md`'s own boundary). */
|
|
466
|
+
type TripFormResponseRow = Awaited<ReturnType<FormsResource['trip']['responses']['list']>>['items'][number];
|
|
467
|
+
/** `POST /api/v1/trips/{ref}/forms/{formId}/responses` body — a manager
|
|
468
|
+
* self-fill (`travellerRef` omitted) or on-behalf capture
|
|
469
|
+
* (`travellerRef` set), `submit: false` for a draft save and `true` for a
|
|
470
|
+
* final submit. No `clientToken` on this route (unlike the share-surface's
|
|
471
|
+
* `save`/`submit`, `useShareForms.ts`'s own header note) — the standard
|
|
472
|
+
* `Idempotency-Key` header `OfflineEngine.enqueue()` mints once per op is
|
|
473
|
+
* this route's only de-dupe key. */
|
|
474
|
+
type SubmitFormResponseInput = Omit<ManagerCreateFormResponseOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
475
|
+
/** `GET /api/v1/agencies/{ref}/forms` row — `FormListResponse.items[]`, the
|
|
476
|
+
* agency's whole catalog (every locale/status/scope), `FormsCatalogScreen`'s
|
|
477
|
+
* own read. Structurally identical to `FormDefinitionResponse`; named
|
|
478
|
+
* separately here because it is reached through a different call
|
|
479
|
+
* (`forms.list`, not `forms.get`). */
|
|
480
|
+
type FormAgencyRow = Awaited<ReturnType<FormsResource['list']>>['items'][number];
|
|
481
|
+
/** The patch-transition family's own return shape — `forms.patch`/
|
|
482
|
+
* `.publish`/`.close`/`.reopen`/`.archive`/`.unarchive`/`.clone` all answer
|
|
483
|
+
* `FormDefinitionResponse`, the metadata-only row (no `sections`/`fields`) —
|
|
484
|
+
* distinct from `FormDefinitionDetail`, which only `.get`/`.create` return. */
|
|
485
|
+
type FormDefinitionSummary = Awaited<ReturnType<FormsResource['patch']>>;
|
|
486
|
+
type FormSectionDetail = FormDefinitionDetail['sections'][number];
|
|
487
|
+
type FormFieldDetail = FormDefinitionDetail['fields'][number];
|
|
488
|
+
/** `GET /api/v1/forms/bindings` row — the closed 16-key binding registry
|
|
489
|
+
* `BindingCatalogBrowser` browses. */
|
|
490
|
+
type BindingEntry = Awaited<ReturnType<FormsResource['bindings']['list']>>['items'][number];
|
|
491
|
+
/** `forms.create`'s own body, `agencyRef` dropped (browser-bound, see this
|
|
492
|
+
* file's own header) and the request-plumbing fields excluded — the exact
|
|
493
|
+
* shape `FormBuilder` composes from its metadata/section/field draft. */
|
|
494
|
+
type CreateFormInput = Omit<BoundCreateFormOptions, 'idempotencyKey' | 'signal'>;
|
|
495
|
+
type PatchFormInput = Omit<PatchFormOptions, 'signal'>;
|
|
496
|
+
type CloneFormInput = Omit<CloneFormOptions, 'idempotencyKey' | 'signal'>;
|
|
497
|
+
type ReorderFormInput = Omit<ReorderFormOptions, 'signal'>;
|
|
498
|
+
type CreateFormSectionInput = Omit<CreateFormSectionOptions, 'signal'>;
|
|
499
|
+
type PatchFormSectionInput = Omit<PatchFormSectionOptions, 'signal'>;
|
|
500
|
+
type DeleteFormSectionInput = Omit<DeleteFormSectionOptions, 'signal'>;
|
|
501
|
+
type CreateFormFieldInput = Omit<CreateFormFieldOptions, 'signal'>;
|
|
502
|
+
type PatchFormFieldInput = Omit<PatchFormFieldOptions, 'signal'>;
|
|
503
|
+
type DeleteFormFieldInput = Omit<DeleteFormFieldOptions, 'signal'>;
|
|
504
|
+
interface FormsReady {
|
|
505
|
+
/** This trip's applicable forms, each with its resolved `window` and
|
|
506
|
+
* `dispatched`/`submitted` counts — `TripFormsPanel`'s own read. Live-
|
|
507
|
+
* fetched and explicitly refreshed (this file's header, corrected this
|
|
508
|
+
* pass) — NOT a cache-first snapshot-list read. */
|
|
509
|
+
readonly forms: readonly TripFormRow[];
|
|
510
|
+
/** The last successful live fetch's own `meta.serverTime` — `undefined`
|
|
511
|
+
* before the first fetch settles. */
|
|
512
|
+
readonly syncedAt: string | undefined;
|
|
513
|
+
/** Live read of one form's definition (sections + fields). */
|
|
514
|
+
readonly getDefinition: (formId: string) => Promise<FormDefinitionDetail>;
|
|
515
|
+
/** Live read of one response's full answer set + staleness flags. */
|
|
516
|
+
readonly getResponse: (formId: string, responseId: string) => Promise<FormResponseDetail>;
|
|
517
|
+
/** Live read of one traveller's responses across every form on the trip —
|
|
518
|
+
* how `FormFillFlow` resolves "already submitted" / prefill without a
|
|
519
|
+
* per-form response list call. */
|
|
520
|
+
readonly travellerResponses: (travellerRef: string) => Promise<readonly TravellerFormResponseRow[]>;
|
|
521
|
+
/** Live read of EVERY respondent's row for one form — how a manager
|
|
522
|
+
* browses "who has answered this `TRAVELLER`-audience form" (the other
|
|
523
|
+
* axis from `travellerResponses`). */
|
|
524
|
+
readonly formResponses: (formId: string) => Promise<readonly TripFormResponseRow[]>;
|
|
525
|
+
/** Save (`submit: false`) or finalize (`submit: true`) a response —
|
|
526
|
+
* self-fill or on-behalf, same endpoint, same outbox-backed write. */
|
|
527
|
+
/** `null` rather than an `OutboxOp` when the write took the DESK lane —
|
|
528
|
+
* a direct call has no outbox row to hand back. */
|
|
529
|
+
readonly submitResponse: (input: SubmitFormResponseInput) => Promise<OutboxOp | null>;
|
|
530
|
+
/** `forms.list` — the agency's whole catalog, browser-bound (no
|
|
531
|
+
* `agencyRef` to supply). */
|
|
532
|
+
/** `getFormsCompletionMatrix` — one row PER FORM, not a traveller grid.
|
|
533
|
+
* See the callback's own note. */
|
|
534
|
+
/** Re-reads the live trip forms list. Exposed because a composite
|
|
535
|
+
* showing a failed read needs a retry, and `forms`/`syncedAt` are
|
|
536
|
+
* fetched, not subscribed. */
|
|
537
|
+
readonly refresh: () => Promise<void>;
|
|
538
|
+
readonly completion: () => Promise<readonly FormCompletionRow[]>;
|
|
539
|
+
/** `dispatchForm` — sends a form to travellers and refreshes the list,
|
|
540
|
+
* since it moves `dispatched`. */
|
|
541
|
+
readonly dispatch: (input: DispatchFormInput) => Promise<void>;
|
|
542
|
+
readonly exportResponses: (input: ExportFormResponsesInput) => Promise<KaafilBinaryResponse>;
|
|
543
|
+
readonly consentReceipt: (input: ConsentReceiptInput) => Promise<KaafilBinaryResponse>;
|
|
544
|
+
readonly listAgencyForms: () => Promise<readonly FormAgencyRow[]>;
|
|
545
|
+
/** `forms.create` — authors a fresh `DRAFT`, browser-bound. */
|
|
546
|
+
readonly createForm: (input: CreateFormInput) => Promise<FormDefinitionDetail>;
|
|
547
|
+
readonly patchForm: (input: PatchFormInput) => Promise<FormDefinitionSummary>;
|
|
548
|
+
readonly publishForm: (formId: string) => Promise<FormDefinitionSummary>;
|
|
549
|
+
readonly closeForm: (formId: string) => Promise<FormDefinitionSummary>;
|
|
550
|
+
readonly reopenForm: (formId: string) => Promise<FormDefinitionSummary>;
|
|
551
|
+
readonly archiveForm: (formId: string) => Promise<FormDefinitionSummary>;
|
|
552
|
+
readonly unarchiveForm: (formId: string) => Promise<FormDefinitionSummary>;
|
|
553
|
+
readonly cloneForm: (input: CloneFormInput) => Promise<FormDefinitionSummary>;
|
|
554
|
+
/** `forms.reorder` — one idempotent transaction over every section's and
|
|
555
|
+
* field's `sortOrder`, never one call per row (this spec entry's own
|
|
556
|
+
* `Does NOT`). */
|
|
557
|
+
readonly reorderForm: (input: ReorderFormInput) => Promise<void>;
|
|
558
|
+
readonly createSection: (input: CreateFormSectionInput) => Promise<FormSectionDetail>;
|
|
559
|
+
readonly patchSection: (input: PatchFormSectionInput) => Promise<FormSectionDetail>;
|
|
560
|
+
readonly deleteSection: (input: DeleteFormSectionInput) => Promise<void>;
|
|
561
|
+
readonly createField: (input: CreateFormFieldInput) => Promise<FormFieldDetail>;
|
|
562
|
+
readonly patchField: (input: PatchFormFieldInput) => Promise<FormFieldDetail>;
|
|
563
|
+
readonly deleteField: (input: DeleteFormFieldInput) => Promise<void>;
|
|
564
|
+
/** `forms.bindings.list` — the closed 16-key registry; `apiKeyAuth`/
|
|
565
|
+
* `agencyAdminAuth`/`managerAuth` all accepted (`generated/security.ts`). */
|
|
566
|
+
readonly listBindings: () => Promise<readonly BindingEntry[]>;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Not mode-dark (`manager/05-forms.md`'s own framing note) — a dark result
|
|
570
|
+
* from this trip's `'forms'` capability row is always data- or flag-shaped
|
|
571
|
+
* in practice. Returns `{ status: 'dark', reason }` before any fetch when
|
|
572
|
+
* that row isn't `lit`.
|
|
573
|
+
*/
|
|
574
|
+
declare function useForms(tripRef: string): DomainHookResult<FormsReady>;
|
|
575
|
+
|
|
576
|
+
type ItineraryReadResult = Awaited<ReturnType<ItineraryResource['read']>>;
|
|
577
|
+
/** One row of `ItineraryReadResponse.days[]` — no tombstone branch (this
|
|
578
|
+
* section is never delta-pulled; see this file's header). */
|
|
579
|
+
type ItineraryDayRow = ItineraryReadResult['days'][number];
|
|
580
|
+
/** `ItineraryReadResponse.items[]`'s generated shape is `anyOf` a live item
|
|
581
|
+
* or a tombstone — the same shared shape the delta (`?since=`) mode uses,
|
|
582
|
+
* even though a full/no-`since` read (the only mode this hook ever calls)
|
|
583
|
+
* has nothing to report a tombstone against. This hook filters the
|
|
584
|
+
* tombstone variant out in `refresh()` below rather than exposing the raw
|
|
585
|
+
* union, so every `ItineraryItemRow` a composite reads is unconditionally
|
|
586
|
+
* live — the same `isLiveRoom`/`Exclude<..., {_tombstone:true}>` pattern
|
|
587
|
+
* `useRooming.ts`'s own consumers use, applied inside the hook here since
|
|
588
|
+
* this hook, unlike `useRooming()`, has no separate delta-row type the spec
|
|
589
|
+
* asks composites to narrow themselves. */
|
|
590
|
+
type ItineraryItemUnion = ItineraryReadResult['items'][number];
|
|
591
|
+
type ItineraryItemRow = Exclude<ItineraryItemUnion, {
|
|
592
|
+
readonly _tombstone: true;
|
|
593
|
+
}>;
|
|
594
|
+
type ItineraryTripSummary = ItineraryReadResult['trip'];
|
|
595
|
+
type ItineraryChangeLogRow = Awaited<ReturnType<ItineraryResource['changeLog']['list']>>[number];
|
|
596
|
+
type AddItineraryItemInput = Omit<AddItineraryItemOptions, 'tripRef' | 'signal'>;
|
|
597
|
+
type PatchItineraryItemInput = Omit<PatchItineraryItemOptions, 'tripRef' | 'signal'>;
|
|
598
|
+
type RemoveItineraryItemInput = Omit<DeleteItineraryItemOptions, 'tripRef' | 'signal'>;
|
|
599
|
+
type ReorderItineraryItemInput = Omit<ReorderItineraryItemOptions, 'tripRef' | 'signal'>;
|
|
600
|
+
type PatchItineraryDayInput = Omit<PatchItineraryDayOptions, 'tripRef' | 'signal'>;
|
|
601
|
+
type LoadItineraryChangeLogInput = Omit<ListItineraryChangeLogOptions, 'tripRef' | 'signal'>;
|
|
602
|
+
interface ItineraryReady {
|
|
603
|
+
readonly trip: ItineraryTripSummary | undefined;
|
|
604
|
+
/** Server's own pick of which day the itinerary screen should land on
|
|
605
|
+
* (today → first future day → Day 1, `FRD:itinerary §5`) — never
|
|
606
|
+
* computed client-side. */
|
|
607
|
+
readonly initialDayIso: string | undefined;
|
|
608
|
+
readonly canAddItems: boolean;
|
|
609
|
+
readonly canAddItemsReason: string | null;
|
|
610
|
+
readonly days: readonly ItineraryDayRow[];
|
|
611
|
+
readonly items: readonly ItineraryItemRow[];
|
|
612
|
+
/** `true` once the first `itinerary.read()` has settled (success or
|
|
613
|
+
* failure) — distinguishes "never fetched yet" from "fetched, trip has
|
|
614
|
+
* zero days," the same discipline `RoomingReady.unassignedPool.loaded`
|
|
615
|
+
* documents. */
|
|
616
|
+
readonly loaded: boolean;
|
|
617
|
+
readonly lastFetchFailed: boolean;
|
|
618
|
+
readonly syncedAt: string | undefined;
|
|
619
|
+
readonly refresh: () => Promise<void>;
|
|
620
|
+
readonly changeLog: readonly ItineraryChangeLogRow[];
|
|
621
|
+
readonly changeLogLoaded: boolean;
|
|
622
|
+
readonly changeLogFetchFailed: boolean;
|
|
623
|
+
readonly loadChangeLog: (input?: LoadItineraryChangeLogInput) => Promise<void>;
|
|
624
|
+
readonly patchDay: (input: PatchItineraryDayInput) => Promise<OutboxOp | null>;
|
|
625
|
+
readonly addItem: (input: AddItineraryItemInput) => Promise<OutboxOp | null>;
|
|
626
|
+
readonly patchItem: (input: PatchItineraryItemInput) => Promise<OutboxOp | null>;
|
|
627
|
+
readonly removeItem: (input: RemoveItineraryItemInput) => Promise<OutboxOp | null>;
|
|
628
|
+
readonly reorderItem: (input: ReorderItineraryItemInput) => Promise<OutboxOp | null>;
|
|
629
|
+
}
|
|
630
|
+
type ItineraryResult = {
|
|
631
|
+
readonly status: 'loading';
|
|
632
|
+
} | ({
|
|
633
|
+
readonly status: 'ready';
|
|
634
|
+
} & ItineraryReady) | {
|
|
635
|
+
readonly status: 'error';
|
|
636
|
+
};
|
|
637
|
+
/**
|
|
638
|
+
* Ungated (see this file's header) — never returns `{status:'dark'}`.
|
|
639
|
+
* `manager`/`agencyAdmin` both call this same hook (`FRD:itinerary`'s
|
|
640
|
+
* `COORDINATOR`-role read-only resolution happens on the OPEN SESSION,
|
|
641
|
+
* server-side, never as a UIKit prop or a second hook).
|
|
642
|
+
*/
|
|
643
|
+
declare function useItinerary(tripRef: string): ItineraryResult;
|
|
644
|
+
|
|
645
|
+
type RoomingStayWindowRow = Awaited<ReturnType<RoomingResource['stayWindows']['list']>>[number];
|
|
646
|
+
type AssignBedInput = Omit<AssignRoomingBedOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
647
|
+
type AutoAssignInput = Omit<AutoAssignRoomingOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
648
|
+
type CreateWindowInput = Omit<CreateRoomingStayWindowOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
649
|
+
type PatchWindowInput = Omit<PatchRoomingStayWindowOptions, 'tripRef' | 'signal'>;
|
|
650
|
+
type RemoveWindowInput = Omit<DeleteRoomingStayWindowOptions, 'tripRef' | 'signal'>;
|
|
651
|
+
type CreateRoomInput = Omit<CreateRoomingRoomOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
|
|
652
|
+
type PatchRoomInput = Omit<PatchRoomingRoomOptions, 'tripRef' | 'signal'>;
|
|
653
|
+
type RemoveRoomInput = Omit<DeleteRoomingRoomOptions, 'tripRef' | 'signal'>;
|
|
654
|
+
/**
|
|
655
|
+
* The unassigned-traveller roster — `RoomingBoardResponse.unassigned[]`
|
|
656
|
+
* (`kaafil-js/openapi/openapi.json`, verified). Its own state, deliberately
|
|
657
|
+
* NOT folded into `rooms`/`stayWindows`'s cache-first shape, so a consumer
|
|
658
|
+
* can never mistake one for the other.
|
|
659
|
+
*/
|
|
660
|
+
interface RoomingUnassignedPool {
|
|
661
|
+
readonly travellers: readonly RoomingOccupant[];
|
|
662
|
+
/** `true` once at least one `client.rooming.read()` call has settled
|
|
663
|
+
* (success or failure) — distinguishes "never fetched yet" from "fetched
|
|
664
|
+
* and genuinely empty." */
|
|
665
|
+
readonly loaded: boolean;
|
|
666
|
+
/** `true` when the MOST RECENT fetch threw (offline, network, server
|
|
667
|
+
* error) — a consumer must render this differently from `travellers: []`
|
|
668
|
+
* with `loaded: true`; those two states are never the same thing. */
|
|
669
|
+
readonly lastFetchFailed: boolean;
|
|
670
|
+
/** The server time (`meta.serverTime`) of the last successful fetch —
|
|
671
|
+
* `undefined` before any fetch has ever succeeded. Never a locally-minted
|
|
672
|
+
* timestamp (hard rule #6). */
|
|
673
|
+
readonly fetchedAt: string | undefined;
|
|
674
|
+
/** Re-runs the live read, optionally scoped to one stay window.
|
|
675
|
+
*
|
|
676
|
+
* `stayWindowId` matters: the engine's own `readBoard` (`kaafil-engine`
|
|
677
|
+
* `rooming.service.ts#unassignedCountForWindow`'s doc comment) computes
|
|
678
|
+
* "unassigned" PER WINDOW deliberately — "a trip-wide unassigned count
|
|
679
|
+
* would have to decide whether a traveller with a bed on night 1 and none
|
|
680
|
+
* on night 2 is assigned, and there is no answer to that a subscriber
|
|
681
|
+
* could use." Passing the board's currently-active window here is what
|
|
682
|
+
* makes the pool answer "who still needs a bed IN THIS WINDOW," which is
|
|
683
|
+
* the only question a manager looking at one window's board is actually
|
|
684
|
+
* asking. Omitting it (this hook's own initial mount-time fetch, before
|
|
685
|
+
* any window is known) falls back to the engine's unscoped trip-wide
|
|
686
|
+
* read — genuinely ambiguous, per the same comment, so a caller that
|
|
687
|
+
* knows the active window should always pass it.
|
|
688
|
+
*
|
|
689
|
+
* `assign` already calls this on its own success (a bed assignment
|
|
690
|
+
* changes who is unassigned); a composite calls it again after any OTHER
|
|
691
|
+
* action that can move someone in or out of the pool. */
|
|
692
|
+
readonly refresh: (stayWindowId?: string) => Promise<void>;
|
|
693
|
+
}
|
|
694
|
+
interface RoomingReady {
|
|
695
|
+
readonly rooms: readonly RoomingRoomDeltaRow[];
|
|
696
|
+
readonly stayWindows: readonly RoomingStayWindowRow[];
|
|
697
|
+
readonly syncedAt: string | undefined;
|
|
698
|
+
/**
|
|
699
|
+
* LIVE-READ, NOT cache-first — deliberately. `RoomingBoardResponse`
|
|
700
|
+
* (`openapi/openapi.json`) carries three arrays; only `rooms[]` is
|
|
701
|
+
* delta-shaped (`anyOf` a live row or a `_tombstone`, with `id`/
|
|
702
|
+
* `version`). `unassigned[]` is a plain object keyed on `travellerId`
|
|
703
|
+
* with no tombstone branch and no version — a DERIVED projection the
|
|
704
|
+
* engine recomputes per read, not a durable delta feed
|
|
705
|
+
* (`kaafil-js/src/offline/pull.ts`'s `extractSectionRows` docstring,
|
|
706
|
+
* verified this session). Caching it as a delta row would be a real bug:
|
|
707
|
+
* no tombstone can ever arrive for a traveller who GETS a bed, and
|
|
708
|
+
* `SnapshotStore.applyPull` rebases by id rather than replacing, so a
|
|
709
|
+
* cached pool would grow monotonically and never shrink. This field is
|
|
710
|
+
* therefore sourced from its own `client.rooming.read()` call, refreshed
|
|
711
|
+
* explicitly rather than subscribed to `snapshot.updated`.
|
|
712
|
+
*/
|
|
713
|
+
readonly unassignedPool: RoomingUnassignedPool;
|
|
714
|
+
/** Re-reads both desk lanes after a write. A NO-OP on a field device,
|
|
715
|
+
* where the outbox drain emits `snapshot.updated` and the cache-first
|
|
716
|
+
* reads re-render themselves — a composite need not know which lane it is
|
|
717
|
+
* on. */
|
|
718
|
+
readonly refreshLive: () => Promise<void>;
|
|
719
|
+
readonly assign: (input: AssignBedInput) => Promise<OutboxOp | null>;
|
|
720
|
+
readonly autoAssign: (input: AutoAssignInput) => ReturnType<RoomingResource['autoAssign']>;
|
|
721
|
+
readonly createStayWindow: (input: CreateWindowInput) => Promise<OutboxOp | null>;
|
|
722
|
+
readonly patchStayWindow: (input: PatchWindowInput) => Promise<OutboxOp | null>;
|
|
723
|
+
readonly removeStayWindow: (input: RemoveWindowInput) => Promise<OutboxOp | null>;
|
|
724
|
+
readonly createRoom: (input: CreateRoomInput) => Promise<OutboxOp | null>;
|
|
725
|
+
readonly patchRoom: (input: PatchRoomInput) => Promise<OutboxOp | null>;
|
|
726
|
+
readonly removeRoom: (input: RemoveRoomInput) => Promise<OutboxOp | null>;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
|
|
731
|
+
* fetch when the `'rooming'` capability triple isn't `lit`
|
|
732
|
+
* (`06-capability-and-personas.md §3`).
|
|
733
|
+
*/
|
|
734
|
+
declare function useRooming(tripRef: string): DomainHookResult<RoomingReady>;
|
|
735
|
+
|
|
736
|
+
/** One row of `vendors.list()`'s array — `VendorSummaryResponse`
|
|
737
|
+
* (`kaafil-js/src/generated/schema.d.ts`, verified): `id`/`name`/`category`/
|
|
738
|
+
* `phone`/`city`. `phone`/`city` are nullable — absent until the CRM feeds
|
|
739
|
+
* one. Still no `status`, no `expenseTotalMinor` on the wire — a consumer
|
|
740
|
+
* that needs either is reading a field this response does not send.
|
|
741
|
+
* `KaafilResponse<readonly VendorSummaryResponse[]>` is `T & { meta }`
|
|
742
|
+
* (`kaafil-js/src/types/meta.ts`'s own intersection, verified) — the
|
|
743
|
+
* response IS the array, `meta` attached, never a nested `.data` field. */
|
|
744
|
+
type TripVendorRow = Awaited<ReturnType<VendorsResource['list']>>[number];
|
|
745
|
+
type UpsertVendorInput = Omit<UpsertVendorOptions, 'agencyRef' | 'idempotencyKey' | 'signal'>;
|
|
746
|
+
type RemoveVendorInput = Omit<DeleteVendorOptions, 'agencyRef' | 'idempotencyKey' | 'signal'>;
|
|
747
|
+
/** `create` takes neither an external id nor a `sourceUpdatedAt` — the
|
|
748
|
+
* server mints the first and stamps the second. See `VendorsReady.create`. */
|
|
749
|
+
type CreateVendorInput = Omit<CreateVendorOptions, 'agencyRef' | 'idempotencyKey' | 'signal'>;
|
|
750
|
+
interface VendorsReady {
|
|
751
|
+
readonly vendors: readonly TripVendorRow[];
|
|
752
|
+
/** `true` once at least one `client.vendors.list()` call has settled
|
|
753
|
+
* (success or failure) — same `loaded` contract `TrekBoardState`/
|
|
754
|
+
* `RoomingUnassignedPool` document. */
|
|
755
|
+
readonly loaded: boolean;
|
|
756
|
+
/** `true` when the MOST RECENT fetch threw — distinct from an honest
|
|
757
|
+
* empty directory (`loaded: true`, `vendors: []`). */
|
|
758
|
+
readonly lastFetchFailed: boolean;
|
|
759
|
+
/** The server time (`meta.serverTime`) of the last successful fetch. */
|
|
760
|
+
readonly syncedAt: string | undefined;
|
|
761
|
+
readonly refresh: () => Promise<void>;
|
|
762
|
+
/**
|
|
763
|
+
* Agency-scoped create for a vendor the AGENCY authored, not one a CRM
|
|
764
|
+
* pushed (`POST /agencies/:ref/vendors`).
|
|
765
|
+
*
|
|
766
|
+
* Takes NO external id and NO `sourceUpdatedAt`: `Vendor.externalId` is
|
|
767
|
+
* `NOT NULL`, so before this route existed the only create path was
|
|
768
|
+
* `upsert`, which is addressed by the caller's own CRM id — something an
|
|
769
|
+
* operator adding a supplier by hand does not have and must not be asked
|
|
770
|
+
* to invent. The server mints it (prefixed `kf-`) and stamps the write
|
|
771
|
+
* with its own clock.
|
|
772
|
+
*/
|
|
773
|
+
readonly create: (input: CreateVendorInput) => ReturnType<VendorsResource['create']>;
|
|
774
|
+
/** Agency-scoped CRM upsert — see this file's header. Doubles as the EDIT
|
|
775
|
+
* of an existing vendor: `sourceUpdatedAt` is optional on the wire now, and
|
|
776
|
+
* omitting it has the server stamp the edit rather than letting it lose an
|
|
777
|
+
* LWW comparison against the very row the operator is looking at. */
|
|
778
|
+
readonly upsert: (input: UpsertVendorInput) => ReturnType<VendorsResource['upsert']>;
|
|
779
|
+
/** Agency-scoped CRM soft-delete — see this file's header. */
|
|
780
|
+
readonly remove: (input: RemoveVendorInput) => ReturnType<VendorsResource['remove']>;
|
|
781
|
+
}
|
|
782
|
+
declare function useVendors(tripRef: string, agencyRef: string): DomainHookResult<VendorsReady>;
|
|
783
|
+
|
|
784
|
+
export { useItinerary as A, useRooming as B, type ChecklistSectionRow as C, type DomainHookResult as D, type ExpenseDeltaRow as E, type FileListItem as F, useVendors as G, type CreateAgencyChecklistTemplateInput as H, type ItineraryDayRow as I, type RemoveAgencyChecklistTemplateInput as J, type PublishAgencyChecklistTemplateInput as K, type FormDefinitionDetail as L, type FormResponseDetail as M, type TravellerFormResponseRow as N, type ExpenseLiveRow as O, type PatchAgencyChecklistTemplateInput as P, type ChecklistAvailableTemplateRow as Q, type RoomingStayWindowRow as R, type FilePurpose as S, type TripFormRow as T, type ManagerFileView as U, type VendorsReady as V, type ChecklistsReady as a, type FloatMovementRow as b, type TripFormResponseRow as c, type ItineraryItemRow as d, type TripVendorRow as e, type FloatSummaryRow as f, type CapabilityTriple as g, type ErrorClassification as h, type CapabilityRow as i, type DarkReason as j, ERROR_HANDLING_PATH as k, type ErrorHandlingPath as l, type ExpensesReady as m, type FilesContext as n, type FilesReady as o, type FloatReady as p, type FormsReady as q, type ItineraryReady as r, type RoomingReady as s, type RoomingUnassignedPool as t, classifyError as u, useChecklists as v, useExpenses as w, useFiles as x, useFloat as y, useForms as z };
|