feat: improve operations diagnostics and channel management
This commit is contained in:
@@ -59,6 +59,7 @@ type AppShellProps = {
|
||||
userRole: string;
|
||||
navSections: ShellNavSection[];
|
||||
auditNotifications?: AuditNotificationItem[];
|
||||
onSessionLockedChange?: (locked: boolean) => void;
|
||||
};
|
||||
|
||||
export function AppShell({
|
||||
@@ -70,6 +71,7 @@ export function AppShell({
|
||||
userRole,
|
||||
navSections,
|
||||
auditNotifications = [],
|
||||
onSessionLockedChange,
|
||||
}: AppShellProps) {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [mobileNavOpen, setMobileNavOpen] = useState(false);
|
||||
@@ -199,6 +201,10 @@ export function AppShell({
|
||||
setMobileNavOpen(false);
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
onSessionLockedChange?.(locked);
|
||||
}, [locked, onSessionLockedChange]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mobileNavOpen) return;
|
||||
const closeOnEscape = (event: KeyboardEvent) => {
|
||||
@@ -213,8 +219,21 @@ export function AppShell({
|
||||
const onActivity = () => markUserActivity();
|
||||
activityEvents.forEach((eventName) => window.addEventListener(eventName, onActivity, { passive: true }));
|
||||
|
||||
const onLocked = () => setLocked(true);
|
||||
const onUnlocked = () => { setLocked(false); markUserActivity(); };
|
||||
const onLocked = () => {
|
||||
updateSessionTiming(portal, { locked: true });
|
||||
setLocked(true);
|
||||
// Business routes must be unmounted while the server session is locked.
|
||||
// This prevents background list and badge requests from repeatedly
|
||||
// receiving 401 responses and guarantees a fresh read after unlocking.
|
||||
setRoutesSuspended(true);
|
||||
};
|
||||
const onUnlocked = () => {
|
||||
updateSessionTiming(portal, { locked: false });
|
||||
setLocked(false);
|
||||
setRoutesSuspended(false);
|
||||
lockRequested.current = false;
|
||||
markUserActivity();
|
||||
};
|
||||
const onLogout = () => { clearSession(portal); navigate(loginPath, { replace: true }); };
|
||||
const lockedEvent = sessionEvent(portal, 'locked');
|
||||
const unlockedEvent = sessionEvent(portal, 'unlocked');
|
||||
@@ -261,8 +280,8 @@ export function AppShell({
|
||||
const remaining = session.idleTimeoutSeconds * 1000 - (now - getLastUserActivityAt());
|
||||
if (remaining <= 0 && !lockRequested.current) {
|
||||
lockRequested.current = true;
|
||||
setLocked(true);
|
||||
setIdleWarningSeconds(null);
|
||||
dispatchSessionEvent(portal, 'locked', { reason: 'client_idle_timer' });
|
||||
void portalSessionApi.lock(portal).catch(() => undefined);
|
||||
} else if (remaining <= 5 * 60 * 1000) {
|
||||
setIdleWarningSeconds(Math.ceil(remaining / 1000));
|
||||
|
||||
Reference in New Issue
Block a user