refactor(employee-list): rework schedule preset selector, fix other ui issues, see notes
Add warning dialog when changes in an employee profile are unsaved. Move schedule preset buttons from side of selector to side of each option in select menu. optimize behavior of selector: will now switch to empty when deleting currently assigned preset, and will assign automatically any new or copied preset to current employee.
This commit is contained in:
parent
1c6f7fd155
commit
6dc1804918
|
|
@ -109,10 +109,11 @@ export default {
|
|||
banked_hours: "available banked hours",
|
||||
sick_hours: "available PTO hours",
|
||||
vacation_hours: "available vacation hours",
|
||||
save_changes_notification: "save changes to employee profile?",
|
||||
schedule_presets: {
|
||||
preset_list_placeholder: "Select a schedule",
|
||||
preset_name_placeholder: "schedule preset name",
|
||||
delete_warning: "",
|
||||
delete_warning: "Are you certain you wish to delete this schedule?",
|
||||
delete_warning_employee_1: "This schedule is used by",
|
||||
delete_warning_employee_2: "Deleting this preset will not affect previous timesheets, but they will no longer be able to apply this preset to their timesheets going forward.",
|
||||
},
|
||||
|
|
@ -237,11 +238,13 @@ export default {
|
|||
cancel: "cancel",
|
||||
update: "update",
|
||||
modify: "modify",
|
||||
copy: "copy",
|
||||
close: "close",
|
||||
download: "download",
|
||||
open: "open",
|
||||
day: "day",
|
||||
empty: "empty",
|
||||
name: "name",
|
||||
},
|
||||
misc: {
|
||||
or: "or",
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ export default {
|
|||
banked_hours: "heures en banque disponibles",
|
||||
sick_hours: "heures d'absence payées disponibles",
|
||||
vacation_hours: "heures de vacances disponibles",
|
||||
save_changes_notification: "Sauvegarder les modifications du profil?",
|
||||
schedule_presets: {
|
||||
preset_list_placeholder: "Sélectionner un horaire",
|
||||
preset_name_placeholder: "nom de l'horaire",
|
||||
|
|
@ -237,11 +238,13 @@ export default {
|
|||
cancel: "annuler",
|
||||
update: "mettre à jour",
|
||||
modify: "modifier",
|
||||
copy: "copier",
|
||||
close: "fermer",
|
||||
download: "télécharger",
|
||||
open: "ouvrir",
|
||||
day: "jour",
|
||||
empty: "vide",
|
||||
name: "nom",
|
||||
},
|
||||
misc: {
|
||||
or: "ou",
|
||||
|
|
|
|||
|
|
@ -2,43 +2,58 @@
|
|||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import HorizontalSlideTransition from 'src/modules/shared/components/horizontal-slide-transition.vue';
|
||||
import SchedulePresetsDialog from 'src/modules/employee-list/components/schedule-presets-dialog.vue';
|
||||
import AddModifyDialogSchedulePreview from './add-modify-dialog-schedule-preview.vue';
|
||||
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useSchedulePresetsStore } from 'src/stores/schedule-presets.store';
|
||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
import { useEmployeeListApi } from '../composables/use-employee-api';
|
||||
import type { PresetManagerMode } from 'src/modules/employee-list/models/schedule-presets.models';
|
||||
import type { QSelectOption } from 'quasar';
|
||||
|
||||
// ================= state ======================
|
||||
// ========== state ========================================
|
||||
|
||||
const { t } = useI18n();
|
||||
const schedule_preset_store = useSchedulePresetsStore();
|
||||
const employee_store = useEmployeeStore();
|
||||
const employee_list_api = useEmployeeListApi();
|
||||
|
||||
const preset_options = ref<{ label: string, value: number }[]>([]);
|
||||
const current_preset = ref<{ label: string | undefined, value: number }>({ label: undefined, value: -1 });
|
||||
const preset_options = ref<QSelectOption<number>[]>([]);
|
||||
const selected_preset = ref<QSelectOption<number>>({ label: '', value: -1 });
|
||||
|
||||
// ====================== methods ========================
|
||||
// ========== methods ========================================
|
||||
|
||||
const getPresetOptions = (): QSelectOption<number>[] => {
|
||||
const options: QSelectOption<number>[] = [{ label: t('shared.label.empty'), value: -1 }];
|
||||
schedule_preset_store.schedule_presets.forEach(preset => {
|
||||
options.push({ label: preset.name, value: preset.id })
|
||||
});
|
||||
options.push({ label: '', value: 0 });
|
||||
|
||||
const getPresetOptions = (): { label: string, value: number }[] => {
|
||||
const options = schedule_preset_store.schedule_presets.map(preset => { return { label: preset.name, value: preset.id } });
|
||||
options.push({ label: 'Aucun', value: -1 });
|
||||
return options;
|
||||
};
|
||||
|
||||
const onClickSchedulePresetManager = (mode: PresetManagerMode, preset_id?: number) => {
|
||||
schedule_preset_store.schedule_preset_dialog_mode = mode;
|
||||
schedule_preset_store.openSchedulePresetManager(preset_id ?? current_preset.value.value);
|
||||
schedule_preset_store.openSchedulePresetManager(preset_id ?? selected_preset.value.value);
|
||||
}
|
||||
|
||||
const loadSelectedPresetOption = () => {
|
||||
preset_options.value = getPresetOptions();
|
||||
const current_option = preset_options.value.find(option => option.value === employee_store.employee.preset_id);
|
||||
current_preset.value = current_option ?? { label: undefined, value: -1 };
|
||||
schedule_preset_store.setCurrentSchedulePreset(current_preset.value.value);
|
||||
const employee = employee_store.employee;
|
||||
|
||||
if (!employee.preset_id)
|
||||
selected_preset.value = preset_options.value[0]!;
|
||||
|
||||
else
|
||||
selected_preset.value = preset_options.value.find(opt =>
|
||||
opt.value === employee.preset_id
|
||||
)!;
|
||||
|
||||
schedule_preset_store.setCurrentSchedulePreset(selected_preset.value.value);
|
||||
schedule_preset_store.schedule_preset_dialog_mode = undefined;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
|
@ -48,76 +63,111 @@
|
|||
|
||||
<template>
|
||||
<div
|
||||
:key="schedule_preset_store.is_manager_open === false ? '0' : '1'"
|
||||
:key="schedule_preset_store.isManagerOpen === false ? '0' : '1'"
|
||||
class="column full-width flex-center items-start"
|
||||
>
|
||||
<SchedulePresetsDialog @before-hide="loadSelectedPresetOption"/>
|
||||
<SchedulePresetsDialog @on-close="loadSelectedPresetOption" />
|
||||
|
||||
<div class="col row justify-center full-width no-wrap">
|
||||
<div class="col row flex-center full-width no-wrap">
|
||||
<q-select
|
||||
v-model="current_preset"
|
||||
standout="bg-accent"
|
||||
v-model="selected_preset"
|
||||
:options="getPresetOptions()"
|
||||
standout="bg-primary"
|
||||
dense
|
||||
options-dense
|
||||
rounded
|
||||
color="accent"
|
||||
:options="getPresetOptions()"
|
||||
class="col-xs-10 col-md-7"
|
||||
popup-content-class="text-uppercase text-weight-medium rounded-20"
|
||||
popup-content-style="border: 2px solid var(--q-accent)"
|
||||
menu-anchor="bottom middle"
|
||||
menu-self="top middle"
|
||||
:menu-offset="[0, 10]"
|
||||
:menu-offset="[0, 5]"
|
||||
class="col-xs-10 col-md-7"
|
||||
popup-content-class="text-uppercase text-weight-medium rounded-20 shadow-24"
|
||||
popup-content-style="border: 2px solid var(--q-primary)"
|
||||
style="font-size: 1.4em;"
|
||||
@update:modelValue="option => employee_list_api.setSchedulePreset(option.value)"
|
||||
>
|
||||
<template #selected>
|
||||
<span
|
||||
class="text-uppercase text-center text-weight-bold full-width"
|
||||
:style="current_preset.label === undefined ? 'opacity: 0.5;' : ''"
|
||||
:style="selected_preset.label === undefined ? 'opacity: 0.5;' : ''"
|
||||
>
|
||||
{{ current_preset.label === undefined ?
|
||||
{{ selected_preset.label === undefined ?
|
||||
$t('employee_management.schedule_presets.preset_list_placeholder') :
|
||||
current_preset.label }}
|
||||
selected_preset.label }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #option="scope">
|
||||
<q-item
|
||||
v-if="scope.opt.value !== 0"
|
||||
v-bind="scope.itemProps"
|
||||
class="row flex-center"
|
||||
style="font-size: 1.2em;"
|
||||
>
|
||||
<div class="col">
|
||||
<span>{{ scope.label }}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="scope.opt.value > 0"
|
||||
class="row items-center no-wrap"
|
||||
>
|
||||
|
||||
<!-- edit currently-selected preset -->
|
||||
<div class="col-auto q-px-sm">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
icon="edit"
|
||||
color="accent"
|
||||
@click.stop="onClickSchedulePresetManager('update', scope.opt.value)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- copy currently-selected preset -->
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
icon="content_copy"
|
||||
color="accent"
|
||||
@click.stop="onClickSchedulePresetManager('copy', scope.opt.value)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- delete currently-selected preset -->
|
||||
<div class="col-auto q-px-xs">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
color="negative"
|
||||
icon="las la-trash"
|
||||
class="q-py-none"
|
||||
@click.stop="onClickSchedulePresetManager('delete', scope.opt.value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
v-else
|
||||
class="q-pa-none"
|
||||
>
|
||||
<q-btn
|
||||
square
|
||||
icon="add"
|
||||
size="md"
|
||||
color="accent"
|
||||
class="full-width q-py-none"
|
||||
:label="$t('shared.label.add')"
|
||||
style="font-size: 1.2em;"
|
||||
@click.stop="onClickSchedulePresetManager('create', -1)"
|
||||
/>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<q-btn
|
||||
icon="add"
|
||||
color="accent"
|
||||
class="col-auto q-px-sm q-ml-sm rounded-50"
|
||||
@click="onClickSchedulePresetManager('create', -1)"
|
||||
/>
|
||||
|
||||
<HorizontalSlideTransition :show="current_preset !== undefined && current_preset?.value !== -1">
|
||||
<div class="col-auto row no-wrap full-height">
|
||||
<q-btn
|
||||
icon="edit"
|
||||
color="accent"
|
||||
class="col-auto q-px-sm q-ml-sm rounded-50"
|
||||
@click="onClickSchedulePresetManager('update')"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
icon="content_copy"
|
||||
color="accent"
|
||||
class="col-auto q-px-sm q-mx-sm rounded-50"
|
||||
@click="onClickSchedulePresetManager('copy')"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
rounded
|
||||
icon="clear"
|
||||
color="negative"
|
||||
class="col-auto q-px-sm full-height"
|
||||
@click="onClickSchedulePresetManager('delete')"
|
||||
/>
|
||||
</div>
|
||||
</HorizontalSlideTransition>
|
||||
|
||||
</div>
|
||||
<AddModifyDialogSchedulePreview :current-preset-id="current_preset.value" />
|
||||
|
||||
<AddModifyDialogSchedulePreview :current-preset-id="selected_preset.value" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -8,21 +8,52 @@
|
|||
|
||||
import { ref } from 'vue';
|
||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
import { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
|
||||
// ========== state ========================================
|
||||
|
||||
const employee_store = useEmployeeStore();
|
||||
const current_step = ref<'form' | 'access' | 'schedule'>('form');
|
||||
const initial_employee_profile = ref(new EmployeeProfile)
|
||||
const initial_employee_details = ref('');
|
||||
const is_showing_close_confirm = ref(false);
|
||||
|
||||
// ========== methods ========================================
|
||||
|
||||
const onBeforeHide = () => {
|
||||
const current_employee_details = JSON.stringify(employee_store.employee);
|
||||
|
||||
if (initial_employee_details.value !== current_employee_details)
|
||||
is_showing_close_confirm.value = true;
|
||||
else
|
||||
employee_store.is_add_modify_dialog_open = false;
|
||||
};
|
||||
|
||||
const onBeforeShow = () => {
|
||||
current_step.value = 'form';
|
||||
initial_employee_details.value = JSON.stringify(employee_store.employee);
|
||||
};
|
||||
|
||||
const onClickSaveChanges = async () => {
|
||||
const success = await employee_store.createOrUpdateEmployee(employee_store.employee);
|
||||
|
||||
if (success)
|
||||
closeAllDialogs();
|
||||
}
|
||||
|
||||
const closeAllDialogs = () => {
|
||||
is_showing_close_confirm.value = false;
|
||||
employee_store.is_add_modify_dialog_open = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog
|
||||
v-model="employee_store.is_add_modify_dialog_open"
|
||||
:model-value="employee_store.is_add_modify_dialog_open"
|
||||
full-width
|
||||
full-height
|
||||
@beforeShow="current_step = 'form'"
|
||||
@show="Object.assign(initial_employee_profile, employee_store.employee)"
|
||||
backdrop-filter="blur(4px)"
|
||||
class="shadow-24"
|
||||
@beforeShow="onBeforeShow"
|
||||
@update:model-value="onBeforeHide"
|
||||
>
|
||||
<div
|
||||
class="column bg-secondary rounded-10 no-wrap"
|
||||
|
|
@ -112,8 +143,50 @@
|
|||
color="accent"
|
||||
:label="employee_store.management_mode === 'add_employee' ? $t('shared.label.save') : $t('shared.label.update')"
|
||||
class="col-auto q-py-sm shadow-up-5"
|
||||
@click="employee_store.createOrUpdateEmployee(employee_store.employee)"
|
||||
@click="onClickSaveChanges"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- dialog to confirm if you want to save changes -->
|
||||
<q-dialog
|
||||
v-model="is_showing_close_confirm"
|
||||
persistent
|
||||
backdrop-filter="blur(4px)"
|
||||
>
|
||||
<q-card class="q-pa-md shadow-24">
|
||||
<span class="text-uppercase text-weight-light text-h6">{{ $t('employee_management.save_changes_notification') }}</span>
|
||||
|
||||
<div class="row full-width">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="lg"
|
||||
:label="$t('shared.label.cancel')"
|
||||
class="col"
|
||||
@click="is_showing_close_confirm = false"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="lg"
|
||||
color="negative"
|
||||
:label="$t('shared.misc.no')"
|
||||
class="col"
|
||||
@click="closeAllDialogs"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="lg"
|
||||
color="accent"
|
||||
:label="$t('shared.misc.yes')"
|
||||
class="col"
|
||||
@click="onClickSaveChanges"
|
||||
/>
|
||||
</div>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
|
@ -147,7 +147,6 @@
|
|||
color="accent"
|
||||
bg-color="white"
|
||||
label-color="accent"
|
||||
class="text-primary"
|
||||
debounce="300"
|
||||
:label="$t('shared.label.search')"
|
||||
>
|
||||
|
|
@ -298,4 +297,8 @@ tbody {
|
|||
:deep(.q-table__grid-content) {
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
:deep(.q-field__native) {
|
||||
color: var(--q-primary);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -6,17 +6,37 @@
|
|||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-employee-api';
|
||||
|
||||
const employee_store = useEmployeeStore();
|
||||
const employee_list_api = useEmployeeListApi();
|
||||
// ========== state ===================================
|
||||
|
||||
const { presetId } = defineProps<{
|
||||
presetId: number;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'onConfirmDelete': [void];
|
||||
}>();
|
||||
|
||||
const employee_store = useEmployeeStore();
|
||||
const employee_list_api = useEmployeeListApi();
|
||||
|
||||
const employee_amount_using_preset = ref(0);
|
||||
const delete_input_string = ref('');
|
||||
|
||||
// ========== computed ==================================
|
||||
|
||||
const is_approve_deletion = computed(() => ['SUPPRIMER', 'DELETE'].includes(delete_input_string.value));
|
||||
|
||||
// ========== methods ===================================
|
||||
|
||||
const onClickDeleteConfirm = async () => {
|
||||
const success = await employee_list_api.deleteSchedulePreset(presetId);
|
||||
|
||||
if (success && employee_store.employee.preset_id === presetId)
|
||||
employee_store.employee.preset_id = null;
|
||||
|
||||
emit('onConfirmDelete');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const employees_with_preset = employee_store.employee_list.filter(employee => employee.preset_id === presetId);
|
||||
employee_amount_using_preset.value = employees_with_preset.length;
|
||||
|
|
@ -68,7 +88,7 @@
|
|||
:color="is_approve_deletion ? 'negative' : 'grey-6'"
|
||||
:label="$t('shared.label.remove')"
|
||||
class="q-px-md"
|
||||
@click="employee_list_api.deleteSchedulePreset(presetId)"
|
||||
@click="onClickDeleteConfirm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,18 +10,35 @@
|
|||
import { useSchedulePresetsStore } from 'src/stores/schedule-presets.store';
|
||||
import { isShiftOverlap } from 'src/modules/timesheets/utils/shift.util';
|
||||
|
||||
const schedule_preset_store = useSchedulePresetsStore();
|
||||
const emit = defineEmits<{
|
||||
'onClose': [void];
|
||||
}>();
|
||||
|
||||
const schedulePresetStore = useSchedulePresetsStore();
|
||||
const employee_list_api = useEmployeeListApi();
|
||||
|
||||
const onClickSaveSchedulePreset = async () => {
|
||||
const success = await employee_list_api.saveSchedulePreset();
|
||||
|
||||
if (success)
|
||||
closePresetManager();
|
||||
}
|
||||
|
||||
const closePresetManager = () => {
|
||||
emit('onClose');
|
||||
schedulePresetStore.isManagerOpen = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog
|
||||
v-model="schedule_preset_store.is_manager_open"
|
||||
v-model="schedulePresetStore.isManagerOpen"
|
||||
full-width
|
||||
>
|
||||
<SchedulePresetsDialogDelete
|
||||
v-if="schedule_preset_store.schedule_preset_dialog_mode === 'delete'"
|
||||
:preset-id="schedule_preset_store.current_schedule_preset.id"
|
||||
v-if="schedulePresetStore.schedule_preset_dialog_mode === 'delete'"
|
||||
:preset-id="schedulePresetStore.current_schedule_preset.id"
|
||||
@on-confirm-delete="closePresetManager"
|
||||
/>
|
||||
|
||||
<div
|
||||
|
|
@ -34,24 +51,23 @@
|
|||
style="border-radius: 8px 8px 0 0;"
|
||||
>
|
||||
<span class="row col-auto text-uppercase text-weight-bold text-white q-py-sm">{{
|
||||
schedule_preset_store.current_schedule_preset.id === -1 ?
|
||||
schedulePresetStore.current_schedule_preset.id === -1 ?
|
||||
$t('shared.label.add') :
|
||||
$t('shared.label.modify') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row col-auto q-px-sm flex-center full-width q-py-sm">
|
||||
<div class="col-8 bg-dark rounded-10 ellipsis">
|
||||
<div class="row col-auto q-px-sm flex-center full-width q-pt-md">
|
||||
<div class="col-8 bg-dark rounded-10 shadow-2 ellipsis">
|
||||
<q-input
|
||||
v-model="schedule_preset_store.current_schedule_preset.name"
|
||||
v-model="schedulePresetStore.current_schedule_preset.name"
|
||||
standout
|
||||
dense
|
||||
hide-bottom-space
|
||||
:placeholder="$t('employee_management.schedule_presets.preset_name_placeholder')"
|
||||
class="text-uppercase"
|
||||
input-class="text-weight-bold text-center"
|
||||
>
|
||||
<template #before>
|
||||
<template #prepend>
|
||||
<q-icon
|
||||
name="edit"
|
||||
color="accent"
|
||||
|
|
@ -62,12 +78,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="schedule_preset_store.schedule_preset_dialog_mode !== 'copy'"
|
||||
class="column col full-width q-py-sm q-px-lg no-wrap scroll"
|
||||
>
|
||||
<div class="column col full-width q-py-sm q-px-lg no-wrap scroll">
|
||||
<div
|
||||
v-for="weekday of schedule_preset_store.current_schedule_preset.weekdays"
|
||||
v-for="weekday of schedulePresetStore.current_schedule_preset.weekdays"
|
||||
:key="weekday.day"
|
||||
class="row col-auto items-center q-my-xs shadow-2 bg-dark rounded-10 ellipsis"
|
||||
style="min-height: 50px;"
|
||||
|
|
@ -106,14 +119,14 @@
|
|||
<div class="col-auto row self-end q-px-lg q-mt-sm full-width">
|
||||
<q-space />
|
||||
<q-btn
|
||||
:disable="schedule_preset_store.current_schedule_preset.name === ''"
|
||||
:disable="schedulePresetStore.current_schedule_preset.name === ''"
|
||||
push
|
||||
dense
|
||||
:color="schedule_preset_store.current_schedule_preset.name === '' ? 'grey-7' : 'accent'"
|
||||
:color="schedulePresetStore.current_schedule_preset.name === '' ? 'grey-7' : 'accent'"
|
||||
icon="download"
|
||||
:label="$t('shared.label.save')"
|
||||
class="col-auto q-px-md q-mb-sm"
|
||||
@click="employee_list_api.saveSchedulePreset"
|
||||
@click="onClickSaveSchedulePreset"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,76 +4,95 @@ import { SchedulePreset } from "../models/schedule-presets.models";
|
|||
import { isShiftOverlap } from "src/modules/timesheets/utils/shift.util";
|
||||
|
||||
export const useEmployeeListApi = () => {
|
||||
const employee_store = useEmployeeStore();
|
||||
const schedule_preset_store = useSchedulePresetsStore();
|
||||
const employeeStore = useEmployeeStore();
|
||||
const schedulePresetStore = useSchedulePresetsStore();
|
||||
|
||||
/**
|
||||
* Populates the employee store with the exhaustive list of all employees past and present
|
||||
* Also populates the schedule preset store with all schedule presets currently available.
|
||||
*/
|
||||
const getEmployeeList = async (): Promise<void> => {
|
||||
employee_store.is_loading = true;
|
||||
employeeStore.is_loading = true;
|
||||
|
||||
const success = await employee_store.getEmployeeList();
|
||||
if (success) await schedule_preset_store.findSchedulePresetList();
|
||||
const success = await employeeStore.getEmployeeList();
|
||||
if (success) await schedulePresetStore.getSchedulePresetList();
|
||||
|
||||
employee_store.is_loading = false;
|
||||
employeeStore.is_loading = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assigns the details of a specific employee to the employee store. If the employee has a
|
||||
* schedule preset assigned, it also assign that preset to the schedule preset store.
|
||||
*
|
||||
* @param email email associated to employee.
|
||||
*/
|
||||
const getEmployeeDetails = async (email: string): Promise<void> => {
|
||||
const success = await employee_store.getEmployeeDetails(email);
|
||||
if (success && employee_store.employee.preset_id !== null) {
|
||||
schedule_preset_store.setCurrentSchedulePreset(employee_store.employee.preset_id ?? -1);
|
||||
const success = await employeeStore.getEmployeeDetails(email);
|
||||
if (success && employeeStore.employee.preset_id !== null) {
|
||||
schedulePresetStore.setCurrentSchedulePreset(employeeStore.employee.preset_id ?? -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified preset as the current preset in the schedule preset store and also
|
||||
* applies it to the current employee in the employee store. If a negative ID is provided, the
|
||||
* employee's assigned preset is set to null.
|
||||
*
|
||||
* @param preset_id - the preset id currently selected
|
||||
*/
|
||||
const setSchedulePreset = (preset_id: number) => {
|
||||
schedule_preset_store.setCurrentSchedulePreset(preset_id);
|
||||
employee_store.employee.preset_id = preset_id < 0 ? null : preset_id;
|
||||
schedulePresetStore.setCurrentSchedulePreset(preset_id);
|
||||
employeeStore.employee.preset_id = preset_id < 0 ? null : preset_id;
|
||||
}
|
||||
|
||||
const saveSchedulePreset = async () => {
|
||||
// Get the currently edited schedule preset from the store (frontend model)
|
||||
const preset = schedule_preset_store.current_schedule_preset;
|
||||
|
||||
// Check if there's any overlap between shifts. If there is, is_error property
|
||||
// will be toggled to true and save process will stop
|
||||
for (const weekday of preset.weekdays) {
|
||||
weekday.is_error = isShiftOverlap(weekday.shifts);
|
||||
}
|
||||
/**
|
||||
* Validates and converts the current schedule preset into a model that the backend
|
||||
* can ingest and save, then sends a request to create or update preset.
|
||||
*
|
||||
* @returns `true` if the preset is valid and was successfully saved, `false` otherwise.
|
||||
*/
|
||||
const saveSchedulePreset = async (): Promise<boolean> => {
|
||||
const preset = schedulePresetStore.current_schedule_preset;
|
||||
preset.weekdays.forEach(weekday => weekday.is_error = isShiftOverlap(weekday.shifts));
|
||||
|
||||
if (preset.weekdays.some(weekday => weekday.is_error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Flatten all weekday shifts into a single array
|
||||
const preset_shifts = preset.weekdays.flatMap(weekday => weekday.shifts);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build a backend-compatible SchedulePreset instance
|
||||
const preset_shifts = preset.weekdays.flatMap(weekday => weekday.shifts);
|
||||
const backend_preset = new SchedulePreset(
|
||||
preset.id,
|
||||
preset.name,
|
||||
preset.id,
|
||||
preset.name,
|
||||
preset_shifts
|
||||
);
|
||||
|
||||
// Track whether the create/update operation succeeds
|
||||
let success = false;
|
||||
|
||||
// Create a new preset if it has no backend ID, otherwise update the existing one
|
||||
if (preset.id === -1)
|
||||
success = await schedule_preset_store.createSchedulePreset(backend_preset);
|
||||
else
|
||||
success = await schedule_preset_store.updateSchedulePreset(backend_preset);
|
||||
if (preset.id === -1)
|
||||
success = await schedulePresetStore.createSchedulePreset(backend_preset);
|
||||
else
|
||||
success = await schedulePresetStore.updateSchedulePreset(backend_preset);
|
||||
|
||||
// On success, refresh the preset list and close the preset manager UI
|
||||
if (success) {
|
||||
await schedule_preset_store.findSchedulePresetList();
|
||||
schedule_preset_store.is_manager_open = false;
|
||||
await schedulePresetStore.getSchedulePresetList();
|
||||
employeeStore.employee.preset_id = schedulePresetStore.current_schedule_preset.id;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
const deleteSchedulePreset = async (preset_id: number) => {
|
||||
const success = await schedule_preset_store.deleteSchedulePreset(preset_id);
|
||||
if (success) {
|
||||
await schedule_preset_store.findSchedulePresetList();
|
||||
schedule_preset_store.is_manager_open = false;
|
||||
}
|
||||
/**
|
||||
* Sends request to delete the preset associated to the provided ID.
|
||||
*
|
||||
* @param preset_id Backend ID of preset to delete
|
||||
* @return `true` if successfully deleted, `false` otherwise.
|
||||
*/
|
||||
const deleteSchedulePreset = async (preset_id: number): Promise<boolean> => {
|
||||
const success = await schedulePresetStore.deleteSchedulePreset(preset_id);
|
||||
|
||||
if (success)
|
||||
await schedulePresetStore.getSchedulePresetList();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,4 @@ export const useEmployeeProfileRules = () => {
|
|||
}
|
||||
}
|
||||
|
||||
export const company_options = [
|
||||
{ label: 'Targo', value: 'Targo' },
|
||||
{ label: 'Solucom', value: 'Solucom' },
|
||||
]
|
||||
export const company_options = ['Targo', 'Solucom'];
|
||||
|
|
@ -3,7 +3,7 @@ import type { SchedulePreset } from "src/modules/employee-list/models/schedule-p
|
|||
import type { BackendResponse } from "src/modules/shared/models/backend-response.models";
|
||||
|
||||
export const SchedulePresetsService = {
|
||||
createSchedulePresets: async (preset: SchedulePreset) => {
|
||||
createSchedulePresets: async (preset: SchedulePreset): Promise<BackendResponse<SchedulePreset>> => {
|
||||
const response = await api.post(`/schedule-presets/create/`, preset);
|
||||
return response.data;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ export const useEmployeeStore = defineStore('employee', () => {
|
|||
const is_loading = ref(false);
|
||||
|
||||
const openAddModifyDialog = async (employee_email?: string) => {
|
||||
|
||||
|
||||
if (employee_email === undefined) {
|
||||
management_mode.value = 'add_employee'
|
||||
employee.value = new EmployeeProfile();
|
||||
is_add_modify_dialog_open.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
is_loading.value = true;
|
||||
management_mode.value = 'modify_employee';
|
||||
await getEmployeeDetails(employee_email);
|
||||
|
|
@ -65,25 +65,29 @@ export const useEmployeeStore = defineStore('employee', () => {
|
|||
return false;
|
||||
};
|
||||
|
||||
const createOrUpdateEmployee = async (profile: EmployeeProfile) => {
|
||||
const createOrUpdateEmployee = async (profile: EmployeeProfile): Promise<boolean> => {
|
||||
let response;
|
||||
|
||||
if (management_mode.value === 'add_employee') {
|
||||
const { birth_date, last_work_day, ...create_payload} = profile;
|
||||
const { birth_date, last_work_day, ...create_payload } = profile;
|
||||
response = await EmployeeListService.createNewEmployee(create_payload);
|
||||
} else {
|
||||
|
||||
|
||||
response = await EmployeeListService.updateEmployee(profile);
|
||||
}
|
||||
|
||||
closeAddModifyDialog();
|
||||
|
||||
if (response.success) await getEmployeeList();
|
||||
else {
|
||||
if (response.success)
|
||||
await getEmployeeList();
|
||||
|
||||
else
|
||||
Notify.create({
|
||||
message: 'failed to update or create employee',
|
||||
color: 'negative',
|
||||
})}
|
||||
message: 'failed to update or create employee',
|
||||
color: 'negative',
|
||||
});
|
||||
|
||||
return response.success;
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -7,39 +7,49 @@ import { type PresetManagerMode, SchedulePreset, SchedulePresetFrontend } from "
|
|||
export const useSchedulePresetsStore = defineStore('schedule_presets_store', () => {
|
||||
const schedule_presets = ref<SchedulePreset[]>([new SchedulePreset]);
|
||||
const current_schedule_preset = ref<SchedulePresetFrontend>(new SchedulePresetFrontend);
|
||||
const schedule_preset_dialog_mode = ref<PresetManagerMode>('create');
|
||||
const is_manager_open = ref(false);
|
||||
const schedule_preset_dialog_mode = ref<PresetManagerMode | undefined>();
|
||||
const isManagerOpen = ref(false);
|
||||
|
||||
/**
|
||||
* Opens the schedule preset manager with the preset associated with provided ID. If mode is
|
||||
* set to `copy`, a clone of the preset will be created with a blank name instead.
|
||||
* @param preset_id
|
||||
*/
|
||||
const openSchedulePresetManager = (preset_id: number) => {
|
||||
if (preset_id === -1)
|
||||
if (preset_id === -1) {
|
||||
current_schedule_preset.value = new SchedulePresetFrontend;
|
||||
else if (schedule_preset_dialog_mode.value === 'copy') {
|
||||
} else if (schedule_preset_dialog_mode.value === 'copy') {
|
||||
const preset = schedule_presets.value.find(preset => preset.id === preset_id)!;
|
||||
const copied_preset = new SchedulePresetFrontend(preset);
|
||||
copied_preset.id = -1;
|
||||
copied_preset.name = "";
|
||||
current_schedule_preset.value = copied_preset;
|
||||
}
|
||||
else
|
||||
} else
|
||||
setCurrentSchedulePreset(preset_id);
|
||||
|
||||
is_manager_open.value = true;
|
||||
isManagerOpen.value = true;
|
||||
};
|
||||
|
||||
const setCurrentSchedulePreset = (preset_id: number) => {
|
||||
if (preset_id === -1) {
|
||||
if (preset_id === -1)
|
||||
current_schedule_preset.value = new SchedulePresetFrontend;
|
||||
return;
|
||||
}
|
||||
current_schedule_preset.value = new SchedulePresetFrontend(schedule_presets.value.find(preset => preset.id === preset_id))
|
||||
else
|
||||
current_schedule_preset.value = new SchedulePresetFrontend(
|
||||
schedule_presets.value.find(preset => preset.id === preset_id)
|
||||
);
|
||||
};
|
||||
|
||||
const createSchedulePreset = async (preset: SchedulePreset): Promise<boolean> => {
|
||||
try {
|
||||
const response = await SchedulePresetsService.createSchedulePresets(preset);
|
||||
|
||||
if (response.success && response.data)
|
||||
current_schedule_preset.value = new SchedulePresetFrontend(response.data);
|
||||
|
||||
return response.success;
|
||||
} catch (error) {
|
||||
console.error('DEV ERROR || error while creating schedule preset: ', error);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
@ -47,9 +57,11 @@ export const useSchedulePresetsStore = defineStore('schedule_presets_store', ()
|
|||
const updateSchedulePreset = async (preset: SchedulePreset): Promise<boolean> => {
|
||||
try {
|
||||
const response = await SchedulePresetsService.updateSchedulePresets(preset);
|
||||
|
||||
return response.success;
|
||||
} catch (error) {
|
||||
console.error('DEV ERROR || error while updating schedule preset: ', error);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -58,17 +70,20 @@ export const useSchedulePresetsStore = defineStore('schedule_presets_store', ()
|
|||
const deleteSchedulePreset = async (preset_id: number): Promise<boolean> => {
|
||||
try {
|
||||
const response = await SchedulePresetsService.deleteSchedulePresets(preset_id);
|
||||
|
||||
return response.success;
|
||||
} catch (error) {
|
||||
console.error('DEV ERROR || error while deleting schedule preset: ', error);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const findSchedulePresetList = async (): Promise<boolean> => {
|
||||
const getSchedulePresetList = async (): Promise<boolean> => {
|
||||
try {
|
||||
const response = await SchedulePresetsService.getSchedulePresetsList();
|
||||
if (response.success && response.data) schedule_presets.value = response.data;
|
||||
if (response.success && response.data)
|
||||
schedule_presets.value = response.data;
|
||||
|
||||
return response.success;
|
||||
} catch (error) {
|
||||
|
|
@ -82,12 +97,12 @@ export const useSchedulePresetsStore = defineStore('schedule_presets_store', ()
|
|||
schedule_presets,
|
||||
current_schedule_preset,
|
||||
schedule_preset_dialog_mode,
|
||||
is_manager_open,
|
||||
isManagerOpen,
|
||||
setCurrentSchedulePreset,
|
||||
openSchedulePresetManager,
|
||||
createSchedulePreset,
|
||||
updateSchedulePreset,
|
||||
deleteSchedulePreset,
|
||||
findSchedulePresetList,
|
||||
getSchedulePresetList,
|
||||
}
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user