kaafil-react-uikit 0.4.1 → 0.5.0
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 +44 -5
- package/dist/admin/index.cjs +355 -353
- package/dist/admin/index.cjs.map +1 -1
- package/dist/admin/index.js +16 -14
- package/dist/admin/index.js.map +1 -1
- package/dist/{chunk-EP4VCID7.js → chunk-3E2FN5JJ.js} +3 -3
- package/dist/{chunk-EP4VCID7.js.map → chunk-3E2FN5JJ.js.map} +1 -1
- package/dist/{chunk-TPBKRXEB.cjs → chunk-3RZDHDEY.cjs} +17 -17
- package/dist/{chunk-TPBKRXEB.cjs.map → chunk-3RZDHDEY.cjs.map} +1 -1
- package/dist/chunk-62HCQWZL.cjs +12 -0
- package/dist/chunk-62HCQWZL.cjs.map +1 -0
- package/dist/{chunk-ABWNV4N4.js → chunk-6FUVFVYY.js} +97 -4
- package/dist/chunk-6FUVFVYY.js.map +1 -0
- package/dist/{chunk-BUE2GJDN.js → chunk-762SWUEG.js} +3 -3
- package/dist/{chunk-BUE2GJDN.js.map → chunk-762SWUEG.js.map} +1 -1
- package/dist/{chunk-CCJ5XNWY.cjs → chunk-CK3L6Y43.cjs} +98 -2
- package/dist/chunk-CK3L6Y43.cjs.map +1 -0
- package/dist/{chunk-XBYLSJOS.cjs → chunk-DI77H7ED.cjs} +83 -83
- package/dist/{chunk-XBYLSJOS.cjs.map → chunk-DI77H7ED.cjs.map} +1 -1
- package/dist/{chunk-5DVTAEQC.js → chunk-EDLIJSMR.js} +5 -5
- package/dist/{chunk-5DVTAEQC.js.map → chunk-EDLIJSMR.js.map} +1 -1
- package/dist/{chunk-VAJ4D7EF.js → chunk-FEJL6GKJ.js} +513 -20
- package/dist/chunk-FEJL6GKJ.js.map +1 -0
- package/dist/{chunk-2D4Y4LOT.cjs → chunk-IBVCH6GC.cjs} +553 -51
- package/dist/chunk-IBVCH6GC.cjs.map +1 -0
- package/dist/{chunk-DABF4PUI.js → chunk-ITGVRN2S.js} +3 -3
- package/dist/{chunk-DABF4PUI.js.map → chunk-ITGVRN2S.js.map} +1 -1
- package/dist/{chunk-DUZG6A22.cjs → chunk-LO3GQFQ6.cjs} +4 -4
- package/dist/{chunk-DUZG6A22.cjs.map → chunk-LO3GQFQ6.cjs.map} +1 -1
- package/dist/{chunk-4LZXDRAD.cjs → chunk-QD4Z5DRT.cjs} +12 -12
- package/dist/{chunk-4LZXDRAD.cjs.map → chunk-QD4Z5DRT.cjs.map} +1 -1
- package/dist/{chunk-TV5QTT44.js → chunk-RW6TJMRK.js} +3 -3
- package/dist/{chunk-TV5QTT44.js.map → chunk-RW6TJMRK.js.map} +1 -1
- package/dist/chunk-XU7GP53M.js +10 -0
- package/dist/chunk-XU7GP53M.js.map +1 -0
- package/dist/{chunk-DDSNXKFN.cjs → chunk-XYIOM75D.cjs} +9 -9
- package/dist/{chunk-DDSNXKFN.cjs.map → chunk-XYIOM75D.cjs.map} +1 -1
- package/dist/core/index.cjs +153 -613
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +36 -1
- package/dist/core/index.d.ts +36 -1
- package/dist/core/index.js +21 -511
- package/dist/core/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/manager/index.cjs +239 -239
- package/dist/manager/index.js +6 -6
- package/dist/traveller/index.cjs +35 -35
- package/dist/traveller/index.js +5 -5
- package/package.json +2 -2
- package/dist/chunk-2D4Y4LOT.cjs.map +0 -1
- package/dist/chunk-2JABR4FF.js +0 -105
- package/dist/chunk-2JABR4FF.js.map +0 -1
- package/dist/chunk-ABWNV4N4.js.map +0 -1
- package/dist/chunk-CCJ5XNWY.cjs.map +0 -1
- package/dist/chunk-KDGBJPPZ.cjs +0 -110
- package/dist/chunk-KDGBJPPZ.cjs.map +0 -1
- package/dist/chunk-VAJ4D7EF.js.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { createDomainHook, useSnapshotList, classifyError } from './chunk-
|
|
2
|
-
import { useServerTime } from './chunk-
|
|
3
|
-
import { useOfflineMutation, useKaafilClient, useCapabilityRows, deriveCapabilityTriple,
|
|
4
|
-
import { useCallback, useState, useEffect, useRef, useMemo } from 'react';
|
|
5
|
-
import { SyncOpMethod } from 'kaafil-js/client';
|
|
1
|
+
import { createDomainHook, useSnapshotList, classifyError } from './chunk-3E2FN5JJ.js';
|
|
2
|
+
import { useServerTime } from './chunk-ITGVRN2S.js';
|
|
3
|
+
import { useOfflineMutation, useKaafilClient, useCapabilityRows, deriveCapabilityTriple, tokenStyle, ConfigContext, KaafilClientContext, usePersona, useSession, OfflineEngineContext, SessionContext, BrandShell, I18nShell, isDevelopment } from './chunk-6FUVFVYY.js';
|
|
4
|
+
import { useCallback, createContext, useState, useEffect, useRef, useMemo, useContext, Component, Fragment } from 'react';
|
|
5
|
+
import { SyncOpMethod, KaafilClient } from 'kaafil-js/client';
|
|
6
|
+
import { createPortal } from 'react-dom';
|
|
7
|
+
import { jsx, Fragment as Fragment$1, jsxs } from 'react/jsx-runtime';
|
|
6
8
|
|
|
7
9
|
function useAgencyAdminMe() {
|
|
8
10
|
const client = useKaafilClient();
|
|
@@ -290,7 +292,7 @@ function useAgencyManagerProfile(managerRef) {
|
|
|
290
292
|
}
|
|
291
293
|
function useAgencySettings() {
|
|
292
294
|
const client = useKaafilClient();
|
|
293
|
-
const [
|
|
295
|
+
const [document2, setDocument] = useState(void 0);
|
|
294
296
|
const [loaded, setLoaded] = useState(false);
|
|
295
297
|
const [lastFetchFailed, setLastFetchFailed] = useState(false);
|
|
296
298
|
const refresh = useCallback(async () => {
|
|
@@ -314,7 +316,7 @@ function useAgencySettings() {
|
|
|
314
316
|
},
|
|
315
317
|
[client, refresh]
|
|
316
318
|
);
|
|
317
|
-
return { document, loaded, lastFetchFailed, refresh, patchSettings };
|
|
319
|
+
return { document: document2, loaded, lastFetchFailed, refresh, patchSettings };
|
|
318
320
|
}
|
|
319
321
|
function useAgencyTrip(tripRef) {
|
|
320
322
|
const client = useKaafilClient();
|
|
@@ -468,7 +470,7 @@ var DirectoriesWriteError = class _DirectoriesWriteError extends Error {
|
|
|
468
470
|
};
|
|
469
471
|
function useDirectories(agencyRef) {
|
|
470
472
|
const client = useKaafilClient();
|
|
471
|
-
|
|
473
|
+
useServerTime();
|
|
472
474
|
const makeManagersFetch = useCallback(
|
|
473
475
|
(filters) => async (cursor) => {
|
|
474
476
|
const page = await client.agencies.managers.listPage({
|
|
@@ -557,8 +559,7 @@ function useDirectories(agencyRef) {
|
|
|
557
559
|
{
|
|
558
560
|
externalTravellerId: input.externalTravellerId,
|
|
559
561
|
fullName: input.fullName,
|
|
560
|
-
phone: input.phone
|
|
561
|
-
sourceUpdatedAt: serverTime.now()
|
|
562
|
+
phone: input.phone
|
|
562
563
|
}
|
|
563
564
|
]
|
|
564
565
|
});
|
|
@@ -582,7 +583,7 @@ function useDirectories(agencyRef) {
|
|
|
582
583
|
throw new DirectoriesWriteError(classifyError(error));
|
|
583
584
|
}
|
|
584
585
|
},
|
|
585
|
-
[client
|
|
586
|
+
[client]
|
|
586
587
|
);
|
|
587
588
|
const createAgencyTraveller = useCallback(
|
|
588
589
|
async (input) => {
|
|
@@ -742,12 +743,6 @@ function usePaginatedList(fetch, options) {
|
|
|
742
743
|
loadPrev
|
|
743
744
|
};
|
|
744
745
|
}
|
|
745
|
-
|
|
746
|
-
// src/core/hooks/useResolvedSetting.ts
|
|
747
|
-
function useResolvedSetting(key) {
|
|
748
|
-
const config = useRequiredContext(ConfigContext, "useResolvedSetting", "ConfigShell");
|
|
749
|
-
return config.resolveSetting(key);
|
|
750
|
-
}
|
|
751
746
|
var SHARE_SECTION_KEYS = [
|
|
752
747
|
"itinerary",
|
|
753
748
|
"rooming",
|
|
@@ -889,6 +884,504 @@ function useTravellerFormResponses(tripRef, travellerRef) {
|
|
|
889
884
|
}, [refresh]);
|
|
890
885
|
return { responses, readFailed, refresh };
|
|
891
886
|
}
|
|
887
|
+
var ThemeAttributesContext = createContext(null);
|
|
888
|
+
var FAMILY_DEFAULT_FRAME = {
|
|
889
|
+
admin: "desk",
|
|
890
|
+
manager: "field",
|
|
891
|
+
traveller: "document"
|
|
892
|
+
};
|
|
893
|
+
var DESK_MINIMUM_CONTAINER_WIDTH_PX = 640;
|
|
894
|
+
function ShadowIsolationBoundary({
|
|
895
|
+
className,
|
|
896
|
+
children
|
|
897
|
+
}) {
|
|
898
|
+
const hostRef = useRef(null);
|
|
899
|
+
const [shadowContainer, setShadowContainer] = useState(null);
|
|
900
|
+
useEffect(() => {
|
|
901
|
+
const host = hostRef.current;
|
|
902
|
+
if (host === null) {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
const shadow = host.shadowRoot ?? host.attachShadow({ mode: "open" });
|
|
906
|
+
let container = shadow.querySelector("div[data-kf-shadow-content]");
|
|
907
|
+
if (container === null) {
|
|
908
|
+
container = document.createElement("div");
|
|
909
|
+
container.setAttribute("data-kf-shadow-content", "");
|
|
910
|
+
shadow.appendChild(container);
|
|
911
|
+
}
|
|
912
|
+
setShadowContainer(container);
|
|
913
|
+
}, []);
|
|
914
|
+
return /* @__PURE__ */ jsx("div", { ref: hostRef, className, "data-kf-isolation": "shadow", children: shadowContainer !== null ? createPortal(children, shadowContainer) : null });
|
|
915
|
+
}
|
|
916
|
+
function useDeskMinimumWidthWarning(rootRef, frame) {
|
|
917
|
+
useEffect(() => {
|
|
918
|
+
if (frame !== "desk" || !isDevelopment() || typeof ResizeObserver === "undefined") {
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
const node = rootRef.current;
|
|
922
|
+
if (node === null) {
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
let warned = false;
|
|
926
|
+
const observer = new ResizeObserver((entries) => {
|
|
927
|
+
const entry = entries[0];
|
|
928
|
+
if (entry === void 0 || warned) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (entry.contentRect.width < DESK_MINIMUM_CONTAINER_WIDTH_PX) {
|
|
932
|
+
warned = true;
|
|
933
|
+
console.warn(
|
|
934
|
+
`kaafil-react-uikit: the 'desk' frame's .kf-root is narrower than the stated minimum container width (05-theming-and-branding.md \xA74.4). AppShell never auto-swaps frames \u2014 mount the manager family, or pass frame="field" explicitly, for this container.`
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
observer.observe(node);
|
|
939
|
+
return () => observer.disconnect();
|
|
940
|
+
}, [rootRef, frame]);
|
|
941
|
+
}
|
|
942
|
+
function useUnfilledConnectivitySlotWarning(frame, connectivitySlot) {
|
|
943
|
+
useEffect(() => {
|
|
944
|
+
if (frame === "field" && connectivitySlot === void 0 && isDevelopment()) {
|
|
945
|
+
console.warn(
|
|
946
|
+
`kaafil-react-uikit: AppShell's "field" frame has no connectivitySlot filled \u2014 the permanently-visible connectivity/sync region this frame reserves is empty (01-app-shell.md, briefing \xA727.2).`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
}, [frame, connectivitySlot]);
|
|
950
|
+
}
|
|
951
|
+
function AppShell({
|
|
952
|
+
frame,
|
|
953
|
+
family,
|
|
954
|
+
isolation = "scoped",
|
|
955
|
+
navSlot,
|
|
956
|
+
actionBarSlot,
|
|
957
|
+
connectivitySlot,
|
|
958
|
+
children,
|
|
959
|
+
style
|
|
960
|
+
}) {
|
|
961
|
+
const themeAttributes = useContext(ThemeAttributesContext);
|
|
962
|
+
const rootRef = useRef(null);
|
|
963
|
+
const resolvedFrame = frame ?? (family !== void 0 ? FAMILY_DEFAULT_FRAME[family] : "document");
|
|
964
|
+
useDeskMinimumWidthWarning(rootRef, resolvedFrame);
|
|
965
|
+
useUnfilledConnectivitySlotWarning(resolvedFrame, connectivitySlot);
|
|
966
|
+
const dataAttributes = {
|
|
967
|
+
"data-kf-frame": resolvedFrame
|
|
968
|
+
};
|
|
969
|
+
if (family !== void 0) {
|
|
970
|
+
dataAttributes["data-kf-family"] = family;
|
|
971
|
+
}
|
|
972
|
+
if (themeAttributes !== null) {
|
|
973
|
+
dataAttributes["data-kf-theme"] = themeAttributes.theme;
|
|
974
|
+
if (themeAttributes.density !== void 0) {
|
|
975
|
+
dataAttributes["data-kf-density"] = themeAttributes.density;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
979
|
+
resolvedFrame === "desk" && navSlot !== void 0 ? /* @__PURE__ */ jsx("div", { className: "kf-app-shell__nav", children: navSlot }) : null,
|
|
980
|
+
/* @__PURE__ */ jsx("div", { className: "kf-app-shell__content", children }),
|
|
981
|
+
resolvedFrame === "field" && actionBarSlot !== void 0 ? /* @__PURE__ */ jsx("div", { className: "kf-app-shell__action-bar", children: actionBarSlot }) : null,
|
|
982
|
+
resolvedFrame === "field" && connectivitySlot !== void 0 ? /* @__PURE__ */ jsx("div", { className: "kf-app-shell__connectivity", children: connectivitySlot }) : null,
|
|
983
|
+
/* @__PURE__ */ jsx("div", { className: "kf-app-shell__live-region", role: "status", "aria-live": "polite" })
|
|
984
|
+
] });
|
|
985
|
+
if (isolation === "shadow") {
|
|
986
|
+
return /* @__PURE__ */ jsx(
|
|
987
|
+
"div",
|
|
988
|
+
{
|
|
989
|
+
ref: rootRef,
|
|
990
|
+
className: "kf-root",
|
|
991
|
+
"data-kf-isolation": "shadow",
|
|
992
|
+
...dataAttributes,
|
|
993
|
+
style: tokenStyle(style),
|
|
994
|
+
children: /* @__PURE__ */ jsx(ShadowIsolationBoundary, { className: "kf-app-shell__shadow-host", children: content })
|
|
995
|
+
}
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
return /* @__PURE__ */ jsx(
|
|
999
|
+
"div",
|
|
1000
|
+
{
|
|
1001
|
+
ref: rootRef,
|
|
1002
|
+
className: "kf-root",
|
|
1003
|
+
"data-kf-isolation": "scoped",
|
|
1004
|
+
...dataAttributes,
|
|
1005
|
+
style: tokenStyle(style),
|
|
1006
|
+
children: content
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
function ConfigShell({ seedConfig = {}, children }) {
|
|
1011
|
+
const value = useMemo(
|
|
1012
|
+
() => ({
|
|
1013
|
+
resolveSetting(key) {
|
|
1014
|
+
const agencyValue = seedConfig.agency?.[key];
|
|
1015
|
+
if (agencyValue !== void 0) {
|
|
1016
|
+
return agencyValue;
|
|
1017
|
+
}
|
|
1018
|
+
const tenantValue = seedConfig.tenant?.[key];
|
|
1019
|
+
return tenantValue === void 0 ? void 0 : tenantValue;
|
|
1020
|
+
}
|
|
1021
|
+
}),
|
|
1022
|
+
[seedConfig.agency, seedConfig.tenant]
|
|
1023
|
+
);
|
|
1024
|
+
return /* @__PURE__ */ jsx(ConfigContext.Provider, { value, children });
|
|
1025
|
+
}
|
|
1026
|
+
function classifyForFallback(error) {
|
|
1027
|
+
const classification = classifyError(error);
|
|
1028
|
+
if (classification.code === "RESOURCE_NOT_FOUND") {
|
|
1029
|
+
return { classification: "isolation", code: classification.code };
|
|
1030
|
+
}
|
|
1031
|
+
if (classification.path === "parked" || classification.unsatisfiableScheme) {
|
|
1032
|
+
return { classification: "parked", code: classification.code };
|
|
1033
|
+
}
|
|
1034
|
+
return { classification: "retryable", code: classification.code };
|
|
1035
|
+
}
|
|
1036
|
+
var FALLBACK_STYLE = {
|
|
1037
|
+
fontFamily: "system-ui, sans-serif",
|
|
1038
|
+
padding: "1.5rem",
|
|
1039
|
+
border: "1px solid #999",
|
|
1040
|
+
borderRadius: "4px",
|
|
1041
|
+
color: "#1a1a1a",
|
|
1042
|
+
background: "#fff"
|
|
1043
|
+
};
|
|
1044
|
+
function DefaultFallback({
|
|
1045
|
+
ctx,
|
|
1046
|
+
onRetry
|
|
1047
|
+
}) {
|
|
1048
|
+
const role = ctx.classification === "retryable" ? "status" : "alert";
|
|
1049
|
+
const ariaLive = ctx.classification === "retryable" ? "polite" : "assertive";
|
|
1050
|
+
const message = ctx.classification === "isolation" ? "Not found." : ctx.classification === "parked" ? "This action can't be completed right now." : "Something went wrong.";
|
|
1051
|
+
return /* @__PURE__ */ jsxs("div", { style: FALLBACK_STYLE, role, "aria-live": ariaLive, children: [
|
|
1052
|
+
/* @__PURE__ */ jsx("p", { style: { margin: 0 }, children: message }),
|
|
1053
|
+
ctx.classification === "retryable" ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onRetry, style: { marginTop: "0.75rem" }, children: "Try again" }) : null
|
|
1054
|
+
] });
|
|
1055
|
+
}
|
|
1056
|
+
var ErrorShell = class extends Component {
|
|
1057
|
+
state = { caught: null, retryKey: 0 };
|
|
1058
|
+
static getDerivedStateFromError(error) {
|
|
1059
|
+
return { caught: { error } };
|
|
1060
|
+
}
|
|
1061
|
+
handleRetry = () => {
|
|
1062
|
+
this.setState((current) => ({ caught: null, retryKey: current.retryKey + 1 }));
|
|
1063
|
+
};
|
|
1064
|
+
render() {
|
|
1065
|
+
if (this.state.caught !== null) {
|
|
1066
|
+
const ctx = classifyForFallback(this.state.caught.error);
|
|
1067
|
+
if (this.props.fallbackSlot !== void 0) {
|
|
1068
|
+
return this.props.fallbackSlot(ctx);
|
|
1069
|
+
}
|
|
1070
|
+
return /* @__PURE__ */ jsx(DefaultFallback, { ctx, onRetry: this.handleRetry });
|
|
1071
|
+
}
|
|
1072
|
+
return /* @__PURE__ */ jsx(Fragment, { children: this.props.children }, this.state.retryKey);
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
var SessionExpiryBridgeContext = createContext(void 0);
|
|
1076
|
+
function OfflineShell({
|
|
1077
|
+
storage,
|
|
1078
|
+
blob,
|
|
1079
|
+
conflictResolver,
|
|
1080
|
+
readEntity,
|
|
1081
|
+
scope,
|
|
1082
|
+
children
|
|
1083
|
+
}) {
|
|
1084
|
+
const client = useContext(KaafilClientContext);
|
|
1085
|
+
const persona = usePersona();
|
|
1086
|
+
const session = useSession();
|
|
1087
|
+
const reportExpired = useContext(SessionExpiryBridgeContext);
|
|
1088
|
+
const [engine, setEngine] = useState(null);
|
|
1089
|
+
const engineRef = useRef(null);
|
|
1090
|
+
const derivedScope = scope ?? `${persona}:${session.agencyRef ?? "share"}`;
|
|
1091
|
+
useEffect(() => {
|
|
1092
|
+
if (session.status !== "authenticated" || client === null) {
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
const newEngine = client.openOffline({
|
|
1096
|
+
storage,
|
|
1097
|
+
scope: derivedScope,
|
|
1098
|
+
...readEntity !== void 0 ? { readEntity } : {},
|
|
1099
|
+
...conflictResolver !== void 0 ? { conflictResolver } : {},
|
|
1100
|
+
...blob !== void 0 ? { blob } : {}
|
|
1101
|
+
// GAP flagged in this phase's report: a share session's local
|
|
1102
|
+
// `expiresAt` enforcement (`OpenOfflineOptions.share`,
|
|
1103
|
+
// `07-offline-and-sync.md §8`) needs an `expiresAt` this shell has no
|
|
1104
|
+
// source for yet — it is not on `useSession()`'s derived shape and no
|
|
1105
|
+
// prop carries it. Omitted here rather than guessed; a share engine
|
|
1106
|
+
// opens without the local expiry/grace/purge enforcement until that
|
|
1107
|
+
// value has a wire source.
|
|
1108
|
+
});
|
|
1109
|
+
engineRef.current = newEngine;
|
|
1110
|
+
let opened = false;
|
|
1111
|
+
let disposed = false;
|
|
1112
|
+
void newEngine.open().then(() => {
|
|
1113
|
+
opened = true;
|
|
1114
|
+
if (disposed) {
|
|
1115
|
+
newEngine.close();
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (engineRef.current === newEngine) setEngine(newEngine);
|
|
1119
|
+
}).catch(() => {
|
|
1120
|
+
if (!disposed && engineRef.current === newEngine) setEngine(null);
|
|
1121
|
+
});
|
|
1122
|
+
const unsubscribeSessionExpired = persona !== "share" ? newEngine.events.on("session.expired", () => reportExpired?.("session")) : void 0;
|
|
1123
|
+
const unsubscribeShareExpired = persona === "share" ? newEngine.events.on("share.expired", () => reportExpired?.("share")) : void 0;
|
|
1124
|
+
const unsubscribeShareRevoked = persona === "share" ? newEngine.events.on("share.revoked", () => reportExpired?.("share")) : void 0;
|
|
1125
|
+
return () => {
|
|
1126
|
+
unsubscribeSessionExpired?.();
|
|
1127
|
+
unsubscribeShareExpired?.();
|
|
1128
|
+
unsubscribeShareRevoked?.();
|
|
1129
|
+
disposed = true;
|
|
1130
|
+
if (opened) newEngine.close();
|
|
1131
|
+
if (engineRef.current === newEngine) {
|
|
1132
|
+
engineRef.current = null;
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
}, [client, derivedScope, session.status, persona, reportExpired]);
|
|
1136
|
+
return /* @__PURE__ */ jsx(OfflineEngineContext.Provider, { value: engine, children });
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// src/core/shells/internal/jwtKind.ts
|
|
1140
|
+
function base64UrlDecode(segment) {
|
|
1141
|
+
const padded = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
1142
|
+
const withPadding = padded + "=".repeat((4 - padded.length % 4) % 4);
|
|
1143
|
+
return atob(withPadding);
|
|
1144
|
+
}
|
|
1145
|
+
function decodeManagerOrAgencyAdminKind(accessToken) {
|
|
1146
|
+
const segments = accessToken.split(".");
|
|
1147
|
+
const payloadSegment = segments[1];
|
|
1148
|
+
if (segments.length !== 3 || payloadSegment === void 0) {
|
|
1149
|
+
throw new Error(
|
|
1150
|
+
'SessionShell could not parse "accessToken" as a JWT \u2014 it does not have the three dot-separated segments every manager/agencyAdmin session token has.'
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
let claim;
|
|
1154
|
+
try {
|
|
1155
|
+
claim = JSON.parse(base64UrlDecode(payloadSegment));
|
|
1156
|
+
} catch {
|
|
1157
|
+
throw new Error(
|
|
1158
|
+
`SessionShell could not decode "accessToken"'s payload segment as JSON \u2014 the value handed to <KaafilUIKitProvider accessToken> is not a well-formed JWT.`
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
const kind = claim.kind;
|
|
1162
|
+
if (kind === "agencyAdmin") {
|
|
1163
|
+
return "agencyAdmin";
|
|
1164
|
+
}
|
|
1165
|
+
if (kind === "managerSession" || kind === "manager") {
|
|
1166
|
+
return "managerSession";
|
|
1167
|
+
}
|
|
1168
|
+
throw new Error(
|
|
1169
|
+
`SessionShell read an unrecognised "kind" claim ("${String(kind)}") off "accessToken" \u2014 expected the manager or agencyAdmin session JWT shape (00-decisions.md U-003).`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
async function resolveCredential(props) {
|
|
1173
|
+
if (props.credentialResolver !== void 0) {
|
|
1174
|
+
const resolved = await props.credentialResolver();
|
|
1175
|
+
if ("shareToken" in resolved) {
|
|
1176
|
+
return { kind: "share", shareToken: resolved.shareToken };
|
|
1177
|
+
}
|
|
1178
|
+
const kind2 = decodeManagerOrAgencyAdminKind(resolved.accessToken) === "agencyAdmin" ? "agencyAdmin" : "manager";
|
|
1179
|
+
return { kind: kind2, ...resolved };
|
|
1180
|
+
}
|
|
1181
|
+
if (props.shareToken !== void 0) {
|
|
1182
|
+
return { kind: "share", shareToken: props.shareToken };
|
|
1183
|
+
}
|
|
1184
|
+
const kind = decodeManagerOrAgencyAdminKind(props.accessToken) === "agencyAdmin" ? "agencyAdmin" : "manager";
|
|
1185
|
+
return {
|
|
1186
|
+
kind,
|
|
1187
|
+
accessToken: props.accessToken,
|
|
1188
|
+
refreshToken: props.refreshToken,
|
|
1189
|
+
agencyRef: props.agencyRef
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
function SessionShell(props) {
|
|
1193
|
+
const { environment = "live", onRefresh, onSessionExpired, children } = props;
|
|
1194
|
+
const [state, setState] = useState(null);
|
|
1195
|
+
const expiredCallbackFiredRef = useRef(false);
|
|
1196
|
+
const currentPersonaRef = useRef("manager");
|
|
1197
|
+
const reportExpired = useCallback(() => {
|
|
1198
|
+
if (expiredCallbackFiredRef.current) {
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
expiredCallbackFiredRef.current = true;
|
|
1202
|
+
onSessionExpired?.(currentPersonaRef.current);
|
|
1203
|
+
setState(
|
|
1204
|
+
(current) => current === null ? current : { ...current, session: { ...current.session, status: "expired" } }
|
|
1205
|
+
);
|
|
1206
|
+
}, [onSessionExpired]);
|
|
1207
|
+
const accessTokenDep = "accessToken" in props ? props.accessToken : void 0;
|
|
1208
|
+
const refreshTokenDep = "refreshToken" in props ? props.refreshToken : void 0;
|
|
1209
|
+
const agencyRefDep = "agencyRef" in props ? props.agencyRef : void 0;
|
|
1210
|
+
const shareTokenDep = "shareToken" in props ? props.shareToken : void 0;
|
|
1211
|
+
useEffect(() => {
|
|
1212
|
+
let cancelled = false;
|
|
1213
|
+
const client = new KaafilClient({ environment });
|
|
1214
|
+
expiredCallbackFiredRef.current = false;
|
|
1215
|
+
async function open() {
|
|
1216
|
+
const credential = await resolveCredential(props);
|
|
1217
|
+
if (cancelled) {
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
currentPersonaRef.current = credential.kind;
|
|
1221
|
+
if (credential.kind === "share") {
|
|
1222
|
+
client.share.open({ token: credential.shareToken });
|
|
1223
|
+
} else if (credential.kind === "agencyAdmin") {
|
|
1224
|
+
client.admin.open({
|
|
1225
|
+
accessToken: credential.accessToken,
|
|
1226
|
+
refreshToken: credential.refreshToken,
|
|
1227
|
+
agencyRef: credential.agencyRef,
|
|
1228
|
+
...onRefresh !== void 0 ? { onRefresh } : {}
|
|
1229
|
+
});
|
|
1230
|
+
} else {
|
|
1231
|
+
client.session.open({
|
|
1232
|
+
accessToken: credential.accessToken,
|
|
1233
|
+
refreshToken: credential.refreshToken,
|
|
1234
|
+
agencyRef: credential.agencyRef,
|
|
1235
|
+
...onRefresh !== void 0 ? { onRefresh } : {}
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
setState({
|
|
1239
|
+
client,
|
|
1240
|
+
session: {
|
|
1241
|
+
status: "authenticated",
|
|
1242
|
+
persona: credential.kind,
|
|
1243
|
+
agencyRef: credential.agencyRef,
|
|
1244
|
+
environment
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
void open();
|
|
1249
|
+
return () => {
|
|
1250
|
+
cancelled = true;
|
|
1251
|
+
client.close();
|
|
1252
|
+
};
|
|
1253
|
+
}, [environment, accessTokenDep, refreshTokenDep, agencyRefDep, shareTokenDep]);
|
|
1254
|
+
if (state === null) {
|
|
1255
|
+
return /* @__PURE__ */ jsx(SessionExpiryBridgeContext.Provider, { value: reportExpired, children: /* @__PURE__ */ jsx(
|
|
1256
|
+
SessionContext.Provider,
|
|
1257
|
+
{
|
|
1258
|
+
value: { status: "opening", persona: "manager", agencyRef: void 0, environment },
|
|
1259
|
+
children
|
|
1260
|
+
}
|
|
1261
|
+
) });
|
|
1262
|
+
}
|
|
1263
|
+
return /* @__PURE__ */ jsx(SessionExpiryBridgeContext.Provider, { value: reportExpired, children: /* @__PURE__ */ jsx(KaafilClientContext.Provider, { value: state.client, children: /* @__PURE__ */ jsx(SessionContext.Provider, { value: state.session, children }) }) });
|
|
1264
|
+
}
|
|
1265
|
+
function emit(onEvent, event) {
|
|
1266
|
+
if (onEvent === void 0) {
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
try {
|
|
1270
|
+
onEvent(event);
|
|
1271
|
+
} catch {
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function TelemetryShell({ onEvent, children }) {
|
|
1275
|
+
const session = useSession();
|
|
1276
|
+
const serverTime = useServerTime();
|
|
1277
|
+
const previousStatusRef = useRef(void 0);
|
|
1278
|
+
useEffect(() => {
|
|
1279
|
+
const previousStatus = previousStatusRef.current;
|
|
1280
|
+
if (serverTime.sinceLastSync !== void 0) {
|
|
1281
|
+
if (session.status === "authenticated" && previousStatus !== "authenticated") {
|
|
1282
|
+
emit(onEvent, {
|
|
1283
|
+
type: "session.opened",
|
|
1284
|
+
timestamp: serverTime.now(),
|
|
1285
|
+
persona: session.persona
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
if (session.status === "expired" && previousStatus !== "expired") {
|
|
1289
|
+
emit(onEvent, {
|
|
1290
|
+
type: "session.expired",
|
|
1291
|
+
timestamp: serverTime.now(),
|
|
1292
|
+
persona: session.persona
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
previousStatusRef.current = session.status;
|
|
1297
|
+
}, [session.status, serverTime.sinceLastSync]);
|
|
1298
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
1299
|
+
}
|
|
1300
|
+
function resolveSystemTheme() {
|
|
1301
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
1302
|
+
return "light";
|
|
1303
|
+
}
|
|
1304
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1305
|
+
}
|
|
1306
|
+
function ThemeShell({ theme = "system", density, children }) {
|
|
1307
|
+
const [systemTheme, setSystemTheme] = useState(
|
|
1308
|
+
() => theme === "system" ? resolveSystemTheme() : theme
|
|
1309
|
+
);
|
|
1310
|
+
useEffect(() => {
|
|
1311
|
+
if (theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const query = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1315
|
+
const listener = (event) => {
|
|
1316
|
+
setSystemTheme(event.matches ? "dark" : "light");
|
|
1317
|
+
};
|
|
1318
|
+
query.addEventListener("change", listener);
|
|
1319
|
+
return () => query.removeEventListener("change", listener);
|
|
1320
|
+
}, [theme]);
|
|
1321
|
+
const resolvedTheme = theme === "system" ? systemTheme : theme;
|
|
1322
|
+
const value = useMemo(() => ({ theme: resolvedTheme, density }), [resolvedTheme, density]);
|
|
1323
|
+
return /* @__PURE__ */ jsx(ThemeAttributesContext.Provider, { value, children });
|
|
1324
|
+
}
|
|
1325
|
+
var DefaultCapabilityViewsContext = createContext({});
|
|
1326
|
+
function useDefaultCapabilityViews() {
|
|
1327
|
+
return useContext(DefaultCapabilityViewsContext);
|
|
1328
|
+
}
|
|
1329
|
+
var BuildShareUrlContext = createContext(null);
|
|
1330
|
+
function useBuildShareUrl() {
|
|
1331
|
+
return useContext(BuildShareUrlContext);
|
|
1332
|
+
}
|
|
1333
|
+
function KaafilUIKitProvider(props) {
|
|
1334
|
+
const {
|
|
1335
|
+
environment,
|
|
1336
|
+
onSessionExpired,
|
|
1337
|
+
density,
|
|
1338
|
+
theme,
|
|
1339
|
+
brand,
|
|
1340
|
+
locale,
|
|
1341
|
+
isolation,
|
|
1342
|
+
onEvent,
|
|
1343
|
+
storage,
|
|
1344
|
+
blob,
|
|
1345
|
+
conflictResolver,
|
|
1346
|
+
readEntity,
|
|
1347
|
+
offlineScope,
|
|
1348
|
+
seedConfig,
|
|
1349
|
+
defaultCapabilityViews,
|
|
1350
|
+
buildShareUrl,
|
|
1351
|
+
children,
|
|
1352
|
+
...credential
|
|
1353
|
+
} = props;
|
|
1354
|
+
const inner = /* @__PURE__ */ jsx(BuildShareUrlContext.Provider, { value: buildShareUrl ?? null, children: /* @__PURE__ */ jsx(DefaultCapabilityViewsContext.Provider, { value: defaultCapabilityViews ?? {}, children: /* @__PURE__ */ jsx(ConfigShell, { seedConfig, children: /* @__PURE__ */ jsx(
|
|
1355
|
+
BrandShell,
|
|
1356
|
+
{
|
|
1357
|
+
appName: brand?.appName,
|
|
1358
|
+
logo: brand?.logo,
|
|
1359
|
+
logoMark: brand?.logoMark,
|
|
1360
|
+
favicon: brand?.favicon,
|
|
1361
|
+
headStrategy: brand?.headStrategy,
|
|
1362
|
+
children: /* @__PURE__ */ jsx(TelemetryShell, { onEvent, children })
|
|
1363
|
+
}
|
|
1364
|
+
) }) }) });
|
|
1365
|
+
return /* @__PURE__ */ jsx(ErrorShell, { children: /* @__PURE__ */ jsx(ThemeShell, { theme, density, children: /* @__PURE__ */ jsx(I18nShell, { locale, children: /* @__PURE__ */ jsx(AppShell, { isolation, children: /* @__PURE__ */ jsx(
|
|
1366
|
+
SessionShell,
|
|
1367
|
+
{
|
|
1368
|
+
...credential,
|
|
1369
|
+
environment,
|
|
1370
|
+
onSessionExpired,
|
|
1371
|
+
children: storage !== void 0 ? /* @__PURE__ */ jsx(
|
|
1372
|
+
OfflineShell,
|
|
1373
|
+
{
|
|
1374
|
+
storage,
|
|
1375
|
+
blob,
|
|
1376
|
+
conflictResolver,
|
|
1377
|
+
readEntity,
|
|
1378
|
+
scope: offlineScope,
|
|
1379
|
+
children: inner
|
|
1380
|
+
}
|
|
1381
|
+
) : inner
|
|
1382
|
+
}
|
|
1383
|
+
) }) }) }) });
|
|
1384
|
+
}
|
|
892
1385
|
|
|
893
1386
|
// src/core/routing/deepLinks.ts
|
|
894
1387
|
var CLOSEOUT_ACTION_TAB = {
|
|
@@ -970,6 +1463,6 @@ var TRIP_TAB_KEY_SET = {
|
|
|
970
1463
|
forms: true
|
|
971
1464
|
};
|
|
972
1465
|
|
|
973
|
-
export { CLOSEOUT_ACTION_TAB, familySafeSections, fromSearchParams, isTripTabKey, resolveBlockerDeepLink, toSearchParams, useAgencyAdminMe, useAgencyChecklistTemplates, useAgencyForms, useAgencyJourneyTriggers, useAgencyManagerProfile, useAgencySettings, useAgencyTrip, useAgencyTripManifest, useAgencyTrips, useBookings, useCapabilityStatuses, useDirectories, usePaginatedList,
|
|
974
|
-
//# sourceMappingURL=chunk-
|
|
975
|
-
//# sourceMappingURL=chunk-
|
|
1466
|
+
export { AppShell, CLOSEOUT_ACTION_TAB, ConfigShell, ErrorShell, KaafilUIKitProvider, OfflineShell, SessionShell, TelemetryShell, ThemeShell, familySafeSections, fromSearchParams, isTripTabKey, resolveBlockerDeepLink, toSearchParams, useAgencyAdminMe, useAgencyChecklistTemplates, useAgencyForms, useAgencyJourneyTriggers, useAgencyManagerProfile, useAgencySettings, useAgencyTrip, useAgencyTripManifest, useAgencyTrips, useBookings, useBuildShareUrl, useCapabilityStatuses, useDefaultCapabilityViews, useDirectories, usePaginatedList, useShareLinks, useTravellerFormResponses };
|
|
1467
|
+
//# sourceMappingURL=chunk-FEJL6GKJ.js.map
|
|
1468
|
+
//# sourceMappingURL=chunk-FEJL6GKJ.js.map
|