refactor(approvals): remake entire card appearance, hook up to pay period picker to backend, make the period picker actually functional.

This commit is contained in:
Nicolas Drolet 2025-08-20 17:03:32 -04:00
parent 0c1d214420
commit 21b98ec825
14 changed files with 434 additions and 361 deletions

View File

@ -1,7 +1,7 @@
// app global css in SCSS form
@each $size in (5, 10, 15, 20, 25) {
.rounded-#{$size} {
border-radius: #{$size}px;
border-radius: #{$size}px !important;
}
}

View File

@ -297,11 +297,11 @@ export default {
tableHeader: 'List of employees',
tableColumnLabelFullname: 'Full name',
tableColumnLabelRegularHours: 'regular hours',
tableColumnLabelEveningHours: 'evening hours',
tableColumnLabelEmergencyHours: 'emergency hours',
tableColumnLabelOvertime: 'overtime hours',
tableColumnLabelExpenses: 'of expenses',
tableColumnLabelMileage: 'of mileage',
tableColumnLabelEveningHours: 'evening',
tableColumnLabelEmergencyHours: 'emergency',
tableColumnLabelOvertime: 'overtime',
tableColumnLabelExpenses: 'expenses',
tableColumnLabelMileage: 'mileage',
actionTitle: 'Please save the changes made.',
actionButton: 'Save',
timeSheetStatusVerified: 'approved',

View File

@ -347,11 +347,11 @@ export default {
tableHeader: 'Liste des employés',
tableColumnLabelFullname: 'nom complet',
tableColumnLabelRegularHours: 'heures régulières',
tableColumnLabelEveningHours: 'heures de soir',
tableColumnLabelEmergencyHours: 'heures durgence',
tableColumnLabelOvertime: 'heures supplémentaires',
tableColumnLabelExpenses: 'de dépenses',
tableColumnLabelMileage: 'de kilométrage',
tableColumnLabelEveningHours: 'soir',
tableColumnLabelEmergencyHours: 'urgence',
tableColumnLabelOvertime: 'supplémentaires',
tableColumnLabelExpenses: 'dépenses',
tableColumnLabelMileage: 'kilométrage',
actionTitle: 'Veuillez enregistrer les changements effectués.',
actionButton: 'Enregistrer',
timeSheetStatusVerified: 'validé',

View File

@ -25,7 +25,7 @@ import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-em
<template>
<q-card
v-ripple
class="rounded-15 bg-white col-xs-6 col-sm-4 col-md-3 col-lg-2 column no-wrap cursor-pointer"
class="rounded-15 bg-white col-xs-6 col-sm-4 col-md-3 col-lg-2 column no-wrap cursor-pointer q-ma-sm"
style="max-width: 230px;"
@click="onProfileCardClick(props.row.email)"
>
@ -37,9 +37,9 @@ import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-em
<q-card-section class="text-center text-h6 text-primary text-weight-medium text-uppercase q-pb-none col-2 content-end" style="line-height: 0.7em;">
<div class="ellipsis">
{{ props.row.first_name }} {{ props.row.last_name }}
<q-separator color="primary" />
</div>
</q-card-section>
<q-separator color="primary" class="q-mx-sm q-mt-xs" />
<q-card-section class="text-caption text-grey-8 text-body2 text-uppercase q-pt-none text-center col content-start" style="min-height: 5em;">
<div class=" ellipsis-2-lines">
{{ props.row.job_title }}

View File

@ -15,6 +15,7 @@
const { t } = useI18n();
const filter = ref("");
const isGridMode = ref(true);
const pagination = ref({ rowsPerPage: 0 });
const employeeListColumns = computed((): QTableColumn<EmployeeListTableItem>[] => [
{name: 'first_name', label: t('usersListPage.userListFirstName'), field: 'first_name', align: 'left'},
@ -35,24 +36,30 @@
<template>
<div class="q-pa-lg col">
<q-table
title=" "
dense
flat
hide-pagination
virtual-scroll
title=" "
card-style="max-height: 70vh;"
:rows="employeeStore.employeeList"
:columns="employeeListColumns"
row-key="name"
v-model:pagination="pagination"
:rows-per-page-options="[0]"
:filter="filter"
class="q-pa-md bg-transparent"
color="primary"
table-header-class="text-primary text-uppercase"
card-container-class="justify-center q-gutter-md"
card-container-class="justify-center"
:grid="isGridMode"
:loading="isLoadingList"
dense
flat
:no-data-label="$t('shared.failedToLoad')"
:no-results-label="$t('shared.failedToSearch')"
:loading-label="$t('shared.loading')"
table-class="bg-white q-pa-md q-mx-md rounded-15 shadow-12"
table-class="bg-white q-pa-md q-mx-md rounded-10 shadow-12"
table-style=""
@row-click="() => console.log('click!')"
>
<template v-slot:item="props">
<SupervisorCrewTableItem :row="props.row"/>

View File

@ -0,0 +1,6 @@
import type { PayPeriod } from "./pay-period-interface";
export interface PayPeriodBundle {
current: PayPeriod;
periods: PayPeriod[];
}

View File

@ -1,7 +1,8 @@
export interface PayPeriod {
period_number: number;
start_date: string;
end_date: string;
year: number;
pay_period_no: number;
period_start: string;
period_end: string;
payday: string;
pay_year: number;
label: string;
};
}

View File

@ -15,24 +15,68 @@
</script>
<template>
<div class="q-px-sm q-pb-sm col-xs-6 col-sm-4 col-md-3 col-lg-2 grid-style-transition">
<div class="q-px-sm q-pb-sm col-xs-12 col-sm-6 col-md-4 col-lg-4 col-xl-3 grid-style-transition">
<q-card class="rounded-10">
<q-card-section class="q-pb-sm">
<div class="text-primary text-h5 text-weight-bolder ellipsis">{{ props.row.employee_name }}</div>
<!-- Card header with employee name -->
<q-card-section horizontal class="q-py-none q-px-md">
<div class="text-primary text-h5 text-weight-bolder q-pt-xs overflow-hidden">{{ props.row.employee_name }}</div>
</q-card-section>
<div
v-for="col in props.cols.filter(col => col.name !== 'employee_name')"
:key="col.name"
class="q-pa-none q-mx-sm items-center row"
:class="{ 'bg-warning': col.name == 'overtime_hours' && col.value as number > 0 }"
>
<q-card-section class="text-right text-weight-bolder text-subtitle1 text-primary q-pr-sm q-py-none col-3 ellipsis" style="line-height: 1.2em;">{{ col.value }}</q-card-section>
<q-card-section class="text-weight-bold q-pa-none col-9" >{{ col.label }}</q-card-section>
</div>
<q-separator color="accent" style="height: 2px;"/>
<!-- Main body of pay period card -->
<q-card-section class="q-pa-none q-mt-xs q-mb-sm">
<div class="row no-wrap">
<!-- left portion of pay period card -->
<div class="column no-wrap" :class="$q.screen.lt.md ? 'col' : 'col-8'">
<!-- Regular hours segment -->
<q-item dense class="column" :class="$q.screen.lt.md ? 'col' : 'col-8'">
<q-item-label class="text-weight-bold text-primary q-pa-none text-uppercase text-caption">
{{ props.cols.find(c => c.name === 'regular_hours')?.label }}
</q-item-label>
<q-item-label class="text-weight-bolder text-h3 text-grey-8 q-py-none">
{{ props.cols.find(c => c.name === 'regular_hours')?.value }}
</q-item-label>
</q-item>
<q-separator color="accent" class="q-mx-sm"/>
<!-- Other hour types segment -->
<div :class="$q.screen.lt.md ? 'column' : 'row no-wrap'">
<q-item dense class="column ellipsis " v-for="col in props.cols.slice(2, 5)" :key="col.label">
<q-item-label class="text-weight-bold text-primary q-pa-none text-uppercase text-caption" style="font-size: 0.65em;">
{{ col.label }}
</q-item-label>
<q-item-label class="text-weight-bolder q-pa-none text-h6 text-grey-8">
{{ col.value }}
</q-item-label>
</q-item>
</div>
</div>
<q-separator vertical color="accent" class="q-mt-xs q-mb-none"/>
<!-- Right portion of pay period card -->
<div class="no-wrap ellipsis col">
<q-item dense class="column" v-for="col in props.cols.slice(5, )" :key="col.label">
<q-item-label class="text-weight-bold text-primary q-pa-none text-uppercase text-caption ellipsis" style="font-size: 0.8em;">
{{ col.label }}
</q-item-label>
<q-item-label class="text-weight-bolder q-pa-none text-h6 text-grey-8">
{{ col.value }}
</q-item-label>
</q-item>
</div>
</div>
</q-card-section>
<q-separator color="primary" style="height: 2px;" />
<!-- Validate entire Pay Period section -->
<q-card-section
horizontal
class="q-pa-sm q-mt-sm text-weight-bold"
:class="props.modelValue ? 'text-white bg-primary' : 'text-primary bg-white'"
horizontal
class="q-pa-sm text-weight-bold"
:class="props.modelValue ? 'text-white bg-primary' : 'text-primary bg-white'"
>
<q-space />
<q-checkbox

View File

@ -11,11 +11,20 @@
import { useTimesheetStore } from 'src/stores/timesheet-store';
const { t } = useI18n();
const timesheetApprovalApi = useTimesheetApprovalApi();
const currentPayPeriod = getCurrentPayPeriod();
const currentYear = (new Date()).getFullYear();
const originalApprovals = ref<Record<string, boolean>>({});
const hasChanges = computed(() => {
return timesheetStore.payPeriodEmployeeOverviews.some(emp => {
return emp.is_approved !== originalApprovals.value[emp.email];
});
});
const authStore = useAuthStore();
const timesheetStore = useTimesheetStore();
const timesheetApprovalApi = useTimesheetApprovalApi();
const columns = computed((): QTableColumn<PayPeriodEmployeeOverview>[] => [
{ name: 'employee_name', label: t('timeSheetValidations.tableColumnLabelFullname'), field: 'employee_name', sortable: true },
@ -30,7 +39,9 @@
const filter = ref('');
onMounted( async () => {
await timesheetApprovalApi.getCurrentAndAllPayPeriods();
await timesheetApprovalApi.getTimesheetApprovalPayPeriodEmployeeOverviews(currentYear, currentPayPeriod, authStore.user.email);
originalApprovals.value = Object.fromEntries( timesheetStore.payPeriodEmployeeOverviews.map(emp => [emp.email, emp.is_approved]));
})
</script>
@ -44,9 +55,10 @@
:filter="filter"
grid
dense
hide-pagination
color="primary"
:rows-per-page-options="[0]"
card-container-class="justify-center q-gutter-md"
card-container-class="justify-center"
:loading="timesheetStore.isLoading"
:no-data-label="$t('shared.failedToLoad')"
:no-results-label="$t('shared.failedToSearch')"
@ -54,7 +66,7 @@
>
<!-- Top Bar that contains Search, Title, Filters -->
<template v-slot:top>
<q-card flat class="full-width bg-transparent row q-px-md">
<div class="full-width row">
<q-space />
<!-- Filters toggle -->
@ -79,7 +91,7 @@
<q-icon name="search" color="primary"/>
</template>
</q-input>
</q-card>
</div>
</template>
<!-- Template for individual employee cards -->

View File

@ -2,20 +2,15 @@
import { useTimesheetStore } from 'src/stores/timesheet-store';
const timesheetStore = useTimesheetStore();
const updateCurrentPayPeriod = () => {
timesheetStore.getCurrentPayPeriod();
};
</script>
<template>
<div class="column items-center">
<div class="text-primary text-h5">{{ timesheetStore.currentPayPeriod?.label }}</div>
<div class="text-primary text-h5">{{ timesheetStore.currentPayPeriod?.label || '' }}</div>
<q-btn-group push rounded>
<q-btn push icon="keyboard_arrow_left" color="primary" class="q-px-xl" />
<q-btn push icon="date_range" color="primary" class="q-px-xl" @click="updateCurrentPayPeriod" />
<q-btn push icon="keyboard_arrow_right" color="primary" class="q-px-xl" />
<q-btn push icon="keyboard_arrow_left" color="primary" class="q-px-xl" @click="timesheetStore.getPreviousOrNextPayPeriod(-1)"/>
<q-btn push icon="date_range" color="primary" class="q-px-xl" />
<q-btn push icon="keyboard_arrow_right" color="primary" class="q-px-xl" @click="timesheetStore.getPreviousOrNextPayPeriod(1)"/>
</q-btn-group>
</div>
</template>

View File

@ -1,13 +1,18 @@
import { useTimesheetStore } from "src/stores/timesheet-store";
export const useTimesheetApprovalApi = () => {
const timesheetStore = useTimesheetStore();
const timesheet_store = useTimesheetStore();
const getCurrentAndAllPayPeriods = async () => {
await timesheet_store.getCurrentAndAllPayPeriods();
}
const getTimesheetApprovalPayPeriodEmployeeOverviews = async (year: number, period_number: number, supervisor_email: string): Promise<void> => {
await timesheetStore.getTimesheetApprovalPayPeriodEmployeeOverviews(year, period_number, supervisor_email);
await timesheet_store.getTimesheetApprovalPayPeriodEmployeeOverviews(year, period_number, supervisor_email);
}
return {
getTimesheetApprovalPayPeriodEmployeeOverviews,
getCurrentAndAllPayPeriods,
}
};

View File

@ -1,22 +1,11 @@
import { api } from "src/boot/axios";
import { mock_pay_periods } from "../timesheet-approval-test-constants";
import type { PayPeriod } from "src/modules/shared/types/pay-period-interface";
import type { PayPeriodOverview } from "../types/timesheet-approval-pay-period-overview-interface";
import type { PayPeriodBundle } from "src/modules/shared/types/pay-period-bundle-interface";
export const timesheetApprovalService = {
getCurrentPayPeriod: (): PayPeriod => {
return {
"period_number": 15,
"start_date": "2025-07-27",
"end_date": "2025-08-09",
"year": 2025,
"label": "2025-07-27 → 2025-08-09"
} as PayPeriod;
},
getAllPayPeriods: async () => {
// TODO: REMOVE MOCK DATA PEFORE PUSHING TO PROD
return await api.get(`/pay-periods/`) || mock_pay_periods;
getCurrentAndAllPayPeriod: async (): Promise<PayPeriodBundle> => {
const response = await api.get('pay-periods/bundle/current-and-all');
return response.data;
},
getPayPeriodEmployeeOverviews: async (year: number, period_number: number, supervisor_email: string): Promise<PayPeriodOverview> => {

View File

@ -1,287 +1,287 @@
import type { PayPeriod } from "../shared/types/pay-period-interface";
import type { PayPeriodEmployeeOverview } from "./types/timesheet-approval-pay-period-employee-overview-interface"
// import type { PayPeriod } from "../shared/types/pay-period-interface";
// import type { PayPeriodEmployeeOverview } from "./types/timesheet-approval-pay-period-employee-overview-interface"
export const mock_pay_period_employee_overviews: PayPeriodEmployeeOverview[] = [
{
"email": 'EMP-001',
"employee_name": 'Alice Johnson',
"regular_hours": 75,
"evening_hours": 12,
"emergency_hours": 3,
"overtime_hours": 5,
"expenses": 120.50,
"mileage": 45,
"is_approved": false
},
{
"email": 'EMP-002',
"employee_name": 'Brian Smith',
"regular_hours": 80,
"evening_hours": 8,
"emergency_hours": 0,
"overtime_hours": 2,
"expenses": 75.00,
"mileage": 12,
"is_approved": true
},
{
"email": 'EMP-003',
"employee_name": 'Chloe Ramirez',
"regular_hours": 68,
"evening_hours": 15,
"emergency_hours": 1,
"overtime_hours": 0,
"expenses": 200.00,
"mileage": 88,
"is_approved": false
},
{
"email": 'EMP-004',
"employee_name": 'David Lee',
"regular_hours": 82,
"evening_hours": 5,
"emergency_hours": 4,
"overtime_hours": 6,
"expenses": 50.75,
"mileage": 20,
"is_approved": true
},
{
"email": 'EMP-005',
"employee_name": 'Emily Carter',
"regular_hours": 78,
"evening_hours": 10,
"emergency_hours": 2,
"overtime_hours": 3,
"expenses": 95.25,
"mileage": 60,
"is_approved": false
},
{
"email": 'EMP-006',
"employee_name": 'Maxime Murray Gendron',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-007',
"employee_name": 'Marc-André Henrico',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-008',
"employee_name": 'Jessy Sharock',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-009',
"employee_name": 'David Richer',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-010',
"employee_name": 'Nicolas Drolet',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-011',
"employee_name": 'Frederick Pruneau',
"regular_hours": 16,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-012',
"employee_name": 'Matthieu Haineault Gervais',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-013',
"employee_name": 'Robinson Viaud',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-014',
"employee_name": 'Geneviève Bourdon',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-015',
"employee_name": 'Frédérique Soulard',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-016',
"employee_name": 'Patrick Doucet',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-017',
"employee_name": 'Dahlia Tremblay',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-018',
"employee_name": 'Louis Morneau',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
},
{
"email": 'EMP-019',
"employee_name": 'Michel Blais',
"regular_hours": 80,
"evening_hours": 0,
"emergency_hours": 0,
"overtime_hours": 0,
"expenses": 0,
"mileage": 0,
"is_approved": false
}
];
// export const mock_pay_period_employee_overviews: PayPeriodEmployeeOverview[] = [
// {
// "email": 'EMP-001',
// "employee_name": 'Alice Johnson',
// "regular_hours": 75,
// "evening_hours": 12,
// "emergency_hours": 3,
// "overtime_hours": 5,
// "expenses": 120.50,
// "mileage": 45,
// "is_approved": false
// },
// {
// "email": 'EMP-002',
// "employee_name": 'Brian Smith',
// "regular_hours": 80,
// "evening_hours": 8,
// "emergency_hours": 0,
// "overtime_hours": 2,
// "expenses": 75.00,
// "mileage": 12,
// "is_approved": true
// },
// {
// "email": 'EMP-003',
// "employee_name": 'Chloe Ramirez',
// "regular_hours": 68,
// "evening_hours": 15,
// "emergency_hours": 1,
// "overtime_hours": 0,
// "expenses": 200.00,
// "mileage": 88,
// "is_approved": false
// },
// {
// "email": 'EMP-004',
// "employee_name": 'David Lee',
// "regular_hours": 82,
// "evening_hours": 5,
// "emergency_hours": 4,
// "overtime_hours": 6,
// "expenses": 50.75,
// "mileage": 20,
// "is_approved": true
// },
// {
// "email": 'EMP-005',
// "employee_name": 'Emily Carter',
// "regular_hours": 78,
// "evening_hours": 10,
// "emergency_hours": 2,
// "overtime_hours": 3,
// "expenses": 95.25,
// "mileage": 60,
// "is_approved": false
// },
// {
// "email": 'EMP-006',
// "employee_name": 'Maxime Murray Gendron',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-007',
// "employee_name": 'Marc-André Henrico',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-008',
// "employee_name": 'Jessy Sharock',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-009',
// "employee_name": 'David Richer',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-010',
// "employee_name": 'Nicolas Drolet',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-011',
// "employee_name": 'Frederick Pruneau',
// "regular_hours": 16,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-012',
// "employee_name": 'Matthieu Haineault Gervais',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-013',
// "employee_name": 'Robinson Viaud',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-014',
// "employee_name": 'Geneviève Bourdon',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-015',
// "employee_name": 'Frédérique Soulard',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-016',
// "employee_name": 'Patrick Doucet',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-017',
// "employee_name": 'Dahlia Tremblay',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-018',
// "employee_name": 'Louis Morneau',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// },
// {
// "email": 'EMP-019',
// "employee_name": 'Michel Blais',
// "regular_hours": 80,
// "evening_hours": 0,
// "emergency_hours": 0,
// "overtime_hours": 0,
// "expenses": 0,
// "mileage": 0,
// "is_approved": false
// }
// ];
export const mock_pay_periods: PayPeriod[] = [
{
"period_number": 15,
"start_date": "2025-07-27",
"end_date": "2025-08-09",
"year": 2025,
"label": "2025-07-27 → 2025-08-09"
},
{
"period_number": 14,
"start_date": "2025-07-13",
"end_date": "2025-07-26",
"year": 2025,
"label": "2025-07-13 → 2025-07-26"
},
{
"period_number": 13,
"start_date": "2025-06-29",
"end_date": "2025-07-12",
"year": 2025,
"label": "2025-06-29 → 2025-07-12"
},
{
"period_number": 12,
"start_date": "2025-06-15",
"end_date": "2025-06-28",
"year": 2025,
"label": "2025-06-15 → 2025-06-28"
},
{
"period_number": 11,
"start_date": "2025-06-01",
"end_date": "2025-06-14",
"year": 2025,
"label": "2025-06-01 → 2025-06-14"
},
{
"period_number": 10,
"start_date": "2025-05-18",
"end_date": "2025-05-31",
"year": 2025,
"label": "2025-05-18 → 2025-05-31"
},
{
"period_number": 9,
"start_date": "2025-05-04",
"end_date": "2025-05-17",
"year": 2025,
"label": "2025-05-04 → 2025-05-17"
},
{
"period_number": 8,
"start_date": "2025-04-20",
"end_date": "2025-05-03",
"year": 2025,
"label": "2025-04-20 → 2025-05-03"
},
{
"period_number": 7,
"start_date": "2025-04-06",
"end_date": "2025-04-19",
"year": 2025,
"label": "2025-04-06 → 2025-04-19"
},
{
"period_number": 6,
"start_date": "2025-03-23",
"end_date": "2025-04-05",
"year": 2025,
"label": "2025-03-23 → 2025-04-05"
}
]
// export const mock_pay_periods: PayPeriod[] = [
// {
// "period_number": 15,
// "start_date": "2025-07-27",
// "end_date": "2025-08-09",
// "year": 2025,
// "label": "2025-07-27 → 2025-08-09"
// },
// {
// "period_number": 14,
// "start_date": "2025-07-13",
// "end_date": "2025-07-26",
// "year": 2025,
// "label": "2025-07-13 → 2025-07-26"
// },
// {
// "period_number": 13,
// "start_date": "2025-06-29",
// "end_date": "2025-07-12",
// "year": 2025,
// "label": "2025-06-29 → 2025-07-12"
// },
// {
// "period_number": 12,
// "start_date": "2025-06-15",
// "end_date": "2025-06-28",
// "year": 2025,
// "label": "2025-06-15 → 2025-06-28"
// },
// {
// "period_number": 11,
// "start_date": "2025-06-01",
// "end_date": "2025-06-14",
// "year": 2025,
// "label": "2025-06-01 → 2025-06-14"
// },
// {
// "period_number": 10,
// "start_date": "2025-05-18",
// "end_date": "2025-05-31",
// "year": 2025,
// "label": "2025-05-18 → 2025-05-31"
// },
// {
// "period_number": 9,
// "start_date": "2025-05-04",
// "end_date": "2025-05-17",
// "year": 2025,
// "label": "2025-05-04 → 2025-05-17"
// },
// {
// "period_number": 8,
// "start_date": "2025-04-20",
// "end_date": "2025-05-03",
// "year": 2025,
// "label": "2025-04-20 → 2025-05-03"
// },
// {
// "period_number": 7,
// "start_date": "2025-04-06",
// "end_date": "2025-04-19",
// "year": 2025,
// "label": "2025-04-06 → 2025-04-19"
// },
// {
// "period_number": 6,
// "start_date": "2025-03-23",
// "end_date": "2025-04-05",
// "year": 2025,
// "label": "2025-03-23 → 2025-04-05"
// }
// ]

View File

@ -4,17 +4,31 @@ import { timesheetApprovalService } from 'src/modules/timesheet-approval/service
import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
import type { PayPeriodEmployeeOverview } from "src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-overview-interface";
const default_current_pay_period: PayPeriod = {"period_number": 1, "start_date": "1970-01-01", "end_date": "1970-01-15", "year": 1970, "label": "1970-01-01 → 1970-01-15"};
export const useTimesheetStore = defineStore('timesheet', () => {
const payPeriods = ref<PayPeriod[]>([]);
const currentPayPeriod = ref<PayPeriod>(default_current_pay_period);
const currentPayPeriod = ref<PayPeriod>();
const currentPayPeriodIndex = ref<number>(-1);
const payPeriodEmployeeOverviews = ref<PayPeriodEmployeeOverview[]>([]);
const isLoading = ref<boolean>(false);
const getCurrentPayPeriod = () => {
currentPayPeriod.value = timesheetApprovalService.getCurrentPayPeriod();
const getCurrentAndAllPayPeriods = async () => {
isLoading.value = true;
try {
const response = await timesheetApprovalService.getCurrentAndAllPayPeriod();
currentPayPeriod.value = response.current;
payPeriods.value = response.periods;
currentPayPeriodIndex.value = payPeriods.value.findIndex( pay_period => pay_period.pay_period_no === currentPayPeriod.value?.pay_period_no);
} catch( error ){
console.error('Could not get current pay period: ', error );
}
isLoading.value = false;
};
const getPreviousOrNextPayPeriod = (direction: number) => {
if ( currentPayPeriodIndex.value + direction >= 0 && currentPayPeriodIndex.value + direction < payPeriods.value.length ) {
currentPayPeriodIndex.value += direction;
currentPayPeriod.value = payPeriods.value.at(currentPayPeriodIndex.value);
}
}
const getTimesheetApprovalPayPeriodEmployeeOverviews = async (year: number, period_number: number, supervisor_email: string) => {
@ -27,7 +41,7 @@ export const useTimesheetStore = defineStore('timesheet', () => {
// TODO: trigger an alert window with an error message here!
}
isLoading.value = false;
}
};
return { payPeriods, currentPayPeriod, payPeriodEmployeeOverviews, isLoading, getCurrentPayPeriod, getTimesheetApprovalPayPeriodEmployeeOverviews};
return { payPeriods, currentPayPeriod, payPeriodEmployeeOverviews, isLoading, getCurrentAndAllPayPeriods, getTimesheetApprovalPayPeriodEmployeeOverviews, getPreviousOrNextPayPeriod};
});