save bullpen to backend

This commit is contained in:
Sascha Kühl 2025-04-10 16:49:54 +02:00
parent da83b0b123
commit a18559a77a
9 changed files with 96 additions and 56 deletions

View File

@ -2,7 +2,7 @@ import Pitch from "@/types/Pitch";
import User from "@/types/User";
import Bullpen from '@/types/Bullpen';
import PitchTypeService from '@/services/PitchTypeService';
// import api from './Api';
import api from '@/services/Api';
export class BullpenSessionService {
public create(user: User): Bullpen {
@ -15,16 +15,26 @@ export class BullpenSessionService {
}
}
public finish(): void {}
public save(bullpen: Bullpen): Promise<Bullpen> {
console.log(JSON.stringify(bullpen, null, 2));
return api
.post('/bullpen_session', {
bullpen
})
.then(response => {
return response.data;
}, (error) => {
console.log(JSON.stringify(error, null, 2));
});
}
public createPitch(): Pitch {
return {
id: 0,
date: new Date(),
pitchType: PitchTypeService.getLocalPitchTypes()[0],
plannedPitchArea: 0,
realPitchArea: 0,
realPitchSubArea: 0
pitchTime: new Date(),
pitchTypeId: PitchTypeService.getLocalPitchTypes()[0].id,
aimedArea: 0,
hitArea: 0
}
}
}

View File

@ -1,4 +1,4 @@
import { Module } from 'vuex';
import {ActionContext, Module} from 'vuex';
import { RootState } from './index';
import Bullpen from '@/types/Bullpen';
import User from '@/types/User';
@ -9,12 +9,24 @@ export interface BullpenState {
bullpen: Bullpen | null;
}
type BullpenActionContext = ActionContext<BullpenState, RootState>;
const bullpen: Module<BullpenState, RootState> = {
namespaced: true,
state: { bullpen: null },
actions: {
start({commit}: BullpenActionContext, user: User) {
const bullpen = BullpenSessionService.create(user);
commit('start', bullpen);
},
async finish({commit}: BullpenActionContext, bullpen: Bullpen) {
await BullpenSessionService.save(bullpen);
commit('finish');
}
},
mutations: {
start(state, user: User) {
state.bullpen = BullpenSessionService.create(user);
start(state, bullpen: Bullpen) {
state.bullpen = bullpen;
},
addPitch(state, pitch: Pitch) {
state.bullpen?.pitches.push({ ...pitch });

View File

@ -1,10 +1,7 @@
import PitchType from "@/types/PitchType";
export default interface Pitch {
id: number,
date: Date,
pitchType: PitchType,
plannedPitchArea: number,
realPitchArea: number,
realPitchSubArea: number
pitchTime: Date,
pitchTypeId: number,
aimedArea: number,
hitArea: number
}

View File

@ -17,11 +17,14 @@ import {
IonToolbar
} from '@ionic/vue';
import {useRouter} from 'vue-router';
import BullpenSessionService from "@/services/BullpenSessionService";
import {ref} from 'vue';
import {computed} from 'vue';
import {useStore} from 'vuex';
const router = useRouter();
const bps = ref(BullpenSessionService);
const store = useStore();
const pitcher = computed(() => store.state.auth.user);
const bullpen = computed(() => store.state.bullpen.bullpen);
const gotoHome = () => {
router.push('/home');
@ -32,11 +35,11 @@ const gotoHome = () => {
<ion-page>
<ion-header :translucent="true">
<ion-toolbar>
<ion-title>Bullpen Stats for {{ bps.getPitcher().firstName }} {{ bps.getPitcher().lastName }}</ion-title>
<ion-title>Bullpen Stats for {{ pitcher.firstName }} {{ pitcher.lastName }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-card v-for="(pitch, index) in bps.getBullpenPitches()" :key="index">
<ion-card v-for="(pitch, index) in bullpen.pitches" :key="index">
<ion-card-header>
<ion-card-title>Pitch {{index+1}}</ion-card-title>
</ion-card-header>
@ -48,11 +51,11 @@ const gotoHome = () => {
</ion-item>
<ion-item>
<ion-label>Planned Pitch Area</ion-label>
<ion-badge>{{pitch.plannedPitchArea}}</ion-badge>
<ion-badge>{{pitch.aimedArea}}</ion-badge>
</ion-item>
<ion-item>
<ion-label>Real Pitch Area</ion-label>
<ion-badge>{{pitch.realPitchArea}}</ion-badge>
<ion-badge>{{pitch.hitArea}}</ion-badge>
</ion-item>
</ion-list>
</ion-card-content>

View File

@ -12,10 +12,11 @@ const store = useStore();
const userImage = ref(null);
// const userImage = ref('../assets/groot.jpg');
const user = computed(() => store.state.auth.user);
const pitcher = computed(() => store.state.auth.user);
const isAuthenticated = computed(() => store.state.auth.isAuthenticated);
console.log('user', user.value);
if (user.value === undefined || user.value === null || user.value === '') {
console.log('user', pitcher.value);
if (pitcher.value === undefined || pitcher.value === null || pitcher.value === '') {
router.push({ path: '/login' });
}
@ -48,7 +49,8 @@ const logout = () => {
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>User Home</ion-title>
<ion-title v-if="isAuthenticated">Home of {{ pitcher.firstName }} {{ pitcher.lastName }}</ion-title>
<ion-title v-else>Home</ion-title>
</ion-toolbar>
</ion-header>
@ -62,7 +64,7 @@ const logout = () => {
<ion-icon :icon="personOutline" class="avatar-placeholder" />
</template>
</ion-avatar>
<div class="user-name">{{ user.firstName }} {{ user.lastName }}</div>
<div v-if="isAuthenticated" class="user-name">{{ pitcher.firstName }} {{ pitcher.lastName }}</div>
</div>
<div class="button-grid">

View File

@ -16,6 +16,9 @@ import {
IonInput,
IonIcon,
} from "@ionic/vue";
// Todo: https://github.com/alanmontgomery/ionic-react-login
import { lockClosedOutline, personOutline } from 'ionicons/icons';
import { useForm } from 'vee-validate';
import { useRouter } from 'vue-router';

View File

@ -28,14 +28,16 @@ const pitch = ref<Pitch>(BullpenSessionService.createPitch());
const currentStep = ref<BullpenStep>(BullpenStep.Prepare);
const pitcher = computed(() => store.state.auth.user);
const isAuthenticated = computed(() => store.state.auth.isAuthenticated);
const pitchTypes = computed(() => store.state.pitchTypes.pitchTypes);
const bullpen = computed(() => store.state.bullpen);
onMounted(async () => {
store.commit("bullpen/start", pitcher);
await store.dispatch("bullpen/start", pitcher.value);
});
const setPitchType = (pitchType: PitchType) => {
pitch.value.pitchType = pitchType;
pitch.value.pitchTypeId = pitchType.id;
}
const gotoFinalizePitch = () => {
@ -44,29 +46,35 @@ const gotoFinalizePitch = () => {
const finalizeAndNextPitch = () => {
store.commit("bullpen/addPitch", pitch.value);
pitch.value = BullpenSessionService.createPitch();
currentStep.value = BullpenStep.Prepare;
}
const finalizeAndEndBullpen = () => {
store.commit("bullpen/addPitch", pitch.value);
pitch.value = BullpenSessionService.createPitch();
currentStep.value = BullpenStep.Prepare;
const bp = bullpen.value.bullpen;
bp.finishedAt = new Date();
store.dispatch("bullpen/finish", bp);
router.push({ name: 'BullpenStats' });
}
const setPlannedPitchArea = (hitArea: number) => {
if (currentStep.value === BullpenStep.Prepare) {
pitch.value.plannedPitchArea = hitArea;
pitch.value.aimedArea = hitArea;
} else {
pitch.value.realPitchArea = hitArea;
pitch.value.hitArea = hitArea;
}
};
const pitchAreaCssClasses = (area: number, name: string) => {
const classes = [];
if (pitch.value.plannedPitchArea === area) {
if (pitch.value.aimedArea === area) {
classes.push(name + '-aim-selected');
}
if (pitch.value.realPitchArea === area) {
if (pitch.value.hitArea === area) {
classes.push(name + '-hit-selected');
}
@ -78,7 +86,8 @@ const pitchAreaCssClasses = (area: number, name: string) => {
<ion-page>
<ion-header :translucent="true">
<ion-toolbar>
<ion-title>Prepare Pitch for {{ pitcher.firstName }} {{ pitcher.lastName }}</ion-title>
<ion-title v-if="isAuthenticated">Pitching {{ pitcher.firstName }} {{ pitcher.lastName }}</ion-title>
<ion-title v-else>Pitching</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
@ -87,7 +96,7 @@ const pitchAreaCssClasses = (area: number, name: string) => {
<ion-card-title>Select Pitch type</ion-card-title>
</ion-card-header>
<ion-card-content>
<ion-button v-for="(pitchType, index) in pitchTypes" :key="index" :color="pitchType.id !== pitch.pitchType.id ? 'primary' : 'warning'" @click="setPitchType(pitchType)">
<ion-button v-for="(pitchType, index) in pitchTypes" :key="index" :color="pitchType.id !== pitch.pitchTypeId ? 'primary' : 'warning'" @click="setPitchType(pitchType)">
<ion-label>{{pitchType.abbreviation}}</ion-label>
</ion-button>
</ion-card-content>
@ -158,25 +167,31 @@ const pitchAreaCssClasses = (area: number, name: string) => {
</ion-card>
</ion-content>
<ion-footer>
<ion-button color="warning" expand="full" size="large" :disabled="pitch.pitchType.id === 0 || pitch.plannedPitchArea === 0" @click="gotoFinalizePitch">Pitch</ion-button>
<div v-if="currentStep === BullpenStep.Prepare">
<ion-grid>
<ion-row>
<ion-col>
<ion-button color="warning" expand="full" size="large" :disabled="pitch.pitchTypeId === 0 || pitch.aimedArea === 0" @click="gotoFinalizePitch">Pitch</ion-button>
</ion-col>
</ion-row>
</ion-grid>
</div>
<div v-if="currentStep === BullpenStep.Finish">
<ion-grid>
<ion-row>
<ion-col>
<ion-button color="success" expand="full" @click="finalizeAndNextPitch" :disabled="pitch.hitArea === 0">Save Pitch<br/>&<br/>Next Pitch</ion-button>
</ion-col>
<ion-col>
<ion-button color="success" expand="full" @click="finalizeAndEndBullpen" :disabled="pitch.hitArea === 0">Save Pitch<br/>&<br/>End Session</ion-button>
</ion-col>
</ion-row>
</ion-grid>
</div>
</ion-footer>
</ion-page>
</template>
<!--<ion-grid>-->
<!-- <div v-if="currentStep === BullpenStep.Prepare">-->
<!--<ion-row>-->
<!-- <ion-col><ion-button color="warning" expand="full" size="large" :disabled="pitch.pitchType.id === 0 || pitch.plannedPitchArea === 0" @click="gotoFinalizePitch">Pitch</ion-button></ion-col>-->
<!--</ion-row>-->
<!-- </div>-->
<!-- <div v-if="currentStep === BullpenStep.Finish">-->
<!--<ion-row>-->
<!-- <ion-col><ion-button color="success" expand="full" @click="finalizeAndNextPitch" :disabled="pitch.realPitchArea === 0">Save Pitch<br/>&<br/>Next Pitch</ion-button></ion-col>-->
<!-- <ion-col><ion-button color="success" expand="full" @click="finalizeAndEndBullpen" :disabled="pitch.realPitchArea === 0">Save Pitch<br/>&<br/>End Session</ion-button></ion-col>-->
<!--</ion-row>-->
<!-- </div>-->
<!--</ion-grid>-->
<style scoped>
.wasted {
fill: firebrick;

View File

@ -108,8 +108,6 @@ exports.refreshToken = async (req, res) => {
try {
let refreshToken = await RefreshToken.findOne({ where: { token: requestToken } });
console.log(refreshToken)
if (!refreshToken) {
res.status(403).json({ message: "Refresh token is not in database!" });
return;

View File

@ -3,7 +3,7 @@ const BullpenSession = db.BullpenSession;
const Pitch = db.Pitch;
exports.insert = (req, res) => {
BullpenSession.create(req.body, { include: [{
BullpenSession.create(req.body.bullpen, { include: [{
model: Pitch,
as: 'pitches'
}]}