263 lines
12 KiB
Vue
263 lines
12 KiB
Vue
<script setup lang="ts">
|
|
/* eslint-disable */
|
|
import { computed, onMounted, ref } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
|
import { useTimesheetApprovalApi } from '../composables/use-timesheet-approval-api';
|
|
import TimesheetApprovalPeriodPicker from '../components/timesheet-approval-period-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 { date, type QTableColumn } from 'quasar';
|
|
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('timeSheetValidations.tableColumnLabelFullname'),
|
|
field: 'employee_name',
|
|
sortable: true
|
|
},
|
|
{
|
|
name: 'email',
|
|
label: t('timeSheetValidations.tableColumnLabelEmail'),
|
|
field: 'email',
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'regular_hours',
|
|
label: t('timeSheetValidations.tableColumnLabelRegularHours'),
|
|
field: 'regular_hours',
|
|
sortable: true
|
|
},
|
|
{
|
|
name: 'evening_hours',
|
|
label: t('timeSheetValidations.tableColumnLabelEveningHours'),
|
|
field: 'evening_hours'
|
|
},
|
|
{
|
|
name: 'emergency_hours',
|
|
label: t('timeSheetValidations.tableColumnLabelEmergencyHours'),
|
|
field: 'emergency_hours'
|
|
},
|
|
{
|
|
name: 'overtime_hours',
|
|
label: t('timeSheetValidations.tableColumnLabelOvertime'),
|
|
field: 'overtime_hours'
|
|
},
|
|
{
|
|
name: 'expenses',
|
|
label: t('timeSheetValidations.tableColumnLabelExpenses'),
|
|
field: 'expenses',
|
|
sortable: true
|
|
},
|
|
{
|
|
name: 'mileage',
|
|
label: t('timeSheetValidations.tableColumnLabelMileage'),
|
|
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('timeSheetValidations.reportFilterShifts'),
|
|
t('timeSheetValidations.reportFilterExpenses'),
|
|
t('timeSheetValidations.reportFilterHoliday'),
|
|
t('timeSheetValidations.reportFilterVacation'),
|
|
]
|
|
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.failedToLoad')"
|
|
:no-results-label="$t('shared.failedToSearch')"
|
|
:loading-label="$t('shared.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 -->
|
|
<TimesheetApprovalPeriodPicker
|
|
: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('timeSheetValidations.reportFilterCategoryCompany')}}</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('timeSheetValidations.reportFilterCategoryType')}}</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> |