feat(login): create dev login bypass for testing, refactor main layout for consistency and more intuitive UI

This commit is contained in:
Nicolas Drolet 2025-08-07 15:40:19 -04:00
parent 7399232ed8
commit 5a4cba5588
29 changed files with 72 additions and 103 deletions

View File

@ -5,7 +5,7 @@ import messages from 'src/i18n';
export type MessageLanguages = keyof typeof messages;
// Type-define 'en-US' as the master schema for the resource
export type MessageSchema = typeof messages['en-ca'];
export type MessageSchema = typeof messages['en'];
// See https://vue-i18n.intlify.dev/guide/advanced/typescript.html#global-resource-schema-type-definition
/* eslint-disable @typescript-eslint/no-empty-object-type */
@ -23,7 +23,7 @@ declare module 'vue-i18n' {
export default defineBoot(({ app }) => {
const i18n = createI18n<{ message: MessageSchema }, MessageLanguages>({
locale: 'fr-ca',
locale: 'fr',
legacy: false,
messages,
});

View File

@ -23,14 +23,13 @@ export default {
clearFilter: 'Clear filter',
},
navBar: {
navItem_1: 'Users list',
navItem_2: 'Shift validations',
menuItem_1: 'Profile',
menuItem_2: 'Help',
menuItem_3: 'Log Out',
menuItem_4: 'Time Sheet',
menuItem_5: 'Annual calendar',
mobileIndexTitle: 'Hi',
userMenuEmployeeList: 'Employee list',
userMenuShiftValidation: 'Timesheet Approval',
userMenuProfile: 'Profile',
userMenuHelp: 'Help',
userMenuLogout: 'Log Out',
userMenuTimesheet: 'Timesheet',
userMenuCalendar: 'Calendar',
},
notFoundPage: {
pageTitle: 'Oops. Nothing here...',

View File

@ -162,14 +162,13 @@ export default {
clearFilter: 'Effacer le filtre',
},
navBar: {
navItem_1: 'Liste des utilisateurs',
navItem_2: 'Validations des quarts de travail',
menuItem_1: 'Profil',
menuItem_2: 'Aide',
menuItem_3: 'Déconnexion',
menuItem_4: 'Carte de temps',
menuItem_5: 'Calendrier annuel',
mobileIndexTitle: 'Bonjour',
userMenuEmployeeList: 'Liste employés',
userMenuShiftValidation: 'Valider les heures',
userMenuProfile: 'Profil',
userMenuHelp: 'Aide',
userMenuLogout: 'Déconnexion',
userMenuTimesheet: 'Carte de temps',
userMenuCalendar: 'Calendrier annuel',
},
notFoundPage: {
pageTitle: 'Oops. Rien ici...',

View File

@ -2,6 +2,6 @@ import enCA from './en-ca';
import frCA from './fr-ca';
export default {
'en-ca': enCA,
'fr-ca': frCA,
'en': enCA,
'fr': frCA,
};

View File

@ -2,11 +2,13 @@
import { RouterView } from 'vue-router';
import HeaderBar from 'src/modules/shared/components/navigation/header-bar.vue';
import FooterBar from 'src/modules/shared/components/navigation/footer-bar.vue';
import RightDrawer from 'src/modules/shared/components/navigation/right-drawer.vue';
</script>
<template>
<q-layout view="hHh lpR fFf">
<HeaderBar />
<RightDrawer />
<q-page-container>
<router-view class="q-pa-sm" />
</q-page-container>

View File

@ -1,48 +0,0 @@
/* eslint-disable */
import { defineStore } from "pinia";
import { User } from "src/modules/users/types/user-interface";
import { AuthState } from "./types/auth-interface";
import { AuthService } from "./services/services-auth";
import { computed, ref } from "vue";
export const useAuthStore = defineStore('auth', () => {
const user = ref ({
firstName: 'Unknown',
lastName: 'Unknown',
email: 'guest@guest.com',
role: 'guest'
} as User);
const error = ref("");
const isAuthorizedUser = computed( () => user.value.role !== 'guest');
const login = () => {
//TODO: manage customer login process
};
const oidcLogin = () => {
const oidcPopup = AuthService.oidcLogin();
if (!oidcPopup) {
error.value = "You have popups blocked on this website!";
}
};
const logout = () => {
return "logout";
};
const setUser = (currentUser: User, isBypass: boolean = false, bypassRole?: string) => {
if (isBypass) {
user.value = {
firstName: "Testing",
lastName: "Tester",
email: "testingT@targointernet.com",
role: bypassRole || "guest"} as User;
} else {
user.value = currentUser;
}
};
return { login, oidcLogin, logout, setUser };
});

View File

@ -1,33 +1,36 @@
import { useAuthStore } from "../auth-store";
import { useAuthStore } from "../../../stores/auth-store";
import type { User } from "src/modules/users/types/user-interface";
export const useAuthApi = () => {
const authStore = useAuthStore();
const login = () => {
const response = authStore.login();
return response;
authStore.login();
};
const oidcLogin = () => {
return {status: 200, message: 'sent an openid connect login request'};
authStore.oidcLogin();
};
const logout = () => {
return {status: 200, message: 'sent a logout request'};
authStore.logout();
};
const isLoggedIn = () => {
return {status: 200, message: 'sent a isLoggedIn request'};
const isAuthorizedUser = () => {
return authStore.isAuthorizedUser;
};
const forgotPassword = (email: string) => {
return {status: 200, message: 'sent a password reset request with email ' + email};
};
const setUser = (currentUser: User) => {
authStore.user = currentUser;
}
return {
login,
oidcLogin,
logout,
isLoggedIn,
forgotPassword,
isAuthorizedUser,
setUser,
};
};

View File

@ -1,12 +1,28 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useAuthStore } from '../auth-store';
import { useAuthApi } from '../composables/use-auth-api';
import type { User } from 'src/modules/users/types/user-interface';
import { useRouter } from 'vue-router';
const authStore = useAuthStore();
const authApi = useAuthApi();
const email = ref('');
const isShowingEmployeeLoginButton = ref(false);
const isRemembered = ref(false);
const router = useRouter();
const setBypassUser = (bypassRole: string) => {
authApi.setUser({
firstName: "Testing",
lastName: bypassRole,
email: "testingT@targointernet.com",
role: bypassRole || "guest"
} as User);
router.push({ name: 'dashboard' }).catch( err => {
console.error('Router navigation failed: ', err);
});
}
watch(email, (value) => {
isShowingEmployeeLoginButton.value = value.includes('@targ');
@ -31,7 +47,7 @@
</div>
</q-card-section>
<q-form class="q-gutter-sm" @submit="authStore.login">
<q-form class="q-gutter-sm" @submit="authApi.login">
<q-input dense outlined label-color="primary" v-model="email" :label="$t('loginPage.email')" />
<q-card-section class="q-ma-none q-pa-none">
@ -61,7 +77,7 @@
<q-slide-transition>
<div v-if="isShowingEmployeeLoginButton">
<transition slow enter-active-class="animated zoomIn" leave-active-class="animated zoomOut">
<q-btn rounded push color="primary" @click="authStore.oidcLogin" :label="$t('loginPage.employeeLoginButton')" class="full-width row" icon="img:src/assets/logo-targo-simple.svg" />
<q-btn rounded push color="primary" @click="authApi.oidcLogin" :label="$t('loginPage.employeeLoginButton')" class="full-width row" icon="img:src/assets/logo-targo-simple.svg" />
</transition>
</div>
</q-slide-transition>
@ -76,10 +92,10 @@
<q-separator color="primary" />
<q-card-section>
<q-btn-group push rounded>
<q-btn push color="primary" text-color="white" label="ACCOUNTING" icon="attach_money" />
<q-btn push color="primary" text-color="white" label="SUPERVISOR" icon="supervisor_account"/>
<q-btn push color="primary" text-color="white" label="HR" icon="diversity_3"/>
<q-btn push color="primary" text-color="white" label="EMPLOYEE" icon="support_agent"/>
<q-btn push color="primary" text-color="white" label="ACCOUNTING" icon="attach_money" @click="setBypassUser('accounting')"/>
<q-btn push color="primary" text-color="white" label="SUPERVISOR" icon="supervisor_account" @click="setBypassUser('supervisor')"/>
<q-btn push color="primary" text-color="white" label="HR" icon="diversity_3" @click="setBypassUser('human resources')"/>
<q-btn push color="primary" text-color="white" label="EMPLOYEE" icon="support_agent" @click="setBypassUser('employee')"/>
</q-btn-group>
</q-card-section>

View File

@ -3,15 +3,18 @@ import { api } from 'src/boot/axios';
export const AuthService = {
// Will likely be deprecated and relegated to Authentik
login: (credentials: { email: string; password: string }) => {
// TODO: possibly add some kind of login logic, but will most likely be redirected
// to Authentik as well.
api.post('/auth/login', credentials)
login: () => {
//TODO: OIDC customer sign-in, eventually
},
oidcLogin: () => {
// TODO: OIDC login logic
api.post('/auth/oidclogin');
oidcLogin: (): Window | null => {
window.addEventListener('message', (event) => {
if (event.data.type === 'authSuccess') {
//some kind of logic here to set user in store
}
})
return window.open('http://localhost:3000/auth/v1/login', 'authPopup', 'width=600,height=800');
},
logout: () => {

View File

@ -1,6 +0,0 @@
export interface User {
firstName: string;
lastName: string;
email: string;
role: string;
}

View File

@ -1,7 +1,7 @@
import { defineRouter } from '#q-app/wrappers';
import { createMemoryHistory, createRouter, createWebHashHistory, createWebHistory, } from 'vue-router';
import routes from './routes';
import { useAuthStore } from 'src/modules/auth/auth-store';
import { useAuthStore } from 'src/stores/auth-store';
/*
* If not building with SSR mode, you can
@ -30,7 +30,7 @@ export default defineRouter(function (/* { store, ssrContext } */) {
Router.beforeEach((destinationPage) => {
const authStore = useAuthStore();
if (destinationPage.meta.requiresAuth && !authStore.isAuthorizedUser()) {
if (destinationPage.meta.requiresAuth && !authStore.isAuthorizedUser) {
console.log("access denied!")
return { name: 'login' };
}

View File

@ -8,6 +8,7 @@ const routes: RouteRecordRaw[] = [
children: [
{
path: '',
name: 'dashboard',
component: () => import('src/pages/test-page.vue'),
},
],