Add buttons to navigate through players cards from different sets

This commit is contained in:
Peter 2024-01-24 20:26:30 -05:00
parent 8a56977446
commit d5252f99b7
5 changed files with 178 additions and 24 deletions

1
components.d.ts vendored
View File

@ -9,6 +9,7 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
CardImagesDisplay: typeof import('./src/components/CardImagesDisplay.vue')['default']
IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']

View File

@ -0,0 +1,125 @@
<template>
<div class="card-images-display">
<div class="col-sm-auto" style="max-width: 941px;">
<div class="row">
<img v-if="selectedCardImage1Url" style="max-height:486px; max-width: 941px;" id="card-image"
:src="selectedCardImage1Url">
</div>
<div class="row">
<img v-if="selectedCardImage2Url" style="max-height:486px; max-width: 941px;" id="card-image"
:src="selectedCardImage2Url">
</div>
<div class="row">
<!-- TODO set visibility off if there is another key with lower value -->
<div v-visible="hasPreviousCard" class="col-sm-5">
<input type="button" class="w-100" @click="decrementCurrentSeasonNumber" value="<<" />
</div>
<strong class="col-sm-2" style="text-align: center;">
{{ getSeasonStringBySeasonNumber(currentSeasonNumber) }}
</strong>
<div v-visible="hasNextCard" class="col-sm-5">
<input type="button" class="w-100" @click="incrementCurrentSeasonNumber" value=">>" />
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import type { Player } from '@/services/playersService'
import { CURRENT_SEASON } from '@/services/utilities'
export default {
name: 'CardImagesDisplay',
data() {
return {
currentSeasonNumber: this.selectedSeasonNumber
}
},
props: {
isAuthenticated: { type: Boolean, default: false },
selectedSeasonNumber: { type: Number, required: true },
playerSeasons: { type: Array<Player>, default: [] },
playerName: { type: String, required: true }
},
computed: {
cardImage1UrlBySeasonNumber(): Map<number, string> {
return new Map(this.playerSeasons.map(ps => [ps.season, ps.image]))
},
cardImage2UrlBySeasonNumber(): Map<number, string> {
return new Map(this.playerSeasons.map(ps => [ps.season, ps.image2]))
},
selectedCardImage1Url(): string | undefined {
if (!this.cardImage1UrlBySeasonNumber.has(this.currentSeasonNumber)) return undefined
return this.cardImage1UrlBySeasonNumber.get(this.currentSeasonNumber)
},
selectedCardImage2Url(): string | undefined {
if (!this.cardImage2UrlBySeasonNumber.has(this.currentSeasonNumber)) return undefined
return this.cardImage2UrlBySeasonNumber.get(this.currentSeasonNumber)
},
hasPreviousCard(): boolean {
const currentImage = this.cardImage1UrlBySeasonNumber.get(this.currentSeasonNumber)
let season = this.currentSeasonNumber
while (season > 0) {
season--
if (this.cardImage1UrlBySeasonNumber.has(season)
&& currentImage !== this.cardImage1UrlBySeasonNumber.get(season)) {
return true
}
}
return false
},
hasNextCard(): boolean {
const currentImage = this.cardImage1UrlBySeasonNumber.get(this.currentSeasonNumber)
let season = this.currentSeasonNumber
while (season <= CURRENT_SEASON) {
season++
if (this.cardImage1UrlBySeasonNumber.has(season)
&& currentImage !== this.cardImage1UrlBySeasonNumber.get(season)) {
return true
}
}
return false
}
},
watch: {
playerName(newName, oldName) {
if (newName !== oldName) {
this.currentSeasonNumber = this.selectedSeasonNumber
}
}
},
methods: {
getSeasonStringBySeasonNumber(season: number): string {
if (season === 1) return 'S1'
// assumes seasons will continue to be paired like S2/3, S4/5, etc.
const firstSeason = Math.trunc(season / 2) * 2
return `S${firstSeason}/${firstSeason + 1}`
},
decrementCurrentSeasonNumber(): void {
const currentImage = this.cardImage1UrlBySeasonNumber.get(this.currentSeasonNumber)
while (this.currentSeasonNumber > 0) {
this.currentSeasonNumber--
if (this.cardImage1UrlBySeasonNumber.has(this.currentSeasonNumber)
&& currentImage !== this.cardImage1UrlBySeasonNumber.get(this.currentSeasonNumber)) {
break
}
}
},
incrementCurrentSeasonNumber(): void {
const currentImage = this.cardImage1UrlBySeasonNumber.get(this.currentSeasonNumber)
while (this.currentSeasonNumber <= CURRENT_SEASON) {
this.currentSeasonNumber++
if (this.cardImage1UrlBySeasonNumber.has(this.currentSeasonNumber)
&& currentImage !== this.cardImage1UrlBySeasonNumber.get(this.currentSeasonNumber)) {
break
}
}
}
}
}
</script>

View File

@ -11,6 +11,11 @@ import './assets/oldSite.css'
const app = createApp(App as any)
app.directive('visible', function (el, binding) {
// eslint-disable-next-line no-extra-boolean-cast
el.style.visibility = !!binding?.value ? 'visible' : 'hidden'
})
app.use(router)
app.mount('#app')

View File

@ -15,6 +15,9 @@
<h2 class="text-center">
Season {{ seasonNumber }} - Week {{ weekNumber }} - Game {{ gameNumber }}
</h2>
<h3>
<!-- TODO show pitching decisions and link to google sheet if exists -->
</h3>
</div>
<div class="col-sm-2">
<RouterLink v-if="homeTeamAbbreviation && homeTeamThumbnail"
@ -417,6 +420,9 @@ export default {
&& fs.game.game_num === this.gameNumber
&& fs.team.abbrev === this.awayTeamAbbreviation)
.sort((a, b) => b.xCheckCount - a.xCheckCount)
},
sheetsUrl(): string | undefined {
return this.game?.scorecard_url
}
},
created() {

View File

@ -5,10 +5,10 @@
<div class="row">
<div class="col-sm">
<h1 id="player-name">{{ playerName }}{{ injuryReturnDate }}</h1>
<h2 v-if="isCurrentPlayer" id="player-wara">{{ player?.wara }} sWAR</h2>
<h2 id="player-wara">{{ player?.wara }} sWAR</h2>
</div>
<div v-if="isCurrentPlayer" class="col-sm-1">
<div class="col-sm-1">
<RouterLink v-if="teamAbbreviation && teamThumbnail"
:to="{ name: 'team', params: { seasonNumber: seasonNumber, teamAbbreviation: teamAbbreviation } }">
<img id="thumbnail" height="125" style="float:right; vertical-align:middle; max-height:100%;"
@ -17,22 +17,39 @@
</div>
</div>
<div v-if="isCurrentPlayer" class="row">
<div class="row">
<div class="col-sm-auto">
<img style="max-height:485px; max-width: 100%;" id="team-image" :src="playerImageUrl" :alt="playerName">
<p><a id="bbref-link" target="_blank" :href="baseballReferenceUrl">Baseball Reference Page</a></p>
</div>
<div class="col-sm-auto">
<CardImagesDisplay :player-seasons="playerSeasons" :player-name="playerName"
:selected-season-number="seasonNumber" :is-authenticated="isAuthenticated" />
<!-- <div class="col-sm-auto">
<div class="row">
<img v-if="playerCardImage1Url" style="max-height:485px; max-width: 100%;" id="card-image"
:src="playerCardImage1Url">
</div>
<div class="row">
<img v-if="playerCardImage2Url" style="max-height:485px; max-width: 100%;" id="card-image"
:src="playerCardImage2Url">
</div>
<div class="row">
<div v-visible="false" class="col-sm-5">
<input type="button" class="w-100" @click="console.log('foo')" value="<<" />
</div>
<strong class="col-sm-2" style="text-align: center;">
S8/9
</strong>
<div v-visible="false" class="col-sm-5">
<input type="button" class="w-100" @click="console.log('bar')" value=">>" />
</div>
</div>
</div> -->
</div>
<!-- Summary -->
<div v-if="isCurrentPlayer" class="row" id="batter-summary">
<div class="row" id="batter-summary">
<div class="col-sm-12">
<h3>Summary</h3>
</div>
@ -158,6 +175,7 @@ import PlayerCareerPitchingTable from '@/components/PlayerCareerPitchingTable.vu
import PlayerCareerFieldingTable from '@/components/PlayerCareerFieldingTable.vue'
import PlayerBattingSummaryTable from '@/components/PlayerBattingSummaryTable.vue'
import PlayerPitchingSummaryTable from '@/components/PlayerPitchingSummaryTable.vue'
import CardImagesDisplay from '@/components/CardImagesDisplay.vue'
import { fetchPitchingStatsBySeasonAndPlayerId, fetchPitchingStatsForLastFourGamesBySeasonAndPlayerId, type PitchingStat } from '@/services/pitchingStatsService'
import { fetchFieldingStatsBySeasonAndPlayerId, fetchFieldingStatsForLastFourGamesBySeasonAndPlayerId, type FieldingStat } from '@/services/fieldingStatsService'
import { fetchTransactionsForCurrentSeasonByPlayerName, type Transaction } from '@/services/transactionsService'
@ -169,6 +187,7 @@ export default {
return {
isAuthenticated: false,
player: undefined as Player | undefined,
playerSeasons: [] as Player[],
last2Decisions: [] as Decision[],
// Batting stats
regularSeasonBattingStats: [] as BattingStat[],
@ -198,19 +217,14 @@ export default {
PlayerPitchingSummaryTable,
PlayerCareerPitchingTable,
LastFourGamesPitchingTable,
PlayerCareerFieldingTable
PlayerCareerFieldingTable,
CardImagesDisplay
},
props: {
seasonNumber: { type: Number, required: true },
playerName: { type: String, required: true }
},
computed: {
playerSeasonNumber(): number | undefined {
return this.player?.season
},
isCurrentPlayer(): boolean {
return this.seasonNumber === this.playerSeasonNumber
},
isBatter(): boolean {
return !this.player?.pos_1.includes('P')
},
@ -313,13 +327,16 @@ export default {
// TODO: this should change, either with an api that can take a player name for every season, a way
// to get multiple seasons stats at once, or a players ids across all seasons at once
const playerSeasons = await Promise.all(Array.from(Array(CURRENT_SEASON), (element, index) => index + 1).map(seasonNumber => fetchPlayerByName(seasonNumber, this.player!.name)))
this.regularSeasonBattingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchBattingStatsBySeasonAndPlayerId(player!.season, player!.id, true)))).filter(isNotUndefined)
this.postSeasonBattingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchBattingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).filter(isNotUndefined)
this.regularSeasonPitchingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchPitchingStatsBySeasonAndPlayerId(player!.season, player!.id, true)))).filter(isNotUndefined)
this.postSeasonPitchingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchPitchingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).filter(isNotUndefined)
this.regularSeasonFieldingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchFieldingStatsBySeasonAndPlayerId(player!.season, player!.id, true)))).flatMap(stat => stat).filter(isNotUndefined)
this.postSeasonFieldingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchFieldingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).flatMap(stat => stat).filter(isNotUndefined)
this.playerSeasons = (await Promise.all(Array.from(Array(CURRENT_SEASON), (element, index) => index + 1).map(seasonNumber => fetchPlayerByName(seasonNumber, this.player!.name)))).filter(isNotUndefined)
this.regularSeasonBattingStats = (await Promise.all(this.playerSeasons.map(player => fetchBattingStatsBySeasonAndPlayerId(player!.season, player!.id, true)))).filter(isNotUndefined)
this.postSeasonBattingStats = (await Promise.all(this.playerSeasons.map(player => fetchBattingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).filter(isNotUndefined)
this.regularSeasonPitchingStats = (await Promise.all(this.playerSeasons.map(player => fetchPitchingStatsBySeasonAndPlayerId(player!.season, player!.id, true)))).filter(isNotUndefined)
this.postSeasonPitchingStats = (await Promise.all(this.playerSeasons.map(player => fetchPitchingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).filter(isNotUndefined)
this.regularSeasonFieldingStats = (await Promise.all(this.playerSeasons.map(player => fetchFieldingStatsBySeasonAndPlayerId(player!.season, player!.id, true)))).flatMap(stat => stat).filter(isNotUndefined)
this.postSeasonFieldingStats = (await Promise.all(this.playerSeasons.map(player => fetchFieldingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).flatMap(stat => stat).filter(isNotUndefined)
// const images = playerSeasons.map(ps => ps.)
// console.log(images.length, Array.from(new Set(images)).length, images, Array.from(new Set(images)))
this.transactions = await fetchTransactionsForCurrentSeasonByPlayerName(this.player.name)
this.awards = await fetchAwardsByPlayerName(this.player.name)