From e44d790a21000fc9cba826edc38a484625dd40cf Mon Sep 17 00:00:00 2001 From: "Nic D." <91558719+Venti-Bear@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:52:45 -0500 Subject: [PATCH 1/7] fix(timesheet-approval): fix oversight to modify employee timesheet from approval module Fixed an oversight in the logic in one of the steps to update and create shifts from the timesheet approval page, which led to update or create requests being sent with the users credentials instead of the employees --- src/i18n/en-ca/index.ts | 3 ++ src/i18n/fr-ca/index.ts | 3 ++ .../components/timesheet-wrapper.vue | 36 ++++++++++++++----- .../timesheets/composables/use-shift-api.ts | 6 ++-- src/stores/shift-store.ts | 3 +- 5 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/i18n/en-ca/index.ts b/src/i18n/en-ca/index.ts index e7514ce..f256e88 100644 --- a/src/i18n/en-ca/index.ts +++ b/src/i18n/en-ca/index.ts @@ -224,6 +224,7 @@ export default { error: { no_data_found: "no data found", no_search_results: "no results matching search", + generic_error: "An error occured", }, label: { search: "search", @@ -284,6 +285,7 @@ export default { apply_preset: "auto-fill", apply_preset_day: "Apply schedule to day", apply_preset_week: "Apply schedule to week", + save_successful: "timesheets saved", nav_button: { calendar_date_picker: "Calendar", current_week: "This week", @@ -359,6 +361,7 @@ export default { SHIFT_TYPE_REQUIRED: "Shift type required", TIMESHEET_NOT_FOUND: "No timesheet found with provided data", UPDATE_ERROR: "Error while updating data", + ERROR_SAVING_SHIFTS: "Timesheet changes were not saved", }, }, diff --git a/src/i18n/fr-ca/index.ts b/src/i18n/fr-ca/index.ts index 13c402c..8ab3e7c 100644 --- a/src/i18n/fr-ca/index.ts +++ b/src/i18n/fr-ca/index.ts @@ -224,6 +224,7 @@ export default { error: { no_data_found: 'aucune donnée à afficher', no_search_results: 'aucun résultat ne correspond à la recherche', + generic_error: "Une erreur est survenue", }, label: { search: 'recherche', @@ -284,6 +285,7 @@ export default { apply_preset: "auto-remplir", apply_preset_day: "Appliquer horaire pour la journée", apply_preset_week: "Appliquer horaire pour la semaine", + save_successful: "feuilles de temps enregistrées", nav_button: { calendar_date_picker: "Calendrier", current_week: "Semaine actuelle", @@ -359,6 +361,7 @@ export default { SHIFT_TYPE_REQUIRED: "Type requis", TIMESHEET_NOT_FOUND: "Aucune feuille de temps ne correspond au détails fournis", UPDATE_ERROR: "Une erreur est survenu lors de la mise à jour", + ERROR_SAVING_SHIFTS: "les changements aux feuilles de temps n'ont pas été enregistrés", }, }, diff --git a/src/modules/timesheets/components/timesheet-wrapper.vue b/src/modules/timesheets/components/timesheet-wrapper.vue index 00318ba..1acb3ba 100644 --- a/src/modules/timesheets/components/timesheet-wrapper.vue +++ b/src/modules/timesheets/components/timesheet-wrapper.vue @@ -2,7 +2,6 @@ setup lang="ts" > - /* eslint-disable */ import ShiftList from 'src/modules/timesheets/components/shift-list.vue'; import ShiftListScrollable from 'src/modules/timesheets/components/shift-list-scrollable.vue'; import LoadingOverlay from 'src/modules/shared/components/loading-overlay.vue'; @@ -13,14 +12,15 @@ import ShiftListWeeklyOverview from 'src/modules/timesheets/components/shift-list-weekly-overview.vue'; import ShiftListWeeklyOverviewMobile from 'src/modules/timesheets/components/mobile/shift-list-weekly-overview-mobile.vue'; + import { useI18n } from 'vue-i18n'; import { computed, onMounted } from 'vue'; import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api'; import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api'; import { useExpensesStore } from 'src/stores/expense-store'; import { useTimesheetStore } from 'src/stores/timesheet-store'; - import { date } from 'quasar'; - + import { date, Notify } from 'quasar'; + const { t } = useI18n(); const expenses_store = useExpensesStore(); const timesheet_store = useTimesheetStore(); const timesheet_api = useTimesheetApi(); @@ -41,13 +41,25 @@ sum + timesheet.weekly_expenses.expenses + timesheet.weekly_expenses.on_call + timesheet.weekly_expenses.per_diem, - 0) //initial value - ); + 0 //initial value + )); const { mode = 'normal' } = defineProps<{ mode?: 'approval' | 'normal'; }>(); + const onClickSaveTimesheets = async () => { + if (mode === 'normal') { + await shift_api.saveShiftChanges(); + Notify.create({ + message: t('timesheet.save_successful'), + color: 'accent', + }); + } else { + await shift_api.saveShiftChanges(timesheet_store.current_pay_period_overview?.email); + } + } + onMounted(async () => { if (mode === 'normal') await timesheet_api.getTimesheetsByDate(date.formatDate(new Date(), 'YYYY-MM-DD')); @@ -97,7 +109,10 @@ v-if="!$q.platform.is.mobile" class="col-xs-6 col-md-4 col-xl-3 q-pa-md" > - + @@ -116,7 +131,10 @@ /> -
+
@@ -204,7 +222,7 @@ :label="$t('shared.label.save')" class="col-auto absolute-bottom shadow-up-10" style="height: 50px;" - @click="shift_api.saveShiftChanges" + @click="onClickSaveTimesheets" /> { timesheet_store.is_loading = false; }; - const saveShiftChanges = async () => { + const saveShiftChanges = async (employee_email?: string) => { timesheet_store.is_loading = true; - const update_success = await shift_store.updateShifts(); - const create_success = await shift_store.createNewShifts(); + const update_success = await shift_store.updateShifts(employee_email); + const create_success = await shift_store.createNewShifts(employee_email); if (create_success || update_success){ await timesheet_store.getTimesheetsByOptionalEmployeeEmail(auth_store.user?.email ?? ''); diff --git a/src/stores/shift-store.ts b/src/stores/shift-store.ts index f2f62f9..db4ede5 100644 --- a/src/stores/shift-store.ts +++ b/src/stores/shift-store.ts @@ -53,8 +53,7 @@ export const useShiftStore = defineStore('shift_store', () => { return true; } } - - Notify.create('No shifts to update') + return false; } catch (error) { Notify.create('Error updating shifts'); From 42de823b870dcde58820edbf11362ab867b5b193 Mon Sep 17 00:00:00 2001 From: "Nic D." <91558719+Venti-Bear@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:05:47 -0500 Subject: [PATCH 2/7] fix(date-time-utils): fix rounding error in util method that converts hours float to HHmm string --- src/utils/date-and-time-utils.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/utils/date-and-time-utils.ts b/src/utils/date-and-time-utils.ts index 3360f77..52a4758 100644 --- a/src/utils/date-and-time-utils.ts +++ b/src/utils/date-and-time-utils.ts @@ -21,7 +21,13 @@ export const getMinutes = (hours: number) => { } export const getHoursMinutesStringFromHoursFloat = (hours: number): string => { - const flat_hours = Math.floor(hours); - const minutes = Math.round((hours - flat_hours) * 60); + let flat_hours = Math.floor(hours); + let minutes = Math.round((hours - flat_hours) * 60); + + if (minutes === 60) { + flat_hours += 1; + minutes = 0; + } + return `${flat_hours}h${minutes > 1 ? ' ' + minutes : ''}` } \ No newline at end of file From 8231b3084da9ca4d4778d8134c6b82466bce4b7f Mon Sep 17 00:00:00 2001 From: "Nic D." <91558719+Venti-Bear@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:08:53 -0500 Subject: [PATCH 3/7] fix(timesheets): fix error where shift payload is not sent when modify employee timesheets from approval page --- src/modules/timesheets/services/shift-service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/timesheets/services/shift-service.ts b/src/modules/timesheets/services/shift-service.ts index aef90ec..5abf09a 100644 --- a/src/modules/timesheets/services/shift-service.ts +++ b/src/modules/timesheets/services/shift-service.ts @@ -15,7 +15,7 @@ export const ShiftService = { createNewShifts: async (new_shifts: Shift[], employee_email?: string):Promise> => { if (employee_email) { - const response = await api.post(`/shift/create/${employee_email}`); + const response = await api.post(`/shift/create/${employee_email}`, new_shifts); return response.data; } @@ -25,7 +25,7 @@ export const ShiftService = { updateShifts: async (existing_shifts: Shift[], employee_email?: string):Promise> => { if (employee_email) { - const response = await api.patch(`/shift/update/${employee_email}`); + const response = await api.patch(`/shift/update/${employee_email}`, existing_shifts); return response.data; } From 75060a22282f4df26439083ddae7394b473fc27f Mon Sep 17 00:00:00 2001 From: "Nic D." <91558719+Venti-Bear@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:41:24 -0500 Subject: [PATCH 4/7] fix(timesheet-approval): minor fix to timesheet reload after changes to employee timesheet was reloading users timesheets instead of employees after update or create --- src/modules/timesheets/composables/use-shift-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/timesheets/composables/use-shift-api.ts b/src/modules/timesheets/composables/use-shift-api.ts index ef1df28..6b6176e 100644 --- a/src/modules/timesheets/composables/use-shift-api.ts +++ b/src/modules/timesheets/composables/use-shift-api.ts @@ -25,7 +25,7 @@ export const useShiftApi = () => { const create_success = await shift_store.createNewShifts(employee_email); if (create_success || update_success){ - await timesheet_store.getTimesheetsByOptionalEmployeeEmail(auth_store.user?.email ?? ''); + await timesheet_store.getTimesheetsByOptionalEmployeeEmail(employee_email); } timesheet_store.is_loading = false; From 1cac8966be081be6f6b4cf2d15ae5dcc825fe202 Mon Sep 17 00:00:00 2001 From: "Nic D." <91558719+Venti-Bear@users.noreply.github.com> Date: Mon, 26 Jan 2026 11:09:50 -0500 Subject: [PATCH 5/7] fix(timesheet-approval): fix issue where expense wouldn't show correct expense type an refuse to update --- .../components/expense-dialog-form.vue | 22 ++++++++++++++----- .../components/expense-dialog-list-item.vue | 2 +- .../timesheets/composables/use-expense-api.ts | 11 +++++----- .../timesheets/composables/use-shift-api.ts | 4 +--- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/modules/timesheets/components/expense-dialog-form.vue b/src/modules/timesheets/components/expense-dialog-form.vue index 6ae8811..1dc7f97 100644 --- a/src/modules/timesheets/components/expense-dialog-form.vue +++ b/src/modules/timesheets/components/expense-dialog-form.vue @@ -33,6 +33,8 @@ const period_start_date = computed(() => timesheet_store.pay_period?.period_start.replaceAll('-', '/') ?? ''); const period_end_date = computed(() => timesheet_store.pay_period?.period_end.replaceAll('-', '/') ?? ''); + const expense = defineModel({ default: new Expense(new Date().toISOString().slice(0, 10)) }) + const expense_options: ExpenseOption[] = [ { label: t('timesheet.expense.types.PER_DIEM'), value: 'PER_DIEM', icon: getExpenseIcon('PER_DIEM') }, { label: t('timesheet.expense.types.EXPENSES'), value: 'EXPENSES', icon: getExpenseIcon('EXPENSES') }, @@ -55,7 +57,10 @@ } const requestExpenseCreationOrUpdate = async () => { - await expenses_api.upsertExpense(expenses_store.current_expense); + if (expenses_store.mode === 'update') + await expenses_api.upsertExpense(expenses_store.current_expense, timesheet_store.current_pay_period_overview?.email); + else + await expenses_api.upsertExpense(expenses_store.current_expense); expenses_store.is_showing_create_form = true; expenses_store.mode = 'create'; @@ -63,13 +68,18 @@ }; onMounted(() => { - expense_selected.value = expense_options.find(expense => expense.value === expenses_store.current_expense.type); + if (expense.value) + expense_selected.value = expense_options.find(expense_option => expense_option.value === expense.value.type); + else + expense_selected.value = expense_options[1]; + + console.log('expense mount triggered: current expense type is ', expenses_store.current_expense.type, ', matching option is: ', expense_selected.value); }) - + \ No newline at end of file diff --git a/src/modules/timesheets/composables/use-expense-api.ts b/src/modules/timesheets/composables/use-expense-api.ts index d5ea046..e4b3485 100644 --- a/src/modules/timesheets/composables/use-expense-api.ts +++ b/src/modules/timesheets/composables/use-expense-api.ts @@ -8,18 +8,19 @@ export const useExpensesApi = () => { const expenses_store = useExpensesStore(); const timesheet_store = useTimesheetStore(); - const upsertExpense = async (expense: Expense): Promise => { - const success = await expenses_store.upsertExpense(expense); + const upsertExpense = async (expense: Expense, employee_email?: string): Promise => { + const success = await expenses_store.upsertExpense(expense, employee_email); + if (success) { expenses_store.current_expense = new Expense(date.formatDate( new Date(), 'YYYY-MM-DD')); - timesheet_store.getTimesheetsByOptionalEmployeeEmail(); + timesheet_store.getTimesheetsByOptionalEmployeeEmail(employee_email); } }; - const deleteExpenseById = async (expense_id: number): Promise => { + const deleteExpenseById = async (expense_id: number, employee_email?: string): Promise => { const success = await expenses_store.deleteExpenseById(expense_id); if (success) { - timesheet_store.getTimesheetsByOptionalEmployeeEmail(); + timesheet_store.getTimesheetsByOptionalEmployeeEmail(employee_email); } }; diff --git a/src/modules/timesheets/composables/use-shift-api.ts b/src/modules/timesheets/composables/use-shift-api.ts index 6b6176e..2696836 100644 --- a/src/modules/timesheets/composables/use-shift-api.ts +++ b/src/modules/timesheets/composables/use-shift-api.ts @@ -1,18 +1,16 @@ -import { useAuthStore } from "src/stores/auth-store"; import { useShiftStore } from "src/stores/shift-store"; import { useTimesheetStore } from "src/stores/timesheet-store"; export const useShiftApi = () => { const timesheet_store = useTimesheetStore(); const shift_store = useShiftStore(); - const auth_store = useAuthStore(); const deleteShiftById = async (shift_id: number, employee_email?: string) => { timesheet_store.is_loading = true; const success = await shift_store.deleteShiftById(shift_id, employee_email); if (success) { - await timesheet_store.getTimesheetsByOptionalEmployeeEmail(auth_store.user?.email ?? ''); + await timesheet_store.getTimesheetsByOptionalEmployeeEmail(employee_email); } timesheet_store.is_loading = false; From b0de761645064869df9df1001bc04eb250fd7ce5 Mon Sep 17 00:00:00 2001 From: "Nic D." <91558719+Venti-Bear@users.noreply.github.com> Date: Mon, 26 Jan 2026 12:16:52 -0500 Subject: [PATCH 6/7] fix(timesheet-approval): overview list now updates when closing details dialog. Graphs at top of details dialog now update dynamically if user makes changes to employee timesheet. --- .../details-dialog-chart-expenses.vue | 36 ++++++------- .../details-dialog-chart-hours-worked.vue | 32 +++++------- .../details-dialog-chart-shift-types.vue | 52 ++++++++++--------- .../components/details-dialog.vue | 1 + .../components/expense-dialog-form.vue | 2 - 5 files changed, 56 insertions(+), 67 deletions(-) diff --git a/src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue b/src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue index 2147d13..aac41f0 100644 --- a/src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue +++ b/src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue @@ -2,12 +2,12 @@ setup lang="ts" > - import { onMounted, ref } from 'vue'; + import { computed, ref } from 'vue'; import { Bar } from 'vue-chartjs'; import { useI18n } from 'vue-i18n'; import { useQuasar, colors } from 'quasar'; import { useTimesheetStore } from 'src/stores/timesheet-store'; - import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale, type ChartDataset } from 'chart.js'; + import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale } from 'chart.js'; const { t } = useI18n(); const $q = useQuasar(); @@ -19,27 +19,21 @@ const timesheet_store = useTimesheetStore(); - const all_days = timesheet_store.timesheets.flatMap(week => week.days.flatMap(day => day.daily_expenses)); + const all_days = computed(() => timesheet_store.timesheets.flatMap(week => week.days.flatMap(day => day.daily_expenses))); const expenses_labels = ref(timesheet_store.timesheets.flatMap(week => week.days.map(day => day.date.slice(-5,)))); - const expenses_dataset = ref[]>([]); - - onMounted(() => { - setTimeout(() => { - expenses_dataset.value = [ - { - label: t('timesheet_approvals.table.expenses'), - data: all_days.map(day => (day.expenses + day.on_call + day.per_diem)), - backgroundColor: colors.getPaletteColor('accent'), - }, - { - label: t('timesheet_approvals.table.mileage'), - data: all_days.map(day => day.mileage), - backgroundColor: colors.getPaletteColor('info'), - } - ] - }, 100) - }); + const expenses_dataset = computed(() => [ + { + label: t('timesheet_approvals.table.expenses'), + data: all_days.value.map(day => (day.expenses + day.on_call + day.per_diem)), + backgroundColor: colors.getPaletteColor('accent'), + }, + { + label: t('timesheet_approvals.table.mileage'), + data: all_days.value.map(day => day.mileage), + backgroundColor: colors.getPaletteColor('info'), + } + ]);