Iniziamo a valorizzare la pagina delle statistiche. La prima cosa che voglio aggiungere è per ogni set quante carte ho e in percentuale quante me ne mancano.
in main.py aggiungere la funzione:
@app.get("/stats/sets")
def get_set_stats(user_id: int):
with engine.connect() as conn:
result = conn.execute(
text("""
SELECT
s.id,
s.name,
s.release_date,
s.total_cards,
COUNT(
DISTINCT CASE
WHEN col.id IS NOT NULL
THEN c.id
END
) AS owned_cards,
ROUND(
COUNT(
DISTINCT CASE
WHEN col.id IS NOT NULL
THEN c.id
END
) * 100.0 / s.total_cards,
1
) AS completion
FROM sets s
LEFT JOIN cards c
ON c.set_id = s.id
LEFT JOIN collection col
ON col.card_id = c.id
AND col.user_id = :user_id
GROUP BY
s.id,
s.name,
s.release_date,
s.total_cards
ORDER BY
completion DESC,
owned_cards DESC,
s.release_date DESC,
s.name
"""),
{
"user_id": user_id
}
)
return result.mappings().all()
In index.html sostituisci <button>Statistiche</button> con:
<button onclick="window.location='/static/dashboard.html'">
Statistiche
</button>
Creare da zero o sostiture completamente il contenuto del file dashboard.html con:
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8">
<title>Statistiche Collezione</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="header">
<div class="logo">Pokemon DB</div>
<div class="user">Paolo ▼</div>
</div>
<div class="container">
<div class="toolbar">
/
← Torna alla collezione
</a>
</div>
<div class="stats-title-card">
<h1>Statistiche</h1>
<p>
Stato della collezione Pokémon
</p>
</div>
<!-- RIEPILOGO GENERALE -->
<div class="stats-summary">
<h2>Totale collezione</h2>
<div class="summary-line">
<span id="globalOwned">
3
</span>
/
<span id="globalTotal">
378
</span>
carte
<span class="summary-percent"
id="globalPercent">
0,8%
</span>
</div>
<div class="progress">
<div
id="globalBar"
class="progress-fill"
style="width:0.8%">
</div>
</div>
</div>
<!-- ELENCO SET -->
<div id="statsContainer">
<!-- Generato da JavaScript -->
</div>
<div class="toolbar">
/
← Torna alla collezione
</a>
</div>
</div>
<script>
async function loadStats() {
const response =
await fetch(
'/stats/sets?user_id=1'
);
const stats =
await response.json();
const container =
document.getElementById(
'statsContainer'
);
container.innerHTML = '';
let globalOwned = 0;
let globalTotal = 0;
stats.forEach(set => {
globalOwned +=
set.owned_cards;
globalTotal +=
set.total_cards;
const card =
document.createElement('div');
card.className =
'set-stat-card';
card.innerHTML = `
<div class="set-stat-header">
<div class="set-name">
${set.name}
</div>
<div class="set-stats">
${set.owned_cards}
/
${set.total_cards}
|
${set.completion}%
</div>
</div>
<div class="progress">
<div
class="progress-fill"
style="width:${set.completion}%">
</div>
</div>
`;
container.appendChild(card);
});
const globalPercent =
(
globalOwned * 100
/ globalTotal
).toFixed(1);
document.getElementById(
'globalOwned'
).textContent =
globalOwned;
document.getElementById(
'globalTotal'
).textContent =
globalTotal;
document.getElementById(
'globalPercent'
).textContent =
globalPercent + '%';
document.getElementById(
'globalBar'
).style.width =
globalPercent + '%';
}
loadStats();
</script>
</body>
</html>
Aggiungere in calce al css:
.back-link {
text-decoration: none;
font-weight: bold;
color: var(--pokemon-blue);
}
.stats-title-card,
.stats-summary,
.set-stat-card {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 18px;
box-shadow:
0 4px 12px rgba(0,0,0,0.10);
}
.summary-line {
font-size: 28px;
font-weight: bold;
margin-bottom: 15px;
}
.summary-percent {
float: right;
}
.set-stat-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.set-name {
font-size: 26px;
font-weight: bold;
}
.set-stats {
font-size: 24px;
font-weight: bold;
}
.progress {
height: 20px;
background: #dcdfe6;
border-radius: 20px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background:
linear-gradient(
90deg,
#d40000,
#ff0000
);
border-radius: 20px;
}
Migliorerei anche un paio di cose:
- Invertire la posizione della barra dei pulsanti con la scelta del set
- Il set che si carica in automatico non è più de default Pitch Black ma il più recente
Editare index.html e sostituire il blocco <div class="container">
interamente con questo:
<div class="container">
<div class="toolbar">
<button>Ricerca</button>
<button onclick="window.location='/static/dashboard.html'">Statistiche</button>
</div>
<div class="set-info"><label>Set: </label><select id="setSelect"></select></div>
<div class="set-info"><h2 id="setTitle"></h2></div>
<div id="card-grid" class="card-grid"></div>
</div>
Sostiture poi tutta la funzione async function loadSets() con questa:
async function loadSets() {
const response = await fetch('/sets');
const sets = await response.json();
const select =
document.getElementById('setSelect');
select.innerHTML = '';
sets.forEach((set, index) => {
const option =
document.createElement('option');
option.value = set.id;
const year =
set.release_date.substring(0, 4);
option.textContent =
`${year} - ${set.name}`;
if (index === 0) {
option.selected = true;
document.getElementById(
'setTitle'
).textContent =
`${year} - ${set.name}`;
}
select.appendChild(option);
});
select.addEventListener('change', () => {
document.getElementById(
'setTitle'
).textContent =
select.options[
select.selectedIndex
].text;
loadCards(select.value);
});
if (sets.length > 0) {
loadCards(sets[0].id);
}
}
<—– Articolo Parte 10 | Articolo Parte 11 —–>
Lascia un commento
Devi essere connesso per inviare un commento.