fix(many): refactor timesheet approval download menu, details window fixes, store refactor
- Timesheet Approval's download menu has had its UI overhauled and the script has been streamlined to better match backend structure and logic - Details window in timesheet approval has a few bug and oversight fixes. - Refactored UI store to work with camelCase instead of snake_case
This commit is contained in:
parent
9213a42d6b
commit
e37ec79827
|
|
@ -401,10 +401,14 @@ export default {
|
|||
title: "Download options",
|
||||
description: "Choose what to include in the report",
|
||||
company: "companies",
|
||||
targo: "Targo",
|
||||
solucom: "Solucom",
|
||||
type: "type",
|
||||
shifts: "shifts",
|
||||
expenses: "expenses",
|
||||
options: "options",
|
||||
download_failed: "download failed",
|
||||
download_failed_caption: "an unexpected error occured",
|
||||
},
|
||||
table: {
|
||||
full_name: "full name",
|
||||
|
|
|
|||
|
|
@ -401,10 +401,14 @@ export default {
|
|||
title: "options de téléchargement",
|
||||
description: "Choisissez ce qui sera inclu dans le rapport",
|
||||
company: "compagnies",
|
||||
targo: "Targo",
|
||||
solucom: "Solucom",
|
||||
type: "types de données",
|
||||
shifts: "quarts de travail",
|
||||
expenses: "dépenses",
|
||||
options: "options",
|
||||
download_failed: "téléchargement échoué",
|
||||
download_failed_caption: "une erreur inconnue est survenue",
|
||||
},
|
||||
table: {
|
||||
full_name: "nom complet",
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ import { useAuthApi } from 'src/modules/auth/composables/use-auth-api';
|
|||
]
|
||||
|
||||
const q = useQuasar();
|
||||
const auth_store = useAuthStore();
|
||||
const authStore = useAuthStore();
|
||||
const authApi = useAuthApi();
|
||||
const ui_store = useUiStore();
|
||||
const uiStore = useUiStore();
|
||||
const router = useRouter();
|
||||
const is_mini = ref(true);
|
||||
const isMini = ref(true);
|
||||
|
||||
const onClickDrawerPage = (page_name: RouteNames) => {
|
||||
is_mini.value = true;
|
||||
isMini.value = true;
|
||||
|
||||
router.push({ name: page_name }).catch(error => {
|
||||
console.error('failed to reach page: ', error);
|
||||
|
|
@ -41,21 +41,21 @@ import { useAuthApi } from 'src/modules/auth/composables/use-auth-api';
|
|||
|
||||
onMounted(() => {
|
||||
if (q.platform.is.mobile) {
|
||||
ui_store.is_left_drawer_open = false;
|
||||
uiStore.isLeftDrawerOpen = false;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-drawer
|
||||
v-model="ui_store.is_left_drawer_open"
|
||||
v-model="uiStore.isLeftDrawerOpen"
|
||||
:persistent="!$q.platform.is.mobile"
|
||||
mini-to-overlay
|
||||
elevated
|
||||
side="left"
|
||||
:mini="is_mini"
|
||||
@mouseenter="is_mini = false"
|
||||
@mouseleave="is_mini = true"
|
||||
:mini="isMini"
|
||||
@mouseenter="isMini = false"
|
||||
@mouseleave="isMini = true"
|
||||
class="bg-dark z-max"
|
||||
>
|
||||
<q-scroll-area class="column fit">
|
||||
|
|
@ -66,7 +66,7 @@ import { useAuthApi } from 'src/modules/auth/composables/use-auth-api';
|
|||
@click="onClickDrawerPage(button.route)"
|
||||
>
|
||||
<div
|
||||
v-if="button.required_module ? auth_store.user?.user_module_access.includes(button.required_module) : true"
|
||||
v-if="button.required_module ? authStore.user?.user_module_access.includes(button.required_module) : true"
|
||||
class="row items-center full-width q-py-sm cursor-pointer"
|
||||
:class="$router.currentRoute.value.name === button.route ? ($q.dark.isActive ? 'bg-green-10' : 'bg-green-2') : ''"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -15,16 +15,16 @@
|
|||
|
||||
const ui_store = useUiStore();
|
||||
const auth_store = useAuthStore();
|
||||
const user_preferences = ref(ui_store.user_preferences);
|
||||
const userPreferences = ref(ui_store.userPreferences);
|
||||
|
||||
onMounted(async () => {
|
||||
if (ui_store.user_preferences.id === -1) {
|
||||
if (ui_store.userPreferences.id === -1) {
|
||||
await ui_store.getUserPreferences();
|
||||
}
|
||||
});
|
||||
|
||||
watch(user_preferences, async () => {
|
||||
if (ui_store.user_preferences.id !== -1) {
|
||||
watch(userPreferences, async () => {
|
||||
if (ui_store.userPreferences.id !== -1) {
|
||||
await ui_store.updateUserPreferences();
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,13 +82,13 @@
|
|||
:filter="filters"
|
||||
:filter-method="filterEmployeeRows"
|
||||
class="bg-transparent no-shadow sticky-header-table full-width q-pt-lg"
|
||||
:style="employee_store.employee_list.length > 0 ? `max-height: ${maxHeight - (ui_store.user_preferences.is_employee_list_grid ? 0 : 20)}px;` : ''"
|
||||
:style="employee_store.employee_list.length > 0 ? `max-height: ${maxHeight - (ui_store.userPreferences.is_employee_list_grid ? 0 : 20)}px;` : ''"
|
||||
:table-class="$q.dark.isActive ? 'q-py-none q-mx-md rounded-10 bg-dark shadow-10 hide-scrollbar' : 'q-py-none q-mx-md rounded-10 bg-white shadow-10 hide-scrollbar'"
|
||||
color="accent"
|
||||
separator="none"
|
||||
table-header-class="text-accent text-uppercase"
|
||||
card-container-class="justify-center"
|
||||
:grid="ui_store.user_preferences.is_employee_list_grid"
|
||||
:grid="ui_store.userPreferences.is_employee_list_grid"
|
||||
:loading="employee_store.is_loading"
|
||||
:no-data-label="$t('shared.error.no_data_found')"
|
||||
:no-results-label="$t('shared.error.no_search_results')"
|
||||
|
|
@ -126,7 +126,7 @@
|
|||
<q-space />
|
||||
|
||||
<q-btn-toggle
|
||||
v-model="ui_store.user_preferences.is_employee_list_grid"
|
||||
v-model="ui_store.userPreferences.is_employee_list_grid"
|
||||
push
|
||||
rounded
|
||||
color="white"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
<div
|
||||
class="col-auto justify-center content-center q-mb-sm q-pa-md rounded-5"
|
||||
:class="ui_store.is_mobile_mode ? 'column' : 'row'"
|
||||
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||
style="border: 1px solid var(--q-accent);"
|
||||
>
|
||||
<div
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
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"
|
||||
@click="ui_store.userPreferences.is_dark_mode = mode.value"
|
||||
>
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
const ui_store = useUiStore();
|
||||
|
||||
const setDisplayLanguage = (locale: MessageLanguages) => {
|
||||
if (ui_store.user_preferences !== undefined) {
|
||||
ui_store.user_preferences.display_language = locale;
|
||||
if (ui_store.userPreferences !== undefined) {
|
||||
ui_store.userPreferences.display_language = locale;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
<q-list
|
||||
dense
|
||||
class="full-width"
|
||||
:class="ui_store.is_mobile_mode ? 'column' : 'row'"
|
||||
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||
>
|
||||
<q-item
|
||||
v-for="locale in $i18n.availableLocales"
|
||||
|
|
|
|||
|
|
@ -57,10 +57,12 @@
|
|||
datasets: [{
|
||||
data: shift_type_data,
|
||||
backgroundColor: [
|
||||
colors.getPaletteColor('accent'), // Regular
|
||||
colors.getPaletteColor('green-10'), // Evening
|
||||
colors.getPaletteColor('warning'), // Emergency
|
||||
colors.getPaletteColor('negative'), // Overtime
|
||||
colors.getPaletteColor('accent'), // Regular
|
||||
colors.getPaletteColor('green-10'), // Evening
|
||||
colors.getPaletteColor('warning'), // Emergency
|
||||
colors.getPaletteColor('negative'), // Overtime
|
||||
colors.getPaletteColor('purple-5'), // Holiday
|
||||
colors.getPaletteColor('deep-orange-5') // Vacation
|
||||
]
|
||||
}],
|
||||
}"
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
}
|
||||
|
||||
const onShowDetailsDialog = () => {
|
||||
isDialogOpen.value = true;
|
||||
isDialogOpen.value = true;
|
||||
expenseStore.is_showing_create_form = false;
|
||||
}
|
||||
</script>
|
||||
|
|
@ -158,7 +158,7 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto column">
|
||||
<div class="col-auto column no-wrap">
|
||||
<q-separator
|
||||
spaced
|
||||
size="4px"
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
:class="$q.platform.is.mobile ? 'q-mb-md' : ''"
|
||||
>
|
||||
<q-btn-toggle
|
||||
v-model="uiStore.user_preferences.is_timesheet_approval_grid"
|
||||
v-model="uiStore.userPreferences.is_timesheet_approval_grid"
|
||||
push
|
||||
rounded
|
||||
color="white"
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
// ========== computed ========================================
|
||||
|
||||
const isGridMode = computed(() => q.platform.is.mobile ? true :
|
||||
uiStore.user_preferences.is_timesheet_approval_grid
|
||||
uiStore.userPreferences.is_timesheet_approval_grid
|
||||
);
|
||||
|
||||
const overviewRows = computed(() =>
|
||||
|
|
@ -134,7 +134,7 @@
|
|||
:filter-method="filterEmployeeRows"
|
||||
:rows-per-page-options="[0]"
|
||||
class="bg-transparent"
|
||||
:class="uiStore.user_preferences.is_timesheet_approval_grid ? '' : 'sticky-header-table sticky-first-column-table sticky-last-column-table no-shadow q-pb-sm'"
|
||||
:class="uiStore.userPreferences.is_timesheet_approval_grid ? '' : 'sticky-header-table sticky-first-column-table sticky-last-column-table no-shadow q-pb-sm'"
|
||||
card-container-class="justify-center"
|
||||
table-class="q-pa-none q-mx-md rounded-10 bg-dark shadow-15"
|
||||
:no-data-label="$t('shared.error.no_data_found')"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
defineProps<{
|
||||
isSelected: boolean;
|
||||
label: string;
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
'clickOption': [void];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<transition
|
||||
enter-active-class="animated pulse faster"
|
||||
mode="out-in"
|
||||
>
|
||||
<div
|
||||
:key="isSelected ? '1' : '0'"
|
||||
class="row items-center q-pa-xs cursor-pointer rounded-25 shadow-4 relative-position non-selectable"
|
||||
:class="isSelected ? 'bg-accent text-white text-bold' : 'text-weight-medium bg-dark'"
|
||||
@click.stop="$emit('clickOption')"
|
||||
>
|
||||
<span class="col text-uppercase q-pl-md">
|
||||
{{ $t(label) }}
|
||||
</span>
|
||||
|
||||
<q-icon
|
||||
:name="isSelected ? 'las la-check-circle' : 'las la-circle'"
|
||||
:color="isSelected ? 'white' : ''"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
|
@ -1,168 +1,166 @@
|
|||
<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 OverviewReportOption from 'src/modules/timesheet-approval/components/overview-report-option.vue';
|
||||
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const report_filter_options = ref<TimesheetApprovalCSVReportFilters>(new TimesheetApprovalCSVReportFilters);
|
||||
const selected_company = ref<'targo' | 'solucom'>('targo');
|
||||
import { Notify } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { useTimesheetApprovalApi } from 'src/modules/timesheet-approval/composables/use-timesheet-approval-api';
|
||||
import { companyOptions, CSVReportFilters, typeOptions } from 'src/modules/timesheet-approval/models/timesheet-approval-csv-report.models';
|
||||
import type { ShiftType } from 'src/modules/timesheets/models/shift.models';
|
||||
import type { CompanyNames } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
|
||||
const selected_report_filters = ref<(keyof TimesheetApprovalCSVReportFilters)[]>(
|
||||
Object.entries(report_filter_options.value).filter(([_key, value]) => value).map(([key]) => key as keyof TimesheetApprovalCSVReportFilters)
|
||||
);
|
||||
// ========== STATE ========================================
|
||||
|
||||
interface ReportOptions {
|
||||
label: string;
|
||||
value: keyof TimesheetApprovalCSVReportFilters;
|
||||
};
|
||||
const { t } = useI18n();
|
||||
const timesheetStore = useTimesheetStore();
|
||||
const timesheetApprovalApi = useTimesheetApprovalApi();
|
||||
const reportFilters = ref<CSVReportFilters>(new CSVReportFilters);
|
||||
|
||||
const company_options: ReportOptions[] = [
|
||||
{ label: 'Targo', value: 'targo' },
|
||||
{ label: 'Solucom', value: 'solucom' },
|
||||
];
|
||||
// ========== COMPUTED ========================================
|
||||
|
||||
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 isDownloadButtonEnabled = computed(() => reportFilters.value.shiftTypes.length > 0);
|
||||
|
||||
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))
|
||||
);
|
||||
// ========== METHODS ========================================
|
||||
|
||||
const onClickedDownload = async () => {
|
||||
try {
|
||||
const data = await timesheet_store.getPayPeriodReport(report_filter_options.value);
|
||||
const onClickedDownload = async () => {
|
||||
const response = await timesheetApprovalApi.getTimesheetApprovalCSVReport(reportFilters.value);
|
||||
console.log(response);
|
||||
if (!response) {
|
||||
Notify.create({
|
||||
message: t('timesheet_approvals.print_report.download_failed'),
|
||||
caption: t('timesheet_approvals.print_report.download_failed_caption'),
|
||||
color: 'negative',
|
||||
textColor: 'white',
|
||||
icon: 'file_download_off',
|
||||
iconSize: 'lg',
|
||||
classes: 'text-uppercase text-center'
|
||||
});
|
||||
|
||||
const companies = Object.entries(report_filter_options.value)
|
||||
.filter(([key, value]) => value && (key === 'targo' || key === 'solucom')).map(([key]) => key).join('-');
|
||||
return;
|
||||
}
|
||||
|
||||
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 blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const url = window.URL.createObjectURL(response.file);
|
||||
const link = document.createElement('a');
|
||||
|
||||
link.href = url;
|
||||
link.setAttribute('download', file_name);
|
||||
link.setAttribute('download', response.fileName);
|
||||
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)
|
||||
|
||||
timesheetStore.is_report_dialog_open = false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
const onClickCompanyOption = (companyName: CompanyNames) => {
|
||||
if (reportFilters.value.companyName === companyName) return;
|
||||
reportFilters.value.companyName = companyName;
|
||||
}
|
||||
|
||||
watch(selected_company, (company) => {
|
||||
report_filter_options.value.targo = company === 'targo';
|
||||
report_filter_options.value.solucom = company === 'solucom';
|
||||
});
|
||||
const onClickShiftOption = (clickedOption: ShiftType) => {
|
||||
const index = reportFilters.value.shiftTypes.findIndex(option => option === clickedOption);
|
||||
|
||||
if (index === -1)
|
||||
reportFilters.value.shiftTypes.push(clickedOption);
|
||||
else
|
||||
reportFilters.value.shiftTypes.splice(index, 1);
|
||||
}
|
||||
|
||||
const onClickExpenseOption = () => {
|
||||
reportFilters.value.includeExpenses = !reportFilters.value.includeExpenses;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog v-model="timesheet_store.is_report_dialog_open">
|
||||
<q-dialog
|
||||
v-model="timesheetStore.is_report_dialog_open"
|
||||
@hide="reportFilters = new CSVReportFilters;"
|
||||
backdrop-filter="blur(4px)"
|
||||
>
|
||||
<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="col-auto column bg-primary text-center text-uppercase">
|
||||
<span class="text-white text-weight-bolder q-py-sm text-h5">
|
||||
{{ $t('timesheet_approvals.print_report.title') }}
|
||||
</span>
|
||||
|
||||
<!-- 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">
|
||||
<span class="col-auto q-py-xs bg-dark full-width shadow-4">
|
||||
{{ $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>
|
||||
<!-- groups -->
|
||||
<div class="col column full-width q-px-md">
|
||||
<!-- company header -->
|
||||
<span class="col-auto q-px-sm q-pt-md text-bold 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-radio
|
||||
v-model="selected_company"
|
||||
left-label
|
||||
color="white"
|
||||
dense
|
||||
:label="company.label"
|
||||
:val="company.value"
|
||||
checked-icon="radio_button_checked"
|
||||
unchecked-icon="radio_button_unchecked"
|
||||
class="q-px-md q-py-xs shadow-4 rounded-25 full-width"
|
||||
:class="selected_company.includes(company.value) ? 'bg-accent text-white text-bold' : 'bg-dark'"
|
||||
/>
|
||||
<!-- company options -->
|
||||
<div class="row q-pb-sm">
|
||||
<div
|
||||
v-for="company, companyIndex in companyOptions"
|
||||
:key="companyIndex"
|
||||
class="col-6 q-pa-sm"
|
||||
>
|
||||
<OverviewReportOption
|
||||
:is-selected="reportFilters.companyName === company.value"
|
||||
:label="company.label"
|
||||
@click-option="onClickCompanyOption(company.value)"
|
||||
/>
|
||||
</div>
|
||||
</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>
|
||||
<!-- data type header -->
|
||||
<span class="col-auto q-px-sm q-pt-md text-bold text-uppercase">
|
||||
{{ $t('timesheet_approvals.print_report.type') }}
|
||||
</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="check_box"
|
||||
unchecked-icon="check_box_outline_blank"
|
||||
: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'"
|
||||
/>
|
||||
<!-- data type options -->
|
||||
<div class="row">
|
||||
<!-- shift types -->
|
||||
<div
|
||||
v-for="shiftType, typeIndex in typeOptions"
|
||||
:key="typeIndex"
|
||||
class="col-6 q-pa-sm"
|
||||
>
|
||||
<OverviewReportOption
|
||||
:is-selected="reportFilters.shiftTypes.includes(shiftType.value)"
|
||||
:label="shiftType.label"
|
||||
@click-option="onClickShiftOption(shiftType.value)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- expenses -->
|
||||
<div class="col-6 q-pa-sm">
|
||||
<OverviewReportOption
|
||||
:is-selected="reportFilters.includeExpenses"
|
||||
:label="'timesheet_approvals.print_report.expenses'"
|
||||
@click-option="onClickExpenseOption"
|
||||
/>
|
||||
</div>
|
||||
</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 class="col-auto row justify-end q-px-md q-pb-md q-pt-xl">
|
||||
<q-btn
|
||||
push
|
||||
:disable="!isDownloadButtonEnabled"
|
||||
icon="download"
|
||||
:color="isDownloadButtonEnabled ? 'accent' : 'grey-5'"
|
||||
:label="$t('shared.label.download')"
|
||||
class="col-auto q-py-sm q-px-xl"
|
||||
@click="onClickedDownload"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,58 +1,72 @@
|
|||
import { useTimesheetStore } from "src/stores/timesheet-store";
|
||||
import type { TimesheetApprovalCSVReportFilters } from "src/modules/timesheet-approval/models/timesheet-approval-csv-report.models";
|
||||
import type { CSVReportFilters } from "src/modules/timesheet-approval/models/timesheet-approval-csv-report.models";
|
||||
import { fromEnToFrShiftType } from "src/utils/translator";
|
||||
|
||||
export const useTimesheetApprovalApi = () => {
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const timesheetStore = useTimesheetStore();
|
||||
const DATE_REGEX = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
|
||||
|
||||
const getTimesheetOverviews = async (date?: string) => {
|
||||
timesheet_store.is_loading = true;
|
||||
timesheetStore.is_loading = true;
|
||||
|
||||
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date);
|
||||
if (success) await timesheet_store.getTimesheetOverviews();
|
||||
const success = await timesheetStore.getPayPeriodByDateOrYearAndNumber(date);
|
||||
if (success) await timesheetStore.getTimesheetOverviews();
|
||||
|
||||
timesheet_store.is_loading = false;
|
||||
timesheetStore.is_loading = false;
|
||||
}
|
||||
|
||||
const getTimesheetOverviewsByDate = async (date: string) => {
|
||||
const valid_date = DATE_REGEX.test(date);
|
||||
timesheet_store.is_loading = true;
|
||||
timesheetStore.is_loading = true;
|
||||
|
||||
if (valid_date) {
|
||||
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date);
|
||||
if (success) await timesheet_store.getTimesheetOverviews();
|
||||
const success = await timesheetStore.getPayPeriodByDateOrYearAndNumber(date);
|
||||
if (success) await timesheetStore.getTimesheetOverviews();
|
||||
}
|
||||
|
||||
timesheet_store.is_loading = false;
|
||||
timesheetStore.is_loading = false;
|
||||
};
|
||||
|
||||
const toggleTimesheetsApprovalByEmployeeEmail = async (email: string, approval_status: boolean) => {
|
||||
timesheet_store.is_loading = true;
|
||||
timesheetStore.is_loading = true;
|
||||
|
||||
const success = await timesheet_store.getTimesheetsByOptionalEmployeeEmail(email);
|
||||
const success = await timesheetStore.getTimesheetsByOptionalEmployeeEmail(email);
|
||||
if (success) {
|
||||
const approval_success = await timesheet_store.toggleTimesheetsApprovalByEmployeeEmail(email, approval_status);
|
||||
const overview = timesheet_store.pay_period_overviews.find(overview => overview.email === email);
|
||||
const approval_success = await timesheetStore.toggleTimesheetsApprovalByEmployeeEmail(email, approval_status);
|
||||
const overview = timesheetStore.pay_period_overviews.find(overview => overview.email === email);
|
||||
|
||||
if (overview && approval_success) {
|
||||
overview.is_approved = approval_status;
|
||||
await timesheet_store.getTimesheetsByOptionalEmployeeEmail(email);
|
||||
await timesheetStore.getTimesheetsByOptionalEmployeeEmail(email);
|
||||
}
|
||||
}
|
||||
|
||||
timesheet_store.is_loading = false;
|
||||
timesheetStore.is_loading = false;
|
||||
};
|
||||
|
||||
const getTimesheetApprovalCSVReport = async (report_filter_company: boolean[], report_filter_type: boolean[]) => {
|
||||
if (timesheet_store.pay_period === undefined) return;
|
||||
const getTimesheetApprovalCSVReport = async (selectedFilters: CSVReportFilters): Promise<{file: Blob, fileName: string} | undefined> => {
|
||||
if (timesheetStore.pay_period === undefined) return;
|
||||
|
||||
const [targo, solucom] = report_filter_company;
|
||||
const [shifts, expenses, holiday, vacation] = report_filter_type;
|
||||
const options = {
|
||||
shifts, expenses, holiday, vacation, targo, solucom
|
||||
} as TimesheetApprovalCSVReportFilters;
|
||||
const success = await timesheetStore.getPayPeriodReport(selectedFilters);
|
||||
if (!success) return;
|
||||
|
||||
await timesheet_store.getPayPeriodReport(options);
|
||||
// const year = timesheetStore.pay_period?.pay_year;
|
||||
// const number = timesheetStore.pay_period?.pay_period_no;
|
||||
const startDate = timesheetStore.pay_period?.period_start;
|
||||
const endDate = timesheetStore.pay_period?.period_end;
|
||||
const content: string[] = [];
|
||||
|
||||
selectedFilters.shiftTypes.forEach(type => content.push(fromEnToFrShiftType(type)));
|
||||
content.push(selectedFilters.companyName);
|
||||
|
||||
if (selectedFilters.includeExpenses) content.push('Dépenses');
|
||||
|
||||
// const fileName = `${year}-${number}_${content.join('_')}.csv`;
|
||||
const fileName = `${startDate}_AU_${endDate}_${content.join('_')}.csv`;
|
||||
|
||||
if (!timesheetStore.payPeriodReport) return;
|
||||
|
||||
return {file: timesheetStore.payPeriodReport, fileName};
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,28 @@
|
|||
export class TimesheetApprovalCSVReportFilters {
|
||||
shifts: boolean;
|
||||
expenses: boolean;
|
||||
holiday: boolean;
|
||||
vacation: boolean;
|
||||
targo: boolean;
|
||||
solucom: boolean;
|
||||
import type { ShiftType } from "src/modules/timesheets/models/shift.models";
|
||||
import type { CompanyNames } from "src/modules/employee-list/models/employee-profile.models";
|
||||
|
||||
export class CSVReportFilters {
|
||||
companyName: CompanyNames;
|
||||
includeExpenses: boolean;
|
||||
shiftTypes: ShiftType[];
|
||||
|
||||
constructor() {
|
||||
this.shifts = true;
|
||||
this.expenses = true;
|
||||
this.holiday = true;
|
||||
this.vacation = true;
|
||||
this.targo = true;
|
||||
this.solucom = false;
|
||||
};
|
||||
}
|
||||
this.companyName = 'Targo';
|
||||
this.includeExpenses = false;
|
||||
this.shiftTypes = ['REGULAR', 'EVENING', 'EMERGENCY', 'SICK']
|
||||
}
|
||||
};
|
||||
|
||||
export const companyOptions: {label: string, value: CompanyNames}[] = [
|
||||
{ label: 'timesheet_approvals.print_report.targo', value: 'Targo' },
|
||||
{ label: 'timesheet_approvals.print_report.solucom', value: 'Solucom' },
|
||||
];
|
||||
|
||||
export const typeOptions: {label: string, value: ShiftType}[] = [
|
||||
{ label: 'shared.shift_type.regular', value: 'REGULAR' },
|
||||
{ label: 'shared.shift_type.evening', value: 'EVENING' },
|
||||
{ label: 'shared.shift_type.emergency', value: 'EMERGENCY' },
|
||||
{ label: 'shared.shift_type.sick', value: 'SICK' },
|
||||
{ label: 'shared.shift_type.holiday', value: 'HOLIDAY' },
|
||||
{ label: 'shared.shift_type.vacation', value: 'VACATION' },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { api } from "src/boot/axios";
|
||||
import type { BackendResponse } from "src/modules/shared/models/backend-response.models";
|
||||
import type { TimesheetApprovalCSVReportFilters } from "src/modules/timesheet-approval/models/timesheet-approval-csv-report.models";
|
||||
import type { CSVReportFilters } from "src/modules/timesheet-approval/models/timesheet-approval-csv-report.models";
|
||||
import type { PayPeriodOverviewResponse } from "src/modules/timesheet-approval/models/timesheet-overview.models";
|
||||
|
||||
export const timesheetApprovalService = {
|
||||
|
|
@ -9,13 +9,13 @@ export const timesheetApprovalService = {
|
|||
return response.data.data;
|
||||
},
|
||||
|
||||
getPayPeriodReportByYearAndPeriodNumber: async (year: number, period_number: number, filters?: TimesheetApprovalCSVReportFilters) => {
|
||||
const response = await api.get(`exports/csv/${year}/${period_number}`, { params: filters, responseType: 'arraybuffer' });
|
||||
return response;
|
||||
getPayPeriodReportByYearAndPeriodNumber: async (year: number, period_number: number, filters?: CSVReportFilters): Promise<Blob> => {
|
||||
const response = await api.post(`exports/csv/${year}/${period_number}`, filters, { responseType: 'blob' });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
updateTimesheetsApprovalStatus: async (email: string, timesheet_ids: number[], is_approved: boolean): Promise<BackendResponse<{shifts: number, expenses: number}>> => {
|
||||
const response = await api.patch<BackendResponse<{shifts: number, expenses: number}>>('pay-periods/pay-period-approval', { email, timesheet_ids, is_approved});
|
||||
updateTimesheetsApprovalStatus: async (email: string, timesheet_ids: number[], is_approved: boolean): Promise<BackendResponse<{ shifts: number, expenses: number }>> => {
|
||||
const response = await api.patch<BackendResponse<{ shifts: number, expenses: number }>>('pay-periods/pay-period-approval', { email, timesheet_ids, is_approved });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { useExpensesStore } from 'src/stores/expense-store';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
||||
|
|
@ -28,7 +27,6 @@
|
|||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const ui_store = useUiStore();
|
||||
const timesheetStore = useTimesheetStore();
|
||||
const expenseStore = useExpensesStore();
|
||||
const expensesApi = useExpensesApi();
|
||||
|
|
@ -168,7 +166,6 @@
|
|||
<template #selected-item="scope">
|
||||
<div
|
||||
class="row items-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
||||
:class="ui_store.is_mobile_mode ? 'full-height' : ''"
|
||||
:tabindex="scope.tabindex"
|
||||
>
|
||||
<q-icon
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
>
|
||||
import { computed, ref, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { useExpensesStore } from 'src/stores/expense-store';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
||||
|
|
@ -26,7 +25,6 @@
|
|||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const ui_store = useUiStore();
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const expenses_store = useExpensesStore();
|
||||
const auth_store = useAuthStore();
|
||||
|
|
@ -168,8 +166,7 @@
|
|||
|
||||
<template #selected-item="scope">
|
||||
<div
|
||||
class="row items-center text-weight-bold q-ma-none q-pa-none full-width"
|
||||
:class="ui_store.is_mobile_mode ? 'full-height' : ''"
|
||||
class="row items-center text-weight-bold q-ma-none q-pa-none fit"
|
||||
:tabindex="scope.tabindex"
|
||||
>
|
||||
<q-icon
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@
|
|||
'onTimeFieldBlur': [void];
|
||||
}>();
|
||||
|
||||
const ui_store = useUiStore();
|
||||
const uiStore = useUiStore();
|
||||
const shiftTypeSelected = ref(SHIFT_OPTIONS.find(option => option.value == shift.value.type));
|
||||
const select_ref = ref<QSelect | null>(null);
|
||||
const is_showing_comment_popup = ref(false);
|
||||
const error_message = ref('');
|
||||
const selectRef = ref<QSelect | null>(null);
|
||||
const isShowingCommentPopup = ref(false);
|
||||
const errorMessageRow = ref('');
|
||||
const isShowingPredefinedTime = ref(shift.value.type === 'HOLIDAY');
|
||||
const predefinedHoursString = ref('');
|
||||
const predefinedHoursBgColor = ref(`bg-${shiftTypeSelected.value?.icon_color ?? ''}`);
|
||||
|
|
@ -63,10 +63,10 @@
|
|||
const onTimeFieldBlur = (time_string: string) => {
|
||||
if (time_string.length < 1 || !time_string) {
|
||||
shift.value.has_error = true;
|
||||
error_message.value = 'timesheet.errors.SHIFT_TIME_REQUIRED';
|
||||
errorMessageRow.value = 'timesheet.errors.SHIFT_TIME_REQUIRED';
|
||||
} else {
|
||||
shift.value.has_error = false;
|
||||
error_message.value = '';
|
||||
errorMessageRow.value = '';
|
||||
emit('onTimeFieldBlur');
|
||||
}
|
||||
}
|
||||
|
|
@ -102,15 +102,15 @@
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (ui_store.focus_next_component) {
|
||||
select_ref.value?.focus();
|
||||
select_ref.value?.showPopup();
|
||||
if (uiStore.focusNextComponent) {
|
||||
selectRef.value?.focus();
|
||||
selectRef.value?.showPopup();
|
||||
shiftTypeSelected.value = undefined;
|
||||
ui_store.focus_next_component = false;
|
||||
uiStore.focusNextComponent = false;
|
||||
}
|
||||
|
||||
if (errorMessage)
|
||||
error_message.value = errorMessage;
|
||||
errorMessageRow.value = errorMessage;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -120,13 +120,13 @@
|
|||
<div class="col row items-center text-uppercase q-px-xs rounded-5">
|
||||
<!-- comment button -->
|
||||
<q-btn
|
||||
v-if="ui_store.is_mobile_mode && !dense"
|
||||
v-if="!dense"
|
||||
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
||||
:text-color="shift.comment ? ((shift.is_approved && isTimesheetApproved) ? 'white' : 'accent') : 'grey-5'"
|
||||
class="col-auto full-height q-mx-xs rounded-5 shadow-1"
|
||||
@click="is_showing_comment_popup = true"
|
||||
@click="isShowingCommentPopup = true"
|
||||
>
|
||||
<q-dialog v-model="is_showing_comment_popup">
|
||||
<q-dialog v-model="isShowingCommentPopup">
|
||||
<q-input
|
||||
color="white"
|
||||
v-model="shift.comment"
|
||||
|
|
@ -190,8 +190,7 @@
|
|||
>
|
||||
<template #selected-item="scope">
|
||||
<div
|
||||
class="row items-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
||||
:class="ui_store.is_mobile_mode ? 'full-height' : ''"
|
||||
class="row items-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis fit"
|
||||
:tabindex="scope.tabindex"
|
||||
>
|
||||
<q-icon
|
||||
|
|
@ -305,7 +304,7 @@
|
|||
no-error-icon
|
||||
hide-bottom-space
|
||||
:error="shift.has_error"
|
||||
:error-message="errorMessage || error_message !== '' ? $t(errorMessage ?? error_message) : ''"
|
||||
:error-message="errorMessage || errorMessageRow !== '' ? $t(errorMessage ?? errorMessageRow) : ''"
|
||||
:label-color="!shift.is_approved ? 'accent' : 'white'"
|
||||
class="rounded-5 bg-dark"
|
||||
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (!shift.is_approved && !isTimesheetApproved ? '' : 'cursor-not-allowed inset-shadow')"
|
||||
|
|
@ -338,7 +337,7 @@
|
|||
no-error-icon
|
||||
hide-bottom-space
|
||||
:error="shift.has_error"
|
||||
:error-message="errorMessage || error_message !== '' ? $t(errorMessage ?? error_message) : ''"
|
||||
:error-message="errorMessage || errorMessageRow !== '' ? $t(errorMessage ?? errorMessageRow) : ''"
|
||||
:label-color="!shift.is_approved ? 'accent' : '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;"
|
||||
|
|
|
|||
287
src/modules/timesheets/components/new-shift-list.vue
Normal file
287
src/modules/timesheets/components/new-shift-list.vue
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import ShiftListDay from 'src/modules/timesheets/components/shift-list-day.vue';
|
||||
import ShiftListDateWidget from 'src/modules/timesheets/components/shift-list-date-widget.vue';
|
||||
|
||||
import { date, useQuasar } from 'quasar';
|
||||
import { ref, computed, watch, onMounted, inject } from 'vue';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { Shift } from 'src/modules/timesheets/models/shift.models';
|
||||
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
||||
import type { TimesheetDay } from 'src/modules/timesheets/models/timesheet.models';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
// ========== constants ========================================
|
||||
|
||||
const CURRENT_DATE_STRING = new Date().toISOString().slice(0, 10);
|
||||
|
||||
// ========== state ========================================
|
||||
|
||||
const emit = defineEmits<{
|
||||
'onCurrentDayComponentFound': [component: HTMLElement | undefined];
|
||||
}>();
|
||||
|
||||
const q = useQuasar();
|
||||
const { extractDate } = date;
|
||||
const { locale } = useI18n();
|
||||
const uiStore = useUiStore();
|
||||
const timesheetApi = useTimesheetApi();
|
||||
const timesheetStore = useTimesheetStore();
|
||||
|
||||
const mobileAnimationDirection = ref('fadeInLeft');
|
||||
const currentDayComponent = ref<HTMLElement[] | null>(null);
|
||||
const currentDayComponentWatcher = ref(currentDayComponent);
|
||||
const employeeEmail = inject<string>('employeeEmail');
|
||||
|
||||
// ========== computed ========================================
|
||||
|
||||
const animationStyle = computed(() => q.platform.is.mobile ? mobileAnimationDirection.value : 'fadeInDown');
|
||||
|
||||
// ========== methods ========================================
|
||||
|
||||
// const timesheetRows = computed(() => timesheetStore.timesheets);
|
||||
|
||||
const addNewShift = (day_shifts: Shift[], date: string, timesheet_id: number) => {
|
||||
uiStore.focusNextComponent = true;
|
||||
const newShift = new Shift;
|
||||
newShift.date = date;
|
||||
newShift.timesheet_id = timesheet_id;
|
||||
day_shifts.push(newShift);
|
||||
};
|
||||
|
||||
const deleteUnsavedShift = (timesheet_index: number, day_index: number) => {
|
||||
if (timesheetStore.timesheets !== undefined) {
|
||||
const day = timesheetStore.timesheets[timesheet_index]!.days[day_index]!;
|
||||
const shifts_without_deleted_shift = day.shifts.filter(shift => shift.id !== 0);
|
||||
day.shifts = shifts_without_deleted_shift;
|
||||
}
|
||||
};
|
||||
|
||||
const getDayApproval = (day: TimesheetDay) => {
|
||||
if (day.shifts.length < 1) return false;
|
||||
return day.shifts.every(shift => shift.is_approved === true);
|
||||
};
|
||||
|
||||
const getMobileDayRef = (iso_date_string: string): string => {
|
||||
return iso_date_string === CURRENT_DATE_STRING ? 'currentDayComponent' : '';
|
||||
};
|
||||
|
||||
const getHolidayName = (date: string) => {
|
||||
const holiday = timesheetStore.federal_holidays.find(holiday => holiday.date === date);
|
||||
if (!holiday) return;
|
||||
|
||||
if (locale.value === 'fr-FR')
|
||||
return holiday.nameFr;
|
||||
|
||||
else if (locale.value === 'en-CA')
|
||||
return holiday.nameEn;
|
||||
};
|
||||
|
||||
const onClickApplyWeeklyPreset = async (timesheet_id: number) => {
|
||||
await timesheetApi.applyPreset(timesheet_id, undefined, undefined, employeeEmail);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await timesheetStore.getCurrentFederalHolidays();
|
||||
});
|
||||
|
||||
watch(currentDayComponentWatcher, () => {
|
||||
if (currentDayComponent.value && q.platform.is.mobile) {
|
||||
emit('onCurrentDayComponentFound', currentDayComponent.value[0])
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fit"
|
||||
:class="$q.platform.is.mobile ? 'column no-wrap q-pb-lg' : 'row'"
|
||||
>
|
||||
<div
|
||||
v-for="timesheet, timesheet_index of timesheetStore.timesheets"
|
||||
:key="timesheet.timesheet_id"
|
||||
class="no-wrap"
|
||||
:class="$q.platform.is.mobile ? 'col-auto column' : 'col column fit items-center'"
|
||||
>
|
||||
<transition
|
||||
appear
|
||||
enter-active-class="animated fadeInDown"
|
||||
leave-active-class="animated fadeOutUp"
|
||||
>
|
||||
<q-btn
|
||||
v-if="!$q.platform.is.mobile && timesheet.days.every(day => day.shifts.length < 1) && timesheetStore.has_timesheet_preset"
|
||||
:disable="!timesheet.days.every(day => day.shifts.length < 1)"
|
||||
flat
|
||||
dense
|
||||
:label="$t('timesheet.apply_preset_week')"
|
||||
class="col-auto text-uppercase text-weight-bold text-accent q-mx-lg q-py-none rounded-5"
|
||||
@click="onClickApplyWeeklyPreset(timesheet.timesheet_id)"
|
||||
>
|
||||
<q-icon
|
||||
name="las la-calendar-week"
|
||||
color="accent"
|
||||
size="md"
|
||||
/>
|
||||
</q-btn>
|
||||
</transition>
|
||||
|
||||
<transition-group
|
||||
appear
|
||||
:enter-active-class="`animated ${animationStyle}`"
|
||||
>
|
||||
<div
|
||||
v-for="day, day_index in timesheet.days"
|
||||
:key="day.date"
|
||||
:ref="getMobileDayRef(day.date)"
|
||||
class="col-auto row q-pa-sm full-width relative-position"
|
||||
:style="`animation-delay: ${day_index / 15}s;`"
|
||||
>
|
||||
<!-- optional label indicating which holiday if today is a holiday -->
|
||||
<span
|
||||
v-if="timesheetStore.federal_holidays.some(holiday => holiday.date === day.date)"
|
||||
class="absolute-top-left text-uppercase text-weight-bolder text-purple-5"
|
||||
style="transform: translate(25px, -7px);"
|
||||
>
|
||||
{{ getHolidayName(day.date) }}
|
||||
</span>
|
||||
|
||||
<!-- mobile version in portrait mode -->
|
||||
<div
|
||||
v-if="$q.platform.is.mobile && ($q.screen.width < $q.screen.height)"
|
||||
class="col-auto full-width q-px-md q-py-sm"
|
||||
>
|
||||
<q-card
|
||||
class="shadow-12"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent rounded-10' : 'bg-dark mobile-rounded-10'"
|
||||
>
|
||||
<q-card-section
|
||||
class="text-weight-bolder text-uppercase text-h6 q-py-sm text-center relative-position"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent text-white' : 'bg-primary text-white'"
|
||||
style="line-height: 1em;"
|
||||
>
|
||||
<span> {{ $d(extractDate(day.date, 'YYYY-MM-DD'), {
|
||||
weekday: 'long', day: 'numeric', month:
|
||||
'long'
|
||||
}) }}</span>
|
||||
|
||||
<q-icon
|
||||
v-if="(getDayApproval(day) || timesheet.is_approved)"
|
||||
name="verified"
|
||||
size="3em"
|
||||
color="white"
|
||||
class="absolute-top-left z-top"
|
||||
style="top: -0.2em; left: 0px;"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section
|
||||
v-if="day.shifts.filter(shift => shift.id !== 0).length > 0"
|
||||
class="q-pa-none transparent"
|
||||
>
|
||||
<ShiftListDay
|
||||
outlined
|
||||
:timesheet-id="timesheet.timesheet_id"
|
||||
:week-day-index="day_index"
|
||||
:animation-delay-multiplier="day_index"
|
||||
:approved="(getDayApproval(day) || timesheet.is_approved)"
|
||||
:day="day"
|
||||
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-btn
|
||||
v-if="!(getDayApproval(day) || timesheet.is_approved)"
|
||||
square
|
||||
dense
|
||||
size="xl"
|
||||
color="accent"
|
||||
icon="more_time"
|
||||
class="full-width"
|
||||
style="border-radius: 0 0 10px 10px;"
|
||||
@click="addNewShift(day.shifts, day.date, timesheet.timesheet_id)"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<!-- desktop version -->
|
||||
<div
|
||||
v-else
|
||||
class="col row full-width rounded-10 ellipsis shadow-10"
|
||||
:style="timesheetStore.federal_holidays.some(holiday => holiday.date === day.date) ? 'border: 2px solid #ab47bc' : ''"
|
||||
>
|
||||
<div
|
||||
class="col row"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? (timesheetStore.federal_holidays.some(holiday => holiday.date === day.date) ? 'bg-purple-5' : 'bg-accent') : 'bg-dark'"
|
||||
>
|
||||
<!-- Date block -->
|
||||
<ShiftListDateWidget
|
||||
:display-date="day.date"
|
||||
:approved="(getDayApproval(day) || timesheet.is_approved)"
|
||||
class="col-auto"
|
||||
/>
|
||||
|
||||
<ShiftListDay
|
||||
:timesheet-id="timesheet.timesheet_id"
|
||||
:week-day-index="day_index"
|
||||
:day="day"
|
||||
:holiday="timesheetStore.federal_holidays.some(holiday => holiday.date === day.date)"
|
||||
:approved="getDayApproval(day) || timesheet.is_approved"
|
||||
class="col"
|
||||
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-auto self-stretch">
|
||||
<q-icon
|
||||
v-if="(getDayApproval(day) || timesheet.is_approved)"
|
||||
name="verified"
|
||||
color="white"
|
||||
size="xl"
|
||||
class="full-height"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? (timesheetStore.federal_holidays.some(holiday => holiday.date === day.date) ? 'bg-purple-5' : 'bg-accent') : ''"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
v-else
|
||||
:dense="!$q.platform.is.mobile"
|
||||
square
|
||||
icon="more_time"
|
||||
size="lg"
|
||||
:color="timesheetStore.federal_holidays.some(holiday => holiday.date === day.date) ? 'purple-5' : 'accent'"
|
||||
text-color="white"
|
||||
class="full-height"
|
||||
:class="$q.platform.is.mobile ? 'q-px-xs' : ''"
|
||||
@click="addNewShift(day.shifts, day.date, timesheet.timesheet_id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss"
|
||||
>
|
||||
@each $size in (1, 2, 3, 4, 5, 10, 15, 20, 25, 50, 75, 100, 200) {
|
||||
.mobile-rounded-#{$size} {
|
||||
border-radius: #{$size}px !important;
|
||||
}
|
||||
|
||||
.mobile-rounded-#{$size}>div:first-child {
|
||||
border-radius: #{$size}px #{$size}px 0 0 !important;
|
||||
}
|
||||
|
||||
.mobile-rounded-#{$size}>div:last-child {
|
||||
border-radius: 0 0 #{$size}px #{$size}px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
:class="approved ? 'text-white' : ''"
|
||||
:style="'font-size: ' + weekday_font_size"
|
||||
>
|
||||
{{ $d(display_date, { weekday: $q.screen.lt.md ? 'short' : 'long'}) }}
|
||||
{{ $d(display_date, { weekday: $q.platform.is.mobile ? 'short' : 'long'}) }}
|
||||
</span>
|
||||
<span
|
||||
class="col-auto text-weight-bolder"
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
:class="approved ? 'text-white' : ''"
|
||||
:style="'font-size: ' + weekday_font_size"
|
||||
>
|
||||
{{ $d(display_date, { month: $q.screen.lt.md ? 'short' : 'long' }) }}
|
||||
{{ $d(display_date, { month: $q.platform.is.mobile ? 'short' : 'long' }) }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
const q = useQuasar();
|
||||
const { t } = useI18n();
|
||||
const ui_store = useUiStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const mode = inject<'normal' | 'approval'>('mode');
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
dense: true,
|
||||
borderless: shift.value.is_approved && isTimesheetApproved,
|
||||
readonly: shift.value.is_approved && isTimesheetApproved,
|
||||
optionsDense: !ui_store.is_mobile_mode,
|
||||
optionsDense: !q.platform.is.mobile,
|
||||
hideDropdownIcon: true,
|
||||
menuOffset: [0, 10],
|
||||
menuAnchor: "bottom middle",
|
||||
|
|
@ -169,11 +169,11 @@
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (ui_store.focus_next_component) {
|
||||
if (uiStore.focusNextComponent) {
|
||||
selectRef.value?.focus();
|
||||
selectRef.value?.showPopup();
|
||||
shiftTypeSelected.value = undefined;
|
||||
ui_store.focus_next_component = false;
|
||||
uiStore.focusNextComponent = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
@ -238,7 +238,7 @@
|
|||
|
||||
<div
|
||||
class="row items-center text-uppercase rounded-5"
|
||||
:class="ui_store.is_mobile_mode ? 'col q-mb-xs q-px-xs' : 'col-4'"
|
||||
:class="$q.platform.is.mobile ? 'col q-mb-xs q-px-xs' : 'col-4'"
|
||||
>
|
||||
<!-- shift type -->
|
||||
<q-select
|
||||
|
|
@ -251,7 +251,7 @@
|
|||
<template #selected-item="scope">
|
||||
<div
|
||||
class="row items-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
||||
:class="ui_store.is_mobile_mode ? 'full-height' : ''"
|
||||
:class="$q.platform.is.mobile ? 'full-height' : ''"
|
||||
:tabindex="scope.tabindex"
|
||||
>
|
||||
<q-icon
|
||||
|
|
@ -386,18 +386,18 @@
|
|||
|
||||
<div
|
||||
class="row full-height"
|
||||
:class="ui_store.is_mobile_mode ? 'col-12' : 'col-auto flex-center'"
|
||||
:class="$q.platform.is.mobile ? 'col-12' : 'col-auto flex-center'"
|
||||
>
|
||||
<!-- comment button -->
|
||||
<q-btn
|
||||
v-if="!ui_store.is_mobile_mode"
|
||||
v-if="!$q.platform.is.mobile"
|
||||
push
|
||||
dense
|
||||
:color="shift.is_approved ? 'white' : (shift.comment ? 'accent' : (holiday ? 'purple-5' : 'blue-grey-5'))"
|
||||
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
||||
:text-color="shift.is_approved ? (holiday ? 'purple-5' : 'accent') : 'white'"
|
||||
class="col"
|
||||
:class="ui_store.is_mobile_mode ? 'q-mt-xs bg-dark' : ''"
|
||||
:class="$q.platform.is.mobile ? 'q-mt-xs bg-dark' : ''"
|
||||
>
|
||||
<q-badge
|
||||
v-if="shift.comment"
|
||||
|
|
|
|||
|
|
@ -27,32 +27,32 @@
|
|||
const q = useQuasar();
|
||||
const { extractDate } = date;
|
||||
const { locale } = useI18n();
|
||||
const ui_store = useUiStore();
|
||||
const timesheet_api = useTimesheetApi();
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const uiStore = useUiStore();
|
||||
const timesheetApi = useTimesheetApi();
|
||||
const timesheetStore = useTimesheetStore();
|
||||
|
||||
const mobile_animation_direction = ref('fadeInLeft');
|
||||
const mobileAnimationDirection = ref('fadeInLeft');
|
||||
const currentDayComponent = ref<HTMLElement[] | null>(null);
|
||||
const currentDayComponentWatcher = ref(currentDayComponent);
|
||||
const employeeEmail = inject<string>('employeeEmail');
|
||||
|
||||
// ========== computed ========================================
|
||||
|
||||
const animation_style = computed(() => ui_store.is_mobile_mode ? mobile_animation_direction.value : 'fadeInDown');
|
||||
const animationStyle = computed(() => q.platform.is.mobile ? mobileAnimationDirection.value : 'fadeInDown');
|
||||
|
||||
// ========== methods ========================================
|
||||
|
||||
const addNewShift = (day_shifts: Shift[], date: string, timesheet_id: number) => {
|
||||
ui_store.focus_next_component = true;
|
||||
const new_shift = new Shift;
|
||||
new_shift.date = date;
|
||||
new_shift.timesheet_id = timesheet_id;
|
||||
day_shifts.push(new_shift);
|
||||
uiStore.focusNextComponent = true;
|
||||
const newShift = new Shift;
|
||||
newShift.date = date;
|
||||
newShift.timesheet_id = timesheet_id;
|
||||
day_shifts.push(newShift);
|
||||
};
|
||||
|
||||
const deleteUnsavedShift = (timesheet_index: number, day_index: number) => {
|
||||
if (timesheet_store.timesheets !== undefined) {
|
||||
const day = timesheet_store.timesheets[timesheet_index]!.days[day_index]!;
|
||||
if (timesheetStore.timesheets !== undefined) {
|
||||
const day = timesheetStore.timesheets[timesheet_index]!.days[day_index]!;
|
||||
const shifts_without_deleted_shift = day.shifts.filter(shift => shift.id !== 0);
|
||||
day.shifts = shifts_without_deleted_shift;
|
||||
}
|
||||
|
|
@ -68,7 +68,7 @@
|
|||
};
|
||||
|
||||
const getHolidayName = (date: string) => {
|
||||
const holiday = timesheet_store.federal_holidays.find(holiday => holiday.date === date);
|
||||
const holiday = timesheetStore.federal_holidays.find(holiday => holiday.date === date);
|
||||
if (!holiday) return;
|
||||
|
||||
if (locale.value === 'fr-FR')
|
||||
|
|
@ -79,11 +79,11 @@
|
|||
};
|
||||
|
||||
const onClickApplyWeeklyPreset = async (timesheet_id: number) => {
|
||||
await timesheet_api.applyPreset(timesheet_id, undefined, undefined, employeeEmail);
|
||||
await timesheetApi.applyPreset(timesheet_id, undefined, undefined, employeeEmail);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await timesheet_store.getCurrentFederalHolidays();
|
||||
await timesheetStore.getCurrentFederalHolidays();
|
||||
});
|
||||
|
||||
watch(currentDayComponentWatcher, () => {
|
||||
|
|
@ -99,9 +99,10 @@
|
|||
:class="$q.platform.is.mobile ? 'column no-wrap q-pb-lg' : 'row'"
|
||||
>
|
||||
<div
|
||||
v-for="timesheet, timesheet_index of timesheet_store.timesheets"
|
||||
v-for="timesheet, timesheet_index of timesheetStore.timesheets"
|
||||
:key="timesheet.timesheet_id"
|
||||
:class="$q.platform.is.mobile ? 'col-auto column no-wrap' : 'col column fit items-center'"
|
||||
class="no-wrap"
|
||||
:class="$q.platform.is.mobile ? 'col-auto column' : 'col column fit items-center'"
|
||||
>
|
||||
<transition
|
||||
appear
|
||||
|
|
@ -109,7 +110,7 @@
|
|||
leave-active-class="animated fadeOutUp"
|
||||
>
|
||||
<q-btn
|
||||
v-if="!$q.platform.is.mobile && timesheet.days.every(day => day.shifts.length < 1) && timesheet_store.has_timesheet_preset"
|
||||
v-if="!$q.platform.is.mobile && timesheet.days.every(day => day.shifts.length < 1) && timesheetStore.has_timesheet_preset"
|
||||
:disable="!timesheet.days.every(day => day.shifts.length < 1)"
|
||||
flat
|
||||
dense
|
||||
|
|
@ -127,7 +128,7 @@
|
|||
|
||||
<transition-group
|
||||
appear
|
||||
:enter-active-class="`animated ${animation_style}`"
|
||||
:enter-active-class="`animated ${animationStyle}`"
|
||||
>
|
||||
<div
|
||||
v-for="day, day_index in timesheet.days"
|
||||
|
|
@ -138,7 +139,7 @@
|
|||
>
|
||||
<!-- optional label indicating which holiday if today is a holiday -->
|
||||
<span
|
||||
v-if="timesheet_store.federal_holidays.some(holiday => holiday.date === day.date)"
|
||||
v-if="timesheetStore.federal_holidays.some(holiday => holiday.date === day.date)"
|
||||
class="absolute-top-left text-uppercase text-weight-bolder text-purple-5"
|
||||
style="transform: translate(25px, -7px);"
|
||||
>
|
||||
|
|
@ -209,11 +210,11 @@
|
|||
<div
|
||||
v-else
|
||||
class="col row full-width rounded-10 ellipsis shadow-10"
|
||||
:style="timesheet_store.federal_holidays.some(holiday => holiday.date === day.date) ? 'border: 2px solid #ab47bc' : ''"
|
||||
:style="timesheetStore.federal_holidays.some(holiday => holiday.date === day.date) ? 'border: 2px solid #ab47bc' : ''"
|
||||
>
|
||||
<div
|
||||
class="col row"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? (timesheet_store.federal_holidays.some(holiday => holiday.date === day.date) ? 'bg-purple-5' : 'bg-accent') : 'bg-dark'"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? (timesheetStore.federal_holidays.some(holiday => holiday.date === day.date) ? 'bg-purple-5' : 'bg-accent') : 'bg-dark'"
|
||||
>
|
||||
<!-- Date block -->
|
||||
<ShiftListDateWidget
|
||||
|
|
@ -226,7 +227,7 @@
|
|||
:timesheet-id="timesheet.timesheet_id"
|
||||
:week-day-index="day_index"
|
||||
:day="day"
|
||||
:holiday="timesheet_store.federal_holidays.some(holiday => holiday.date === day.date)"
|
||||
:holiday="timesheetStore.federal_holidays.some(holiday => holiday.date === day.date)"
|
||||
:approved="getDayApproval(day) || timesheet.is_approved"
|
||||
class="col"
|
||||
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
||||
|
|
@ -241,7 +242,7 @@
|
|||
color="white"
|
||||
size="xl"
|
||||
class="full-height"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? (timesheet_store.federal_holidays.some(holiday => holiday.date === day.date) ? 'bg-purple-5' : 'bg-accent') : ''"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? (timesheetStore.federal_holidays.some(holiday => holiday.date === day.date) ? 'bg-purple-5' : 'bg-accent') : ''"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
|
|
@ -250,7 +251,7 @@
|
|||
square
|
||||
icon="more_time"
|
||||
size="lg"
|
||||
:color="timesheet_store.federal_holidays.some(holiday => holiday.date === day.date) ? 'purple-5' : 'accent'"
|
||||
:color="timesheetStore.federal_holidays.some(holiday => holiday.date === day.date) ? 'purple-5' : 'accent'"
|
||||
text-color="white"
|
||||
class="full-height"
|
||||
:class="$q.platform.is.mobile ? 'q-px-xs' : ''"
|
||||
|
|
|
|||
|
|
@ -40,21 +40,14 @@ import { RouteNames } from 'src/router/router-constants';
|
|||
|
||||
const hasShiftErrors = computed(() => timesheetStore.all_current_shifts.filter(shift => shift.has_error === true).length > 0);
|
||||
const isTimesheetsApproved = computed(() => timesheetStore.timesheets.every(timesheet => timesheet.is_approved));
|
||||
// const timesheetStore.canSaveTimesheets = computed(() => {
|
||||
// /* eslint-disable-next-line */
|
||||
// const currentShifts = timesheetStore.timesheets.flatMap(timesheet => timesheet.days.flatMap(day => day.shifts.map(shift => { const { has_error, ...shft } = shift; return shft; })));
|
||||
// const initialShifts = timesheetStore.initial_timesheets.flatMap(timesheet => timesheet.days.flatMap(day => day.shifts));
|
||||
|
||||
// return JSON.stringify(currentShifts) !== JSON.stringify(initialShifts);
|
||||
// });
|
||||
|
||||
const totalHours = computed(() => timesheetStore.timesheets.reduce((sum, timesheet) =>
|
||||
sum += timesheet.weekly_hours.regular
|
||||
+ timesheet.weekly_hours.evening
|
||||
+ timesheet.weekly_hours.emergency
|
||||
+ timesheet.weekly_hours.overtime,
|
||||
0) //initial value
|
||||
);
|
||||
0 //initial value
|
||||
));
|
||||
|
||||
const totalExpenses = computed(() => timesheetStore.timesheets.reduce((sum, timesheet) =>
|
||||
sum + timesheet.weekly_expenses.expenses
|
||||
|
|
|
|||
|
|
@ -44,3 +44,8 @@ export interface TotalExpenses {
|
|||
on_call: number;
|
||||
mileage: number;
|
||||
}
|
||||
|
||||
export interface TimesheetDayDisplay extends TimesheetDay {
|
||||
timesheet_id: number;
|
||||
i18WeekdayKey: string;
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ import type { PayPeriod } from 'src/modules/shared/models/pay-period.models';
|
|||
import type { Timesheet } from 'src/modules/timesheets/models/timesheet.models';
|
||||
import type { PaidTimeOff } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
import type { PayPeriodEvent } from 'src/modules/timesheet-approval/models/pay-period-event.models';
|
||||
import type { TimesheetApprovalCSVReportFilters } from 'src/modules/timesheet-approval/models/timesheet-approval-csv-report.models';
|
||||
import type { CSVReportFilters } from 'src/modules/timesheet-approval/models/timesheet-approval-csv-report.models';
|
||||
import { type FederalHoliday, TARGO_HOLIDAY_NAMES_FR } from 'src/modules/timesheets/models/federal-holidays.models';
|
||||
import type { RouteNames } from 'src/router/router-constants';
|
||||
import type { RouteRecordNameGeneric } from 'vue-router';
|
||||
|
|
@ -42,7 +42,7 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
|||
const selected_employee_name = ref<string>();
|
||||
const has_timesheet_preset = ref(false);
|
||||
const current_pay_period_overview = ref<TimesheetApprovalOverview>();
|
||||
const pay_period_report = ref();
|
||||
const payPeriodReport = ref<Blob>();
|
||||
const pay_period_observer = ref<EventSource | undefined>();
|
||||
|
||||
const federal_holidays = ref<FederalHoliday[]>([]);
|
||||
|
|
@ -191,12 +191,16 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
|||
return false;
|
||||
}
|
||||
|
||||
const getPayPeriodReport = async (report_filters: TimesheetApprovalCSVReportFilters) => {
|
||||
const getPayPeriodReport = async (filters: CSVReportFilters): Promise<boolean> => {
|
||||
try {
|
||||
if (!pay_period.value) return false;
|
||||
const response = await timesheetApprovalService.getPayPeriodReportByYearAndPeriodNumber(pay_period.value.pay_year, pay_period.value.pay_period_no, report_filters);
|
||||
pay_period_report.value = response;
|
||||
return response.data;
|
||||
|
||||
const response = await timesheetApprovalService.getPayPeriodReportByYearAndPeriodNumber(pay_period.value.pay_year, pay_period.value.pay_period_no, filters);
|
||||
|
||||
if (response){
|
||||
payPeriodReport.value = response;
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving the report CSV: ', error);
|
||||
// TODO: More in-depth error-handling here
|
||||
|
|
@ -256,6 +260,7 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
|||
return {
|
||||
is_loading,
|
||||
is_report_dialog_open,
|
||||
payPeriodReport,
|
||||
is_details_dialog_open,
|
||||
pay_period,
|
||||
pay_period_overviews,
|
||||
|
|
|
|||
|
|
@ -1,33 +1,27 @@
|
|||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { defineStore } from 'pinia';
|
||||
import { computed, ref } from 'vue';
|
||||
import { LocalStorage, useQuasar, Dark } from 'quasar';
|
||||
import { LocalStorage, 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', () => {
|
||||
const q = useQuasar();
|
||||
const { locale } = useI18n();
|
||||
const is_left_drawer_open = ref(true);
|
||||
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 isLeftDrawerOpen = ref(true);
|
||||
const focusNextComponent = ref(false);
|
||||
const userPreferences = ref<Preferences>(new Preferences);
|
||||
|
||||
const toggleRightDrawer = () => {
|
||||
is_left_drawer_open.value = !is_left_drawer_open.value;
|
||||
isLeftDrawerOpen.value = !isLeftDrawerOpen.value;
|
||||
};
|
||||
|
||||
const getUserPreferences = async () => {
|
||||
try {
|
||||
const local_user_preferences = LocalStorage.getItem<Preferences>('user_preferences');
|
||||
const local_userPreferences = LocalStorage.getItem<Preferences>('userPreferences');
|
||||
|
||||
if (local_user_preferences !== null) {
|
||||
if (local_user_preferences.id !== -1) {
|
||||
Object.assign(user_preferences.value, local_user_preferences);
|
||||
if (local_userPreferences !== null) {
|
||||
if (local_userPreferences.id !== -1) {
|
||||
Object.assign(userPreferences.value, local_userPreferences);
|
||||
setPreferences();
|
||||
return;
|
||||
}
|
||||
|
|
@ -36,24 +30,24 @@ export const useUiStore = defineStore('ui', () => {
|
|||
const response = await ProfileService.getUserPreferences();
|
||||
|
||||
if (response.success && response.data) {
|
||||
LocalStorage.setItem('user_preferences', response.data);
|
||||
Object.assign(user_preferences.value, response.data);
|
||||
LocalStorage.setItem('userPreferences', response.data);
|
||||
Object.assign(userPreferences.value, response.data);
|
||||
setPreferences();
|
||||
}
|
||||
} catch (error) {
|
||||
user_preferences.value = new Preferences;
|
||||
userPreferences.value = new Preferences;
|
||||
console.error('Could not retrieve user preferences: ', error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateUserPreferences = async () => {
|
||||
try {
|
||||
if (user_preferences.value.id === -1) return;
|
||||
if (userPreferences.value.id === -1) return;
|
||||
|
||||
const response = await ProfileService.updateUserPreferences(user_preferences.value);
|
||||
const response = await ProfileService.updateUserPreferences(userPreferences.value);
|
||||
if (response.success && response.data) {
|
||||
Object.assign(user_preferences.value, response.data);
|
||||
LocalStorage.setItem('user_preferences', response.data);
|
||||
Object.assign(userPreferences.value, response.data);
|
||||
LocalStorage.setItem('userPreferences', response.data);
|
||||
setPreferences();
|
||||
return;
|
||||
}
|
||||
|
|
@ -63,19 +57,17 @@ export const useUiStore = defineStore('ui', () => {
|
|||
};
|
||||
|
||||
const setPreferences = () => {
|
||||
if (user_preferences.value !== undefined) {
|
||||
// if user_preferences.value.is_dark_mode === null
|
||||
Dark.set(user_preferences.value.is_dark_mode ?? "auto");
|
||||
locale.value = user_preferences.value.display_language;
|
||||
if (userPreferences.value !== undefined) {
|
||||
// if userPreferences.value.is_dark_mode === null
|
||||
Dark.set(userPreferences.value.is_dark_mode ?? "auto");
|
||||
locale.value = userPreferences.value.display_language;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
current_page,
|
||||
is_mobile_mode,
|
||||
focus_next_component,
|
||||
is_left_drawer_open,
|
||||
user_preferences,
|
||||
focusNextComponent,
|
||||
isLeftDrawerOpen,
|
||||
userPreferences,
|
||||
toggleRightDrawer,
|
||||
getUserPreferences,
|
||||
updateUserPreferences,
|
||||
|
|
|
|||
13
src/utils/translator.ts
Normal file
13
src/utils/translator.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import type { ShiftType } from "src/modules/timesheets/models/shift.models";
|
||||
|
||||
export const fromEnToFrShiftType = (shiftType: ShiftType): string => {
|
||||
switch(shiftType) {
|
||||
case "REGULAR": return 'Régulier';
|
||||
case "EVENING": return 'Soir';
|
||||
case "EMERGENCY": return 'Urgence';
|
||||
case "HOLIDAY": return 'Férié';
|
||||
case "VACATION": return 'Vacances';
|
||||
case "SICK": return 'Maladie';
|
||||
case "WITHDRAW_BANKED": return 'Heures-en-Banque';
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user