feat(guide): Testing role-tab (fleet pilot) + sidebar identity indicator
- New Testing tab (order 35, audience internal, 🧪): test accounts, manual flow, copy-paste API smoke tests, role/scoping checks, regression watch. Fleet pilot. - Reader shows 'Signed in as {email} · {role}' above Sign out (decoded from JWT), so you always know which identity you're testing as. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+12
-2
@@ -11,6 +11,14 @@
|
||||
let showPassword = false;
|
||||
let loginError = '';
|
||||
let loggingIn = false;
|
||||
let userInfo: { email: string; role: string } | null = null;
|
||||
|
||||
function decodeJwt(t: string): { email: string; role: string } | null {
|
||||
try {
|
||||
const p = JSON.parse(atob(t.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')));
|
||||
return { email: p.email ?? '', role: p.role ?? '' };
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
async function checkAuth() {
|
||||
const t = getToken();
|
||||
@@ -18,6 +26,7 @@
|
||||
try {
|
||||
await authApi.get('/api/users/me', { headers: { Authorization: `Bearer ${t}` } });
|
||||
authenticated = true;
|
||||
userInfo = decodeJwt(t);
|
||||
} catch {
|
||||
clearToken();
|
||||
} finally {
|
||||
@@ -33,6 +42,7 @@
|
||||
const token = res.data.token;
|
||||
if (!token) throw new Error('No token in response');
|
||||
setToken(token);
|
||||
userInfo = decodeJwt(token);
|
||||
authenticated = true;
|
||||
} catch (e: any) {
|
||||
loginError = e?.response?.data?.message ?? 'Invalid email or password.';
|
||||
@@ -41,7 +51,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function logout() { clearToken(); authenticated = false; }
|
||||
function logout() { clearToken(); authenticated = false; userInfo = null; }
|
||||
|
||||
onMount(checkAuth);
|
||||
</script>
|
||||
@@ -80,7 +90,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<GuideReader on:logout={logout} />
|
||||
<GuideReader on:logout={logout} userEmail={userInfo?.email ?? ''} userRole={userInfo?.role ?? ''} />
|
||||
{/if}
|
||||
|
||||
<!-- Toast notifications -->
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
import { addToast } from '../lib/stores';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
export let userEmail = '';
|
||||
export let userRole = '';
|
||||
|
||||
let nav: NavApp[] = [];
|
||||
let loadingNav = true;
|
||||
@@ -33,7 +35,7 @@
|
||||
|
||||
// role-tab icons (Customer / Internal / Architect / Claude)
|
||||
const ROLE_ICON: Record<string, string> = {
|
||||
Customer: '👤', Internal: '🔧', Architect: '📐', Claude: '✨', Overview: '📄', Data: '🗄️', Technical: '📐',
|
||||
Customer: '👤', Internal: '🔧', Architect: '📐', Testing: '🧪', Claude: '✨', Overview: '📄', Data: '🗄️', Technical: '📐',
|
||||
};
|
||||
|
||||
$: filtered = !search.trim()
|
||||
@@ -161,6 +163,13 @@
|
||||
{/if}
|
||||
</nav>
|
||||
|
||||
{#if userEmail}
|
||||
<div class="side-user">
|
||||
<span class="side-user-label">Signed in as</span>
|
||||
<span class="side-user-email" title={userEmail}>{userEmail}</span>
|
||||
{#if userRole}<span class="side-user-role">{userRole}</span>{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<button class="side-logout" on:click={() => dispatch('logout')}>Sign out</button>
|
||||
</aside>
|
||||
|
||||
@@ -258,6 +267,19 @@
|
||||
.nav-item:hover { background: rgba(255,255,255,0.08); color: white; }
|
||||
.nav-item.active { background: rgba(255,255,255,0.14); color: white; border-left-color: white; font-weight: 600; }
|
||||
|
||||
.side-user {
|
||||
margin: 0.4rem 1rem 0.2rem; padding: 0.5rem 0.15rem 0.4rem;
|
||||
border-top: 1px solid rgba(255,255,255,0.12);
|
||||
display: flex; flex-direction: column; gap: 1px;
|
||||
}
|
||||
.side-user-label { color: rgba(255,255,255,0.5); font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||
.side-user-email { color: rgba(255,255,255,0.92); font-size: 0.78rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.side-user-role {
|
||||
align-self: flex-start; margin-top: 3px;
|
||||
color: #93c5fd; background: rgba(59,130,246,0.18); border: 1px solid rgba(59,130,246,0.35);
|
||||
font-size: 0.6rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
|
||||
border-radius: 4px; padding: 1px 6px;
|
||||
}
|
||||
.side-logout {
|
||||
margin: 0.5rem 1rem 1rem; padding: 0.5rem; border-radius: 6px;
|
||||
background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2);
|
||||
|
||||
Reference in New Issue
Block a user