510d7ca4ec
Primo passo della separazione sito / piattaforme decisa da Adriano il 03/09: due applicazioni autosufficienti, senza workspace di root, cosi' che ognuna resti un progetto Astro completo ed estraibile. Qui non cambia niente del codice: solo la radice. I pattern del .gitignore che avevano una barra erano ancorati alla radice e dopo lo spostamento non avrebbero piu' coperto niente (data/*.db*, uploads/*): resi validi a qualunque profondita'. Suite: 45 file, 347 test verdi prima e dopo lo spostamento. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EndnceRA5WnA6rvA5iV9WL
68 lines
2.5 KiB
Plaintext
68 lines
2.5 KiB
Plaintext
---
|
|
// Creazione di un utente, su pagina propria come per gli articoli: l'elenco resta pulito e
|
|
// il modulo ha spazio per le sue spiegazioni.
|
|
import Admin from '../../../layouts/Admin.astro';
|
|
import { ROLES } from '../../../lib/auth';
|
|
export const prerender = false;
|
|
|
|
// Descrizioni solo per la UI: se un ruolo nuovo non è qui, compare comunque nella tendina
|
|
// (fonte di validità unica: ROLES), semplicemente senza spiegazione accanto.
|
|
const DESCRIZIONI: Partial<Record<(typeof ROLES)[number], string>> = {
|
|
user: 'scrive i propri articoli',
|
|
superuser: 'articoli e contenuti del sito',
|
|
piattaforme: 'solo Campus e Stress Index',
|
|
admin: 'accesso completo',
|
|
cliente: 'solo il proprio fascicolo Longevity',
|
|
trainer: 'gestionale Longevity',
|
|
};
|
|
---
|
|
<Admin title="Nuovo utente">
|
|
<h1>Nuovo utente</h1>
|
|
|
|
<form class="aform" id="new-user">
|
|
<label class="alabel" for="username">Nome utente</label>
|
|
<input class="afield" id="username" name="username" required autocomplete="off" />
|
|
|
|
<label class="alabel" for="password">Password</label>
|
|
<input class="afield" id="password" name="password" type="text" required minlength="8" autocomplete="off" />
|
|
<p class="ahint">Almeno 8 caratteri. Resta in chiaro qui: copiala e consegnala alla persona.</p>
|
|
|
|
<label class="alabel" for="role">Ruolo</label>
|
|
<select class="afield" id="role" name="role">
|
|
{ROLES.map((r) => <option value={r}>{r}{DESCRIZIONI[r] ? ` — ${DESCRIZIONI[r]}` : ''}</option>)}
|
|
</select>
|
|
|
|
<div class="aactions">
|
|
<button class="abtn" type="submit">Crea utente</button>
|
|
<a class="abtn abtn--ghost" href="/admin/users">Annulla</a>
|
|
</div>
|
|
<p class="form-msg" id="new-msg" hidden></p>
|
|
</form>
|
|
</Admin>
|
|
|
|
<script>
|
|
const form = document.getElementById('new-user') as HTMLFormElement;
|
|
const msg = document.getElementById('new-msg') as HTMLElement;
|
|
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const dati = new FormData(form);
|
|
const res = await fetch('/api/admin/users', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
username: String(dati.get('username') ?? ''),
|
|
password: String(dati.get('password') ?? ''),
|
|
role: String(dati.get('role') ?? 'user'),
|
|
}),
|
|
});
|
|
if (res.ok) {
|
|
location.href = '/admin/users';
|
|
return;
|
|
}
|
|
msg.hidden = false;
|
|
msg.className = 'form-msg form-msg--err';
|
|
msg.textContent = (await res.json()).error ?? 'Creazione non riuscita.';
|
|
});
|
|
</script>
|