Skip to content

Commit

Permalink
feat: Even more team stats
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobi2K committed Mar 3, 2025
1 parent d1ee0b0 commit 00fc7a5
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 39 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ android {
applicationId "app.kalmbach.dev"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 37
versionName "2.9.6"
versionCode 38
versionName "3.9.6"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "top-tips-app",
"version": "2.9.6",
"version": "3.9.6",
"private": true,
"description": "Lightweight app for a handball guessing game",
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion src/components/CurrentGroup.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<ion-list v-if="groupData != null">
<ion-list-header>Current Group</ion-list-header>
<ion-list-header style="font-size: 150%; text-decoration: underline;">Current Group</ion-list-header>
<ion-item>
<ion-label>
<ion-row>
Expand Down Expand Up @@ -47,6 +47,9 @@
-
{{ this.moment(groupData.season.end_date).format("DD MMM, YYYY") }}
</p>
<p style="color: red;" v-if="groupData.season.important == 0">
{{ groupData.season.name }} is not actively synced as there is not enough interest! Let me know at <a href="mailto:toptips@kalmbach.dev">toptips@kalmbach.dev</a> if you want to play this season.
</p>
</ion-label>
</ion-item>
<ion-item>
Expand Down
5 changes: 3 additions & 2 deletions src/components/GameDaySlider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
slot="fixed"
v-if="this.ranking.length > 0 || this.activeGamedays.length > 0"
>
<ion-badge v-if="this.activeGamedays.length > 0" style="left: -5px; z-index: 3; position: absolute; padding: 0;"><ion-icon style="font-size: 170%" :icon="alertCircleOutline"/></ion-badge>
<ion-fab-button size="small" color="medium">
<ion-badge v-if="this.activeGamedays.length > 0 && showBadge" style="left: -5px; z-index: 3; position: absolute; padding: 0;"><ion-icon style="font-size: 170%" :icon="alertCircleOutline"/></ion-badge>
<ion-fab-button size="small" color="medium" @click="showBadge = false">
<ion-icon :icon="caretUpOutline"/>
</ion-fab-button>

Expand Down Expand Up @@ -127,6 +127,7 @@ export default defineComponent({
// gameDay is 1-indexed!
gameDay: this.$store.state.currentGameday,
loaded: false,
showBadge: true,
};
},
methods: {
Expand Down
10 changes: 7 additions & 3 deletions src/components/GuessModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ import TeamStats from "@/components/TeamStats.vue";
import moment from "moment";
import { mapState } from "vuex";
import { showToast } from "@/store/helper";
import { showToast, showDangerToast } from "@/store/helper";
export default defineComponent({
name: "GuessModal",
props: {
Expand Down Expand Up @@ -377,9 +377,13 @@ export default defineComponent({
this.pointsTeam1 == undefined ||
this.pointsTeam2 == undefined
) {
showToast("Please fill out everything (≥0).");
showDangerToast("Please fill out everything (≥0).");
} else if (new Date(this.gameInfo.date) < new Date()) {
showToast("You are too late :(");
showDangerToast("You are too late :(");
} else if (!/^[0-9]+$/.test(this.pointsTeam1) || !/^[0-9]+$/.test(this.pointsTeam2)) {
showDangerToast("Only use numbers!");
} else if (this.pointsTeam1 > 2147483647 || this.pointsTeam2 > 2147483647) {
showDangerToast("That number is too large! Use something smaller.")
} else {
this.$store
.dispatch("addGuess", {
Expand Down
2 changes: 1 addition & 1 deletion src/components/RankingModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
</ion-row>
<ion-row class="align-middle">
<small><ion-icon :icon="informationCircleOutline" />
Leaderboards are updated daily at 04:00 AM CEST.</small>
Leaderboards are updated daily at 04:00 AM, Berlin Time.</small>
</ion-row>
</ion-grid>
</ion-content>
Expand Down
167 changes: 143 additions & 24 deletions src/components/TeamStats.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
#{{ gameInfo.team2_stats.position }}
</ion-col>
</ion-row>
<ion-row class="align-middle" v-if="gameInfo.team1_stats.history && gameInfo.team2_stats.history">
<ion-row class="align-middle" v-if="gameInfo.team1_stats.history.result && gameInfo.team2_stats.history.result">
<ion-col class="ion-text-center" size="5">
<span v-for="i in gameInfo.team1_stats.history" :key="i">
<ion-icon v-if="i == 'W'" style="color: green" :icon="ellipse" />
<ion-icon v-if="i == 'L'" style="color: red" :icon="ellipse" />
<ion-icon v-if="i == 'D'" style="color: orange" :icon="ellipse" />
<span v-for="i in gameInfo.team1_stats.history.result" :key="i">
<ion-icon v-if="i == 'W'" style="color: green" :icon="checkmarkCircle" />
<ion-icon v-if="i == 'L'" style="color: red" :icon="closeCircle" />
<ion-icon v-if="i == 'D'" style="color: orange" :icon="removeCircle" />
</span>
</ion-col>
<ion-col class="ion-text-center" size="2">
Expand All @@ -23,24 +23,39 @@
<small>
5 most recent games (from left to right)
<ul>
<li>Win: Green</li>
<li>Loss: Red</li>
<li>Draw: Orange</li>
<li>Win: Green <ion-icon :icon="checkmarkCircleOutline" /></li>
<li>Loss: Red <ion-icon :icon="closeCircleOutline" /></li>
<li>Draw: Orange <ion-icon :icon="removeCircleOutline" /></li>
</ul>
</small>
</ion-content>
</ion-popover>
</ion-col>
<ion-col class="ion-text-center" size="5">
<span v-for="i in gameInfo.team2_stats.history" :key="i">
<ion-icon v-if="i == 'W'" style="color: green" :icon="ellipse" />
<ion-icon v-if="i == 'L'" style="color: red" :icon="ellipse" />
<ion-icon v-if="i == 'D'" style="color: orange" :icon="ellipse" />
<span v-for="i in gameInfo.team2_stats.history.result" :key="i">
<ion-icon v-if="i == 'W'" style="color: green" :icon="checkmarkCircle" />
<ion-icon v-if="i == 'L'" style="color: red" :icon="closeCircle" />
<ion-icon v-if="i == 'D'" style="color: orange" :icon="removeCircle" />
</span>
</ion-col>
</ion-row>

<ion-row v-if="showStats">
<ion-col class="ion-text-center">
<ion-button fill="clear" size="small" @click="setMoreStats(false)">Hide</ion-button>
</ion-col>
</ion-row>
<ion-row v-if="!showStats">
<ion-col class="ion-text-center">
<ion-button fill="clear" size="small" @click="setMoreStats(true)">More stats</ion-button>
</ion-col>
</ion-row>

<ion-row class="tiny-text ion-margin-bottom" v-if="showStats">
<ion-col size="5" style="overflow-x: scroll" class="colNoPaddingRight">
<ion-col size="12" class="ion-text-center">
<h6>General Stats</h6>
</ion-col>
<ion-col size="6" style="overflow-x: scroll; border-right: 1px solid black;" class="colNoPaddingRight">
<table class="coolTable mx-auto">
<thead>
<tr class="border-bottom">
Expand Down Expand Up @@ -72,12 +87,7 @@
</tbody>
</table>
</ion-col>
<ion-col class="ion-text-center" size="2">
<ion-button fill="clear" size="small" @click="setMoreStats(false)" expand="block">
Hide
</ion-button>
</ion-col>
<ion-col size="5" style="overflow-x: scroll" class="colNoPaddingLeft">
<ion-col size="6" style="overflow-x: scroll; border-left: 1px solid black;" class="colNoPaddingLeft">
<table class="coolTable mx-auto">
<thead>
<tr class="border-bottom">
Expand Down Expand Up @@ -110,9 +120,100 @@
</table>
</ion-col>
</ion-row>
<ion-row v-else>
<ion-col class="ion-text-center">
<ion-button fill="clear" size="small" @click="setMoreStats(true)">More stats</ion-button>
<ion-row v-if="showStats">
<ion-col v-if="showEvenMoreStats" class="ion-text-center">
<ion-button fill="clear" size="small" @click="setEvenMoreStats(false)">Hide extended stats</ion-button>
</ion-col>
<ion-col v-if="!showEvenMoreStats" class="ion-text-center">
<ion-button fill="clear" size="small" @click="setEvenMoreStats(true)">Even more stats</ion-button>
</ion-col>
</ion-row>
<ion-row class="tiny-text ion-margin-bottom" v-if="showEvenMoreStats">
<ion-col size="12" class="ion-text-center">
<h6>Past Results</h6>
</ion-col>
<ion-col size="6" style="overflow-x: scroll; border-right: 1px solid black;" class="colNoPaddingRight">
<table class="coolTable mx-auto">
<thead>
<tr class="border-bottom">
<th v-for="i in Array.from({ length: gameInfo.team1_stats.history.scores_home_team.length }, (_, index) => index)" :key="i">
<ion-icon v-if="gameInfo.team1_stats.history.result[i] == 'W'" style="color: green" :icon="checkmarkCircle" />
<ion-icon v-if="gameInfo.team1_stats.history.result[i] == 'L'" style="color: red" :icon="closeCircle" />
<ion-icon v-if="gameInfo.team1_stats.history.result[i] == 'D'" style="color: orange" :icon="removeCircle" />
{{ gameInfo.team1_stats.history.scores_home_team[i] }} - {{ gameInfo.team1_stats.history.scores_away_team[i] }}
</th>
</tr>
</thead>
<tbody>
<tr>
<td v-for="name in gameInfo.team1_stats.history.other_team_name" :key="name">vs. {{ name }}</td>
</tr>
</tbody>
</table>
</ion-col>
<ion-col size="6" style="overflow-x: scroll; border-left: 1px solid black;" class="colNoPaddingLeft">
<table class="coolTable mx-auto">
<thead>
<tr class="border-bottom">
<th v-for="i in Array.from({ length: gameInfo.team2_stats.history.scores_home_team.length }, (_, index) => index)" :key="i">
<ion-icon v-if="gameInfo.team2_stats.history.result[i] == 'W'" style="color: green" :icon="checkmarkCircle" />
<ion-icon v-if="gameInfo.team2_stats.history.result[i] == 'L'" style="color: red" :icon="closeCircle" />
<ion-icon v-if="gameInfo.team2_stats.history.result[i] == 'D'" style="color: orange" :icon="removeCircle" />
{{ gameInfo.team2_stats.history.scores_home_team[i] }} - {{ gameInfo.team2_stats.history.scores_away_team[i] }}
</th>
</tr>
</thead>
<tbody>
<tr>
<td v-for="name in gameInfo.team2_stats.history.other_team_name" :key="name">vs. {{ name }}</td>
</tr>
</tbody>
</table>
</ion-col>
</ion-row>

<ion-row class="tiny-text ion-margin-bottom" v-if="showEvenMoreStats">
<ion-col size="12" class="ion-text-center">
<h6>Head to Head</h6>
Only showing results from competitions that have been used (by anyone) in TopTips.
<br>
</ion-col>
<ion-col size="12" class="ion-text-center">
<table class="coolTable mx-auto">
<tbody>
<tr>
<td>Games played:</td>
<td>{{ gameInfo.home_team_won + gameInfo.away_team_won + gameInfo.draw_count }}</td>
</tr>
<tr>
<td>Wins {{ gameInfo.team1_name }}:</td>
<td>{{ gameInfo.home_team_won }}</td>
</tr>
<tr>
<td>Wins {{ gameInfo.team2_name }}:</td>
<td>{{ gameInfo.away_team_won }}</td>
</tr>
<tr>
<td>Draws:</td>
<td>{{ gameInfo.draw_count }}</td>
</tr>
</tbody>
</table>
</ion-col>
</ion-row>

<ion-row class="tiny-text ion-margin-bottom" v-if="showEvenMoreStats" v-for="game in gameInfo.head_to_head" :key="game" style="border-bottom: 1px solid grey;">
<ion-col size="12" class="ion-text-center">
{{ this.moment(game.game.date).format("DD MMM, YYYY") }}
</ion-col>
<ion-col size="5" class="ion-text-center" style="font-size: 120%;">
<b v-if="game.team1_won">{{ game.game.team1.name }}</b><span v-else>{{ game.game.team1.name }}</span>
</ion-col>
<ion-col size="2" class="ion-text-center" style="font-size: 120%;">
{{ game.game.score_team1 }} - {{ game.game.score_team2 }}
</ion-col>
<ion-col size="5" class="ion-text-center" style="font-size: 120%;">
<b v-if="game.team2_won">{{ game.game.team2.name }}</b><span v-else>{{ game.game.team2.name }}</span>
</ion-col>
</ion-row>
</template>
Expand All @@ -126,9 +227,11 @@ import {
IonPopover,
IonButton,
} from "@ionic/vue";
import { ellipse, helpCircleOutline } from "ionicons/icons";
import { ellipse, helpCircleOutline, closeCircle, checkmarkCircle, removeCircle, closeCircleOutline, checkmarkCircleOutline, removeCircleOutline } from "ionicons/icons";
import { defineComponent } from "vue";
import moment from "moment";
export default defineComponent({
name: "TeamStats",
props: {
Expand All @@ -145,17 +248,33 @@ export default defineComponent({
data() {
return {
showStats: false,
showEvenMoreStats: false,
};
},
setup() {
return {
ellipse,
helpCircleOutline,
closeCircle,
checkmarkCircle,
removeCircle,
closeCircleOutline,
checkmarkCircleOutline,
removeCircleOutline,
moment,
};
},
methods: {
setMoreStats(bool) {
setMoreStats(bool) {
this.showStats = bool;
if (!bool) {
this.showEvenMoreStats = false
}
},
setEvenMoreStats(bool) {
console.log(this.gameInfo);
this.showEvenMoreStats = bool;
},
},
});
Expand Down
17 changes: 17 additions & 0 deletions src/store/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,20 @@ export async function showToast(toastMessage: string) {
value.present();
});
}

/**
* Generates a danger toast to be shown at the bottom of the screen
* @param {string} toastMessage the message to be displayed
*/
export async function showDangerToast(toastMessage: string) {
toastController
.create({
message: toastMessage,
duration: 2000,
color: "danger",
})
.then((value) => {
value.present();
});
}

4 changes: 2 additions & 2 deletions src/views/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ export default defineComponent({
const alert = await alertController.create({
header: "Reset Password",
message: "Please enter your username. You will receive a new password at the email you set. " +
"If you don't have an email address set, please contact me at tobias@kalmbach.dev",
"If you don't have an email address set, please contact me at toptips@kalmbach.dev",
inputs: [
{
name: "username",
Expand Down Expand Up @@ -250,7 +250,7 @@ export default defineComponent({
cssClass: "points-alert",
header: "Hi there, welcome to TopTips! ",
message: "Log in or register to use the app. \n\n" +
"If you have any suggestions, questions or complaints, feel free to contact me at tobias@kalmbach.dev.",
"If you have any suggestions, questions or complaints, feel free to contact me at toptips@kalmbach.dev.",
buttons: [
{
text: "Close",
Expand Down
6 changes: 3 additions & 3 deletions src/views/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
</ion-button>
</ion-col>
</ion-row>
<ion-list-header>Notifications for pending games (at 12 PM CEST).</ion-list-header>
<ion-list-header>Notifications for pending games (at 12 PM, Berlin Time).</ion-list-header>
<div v-if="gameNoti">
<ion-item v-for="season in userSeasons" :key="season">
<ion-toggle
Expand All @@ -158,7 +158,7 @@
<ion-list>
<h3>Email Notification Settings</h3>
<p>
You will receive a notification (at 12:15 PM CEST) via the email used to register. Please add <a href="mailto:toptips@kalmbach.dev">toptips@kalmbach.dev</a> to your email contacts for optimal delivery.
You will receive a notification (at 12:15 PM, Berlin Time) via the email used to register. Please add <a href="mailto:toptips@kalmbach.dev">toptips@kalmbach.dev</a> to your email contacts for optimal delivery.
</p>
<ion-row>
<ion-col>
Expand Down Expand Up @@ -211,7 +211,7 @@
<p style="text-align: center; margin-top: 100px;">
<small>
If you have any suggestions, questions or complaints, feel free to contact me at
<a href="mailto:tobias@kalmbach.dev">tobias@kalmbach.dev</a>.
<a href="mailto:toptips@kalmbach.dev">toptips@kalmbach.dev</a>.
<br />
<a href="https://kalmbach.dev/privacy.html">Privacy Policy</a> |
<a href="https://kalmbach.dev/imprint.html">Imprint</a>
Expand Down

0 comments on commit 00fc7a5

Please sign in to comment.