b913a2bf90
Refs gitea:Adriano/AI-OS#310 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
92 lines
4.4 KiB
Markdown
92 lines
4.4 KiB
Markdown
### Task 1: Il guscio — layout, stile, e una rotta che risponde
|
|
|
|
**Files:**
|
|
- Create: `src/layouts/Longevity.astro`, `src/styles/longevity.css`, `src/pages/longevity/index.astro`
|
|
- Test: `tests/longevity/pagine.test.ts`
|
|
|
|
**Interfaces:**
|
|
- Consumes: `isProtectedPath`, `canAccessAdminPath` da `src/lib/auth.ts`
|
|
- Produces: il layout `Longevity.astro` con `title` e `crumbs`, e le classi `.lg-*`
|
|
|
|
**Il modello da seguire:** `src/layouts/StressIndex.astro` e `src/styles/stress-index.css`. Leggili prima di scrivere. Il foglio di stile va **globale e prefissato**, non in `<style>` scoped: le stesse classi servono ai componenti Astro e alle isole React, che lo scope di Astro non raggiunge — la ragione è scritta in testa al foglio di Stress Index.
|
|
|
|
**La differenza voluta rispetto a Stress Index.** Quella piattaforma ha una barra laterale con sette voci, giusta per chi ci **lavora** dentro. Qui i pubblici sono due e uno non è staff: il **cliente** entra tre o quattro volte l'anno per guardare il proprio referto, e una barra di navigazione è l'arredamento di uno strumento di lavoro. Quindi il layout prevede due forme: `variante="referto"` (una colonna che scorre, nessuna navigazione) e `variante="gestionale"` (barra laterale come Stress Index). Questo task realizza la prima; la seconda arriva col piano del gestionale.
|
|
|
|
- [ ] **Step 1: Write the failing test**
|
|
|
|
```ts
|
|
// tests/longevity/pagine.test.ts
|
|
import { describe, it, expect } from 'vitest';
|
|
import { readFileSync, existsSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
const leggi = (p: string) => readFileSync(join(process.cwd(), p), 'utf8');
|
|
|
|
describe('il guscio della piattaforma', () => {
|
|
it('il layout esiste e non monta header e footer pubblici', () => {
|
|
const l = leggi('src/layouts/Longevity.astro');
|
|
expect(l).not.toMatch(/import\s+Header/);
|
|
expect(l).not.toMatch(/import\s+Footer/);
|
|
expect(l).toMatch(/noindex/); // area riservata: mai indicizzata
|
|
});
|
|
|
|
it('il foglio di stile e globale e prefissato, non scoped', () => {
|
|
expect(existsSync(join(process.cwd(), 'src/styles/longevity.css'))).toBe(true);
|
|
const css = leggi('src/styles/longevity.css');
|
|
const classi = [...css.matchAll(/^\.([a-z-]+)/gm)].map((m) => m[1]);
|
|
expect(classi.length).toBeGreaterThan(3);
|
|
expect(classi.every((c) => c.startsWith('lg'))).toBe(true);
|
|
});
|
|
|
|
it('usa i token del sito invece di inventare colori', () => {
|
|
const css = leggi('src/styles/longevity.css');
|
|
expect(css).toMatch(/var\(--c-/);
|
|
});
|
|
|
|
it('nessun semaforo sui punteggi: il colore non giudica il corpo', () => {
|
|
const css = leggi('src/styles/longevity.css').toLowerCase();
|
|
// il mattone del fuori range e ammesso; un verde "tutto bene" no
|
|
expect(css).not.toMatch(/--lg-ok|--lg-buono|--lg-verde/);
|
|
});
|
|
|
|
it('la rotta del cliente e sotto /longevity, dove le regole la proteggono', () => {
|
|
expect(existsSync(join(process.cwd(), 'src/pages/longevity/index.astro'))).toBe(true);
|
|
});
|
|
});
|
|
```
|
|
|
|
- [ ] **Step 2: Run test to verify it fails**
|
|
|
|
Run: `npm test -- tests/longevity/pagine.test.ts`
|
|
Expected: FAIL — i file non esistono
|
|
|
|
- [ ] **Step 3: Write minimal implementation**
|
|
|
|
Il layout prende `title`, `crumbs` e `variante`, importa i font e i due fogli di stile (`global.css` e `longevity.css`), e dichiara `noindex`. Nessun `Header`/`Footer` pubblico: la navigazione è interna.
|
|
|
|
Il foglio `longevity.css` definisce, **tutte prefissate `lg`**: il guscio del referto (una colonna centrata, larghezza leggibile), l'intestazione, la carta di un punteggio, e i token locali. Le variabili locali riprendono quelle del sito (`--c-accent`, `--c-dark`, `--c-bg-alt`) e ne aggiungono due sole:
|
|
|
|
```css
|
|
--lg-tratteggio: #d8d2c8; /* il perimetro di cio che non sappiamo */
|
|
--lg-fuori-range: #b05a4e; /* SOLO per una misura sospetta, mai per un punteggio */
|
|
```
|
|
|
|
⚠️ Non definire un colore «buono» o «cattivo» per i punteggi: l'intensità si ottiene variando l'opacità dell'accento. È la regola di prodotto, e il test la controlla.
|
|
|
|
`src/pages/longevity/index.astro` reindirizza a `/longevity/io`.
|
|
|
|
- [ ] **Step 4: Run test to verify it passes**
|
|
|
|
Run: `npm test -- tests/longevity/pagine.test.ts` → PASS (5 test)
|
|
Poi: `npm test` per intero e `npx tsc --noEmit`.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/layouts/Longevity.astro src/styles/longevity.css src/pages/longevity/index.astro tests/longevity/pagine.test.ts
|
|
git commit -m "longevity: il guscio della piattaforma, senza semaforo sui punteggi"
|
|
```
|
|
|
|
---
|
|
|