Files
rogaining_srv/supervisor/html/ranking.html
2024-11-12 07:19:18 +09:00

295 lines
12 KiB
HTML

<!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>