fix(approvals): progress on layout, dynamic resizing of table scroll area, UI/UX improvements, redo of left drawer
This commit is contained in:
parent
720417ab16
commit
8989a7d9c0
|
|
@ -97,7 +97,8 @@ export default defineConfig((ctx) => {
|
|||
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#devserver
|
||||
devServer: {
|
||||
// https: true,
|
||||
open: true // opens browser window automatically
|
||||
open: true, // opens browser window automatically
|
||||
allowedHosts: true
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#framework
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 938 KiB After Width: | Height: | Size: 578 KiB |
|
|
@ -329,6 +329,7 @@ export default {
|
|||
},
|
||||
print_report: {
|
||||
title: "Download options",
|
||||
description: "Choose what to include in the report",
|
||||
company: "companies",
|
||||
type: "type",
|
||||
shifts: "shifts",
|
||||
|
|
@ -345,7 +346,7 @@ export default {
|
|||
unverified: "pending",
|
||||
inactive: "inactive",
|
||||
filter_active: "show only active employees",
|
||||
filter_team: "",
|
||||
filter_team: "show my team only",
|
||||
},
|
||||
tooltip: {
|
||||
button_detailed_view: "detailed view",
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ export default {
|
|||
update: "mettre à jour",
|
||||
modify: "modifier",
|
||||
close: "fermer",
|
||||
download: "téléchargement",
|
||||
download: "télécharger",
|
||||
},
|
||||
misc: {
|
||||
or: "ou",
|
||||
|
|
@ -330,6 +330,7 @@ export default {
|
|||
},
|
||||
print_report: {
|
||||
title: "options de téléchargement",
|
||||
description: "Choisissez ce qui sera inclu dans le rapport",
|
||||
company: "compagnies",
|
||||
type: "types de données",
|
||||
shifts: "quarts de travail",
|
||||
|
|
@ -345,8 +346,8 @@ export default {
|
|||
verified: "approuvé",
|
||||
unverified: "à vérifier",
|
||||
inactive: "inactif",
|
||||
filter_active: "",
|
||||
filter_team: "",
|
||||
filter_active: "montrer les employés inactifs",
|
||||
filter_team: "montrer mon équipe seulement",
|
||||
},
|
||||
tooltip: {
|
||||
button_detailed_view: "vue détaillée",
|
||||
|
|
|
|||
|
|
@ -2,21 +2,33 @@
|
|||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useAuthStore } from 'src/stores/auth-store';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { ref } from 'vue';
|
||||
import { RouteNames } from 'src/router/router-constants';
|
||||
import { ModuleNames } from 'src/modules/shared/models/user.models';
|
||||
import { ModuleNames, type UserModuleAccess } from 'src/modules/shared/models/user.models';
|
||||
|
||||
const DRAWER_BUTTONS: { i18n_key: string, icon: string, route: RouteNames, required_module?: UserModuleAccess }[] = [
|
||||
{ i18n_key: 'nav_bar.home', icon: "home", route: RouteNames.DASHBOARD, required_module: ModuleNames.DASHBOARD },
|
||||
{ i18n_key: 'nav_bar.timesheet_approvals', icon: "event_available", route: RouteNames.TIMESHEET_APPROVALS, required_module: ModuleNames.TIMESHEETS_APPROVAL },
|
||||
{ i18n_key: 'nav_bar.employee_list', icon: "groups", route: RouteNames.EMPLOYEE_LIST },
|
||||
{ i18n_key: 'nav_bar.timesheet', icon: "punch_clock", route: RouteNames.TIMESHEET, required_module: ModuleNames.TIMESHEETS },
|
||||
{ i18n_key: 'nav_bar.profile', icon: "account_box", route: RouteNames.PROFILE, required_module: ModuleNames.PERSONAL_PROFILE },
|
||||
{ i18n_key: 'nav_bar.help', icon: "contact_support", route: RouteNames.HELP },
|
||||
]
|
||||
|
||||
const auth_store = useAuthStore();
|
||||
const ui_store = useUiStore();
|
||||
const router = useRouter();
|
||||
const is_mini = ref(true);
|
||||
|
||||
const goToPageName = (page_name: string) => {
|
||||
router.push({ name: page_name }).catch(err => {
|
||||
console.error('Error with Vue Router: ', err);
|
||||
const onClickDrawerPage = (page_name: RouteNames) => {
|
||||
ui_store.current_page = page_name;
|
||||
is_mini.value = true;
|
||||
|
||||
router.push({ name: page_name }).catch( error => {
|
||||
console.error('failed to reach page: ', error);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -26,7 +38,7 @@
|
|||
router.push({ name: 'login' }).catch(err => {
|
||||
console.error('could not log you out: ', err);
|
||||
})
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -39,102 +51,46 @@
|
|||
:mini="is_mini"
|
||||
@mouseenter="is_mini = false"
|
||||
@mouseleave="is_mini = true"
|
||||
class="bg-dark z-max column no-wrap"
|
||||
:class="!$q.platform.is.mobile && is_mini ? 'items-center' : 'items-start'"
|
||||
class="bg-dark z-max"
|
||||
>
|
||||
<!-- Home -->
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
no-wrap
|
||||
size="lg"
|
||||
icon="home"
|
||||
:label="!$q.platform.is.mobile && is_mini ? '' : $t('nav_bar.home')"
|
||||
class="col-auto text-uppercase text-weight-bold q-my-xs"
|
||||
:class="!$q.platform.is.mobile && is_mini ? '': 'q-px-sm'"
|
||||
@click="goToPageName(RouteNames.DASHBOARD)"
|
||||
/>
|
||||
<q-scroll-area class="column fit">
|
||||
<div
|
||||
v-for="button, index in DRAWER_BUTTONS"
|
||||
:key="index"
|
||||
v-show="button.required_module ?? true"
|
||||
class="row items-center full-width q-py-sm cursor-pointer"
|
||||
:class="ui_store.current_page === button.route ? ($q.dark.isActive ? 'bg-green-10' : 'bg-green-2') : ''"
|
||||
@click="onClickDrawerPage(button.route)"
|
||||
>
|
||||
<q-icon
|
||||
:name="button.icon"
|
||||
color="accent"
|
||||
size="lg"
|
||||
class="col-auto q-pl-sm"
|
||||
/>
|
||||
|
||||
<!-- Timesheet Validation -->
|
||||
<q-btn
|
||||
v-if="auth_store.user?.user_module_access.includes(ModuleNames.TIMESHEETS_APPROVAL)"
|
||||
flat
|
||||
dense
|
||||
no-wrap
|
||||
size="lg"
|
||||
icon="event_available"
|
||||
:label="!$q.platform.is.mobile && is_mini ? '' : $t('nav_bar.timesheet_approvals')"
|
||||
class="col-auto text-uppercase text-weight-bold q-my-xs"
|
||||
:class="!$q.platform.is.mobile && is_mini ? '': 'q-px-sm'"
|
||||
@click="goToPageName(RouteNames.TIMESHEET_APPROVALS)"
|
||||
/>
|
||||
<div class="col text-uppercase text-weight-bold text-h6 q-mini-drawer-hide q-pl-sm">
|
||||
{{ $t(button.i18n_key) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Employee List -->
|
||||
<q-btn
|
||||
v-if="auth_store.user?.user_module_access.includes(ModuleNames.EMPLOYEE_LIST)"
|
||||
flat
|
||||
dense
|
||||
no-wrap
|
||||
size="lg"
|
||||
icon="groups"
|
||||
:label="!$q.platform.is.mobile && is_mini ? '' : $t('nav_bar.employee_list')"
|
||||
class="col-auto text-uppercase text-weight-bold q-my-xs"
|
||||
:class="!$q.platform.is.mobile && is_mini ? '': 'q-px-sm'"
|
||||
@click="goToPageName(RouteNames.EMPLOYEE_LIST)"
|
||||
/>
|
||||
<q-separator spaced />
|
||||
|
||||
<!-- Employee Timesheet -->
|
||||
<q-btn
|
||||
v-if="auth_store.user?.user_module_access.includes(ModuleNames.TIMESHEETS)"
|
||||
flat
|
||||
dense
|
||||
no-wrap
|
||||
size="lg"
|
||||
icon="punch_clock"
|
||||
:label="!$q.platform.is.mobile && is_mini ? '' : $t('nav_bar.timesheet')"
|
||||
class="col-auto text-uppercase text-weight-bold q-my-xs"
|
||||
:class="!$q.platform.is.mobile && is_mini ? '': 'q-px-sm'"
|
||||
@click="goToPageName(RouteNames.TIMESHEET)"
|
||||
/>
|
||||
<div
|
||||
class="row items-center full-width cursor-pointer q-py-sm"
|
||||
@click="handleLogout"
|
||||
>
|
||||
<q-icon
|
||||
name="exit_to_app"
|
||||
color="accent"
|
||||
size="lg"
|
||||
class="col-auto q-pl-sm"
|
||||
/>
|
||||
|
||||
<!-- Profile -->
|
||||
<q-btn
|
||||
v-if="auth_store.user?.user_module_access.includes(ModuleNames.PERSONAL_PROFILE)"
|
||||
flat
|
||||
dense
|
||||
no-wrap
|
||||
size="lg"
|
||||
icon="account_box"
|
||||
:label="!$q.platform.is.mobile && is_mini ? '' : $t('nav_bar.profile')"
|
||||
class="col-auto text-uppercase text-weight-bold q-my-xs"
|
||||
:class="!$q.platform.is.mobile && is_mini ? '': 'q-px-sm'"
|
||||
@click="goToPageName(RouteNames.PROFILE)"
|
||||
/>
|
||||
|
||||
<!-- Help -->
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
no-wrap
|
||||
size="lg"
|
||||
icon="contact_support"
|
||||
:label="!$q.platform.is.mobile && is_mini ? '' : $t('nav_bar.help')"
|
||||
class="col-auto text-uppercase text-weight-bold q-my-xs"
|
||||
:class="!$q.platform.is.mobile && is_mini ? '': 'q-px-sm'"
|
||||
@click="goToPageName(RouteNames.HELP)"
|
||||
/>
|
||||
|
||||
<!-- Logout -->
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
no-wrap
|
||||
size="lg"
|
||||
icon="exit_to_app"
|
||||
:label="!$q.platform.is.mobile && is_mini ? '' : $t('nav_bar.logout')"
|
||||
class="col-auto text-uppercase text-weight-bold q-my-xs"
|
||||
:class="!$q.platform.is.mobile && is_mini ? 'absolute-bottom': 'absolute-bottom-left'"
|
||||
@click="handleLogout"
|
||||
/>
|
||||
<div class="col text-uppercase text-weight-bold text-h6 q-mini-drawer-hide q-pl-sm">
|
||||
{{ $t('nav_bar.logout') }}
|
||||
</div>
|
||||
</div>
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
</template>
|
||||
|
|
@ -32,10 +32,13 @@
|
|||
<template>
|
||||
<q-layout view="hHh lpR fFf">
|
||||
<HeaderBar />
|
||||
|
||||
<LeftDrawer />
|
||||
|
||||
<q-page-container>
|
||||
<router-view class="q-pa-sm bg-secondary" />
|
||||
<router-view />
|
||||
</q-page-container>
|
||||
|
||||
<FooterBar />
|
||||
</q-layout>
|
||||
</template>
|
||||
|
|
@ -86,16 +86,32 @@
|
|||
:visible-columns="visible_columns"
|
||||
>
|
||||
<template #top>
|
||||
<div class="row full-width q-mb-sm">
|
||||
<div class="row flex-center full-width q-mb-sm">
|
||||
<q-btn
|
||||
push
|
||||
rounded
|
||||
color="accent"
|
||||
icon="las la-user-edit"
|
||||
:label="$t('shared.label.add')"
|
||||
class="text-uppercase"
|
||||
class="text-uppercase q-py-sm"
|
||||
@click.stop="_evt => employee_store.openAddModifyDialog()"
|
||||
/>
|
||||
|
||||
<q-checkbox
|
||||
v-model="filters.hide_inactive_users"
|
||||
color="accent"
|
||||
:label="$t('employee_management.filter.hide_terminated')"
|
||||
class="text-uppercase q-ml-md text-weight-medium q-px-sm"
|
||||
:class="filters.hide_inactive_users ? 'rounded-25 bg-accent' : ''"
|
||||
>
|
||||
<q-icon
|
||||
name="las la-user-times"
|
||||
:color="filters.hide_inactive_users ? 'white' : 'negative'"
|
||||
size="sm"
|
||||
class="q-px-sm"
|
||||
/>
|
||||
</q-checkbox>
|
||||
|
||||
|
||||
<q-space />
|
||||
|
||||
<q-btn-toggle
|
||||
|
|
@ -131,15 +147,6 @@
|
|||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<q-space />
|
||||
<q-checkbox
|
||||
v-model="filters.hide_inactive_users"
|
||||
color="accent"
|
||||
:label="$t('employee_management.filter.hide_terminated')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #header="props">
|
||||
|
|
|
|||
|
|
@ -23,36 +23,40 @@
|
|||
:class="ui_store.is_mobile_mode ? 'column' : 'row'"
|
||||
style="border: 1px solid var(--q-accent);"
|
||||
>
|
||||
<q-item
|
||||
<div
|
||||
v-for="mode of dark_mode_options"
|
||||
:key="mode.label"
|
||||
clickable
|
||||
dense
|
||||
v-ripple
|
||||
class="col row rounded-5 q-ma-sm shadow-4"
|
||||
:class="(mode.quasar_value === $q.dark.mode ? 'bg-accent text-white text-weight-bolder' : '') + ($q.platform.is.mobile ? ' full-width q-py-xs' : '')"
|
||||
@click="ui_store.user_preferences.is_dark_mode = mode.value"
|
||||
class="col q-pa-sm"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon
|
||||
:name="mode.icon"
|
||||
size="md"
|
||||
:color="mode.quasar_value === $q.dark.mode ? 'white' : ''"
|
||||
/>
|
||||
</q-item-section>
|
||||
<q-item
|
||||
clickable
|
||||
dense
|
||||
v-ripple
|
||||
class="rounded-5 shadow-4 q-py-xs"
|
||||
:class="(mode.quasar_value === $q.dark.mode ? 'bg-accent text-white text-weight-bolder' : '') + ($q.platform.is.mobile ? ' full-width q-py-xs' : '')"
|
||||
@click="ui_store.user_preferences.is_dark_mode = mode.value"
|
||||
>
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
:name="mode.icon"
|
||||
size="md"
|
||||
:color="mode.quasar_value === $q.dark.mode ? 'white' : ''"
|
||||
/>
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section class="text-uppercase justify-center">
|
||||
<q-item-label> {{ $t(mode.label) }}</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label class="text-uppercase justify-center">{{ $t(mode.label) }}</q-item-label>
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
v-if="mode.quasar_value === $q.dark.mode"
|
||||
name="check"
|
||||
color="white"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
v-if="mode.quasar_value === $q.dark.mode"
|
||||
name="check"
|
||||
color="white"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="column text-uppercase text-center text-weight-bolder text-h4">
|
||||
<div class="column text-uppercase text-center text-weight-bolder text-h4 q-pt-lg">
|
||||
<span
|
||||
v-if="!$q.platform.is.mobile"
|
||||
class="col q-mt-lg"
|
||||
class="col"
|
||||
>{{ $t(title) }}</span>
|
||||
|
||||
<transition
|
||||
|
|
|
|||
|
|
@ -15,17 +15,18 @@
|
|||
keep-color
|
||||
size="lg"
|
||||
color="accent"
|
||||
label="show inactive"
|
||||
:label="$t('timesheet_approvals.table.filter_active')"
|
||||
class="col"
|
||||
:class="filters.is_showing_inactive ? 'text-accent text-weight-bolder' : 'text-white text-weight-medium'"
|
||||
/>
|
||||
|
||||
<q-checkbox
|
||||
v-model="filters.is_showing_team_only"
|
||||
keep-color
|
||||
size="lg"
|
||||
val="team"
|
||||
color="accent"
|
||||
label="show team only"
|
||||
:label="$t('timesheet_approvals.table.filter_team')"
|
||||
class="col"
|
||||
:class="filters.is_showing_team_only ? 'text-accent text-weight-bolder' : 'text-white text-weight-medium'"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -154,7 +154,6 @@ import { getHoursMinutesStringFromHoursFloat, getMinutes } from 'src/utils/date-
|
|||
>
|
||||
<div
|
||||
class="col text-uppercase"
|
||||
:class="row.total_hours > 80 || !row.is_active ? 'text-negative' : ''"
|
||||
>
|
||||
<span class="text-h6 q-ml-sm text-weight-bolder">{{ 'Total : ' + Math.floor(row.total_hours)
|
||||
}}</span>
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@
|
|||
'is_approved',
|
||||
]);
|
||||
|
||||
const { maxHeight } = defineProps<{
|
||||
maxHeight: number;
|
||||
}>();
|
||||
|
||||
const is_showing_filters = ref(false);
|
||||
const search_string = ref('');
|
||||
|
||||
|
|
@ -80,211 +84,213 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="q-px-md full-height">
|
||||
<div class="full-width">
|
||||
<LoadingOverlay v-model="timesheet_store.is_loading" />
|
||||
|
||||
<q-table
|
||||
:key="timesheet_store.is_approval_grid_mode ? 'grid' : 'list'"
|
||||
:visible-columns="VISIBLE_COLUMNS"
|
||||
: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;'"
|
||||
>
|
||||
<template #top>
|
||||
<div class="column full-width">
|
||||
|
||||
<div
|
||||
class="col-auto row items-start full-width q-px-lg"
|
||||
:class="$q.platform.is.mobile ? 'column flex-center' : 'row q-mt-md'"
|
||||
>
|
||||
<PayPeriodNavigator
|
||||
@date-selected="timesheet_approval_api.getTimesheetOverviews"
|
||||
@pressed-next-button="timesheet_approval_api.getTimesheetOverviews"
|
||||
@pressed-previous-button="timesheet_approval_api.getTimesheetOverviews"
|
||||
:class="$q.platform.is.mobile ? 'q-mb-sm' : ''"
|
||||
style="height: 40px;"
|
||||
/>
|
||||
|
||||
<q-space />
|
||||
<q-table
|
||||
dense
|
||||
row-key="email"
|
||||
color="accent"
|
||||
hide-pagination
|
||||
:rows="overview_rows"
|
||||
:columns="pay_period_overview_columns"
|
||||
:visible-columns="VISIBLE_COLUMNS"
|
||||
:grid="timesheet_store.is_approval_grid_mode"
|
||||
:pagination="{ sortBy: 'is_active' }"
|
||||
:filter="overview_filters"
|
||||
:filter-method="filterEmployeeRows"
|
||||
:rows-per-page-options="[0]"
|
||||
class="bg-transparent"
|
||||
:class="timesheet_store.is_approval_grid_mode ? '' : 'sticky-header-table no-shadow'"
|
||||
card-container-class="justify-center"
|
||||
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="`max-height: ${maxHeight}px;`"
|
||||
@row-click="(_evt, row: TimesheetApprovalOverview) => onClickedDetails(row)"
|
||||
>
|
||||
<template #top>
|
||||
<div class="column full-width">
|
||||
|
||||
<div
|
||||
class="col-auto row no-wrap items-start"
|
||||
:class="$q.platform.is.mobile ? 'q-mb-md' : ''"
|
||||
class="col-auto row items-start full-width q-px-lg"
|
||||
:class="$q.platform.is.mobile ? 'column flex-center' : 'row q-mt-md'"
|
||||
>
|
||||
<q-btn-toggle
|
||||
v-model="timesheet_store.is_approval_grid_mode"
|
||||
push
|
||||
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 },
|
||||
]"
|
||||
<PayPeriodNavigator
|
||||
@date-selected="timesheet_approval_api.getTimesheetOverviews"
|
||||
@pressed-next-button="timesheet_approval_api.getTimesheetOverviews"
|
||||
@pressed-previous-button="timesheet_approval_api.getTimesheetOverviews"
|
||||
:class="$q.platform.is.mobile ? 'q-mb-sm' : ''"
|
||||
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"
|
||||
/>
|
||||
<q-space />
|
||||
|
||||
<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
|
||||
color="primary"
|
||||
size="5px"
|
||||
class="q-mx-lg q-my-none q-pa-none"
|
||||
/>
|
||||
</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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
<div
|
||||
class="col-auto row no-wrap items-start"
|
||||
:class="$q.platform.is.mobile ? 'q-mb-md' : ''"
|
||||
>
|
||||
<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)"
|
||||
<q-btn-toggle
|
||||
v-model="timesheet_store.is_approval_grid_mode"
|
||||
push
|
||||
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;"
|
||||
/>
|
||||
</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 }}
|
||||
<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
|
||||
color="primary"
|
||||
size="5px"
|
||||
class="q-mx-lg q-my-none q-pa-none"
|
||||
/>
|
||||
</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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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 / 15}s; opacity: ${props.row.is_active ? '1' : '0.5'};`"
|
||||
>
|
||||
<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 ? (props.row.is_active ? 'bg-accent' : 'bg-negative') : ''"
|
||||
@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 q-mr-xs"
|
||||
:class="props.row.is_active ? 'text-accent' : 'text-negative'"
|
||||
>
|
||||
{{ 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>
|
||||
|
||||
<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'"
|
||||
<!-- 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>
|
||||
|
||||
<span class="text-h6">
|
||||
{{ message }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</q-table>
|
||||
<!-- 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>
|
||||
</template>
|
||||
|
||||
|
|
@ -305,4 +311,7 @@
|
|||
|
||||
tbody
|
||||
scroll-margin-top: 48px
|
||||
|
||||
.q-table__grid-content
|
||||
overflow: auto
|
||||
</style>
|
||||
|
|
@ -1,143 +1,163 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { TimesheetApprovalCSVReportFilters } from 'src/modules/timesheet-approval/models/timesheet-approval-csv-report.models';
|
||||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { TimesheetApprovalCSVReportFilters } from 'src/modules/timesheet-approval/models/timesheet-approval-csv-report.models';
|
||||
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const report_filter_options = ref<TimesheetApprovalCSVReportFilters>(new TimesheetApprovalCSVReportFilters);
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const report_filter_options = ref<TimesheetApprovalCSVReportFilters>(new TimesheetApprovalCSVReportFilters);
|
||||
|
||||
const selected_report_filters = ref<(keyof TimesheetApprovalCSVReportFilters)[]>(
|
||||
Object.entries(report_filter_options.value).filter(([_key, value]) => value).map(([key]) => key as keyof TimesheetApprovalCSVReportFilters)
|
||||
);
|
||||
const selected_report_filters = ref<(keyof TimesheetApprovalCSVReportFilters)[]>(
|
||||
Object.entries(report_filter_options.value).filter(([_key, value]) => value).map(([key]) => key as keyof TimesheetApprovalCSVReportFilters)
|
||||
);
|
||||
|
||||
interface ReportOptions {
|
||||
label: string;
|
||||
value: keyof TimesheetApprovalCSVReportFilters;
|
||||
};
|
||||
interface ReportOptions {
|
||||
label: string;
|
||||
value: keyof TimesheetApprovalCSVReportFilters;
|
||||
};
|
||||
|
||||
const company_options: ReportOptions[] = [
|
||||
{ label: 'Targo', value: 'targo' },
|
||||
{ label: 'Solucom', value: 'solucom' },
|
||||
];
|
||||
const company_options: ReportOptions[] = [
|
||||
{ label: 'Targo', value: 'targo' },
|
||||
{ label: 'Solucom', value: 'solucom' },
|
||||
];
|
||||
|
||||
const type_options: ReportOptions[] = [
|
||||
{ label: 'timesheet_approvals.print_report.shifts', value: 'shifts' },
|
||||
{ label: 'timesheet_approvals.print_report.expenses', value: 'expenses' },
|
||||
{ label: 'shared.shift_type.holiday', value: 'holiday' },
|
||||
{ label: 'shared.shift_type.vacation', value: 'vacation' },
|
||||
];
|
||||
const type_options: ReportOptions[] = [
|
||||
{ label: 'timesheet_approvals.print_report.shifts', value: 'shifts' },
|
||||
{ label: 'timesheet_approvals.print_report.expenses', value: 'expenses' },
|
||||
{ label: 'shared.shift_type.holiday', value: 'holiday' },
|
||||
{ label: 'shared.shift_type.vacation', value: 'vacation' },
|
||||
];
|
||||
|
||||
const is_download_button_enable = computed(() =>
|
||||
company_options.map(option => option.value).some(option => selected_report_filters.value.includes(option)) &&
|
||||
type_options.map(option => option.value).some(option => selected_report_filters.value.includes(option))
|
||||
);
|
||||
const is_download_button_enable = computed(() =>
|
||||
company_options.map(option => option.value).some(option => selected_report_filters.value.includes(option)) &&
|
||||
type_options.map(option => option.value).some(option => selected_report_filters.value.includes(option))
|
||||
);
|
||||
|
||||
const onClickedDownload = async () => {
|
||||
try {
|
||||
const data = await timesheet_store.getPayPeriodReport(report_filter_options.value);
|
||||
const onClickedDownload = async () => {
|
||||
try {
|
||||
const data = await timesheet_store.getPayPeriodReport(report_filter_options.value);
|
||||
|
||||
const companies = Object.entries(report_filter_options.value)
|
||||
.filter(([key, value]) => value && (key === 'targo' || key === 'solucom')).map(([key]) => key).join('-');
|
||||
const companies = Object.entries(report_filter_options.value)
|
||||
.filter(([key, value]) => value && (key === 'targo' || key === 'solucom')).map(([key]) => key).join('-');
|
||||
|
||||
const types = Object.entries(report_filter_options.value)
|
||||
.filter(([key, value]) => value && ['shifts', 'expenses', 'holiday', 'vacation'].includes(key)).map(([key]) => key).join('-');
|
||||
const types = Object.entries(report_filter_options.value)
|
||||
.filter(([key, value]) => value && ['shifts', 'expenses', 'holiday', 'vacation'].includes(key)).map(([key]) => key).join('-');
|
||||
|
||||
const file_name = `Desjardins_${companies}_${types}_${new Date().toISOString().split('T')[0]}.csv`;
|
||||
const file_name = `Desjardins_${companies}_${types}_${new Date().toISOString().split('T')[0]}.csv`;
|
||||
|
||||
const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
|
||||
link.href = url;
|
||||
link.setAttribute('download', file_name);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error(`An error occured during the CSV download: `, error)
|
||||
link.href = url;
|
||||
link.setAttribute('download', file_name);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error(`An error occured during the CSV download: `, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(selected_report_filters, (new_values) => {
|
||||
Object.keys(report_filter_options.value).forEach(key => {
|
||||
const typed_key = key as keyof TimesheetApprovalCSVReportFilters;
|
||||
report_filter_options.value[typed_key] = new_values.includes(key as keyof TimesheetApprovalCSVReportFilters);
|
||||
watch(selected_report_filters, (new_values) => {
|
||||
Object.keys(report_filter_options.value).forEach(key => {
|
||||
const typed_key = key as keyof TimesheetApprovalCSVReportFilters;
|
||||
report_filter_options.value[typed_key] = new_values.includes(key as keyof TimesheetApprovalCSVReportFilters);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog v-model="timesheet_store.is_report_dialog_open">
|
||||
<div class="bg-secondary full-width shadow-24 rounded-10 column">
|
||||
<div class="shadow-1 bg-primary text-accent text-weight-bold text-center text-uppercase">
|
||||
<span> {{ $t('timesheet_approvals.print_report.title') }}</span>
|
||||
<div
|
||||
class="column bg-secondary shadow-24 rounded-10"
|
||||
:style="$q.dark.isActive ? 'border: 2px solid var(--q-accent)' : ''"
|
||||
>
|
||||
<!-- main header -->
|
||||
<div class="col-auto bg-primary text-accent text-weight-bolder text-center text-uppercase text-h6 q-py-xs z-top">
|
||||
{{ $t('timesheet_approvals.print_report.title') }}
|
||||
</div>
|
||||
<div class="row q-py-md q-px-lg">
|
||||
<div class="col-auto full-width shadow-1 row bg-dark q-py-xs q-px-lg rounded-10">
|
||||
<span class="col q-px-sm q-pt-xs text-weight-bolder text-accent text-uppercase col-3">
|
||||
{{ $t('timesheet_approvals.print_report.company') }}
|
||||
</span>
|
||||
<div class="row bordered-primary col-auto full-width">
|
||||
<div
|
||||
v-for="company, index in company_options"
|
||||
:key="index"
|
||||
class="q-pa-xs col-6"
|
||||
>
|
||||
<q-checkbox
|
||||
v-model="selected_report_filters"
|
||||
left-label
|
||||
color="white"
|
||||
class="q-px-md shadow-4 rounded-25 full-width"
|
||||
dense
|
||||
:class="selected_report_filters.includes(company.value) ? 'bg-accent text-white' : 'bg-dark'"
|
||||
:label="$t(company.label)"
|
||||
:val="company.value"
|
||||
checked-icon="download"
|
||||
unchecked-icon="highlight_off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row q-py-md">
|
||||
<div class="col-auto full-width shadow-1 row bg-dark q-px-lg rounded-10 q-pb-sm">
|
||||
<span class="col q-px-sm q-pt-xs text-weight-bolder text-accent text-uppercase col-3">
|
||||
{{ $t('timesheet_approvals.print_report.options') }}
|
||||
</span>
|
||||
<div class=" row bordered-primary col-auto full-width">
|
||||
<div
|
||||
v-for="type, index in type_options"
|
||||
:key="index"
|
||||
class="q-pa-xs col-6"
|
||||
>
|
||||
<q-checkbox
|
||||
v-model="selected_report_filters"
|
||||
left-label
|
||||
color="white"
|
||||
class="q-px-md shadow-4 rounded-25 full-width"
|
||||
dense
|
||||
:class="selected_report_filters.includes(type.value) ? 'bg-accent text-white' : 'bg-dark'"
|
||||
:label="$t(type.label)"
|
||||
:val="type.value"
|
||||
checked-icon="download"
|
||||
unchecked-icon="highlight_off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<q-btn
|
||||
:disable="!is_download_button_enable"
|
||||
square
|
||||
size="md"
|
||||
icon="download"
|
||||
:color="is_download_button_enable ? 'accent' : 'grey-5'"
|
||||
:label="$t('shared.label.download')"
|
||||
@click="onClickedDownload()"
|
||||
|
||||
<!-- info blurb -->
|
||||
<div class="col-auto row flex-center full-width bg-dark shadow-2">
|
||||
<q-icon
|
||||
name="info_outline"
|
||||
size="sm"
|
||||
class="col-auto q-mr-xs"
|
||||
/>
|
||||
|
||||
<span class="col-auto text-weight-light q-mr-sm">
|
||||
{{ $t('timesheet_approvals.print_report.description') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- company header -->
|
||||
<span class="col-auto q-px-sm q-pt-md text-weight-medium text-accent text-uppercase">
|
||||
{{ $t('timesheet_approvals.print_report.company') }}
|
||||
</span>
|
||||
|
||||
<!-- company options -->
|
||||
<div class="col row text-uppercase full-width q-px-md">
|
||||
<div
|
||||
v-for="company, index in company_options"
|
||||
:key="index"
|
||||
class="q-pa-xs col-6"
|
||||
>
|
||||
<q-checkbox
|
||||
v-model="selected_report_filters"
|
||||
left-label
|
||||
color="white"
|
||||
dense
|
||||
:label="$t(company.label)"
|
||||
:val="company.value"
|
||||
checked-icon="download"
|
||||
unchecked-icon="highlight_off"
|
||||
class="q-px-md q-py-xs shadow-4 rounded-25 full-width"
|
||||
:class="selected_report_filters.includes(company.value) ? 'bg-accent text-white text-bold' : 'bg-dark'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- shift type header -->
|
||||
<span class="col-auto q-px-sm q-pt-md text-weight-medium text-uppercase text-accent">
|
||||
{{ $t('timesheet_approvals.print_report.options') }}
|
||||
</span>
|
||||
|
||||
<!-- shift type options -->
|
||||
<div class="col row text-uppercase full-width q-px-md q-pb-md">
|
||||
<div
|
||||
v-for="type, index in type_options"
|
||||
:key="index"
|
||||
class="q-pa-xs col-6"
|
||||
>
|
||||
<q-checkbox
|
||||
v-model="selected_report_filters"
|
||||
left-label
|
||||
color="white"
|
||||
dense
|
||||
:val="type.value"
|
||||
checked-icon="download"
|
||||
unchecked-icon="highlight_off"
|
||||
:label="$t(type.label)"
|
||||
class="q-px-md q-py-xs shadow-4 rounded-25 full-width"
|
||||
:class="selected_report_filters.includes(type.value) ? 'bg-accent text-white text-bold' : 'bg-white text-primary'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- download button -->
|
||||
<q-btn
|
||||
:disable="!is_download_button_enable"
|
||||
square
|
||||
icon="download"
|
||||
:color="is_download_button_enable ? 'accent' : 'grey-5'"
|
||||
:label="$t('shared.label.download')"
|
||||
class="col-auto q-py-sm shadow-up-2"
|
||||
@click="onClickedDownload()"
|
||||
/>
|
||||
</div>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<q-page class="column items-center justify-start">
|
||||
<q-page class="column flex-center bg-secondary">
|
||||
<AddModifyDialog />
|
||||
|
||||
<PageHeaderTemplate title="employee_list.page_header" />
|
||||
|
|
|
|||
|
|
@ -9,13 +9,27 @@
|
|||
import OverviewReport from 'src/modules/timesheet-approval/components/overview-report.vue';
|
||||
|
||||
import { date } from 'quasar';
|
||||
import { onMounted } from 'vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useTimesheetApprovalApi } from 'src/modules/timesheet-approval/composables/use-timesheet-approval-api';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
|
||||
const timesheet_approval_api = useTimesheetApprovalApi();
|
||||
const timesheet_store = useTimesheetStore();
|
||||
|
||||
const page_height = ref(0);
|
||||
const headerComponent = ref<HTMLElement | null>(null);
|
||||
|
||||
const table_max_height = computed(() => {
|
||||
const height = page_height.value - (headerComponent.value?.clientHeight ?? 0) - 20;
|
||||
console.log('offset height of header: ', headerComponent.value?.clientHeight);
|
||||
console.log('height calculated: ', height);
|
||||
return height;
|
||||
});
|
||||
|
||||
const tableStyleFunction = (offset: number, height: number) => {
|
||||
page_height.value = height - offset;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await timesheet_approval_api.getTimesheetOverviewsByDate(date.formatDate(new Date(), 'YYYY-MM-DD'));
|
||||
});
|
||||
|
|
@ -23,16 +37,9 @@
|
|||
|
||||
<template>
|
||||
<q-page
|
||||
padding
|
||||
class="column q-pa-md bg-secondary"
|
||||
class="bg-secondary"
|
||||
:style-fn="tableStyleFunction"
|
||||
>
|
||||
<PageHeaderTemplate
|
||||
title="timesheet_approvals.page_title"
|
||||
:start-date="timesheet_store.pay_period?.period_start ?? ''"
|
||||
:end-date="timesheet_store.pay_period?.period_end ?? ''"
|
||||
class="col-auto"
|
||||
/>
|
||||
|
||||
<DetailsDialog
|
||||
:is-loading="timesheet_store.is_loading"
|
||||
:employee-overview="timesheet_store.current_pay_period_overview"
|
||||
|
|
@ -41,6 +48,24 @@
|
|||
|
||||
<OverviewReport />
|
||||
|
||||
<OverviewList class="col" />
|
||||
<div
|
||||
class="column items-center scroll q-pa-sm"
|
||||
style="min-height: inherit;"
|
||||
>
|
||||
<div
|
||||
ref="headerComponent"
|
||||
class="col-auto"
|
||||
>
|
||||
<PageHeaderTemplate
|
||||
title="timesheet_approvals.page_title"
|
||||
:start-date="timesheet_store.pay_period?.period_start ?? ''"
|
||||
:end-date="timesheet_store.pay_period?.period_end ?? ''"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<OverviewList :max-height="table_max_height" />
|
||||
</div>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
export enum RouteNames {
|
||||
LOGIN = 'login',
|
||||
LOGIN_SUCCESS = 'login-success',
|
||||
DASHBOARD = 'dashboard',
|
||||
TIMESHEET_APPROVALS = 'timesheets_approval',
|
||||
EMPLOYEE_LIST = 'employee_list',
|
||||
EMPLOYEE_MANAGEMENT = 'employee_management',
|
||||
PROFILE = 'personal_profile',
|
||||
TIMESHEET = 'timesheets',
|
||||
DASHBOARD = '/',
|
||||
TIMESHEET_APPROVALS = 'timesheet-approvals',
|
||||
EMPLOYEE_LIST = 'employees',
|
||||
PROFILE = 'profile',
|
||||
TIMESHEET = 'timesheet',
|
||||
HELP = 'help',
|
||||
|
||||
ERROR = 'error',
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const routes: RouteRecordRaw[] = [
|
|||
meta: { required_module: ModuleNames.TIMESHEETS },
|
||||
},
|
||||
{
|
||||
path: 'user/profile',
|
||||
path: 'profile',
|
||||
name: RouteNames.PROFILE,
|
||||
component: () => import('src/pages/profile-page.vue'),
|
||||
meta: { required_module: ModuleNames.PERSONAL_PROFILE },
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { computed, ref } from 'vue';
|
|||
import { LocalStorage, useQuasar, Dark } from 'quasar';
|
||||
import { Preferences } from 'src/modules/profile/models/preferences.models';
|
||||
import { ProfileService } from 'src/modules/profile/services/profile-service';
|
||||
import { RouteNames } from 'src/router/router-constants';
|
||||
|
||||
|
||||
export const useUiStore = defineStore('ui', () => {
|
||||
|
|
@ -13,6 +14,7 @@ export const useUiStore = defineStore('ui', () => {
|
|||
const focus_next_component = ref(false);
|
||||
const is_mobile_mode = computed(() => q.screen.lt.md);
|
||||
const user_preferences = ref<Preferences>(new Preferences);
|
||||
const current_page = ref<RouteNames>(RouteNames.DASHBOARD);
|
||||
|
||||
|
||||
const toggleRightDrawer = () => {
|
||||
|
|
@ -69,6 +71,7 @@ export const useUiStore = defineStore('ui', () => {
|
|||
}
|
||||
|
||||
return {
|
||||
current_page,
|
||||
is_mobile_mode,
|
||||
focus_next_component,
|
||||
is_left_drawer_open,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user