ajout d'historique

This commit is contained in:
H3XploR
2026-03-09 00:15:01 +01:00
parent 0c8b6a663a
commit ad4becc38f
7 changed files with 143 additions and 2 deletions
+20
View File
@@ -56,6 +56,17 @@ async function runMigrations()
END IF;
END $$;
`);
// Create tetris_game_history table if not exists
await pool.query(`
CREATE TABLE IF NOT EXISTS tetris_game_history (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id) ON DELETE CASCADE,
score INT NOT NULL DEFAULT 0,
game_type VARCHAR(10) NOT NULL DEFAULT 'solo',
result VARCHAR(10) DEFAULT NULL,
played_at TIMESTAMP DEFAULT NOW()
);
`);
console.log('Migrations completed!');
}
catch (err)
@@ -147,6 +158,15 @@ async function createTables()
started_at TIMESTAMP DEFAULT NOW(),
ended_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS tetris_game_history (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id) ON DELETE CASCADE,
score INT NOT NULL DEFAULT 0,
game_type VARCHAR(10) NOT NULL DEFAULT 'solo',
result VARCHAR(10) DEFAULT NULL,
played_at TIMESTAMP DEFAULT NOW()
);
`);
console.log('Tables created!');
}
@@ -43,7 +43,7 @@ router.get('/leaderboard', authenticateToken, async (req, res) => {
}
});
// Save tetris score (solo or duel) — updates best score if higher
// Save tetris score (solo) — updates best score if higher + saves to history
router.post('/tetris/score', authenticateToken, async (req, res) => {
try {
const { score } = req.body;
@@ -52,6 +52,7 @@ router.post('/tetris/score', authenticateToken, async (req, res) => {
}
const bestScore = await playerStatsService.updateTetrisBestScore(req.user.userId, score);
await playerStatsService.incrementTetrisGamesPlayed(req.user.userId);
await playerStatsService.addTetrisGameHistory(req.user.userId, score, 'solo', null);
res.json({ bestScore });
} catch (err) {
console.error('Error saving tetris score:', err);
@@ -94,6 +95,17 @@ router.get('/tetris/rank/score', authenticateToken, async (req, res) => {
}
});
// Get current user's tetris game history (last 15)
router.get('/tetris/history', authenticateToken, async (req, res) => {
try {
const history = await playerStatsService.getTetrisGameHistory(req.user.userId);
res.json(history);
} catch (err) {
console.error('Error getting tetris history:', err);
res.status(500).json({ error: 'Server error' });
}
});
// Current user's rank by tetris duel wins
router.get('/tetris/rank/wins', authenticateToken, async (req, res) => {
try {
@@ -129,6 +129,38 @@ async function getTetrisDuelWinsLeaderboard(limit = 10) {
return result.rows;
}
// Add a game to tetris history (keep max 15 per user)
async function addTetrisGameHistory(userId, score, gameType = 'solo', result = null) {
await query(
`INSERT INTO tetris_game_history (user_id, score, game_type, result) VALUES ($1, $2, $3, $4)`,
[userId, score, gameType, result]
);
// Keep only the 15 most recent entries
await query(
`DELETE FROM tetris_game_history
WHERE id IN (
SELECT id FROM tetris_game_history
WHERE user_id = $1
ORDER BY played_at DESC
OFFSET 15
)`,
[userId]
);
}
// Get the last 15 games for a user
async function getTetrisGameHistory(userId) {
const result = await query(
`SELECT id, score, game_type, result, played_at
FROM tetris_game_history
WHERE user_id = $1
ORDER BY played_at DESC
LIMIT 15`,
[userId]
);
return result.rows;
}
// Rank of a user by tetris best score (1 = best)
async function getTetrisScoreRank(userId) {
const result = await query(
@@ -166,5 +198,7 @@ export default {
getTetrisBestScoreLeaderboard,
getTetrisDuelWinsLeaderboard,
getTetrisScoreRank,
getTetrisDuelWinsRank
getTetrisDuelWinsRank,
addTetrisGameHistory,
getTetrisGameHistory
};
@@ -721,6 +721,7 @@ function setupSocketIO(io)
// Relay pur : grid-update → adversaire uniquement
socket.on('tetris:grid-update', (data) => {
if (data.score !== undefined) socket.tetrisLastScore = data.score;
_tetrisRelayToOpponent(socket, 'tetris:grid-update', data);
});
@@ -779,6 +780,7 @@ function setupSocketIO(io)
try {
await playerStatsService.updateTetrisBestScore(loserId, data.score || 0);
await playerStatsService.incrementTetrisGamesPlayed(loserId);
await playerStatsService.addTetrisGameHistory(loserId, data.score || 0, 'duel', 'loss');
} catch (err) {
console.error('Error saving tetris loser stats:', err);
}
@@ -793,6 +795,8 @@ function setupSocketIO(io)
try {
await playerStatsService.incrementTetrisWins(s.user.userId);
await playerStatsService.incrementTetrisGamesPlayed(s.user.userId);
const winnerScore = s.tetrisLastScore || 0;
await playerStatsService.addTetrisGameHistory(s.user.userId, winnerScore, 'duel', 'win');
} catch (err) {
console.error('Error saving tetris winner stats:', err);
}
@@ -524,4 +524,13 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
width: 30px;
}
.hist-win {
color: var(--accent);
font-weight: bold;
}
.hist-loss {
color: var(--accent2);
}
body { overflow-y: auto; }
@@ -127,6 +127,7 @@
<div class="leaderboard-tabs">
<button class="lb-tab lb-tab--active" data-tab="scores">Meilleurs scores</button>
<button class="lb-tab" data-tab="wins">Duels gagnés</button>
<button class="lb-tab" data-tab="history">Mes parties</button>
</div>
<div id="lb-scores" class="lb-content lb-content--active">
@@ -150,6 +151,17 @@
</tbody>
</table>
</div>
<div id="lb-history" class="lb-content">
<table class="lb-table">
<thead>
<tr><th>#</th><th>Date</th><th>Type</th><th>Score</th><th>Résultat</th></tr>
</thead>
<tbody id="lb-history-body">
<tr><td colspan="5">Chargement…</td></tr>
</tbody>
</table>
</div>
</div>
+50
View File
@@ -202,6 +202,7 @@ const game = new Tetris(
updateButtons();
showOverlay('GAME OVER', score);
loadLeaderboards();
loadGameHistory();
},
// onBlockPlaced — relay duel
(grid) => {
@@ -267,6 +268,53 @@ if (btnRestart) {
});
}
// ─────────────────────────────────────────────
// GAME HISTORY
// ─────────────────────────────────────────────
async function loadGameHistory() {
const token = localStorage.getItem('auth_token');
if (!token) return;
try {
const res = await fetch('/api/stats/tetris/history', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!res.ok) return;
const history = await res.json();
renderGameHistory(history);
} catch (err) {
console.error('Erreur chargement historique:', err);
}
}
function renderGameHistory(history) {
const tbody = document.getElementById('lb-history-body');
if (!tbody) return;
if (!history.length) {
tbody.innerHTML = '<tr><td colspan="5">Aucune partie jouée</td></tr>';
return;
}
tbody.innerHTML = history.map((entry, i) => {
const date = new Date(entry.played_at).toLocaleDateString('fr-FR', {
day: '2-digit', month: '2-digit', year: '2-digit',
hour: '2-digit', minute: '2-digit'
});
const type = entry.game_type === 'duel' ? 'Duel' : 'Solo';
let resultHtml = '—';
if (entry.result === 'win') resultHtml = '<span class="hist-win">Victoire</span>';
if (entry.result === 'loss') resultHtml = '<span class="hist-loss">Défaite</span>';
return `<tr>
<td>${i + 1}</td>
<td>${date}</td>
<td>${type}</td>
<td>${entry.score}</td>
<td>${resultHtml}</td>
</tr>`;
}).join('');
}
// ─────────────────────────────────────────────
// LEADERBOARDS
// ─────────────────────────────────────────────
@@ -349,8 +397,10 @@ document.querySelectorAll('.lb-tab').forEach(tab => {
document.querySelectorAll('.lb-content').forEach(c => c.classList.remove('lb-content--active'));
tab.classList.add('lb-tab--active');
document.getElementById(`lb-${tab.dataset.tab}`).classList.add('lb-content--active');
if (tab.dataset.tab === 'history') loadGameHistory();
});
});
// Chargement initial des leaderboards
loadLeaderboards();
loadGameHistory();