Files
RushStatistics/frontend/src/vues/ServerStats.vue
2024-06-12 00:05:27 -07:00

286 lines
6.8 KiB
Vue

<template>
<div class="d-flex flex-column">
<v-select class="date-selector" v-model="dateSelect" variant="underlined" :items="dateItems">
</v-select>
<div class="stat-bar d-flex flex-row justify-space-around">
<div class="d-flex flex-column">
<v-label class="title">Completed Games</v-label>
<v-label>{{ gameStats.Complete }}</v-label>
</div>
<div class="d-flex flex-column">
<v-label class="title">Postponed Games</v-label>
<v-label>{{ gameStats.Postponed }}</v-label>
</div>
<div class="d-flex flex-column">
<v-label class="title">Events / Fixes</v-label>
<v-label>{{ gameStats.Events }} / {{ gameStats.Fixes }}</v-label>
</div>
<div class="d-flex flex-column">
<v-label class="title">Total / Average EB</v-label>
<v-label
>{{ Math.floor(gameStats.TotalEB) }} / {{ Math.floor(gameStats.AverageEB) }}</v-label
>
</div>
<div class="d-flex flex-column">
<v-label class="title">Total / Average IP</v-label>
<v-label
>{{ Math.floor(gameStats.TotalIP) }} / {{ Math.floor(gameStats.AverageIP) }}</v-label
>
</div>
</div>
<div class="role-table d-flex flex-row">
<v-table class="flex-1-1">
<thead>
<tr>
<th class="text-left">Role</th>
<th class="text-left">
Characters
</th>
<th class="text-left">App Count</th>
<th class="text-left">Games Played</th>
<th class="text-left">Pick Rate %</th>
<th class="text-left">% Games With Role</th>
<th cladd="text-ledt">Apps Per Game</th>
</tr>
</thead>
<tbody>
<tr v-for="(roleStat, roleName) in roleStats">
<td>{{ roleName }}</td>
<td class="text-left">
{{ roleStat.active }}
</td>
<td class="text-left">{{ roleStat.apps }}</td>
<td class="text-left">{{ roleStat.picks }}</td>
<td class="text-left">{{ Math.floor((roleStat.picks / roleStat.apps) * 100) }}%</td>
<td class="text-left">
{{ Math.floor((roleStat.picks / gameStats.Complete) * 100) }}%
</td>
<td class="text-left">
{{ Math.round((roleStat.apps / gameStats.Complete) * 100) / 100 }}
</td>
</tr>
</tbody>
</v-table>
<v-sheet class="chart d-flex flex-column">
<v-select v-model="chartSelect" :items="chartItems"></v-select>
<canvas id="piechart"></canvas>
<div class="flex-1-1"></div>
</v-sheet>
</div>
</div>
</template>
<style>
.date-selector {
margin-left: 15px;
margin-right: 15px;
}
.graph-selector {
margin-left: 15px;
margin-right: 15px;
}
.stat-bar {
padding: 10px;
margin-left: 15px;
margin-right: 15px;
margin-bottom: 20px;
.title {
font-weight: bold;
}
}
.role-table {
margin-left: 15px;
margin-right: 15px;
}
.chart {
margin-left: 20px;
width: 25%;
width: 450px;
}
</style>
<script setup lang="ts">
import { GameStats, RoleStats } from '../types'
import { onMounted, watch, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import Chart from 'chart.js/auto'
import axios from 'axios'
type ChartType = 'gametypes' | 'apps' | 'picks' | 'active'
const route = useRoute()
const router = useRouter()
const dateSelect = ref(-1)
const dateItems = buildDateItems()
const chartSelect = ref<ChartType>('active')
const chartItems = [
{ title: 'Game Types', value: 'gametypes' },
{ title: 'Active Characters', value: 'active' },
{ title: 'Role Picks', value: 'picks' },
{ title: 'Role Applications', value: 'apps' }
]
const gameStats = ref<GameStats>({
Complete: 0,
Postponed: 0,
Pending: 0,
Fixes: 0,
Events: 0,
AverageIP: 0,
AverageEB: 0,
TotalIP: 0,
TotalEB: 0
})
const roleStats = ref<RoleStats>({
Fixer: {
apps: 0,
picks: 0,
active: 0
},
Tech: {
apps: 0,
picks: 0,
active: 0
},
Medtech: {
apps: 0,
picks: 0,
active: 0
},
Media: {
apps: 0,
picks: 0,
active: 0
},
Netrunner: {
apps: 0,
picks: 0,
active: 0
},
Solo: {
apps: 0,
picks: 0,
active: 0
},
Nomad: {
apps: 0,
picks: 0,
active: 0
},
Exec: {
apps: 0,
picks: 0,
active: 0
},
Lawman: {
apps: 0,
picks: 0,
active: 0
},
Rocker: {
apps: 0,
picks: 0,
active: 0
}
})
let chart: Chart<'pie', number[], string>
async function loadData() {
const gameStatsResponse = await axios.post('/api/serverstats/gamestats', {
monthId: dateSelect.value
})
gameStats.value = gameStatsResponse.data
const roleStatsResponse = await axios.post('/api/serverstats/rolestats', {
monthId: dateSelect.value
})
roleStats.value = roleStatsResponse.data
}
function buildDateItems() {
const items = [{ title: 'All Time', value: -1 }]
const date = new Date()
while (date.getUTCFullYear() != 2023 || date.getUTCMonth() != 0) {
items.push(buildDateEntry(date))
date.setUTCMonth(date.getUTCMonth() - 1)
}
items.push(buildDateEntry(date))
return items
}
function buildDateEntry(date: Date) {
const monthId = dateToMonthId(date)
return {
title: date.toLocaleString('en-us', { month: 'short', year: 'numeric' }),
value: monthId
}
}
function dateToMonthId(date: Date) {
return (date.getUTCFullYear() - 2023) * 12 + date.getUTCMonth()
}
function updateChart(stats: RoleStats, tag: ChartType) {
if (tag === 'gametypes') {
chart.data = {
labels: ['Standard', 'Postponed', 'Pending', 'Event', 'Fix'],
datasets: [
{
label: 'Game Type',
data: [
gameStats.value.Complete - gameStats.value.Events - gameStats.value.Fixes,
gameStats.value.Postponed,
gameStats.value.Pending,
gameStats.value.Events,
gameStats.value.Fixes
]
}
]
}
} else {
chart.data = {
labels: Object.keys(stats),
datasets: [
{
label: tag,
data: Object.values(stats).map((p) => p[tag])
}
]
}
}
chart.update()
}
watch(dateSelect, async (newValue, oldValue) => {
router.replace({ query: { monthId: dateSelect.value } })
loadData()
})
watch(roleStats, async (newValue, oldValue) => {
updateChart(newValue, chartSelect.value)
})
watch(chartSelect, async (newValue: ChartType, oldValue: ChartType) => {
updateChart(roleStats.value, newValue)
})
onMounted(async () => {
if (!route.query.monthId) {
router.replace({ query: { monthId: -1 } })
} else {
dateSelect.value = Number(route.query.monthId)
}
loadData()
chart = new Chart(document.getElementById('piechart')! as HTMLCanvasElement, {
type: 'pie',
data: {
labels: [],
datasets: []
}
})
})
</script>