Area riservata: menu essenziale e veste grafica del sito
Il menu in alto si riduce alle voci che servono davvero — Articoli, Contenuti, Utenti, Sito, Esci — con la voce corrente evidenziata; ognuna resta visibile solo a chi ha il ruolo per aprirla, e in pagina di login la barra mostra il solo marchio. "Nuovo" esce dal menu perché il pulsante per creare un articolo è già in cima all'elenco Articoli. Il pannello smette di essere un foglio di stile a sé: adotta i font e la palette del sito, come Campus e Stress Index, con tabelle, pulsanti, campi e pillole di stato riportati allo stesso linguaggio. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+123
-33
@@ -1,7 +1,28 @@
|
||||
---
|
||||
// Layout dell'area riservata. Usa i token e i font del sito, come il Campus e Stress Index:
|
||||
// il pannello non è più un foglio di stile a sé, così chi passa dal sito all'amministrazione
|
||||
// resta nello stesso mondo visivo.
|
||||
import '@fontsource/montserrat/400.css';
|
||||
import '@fontsource/montserrat/500.css';
|
||||
import '@fontsource/montserrat/600.css';
|
||||
import '@fontsource/open-sans/400.css';
|
||||
import '@fontsource/open-sans/600.css';
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props { title: string }
|
||||
const { title } = Astro.props;
|
||||
const user = Astro.locals.user;
|
||||
const path = Astro.url.pathname;
|
||||
const isManager = user?.role === 'superuser' || user?.role === 'admin';
|
||||
|
||||
// Voci del menu: ognuna compare solo a chi può davvero aprirla.
|
||||
const VOCI = [
|
||||
{ href: '/admin', label: 'Articoli', visibile: true, exact: true },
|
||||
{ href: '/admin/content', label: 'Contenuti', visibile: isManager },
|
||||
{ href: '/admin/users', label: 'Utenti', visibile: user?.role === 'admin' },
|
||||
];
|
||||
const attiva = (v: { href: string; exact?: boolean }) =>
|
||||
v.exact ? path === v.href : path.startsWith(v.href);
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang="it">
|
||||
@@ -10,41 +31,110 @@ const user = Astro.locals.user;
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{title} — Admin InsanityLab</title>
|
||||
<meta name="robots" content="noindex" />
|
||||
<style is:global>
|
||||
body { font-family: system-ui, sans-serif; margin: 0; background: #f4f2ef; color: #333; }
|
||||
.abar { background: #3a2d26; color: #fff; padding: 12px 24px; display: flex; justify-content: space-between; align-items: center; }
|
||||
.abar a { color: #d9cfc4; text-decoration: none; margin-left: 16px; }
|
||||
.awrap { max-width: 960px; margin: 30px auto; padding: 0 20px; }
|
||||
.abtn { background: #b5a48b; color: #fff; border: 0; padding: 10px 18px; cursor: pointer; font-size: .9rem; text-decoration: none; display: inline-block; }
|
||||
.abtn--danger { background: #a33; }
|
||||
.afield { width: 100%; padding: 10px; margin-bottom: 14px; border: 1px solid #ccc; box-sizing: border-box; font: inherit; }
|
||||
table { width: 100%; border-collapse: collapse; background: #fff; }
|
||||
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid #eee; font-size: .92rem; }
|
||||
.pill { font-size: .75rem; padding: 2px 10px; border-radius: 10px; background: #e5decf; }
|
||||
.pill--draft { background: #f3d9a4; }
|
||||
.form-msg { font-size: .9rem; margin-top: 8px; }
|
||||
.form-msg--ok { color: #3c7a3c; }
|
||||
.form-msg--err { color: #b03030; }
|
||||
</style>
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="abar">
|
||||
<strong>InsanityLab · Admin</strong>
|
||||
<span>
|
||||
<body class="ad">
|
||||
<header class="abar">
|
||||
<a class="abar__brand" href="/admin">
|
||||
<span class="abar__eyebrow">InsanityLab</span>
|
||||
Area riservata
|
||||
</a>
|
||||
|
||||
{user && (
|
||||
<>
|
||||
{`Ciao, ${user.username}`}
|
||||
<a href="/admin">Articoli</a>
|
||||
{(user.role === 'superuser' || user.role === 'admin') && <a href="/admin/content">Contenuti</a>}
|
||||
{user.role === 'admin' && <a href="/admin/users">Utenti</a>}
|
||||
{user.role === 'admin' && <a href="/piattaforme">Piattaforme</a>}
|
||||
<a href="/admin/new">Nuovo</a>
|
||||
<a href="/admin/logout">Esci</a>
|
||||
<a href="/blog" target="_blank">Vedi sito ↗</a>
|
||||
</>
|
||||
<nav class="abar__nav" aria-label="pannello">
|
||||
{VOCI.filter((v) => v.visibile).map((v) => (
|
||||
<a href={v.href} class:list={['abar__link', { 'is-active': attiva(v) }]}>{v.label}</a>
|
||||
))}
|
||||
<a class="abar__link" href="/" target="_blank" rel="noopener">Sito</a>
|
||||
<a class="abar__link abar__link--out" href="/admin/logout">Esci</a>
|
||||
</nav>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="awrap"><slot /></div>
|
||||
|
||||
{user && <p class="abar__user">{user.username}</p>}
|
||||
</header>
|
||||
|
||||
<main class="awrap"><slot /></main>
|
||||
|
||||
<style is:global>
|
||||
body.ad { background: var(--c-bg-alt); }
|
||||
|
||||
/* --- barra --- */
|
||||
.abar {
|
||||
display: flex; align-items: center; gap: 28px; flex-wrap: wrap;
|
||||
background: var(--c-dark); color: #cfc6bd; padding: 16px 32px;
|
||||
}
|
||||
.abar__brand {
|
||||
font-family: var(--font-heading); font-size: 1rem; font-weight: 600; letter-spacing: .12em;
|
||||
text-transform: uppercase; color: #fff; text-decoration: none; line-height: 1.15;
|
||||
}
|
||||
.abar__eyebrow { display: block; font-size: .62rem; letter-spacing: .2em; color: var(--c-accent); }
|
||||
.abar__nav { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; }
|
||||
.abar__link {
|
||||
font-family: var(--font-heading); font-size: .72rem; font-weight: 600; letter-spacing: .16em;
|
||||
text-transform: uppercase; color: #b6ada3; text-decoration: none; padding: 4px 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
.abar__link:hover { color: #fff; }
|
||||
.abar__link.is-active { color: #fff; border-bottom-color: var(--c-accent); }
|
||||
.abar__link--out { color: var(--c-accent); }
|
||||
.abar__user {
|
||||
margin: 0 0 0 auto; font-size: .74rem; letter-spacing: .12em; text-transform: uppercase; color: #8d8379;
|
||||
}
|
||||
|
||||
/* --- contenuto --- */
|
||||
.awrap { max-width: 1080px; margin: 40px auto 80px; padding: 0 24px; }
|
||||
.awrap h1 {
|
||||
font-size: clamp(1.6rem, 3vw, 2.1rem); text-transform: none; letter-spacing: .02em; margin-bottom: .6em;
|
||||
}
|
||||
.awrap h2 { font-size: 1.15rem; text-transform: none; letter-spacing: .02em; }
|
||||
|
||||
/* --- comandi --- */
|
||||
.abtn {
|
||||
display: inline-block; background: var(--c-dark); color: #fff; border: 1px solid var(--c-dark);
|
||||
padding: 10px 20px; cursor: pointer; text-decoration: none; font-family: var(--font-heading);
|
||||
font-size: .7rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
|
||||
}
|
||||
.abtn:hover { background: var(--c-dark-2); border-color: var(--c-dark-2); }
|
||||
.abtn--danger { background: none; color: #a4544a; border-color: #ddcfc9; }
|
||||
.abtn--danger:hover { background: #a4544a; border-color: #a4544a; color: #fff; }
|
||||
|
||||
.afield {
|
||||
width: 100%; padding: 11px 13px; margin-bottom: 14px; border: 1px solid #ddd6cb;
|
||||
box-sizing: border-box; font: inherit; font-size: .92rem; background: #fff; border-radius: 0;
|
||||
}
|
||||
.afield:focus { outline: 2px solid var(--c-accent); outline-offset: -2px; }
|
||||
|
||||
/* --- tabelle --- */
|
||||
table { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid #e4dfd7; }
|
||||
th, td { text-align: left; padding: 12px 14px; border-bottom: 1px solid #efeae2; font-size: .9rem; }
|
||||
th {
|
||||
font-family: var(--font-heading); font-size: .68rem; font-weight: 600; letter-spacing: .14em;
|
||||
text-transform: uppercase; color: var(--c-text-light); background: #faf8f5;
|
||||
}
|
||||
tbody tr:last-child td { border-bottom: 0; }
|
||||
tbody tr:hover { background: #faf8f5; }
|
||||
td a { color: var(--c-accent-dark); font-weight: 600; text-decoration: none; }
|
||||
td a:hover { text-decoration: underline; }
|
||||
|
||||
.pill {
|
||||
display: inline-block; font-family: var(--font-heading); font-size: .64rem; font-weight: 600;
|
||||
letter-spacing: .1em; text-transform: uppercase; padding: 3px 10px;
|
||||
background: rgba(111,143,106,.16); color: #5f7c5a;
|
||||
}
|
||||
.pill--draft { background: rgba(192,138,62,.18); color: #96682a; }
|
||||
|
||||
.aform { max-width: 380px; background: #fff; border: 1px solid #e4dfd7; padding: 26px 24px; }
|
||||
|
||||
.form-msg { font-size: .88rem; margin-top: 8px; }
|
||||
.form-msg--ok { color: #5f7c5a; }
|
||||
.form-msg--err { color: #a4544a; }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.abar { padding: 14px 20px; gap: 14px; }
|
||||
.abar__nav { gap: 16px; width: 100%; }
|
||||
.abar__user { margin-left: 0; }
|
||||
.awrap { margin-top: 26px; padding: 0 20px; }
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -109,22 +109,26 @@ const allStyles = STYLE_GROUPS as Record<string, readonly string[]>;
|
||||
|
||||
<style is:global>
|
||||
.chead { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; flex-wrap: wrap; }
|
||||
.chead a { color: #7a6a55; }
|
||||
.cgroup { background: #fff; border: 1px solid #ddd; margin-bottom: 14px; }
|
||||
.cgroup > summary { padding: 12px 16px; cursor: pointer; background: #eee9e1; font-size: 1.05rem; }
|
||||
.crow { border-top: 1px solid #eee; }
|
||||
.chead a { color: var(--c-accent-dark); font-weight: 600; text-decoration: none; }
|
||||
.cgroup { background: #fff; border: 1px solid #e4dfd7; margin-bottom: 14px; }
|
||||
.cgroup > summary {
|
||||
padding: 13px 16px; cursor: pointer; background: #faf8f5; border-bottom: 1px solid #efeae2;
|
||||
font-family: var(--font-heading); font-size: .78rem; font-weight: 600; letter-spacing: .14em;
|
||||
text-transform: uppercase; color: var(--c-heading);
|
||||
}
|
||||
.crow { border-top: 1px solid #efeae2; }
|
||||
.crow > summary { padding: 8px 16px; cursor: pointer; display: flex; gap: 12px; align-items: baseline; }
|
||||
.crow > summary code { background: #f0ece5; padding: 1px 6px; font-size: .82rem; white-space: nowrap; }
|
||||
.crow > summary code { background: #f2ede5; padding: 2px 7px; font-size: .78rem; white-space: nowrap; color: #6f6357; }
|
||||
.crow-snippet { color: #888; font-size: .85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.crow-body { padding: 6px 16px 12px; }
|
||||
.crow-body .afield { margin-bottom: 8px; }
|
||||
.cstyles { display: flex; gap: 14px; flex-wrap: wrap; margin: 0 0 8px; }
|
||||
.cstyles label { font-size: .85rem; color: #555; }
|
||||
.cstyles label { font-size: .82rem; color: var(--c-text-light); }
|
||||
.cstyles select { margin-left: 4px; padding: 4px; font: inherit; }
|
||||
.cactions { margin: 0 0 4px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
||||
.cimg-preview img { max-width: 240px; max-height: 140px; display: block; border: 1px solid #ddd; }
|
||||
.cmeta { color: #999; font-size: .78rem; margin: 4px 0 0; }
|
||||
.crow--hl { outline: 2px solid #b5a48b; }
|
||||
.cimg-preview img { max-width: 240px; max-height: 140px; display: block; border: 1px solid #e4dfd7; }
|
||||
.cmeta { color: var(--c-text-light); font-size: .76rem; margin: 4px 0 0; }
|
||||
.crow--hl { outline: 2px solid var(--c-accent); }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -22,8 +22,8 @@ if (Astro.request.method === 'POST') {
|
||||
---
|
||||
<Admin title="Login">
|
||||
<h1>Accesso</h1>
|
||||
{error && <p style="color:#a33">{error}</p>}
|
||||
<form method="post" style="max-width:360px">
|
||||
{error && <p class="form-msg form-msg--err">{error}</p>}
|
||||
<form class="aform" method="post">
|
||||
<input class="afield" name="username" placeholder="Utente" required autocomplete="username" />
|
||||
<input class="afield" type="password" name="password" placeholder="Password" required autocomplete="current-password" />
|
||||
<button class="abtn" type="submit">Entra</button>
|
||||
|
||||
Reference in New Issue
Block a user