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,2154 @@
|
|
|
1
|
+
import { OfflineEngineContext, KaafilClientContext, useCapabilityRows, UNKNOWN_CAPABILITY_TRIPLE, deriveCapabilityTriple, darkReasonFor, useKaafilClient, useOfflineMutation, usePersona, isDevelopment, useOfflineEvent } from './chunk-6FPAGGFL.js';
|
|
2
|
+
import { UnsatisfiableSchemeError, isKaafilError, SyncOpMethod } from 'kaafil-js/client';
|
|
3
|
+
export { FALLBACK_GLYPH, SHADES_PER_FAMILY, TONE_FAMILIES, parseRoomingTone, roomingHeuristicSeverity } from 'kaafil-js/client';
|
|
4
|
+
import { useContext, useState, useRef, useCallback, useEffect } from 'react';
|
|
5
|
+
|
|
6
|
+
// src/core/errors/catalog.ts
|
|
7
|
+
var ERROR_HANDLING_PATH = {
|
|
8
|
+
// re-auth
|
|
9
|
+
UNAUTHENTICATED: "re-auth",
|
|
10
|
+
// retryable (TRANSIENT, `06-offline-first.md §7`)
|
|
11
|
+
RATE_LIMITED: "retryable",
|
|
12
|
+
INTERNAL_ERROR: "retryable",
|
|
13
|
+
TENANT_SCOPE_MISSING: "retryable",
|
|
14
|
+
// parked (the closed never-retried set, `07-offline-and-sync.md §10`)
|
|
15
|
+
PLAN_FEATURE_DISABLED: "parked",
|
|
16
|
+
FILE_PURGED: "parked",
|
|
17
|
+
LOCKED: "parked",
|
|
18
|
+
// surface-to-user (everything else in the 24-code catalog)
|
|
19
|
+
VALIDATION_ERROR: "surface-to-user",
|
|
20
|
+
RESOURCE_NOT_FOUND: "surface-to-user",
|
|
21
|
+
CONFLICT_VERSION: "surface-to-user",
|
|
22
|
+
STOP_HAS_PENDING: "surface-to-user",
|
|
23
|
+
RESPONSE_CLOSED: "surface-to-user",
|
|
24
|
+
BUSINESS_RULE_VIOLATION: "surface-to-user",
|
|
25
|
+
OUT_OF_TRIP_WINDOW: "surface-to-user",
|
|
26
|
+
UPLOAD_MISMATCH: "surface-to-user",
|
|
27
|
+
FILE_ORPHANED: "surface-to-user",
|
|
28
|
+
CAPABILITY_UNAVAILABLE: "surface-to-user",
|
|
29
|
+
READ_ONLY_ROLE: "surface-to-user",
|
|
30
|
+
SUBJECT_REQUIRED: "surface-to-user",
|
|
31
|
+
IDEMPOTENCY_KEY_REUSED: "surface-to-user",
|
|
32
|
+
SECRET_NOT_REPLAYABLE: "surface-to-user",
|
|
33
|
+
NOT_IMPLEMENTED: "surface-to-user",
|
|
34
|
+
SHARE_TOKEN_EXPIRED: "surface-to-user",
|
|
35
|
+
SHARE_TOKEN_REVOKED: "surface-to-user"
|
|
36
|
+
};
|
|
37
|
+
function pathForStatusOnly(status) {
|
|
38
|
+
if (status === void 0 || status === null) {
|
|
39
|
+
return "retryable";
|
|
40
|
+
}
|
|
41
|
+
if (status === 401) {
|
|
42
|
+
return "re-auth";
|
|
43
|
+
}
|
|
44
|
+
if (status === 429 || status >= 500) {
|
|
45
|
+
return "retryable";
|
|
46
|
+
}
|
|
47
|
+
if (status === 402 || status === 410 || status === 423) {
|
|
48
|
+
return "parked";
|
|
49
|
+
}
|
|
50
|
+
return "surface-to-user";
|
|
51
|
+
}
|
|
52
|
+
function classifyError(error) {
|
|
53
|
+
if (error instanceof UnsatisfiableSchemeError) {
|
|
54
|
+
return {
|
|
55
|
+
path: "parked",
|
|
56
|
+
code: void 0,
|
|
57
|
+
status: void 0,
|
|
58
|
+
unsatisfiableScheme: true,
|
|
59
|
+
cause: error
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (isKaafilError(error)) {
|
|
63
|
+
const catalogPath = error.code !== void 0 ? ERROR_HANDLING_PATH[error.code] : void 0;
|
|
64
|
+
const path = catalogPath ?? pathForStatusOnly(error.status);
|
|
65
|
+
return {
|
|
66
|
+
path,
|
|
67
|
+
code: error.code,
|
|
68
|
+
status: error.status,
|
|
69
|
+
unsatisfiableScheme: false,
|
|
70
|
+
cause: error
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
path: "surface-to-user",
|
|
75
|
+
code: void 0,
|
|
76
|
+
status: void 0,
|
|
77
|
+
unsatisfiableScheme: false,
|
|
78
|
+
cause: error
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/core/internal/ensureTripPulled.ts
|
|
83
|
+
var pulledTrips = /* @__PURE__ */ new WeakMap();
|
|
84
|
+
function ensureTripPulled(engine, tripRef) {
|
|
85
|
+
let seen = pulledTrips.get(engine);
|
|
86
|
+
if (seen === void 0) {
|
|
87
|
+
seen = /* @__PURE__ */ new Set();
|
|
88
|
+
pulledTrips.set(engine, seen);
|
|
89
|
+
}
|
|
90
|
+
if (seen.has(tripRef)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
seen.add(tripRef);
|
|
94
|
+
void engine.pull.pullTrip({ tripRef }).catch(() => {
|
|
95
|
+
seen?.delete(tripRef);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/core/hooks/useSnapshotList.ts
|
|
100
|
+
function useSnapshotList(tripRef, listName, options) {
|
|
101
|
+
const engine = useContext(OfflineEngineContext);
|
|
102
|
+
const client = useContext(KaafilClientContext);
|
|
103
|
+
const [rows, setRows] = useState(
|
|
104
|
+
() => engine === null ? [] : engine.snapshot.list(tripRef, listName)
|
|
105
|
+
);
|
|
106
|
+
const [liveRows, setLiveRows] = useState(void 0);
|
|
107
|
+
const [liveSyncedAt, setLiveSyncedAt] = useState(void 0);
|
|
108
|
+
const liveRef = useRef(options?.live);
|
|
109
|
+
liveRef.current = options?.live;
|
|
110
|
+
const hasLive = options?.live !== void 0;
|
|
111
|
+
const readLive = useCallback(async () => {
|
|
112
|
+
const live = liveRef.current;
|
|
113
|
+
if (engine !== null || live === void 0 || client === null) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const result = await live(client, tripRef);
|
|
118
|
+
setLiveRows(result.rows);
|
|
119
|
+
setLiveSyncedAt(result.syncedAt);
|
|
120
|
+
} catch {
|
|
121
|
+
}
|
|
122
|
+
}, [engine, client, tripRef, hasLive]);
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
void readLive();
|
|
125
|
+
}, [readLive]);
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (engine === null) {
|
|
128
|
+
setRows([]);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
ensureTripPulled(engine, tripRef);
|
|
132
|
+
setRows(engine.snapshot.list(tripRef, listName));
|
|
133
|
+
const unsubscribe = engine.events.on("snapshot.updated", (event) => {
|
|
134
|
+
if (event.tripRef === tripRef && event.lists.includes(listName)) {
|
|
135
|
+
setRows(engine.snapshot.list(tripRef, listName));
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
return unsubscribe;
|
|
139
|
+
}, [engine, tripRef, listName]);
|
|
140
|
+
if (engine === null) {
|
|
141
|
+
return {
|
|
142
|
+
rows: liveRows ?? rows,
|
|
143
|
+
syncedAt: liveSyncedAt,
|
|
144
|
+
refreshLive: readLive
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
let syncedAt;
|
|
148
|
+
for (const row of rows) {
|
|
149
|
+
const stamp = engine.snapshot.syncedAt(tripRef, listName, row.id);
|
|
150
|
+
if (stamp !== void 0 && (syncedAt === void 0 || stamp > syncedAt)) {
|
|
151
|
+
syncedAt = stamp;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (syncedAt === void 0 && rows.length === 0) {
|
|
155
|
+
syncedAt = engine.snapshot.cursor(tripRef, listName);
|
|
156
|
+
}
|
|
157
|
+
return { rows, syncedAt, refreshLive: readLive };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/core/internal/createDomainHook.ts
|
|
161
|
+
function createDomainHook(moduleKey, build) {
|
|
162
|
+
return function useDomainHook(tripRef) {
|
|
163
|
+
const { rows, loaded } = useCapabilityRows(tripRef);
|
|
164
|
+
const ready = build(tripRef);
|
|
165
|
+
if (!loaded) {
|
|
166
|
+
return { status: "loading" };
|
|
167
|
+
}
|
|
168
|
+
const row = rows.find((candidate) => candidate.capability === moduleKey);
|
|
169
|
+
const triple = row === void 0 ? UNKNOWN_CAPABILITY_TRIPLE : deriveCapabilityTriple(row);
|
|
170
|
+
if (triple.status !== "lit") {
|
|
171
|
+
const reason = darkReasonFor(triple);
|
|
172
|
+
return { status: "dark", reason };
|
|
173
|
+
}
|
|
174
|
+
return { status: "ready", ...ready };
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/core/hooks/useChecklists.ts
|
|
179
|
+
var useChecklistsInternal = createDomainHook("checklists", (tripRef) => {
|
|
180
|
+
const client = useKaafilClient();
|
|
181
|
+
const engine = useContext(OfflineEngineContext);
|
|
182
|
+
const { rows: snapshotItems, syncedAt } = useSnapshotList(
|
|
183
|
+
tripRef,
|
|
184
|
+
"checklist"
|
|
185
|
+
);
|
|
186
|
+
const [aggregate, setAggregate] = useState({
|
|
187
|
+
title: void 0,
|
|
188
|
+
subtitle: void 0,
|
|
189
|
+
progress: void 0,
|
|
190
|
+
hasOpenMandatoryByPhase: void 0,
|
|
191
|
+
sections: [],
|
|
192
|
+
items: [],
|
|
193
|
+
availableTemplates: [],
|
|
194
|
+
loaded: false,
|
|
195
|
+
lastFetchFailed: false,
|
|
196
|
+
fetchedAt: void 0
|
|
197
|
+
});
|
|
198
|
+
const refreshAggregate = useCallback(async () => {
|
|
199
|
+
try {
|
|
200
|
+
const response = await client.checklists.read({ tripRef });
|
|
201
|
+
setAggregate({
|
|
202
|
+
title: response.title,
|
|
203
|
+
subtitle: response.subtitle,
|
|
204
|
+
progress: response.progress,
|
|
205
|
+
hasOpenMandatoryByPhase: response.hasOpenMandatoryByPhase,
|
|
206
|
+
sections: response.sections,
|
|
207
|
+
items: response.items,
|
|
208
|
+
availableTemplates: response.availableTemplates,
|
|
209
|
+
loaded: true,
|
|
210
|
+
lastFetchFailed: false,
|
|
211
|
+
fetchedAt: response.meta.serverTime
|
|
212
|
+
});
|
|
213
|
+
} catch {
|
|
214
|
+
setAggregate((previous) => ({ ...previous, lastFetchFailed: true }));
|
|
215
|
+
}
|
|
216
|
+
}, [client, tripRef]);
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
void refreshAggregate();
|
|
219
|
+
}, [refreshAggregate]);
|
|
220
|
+
const refreshAggregateAfterDrain = useCallback(() => {
|
|
221
|
+
if (engine === null) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const unsubscribe = engine.events.on("sync.drained", () => {
|
|
225
|
+
unsubscribe();
|
|
226
|
+
void refreshAggregate();
|
|
227
|
+
});
|
|
228
|
+
}, [engine, refreshAggregate]);
|
|
229
|
+
const addItemMutation = useOfflineMutation({
|
|
230
|
+
tripRef,
|
|
231
|
+
enqueue: (engine2, input) => engine2.enqueue({
|
|
232
|
+
tripRef,
|
|
233
|
+
method: SyncOpMethod.Post,
|
|
234
|
+
path: `/api/v1/trips/${tripRef}/checklist/items`,
|
|
235
|
+
operationId: "addChecklistItem",
|
|
236
|
+
body: input
|
|
237
|
+
}),
|
|
238
|
+
// ── THE DESK LANE ─────────────────────────────────────────────────
|
|
239
|
+
// All five checklist writes take the admin triple, and a desk console
|
|
240
|
+
// mounts no `OfflineShell` — without this the write has no transport
|
|
241
|
+
// at all and every attempt fails as an unclassified error.
|
|
242
|
+
// `idempotencyKey` is OPTIONAL on these SDK options, so no lane here
|
|
243
|
+
// mints one (hard rule #4); `linkExpenseReceipt`, whose key is
|
|
244
|
+
// required, is the one write in this package that CANNOT have a desk
|
|
245
|
+
// lane for exactly that reason.
|
|
246
|
+
direct: (liveClient, input) => liveClient.checklists.items.add({ tripRef, ...input })
|
|
247
|
+
});
|
|
248
|
+
const patchItemMutation = useOfflineMutation({
|
|
249
|
+
tripRef,
|
|
250
|
+
enqueue: (engine2, input) => {
|
|
251
|
+
const { itemId, version, ...body } = input;
|
|
252
|
+
return engine2.enqueue({
|
|
253
|
+
tripRef,
|
|
254
|
+
method: SyncOpMethod.Patch,
|
|
255
|
+
path: `/api/v1/trips/${tripRef}/checklist/items/${itemId}`,
|
|
256
|
+
operationId: "patchChecklistItem",
|
|
257
|
+
body,
|
|
258
|
+
ifMatch: version
|
|
259
|
+
});
|
|
260
|
+
},
|
|
261
|
+
direct: (liveClient, input) => liveClient.checklists.items.patch({ tripRef, ...input })
|
|
262
|
+
});
|
|
263
|
+
const removeItemMutation = useOfflineMutation({
|
|
264
|
+
tripRef,
|
|
265
|
+
enqueue: (engine2, input) => {
|
|
266
|
+
const { itemId, version } = input;
|
|
267
|
+
return engine2.enqueue({
|
|
268
|
+
tripRef,
|
|
269
|
+
method: SyncOpMethod.Delete,
|
|
270
|
+
path: `/api/v1/trips/${tripRef}/checklist/items/${itemId}`,
|
|
271
|
+
operationId: "deleteChecklistItem",
|
|
272
|
+
ifMatch: version
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
direct: (liveClient, input) => liveClient.checklists.items.remove({ tripRef, ...input })
|
|
276
|
+
});
|
|
277
|
+
const toggleItemMutation = useOfflineMutation({
|
|
278
|
+
tripRef,
|
|
279
|
+
enqueue: (engine2, input) => {
|
|
280
|
+
const { itemId, ...body } = input;
|
|
281
|
+
return engine2.enqueue({
|
|
282
|
+
tripRef,
|
|
283
|
+
method: SyncOpMethod.Post,
|
|
284
|
+
path: `/api/v1/trips/${tripRef}/checklist/items/${itemId}/toggle`,
|
|
285
|
+
// `expectedStatus`, not `version`/`ifMatch` — this file's header,
|
|
286
|
+
// quirk 1.
|
|
287
|
+
operationId: "toggleChecklistItem",
|
|
288
|
+
body
|
|
289
|
+
});
|
|
290
|
+
},
|
|
291
|
+
direct: (liveClient, input) => liveClient.checklists.items.toggle({ tripRef, ...input })
|
|
292
|
+
});
|
|
293
|
+
const pullTemplateMutation = useOfflineMutation({
|
|
294
|
+
tripRef,
|
|
295
|
+
enqueue: (engine2, input) => engine2.enqueue({
|
|
296
|
+
tripRef,
|
|
297
|
+
method: SyncOpMethod.Post,
|
|
298
|
+
path: `/api/v1/trips/${tripRef}/checklist/templates/pull`,
|
|
299
|
+
operationId: "pullChecklistTemplate",
|
|
300
|
+
body: input
|
|
301
|
+
}),
|
|
302
|
+
direct: (liveClient, input) => liveClient.checklists.templates.pull({ tripRef, ...input })
|
|
303
|
+
});
|
|
304
|
+
const addItemMutate = addItemMutation.mutate;
|
|
305
|
+
const addItem = useCallback(
|
|
306
|
+
async (input) => {
|
|
307
|
+
const op = await addItemMutate(input);
|
|
308
|
+
void refreshAggregate();
|
|
309
|
+
refreshAggregateAfterDrain();
|
|
310
|
+
return op;
|
|
311
|
+
},
|
|
312
|
+
[addItemMutate, refreshAggregate, refreshAggregateAfterDrain]
|
|
313
|
+
);
|
|
314
|
+
const patchItemMutate = patchItemMutation.mutate;
|
|
315
|
+
const patchItem = useCallback(
|
|
316
|
+
async (input) => {
|
|
317
|
+
const op = await patchItemMutate(input);
|
|
318
|
+
void refreshAggregate();
|
|
319
|
+
refreshAggregateAfterDrain();
|
|
320
|
+
return op;
|
|
321
|
+
},
|
|
322
|
+
[patchItemMutate, refreshAggregate, refreshAggregateAfterDrain]
|
|
323
|
+
);
|
|
324
|
+
const removeItemMutate = removeItemMutation.mutate;
|
|
325
|
+
const removeItem = useCallback(
|
|
326
|
+
async (input) => {
|
|
327
|
+
const op = await removeItemMutate(input);
|
|
328
|
+
void refreshAggregate();
|
|
329
|
+
refreshAggregateAfterDrain();
|
|
330
|
+
return op;
|
|
331
|
+
},
|
|
332
|
+
[removeItemMutate, refreshAggregate, refreshAggregateAfterDrain]
|
|
333
|
+
);
|
|
334
|
+
const toggleItemMutate = toggleItemMutation.mutate;
|
|
335
|
+
const toggleItem = useCallback(
|
|
336
|
+
async (input) => {
|
|
337
|
+
const op = await toggleItemMutate(input);
|
|
338
|
+
void refreshAggregate();
|
|
339
|
+
refreshAggregateAfterDrain();
|
|
340
|
+
return op;
|
|
341
|
+
},
|
|
342
|
+
[toggleItemMutate, refreshAggregate, refreshAggregateAfterDrain]
|
|
343
|
+
);
|
|
344
|
+
const pullTemplateMutate = pullTemplateMutation.mutate;
|
|
345
|
+
const pullTemplate = useCallback(
|
|
346
|
+
async (input) => {
|
|
347
|
+
const op = await pullTemplateMutate(input);
|
|
348
|
+
void refreshAggregate();
|
|
349
|
+
refreshAggregateAfterDrain();
|
|
350
|
+
return op;
|
|
351
|
+
},
|
|
352
|
+
[pullTemplateMutate, refreshAggregate, refreshAggregateAfterDrain]
|
|
353
|
+
);
|
|
354
|
+
const listTemplates = useCallback(
|
|
355
|
+
(input) => client.checklists.templates.list({ tripRef, ...input }),
|
|
356
|
+
[client, tripRef]
|
|
357
|
+
);
|
|
358
|
+
const listAgencyTemplates = useCallback(
|
|
359
|
+
(input) => client.checklists.agencyTemplates.list(input),
|
|
360
|
+
[client]
|
|
361
|
+
);
|
|
362
|
+
const createAgencyTemplate = useCallback(
|
|
363
|
+
(input) => client.checklists.agencyTemplates.create(input),
|
|
364
|
+
[client]
|
|
365
|
+
);
|
|
366
|
+
const patchAgencyTemplate = useCallback(
|
|
367
|
+
(input) => client.checklists.agencyTemplates.patch(input),
|
|
368
|
+
[client]
|
|
369
|
+
);
|
|
370
|
+
const removeAgencyTemplate = useCallback(
|
|
371
|
+
(input) => client.checklists.agencyTemplates.remove(input),
|
|
372
|
+
[client]
|
|
373
|
+
);
|
|
374
|
+
const publishAgencyTemplate = useCallback(
|
|
375
|
+
(input) => client.checklists.agencyTemplates.publish(input),
|
|
376
|
+
[client]
|
|
377
|
+
);
|
|
378
|
+
const items = syncedAt === void 0 ? aggregate.items : snapshotItems;
|
|
379
|
+
return {
|
|
380
|
+
items,
|
|
381
|
+
syncedAt,
|
|
382
|
+
aggregate: { ...aggregate, refresh: refreshAggregate },
|
|
383
|
+
addItem,
|
|
384
|
+
patchItem,
|
|
385
|
+
removeItem,
|
|
386
|
+
toggleItem,
|
|
387
|
+
listTemplates,
|
|
388
|
+
pullTemplate,
|
|
389
|
+
listAgencyTemplates,
|
|
390
|
+
createAgencyTemplate,
|
|
391
|
+
patchAgencyTemplate,
|
|
392
|
+
removeAgencyTemplate,
|
|
393
|
+
publishAgencyTemplate
|
|
394
|
+
};
|
|
395
|
+
});
|
|
396
|
+
function useChecklists(tripRef) {
|
|
397
|
+
return useChecklistsInternal(tripRef);
|
|
398
|
+
}
|
|
399
|
+
var useCloseoutInternal = createDomainHook("closing-day", (tripRef) => {
|
|
400
|
+
const client = useKaafilClient();
|
|
401
|
+
const [aggregate, setAggregate] = useState(void 0);
|
|
402
|
+
const [loaded, setLoaded] = useState(false);
|
|
403
|
+
const [lastFetchFailed, setLastFetchFailed] = useState(false);
|
|
404
|
+
const [syncedAt, setSyncedAt] = useState(void 0);
|
|
405
|
+
const refresh = useCallback(async () => {
|
|
406
|
+
try {
|
|
407
|
+
const response = await client.closeout.get({ tripRef });
|
|
408
|
+
setAggregate(response);
|
|
409
|
+
setLoaded(true);
|
|
410
|
+
setLastFetchFailed(false);
|
|
411
|
+
setSyncedAt(response.meta.serverTime);
|
|
412
|
+
} catch {
|
|
413
|
+
setLastFetchFailed(true);
|
|
414
|
+
}
|
|
415
|
+
}, [client, tripRef]);
|
|
416
|
+
useEffect(() => {
|
|
417
|
+
void refresh();
|
|
418
|
+
}, [refresh]);
|
|
419
|
+
const saveHandover = useCallback(
|
|
420
|
+
async (input) => {
|
|
421
|
+
const response = await client.closeout.saveHandover({ tripRef, ...input });
|
|
422
|
+
setAggregate(response);
|
|
423
|
+
setLoaded(true);
|
|
424
|
+
setLastFetchFailed(false);
|
|
425
|
+
setSyncedAt(response.meta.serverTime);
|
|
426
|
+
return response;
|
|
427
|
+
},
|
|
428
|
+
[client, tripRef]
|
|
429
|
+
);
|
|
430
|
+
const lock = useCallback(
|
|
431
|
+
async (input = {}) => {
|
|
432
|
+
const response = await client.closeout.lock({ tripRef, ...input });
|
|
433
|
+
setAggregate(response);
|
|
434
|
+
setLoaded(true);
|
|
435
|
+
setLastFetchFailed(false);
|
|
436
|
+
setSyncedAt(response.meta.serverTime);
|
|
437
|
+
return response;
|
|
438
|
+
},
|
|
439
|
+
[client, tripRef]
|
|
440
|
+
);
|
|
441
|
+
const unlock = useCallback(
|
|
442
|
+
async (input) => {
|
|
443
|
+
const response = await client.closeout.unlock({ tripRef, ...input });
|
|
444
|
+
setAggregate(response);
|
|
445
|
+
setLoaded(true);
|
|
446
|
+
setLastFetchFailed(false);
|
|
447
|
+
setSyncedAt(response.meta.serverTime);
|
|
448
|
+
return response;
|
|
449
|
+
},
|
|
450
|
+
[client, tripRef]
|
|
451
|
+
);
|
|
452
|
+
const exportPdf = useCallback(
|
|
453
|
+
(input = {}) => client.closeout.exportPdf({ tripRef, ...input }),
|
|
454
|
+
[client, tripRef]
|
|
455
|
+
);
|
|
456
|
+
return {
|
|
457
|
+
aggregate,
|
|
458
|
+
loaded,
|
|
459
|
+
lastFetchFailed,
|
|
460
|
+
syncedAt,
|
|
461
|
+
refresh,
|
|
462
|
+
saveHandover,
|
|
463
|
+
lock,
|
|
464
|
+
unlock,
|
|
465
|
+
exportPdf
|
|
466
|
+
};
|
|
467
|
+
});
|
|
468
|
+
function useCloseout(tripRef) {
|
|
469
|
+
return useCloseoutInternal(tripRef);
|
|
470
|
+
}
|
|
471
|
+
var useCollectionsInternal = createDomainHook("collections", (tripRef) => {
|
|
472
|
+
const client = useKaafilClient();
|
|
473
|
+
const {
|
|
474
|
+
rows: collections,
|
|
475
|
+
syncedAt,
|
|
476
|
+
refreshLive
|
|
477
|
+
} = useSnapshotList(tripRef, "collections", {
|
|
478
|
+
live: async (liveClient, ref) => {
|
|
479
|
+
const response = await liveClient.collections.list({ tripRef: ref });
|
|
480
|
+
return { rows: response, syncedAt: response.meta.serverTime };
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
const [eligibleStatus, setEligibleStatus] = useState("due");
|
|
484
|
+
const [eligibleRows, setEligibleRows] = useState([]);
|
|
485
|
+
const [eligibleLoaded, setEligibleLoaded] = useState(false);
|
|
486
|
+
const [eligibleFetchFailed, setEligibleFetchFailed] = useState(false);
|
|
487
|
+
const [eligibleFetchedAt, setEligibleFetchedAt] = useState(void 0);
|
|
488
|
+
const refreshEligible = useCallback(
|
|
489
|
+
async (status) => {
|
|
490
|
+
const nextStatus = status ?? eligibleStatus;
|
|
491
|
+
if (status !== void 0 && status !== eligibleStatus) {
|
|
492
|
+
setEligibleStatus(status);
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
const response = await client.collections.eligible({ tripRef, status: nextStatus });
|
|
496
|
+
setEligibleRows(response);
|
|
497
|
+
setEligibleLoaded(true);
|
|
498
|
+
setEligibleFetchFailed(false);
|
|
499
|
+
setEligibleFetchedAt(response.meta.serverTime);
|
|
500
|
+
} catch {
|
|
501
|
+
setEligibleFetchFailed(true);
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
[client, tripRef, eligibleStatus]
|
|
505
|
+
);
|
|
506
|
+
useEffect(() => {
|
|
507
|
+
void refreshEligible(eligibleStatus);
|
|
508
|
+
}, [refreshEligible, eligibleStatus]);
|
|
509
|
+
const recordMutation = useOfflineMutation({
|
|
510
|
+
tripRef,
|
|
511
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
512
|
+
tripRef,
|
|
513
|
+
method: SyncOpMethod.Post,
|
|
514
|
+
path: `/api/v1/trips/${tripRef}/collections`,
|
|
515
|
+
// `OperationId` is not exported from `kaafil-js/client`'s public
|
|
516
|
+
// surface (the same gap `useRooming()`'s own comment names) —
|
|
517
|
+
// hardcoded literal, verified against
|
|
518
|
+
// `kaafil-js/src/generated/security.ts` this session.
|
|
519
|
+
operationId: "recordCollection",
|
|
520
|
+
body: input
|
|
521
|
+
}),
|
|
522
|
+
// The desk lane. `recordCollection` takes the admin triple, and a desk
|
|
523
|
+
// console mounts no `OfflineShell` — without this the write has no
|
|
524
|
+
// transport at all and every attempt fails as an unclassified error.
|
|
525
|
+
direct: (liveClient, input) => liveClient.collections.record({ tripRef, ...input })
|
|
526
|
+
});
|
|
527
|
+
const recordMutate = recordMutation.mutate;
|
|
528
|
+
const record = useCallback(
|
|
529
|
+
async (input) => {
|
|
530
|
+
const op = await recordMutate(input);
|
|
531
|
+
void refreshEligible();
|
|
532
|
+
return op;
|
|
533
|
+
},
|
|
534
|
+
[recordMutate, refreshEligible]
|
|
535
|
+
);
|
|
536
|
+
const voidMutation = useOfflineMutation({
|
|
537
|
+
tripRef,
|
|
538
|
+
enqueue: (engine, input) => {
|
|
539
|
+
const { collectionId, version, ...body } = input;
|
|
540
|
+
return engine.enqueue({
|
|
541
|
+
tripRef,
|
|
542
|
+
method: SyncOpMethod.Delete,
|
|
543
|
+
path: `/api/v1/trips/${tripRef}/collections/${collectionId}/void`,
|
|
544
|
+
operationId: "voidCollection",
|
|
545
|
+
body,
|
|
546
|
+
ifMatch: version
|
|
547
|
+
});
|
|
548
|
+
},
|
|
549
|
+
direct: (liveClient, input) => liveClient.collections.void({ tripRef, ...input })
|
|
550
|
+
});
|
|
551
|
+
const voidMutate = voidMutation.mutate;
|
|
552
|
+
const voidCollection = useCallback(
|
|
553
|
+
async (input) => {
|
|
554
|
+
const op = await voidMutate(input);
|
|
555
|
+
void refreshEligible();
|
|
556
|
+
return op;
|
|
557
|
+
},
|
|
558
|
+
[voidMutate, refreshEligible]
|
|
559
|
+
);
|
|
560
|
+
return {
|
|
561
|
+
collections,
|
|
562
|
+
syncedAt,
|
|
563
|
+
refreshLive,
|
|
564
|
+
eligible: {
|
|
565
|
+
rows: eligibleRows,
|
|
566
|
+
status: eligibleStatus,
|
|
567
|
+
loaded: eligibleLoaded,
|
|
568
|
+
lastFetchFailed: eligibleFetchFailed,
|
|
569
|
+
fetchedAt: eligibleFetchedAt,
|
|
570
|
+
refresh: refreshEligible
|
|
571
|
+
},
|
|
572
|
+
record,
|
|
573
|
+
void: voidCollection
|
|
574
|
+
};
|
|
575
|
+
});
|
|
576
|
+
function useCollections(tripRef) {
|
|
577
|
+
return useCollectionsInternal(tripRef);
|
|
578
|
+
}
|
|
579
|
+
var useExpensesInternal = createDomainHook("expenses", (tripRef) => {
|
|
580
|
+
const client = useKaafilClient();
|
|
581
|
+
const {
|
|
582
|
+
rows: expenses,
|
|
583
|
+
syncedAt,
|
|
584
|
+
refreshLive
|
|
585
|
+
} = useSnapshotList(tripRef, "expenses", {
|
|
586
|
+
live: async (liveClient, ref) => {
|
|
587
|
+
const response = await liveClient.expenses.list({ tripRef: ref });
|
|
588
|
+
return { rows: response.items, syncedAt: response.meta.serverTime };
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
const [spendTotalMinor, setSpendTotalMinor] = useState(void 0);
|
|
592
|
+
const [categoryTotals, setCategoryTotals] = useState(void 0);
|
|
593
|
+
const [totalsLoaded, setTotalsLoaded] = useState(false);
|
|
594
|
+
const [totalsFailed, setTotalsFailed] = useState(false);
|
|
595
|
+
const refreshTotals = useCallback(async () => {
|
|
596
|
+
try {
|
|
597
|
+
const response = await client.expenses.list({ tripRef });
|
|
598
|
+
setSpendTotalMinor(response.spendTotalMinor);
|
|
599
|
+
setCategoryTotals(response.categoryTotals);
|
|
600
|
+
setTotalsLoaded(true);
|
|
601
|
+
setTotalsFailed(false);
|
|
602
|
+
} catch {
|
|
603
|
+
setTotalsFailed(true);
|
|
604
|
+
}
|
|
605
|
+
}, [client, tripRef]);
|
|
606
|
+
useEffect(() => {
|
|
607
|
+
void refreshTotals();
|
|
608
|
+
}, [refreshTotals]);
|
|
609
|
+
const logMutation = useOfflineMutation({
|
|
610
|
+
tripRef,
|
|
611
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
612
|
+
tripRef,
|
|
613
|
+
method: SyncOpMethod.Post,
|
|
614
|
+
path: `/api/v1/trips/${tripRef}/expenses`,
|
|
615
|
+
// Hardcoded literal, not imported: `OperationId` is not exported
|
|
616
|
+
// from `kaafil-js/client`'s public surface (verified — same gap
|
|
617
|
+
// `useRooming.ts` documents). Value verified against
|
|
618
|
+
// `kaafil-js/src/generated/security.ts` this session.
|
|
619
|
+
operationId: "logExpense",
|
|
620
|
+
body: input
|
|
621
|
+
}),
|
|
622
|
+
// The desk lane. `log`, `linkReceipt` and `void` each additionally
|
|
623
|
+
// accept an agency-admin session (the SDK's own header says so), and a
|
|
624
|
+
// desk console mounts no `OfflineShell` — without these the writes have
|
|
625
|
+
// no transport at all. `submitClaim`/`withdrawClaim` deliberately get
|
|
626
|
+
// NONE: those are `['manager', 'apiKey']`, a manager claiming their own
|
|
627
|
+
// reimbursement, and an admin has no business standing in for them.
|
|
628
|
+
direct: (liveClient, input) => liveClient.expenses.log({ tripRef, ...input })
|
|
629
|
+
});
|
|
630
|
+
const linkReceiptMutation = useOfflineMutation({
|
|
631
|
+
tripRef,
|
|
632
|
+
enqueue: (engine, input) => {
|
|
633
|
+
const { expenseId, ...body } = input;
|
|
634
|
+
return engine.enqueue({
|
|
635
|
+
tripRef,
|
|
636
|
+
method: SyncOpMethod.Patch,
|
|
637
|
+
path: `/api/v1/trips/${tripRef}/expenses/${expenseId}/receipt`,
|
|
638
|
+
operationId: "linkExpenseReceipt",
|
|
639
|
+
body
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
// NO DESK LANE, AND IT CANNOT HAVE ONE. `LinkExpenseReceiptOptions`
|
|
643
|
+
// declares `idempotencyKey` as REQUIRED — the route "addresses no
|
|
644
|
+
// client-held version", so the key is its only replay guard — and hard
|
|
645
|
+
// rule #4 forbids any hook here from minting one or accepting one in its
|
|
646
|
+
// signature. The outbox path is unaffected: `OfflineEngine.enqueue()`
|
|
647
|
+
// mints the key itself, which is exactly the arrangement rule #4 exists
|
|
648
|
+
// to preserve. So this write is FIELD-ONLY in practice, and a desk
|
|
649
|
+
// surface must not offer it rather than offering a control that throws.
|
|
650
|
+
// Widening it would mean either breaking rule #4 or making the key
|
|
651
|
+
// optional on the wire, neither of which belongs in a UIKit pass.
|
|
652
|
+
});
|
|
653
|
+
const voidMutation = useOfflineMutation({
|
|
654
|
+
tripRef,
|
|
655
|
+
enqueue: (engine, input) => {
|
|
656
|
+
const { expenseId, version, ...body } = input;
|
|
657
|
+
return engine.enqueue({
|
|
658
|
+
tripRef,
|
|
659
|
+
method: SyncOpMethod.Delete,
|
|
660
|
+
path: `/api/v1/trips/${tripRef}/expenses/${expenseId}/void`,
|
|
661
|
+
operationId: "voidExpense",
|
|
662
|
+
body,
|
|
663
|
+
ifMatch: version
|
|
664
|
+
});
|
|
665
|
+
},
|
|
666
|
+
direct: (liveClient, input) => liveClient.expenses.void({ tripRef, ...input })
|
|
667
|
+
});
|
|
668
|
+
const submitClaimMutation = useOfflineMutation({
|
|
669
|
+
tripRef,
|
|
670
|
+
enqueue: (engine, input) => {
|
|
671
|
+
const { expenseId } = input;
|
|
672
|
+
return engine.enqueue({
|
|
673
|
+
tripRef,
|
|
674
|
+
method: SyncOpMethod.Post,
|
|
675
|
+
path: `/api/v1/trips/${tripRef}/expenses/${expenseId}/claim`,
|
|
676
|
+
operationId: "submitExpenseClaim",
|
|
677
|
+
body: {}
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
const withdrawClaimMutation = useOfflineMutation({
|
|
682
|
+
tripRef,
|
|
683
|
+
enqueue: (engine, input) => {
|
|
684
|
+
const { expenseId, version } = input;
|
|
685
|
+
return engine.enqueue({
|
|
686
|
+
tripRef,
|
|
687
|
+
method: SyncOpMethod.Delete,
|
|
688
|
+
path: `/api/v1/trips/${tripRef}/expenses/${expenseId}/claim/withdraw`,
|
|
689
|
+
operationId: "withdrawExpenseClaim",
|
|
690
|
+
body: {},
|
|
691
|
+
ifMatch: version
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
const read = useCallback(
|
|
696
|
+
(expenseId) => client.expenses.read({ tripRef, expenseId }),
|
|
697
|
+
[client, tripRef]
|
|
698
|
+
);
|
|
699
|
+
return {
|
|
700
|
+
expenses,
|
|
701
|
+
syncedAt,
|
|
702
|
+
refreshLive,
|
|
703
|
+
totals: {
|
|
704
|
+
spendTotalMinor,
|
|
705
|
+
categoryTotals,
|
|
706
|
+
loaded: totalsLoaded,
|
|
707
|
+
lastFetchFailed: totalsFailed,
|
|
708
|
+
refresh: refreshTotals
|
|
709
|
+
},
|
|
710
|
+
read,
|
|
711
|
+
log: logMutation.mutate,
|
|
712
|
+
linkReceipt: linkReceiptMutation.mutate,
|
|
713
|
+
void: voidMutation.mutate,
|
|
714
|
+
submitClaim: submitClaimMutation.mutate,
|
|
715
|
+
withdrawClaim: withdrawClaimMutation.mutate
|
|
716
|
+
};
|
|
717
|
+
});
|
|
718
|
+
function useExpenses(tripRef) {
|
|
719
|
+
return useExpensesInternal(tripRef);
|
|
720
|
+
}
|
|
721
|
+
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
722
|
+
function useFiles(context) {
|
|
723
|
+
const client = useKaafilClient();
|
|
724
|
+
const { tripRef } = context;
|
|
725
|
+
const request = useCallback(
|
|
726
|
+
(input) => client.files.request({ ...input, tripRef }),
|
|
727
|
+
[client, tripRef]
|
|
728
|
+
);
|
|
729
|
+
const confirm = useCallback((input) => client.files.confirm(input), [client]);
|
|
730
|
+
const meta = useCallback((fileId) => client.files.meta({ fileId }), [client]);
|
|
731
|
+
const url = useCallback((fileId) => client.files.url({ fileId }), [client]);
|
|
732
|
+
const list = useCallback(
|
|
733
|
+
async (query) => (await client.files.list({ ...query, tripRef })).items,
|
|
734
|
+
[client, tripRef]
|
|
735
|
+
);
|
|
736
|
+
const managerView = useCallback(
|
|
737
|
+
(fileId) => client.files.managerView({ fileId }),
|
|
738
|
+
[client]
|
|
739
|
+
);
|
|
740
|
+
const upload = useCallback(
|
|
741
|
+
async ({
|
|
742
|
+
file,
|
|
743
|
+
purpose,
|
|
744
|
+
title,
|
|
745
|
+
travellerRef,
|
|
746
|
+
maxAttempts = DEFAULT_MAX_ATTEMPTS,
|
|
747
|
+
signal
|
|
748
|
+
}) => {
|
|
749
|
+
let lastError;
|
|
750
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
751
|
+
const created = await client.files.request({
|
|
752
|
+
tripRef,
|
|
753
|
+
purpose,
|
|
754
|
+
contentType: file.type,
|
|
755
|
+
sizeBytes: file.size,
|
|
756
|
+
// Conditional spreads, not `title: title` — the engine rejects a
|
|
757
|
+
// present-but-undefined key on the wrong purpose the same way it
|
|
758
|
+
// rejects a real one, and "absent" is the only thing that means
|
|
759
|
+
// "no title"/"the whole trip".
|
|
760
|
+
...title !== void 0 ? { title } : {},
|
|
761
|
+
...travellerRef !== void 0 ? { travellerRef } : {},
|
|
762
|
+
...signal !== void 0 ? { signal } : {}
|
|
763
|
+
});
|
|
764
|
+
try {
|
|
765
|
+
const put = await fetch(created.uploadUrl, {
|
|
766
|
+
method: "PUT",
|
|
767
|
+
headers: { "Content-Type": file.type },
|
|
768
|
+
body: file,
|
|
769
|
+
...signal !== void 0 ? { signal } : {}
|
|
770
|
+
});
|
|
771
|
+
if (!put.ok) {
|
|
772
|
+
lastError = new Error(`Upload PUT failed with status ${put.status}`);
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
} catch (putError) {
|
|
776
|
+
lastError = putError;
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
try {
|
|
780
|
+
return await client.files.confirm({
|
|
781
|
+
fileId: created.fileId,
|
|
782
|
+
...signal !== void 0 ? { signal } : {}
|
|
783
|
+
});
|
|
784
|
+
} catch (confirmError) {
|
|
785
|
+
lastError = confirmError;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
throw lastError instanceof Error ? lastError : new Error("File upload did not confirm after retrying.");
|
|
789
|
+
},
|
|
790
|
+
[client, tripRef]
|
|
791
|
+
);
|
|
792
|
+
return { request, confirm, meta, url, list, managerView, upload };
|
|
793
|
+
}
|
|
794
|
+
var useFloatInternal = createDomainHook("float", (tripRef) => {
|
|
795
|
+
const client = useKaafilClient();
|
|
796
|
+
const { rows: movements, syncedAt } = useSnapshotList(tripRef, "float");
|
|
797
|
+
const [summaryRows, setSummaryRows] = useState([]);
|
|
798
|
+
const [summaryLoaded, setSummaryLoaded] = useState(false);
|
|
799
|
+
const [summaryFetchFailed, setSummaryFetchFailed] = useState(false);
|
|
800
|
+
const [summaryFetchedAt, setSummaryFetchedAt] = useState(void 0);
|
|
801
|
+
const refreshSummary = useCallback(async () => {
|
|
802
|
+
try {
|
|
803
|
+
const response = await client.float.readSummary({ tripRef });
|
|
804
|
+
setSummaryRows(response.data);
|
|
805
|
+
setSummaryLoaded(true);
|
|
806
|
+
setSummaryFetchFailed(false);
|
|
807
|
+
setSummaryFetchedAt(response.meta.serverTime);
|
|
808
|
+
} catch {
|
|
809
|
+
setSummaryFetchFailed(true);
|
|
810
|
+
}
|
|
811
|
+
}, [client, tripRef]);
|
|
812
|
+
useEffect(() => {
|
|
813
|
+
void refreshSummary();
|
|
814
|
+
}, [refreshSummary]);
|
|
815
|
+
const issueMutation = useOfflineMutation({
|
|
816
|
+
tripRef,
|
|
817
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
818
|
+
tripRef,
|
|
819
|
+
method: SyncOpMethod.Post,
|
|
820
|
+
path: `/api/v1/trips/${tripRef}/float/issue`,
|
|
821
|
+
// `OperationId` is not exported from `kaafil-js/client`'s public
|
|
822
|
+
// surface (verified, same gap `useRooming.ts` documents for its own
|
|
823
|
+
// literal operationId casts).
|
|
824
|
+
operationId: "issueFloat",
|
|
825
|
+
body: input
|
|
826
|
+
}),
|
|
827
|
+
// The desk lane. All three float writes take the admin triple — `return`
|
|
828
|
+
// included, contrary to what this file used to claim — and a desk
|
|
829
|
+
// console mounts no `OfflineShell`, so without these the writes have no
|
|
830
|
+
// transport at all.
|
|
831
|
+
direct: (liveClient, input) => liveClient.float.issue({ tripRef, ...input })
|
|
832
|
+
});
|
|
833
|
+
const returnMutation = useOfflineMutation({
|
|
834
|
+
tripRef,
|
|
835
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
836
|
+
tripRef,
|
|
837
|
+
method: SyncOpMethod.Post,
|
|
838
|
+
path: `/api/v1/trips/${tripRef}/float/return`,
|
|
839
|
+
operationId: "returnFloat",
|
|
840
|
+
body: input
|
|
841
|
+
}),
|
|
842
|
+
direct: (liveClient, input) => liveClient.float.return({ tripRef, ...input })
|
|
843
|
+
});
|
|
844
|
+
const adjustMutation = useOfflineMutation({
|
|
845
|
+
tripRef,
|
|
846
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
847
|
+
tripRef,
|
|
848
|
+
method: SyncOpMethod.Post,
|
|
849
|
+
path: `/api/v1/trips/${tripRef}/float/adjust`,
|
|
850
|
+
operationId: "adjustFloat",
|
|
851
|
+
body: input
|
|
852
|
+
}),
|
|
853
|
+
direct: (liveClient, input) => liveClient.float.adjust({ tripRef, ...input })
|
|
854
|
+
});
|
|
855
|
+
const issueMutate = issueMutation.mutate;
|
|
856
|
+
const issue = useCallback(
|
|
857
|
+
async (input) => {
|
|
858
|
+
const op = await issueMutate(input);
|
|
859
|
+
void refreshSummary();
|
|
860
|
+
return op;
|
|
861
|
+
},
|
|
862
|
+
[issueMutate, refreshSummary]
|
|
863
|
+
);
|
|
864
|
+
const returnMutate = returnMutation.mutate;
|
|
865
|
+
const returnFloat = useCallback(
|
|
866
|
+
async (input) => {
|
|
867
|
+
const op = await returnMutate(input);
|
|
868
|
+
void refreshSummary();
|
|
869
|
+
return op;
|
|
870
|
+
},
|
|
871
|
+
[returnMutate, refreshSummary]
|
|
872
|
+
);
|
|
873
|
+
const adjustMutate = adjustMutation.mutate;
|
|
874
|
+
const adjust = useCallback(
|
|
875
|
+
async (input) => {
|
|
876
|
+
const op = await adjustMutate(input);
|
|
877
|
+
void refreshSummary();
|
|
878
|
+
return op;
|
|
879
|
+
},
|
|
880
|
+
[adjustMutate, refreshSummary]
|
|
881
|
+
);
|
|
882
|
+
const readLedger = useCallback(
|
|
883
|
+
(managerId) => client.float.readLedger({ tripRef, managerId }),
|
|
884
|
+
[client, tripRef]
|
|
885
|
+
);
|
|
886
|
+
return {
|
|
887
|
+
movements,
|
|
888
|
+
syncedAt,
|
|
889
|
+
readLedger,
|
|
890
|
+
summary: {
|
|
891
|
+
rows: summaryRows,
|
|
892
|
+
loaded: summaryLoaded,
|
|
893
|
+
lastFetchFailed: summaryFetchFailed,
|
|
894
|
+
fetchedAt: summaryFetchedAt,
|
|
895
|
+
refresh: refreshSummary
|
|
896
|
+
},
|
|
897
|
+
issue,
|
|
898
|
+
return: returnFloat,
|
|
899
|
+
adjust
|
|
900
|
+
};
|
|
901
|
+
});
|
|
902
|
+
function useFloat(tripRef) {
|
|
903
|
+
return useFloatInternal(tripRef);
|
|
904
|
+
}
|
|
905
|
+
var FORM_FIELD_KIND_SINGLE_BOOLEAN = "CHECKBOX";
|
|
906
|
+
var useFormsInternal = createDomainHook("forms", (tripRef) => {
|
|
907
|
+
const client = useKaafilClient();
|
|
908
|
+
const engine = useContext(OfflineEngineContext);
|
|
909
|
+
const [forms, setForms] = useState([]);
|
|
910
|
+
const [syncedAt, setSyncedAt] = useState(void 0);
|
|
911
|
+
const refreshForms = useCallback(async () => {
|
|
912
|
+
try {
|
|
913
|
+
const response = await client.forms.trip.list({ tripRef });
|
|
914
|
+
setForms(response.items);
|
|
915
|
+
setSyncedAt(response.meta.serverTime);
|
|
916
|
+
} catch {
|
|
917
|
+
}
|
|
918
|
+
}, [client, tripRef]);
|
|
919
|
+
useEffect(() => {
|
|
920
|
+
void refreshForms();
|
|
921
|
+
}, [refreshForms]);
|
|
922
|
+
const refreshFormsAfterDrain = useCallback(() => {
|
|
923
|
+
if (engine === null) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const unsubscribe = engine.events.on("sync.drained", () => {
|
|
927
|
+
unsubscribe();
|
|
928
|
+
void refreshForms();
|
|
929
|
+
});
|
|
930
|
+
}, [engine, refreshForms]);
|
|
931
|
+
const getDefinition = useCallback(
|
|
932
|
+
async (formId) => client.forms.get({ formId }),
|
|
933
|
+
[client]
|
|
934
|
+
);
|
|
935
|
+
const getResponse = useCallback(
|
|
936
|
+
async (formId, responseId) => client.forms.responses.get({ formId, responseId }),
|
|
937
|
+
[client]
|
|
938
|
+
);
|
|
939
|
+
const travellerResponses = useCallback(
|
|
940
|
+
async (travellerRef) => {
|
|
941
|
+
const response = await client.forms.trip.travellerResponses.list({ tripRef, travellerRef });
|
|
942
|
+
return response.items;
|
|
943
|
+
},
|
|
944
|
+
[client, tripRef]
|
|
945
|
+
);
|
|
946
|
+
const formResponses = useCallback(
|
|
947
|
+
async (formId) => {
|
|
948
|
+
const response = await client.forms.trip.responses.list({ tripRef, formId });
|
|
949
|
+
return response.items;
|
|
950
|
+
},
|
|
951
|
+
[client, tripRef]
|
|
952
|
+
);
|
|
953
|
+
const submitMutation = useOfflineMutation({
|
|
954
|
+
tripRef,
|
|
955
|
+
enqueue: (engine2, input) => {
|
|
956
|
+
const { formId, ...body } = input;
|
|
957
|
+
return engine2.enqueue({
|
|
958
|
+
tripRef,
|
|
959
|
+
method: SyncOpMethod.Post,
|
|
960
|
+
path: `/api/v1/trips/${tripRef}/forms/${formId}/responses`,
|
|
961
|
+
// Hardcoded literal, not imported: `OperationId` is not exported
|
|
962
|
+
// from `kaafil-js/client`'s public surface (same reasoning
|
|
963
|
+
// `useRooming.ts` documents). Verified against
|
|
964
|
+
// `kaafil-js/src/generated/security.ts` this session:
|
|
965
|
+
// `OperationId.ManagerCreateFormResponse === 'managerCreateFormResponse'`.
|
|
966
|
+
operationId: "managerCreateFormResponse",
|
|
967
|
+
body
|
|
968
|
+
});
|
|
969
|
+
},
|
|
970
|
+
// THE DESK LANE. On-behalf capture is exactly what an office does when
|
|
971
|
+
// a traveller phones in an answer, and a desk console mounts no
|
|
972
|
+
// `OfflineShell` — without this the write has no transport at all.
|
|
973
|
+
// `idempotencyKey` is OPTIONAL on `ManagerCreateFormResponseOptions`,
|
|
974
|
+
// so nothing here mints one (hard rule #4).
|
|
975
|
+
direct: (liveClient, input) => {
|
|
976
|
+
const { formId, ...body } = input;
|
|
977
|
+
return liveClient.forms.trip.responses.create({ tripRef, formId, ...body });
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
const submitMutate = submitMutation.mutate;
|
|
981
|
+
const submitResponse = useCallback(
|
|
982
|
+
async (input) => {
|
|
983
|
+
const op = await submitMutate(input);
|
|
984
|
+
void refreshForms();
|
|
985
|
+
refreshFormsAfterDrain();
|
|
986
|
+
return op;
|
|
987
|
+
},
|
|
988
|
+
[submitMutate, refreshForms, refreshFormsAfterDrain]
|
|
989
|
+
);
|
|
990
|
+
const completion = useCallback(async () => {
|
|
991
|
+
const response = await client.forms.trip.completion({ tripRef });
|
|
992
|
+
return response.items;
|
|
993
|
+
}, [client, tripRef]);
|
|
994
|
+
const dispatch = useCallback(
|
|
995
|
+
async (input) => {
|
|
996
|
+
const { formId, ...body } = input;
|
|
997
|
+
await client.forms.trip.dispatch({ tripRef, formId, ...body });
|
|
998
|
+
void refreshForms();
|
|
999
|
+
},
|
|
1000
|
+
[client, tripRef, refreshForms]
|
|
1001
|
+
);
|
|
1002
|
+
const exportResponses = useCallback(
|
|
1003
|
+
(input) => client.forms.responses.export(input),
|
|
1004
|
+
[client]
|
|
1005
|
+
);
|
|
1006
|
+
const consentReceipt = useCallback(
|
|
1007
|
+
(input) => client.forms.responses.consentReceipt(input),
|
|
1008
|
+
[client]
|
|
1009
|
+
);
|
|
1010
|
+
const listAgencyForms = useCallback(async () => {
|
|
1011
|
+
const response = await client.forms.list({});
|
|
1012
|
+
return response.items;
|
|
1013
|
+
}, [client]);
|
|
1014
|
+
const createForm = useCallback(
|
|
1015
|
+
async (input) => client.forms.create(input),
|
|
1016
|
+
[client]
|
|
1017
|
+
);
|
|
1018
|
+
const patchForm = useCallback(
|
|
1019
|
+
async (input) => client.forms.patch(input),
|
|
1020
|
+
[client]
|
|
1021
|
+
);
|
|
1022
|
+
const publishForm = useCallback(
|
|
1023
|
+
async (formId) => client.forms.publish({ formId }),
|
|
1024
|
+
[client]
|
|
1025
|
+
);
|
|
1026
|
+
const closeForm = useCallback(
|
|
1027
|
+
async (formId) => client.forms.close({ formId }),
|
|
1028
|
+
[client]
|
|
1029
|
+
);
|
|
1030
|
+
const reopenForm = useCallback(
|
|
1031
|
+
async (formId) => client.forms.reopen({ formId }),
|
|
1032
|
+
[client]
|
|
1033
|
+
);
|
|
1034
|
+
const archiveForm = useCallback(
|
|
1035
|
+
async (formId) => client.forms.archive({ formId }),
|
|
1036
|
+
[client]
|
|
1037
|
+
);
|
|
1038
|
+
const unarchiveForm = useCallback(
|
|
1039
|
+
async (formId) => client.forms.unarchive({ formId }),
|
|
1040
|
+
[client]
|
|
1041
|
+
);
|
|
1042
|
+
const cloneForm = useCallback(
|
|
1043
|
+
async (input) => client.forms.clone(input),
|
|
1044
|
+
[client]
|
|
1045
|
+
);
|
|
1046
|
+
const reorderForm = useCallback(
|
|
1047
|
+
async (input) => {
|
|
1048
|
+
await client.forms.reorder(input);
|
|
1049
|
+
},
|
|
1050
|
+
[client]
|
|
1051
|
+
);
|
|
1052
|
+
const createSection = useCallback(
|
|
1053
|
+
async (input) => client.forms.sections.create(input),
|
|
1054
|
+
[client]
|
|
1055
|
+
);
|
|
1056
|
+
const patchSection = useCallback(
|
|
1057
|
+
async (input) => client.forms.sections.patch(input),
|
|
1058
|
+
[client]
|
|
1059
|
+
);
|
|
1060
|
+
const deleteSection = useCallback(
|
|
1061
|
+
async (input) => {
|
|
1062
|
+
await client.forms.sections.delete(input);
|
|
1063
|
+
},
|
|
1064
|
+
[client]
|
|
1065
|
+
);
|
|
1066
|
+
const createField = useCallback(
|
|
1067
|
+
async (input) => client.forms.fields.create(input),
|
|
1068
|
+
[client]
|
|
1069
|
+
);
|
|
1070
|
+
const patchField = useCallback(
|
|
1071
|
+
async (input) => client.forms.fields.patch(input),
|
|
1072
|
+
[client]
|
|
1073
|
+
);
|
|
1074
|
+
const deleteField = useCallback(
|
|
1075
|
+
async (input) => {
|
|
1076
|
+
await client.forms.fields.delete(input);
|
|
1077
|
+
},
|
|
1078
|
+
[client]
|
|
1079
|
+
);
|
|
1080
|
+
const listBindings = useCallback(async () => {
|
|
1081
|
+
const response = await client.forms.bindings.list();
|
|
1082
|
+
return response.items;
|
|
1083
|
+
}, [client]);
|
|
1084
|
+
return {
|
|
1085
|
+
forms,
|
|
1086
|
+
syncedAt,
|
|
1087
|
+
getDefinition,
|
|
1088
|
+
getResponse,
|
|
1089
|
+
travellerResponses,
|
|
1090
|
+
formResponses,
|
|
1091
|
+
submitResponse,
|
|
1092
|
+
refresh: refreshForms,
|
|
1093
|
+
completion,
|
|
1094
|
+
dispatch,
|
|
1095
|
+
exportResponses,
|
|
1096
|
+
consentReceipt,
|
|
1097
|
+
listAgencyForms,
|
|
1098
|
+
createForm,
|
|
1099
|
+
patchForm,
|
|
1100
|
+
publishForm,
|
|
1101
|
+
closeForm,
|
|
1102
|
+
reopenForm,
|
|
1103
|
+
archiveForm,
|
|
1104
|
+
unarchiveForm,
|
|
1105
|
+
cloneForm,
|
|
1106
|
+
reorderForm,
|
|
1107
|
+
createSection,
|
|
1108
|
+
patchSection,
|
|
1109
|
+
deleteSection,
|
|
1110
|
+
createField,
|
|
1111
|
+
patchField,
|
|
1112
|
+
deleteField,
|
|
1113
|
+
listBindings
|
|
1114
|
+
};
|
|
1115
|
+
});
|
|
1116
|
+
function useForms(tripRef) {
|
|
1117
|
+
return useFormsInternal(tripRef);
|
|
1118
|
+
}
|
|
1119
|
+
function isLiveItineraryItem(row) {
|
|
1120
|
+
return !("_tombstone" in row);
|
|
1121
|
+
}
|
|
1122
|
+
function useItinerary(tripRef) {
|
|
1123
|
+
const client = useKaafilClient();
|
|
1124
|
+
const [trip, setTrip] = useState(void 0);
|
|
1125
|
+
const [initialDayIso, setInitialDayIso] = useState(void 0);
|
|
1126
|
+
const [canAddItems, setCanAddItems] = useState(false);
|
|
1127
|
+
const [canAddItemsReason, setCanAddItemsReason] = useState(null);
|
|
1128
|
+
const [days, setDays] = useState([]);
|
|
1129
|
+
const [items, setItems] = useState([]);
|
|
1130
|
+
const [loaded, setLoaded] = useState(false);
|
|
1131
|
+
const [lastFetchFailed, setLastFetchFailed] = useState(false);
|
|
1132
|
+
const [syncedAt, setSyncedAt] = useState(void 0);
|
|
1133
|
+
const refresh = useCallback(async () => {
|
|
1134
|
+
try {
|
|
1135
|
+
const response = await client.itinerary.read({ tripRef });
|
|
1136
|
+
setTrip(response.trip);
|
|
1137
|
+
setInitialDayIso(response.initialDayIso);
|
|
1138
|
+
setCanAddItems(response.canAddItems);
|
|
1139
|
+
setCanAddItemsReason(response.canAddItemsReason);
|
|
1140
|
+
setDays(response.days);
|
|
1141
|
+
setItems(response.items.filter(isLiveItineraryItem));
|
|
1142
|
+
setLoaded(true);
|
|
1143
|
+
setLastFetchFailed(false);
|
|
1144
|
+
setSyncedAt(response.meta.serverTime);
|
|
1145
|
+
} catch {
|
|
1146
|
+
setLastFetchFailed(true);
|
|
1147
|
+
}
|
|
1148
|
+
}, [client, tripRef]);
|
|
1149
|
+
useEffect(() => {
|
|
1150
|
+
void refresh();
|
|
1151
|
+
}, [refresh]);
|
|
1152
|
+
const [changeLog, setChangeLog] = useState([]);
|
|
1153
|
+
const [changeLogLoaded, setChangeLogLoaded] = useState(false);
|
|
1154
|
+
const [changeLogFetchFailed, setChangeLogFetchFailed] = useState(false);
|
|
1155
|
+
const loadChangeLog = useCallback(
|
|
1156
|
+
async (input) => {
|
|
1157
|
+
try {
|
|
1158
|
+
const response = await client.itinerary.changeLog.list({ tripRef, ...input });
|
|
1159
|
+
setChangeLog(response);
|
|
1160
|
+
setChangeLogLoaded(true);
|
|
1161
|
+
setChangeLogFetchFailed(false);
|
|
1162
|
+
} catch {
|
|
1163
|
+
setChangeLogFetchFailed(true);
|
|
1164
|
+
}
|
|
1165
|
+
},
|
|
1166
|
+
[client, tripRef]
|
|
1167
|
+
);
|
|
1168
|
+
const patchDayMutation = useOfflineMutation({
|
|
1169
|
+
tripRef,
|
|
1170
|
+
enqueue: (engine, input) => {
|
|
1171
|
+
const { dayIndex, version, ...body } = input;
|
|
1172
|
+
return engine.enqueue({
|
|
1173
|
+
tripRef,
|
|
1174
|
+
method: SyncOpMethod.Patch,
|
|
1175
|
+
path: `/api/v1/trips/${tripRef}/itinerary/days/${dayIndex}`,
|
|
1176
|
+
operationId: "patchItineraryDay",
|
|
1177
|
+
body,
|
|
1178
|
+
ifMatch: version
|
|
1179
|
+
});
|
|
1180
|
+
},
|
|
1181
|
+
direct: (client2, input) => client2.itinerary.days.patch({ tripRef, ...input })
|
|
1182
|
+
});
|
|
1183
|
+
const patchDayMutate = patchDayMutation.mutate;
|
|
1184
|
+
const patchDay = useCallback(
|
|
1185
|
+
async (input) => {
|
|
1186
|
+
const op = await patchDayMutate(input);
|
|
1187
|
+
void refresh();
|
|
1188
|
+
return op;
|
|
1189
|
+
},
|
|
1190
|
+
[patchDayMutate, refresh]
|
|
1191
|
+
);
|
|
1192
|
+
const addItemMutation = useOfflineMutation({
|
|
1193
|
+
tripRef,
|
|
1194
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1195
|
+
tripRef,
|
|
1196
|
+
method: SyncOpMethod.Post,
|
|
1197
|
+
path: `/api/v1/trips/${tripRef}/itinerary/items`,
|
|
1198
|
+
operationId: "addItineraryItem",
|
|
1199
|
+
body: input
|
|
1200
|
+
}),
|
|
1201
|
+
direct: (client2, input) => client2.itinerary.items.add({ tripRef, ...input })
|
|
1202
|
+
});
|
|
1203
|
+
const addItemMutate = addItemMutation.mutate;
|
|
1204
|
+
const addItem = useCallback(
|
|
1205
|
+
async (input) => {
|
|
1206
|
+
const op = await addItemMutate(input);
|
|
1207
|
+
void refresh();
|
|
1208
|
+
return op;
|
|
1209
|
+
},
|
|
1210
|
+
[addItemMutate, refresh]
|
|
1211
|
+
);
|
|
1212
|
+
const patchItemMutation = useOfflineMutation({
|
|
1213
|
+
tripRef,
|
|
1214
|
+
enqueue: (engine, input) => {
|
|
1215
|
+
const { itemId, version, ...body } = input;
|
|
1216
|
+
return engine.enqueue({
|
|
1217
|
+
tripRef,
|
|
1218
|
+
method: SyncOpMethod.Patch,
|
|
1219
|
+
path: `/api/v1/trips/${tripRef}/itinerary/items/${itemId}`,
|
|
1220
|
+
operationId: "patchItineraryItem",
|
|
1221
|
+
body,
|
|
1222
|
+
ifMatch: version
|
|
1223
|
+
});
|
|
1224
|
+
},
|
|
1225
|
+
direct: (client2, input) => client2.itinerary.items.patch({ tripRef, ...input })
|
|
1226
|
+
});
|
|
1227
|
+
const patchItemMutate = patchItemMutation.mutate;
|
|
1228
|
+
const patchItem = useCallback(
|
|
1229
|
+
async (input) => {
|
|
1230
|
+
const op = await patchItemMutate(input);
|
|
1231
|
+
void refresh();
|
|
1232
|
+
return op;
|
|
1233
|
+
},
|
|
1234
|
+
[patchItemMutate, refresh]
|
|
1235
|
+
);
|
|
1236
|
+
const removeItemMutation = useOfflineMutation({
|
|
1237
|
+
tripRef,
|
|
1238
|
+
enqueue: (engine, input) => {
|
|
1239
|
+
const { itemId, version } = input;
|
|
1240
|
+
return engine.enqueue({
|
|
1241
|
+
tripRef,
|
|
1242
|
+
method: SyncOpMethod.Delete,
|
|
1243
|
+
path: `/api/v1/trips/${tripRef}/itinerary/items/${itemId}`,
|
|
1244
|
+
operationId: "deleteItineraryItem",
|
|
1245
|
+
ifMatch: version
|
|
1246
|
+
});
|
|
1247
|
+
},
|
|
1248
|
+
direct: (client2, input) => client2.itinerary.items.remove({ tripRef, ...input })
|
|
1249
|
+
});
|
|
1250
|
+
const removeItemMutate = removeItemMutation.mutate;
|
|
1251
|
+
const removeItem = useCallback(
|
|
1252
|
+
async (input) => {
|
|
1253
|
+
const op = await removeItemMutate(input);
|
|
1254
|
+
void refresh();
|
|
1255
|
+
return op;
|
|
1256
|
+
},
|
|
1257
|
+
[removeItemMutate, refresh]
|
|
1258
|
+
);
|
|
1259
|
+
const reorderItemMutation = useOfflineMutation({
|
|
1260
|
+
tripRef,
|
|
1261
|
+
enqueue: (engine, input) => {
|
|
1262
|
+
const { itemId, ...body } = input;
|
|
1263
|
+
return engine.enqueue({
|
|
1264
|
+
tripRef,
|
|
1265
|
+
method: SyncOpMethod.Post,
|
|
1266
|
+
path: `/api/v1/trips/${tripRef}/itinerary/items/${itemId}/reorder`,
|
|
1267
|
+
operationId: "reorderItineraryItem",
|
|
1268
|
+
body
|
|
1269
|
+
});
|
|
1270
|
+
},
|
|
1271
|
+
direct: (client2, input) => client2.itinerary.items.reorder({ tripRef, ...input })
|
|
1272
|
+
});
|
|
1273
|
+
const reorderItemMutate = reorderItemMutation.mutate;
|
|
1274
|
+
const reorderItem = useCallback(
|
|
1275
|
+
async (input) => {
|
|
1276
|
+
const op = await reorderItemMutate(input);
|
|
1277
|
+
void refresh();
|
|
1278
|
+
return op;
|
|
1279
|
+
},
|
|
1280
|
+
[reorderItemMutate, refresh]
|
|
1281
|
+
);
|
|
1282
|
+
if (!loaded && !lastFetchFailed) {
|
|
1283
|
+
return { status: "loading" };
|
|
1284
|
+
}
|
|
1285
|
+
if (!loaded && lastFetchFailed) {
|
|
1286
|
+
return { status: "error" };
|
|
1287
|
+
}
|
|
1288
|
+
return {
|
|
1289
|
+
status: "ready",
|
|
1290
|
+
trip,
|
|
1291
|
+
initialDayIso,
|
|
1292
|
+
canAddItems,
|
|
1293
|
+
canAddItemsReason,
|
|
1294
|
+
days,
|
|
1295
|
+
items,
|
|
1296
|
+
loaded,
|
|
1297
|
+
lastFetchFailed,
|
|
1298
|
+
syncedAt,
|
|
1299
|
+
refresh,
|
|
1300
|
+
changeLog,
|
|
1301
|
+
changeLogLoaded,
|
|
1302
|
+
changeLogFetchFailed,
|
|
1303
|
+
loadChangeLog,
|
|
1304
|
+
patchDay,
|
|
1305
|
+
addItem,
|
|
1306
|
+
patchItem,
|
|
1307
|
+
removeItem,
|
|
1308
|
+
reorderItem
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
function useManagers(tripRef) {
|
|
1312
|
+
const client = useKaafilClient();
|
|
1313
|
+
const persona = usePersona();
|
|
1314
|
+
const [result, setResult] = useState({ status: "loading" });
|
|
1315
|
+
useEffect(() => {
|
|
1316
|
+
if (persona === "share") {
|
|
1317
|
+
if (isDevelopment()) {
|
|
1318
|
+
throw new Error(
|
|
1319
|
+
"useManagers() was called from a share session \u2014 it is manager/agencyAdmin only."
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
let cancelled = false;
|
|
1325
|
+
setResult({ status: "loading" });
|
|
1326
|
+
void Promise.resolve().then(() => client.trips.managers.list({ tripRef })).then((managers) => {
|
|
1327
|
+
if (!cancelled) {
|
|
1328
|
+
setResult({ status: "ready", managers });
|
|
1329
|
+
}
|
|
1330
|
+
}).catch((error) => {
|
|
1331
|
+
if (!cancelled) {
|
|
1332
|
+
setResult({ status: "error", error: classifyError(error) });
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
return () => {
|
|
1336
|
+
cancelled = true;
|
|
1337
|
+
};
|
|
1338
|
+
}, [client, persona, tripRef]);
|
|
1339
|
+
return result;
|
|
1340
|
+
}
|
|
1341
|
+
function useManagerToday(tripRef) {
|
|
1342
|
+
const client = useKaafilClient();
|
|
1343
|
+
const [result, setResult] = useState({ status: "loading" });
|
|
1344
|
+
useEffect(() => {
|
|
1345
|
+
let cancelled = false;
|
|
1346
|
+
setResult({ status: "loading" });
|
|
1347
|
+
client.managerToday.read({ tripRef }).then((response) => {
|
|
1348
|
+
if (cancelled) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
const { meta, ...data } = response;
|
|
1352
|
+
setResult({ status: "ready", ...data, syncedAt: meta.serverTime });
|
|
1353
|
+
}).catch(() => {
|
|
1354
|
+
if (!cancelled) {
|
|
1355
|
+
setResult({ status: "error" });
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
return () => {
|
|
1359
|
+
cancelled = true;
|
|
1360
|
+
};
|
|
1361
|
+
}, [client, tripRef]);
|
|
1362
|
+
return result;
|
|
1363
|
+
}
|
|
1364
|
+
function isLiveParty(row) {
|
|
1365
|
+
return !("_tombstone" in row);
|
|
1366
|
+
}
|
|
1367
|
+
function useParties(tripRef) {
|
|
1368
|
+
const client = useKaafilClient();
|
|
1369
|
+
const [rows, setRows] = useState([]);
|
|
1370
|
+
const [listLoaded, setListLoaded] = useState(false);
|
|
1371
|
+
const [listFetchFailed, setListFetchFailed] = useState(false);
|
|
1372
|
+
const [listFetchedAt, setListFetchedAt] = useState(void 0);
|
|
1373
|
+
const [firstLoadError, setFirstLoadError] = useState(void 0);
|
|
1374
|
+
const refreshList = useCallback(async () => {
|
|
1375
|
+
try {
|
|
1376
|
+
const response = await client.trips.parties.list({ tripRef });
|
|
1377
|
+
setRows(response.filter(isLiveParty));
|
|
1378
|
+
setListLoaded(true);
|
|
1379
|
+
setListFetchFailed(false);
|
|
1380
|
+
setListFetchedAt(response.meta.serverTime);
|
|
1381
|
+
setFirstLoadError(void 0);
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
setListFetchFailed(true);
|
|
1384
|
+
setFirstLoadError((previous) => previous ?? classifyError(error));
|
|
1385
|
+
}
|
|
1386
|
+
}, [client, tripRef]);
|
|
1387
|
+
useEffect(() => {
|
|
1388
|
+
void refreshList();
|
|
1389
|
+
}, [refreshList]);
|
|
1390
|
+
const [rosterRows, setRosterRows] = useState([]);
|
|
1391
|
+
const [rosterLoaded, setRosterLoaded] = useState(false);
|
|
1392
|
+
const [rosterFetchFailed, setRosterFetchFailed] = useState(false);
|
|
1393
|
+
const [rosterFetchedAt, setRosterFetchedAt] = useState(void 0);
|
|
1394
|
+
const refreshRoster = useCallback(async () => {
|
|
1395
|
+
try {
|
|
1396
|
+
const response = await client.travellers.listForTrip({ tripRef });
|
|
1397
|
+
setRosterRows(response);
|
|
1398
|
+
setRosterLoaded(true);
|
|
1399
|
+
setRosterFetchFailed(false);
|
|
1400
|
+
setRosterFetchedAt(response.meta.serverTime);
|
|
1401
|
+
} catch {
|
|
1402
|
+
setRosterFetchFailed(true);
|
|
1403
|
+
}
|
|
1404
|
+
}, [client, tripRef]);
|
|
1405
|
+
useEffect(() => {
|
|
1406
|
+
void refreshRoster();
|
|
1407
|
+
}, [refreshRoster]);
|
|
1408
|
+
const addMutation = useOfflineMutation({
|
|
1409
|
+
tripRef,
|
|
1410
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1411
|
+
tripRef,
|
|
1412
|
+
method: SyncOpMethod.Post,
|
|
1413
|
+
path: `/api/v1/trips/${tripRef}/parties`,
|
|
1414
|
+
operationId: "createParty",
|
|
1415
|
+
body: input
|
|
1416
|
+
})
|
|
1417
|
+
});
|
|
1418
|
+
const patchMutation = useOfflineMutation({
|
|
1419
|
+
tripRef,
|
|
1420
|
+
enqueue: (engine, input) => {
|
|
1421
|
+
const { partyId, version, ...body } = input;
|
|
1422
|
+
return engine.enqueue({
|
|
1423
|
+
tripRef,
|
|
1424
|
+
method: SyncOpMethod.Patch,
|
|
1425
|
+
path: `/api/v1/trips/${tripRef}/parties/${partyId}`,
|
|
1426
|
+
operationId: "patchParty",
|
|
1427
|
+
body,
|
|
1428
|
+
ifMatch: version
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
const removeMutation = useOfflineMutation({
|
|
1433
|
+
tripRef,
|
|
1434
|
+
enqueue: (engine, input) => {
|
|
1435
|
+
const { partyId } = input;
|
|
1436
|
+
return engine.enqueue({
|
|
1437
|
+
tripRef,
|
|
1438
|
+
method: SyncOpMethod.Delete,
|
|
1439
|
+
path: `/api/v1/trips/${tripRef}/parties/${partyId}`,
|
|
1440
|
+
operationId: "deleteParty"
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
const addMutate = addMutation.mutate;
|
|
1445
|
+
const add = useCallback(
|
|
1446
|
+
async (input) => {
|
|
1447
|
+
const op = await addMutate(input);
|
|
1448
|
+
void refreshList();
|
|
1449
|
+
return op;
|
|
1450
|
+
},
|
|
1451
|
+
[addMutate, refreshList]
|
|
1452
|
+
);
|
|
1453
|
+
const patchMutate = patchMutation.mutate;
|
|
1454
|
+
const patch = useCallback(
|
|
1455
|
+
async (input) => {
|
|
1456
|
+
const op = await patchMutate(input);
|
|
1457
|
+
void refreshList();
|
|
1458
|
+
return op;
|
|
1459
|
+
},
|
|
1460
|
+
[patchMutate, refreshList]
|
|
1461
|
+
);
|
|
1462
|
+
const removeMutate = removeMutation.mutate;
|
|
1463
|
+
const remove = useCallback(
|
|
1464
|
+
async (input) => {
|
|
1465
|
+
const op = await removeMutate(input);
|
|
1466
|
+
void refreshList();
|
|
1467
|
+
return op;
|
|
1468
|
+
},
|
|
1469
|
+
[removeMutate, refreshList]
|
|
1470
|
+
);
|
|
1471
|
+
if (!listLoaded && !listFetchFailed) {
|
|
1472
|
+
return { status: "loading" };
|
|
1473
|
+
}
|
|
1474
|
+
if (!listLoaded && listFetchFailed) {
|
|
1475
|
+
return {
|
|
1476
|
+
status: "error",
|
|
1477
|
+
error: firstLoadError ?? {
|
|
1478
|
+
path: "surface-to-user",
|
|
1479
|
+
code: void 0,
|
|
1480
|
+
status: void 0,
|
|
1481
|
+
unsatisfiableScheme: false,
|
|
1482
|
+
cause: void 0
|
|
1483
|
+
},
|
|
1484
|
+
retry: refreshList
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
return {
|
|
1488
|
+
status: "ready",
|
|
1489
|
+
list: {
|
|
1490
|
+
rows,
|
|
1491
|
+
loaded: listLoaded,
|
|
1492
|
+
lastFetchFailed: listFetchFailed,
|
|
1493
|
+
fetchedAt: listFetchedAt,
|
|
1494
|
+
refresh: refreshList
|
|
1495
|
+
},
|
|
1496
|
+
tripRoster: {
|
|
1497
|
+
rows: rosterRows,
|
|
1498
|
+
loaded: rosterLoaded,
|
|
1499
|
+
lastFetchFailed: rosterFetchFailed,
|
|
1500
|
+
fetchedAt: rosterFetchedAt,
|
|
1501
|
+
refresh: refreshRoster
|
|
1502
|
+
},
|
|
1503
|
+
add,
|
|
1504
|
+
patch,
|
|
1505
|
+
remove
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
var usePickupsInternal = createDomainHook("pickup-points", (tripRef) => {
|
|
1509
|
+
const client = useKaafilClient();
|
|
1510
|
+
const {
|
|
1511
|
+
rows: stops,
|
|
1512
|
+
syncedAt,
|
|
1513
|
+
refreshLive
|
|
1514
|
+
} = useSnapshotList(tripRef, "pickups", {
|
|
1515
|
+
live: async (liveClient, ref) => {
|
|
1516
|
+
const response = await liveClient.pickups.list({ tripRef: ref });
|
|
1517
|
+
return { rows: response, syncedAt: response.meta.serverTime };
|
|
1518
|
+
}
|
|
1519
|
+
});
|
|
1520
|
+
const createMutation = useOfflineMutation({
|
|
1521
|
+
tripRef,
|
|
1522
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1523
|
+
tripRef,
|
|
1524
|
+
method: SyncOpMethod.Post,
|
|
1525
|
+
path: `/api/v1/trips/${tripRef}/pickups`,
|
|
1526
|
+
// `OperationId` is not exported from `kaafil-js/client`'s public
|
|
1527
|
+
// surface (verified, same gap `useRooming.ts` documents) — every
|
|
1528
|
+
// literal below is checked against `kaafil-js/src/generated/
|
|
1529
|
+
// security.ts`'s own operation-id table.
|
|
1530
|
+
operationId: "createPickupStop",
|
|
1531
|
+
body: input
|
|
1532
|
+
}),
|
|
1533
|
+
// The desk lane. `board`, `close` and `reopen` below deliberately get
|
|
1534
|
+
// NONE — they are `managerAuth`-only on the wire, so a direct call from
|
|
1535
|
+
// an agency-admin session would be refused by `CredentialResolver`
|
|
1536
|
+
// before it left the process. See this file's own persona note.
|
|
1537
|
+
direct: (liveClient, input) => liveClient.pickups.create({ tripRef, ...input })
|
|
1538
|
+
});
|
|
1539
|
+
const patchMutation = useOfflineMutation({
|
|
1540
|
+
tripRef,
|
|
1541
|
+
enqueue: (engine, input) => {
|
|
1542
|
+
const { pointId, version, ...body } = input;
|
|
1543
|
+
return engine.enqueue({
|
|
1544
|
+
tripRef,
|
|
1545
|
+
method: SyncOpMethod.Patch,
|
|
1546
|
+
path: `/api/v1/trips/${tripRef}/pickups/${pointId}`,
|
|
1547
|
+
operationId: "patchPickupStop",
|
|
1548
|
+
body,
|
|
1549
|
+
ifMatch: version
|
|
1550
|
+
});
|
|
1551
|
+
},
|
|
1552
|
+
direct: (liveClient, input) => liveClient.pickups.patch({ tripRef, ...input })
|
|
1553
|
+
});
|
|
1554
|
+
const removeMutation = useOfflineMutation({
|
|
1555
|
+
tripRef,
|
|
1556
|
+
enqueue: (engine, input) => {
|
|
1557
|
+
const { pointId, version, force } = input;
|
|
1558
|
+
const query = force === true ? "?force=true" : "";
|
|
1559
|
+
return engine.enqueue({
|
|
1560
|
+
tripRef,
|
|
1561
|
+
method: SyncOpMethod.Delete,
|
|
1562
|
+
path: `/api/v1/trips/${tripRef}/pickups/${pointId}${query}`,
|
|
1563
|
+
operationId: "deletePickupStop",
|
|
1564
|
+
ifMatch: version
|
|
1565
|
+
});
|
|
1566
|
+
},
|
|
1567
|
+
direct: (liveClient, input) => liveClient.pickups.remove({ tripRef, ...input })
|
|
1568
|
+
});
|
|
1569
|
+
const reorderMutation = useOfflineMutation({
|
|
1570
|
+
tripRef,
|
|
1571
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1572
|
+
tripRef,
|
|
1573
|
+
method: SyncOpMethod.Post,
|
|
1574
|
+
path: `/api/v1/trips/${tripRef}/pickups/reorder`,
|
|
1575
|
+
operationId: "reorderPickupStops",
|
|
1576
|
+
body: input
|
|
1577
|
+
}),
|
|
1578
|
+
direct: (liveClient, input) => liveClient.pickups.reorder({ tripRef, ...input })
|
|
1579
|
+
});
|
|
1580
|
+
const assignMutation = useOfflineMutation({
|
|
1581
|
+
tripRef,
|
|
1582
|
+
enqueue: (engine, input) => {
|
|
1583
|
+
const { pointId, ...body } = input;
|
|
1584
|
+
return engine.enqueue({
|
|
1585
|
+
tripRef,
|
|
1586
|
+
method: SyncOpMethod.Post,
|
|
1587
|
+
path: `/api/v1/trips/${tripRef}/pickups/${pointId}/assign`,
|
|
1588
|
+
operationId: "assignPickupTraveller",
|
|
1589
|
+
body
|
|
1590
|
+
});
|
|
1591
|
+
},
|
|
1592
|
+
direct: (liveClient, input) => liveClient.pickups.assign({ tripRef, ...input })
|
|
1593
|
+
});
|
|
1594
|
+
const boardMutation = useOfflineMutation({
|
|
1595
|
+
tripRef,
|
|
1596
|
+
enqueue: (engine, input) => {
|
|
1597
|
+
const { pointId, ...body } = input;
|
|
1598
|
+
return engine.enqueue({
|
|
1599
|
+
tripRef,
|
|
1600
|
+
method: SyncOpMethod.Post,
|
|
1601
|
+
path: `/api/v1/trips/${tripRef}/pickups/${pointId}/board`,
|
|
1602
|
+
operationId: "boardPickupTraveller",
|
|
1603
|
+
body
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
const correctBoardStatusMutation = useOfflineMutation({
|
|
1608
|
+
tripRef,
|
|
1609
|
+
enqueue: (engine, input) => {
|
|
1610
|
+
const { pointId, travellerId, ...body } = input;
|
|
1611
|
+
return engine.enqueue({
|
|
1612
|
+
tripRef,
|
|
1613
|
+
method: SyncOpMethod.Patch,
|
|
1614
|
+
path: `/api/v1/trips/${tripRef}/pickups/${pointId}/board/${travellerId}`,
|
|
1615
|
+
operationId: "correctPickupBoardStatus",
|
|
1616
|
+
body
|
|
1617
|
+
});
|
|
1618
|
+
},
|
|
1619
|
+
// The office's ONE door onto the board table, so it needs the desk lane
|
|
1620
|
+
// as much as the five stop writes do — `board`/`close`/`reopen` below
|
|
1621
|
+
// still get none, because they are `managerAuth`-only.
|
|
1622
|
+
direct: (liveClient, input) => liveClient.pickups.correctBoardStatus({ tripRef, ...input })
|
|
1623
|
+
});
|
|
1624
|
+
const closeMutation = useOfflineMutation({
|
|
1625
|
+
tripRef,
|
|
1626
|
+
enqueue: (engine, input) => {
|
|
1627
|
+
const { pointId, ...body } = input;
|
|
1628
|
+
return engine.enqueue({
|
|
1629
|
+
tripRef,
|
|
1630
|
+
method: SyncOpMethod.Post,
|
|
1631
|
+
path: `/api/v1/trips/${tripRef}/pickups/${pointId}/close`,
|
|
1632
|
+
operationId: "closePickupStop",
|
|
1633
|
+
body
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1637
|
+
const reopenMutation = useOfflineMutation({
|
|
1638
|
+
tripRef,
|
|
1639
|
+
enqueue: (engine, input) => {
|
|
1640
|
+
const { pointId } = input;
|
|
1641
|
+
return engine.enqueue({
|
|
1642
|
+
tripRef,
|
|
1643
|
+
method: SyncOpMethod.Post,
|
|
1644
|
+
path: `/api/v1/trips/${tripRef}/pickups/${pointId}/reopen`,
|
|
1645
|
+
operationId: "reopenPickupStop"
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
const reassign = useCallback(
|
|
1650
|
+
(input) => client.pickups.reassign({ tripRef, ...input }),
|
|
1651
|
+
[client, tripRef]
|
|
1652
|
+
);
|
|
1653
|
+
const manifestByPickup = useCallback(
|
|
1654
|
+
(input) => client.pickups.manifestByPickup({ tripRef, ...input }),
|
|
1655
|
+
[client, tripRef]
|
|
1656
|
+
);
|
|
1657
|
+
return {
|
|
1658
|
+
stops,
|
|
1659
|
+
syncedAt,
|
|
1660
|
+
refreshLive,
|
|
1661
|
+
create: createMutation.mutate,
|
|
1662
|
+
patch: patchMutation.mutate,
|
|
1663
|
+
remove: removeMutation.mutate,
|
|
1664
|
+
reorder: reorderMutation.mutate,
|
|
1665
|
+
assign: assignMutation.mutate,
|
|
1666
|
+
reassign,
|
|
1667
|
+
board: boardMutation.mutate,
|
|
1668
|
+
correctBoardStatus: correctBoardStatusMutation.mutate,
|
|
1669
|
+
close: closeMutation.mutate,
|
|
1670
|
+
reopen: reopenMutation.mutate,
|
|
1671
|
+
manifestByPickup
|
|
1672
|
+
};
|
|
1673
|
+
});
|
|
1674
|
+
function usePickups(tripRef) {
|
|
1675
|
+
return usePickupsInternal(tripRef);
|
|
1676
|
+
}
|
|
1677
|
+
var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
|
|
1678
|
+
const client = useKaafilClient();
|
|
1679
|
+
const {
|
|
1680
|
+
rows: rooms,
|
|
1681
|
+
syncedAt: roomsSyncedAt,
|
|
1682
|
+
refreshLive: refreshRoomsLive
|
|
1683
|
+
} = useSnapshotList(tripRef, "rooming", {
|
|
1684
|
+
live: async (liveClient, ref) => {
|
|
1685
|
+
const response = await liveClient.rooming.read({ tripRef: ref });
|
|
1686
|
+
return { rows: response.rooms, syncedAt: response.meta.serverTime };
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
const {
|
|
1690
|
+
rows: stayWindows,
|
|
1691
|
+
syncedAt: windowsSyncedAt,
|
|
1692
|
+
refreshLive: refreshWindowsLive
|
|
1693
|
+
} = useSnapshotList(tripRef, "stayWindows", {
|
|
1694
|
+
live: async (liveClient, ref) => {
|
|
1695
|
+
const response = await liveClient.rooming.stayWindows.list({ tripRef: ref });
|
|
1696
|
+
return { rows: response, syncedAt: response.meta.serverTime };
|
|
1697
|
+
}
|
|
1698
|
+
});
|
|
1699
|
+
const refreshLive = useCallback(async () => {
|
|
1700
|
+
await Promise.all([refreshRoomsLive(), refreshWindowsLive()]);
|
|
1701
|
+
}, [refreshRoomsLive, refreshWindowsLive]);
|
|
1702
|
+
const assignMutation = useOfflineMutation({
|
|
1703
|
+
tripRef,
|
|
1704
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1705
|
+
tripRef,
|
|
1706
|
+
method: SyncOpMethod.Post,
|
|
1707
|
+
path: `/api/v1/trips/${tripRef}/rooming/assign`,
|
|
1708
|
+
// Hardcoded literal, not imported: `OperationId` is not exported from
|
|
1709
|
+
// `kaafil-js/client`'s public surface (verified — see this worker's
|
|
1710
|
+
// final report). Value verified against
|
|
1711
|
+
// `kaafil-js/src/generated/security.ts` this session.
|
|
1712
|
+
operationId: "assignRoomingBed",
|
|
1713
|
+
body: input,
|
|
1714
|
+
// `RoomingAssignDto.rooms` is every room this write touched, whole —
|
|
1715
|
+
// up to two, on a swap. `arrayKey` is this hook's multi-row rebase
|
|
1716
|
+
// (`OutboxEntityRef.arrayKey`), the reason the room grid now
|
|
1717
|
+
// repaints the instant an assign lands rather than waiting for the
|
|
1718
|
+
// next full pull.
|
|
1719
|
+
entity: { listName: "rooming", arrayKey: "rooms" }
|
|
1720
|
+
}),
|
|
1721
|
+
// The desk lane. Every rooming write takes the admin triple, so unlike
|
|
1722
|
+
// `usePickups` there is no member here that deliberately lacks one.
|
|
1723
|
+
direct: (liveClient, input) => liveClient.rooming.assign({ tripRef, ...input })
|
|
1724
|
+
});
|
|
1725
|
+
const [poolTravellers, setPoolTravellers] = useState([]);
|
|
1726
|
+
const [poolLoaded, setPoolLoaded] = useState(false);
|
|
1727
|
+
const [poolFetchFailed, setPoolFetchFailed] = useState(false);
|
|
1728
|
+
const [poolFetchedAt, setPoolFetchedAt] = useState(void 0);
|
|
1729
|
+
const lastRequestedStayWindowIdRef = useRef(void 0);
|
|
1730
|
+
const refreshUnassignedPool = useCallback(
|
|
1731
|
+
async (stayWindowId) => {
|
|
1732
|
+
lastRequestedStayWindowIdRef.current = stayWindowId;
|
|
1733
|
+
try {
|
|
1734
|
+
const response = await client.rooming.read({ tripRef, stayWindowId });
|
|
1735
|
+
setPoolTravellers(response.unassigned);
|
|
1736
|
+
setPoolLoaded(true);
|
|
1737
|
+
setPoolFetchFailed(false);
|
|
1738
|
+
setPoolFetchedAt(response.meta.serverTime);
|
|
1739
|
+
} catch {
|
|
1740
|
+
setPoolFetchFailed(true);
|
|
1741
|
+
}
|
|
1742
|
+
},
|
|
1743
|
+
[client, tripRef]
|
|
1744
|
+
);
|
|
1745
|
+
useEffect(() => {
|
|
1746
|
+
void refreshUnassignedPool();
|
|
1747
|
+
}, [refreshUnassignedPool]);
|
|
1748
|
+
const assignMutate = assignMutation.mutate;
|
|
1749
|
+
const assign = useCallback(
|
|
1750
|
+
async (input) => {
|
|
1751
|
+
const op = await assignMutate(input);
|
|
1752
|
+
void refreshUnassignedPool(lastRequestedStayWindowIdRef.current);
|
|
1753
|
+
return op;
|
|
1754
|
+
},
|
|
1755
|
+
[assignMutate, refreshUnassignedPool]
|
|
1756
|
+
);
|
|
1757
|
+
const createWindowMutation = useOfflineMutation({
|
|
1758
|
+
tripRef,
|
|
1759
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1760
|
+
tripRef,
|
|
1761
|
+
method: SyncOpMethod.Post,
|
|
1762
|
+
path: `/api/v1/trips/${tripRef}/rooming/stay-windows`,
|
|
1763
|
+
operationId: "createRoomingStayWindow",
|
|
1764
|
+
body: input
|
|
1765
|
+
}),
|
|
1766
|
+
direct: (liveClient, input) => liveClient.rooming.stayWindows.create({ tripRef, ...input })
|
|
1767
|
+
});
|
|
1768
|
+
const patchWindowMutation = useOfflineMutation({
|
|
1769
|
+
tripRef,
|
|
1770
|
+
enqueue: (engine, input) => {
|
|
1771
|
+
const { windowId, version, ...body } = input;
|
|
1772
|
+
return engine.enqueue({
|
|
1773
|
+
tripRef,
|
|
1774
|
+
method: SyncOpMethod.Patch,
|
|
1775
|
+
path: `/api/v1/trips/${tripRef}/rooming/stay-windows/${windowId}`,
|
|
1776
|
+
operationId: "patchRoomingStayWindow",
|
|
1777
|
+
body,
|
|
1778
|
+
ifMatch: version
|
|
1779
|
+
});
|
|
1780
|
+
},
|
|
1781
|
+
direct: (liveClient, input) => liveClient.rooming.stayWindows.patch({ tripRef, ...input })
|
|
1782
|
+
});
|
|
1783
|
+
const removeWindowMutation = useOfflineMutation({
|
|
1784
|
+
tripRef,
|
|
1785
|
+
enqueue: (engine, input) => {
|
|
1786
|
+
const { windowId, version, force } = input;
|
|
1787
|
+
const query = force === true ? "?force=true" : "";
|
|
1788
|
+
return engine.enqueue({
|
|
1789
|
+
tripRef,
|
|
1790
|
+
method: SyncOpMethod.Delete,
|
|
1791
|
+
path: `/api/v1/trips/${tripRef}/rooming/stay-windows/${windowId}${query}`,
|
|
1792
|
+
operationId: "deleteRoomingStayWindow",
|
|
1793
|
+
ifMatch: version
|
|
1794
|
+
});
|
|
1795
|
+
},
|
|
1796
|
+
direct: (liveClient, input) => liveClient.rooming.stayWindows.remove({ tripRef, ...input })
|
|
1797
|
+
});
|
|
1798
|
+
const createRoomMutation = useOfflineMutation({
|
|
1799
|
+
tripRef,
|
|
1800
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1801
|
+
tripRef,
|
|
1802
|
+
method: SyncOpMethod.Post,
|
|
1803
|
+
path: `/api/v1/trips/${tripRef}/rooming/rooms`,
|
|
1804
|
+
operationId: "createRoomingRoom",
|
|
1805
|
+
body: input
|
|
1806
|
+
}),
|
|
1807
|
+
direct: (liveClient, input) => liveClient.rooming.rooms.create({ tripRef, ...input })
|
|
1808
|
+
});
|
|
1809
|
+
const patchRoomMutation = useOfflineMutation({
|
|
1810
|
+
tripRef,
|
|
1811
|
+
enqueue: (engine, input) => {
|
|
1812
|
+
const { roomId, version, ...body } = input;
|
|
1813
|
+
return engine.enqueue({
|
|
1814
|
+
tripRef,
|
|
1815
|
+
method: SyncOpMethod.Patch,
|
|
1816
|
+
path: `/api/v1/trips/${tripRef}/rooming/rooms/${roomId}`,
|
|
1817
|
+
operationId: "patchRoomingRoom",
|
|
1818
|
+
body,
|
|
1819
|
+
ifMatch: version
|
|
1820
|
+
});
|
|
1821
|
+
},
|
|
1822
|
+
direct: (liveClient, input) => liveClient.rooming.rooms.patch({ tripRef, ...input })
|
|
1823
|
+
});
|
|
1824
|
+
const removeRoomMutation = useOfflineMutation({
|
|
1825
|
+
tripRef,
|
|
1826
|
+
enqueue: (engine, input) => {
|
|
1827
|
+
const { roomId, version, force } = input;
|
|
1828
|
+
const query = force === true ? "?force=true" : "";
|
|
1829
|
+
return engine.enqueue({
|
|
1830
|
+
tripRef,
|
|
1831
|
+
method: SyncOpMethod.Delete,
|
|
1832
|
+
path: `/api/v1/trips/${tripRef}/rooming/rooms/${roomId}${query}`,
|
|
1833
|
+
operationId: "deleteRoomingRoom",
|
|
1834
|
+
ifMatch: version
|
|
1835
|
+
});
|
|
1836
|
+
},
|
|
1837
|
+
direct: (liveClient, input) => liveClient.rooming.rooms.remove({ tripRef, ...input })
|
|
1838
|
+
});
|
|
1839
|
+
const syncedAt = roomsSyncedAt === void 0 ? windowsSyncedAt : windowsSyncedAt === void 0 ? roomsSyncedAt : roomsSyncedAt > windowsSyncedAt ? roomsSyncedAt : windowsSyncedAt;
|
|
1840
|
+
const autoAssign = useCallback(
|
|
1841
|
+
(input) => client.rooming.autoAssign({ tripRef, ...input }),
|
|
1842
|
+
[client, tripRef]
|
|
1843
|
+
);
|
|
1844
|
+
return {
|
|
1845
|
+
rooms,
|
|
1846
|
+
stayWindows,
|
|
1847
|
+
syncedAt,
|
|
1848
|
+
refreshLive,
|
|
1849
|
+
unassignedPool: {
|
|
1850
|
+
travellers: poolTravellers,
|
|
1851
|
+
loaded: poolLoaded,
|
|
1852
|
+
lastFetchFailed: poolFetchFailed,
|
|
1853
|
+
fetchedAt: poolFetchedAt,
|
|
1854
|
+
refresh: refreshUnassignedPool
|
|
1855
|
+
},
|
|
1856
|
+
assign,
|
|
1857
|
+
autoAssign,
|
|
1858
|
+
createStayWindow: createWindowMutation.mutate,
|
|
1859
|
+
patchStayWindow: patchWindowMutation.mutate,
|
|
1860
|
+
removeStayWindow: removeWindowMutation.mutate,
|
|
1861
|
+
createRoom: createRoomMutation.mutate,
|
|
1862
|
+
patchRoom: patchRoomMutation.mutate,
|
|
1863
|
+
removeRoom: removeRoomMutation.mutate
|
|
1864
|
+
};
|
|
1865
|
+
});
|
|
1866
|
+
function useRooming(tripRef) {
|
|
1867
|
+
return useRoomingInternal(tripRef);
|
|
1868
|
+
}
|
|
1869
|
+
var useSeatingInternal = createDomainHook("transport-seating", (tripRef) => {
|
|
1870
|
+
const client = useKaafilClient();
|
|
1871
|
+
const {
|
|
1872
|
+
rows: vehicles,
|
|
1873
|
+
syncedAt,
|
|
1874
|
+
refreshLive
|
|
1875
|
+
} = useSnapshotList(tripRef, "seating", {
|
|
1876
|
+
live: async (liveClient, ref) => {
|
|
1877
|
+
const response = await liveClient.seating.read({ tripRef: ref });
|
|
1878
|
+
return { rows: response.vehicles, syncedAt: response.meta.serverTime };
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
const assignMutation = useOfflineMutation({
|
|
1882
|
+
tripRef,
|
|
1883
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1884
|
+
tripRef,
|
|
1885
|
+
method: SyncOpMethod.Post,
|
|
1886
|
+
path: `/api/v1/trips/${tripRef}/seating/assign`,
|
|
1887
|
+
operationId: "assignSeating",
|
|
1888
|
+
body: input,
|
|
1889
|
+
// `SeatingAssignDto.vehicles` is "every vehicle touched by this
|
|
1890
|
+
// write, whole — a client rebases without a follow-up read" (the
|
|
1891
|
+
// engine DTO's own words) — up to two, on a displacement. `arrayKey`
|
|
1892
|
+
// is this hook's one multi-row rebase (`OutboxEntityRef.arrayKey`),
|
|
1893
|
+
// the reason the vehicle grid now repaints the instant an assign
|
|
1894
|
+
// lands rather than waiting for the next full pull.
|
|
1895
|
+
entity: { listName: "seating", arrayKey: "vehicles" }
|
|
1896
|
+
}),
|
|
1897
|
+
// The desk lane. Every `transport-seating` write takes the admin triple,
|
|
1898
|
+
// so unlike `usePickups` there is no member here that deliberately
|
|
1899
|
+
// lacks one.
|
|
1900
|
+
direct: (liveClient, input) => liveClient.seating.assign({ tripRef, ...input })
|
|
1901
|
+
});
|
|
1902
|
+
const [poolTravellers, setPoolTravellers] = useState([]);
|
|
1903
|
+
const [poolLoaded, setPoolLoaded] = useState(false);
|
|
1904
|
+
const [poolFetchFailed, setPoolFetchFailed] = useState(false);
|
|
1905
|
+
const [poolFetchedAt, setPoolFetchedAt] = useState(void 0);
|
|
1906
|
+
const refreshUnassignedPool = useCallback(async () => {
|
|
1907
|
+
try {
|
|
1908
|
+
const response = await client.seating.read({ tripRef });
|
|
1909
|
+
setPoolTravellers(response.unassignedPool);
|
|
1910
|
+
setPoolLoaded(true);
|
|
1911
|
+
setPoolFetchFailed(false);
|
|
1912
|
+
setPoolFetchedAt(response.meta.serverTime);
|
|
1913
|
+
} catch {
|
|
1914
|
+
setPoolFetchFailed(true);
|
|
1915
|
+
}
|
|
1916
|
+
}, [client, tripRef]);
|
|
1917
|
+
useEffect(() => {
|
|
1918
|
+
void refreshUnassignedPool();
|
|
1919
|
+
}, [refreshUnassignedPool]);
|
|
1920
|
+
const assignMutate = assignMutation.mutate;
|
|
1921
|
+
const assign = useCallback(
|
|
1922
|
+
async (input) => {
|
|
1923
|
+
const op = await assignMutate(input);
|
|
1924
|
+
void refreshUnassignedPool();
|
|
1925
|
+
return op;
|
|
1926
|
+
},
|
|
1927
|
+
[assignMutate, refreshUnassignedPool]
|
|
1928
|
+
);
|
|
1929
|
+
const createVehicleMutation = useOfflineMutation({
|
|
1930
|
+
tripRef,
|
|
1931
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
1932
|
+
tripRef,
|
|
1933
|
+
method: SyncOpMethod.Post,
|
|
1934
|
+
path: `/api/v1/trips/${tripRef}/seating/vehicles`,
|
|
1935
|
+
operationId: "createSeatingVehicle",
|
|
1936
|
+
body: input
|
|
1937
|
+
}),
|
|
1938
|
+
direct: (liveClient, input) => liveClient.seating.vehicles.create({ tripRef, ...input })
|
|
1939
|
+
});
|
|
1940
|
+
const patchVehicleMutation = useOfflineMutation({
|
|
1941
|
+
tripRef,
|
|
1942
|
+
enqueue: (engine, input) => {
|
|
1943
|
+
const { vehicleId, version, ...body } = input;
|
|
1944
|
+
return engine.enqueue({
|
|
1945
|
+
tripRef,
|
|
1946
|
+
method: SyncOpMethod.Patch,
|
|
1947
|
+
path: `/api/v1/trips/${tripRef}/seating/vehicles/${vehicleId}`,
|
|
1948
|
+
operationId: "patchSeatingVehicle",
|
|
1949
|
+
body,
|
|
1950
|
+
ifMatch: version
|
|
1951
|
+
});
|
|
1952
|
+
},
|
|
1953
|
+
direct: (liveClient, input) => liveClient.seating.vehicles.patch({ tripRef, ...input })
|
|
1954
|
+
});
|
|
1955
|
+
const removeVehicleMutation = useOfflineMutation({
|
|
1956
|
+
tripRef,
|
|
1957
|
+
enqueue: (engine, input) => {
|
|
1958
|
+
const { vehicleId, version } = input;
|
|
1959
|
+
return engine.enqueue({
|
|
1960
|
+
tripRef,
|
|
1961
|
+
method: SyncOpMethod.Delete,
|
|
1962
|
+
path: `/api/v1/trips/${tripRef}/seating/vehicles/${vehicleId}`,
|
|
1963
|
+
operationId: "deleteSeatingVehicle",
|
|
1964
|
+
ifMatch: version
|
|
1965
|
+
});
|
|
1966
|
+
},
|
|
1967
|
+
direct: (liveClient, input) => liveClient.seating.vehicles.remove({ tripRef, ...input })
|
|
1968
|
+
});
|
|
1969
|
+
const removeVehicleMutate = removeVehicleMutation.mutate;
|
|
1970
|
+
const removeVehicle = useCallback(
|
|
1971
|
+
async (input) => {
|
|
1972
|
+
const op = await removeVehicleMutate(input);
|
|
1973
|
+
void refreshUnassignedPool();
|
|
1974
|
+
return op;
|
|
1975
|
+
},
|
|
1976
|
+
[removeVehicleMutate, refreshUnassignedPool]
|
|
1977
|
+
);
|
|
1978
|
+
const patchVehicleMutate = patchVehicleMutation.mutate;
|
|
1979
|
+
const patchVehicleDays = useCallback(
|
|
1980
|
+
async (vehicleRef, days) => {
|
|
1981
|
+
const current = vehicles.find(
|
|
1982
|
+
(row) => row.id === vehicleRef && !("_tombstone" in row)
|
|
1983
|
+
);
|
|
1984
|
+
if (current === void 0) {
|
|
1985
|
+
throw new Error(
|
|
1986
|
+
`patchVehicleDays: vehicle "${vehicleRef}" is not in the current seating cache \u2014 read the board before patching its day coverage.`
|
|
1987
|
+
);
|
|
1988
|
+
}
|
|
1989
|
+
return patchVehicleMutate({
|
|
1990
|
+
vehicleId: vehicleRef,
|
|
1991
|
+
version: current.version,
|
|
1992
|
+
days: [...days]
|
|
1993
|
+
});
|
|
1994
|
+
},
|
|
1995
|
+
[patchVehicleMutate, vehicles]
|
|
1996
|
+
);
|
|
1997
|
+
const linkManagerMutation = useOfflineMutation({
|
|
1998
|
+
tripRef,
|
|
1999
|
+
enqueue: (engine, input) => {
|
|
2000
|
+
const { vehicleId, ...body } = input;
|
|
2001
|
+
return engine.enqueue({
|
|
2002
|
+
tripRef,
|
|
2003
|
+
method: SyncOpMethod.Post,
|
|
2004
|
+
path: `/api/v1/trips/${tripRef}/seating/vehicles/${vehicleId}/manager`,
|
|
2005
|
+
operationId: "linkSeatingManager",
|
|
2006
|
+
body
|
|
2007
|
+
});
|
|
2008
|
+
},
|
|
2009
|
+
direct: (liveClient, input) => liveClient.seating.vehicles.manager.link({ tripRef, ...input })
|
|
2010
|
+
});
|
|
2011
|
+
const unlinkManagerMutation = useOfflineMutation({
|
|
2012
|
+
tripRef,
|
|
2013
|
+
enqueue: (engine, input) => {
|
|
2014
|
+
const { vehicleId, version } = input;
|
|
2015
|
+
return engine.enqueue({
|
|
2016
|
+
tripRef,
|
|
2017
|
+
method: SyncOpMethod.Delete,
|
|
2018
|
+
path: `/api/v1/trips/${tripRef}/seating/vehicles/${vehicleId}/manager`,
|
|
2019
|
+
operationId: "unlinkSeatingManager",
|
|
2020
|
+
ifMatch: version
|
|
2021
|
+
});
|
|
2022
|
+
},
|
|
2023
|
+
direct: (liveClient, input) => liveClient.seating.vehicles.manager.unlink({ tripRef, ...input })
|
|
2024
|
+
});
|
|
2025
|
+
const autoAssign = useCallback(
|
|
2026
|
+
(input) => client.seating.autoAssign({ tripRef, ...input }),
|
|
2027
|
+
[client, tripRef]
|
|
2028
|
+
);
|
|
2029
|
+
return {
|
|
2030
|
+
vehicles,
|
|
2031
|
+
syncedAt,
|
|
2032
|
+
refreshLive,
|
|
2033
|
+
unassignedPool: {
|
|
2034
|
+
travellers: poolTravellers,
|
|
2035
|
+
loaded: poolLoaded,
|
|
2036
|
+
lastFetchFailed: poolFetchFailed,
|
|
2037
|
+
fetchedAt: poolFetchedAt,
|
|
2038
|
+
refresh: refreshUnassignedPool
|
|
2039
|
+
},
|
|
2040
|
+
assign,
|
|
2041
|
+
autoAssign,
|
|
2042
|
+
createVehicle: createVehicleMutation.mutate,
|
|
2043
|
+
patchVehicle: patchVehicleMutation.mutate,
|
|
2044
|
+
removeVehicle,
|
|
2045
|
+
patchVehicleDays,
|
|
2046
|
+
linkManager: linkManagerMutation.mutate,
|
|
2047
|
+
unlinkManager: unlinkManagerMutation.mutate
|
|
2048
|
+
};
|
|
2049
|
+
});
|
|
2050
|
+
function useSeating(tripRef) {
|
|
2051
|
+
return useSeatingInternal(tripRef);
|
|
2052
|
+
}
|
|
2053
|
+
function useTravellerProfile(tripRef, travellerRef) {
|
|
2054
|
+
const client = useKaafilClient();
|
|
2055
|
+
const [read, setRead] = useState(void 0);
|
|
2056
|
+
const [readFailed, setReadFailed] = useState(false);
|
|
2057
|
+
const refresh = useCallback(async () => {
|
|
2058
|
+
try {
|
|
2059
|
+
const response = await client.travellers.getDetail({ tripRef, travellerRef });
|
|
2060
|
+
setRead(response);
|
|
2061
|
+
setReadFailed(false);
|
|
2062
|
+
} catch {
|
|
2063
|
+
setReadFailed(true);
|
|
2064
|
+
}
|
|
2065
|
+
}, [client, tripRef, travellerRef]);
|
|
2066
|
+
useEffect(() => {
|
|
2067
|
+
void refresh();
|
|
2068
|
+
}, [refresh]);
|
|
2069
|
+
useOfflineEvent("sync.drained", refresh);
|
|
2070
|
+
const noteMutation = useOfflineMutation({
|
|
2071
|
+
tripRef,
|
|
2072
|
+
enqueue: (engine, input) => engine.enqueue({
|
|
2073
|
+
tripRef,
|
|
2074
|
+
method: SyncOpMethod.Post,
|
|
2075
|
+
path: `/api/v1/trips/${tripRef}/travellers/${travellerRef}/notes`,
|
|
2076
|
+
operationId: "addTravellerNote",
|
|
2077
|
+
body: input
|
|
2078
|
+
})
|
|
2079
|
+
});
|
|
2080
|
+
const noteMutate = noteMutation.mutate;
|
|
2081
|
+
const addNote = useCallback(
|
|
2082
|
+
async (input) => {
|
|
2083
|
+
const op = await noteMutate(input);
|
|
2084
|
+
void refresh();
|
|
2085
|
+
return op;
|
|
2086
|
+
},
|
|
2087
|
+
[noteMutate, refresh]
|
|
2088
|
+
);
|
|
2089
|
+
return { read, readFailed, refresh, addNote };
|
|
2090
|
+
}
|
|
2091
|
+
function useVendors(tripRef, agencyRef) {
|
|
2092
|
+
const client = useKaafilClient();
|
|
2093
|
+
const { rows, loaded: capabilitiesLoaded } = useCapabilityRows(tripRef);
|
|
2094
|
+
const [vendors, setVendors] = useState([]);
|
|
2095
|
+
const [listLoaded, setListLoaded] = useState(false);
|
|
2096
|
+
const [lastFetchFailed, setLastFetchFailed] = useState(false);
|
|
2097
|
+
const [syncedAt, setSyncedAt] = useState(void 0);
|
|
2098
|
+
const [firstLoadError, setFirstLoadError] = useState(void 0);
|
|
2099
|
+
const refresh = useCallback(async () => {
|
|
2100
|
+
try {
|
|
2101
|
+
const response = await client.vendors.list({ tripRef });
|
|
2102
|
+
setVendors(response);
|
|
2103
|
+
setListLoaded(true);
|
|
2104
|
+
setLastFetchFailed(false);
|
|
2105
|
+
setSyncedAt(response.meta.serverTime);
|
|
2106
|
+
setFirstLoadError(void 0);
|
|
2107
|
+
} catch (error) {
|
|
2108
|
+
setLastFetchFailed(true);
|
|
2109
|
+
setFirstLoadError((previous) => previous ?? classifyError(error));
|
|
2110
|
+
}
|
|
2111
|
+
}, [client, tripRef]);
|
|
2112
|
+
useEffect(() => {
|
|
2113
|
+
void refresh();
|
|
2114
|
+
}, [refresh]);
|
|
2115
|
+
const create = useCallback((input) => client.vendors.create(input), [client]);
|
|
2116
|
+
const upsert = useCallback((input) => client.vendors.upsert(input), [client]);
|
|
2117
|
+
const remove = useCallback((input) => client.vendors.remove(input), [client]);
|
|
2118
|
+
if (!capabilitiesLoaded) {
|
|
2119
|
+
return { status: "loading" };
|
|
2120
|
+
}
|
|
2121
|
+
const row = rows.find((candidate) => candidate.capability === "vendors");
|
|
2122
|
+
const triple = row === void 0 ? UNKNOWN_CAPABILITY_TRIPLE : deriveCapabilityTriple(row);
|
|
2123
|
+
if (triple.status !== "lit") {
|
|
2124
|
+
const reason = darkReasonFor(triple);
|
|
2125
|
+
return { status: "dark", reason };
|
|
2126
|
+
}
|
|
2127
|
+
if (!listLoaded && lastFetchFailed) {
|
|
2128
|
+
return {
|
|
2129
|
+
status: "error",
|
|
2130
|
+
error: firstLoadError ?? {
|
|
2131
|
+
path: "surface-to-user",
|
|
2132
|
+
code: void 0,
|
|
2133
|
+
status: void 0,
|
|
2134
|
+
unsatisfiableScheme: false,
|
|
2135
|
+
cause: void 0
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
return {
|
|
2140
|
+
status: "ready",
|
|
2141
|
+
vendors,
|
|
2142
|
+
loaded: listLoaded,
|
|
2143
|
+
lastFetchFailed,
|
|
2144
|
+
syncedAt,
|
|
2145
|
+
refresh,
|
|
2146
|
+
create,
|
|
2147
|
+
upsert,
|
|
2148
|
+
remove
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
export { ERROR_HANDLING_PATH, FORM_FIELD_KIND_SINGLE_BOOLEAN, classifyError, createDomainHook, useChecklists, useCloseout, useCollections, useExpenses, useFiles, useFloat, useForms, useItinerary, useManagerToday, useManagers, useParties, usePickups, useRooming, useSeating, useSnapshotList, useTravellerProfile, useVendors };
|
|
2153
|
+
//# sourceMappingURL=chunk-UYJ6LIWP.js.map
|
|
2154
|
+
//# sourceMappingURL=chunk-UYJ6LIWP.js.map
|