refactor(approvals): more work on filters, table list view tweaks to columns, plug in list view functionalities
This commit is contained in:
parent
f66934cc4f
commit
9fab8ae1ca
|
|
@ -285,6 +285,8 @@ export default {
|
||||||
verified: "approved",
|
verified: "approved",
|
||||||
unverified: "pending",
|
unverified: "pending",
|
||||||
inactive: "inactive",
|
inactive: "inactive",
|
||||||
|
filter_active: "show only active employees",
|
||||||
|
filter_team: "",
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
button_detailed_view: "detailed view",
|
button_detailed_view: "detailed view",
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,8 @@ export default {
|
||||||
verified: "approuvé",
|
verified: "approuvé",
|
||||||
unverified: "à vérifier",
|
unverified: "à vérifier",
|
||||||
inactive: "inactif",
|
inactive: "inactif",
|
||||||
|
filter_active: "",
|
||||||
|
filter_team: "",
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
button_detailed_view: "vue détaillée",
|
button_detailed_view: "vue détaillée",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
import type { TimesheetApprovalOverview } from 'src/modules/timesheet-approval/models/timesheet-overview.models';
|
import type { TimesheetApprovalOverview } from 'src/modules/timesheet-approval/models/timesheet-overview.models';
|
||||||
|
import { getHoursMinutesStringFromHoursFloat, getMinutes } from 'src/utils/date-and-time-utils';
|
||||||
|
|
||||||
const modelApproval = defineModel<boolean>();
|
const modelApproval = defineModel<boolean>();
|
||||||
|
|
||||||
|
|
@ -15,18 +16,6 @@
|
||||||
'clickDetails': [overview: TimesheetApprovalOverview];
|
'clickDetails': [overview: TimesheetApprovalOverview];
|
||||||
'clickApprovalAll' : [is_approved: boolean];
|
'clickApprovalAll' : [is_approved: boolean];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const getMinutes = (hours: number) => {
|
|
||||||
const minutes_percent = hours - Math.floor(hours);
|
|
||||||
const minutes = Math.round(minutes_percent * 60);
|
|
||||||
return minutes > 1 ? minutes.toString() : '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
const getHoursMinutesString = (hours: number): string => {
|
|
||||||
const flat_hours = Math.floor(hours);
|
|
||||||
const minutes = Math.round((hours - flat_hours) * 60);
|
|
||||||
return `${flat_hours}h ${minutes > 1 ? minutes : ''}`
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -95,7 +84,7 @@
|
||||||
<span
|
<span
|
||||||
class="text-weight-bolder text-h3 q-py-none"
|
class="text-weight-bolder text-h3 q-py-none"
|
||||||
:class="row.regular_hours > 80 || !row.is_active ? 'text-negative' : ''"
|
:class="row.regular_hours > 80 || !row.is_active ? 'text-negative' : ''"
|
||||||
> {{ getHoursMinutesString(row.regular_hours) }} </span>
|
> {{ getHoursMinutesStringFromHoursFloat(row.regular_hours) }} </span>
|
||||||
<q-separator class="q-mr-sm" />
|
<q-separator class="q-mr-sm" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -116,7 +105,7 @@
|
||||||
<span
|
<span
|
||||||
class="text-weight-bolder q-pa-none q-mb-xs"
|
class="text-weight-bolder q-pa-none q-mb-xs"
|
||||||
style="font-size: 1.2em; line-height: 1em;"
|
style="font-size: 1.2em; line-height: 1em;"
|
||||||
> {{ getHoursMinutesString(hour_type) }} </span>
|
> {{ getHoursMinutesStringFromHoursFloat(hour_type) }} </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -13,23 +13,28 @@
|
||||||
import { useAuthStore } from 'src/stores/auth-store';
|
import { useAuthStore } from 'src/stores/auth-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 { overview_column_names, pay_period_overview_columns, PayPeriodOverviewFilters, type TimesheetApprovalOverview } from 'src/modules/timesheet-approval/models/timesheet-overview.models';
|
import { overview_column_names, OverviewColumns, pay_period_overview_columns, PayPeriodOverviewFilters, type TimesheetApprovalOverview } from 'src/modules/timesheet-approval/models/timesheet-overview.models';
|
||||||
|
import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-utils';
|
||||||
|
|
||||||
|
|
||||||
const auth_store = useAuthStore();
|
const auth_store = useAuthStore();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
const timesheet_approval_api = useTimesheetApprovalApi();
|
const timesheet_approval_api = useTimesheetApprovalApi();
|
||||||
|
|
||||||
const visible_columns = ref<string[]>([
|
const TIME_COLUMNS: OverviewColumns[] = ['REGULAR', 'EVENING', 'EMERGENCY', 'OVERTIME', 'HOLIDAY', 'VACATION'];
|
||||||
overview_column_names.REGULAR,
|
const VISIBLE_COLUMNS = ref<OverviewColumns[]>([
|
||||||
overview_column_names.EVENING,
|
'employee_first_name',
|
||||||
overview_column_names.EMERGENCY,
|
'REGULAR',
|
||||||
overview_column_names.SICK,
|
'EVENING',
|
||||||
overview_column_names.VACATION,
|
'EMERGENCY',
|
||||||
overview_column_names.HOLIDAY,
|
'OVERTIME',
|
||||||
overview_column_names.OVERTIME,
|
'HOLIDAY',
|
||||||
overview_column_names.IS_APPROVED,
|
'VACATION',
|
||||||
|
'expenses',
|
||||||
|
'mileage',
|
||||||
|
'is_approved',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const is_showing_filters = ref(false);
|
const is_showing_filters = ref(false);
|
||||||
const search_string = ref('');
|
const search_string = ref('');
|
||||||
|
|
||||||
|
|
@ -60,7 +65,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (terms.is_showing_team_only) {
|
if (terms.is_showing_team_only) {
|
||||||
result = result.filter(row => row.supervisor !== null && row.supervisor.email === (auth_store.user ? auth_store.user.email : '') );
|
result = result.filter(row => row.supervisor !== null && row.supervisor.email === (auth_store.user ? auth_store.user.email : ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (terms.name_search_string.length > 0) {
|
if (terms.name_search_string.length > 0) {
|
||||||
|
|
@ -77,206 +82,209 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="q-px-md full-height">
|
<div class="q-px-md full-height">
|
||||||
<LoadingOverlay v-model="timesheet_store.is_loading" />
|
<LoadingOverlay v-model="timesheet_store.is_loading" />
|
||||||
<transition
|
|
||||||
appear
|
<q-table
|
||||||
enter-active-class="animated fadeInUp"
|
:key="timesheet_store.is_approval_grid_mode ? 'grid' : 'list'"
|
||||||
leave-active-class="animated fadeOutDown"
|
:visible-columns="VISIBLE_COLUMNS"
|
||||||
mode="out-in"
|
:rows="overview_rows"
|
||||||
|
:columns="pay_period_overview_columns"
|
||||||
|
row-key="email"
|
||||||
|
:grid="timesheet_store.is_approval_grid_mode"
|
||||||
|
:dense="timesheet_store.is_approval_grid_mode"
|
||||||
|
hide-pagination
|
||||||
|
:pagination="{ sortBy: 'is_active' }"
|
||||||
|
:filter="overview_filters"
|
||||||
|
:filter-method="filterEmployeeRows"
|
||||||
|
color="accent"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
card-container-class="justify-center"
|
||||||
|
class="bg-transparent"
|
||||||
|
:class="timesheet_store.is_approval_grid_mode ? '' : 'sticky-header-table no-shadow'"
|
||||||
|
table-class="q-pa-none q-mx-md rounded-10 bg-dark shadow-15 hide-scrollbar"
|
||||||
|
:no-data-label="$t('shared.error.no_data_found')"
|
||||||
|
:no-results-label="$t('shared.error.no_search_results')"
|
||||||
|
:loading-label="$t('shared.label.loading')"
|
||||||
|
:style="$q.platform.is.mobile ? '' : 'max-height: 70vh;'"
|
||||||
>
|
>
|
||||||
<q-table
|
<template #top>
|
||||||
:key="timesheet_store.is_approval_grid_mode ? 'grid' : 'list'"
|
<div class="column full-width">
|
||||||
:visible-columns="visible_columns"
|
|
||||||
:rows="overview_rows"
|
<div
|
||||||
:columns="pay_period_overview_columns"
|
class="col-auto row items-start full-width q-px-lg"
|
||||||
row-key="email"
|
:class="$q.platform.is.mobile ? 'column flex-center' : 'row q-mt-md'"
|
||||||
:grid="timesheet_store.is_approval_grid_mode"
|
>
|
||||||
:dense="timesheet_store.is_approval_grid_mode"
|
<PayPeriodNavigator
|
||||||
hide-pagination
|
@date-selected="timesheet_approval_api.getTimesheetOverviews"
|
||||||
:pagination="{ sortBy: 'is_active' }"
|
@pressed-next-button="timesheet_approval_api.getTimesheetOverviews"
|
||||||
:filter="overview_filters"
|
@pressed-previous-button="timesheet_approval_api.getTimesheetOverviews"
|
||||||
:filter-method="filterEmployeeRows"
|
:class="$q.platform.is.mobile ? 'q-mb-sm' : ''"
|
||||||
color="accent"
|
style="height: 40px;"
|
||||||
:rows-per-page-options="[0]"
|
/>
|
||||||
card-container-class="justify-center"
|
|
||||||
class="bg-transparent"
|
<q-space />
|
||||||
:class="timesheet_store.is_approval_grid_mode ? '' : 'sticky-header-table no-shadow'"
|
|
||||||
table-class="q-pa-none q-mx-md rounded-10 bg-dark shadow-15"
|
|
||||||
: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="column full-width">
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="col-auto row items-start full-width q-px-lg"
|
class="col-auto row no-wrap items-start"
|
||||||
:class="($q.platform.is.mobile ? 'column flex-center' : 'row q-mt-md') + (timesheet_store.is_approval_grid_mode ? '' : ' q-mb-md')"
|
:class="$q.platform.is.mobile ? 'q-mb-md' : ''"
|
||||||
>
|
>
|
||||||
<PayPeriodNavigator
|
<q-btn-toggle
|
||||||
@date-selected="timesheet_approval_api.getTimesheetOverviews"
|
v-model="timesheet_store.is_approval_grid_mode"
|
||||||
@pressed-next-button="timesheet_approval_api.getTimesheetOverviews"
|
push
|
||||||
@pressed-previous-button="timesheet_approval_api.getTimesheetOverviews"
|
rounded
|
||||||
:class="$q.platform.is.mobile ? 'q-mb-sm' : ''"
|
color="white"
|
||||||
|
text-color="accent"
|
||||||
|
toggle-color="accent"
|
||||||
|
class="col-auto"
|
||||||
|
:class="$q.platform.is.mobile ? 'q-mb-sm' : 'q-mr-sm'"
|
||||||
|
:options="[
|
||||||
|
{ icon: 'grid_view', value: true },
|
||||||
|
{ icon: 'view_list', value: false },
|
||||||
|
]"
|
||||||
style="height: 40px;"
|
style="height: 40px;"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-space />
|
<q-btn
|
||||||
|
push
|
||||||
<div
|
rounded
|
||||||
class="col-auto row no-wrap items-start"
|
icon="download"
|
||||||
:class="$q.platform.is.mobile ? 'q-mb-md' : ''"
|
color="accent"
|
||||||
>
|
:label="$q.screen.lt.md ? '' : $t('shared.label.download')"
|
||||||
<q-btn-toggle
|
class="col-auto q-mr-sm"
|
||||||
v-model="timesheet_store.is_approval_grid_mode"
|
style="height: 40px;"
|
||||||
push
|
@click="timesheet_store.is_report_dialog_open = true"
|
||||||
rounded
|
|
||||||
color="white"
|
|
||||||
text-color="accent"
|
|
||||||
toggle-color="accent"
|
|
||||||
class="col-auto"
|
|
||||||
:class="$q.platform.is.mobile ? 'q-mb-sm' : 'q-mr-sm'"
|
|
||||||
:options="[
|
|
||||||
{ icon: 'grid_view', value: true },
|
|
||||||
{ icon: 'view_list', value: false },
|
|
||||||
]"
|
|
||||||
style="height: 40px;"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<q-btn
|
|
||||||
push
|
|
||||||
rounded
|
|
||||||
icon="download"
|
|
||||||
color="accent"
|
|
||||||
:label="$q.screen.lt.md ? '' : $t('shared.label.download')"
|
|
||||||
class="col-auto q-mr-sm"
|
|
||||||
style="height: 40px;"
|
|
||||||
@click="timesheet_store.is_report_dialog_open = true"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<QTableFilters
|
|
||||||
v-model:search="search_string"
|
|
||||||
class="col-auto q-mb-sm"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<q-btn
|
|
||||||
flat
|
|
||||||
icon="filter_alt"
|
|
||||||
color="white"
|
|
||||||
:label="$q.platform.is.mobile ? '' : $t('shared.label.filter')"
|
|
||||||
class="col q-ml-sm self-stretch bg-primary"
|
|
||||||
style="border-radius: 5px 5px 0 0;"
|
|
||||||
@click="is_showing_filters = !is_showing_filters"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-slide-transition>
|
|
||||||
<OverviewListFilters
|
|
||||||
v-if="is_showing_filters"
|
|
||||||
v-model:filters="overview_filters"
|
|
||||||
class="q-mx-lg col-auto"
|
|
||||||
/>
|
/>
|
||||||
</q-slide-transition>
|
|
||||||
|
|
||||||
<q-separator
|
<QTableFilters
|
||||||
color="primary"
|
v-model:search="search_string"
|
||||||
size="5px"
|
class="col-auto q-mb-sm"
|
||||||
class="q-mx-lg q-my-none q-pa-none"
|
/>
|
||||||
/>
|
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
icon="filter_alt"
|
||||||
|
color="white"
|
||||||
|
:label="$q.platform.is.mobile ? '' : $t('shared.label.filter')"
|
||||||
|
class="col q-ml-sm self-stretch bg-primary"
|
||||||
|
style="border-radius: 5px 5px 0 0;"
|
||||||
|
@click="is_showing_filters = !is_showing_filters"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #header="props">
|
<q-slide-transition>
|
||||||
<q-tr
|
<OverviewListFilters
|
||||||
:props="props"
|
v-if="is_showing_filters"
|
||||||
class="bg-primary"
|
v-model:filters="overview_filters"
|
||||||
>
|
class="q-mx-lg col-auto"
|
||||||
<q-th
|
/>
|
||||||
v-for="col in props.cols"
|
</q-slide-transition>
|
||||||
:key="col.name"
|
|
||||||
:props="props"
|
|
||||||
>
|
|
||||||
<span class="text-uppercase text-weight-bolder text-white">
|
|
||||||
{{ $t(col.label) }}
|
|
||||||
</span>
|
|
||||||
</q-th>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #body-cell="props">
|
<q-separator
|
||||||
<q-td
|
color="primary"
|
||||||
:props="props"
|
size="5px"
|
||||||
class="text-weight-medium"
|
class="q-mx-lg q-my-none q-pa-none"
|
||||||
>
|
|
||||||
<transition
|
|
||||||
appear
|
|
||||||
enter-active-class="animated fadeInUp slow"
|
|
||||||
leave-active-class="animated fadeOutDown"
|
|
||||||
mode="out-in"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
:key="props.rowIndex + (timesheet_store.pay_period?.pay_period_no ?? 0)"
|
|
||||||
class="rounded-5"
|
|
||||||
style="font-size: 1.2em;"
|
|
||||||
:style="`animation-delay: ${props.rowIndex / 30}s;`"
|
|
||||||
>
|
|
||||||
<transition
|
|
||||||
v-if="props.col.name === 'is_approved'"
|
|
||||||
enter-active-class="animated swing"
|
|
||||||
mode="out-in"
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
:key="props.row.is_approved"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
:icon="props.value ? 'lock' : 'lock_open'"
|
|
||||||
:color="props.value ? 'white' : 'grey-5'"
|
|
||||||
class="rounded-5 "
|
|
||||||
:class="props.value ? 'bg-accent' : ''"
|
|
||||||
@click.stop="props.row.is_approved = !props.row.is_approved"
|
|
||||||
/>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Template for individual employee cards -->
|
|
||||||
<template #item="props: { row: TimesheetApprovalOverview, 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="onClickedDetails"
|
|
||||||
@click-approval-all="is_approved => onClickApproveAll(props.row.email, is_approved)"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- Template for custome failed-to-load state -->
|
<template #header="props">
|
||||||
<template #no-data="{ message, filter }">
|
<q-tr
|
||||||
<div class="full-width column items-center text-accent q-gutter-sm">
|
:props="props"
|
||||||
<q-icon
|
class="bg-primary"
|
||||||
size="4em"
|
>
|
||||||
:name="filter ? 'filter_alt_off' : 'error_outline'"
|
<q-th
|
||||||
/>
|
v-for="col in props.cols"
|
||||||
|
:key="col.name"
|
||||||
<span class="text-h6">
|
:props="props"
|
||||||
{{ message }}
|
>
|
||||||
|
<span class="text-uppercase text-weight-bolder text-white">
|
||||||
|
{{ $t(col.label) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</q-th>
|
||||||
</template>
|
</q-tr>
|
||||||
</q-table>
|
</template>
|
||||||
</transition>
|
|
||||||
|
<template #body-cell="props">
|
||||||
|
<q-td
|
||||||
|
:props="props"
|
||||||
|
class="text-weight-medium"
|
||||||
|
>
|
||||||
|
<transition
|
||||||
|
appear
|
||||||
|
enter-active-class="animated fadeInUp slow"
|
||||||
|
leave-active-class="animated fadeOutDown"
|
||||||
|
mode="out-in"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:key="props.rowIndex + (timesheet_store.pay_period?.pay_period_no ?? 0)"
|
||||||
|
class="rounded-5"
|
||||||
|
style="font-size: 1.2em;"
|
||||||
|
:style="`animation-delay: ${props.rowIndex / 30}s;`"
|
||||||
|
>
|
||||||
|
<transition
|
||||||
|
v-if="props.col.name === 'is_approved'"
|
||||||
|
enter-active-class="animated swing"
|
||||||
|
mode="out-in"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
:key="props.row.is_approved"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
:icon="props.value ? 'lock' : 'lock_open'"
|
||||||
|
:color="props.value ? 'white' : 'grey-5'"
|
||||||
|
class="rounded-5 "
|
||||||
|
:class="props.value ? 'bg-accent' : ''"
|
||||||
|
@click.stop="onClickApproveAll(props.row.email, props.row.is_approved)"
|
||||||
|
/>
|
||||||
|
</transition>
|
||||||
|
|
||||||
|
<div v-else-if="props.col.name === 'employee_first_name'">
|
||||||
|
<span class="text-h5 text-uppercase text-accent q-mr-xs">
|
||||||
|
{{ props.value }}
|
||||||
|
</span>
|
||||||
|
<span class="text-uppercase text-weight-light">
|
||||||
|
{{ props.row.employee_last_name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
:class="props.col.name === overview_column_names.REGULAR && props.row.overtime > 0 ? 'text-negative text-weight-bolder' : 'text-weight-regular'"
|
||||||
|
>
|
||||||
|
{{ TIME_COLUMNS.includes(props.col.name) ?
|
||||||
|
getHoursMinutesStringFromHoursFloat(props.value) : props.value }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Template for individual employee cards -->
|
||||||
|
<template #item="props: { row: TimesheetApprovalOverview, 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="onClickedDetails"
|
||||||
|
@click-approval-all="is_approved => onClickApproveAll(props.row.email, is_approved)"
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,11 @@ export interface PayPeriodOverviewFilters {
|
||||||
name_search_string: string;
|
name_search_string: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OverviewColumns = 'employee_first_name' | 'employee_last_name' | 'email' | 'REGULAR' | 'EVENING' | 'EMERGENCY' | 'SICK' | 'HOLIDAY' | 'VACATION' | 'OVERTIME' | 'expenses' | 'mileage' | 'is_approved' | 'is_active'
|
||||||
|
|
||||||
export const overview_column_names = {
|
export const overview_column_names = {
|
||||||
EMPLOYEE_NAME: 'employee_name',
|
FIRST_NAME: 'employee_first_name',
|
||||||
|
LAST_NAME: 'employee_last_name',
|
||||||
EMAIL: 'email',
|
EMAIL: 'email',
|
||||||
REGULAR: 'REGULAR',
|
REGULAR: 'REGULAR',
|
||||||
EVENING: 'EVENING',
|
EVENING: 'EVENING',
|
||||||
|
|
@ -81,18 +84,25 @@ export const overview_column_names = {
|
||||||
|
|
||||||
export const pay_period_overview_columns: QTableColumn[] = [
|
export const pay_period_overview_columns: QTableColumn[] = [
|
||||||
{
|
{
|
||||||
name: overview_column_names.EMPLOYEE_NAME,
|
name: overview_column_names.FIRST_NAME,
|
||||||
label: 'timesheet_approvals.table.full_name',
|
label: 'timesheet_approvals.table.full_name',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
field: 'employee_name',
|
field: overview_column_names.FIRST_NAME,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: overview_column_names.LAST_NAME,
|
||||||
|
label: 'timesheet_approvals.table.full_name',
|
||||||
|
align: 'left',
|
||||||
|
field: 'employee_last_name',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: overview_column_names.EMAIL,
|
name: overview_column_names.EMAIL,
|
||||||
label: 'timesheet_approvals.table.email',
|
label: 'timesheet_approvals.table.email',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
field: 'email',
|
field: overview_column_names.EMAIL,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -70,14 +70,11 @@
|
||||||
</q-carousel>
|
</q-carousel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column col flex-center q-pt-md q-pl-md">
|
<div class="column col items-center q-pl-md">
|
||||||
<div class="col"></div>
|
<div class="col-auto row justify-end full-width within-iframe" style="height: 50vh;">
|
||||||
|
|
||||||
<div class="col-auto row justify-end full-width within-iframe">
|
|
||||||
<iframe
|
<iframe
|
||||||
title="Environment Canada Weather"
|
title="Environment Canada Weather"
|
||||||
height="100%"
|
height="400px"
|
||||||
width="100%"
|
|
||||||
src="https://weather.gc.ca/wxlink/wxlink.html?coords=45.159%2C-73.676&lang=e"
|
src="https://weather.gc.ca/wxlink/wxlink.html?coords=45.159%2C-73.676&lang=e"
|
||||||
allowtransparency="true"
|
allowtransparency="true"
|
||||||
style="border: 0;"
|
style="border: 0;"
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,25 @@ import { date } from 'quasar';
|
||||||
const anchor_date: Date = new Date('2023-12-17');
|
const anchor_date: Date = new Date('2023-12-17');
|
||||||
|
|
||||||
export const getCurrentPayPeriod = (today = new Date()): number => {
|
export const getCurrentPayPeriod = (today = new Date()): number => {
|
||||||
const period_length = 14; // days
|
const period_length = 14; // days
|
||||||
const periods_per_year = 26;
|
const periods_per_year = 26;
|
||||||
|
|
||||||
const days_since_anchor = date.getDateDiff(today, anchor_date, 'days');
|
const days_since_anchor = date.getDateDiff(today, anchor_date, 'days');
|
||||||
const periods_since_anchor = Math.floor(days_since_anchor / period_length);
|
const periods_since_anchor = Math.floor(days_since_anchor / period_length);
|
||||||
|
|
||||||
const current_period = (periods_since_anchor % periods_per_year) + 1;
|
const current_period = (periods_since_anchor % periods_per_year) + 1;
|
||||||
|
|
||||||
return current_period;
|
return current_period;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMinutes = (hours: number) => {
|
||||||
|
const minutes_percent = hours - Math.floor(hours);
|
||||||
|
const minutes = Math.round(minutes_percent * 60);
|
||||||
|
return minutes > 1 ? minutes.toString() : '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHoursMinutesStringFromHoursFloat = (hours: number): string => {
|
||||||
|
const flat_hours = Math.floor(hours);
|
||||||
|
const minutes = Math.round((hours - flat_hours) * 60);
|
||||||
|
return `${flat_hours}h ${minutes > 1 ? minutes : ''}`
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user