renamed login and home view, restructured login view and router, added logo
This commit is contained in:
parent
a18559a77a
commit
9071886991
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
|
|
@ -1,45 +1,24 @@
|
|||
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||
import { useStore } from 'vuex'
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import PitcherList from '../views/PitcherList.vue'
|
||||
import Login from '../views/Login.vue'
|
||||
import Home from '../views/Home.vue'
|
||||
import LoginView from '../views/LoginView.vue'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
import PreparePitch from "@/views/PreparePitch.vue";
|
||||
import FinalizePitch from "@/views/FinalizePitch.vue";
|
||||
import BullpenStats from "@/views/BullpenStats.vue";
|
||||
import SetupView from '@/views/SetupView.vue';
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/login'
|
||||
}, {
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: Login
|
||||
}, {
|
||||
path: '/home',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
}, {
|
||||
path: '/pitchers',
|
||||
name: 'Pitchers',
|
||||
component: PitcherList
|
||||
}, {
|
||||
path: '/bullpen',
|
||||
name: 'PreparePitch',
|
||||
component: PreparePitch
|
||||
}, {
|
||||
path: '/prepare',
|
||||
name: 'PreparePitch',
|
||||
component: PreparePitch
|
||||
}, {
|
||||
path: '/finalize',
|
||||
name: 'FinalizePitch',
|
||||
component: FinalizePitch
|
||||
}, {
|
||||
path: '/stats',
|
||||
name: 'BullpenStats',
|
||||
component: BullpenStats
|
||||
}
|
||||
{ path: '/', redirect: '/login' },
|
||||
{ path: '/login', component: LoginView },
|
||||
{ path: '/setup', component: SetupView },
|
||||
{ path: '/home', component: HomeView },
|
||||
{ path: '/pitchers', component: PitcherList },
|
||||
{ path: '/bullpen', component: PreparePitch },
|
||||
{ path: '/prepare', component: PreparePitch },
|
||||
{ path: '/finalize', component: FinalizePitch },
|
||||
{ path: '/stats', component: BullpenStats }
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
|
@ -47,4 +26,24 @@ const router = createRouter({
|
|||
routes
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const store = useStore();
|
||||
const serverAddress = localStorage.getItem('serverAddress');
|
||||
const isAuthenticated = store.state.auth.isAuthenticated;
|
||||
|
||||
if (to.meta.requiresAuth && !isAuthenticated) {
|
||||
return next('/login');
|
||||
}
|
||||
|
||||
if (!serverAddress && to.path !== '/setup') {
|
||||
return next('/setup');
|
||||
}
|
||||
|
||||
if (serverAddress && to.path === '/setup') {
|
||||
return next('/login');
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
export default router
|
||||
|
|
|
|||
|
|
@ -1,2 +1,50 @@
|
|||
/* For information on how to create your own theme, please see:
|
||||
http://ionicframework.com/docs/theming/ */
|
||||
/*:root {*/
|
||||
/* --ion-background-color: #7c3b6a;*/
|
||||
/* --ion-text-color: white;*/
|
||||
/* --ion-tab-bar-background: #7c3b6a;*/
|
||||
/* --ion-toolbar-background: #7c3b6a;;*/
|
||||
/* --ion-toolbar-color: white;*/
|
||||
/*}*/
|
||||
|
||||
.custom-back {
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||
border-radius: 5px;
|
||||
font-size: 0.8rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
.custom-button {
|
||||
|
||||
--border-radius: 5px !important;
|
||||
--background: rgb(236, 149, 35);
|
||||
--background-activated: rgb(192, 125, 38);
|
||||
--background-focused: rgb(192, 125, 38);
|
||||
font-size: 1.2rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.custom-input {
|
||||
|
||||
--background: #834e76;
|
||||
--padding-bottom: 1rem;
|
||||
--padding-top: 1rem;
|
||||
--padding-start: 1rem;
|
||||
--padding-end: 1rem;
|
||||
border-radius: 10px !important;
|
||||
margin-top: 0.25rem;
|
||||
transition: all 0.2s linear;
|
||||
}
|
||||
|
||||
.ion-label {
|
||||
padding-left: 0.2rem;
|
||||
padding-right: 0.5rem;
|
||||
color: #d3a6c7;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,146 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref, onMounted } from "vue";
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonMenuButton,
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonTitle,
|
||||
IonRow,
|
||||
IonCol,
|
||||
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';
|
||||
import { useStore } from 'vuex'
|
||||
import * as yup from 'yup';
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const { meta, errors, handleSubmit, defineField } = useForm({
|
||||
validationSchema: yup.object({
|
||||
email: yup.string().email().required('No email provided.'),
|
||||
password: yup.string().required('No password provided.').min(8)
|
||||
})
|
||||
});
|
||||
|
||||
const [email, emailAttrs] = defineField('email');
|
||||
const [password, passwordAttrs] = defineField('password');
|
||||
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
|
||||
const loggedIn = computed(() => store.state.auth.isAuthenticated);
|
||||
|
||||
onMounted(() => {
|
||||
if (loggedIn.value) {
|
||||
onLogin();
|
||||
}
|
||||
});
|
||||
|
||||
const submit = handleSubmit((values, { resetForm }) => {
|
||||
store.dispatch('auth/login', {
|
||||
email: values.email,
|
||||
password: values.password,
|
||||
}).then(
|
||||
() => {
|
||||
resetForm();
|
||||
onLogin();
|
||||
},
|
||||
error => {
|
||||
loading.value = false;
|
||||
console.log(error);
|
||||
// message.value =
|
||||
// (error.response && error.response.data && error.response.data.message) ||
|
||||
// error.message ||
|
||||
// error.toString();
|
||||
}
|
||||
);
|
||||
}, ({errors}) => {
|
||||
console.log(errors);
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
const onLogin = () => {
|
||||
console.log('check if pitch types are available.');
|
||||
|
||||
store.dispatch('pitchTypes/initialize').then(() => {
|
||||
router.push({ path: '/home'});
|
||||
}, error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Login</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<form @submit="submit">
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon :icon="personOutline" class="icon-login"></ion-icon>
|
||||
<ion-input name="user" v-model="email" v-bind="emailAttrs" type="text" required placeholder="Username"></ion-input>
|
||||
</ion-item>
|
||||
<br />
|
||||
<ion-item>
|
||||
<ion-icon :icon="lockClosedOutline" class="icon-login"></ion-icon>
|
||||
<ion-input name="password" v-model="password" v-bind="passwordAttrs" type="password" required placeholder="Password"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-button :disabled="!meta.valid" type="submit" fill="solid" expand="full">
|
||||
Login
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<pre>errors: {{ errors }}</pre>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</form>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.login-logo {
|
||||
padding: 20px 0;
|
||||
min-height: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-logo img {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref, onMounted } from "vue";
|
||||
import {
|
||||
IonPage,
|
||||
IonIcon,
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonInput
|
||||
} from "@ionic/vue";
|
||||
|
||||
// Todo: https://github.com/alanmontgomery/ionic-react-login
|
||||
|
||||
import {
|
||||
lockClosedOutline,
|
||||
personOutline,
|
||||
// arrowBack,
|
||||
// shapesOutline
|
||||
} from 'ionicons/icons';
|
||||
import {Field, useForm} from 'vee-validate';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex'
|
||||
import * as yup from 'yup';
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const schema = yup.object({
|
||||
email: yup.string().email('Invalid email address').required('Email is required'),
|
||||
password: yup
|
||||
.string()
|
||||
.min(8, 'Minimum 8 characters')
|
||||
.matches(/\d/, 'Must include a number')
|
||||
.matches(/[^A-Za-z\d]/, 'Must include a special character')
|
||||
.required('Password is required'),
|
||||
});
|
||||
|
||||
const { meta, errors, handleSubmit, defineField } = useForm({
|
||||
validationSchema: schema
|
||||
});
|
||||
|
||||
const [email, emailAttrs] = defineField('email');
|
||||
const [password, passwordAttrs] = defineField('password');
|
||||
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
|
||||
const loggedIn = computed(() => store.state.auth.isAuthenticated);
|
||||
|
||||
onMounted(() => {
|
||||
if (loggedIn.value) {
|
||||
onLogin();
|
||||
}
|
||||
});
|
||||
|
||||
const submit = handleSubmit((values, { resetForm }) => {
|
||||
store.dispatch('auth/login', {
|
||||
email: values.email,
|
||||
password: values.password,
|
||||
}).then(
|
||||
() => {
|
||||
resetForm();
|
||||
onLogin();
|
||||
},
|
||||
error => {
|
||||
loading.value = false;
|
||||
console.log(error);
|
||||
// message.value =
|
||||
// (error.response && error.response.data && error.response.data.message) ||
|
||||
// error.message ||
|
||||
// error.toString();
|
||||
}
|
||||
);
|
||||
}, ({errors}) => {
|
||||
console.log(errors);
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
const onLogin = () => {
|
||||
console.log('check if pitch types are available.');
|
||||
|
||||
store.dispatch('pitchTypes/initialize').then(() => {
|
||||
router.push({ path: '/home'});
|
||||
}, error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-content>
|
||||
<div class="login-container">
|
||||
<div class="top-section">
|
||||
<h1 class="login-title">Login</h1>
|
||||
|
||||
<div class="logo-container">
|
||||
<img src="../assets/Bonn_Capitals_Insignia.png" alt="Logo" class="logo" />
|
||||
</div>
|
||||
</div>
|
||||
<form @submit="submit" class="form-container">
|
||||
<div class="input-group">
|
||||
<div class="label-row">
|
||||
<label class="input-label" for="email">Email</label>
|
||||
<small v-if="!meta.valid" class="error-message">{{ errors.email }}</small>
|
||||
</div>
|
||||
<Field id="email" name="email" type="email" >
|
||||
<div class="input-with-icon">
|
||||
<ion-icon :icon="personOutline" class="icon-login"></ion-icon>
|
||||
<ion-input
|
||||
name="email"
|
||||
v-model="email"
|
||||
v-bind="emailAttrs"
|
||||
class="rounded-input"
|
||||
:class="{ 'input-invalid': !meta.valid && errors.email }"
|
||||
type="text" required placeholder="Email"></ion-input>
|
||||
</div>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="label-row">
|
||||
<label class="input-label" for="password">Password</label>
|
||||
<small v-if="!meta.valid" class="error-message">{{ errors.password }}</small>
|
||||
</div>
|
||||
<Field id="password" name="password" type="password">
|
||||
<div class="input-with-icon">
|
||||
<ion-icon :icon="lockClosedOutline" class="icon-login"></ion-icon>
|
||||
<ion-input
|
||||
name="password"
|
||||
v-model="password"
|
||||
v-bind="passwordAttrs"
|
||||
type="password"
|
||||
class="rounded-input"
|
||||
:class="{ 'input-invalid': !meta.valid && errors.password }"
|
||||
required placeholder="Password"></ion-input>
|
||||
</div>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<ion-button expand="block" class="rounded-button" type="submit">
|
||||
Login
|
||||
</ion-button>
|
||||
</form>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.login-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-section {
|
||||
background-color: #cbeec9;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 25vh;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
padding-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.label-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 4px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.input-with-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.rounded-input {
|
||||
--border-radius: 50%;
|
||||
padding: 12px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.input-invalid {
|
||||
background-color: lightcoral;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: red;
|
||||
font-size: 0.75rem;
|
||||
text-align: right;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.rounded-button {
|
||||
--border-radius: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import {IonButton, IonContent, IonCol, IonHeader, IonInput, IonPage, IonTitle, IonToolbar} from '@ionic/vue';
|
||||
|
||||
const serverAddress = ref('');
|
||||
const router = useRouter();
|
||||
|
||||
const saveServerAddress = () => {
|
||||
localStorage.setItem('serverAddress', serverAddress.value);
|
||||
router.push('/login');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Setup Backend Server</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<ion-row>
|
||||
<ion-col class="ion-padding">
|
||||
<ion-input name="ipaddress" v-model="serverAddress" type="text" required placeholder="Enter server address"></ion-input>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button type="button" fill="solid" expand="full" @click="saveServerAddress">Save</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Loading…
Reference in New Issue