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",
|
title: "Download options",
|
||||||
description: "Choose what to include in the report",
|
description: "Choose what to include in the report",
|
||||||
company: "companies",
|
company: "companies",
|
||||||
|
targo: "Targo",
|
||||||
|
solucom: "Solucom",
|
||||||
type: "type",
|
type: "type",
|
||||||
shifts: "shifts",
|
shifts: "shifts",
|
||||||
expenses: "expenses",
|
expenses: "expenses",
|
||||||
options: "options",
|
options: "options",
|
||||||
|
download_failed: "download failed",
|
||||||
|
download_failed_caption: "an unexpected error occured",
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
full_name: "full name",
|
full_name: "full name",
|
||||||
|
|
|
||||||
|
|
@ -401,10 +401,14 @@ export default {
|
||||||
title: "options de téléchargement",
|
title: "options de téléchargement",
|
||||||
description: "Choisissez ce qui sera inclu dans le rapport",
|
description: "Choisissez ce qui sera inclu dans le rapport",
|
||||||
company: "compagnies",
|
company: "compagnies",
|
||||||
|
targo: "Targo",
|
||||||
|
solucom: "Solucom",
|
||||||
type: "types de données",
|
type: "types de données",
|
||||||
shifts: "quarts de travail",
|
shifts: "quarts de travail",
|
||||||
expenses: "dépenses",
|
expenses: "dépenses",
|
||||||
options: "options",
|
options: "options",
|
||||||
|
download_failed: "téléchargement échoué",
|
||||||
|
download_failed_caption: "une erreur inconnue est survenue",
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
full_name: "nom complet",
|
full_name: "nom complet",
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,14 @@ import { useAuthApi } from 'src/modules/auth/composables/use-auth-api';
|
||||||
]
|
]
|
||||||
|
|
||||||
const q = useQuasar();
|
const q = useQuasar();
|
||||||
const auth_store = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const authApi = useAuthApi();
|
const authApi = useAuthApi();
|
||||||
const ui_store = useUiStore();
|
const uiStore = useUiStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const is_mini = ref(true);
|
const isMini = ref(true);
|
||||||
|
|
||||||
const onClickDrawerPage = (page_name: RouteNames) => {
|
const onClickDrawerPage = (page_name: RouteNames) => {
|
||||||
is_mini.value = true;
|
isMini.value = true;
|
||||||
|
|
||||||
router.push({ name: page_name }).catch(error => {
|
router.push({ name: page_name }).catch(error => {
|
||||||
console.error('failed to reach page: ', error);
|
console.error('failed to reach page: ', error);
|
||||||
|
|
@ -41,21 +41,21 @@ import { useAuthApi } from 'src/modules/auth/composables/use-auth-api';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (q.platform.is.mobile) {
|
if (q.platform.is.mobile) {
|
||||||
ui_store.is_left_drawer_open = false;
|
uiStore.isLeftDrawerOpen = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-drawer
|
<q-drawer
|
||||||
v-model="ui_store.is_left_drawer_open"
|
v-model="uiStore.isLeftDrawerOpen"
|
||||||
:persistent="!$q.platform.is.mobile"
|
:persistent="!$q.platform.is.mobile"
|
||||||
mini-to-overlay
|
mini-to-overlay
|
||||||
elevated
|
elevated
|
||||||
side="left"
|
side="left"
|
||||||
:mini="is_mini"
|
:mini="isMini"
|
||||||
@mouseenter="is_mini = false"
|
@mouseenter="isMini = false"
|
||||||
@mouseleave="is_mini = true"
|
@mouseleave="isMini = true"
|
||||||
class="bg-dark z-max"
|
class="bg-dark z-max"
|
||||||
>
|
>
|
||||||
<q-scroll-area class="column fit">
|
<q-scroll-area class="column fit">
|
||||||
|
|
@ -66,7 +66,7 @@ import { useAuthApi } from 'src/modules/auth/composables/use-auth-api';
|
||||||
@click="onClickDrawerPage(button.route)"
|
@click="onClickDrawerPage(button.route)"
|
||||||
>
|
>
|
||||||
<div
|
<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="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') : ''"
|
: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 ui_store = useUiStore();
|
||||||
const auth_store = useAuthStore();
|
const auth_store = useAuthStore();
|
||||||
const user_preferences = ref(ui_store.user_preferences);
|
const userPreferences = ref(ui_store.userPreferences);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (ui_store.user_preferences.id === -1) {
|
if (ui_store.userPreferences.id === -1) {
|
||||||
await ui_store.getUserPreferences();
|
await ui_store.getUserPreferences();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(user_preferences, async () => {
|
watch(userPreferences, async () => {
|
||||||
if (ui_store.user_preferences.id !== -1) {
|
if (ui_store.userPreferences.id !== -1) {
|
||||||
await ui_store.updateUserPreferences();
|
await ui_store.updateUserPreferences();
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,13 +82,13 @@
|
||||||
:filter="filters"
|
:filter="filters"
|
||||||
:filter-method="filterEmployeeRows"
|
:filter-method="filterEmployeeRows"
|
||||||
class="bg-transparent no-shadow sticky-header-table full-width q-pt-lg"
|
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'"
|
: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"
|
color="accent"
|
||||||
separator="none"
|
separator="none"
|
||||||
table-header-class="text-accent text-uppercase"
|
table-header-class="text-accent text-uppercase"
|
||||||
card-container-class="justify-center"
|
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"
|
:loading="employee_store.is_loading"
|
||||||
:no-data-label="$t('shared.error.no_data_found')"
|
:no-data-label="$t('shared.error.no_data_found')"
|
||||||
:no-results-label="$t('shared.error.no_search_results')"
|
:no-results-label="$t('shared.error.no_search_results')"
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
<q-space />
|
<q-space />
|
||||||
|
|
||||||
<q-btn-toggle
|
<q-btn-toggle
|
||||||
v-model="ui_store.user_preferences.is_employee_list_grid"
|
v-model="ui_store.userPreferences.is_employee_list_grid"
|
||||||
push
|
push
|
||||||
rounded
|
rounded
|
||||||
color="white"
|
color="white"
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="col-auto justify-center content-center q-mb-sm q-pa-md rounded-5"
|
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);"
|
style="border: 1px solid var(--q-accent);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
v-ripple
|
v-ripple
|
||||||
class="rounded-5 shadow-4 q-py-xs"
|
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' : '')"
|
: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-item-section side>
|
||||||
<q-icon
|
<q-icon
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
const ui_store = useUiStore();
|
const ui_store = useUiStore();
|
||||||
|
|
||||||
const setDisplayLanguage = (locale: MessageLanguages) => {
|
const setDisplayLanguage = (locale: MessageLanguages) => {
|
||||||
if (ui_store.user_preferences !== undefined) {
|
if (ui_store.userPreferences !== undefined) {
|
||||||
ui_store.user_preferences.display_language = locale;
|
ui_store.userPreferences.display_language = locale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
<q-list
|
<q-list
|
||||||
dense
|
dense
|
||||||
class="full-width"
|
class="full-width"
|
||||||
:class="ui_store.is_mobile_mode ? 'column' : 'row'"
|
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||||
>
|
>
|
||||||
<q-item
|
<q-item
|
||||||
v-for="locale in $i18n.availableLocales"
|
v-for="locale in $i18n.availableLocales"
|
||||||
|
|
|
||||||
|
|
@ -57,10 +57,12 @@
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: shift_type_data,
|
data: shift_type_data,
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
colors.getPaletteColor('accent'), // Regular
|
colors.getPaletteColor('accent'), // Regular
|
||||||
colors.getPaletteColor('green-10'), // Evening
|
colors.getPaletteColor('green-10'), // Evening
|
||||||
colors.getPaletteColor('warning'), // Emergency
|
colors.getPaletteColor('warning'), // Emergency
|
||||||
colors.getPaletteColor('negative'), // Overtime
|
colors.getPaletteColor('negative'), // Overtime
|
||||||
|
colors.getPaletteColor('purple-5'), // Holiday
|
||||||
|
colors.getPaletteColor('deep-orange-5') // Vacation
|
||||||
]
|
]
|
||||||
}],
|
}],
|
||||||
}"
|
}"
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const onShowDetailsDialog = () => {
|
const onShowDetailsDialog = () => {
|
||||||
isDialogOpen.value = true;
|
isDialogOpen.value = true;
|
||||||
expenseStore.is_showing_create_form = false;
|
expenseStore.is_showing_create_form = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -158,7 +158,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-auto column">
|
<div class="col-auto column no-wrap">
|
||||||
<q-separator
|
<q-separator
|
||||||
spaced
|
spaced
|
||||||
size="4px"
|
size="4px"
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
:class="$q.platform.is.mobile ? 'q-mb-md' : ''"
|
:class="$q.platform.is.mobile ? 'q-mb-md' : ''"
|
||||||
>
|
>
|
||||||
<q-btn-toggle
|
<q-btn-toggle
|
||||||
v-model="uiStore.user_preferences.is_timesheet_approval_grid"
|
v-model="uiStore.userPreferences.is_timesheet_approval_grid"
|
||||||
push
|
push
|
||||||
rounded
|
rounded
|
||||||
color="white"
|
color="white"
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
// ========== computed ========================================
|
// ========== computed ========================================
|
||||||
|
|
||||||
const isGridMode = computed(() => q.platform.is.mobile ? true :
|
const isGridMode = computed(() => q.platform.is.mobile ? true :
|
||||||
uiStore.user_preferences.is_timesheet_approval_grid
|
uiStore.userPreferences.is_timesheet_approval_grid
|
||||||
);
|
);
|
||||||
|
|
||||||
const overviewRows = computed(() =>
|
const overviewRows = computed(() =>
|
||||||
|
|
@ -134,7 +134,7 @@
|
||||||
:filter-method="filterEmployeeRows"
|
:filter-method="filterEmployeeRows"
|
||||||
:rows-per-page-options="[0]"
|
:rows-per-page-options="[0]"
|
||||||
class="bg-transparent"
|
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"
|
card-container-class="justify-center"
|
||||||
table-class="q-pa-none q-mx-md rounded-10 bg-dark shadow-15"
|
table-class="q-pa-none q-mx-md rounded-10 bg-dark shadow-15"
|
||||||
:no-data-label="$t('shared.error.no_data_found')"
|
: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">
|
<script
|
||||||
import { computed, ref, watch } from 'vue';
|
setup
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
lang="ts"
|
||||||
import { TimesheetApprovalCSVReportFilters } from 'src/modules/timesheet-approval/models/timesheet-approval-csv-report.models';
|
>
|
||||||
|
import OverviewReportOption from 'src/modules/timesheet-approval/components/overview-report-option.vue';
|
||||||
|
|
||||||
const timesheet_store = useTimesheetStore();
|
import { Notify } from 'quasar';
|
||||||
const report_filter_options = ref<TimesheetApprovalCSVReportFilters>(new TimesheetApprovalCSVReportFilters);
|
import { useI18n } from 'vue-i18n';
|
||||||
const selected_company = ref<'targo' | 'solucom'>('targo');
|
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)[]>(
|
// ========== STATE ========================================
|
||||||
Object.entries(report_filter_options.value).filter(([_key, value]) => value).map(([key]) => key as keyof TimesheetApprovalCSVReportFilters)
|
|
||||||
);
|
|
||||||
|
|
||||||
interface ReportOptions {
|
const { t } = useI18n();
|
||||||
label: string;
|
const timesheetStore = useTimesheetStore();
|
||||||
value: keyof TimesheetApprovalCSVReportFilters;
|
const timesheetApprovalApi = useTimesheetApprovalApi();
|
||||||
};
|
const reportFilters = ref<CSVReportFilters>(new CSVReportFilters);
|
||||||
|
|
||||||
const company_options: ReportOptions[] = [
|
// ========== COMPUTED ========================================
|
||||||
{ label: 'Targo', value: 'targo' },
|
|
||||||
{ label: 'Solucom', value: 'solucom' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const type_options: ReportOptions[] = [
|
const isDownloadButtonEnabled = computed(() => reportFilters.value.shiftTypes.length > 0);
|
||||||
{ 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(() =>
|
// ========== METHODS ========================================
|
||||||
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 () => {
|
const onClickedDownload = async () => {
|
||||||
try {
|
const response = await timesheetApprovalApi.getTimesheetApprovalCSVReport(reportFilters.value);
|
||||||
const data = await timesheet_store.getPayPeriodReport(report_filter_options.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)
|
return;
|
||||||
.filter(([key, value]) => value && (key === 'targo' || key === 'solucom')).map(([key]) => key).join('-');
|
}
|
||||||
|
|
||||||
const types = Object.entries(report_filter_options.value)
|
const url = window.URL.createObjectURL(response.file);
|
||||||
.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 link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
|
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.setAttribute('download', file_name);
|
link.setAttribute('download', response.fileName);
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
window.URL.revokeObjectURL(url);
|
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) => {
|
const onClickCompanyOption = (companyName: CompanyNames) => {
|
||||||
Object.keys(report_filter_options.value).forEach(key => {
|
if (reportFilters.value.companyName === companyName) return;
|
||||||
const typed_key = key as keyof TimesheetApprovalCSVReportFilters;
|
reportFilters.value.companyName = companyName;
|
||||||
report_filter_options.value[typed_key] = new_values.includes(key as keyof TimesheetApprovalCSVReportFilters);
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(selected_company, (company) => {
|
const onClickShiftOption = (clickedOption: ShiftType) => {
|
||||||
report_filter_options.value.targo = company === 'targo';
|
const index = reportFilters.value.shiftTypes.findIndex(option => option === clickedOption);
|
||||||
report_filter_options.value.solucom = company === 'solucom';
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (index === -1)
|
||||||
|
reportFilters.value.shiftTypes.push(clickedOption);
|
||||||
|
else
|
||||||
|
reportFilters.value.shiftTypes.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickExpenseOption = () => {
|
||||||
|
reportFilters.value.includeExpenses = !reportFilters.value.includeExpenses;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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
|
<div
|
||||||
class="column bg-secondary shadow-24 rounded-10"
|
class="column bg-secondary shadow-24 rounded-10"
|
||||||
:style="$q.dark.isActive ? 'border: 2px solid var(--q-accent)' : ''"
|
:style="$q.dark.isActive ? 'border: 2px solid var(--q-accent)' : ''"
|
||||||
>
|
>
|
||||||
<!-- main header -->
|
<!-- main header -->
|
||||||
<div
|
<div class="col-auto column bg-primary text-center text-uppercase">
|
||||||
class="col-auto bg-primary text-accent text-weight-bolder text-center text-uppercase text-h6 q-py-xs z-top">
|
<span class="text-white text-weight-bolder q-py-sm text-h5">
|
||||||
{{ $t('timesheet_approvals.print_report.title') }}
|
{{ $t('timesheet_approvals.print_report.title') }}
|
||||||
</div>
|
</span>
|
||||||
|
|
||||||
<!-- info blurb -->
|
<span class="col-auto q-py-xs bg-dark full-width shadow-4">
|
||||||
<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') }}
|
{{ $t('timesheet_approvals.print_report.description') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- company header -->
|
<!-- groups -->
|
||||||
<span class="col-auto q-px-sm q-pt-md text-weight-medium text-accent text-uppercase">
|
<div class="col column full-width q-px-md">
|
||||||
{{ $t('timesheet_approvals.print_report.company') }}
|
<!-- company header -->
|
||||||
</span>
|
<span class="col-auto q-px-sm q-pt-md text-bold text-uppercase">
|
||||||
|
{{ $t('timesheet_approvals.print_report.company') }}
|
||||||
|
</span>
|
||||||
|
|
||||||
<!-- company options -->
|
<!-- company options -->
|
||||||
<div class="col row text-uppercase full-width q-px-md">
|
<div class="row q-pb-sm">
|
||||||
<div
|
<div
|
||||||
v-for="company, index in company_options"
|
v-for="company, companyIndex in companyOptions"
|
||||||
:key="index"
|
:key="companyIndex"
|
||||||
class="q-pa-xs col-6"
|
class="col-6 q-pa-sm"
|
||||||
>
|
>
|
||||||
<q-radio
|
<OverviewReportOption
|
||||||
v-model="selected_company"
|
:is-selected="reportFilters.companyName === company.value"
|
||||||
left-label
|
:label="company.label"
|
||||||
color="white"
|
@click-option="onClickCompanyOption(company.value)"
|
||||||
dense
|
/>
|
||||||
:label="company.label"
|
</div>
|
||||||
: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'"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- shift type header -->
|
<!-- data type header -->
|
||||||
<span class="col-auto q-px-sm q-pt-md text-weight-medium text-uppercase text-accent">
|
<span class="col-auto q-px-sm q-pt-md text-bold text-uppercase">
|
||||||
{{ $t('timesheet_approvals.print_report.options') }}
|
{{ $t('timesheet_approvals.print_report.type') }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- shift type options -->
|
<!-- data type options -->
|
||||||
<div class="col row text-uppercase full-width q-px-md q-pb-md">
|
<div class="row">
|
||||||
<div
|
<!-- shift types -->
|
||||||
v-for="type, index in type_options"
|
<div
|
||||||
:key="index"
|
v-for="shiftType, typeIndex in typeOptions"
|
||||||
class="q-pa-xs col-6"
|
:key="typeIndex"
|
||||||
>
|
class="col-6 q-pa-sm"
|
||||||
<q-checkbox
|
>
|
||||||
v-model="selected_report_filters"
|
<OverviewReportOption
|
||||||
left-label
|
:is-selected="reportFilters.shiftTypes.includes(shiftType.value)"
|
||||||
color="white"
|
:label="shiftType.label"
|
||||||
dense
|
@click-option="onClickShiftOption(shiftType.value)"
|
||||||
:val="type.value"
|
/>
|
||||||
checked-icon="check_box"
|
</div>
|
||||||
unchecked-icon="check_box_outline_blank"
|
|
||||||
:label="$t(type.label)"
|
<!-- expenses -->
|
||||||
class="q-px-md q-py-xs shadow-4 rounded-25 full-width"
|
<div class="col-6 q-pa-sm">
|
||||||
:class="selected_report_filters.includes(type.value) ? 'bg-accent text-white text-bold' : 'bg-white text-primary'"
|
<OverviewReportOption
|
||||||
/>
|
:is-selected="reportFilters.includeExpenses"
|
||||||
|
:label="'timesheet_approvals.print_report.expenses'"
|
||||||
|
@click-option="onClickExpenseOption"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- download button -->
|
<!-- download button -->
|
||||||
<q-btn
|
<div class="col-auto row justify-end q-px-md q-pb-md q-pt-xl">
|
||||||
:disable="!is_download_button_enable"
|
<q-btn
|
||||||
square
|
push
|
||||||
icon="download"
|
:disable="!isDownloadButtonEnabled"
|
||||||
:color="is_download_button_enable ? 'accent' : 'grey-5'"
|
icon="download"
|
||||||
:label="$t('shared.label.download')"
|
:color="isDownloadButtonEnabled ? 'accent' : 'grey-5'"
|
||||||
class="col-auto q-py-sm shadow-up-2"
|
:label="$t('shared.label.download')"
|
||||||
@click="onClickedDownload()"
|
class="col-auto q-py-sm q-px-xl"
|
||||||
/>
|
@click="onClickedDownload"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,72 @@
|
||||||
import { useTimesheetStore } from "src/stores/timesheet-store";
|
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 = () => {
|
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 DATE_REGEX = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
|
||||||
|
|
||||||
const getTimesheetOverviews = async (date?: string) => {
|
const getTimesheetOverviews = async (date?: string) => {
|
||||||
timesheet_store.is_loading = true;
|
timesheetStore.is_loading = true;
|
||||||
|
|
||||||
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date);
|
const success = await timesheetStore.getPayPeriodByDateOrYearAndNumber(date);
|
||||||
if (success) await timesheet_store.getTimesheetOverviews();
|
if (success) await timesheetStore.getTimesheetOverviews();
|
||||||
|
|
||||||
timesheet_store.is_loading = false;
|
timesheetStore.is_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTimesheetOverviewsByDate = async (date: string) => {
|
const getTimesheetOverviewsByDate = async (date: string) => {
|
||||||
const valid_date = DATE_REGEX.test(date);
|
const valid_date = DATE_REGEX.test(date);
|
||||||
timesheet_store.is_loading = true;
|
timesheetStore.is_loading = true;
|
||||||
|
|
||||||
if (valid_date) {
|
if (valid_date) {
|
||||||
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date);
|
const success = await timesheetStore.getPayPeriodByDateOrYearAndNumber(date);
|
||||||
if (success) await timesheet_store.getTimesheetOverviews();
|
if (success) await timesheetStore.getTimesheetOverviews();
|
||||||
}
|
}
|
||||||
|
|
||||||
timesheet_store.is_loading = false;
|
timesheetStore.is_loading = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleTimesheetsApprovalByEmployeeEmail = async (email: string, approval_status: boolean) => {
|
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) {
|
if (success) {
|
||||||
const approval_success = await timesheet_store.toggleTimesheetsApprovalByEmployeeEmail(email, approval_status);
|
const approval_success = await timesheetStore.toggleTimesheetsApprovalByEmployeeEmail(email, approval_status);
|
||||||
const overview = timesheet_store.pay_period_overviews.find(overview => overview.email === email);
|
const overview = timesheetStore.pay_period_overviews.find(overview => overview.email === email);
|
||||||
|
|
||||||
if (overview && approval_success) {
|
if (overview && approval_success) {
|
||||||
overview.is_approved = approval_status;
|
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[]) => {
|
const getTimesheetApprovalCSVReport = async (selectedFilters: CSVReportFilters): Promise<{file: Blob, fileName: string} | undefined> => {
|
||||||
if (timesheet_store.pay_period === undefined) return;
|
if (timesheetStore.pay_period === undefined) return;
|
||||||
|
|
||||||
const [targo, solucom] = report_filter_company;
|
const success = await timesheetStore.getPayPeriodReport(selectedFilters);
|
||||||
const [shifts, expenses, holiday, vacation] = report_filter_type;
|
if (!success) return;
|
||||||
const options = {
|
|
||||||
shifts, expenses, holiday, vacation, targo, solucom
|
|
||||||
} as TimesheetApprovalCSVReportFilters;
|
|
||||||
|
|
||||||
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 {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,28 @@
|
||||||
export class TimesheetApprovalCSVReportFilters {
|
import type { ShiftType } from "src/modules/timesheets/models/shift.models";
|
||||||
shifts: boolean;
|
import type { CompanyNames } from "src/modules/employee-list/models/employee-profile.models";
|
||||||
expenses: boolean;
|
|
||||||
holiday: boolean;
|
export class CSVReportFilters {
|
||||||
vacation: boolean;
|
companyName: CompanyNames;
|
||||||
targo: boolean;
|
includeExpenses: boolean;
|
||||||
solucom: boolean;
|
shiftTypes: ShiftType[];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.shifts = true;
|
this.companyName = 'Targo';
|
||||||
this.expenses = true;
|
this.includeExpenses = false;
|
||||||
this.holiday = true;
|
this.shiftTypes = ['REGULAR', 'EVENING', 'EMERGENCY', 'SICK']
|
||||||
this.vacation = true;
|
}
|
||||||
this.targo = true;
|
};
|
||||||
this.solucom = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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 { api } from "src/boot/axios";
|
||||||
import type { BackendResponse } from "src/modules/shared/models/backend-response.models";
|
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";
|
import type { PayPeriodOverviewResponse } from "src/modules/timesheet-approval/models/timesheet-overview.models";
|
||||||
|
|
||||||
export const timesheetApprovalService = {
|
export const timesheetApprovalService = {
|
||||||
|
|
@ -9,13 +9,13 @@ export const timesheetApprovalService = {
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
getPayPeriodReportByYearAndPeriodNumber: async (year: number, period_number: number, filters?: TimesheetApprovalCSVReportFilters) => {
|
getPayPeriodReportByYearAndPeriodNumber: async (year: number, period_number: number, filters?: CSVReportFilters): Promise<Blob> => {
|
||||||
const response = await api.get(`exports/csv/${year}/${period_number}`, { params: filters, responseType: 'arraybuffer' });
|
const response = await api.post(`exports/csv/${year}/${period_number}`, filters, { responseType: 'blob' });
|
||||||
return response;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateTimesheetsApprovalStatus: async (email: string, timesheet_ids: number[], is_approved: boolean): Promise<BackendResponse<{shifts: number, expenses: number}>> => {
|
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});
|
const response = await api.patch<BackendResponse<{ shifts: number, expenses: number }>>('pay-periods/pay-period-approval', { email, timesheet_ids, is_approved });
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useUiStore } from 'src/stores/ui-store';
|
|
||||||
import { useExpensesStore } from 'src/stores/expense-store';
|
import { useExpensesStore } from 'src/stores/expense-store';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
||||||
|
|
@ -28,7 +27,6 @@
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ui_store = useUiStore();
|
|
||||||
const timesheetStore = useTimesheetStore();
|
const timesheetStore = useTimesheetStore();
|
||||||
const expenseStore = useExpensesStore();
|
const expenseStore = useExpensesStore();
|
||||||
const expensesApi = useExpensesApi();
|
const expensesApi = useExpensesApi();
|
||||||
|
|
@ -168,7 +166,6 @@
|
||||||
<template #selected-item="scope">
|
<template #selected-item="scope">
|
||||||
<div
|
<div
|
||||||
class="row items-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
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"
|
:tabindex="scope.tabindex"
|
||||||
>
|
>
|
||||||
<q-icon
|
<q-icon
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
>
|
>
|
||||||
import { computed, ref, onMounted } from 'vue';
|
import { computed, ref, onMounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useUiStore } from 'src/stores/ui-store';
|
|
||||||
import { useExpensesStore } from 'src/stores/expense-store';
|
import { useExpensesStore } from 'src/stores/expense-store';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
||||||
|
|
@ -26,7 +25,6 @@
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ui_store = useUiStore();
|
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
const expenses_store = useExpensesStore();
|
const expenses_store = useExpensesStore();
|
||||||
const auth_store = useAuthStore();
|
const auth_store = useAuthStore();
|
||||||
|
|
@ -168,8 +166,7 @@
|
||||||
|
|
||||||
<template #selected-item="scope">
|
<template #selected-item="scope">
|
||||||
<div
|
<div
|
||||||
class="row items-center text-weight-bold q-ma-none q-pa-none full-width"
|
class="row items-center text-weight-bold q-ma-none q-pa-none fit"
|
||||||
:class="ui_store.is_mobile_mode ? 'full-height' : ''"
|
|
||||||
:tabindex="scope.tabindex"
|
:tabindex="scope.tabindex"
|
||||||
>
|
>
|
||||||
<q-icon
|
<q-icon
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,11 @@
|
||||||
'onTimeFieldBlur': [void];
|
'onTimeFieldBlur': [void];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const ui_store = useUiStore();
|
const uiStore = useUiStore();
|
||||||
const shiftTypeSelected = ref(SHIFT_OPTIONS.find(option => option.value == shift.value.type));
|
const shiftTypeSelected = ref(SHIFT_OPTIONS.find(option => option.value == shift.value.type));
|
||||||
const select_ref = ref<QSelect | null>(null);
|
const selectRef = ref<QSelect | null>(null);
|
||||||
const is_showing_comment_popup = ref(false);
|
const isShowingCommentPopup = ref(false);
|
||||||
const error_message = ref('');
|
const errorMessageRow = ref('');
|
||||||
const isShowingPredefinedTime = ref(shift.value.type === 'HOLIDAY');
|
const isShowingPredefinedTime = ref(shift.value.type === 'HOLIDAY');
|
||||||
const predefinedHoursString = ref('');
|
const predefinedHoursString = ref('');
|
||||||
const predefinedHoursBgColor = ref(`bg-${shiftTypeSelected.value?.icon_color ?? ''}`);
|
const predefinedHoursBgColor = ref(`bg-${shiftTypeSelected.value?.icon_color ?? ''}`);
|
||||||
|
|
@ -63,10 +63,10 @@
|
||||||
const onTimeFieldBlur = (time_string: string) => {
|
const onTimeFieldBlur = (time_string: string) => {
|
||||||
if (time_string.length < 1 || !time_string) {
|
if (time_string.length < 1 || !time_string) {
|
||||||
shift.value.has_error = true;
|
shift.value.has_error = true;
|
||||||
error_message.value = 'timesheet.errors.SHIFT_TIME_REQUIRED';
|
errorMessageRow.value = 'timesheet.errors.SHIFT_TIME_REQUIRED';
|
||||||
} else {
|
} else {
|
||||||
shift.value.has_error = false;
|
shift.value.has_error = false;
|
||||||
error_message.value = '';
|
errorMessageRow.value = '';
|
||||||
emit('onTimeFieldBlur');
|
emit('onTimeFieldBlur');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,15 +102,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (ui_store.focus_next_component) {
|
if (uiStore.focusNextComponent) {
|
||||||
select_ref.value?.focus();
|
selectRef.value?.focus();
|
||||||
select_ref.value?.showPopup();
|
selectRef.value?.showPopup();
|
||||||
shiftTypeSelected.value = undefined;
|
shiftTypeSelected.value = undefined;
|
||||||
ui_store.focus_next_component = false;
|
uiStore.focusNextComponent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMessage)
|
if (errorMessage)
|
||||||
error_message.value = errorMessage;
|
errorMessageRow.value = errorMessage;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -120,13 +120,13 @@
|
||||||
<div class="col row items-center text-uppercase q-px-xs rounded-5">
|
<div class="col row items-center text-uppercase q-px-xs rounded-5">
|
||||||
<!-- comment button -->
|
<!-- comment button -->
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="ui_store.is_mobile_mode && !dense"
|
v-if="!dense"
|
||||||
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
||||||
:text-color="shift.comment ? ((shift.is_approved && isTimesheetApproved) ? 'white' : 'accent') : 'grey-5'"
|
:text-color="shift.comment ? ((shift.is_approved && isTimesheetApproved) ? 'white' : 'accent') : 'grey-5'"
|
||||||
class="col-auto full-height q-mx-xs rounded-5 shadow-1"
|
class="col-auto full-height q-mx-xs rounded-5 shadow-1"
|
||||||
@click="is_showing_comment_popup = true"
|
@click="isShowingCommentPopup = true"
|
||||||
>
|
>
|
||||||
<q-dialog v-model="is_showing_comment_popup">
|
<q-dialog v-model="isShowingCommentPopup">
|
||||||
<q-input
|
<q-input
|
||||||
color="white"
|
color="white"
|
||||||
v-model="shift.comment"
|
v-model="shift.comment"
|
||||||
|
|
@ -190,8 +190,7 @@
|
||||||
>
|
>
|
||||||
<template #selected-item="scope">
|
<template #selected-item="scope">
|
||||||
<div
|
<div
|
||||||
class="row items-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
class="row items-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis fit"
|
||||||
:class="ui_store.is_mobile_mode ? 'full-height' : ''"
|
|
||||||
:tabindex="scope.tabindex"
|
:tabindex="scope.tabindex"
|
||||||
>
|
>
|
||||||
<q-icon
|
<q-icon
|
||||||
|
|
@ -305,7 +304,7 @@
|
||||||
no-error-icon
|
no-error-icon
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
:error="shift.has_error"
|
: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'"
|
:label-color="!shift.is_approved ? 'accent' : 'white'"
|
||||||
class="rounded-5 bg-dark"
|
class="rounded-5 bg-dark"
|
||||||
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (!shift.is_approved && !isTimesheetApproved ? '' : 'cursor-not-allowed inset-shadow')"
|
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (!shift.is_approved && !isTimesheetApproved ? '' : 'cursor-not-allowed inset-shadow')"
|
||||||
|
|
@ -338,7 +337,7 @@
|
||||||
no-error-icon
|
no-error-icon
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
:error="shift.has_error"
|
: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'"
|
: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-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'text-white cursor-not-allowed q-px-sm' : '')"
|
||||||
input-style="font-size: 1.2em;"
|
input-style="font-size: 1.2em;"
|
||||||
|
|
|
||||||
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' : ''"
|
:class="approved ? 'text-white' : ''"
|
||||||
:style="'font-size: ' + weekday_font_size"
|
: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>
|
||||||
<span
|
<span
|
||||||
class="col-auto text-weight-bolder"
|
class="col-auto text-weight-bolder"
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
:class="approved ? 'text-white' : ''"
|
:class="approved ? 'text-white' : ''"
|
||||||
:style="'font-size: ' + weekday_font_size"
|
: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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
const q = useQuasar();
|
const q = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ui_store = useUiStore();
|
const uiStore = useUiStore();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const mode = inject<'normal' | 'approval'>('mode');
|
const mode = inject<'normal' | 'approval'>('mode');
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
dense: true,
|
dense: true,
|
||||||
borderless: shift.value.is_approved && isTimesheetApproved,
|
borderless: shift.value.is_approved && isTimesheetApproved,
|
||||||
readonly: shift.value.is_approved && isTimesheetApproved,
|
readonly: shift.value.is_approved && isTimesheetApproved,
|
||||||
optionsDense: !ui_store.is_mobile_mode,
|
optionsDense: !q.platform.is.mobile,
|
||||||
hideDropdownIcon: true,
|
hideDropdownIcon: true,
|
||||||
menuOffset: [0, 10],
|
menuOffset: [0, 10],
|
||||||
menuAnchor: "bottom middle",
|
menuAnchor: "bottom middle",
|
||||||
|
|
@ -169,11 +169,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (ui_store.focus_next_component) {
|
if (uiStore.focusNextComponent) {
|
||||||
selectRef.value?.focus();
|
selectRef.value?.focus();
|
||||||
selectRef.value?.showPopup();
|
selectRef.value?.showPopup();
|
||||||
shiftTypeSelected.value = undefined;
|
shiftTypeSelected.value = undefined;
|
||||||
ui_store.focus_next_component = false;
|
uiStore.focusNextComponent = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -238,7 +238,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="row items-center text-uppercase rounded-5"
|
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 -->
|
<!-- shift type -->
|
||||||
<q-select
|
<q-select
|
||||||
|
|
@ -251,7 +251,7 @@
|
||||||
<template #selected-item="scope">
|
<template #selected-item="scope">
|
||||||
<div
|
<div
|
||||||
class="row items-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
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"
|
:tabindex="scope.tabindex"
|
||||||
>
|
>
|
||||||
<q-icon
|
<q-icon
|
||||||
|
|
@ -386,18 +386,18 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="row full-height"
|
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 -->
|
<!-- comment button -->
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="!ui_store.is_mobile_mode"
|
v-if="!$q.platform.is.mobile"
|
||||||
push
|
push
|
||||||
dense
|
dense
|
||||||
:color="shift.is_approved ? 'white' : (shift.comment ? 'accent' : (holiday ? 'purple-5' : 'blue-grey-5'))"
|
:color="shift.is_approved ? 'white' : (shift.comment ? 'accent' : (holiday ? 'purple-5' : 'blue-grey-5'))"
|
||||||
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
||||||
:text-color="shift.is_approved ? (holiday ? 'purple-5' : 'accent') : 'white'"
|
:text-color="shift.is_approved ? (holiday ? 'purple-5' : 'accent') : 'white'"
|
||||||
class="col"
|
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
|
<q-badge
|
||||||
v-if="shift.comment"
|
v-if="shift.comment"
|
||||||
|
|
|
||||||
|
|
@ -27,32 +27,32 @@
|
||||||
const q = useQuasar();
|
const q = useQuasar();
|
||||||
const { extractDate } = date;
|
const { extractDate } = date;
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
const ui_store = useUiStore();
|
const uiStore = useUiStore();
|
||||||
const timesheet_api = useTimesheetApi();
|
const timesheetApi = useTimesheetApi();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheetStore = useTimesheetStore();
|
||||||
|
|
||||||
const mobile_animation_direction = ref('fadeInLeft');
|
const mobileAnimationDirection = ref('fadeInLeft');
|
||||||
const currentDayComponent = ref<HTMLElement[] | null>(null);
|
const currentDayComponent = ref<HTMLElement[] | null>(null);
|
||||||
const currentDayComponentWatcher = ref(currentDayComponent);
|
const currentDayComponentWatcher = ref(currentDayComponent);
|
||||||
const employeeEmail = inject<string>('employeeEmail');
|
const employeeEmail = inject<string>('employeeEmail');
|
||||||
|
|
||||||
// ========== computed ========================================
|
// ========== 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 ========================================
|
// ========== methods ========================================
|
||||||
|
|
||||||
const addNewShift = (day_shifts: Shift[], date: string, timesheet_id: number) => {
|
const addNewShift = (day_shifts: Shift[], date: string, timesheet_id: number) => {
|
||||||
ui_store.focus_next_component = true;
|
uiStore.focusNextComponent = true;
|
||||||
const new_shift = new Shift;
|
const newShift = new Shift;
|
||||||
new_shift.date = date;
|
newShift.date = date;
|
||||||
new_shift.timesheet_id = timesheet_id;
|
newShift.timesheet_id = timesheet_id;
|
||||||
day_shifts.push(new_shift);
|
day_shifts.push(newShift);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteUnsavedShift = (timesheet_index: number, day_index: number) => {
|
const deleteUnsavedShift = (timesheet_index: number, day_index: number) => {
|
||||||
if (timesheet_store.timesheets !== undefined) {
|
if (timesheetStore.timesheets !== undefined) {
|
||||||
const day = timesheet_store.timesheets[timesheet_index]!.days[day_index]!;
|
const day = timesheetStore.timesheets[timesheet_index]!.days[day_index]!;
|
||||||
const shifts_without_deleted_shift = day.shifts.filter(shift => shift.id !== 0);
|
const shifts_without_deleted_shift = day.shifts.filter(shift => shift.id !== 0);
|
||||||
day.shifts = shifts_without_deleted_shift;
|
day.shifts = shifts_without_deleted_shift;
|
||||||
}
|
}
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHolidayName = (date: string) => {
|
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 (!holiday) return;
|
||||||
|
|
||||||
if (locale.value === 'fr-FR')
|
if (locale.value === 'fr-FR')
|
||||||
|
|
@ -79,11 +79,11 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickApplyWeeklyPreset = async (timesheet_id: number) => {
|
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 () => {
|
onMounted(async () => {
|
||||||
await timesheet_store.getCurrentFederalHolidays();
|
await timesheetStore.getCurrentFederalHolidays();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(currentDayComponentWatcher, () => {
|
watch(currentDayComponentWatcher, () => {
|
||||||
|
|
@ -99,9 +99,10 @@
|
||||||
:class="$q.platform.is.mobile ? 'column no-wrap q-pb-lg' : 'row'"
|
:class="$q.platform.is.mobile ? 'column no-wrap q-pb-lg' : 'row'"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="timesheet, timesheet_index of timesheet_store.timesheets"
|
v-for="timesheet, timesheet_index of timesheetStore.timesheets"
|
||||||
:key="timesheet.timesheet_id"
|
: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
|
<transition
|
||||||
appear
|
appear
|
||||||
|
|
@ -109,7 +110,7 @@
|
||||||
leave-active-class="animated fadeOutUp"
|
leave-active-class="animated fadeOutUp"
|
||||||
>
|
>
|
||||||
<q-btn
|
<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)"
|
:disable="!timesheet.days.every(day => day.shifts.length < 1)"
|
||||||
flat
|
flat
|
||||||
dense
|
dense
|
||||||
|
|
@ -127,7 +128,7 @@
|
||||||
|
|
||||||
<transition-group
|
<transition-group
|
||||||
appear
|
appear
|
||||||
:enter-active-class="`animated ${animation_style}`"
|
:enter-active-class="`animated ${animationStyle}`"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="day, day_index in timesheet.days"
|
v-for="day, day_index in timesheet.days"
|
||||||
|
|
@ -138,7 +139,7 @@
|
||||||
>
|
>
|
||||||
<!-- optional label indicating which holiday if today is a holiday -->
|
<!-- optional label indicating which holiday if today is a holiday -->
|
||||||
<span
|
<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"
|
class="absolute-top-left text-uppercase text-weight-bolder text-purple-5"
|
||||||
style="transform: translate(25px, -7px);"
|
style="transform: translate(25px, -7px);"
|
||||||
>
|
>
|
||||||
|
|
@ -209,11 +210,11 @@
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="col row full-width rounded-10 ellipsis shadow-10"
|
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
|
<div
|
||||||
class="col row"
|
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 -->
|
<!-- Date block -->
|
||||||
<ShiftListDateWidget
|
<ShiftListDateWidget
|
||||||
|
|
@ -226,7 +227,7 @@
|
||||||
:timesheet-id="timesheet.timesheet_id"
|
:timesheet-id="timesheet.timesheet_id"
|
||||||
:week-day-index="day_index"
|
:week-day-index="day_index"
|
||||||
:day="day"
|
: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"
|
:approved="getDayApproval(day) || timesheet.is_approved"
|
||||||
class="col"
|
class="col"
|
||||||
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
||||||
|
|
@ -241,7 +242,7 @@
|
||||||
color="white"
|
color="white"
|
||||||
size="xl"
|
size="xl"
|
||||||
class="full-height"
|
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
|
<q-btn
|
||||||
|
|
@ -250,7 +251,7 @@
|
||||||
square
|
square
|
||||||
icon="more_time"
|
icon="more_time"
|
||||||
size="lg"
|
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"
|
text-color="white"
|
||||||
class="full-height"
|
class="full-height"
|
||||||
:class="$q.platform.is.mobile ? 'q-px-xs' : ''"
|
: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 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 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) =>
|
const totalHours = computed(() => timesheetStore.timesheets.reduce((sum, timesheet) =>
|
||||||
sum += timesheet.weekly_hours.regular
|
sum += timesheet.weekly_hours.regular
|
||||||
+ timesheet.weekly_hours.evening
|
+ timesheet.weekly_hours.evening
|
||||||
+ timesheet.weekly_hours.emergency
|
+ timesheet.weekly_hours.emergency
|
||||||
+ timesheet.weekly_hours.overtime,
|
+ timesheet.weekly_hours.overtime,
|
||||||
0) //initial value
|
0 //initial value
|
||||||
);
|
));
|
||||||
|
|
||||||
const totalExpenses = computed(() => timesheetStore.timesheets.reduce((sum, timesheet) =>
|
const totalExpenses = computed(() => timesheetStore.timesheets.reduce((sum, timesheet) =>
|
||||||
sum + timesheet.weekly_expenses.expenses
|
sum + timesheet.weekly_expenses.expenses
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,8 @@ export interface TotalExpenses {
|
||||||
on_call: number;
|
on_call: number;
|
||||||
mileage: 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 { Timesheet } from 'src/modules/timesheets/models/timesheet.models';
|
||||||
import type { PaidTimeOff } from 'src/modules/employee-list/models/employee-profile.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 { 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 FederalHoliday, TARGO_HOLIDAY_NAMES_FR } from 'src/modules/timesheets/models/federal-holidays.models';
|
||||||
import type { RouteNames } from 'src/router/router-constants';
|
import type { RouteNames } from 'src/router/router-constants';
|
||||||
import type { RouteRecordNameGeneric } from 'vue-router';
|
import type { RouteRecordNameGeneric } from 'vue-router';
|
||||||
|
|
@ -42,7 +42,7 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
const selected_employee_name = ref<string>();
|
const selected_employee_name = ref<string>();
|
||||||
const has_timesheet_preset = ref(false);
|
const has_timesheet_preset = ref(false);
|
||||||
const current_pay_period_overview = ref<TimesheetApprovalOverview>();
|
const current_pay_period_overview = ref<TimesheetApprovalOverview>();
|
||||||
const pay_period_report = ref();
|
const payPeriodReport = ref<Blob>();
|
||||||
const pay_period_observer = ref<EventSource | undefined>();
|
const pay_period_observer = ref<EventSource | undefined>();
|
||||||
|
|
||||||
const federal_holidays = ref<FederalHoliday[]>([]);
|
const federal_holidays = ref<FederalHoliday[]>([]);
|
||||||
|
|
@ -191,12 +191,16 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPayPeriodReport = async (report_filters: TimesheetApprovalCSVReportFilters) => {
|
const getPayPeriodReport = async (filters: CSVReportFilters): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
if (!pay_period.value) return false;
|
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;
|
const response = await timesheetApprovalService.getPayPeriodReportByYearAndPeriodNumber(pay_period.value.pay_year, pay_period.value.pay_period_no, filters);
|
||||||
return response.data;
|
|
||||||
|
if (response){
|
||||||
|
payPeriodReport.value = response;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('There was an error retrieving the report CSV: ', error);
|
console.error('There was an error retrieving the report CSV: ', error);
|
||||||
// TODO: More in-depth error-handling here
|
// TODO: More in-depth error-handling here
|
||||||
|
|
@ -256,6 +260,7 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
||||||
return {
|
return {
|
||||||
is_loading,
|
is_loading,
|
||||||
is_report_dialog_open,
|
is_report_dialog_open,
|
||||||
|
payPeriodReport,
|
||||||
is_details_dialog_open,
|
is_details_dialog_open,
|
||||||
pay_period,
|
pay_period,
|
||||||
pay_period_overviews,
|
pay_period_overviews,
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,27 @@
|
||||||
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { computed, ref } from 'vue';
|
import { LocalStorage, Dark } from 'quasar';
|
||||||
import { LocalStorage, useQuasar, Dark } from 'quasar';
|
|
||||||
import { Preferences } from 'src/modules/profile/models/preferences.models';
|
import { Preferences } from 'src/modules/profile/models/preferences.models';
|
||||||
import { ProfileService } from 'src/modules/profile/services/profile-service';
|
import { ProfileService } from 'src/modules/profile/services/profile-service';
|
||||||
import { RouteNames } from 'src/router/router-constants';
|
|
||||||
|
|
||||||
|
|
||||||
export const useUiStore = defineStore('ui', () => {
|
export const useUiStore = defineStore('ui', () => {
|
||||||
const q = useQuasar();
|
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
const is_left_drawer_open = ref(true);
|
const isLeftDrawerOpen = ref(true);
|
||||||
const focus_next_component = ref(false);
|
const focusNextComponent = ref(false);
|
||||||
const is_mobile_mode = computed(() => q.screen.lt.md);
|
const userPreferences = ref<Preferences>(new Preferences);
|
||||||
const user_preferences = ref<Preferences>(new Preferences);
|
|
||||||
const current_page = ref<RouteNames>(RouteNames.DASHBOARD);
|
|
||||||
|
|
||||||
|
|
||||||
const toggleRightDrawer = () => {
|
const toggleRightDrawer = () => {
|
||||||
is_left_drawer_open.value = !is_left_drawer_open.value;
|
isLeftDrawerOpen.value = !isLeftDrawerOpen.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUserPreferences = async () => {
|
const getUserPreferences = async () => {
|
||||||
try {
|
try {
|
||||||
const local_user_preferences = LocalStorage.getItem<Preferences>('user_preferences');
|
const local_userPreferences = LocalStorage.getItem<Preferences>('userPreferences');
|
||||||
|
|
||||||
if (local_user_preferences !== null) {
|
if (local_userPreferences !== null) {
|
||||||
if (local_user_preferences.id !== -1) {
|
if (local_userPreferences.id !== -1) {
|
||||||
Object.assign(user_preferences.value, local_user_preferences);
|
Object.assign(userPreferences.value, local_userPreferences);
|
||||||
setPreferences();
|
setPreferences();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -36,24 +30,24 @@ export const useUiStore = defineStore('ui', () => {
|
||||||
const response = await ProfileService.getUserPreferences();
|
const response = await ProfileService.getUserPreferences();
|
||||||
|
|
||||||
if (response.success && response.data) {
|
if (response.success && response.data) {
|
||||||
LocalStorage.setItem('user_preferences', response.data);
|
LocalStorage.setItem('userPreferences', response.data);
|
||||||
Object.assign(user_preferences.value, response.data);
|
Object.assign(userPreferences.value, response.data);
|
||||||
setPreferences();
|
setPreferences();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
user_preferences.value = new Preferences;
|
userPreferences.value = new Preferences;
|
||||||
console.error('Could not retrieve user preferences: ', error);
|
console.error('Could not retrieve user preferences: ', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateUserPreferences = async () => {
|
const updateUserPreferences = async () => {
|
||||||
try {
|
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) {
|
if (response.success && response.data) {
|
||||||
Object.assign(user_preferences.value, response.data);
|
Object.assign(userPreferences.value, response.data);
|
||||||
LocalStorage.setItem('user_preferences', response.data);
|
LocalStorage.setItem('userPreferences', response.data);
|
||||||
setPreferences();
|
setPreferences();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -63,19 +57,17 @@ export const useUiStore = defineStore('ui', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPreferences = () => {
|
const setPreferences = () => {
|
||||||
if (user_preferences.value !== undefined) {
|
if (userPreferences.value !== undefined) {
|
||||||
// if user_preferences.value.is_dark_mode === null
|
// if userPreferences.value.is_dark_mode === null
|
||||||
Dark.set(user_preferences.value.is_dark_mode ?? "auto");
|
Dark.set(userPreferences.value.is_dark_mode ?? "auto");
|
||||||
locale.value = user_preferences.value.display_language;
|
locale.value = userPreferences.value.display_language;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
current_page,
|
focusNextComponent,
|
||||||
is_mobile_mode,
|
isLeftDrawerOpen,
|
||||||
focus_next_component,
|
userPreferences,
|
||||||
is_left_drawer_open,
|
|
||||||
user_preferences,
|
|
||||||
toggleRightDrawer,
|
toggleRightDrawer,
|
||||||
getUserPreferences,
|
getUserPreferences,
|
||||||
updateUserPreferences,
|
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