Fix Ranking code step1
This commit is contained in:
295
supervisor/html/ranking.html
Normal file
295
supervisor/html/ranking.html
Normal file
@ -0,0 +1,295 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ランキング</title>
|
||||
<style>
|
||||
.box2 { margin: 10px; padding: 10px; border: 1px solid #ccc; }
|
||||
.best3 { margin: 5px 0; padding: 5px; }
|
||||
.span2 { margin-left: 20px; }
|
||||
.span3 { font-weight: bold; }
|
||||
.span6 { display: inline-block; width: 30px; }
|
||||
.black { background-color: #f0f0f0; padding: 10px; }
|
||||
.arrow { margin: 10px 0; }
|
||||
.arrow2 { margin: 10px 0; }
|
||||
.disqualified { color: #999; }
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.status-retired {
|
||||
background-color: #ffebee;
|
||||
color: #c62828;
|
||||
}
|
||||
.status-finished {
|
||||
background-color: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
}
|
||||
.status-running {
|
||||
background-color: #e3f2fd;
|
||||
color: #1565c0;
|
||||
}
|
||||
.disqualified-header {
|
||||
margin-top: 20px;
|
||||
padding: 5px;
|
||||
background-color: #f0f0f0;
|
||||
border-left: 4px solid #999;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="ranking">
|
||||
<div class="black">
|
||||
<span class="span3"></span>
|
||||
<span style="font-size: 24px">ランキング</span>
|
||||
</div>
|
||||
|
||||
<div class="arrow">
|
||||
<select id="eventSelect">
|
||||
<option value="">イベントを選択してください</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="arrow2">
|
||||
<select id="classSelect" disabled>
|
||||
<option value="">クラスを選択してください</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button id="toggleButton" onclick="toggleView()">TOP3表示</button>
|
||||
|
||||
<!-- 通常のランキング表示 -->
|
||||
<div id="normalRanking">
|
||||
<div id="teamList"></div>
|
||||
</div>
|
||||
|
||||
<!-- TOP3のランキング表示 -->
|
||||
<div id="top3Ranking" style="display: none;">
|
||||
<div id="top3List"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let showTop3 = false;
|
||||
|
||||
// ページ読み込み時にイベント一覧を取得
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await loadEvents();
|
||||
setupEventListeners();
|
||||
});
|
||||
|
||||
// 日時のフォーマット
|
||||
function formatDateTime(dateStr) {
|
||||
if (!dateStr) return '未ゴール';
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString('ja-JP', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
// イベントリスナーの設定
|
||||
function setupEventListeners() {
|
||||
document.getElementById('eventSelect').addEventListener('change', async function() {
|
||||
const eventCode = this.value;
|
||||
if (eventCode) {
|
||||
await loadClasses(eventCode);
|
||||
await updateRankings();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('classSelect').addEventListener('change', async function() {
|
||||
await updateRankings();
|
||||
});
|
||||
}
|
||||
|
||||
// イベント一覧の取得と表示
|
||||
async function loadEvents() {
|
||||
try {
|
||||
const response = await fetch('/api/newevent2/');
|
||||
const events = await response.json();
|
||||
const select = document.getElementById('eventSelect');
|
||||
|
||||
events.filter(event => event.public).forEach(event => {
|
||||
const option = document.createElement('option');
|
||||
option.value = event.event_name;
|
||||
option.textContent = event.event_name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('イベント一覧の取得に失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// クラス一覧の取得と表示
|
||||
async function loadClasses(eventCode) {
|
||||
try {
|
||||
const response = await fetch(`/api/categories/${eventCode}/`);
|
||||
const classes = await response.json();
|
||||
const select = document.getElementById('classSelect');
|
||||
|
||||
// 既存のオプションをクリア
|
||||
select.innerHTML = '<option value="">クラスを選択してください</option>';
|
||||
select.disabled = false;
|
||||
|
||||
classes.forEach(cls => {
|
||||
const option = document.createElement('option');
|
||||
option.value = cls.category_name;
|
||||
option.textContent = cls.category_name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('クラス一覧の取得に失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ランキングの更新
|
||||
async function updateRankings() {
|
||||
const eventCode = document.getElementById('eventSelect').value;
|
||||
if (!eventCode) return;
|
||||
|
||||
try {
|
||||
if (showTop3) {
|
||||
await loadTop3Rankings(eventCode);
|
||||
} else {
|
||||
const classCode = document.getElementById('classSelect').value;
|
||||
if (classCode) {
|
||||
await loadClassRankings(eventCode, classCode);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('ランキングの取得に失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// クラス別ランキングの表示
|
||||
async function loadClassRankings(eventCode, classCode) {
|
||||
const response = await fetch(`/api/rankings/${eventCode}/${classCode}/`);
|
||||
const rankingData = await response.json();
|
||||
displayNormalRankings(rankingData);
|
||||
}
|
||||
|
||||
// TOP3ランキングの表示
|
||||
async function loadTop3Rankings(eventCode) {
|
||||
const response = await fetch(`/api/rankings/top3/${eventCode}/`);
|
||||
const rankingData = await response.json();
|
||||
displayTop3Rankings(rankingData);
|
||||
}
|
||||
|
||||
// 通常ランキングの表示処理
|
||||
function displayNormalRankings(rankingData) {
|
||||
const container = document.getElementById('teamList');
|
||||
container.innerHTML = '';
|
||||
|
||||
// 有効なランキングの表示
|
||||
rankingData.rankings.forEach((team, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'best';
|
||||
const statusClass = team.status === '棄権' ? 'status-retired' :
|
||||
team.status === 'ゴール' ? 'status-finished' : 'status-running';
|
||||
div.innerHTML = `
|
||||
${index + 1}. ${team.team_name} (${team.zekken_number})
|
||||
<span class="span2">合計得点:${team.final_point}</span>
|
||||
<span class="span2">獲得ポイント:${team.point}</span>
|
||||
<span class="span2">遅刻減点: ${team.late_point}</span>
|
||||
<span class="span2">最終更新: ${formatDateTime(team.last_checkin)}</span>
|
||||
<span class="status ${statusClass}">(${team.status})</span>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
// 失格チームの表示(見出し)
|
||||
if (rankingData.disqualified && rankingData.disqualified.length > 0) {
|
||||
const disqHeader = document.createElement('div');
|
||||
disqHeader.className = 'disqualified-header';
|
||||
disqHeader.innerHTML = '<h3>失格チーム</h3>';
|
||||
container.appendChild(disqHeader);
|
||||
|
||||
// 失格チームのリスト
|
||||
rankingData.disqualified.forEach(team => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'best disqualified';
|
||||
div.innerHTML = `
|
||||
${team.team_name} (${team.zekken_number})
|
||||
<span class="span2">獲得ポイント:${team.point}</span>
|
||||
<span class="span2">理由: ${team.reason}</span>
|
||||
<span class="span2">ゴール: ${formatDateTime(team.goal_time)}</span>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TOP3ランキングの表示処理
|
||||
function displayTop3Rankings(rankingData) {
|
||||
const container = document.getElementById('top3List');
|
||||
container.innerHTML = '';
|
||||
|
||||
Object.entries(rankingData).forEach(([category, data]) => {
|
||||
const categoryDiv = document.createElement('div');
|
||||
categoryDiv.className = 'box2';
|
||||
|
||||
const categoryHeader = document.createElement('h3');
|
||||
categoryHeader.textContent = category;
|
||||
categoryDiv.appendChild(categoryHeader);
|
||||
|
||||
// 有効なランキングの表示
|
||||
data.rankings.forEach((team, index) => {
|
||||
const teamDiv = document.createElement('div');
|
||||
teamDiv.className = 'best3';
|
||||
const statusClass = team.status === '棄権' ? 'status-retired' :
|
||||
team.status === 'ゴール' ? 'status-finished' : 'status-running';
|
||||
teamDiv.innerHTML = `
|
||||
<span class="span6">${index + 1}</span>
|
||||
${team.team_name} (${team.zekken_number}) <span class="status ${statusClass}">(${team.status})</span><br>
|
||||
合計得点:${team.final_point} (獲得:${team.point} 減点:${team.late_point})<br>
|
||||
最終更新: ${formatDateTime(team.last_checkin)}
|
||||
`;
|
||||
categoryDiv.appendChild(teamDiv);
|
||||
});
|
||||
|
||||
// 失格チームの表示
|
||||
if (data.disqualified && data.disqualified.length > 0) {
|
||||
const disqHeader = document.createElement('div');
|
||||
disqHeader.className = 'disqualified-header';
|
||||
disqHeader.innerHTML = '<h4>失格チーム</h4>';
|
||||
categoryDiv.appendChild(disqHeader);
|
||||
|
||||
data.disqualified.forEach(team => {
|
||||
const teamDiv = document.createElement('div');
|
||||
teamDiv.className = 'best3 disqualified';
|
||||
teamDiv.innerHTML = `
|
||||
${team.team_name} (${team.zekken_number})<br>
|
||||
獲得ポイント:${team.point} 理由:${team.reason}<br>
|
||||
ゴール: ${formatDateTime(team.goal_time)}
|
||||
`;
|
||||
categoryDiv.appendChild(teamDiv);
|
||||
});
|
||||
}
|
||||
|
||||
container.appendChild(categoryDiv);
|
||||
});
|
||||
}
|
||||
|
||||
// 表示モードの切り替え
|
||||
function toggleView() {
|
||||
showTop3 = !showTop3;
|
||||
const button = document.getElementById('toggleButton');
|
||||
const normalRanking = document.getElementById('normalRanking');
|
||||
const top3Ranking = document.getElementById('top3Ranking');
|
||||
const classSelect = document.getElementById('classSelect');
|
||||
|
||||
button.textContent = showTop3 ? 'クラス別ランキング' : 'TOP3表示';
|
||||
normalRanking.style.display = showTop3 ? 'none' : 'block';
|
||||
top3Ranking.style.display = showTop3 ? 'block' : 'none';
|
||||
classSelect.disabled = showTop3;
|
||||
|
||||
updateRankings();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user