Merge pull request 'dev/matthieu/timesheet-form' (#14) from dev/matthieu/timesheet-form into main
Reviewed-on: Targo/targo_frontend#14
This commit is contained in:
commit
ea91da2c8c
|
|
@ -26,6 +26,7 @@ export default {
|
||||||
userMenuHome: 'Homepage',
|
userMenuHome: 'Homepage',
|
||||||
userMenuEmployeeList: 'Employee Directory',
|
userMenuEmployeeList: 'Employee Directory',
|
||||||
userMenuShiftValidation: 'Timesheet Approval',
|
userMenuShiftValidation: 'Timesheet Approval',
|
||||||
|
userMenuTimesheetTemp: 'Timesheet',
|
||||||
userMenuProfile: 'Profile',
|
userMenuProfile: 'Profile',
|
||||||
userMenuHelp: 'Help',
|
userMenuHelp: 'Help',
|
||||||
userMenuLogout: 'Log Out',
|
userMenuLogout: 'Log Out',
|
||||||
|
|
@ -248,7 +249,35 @@ export default {
|
||||||
timeSheets: 'Time sheet',
|
timeSheets: 'Time sheet',
|
||||||
timeSheetValidations: 'Time sheet approvals',
|
timeSheetValidations: 'Time sheet approvals',
|
||||||
},
|
},
|
||||||
timeSheet: {
|
timesheet: {
|
||||||
|
//employee's timesheet page
|
||||||
|
days: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
|
||||||
|
nav_button: {
|
||||||
|
calendar_date_picker:'Calendar',
|
||||||
|
current_week:'This week',
|
||||||
|
next_week:'Next week',
|
||||||
|
previous_week:'Previous week',
|
||||||
|
},
|
||||||
|
save_button:'Save',
|
||||||
|
cancel_button:'Cancel',
|
||||||
|
remote_button: 'Remote work',
|
||||||
|
add_shift:'Add Shift',
|
||||||
|
shift_types_label: 'Shift`s Type',
|
||||||
|
shift_types: {
|
||||||
|
EMERGENCY: 'Emergency',
|
||||||
|
EVENING: 'Evening',
|
||||||
|
HOLIDAY: 'Holiday',
|
||||||
|
REGULAR: 'Regular',
|
||||||
|
SICK: 'Sick Leave',
|
||||||
|
VACATION: 'Vacation',
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
start:'Start',
|
||||||
|
end:'End',
|
||||||
|
header_comment:'Shift`s comment',
|
||||||
|
textarea_comment: 'Leave a comment here',
|
||||||
|
},
|
||||||
|
//rest
|
||||||
timeSheetTab_1: 'Shifts',
|
timeSheetTab_1: 'Shifts',
|
||||||
timeSheetTab_2: 'Expenses',
|
timeSheetTab_2: 'Expenses',
|
||||||
templateButton: 'Apply Templates',
|
templateButton: 'Apply Templates',
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,7 @@ export default {
|
||||||
userMenuHome: 'Accueil',
|
userMenuHome: 'Accueil',
|
||||||
userMenuEmployeeList: 'Répertoire employés',
|
userMenuEmployeeList: 'Répertoire employés',
|
||||||
userMenuShiftValidation: 'Valider les heures',
|
userMenuShiftValidation: 'Valider les heures',
|
||||||
|
userMenuTimesheetTemp: 'Carte de temps',
|
||||||
userMenuProfile: 'Profil',
|
userMenuProfile: 'Profil',
|
||||||
userMenuHelp: 'Aide',
|
userMenuHelp: 'Aide',
|
||||||
userMenuLogout: 'Déconnexion',
|
userMenuLogout: 'Déconnexion',
|
||||||
|
|
@ -298,7 +299,36 @@ export default {
|
||||||
noResultsLabel: 'Le filtre n’a révélé aucun résultat',
|
noResultsLabel: 'Le filtre n’a révélé aucun résultat',
|
||||||
noDataLabel: 'Je n’ai rien trouvé pour toi',
|
noDataLabel: 'Je n’ai rien trouvé pour toi',
|
||||||
},
|
},
|
||||||
timeSheet: {
|
timesheet: {
|
||||||
|
//employee's timesheet page
|
||||||
|
days: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
|
||||||
|
nav_button: {
|
||||||
|
calendar_date_picker:'Calendrier',
|
||||||
|
current_week:'Semaine actuelle',
|
||||||
|
next_week:'Prochaine semaine',
|
||||||
|
previous_week:'Semaine précédente',
|
||||||
|
},
|
||||||
|
save_button:'Enregistrer',
|
||||||
|
cancel_button:'Annuler',
|
||||||
|
remote_button: 'Télétravail',
|
||||||
|
|
||||||
|
add_shift:'Ajouter une quart',
|
||||||
|
shift_types_label: 'Type de quart',
|
||||||
|
shift_types: {
|
||||||
|
EMERGENCY: 'Urgence',
|
||||||
|
EVENING: 'Soir',
|
||||||
|
HOLIDAY: 'Férier',
|
||||||
|
SICK: 'Absence',
|
||||||
|
REGULAR: 'Régulier',
|
||||||
|
VACATION: 'Vacance',
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
start:'Entrée',
|
||||||
|
end:'Sortie',
|
||||||
|
header_comment:'Commentaire du Quart',
|
||||||
|
textarea_comment:'Laissez votre commentaire',
|
||||||
|
},
|
||||||
|
//rest
|
||||||
timeSheetTab_1: 'Quarts de travail',
|
timeSheetTab_1: 'Quarts de travail',
|
||||||
timeSheetTab_2: 'Dépenses',
|
timeSheetTab_2: 'Dépenses',
|
||||||
templateButton: 'Appliquer le modèle',
|
templateButton: 'Appliquer le modèle',
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,17 @@
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|
||||||
|
<!-- Employee Timesheet temp -- Employee, Supervisor, Accounting only -->
|
||||||
|
<q-item v-ripple clickable side @click="goToPageName(RouteNames.TIMESHEET_TEMP)"
|
||||||
|
v-if="['supervisor', 'accounting', 'employee'].includes(authStore.user.role)">
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="punch_clock" color="primary" />
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label class="text-uppercase text-grey-8 text-weight-bold">{{ $t('navBar.userMenuTimesheetTemp') }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
<!-- Profile -->
|
<!-- Profile -->
|
||||||
<q-item v-ripple clickable side @click="goToPageName(RouteNames.PROFILE)">
|
<q-item v-ripple clickable side @click="goToPageName(RouteNames.PROFILE)">
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,12 @@
|
||||||
|
|
||||||
expenses_dataset.value = [
|
expenses_dataset.value = [
|
||||||
{
|
{
|
||||||
label: t('timeSheet.refund'),
|
label: t('timesheet.refund'),
|
||||||
data: all_costs,
|
data: all_costs,
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-primary').trim(),
|
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-primary').trim(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('timeSheet.mileage'),
|
label: t('timesheet.mileage'),
|
||||||
data: all_mileage,
|
data: all_mileage,
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-info').trim(),
|
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-info').trim(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,16 +40,16 @@ import { colors } from 'quasar';
|
||||||
|
|
||||||
const shift_type_legend: shiftColor[] = [
|
const shift_type_legend: shiftColor[] = [
|
||||||
{
|
{
|
||||||
type: t('timeSheet.shiftRegular'),
|
type: t('timesheet.shift_types.REGULAR'),
|
||||||
color: 'secondary',
|
color: 'secondary',
|
||||||
text_color: 'grey-8',
|
text_color: 'grey-8',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: t('timeSheet.shiftEvening'),
|
type: t('timesheet.shift_types.EVENING'),
|
||||||
color: 'warning',
|
color: 'warning',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: t('timeSheet.shiftEmergency'),
|
type: t('timesheet.shift_types.EMERGENCY'),
|
||||||
color: 'amber-10',
|
color: 'amber-10',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -57,15 +57,15 @@ import { colors } from 'quasar';
|
||||||
color: 'negative',
|
color: 'negative',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: t('timeSheet.shiftVacation'),
|
type: t('timesheet.shift_types.VACATION'),
|
||||||
color: 'purple-10',
|
color: 'purple-10',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: t('timeSheet.shiftHoliday'),
|
type: t('timesheet.shift_types.HOLIDAY'),
|
||||||
color: 'purple-8',
|
color: 'purple-8',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: t('timeSheet.shiftSick'),
|
type: t('timesheet.shift_types.SICK'),
|
||||||
color: 'grey-8',
|
color: 'grey-8',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
class="text-grey-8 text-uppercase q-mx-md"
|
class="text-grey-8 text-uppercase q-mx-md"
|
||||||
:class="$q.screen.lt.md ? 'text-weight-medium text-caption' : 'text-weight-bold'"
|
:class="$q.screen.lt.md ? 'text-weight-medium text-caption' : 'text-weight-bold'"
|
||||||
>
|
>
|
||||||
{{ $t('timeSheet.dateRangesTo') }}
|
{{ $t('timesheet.dateRangesTo') }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-primary text-uppercase text-center text-weight-bold"
|
class="text-primary text-uppercase text-center text-weight-bold"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
/* eslint-disable */
|
||||||
|
const props = defineProps<{
|
||||||
|
commentString: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
clickClose: [];
|
||||||
|
clickSave: [comment: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const text = ref(props.commentString);
|
||||||
|
|
||||||
|
const close = ()=> {
|
||||||
|
emit('clickClose');
|
||||||
|
text.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = ()=> {
|
||||||
|
emit('clickSave',text.value);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-card class="full-width">
|
||||||
|
<div class="row items-center justify-between q-pa-md">
|
||||||
|
{{ $t('timesheet.fields.header_comment') }}
|
||||||
|
</div>
|
||||||
|
<q-separator/>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<q-input
|
||||||
|
v-model="text"
|
||||||
|
type="textarea"
|
||||||
|
autogrow
|
||||||
|
filled
|
||||||
|
:label= "$t('timesheet.fields.textarea_comment')"
|
||||||
|
:counter=true
|
||||||
|
maxlength="512"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-end q-gutter-sm q-pa-sm">
|
||||||
|
<q-btn
|
||||||
|
color="secondary"
|
||||||
|
text-color="grey-8"
|
||||||
|
:label="$t('timesheet.cancel_button')"
|
||||||
|
@click="close"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
:label="$t('timesheet.save_button')"
|
||||||
|
color="primary"
|
||||||
|
@click="save"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
/* eslint-disable */
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { date } 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,
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'date-selected': [value: string, reason?: string, details?: QDateDetails]
|
||||||
|
'pressed-previous-button': []
|
||||||
|
'pressed-next-button': []
|
||||||
|
'pressed-current-button' : []
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const onDateSelected = (value: string, reason: string, details: QDateDetails) => {
|
||||||
|
calendar_date.value = value;
|
||||||
|
is_showing_calendar_picker.value = false;
|
||||||
|
emit('date-selected', value, reason, details);
|
||||||
|
}
|
||||||
|
</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"
|
||||||
|
class="q-mr-sm q-px-sm"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="center middle"
|
||||||
|
class="bg-primary text-uppercase text-weight-bold"
|
||||||
|
> {{ $t( 'timesheet.nav_button.previous_week' )}}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<!-- navigation to current week -->
|
||||||
|
<q-btn
|
||||||
|
push rounded
|
||||||
|
icon="today"
|
||||||
|
color="primary"
|
||||||
|
@click="emit('pressed-current-button')"
|
||||||
|
:disable="props.isDisabled"
|
||||||
|
class="q-mr-sm q-px-lg"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="center middle"
|
||||||
|
class="bg-primary text-uppercase text-weight-bold"
|
||||||
|
>{{ $t('timesheet.nav_button.current_week') }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<!-- navigation through calendar date picker -->
|
||||||
|
<q-btn
|
||||||
|
push rounded
|
||||||
|
icon="calendar_month"
|
||||||
|
color="primary"
|
||||||
|
@click="is_showing_calendar_picker = true"
|
||||||
|
:disable="props.isDisabled"
|
||||||
|
class="q-px-lg"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="center middle"
|
||||||
|
class="bg-primary text-uppercase text-weight-bold"
|
||||||
|
>{{ $t('timesheet.nav_button.calendar_date_picker') }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<!-- navigation to next week -->
|
||||||
|
<q-btn
|
||||||
|
push rounded
|
||||||
|
icon="keyboard_arrow_right"
|
||||||
|
color="primary"
|
||||||
|
@click="emit('pressed-next-button')"
|
||||||
|
:disable="props.isDisabled"
|
||||||
|
class="q-ml-sm q-px-sm"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="center middle"
|
||||||
|
class="bg-primary text-uppercase text-weight-bold"
|
||||||
|
> {{ $t('timesheet.nav_button.next_week') }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- date picker calendar -->
|
||||||
|
<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"
|
||||||
|
today-btn
|
||||||
|
mask="YYYY-MM-DD"
|
||||||
|
:options="date => date > '2023-12-16'"
|
||||||
|
@update:model-value="onDateSelected"
|
||||||
|
/>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
/* eslint-disable */
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import type { CreateShiftPayload, Shift } from '../../types/timesheet-shift-interface';
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rows: Shift[];
|
||||||
|
week_dates: string[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save', payload: CreateShiftPayload[]): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const buildPayload = (): CreateShiftPayload[] => {
|
||||||
|
const dates = props.week_dates;
|
||||||
|
if(!Array.isArray(dates) || dates.length === 0) return [];
|
||||||
|
|
||||||
|
return props.rows.flatMap((row, idx) => {
|
||||||
|
const date = dates[idx];
|
||||||
|
const has_data = !!(row.type || row.start_time || row.end_time || row.comment);
|
||||||
|
if(!date || !has_data) return [];
|
||||||
|
|
||||||
|
const item: CreateShiftPayload = {
|
||||||
|
date,
|
||||||
|
type: row.type,
|
||||||
|
start_time: row.start_time,
|
||||||
|
end_time: row.end_time,
|
||||||
|
is_remote: row.is_remote,
|
||||||
|
};
|
||||||
|
if(row.comment) item.description = row.comment;
|
||||||
|
return[item];
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const payload = computed(buildPayload);
|
||||||
|
const canSave = computed(()=> payload.value.length > 0);
|
||||||
|
|
||||||
|
const saveWeek = () => {
|
||||||
|
emit('save', payload.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- save button -->
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
icon="save_alt"
|
||||||
|
class="col-1"
|
||||||
|
push
|
||||||
|
rounded
|
||||||
|
:disable="!canSave"
|
||||||
|
@click="saveWeek"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="center middle"
|
||||||
|
class="bg-primary text-uppercase text-weight-bold"
|
||||||
|
>{{ $t('timesheet.save_button') }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
/* eslint-disable */
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
|
import TimesheetShiftComment from '../shift/timesheet-shift-comment.vue';
|
||||||
|
import TimesheetSavePayload from './timesheet-save-payload.vue';
|
||||||
|
import { Shift } from '../../types/timesheet-shift-interface';
|
||||||
|
import type { CreateShiftPayload } from '../../types/timesheet-shift-interface';
|
||||||
|
|
||||||
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
||||||
|
const { t, tm, locale } = useI18n();
|
||||||
|
|
||||||
|
const SHIFT_KEY = ['REGULAR', 'EVENING', 'EMERGENCY', 'HOLIDAY', 'VACATION', 'SICK'] as const;
|
||||||
|
const days = computed(()=> {
|
||||||
|
void locale.value;
|
||||||
|
return (tm('timesheet.days') as string[]) ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const shift_options = computed(()=> {
|
||||||
|
void locale.value;
|
||||||
|
return SHIFT_KEY.map(key => ({ value: key, label: t(`timesheet.shift_types.${key}`)}))
|
||||||
|
});
|
||||||
|
|
||||||
|
const empty_row = { date:'', type: '', start_time: '', end_time: '', comment: '', is_approved: false, is_remote: false };
|
||||||
|
//Week dates
|
||||||
|
const week_dates = computed(() => {
|
||||||
|
const start_date = timesheet_store.current_timesheet.start_day;
|
||||||
|
if(!start_date) return [];
|
||||||
|
|
||||||
|
const mm = /^(\d{4})-(\d{2})-(\d{2})$/.exec(start_date);
|
||||||
|
if(!mm) return [];
|
||||||
|
const year = Number(mm[1]), month = Number(mm[2]), day = Number(mm[3])
|
||||||
|
|
||||||
|
const base = new Date(Date.UTC(year, month - 1, day));
|
||||||
|
const yyyymmdd = (date: Date) => {
|
||||||
|
const yyyy = date.getFullYear();
|
||||||
|
const mm = String(date.getUTCMonth() + 1).padStart(2,'0');
|
||||||
|
const dd = String(date.getUTCDate()).padStart(2,'0');
|
||||||
|
return `${yyyy}-${mm}-${dd}`};
|
||||||
|
|
||||||
|
return Array.from({length:7 }, (_, i) => {
|
||||||
|
const date = new Date(base);
|
||||||
|
date.setUTCDate(base.getDate() + i);
|
||||||
|
return yyyymmdd(date);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//filling timesheet with shifts
|
||||||
|
const rows = ref<Shift[]>(
|
||||||
|
days.value.map((_,index) => {
|
||||||
|
const date_ISO = week_dates.value[index];
|
||||||
|
const shift = timesheet_store.current_timesheet.shifts.find(sh => sh.date === date_ISO);
|
||||||
|
return shift ? {
|
||||||
|
date:shift.date || '',
|
||||||
|
type: shift.bank_type || '',
|
||||||
|
start_time: shift.start_time || '',
|
||||||
|
end_time: shift.end_time || '',
|
||||||
|
comment: shift.description || '',
|
||||||
|
is_approved: !!shift.is_approved,
|
||||||
|
is_remote: !!shift.is_remote,
|
||||||
|
}
|
||||||
|
: { ...empty_row };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const hasData = (row: Shift) => !!(row.type || row.start_time || row.end_time || row.comment);
|
||||||
|
|
||||||
|
const show_comment = ref(false);
|
||||||
|
const selected_index = ref<number | null>(null);
|
||||||
|
const selected_row = computed<Shift | undefined>(()=>
|
||||||
|
selected_index.value != null ? rows.value[selected_index.value] : undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
const setComment = (comment: string) => {
|
||||||
|
if(selected_row.value) selected_row.value.comment = comment;
|
||||||
|
show_comment.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickComment = (index: number)=> {
|
||||||
|
selected_index.value = index;
|
||||||
|
show_comment.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearRow = (index: number) => {
|
||||||
|
rows.value[index] = { ...empty_row };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const emit = defineEmits<{ (e: 'save', payload: CreateShiftPayload[]): void }>();
|
||||||
|
|
||||||
|
//onMounted?
|
||||||
|
watch(
|
||||||
|
() => [timesheet_store.current_timesheet.start_day, timesheet_store.current_timesheet.shifts],
|
||||||
|
() => {
|
||||||
|
const dates = week_dates.value;
|
||||||
|
rows.value = days.value.map((_, idx)=> {
|
||||||
|
const shift = timesheet_store.current_timesheet.shifts.find(sh => sh.date === dates[idx]);
|
||||||
|
return shift
|
||||||
|
? { date: shift.date || '',
|
||||||
|
type: shift.bank_type || '',
|
||||||
|
start_time: shift.start_time || '',
|
||||||
|
end_time: shift.end_time || '',
|
||||||
|
comment: shift.description || '',
|
||||||
|
is_approved: shift.is_approved,
|
||||||
|
is_remote: shift.is_remote,
|
||||||
|
}
|
||||||
|
: { date: '',
|
||||||
|
type: '',
|
||||||
|
start_time: '',
|
||||||
|
end_time: '',
|
||||||
|
comment: '',
|
||||||
|
is_approved: false,
|
||||||
|
is_remote: false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-card class="bg-transparent q-pa-md q-ma-md">
|
||||||
|
<q-dialog
|
||||||
|
v-model="show_comment"
|
||||||
|
transition-show="fade"
|
||||||
|
transition-hide="fade"
|
||||||
|
persistent
|
||||||
|
>
|
||||||
|
<!-- comment popup -->
|
||||||
|
<TimesheetShiftComment
|
||||||
|
:comment-string="selected_row?.comment ?? ''"
|
||||||
|
@click-save="setComment"
|
||||||
|
@click-close="show_comment = false"
|
||||||
|
/>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-form
|
||||||
|
autofocus
|
||||||
|
class="bg-white q-pa-sm q-pt-lg rounded-10">
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="(row, index) in rows"
|
||||||
|
:key="week_dates[index] ?? index"
|
||||||
|
class="q-gutter-sm q-mb-sm"
|
||||||
|
:class="$q.screen.lt.md ? 'column' : 'row'" >
|
||||||
|
<!--Week days-->
|
||||||
|
<span class="text-weight-bold text-primary col-1">{{ days[index] }}</span>
|
||||||
|
<!-- remote work toggle -->
|
||||||
|
<q-toggle
|
||||||
|
v-model="row.is_remote"
|
||||||
|
:disable="row.is_approved"
|
||||||
|
color="primary"
|
||||||
|
checked-icon="home_work"
|
||||||
|
unchecked-icon="business"
|
||||||
|
icon="home_work"
|
||||||
|
class="col-auto q-ml-sm"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="center middle"
|
||||||
|
class="bg-primary text-uppercase text-weight-bold"
|
||||||
|
>{{ $t('timesheet.remote_button') }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-toggle>
|
||||||
|
<!-- type selection -->
|
||||||
|
<q-select
|
||||||
|
v-model="row.type"
|
||||||
|
:options="shift_options"
|
||||||
|
:readonly="row.is_approved"
|
||||||
|
class="col-3"
|
||||||
|
:label="$t('timesheet.shift_types_label')"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
color="primary"
|
||||||
|
standout="bg-primary text-white"
|
||||||
|
options-dense
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
hide-dropdown-icon
|
||||||
|
/>
|
||||||
|
<!-- start time input -->
|
||||||
|
<q-input
|
||||||
|
v-model="row.start_time"
|
||||||
|
:readonly="row.is_approved"
|
||||||
|
class="col-auto"
|
||||||
|
:label="$t('timesheet.fields.start')"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
color="primary"
|
||||||
|
type="time"
|
||||||
|
step="300"
|
||||||
|
standout="bg-primary text-white"
|
||||||
|
/>
|
||||||
|
<!-- end time input -->
|
||||||
|
<q-input
|
||||||
|
v-model="row.end_time"
|
||||||
|
:readonly="row.is_approved"
|
||||||
|
class="col-auto"
|
||||||
|
:label="$t('timesheet.fields.end')"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
color="primary"
|
||||||
|
type="time"
|
||||||
|
step="300"
|
||||||
|
standout="bg-primary text-white"
|
||||||
|
/>
|
||||||
|
<div class="col-3">
|
||||||
|
<!-- comment button -->
|
||||||
|
<q-btn
|
||||||
|
:icon="row.comment.length > 0 ? 'announcement':'chat_bubble_outline'"
|
||||||
|
:color="row.comment.length > 0 ? 'primary' : 'grey-8'"
|
||||||
|
:disable="row.is_approved"
|
||||||
|
flat
|
||||||
|
class="col-auto"
|
||||||
|
@click="onClickComment(index)"
|
||||||
|
/>
|
||||||
|
<!-- expense button -->
|
||||||
|
<q-btn
|
||||||
|
:icon="row.comment.length > 0 ? 'receipt_long':'attach_money'"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
class="q-pa-none q-ma-sm col-1"
|
||||||
|
:color="hasData(row) ? 'primary' : 'grey-8'"
|
||||||
|
@click="clearRow(index)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- reset entries button -->
|
||||||
|
<q-btn
|
||||||
|
icon="cleaning_services"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
class="q-pa-none q-ma-sm col-auto"
|
||||||
|
:color="hasData(row) ? 'primary' : 'grey-4'"
|
||||||
|
@click="clearRow(index)"
|
||||||
|
/>
|
||||||
|
<!-- add one more shift buttons -->
|
||||||
|
<q-btn
|
||||||
|
icon="more_time"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
class="q-pa-none q-ma-sm col-auto"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="center middle"
|
||||||
|
class="bg-primary text-uppercase text-weight-bold"
|
||||||
|
>{{ $t('timesheet.add_shift') }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
<TimesheetSavePayload
|
||||||
|
:week_dates="week_dates"
|
||||||
|
:rows="rows"
|
||||||
|
@save="(payload) => emit('save', payload)"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { useAuthStore } from "src/stores/auth-store";
|
||||||
|
import { useTimesheetStore } from "src/stores/timesheet-store"
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { timesheetTempService } from "../services/timesheet-services";
|
||||||
|
import type { CreateShiftPayload } from "../types/timesheet-shift-interface";
|
||||||
|
|
||||||
|
|
||||||
|
export const useTimesheetApi = () => {
|
||||||
|
const timesheet_store = useTimesheetStore();
|
||||||
|
const auth_store = useAuthStore();
|
||||||
|
const week_offset = ref(0);
|
||||||
|
|
||||||
|
const fetchWeek = async (offset = week_offset.value) => {
|
||||||
|
const email = auth_store.user?.email;
|
||||||
|
if(!email) return;
|
||||||
|
try{
|
||||||
|
timesheet_store.is_loading = true;
|
||||||
|
const timesheet = await timesheetTempService.getTimesheetsByEmail(email, offset);
|
||||||
|
timesheet_store.current_timesheet = timesheet;
|
||||||
|
week_offset.value = offset;
|
||||||
|
}catch (err) {
|
||||||
|
console.error('fetch week error', err);
|
||||||
|
timesheet_store.current_timesheet = { ...timesheet_store.current_timesheet, shifts: [], expenses: [] };
|
||||||
|
} finally {
|
||||||
|
timesheet_store.is_loading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const this_week = async () => fetchWeek(0);
|
||||||
|
const next_week = async () => fetchWeek(week_offset.value + 1);
|
||||||
|
const previous_week = async () => fetchWeek(week_offset.value - 1);
|
||||||
|
|
||||||
|
const saveTimesheetShifts = async (shifts: CreateShiftPayload[]) => {
|
||||||
|
const email = auth_store.user?.email;
|
||||||
|
if(!email || shifts.length === 0) return;
|
||||||
|
await timesheet_store.createTimesheetShifts(email, shifts, week_offset.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const weekStart = (date: Date) => {
|
||||||
|
const x = new Date(date);
|
||||||
|
x.setHours(0, 0, 0, 0);
|
||||||
|
x.setDate(x.getDate() - x.getDay());
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentWeekTimesheetOverview = async (when: Date = new Date()) => {
|
||||||
|
const off = Math.trunc((weekStart(when).getTime() - weekStart(new Date()).getTime()) / 604800000);
|
||||||
|
await fetchWeek(off);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
week_offset,
|
||||||
|
fetchWeek,
|
||||||
|
this_week,
|
||||||
|
next_week,
|
||||||
|
previous_week,
|
||||||
|
saveTimesheetShifts,
|
||||||
|
getCurrentWeekTimesheetOverview,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
<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 type { CreateShiftPayload } from '../types/timesheet-shift-interface';
|
||||||
|
import TimesheetNavigation from '../components/timesheet/timesheet-navigation.vue';
|
||||||
|
import { date as qdate } from 'quasar';
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { api } from "src/boot/axios";
|
||||||
|
import type {Timesheet} from "src/modules/timesheets/types/timesheet-interface";
|
||||||
|
import type { CreateShiftPayload, CreateWeekShiftPayload } from "../types/timesheet-shift-interface";
|
||||||
|
|
||||||
|
export const timesheetTempService = {
|
||||||
|
//GET
|
||||||
|
getTimesheetsByEmail: async ( email: string, offset = 0): Promise<Timesheet> => {
|
||||||
|
const response = await api.get(`/timesheets/${encodeURIComponent(email)}`, {params: offset ? { offset } : undefined});
|
||||||
|
return response.data as Timesheet;
|
||||||
|
},
|
||||||
|
|
||||||
|
//POST
|
||||||
|
createTimesheetShifts: async ( email: string, shifts: CreateShiftPayload[], offset = 0): Promise<Timesheet> => {
|
||||||
|
const payload: CreateWeekShiftPayload = { shifts };
|
||||||
|
const response = await api.post(`/timesheets/shifts/${encodeURIComponent(email)}`, payload, { params: offset ? { offset }: undefined });
|
||||||
|
return response.data as Timesheet;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
//mock data
|
||||||
|
|
@ -38,7 +38,12 @@ interface TimesheetDetailsDailyExpenses {
|
||||||
[otherType: string]: Expense[]; //for possible future types of expenses
|
[otherType: string]: Expense[]; //for possible future types of expenses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//employee timesheet template
|
||||||
|
export interface EmployeeTimesheetDetailsWeek {
|
||||||
|
is_approved: boolean;
|
||||||
|
shifts: WeekDay<TimesheetDetailsDailySchedule>;
|
||||||
|
expenses: WeekDay<TimesheetDetailsDailyExpenses>;
|
||||||
|
}
|
||||||
// empty default builder
|
// empty default builder
|
||||||
const makeWeek = <T>(factory: () => T): WeekDay<T> => ({
|
const makeWeek = <T>(factory: () => T): WeekDay<T> => ({
|
||||||
sun: factory(),
|
sun: factory(),
|
||||||
|
|
|
||||||
28
src/modules/timesheets/types/timesheet-interface.ts
Normal file
28
src/modules/timesheets/types/timesheet-interface.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
export interface Timesheet {
|
||||||
|
is_approved: boolean;
|
||||||
|
start_day: string;
|
||||||
|
end_day: string;
|
||||||
|
label: string;
|
||||||
|
shifts: Shifts[];
|
||||||
|
expenses: Expenses[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type Shifts = {
|
||||||
|
bank_type: string;
|
||||||
|
date: string;
|
||||||
|
start_time: string;
|
||||||
|
end_time: string;
|
||||||
|
description: string;
|
||||||
|
is_approved: boolean;
|
||||||
|
is_remote: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Expenses = {
|
||||||
|
bank_type: string;
|
||||||
|
date: string;
|
||||||
|
amount: number;
|
||||||
|
km: number;
|
||||||
|
description: string;
|
||||||
|
supervisor_comment: string;
|
||||||
|
is_approved: boolean;
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,24 @@
|
||||||
|
export interface CreateShiftPayload {
|
||||||
|
date: string;
|
||||||
|
type: string;
|
||||||
|
start_time: string;
|
||||||
|
end_time: string;
|
||||||
|
description?: string;
|
||||||
|
is_remote?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface CreateWeekShiftPayload {
|
||||||
|
shifts: CreateShiftPayload[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface Shift {
|
export interface Shift {
|
||||||
date : string;
|
date : string;
|
||||||
|
type : string;
|
||||||
start_time : string;
|
start_time : string;
|
||||||
end_time : string;
|
end_time : string;
|
||||||
type : string;
|
comment : string;
|
||||||
is_approved : boolean;
|
is_approved: boolean;
|
||||||
|
is_remote : boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const default_shift: Shift = {
|
export const default_shift: Shift = {
|
||||||
|
|
@ -11,5 +26,7 @@ export const default_shift: Shift = {
|
||||||
start_time: '--:--',
|
start_time: '--:--',
|
||||||
end_time: '--:--',
|
end_time: '--:--',
|
||||||
type: '',
|
type: '',
|
||||||
|
comment: '',
|
||||||
is_approved: false,
|
is_approved: false,
|
||||||
|
is_remote: false,
|
||||||
}
|
}
|
||||||
|
|
@ -6,4 +6,5 @@ export enum RouteNames {
|
||||||
TIMESHEET_APPROVALS = 'timesheet-approvals',
|
TIMESHEET_APPROVALS = 'timesheet-approvals',
|
||||||
EMPLOYEE_LIST = 'employee-list',
|
EMPLOYEE_LIST = 'employee-list',
|
||||||
PROFILE = 'user/profile',
|
PROFILE = 'user/profile',
|
||||||
|
TIMESHEET_TEMP = 'timesheet-temp'
|
||||||
}
|
}
|
||||||
|
|
@ -22,6 +22,11 @@ const routes: RouteRecordRaw[] = [
|
||||||
name: RouteNames.EMPLOYEE_LIST,
|
name: RouteNames.EMPLOYEE_LIST,
|
||||||
component: () => import('src/modules/employee-list/pages/supervisor-crew-page.vue'),
|
component: () => import('src/modules/employee-list/pages/supervisor-crew-page.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'timesheet-temp',
|
||||||
|
name: RouteNames.TIMESHEET_TEMP,
|
||||||
|
component: () => import('src/modules/timesheets/pages/timesheet-temp-page.vue')
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { timesheetApprovalService } from 'src/modules/timesheet-approval/services/services-timesheet-approval';
|
import { timesheetApprovalService } from 'src/modules/timesheet-approval/services/services-timesheet-approval';
|
||||||
|
import { timesheetTempService } from 'src/modules/timesheets/services/timesheet-services';
|
||||||
import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
import type { PayPeriodOverviewEmployee } from "src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface";
|
import type { PayPeriodOverviewEmployee } from "src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface";
|
||||||
import { default_pay_period_employee_details, type PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface';
|
import { default_pay_period_employee_details, type PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface';
|
||||||
import type { PayPeriodReportFilters } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-report-interface';
|
import type { PayPeriodReportFilters } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-report-interface';
|
||||||
|
import type { Timesheet } from 'src/modules/timesheets/types/timesheet-interface';
|
||||||
|
import type { CreateShiftPayload } from 'src/modules/timesheets/types/timesheet-shift-interface';
|
||||||
|
|
||||||
const default_pay_period: PayPeriod = {
|
const default_pay_period: PayPeriod = {
|
||||||
pay_period_no: -1,
|
pay_period_no: -1,
|
||||||
|
|
@ -15,6 +18,16 @@ const default_pay_period: PayPeriod = {
|
||||||
label: ''
|
label: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//employee timesheet
|
||||||
|
const default_timesheet: Timesheet = {
|
||||||
|
start_day: '',
|
||||||
|
end_day: '',
|
||||||
|
label: '',
|
||||||
|
is_approved: false,
|
||||||
|
shifts: [],
|
||||||
|
expenses: [],
|
||||||
|
};
|
||||||
|
|
||||||
export const useTimesheetStore = defineStore('timesheet', () => {
|
export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
const is_loading = ref<boolean>(false);
|
const is_loading = ref<boolean>(false);
|
||||||
const current_pay_period = ref<PayPeriod>(default_pay_period);
|
const current_pay_period = ref<PayPeriod>(default_pay_period);
|
||||||
|
|
@ -23,6 +36,9 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
const pay_period_employee_details = ref<PayPeriodEmployeeDetails>(default_pay_period_employee_details);
|
const pay_period_employee_details = ref<PayPeriodEmployeeDetails>(default_pay_period_employee_details);
|
||||||
const pay_period_report = ref();
|
const pay_period_report = ref();
|
||||||
|
|
||||||
|
//employee timesheet
|
||||||
|
const current_timesheet = ref<Timesheet>(default_timesheet);
|
||||||
|
|
||||||
const getPayPeriodByDate = async (date_string: string): Promise<boolean> => {
|
const getPayPeriodByDate = async (date_string: string): Promise<boolean> => {
|
||||||
is_loading.value = true;
|
is_loading.value = true;
|
||||||
|
|
||||||
|
|
@ -85,6 +101,32 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
is_loading.value = false;
|
is_loading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//employee timesheet
|
||||||
|
const getTimesheetByEmail = async (employee_email: string) => {
|
||||||
|
is_loading.value = true;
|
||||||
|
try{
|
||||||
|
const response = await timesheetTempService.getTimesheetsByEmail(employee_email);
|
||||||
|
current_timesheet.value = response;
|
||||||
|
}catch (error) {
|
||||||
|
console.error('There was an error retrieving timesheet details for this employee: ', error);
|
||||||
|
current_timesheet.value = { ...default_timesheet }
|
||||||
|
} finally {
|
||||||
|
is_loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//employee timesheet
|
||||||
|
const createTimesheetShifts = async (employee_email: string, shifts: CreateShiftPayload[], offset = 0) => {
|
||||||
|
is_loading.value = true;
|
||||||
|
try{
|
||||||
|
const timesheet = await timesheetTempService.createTimesheetShifts(employee_email, shifts, offset);
|
||||||
|
current_timesheet.value = timesheet;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('createTimesheetShifts error: ', err);
|
||||||
|
} finally {
|
||||||
|
is_loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getTimesheetsByPayPeriodAndEmail = async (employee_email: string) => {
|
const getTimesheetsByPayPeriodAndEmail = async (employee_email: string) => {
|
||||||
is_loading.value = true;
|
is_loading.value = true;
|
||||||
|
|
||||||
|
|
@ -126,8 +168,11 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
pay_period_overview_employees,
|
pay_period_overview_employees,
|
||||||
pay_period_overview_employee_approval_statuses,
|
pay_period_overview_employee_approval_statuses,
|
||||||
pay_period_employee_details,
|
pay_period_employee_details,
|
||||||
|
current_timesheet,
|
||||||
is_loading,
|
is_loading,
|
||||||
getPayPeriodByDate,
|
getPayPeriodByDate,
|
||||||
|
getTimesheetByEmail,
|
||||||
|
createTimesheetShifts,
|
||||||
getPayPeriodByYearAndPeriodNumber,
|
getPayPeriodByYearAndPeriodNumber,
|
||||||
getTimesheetApprovalPayPeriodEmployeeOverviews,
|
getTimesheetApprovalPayPeriodEmployeeOverviews,
|
||||||
getTimesheetsByPayPeriodAndEmail,
|
getTimesheetsByPayPeriodAndEmail,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user