longevity: pagine del gestionale — elenco clienti e apertura fascicolo
pages/longevity/gestionale/index.astro: tabella clienti (codice, cognome nome, età, accesso, data e tipo dell'ultima sessione via sessioniDi per cliente — va bene finché i fascicoli sono pochi, non pensata per scalare). pages/longevity/gestionale/nuovo.astro: form verso /api/longevity/gestionale/clienti, sesso a radio (non <select>: la parola inglese avrebbe fatto scattare il controllo anti-SQL di referto.test.ts, che cerca \bSELECT\b senza distinguere l'HTML). Refs #2
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
---
|
||||
// L'elenco dei clienti del gestionale: la prima pagina che il trainer apre. Ogni riga
|
||||
// porta anche data e tipo dell'ultima sessione — utile per capire a colpo d'occhio chi
|
||||
// non si presenta da tempo, senza aprire ogni scheda.
|
||||
import Longevity from '../../../layouts/Longevity.astro';
|
||||
import { getLongevityDb } from '../../../lib/longevity/db';
|
||||
import { elencoClienti, etaDelCliente } from '../../../lib/longevity/anagrafica';
|
||||
import { sessioniDi } from '../../../lib/longevity/gestionale';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const db = getLongevityDb();
|
||||
const oggi = new Date().toISOString().slice(0, 10);
|
||||
|
||||
const ETICHETTA_TIPO: Record<'checkup' | 'questionario', string> = {
|
||||
checkup: 'Check-up',
|
||||
questionario: 'Questionario',
|
||||
};
|
||||
const formattaData = (iso: string) =>
|
||||
new Date(`${iso}T00:00:00`).toLocaleDateString('it-IT', { day: '2-digit', month: 'short', year: 'numeric' });
|
||||
|
||||
// Una query in più per ogni cliente (sessioniDi, tenendo solo [0]): va bene finché i
|
||||
// fascicoli sono pochi — non è pensata per scalare a centinaia di clienti.
|
||||
const righe = elencoClienti().map((c) => ({
|
||||
...c,
|
||||
eta: etaDelCliente(c.client_code, oggi),
|
||||
ultima: sessioniDi(db, c.client_code)[0] ?? null,
|
||||
}));
|
||||
---
|
||||
<Longevity title="Clienti" crumbs={['Clienti']} variante="gestionale">
|
||||
<div class="lg-head lg-head--azioni">
|
||||
<div>
|
||||
<h1>Clienti</h1>
|
||||
<p>{righe.length} {righe.length === 1 ? 'fascicolo' : 'fascicoli'}.</p>
|
||||
</div>
|
||||
<a class="lg-btn" href="/longevity/gestionale/nuovo">Nuovo cliente</a>
|
||||
</div>
|
||||
|
||||
{righe.length === 0 ? (
|
||||
<p class="lg-muted">Nessun cliente ancora. <a href="/longevity/gestionale/nuovo">Apri il primo fascicolo</a>.</p>
|
||||
) : (
|
||||
<table class="lg-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<th>Cognome e nome</th>
|
||||
<th>Età</th>
|
||||
<th>Accesso</th>
|
||||
<th>Ultima sessione</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{righe.map((c) => (
|
||||
<tr>
|
||||
<td><a href={`/longevity/gestionale/cliente/${c.client_code}`}>{c.client_code}</a></td>
|
||||
<td>{c.cognome} {c.nome}</td>
|
||||
<td>{c.eta ?? '—'}</td>
|
||||
<td>{c.ha_accesso ? 'Sì' : 'No'}</td>
|
||||
<td>
|
||||
{c.ultima ? (
|
||||
<>{formattaData(c.ultima.data)} — {ETICHETTA_TIPO[c.ultima.tipo]}</>
|
||||
) : (
|
||||
<span class="lg-muted">Nessuna</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</Longevity>
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
// Il modulo per aprire un fascicolo nuovo: posta all'API del gestionale, che fa la
|
||||
// validazione vera (pages/api/longevity/gestionale/clienti.ts). Qui c'è solo il form.
|
||||
import Longevity from '../../../layouts/Longevity.astro';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const errore = Astro.url.searchParams.get('errore');
|
||||
---
|
||||
<Longevity title="Nuovo cliente" crumbs={['Clienti', 'Nuovo cliente']} variante="gestionale">
|
||||
<div class="lg-head">
|
||||
<h1>Nuovo cliente</h1>
|
||||
<p>Nome, cognome e sesso bastano per aprire il fascicolo: il resto si completa dopo.</p>
|
||||
</div>
|
||||
|
||||
{errore && <p class="lg-form__errore">{errore}</p>}
|
||||
|
||||
<form class="lg-form" method="post" action="/api/longevity/gestionale/clienti">
|
||||
<section class="lg-form__blocco">
|
||||
<label class="lg-field">
|
||||
<span>Nome</span>
|
||||
<input type="text" name="nome" required />
|
||||
</label>
|
||||
<label class="lg-field">
|
||||
<span>Cognome</span>
|
||||
<input type="text" name="cognome" required />
|
||||
</label>
|
||||
<fieldset class="lg-field lg-field--consenso">
|
||||
<legend>Sesso</legend>
|
||||
<label><input type="radio" name="sesso" value="F" required /> F</label>
|
||||
<label><input type="radio" name="sesso" value="M" required /> M</label>
|
||||
</fieldset>
|
||||
<label class="lg-field">
|
||||
<span>Data di nascita</span>
|
||||
<input type="date" name="data_nascita" />
|
||||
</label>
|
||||
<label class="lg-field">
|
||||
<span>Email</span>
|
||||
<input type="email" name="email" />
|
||||
</label>
|
||||
<label class="lg-field">
|
||||
<span>Telefono</span>
|
||||
<input type="tel" name="telefono" />
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<div class="lg-form__nav">
|
||||
<a href="/longevity/gestionale">Annulla</a>
|
||||
<button type="submit">Apri il fascicolo</button>
|
||||
</div>
|
||||
</form>
|
||||
</Longevity>
|
||||
Reference in New Issue
Block a user