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