Skip to content

Commit

Permalink
feat (WIP): refactor reports tab, add post-game stats
Browse files Browse the repository at this point in the history
  • Loading branch information
kellytea authored and alee committed Oct 16, 2024
1 parent 41b040a commit ff5812b
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 43 deletions.
27 changes: 27 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"bootstrap": "^4.5.3",
"bootstrap-vue": "^2.21.2",
"calendar-link": "^2.0.8",
"chart.js": "^4.4.4",
"colyseus.js": "^0.14.12",
"howler": "^2.2.3",
"jquery": "^3.5.1",
Expand All @@ -32,6 +33,7 @@
"popper.js": "^1.16.0",
"vue": "2.7",
"vue-carousel-3d": "^1.0.1",
"vue-chartjs": "^5.3.1",
"vue-class-component": "^7.2.6",
"vue-gtag": "^1.16.1",
"vue-meta": "^2.4.0",
Expand Down
203 changes: 160 additions & 43 deletions client/src/components/educator/dashboard/Reports.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<div v-if="completedGames.length < 1" class="empty-container">
<p>Reports will display once games have concluded.</p>
</div>
<b-row v-else>
<b-row>
<!-- finished games -->
<b-col :cols="inspectedCompletedGame ? '7' : '12'">
<b-col v-if="!inspectedCompletedGame">
<h4>Completed Games</h4>
<div class="content-container">
<b-table
Expand Down Expand Up @@ -36,53 +36,77 @@
variant="light"
size="sm"
class="float-right"
:disabled="data.item.id === inspectedCompletedGame?.id"
@click="inspectedCompletedGame = data.item"
>Scoreboard
>Inspect Stats
<b-icon-box-arrow-right class="float-right ml-2"></b-icon-box-arrow-right>
</b-button>
</template>
</b-table>
</div>
<!-- scoreboard -->
</b-col>
<b-col v-if="inspectedCompletedGame" cols="5">
<h4 class="header-nowrap">Game #{{ inspectedCompletedGame.id }} Scoreboard</h4>
<div class="content-container">
<b-table
dark
sticky-header
class="m-0 custom-table"
style="overflow-y: auto; max-height: 61vh"
:tbody-tr-attr="playerRowStyle"
:fields="playerFields"
:items="inspectedCompletedGame.players"
sort-by="points"
:sort-desc="true"
>
<template #cell(username)="data">
<b-icon-laptop v-if="data.item.user.isSystemBot"></b-icon-laptop>
<b-icon-person-fill v-else></b-icon-person-fill>
{{ data.item.user.username }}
<p style="margin-left: 1.3rem; margin-bottom: 0" v-if="!data.item.user.isSystemBot">
{{ data.item.user.name }}
</p>
</template>
<template #cell(points)="data">
{{ data.item.points }}
<b-badge
variant="success"
v-if="
isEligibleForPrize(data.item.user) &&
inspectedCompletedGame.highScore &&
data.item.points === inspectedCompletedGame.highScore
"
>winner</b-badge
>
</template>
</b-table>
</div>
</b-col>
<!-- selected game stats -->
<b-row v-else class="w-100 h-100 pl-3">
<b-col cols="4">
<b-button class="m-2" variant="light" @click="inspectedCompletedGame = null">
Return <b-icon-box-arrow-right class="float-right ml-2"></b-icon-box-arrow-right>
</b-button>
<b-dropdown text="Select Stat to View" variant="outline-primary" lazy>
<b-dropdown-item-button @click="stat = 'Points'"
>Player Points Stats</b-dropdown-item-button
>
<b-dropdown-item-button @click="stat = 'System'"
>System Health Stats</b-dropdown-item-button
>
<b-dropdown-item-button @click="stat = 'Chat'">Chat History</b-dropdown-item-button>
</b-dropdown>
<b-row class="ml-1 justify-content-between">
<h4 class="header-nowrap mt-3">Game #{{ inspectedCompletedGame.id }} Stats</h4>
</b-row>
<div class="content-container" style="max-height: 55vh">
<b-table
dark
sticky-header
class="m-0 custom-table"
style="overflow-y: auto; overflow-x: hidden; max-height: 50vh"
:tbody-tr-attr="playerRowStyle"
:fields="playerFields"
:items="inspectedCompletedGame.players"
sort-by="points"
:sort-desc="true"
>
<template #cell(username)="data">
<b-icon-laptop v-if="data.item.user.isSystemBot"></b-icon-laptop>
<b-icon-person-fill v-else></b-icon-person-fill>
{{ data.item.user.username }}
<p style="margin-left: 1.3rem; margin-bottom: 0" v-if="!data.item.user.isSystemBot">
{{ data.item.user.name }}
</p>
</template>
<template #cell(points)="data">
{{ data.item.points }}
<b-badge
variant="success"
v-if="
isEligibleForPrize(data.item.user) &&
inspectedCompletedGame.highScore &&
data.item.points === inspectedCompletedGame.highScore
"
>winner</b-badge
>
</template>
</b-table>
</div>
</b-col>
<b-col cols="8" class="p-3">
<div v-if="stat == 'Points'" style="height: 60vh; overflow-x: auto">
<LineChart :data="playerChartData" :options="playerChartOptions" />
</div>
<div v-if="stat == 'System'" style="height: 60vh; overflow-x: auto">
<LineChart :data="systemHealthChart" :options="systemChartOptions" />
</div>
<div v-if="stat == 'Chat'"></div>
</b-col>
</b-row>
</b-row>
</div>
</template>
Expand All @@ -92,9 +116,23 @@ import { Component, Inject, Vue, Prop } from "vue-property-decorator";
import { Client } from "colyseus.js";
import { EducatorAPI } from "@port-of-mars/client/api/educator/request";
import { AdminGameData, ClientSafeUser, ClassroomData } from "@port-of-mars/shared/types";
import { Line as LineChart } from "vue-chartjs";
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
CategoryScale,
PointElement,
} from "chart.js"; //Need to remove any unused imports later
ChartJS.register(Title, Tooltip, Legend, LineElement, LinearScale, CategoryScale, PointElement);
ChartJS.defaults.color = "rgb(241, 224, 197)";
ChartJS.defaults.font.family = "Ruda";
@Component({
components: {},
components: { LineChart },
})
export default class TeacherDashboard extends Vue {
@Inject() readonly $client!: Client;
Expand All @@ -103,6 +141,7 @@ export default class TeacherDashboard extends Vue {
pollingIntervalId = 0;
highestScore = 0;
stat = "Points";
completedGames: AdminGameData[] = [];
inspectedCompletedGame: AdminGameData | null = null;
Expand All @@ -122,6 +161,84 @@ export default class TeacherDashboard extends Vue {
{ key: "points", label: "Points" },
];
//Mock data for player points stats
playerChartData = {
labels: ["0", "1", "2", "3", "4", "5"],
datasets: [
{ label: "OrbitingQuagga5472", borderColor: "#aa2929af", data: [0, 2, 5, 6, 6, 8] },
{ label: "IllustriousAxolotl8430", borderColor: "#6f248694", data: [0, 5, 6, 9, 12, 12] },
{ label: "IllustriousAxolotl8430", borderColor: "#437cae9a", data: [0, 2, 3, 4, 5, 5] },
{ label: "StellarGiraffe3352", borderColor: "#67411da8", data: [0, 4, 6, 7, 9, 10] },
{ label: "AuroralIguana1167", borderColor: "#c7a72898", data: [0, 3, 6, 7, 7, 9] },
],
};
playerChartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
font: { size: 24 },
padding: { bottom: 10 },
text: "Total Player Points per Round",
},
legend: {},
},
scales: {
x: {
title: {
display: true,
text: "Rounds",
font: { size: 14 },
},
},
y: {
title: {
display: true,
text: "Points",
font: { size: 14 },
},
},
},
};
// Mock data for system health stats
systemHealthChart = {
labels: ["0", "1", "2", "3", "4", "5"],
datasets: [
{ label: "System Health", borderColor: "#5f8d4b", data: [100, 75, 60, 55, 42, 20, 0] },
],
};
systemChartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
font: { size: 24 },
padding: { bottom: 10 },
text: "System Health Status per Round",
},
legend: { display: false },
},
scales: {
x: {
title: {
display: true,
text: "Rounds",
font: { size: 14 },
},
},
y: {
title: {
display: true,
text: "Health (%)",
font: { size: 14 },
},
},
},
};
getHumanCount(players: AdminGameData["players"]) {
return players.filter(p => !p.user.isSystemBot).length;
}
Expand Down

0 comments on commit ff5812b

Please sign in to comment.