Files
InsanityLab-web/.superpowers/sdd/task-8-brief.md
T
Adriano b913a2bf90 registri dei piani in git (AI-OS#309)
Refs gitea:Adriano/AI-OS#310

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:36:26 +02:00

4.8 KiB

Task 8: Header SSR con identità utente

Files:

  • Modify: src/layouts/Base.astro
  • Modify: src/components/Header.astro

Interfaces:

  • Consumes: getSessionUser, SESSION_COOKIE (auth.ts).

  • Produces: Header accetta Props { user: { username: string; role: string } | null; showTags: boolean; canTags: boolean }. In questo task showTags/canTags sono passati ma il toggle è aggiunto nel Task 9 (qui il menu mostra solo identità + area riservata + esci).

  • Step 1: Calcola la sessione in Base.astro e passala all'header

In src/layouts/Base.astro, sostituisci il blocco // Overlay annotate... con:

const tok = Astro.cookies.get(SESSION_COOKIE)?.value;
const user = tok ? getSessionUser(getDb(), tok) : null;
const canTags = !!user && (user.role === 'superuser' || user.role === 'admin');
const wantTags = Astro.cookies.get('showtags')?.value === '1' || Astro.url.searchParams.get('annotate') === '1';
const showTags = canTags && wantTags;

E cambia <Header /> in:

<Header user={user} showTags={showTags} canTags={canTags} />

(La sostituzione della variabile annotate con showTags nell'overlay avviene nel Task 10; per ora l'overlay resta gestito da showTags — aggiorna il blocco {annotate && ( in {showTags && ( già qui, così la pagina compila.)

  • Step 2: Rendi l'header consapevole della sessione

Sostituisci il frontmatter e il markup di src/components/Header.astro:

---
import { site } from '../data/site';
import { t, contentImageUrl } from '../lib/content';
interface Props {
  user: { username: string; role: string } | null;
  showTags: boolean;
  canTags: boolean;
}
const { user, canTags } = Astro.props;
const path = Astro.url.pathname;
const loginHref = `/login?next=${encodeURIComponent(path)}`;
const areaHref = user?.role === 'superuser' ? '/admin/content' : '/admin';
---
<header class="hdr" id="site-header">
  <div class="container hdr__in">
    <a href="/" class="hdr__logo"><img src={contentImageUrl('header.logo.image', '/img/logo-dark.png')} alt={t('header.logo.alt')} width="150" /></a>
    <nav class="hdr__nav" id="site-nav" aria-label="principale">
      {site.navigation.map((item, i) => (
        <a href={item.href} class:list={['hdr__link', { 'is-active': path === item.href || (item.href !== '/' && path.startsWith(item.href)) }]}>{t(`global.nav.${i + 1}.label`)}</a>
      ))}
    </nav>
    <div class="hdr__user">
      {user ? (
        <details class="umenu">
          <summary class="umenu__btn">{user.username} ▾</summary>
          <div class="umenu__panel">
            <a href={areaHref}>Area riservata</a>
            {canTags && (
              <button type="button" class="umenu__tags" id="toggle-tags" data-slot="tags">Mostra tag</button>
            )}
            <a href="/admin/logout">Esci</a>
          </div>
        </details>
      ) : (
        <a class="hdr__login" href={loginHref}>Accedi</a>
      )}
    </div>
    <button class="hdr__burger" id="nav-toggle" aria-label="Apri menu" aria-expanded="false">
      <span></span><span></span><span></span>
    </button>
  </div>
</header>

Aggiungi in fondo al blocco <style> esistente dell'header:

  .hdr__user { display: flex; align-items: center; }
  .hdr__login { font-family: var(--font-heading); font-size: .72rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; text-decoration: none; color: var(--c-heading); }
  .hdr__login:hover { color: var(--c-accent-dark); }
  .umenu { position: relative; }
  .umenu__btn { cursor: pointer; list-style: none; font-family: var(--font-heading); font-size: .72rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--c-heading); }
  .umenu__btn::-webkit-details-marker { display: none; }
  .umenu__panel { position: absolute; right: 0; top: calc(100% + 8px); background: #fff; box-shadow: 0 8px 22px rgba(0,0,0,.12); min-width: 180px; display: flex; flex-direction: column; padding: 8px 0; z-index: 200; }
  .umenu__panel a, .umenu__tags { padding: 10px 16px; text-align: left; background: none; border: 0; cursor: pointer; font: inherit; font-size: .8rem; text-decoration: none; color: var(--c-heading); }
  .umenu__panel a:hover, .umenu__tags:hover { background: #f4f2ef; }

(Il pulsante #toggle-tags è reso qui ma il suo comportamento è cablato nel Task 9.)

  • Step 3: Verifica build

Run: npm run build Expected: build OK.

  • Step 4: Verifica manuale in dev

Run: fuser -k 4321/tcp 2>/dev/null; npm run dev & poi curl -s localhost:4321/ | grep -o 'Accedi' Expected: Accedi presente da anonimo. Ferma il server dopo la verifica (fuser -k 4321/tcp).

  • Step 5: Commit
git add src/layouts/Base.astro src/components/Header.astro
git commit -m "feat(header): identità utente SSR, link Accedi / menu utente"