refactor(approvals): Reconnect Services to backend logic and routes, adjust UI/UX, fix list mode
This commit is contained in:
parent
3b0652fb8b
commit
75ca572040
|
|
@ -206,6 +206,7 @@ export default {
|
||||||
table: {
|
table: {
|
||||||
full_name: "full name",
|
full_name: "full name",
|
||||||
email: "email address",
|
email: "email address",
|
||||||
|
is_approved: "approval",
|
||||||
expenses: "expenses",
|
expenses: "expenses",
|
||||||
mileage: "mileage",
|
mileage: "mileage",
|
||||||
verified: "approved",
|
verified: "approved",
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,7 @@ export default {
|
||||||
table: {
|
table: {
|
||||||
full_name: "nom complet",
|
full_name: "nom complet",
|
||||||
email: "courriel",
|
email: "courriel",
|
||||||
|
is_approved: "approuvé",
|
||||||
expenses: "dépenses",
|
expenses: "dépenses",
|
||||||
mileage: "kilométrage",
|
mileage: "kilométrage",
|
||||||
verified: "approuvé",
|
verified: "approuvé",
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@
|
||||||
<transition appear enter-active-class="animated slow flipInX" leave-active-class="animated flipOutX">
|
<transition appear enter-active-class="animated slow flipInX" leave-active-class="animated flipOutX">
|
||||||
<q-card class="col-3 items-center">
|
<q-card class="col-3 items-center">
|
||||||
<q-card-section class="row justify-center ">
|
<q-card-section class="row justify-center ">
|
||||||
<q-icon name="check_circle" color="green" size="xl" />
|
<q-icon name="check_circle" color="accent" size="xl" />
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-separator inset color="primary" />
|
<q-separator inset color="accent" />
|
||||||
<q-card-section class="row justify-center">
|
<q-card-section class="row justify-center">
|
||||||
<span class="row text-primary text-h3">Login Successful!</span>
|
<span class="row text-h3">Login Successful!</span>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
|
||||||
21
src/modules/shared/components/loading-overlay.vue
Normal file
21
src/modules/shared/components/loading-overlay.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script
|
||||||
|
setup
|
||||||
|
lang="ts"
|
||||||
|
>
|
||||||
|
const is_loading = defineModel < boolean > ({ required: true });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-dialog
|
||||||
|
v-model="is_loading"
|
||||||
|
transition-show="jump-down"
|
||||||
|
transition-hide="jump-down"
|
||||||
|
>
|
||||||
|
<q-card class="q-pa-xl rounded-200 frosted-glass">
|
||||||
|
<q-spinner-radio
|
||||||
|
color="accent"
|
||||||
|
size="20vh"
|
||||||
|
/>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
rounded
|
rounded
|
||||||
icon="calendar_month"
|
icon="calendar_month"
|
||||||
color="accent"
|
color="accent"
|
||||||
@click="is_showing_calendar_picker = true"
|
@click="is_showing_calendar_picker = !is_showing_calendar_picker"
|
||||||
:disable="timesheet_store.is_loading || is_disabled"
|
:disable="timesheet_store.is_loading || is_disabled"
|
||||||
class="q-px-xl"
|
class="q-px-xl"
|
||||||
>
|
>
|
||||||
|
|
@ -102,6 +102,8 @@
|
||||||
|
|
||||||
<!-- date picker calendar -->
|
<!-- date picker calendar -->
|
||||||
<q-menu
|
<q-menu
|
||||||
|
v-model="is_showing_calendar_picker"
|
||||||
|
no-parent-event
|
||||||
anchor="bottom middle"
|
anchor="bottom middle"
|
||||||
self="top middle"
|
self="top middle"
|
||||||
:offset="[0, 10]"
|
:offset="[0, 10]"
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const search_model = defineModel<string | number | null>({ default: null, required: true });
|
const search_model = defineModel<string | number | null>('search', { default: null, required: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- Filters toggle -->
|
|
||||||
<q-btn-dropdown
|
|
||||||
push
|
|
||||||
rounded
|
|
||||||
icon="filter_alt"
|
|
||||||
color="accent"
|
|
||||||
:label="$t('shared.label.filter')"
|
|
||||||
class="q-mr-md"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Search bar -->
|
|
||||||
<q-input
|
<q-input
|
||||||
v-model="search_model"
|
v-model="search_model"
|
||||||
outlined
|
outlined
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,12 @@
|
||||||
import type { TimesheetOverview } from 'src/modules/timesheet-approval/models/timesheet-overview.models';
|
import type { TimesheetOverview } from 'src/modules/timesheet-approval/models/timesheet-overview.models';
|
||||||
|
|
||||||
const modelApproval = defineModel<boolean>();
|
const modelApproval = defineModel<boolean>();
|
||||||
const { row } = defineProps<{ row: TimesheetOverview; }>();
|
|
||||||
|
const { row, index = 0 } = defineProps<{
|
||||||
|
row: TimesheetOverview;
|
||||||
|
index?: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'clickDetails': [overview: TimesheetOverview];
|
'clickDetails': [overview: TimesheetOverview];
|
||||||
}>();
|
}>();
|
||||||
|
|
@ -13,129 +18,148 @@
|
||||||
|
|
||||||
<template>
|
<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">
|
<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">
|
<transition
|
||||||
<!-- Card header with employee name and details button-->
|
appear
|
||||||
<q-card-section
|
enter-active-class="animated fadeInUp"
|
||||||
horizontal
|
>
|
||||||
class="q-py-none q-px-sm q-ma-none justify-between items-center"
|
<q-card
|
||||||
|
class="rounded-10 shadow-15"
|
||||||
|
:style="`animation-delay: ${index / 15}s;`"
|
||||||
>
|
>
|
||||||
<span class="col text-primary text-h5 text-weight-bolder q-pt-xs"> {{ row.employee_name }} </span>
|
<!-- Card header with employee name and details button-->
|
||||||
|
<q-card-section
|
||||||
<!-- Buttons to view detailed shifts or view employee timesheet -->
|
horizontal
|
||||||
<q-btn
|
class="q-py-none q-px-sm q-ma-none justify-between items-center bg-primary text-white"
|
||||||
flat
|
|
||||||
dense
|
|
||||||
square
|
|
||||||
unelevated
|
|
||||||
class="col-auto q-pa-none q-ma-none"
|
|
||||||
color="primary"
|
|
||||||
icon="work_history"
|
|
||||||
@click="emit('clickDetails', row)"
|
|
||||||
>
|
>
|
||||||
<q-tooltip
|
<div>
|
||||||
anchor="top middle"
|
<span class="text-h5 text-uppercase text-weight-medium text-accent q-mr-xs">{{ row.employee_name.split(' ')[0]
|
||||||
self="center middle"
|
}}</span>
|
||||||
class="bg-primary text-uppercase text-weight-bold"
|
<span class="text-uppercase text-weight-light">{{ row.employee_name.split(' ')[1] }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 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="accent"
|
||||||
|
icon="work_history"
|
||||||
|
@click="emit('clickDetails', row)"
|
||||||
>
|
>
|
||||||
{{ $t('timesheet_approvals.tooltip.button_detailed_view') }}
|
<q-tooltip
|
||||||
</q-tooltip>
|
anchor="top middle"
|
||||||
</q-btn>
|
self="center middle"
|
||||||
</q-card-section>
|
class="bg-accent 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-py-sm bg-dark">
|
||||||
|
<div class="row">
|
||||||
|
<!-- left portion of pay period card -->
|
||||||
|
<div class="col column q-px-sm">
|
||||||
|
<!-- Regular hours segment -->
|
||||||
|
<div class="col column">
|
||||||
|
<span
|
||||||
|
class="text-weight-bold text-uppercase q-pa-none q-my-none"
|
||||||
|
:class="row.regular_hours > 80 ? 'text-negative' : 'text-accent'"
|
||||||
|
> {{
|
||||||
|
$t('shared.shift_type.regular') }} </span>
|
||||||
|
<span
|
||||||
|
class="text-weight-bolder text-h3 q-py-none"
|
||||||
|
:class="row.regular_hours > 80 ? 'text-negative' : ''"
|
||||||
|
> {{ row.regular_hours }} </span>
|
||||||
|
<q-separator class="q-mr-sm" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Main body of pay period card -->
|
<!-- Other hour types segment -->
|
||||||
<q-card-section class="q-py-none q-px-sm q-my-sm">
|
<div class="col-auto row ellipsis q-mt-xs">
|
||||||
<div class="row">
|
<div
|
||||||
<!-- left portion of pay period card -->
|
v-for="hour_type, index in row.other_hours"
|
||||||
<div class="col column q-px-sm">
|
:key="index"
|
||||||
<!-- Regular hours segment -->
|
class="col-4 column ellipsis"
|
||||||
<div class="col column">
|
:class="hour_type === 0 ? 'invisible' : ''"
|
||||||
<span class="text-weight-bold text-primary text-uppercase q-pa-none q-my-none"> {{ $t('shared.shift_type.regular') }} </span>
|
>
|
||||||
<span class="text-weight-bolder text-h3 q-py-none"> {{ row.regular_hours }} </span>
|
<span
|
||||||
<q-separator class="q-mx-sm" />
|
class="text-weight-bold text-accent text-uppercase q-pa-none q-my-none"
|
||||||
|
style="font-size: 0.7em;"
|
||||||
|
> {{ $t(`shared.shift_type.${index.replace('_hours', '')}`) }} </span>
|
||||||
|
<span
|
||||||
|
class="text-weight-bolder q-pa-none q-mb-xs"
|
||||||
|
style="font-size: 1.2em; line-height: 1em;"
|
||||||
|
> {{ hour_type }} </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Other hour types segment -->
|
<q-separator
|
||||||
<div class="col-auto row ellipsis q-mt-xs">
|
vertical
|
||||||
<div
|
class="q-mt-xs q-mb-none"
|
||||||
v-for="hour_type, index in row.other_hours"
|
/>
|
||||||
:key="index"
|
|
||||||
class="col-4 column ellipsis"
|
<!-- Right portion of pay period card -->
|
||||||
:class="hour_type === 0 ? 'invisible' : ''"
|
<div class="col-auto column q-px-sm">
|
||||||
>
|
<div class="col column no-wrap">
|
||||||
<span
|
<span
|
||||||
class="text-weight-bold text-primary text-uppercase q-pa-none q-my-none"
|
class="text-weight-bold text-accent text-uppercase q-pa-none q-my-none"
|
||||||
style="font-size: 0.7em;"
|
style="font-size: 0.8em;"
|
||||||
> {{ $t(`shared.shift_type.${index.replace('_hours', '')}`) }} </span>
|
> {{ $t('timesheet.expense.types.EXPENSES') }} </span>
|
||||||
<span
|
<span
|
||||||
class="text-weight-bolder q-pa-none q-mb-xs"
|
class="text-weight-bolder text-h6 q-pa-none"
|
||||||
style="font-size: 1.2em; line-height: 1em;"
|
style="line-height: 0.9em;"
|
||||||
> {{ hour_type }} </span>
|
> {{ row.expenses }} <span class="text-weight-light">$</span> </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col column no-wrap">
|
||||||
|
<span
|
||||||
|
class="text-weight-bold text-accent text-uppercase q-pa-none q-my-none"
|
||||||
|
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 class="text-weight-light text-body2">km</span></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
<q-separator
|
<!-- Validate Pay Period section -->
|
||||||
vertical
|
<q-card-section
|
||||||
class="q-mt-xs q-mb-none"
|
horizontal
|
||||||
/>
|
class="justify-between items-center text-weight-bold q-pa-none"
|
||||||
|
:class="row.is_approved ? 'text-white bg-accent' : 'bg-dark text-accent'"
|
||||||
<!-- Right portion of pay period card -->
|
>
|
||||||
<div class="col-auto column q-px-sm">
|
<div class="col-auto">
|
||||||
<div class="col column no-wrap">
|
<span class="text-uppercase text-h6 q-ml-sm text-weight-bolder"> {{ row.total_hours }} </span>
|
||||||
<span
|
<span class="text-uppercase text-weight-bold text-caption q-ml-xs"> total </span>
|
||||||
class="text-weight-bold text-primary text-uppercase q-pa-none q-my-none"
|
|
||||||
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="text-weight-bold text-primary text-uppercase q-pa-none q-my-none"
|
|
||||||
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>
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-separator
|
<div
|
||||||
color="primary"
|
class="col-auto q-py-xs q-px-md"
|
||||||
size="2px"
|
style="border: 1px solid var(--q-accent);"
|
||||||
/>
|
>
|
||||||
|
<q-checkbox
|
||||||
<!-- Validate Pay Period section -->
|
v-model="modelApproval"
|
||||||
<q-card-section
|
dense
|
||||||
horizontal
|
left-label
|
||||||
class="justify-between items-center text-weight-bold q-px-sm"
|
keep-color
|
||||||
:class="row.is_approved ? 'text-white bg-primary' : 'bg-dark'"
|
size="lg"
|
||||||
>
|
checked-icon="lock"
|
||||||
<div class="col-auto">
|
unchecked-icon="lock_open"
|
||||||
<span class="text-uppercase text-h6 q-ml-sm text-weight-bolder"> {{ row.total_hours }} </span>
|
:color="row.is_approved ? 'white' : 'accent'"
|
||||||
<span class="text-uppercase text-weight-bold text-caption q-ml-xs"> total </span>
|
:label="row.is_approved ? $t('timesheet_approvals.table.verified') : $t('timesheet_approvals.table.unverified')"
|
||||||
</div>
|
class="text-uppercase"
|
||||||
|
:class="row.is_approved ? '' : 'text-accent'"
|
||||||
<q-checkbox
|
/>
|
||||||
v-model="modelApproval"
|
</div>
|
||||||
dense
|
</q-card-section>
|
||||||
left-label
|
</q-card>
|
||||||
size="lg"
|
</transition>
|
||||||
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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -4,26 +4,20 @@
|
||||||
>
|
>
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
import OverviewListItem from 'src/modules/timesheet-approval/components/overview-list-item.vue';
|
||||||
|
import LoadingOverlay from 'src/modules/shared/components/loading-overlay.vue';
|
||||||
import { useExpensesStore } from 'src/stores/expense-store';
|
import { useExpensesStore } from 'src/stores/expense-store';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { useTimesheetApprovalApi } from 'src/modules/timesheet-approval/composables/use-timesheet-approval-api';
|
import { useTimesheetApprovalApi } from 'src/modules/timesheet-approval/composables/use-timesheet-approval-api';
|
||||||
import OverviewListItem from 'src/modules/timesheet-approval/components/overview-list-item.vue';
|
|
||||||
import QTableFilters from 'src/modules/shared/components/q-table-filters.vue';
|
|
||||||
import PayPeriodNavigator from 'src/modules/shared/components/pay-period-navigator.vue';
|
|
||||||
import { pay_period_overview_columns, type TimesheetOverview } from 'src/modules/timesheet-approval/models/timesheet-overview.models';
|
import { pay_period_overview_columns, type TimesheetOverview } from 'src/modules/timesheet-approval/models/timesheet-overview.models';
|
||||||
|
|
||||||
const expenses_store = useExpensesStore();
|
const expenses_store = useExpensesStore();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
const timesheet_approval_api = useTimesheetApprovalApi();
|
const timesheet_approval_api = useTimesheetApprovalApi();
|
||||||
|
|
||||||
const filter = ref<string | number | null>('');
|
|
||||||
const is_grid_mode = ref(true);
|
|
||||||
const IS_ABNORMAL_SHIFT = ['OVERTIME', 'EMERGENCY'];
|
|
||||||
const IS_PTO = ['HOLIDAY', 'VACATION', 'SICK'];
|
|
||||||
|
|
||||||
const employeeEmail = defineModel();
|
const employeeEmail = defineModel();
|
||||||
|
|
||||||
const visible_columns = ref<string[]>(['REGULAR', 'email']);
|
const visible_columns = ref<string[]>(['employee_name', 'REGULAR', 'email', 'is_approved']);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'clickedDetailsButton': [email: string];
|
'clickedDetailsButton': [email: string];
|
||||||
|
|
@ -46,114 +40,97 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="q-pa-md">
|
<div class="q-pa-md">
|
||||||
<q-table
|
<LoadingOverlay v-model="timesheet_store.is_loading" />
|
||||||
:visible-columns="visible_columns"
|
<transition
|
||||||
:rows="overview_rows"
|
appear
|
||||||
:columns="pay_period_overview_columns"
|
enter-active-class="animated fadeInUp"
|
||||||
row-key="email"
|
leave-active-class="animated fadeOutDown"
|
||||||
:filter="filter"
|
mode="out-in"
|
||||||
:grid="is_grid_mode"
|
|
||||||
dense
|
|
||||||
hide-pagination
|
|
||||||
color="primary"
|
|
||||||
:rows-per-page-options="[0]"
|
|
||||||
card-container-class="justify-center"
|
|
||||||
:loading="timesheet_store.is_loading"
|
|
||||||
class="q-py-md bg-transparent"
|
|
||||||
:class="is_grid_mode ? '' : 'sticky-header-table no-shadow'"
|
|
||||||
table-class="q-pa-none q-py-none q-mx-md rounded-10 bg-dark shadow-4'"
|
|
||||||
: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>
|
<q-table
|
||||||
<div
|
:key="timesheet_store.is_approval_grid_mode ? 'grid' : 'list'"
|
||||||
class="full-width"
|
:visible-columns="visible_columns"
|
||||||
:class="$q.screen.lt.md ? 'text-center' : 'row'"
|
:rows="overview_rows"
|
||||||
>
|
:columns="pay_period_overview_columns"
|
||||||
<PayPeriodNavigator
|
row-key="email"
|
||||||
@date-selected="timesheet_approval_api.getPayPeriodOverviewsByDateOrYearAndNumber"
|
:filter="timesheet_store.search_filter"
|
||||||
/>
|
:grid="timesheet_store.is_approval_grid_mode"
|
||||||
|
dense
|
||||||
<q-space />
|
hide-pagination
|
||||||
|
color="accent"
|
||||||
<q-btn-toggle
|
:rows-per-page-options="[0]"
|
||||||
v-model="is_grid_mode"
|
card-container-class="justify-center"
|
||||||
push
|
class="q-py-md bg-transparent"
|
||||||
color="white"
|
:class="timesheet_store.is_approval_grid_mode ? '' : 'sticky-header-table no-shadow'"
|
||||||
text-color="accent"
|
table-class="q-pa-none q-py-none q-mx-md rounded-10 bg-dark shadow-15"
|
||||||
toggle-color="accent"
|
:no-data-label="$t('shared.error.no_data_found')"
|
||||||
class="q-mr-md"
|
:no-results-label="$t('shared.error.no_search_results')"
|
||||||
:options="[
|
:loading-label="$t('shared.label.loading')"
|
||||||
{ icon: 'grid_view', value: true },
|
>
|
||||||
{ icon: 'view_list', value: false },
|
<template #header="props">
|
||||||
]"
|
<q-tr
|
||||||
/>
|
|
||||||
|
|
||||||
<QTableFilters v-model="filter" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #header="props">
|
|
||||||
<q-tr
|
|
||||||
:props="props"
|
|
||||||
class="bg-primary"
|
|
||||||
>
|
|
||||||
<q-th
|
|
||||||
v-for="col in props.cols"
|
|
||||||
:key="col.name"
|
|
||||||
:props="props"
|
:props="props"
|
||||||
|
class="bg-primary"
|
||||||
>
|
>
|
||||||
<span
|
<q-th
|
||||||
v-if="col.label !== 'timesheet_approvals.table.is_approved'"
|
v-for="col in props.cols"
|
||||||
class="text-uppercase text-weight-bolder text-white"
|
:key="col.name"
|
||||||
|
:props="props"
|
||||||
>
|
>
|
||||||
{{ $t(col.label) }}
|
<span class="text-uppercase text-weight-bolder text-white">
|
||||||
|
{{ $t(col.label) }}
|
||||||
|
</span>
|
||||||
|
</q-th>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell="props">
|
||||||
|
<q-td
|
||||||
|
:props="props"
|
||||||
|
class="text-weight-medium"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
v-if="props.col.name === 'is_approved'"
|
||||||
|
:name="props.value ? 'verified' : 'remove_circle_outline'"
|
||||||
|
:color="props.value ? 'accent' : 'grey-5'"
|
||||||
|
:class="props.value ? 'bg-white rounded-20' : ''"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-else-if="props.col.name === 'employee_name'">
|
||||||
|
<span class="text-h5 text-uppercase text-accent q-mr-xs">{{ props.value.split(' ')[0]}}</span>
|
||||||
|
<span class="text-uppercase text-weight-light">{{ props.value.split(' ')[1] }}</span>
|
||||||
|
</div>
|
||||||
|
<span v-else>{{ props.value }}</span>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Template for individual employee cards -->
|
||||||
|
<template #item="props: { row: TimesheetOverview, rowIndex: number }">
|
||||||
|
<OverviewListItem
|
||||||
|
v-model="props.row.is_approved"
|
||||||
|
:key="props.row.email + timesheet_store.pay_period?.pay_period_no"
|
||||||
|
:index="props.rowIndex"
|
||||||
|
:row="props.row"
|
||||||
|
@click-details="overview => onClickedDetails(props.row.email, overview)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Template for custome failed-to-load state -->
|
||||||
|
<template #no-data="{ message, filter }">
|
||||||
|
<div class="full-width column items-center text-accent q-gutter-sm">
|
||||||
|
<q-icon
|
||||||
|
size="4em"
|
||||||
|
:name="filter ? 'filter_alt_off' : 'error_outline'"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="text-h6">
|
||||||
|
{{ message }}
|
||||||
</span>
|
</span>
|
||||||
</q-th>
|
</div>
|
||||||
</q-tr>
|
</template>
|
||||||
</template>
|
</q-table>
|
||||||
|
</transition>
|
||||||
<template #body-cell="props">
|
|
||||||
<q-td
|
|
||||||
:props="props"
|
|
||||||
class="text-weight-medium"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
v-if="(props.value > 0 && typeof props.value !== 'boolean') || typeof props.value === 'string'"
|
|
||||||
>{{ props.value }}</span>
|
|
||||||
<q-icon
|
|
||||||
v-if="typeof props.value === 'boolean'"
|
|
||||||
:name="props.value ? 'verified' : 'fiber_manual_record'"
|
|
||||||
:color="props.value ? 'primary' : 'grey-5'"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Template for individual employee cards -->
|
|
||||||
<template #item="props: { row: TimesheetOverview, key: string }">
|
|
||||||
<OverviewListItem
|
|
||||||
v-model="props.row.is_approved"
|
|
||||||
:row="props.row"
|
|
||||||
@click-details="overview => onClickedDetails(props.row.email, overview)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Template for custome failed-to-load state -->
|
|
||||||
<template #no-data="{ message, filter }">
|
|
||||||
<div class="full-width column items-center text-accent q-gutter-sm">
|
|
||||||
<q-icon
|
|
||||||
size="4em"
|
|
||||||
:name="filter ? 'filter_alt_off' : 'error_outline'"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span class="text-h6">
|
|
||||||
{{ message }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -167,7 +144,7 @@
|
||||||
position: sticky
|
position: sticky
|
||||||
z-index: 1
|
z-index: 1
|
||||||
thead tr:first-child th
|
thead tr:first-child th
|
||||||
top: 0
|
top: 0px
|
||||||
|
|
||||||
&.q-table--loading thead tr:last-child th
|
&.q-table--loading thead tr:last-child th
|
||||||
top: 48px
|
top: 48px
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,33 @@
|
||||||
|
import { ref } from "vue";
|
||||||
import { useTimesheetStore } from "src/stores/timesheet-store";
|
import { useTimesheetStore } from "src/stores/timesheet-store";
|
||||||
import { useAuthStore } from "src/stores/auth-store";
|
|
||||||
import type { TimesheetApprovalCSVReportFilters } from "src/modules/timesheet-approval/models/timesheet-approval-csv-report.models";
|
import type { TimesheetApprovalCSVReportFilters } from "src/modules/timesheet-approval/models/timesheet-approval-csv-report.models";
|
||||||
import { NavigatorConstants } from "src/modules/timesheet-approval/models/timesheet-overview.models";
|
|
||||||
|
|
||||||
export const useTimesheetApprovalApi = () => {
|
export const useTimesheetApprovalApi = () => {
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
const auth_store = useAuthStore();
|
const DATE_REGEX = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
|
||||||
|
|
||||||
const getPayPeriodOverviewsByDateOrYearAndNumber = async (date_or_year: string | number, period_number?: number): Promise<void> => {
|
const getTimesheetOverviews = async (date?: string) => {
|
||||||
let success = false;
|
timesheet_store.is_loading = true;
|
||||||
if (typeof date_or_year === 'string') success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date_or_year);
|
const success = ref(false);
|
||||||
else if (typeof date_or_year === 'number' && period_number) success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date_or_year, period_number);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
await timesheet_store.getTimesheetOverviewsByPayPeriod(
|
|
||||||
timesheet_store.pay_period?.pay_year ?? 1,
|
|
||||||
timesheet_store.pay_period?.pay_period_no ?? 1,
|
|
||||||
auth_store.user?.email
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNextOrPreviousPayPeriodOverview = async (direction: number) => {
|
|
||||||
if (timesheet_store.pay_period === undefined) return;
|
|
||||||
|
|
||||||
let new_period_number = (timesheet_store.pay_period.pay_period_no) + direction;
|
if (date !== undefined) {
|
||||||
let new_year = timesheet_store.pay_period.pay_year;
|
success.value = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date);
|
||||||
|
} else {
|
||||||
if ( new_period_number > 26 || new_period_number < 1) {
|
success.value = await timesheet_store.getPayPeriodByDateOrYearAndNumber();
|
||||||
new_period_number = 1;
|
|
||||||
new_year += direction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await getPayPeriodOverviewsByDateOrYearAndNumber(new_year, new_period_number);
|
if (success.value === true) {
|
||||||
};
|
await timesheet_store.getTimesheetOverviews();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getNextPayPeriodOverview = async () => {
|
const getTimesheetOverviewsByDate = async (date: string) => {
|
||||||
await getNextOrPreviousPayPeriodOverview(NavigatorConstants.NEXT_PERIOD);
|
const valid_date = DATE_REGEX.test(date);
|
||||||
};
|
|
||||||
|
|
||||||
const getPreviousPayPeriodOverview = async () => {
|
if (valid_date) {
|
||||||
await getNextOrPreviousPayPeriodOverview(NavigatorConstants.PREVIOUS_PERIOD);
|
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date);
|
||||||
|
if (success) await timesheet_store.getTimesheetOverviews();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTimesheetApprovalCSVReport = async ( report_filter_company: boolean[], report_filter_type: boolean[], year?: number, period_number?: number ) => {
|
const getTimesheetApprovalCSVReport = async ( report_filter_company: boolean[], report_filter_type: boolean[], year?: number, period_number?: number ) => {
|
||||||
|
|
@ -61,9 +48,8 @@ export const useTimesheetApprovalApi = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getPayPeriodOverviewsByDateOrYearAndNumber,
|
getTimesheetOverviewsByDate,
|
||||||
getTimesheetApprovalCSVReport,
|
getTimesheetApprovalCSVReport,
|
||||||
getNextPayPeriodOverview,
|
getTimesheetOverviews,
|
||||||
getPreviousPayPeriodOverview,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -3,8 +3,8 @@ import type { TimesheetApprovalCSVReportFilters } from "src/modules/timesheet-ap
|
||||||
import type { PayPeriodOverviewResponse } from "src/modules/timesheet-approval/models/timesheet-overview.models";
|
import type { PayPeriodOverviewResponse } from "src/modules/timesheet-approval/models/timesheet-overview.models";
|
||||||
|
|
||||||
export const timesheetApprovalService = {
|
export const timesheetApprovalService = {
|
||||||
getPayPeriodOverviewsBySupervisorEmail: async (year: number, period_number: number, supervisor_email: string): Promise<PayPeriodOverviewResponse> => {
|
getPayPeriodOverviews: async (year: number, period_number: number): Promise<PayPeriodOverviewResponse> => {
|
||||||
const response = await api.get(`pay-periods/${year}/${period_number}/${supervisor_email}`);
|
const response = await api.get(`pay-periods/crew/${year}/${period_number}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="ui_store.is_mobile_mode && !dense"
|
v-if="ui_store.is_mobile_mode && !dense"
|
||||||
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
||||||
:text-color="shift.comment ? ((shift.is_approved || isTimesheetApproved) ? 'white' : 'accent') : 'grey-5'"
|
:text-color="shift.comment ? ((shift.is_approved && isTimesheetApproved) ? 'white' : 'accent') : 'grey-5'"
|
||||||
class="col-auto full-height q-mx-xs rounded-5 shadow-1"
|
class="col-auto full-height q-mx-xs rounded-5 shadow-1"
|
||||||
>
|
>
|
||||||
<q-popup-edit
|
<q-popup-edit
|
||||||
|
|
@ -160,8 +160,8 @@
|
||||||
v-model="shift_type_selected"
|
v-model="shift_type_selected"
|
||||||
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
||||||
dense
|
dense
|
||||||
:borderless="(shift.is_approved || isTimesheetApproved)"
|
:borderless="(shift.is_approved && isTimesheetApproved)"
|
||||||
:readonly="(shift.is_approved || isTimesheetApproved)"
|
:readonly="(shift.is_approved && isTimesheetApproved)"
|
||||||
:options-dense="!ui_store.is_mobile_mode"
|
:options-dense="!ui_store.is_mobile_mode"
|
||||||
hide-dropdown-icon
|
hide-dropdown-icon
|
||||||
:menu-offset="[0, 10]"
|
:menu-offset="[0, 10]"
|
||||||
|
|
@ -169,8 +169,8 @@
|
||||||
menu-self="top middle"
|
menu-self="top middle"
|
||||||
:options="SHIFT_OPTIONS"
|
:options="SHIFT_OPTIONS"
|
||||||
class="col rounded-5 q-mx-xs bg-dark"
|
class="col rounded-5 q-mx-xs bg-dark"
|
||||||
:class="(shift.is_approved || isTimesheetApproved) ? 'inset-shadow' : ''"
|
:class="!shift.is_approved && !isTimesheetApproved ? '' : 'inset-shadow'"
|
||||||
:style="(shift.is_approved || isTimesheetApproved) ? 'background-color: #0a7d32 !important;' : ''"
|
:style="shift.is_approved ? 'background-color: #0a7d32 !important;' : ''"
|
||||||
popup-content-class="text-uppercase text-weight-bold text-center rounded-5"
|
popup-content-class="text-uppercase text-weight-bold text-center rounded-5"
|
||||||
popup-content-style="border: 2px solid var(--q-accent)"
|
popup-content-style="border: 2px solid var(--q-accent)"
|
||||||
@blur="onBlurShiftTypeSelect"
|
@blur="onBlurShiftTypeSelect"
|
||||||
|
|
@ -191,33 +191,34 @@
|
||||||
<span
|
<span
|
||||||
style="line-height: 0.9em;"
|
style="line-height: 0.9em;"
|
||||||
class="col-auto ellipsis"
|
class="col-auto ellipsis"
|
||||||
:class="(shift.is_approved || isTimesheetApproved) ? 'text-white' : ''"
|
:class="!shift.is_approved ? '' : 'text-white'"
|
||||||
>{{ scope.opt.label }}</span>
|
>{{ scope.opt.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</q-select>
|
</q-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col row flex-center text-uppercase rounded-5 bg-transparent q-pa-xs">
|
<div class="col row flex-center text-uppercase rounded-5 q-pa-xs">
|
||||||
<!-- punch in field -->
|
<!-- punch in field -->
|
||||||
<q-input
|
<q-input
|
||||||
v-model="shift.start_time"
|
v-model="shift.start_time"
|
||||||
dense
|
dense
|
||||||
:borderless="(shift.is_approved || isTimesheetApproved)"
|
:borderless="(shift.is_approved && isTimesheetApproved)"
|
||||||
:readonly="(shift.is_approved || isTimesheetApproved)"
|
:readonly="(shift.is_approved && isTimesheetApproved)"
|
||||||
type="time"
|
type="time"
|
||||||
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
||||||
label-slot
|
label-slot
|
||||||
:label-color="(shift.is_approved || isTimesheetApproved) ? 'white' : 'accent'"
|
:label-color="!shift.is_approved ? 'accent' : 'white'"
|
||||||
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + ((shift.is_approved || isTimesheetApproved) ? 'cursor-not-allowed text-white' : '')"
|
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'text-white cursor-not-allowed q-px-sm' : '')"
|
||||||
input-style="font-size: 1.2em;"
|
input-style="font-size: 1.2em;"
|
||||||
class="col rounded-5 bg-dark"
|
class="col rounded-5 bg-dark"
|
||||||
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (ui_store.is_mobile_mode ? 'q-mr-xs ' : 'q-mx-xs ') + ((shift.is_approved || isTimesheetApproved) ? 'cursor-not-allowed q-px-xs transparent inset-shadow' : '')"
|
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (ui_store.is_mobile_mode ? 'q-mr-xs ' : 'q-mx-xs ') + (!shift.is_approved && !isTimesheetApproved ? '' : 'cursor-not-allowed inset-shadow')"
|
||||||
:style="(shift.is_approved || isTimesheetApproved) ? 'background-color: #0a7d32 !important;' : ''"
|
:style="shift.is_approved ? 'background-color: #0a7d32 !important;' : ''"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span
|
<span
|
||||||
class="text-weight-bolder"
|
class="text-weight-bolder"
|
||||||
|
:class="shift.is_approved ? ' q-ml-md' : ''"
|
||||||
style="font-size: 0.95em;"
|
style="font-size: 0.95em;"
|
||||||
>{{ $t('shared.misc.in') }}</span>
|
>{{ $t('shared.misc.in') }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -228,20 +229,21 @@
|
||||||
v-model="shift.end_time"
|
v-model="shift.end_time"
|
||||||
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
||||||
dense
|
dense
|
||||||
:borderless="(shift.is_approved || isTimesheetApproved)"
|
:borderless="(shift.is_approved && isTimesheetApproved)"
|
||||||
:readonly="(shift.is_approved || isTimesheetApproved)"
|
:readonly="(shift.is_approved && isTimesheetApproved)"
|
||||||
type="time"
|
type="time"
|
||||||
label-slot
|
label-slot
|
||||||
:label-color="(shift.is_approved || isTimesheetApproved) ? 'white' : 'accent'"
|
:label-color="!shift.is_approved ? 'accent' : 'white'"
|
||||||
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + ((shift.is_approved || isTimesheetApproved) ? 'cursor-not-allowed text-white' : '')"
|
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'text-white cursor-not-allowed q-px-sm' : '')"
|
||||||
input-style="font-size: 1.2em;"
|
input-style="font-size: 1.2em;"
|
||||||
class="col rounded-5 bg-dark"
|
class="col rounded-5 bg-dark"
|
||||||
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (ui_store.is_mobile_mode ? 'q-ml-xs ' : 'q-mx-xs ') + ((shift.is_approved || isTimesheetApproved) ? 'cursor-not-allowed q-px-xs transparent inset-shadow' : '')"
|
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (ui_store.is_mobile_mode ? 'q-mr-xs ' : 'q-mx-xs ') + (shift.is_approved ? 'cursor-not-allowed q-px-xs transparent inset-shadow' : (isTimesheetApproved ? 'inset-shadow' : ''))"
|
||||||
:style="shift.is_approved ? 'background-color: #0a7d32 !important;' : ''"
|
:style="shift.is_approved ? 'background-color: #0a7d32 !important;' : ''"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span
|
<span
|
||||||
class="text-weight-bolder"
|
class="text-weight-bolder"
|
||||||
|
:class="shift.is_approved ? ' q-ml-md' : ''"
|
||||||
style="font-size: 0.95em;"
|
style="font-size: 0.95em;"
|
||||||
>{{ $t('shared.misc.out') }}</span>
|
>{{ $t('shared.misc.out') }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -325,7 +327,14 @@
|
||||||
class="col"
|
class="col"
|
||||||
:class="shift.is_approved ? 'invisible' : ''"
|
:class="shift.is_approved ? 'invisible' : ''"
|
||||||
@click="$emit('requestDelete')"
|
@click="$emit('requestDelete')"
|
||||||
/>
|
>
|
||||||
|
<q-badge
|
||||||
|
v-if="!shift.is_approved"
|
||||||
|
color="white"
|
||||||
|
class="absolute"
|
||||||
|
style="z-index: -1;"
|
||||||
|
/>
|
||||||
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
import ExpenseDialog from 'src/modules/timesheets/components/expense-dialog.vue';
|
import ExpenseDialog from 'src/modules/timesheets/components/expense-dialog.vue';
|
||||||
import PayPeriodNavigator from 'src/modules/shared/components/pay-period-navigator.vue';
|
import PayPeriodNavigator from 'src/modules/shared/components/pay-period-navigator.vue';
|
||||||
import TimesheetErrorWidget from 'src/modules/timesheets/components/timesheet-error-widget.vue';
|
import TimesheetErrorWidget from 'src/modules/timesheets/components/timesheet-error-widget.vue';
|
||||||
|
import LoadingOverlay from 'src/modules/shared/components/loading-overlay.vue';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
||||||
import { useExpensesStore } from 'src/stores/expense-store';
|
import { useExpensesStore } from 'src/stores/expense-store';
|
||||||
|
|
@ -29,18 +30,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column flex-center full-width">
|
<div class="column flex-center full-width">
|
||||||
|
|
||||||
<q-dialog
|
<LoadingOverlay v-model="timesheet_store.is_loading"/>
|
||||||
v-model="timesheet_store.is_loading"
|
|
||||||
transition-show="jump-down"
|
|
||||||
transition-hide="jump-down"
|
|
||||||
>
|
|
||||||
<q-card class="q-pa-xl rounded-200 bg-white frosted-glass">
|
|
||||||
<q-spinner-radio
|
|
||||||
color="accent"
|
|
||||||
size="20vh"
|
|
||||||
/>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-card
|
<q-card
|
||||||
flat
|
flat
|
||||||
|
|
@ -55,7 +45,7 @@
|
||||||
<!-- navigation btn -->
|
<!-- navigation btn -->
|
||||||
<PayPeriodNavigator
|
<PayPeriodNavigator
|
||||||
v-if="!dense"
|
v-if="!dense"
|
||||||
@date-selected="timesheet_api.getTimesheetsByDate(employeeEmail)"
|
@date-selected="date_value => timesheet_api.getTimesheetsByDate(date_value, employeeEmail)"
|
||||||
@pressed-previous-button="timesheet_api.getTimesheetsByCurrentPayPeriod(employeeEmail)"
|
@pressed-previous-button="timesheet_api.getTimesheetsByCurrentPayPeriod(employeeEmail)"
|
||||||
@pressed-next-button="timesheet_api.getTimesheetsByCurrentPayPeriod(employeeEmail)"
|
@pressed-next-button="timesheet_api.getTimesheetsByCurrentPayPeriod(employeeEmail)"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export const useTimesheetApi = () => {
|
||||||
if (timesheet_store.pay_period === undefined) return false;
|
if (timesheet_store.pay_period === undefined) return false;
|
||||||
|
|
||||||
timesheet_store.is_loading = true;
|
timesheet_store.is_loading = true;
|
||||||
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(timesheet_store.pay_period.pay_year, timesheet_store.pay_period.pay_period_no );
|
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber();
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
await timesheet_store.getTimesheetsByEmployeeEmail(employee_email ?? auth_store.user?.email ?? '');
|
await timesheet_store.getTimesheetsByEmployeeEmail(employee_email ?? auth_store.user?.email ?? '');
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script
|
||||||
|
setup
|
||||||
|
lang="ts"
|
||||||
|
>
|
||||||
|
/* eslint-disable */
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { date } from 'quasar';
|
import { date } from 'quasar';
|
||||||
import { useTimesheetApprovalApi } from 'src/modules/timesheet-approval/composables/use-timesheet-approval-api';
|
import { useTimesheetApprovalApi } from 'src/modules/timesheet-approval/composables/use-timesheet-approval-api';
|
||||||
|
|
@ -6,6 +10,8 @@
|
||||||
import PageHeaderTemplate from 'src/modules/shared/components/page-header-template.vue';
|
import PageHeaderTemplate from 'src/modules/shared/components/page-header-template.vue';
|
||||||
import OverviewList from 'src/modules/timesheet-approval/components/overview-list.vue';
|
import OverviewList from 'src/modules/timesheet-approval/components/overview-list.vue';
|
||||||
import DetailsDialog from 'src/modules/timesheet-approval/components/details-dialog.vue';
|
import DetailsDialog from 'src/modules/timesheet-approval/components/details-dialog.vue';
|
||||||
|
import QTableFilters from 'src/modules/shared/components/q-table-filters.vue';
|
||||||
|
import PayPeriodNavigator from 'src/modules/shared/components/pay-period-navigator.vue';
|
||||||
|
|
||||||
const timesheet_approval_api = useTimesheetApprovalApi();
|
const timesheet_approval_api = useTimesheetApprovalApi();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
@ -17,22 +23,22 @@
|
||||||
is_details_dialog_open.value = true;
|
is_details_dialog_open.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted( async () => {
|
onMounted(async () => {
|
||||||
await timesheet_approval_api.getPayPeriodOverviewsByDateOrYearAndNumber(date.formatDate( new Date(), 'YYYY-MM-DD'));
|
await timesheet_approval_api.getTimesheetOverviewsByDate(date.formatDate(new Date(), 'YYYY-MM-DD'));
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-page
|
<q-page
|
||||||
padding
|
padding
|
||||||
class="q-pa-md bg-secondary "
|
class="q-pa-md bg-secondary "
|
||||||
>
|
>
|
||||||
<PageHeaderTemplate
|
<PageHeaderTemplate
|
||||||
title="timesheet_approvals.page_title"
|
title="timesheet_approvals.page_title"
|
||||||
:start-date="timesheet_store.pay_period?.period_start ?? ''"
|
:start-date="timesheet_store.pay_period?.period_start ?? ''"
|
||||||
:end-date="timesheet_store.pay_period?.period_end ?? ''"
|
:end-date="timesheet_store.pay_period?.period_end ?? ''"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DetailsDialog
|
<DetailsDialog
|
||||||
v-model:dialog="is_details_dialog_open"
|
v-model:dialog="is_details_dialog_open"
|
||||||
:employee-email="employee_email"
|
:employee-email="employee_email"
|
||||||
|
|
@ -41,6 +47,44 @@
|
||||||
:timesheets="timesheet_store.timesheets"
|
:timesheets="timesheet_store.timesheets"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<OverviewList @clickedDetailsButton="onDetailsClicked"/>
|
<div
|
||||||
|
class="full-width q-mb-sm"
|
||||||
|
:class="$q.screen.lt.md ? 'text-center' : 'row'"
|
||||||
|
>
|
||||||
|
<PayPeriodNavigator
|
||||||
|
@date-selected="timesheet_approval_api.getTimesheetOverviews"
|
||||||
|
@pressed-next-button="timesheet_approval_api.getTimesheetOverviews"
|
||||||
|
@pressed-previous-button="timesheet_approval_api.getTimesheetOverviews"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-space />
|
||||||
|
|
||||||
|
<q-btn-toggle
|
||||||
|
v-model="timesheet_store.is_approval_grid_mode"
|
||||||
|
push
|
||||||
|
rounded
|
||||||
|
color="white"
|
||||||
|
text-color="accent"
|
||||||
|
toggle-color="accent"
|
||||||
|
class="q-mr-md"
|
||||||
|
:options="[
|
||||||
|
{ icon: 'grid_view', value: true },
|
||||||
|
{ icon: 'view_list', value: false },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-btn-dropdown
|
||||||
|
push
|
||||||
|
rounded
|
||||||
|
icon="filter_alt"
|
||||||
|
color="accent"
|
||||||
|
:label="$t('shared.label.filter')"
|
||||||
|
class="q-mr-md"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<QTableFilters v-model:search="timesheet_store.search_filter" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<OverviewList @clickedDetailsButton="onDetailsClicked" />
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { useAuthStore } from 'src/stores/auth-store';
|
|
||||||
import { unwrapAndClone } from 'src/utils/unwrap-and-clone';
|
import { unwrapAndClone } from 'src/utils/unwrap-and-clone';
|
||||||
import { timesheetApprovalService } from 'src/modules/timesheet-approval/services/timesheet-approval-service';
|
import { timesheetApprovalService } from 'src/modules/timesheet-approval/services/timesheet-approval-service';
|
||||||
import { timesheetService } from 'src/modules/timesheets/services/timesheet-service';
|
import { timesheetService } from 'src/modules/timesheets/services/timesheet-service';
|
||||||
|
|
@ -11,24 +10,23 @@ import type { TimesheetApprovalCSVReportFilters } from 'src/modules/timesheet-ap
|
||||||
|
|
||||||
|
|
||||||
export const useTimesheetStore = defineStore('timesheet', () => {
|
export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
const auth_store = useAuthStore();
|
|
||||||
const is_loading = ref<boolean>(false);
|
const is_loading = ref<boolean>(false);
|
||||||
const pay_period = ref<PayPeriod>();
|
const pay_period = ref<PayPeriod>();
|
||||||
const pay_period_overviews = ref<TimesheetOverview[]>([]);
|
const pay_period_overviews = ref<TimesheetOverview[]>([]);
|
||||||
const current_pay_period_overview = ref<TimesheetOverview>();
|
const current_pay_period_overview = ref<TimesheetOverview>();
|
||||||
const timesheets = ref<Timesheet[]>([]);
|
const timesheets = ref<Timesheet[]>([]);
|
||||||
const initial_timesheets = ref<Timesheet[]>([]);
|
const initial_timesheets = ref<Timesheet[]>([]);
|
||||||
|
const is_approval_grid_mode = ref<boolean>(true);
|
||||||
|
const search_filter = ref<string | number | null>('');
|
||||||
const pay_period_report = ref();
|
const pay_period_report = ref();
|
||||||
|
|
||||||
const getPayPeriodByDateOrYearAndNumber = async (date_or_year: string | number, period_number?: number): Promise<boolean> => {
|
const getPayPeriodByDateOrYearAndNumber = async (date?: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
if (typeof date_or_year === 'string') {
|
if (date!== undefined) {
|
||||||
pay_period.value = await timesheetService.getPayPeriodByDate(date_or_year);
|
pay_period.value = await timesheetService.getPayPeriodByDate(date);
|
||||||
}
|
} else if (pay_period.value !== undefined) {
|
||||||
else if (typeof date_or_year === 'number' && period_number) {
|
pay_period.value = await timesheetService.getPayPeriodByYearAndPeriodNumber(pay_period.value.pay_year, pay_period.value.pay_period_no);
|
||||||
pay_period.value = await timesheetService.getPayPeriodByYearAndPeriodNumber(date_or_year, period_number);
|
} else return false;
|
||||||
}
|
|
||||||
else pay_period.value = undefined;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -41,11 +39,16 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTimesheetOverviewsByPayPeriod = async (pay_year: number, period_number: number, supervisor_email?: string): Promise<boolean> => {
|
const getTimesheetOverviews = async (): Promise<boolean> => {
|
||||||
is_loading.value = true;
|
is_loading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await timesheetApprovalService.getPayPeriodOverviewsBySupervisorEmail(pay_year, period_number, supervisor_email ?? auth_store.user?.email ?? '');
|
if (pay_period.value === undefined) {
|
||||||
|
is_loading.value = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await timesheetApprovalService.getPayPeriodOverviews(pay_period.value.pay_year, pay_period.value.pay_period_no);
|
||||||
pay_period_overviews.value = response.employees_overview;
|
pay_period_overviews.value = response.employees_overview;
|
||||||
is_loading.value = false;
|
is_loading.value = false;
|
||||||
|
|
||||||
|
|
@ -99,13 +102,15 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
is_loading,
|
is_loading,
|
||||||
|
is_approval_grid_mode,
|
||||||
|
search_filter,
|
||||||
pay_period,
|
pay_period,
|
||||||
pay_period_overviews,
|
pay_period_overviews,
|
||||||
current_pay_period_overview,
|
current_pay_period_overview,
|
||||||
timesheets,
|
timesheets,
|
||||||
initial_timesheets,
|
initial_timesheets,
|
||||||
getPayPeriodByDateOrYearAndNumber,
|
getPayPeriodByDateOrYearAndNumber,
|
||||||
getTimesheetOverviewsByPayPeriod,
|
getTimesheetOverviews,
|
||||||
getTimesheetsByEmployeeEmail,
|
getTimesheetsByEmployeeEmail,
|
||||||
getPayPeriodReportByYearAndPeriodNumber,
|
getPayPeriodReportByYearAndPeriodNumber,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user