feat(timesheet): added pay-period interface to timesheet employee page

This commit is contained in:
Matthieu Haineault 2025-09-10 08:34:21 -04:00
parent 3be76b9c96
commit 5c0c9036c4
15 changed files with 490 additions and 127 deletions

View File

@ -267,6 +267,7 @@ export default {
EMERGENCY: 'Emergency',
EVENING: 'Evening',
HOLIDAY: 'Holiday',
OVERTIME: 'Overtime',
REGULAR: 'Regular',
SICK: 'Sick Leave',
VACATION: 'Vacation',

View File

@ -318,6 +318,7 @@ export default {
EMERGENCY: 'Urgence',
EVENING: 'Soir',
HOLIDAY: 'Férier',
OVERTIME: 'Supplémentaire',
SICK: 'Absence',
REGULAR: 'Régulier',
VACATION: 'Vacance',

View File

@ -0,0 +1,48 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const props = defineProps<{ isLoading: boolean; }>();
type ShiftLegendItem = {
type: 'REGULAR'|'EVENING'|'EMERGENCY'|'OVERTIME'|'VACATION'|'HOLIDAY'|'SICK';
color: string;
label_key: string;
text_color?: string;
};
const legend: ShiftLegendItem[] = [
{type:'REGULAR' , color: 'secondary', label_key: 'timesheet.shift_types.REGULAR', text_color: 'grey-8'},
{type:'EVENING' , color: 'warning' , label_key: 'timesheet.shift_types.EVENING'},
{type:'EMERGENCY', color: 'amber-10' , label_key: 'timesheet.shift_types.EMERGENCY'},
{type:'OVERTIME' , color: 'negative' , label_key: 'timesheet.shift_types.OVERTIME'},
{type:'VACATION' , color: 'purple-10', label_key: 'timesheet.shift_types.VACATION'},
{type:'HOLIDAY' , color: 'purple-8' , label_key: 'timesheet.shift_types.HOLIDAY'},
{type:'SICK' , color: 'grey-8' , label_key: 'timesheet.shift_types.SICK'},
]
const shift_type_legend = computed(()=>
legend.map(item => ({ ...item, label: t(item.label_key)} ))
);
</script>
<template>
<q-card class="q-px-xs q-pt-xs col rounded-10 q-mx-xs q-py-xs">
<q-card-section
class="q-py-xs q-pa-none text-center q-my-s"
v-if="!props.isLoading"
>
<q-badge
v-for="shift_type in shift_type_legend"
:key="shift_type.type"
:color="shift_type.color"
:label="shift_type.label"
:text-color="shift_type.text_color || 'white'"
class="q-px-md q-py-xs q-mx-xs q-my-none text-uppercase text-weight-bolder justify-center"
style="width: 120px; font-size: 0.8em;"
/>
</q-card-section>
</q-card>
</template>

View File

@ -0,0 +1,33 @@
<template>
<q-card-section
horizontal
class="text-uppercase text-center items-center q-pa-none"
>
<!-- shift row itself -->
<q-card-section class="col q-pa-none">
<q-card-section horizontal class="col q-pa-none">
<!-- punch-in timestamps -->
<q-card-section class="col q-pa-none">
<q-item-label class="text-weight-bolder text-primary" style="font-size: 0.7em;">
{{ $t('shiftColumns.labelIn') }}
</q-item-label>
</q-card-section>
<!-- arrows pointing to punch-out timestamps -->
<q-card-section class="col q-py-none q-px-sm">
</q-card-section>
<!-- punch-out timestamps -->
<q-card-section class="col q-pa-none">
<q-item-label class="text-weight-bolder text-primary" style="font-size: 0.7em;">
{{ $t('shiftColumns.labelOut') }}
</q-item-label>
</q-card-section>
<!-- comment button -->
<q-card-section class="col column q-pa-none">
</q-card-section>
</q-card-section>
</q-card-section>
</q-card-section>
</template>

View File

@ -0,0 +1,104 @@
<script setup lang="ts">
import type { Shift } from 'src/modules/timesheets/types/timesheet-shift-interface';
const props = defineProps<{
shift: Shift;
}>();
const getShiftColor = (type: string): string => {
switch(type) {
case 'REGULAR': return 'secondary';
case 'EVENING': return 'warning';
case 'EMERGENCY': return 'amber-10';
case 'OVERTIME': return 'negative';
case 'VACATION': return 'purple-10';
case 'HOLIDAY': return 'purple-10';
case 'SICK': return 'grey-8';
default : return 'transparent';
}
};
const getTextColor = (type: string): string => {
switch(type) {
case 'REGULAR': return 'grey-8';
case '': return 'transparent';
default: return 'white';
}
}
</script>
<template>
<q-card-section
horizontal
class="q-pa-none text-uppercase text-center items-center cursor-pointer rounded-10"
style="line-height: 1;"
>
<!-- punch-in timestamps -->
<q-card-section class="q-pa-none col">
<q-item-label
class="text-weight-bolder q-pa-xs rounded-5"
:class="'bg-' + getShiftColor(props.shift.type) + ' text-' + getTextColor(props.shift.type)"
style="font-size: 1.5em; line-height: 80% !important;"
>
{{ props.shift.start_time }}
</q-item-label>
</q-card-section>
<!-- arrows pointing to punch-out timestamps -->
<q-card-section
horizontal
class="items-center justify-center q-mx-sm col"
>
<div
v-for="icon_data, index in [
{ transform: 'transform: translateX(5px);', color: 'accent' },
{ transform: 'transform: translateX(-5px);', color: 'primary' }]"
:key="index"
>
<q-icon
v-if="props.shift.type !== ''"
name="double_arrow"
:color="icon_data.color"
size="24px"
:style="icon_data.transform"
/>
</div>
</q-card-section>
<!-- punch-out timestamps -->
<q-card-section class="q-pa-none col">
<q-item-label
class="text-weight-bolder text-white q-pa-xs rounded-5"
:class="'bg-' + getShiftColor(props.shift.type) + ' text-' + getTextColor(props.shift.type)"
style="font-size: 1.5em; line-height: 80% !important;"
>
{{ props.shift.end_time }}
</q-item-label>
</q-card-section>
<!-- comment and expenses buttons -->
<q-card-section
class="col q-pa-none text-right"
>
<!-- chat_bubble_outline or announcement -->
<q-btn
v-if="props.shift.type !== ''"
flat
dense
color='grey-8'
icon="chat_bubble_outline"
class="q-pa-none"
/>
<!-- insert_drive_file or request_quote -->
<q-btn
v-if="props.shift.type !== ''"
flat
dense
color='grey-8'
icon="attach_money"
class="q-pa-none q-mx-xs"
/>
</q-card-section>
</q-card-section>
</template>

View File

@ -0,0 +1,72 @@
<script setup lang="ts">
import { default_shift, type Shift } from 'src/modules/timesheets/types/timesheet-shift-interface';
import TimesheetEmployeeDetailsShiftsRowHeader from './timesheet-details-shifts-row-header.vue';
import TimesheetEmployeeDetailsShiftsRow from './timesheet-details-shifts-row.vue';
import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
import type { TimesheetPayPeriodDetailsOverview } from '../types/timesheet-pay-period-details-overview-interface';
const props = defineProps<{
rawData: TimesheetPayPeriodDetailsOverview;
currentPayPeriod: PayPeriod;
}>();
const shifts_or_placeholder = (shifts: Shift[]): Shift[] => {
return shifts.length > 0 ? shifts : [default_shift];
};
const getDate = (shift_date: string): Date => {
return new Date(props.currentPayPeriod.pay_year.toString() + '/' + shift_date);
};
</script>
<template>
<div
v-for="week, index in props.rawData"
:key="index"
class="q-px-xs q-pt-xs rounded-5 col"
>
<q-card
v-for="day, day_index in week.shifts"
:key="day_index"
flat
bordered
class="row items-center rounded-10 q-mb-xs"
>
<q-card-section class="col-auto q-pa-xs text-white">
<div
class="bg-primary rounded-10 q-pa-xs text-center"
:style="$q.screen.lt.md ? '' : 'width: 75px;'"
>
<q-item-label
style="font-size: 0.7em;"
class="text-uppercase"
>{{ $d(getDate(day.short_date), {weekday: $q.screen.lt.md ? 'short' : 'long'}) }}</q-item-label>
<q-item-label
class="text-weight-bolder"
style="font-size: 2.5em; line-height: 90% !important;"
>{{ day.short_date.split('/')[1] }}</q-item-label>
<q-item-label
style="font-size: 0.7em;"
class="text-uppercase"
>{{ $d(getDate(day.short_date), {month: $q.screen.lt.md ? 'short' : 'long'}) }}</q-item-label>
</div>
</q-card-section>
<q-card-section class="col q-pa-none">
<TimesheetEmployeeDetailsShiftsRowHeader />
<TimesheetEmployeeDetailsShiftsRow
v-for="shift, shift_index in shifts_or_placeholder(day.shifts)"
:key="shift_index"
:shift="shift"
/>
</q-card-section>
<q-card-section class="q-pr-xs col-auto">
<q-btn
push
color="primary"
icon="more_time"
class="q-pa-sm"
/>
</q-card-section>
</q-card>
</div>
</template>

View File

@ -6,7 +6,7 @@ import { useTimesheetStore } from 'src/stores/timesheet-store';
import TimesheetShiftComment from '../shift/timesheet-shift-comment.vue';
import TimesheetSavePayload from './timesheet-save-payload.vue';
import type { Shift } from '../../types/timesheet-shift-interface';
import type { CreateShiftPayload } from '../../types/timesheet-shifts-payload-interface';
import type{ CreateShiftPayload } from '../../types/timesheet-shifts-payload-interface';
const timesheet_store = useTimesheetStore();

View File

@ -1,45 +1,94 @@
<script setup lang="ts">
/* eslint-disable */
import { ref } from 'vue';
import { date } from 'quasar';
import { computed, ref, watch } from 'vue';
import { date as qdate } from 'quasar';
import type { QDateDetails } from 'src/modules/shared/types/q-date-details';
const is_showing_calendar_picker = ref(false);
const calendar_date = ref(date.formatDate( Date.now(), 'YYYY-MM-DD' ));
const props = defineProps<{
isDisabled: boolean,
isPreviousLimit: boolean,
isDisabled?: boolean;
minPickableDate?: string;
currentLabel?: string;
}>();
const emit = defineEmits<{
(e: 'navigate-to', targetIso: string, meta?: { reason: 'previous' | 'next' | 'current' | 'picked' }): void;
(e: 'date-selected', value: string, reason?: string, details?: QDateDetails): void;
(e: 'pressed-previous-button'): void;
(e: 'pressed-next-button'): void;
(e: 'pressed-current-button'): void;
}>();
const emit = defineEmits<{
'date-selected': [value: string, reason?: string, details?: QDateDetails]
'pressed-previous-button': []
'pressed-next-button': []
'pressed-current-button' : []
}>();
const is_showing_calendar_picker = ref(false);
const currentStartEndDate = computed(()=> {
const label = props.currentLabel ?? '';
const parts = label.split('.');
if(parts.length < 2) return null;
return { start_iso: parts[0]!, end_iso: parts[1]! };
});
const to_date = (iso?: string) => (iso ? qdate.extractDate(iso, 'YYYY-MM-DD'): null);
const to_iso = (date: Date) => qdate.formatDate(date, 'YYYY-MM-DD');
const calendar_date = ref(currentStartEndDate.value?.start_iso ?? to_iso(new Date()));
watch(() => props.currentLabel, () => {
if(currentStartEndDate.value?.start_iso) {
calendar_date.value = currentStartEndDate.value.start_iso;
}
});
const is_previous_limit = computed(() => {
const start_end = currentStartEndDate.value;
if (!start_end || !props.minPickableDate) return false;
const prev_anchor = qdate.addToDate(to_date(start_end.start_iso)!, { days: -1 });
return to_iso(prev_anchor) <= props.minPickableDate;
});
const onDateSelected = (value: string, reason: string, details: QDateDetails) => {
calendar_date.value = value;
is_showing_calendar_picker.value = false;
emit('date-selected', value, reason, details);
}
emit('navigate-to', value, { reason: 'picked' });
};
const goPrevious = () => {
emit('pressed-previous-button');
const start_end = currentStartEndDate.value;
if (!start_end) return;
const prev_anchor = qdate.addToDate(to_date(start_end.start_iso)!, { days: -1 });
emit('navigate-to', to_iso(prev_anchor), { reason: 'previous' });
};
const goCurrent = () => {
emit('pressed-current-button');
emit('navigate-to', to_iso(new Date()), { reason: 'current' });
};
const goNext = () => {
emit('pressed-next-button');
const start_end = currentStartEndDate.value;
if (!start_end) return;
const next_anchor = qdate.addToDate(to_date(start_end.end_iso)!, { days: 1 });
emit('navigate-to', to_iso(next_anchor), { reason: 'next' });
};
</script>
<template>
<div class="row q-mb-lg q-mt-lg" >
<!-- navigation to previous week -->
<q-btn
push rounded
icon="keyboard_arrow_left"
color="primary"
@click="emit('pressed-previous-button')"
:disable="props.isPreviousLimit || props.isDisabled"
push rounded
icon="keyboard_arrow_left"
color="primary"
@click="goPrevious"
:disable="is_previous_limit || props.isDisabled"
class="q-mr-sm q-px-sm"
>
<q-tooltip
anchor="top middle"
self="center middle"
anchor="top middle"
self="center middle"
class="bg-primary text-uppercase text-weight-bold"
> {{ $t( 'timesheet.nav_button.previous_week' )}}
</q-tooltip>
@ -49,8 +98,8 @@
push rounded
icon="today"
color="primary"
@click="emit('pressed-current-button')"
:disable="props.isDisabled"
@click="goCurrent"
:disable="!!props.isDisabled"
class="q-mr-sm q-px-lg"
>
<q-tooltip
@ -62,11 +111,11 @@
</q-btn>
<!-- navigation through calendar date picker -->
<q-btn
push rounded
icon="calendar_month"
color="primary"
push rounded
icon="calendar_month"
color="primary"
@click="is_showing_calendar_picker = true"
:disable="props.isDisabled"
:disable="!!props.isDisabled"
class="q-px-lg"
>
<q-tooltip
@ -78,11 +127,11 @@
</q-btn>
<!-- navigation to next week -->
<q-btn
push rounded
icon="keyboard_arrow_right"
color="primary"
@click="emit('pressed-next-button')"
:disable="props.isDisabled"
push rounded
icon="keyboard_arrow_right"
color="primary"
@click="goNext"
:disable="!!props.isDisabled"
class="q-ml-sm q-px-sm"
>
<q-tooltip
@ -95,14 +144,18 @@
</div>
<!-- date picker calendar -->
<q-dialog v-model="is_showing_calendar_picker" transition-show="jump-down" transition-hide="jump-up" position="top">
<q-dialog
v-model="is_showing_calendar_picker"
transition-show="jump-down"
transition-hide="jump-up"
position="top">
<q-date
v-model="calendar_date"
color="primary"
class="q-mt-xl"
v-model="calendar_date"
color="primary"
class="q-mt-xl"
today-btn
mask="YYYY-MM-DD"
:options="date => date > '2023-12-16'"
:options="date => !props.minPickableDate || date > props.minPickableDate"
@update:model-value="onDateSelected"
/>
</q-dialog>

View File

@ -0,0 +1,93 @@
<script setup lang="ts">
import { useTimesheetStore } from 'src/stores/timesheet-store';
import { computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { date } from 'quasar';
import TimesheetEmployeeDetailsShifts from '../components/timesheet-details-shifts.vue';
import { useAuthStore } from 'src/stores/auth-store';
import TimesheetNavigation from '../components/timesheet/timesheet-navigation.vue';
import ShiftsLegend from '../components/shift/shifts-legend.vue';
const { locale } = useI18n();
const timesheet_store = useTimesheetStore();
const auth_store = useAuthStore();
const date_options: Intl.DateTimeFormatOptions = {
day: 'numeric',
month: 'long',
year: 'numeric',
};
const pay_period_label = computed(() => {
const label = timesheet_store.current_pay_period?.label ?? '';
const dates = label.split('.');
if ( dates.length < 2 ) {
return { start_date: '—', end_date:'—' }
}
const start_date = new Intl.DateTimeFormat(locale.value, date_options).format(date.extractDate(dates[0] as string, 'YYYY-MM-DD'));
const end_date = new Intl.DateTimeFormat(locale.value, date_options).format(date.extractDate(dates[1] as string, 'YYYY-MM-DD'));
return { start_date, end_date };
});
const loadByDate = async (isoDate: string) => {
await timesheet_store.getPayPeriodByDate(isoDate);
await timesheet_store.getTimesheetsByPayPeriodAndEmail(auth_store.user.email);
};
onMounted( async () => {
await timesheet_store.getPayPeriodByDate(date.formatDate(new Date(), 'YYYY-MM-DD'));
await timesheet_store.getTimesheetsByPayPeriodAndEmail(auth_store.user.email);
await loadByDate(date.formatDate(new Date(), 'YYYY-MM-DD' ));
});
</script>
<template>
<q-page padding class="q-pa-md bg-secondary" >
<div class="text-h4 row justify-center text-center q-mt-lg text-uppercase text-weight-bolder text-grey-8">
{{ $t('pageTitles.timeSheets') }}
</div>
<div class="row items-center justify-center q-py-none q-my-none">
<div
class="text-primary text-uppercase text-weight-bold"
:class="$q.screen.lt.md ? '' : 'text-h6'"
>
{{ pay_period_label.start_date }}
</div>
<div
class="text-grey-8 text-uppercase q-mx-md"
:class="$q.screen.lt.md ? 'text-weight-medium text-caption' : 'text-weight-bold'"
>
{{ $t('timesheet.dateRangesTo') }}
</div>
<div
class="text-primary text-uppercase text-center text-weight-bold"
:class="$q.screen.lt.md ? '' : 'text-h6'"
>
{{ pay_period_label.end_date }}
</div>
</div>
<div>
<q-card flat class="q-mt-md bg-secondary">
<TimesheetNavigation
:is-disabled="timesheet_store.is_loading"
:min-pickable-date="'2023-12-16'"
:current-label="timesheet_store.current_pay_period?.label || ''"
@navigate-to="(iso) => loadByDate(iso)"
/>
<ShiftsLegend
:is-loading="false"
/>
<q-card-section horizontal>
<TimesheetEmployeeDetailsShifts
:raw-data="timesheet_store.pay_period_employee_details"
:current-pay-period="timesheet_store.current_pay_period"
/>
</q-card-section>
<q-inner-loading :showing="timesheet_store.is_loading" color="primary"/>
</q-card>
</div>
</q-page>
</template>

View File

@ -1,86 +0,0 @@
<script setup lang="ts">
import { useTimesheetStore } from 'src/stores/timesheet-store';
import { useTimesheetApi } from '../composables/use-timesheet-api';
import { computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import TimesheetShiftForm from '../components/timesheet/timesheet-shift-form.vue';
import TimesheetNavigation from '../components/timesheet/timesheet-navigation.vue';
import { date as qdate } from 'quasar';
import type { CreateShiftPayload } from '../types/timesheet-shifts-payload-interface';
const { locale } = useI18n();
const timesheet_store = useTimesheetStore();
const timesheet_api = useTimesheetApi();
const { this_week, saveTimesheetShifts } = timesheet_api;
onMounted(async () => {
await this_week();
});
const date_options: Intl.DateTimeFormatOptions = {
day: 'numeric',
month: 'long',
year: 'numeric',
};
const timesheet_label = computed(() => {
const dates = timesheet_store.current_timesheet.label.split('.');
const start_date = new Intl.DateTimeFormat(locale.value, date_options).format(qdate.extractDate(dates[0] as string, 'YYYY-MM-DD'));
const end_date = new Intl.DateTimeFormat(locale.value, date_options).format(qdate.extractDate(dates[1] as string, 'YYYY-MM-DD'));
if ( dates.length === 1) {
return { start_date: '_', end_date: '_', }
}
return { start_date, end_date };
});
const onSaveShifts = async (payload: CreateShiftPayload[]) => {
await saveTimesheetShifts(payload);
};
const is_calendar_limit = computed( () => {
return timesheet_store.current_pay_period.pay_year === 2024 &&
timesheet_store.current_pay_period.pay_period_no <= 1;
});
const onDateSelected = async (date_string: string) => {
const when = qdate.extractDate(date_string, 'YYYY-MM-DD');
await timesheet_api.getCurrentWeekTimesheetOverview(when);
};
</script>
<template>
<q-page padding class="q-pa-md bg-secondary" >
<div class="text-h4 row justify-center q-mt-lg text-uppercase text-weight-bolder text-grey-8 col-auto">
{{ $t('pageTitles.timeSheets') }}
</div>
<div class="row items-center justify-center q-py-none q-my-none">
<div class="text-primary text-h6 text-uppercase">
{{ timesheet_label.start_date }}
</div>
<div class="text-grey-8 text-weight-bold text-uppercase q-mx-md">
{{ $t('timesheet.dateRangesTo') }}
</div>
<div class="text-primary text-h6 text-uppercase">
{{ timesheet_label.end_date }}
</div>
</div>
<div>
<TimesheetNavigation
:is-disabled="timesheet_store.is_loading"
:is-previous-limit="is_calendar_limit"
@date-selected="onDateSelected"
@pressed-previous-button="timesheet_api.previous_week()"
@pressed-current-button="timesheet_api.getCurrentWeekTimesheetOverview()"
@pressed-next-button="timesheet_api.next_week()"
/>
<q-card flat class="q-mt-md bg-secondary">
<TimesheetShiftForm @save="onSaveShifts" class="col-12"/>
<q-inner-loading :showing="timesheet_store.is_loading" color="primary"/>
</q-card>
</div>
<!-- navigation buttons -->
</q-page>
</template>

View File

@ -1,6 +1,10 @@
import { api } from "src/boot/axios";
import type {Timesheet} from "src/modules/timesheets/types/timesheet-interface";
import type {Timesheet} from "src/modules/timesheets/types/timesheet-interface";
import type { CreateShiftPayload, CreateWeekShiftPayload } from "../types/timesheet-shifts-payload-interface";
import type { PayPeriod } from "src/modules/shared/types/pay-period-interface";
import type { PayPeriodEmployeeDetails } from "src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface";
import type { PayPeriodOverview } from "src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-interface";
import type { PayPeriodReportFilters } from "src/modules/timesheet-approval/types/timesheet-approval-pay-period-report-interface";
export const timesheetTempService = {
//GET
@ -14,5 +18,34 @@ export const timesheetTempService = {
const payload: CreateWeekShiftPayload = { shifts };
const response = await api.post(`/timesheets/shifts/${encodeURIComponent(email)}`, payload, { params: offset ? { offset }: undefined });
return response.data as Timesheet;
}
},
getPayPeriodByDate: async (date_string: string): Promise<PayPeriod> => {
const response = await api.get(`pay-periods/date/${date_string}`);
return response.data;
},
getPayPeriodByYearAndPeriodNumber: async (year: number, period_number: number): Promise<PayPeriod> => {
const response = await api.get(`pay-periods/${year}/${period_number}`);
return response.data;
},
getPayPeriodEmployeeOverviews: async (year: number, period_number: number, supervisor_email: string): Promise<PayPeriodOverview> => {
// TODO: REMOVE MOCK DATA PEFORE PUSHING TO PROD
const response = await api.get(`pay-periods/${year}/${period_number}/${supervisor_email}`);
console.log('pay period data: ', response.data);
return response.data;
},
getTimesheetsByPayPeriodAndEmail: async (year: number, period_no: number, email: string): Promise<PayPeriodEmployeeDetails> => {
const response = await api.get('timesheets', { params: { year, period_no, email, }});
console.log('employee details: ', response.data);
return response.data;
},
getTimesheetApprovalCSVReport: async (year: number, period_number: number, report_filters?: PayPeriodReportFilters) => {
const response = await api.get(`csv/${year}/${period_number}`, { params: { report_filters, }});
return response.data;
},
};

View File

@ -0,0 +1,11 @@
import { default_timesheet_details_week, type TimesheetDetailsWeek } from "./timesheet-details-interface";
export interface TimesheetPayPeriodDetailsOverview {
week1: TimesheetDetailsWeek;
week2: TimesheetDetailsWeek;
};
export const default_pay_period_employee_details = {
week1: default_timesheet_details_week(),
week2: default_timesheet_details_week(),
}

View File

@ -25,7 +25,7 @@ const routes: RouteRecordRaw[] = [
{
path: 'timesheet-temp',
name: RouteNames.TIMESHEET_TEMP,
component: () => import('src/modules/timesheets/pages/timesheet-temp-page.vue')
component: () => import('src/modules/timesheets/pages/timesheet-details-overview.vue')
}
],
},