Compare commits
5 Commits
9c9d9c9413
...
8618533283
| Author | SHA1 | Date |
|---|---|---|
|
|
8618533283 | |
|
|
7891c1e229 | |
|
|
55c62b8a6b | |
|
|
068c1e9f63 | |
|
|
8481740ea5 |
|
|
@ -0,0 +1,37 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { IonInput, IonItem } from '@ionic/vue';
|
||||||
|
import { Field, useField } from 'vee-validate';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
name: string; // The name of the field for validation
|
||||||
|
label: string; // The label text
|
||||||
|
labelPlacement?: string; // Placement of the label (optional)
|
||||||
|
type?: string; // Input type, e.g., "text", "number", etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>(); // Define props for the component
|
||||||
|
const { value, errorMessage, ...fieldAttrs } = useField(props.name); // Setup field logic using vee-validate
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ion-item>
|
||||||
|
<Field name="name">
|
||||||
|
<IonInput
|
||||||
|
:label="label"
|
||||||
|
:label-placement="labelPlacement || 'stacked'"
|
||||||
|
v-model="value"
|
||||||
|
v-bind="fieldAttrs"
|
||||||
|
:type="type || 'text'"
|
||||||
|
>
|
||||||
|
<small v-if="errorMessage" class="error-message">{{ errorMessage }}</small>
|
||||||
|
</IonInput>
|
||||||
|
</Field>
|
||||||
|
</ion-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -2,7 +2,7 @@ import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { RouteRecordRaw, RouteLocationNormalizedGeneric } from 'vue-router';
|
import { RouteRecordRaw, RouteLocationNormalizedGeneric } from 'vue-router';
|
||||||
import PlayerList from '../views/PlayerList.vue'
|
import PlayerList from '../views/PlayerList.vue'
|
||||||
// import PlayerView from '../views/PlayerView.vue'
|
import PlayerView from '../views/PlayerView.vue'
|
||||||
import LoginView from '../views/LoginView.vue'
|
import LoginView from '../views/LoginView.vue'
|
||||||
import HomeView from '../views/HomeView.vue'
|
import HomeView from '../views/HomeView.vue'
|
||||||
import BullpenView from "@/views/BullpenView.vue";
|
import BullpenView from "@/views/BullpenView.vue";
|
||||||
|
|
@ -24,12 +24,10 @@ const routes: Array<RouteRecordRaw> = [
|
||||||
{ path: '/stats', component: BullpenListView },
|
{ path: '/stats', component: BullpenListView },
|
||||||
{ path: '/summary', component: BullpenSummaryView },
|
{ path: '/summary', component: BullpenSummaryView },
|
||||||
{
|
{
|
||||||
path: '/player/:id',
|
path: '/player',
|
||||||
name: 'EditPlayer',
|
component: PlayerView,
|
||||||
component: () => import('@/views/PlayerView.vue'),
|
|
||||||
props: (route: RouteLocationNormalizedGeneric) => ({
|
props: (route: RouteLocationNormalizedGeneric) => ({
|
||||||
player: route.state?.player || null, // Pass the player from the state if available
|
id: route.query.id
|
||||||
mode: route.query.mode || 'create', // Default to "create" if no query exists
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,5 @@ export default interface User {
|
||||||
roles: string[],
|
roles: string[],
|
||||||
createdAt: Date,
|
createdAt: Date,
|
||||||
updatedAt: Date,
|
updatedAt: Date,
|
||||||
|
auth: { email: string }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ const showStats = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const showProfile = () => {
|
const showProfile = () => {
|
||||||
router.push({ path: '/profile' });
|
router.push({ path: '/player', query: { id: user.value.id }});
|
||||||
};
|
};
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ const submit = handleSubmit((values, { resetForm }) => {
|
||||||
email: values.email,
|
email: values.email,
|
||||||
password: values.password,
|
password: values.password,
|
||||||
}).then((user: User) => {
|
}).then((user: User) => {
|
||||||
return store.dispatch('player/determinePlayer', user);
|
return store.dispatch('player/selectPlayer', user);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
resetForm();
|
resetForm();
|
||||||
onLogin();
|
onLogin();
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,22 @@ import {
|
||||||
import { Field, useForm } from 'vee-validate';
|
import { Field, useForm } from 'vee-validate';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { ref, computed, watch } from 'vue';
|
import { ref, onMounted, watch } from 'vue';
|
||||||
import Player from '@/types/Player';
|
import Player from '@/types/Player';
|
||||||
|
// import CrudField from '@/components/CrudField.vue'
|
||||||
|
import PlayerService from '@/services/PlayerService';
|
||||||
|
import {useRoute} from 'vue-router';
|
||||||
|
|
||||||
const props = defineProps<{ player?: Player }>();
|
const props = defineProps<{ player?: Player }>();
|
||||||
|
|
||||||
// Todo: create default player
|
|
||||||
const defaultPlayer: Player = {
|
const defaultPlayer: Player = {
|
||||||
id: 0,
|
id: 0,
|
||||||
gender: 'male',
|
gender: 'male',
|
||||||
bats: 'R',
|
bats: 'R',
|
||||||
throws: 'R',
|
throws: 'R',
|
||||||
|
height: 0,
|
||||||
|
weight: 0,
|
||||||
|
state: 'active',
|
||||||
jerseyNumber: 0,
|
jerseyNumber: 0,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
|
@ -41,12 +46,17 @@ const defaultPlayer: Player = {
|
||||||
roles: [],
|
roles: [],
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
auth: {
|
||||||
|
email: ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ...
|
|
||||||
|
|
||||||
// Computed reactive player object: Either use the provided player or initialize with default
|
// Computed reactive player object: Either use the provided player or initialize with default
|
||||||
const player = ref<Player>(props.player ? { ...props.player } : { ...defaultPlayer });
|
const player = ref<Player>(props.player ? { ...props.player } : { ...defaultPlayer });
|
||||||
|
const isEdit = ref(false);
|
||||||
|
const route = useRoute();
|
||||||
|
const formattedDate = ref(player.value.user.dateOfBirth.toISOString().split('T')[0]);
|
||||||
|
|
||||||
const schema = yup.object({
|
const schema = yup.object({
|
||||||
email: yup
|
email: yup
|
||||||
|
|
@ -86,23 +96,40 @@ const schema = yup.object({
|
||||||
.required('Height is required'),
|
.required('Height is required'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { errors, handleSubmit, defineField } = useForm({
|
const { errors, resetForm, handleSubmit, defineField } = useForm({
|
||||||
validationSchema: schema,
|
validationSchema: schema,
|
||||||
initialValues: player.value
|
initialValues: player.value
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watch for changes in props.player to reset form when editing
|
// Watch for changes in props.player to reset form when editing
|
||||||
watch(
|
watch(
|
||||||
() => props.player,
|
() => route.params.id || route.query.id,
|
||||||
(newPlayer) => {
|
(newId) => {
|
||||||
if (newPlayer) {
|
if (newId) {
|
||||||
resetForm({ values: { ...newPlayer } });
|
fetchPlayerById(Number(newId)); // Fetch player when the ID changes
|
||||||
} else {
|
|
||||||
resetForm({ values: { ...defaultPlayer } });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const id = route.params.id || route.query.id; // Get ID from params or query
|
||||||
|
if (id) {
|
||||||
|
fetchPlayerById(Number(id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchPlayerById = async (id: number) => {
|
||||||
|
try {
|
||||||
|
const data = await PlayerService.fetchByUserId(id); // Fetch the player from the API
|
||||||
|
player.value = { ...data }; // Load the API response into the form
|
||||||
|
resetForm({ values: { ...player.value } }); // Reset form with fetched data
|
||||||
|
formattedDate.value = dayjs(player.value.user.dateOfBirth).format('YYYY-MM-DD');
|
||||||
|
isEdit.value = true; // Set to edit mode
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching player data:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Submit logic: Differentiate between create and update
|
// Submit logic: Differentiate between create and update
|
||||||
const submit = handleSubmit((values: Player) => {
|
const submit = handleSubmit((values: Player) => {
|
||||||
if (player.value.id) {
|
if (player.value.id) {
|
||||||
|
|
@ -112,12 +139,15 @@ const submit = handleSubmit((values: Player) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const isEdit = computed(() => !!player.value.id);
|
const onDateChange = (value: string) => {
|
||||||
|
formattedDate.value = value; // Update formatted string
|
||||||
|
player.value.user.dateOfBirth = new Date(value); // Convert to Date object and update
|
||||||
|
};
|
||||||
|
|
||||||
const [email, emailAttrs] = defineField('user.email');
|
const [email, emailAttrs] = defineField('user.auth.email');
|
||||||
const [firstName, firstNameAttrs] = defineField('user.firstName');
|
const [firstName, firstNameAttrs] = defineField('user.firstName');
|
||||||
const [lastName, lastNameAttrs] = defineField('user.lastName');
|
const [lastName, lastNameAttrs] = defineField('user.lastName');
|
||||||
const [dateOfBirth, dateOfBirthAttrs] = defineField('user.dateOfBirth');
|
// const [dateOfBirth, dateOfBirthAttrs] = defineField('formattedDate');
|
||||||
const [gender, genderAttrs] = defineField('gender');
|
const [gender, genderAttrs] = defineField('gender');
|
||||||
const [bats, batsAttrs] = defineField('bats');
|
const [bats, batsAttrs] = defineField('bats');
|
||||||
const [throws, throwsAttrs] = defineField('throws');
|
const [throws, throwsAttrs] = defineField('throws');
|
||||||
|
|
@ -142,9 +172,11 @@ const [height, heightAttrs] = defineField('height');
|
||||||
<ion-card-title>Personal Data</ion-card-title>
|
<ion-card-title>Personal Data</ion-card-title>
|
||||||
</ion-card-header>
|
</ion-card-header>
|
||||||
<ion-card-content>
|
<ion-card-content>
|
||||||
|
<!-- <CrudField name="user.email" label="Email" labelPlacement="floating"/>-->
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<Field name="email">
|
<Field name="email">
|
||||||
<ion-input
|
<ion-input
|
||||||
|
readonly: true
|
||||||
label="E-Mail"
|
label="E-Mail"
|
||||||
label-placement="stacked"
|
label-placement="stacked"
|
||||||
v-model="email"
|
v-model="email"
|
||||||
|
|
@ -159,6 +191,7 @@ const [height, heightAttrs] = defineField('height');
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<Field name="firstName">
|
<Field name="firstName">
|
||||||
<ion-input
|
<ion-input
|
||||||
|
readonly: true
|
||||||
label="First Name"
|
label="First Name"
|
||||||
label-placement="stacked"
|
label-placement="stacked"
|
||||||
v-model="firstName"
|
v-model="firstName"
|
||||||
|
|
@ -189,8 +222,8 @@ const [height, heightAttrs] = defineField('height');
|
||||||
<ion-input
|
<ion-input
|
||||||
label="Date of Birth"
|
label="Date of Birth"
|
||||||
label-placement="stacked"
|
label-placement="stacked"
|
||||||
v-model="dateOfBirth"
|
v-model="formattedDate"
|
||||||
v-bind="dateOfBirthAttrs"
|
@input="onDateChange($event.target.value)"
|
||||||
type="date"
|
type="date"
|
||||||
>
|
>
|
||||||
<small v-if="errors['user.dateOfBirth']" class="error-message">{{ errors['user.dateOfBirth'] }}</small>
|
<small v-if="errors['user.dateOfBirth']" class="error-message">{{ errors['user.dateOfBirth'] }}</small>
|
||||||
|
|
@ -230,9 +263,9 @@ const [height, heightAttrs] = defineField('height');
|
||||||
v-bind="batsAttrs"
|
v-bind="batsAttrs"
|
||||||
interface="action-sheet"
|
interface="action-sheet"
|
||||||
>
|
>
|
||||||
<ion-select-option value="R">Right</ion-select-option>
|
<ion-select-option value="right">Right</ion-select-option>
|
||||||
<ion-select-option value="L">Left</ion-select-option>
|
<ion-select-option value="left">Left</ion-select-option>
|
||||||
<ion-select-option value="S">Switch</ion-select-option>
|
<ion-select-option value="both">Switch</ion-select-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
</Field>
|
</Field>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
@ -244,9 +277,10 @@ const [height, heightAttrs] = defineField('height');
|
||||||
label-placement="stacked"
|
label-placement="stacked"
|
||||||
v-model="throws"
|
v-model="throws"
|
||||||
v-bind="throwsAttrs"
|
v-bind="throwsAttrs"
|
||||||
|
interface="action-sheet"
|
||||||
>
|
>
|
||||||
<ion-select-option value="R">Right</ion-select-option>
|
<ion-select-option value="right">Right</ion-select-option>
|
||||||
<ion-select-option value="L">Left</ion-select-option>
|
<ion-select-option value="left">Left</ion-select-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
</Field>
|
</Field>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ const db = require("../models/index");
|
||||||
const {registerUser} = require("../helper/user.helper");
|
const {registerUser} = require("../helper/user.helper");
|
||||||
const Player = db.Player;
|
const Player = db.Player;
|
||||||
const User = db.User;
|
const User = db.User;
|
||||||
|
const Auth = db.Auth;
|
||||||
const Op = db.Sequelize.Op;
|
const Op = db.Sequelize.Op;
|
||||||
|
|
||||||
exports.insert = (req, res) => {
|
exports.insert = (req, res) => {
|
||||||
|
|
@ -63,7 +64,15 @@ exports.findOneByUserId = (req, res) => {
|
||||||
|
|
||||||
Player.findOne({
|
Player.findOne({
|
||||||
where: { userId: userId },
|
where: { userId: userId },
|
||||||
include: { model: User }
|
include: {
|
||||||
|
model: User,
|
||||||
|
as: 'user',
|
||||||
|
include: {
|
||||||
|
model: Auth,
|
||||||
|
as: 'auth',
|
||||||
|
attributes: ['email']
|
||||||
|
}
|
||||||
|
}
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
res.send(data);
|
res.send(data);
|
||||||
|
|
@ -73,4 +82,4 @@ exports.findOneByUserId = (req, res) => {
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
res.status(500).send({message: `Error retrieving player with user id=${userId} (${err.message}).`});
|
res.status(500).send({message: `Error retrieving player with user id=${userId} (${err.message}).`});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ module.exports = (sequelize, DataTypes) => {
|
||||||
type: DataTypes.ENUM('left', 'right', 'both'),
|
type: DataTypes.ENUM('left', 'right', 'both'),
|
||||||
},
|
},
|
||||||
throws: {
|
throws: {
|
||||||
type: DataTypes.ENUM('left', 'right', 'both'),
|
type: DataTypes.ENUM('left', 'right'),
|
||||||
},
|
},
|
||||||
jerseyNumber: {
|
jerseyNumber: {
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,16 @@ module.exports = {
|
||||||
|
|
||||||
const roles = await queryInterface.select(null, 'Roles');
|
const roles = await queryInterface.select(null, 'Roles');
|
||||||
const players = [
|
const players = [
|
||||||
{ email: 'ryan.nolan@bullpen.com', password: 'ryan$123', firstName: 'Nolan', lastName: 'Ryan', dateOfBirth: new Date(1947, 1, 31), jerseyNumber: 17, gender: 'male', bats: 'right', throws: 'right', state: 'active' },
|
{ email: 'ryan.nolan@bullpen.com', password: 'ryan$123', firstName: 'Nolan', lastName: 'Ryan', dateOfBirth: new Date(1947, 0, 31), jerseyNumber: 17, gender: 'male', bats: 'right', throws: 'right', state: 'active' },
|
||||||
{ email: 'sandy.koufax@bullpen.com', password: 'sandy$123', firstName: 'Sandy', lastName: 'Koufax', dateOfBirth: new Date(1935, 12, 30), jerseyNumber: 18, gender: 'male', bats: 'right', throws: 'right', state: 'active' },
|
{ email: 'sandy.koufax@bullpen.com', password: 'sandy$123', firstName: 'Sandy', lastName: 'Koufax', dateOfBirth: new Date(1935, 11, 30), jerseyNumber: 18, gender: 'male', bats: 'right', throws: 'right', state: 'active' },
|
||||||
{ email: 'pedro.martinez@bullpen.com', password: 'pedro$123', firstName: 'Pedro', lastName: 'Martinez', dateOfBirth: new Date(1971, 10, 25), jerseyNumber: 19, gender: 'male', bats: 'right', throws: 'right', state: 'active' },
|
{ email: 'pedro.martinez@bullpen.com', password: 'pedro$123', firstName: 'Pedro', lastName: 'Martinez', dateOfBirth: new Date(1971, 9, 25), jerseyNumber: 19, gender: 'male', bats: 'right', throws: 'right', state: 'active' },
|
||||||
{ email: 'randy.johnson@bullpen.com', password: 'randy$123', firstName: 'Randy', lastName: 'Johnson', dateOfBirth: new Date(1963, 9, 10), jerseyNumber: 20, gender: 'male', bats: 'right', throws: 'right', state: 'active' },
|
{ email: 'randy.johnson@bullpen.com', password: 'randy$123', firstName: 'Randy', lastName: 'Johnson', dateOfBirth: new Date(1963, 8, 10), jerseyNumber: 20, gender: 'male', bats: 'right', throws: 'right', state: 'active' },
|
||||||
];
|
];
|
||||||
|
|
||||||
await createPlayer(queryInterface, roles, players);
|
await createPlayer(queryInterface, roles, players);
|
||||||
|
|
||||||
await createUsers(queryInterface, roles, [
|
await createUsers(queryInterface, roles, [
|
||||||
{ email: 'sparky.anderson@bullpen.com', password: 'sparky$123', firstName: 'Sparky', lastName: 'Anderson', dateOfBirth: new Date(1934, 22, 2), roles: ['coach', 'admin'] },
|
{ email: 'sparky.anderson@bullpen.com', password: 'sparky$123', firstName: 'Sparky', lastName: 'Anderson', dateOfBirth: new Date(1934, 1, 22), roles: ['coach', 'admin'] },
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue