Files
esercizi-web/javascript/ripasso_completo/esercizio_3_api/index.html
2026-03-01 20:49:28 +01:00

474 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Esercizio 3 — La Biblioteca Online (API)</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Merriweather:wght@700&family=Inter:wght@400;500;600;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', 'Segoe UI', sans-serif;
background: linear-gradient(145deg, #fdf8f0 0%, #f5f0e8 40%, #faf5ed 100%);
color: #2d2418;
padding: 24px;
line-height: 1.6;
min-height: 100vh;
}
.container {
max-width: 880px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(12px);
border-radius: 20px;
padding: 36px;
box-shadow:
0 1px 3px rgba(0,0,0,0.04),
0 8px 32px rgba(139,90,43,0.08);
border: 1px solid rgba(255,255,255,0.6);
}
h1 {
text-align: center;
font-family: 'Merriweather', Georgia, serif;
font-size: 2.2em;
margin-bottom: 6px;
background: linear-gradient(135deg, #8b5a2b, #a0522d);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sottotitolo {
text-align: center;
color: #94867a;
font-size: 0.9em;
font-weight: 500;
margin-bottom: 10px;
}
.avviso-server {
text-align: center;
background: linear-gradient(135deg, #fef3c7, #fef9c3);
border: 1px solid #fbbf24;
color: #92400e;
padding: 12px 18px;
border-radius: 12px;
font-size: 0.88em;
font-weight: 500;
margin-bottom: 28px;
}
.avviso-server code {
background: rgba(0,0,0,0.08);
padding: 2px 6px;
border-radius: 4px;
}
/* ─── STATISTICHE ─── */
#stats {
display: flex;
justify-content: center;
gap: 16px;
margin-bottom: 28px;
flex-wrap: wrap;
}
.stat-box {
background: linear-gradient(145deg, #fff, #faf5ed);
border: 1px solid #e8ddd0;
border-radius: 14px;
padding: 18px 28px;
text-align: center;
min-width: 140px;
position: relative;
overflow: hidden;
transition: all 0.25s ease;
}
.stat-box::after {
content: '';
position: absolute;
bottom: 0; left: 0; right: 0;
height: 3px;
background: linear-gradient(90deg, #8b5a2b, #cd853f);
opacity: 0;
transition: opacity 0.25s;
}
.stat-box:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(139,90,43,0.1);
}
.stat-box:hover::after { opacity: 1; }
.stat-box .label {
font-size: 0.75em;
color: #94867a;
text-transform: uppercase;
letter-spacing: 0.8px;
font-weight: 600;
}
.stat-box .valore {
font-size: 2em;
font-weight: 700;
color: #8b5a2b;
}
/* ─── LOADING ─── */
#loading {
display: none;
text-align: center;
padding: 30px;
}
#loading.visibile { display: block; }
.spinner {
width: 40px; height: 40px;
border: 4px solid #e8ddd0;
border-top: 4px solid #8b5a2b;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin: 0 auto 12px;
}
@keyframes spin { to { transform: rotate(360deg); } }
#loading p { color: #94867a; font-size: 0.9em; }
/* ─── SEZIONI ─── */
.sezione {
background: linear-gradient(145deg, #fdf8f0, #faf5ed);
border: 1px solid #e8ddd0;
border-radius: 14px;
padding: 26px;
margin-bottom: 24px;
}
.sezione h2 {
font-size: 1.15em;
margin-bottom: 18px;
color: #8b5a2b;
font-weight: 700;
}
/* ─── FORM ─── */
.form-riga {
display: flex;
gap: 12px;
margin-bottom: 12px;
flex-wrap: wrap;
}
.form-riga input,
.form-riga select {
flex: 1;
min-width: 140px;
padding: 11px 16px;
border: 1.5px solid #e0d5c7;
border-radius: 10px;
font-size: 0.95em;
font-family: inherit;
background: rgba(255,255,255,0.85);
color: #2d2418;
transition: all 0.25s ease;
}
.form-riga input:focus,
.form-riga select:focus {
outline: none;
border-color: #8b5a2b;
box-shadow: 0 0 0 3px rgba(139,90,43,0.1);
background: #fff;
}
.form-riga input::placeholder { color: #b8a898; }
/* ─── BOTTONI ─── */
.bottoni {
display: flex;
gap: 10px;
margin-bottom: 18px;
flex-wrap: wrap;
}
button {
padding: 10px 22px;
border: none;
border-radius: 10px;
font-size: 0.9em;
font-weight: 600;
font-family: inherit;
cursor: pointer;
transition: all 0.25s ease;
}
.btn-primario {
background: linear-gradient(135deg, #8b5a2b, #a0522d);
color: white;
box-shadow: 0 2px 10px rgba(139,90,43,0.25);
}
.btn-primario:hover {
box-shadow: 0 4px 16px rgba(139,90,43,0.35);
transform: translateY(-1px);
}
.btn-secondario {
background: rgba(255,255,255,0.7);
color: #5d4e37;
border: 1.5px solid #e0d5c7;
}
.btn-secondario:hover {
background: #fff;
border-color: #8b5a2b;
transform: translateY(-1px);
}
.btn-danger {
background: #fff5f5;
color: #c53030;
border: 1.5px solid #fcc;
padding: 6px 14px;
font-size: 0.85em;
border-radius: 8px;
}
.btn-danger:hover {
background: #fed7d7;
border-color: #c53030;
}
/* ─── LISTA LIBRI ─── */
#lista-libri {
list-style: none;
padding: 0;
margin-top: 16px;
}
#lista-libri li {
background: #fff;
border: 1px solid #e8ddd0;
border-left: 4px solid #8b5a2b;
border-radius: 0 10px 10px 0;
padding: 14px 18px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.2s ease;
}
#lista-libri li:hover {
background: #fdf8f0;
box-shadow: 0 2px 8px rgba(139,90,43,0.08);
transform: translateX(4px);
}
#lista-libri li .info-libro { flex: 1; }
#lista-libri li .titolo-libro {
font-weight: 700;
color: #8b5a2b;
font-size: 1.05em;
}
#lista-libri li .dettagli-libro {
color: #94867a;
font-size: 0.85em;
margin-top: 2px;
}
.letto-badge {
display: inline-block;
padding: 2px 10px;
border-radius: 12px;
font-size: 0.78em;
font-weight: 600;
}
.letto-si { background: #d1fae5; color: #065f46; }
.letto-no { background: #fee2e2; color: #991b1b; }
/* ─── DETTAGLIO ─── */
#dettaglio-libro {
background: #fff;
border: 1px solid #e8ddd0;
border-radius: 12px;
padding: 22px;
margin-top: 16px;
display: none;
}
#dettaglio-libro.visibile {
display: block;
animation: slideIn 0.3s ease;
}
#dettaglio-libro h3 {
color: #8b5a2b;
font-size: 1.2em;
margin-bottom: 8px;
}
#dettaglio-libro .info {
color: #5d4e37;
font-size: 0.95em;
margin-bottom: 4px;
}
/* ─── MESSAGGIO ─── */
#messaggio {
display: none;
padding: 14px 18px;
border-radius: 10px;
margin: 16px 0;
font-weight: 600;
text-align: center;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
.msg-successo {
background: linear-gradient(135deg, #d1fae5, #a7f3d0);
color: #065f46;
border: 1px solid #6ee7b7;
}
.msg-errore {
background: linear-gradient(135deg, #fee2e2, #fecaca);
color: #991b1b;
border: 1px solid #fca5a5;
}
/* ─── EMPTY STATE ─── */
.empty-state {
text-align: center;
padding: 40px 20px;
color: #94867a;
}
.empty-state .emoji { font-size: 3em; margin-bottom: 10px; }
</style>
</head>
<body>
<div class="container">
<h1>📚 La Biblioteca Online</h1>
<p class="sottotitolo">Ripasso API — fetch, async/await, CRUD</p>
<div class="avviso-server">
⚠️ Prima di iniziare, assicurati che il server sia avviato e funzionante.
</div>
<!-- MESSAGGIO -->
<div id="messaggio"></div>
<!-- STATISTICHE -->
<div id="stats">
<div class="stat-box">
<div class="label">Totale Libri</div>
<div class="valore" id="stat-totale">0</div>
</div>
<div class="stat-box">
<div class="label">Già Letti</div>
<div class="valore" id="stat-letti">0</div>
</div>
<div class="stat-box">
<div class="label">Da Leggere</div>
<div class="valore" id="stat-da-leggere">0</div>
</div>
</div>
<!-- LOADING -->
<div id="loading">
<div class="spinner"></div>
<p>Caricamento in corso...</p>
</div>
<!-- SEZIONE 1: CARICA LISTA -->
<div class="sezione" id="sezione-lista">
<h2>📋 Lista Libri</h2>
<div class="bottoni">
<button class="btn-primario" id="btn-carica-tutti">📥 Carica Tutti i Libri</button>
</div>
<ul id="lista-libri">
<div class="empty-state">
<div class="emoji">📚</div>
<p>Clicca "Carica Tutti i Libri" per iniziare</p>
</div>
</ul>
</div>
<!-- SEZIONE 2: DETTAGLIO SINGOLO -->
<div class="sezione" id="sezione-dettaglio">
<h2>🔎 Cerca Libro per ID</h2>
<div class="form-riga">
<input type="number" id="input-id" placeholder="Inserisci ID libro (1-20)" min="1" max="20">
<button class="btn-primario" id="btn-dettaglio">Cerca</button>
</div>
<div id="dettaglio-libro"></div>
</div>
<!-- SEZIONE 3: AGGIUNGI LIBRO -->
<div class="sezione" id="sezione-aggiungi">
<h2> Aggiungi Nuovo Libro</h2>
<div class="form-riga">
<input type="text" id="input-titolo" placeholder="Titolo">
<input type="text" id="input-autore" placeholder="Autore">
</div>
<div class="form-riga">
<select id="select-genere">
<option value="">-- Genere --</option>
<option value="Fantasy">🧙 Fantasy</option>
<option value="Giallo">🔍 Giallo</option>
<option value="Fantascienza">🚀 Fantascienza</option>
<option value="Narrativa">📖 Narrativa</option>
<option value="Horror">👻 Horror</option>
<option value="Romanzo">💕 Romanzo</option>
<option value="Umorismo">😂 Umorismo</option>
</select>
<input type="number" id="input-pagine" placeholder="Numero pagine" min="1">
</div>
<button class="btn-primario" id="btn-aggiungi"> Aggiungi Libro</button>
</div>
<!-- SEZIONE 4: SEGNA COME LETTO -->
<div class="sezione" id="sezione-modifica">
<h2>✏️ Segna come Letto / Non Letto</h2>
<div class="form-riga">
<input type="number" id="input-modifica-id" placeholder="ID del libro" min="1">
<select id="select-letto">
<option value="true">✅ Letto</option>
<option value="false">❌ Non letto</option>
</select>
<button class="btn-primario" id="btn-modifica">Aggiorna</button>
</div>
</div>
<!-- SEZIONE 5: ELIMINA -->
<div class="sezione" id="sezione-elimina">
<h2>🗑️ Elimina Libro</h2>
<div class="form-riga">
<input type="number" id="input-elimina-id" placeholder="ID del libro da eliminare" min="1">
<button class="btn-danger" id="btn-elimina">🗑️ Elimina</button>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>