docs(ui): Add UI layout refactor plan and OpenSpec changes
- Update decision-log with UI layout decisions (Feb 18, 2026) - Update architecture with frontend layout patterns - Update config.yaml with TDD, documentation, UI standards rules - Create p00-api-documentation change (Scribe annotations) - Create p01-ui-foundation change (types, stores, Lucide) - Create p02-app-layout change (AppLayout, Sidebar, TopBar) - Create p03-dashboard-enhancement change (PageHeader, StatCard) - Create p04-content-patterns change (DataTable, FilterBar) - Create p05-page-migrations change (page migrations) - Fix E2E auth tests (11/11 passing) - Add JWT expiry validation to dashboard guard
This commit is contained in:
@@ -22,6 +22,33 @@ export function getRefreshToken(): string | null {
|
||||
return localStorage.getItem(REFRESH_TOKEN_KEY);
|
||||
}
|
||||
|
||||
export function isValidJwtFormat(token: string | null): boolean {
|
||||
if (!token) return false;
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) return false;
|
||||
return parts.every(part => part.length > 0);
|
||||
}
|
||||
|
||||
export function isJwtExpired(token: string | null): boolean {
|
||||
if (!isValidJwtFormat(token)) return true;
|
||||
|
||||
try {
|
||||
const payload = token!.split('.')[1];
|
||||
const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');
|
||||
const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
|
||||
const decoded = atob(padded);
|
||||
const data = JSON.parse(decoded) as { exp?: number };
|
||||
|
||||
if (typeof data.exp !== 'number') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return data.exp <= Math.floor(Date.now() / 1000);
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function setTokens(accessToken: string, refreshToken: string): void {
|
||||
if (typeof localStorage === 'undefined') return;
|
||||
localStorage.setItem(ACCESS_TOKEN_KEY, accessToken);
|
||||
|
||||
Reference in New Issue
Block a user