- Timesheet Approval's download menu has had its UI overhauled and the script has been streamlined to better match backend structure and logic - Details window in timesheet approval has a few bug and oversight fixes. - Refactored UI store to work with camelCase instead of snake_case
230 lines
9.0 KiB
Vue
230 lines
9.0 KiB
Vue
<script
|
|
setup
|
|
lang="ts"
|
|
>
|
|
import DetailsDialogChartHoursWorked from 'src/modules/timesheet-approval/components/details-dialog-chart-hours-worked.vue';
|
|
import DetailsDialogChartShiftTypes from 'src/modules/timesheet-approval/components/details-dialog-chart-shift-types.vue';
|
|
import DetailsDialogChartExpenses from 'src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue';
|
|
import TimesheetWrapper from 'src/modules/timesheets/components/timesheet-wrapper.vue';
|
|
import ExpenseDialogList from 'src/modules/timesheets/components/expense-dialog-list.vue';
|
|
import ExpenseDialogForm from 'src/modules/timesheets/components/expense-dialog-form.vue';
|
|
|
|
import { date } from 'quasar';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { computed, ref } from 'vue';
|
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
|
import { useTimesheetApprovalApi } from '../composables/use-timesheet-approval-api';
|
|
import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api';
|
|
import { useExpensesStore } from 'src/stores/expense-store';
|
|
import { Expense } from 'src/modules/timesheets/models/expense.models';
|
|
|
|
// ========== state ========================================
|
|
|
|
const { t } = useI18n();
|
|
const timesheetStore = useTimesheetStore();
|
|
const expenseStore = useExpensesStore();
|
|
const timesheetApprovalApi = useTimesheetApprovalApi();
|
|
const shiftApi = useShiftApi();
|
|
const isDialogOpen = ref(false);
|
|
const refreshKey = ref(0);
|
|
|
|
// ========== computed ========================================
|
|
|
|
const isApproved = computed(() => timesheetStore.timesheets.every(timesheet => timesheet.is_approved));
|
|
|
|
const approveButtonLabel = computed(() => isApproved.value ?
|
|
t('shared.label.unlock') :
|
|
t('shared.label.lock')
|
|
);
|
|
|
|
const approveButtonIcon = computed(() => isApproved.value ? 'las la-lock' : 'las la-unlock');
|
|
|
|
const hasExpenses = computed(() => timesheetStore.timesheets.some(timesheet =>
|
|
Object.values(timesheet.weekly_expenses).some(hours => hours > 0))
|
|
);
|
|
|
|
// ========== methods ========================================
|
|
|
|
const onClickApproveAll = async () => {
|
|
const employeeEmail = timesheetStore.current_pay_period_overview?.email;
|
|
const isApproved = timesheetStore.timesheets.every(timesheet => timesheet.is_approved);
|
|
|
|
if (employeeEmail !== undefined && isApproved !== undefined) {
|
|
await timesheetApprovalApi.toggleTimesheetsApprovalByEmployeeEmail(
|
|
employeeEmail,
|
|
!isApproved
|
|
);
|
|
}
|
|
}
|
|
|
|
const onClickSaveTimesheets = async () => {
|
|
await shiftApi.saveShiftChanges(timesheetStore.current_pay_period_overview?.email);
|
|
expenseStore.is_showing_create_form = false;
|
|
}
|
|
|
|
const onClickNewExpense = () => {
|
|
expenseStore.mode = 'create';
|
|
if (timesheetStore.pay_period)
|
|
expenseStore.current_expense = new Expense(timesheetStore.pay_period.period_start);
|
|
else
|
|
expenseStore.current_expense = new Expense(date.formatDate(new Date(), 'YYYY-MM-DD'));
|
|
}
|
|
|
|
const onClickSaveNewExpense = () => {
|
|
expenseStore.is_showing_create_form = false;
|
|
}
|
|
|
|
const onShowDetailsDialog = () => {
|
|
isDialogOpen.value = true;
|
|
expenseStore.is_showing_create_form = false;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<q-dialog
|
|
v-model="timesheetStore.is_details_dialog_open"
|
|
full-width
|
|
full-height
|
|
transition-show="jump-down"
|
|
transition-hide="jump-down"
|
|
backdrop-filter="blur(6px)"
|
|
@show="onShowDetailsDialog"
|
|
@hide="isDialogOpen = false"
|
|
@before-hide="timesheetStore.getTimesheetOverviews"
|
|
>
|
|
<div
|
|
class="column bg-secondary hide-scrollbar shadow-12 rounded-15 q-pb-sm no-wrap"
|
|
:style="($q.screen.lt.md ? '' : 'width:80vw !important;') + ($q.dark.isActive ? ' border: 2px solid var(--q-accent)' : '')"
|
|
>
|
|
<!-- employee name -->
|
|
<div class="col-auto row flex-center q-px-none q-py-sm sticky-top bg-secondary full-width shadow-4">
|
|
<span class="col-auto text-h4 text-weight-bolder text-uppercase q-px-lg">
|
|
{{ timesheetStore.selected_employee_name }}
|
|
</span>
|
|
|
|
<div class="col-auto q-px-lg">
|
|
<q-btn
|
|
:push="isApproved"
|
|
:outline="!isApproved"
|
|
dense
|
|
size="lg"
|
|
color="accent"
|
|
:label="approveButtonLabel"
|
|
class="q-px-xl"
|
|
@click="onClickApproveAll"
|
|
>
|
|
<transition
|
|
enter-active-class="animated swing"
|
|
mode="out-in"
|
|
>
|
|
<q-icon
|
|
:key="isApproved ? '1' : '2'"
|
|
:name="approveButtonIcon"
|
|
class="q-pl-md"
|
|
/>
|
|
</transition>
|
|
</q-btn>
|
|
</div>
|
|
|
|
<q-space />
|
|
|
|
<div class="col-auto q-px-md">
|
|
<q-btn
|
|
push
|
|
dense
|
|
size="lg"
|
|
:disable="timesheetStore.is_loading || !timesheetStore.canSaveTimesheets"
|
|
:color="timesheetStore.is_loading || !timesheetStore.canSaveTimesheets ? 'grey-5' : 'accent'"
|
|
icon="upload"
|
|
:label="$t('shared.label.save')"
|
|
:class="$q.platform.is.mobile && ($q.screen.width < $q.screen.height) ? 'full-width' : 'q-ml-md'"
|
|
@click="onClickSaveTimesheets"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- employee pay period details using chart -->
|
|
<div
|
|
v-if="isDialogOpen"
|
|
:key="hasExpenses ? 0 : 1"
|
|
class="col-auto q-px-md no-wrap"
|
|
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
|
>
|
|
<DetailsDialogChartHoursWorked class="col" />
|
|
<DetailsDialogChartShiftTypes class="col" />
|
|
<DetailsDialogChartExpenses
|
|
v-if="hasExpenses"
|
|
class="col"
|
|
/>
|
|
</div>
|
|
|
|
<div class="col-auto column no-wrap">
|
|
<q-separator
|
|
spaced
|
|
size="4px"
|
|
class="q-mx-md"
|
|
/>
|
|
|
|
<ExpenseDialogList
|
|
mode="approval"
|
|
:key="refreshKey + 1"
|
|
/>
|
|
|
|
<q-expansion-item
|
|
v-if="!isApproved"
|
|
v-model="expenseStore.is_showing_create_form"
|
|
hide-expand-icon
|
|
:dense="!$q.platform.is.mobile"
|
|
group="expenses"
|
|
@show="onClickNewExpense()"
|
|
header-class="bg-accent text-white q-mx-md rounded-5"
|
|
>
|
|
<template #header>
|
|
<div class="row items-center">
|
|
<q-icon
|
|
name="add_circle_outline"
|
|
size="md"
|
|
class="col-auto"
|
|
:class="expenseStore.is_showing_create_form ? 'invisible' : ''"
|
|
/>
|
|
|
|
<span class="col-auto text-uppercase text-weight-bold text-h6 q-ml-xs q-mr-sm">
|
|
{{ $t('timesheet.expense.add_expense') }}
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<ExpenseDialogForm
|
|
:email="timesheetStore.current_pay_period_overview?.email"
|
|
:key="refreshKey"
|
|
mode="approval"
|
|
@click-save="onClickSaveNewExpense"
|
|
/>
|
|
</q-expansion-item>
|
|
|
|
<q-separator
|
|
spaced
|
|
size="4px"
|
|
class="q-mx-md"
|
|
/>
|
|
</div>
|
|
|
|
<!-- list of shifts -->
|
|
<div class="col-auto q-mt-md">
|
|
<TimesheetWrapper
|
|
mode="approval"
|
|
:employee-email="timesheetStore.current_pay_period_overview?.email"
|
|
class="col-auto"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</q-dialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.sticky-top {
|
|
position: sticky;
|
|
z-index: 5;
|
|
top: 0;
|
|
}
|
|
</style> |