Compare commits

..

2 Commits

Author SHA1 Message Date
Sascha Kühl acfaffad2c split home view in player and coach component 2025-06-12 16:12:44 +02:00
Sascha Kühl 66b41022b4 fixed docker-compose.dev.yml 2025-06-12 16:12:23 +02:00
6 changed files with 191 additions and 74 deletions

View File

@ -49,7 +49,8 @@ onMounted(() => {
if (isOnline.value && !isAuthenticated.value) { if (isOnline.value && !isAuthenticated.value) {
router.push({path: '/login'}); router.push({path: '/login'});
} else { } else {
// checkStoredUserData(); store.dispatch('pitchTypes/fetch');
store.dispatch("player/selectPlayer", user.value);
} }
}); });
@ -86,7 +87,7 @@ const logoutUser = () => {
<template> <template>
<ion-app> <ion-app>
<ion-menu content-id="user-menu"> <ion-menu v-if="isAuthenticated" content-id="user-menu">
<ion-header> <ion-header>
<ion-toolbar> <ion-toolbar>
<ion-buttons slot="secondary"> <ion-buttons slot="secondary">
@ -118,7 +119,7 @@ const logoutUser = () => {
</ion-list> </ion-list>
</ion-content> </ion-content>
</ion-menu> </ion-menu>
<ion-router-outlet /> <ion-router-outlet id="user-menu"/>
</ion-app> </ion-app>
</template> </template>

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import { computed } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
const store = useStore();
const router = useRouter();
const user = computed(() => store.state.auth.user);
const managePlayers = () => {
router.push({ path: "/players" });
};
</script>
<template>
<div>
<p>Willkommen, Coach {{ user.firstName }}!</p>
<ion-button @click="managePlayers">Spieler verwalten</ion-button>
</div>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,149 @@
<script setup lang="ts">
import {computed, reactive, ref} from "vue";
import {
InfiniteScrollCustomEvent,
IonIcon,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonItem,
IonLabel,
IonList
} from '@ionic/vue';
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import {baseballOutline} from 'ionicons/icons';
import Bullpen from '@/types/Bullpen';
import BullpenSessionService from "@/services/BullpenSessionService";
import dayjs from 'dayjs';
import PitchType from '@/types/PitchType';
const store = useStore();
const router = useRouter();
const user = computed(() => store.state.auth.user);
const pitchTypes = computed(() => store.state.pitchTypes.pitchTypes);
const items = reactive<Bullpen[]>([]);
const page = ref(0);
const startBullpenSession = () => {
router.push({ path: "/bullpen" });
};
const generateItems = () => {
BullpenSessionService
.fetchAllByUser(user.value.id, page.value, 10)
.then((bullpens) => {
++page.value;
bullpens.data.forEach((bullpen: Bullpen) => {
items.push(bullpen);
})
}, error => {
console.log(error);
});
};
const formatDate = (date: Date) => {
return dayjs(date).format('YYYY.MM.DD HH:mm');
}
const determinePitchTypeName = (id: number): string => {
const pitchType = pitchTypes.value.find((pitchType: PitchType) => pitchType.id === id);
return pitchType?.name ?? 'Unknown';
}
const ionInfinite = (event: InfiniteScrollCustomEvent) => {
generateItems();
setTimeout(() => event.target.complete(), 500);
};
generateItems();
</script>
<template>
<ion-list>
<ion-item v-for="(bullpen, index) in items" :key="index" lines="none" class="custom-item" :button="true" detail="false">
<!-- Top-left icon -->
<ion-icon :icon="baseballOutline" slot="start" class="item-icon"></ion-icon>
<ion-label class="item-labels">
<!-- First Progress Bar -->
<div class="progress-section">
<div class="top-row">
<span class="left-label">Task 1</span>
<span class="right-label">{{ (0.57 * 100).toFixed(0) }}%</span>
</div>
<ion-progress-bar :value="0.57"></ion-progress-bar>
</div>
<!-- Second Progress Bar -->
<div class="progress-section">
<div class="top-row">
<span class="left-label">Task 2</span>
<span class="right-label">{{ (0.33 * 100).toFixed(0) }}%</span>
</div>
<ion-progress-bar :value="0.33" color="secondary"></ion-progress-bar>
</div>
</ion-label>
</ion-item>
<!-- <ion-item v-for="(bullpen, index) in items" :key="index">-->
<!-- <ion-icon aria-hidden="true" :icon="baseball" slot="start"></ion-icon>-->
<!-- <ion-label>Bullpen from ({{formatDate(bullpen.startedAt)}})</ion-label>-->
<!-- <ion-list v-for="(pitch, index) in bullpen.pitches" :key="index">-->
<!-- <ion-item>-->
<!-- <ion-icon slot="start" :icon="baseballOutline" class="icon-login"></ion-icon>-->
<!-- <ion-label>{{determinePitchTypeName(pitch.pitchTypeId)}}</ion-label>-->
<!-- <ion-badge>{{pitch.aimedArea}}</ion-badge>-->
<!-- <ion-badge>{{pitch.hitArea}}</ion-badge>-->
<!-- </ion-item>-->
<!-- </ion-list>-->
<!-- </ion-item>-->
</ion-list>
<ion-infinite-scroll @ionInfinite="ionInfinite">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</template>
<style scoped>
.custom-item {
align-items: flex-start !important; /* important override */
--padding-start: 16px;
--inner-padding-end: 16px;
--min-height: 100px;
}
.item-icon {
font-size: 28px;
color: var(--ion-color-danger);
margin-right: 12px;
margin-top: 18px;
}
.item-labels {
width: 100%;
}
.progress-section {
margin-bottom: 12px;
}
.top-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
}
.left-label {
font-weight: bold;
font-size: 16px;
}
.right-label {
font-weight: normal;
font-size: 14px;
}
</style>

View File

@ -1,44 +1,32 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref, reactive} from 'vue'; import {computed} from 'vue';
import { import {
IonPage, IonPage,
IonHeader, IonHeader,
IonToolbar, IonToolbar,
IonTitle, IonTitle,
IonContent, IonContent,
IonAvatar,
IonButtons, IonButtons,
IonIcon, IonIcon,
IonMenuButton, IonMenuButton,
IonFab, IonFab,
IonFabButton, IonFabButton,
IonInfiniteScroll,
IonInfiniteScrollContent,
InfiniteScrollCustomEvent,
IonList,
IonItem,
IonLabel, IonBadge,
} from '@ionic/vue'; } from '@ionic/vue';
import { import {
baseball,
add, add,
baseballOutline
} from 'ionicons/icons'; } from 'ionicons/icons';
import {useStore} from "vuex"; import {useStore} from "vuex";
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import BullpenSessionService from "@/services/BullpenSessionService"; import BullpenSessionService from "@/services/BullpenSessionService";
import TokenService from "@/services/TokenService"; import TokenService from "@/services/TokenService";
import dayjs from "dayjs";
import Bullpen from "@/types/Bullpen";
import PitchType from "@/types/PitchType";
const router = useRouter(); const router = useRouter();
const store = useStore(); const store = useStore();
const user = computed(() => store.state.auth.user); const user = computed(() => store.state.auth.user);
const pitchTypes = computed(() => store.state.pitchTypes.pitchTypes);
const isAuthenticated = computed(() => store.state.auth.isAuthenticated); import PlayerContent from "@/components/PlayerComponent.vue"
import CoachContent from "@/components/CoachComponent.vue";
if (user.value === undefined || user.value === null || user.value === '') { if (user.value === undefined || user.value === null || user.value === '') {
router.push({ path: '/login' }); router.push({ path: '/login' });
@ -66,43 +54,12 @@ const addPlayer = () => {
router.push({ path: '/player/create' }); router.push({ path: '/player/create' });
} }
const items = reactive<Bullpen[]>([]);
const page = ref(0);
const generateItems = () => {
BullpenSessionService
.fetchAllByUser(user.value.id, page.value, 10)
.then((bullpens) => {
++page.value;
bullpens.data.forEach((bullpen: Bullpen) => {
items.push(bullpen);
})
}, error => {
console.log(error);
});
};
const formatDate = (date: Date) => {
return dayjs(date).format('YYYY.MM.DD HH:mm');
}
const determinePitchTypeName = (id: number): string => {
const pitchType = pitchTypes.value.find((pitchType: PitchType) => pitchType.id === id);
return pitchType?.name ?? 'Unknown';
}
const ionInfinite = (event: InfiniteScrollCustomEvent) => {
generateItems();
setTimeout(() => event.target.complete(), 500);
};
generateItems();
</script> </script>
<template> <template>
<ion-page id="user-menu"> <ion-page>
<ion-header> <ion-header>
<ion-toolbar> <ion-toolbar>
<ion-buttons slot="start"> <ion-buttons slot="start">
@ -115,23 +72,8 @@ generateItems();
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-list> <PlayerContent v-if="TokenService.isPlayer()" />
<ion-item v-for="(bullpen, index) in items" :key="index"> <CoachContent v-else-if="TokenService.isCoach()" />
<ion-icon aria-hidden="true" :icon="baseball" slot="start"></ion-icon>
<ion-label>Bullpen from ({{formatDate(bullpen.startedAt)}})</ion-label>
<ion-list v-for="(pitch, index) in bullpen.pitches" :key="index">
<ion-item>
<ion-icon slot="start" :icon="baseballOutline" class="icon-login"></ion-icon>
<ion-label>{{determinePitchTypeName(pitch.pitchTypeId)}}</ion-label>
<ion-badge>{{pitch.aimedArea}}</ion-badge>
<ion-badge>{{pitch.hitArea}}</ion-badge>
</ion-item>
</ion-list>
</ion-item>
</ion-list>
<ion-infinite-scroll @ionInfinite="ionInfinite">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
<!-- <div class="user-container">--> <!-- <div class="user-container">-->
<!-- <ion-avatar class="user-avatar">--> <!-- <ion-avatar class="user-avatar">-->
<!-- <template v-if="userImage">--> <!-- <template v-if="userImage">-->

View File

@ -68,7 +68,7 @@ const submit = handleSubmit((values, { resetForm }) => {
return store.dispatch('pitchTypes/fetch'); return store.dispatch('pitchTypes/fetch');
}).then(() => { }).then(() => {
resetForm(); resetForm();
router.push({path: '/home'}); return router.push({path: '/home'});
}).catch(error => { }).catch(error => {
loading.value = false; loading.value = false;
console.log(error); console.log(error);

View File

@ -1,12 +1,11 @@
services: services:
postgres: dev-db:
image: postgres:14-alpine image: postgres:15
container_name: postgres14
ports: ports:
- "5432:5432" - "5434:5432"
volumes:
- ~/apps/postgres:/var/lib/postgresql/data
environment: environment:
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
POSTGRES_DB: bullpen-dev POSTGRES_DB: bullpen-dev
volumes:
- /tmp/test-pgdata:/var/lib/postgresql/data