refactor(approvals): massive refactor of names, DRY component scripts, separation of concern, trim unnecessary code
This commit is contained in:
parent
f91a664a92
commit
3bf8c57f74
|
|
@ -31,5 +31,5 @@ body.body--dark {
|
|||
|
||||
.body--light {
|
||||
--q-dark: #FFF;
|
||||
color: $grey-8;
|
||||
color: $blue-grey-8;
|
||||
}
|
||||
|
|
@ -74,6 +74,7 @@ export default {
|
|||
},
|
||||
label: {
|
||||
search: "search",
|
||||
filter: "filters",
|
||||
loading: "loading...",
|
||||
language: "Language",
|
||||
add: "ajouter",
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export default {
|
|||
},
|
||||
label: {
|
||||
search: 'recherche',
|
||||
filter: "filtres",
|
||||
loading: 'chargement en cours...',
|
||||
language: 'langue',
|
||||
add: "ajouter",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import HeaderBarAvatar from './header-bar-avatar.vue';
|
||||
import HeaderBarNotification from './main-layout-header-bar-notification.vue';
|
||||
|
||||
const uiStore = useUiStore();
|
||||
</script>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
</q-btn>
|
||||
</q-toolbar-title>
|
||||
<q-item class="q-pa-none">
|
||||
<HeaderBarAvatar />
|
||||
<HeaderBarNotification />
|
||||
</q-item>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
|
|
@ -26,8 +26,16 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<q-drawer overlay elevated side="left" :mini="miniState" @mouseenter="miniState = false"
|
||||
@mouseleave="miniState = true" v-model="uiStore.isRightDrawerOpen">
|
||||
<q-drawer
|
||||
v-model="uiStore.isRightDrawerOpen"
|
||||
overlay
|
||||
elevated
|
||||
side="left"
|
||||
:mini="miniState"
|
||||
@mouseenter="miniState = false"
|
||||
@mouseleave="miniState = true"
|
||||
class="bg-dark"
|
||||
>
|
||||
<q-scroll-area class="fit">
|
||||
<q-list>
|
||||
<!-- Home -->
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts" setup>
|
||||
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';
|
||||
import HeaderBar from 'src/modules/layouts/components/main-layout-header-bar.vue';
|
||||
import FooterBar from 'src/modules/layouts/components/main-layout-footer-bar.vue';
|
||||
import RightDrawer from 'src/modules/layouts/components/main-layout-right-drawer.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
30
src/modules/shared/components/page-header-template.vue
Normal file
30
src/modules/shared/components/page-header-template.vue
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
const { title, startDate = "", endDate = "" } = defineProps<{
|
||||
title: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
}>();
|
||||
|
||||
const date_format_options = { day: 'numeric', month: 'long', year: 'numeric', };
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="column q-mt-lg text-uppercase text-center text-weight-bolder text-h4">
|
||||
<span class="col">{{ $t(title) }}</span>
|
||||
|
||||
<div
|
||||
v-if="startDate.length > 0"
|
||||
class="col row flex-center full-width q-py-none q-my-none"
|
||||
>
|
||||
<div class="text-primary text-weight-bold text-h6">
|
||||
{{ $d(new Date(startDate), date_format_options) }}
|
||||
</div>
|
||||
<div class="text-body2 q-mx-md text-weight-medium">
|
||||
{{ $t('shared.misc.to') }}
|
||||
</div>
|
||||
<div class="text-primary text-weight-bold text-h6">
|
||||
{{ $d(new Date(endDate), date_format_options) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
43
src/modules/shared/components/q-table-filters.vue
Normal file
43
src/modules/shared/components/q-table-filters.vue
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<script setup lang="ts">
|
||||
const search_model = defineModel<string | number | null>({ default: null, required: true });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Filters toggle -->
|
||||
<q-btn-dropdown
|
||||
push
|
||||
class="q-mr-md bg-white text-primary left-rounded"
|
||||
:label="$t('shared.label.filter')"
|
||||
icon="filter_alt"
|
||||
/>
|
||||
|
||||
<!-- Search bar -->
|
||||
<q-input
|
||||
v-model="search_model"
|
||||
outlined
|
||||
dense
|
||||
debounce="300"
|
||||
class="right-rounded"
|
||||
:label="$t('shared.label.search')"
|
||||
label-color="primary"
|
||||
bg-color="white"
|
||||
color="primary"
|
||||
>
|
||||
<template #before>
|
||||
<q-icon
|
||||
name="search"
|
||||
color="primary"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.left-rounded {
|
||||
border-radius: 50% 0 0 50%;
|
||||
}
|
||||
|
||||
.right-rounded {
|
||||
border-radius: 0 50% 50% 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<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': []
|
||||
}>();
|
||||
|
||||
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 justify-center">
|
||||
<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-btn
|
||||
push rounded
|
||||
icon="date_range"
|
||||
color="primary"
|
||||
@click="is_showing_calendar_picker = true"
|
||||
:disable="props.isDisabled"
|
||||
class="q-px-lg"
|
||||
/>
|
||||
<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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
const props = defineProps<{
|
||||
searchModel: string | number | null
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
onSearchValueUpdated: [value: string | number | null]
|
||||
}>();
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Filters toggle -->
|
||||
<q-btn-dropdown
|
||||
rounded
|
||||
push
|
||||
class="q-mr-md bg-white text-primary"
|
||||
label="filters"
|
||||
icon="filter_alt"
|
||||
/>
|
||||
|
||||
<!-- Search bar -->
|
||||
<q-input
|
||||
outlined
|
||||
dense
|
||||
rounded
|
||||
debounce="300"
|
||||
:label="$t('shared.label.search')"
|
||||
label-color="primary"
|
||||
bg-color="white"
|
||||
color="primary"
|
||||
:model-value="props.searchModel"
|
||||
@update:model-value="value => emit('onSearchValueUpdated', value)"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
name="search"
|
||||
color="primary"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
|
@ -6,3 +6,12 @@ export interface PayPeriod {
|
|||
pay_year: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const default_pay_period: PayPeriod = {
|
||||
pay_period_no: -1,
|
||||
period_start: '',
|
||||
period_end: '',
|
||||
payday: '',
|
||||
pay_year: -1,
|
||||
label: ''
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
export interface QDateDetails {
|
||||
year: number;
|
||||
month: number;
|
||||
day: number;
|
||||
from?: {
|
||||
year: number;
|
||||
month: number;
|
||||
day: number;
|
||||
};
|
||||
to?: {
|
||||
year: number;
|
||||
month: number;
|
||||
day: number;
|
||||
};
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale, type ChartData, type ChartDataset } from 'chart.js';
|
||||
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface';
|
||||
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/pay-period-employee-details';
|
||||
import type { Expense } from 'src/modules/timesheets/types/timesheet-details-interface';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, type ChartData, type ChartOptions, type Plugin, type ChartDataset } from 'chart.js';
|
||||
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface';
|
||||
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/pay-period-employee-details';
|
||||
|
||||
const { t } = useI18n();
|
||||
const $q = useQuasar();
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
import { useQuasar } from 'quasar';
|
||||
import { Doughnut } from 'vue-chartjs';
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale, LinearScale, type ChartDataset } from 'chart.js';
|
||||
import type { PayPeriodOverviewEmployee } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface';
|
||||
import type { PayPeriodEmployeeOverview } from 'src/modules/timesheet-approval/types/pay-period-employee-overview';
|
||||
|
||||
const { t } = useI18n();
|
||||
const $q = useQuasar();
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
ChartJS.defaults.color = $q.dark.isActive ? '#F5F5F5' : '#616161';
|
||||
|
||||
const props = defineProps<{
|
||||
rawData: PayPeriodOverviewEmployee | undefined;
|
||||
rawData: PayPeriodEmployeeOverview | undefined;
|
||||
}>();
|
||||
|
||||
const shift_type_labels = ref<string[]>([]);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import type { Shift } from 'src/modules/timesheets/types/timesheet-shift-interface';
|
||||
import { ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
shift: Shift;
|
||||
|
|
@ -1,27 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
import TimesheetApprovalEmployeeDetailsShiftsRow from 'src/modules/timesheet-approval/components/timesheet-approval-employee-details-shifts-row.vue';
|
||||
import TimesheetApprovalEmployeeDetailsShiftsRowHeader from 'src/modules/timesheet-approval/components/timesheet-approval-employee-details-shifts-row-header.vue';
|
||||
import DetailedShiftListRow from 'src/modules/timesheet-approval/components/detailed-shift-list-row.vue';
|
||||
import DetailedShiftListHeader from 'src/modules/timesheet-approval/components/detailed-shift-list-header.vue';
|
||||
import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||
import { default_shift, type Shift } from 'src/modules/timesheets/types/timesheet-shift-interface';
|
||||
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface';
|
||||
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/pay-period-employee-details';
|
||||
|
||||
const props = defineProps<{
|
||||
const { rawData, currentPayPeriod } = defineProps<{
|
||||
rawData: PayPeriodEmployeeDetails;
|
||||
currentPayPeriod: PayPeriod;
|
||||
}>();
|
||||
|
||||
const weeks = [ rawData.week1, rawData.week2 ];
|
||||
|
||||
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);
|
||||
return new Date(currentPayPeriod.pay_year.toString() + '/' + shift_date);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-for="week, index in props.rawData"
|
||||
v-for="week, index in weeks"
|
||||
:key="index"
|
||||
class="q-px-xs q-pt-xs rounded-5 col"
|
||||
>
|
||||
|
|
@ -40,20 +42,27 @@
|
|||
<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>
|
||||
>
|
||||
{{ $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>
|
||||
>
|
||||
{{ 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>
|
||||
>
|
||||
{{ $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">
|
||||
<TimesheetApprovalEmployeeDetailsShiftsRowHeader />
|
||||
<TimesheetApprovalEmployeeDetailsShiftsRow
|
||||
<DetailedShiftListHeader />
|
||||
<DetailedShiftListRow
|
||||
v-for="shift, shift_index in shifts_or_placeholder(day.shifts)"
|
||||
:key="shift_index"
|
||||
:shift="shift"
|
||||
123
src/modules/timesheet-approval/components/overview-list-item.vue
Normal file
123
src/modules/timesheet-approval/components/overview-list-item.vue
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<script setup lang="ts">
|
||||
import type { PayPeriodEmployeeOverview } from 'src/modules/timesheet-approval/types/pay-period-employee-overview';
|
||||
|
||||
const modelApproval = defineModel<boolean>();
|
||||
const { row } = defineProps<{ row: PayPeriodEmployeeOverview; }>();
|
||||
const emit = defineEmits<{ clickDetails: []; }>();
|
||||
|
||||
const stack_label_class = "text-weight-bold text-primary text-uppercase text-caption q-pa-none q-my-none ellipsis";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="q-px-sm q-pb-sm q-mt-sm col-xs-12 col-sm-6 col-md-4 col-lg-4 col-xl-3 grid-style-transition">
|
||||
<q-card class="rounded-10">
|
||||
|
||||
<!-- Card header with employee name and details button-->
|
||||
<q-card-section horizontal class="q-py-none q-px-sm q-ma-none justify-between items-center">
|
||||
<span class="col text-primary text-h5 text-weight-bolder q-pt-xs"> {{ row.employee_name }} </span>
|
||||
|
||||
<!-- Buttons to view detailed shifts or view employee timesheet -->
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
square
|
||||
unelevated
|
||||
class="col-auto q-pa-none q-ma-none"
|
||||
color="primary"
|
||||
icon="work_history"
|
||||
@click="emit('clickDetails')"
|
||||
>
|
||||
<q-tooltip
|
||||
anchor="top middle"
|
||||
self="center middle"
|
||||
class="bg-primary text-uppercase text-weight-bold"
|
||||
>
|
||||
{{ $t('timesheet_approvals.tooltip.button_detailed_view') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-section>
|
||||
|
||||
<q-separator size="2px" />
|
||||
|
||||
<!-- Main body of pay period card -->
|
||||
<q-card-section class="q-py-none q-px-sm q-mt-sm q-mb-md">
|
||||
<div class="row no-wrap">
|
||||
|
||||
<!-- left portion of pay period card -->
|
||||
<div class="col column no-wrap q-px-sm">
|
||||
|
||||
<!-- Regular hours segment -->
|
||||
<div class="column" :class="$q.screen.lt.md ? 'col' : 'col-8'">
|
||||
<span :class="stack_label_class"> {{ $t('shared.shift_type.regular') }} </span>
|
||||
<span class="text-weight-bolder text-h3 q-py-none"> {{ row.regular_hours }} </span>
|
||||
</div>
|
||||
|
||||
<q-separator class="q-mx-sm" />
|
||||
|
||||
<!-- Other hour types segment -->
|
||||
<div class="row q-px-xs">
|
||||
<div class="col column no-wrap">
|
||||
<span :class="stack_label_class" style="font-size: 0.65em;"> {{ $t('shared.shift_type.evening') }} </span>
|
||||
<span class="text-weight-bolder text-h6 q-pa-none" style="line-height: 0.9em;"> {{ row.evening_hours }} </span>
|
||||
</div>
|
||||
|
||||
<div class="col column no-wrap">
|
||||
<span :class="stack_label_class" style="font-size: 0.65em;"> {{ $t('shared.shift_type.emergency') }} </span>
|
||||
<span class="text-weight-bolder text-h6 q-pa-none" style="line-height: 0.9em;"> {{ row.emergency_hours }} </span>
|
||||
</div>
|
||||
|
||||
<div class="col column no-wrap">
|
||||
<span :class="stack_label_class" style="font-size: 0.65em;"> {{ $t('shared.shift_type.overtime') }} </span>
|
||||
<span class="text-weight-bolder text-h6 q-pa-none" style="line-height: 0.9em;"> {{ row.overtime_hours }} </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-separator
|
||||
vertical
|
||||
class="q-mt-xs q-mb-none"
|
||||
/>
|
||||
|
||||
<!-- Right portion of pay period card -->
|
||||
<div class="col-auto column q-px-sm">
|
||||
<div class="col column no-wrap">
|
||||
<span :class="stack_label_class" style="font-size: 0.8em;"> {{ $t('timesheet.expense.types.EXPENSES') }} </span>
|
||||
<span class="text-weight-bolder text-h6 q-pa-none" style="line-height: 0.9em;"> {{ row.expenses }} </span>
|
||||
</div>
|
||||
|
||||
<div class="col column no-wrap">
|
||||
<span :class="stack_label_class" style="font-size: 0.8em;"> {{ $t('timesheet.expense.types.MILEAGE') }} </span>
|
||||
<span class="text-weight-bolder text-h6 q-pa-none" style="line-height: 0.9em;"> {{ row.mileage }} </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-separator color="primary" size="2px" />
|
||||
|
||||
<!-- Validate Pay Period section -->
|
||||
<q-card-section
|
||||
horizontal
|
||||
class="justify-between items-center text-weight-bold q-px-sm"
|
||||
:class="row.is_approved ? 'text-white bg-primary' : 'bg-dark'"
|
||||
>
|
||||
<div class="col-auto">
|
||||
<span class="text-uppercase text-h6 q-ml-sm text-weight-bolder"> {{ row.total_hours }} </span>
|
||||
<span class="text-uppercase text-weight-bold text-caption q-ml-xs"> total </span>
|
||||
</div>
|
||||
|
||||
<q-checkbox
|
||||
v-model="modelApproval"
|
||||
dense
|
||||
left-label
|
||||
size="lg"
|
||||
checked-icon="lock"
|
||||
unchecked-icon="lock_open"
|
||||
:color="row.is_approved ? 'white' : 'primary'"
|
||||
:label="row.is_approved ? $t('timesheet_approvals.table.verified') : $t('timesheet_approvals.table.unverified')"
|
||||
class="col-auto text-uppercase"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
72
src/modules/timesheet-approval/components/overview-list.vue
Normal file
72
src/modules/timesheet-approval/components/overview-list.vue
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import OverviewListItem from 'src/modules/timesheet-approval/components/employee-overview/overview-list-item.vue';
|
||||
import QTableFilters from 'src/modules/shared/components/utils/q-table-filters.vue';
|
||||
import { pay_period_employee_overview_columns, type PayPeriodEmployeeOverview } from 'src/modules/timesheet-approval/types/pay-period-employee-overview';
|
||||
|
||||
const timesheet_store = useTimesheetStore();
|
||||
|
||||
// const FORWARD = 1
|
||||
// const BACKWARD = -1
|
||||
const filter = ref<string | number | null>('');
|
||||
|
||||
const onClickedDetails = async ( employee_email: string ) => {
|
||||
await timesheet_store.getPayPeriodEmployeeDetailsByEmployeeEmail(employee_email);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
:rows="timesheet_store.pay_period_employee_overview_list"
|
||||
:columns="pay_period_employee_overview_columns"
|
||||
row-key="email"
|
||||
:filter="filter"
|
||||
grid
|
||||
dense
|
||||
hide-pagination
|
||||
color="primary"
|
||||
:rows-per-page-options="[0]"
|
||||
card-container-class="justify-center"
|
||||
:loading="timesheet_store.is_loading"
|
||||
:no-data-label="$t('shared.error.no_data_found')"
|
||||
:no-results-label="$t('shared.error.no_search_results')"
|
||||
:loading-label="$t('shared.label.loading')"
|
||||
>
|
||||
<template #top>
|
||||
<div class="full-width" :class="$q.screen.lt.md ? 'text-center q-gutter-sm' : 'row'">
|
||||
<!-- Calendar Picker goes here -->
|
||||
|
||||
<q-space />
|
||||
|
||||
<!-- Grid-or-List toggle goes here -->
|
||||
|
||||
<QTableFilters v-model="filter"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Template for individual employee cards -->
|
||||
<template #item="props: { row: PayPeriodEmployeeOverview, key: string }">
|
||||
<OverviewListItem
|
||||
v-model="props.row.is_approved"
|
||||
:row="props.row"
|
||||
@click-details="onClickedDetails(props.row.email)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Template for custome failed-to-load state -->
|
||||
<template #no-data="{ message, filter }">
|
||||
<div class="full-width column items-center text-primary q-gutter-sm">
|
||||
<span class="text-h6 q-mt-xl">
|
||||
{{ message }}
|
||||
</span>
|
||||
<q-icon
|
||||
size="4em"
|
||||
:name="filter ? 'filter_alt_off' : 'error_outline'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { default_pay_period_report_filters, type PayPeriodReportFilters } from 'src/modules/timesheet-approval/types/pay-period-report-options';
|
||||
|
||||
const report_filter_options = ref<PayPeriodReportFilters>(default_pay_period_report_filters);
|
||||
|
||||
const company_options = [
|
||||
{ label: 'Targo', value: report_filter_options.value.companies.targo },
|
||||
{ label: 'Solucom', value: report_filter_options.value.companies.solucom },
|
||||
];
|
||||
|
||||
const type_options = [
|
||||
{ label: 'timesheet_approvals.print_report.shifts', value: report_filter_options.value.types.shifts },
|
||||
{ label: 'timesheet_approvals.print_report.expenses', value: report_filter_options.value.types.expenses },
|
||||
{ label: 'shared.shift_type.holiday', value: report_filter_options.value.types.holiday },
|
||||
{ label: 'shared.shift_type.vacation', value: report_filter_options.value.types.vacation },
|
||||
];
|
||||
|
||||
const is_download_button_disabled = computed(() => {
|
||||
return company_options.map( option => option.value ).filter( value => value === true ).length > 0 ||
|
||||
type_options.map( option => option.value ).filter( value => value === true ).length > 0;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-btn-group rounded push>
|
||||
<q-btn
|
||||
rounded
|
||||
push
|
||||
color="primary"
|
||||
icon="print"
|
||||
:disable="is_download_button_disabled"
|
||||
/>
|
||||
|
||||
<q-btn-dropdown
|
||||
rounded
|
||||
push
|
||||
color="white"
|
||||
text-color="primary"
|
||||
icon="checklist"
|
||||
>
|
||||
<q-list class="row">
|
||||
<q-item class="col">
|
||||
<q-item-label class="text-weight-bolder text-primary q-ma-none q-pa-none text-uppercase">
|
||||
{{$t('timesheet_approvals.print_report.company')}}
|
||||
</q-item-label>
|
||||
|
||||
<q-item-section row no-wrap>
|
||||
<q-checkbox
|
||||
v-for="option, index in company_options"
|
||||
:key="index"
|
||||
v-model="option.value"
|
||||
:val="option.label"
|
||||
:label="option.label"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-separator
|
||||
spaced
|
||||
vertical
|
||||
color="primary"
|
||||
/>
|
||||
|
||||
<q-item class="col">
|
||||
<q-item-section row no-wrap>
|
||||
<p class="text-weight-bolder text-primary q-ma-none q-pa-none text-uppercase">{{$t('timesheet_approvals.print_report.type')}}</p>
|
||||
<q-checkbox
|
||||
v-for="option, index in type_options"
|
||||
:key="index"
|
||||
v-model="option.value"
|
||||
:val="option.label"
|
||||
:label="option.label"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</q-btn-group>
|
||||
</template>
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import type { PayPeriodOverviewEmployee } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface';
|
||||
|
||||
type TableColumn = {
|
||||
name: string;
|
||||
label: string;
|
||||
value: unknown;
|
||||
};
|
||||
|
||||
const { cols, row, initialState } = defineProps<{
|
||||
cols: TableColumn[];
|
||||
row: PayPeriodOverviewEmployee;
|
||||
initialState: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
clickDetails: [ email: string ];
|
||||
updateApproval: [ value: boolean ];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="q-px-sm q-pb-sm q-mt-sm col-xs-12 col-sm-6 col-md-4 col-lg-4 col-xl-3 grid-style-transition">
|
||||
<q-card class="rounded-10">
|
||||
<!-- Card header with employee name and details button-->
|
||||
<q-card-section
|
||||
horizontal
|
||||
class="q-py-none q-pl-md relative"
|
||||
>
|
||||
<div class="text-primary text-h5 text-weight-bolder q-pt-xs overflow-hidden">
|
||||
{{ row.employee_name }}
|
||||
</div>
|
||||
|
||||
<q-space />
|
||||
|
||||
<!-- Buttons to view detailed shifts or view employee timesheet -->
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
square
|
||||
unelevated
|
||||
class="q-py-none q-my-xs"
|
||||
color="primary"
|
||||
icon="work_history"
|
||||
@click="emit('clickDetails', row.email)"
|
||||
>
|
||||
<q-tooltip
|
||||
anchor="top middle"
|
||||
self="center middle"
|
||||
class="bg-primary text-uppercase text-weight-bold"
|
||||
>
|
||||
{{ $t('timesheet_approvals.tooltip.button_detailed_view') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-section>
|
||||
|
||||
<q-separator size="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">
|
||||
{{ cols.find(c => c.name === 'regular_hours')?.label }}
|
||||
</q-item-label>
|
||||
<q-item-label class="text-weight-bolder text-h3 q-py-none">
|
||||
{{ cols.find(c => c.name === 'regular_hours')?.value }}
|
||||
</q-item-label>
|
||||
</q-item>
|
||||
|
||||
<q-separator 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 cols.slice(3, 6)"
|
||||
: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 ">
|
||||
{{ col.value }}
|
||||
</q-item-label>
|
||||
</q-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-separator
|
||||
vertical
|
||||
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 cols.slice(6, )"
|
||||
: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 ">
|
||||
{{ 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 text-weight-bold"
|
||||
:class="initialState ? 'text-white bg-primary' : 'bg-dark'"
|
||||
>
|
||||
<q-item-label class="text-uppercase text-h6 q-ml-sm text-weight-bolder"> {{ row.total_hours + ' h' }} </q-item-label>
|
||||
<q-item-label class="text-uppercase text-weight-bold q-ml-xs"> total </q-item-label>
|
||||
<q-space />
|
||||
<q-checkbox
|
||||
dense
|
||||
left-label
|
||||
size="lg"
|
||||
checked-icon="lock"
|
||||
unchecked-icon="lock_open"
|
||||
:color="initialState ? 'white' : 'primary'" keep-color
|
||||
:model-value="initialState"
|
||||
@update:model-value="val => emit('updateApproval', val)"
|
||||
:label="initialState ? $t('timesheet_approvals.table.verified') : $t('timesheet_approvals.table.unverified')"
|
||||
class="text-uppercase"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
/* eslint-disable */
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { date, type QTableColumn } from 'quasar';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { useTimesheetApprovalApi } from '../composables/use-timesheet-approval-api';
|
||||
import PayPeriodCalendarPicker from 'src/modules/shared/components/utils/pay-period-calendar-picker.vue';
|
||||
import TimesheetApprovalEmployeeOverviewListItem from './timesheet-approval-employee-overview-list-item.vue';
|
||||
import TimesheetApprovalEmployeeDetails from 'src/modules/timesheet-approval/pages/timesheet-approval-employee-details.vue';
|
||||
import { type PayPeriodOverviewEmployee } from '../types/timesheet-approval-pay-period-overview-employee-interface';
|
||||
|
||||
const { t } = useI18n();
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const timesheet_approval_api = useTimesheetApprovalApi();
|
||||
const FORWARD = 1
|
||||
const BACKWARD = -1
|
||||
const filter = ref<string | number | null>('');
|
||||
const original_approvals = ref<Record<string, boolean>>({});
|
||||
const is_showing_details = ref<boolean>(false);
|
||||
const report_filter_company = ref<boolean[]>([true, true]);
|
||||
const report_filter_type = ref<boolean[]>([true, true, true, true]);
|
||||
const clicked_employee_name = ref<string>('');
|
||||
const clicked_employee_email = ref<string>('');
|
||||
const update_key = ref<number>(0);
|
||||
const columns = computed((): QTableColumn<PayPeriodOverviewEmployee>[] => [
|
||||
{
|
||||
name: 'employee_name',
|
||||
label: t('timesheet_approvals.table.full_name'),
|
||||
field: 'employee_name',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'email',
|
||||
label: t('timesheet_approvals.table.email'),
|
||||
field: 'email',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'regular_hours',
|
||||
label: t('shared.shift_type.regular'),
|
||||
field: 'regular_hours',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'evening_hours',
|
||||
label: t('shared.shift_type.evening'),
|
||||
field: 'evening_hours'
|
||||
},
|
||||
{
|
||||
name: 'emergency_hours',
|
||||
label: t('shared.shift_type.emergency'),
|
||||
field: 'emergency_hours'
|
||||
},
|
||||
{
|
||||
name: 'overtime_hours',
|
||||
label: t('shared.shift_type.overtime'),
|
||||
field: 'overtime_hours'
|
||||
},
|
||||
{
|
||||
name: 'expenses',
|
||||
label: t('timesheet_approvals.table.expenses'),
|
||||
field: 'expenses',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'mileage',
|
||||
label: t('timesheet_approvals.table.mileage'),
|
||||
field: 'mileage',
|
||||
sortable: true
|
||||
}
|
||||
]);
|
||||
// const has_changes = computed(() => {
|
||||
// return timesheet_store.pay_period_overview_employees.some(emp => {
|
||||
// return emp.is_approved !== original_approvals.value[emp.email];
|
||||
// });
|
||||
// });
|
||||
|
||||
const is_not_enough_filters = computed(() => {
|
||||
return report_filter_company.value.filter(val => val === true).length < 1 ||
|
||||
report_filter_type.value.filter(val => val === true).length < 1;
|
||||
});
|
||||
|
||||
const filter_types_labels = [
|
||||
t('timesheet_approvals.print_report.shifts'),
|
||||
t('timesheet_approvals.print_report.expenses'),
|
||||
t('shared.shift_type.holiday'),
|
||||
t('shared.shift_type.vacation'),
|
||||
];
|
||||
|
||||
const is_calendar_limit = computed( () => {
|
||||
return timesheet_store.current_pay_period.pay_year === 2024 &&
|
||||
timesheet_store.current_pay_period.pay_period_no <= 1;
|
||||
});
|
||||
|
||||
const getEmployeeApprovalStatusReference = (email: string): boolean => {
|
||||
const approval_status = timesheet_store.pay_period_overview_employee_approval_statuses?.find( status => status.key === email);
|
||||
if (approval_status) {
|
||||
return approval_status.value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const updateEmployeeApprovalStatus = (email: string, value: boolean) => {
|
||||
const approval_status = timesheet_store.pay_period_overview_employee_approval_statuses?.find( status => status.key === email);
|
||||
if (approval_status) {
|
||||
approval_status.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
const getEmployeeOverview = (email: string): PayPeriodOverviewEmployee => {
|
||||
return timesheet_approval_api.getPayPeriodOverviewByEmployeeEmail(email);
|
||||
}
|
||||
|
||||
const onDateSelected = async (date_string: string) => {
|
||||
await timesheet_approval_api.getPayPeriodOverviewByDate(date_string);
|
||||
};
|
||||
|
||||
const onClickedDetails = async (email: string, name: string) => {
|
||||
clicked_employee_name.value = name;
|
||||
clicked_employee_email.value = email;
|
||||
is_showing_details.value = true;
|
||||
|
||||
await timesheet_approval_api.getTimesheetsByPayPeriodAndEmail(email);
|
||||
};
|
||||
|
||||
const onClickPrintReport = async () => {
|
||||
await timesheet_approval_api.getTimesheetApprovalCSVReport(report_filter_company.value, report_filter_type.value);
|
||||
};
|
||||
|
||||
onMounted( async () => {
|
||||
const today = date.formatDate(new Date(), 'YYYY-MM-DD');
|
||||
await timesheet_approval_api.getPayPeriodOverviewByDate(today);
|
||||
|
||||
const approvals = timesheet_store.pay_period_overview_employees.map(emp => [emp.email, emp.is_approved]);
|
||||
original_approvals.value = Object.fromEntries(approvals);
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog
|
||||
v-model="is_showing_details"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-down"
|
||||
@before-show="() => update_key += 1"
|
||||
full-width
|
||||
:full-height="$q.screen.gt.sm"
|
||||
>
|
||||
<TimesheetApprovalEmployeeDetails
|
||||
:is-loading="timesheet_store.is_loading"
|
||||
:employee-name="clicked_employee_name"
|
||||
:employee-overview="getEmployeeOverview(clicked_employee_email)"
|
||||
:employee-details="timesheet_store.pay_period_employee_details"
|
||||
:current-pay-period="timesheet_store.current_pay_period"
|
||||
:update-key="update_key"
|
||||
/>
|
||||
</q-dialog>
|
||||
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
:rows="timesheet_store.pay_period_overview_employees"
|
||||
:columns="columns"
|
||||
row-key="email"
|
||||
:filter="filter"
|
||||
grid
|
||||
dense
|
||||
hide-pagination
|
||||
color="primary"
|
||||
:rows-per-page-options="[0]"
|
||||
card-container-class="justify-center"
|
||||
:loading="timesheet_store.is_loading"
|
||||
:no-data-label="$t('shared.error.no_data_found')"
|
||||
:no-results-label="$t('shared.error.no_search_results')"
|
||||
:loading-label="$t('shared.label.loading')"
|
||||
>
|
||||
<!-- Top Bar that contains Date Picker, Search, Filters, Print Report, etc -->
|
||||
<template #top>
|
||||
<div class="full-width" :class="$q.screen.lt.md ? 'text-center q-gutter-sm' : 'row'">
|
||||
<!-- Date Picker -->
|
||||
<PayPeriodCalendarPicker
|
||||
:is-disabled="timesheet_store.is_loading"
|
||||
:is-previous-limit="is_calendar_limit"
|
||||
@date-selected="onDateSelected"
|
||||
@pressed-previous-button="timesheet_approval_api.getNextPayPeriodOverview(BACKWARD)"
|
||||
@pressed-next-button="timesheet_approval_api.getNextPayPeriodOverview(FORWARD)"
|
||||
/>
|
||||
|
||||
<q-space />
|
||||
|
||||
<q-btn-group rounded push>
|
||||
<q-btn
|
||||
rounded
|
||||
push
|
||||
color="primary"
|
||||
icon="print"
|
||||
:disable="is_not_enough_filters"
|
||||
@click="onClickPrintReport"
|
||||
/>
|
||||
<q-btn-dropdown
|
||||
rounded
|
||||
push
|
||||
color="white"
|
||||
text-color="primary"
|
||||
icon="checklist"
|
||||
>
|
||||
<q-list>
|
||||
<q-item>
|
||||
<q-item-section row no-wrap>
|
||||
<p class="text-weight-bolder text-primary q-ma-none q-pa-none text-uppercase">{{$t('timesheet_approvals.print_report.company')}}</p>
|
||||
<q-checkbox
|
||||
v-for="label, index in ['Targo', 'Solucom']"
|
||||
v-model="report_filter_company[index]"
|
||||
:val="label"
|
||||
:label=label
|
||||
:key="index"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-separator color="primary" class="q-mx-md"/>
|
||||
<q-item>
|
||||
<q-item-section row no-wrap>
|
||||
<p class="text-weight-bolder text-primary q-ma-none q-pa-none text-uppercase">{{$t('timesheet_approvals.print_report.type')}}</p>
|
||||
<q-checkbox
|
||||
v-for="label, index in filter_types_labels"
|
||||
v-model="report_filter_type[index]"
|
||||
:val="label"
|
||||
:label="label"
|
||||
:key="index"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</q-btn-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Template for individual employee cards -->
|
||||
<template #item="props: {
|
||||
cols: (QTableColumn<PayPeriodOverviewEmployee> & { value: unknown })[],
|
||||
row: PayPeriodOverviewEmployee,
|
||||
key: string,
|
||||
}">
|
||||
<TimesheetApprovalEmployeeOverviewListItem
|
||||
:cols="props.cols"
|
||||
:row="props.row"
|
||||
:initial-state="getEmployeeApprovalStatusReference(props.key)"
|
||||
@click-details="email => onClickedDetails(email, props.row.employee_name)"
|
||||
@update-approval="value => updateEmployeeApprovalStatus(props.key, value)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Template for custome failed-to-load state -->
|
||||
<template #no-data="{ message, filter }">
|
||||
<div class="full-width column items-center text-primary q-gutter-sm">
|
||||
<span class="text-h6 q-mt-xl">
|
||||
{{ message }}
|
||||
</span>
|
||||
<q-icon
|
||||
size="4em"
|
||||
:name="filter ? 'filter_alt_off' : 'error_outline'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,36 +1,39 @@
|
|||
import { useTimesheetStore } from "src/stores/timesheet-store";
|
||||
import { useAuthStore } from "src/stores/auth-store";
|
||||
import type { PayPeriodReportFilters } from "../types/timesheet-approval-pay-period-report-interface";
|
||||
import { default_pay_period_overview_employee, type PayPeriodOverviewEmployee } from "../types/timesheet-approval-pay-period-overview-employee-interface";
|
||||
import type { PayPeriodReportFilters } from "src/modules/timesheet-approval/types/pay-period-report";
|
||||
import { default_pay_period_employee_overview, type PayPeriodEmployeeOverview } from "src/modules/timesheet-approval/types/pay-period-employee-overview";
|
||||
import { date } from "quasar";
|
||||
|
||||
export const useTimesheetApprovalApi = () => {
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const auth_store = useAuthStore();
|
||||
|
||||
const getPayPeriodOverviewByDate = async (date_string: string) => {
|
||||
const success = await timesheet_store.getPayPeriodByDate(date_string);
|
||||
const getPayPeriodOverviewByDate = async (date_string: string): Promise<void> => {
|
||||
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date_string);
|
||||
|
||||
if (success) {
|
||||
const current_pay_period = timesheet_store.current_pay_period;
|
||||
await timesheet_store.getTimesheetApprovalPayPeriodEmployeeOverviews(current_pay_period.pay_year, current_pay_period.pay_period_no, auth_store.user.email);
|
||||
await timesheet_store.getPayPeriodEmployeeOverviewListBySupervisorEmail(
|
||||
timesheet_store.pay_period.pay_year,
|
||||
timesheet_store.pay_period.pay_period_no,
|
||||
auth_store.user.email
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getPayPeriodOverviewByEmployeeEmail = (email: string): PayPeriodOverviewEmployee => {
|
||||
const employee_overview = timesheet_store.pay_period_overview_employees.find(overview => overview.email === email);
|
||||
if (employee_overview !== undefined) return employee_overview;
|
||||
return default_pay_period_overview_employee;
|
||||
const getPayPeriodOverviewByEmployeeEmail = (email: string): void => {
|
||||
const employee_overview = timesheet_store.getPayPeriodOverviewByEmployeeEmail(email);
|
||||
};
|
||||
|
||||
/* This method attempts to get the next or previous pay period.
|
||||
It checks if pay period number is within a certain range, adjusts pay period and year accordingly.
|
||||
It then requests the matching pay period object to set as current pay period from server.
|
||||
If successful, it then requests pay period overviews from that new pay period. */
|
||||
const getNextPayPeriodOverview = async (direction: number) => {
|
||||
const current_pay_period = timesheet_store.current_pay_period;
|
||||
let new_pay_period_no = current_pay_period.pay_period_no + direction;
|
||||
let new_pay_year = current_pay_period.pay_year;
|
||||
If successful, it then requests pay period overviews from that new pay period, using either the current user or
|
||||
any other supervisor email provided. */
|
||||
const getNextOrPreviousPayPeriodOverviewList = async (direction: number, supervisor_email?: string): Promise<void> => {
|
||||
const email = supervisor_email ?? auth_store.user.email;
|
||||
|
||||
let new_pay_period_no = timesheet_store.pay_period.pay_period_no + direction;
|
||||
let new_pay_year = timesheet_store.pay_period.pay_year;
|
||||
|
||||
if (new_pay_period_no > 26) {
|
||||
new_pay_period_no = 1;
|
||||
|
|
@ -42,17 +45,13 @@ export const useTimesheetApprovalApi = () => {
|
|||
new_pay_year -= 1;
|
||||
}
|
||||
|
||||
const success = await timesheet_store.getPayPeriodByYearAndPeriodNumber(new_pay_year, new_pay_period_no);
|
||||
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(new_pay_year, new_pay_period_no);
|
||||
|
||||
if (success) {
|
||||
await timesheet_store.getTimesheetApprovalPayPeriodEmployeeOverviews(new_pay_year, new_pay_period_no, auth_store.user.email);
|
||||
await timesheet_store.getPayPeriodEmployeeOverviewListBySupervisorEmail(new_pay_year, new_pay_period_no, email);
|
||||
}
|
||||
};
|
||||
|
||||
const getTimesheetsByPayPeriodAndEmail = async (employee_email: string) => {
|
||||
await timesheet_store.getTimesheetsByPayPeriodAndEmail(employee_email);
|
||||
};
|
||||
|
||||
const getTimesheetApprovalCSVReport = async ( report_filter_company: boolean[], report_filter_type: boolean[] ) => {
|
||||
const [ targo, solucom ] = report_filter_company;
|
||||
const [ shifts, expenses, holiday, vacation ] = report_filter_type;
|
||||
|
|
@ -64,29 +63,10 @@ export const useTimesheetApprovalApi = () => {
|
|||
await timesheet_store.getTimesheetApprovalCSVReport(options);
|
||||
};
|
||||
|
||||
const getCurrentPayPerdioOverview = async (): Promise<void> => {
|
||||
const today = date.formatDate(new Date(), 'YYYY-MM-DD');
|
||||
const success = await timesheet_store.getPayPeriodByDate(today);
|
||||
if(!success) return;
|
||||
|
||||
const { pay_year, pay_period_no } = timesheet_store.current_pay_period;
|
||||
|
||||
await timesheet_store.getTimesheetApprovalPayPeriodEmployeeOverviews(
|
||||
pay_year,
|
||||
pay_period_no,
|
||||
auth_store.user.email
|
||||
);
|
||||
|
||||
await timesheet_store.getTimesheetsByPayPeriodAndEmail(auth_store.user.email);
|
||||
|
||||
};
|
||||
|
||||
return {
|
||||
getPayPeriodOverviewByDate,
|
||||
getNextPayPeriodOverview,
|
||||
getNextOrPreviousPayPeriodOverviewList,
|
||||
getPayPeriodOverviewByEmployeeEmail,
|
||||
getTimesheetsByPayPeriodAndEmail,
|
||||
getTimesheetApprovalCSVReport,
|
||||
getCurrentPayPerdioOverview
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import DetailedShiftList from 'src/modules/timesheet-approval/components/detailed-shift-list.vue';
|
||||
import DetailedChartHoursWorked from 'src/modules/timesheet-approval/components/graphs/detailed-chart-hours-worked.vue';
|
||||
import DetailedChartShiftTypes from 'src/modules/timesheet-approval/components/graphs/detailed-chart-shift-types.vue';
|
||||
import DetailedChartExpenses from 'src/modules/timesheet-approval/components/graphs/detailed-chart-expenses.vue';
|
||||
import type { PayPeriodEmployeeOverview } from 'src/modules/timesheet-approval/types/pay-period-employee-overview';
|
||||
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/pay-period-employee-details';
|
||||
import { shift_type_legend } from 'src/modules/timesheet-approval/types/detailed-shift-color';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
|
||||
const dialog_model = defineModel<boolean>('dialog', { default: false });
|
||||
|
||||
defineProps<{
|
||||
isLoading: boolean;
|
||||
employeeOverview: PayPeriodEmployeeOverview;
|
||||
employeeDetails: PayPeriodEmployeeDetails;
|
||||
}>();
|
||||
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const is_showing_graph = ref<boolean>(true);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog
|
||||
v-model="dialog_model"
|
||||
full-width
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-down"
|
||||
>
|
||||
<q-card
|
||||
class="q-pa-sm shadow-12 rounded-15 column no-wrap relative"
|
||||
:style="$q.screen.lt.md ? '' : 'width: 60vw !important;'"
|
||||
>
|
||||
<!-- loader -->
|
||||
<q-card-section
|
||||
v-if="isLoading"
|
||||
class="column flex-center text-center"
|
||||
>
|
||||
<q-spinner
|
||||
color="primary"
|
||||
size="5em"
|
||||
:thickness="10"
|
||||
class="col-auto"
|
||||
/>
|
||||
<div class="col-auto text-primary text-h6 text-weight-bold text-center ">
|
||||
{{ $t('shared.loading') }}
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<!-- employee name -->
|
||||
<q-card-section
|
||||
v-if="!isLoading"
|
||||
class="text-h5 text-weight-bolder text-center text-primary q-pa-none text-uppercase col-auto"
|
||||
>
|
||||
<span> {{ employeeDetails.employee_full_name }} </span>
|
||||
|
||||
<q-separator
|
||||
spaced
|
||||
size="2px"
|
||||
/>
|
||||
|
||||
<q-card-actions
|
||||
align="center"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<q-btn-toggle
|
||||
v-model="is_showing_graph"
|
||||
color="white"
|
||||
text-color="primary"
|
||||
toggle-color="primary"
|
||||
:options="[
|
||||
{ icon: 'bar_chart', value: true },
|
||||
{ icon: 'edit', value: false },
|
||||
]"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card-section>
|
||||
|
||||
<!-- employee timesheet details edit -->
|
||||
<q-card-section
|
||||
v-if="!is_showing_graph"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<!-- shift type color legend -->
|
||||
<q-card-section class="q-py-xs q-px-none text-center q-my-s">
|
||||
<q-badge
|
||||
v-for="shift_type, index in shift_type_legend"
|
||||
:key="index"
|
||||
:color="shift_type.background_color"
|
||||
:label="$t(shift_type.type_label)"
|
||||
:text-color="shift_type.font_color"
|
||||
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>
|
||||
|
||||
<!-- list of shifts, broken down into weekly columns -->
|
||||
<q-card-section
|
||||
:horizontal="$q.screen.gt.sm"
|
||||
class="q-pa-none bg-secondary rounded-10"
|
||||
>
|
||||
<DetailedShiftList
|
||||
:raw-data="employeeDetails"
|
||||
:current-pay-period="timesheet_store.pay_period"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card-section>
|
||||
|
||||
<!-- employee timesheet details with chart -->
|
||||
<q-card-section
|
||||
v-if="is_showing_graph"
|
||||
class="q-pa-md col column full-width no-wrap"
|
||||
>
|
||||
<q-card-section
|
||||
:horizontal="!$q.screen.lt.md"
|
||||
class="q-pa-none col no-wrap"
|
||||
style="min-height: 300px;"
|
||||
>
|
||||
<DetailedChartHoursWorked
|
||||
:raw-data="employeeDetails"
|
||||
class="col-7"
|
||||
/>
|
||||
|
||||
<q-separator
|
||||
spaced
|
||||
:vertical="!$q.screen.lt.md"
|
||||
/>
|
||||
|
||||
<div class="column col justify-center no-wrap q-pa-none">
|
||||
<DetailedChartShiftTypes
|
||||
:raw-data="employeeOverview"
|
||||
class="col-5"
|
||||
/>
|
||||
|
||||
<q-separator
|
||||
spaced
|
||||
:vertical="!$q.screen.lt.md"
|
||||
/>
|
||||
|
||||
<DetailedChartExpenses
|
||||
:raw-data="employeeDetails"
|
||||
class="col"
|
||||
/>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
/*eslint-disable*/
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import TimesheetApprovalEmployeeDetailsShifts from 'src/modules/timesheet-approval/components/timesheet-approval-employee-details-shifts.vue';
|
||||
import TimesheetApprovalEmployeeDetailsHoursWorkedChart from 'src/modules/timesheet-approval/components/graphs/timesheet-approval-employee-details-hours-worked-chart.vue';
|
||||
import TimesheetApprovalEmployeeDetailsShiftTypesChart from 'src/modules/timesheet-approval/components/graphs/timesheet-approval-employee-details-shift-types-chart.vue';
|
||||
import TimesheetApprovalEmployeeExpensesChart from 'src/modules/timesheet-approval/components/graphs/timesheet-approval-employee-expenses-chart.vue';
|
||||
import type { PayPeriodOverviewEmployee } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface';
|
||||
import type { PayPeriodEmployeeDetails } from '../types/timesheet-approval-pay-period-employee-details-interface';
|
||||
import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||
|
||||
const props = defineProps<{
|
||||
isLoading: boolean;
|
||||
employeeName: string;
|
||||
employeeOverview: PayPeriodOverviewEmployee;
|
||||
employeeDetails: PayPeriodEmployeeDetails;
|
||||
currentPayPeriod: PayPeriod;
|
||||
updateKey: number;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const is_showing_graph = ref<boolean>(true);
|
||||
|
||||
type shiftColor = {
|
||||
type: string;
|
||||
color: string;
|
||||
text_color?: string;
|
||||
}
|
||||
|
||||
const shift_type_legend: shiftColor[] = [
|
||||
{
|
||||
type: t('shared.shift_type.regular'),
|
||||
color: 'secondary',
|
||||
text_color: '',
|
||||
},
|
||||
{
|
||||
type: t('shared.shift_type.evening'),
|
||||
color: 'warning',
|
||||
},
|
||||
{
|
||||
type: t('shared.shift_type.emergency'),
|
||||
color: 'amber-10',
|
||||
},
|
||||
{
|
||||
type: t('shared.shift_type.overtime'),
|
||||
color: 'negative',
|
||||
},
|
||||
{
|
||||
type: t('shared.shift_type.vacation'),
|
||||
color: 'purple-10',
|
||||
},
|
||||
{
|
||||
type: t('shared.shift_type.holiday'),
|
||||
color: 'purple-8',
|
||||
},
|
||||
{
|
||||
type: t('shared.shift_type.sick'),
|
||||
color: 'grey-8',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-card
|
||||
class="q-pa-sm shadow-12 rounded-15 column no-wrap relative"
|
||||
:style="$q.screen.lt.md ? '' : 'width: 60vw !important; height: 70vh !important;' "
|
||||
>
|
||||
<!-- loader -->
|
||||
<q-card-section
|
||||
v-if="props.isLoading"
|
||||
class="absolute-center text-center"
|
||||
>
|
||||
<q-spinner
|
||||
color="primary"
|
||||
size="5em"
|
||||
:thickness="10"
|
||||
/>
|
||||
<div class="text-primary text-h6 text-weight-bold">
|
||||
{{ $t('shared.loading') }}
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<!-- employee name -->
|
||||
<q-card-section
|
||||
v-if="!props.isLoading"
|
||||
class="text-h5 text-weight-bolder text-center text-primary q-pa-none text-uppercase col-auto"
|
||||
>
|
||||
{{ props.employeeName }}
|
||||
|
||||
<q-separator spaced size="2px" />
|
||||
<q-card-actions align="center" class="q-pa-none">
|
||||
<q-card flat class="bg-secondary rounded-5 q-pa-xs">
|
||||
<q-btn-toggle
|
||||
color="white"
|
||||
text-color="primary"
|
||||
toggle-color="primary"
|
||||
v-model="is_showing_graph"
|
||||
:options="[
|
||||
{icon: 'bar_chart', value: true},
|
||||
{icon: 'edit', value: false},
|
||||
]"
|
||||
/>
|
||||
</q-card>
|
||||
</q-card-actions>
|
||||
</q-card-section>
|
||||
|
||||
<!-- employee timesheet details edit -->
|
||||
<q-card-section
|
||||
v-if="!props.isLoading && !is_showing_graph"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<!-- shift type color legend -->
|
||||
<q-card-section class="q-py-xs q-px-none text-center q-my-s">
|
||||
<q-badge
|
||||
v-for="shift_type in shift_type_legend"
|
||||
:color="shift_type.color"
|
||||
:label="shift_type.type"
|
||||
: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>
|
||||
|
||||
<!-- list of shifts, broken down into weekly columns -->
|
||||
<q-card-section
|
||||
:horizontal="$q.screen.gt.sm"
|
||||
class="q-pa-none bg-secondary rounded-10"
|
||||
>
|
||||
<TimesheetApprovalEmployeeDetailsShifts
|
||||
:raw-data="props.employeeDetails"
|
||||
:current-pay-period="props.currentPayPeriod"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card-section>
|
||||
|
||||
<!-- employee timesheet details with graphs -->
|
||||
<q-card-section v-if="!props.isLoading && is_showing_graph" class="q-pa-md col column full-width no-wrap">
|
||||
<q-card-section :horizontal="!$q.screen.lt.md" class="q-pa-none col no-wrap" style="min-height: 300px;">
|
||||
<TimesheetApprovalEmployeeDetailsHoursWorkedChart
|
||||
:raw-data="props.employeeDetails"
|
||||
class="col-7"
|
||||
/>
|
||||
|
||||
<q-separator vertical spaced />
|
||||
|
||||
<div class="column col justify-center no-wrap q-pa-none">
|
||||
<TimesheetApprovalEmployeeDetailsShiftTypesChart
|
||||
:raw-data="props.employeeOverview"
|
||||
class="col-5"
|
||||
/>
|
||||
|
||||
<q-separator :vertical="$q.screen.lt.md" spaced />
|
||||
|
||||
<TimesheetApprovalEmployeeExpensesChart
|
||||
:raw-data="props.employeeDetails"
|
||||
class="col"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
|
@ -1,23 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import TimesheetApprovalEmployeeOverviewList from '../components/timesheet-approval-employee-overview-list.vue';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { onMounted } from 'vue';
|
||||
import { date } from 'quasar';
|
||||
import { useTimesheetApprovalApi } from 'src/modules/timesheet-approval/composables/use-timesheet-approval-api';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import EmployeeOverviewList from 'src/modules/timesheet-approval/components/employee-overview/overview-list.vue';
|
||||
import TimesheetApprovalDetailed from 'src/modules/timesheet-approval/pages/timesheet-approval-detailed.vue';
|
||||
import PageHeaderTemplate from 'src/modules/shared/components/page-header-template.vue';
|
||||
|
||||
const { d } = useI18n();
|
||||
const timesheet_approval_api = useTimesheetApprovalApi();
|
||||
const timesheet_store = useTimesheetStore();
|
||||
|
||||
const pay_period_label = computed(() => {
|
||||
const dates = timesheet_store.current_pay_period.label.split('.');
|
||||
|
||||
if ( dates.length < 2 ) {
|
||||
return { start_date: '—', end_date: '—' }
|
||||
}
|
||||
|
||||
const start_date = d(new Date(dates[0] as string), { day: 'numeric', month: 'long', year: 'numeric', });
|
||||
const end_date = d(new Date(dates[1] as string), { day: 'numeric', month: 'long', year: 'numeric', });
|
||||
|
||||
return { start_date, end_date };
|
||||
onMounted( async () => {
|
||||
await timesheet_approval_api.getPayPeriodOverviewByDate(date.formatDate( new Date(), 'YYYY-MM-DD'));
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -26,21 +20,18 @@
|
|||
padding
|
||||
class="q-pa-md bg-secondary "
|
||||
>
|
||||
<div class="column q-mt-lg text-uppercase text-center text-weight-bolder text-h4">
|
||||
{{ $t('timesheet_approvals.page_title') }}
|
||||
<div class="col row items-center justify-center full-width q-py-none q-my-none">
|
||||
<div class="text-primary text-weight-bold text-h6">
|
||||
{{ pay_period_label.start_date }}
|
||||
</div>
|
||||
<div class="text-body2 q-mx-md text-weight-medium">
|
||||
{{ $t('shared.misc.to') }}
|
||||
</div>
|
||||
<div class="text-primary text-weight-bold text-h6">
|
||||
{{ pay_period_label.end_date }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PageHeaderTemplate
|
||||
title="timesheet_approvals.page_title"
|
||||
:start-date="timesheet_store.pay_period.period_start"
|
||||
:end-date="timesheet_store.pay_period.period_end"
|
||||
/>
|
||||
|
||||
<TimesheetApprovalEmployeeOverviewList />
|
||||
<TimesheetApprovalDetailed
|
||||
:is-loading="timesheet_store.is_loading"
|
||||
:employee-overview="timesheet_store.pay_period_employee_overview"
|
||||
:employee-details="timesheet_store.pay_period_employee_details"
|
||||
/>
|
||||
|
||||
<EmployeeOverviewList />
|
||||
</q-page>
|
||||
</template>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { api } from "src/boot/axios";
|
||||
import type { PayPeriodOverview } from "../types/timesheet-approval-pay-period-overview-interface";
|
||||
import type { PayPeriodOverview } from "src/modules/timesheet-approval/types/pay-period-overview";
|
||||
import type { PayPeriod } from "src/modules/shared/types/pay-period-interface";
|
||||
import type { PayPeriodEmployeeDetails } from "../types/timesheet-approval-pay-period-employee-details-interface";
|
||||
import type { PayPeriodReportFilters } from "../types/timesheet-approval-pay-period-report-interface";
|
||||
import type { PayPeriodEmployeeDetails } from "src/modules/timesheet-approval/types/pay-period-employee-details";
|
||||
import type { PayPeriodReportFilters } from "src/modules/timesheet-approval/types/pay-period-report";
|
||||
|
||||
export const timesheetApprovalService = {
|
||||
getPayPeriodByDate: async (date_string: string): Promise<PayPeriod> => {
|
||||
|
|
@ -16,13 +16,13 @@ export const timesheetApprovalService = {
|
|||
},
|
||||
|
||||
|
||||
getPayPeriodEmployeeOverviews: async (year: number, period_number: number, supervisor_email: string): Promise<PayPeriodOverview> => {
|
||||
getPayPeriodEmployeeOverviewListBySupervisorEmail: 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}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getTimesheetsByPayPeriodAndEmail: async (year: number, period_no: number, email: string): Promise<PayPeriodEmployeeDetails> => {
|
||||
getPayPeriodEmployeeDetailsByPayPeriodAndEmail: async (year: number, period_no: number, email: string): Promise<PayPeriodEmployeeDetails> => {
|
||||
const response = await api.get('timesheets', { params: { year, period_no, email, }});
|
||||
return response.data;
|
||||
},
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
// 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_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"
|
||||
// }
|
||||
// ]
|
||||
43
src/modules/timesheet-approval/types/detailed-shift-color.ts
Normal file
43
src/modules/timesheet-approval/types/detailed-shift-color.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
export interface shiftColor {
|
||||
type_label: string;
|
||||
background_color: string;
|
||||
font_color: string;
|
||||
}
|
||||
|
||||
export const shift_type_legend: shiftColor[] = [
|
||||
{
|
||||
type_label: 'shared.shift_type.regular',
|
||||
background_color: 'blue-grey-4',
|
||||
font_color: 'blue-grey-8',
|
||||
},
|
||||
{
|
||||
type_label: 'shared.shift_type.evening',
|
||||
background_color: 'warning',
|
||||
font_color: 'blue-grey-2',
|
||||
},
|
||||
{
|
||||
type_label: 'shared.shift_type.emergency',
|
||||
background_color: 'amber-10',
|
||||
font_color: 'blue-grey-2',
|
||||
},
|
||||
{
|
||||
type_label: 'shared.shift_type.overtime',
|
||||
background_color: 'negative',
|
||||
font_color: 'blue-grey-2',
|
||||
},
|
||||
{
|
||||
type_label: 'shared.shift_type.vacation',
|
||||
background_color: 'purple-10',
|
||||
font_color: 'blue-grey-2',
|
||||
},
|
||||
{
|
||||
type_label: 'shared.shift_type.holiday',
|
||||
background_color: 'purple-8',
|
||||
font_color: 'blue-grey-2',
|
||||
},
|
||||
{
|
||||
type_label: 'shared.shift_type.sick',
|
||||
background_color: 'grey-8',
|
||||
font_color: 'blue-grey-2',
|
||||
},
|
||||
]
|
||||
|
|
@ -3,9 +3,11 @@ import { default_timesheet_details_week, type TimesheetDetailsWeek } from "src/m
|
|||
export interface PayPeriodEmployeeDetails {
|
||||
week1: TimesheetDetailsWeek;
|
||||
week2: TimesheetDetailsWeek;
|
||||
employee_full_name: string;
|
||||
};
|
||||
|
||||
export const default_pay_period_employee_details = {
|
||||
week1: default_timesheet_details_week(),
|
||||
week2: default_timesheet_details_week(),
|
||||
employee_full_name: "",
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
export interface PayPeriodEmployeeOverview {
|
||||
email: string;
|
||||
employee_name: string;
|
||||
regular_hours: number;
|
||||
evening_hours: number;
|
||||
emergency_hours: number;
|
||||
overtime_hours: number;
|
||||
total_hours: number;
|
||||
expenses: number;
|
||||
mileage: number;
|
||||
is_approved: boolean;
|
||||
};
|
||||
|
||||
export const default_pay_period_employee_overview: PayPeriodEmployeeOverview = {
|
||||
email: '',
|
||||
employee_name: '',
|
||||
regular_hours: -1,
|
||||
evening_hours: -1,
|
||||
emergency_hours: -1,
|
||||
overtime_hours: -1,
|
||||
total_hours: -1,
|
||||
expenses: -1,
|
||||
mileage: -1,
|
||||
is_approved: false
|
||||
}
|
||||
|
||||
export const pay_period_employee_overview_columns = [
|
||||
{
|
||||
name: 'employee_name',
|
||||
label: 'timesheet_approvals.table.full_name',
|
||||
field: 'employee_name',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'email',
|
||||
label: 'timesheet_approvals.table.email',
|
||||
field: 'email',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'regular_hours',
|
||||
label: 'shared.shift_type.regular',
|
||||
field: 'regular_hours',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'evening_hours',
|
||||
label: 'shared.shift_type.evening',
|
||||
field: 'evening_hours',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'emergency_hours',
|
||||
label: 'shared.shift_type.emergency',
|
||||
field: 'emergency_hours',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'overtime_hours',
|
||||
label: 'shared.shift_type.overtime',
|
||||
field: 'overtime_hours',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'expenses',
|
||||
label: 'timesheet_approvals.table.expenses',
|
||||
field: 'expenses',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'mileage',
|
||||
label: 'timesheet_approvals.table.mileage',
|
||||
field: 'mileage',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'is_approved',
|
||||
label: 'timesheet_approvals.table.is_approved',
|
||||
field: 'is_approved',
|
||||
sortable: true,
|
||||
}
|
||||
];
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { PayPeriodOverviewEmployee } from "./timesheet-approval-pay-period-overview-employee-interface";
|
||||
import type { PayPeriodEmployeeOverview } from "./pay-period-employee-overview";
|
||||
|
||||
export interface PayPeriodOverview {
|
||||
pay_period_no: number;
|
||||
|
|
@ -7,5 +7,5 @@ export interface PayPeriodOverview {
|
|||
period_start: string;
|
||||
period_end: string;
|
||||
label: string;
|
||||
employees_overview: PayPeriodOverviewEmployee[];
|
||||
employees_overview: PayPeriodEmployeeOverview[];
|
||||
};
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
export interface PayPeriodReportFilters {
|
||||
types: {
|
||||
shifts: boolean;
|
||||
expenses: boolean;
|
||||
holiday: boolean;
|
||||
vacation: boolean;
|
||||
};
|
||||
companies: {
|
||||
targo: boolean;
|
||||
solucom: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export const default_pay_period_report_filters: PayPeriodReportFilters = {
|
||||
types: {
|
||||
shifts: true,
|
||||
expenses: true,
|
||||
holiday: true,
|
||||
vacation: true,
|
||||
},
|
||||
companies: {
|
||||
targo: true,
|
||||
solucom: true,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
export interface PayPeriodOverviewEmployee {
|
||||
email: string;
|
||||
employee_name: string;
|
||||
regular_hours: number;
|
||||
evening_hours: number;
|
||||
emergency_hours: number;
|
||||
overtime_hours: number;
|
||||
total_hours: number;
|
||||
expenses: number;
|
||||
mileage: number;
|
||||
is_approved: boolean;
|
||||
};
|
||||
|
||||
export const default_pay_period_overview_employee: PayPeriodOverviewEmployee = {
|
||||
email: '',
|
||||
employee_name: '',
|
||||
regular_hours: -1,
|
||||
evening_hours: -1,
|
||||
emergency_hours: -1,
|
||||
overtime_hours: -1,
|
||||
total_hours: -1,
|
||||
expenses: -1,
|
||||
mileage: -1,
|
||||
is_approved: false
|
||||
}
|
||||
|
|
@ -2,9 +2,9 @@ import { api } from "src/boot/axios";
|
|||
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";
|
||||
import type { PayPeriodEmployeeDetails } from "src/modules/timesheet-approval/types/pay-period-employee-details";
|
||||
import type { PayPeriodOverview } from "src/modules/timesheet-approval/types/pay-period-overview";
|
||||
import type { PayPeriodReportFilters } from "src/modules/timesheet-approval/types/pay-period-report";
|
||||
|
||||
export const timesheetTempService = {
|
||||
//GET
|
||||
|
|
|
|||
|
|
@ -26,3 +26,12 @@ type Expenses = {
|
|||
supervisor_comment: string;
|
||||
is_approved: boolean;
|
||||
}
|
||||
|
||||
export const default_timesheet: Timesheet = {
|
||||
start_day: '',
|
||||
end_day: '',
|
||||
label: '',
|
||||
is_approved: false,
|
||||
shifts: [],
|
||||
expenses: [],
|
||||
};
|
||||
|
|
@ -1,181 +1,166 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { timesheetApprovalService } from 'src/modules/timesheet-approval/services/services-timesheet-approval';
|
||||
import { timesheetApprovalService } from 'src/modules/timesheet-approval/services/timesheet-approval-service';
|
||||
import { timesheetTempService } from 'src/modules/timesheets/services/timesheet-services';
|
||||
import { default_pay_period_employee_details, type PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-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 { PayPeriodReportFilters } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-report-interface';
|
||||
import type { Timesheet } from 'src/modules/timesheets/types/timesheet-interface';
|
||||
import { default_pay_period_employee_details, type PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/pay-period-employee-details';
|
||||
import { default_pay_period, type PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||
import { default_pay_period_employee_overview, type PayPeriodEmployeeOverview } from "src/modules/timesheet-approval/types/pay-period-employee-overview";
|
||||
import type { PayPeriodReportFilters } from 'src/modules/timesheet-approval/types/pay-period-report';
|
||||
import { default_timesheet, type Timesheet } from 'src/modules/timesheets/types/timesheet-interface';
|
||||
import type { CreateShiftPayload } from 'src/modules/timesheets/types/timesheet-shifts-payload-interface';
|
||||
|
||||
const default_pay_period: PayPeriod = {
|
||||
pay_period_no: -1,
|
||||
period_start: '',
|
||||
period_end: '',
|
||||
payday: '',
|
||||
pay_year: -1,
|
||||
label: ''
|
||||
};
|
||||
|
||||
//employee timesheet
|
||||
const default_timesheet: Timesheet = {
|
||||
start_day: '',
|
||||
end_day: '',
|
||||
label: '',
|
||||
is_approved: false,
|
||||
shifts: [],
|
||||
expenses: [],
|
||||
};
|
||||
import { withLoading } from 'src/utils/store-helpers';
|
||||
|
||||
export const useTimesheetStore = defineStore('timesheet', () => {
|
||||
const is_loading = ref<boolean>(false);
|
||||
const current_pay_period = ref<PayPeriod>(default_pay_period);
|
||||
const pay_period_overview_employees = ref<PayPeriodOverviewEmployee[]>([]);
|
||||
const pay_period_overview_employee_approval_statuses = ref<{key: string, value: boolean}[] | undefined>();
|
||||
const pay_period = ref<PayPeriod>(default_pay_period);
|
||||
const pay_period_employee_overview_list = ref<PayPeriodEmployeeOverview[]>([]);
|
||||
const pay_period_employee_overview = ref<PayPeriodEmployeeOverview>(default_pay_period_employee_overview);
|
||||
const pay_period_employee_details = ref<PayPeriodEmployeeDetails>(default_pay_period_employee_details);
|
||||
const pay_period_report = ref();
|
||||
const timesheet = ref<Timesheet>(default_timesheet);
|
||||
|
||||
//employee timesheet
|
||||
const current_timesheet = ref<Timesheet>(default_timesheet);
|
||||
const getPayPeriodByDateOrYearAndNumber = (date_or_year: string | number, period_number?: number): Promise<boolean> => {
|
||||
return withLoading( is_loading, async () => {
|
||||
try {
|
||||
let response;
|
||||
|
||||
const getPayPeriodByDate = async (date_string: string): Promise<boolean> => {
|
||||
is_loading.value = true;
|
||||
if (typeof date_or_year === 'string') {
|
||||
response = await timesheetApprovalService.getPayPeriodByDate(date_or_year);
|
||||
return true;
|
||||
}
|
||||
else if ( typeof date_or_year === 'number' && period_number ) {
|
||||
response = await timesheetApprovalService.getPayPeriodByYearAndPeriodNumber(date_or_year, period_number);
|
||||
return true;
|
||||
}
|
||||
else response = default_pay_period;
|
||||
|
||||
try {
|
||||
const response = await timesheetApprovalService.getPayPeriodByDate(date_string);
|
||||
current_pay_period.value = response;
|
||||
is_loading.value = false;
|
||||
pay_period.value = response;
|
||||
return false;
|
||||
} catch(error){
|
||||
console.error('Could not get current pay period: ', error );
|
||||
pay_period.value = default_pay_period;
|
||||
pay_period_employee_overview_list.value = [];
|
||||
//TODO: More in-depth error-handling here
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch(error){
|
||||
console.error('Could not get current pay period: ', error );
|
||||
current_pay_period.value = default_pay_period;
|
||||
pay_period_overview_employees.value = [];
|
||||
//TODO: More in-depth error-handling here
|
||||
}
|
||||
|
||||
is_loading.value = false;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const getPayPeriodByYearAndPeriodNumber = async (year: number, period_number: number): Promise<boolean> => {
|
||||
is_loading.value = true;
|
||||
const getPayPeriodEmployeeOverviewListBySupervisorEmail = async (pay_year: number, period_number: number, supervisor_email: string): Promise<boolean> => {
|
||||
return withLoading( is_loading, async () => {
|
||||
try {
|
||||
const response = await timesheetApprovalService.getPayPeriodEmployeeOverviewListBySupervisorEmail( pay_year, period_number, supervisor_email );
|
||||
pay_period_employee_overview_list.value = response.employees_overview;
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving Employee Pay Period overviews: ', error);
|
||||
pay_period_employee_overview_list.value = [];
|
||||
// TODO: More in-depth error-handling here
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await timesheetApprovalService.getPayPeriodByYearAndPeriodNumber(year, period_number);
|
||||
current_pay_period.value = response;
|
||||
is_loading.value = false;
|
||||
|
||||
return true;
|
||||
} catch(error){
|
||||
console.error('Could not get current pay period: ', error );
|
||||
current_pay_period.value = default_pay_period;
|
||||
pay_period_overview_employees.value = [];
|
||||
//TODO: More in-depth error-handling here
|
||||
}
|
||||
|
||||
is_loading.value = false;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const getTimesheetApprovalPayPeriodEmployeeOverviews = async (pay_year: number, period_number: number, supervisor_email: string) => {
|
||||
is_loading.value = true;
|
||||
|
||||
try {
|
||||
const response = await timesheetApprovalService.getPayPeriodEmployeeOverviews(
|
||||
pay_year,
|
||||
period_number,
|
||||
supervisor_email
|
||||
);
|
||||
pay_period_overview_employees.value = response.employees_overview;
|
||||
pay_period_overview_employee_approval_statuses.value = response.employees_overview.map( employee => ({ key: employee.email, value: employee.is_approved }) );
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving Employee Pay Period overviews: ', error);
|
||||
pay_period_overview_employees.value = [];
|
||||
// TODO: More in-depth error-handling here
|
||||
const getPayPeriodOverviewByEmployeeEmail = (email: string): PayPeriodEmployeeOverview => {
|
||||
const response = pay_period_employee_overview_list.value?.find( employee_overview => employee_overview.email === email);
|
||||
if (typeof response === 'undefined') {
|
||||
pay_period_employee_overview.value = default_pay_period_employee_overview;
|
||||
} else {
|
||||
pay_period_employee_overview.value = response;
|
||||
}
|
||||
|
||||
is_loading.value = false;
|
||||
return pay_period_employee_overview.value;
|
||||
};
|
||||
|
||||
//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;
|
||||
}
|
||||
return withLoading( is_loading, async () => {
|
||||
try{
|
||||
const response = await timesheetTempService.getTimesheetsByEmail(employee_email);
|
||||
timesheet.value = response;
|
||||
|
||||
return true;
|
||||
}catch (error) {
|
||||
console.error('There was an error retrieving timesheet details for this employee: ', error);
|
||||
timesheet.value = { ...default_timesheet }
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const getTimesheetsByPayPeriodAndEmail = async (employee_email: string) => {
|
||||
is_loading.value = true;
|
||||
//employee timesheet
|
||||
const createTimesheetShifts = async (employee_email: string, shifts: CreateShiftPayload[], offset = 0) => {
|
||||
return withLoading( is_loading, async () => {
|
||||
try{
|
||||
const response = await timesheetTempService.createTimesheetShifts(employee_email, shifts, offset);
|
||||
timesheet.value = response;
|
||||
|
||||
try {
|
||||
const response = await timesheetApprovalService.getTimesheetsByPayPeriodAndEmail(
|
||||
current_pay_period.value.pay_year,
|
||||
current_pay_period.value.pay_period_no,
|
||||
employee_email
|
||||
);
|
||||
pay_period_employee_details.value = response;
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving timesheet details for this employee: ', error);
|
||||
// TODO: More in-depth error-handling here
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('createTimesheetShifts error: ', err);
|
||||
}
|
||||
|
||||
is_loading.value = false;
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const getPayPeriodEmployeeDetailsByEmployeeEmail = async (employee_email: string) => {
|
||||
return withLoading( is_loading, async () => {
|
||||
try {
|
||||
const response = await timesheetApprovalService.getPayPeriodEmployeeDetailsByPayPeriodAndEmail(
|
||||
pay_period.value.pay_year,
|
||||
pay_period.value.pay_period_no,
|
||||
employee_email
|
||||
);
|
||||
pay_period_employee_details.value = response;
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving timesheet details for this employee: ', error);
|
||||
// TODO: More in-depth error-handling here
|
||||
}
|
||||
|
||||
pay_period_employee_details.value = default_pay_period_employee_details;
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const getTimesheetApprovalCSVReport = async (report_filters?: PayPeriodReportFilters) => {
|
||||
is_loading.value = true;
|
||||
return withLoading( is_loading, async () => {
|
||||
try {
|
||||
const response = await timesheetApprovalService.getTimesheetApprovalCSVReport(
|
||||
pay_period.value.pay_year,
|
||||
pay_period.value.pay_period_no,
|
||||
report_filters
|
||||
);
|
||||
pay_period_report.value = response;
|
||||
|
||||
try {
|
||||
const response = await timesheetApprovalService.getTimesheetApprovalCSVReport(
|
||||
current_pay_period.value.pay_year,
|
||||
current_pay_period.value.pay_period_no,
|
||||
report_filters
|
||||
);
|
||||
pay_period_report.value = response;
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving the report CSV: ', error);
|
||||
// TODO: More in-depth error-handling here
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving the report CSV: ', error);
|
||||
// TODO: More in-depth error-handling here
|
||||
}
|
||||
|
||||
is_loading.value = false;
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
current_pay_period,
|
||||
pay_period_overview_employees,
|
||||
pay_period_overview_employee_approval_statuses,
|
||||
pay_period,
|
||||
pay_period_employee_overview_list,
|
||||
pay_period_employee_overview,
|
||||
pay_period_employee_details,
|
||||
current_timesheet,
|
||||
timesheet,
|
||||
is_loading,
|
||||
getPayPeriodByDate,
|
||||
getPayPeriodByDateOrYearAndNumber,
|
||||
getTimesheetByEmail,
|
||||
createTimesheetShifts,
|
||||
getPayPeriodByYearAndPeriodNumber,
|
||||
getTimesheetApprovalPayPeriodEmployeeOverviews,
|
||||
getTimesheetsByPayPeriodAndEmail,
|
||||
getPayPeriodEmployeeOverviewListBySupervisorEmail,
|
||||
getPayPeriodOverviewByEmployeeEmail,
|
||||
getPayPeriodEmployeeDetailsByEmployeeEmail,
|
||||
getTimesheetApprovalCSVReport,
|
||||
};
|
||||
});
|
||||
10
src/utils/store-helpers.ts
Normal file
10
src/utils/store-helpers.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import type { Ref } from "vue";
|
||||
|
||||
export const withLoading = async <T>( loading_state: Ref<boolean>, fn: () => Promise<T> ) => {
|
||||
loading_state.value = true;
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
loading_state.value = false;
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user