From a2103a306b7b2755a8575e4b2cf6043a78a62ff9 Mon Sep 17 00:00:00 2001 From: "Nic D." <91558719+Venti-Bear@users.noreply.github.com> Date: Thu, 11 Dec 2025 16:59:35 -0500 Subject: [PATCH 1/4] feat(schedule-preset): can now add new presets and assign them to employees. Also fixed issue with with backend not treating first_work_day properly when creating new employee. Also did some optimizing for the employee list table with consistent sorting and better list mode. --- .../components/add-modify-dialog-schedule.vue | 1 + .../components/employee-list-table.vue | 12 ++++++------ .../models/employee-profile.models.ts | 3 +-- .../services/employee-list-service.ts | 2 +- src/stores/employee-store.ts | 9 +++++---- src/stores/schedule-presets.store.ts | 16 ++++++++-------- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/modules/employee-list/components/add-modify-dialog-schedule.vue b/src/modules/employee-list/components/add-modify-dialog-schedule.vue index a156bfd..237d286 100644 --- a/src/modules/employee-list/components/add-modify-dialog-schedule.vue +++ b/src/modules/employee-list/components/add-modify-dialog-schedule.vue @@ -28,6 +28,7 @@ import { useEmployeeListApi } from '../composables/use-employee-api'; 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); }); diff --git a/src/modules/employee-list/components/employee-list-table.vue b/src/modules/employee-list/components/employee-list-table.vue index 1461281..b533e4d 100644 --- a/src/modules/employee-list/components/employee-list-table.vue +++ b/src/modules/employee-list/components/employee-list-table.vue @@ -15,7 +15,7 @@ const employee_store = useEmployeeStore(); const timesheet_store = useTimesheetStore(); const ui_store = useUiStore(); - const visible_columns = ref<(keyof EmployeeProfile)[]>(['first_name', 'email', 'company_name', 'supervisor_full_name', 'company_name', 'job_title', 'last_work_day']); + const visible_columns = ref<(keyof EmployeeProfile)[]>(['first_name', 'email', 'job_title', 'last_work_day']); const table_grid_container = ref(null); @@ -72,12 +72,12 @@ :columns="employee_list_columns" row-key="email" :rows-per-page-options="[0]" - :pagination="{ sortBy: 'last_work_day', descending: true, }" + :pagination="{ sortBy: 'first_name' }" :filter="filters" :filter-method="filterEmployeeRows" class="bg-transparent no-shadow sticky-header-table" :style="$q.screen.lt.md ? '' : 'width: 80vw;'" - :table-class="$q.dark.isActive ? 'q-py-none q-mx-md rounded-10 bg-dark shadow-10' : 'q-py-none q-mx-md rounded-10 bg-white shadow-10'" + :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" table-header-class="text-accent text-uppercase" card-container-class="justify-center" @@ -183,15 +183,15 @@ >
[] = [ label: 'employee_list.table.role', field: 'job_title', align: 'left', - sortable: true, }, { name: 'last_work_day', diff --git a/src/modules/employee-list/services/employee-list-service.ts b/src/modules/employee-list/services/employee-list-service.ts index 0c546e1..d2c0c7d 100644 --- a/src/modules/employee-list/services/employee-list-service.ts +++ b/src/modules/employee-list/services/employee-list-service.ts @@ -19,7 +19,7 @@ export const EmployeeListService = { return response.data; }, - createNewEmployee: async (profile: Omit): Promise> => { + createNewEmployee: async (profile: Omit): Promise> => { const response = await api.post('employees/create', profile); return response.data; }, diff --git a/src/stores/employee-store.ts b/src/stores/employee-store.ts index 0fbf103..7a243f9 100644 --- a/src/stores/employee-store.ts +++ b/src/stores/employee-store.ts @@ -13,18 +13,19 @@ export const useEmployeeStore = defineStore('employee', () => { const is_loading = ref(false); const openAddModifyDialog = async (employee_email?: string) => { - is_add_modify_dialog_open.value = true; - + 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); is_loading.value = false; + is_add_modify_dialog_open.value = true; } const closeAddModifyDialog = () => { @@ -68,7 +69,7 @@ export const useEmployeeStore = defineStore('employee', () => { let response; if (management_mode.value === 'add_employee') { - const { birth_date, external_payroll_id, last_work_day, ...create_payload} = profile; + const { birth_date, last_work_day, ...create_payload} = profile; response = await EmployeeListService.createNewEmployee(create_payload); } else { diff --git a/src/stores/schedule-presets.store.ts b/src/stores/schedule-presets.store.ts index 8dc72a4..434db22 100644 --- a/src/stores/schedule-presets.store.ts +++ b/src/stores/schedule-presets.store.ts @@ -10,14 +10,6 @@ export const useSchedulePresetsStore = defineStore('schedule_presets_store', () const current_schedule_preset = ref(new SchedulePresetFrontend); const is_manager_open = ref(false); - const setCurrentSchedulePreset = (preset_id: number) => { - 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)!) - }; - const openSchedulePresetManager = (preset_id: number) => { if (preset_id === -1) { current_schedule_preset.value = new SchedulePresetFrontend; @@ -27,6 +19,14 @@ export const useSchedulePresetsStore = defineStore('schedule_presets_store', () is_manager_open.value = true; }; + + const setCurrentSchedulePreset = (preset_id: number) => { + 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)!) + }; const createSchedulePreset = async (preset: SchedulePreset): Promise => { try { From 34f1ce5762a6ada68e2b1425499b8910e3719036 Mon Sep 17 00:00:00 2001 From: Nicolas Drolet Date: Fri, 12 Dec 2025 14:54:25 -0500 Subject: [PATCH 2/4] fix(presets): fix issue with shifts in preset editing getting sorted reactively, due to shifts being unsorted from backend. Backend now sorts shifts before sending to front. --- src/i18n/en-ca/index.ts | 4 + src/i18n/fr-ca/index.ts | 4 + .../add-modify-dialog-schedule-preview.vue | 4 +- .../components/add-modify-dialog-schedule.vue | 52 +++++++++---- .../schedule-presets-dialog-delete.vue | 75 +++++++++++++++++++ ...dialog.vue => schedule-presets-dialog.vue} | 7 ++ .../composables/use-employee-api.ts | 11 ++- .../models/schedule-presets.models.ts | 4 +- .../services/schedule-presets-service.ts | 2 +- src/pages/dashboard-page.vue | 22 +++++- src/stores/schedule-presets.store.ts | 10 ++- 11 files changed, 168 insertions(+), 27 deletions(-) create mode 100644 src/modules/employee-list/components/schedule-presets-dialog-delete.vue rename src/modules/employee-list/components/{schedule_presets_dialog.vue => schedule-presets-dialog.vue} (90%) diff --git a/src/i18n/en-ca/index.ts b/src/i18n/en-ca/index.ts index 61cd1d9..10ac12f 100644 --- a/src/i18n/en-ca/index.ts +++ b/src/i18n/en-ca/index.ts @@ -21,9 +21,13 @@ export default { access_label: "access", details_label: "details", schedule_label: "schedule", + enter_delete_input: "type 'DELETE' to remove", schedule_presets: { preset_list_placeholder: "Select a schedule", preset_name_placeholder: "schedule preset name", + delete_warning: "", + 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.", }, module_access: { dashboard: "Dashboard", diff --git a/src/i18n/fr-ca/index.ts b/src/i18n/fr-ca/index.ts index 402646e..459feab 100644 --- a/src/i18n/fr-ca/index.ts +++ b/src/i18n/fr-ca/index.ts @@ -21,9 +21,13 @@ export default { access_label: "accès", details_label: "détails", schedule_label: "horaire", + enter_delete_input: "tappez 'SUPPRIMER' pour confirmer", schedule_presets: { preset_list_placeholder: "Sélectionner un horaire", preset_name_placeholder: "nom de l'horaire", + delete_warning: "Êtes-vous certain de vouloir supprimer cet horaire?", + delete_warning_employee_1: "Cet horaire est présentement utilisé par", + delete_warning_employee_2: "La suppression n'affectera pas leurs feuilles de temps antérieures, mais ils ne pourront plus appliquer cet horaire à leurs feuilles de temps à partir de maintenant.", }, module_access: { dashboard: "Accueil", diff --git a/src/modules/employee-list/components/add-modify-dialog-schedule-preview.vue b/src/modules/employee-list/components/add-modify-dialog-schedule-preview.vue index 65c761a..631909e 100644 --- a/src/modules/employee-list/components/add-modify-dialog-schedule-preview.vue +++ b/src/modules/employee-list/components/add-modify-dialog-schedule-preview.vue @@ -2,7 +2,7 @@ setup lang="ts" > - import { date } from 'quasar'; + // import { date } from 'quasar'; import { useSchedulePresetsStore } from 'src/stores/schedule-presets.store'; const schedule_preset_store = useSchedulePresetsStore(); @@ -27,7 +27,7 @@ import { useSchedulePresetsStore } from 'src/stores/schedule-presets.store'; {{ $t(`shared.weekday.${weekday.day.toLowerCase()}`) }}
diff --git a/src/modules/employee-list/components/add-modify-dialog-schedule.vue b/src/modules/employee-list/components/add-modify-dialog-schedule.vue index 237d286..2cf2c51 100644 --- a/src/modules/employee-list/components/add-modify-dialog-schedule.vue +++ b/src/modules/employee-list/components/add-modify-dialog-schedule.vue @@ -3,13 +3,14 @@ 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 SchedulePresetsDialog from 'src/modules/employee-list/components/schedule-presets-dialog.vue'; import AddModifyDialogSchedulePreview from './add-modify-dialog-schedule-preview.vue'; - import { onMounted, ref } from 'vue'; + import { onMounted, ref, watch } 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 { useEmployeeListApi } from '../composables/use-employee-api'; + import type { PresetManagerMode } from 'src/modules/employee-list/models/schedule-presets.models'; const schedule_preset_store = useSchedulePresetsStore(); const employee_store = useEmployeeStore(); @@ -17,6 +18,7 @@ import { useEmployeeListApi } from '../composables/use-employee-api'; const preset_options = ref<{ label: string, value: number }[]>([]); const current_preset = ref<{ label: string | undefined, value: number }>({ label: undefined, value: -1 }); + const manager_watcher = ref(schedule_preset_store.is_manager_open); const getPresetOptions = (): { label: string, value: number }[] => { const options = schedule_preset_store.schedule_presets.map(preset => { return { label: preset.name, value: preset.id } }); @@ -24,16 +26,31 @@ import { useEmployeeListApi } from '../composables/use-employee-api'; return options; }; - onMounted(() => { + const onClickSchedulePresetManager = (mode: PresetManagerMode, preset_id?: number) => { + schedule_preset_store.schedule_preset_dialog_mode = mode; + console.log('preset id: ', preset_id); + schedule_preset_store.openSchedulePresetManager(preset_id ?? current_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); + }; + + onMounted(() => { + loadSelectedPresetOption(); }); + + watch(manager_watcher, loadSelectedPresetOption) \ No newline at end of file From c5cf6becdade2f517d8ed3435ecfa8743632e8d3 Mon Sep 17 00:00:00 2001 From: Nicolas Drolet Date: Mon, 15 Dec 2025 14:17:37 -0500 Subject: [PATCH 4/4] feat(employee-list): add check for overlap when creating or editing schedule presets module complete for staging --- .../components/add-modify-dialog-schedule.vue | 18 ++++---- .../components/add-modify-dialog.vue | 3 +- .../components/employee-list-table.vue | 9 ++-- .../schedule-presets-dialog-row.vue | 18 +++++++- .../components/schedule-presets-dialog.vue | 14 ++++-- .../composables/use-employee-api.ts | 46 +++++++++++++++---- .../models/schedule-presets.models.ts | 2 + src/modules/timesheets/utils/shift.util.ts | 11 +++-- src/stores/schedule-presets.store.ts | 25 ++++------ src/utils/table-grid-FLIP.ts | 31 ------------- 10 files changed, 99 insertions(+), 78 deletions(-) delete mode 100644 src/utils/table-grid-FLIP.ts diff --git a/src/modules/employee-list/components/add-modify-dialog-schedule.vue b/src/modules/employee-list/components/add-modify-dialog-schedule.vue index 2cf2c51..9bedebd 100644 --- a/src/modules/employee-list/components/add-modify-dialog-schedule.vue +++ b/src/modules/employee-list/components/add-modify-dialog-schedule.vue @@ -28,7 +28,6 @@ const onClickSchedulePresetManager = (mode: PresetManagerMode, preset_id?: number) => { schedule_preset_store.schedule_preset_dialog_mode = mode; - console.log('preset id: ', preset_id); schedule_preset_store.openSchedulePresetManager(preset_id ?? current_preset.value.value); } @@ -83,27 +82,28 @@
+ +
diff --git a/src/modules/employee-list/components/employee-list-table.vue b/src/modules/employee-list/components/employee-list-table.vue index b533e4d..fb01de5 100644 --- a/src/modules/employee-list/components/employee-list-table.vue +++ b/src/modules/employee-list/components/employee-list-table.vue @@ -10,7 +10,6 @@ import { useEmployeeStore } from 'src/stores/employee-store'; import { useTimesheetStore } from 'src/stores/timesheet-store'; import { employee_list_columns, type EmployeeProfile, type EmployeeListFilters } from 'src/modules/employee-list/models/employee-profile.models'; - import { animateFlip } from 'src/utils/table-grid-FLIP'; const employee_store = useEmployeeStore(); const timesheet_store = useTimesheetStore(); @@ -50,8 +49,6 @@ }); } - animateFlip(table_grid_container); - return result; }; @@ -114,9 +111,10 @@ { icon: 'view_list', value: false }, ]" /> +
+
('shift', { required: true }); const shift_type_selected = ref(SHIFT_OPTIONS[0]); + + defineProps<{ + error: boolean; + }>(); defineEmits<{ - 'click-delete': [void]; + 'clickDelete': [void]; + 'blurTimeField': [void]; }>(); @@ -72,6 +77,8 @@ hide-bottom-space type="time" class="text-uppercase weekday-field" + :error="error" + @blur="$emit('blurTimeField')" >