From 2eae6e5a211005467657d30e0b0556633d3829f5 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 29 Aug 2025 16:29:07 -0400 Subject: [PATCH 1/7] feat(timesheet): created interfaces and access button to access and employee timesheet --- src/i18n/en-ca/index.ts | 1 + src/i18n/fr-ca/index.ts | 1 + .../components/navigation/right-drawer.vue | 11 ++++ .../timesheets/pages/timesheet-temp-page.vue | 54 +++++++++++++++++++ .../timesheets/services/timesheet-services.ts | 9 ++++ .../types/timesheet-details-interface.ts | 9 +++- .../timesheets/types/timesheet-interface.ts | 9 ++++ src/router/router-constants.ts | 1 + src/router/routes.ts | 5 ++ src/stores/timesheet-store.ts | 31 +++++++++++ 10 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/modules/timesheets/types/timesheet-interface.ts diff --git a/src/i18n/en-ca/index.ts b/src/i18n/en-ca/index.ts index 71b4552..836035f 100644 --- a/src/i18n/en-ca/index.ts +++ b/src/i18n/en-ca/index.ts @@ -26,6 +26,7 @@ export default { userMenuHome: 'Homepage', userMenuEmployeeList: 'Employee Directory', userMenuShiftValidation: 'Timesheet Approval', + userMenuTimesheetTemp: 'Timesheet', userMenuProfile: 'Profile', userMenuHelp: 'Help', userMenuLogout: 'Log Out', diff --git a/src/i18n/fr-ca/index.ts b/src/i18n/fr-ca/index.ts index 2008d4a..11dee1a 100644 --- a/src/i18n/fr-ca/index.ts +++ b/src/i18n/fr-ca/index.ts @@ -165,6 +165,7 @@ export default { userMenuHome: 'Accueil', userMenuEmployeeList: 'Répertoire employés', userMenuShiftValidation: 'Valider les heures', + userMenuTimesheetTemp: 'Carte de temps', userMenuProfile: 'Profil', userMenuHelp: 'Aide', userMenuLogout: 'Déconnexion', diff --git a/src/modules/shared/components/navigation/right-drawer.vue b/src/modules/shared/components/navigation/right-drawer.vue index e1352b1..875f3d4 100644 --- a/src/modules/shared/components/navigation/right-drawer.vue +++ b/src/modules/shared/components/navigation/right-drawer.vue @@ -62,6 +62,17 @@ + + + + + + + {{ $t('navBar.userMenuTimesheetTemp') }} + + + diff --git a/src/modules/timesheets/pages/timesheet-temp-page.vue b/src/modules/timesheets/pages/timesheet-temp-page.vue index e69de29..d030193 100644 --- a/src/modules/timesheets/pages/timesheet-temp-page.vue +++ b/src/modules/timesheets/pages/timesheet-temp-page.vue @@ -0,0 +1,54 @@ + + + + \ No newline at end of file diff --git a/src/modules/timesheets/services/timesheet-services.ts b/src/modules/timesheets/services/timesheet-services.ts index e69de29..c660cbc 100644 --- a/src/modules/timesheets/services/timesheet-services.ts +++ b/src/modules/timesheets/services/timesheet-services.ts @@ -0,0 +1,9 @@ +import { api } from "src/boot/axios"; +import type {Timesheet} from "src/modules/timesheets/types/timesheet-interface"; + +export const timesheetTempService = { + getTimesheetsByNumberAndEmail: async ( timesheet_id: number, email: string): Promise => { + const response = await api.get(`timesheet/id/${timesheet_id}/email/${email}`); + return response.data; + }, +}; \ No newline at end of file diff --git a/src/modules/timesheets/types/timesheet-details-interface.ts b/src/modules/timesheets/types/timesheet-details-interface.ts index b9d940d..4a2e921 100644 --- a/src/modules/timesheets/types/timesheet-details-interface.ts +++ b/src/modules/timesheets/types/timesheet-details-interface.ts @@ -35,4 +35,11 @@ interface TimesheetDetailsDailyExpenses { interface Expense { is_approved: boolean; amount: number; -}; \ No newline at end of file +}; + +//employee timesheet template +export interface EmployeeTimesheetDetailsWeek { + is_approved: boolean; + shifts: WeekDay; + expenses: WeekDay; +} diff --git a/src/modules/timesheets/types/timesheet-interface.ts b/src/modules/timesheets/types/timesheet-interface.ts new file mode 100644 index 0000000..66157f0 --- /dev/null +++ b/src/modules/timesheets/types/timesheet-interface.ts @@ -0,0 +1,9 @@ +export interface Timesheet { + timesheet_id: number; + is_approved: boolean; + start_day: string; + end_day: string; + label: string; + shifts: Shifts[]; + expenses: Expenses; +} \ No newline at end of file diff --git a/src/router/router-constants.ts b/src/router/router-constants.ts index b264bf2..b24aa80 100644 --- a/src/router/router-constants.ts +++ b/src/router/router-constants.ts @@ -6,4 +6,5 @@ export enum RouteNames { TIMESHEET_APPROVALS = 'timesheet-approvals', EMPLOYEE_LIST = 'employee-list', PROFILE = 'user/profile', + TIMESHEET_TEMP = 'timesheet-temp' } \ No newline at end of file diff --git a/src/router/routes.ts b/src/router/routes.ts index 564f357..33cb2a6 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -22,6 +22,11 @@ const routes: RouteRecordRaw[] = [ name: RouteNames.EMPLOYEE_LIST, component: () => import('src/modules/employee-list/pages/supervisor-crew-page.vue'), }, + { + path: 'timesheet-temp', + name: RouteNames.TIMESHEET_TEMP, + component: () => import('src/modules/timesheets/pages/timesheet-temp-page.vue') + } ], }, diff --git a/src/stores/timesheet-store.ts b/src/stores/timesheet-store.ts index caeba8c..da76327 100644 --- a/src/stores/timesheet-store.ts +++ b/src/stores/timesheet-store.ts @@ -5,6 +5,9 @@ import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface'; import type { PayPeriodOverviewEmployee } from "src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface"; import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface'; import type { PayPeriodReportFilters } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-report-interface'; +import type { Timesheet } from 'src/modules/timesheets/types/timesheet-interface'; +import { timesheetTempService } from 'src/modules/timesheets/services/timesheet-services'; +import type { EmployeeTimesheetDetailsWeek } from 'src/modules/timesheets/types/timesheet-details-interface'; const default_pay_period: PayPeriod = { pay_period_no: -1, @@ -15,6 +18,13 @@ const default_pay_period: PayPeriod = { label: '' }; + const default_timesheet: Timesheet = { + timesheet_id: -1, + start_day: '', + end_day: '', + label: '', + }; + export const useTimesheetStore = defineStore('timesheet', () => { const is_loading = ref(false); const current_pay_period = ref(default_pay_period); @@ -23,6 +33,10 @@ export const useTimesheetStore = defineStore('timesheet', () => { const pay_period_employee_details = ref(); const pay_period_report = ref(); + //employee timesheet + const current_timesheet = ref(default_timesheet); + const timesheet_employee_details = ref(); + const getPayPeriodByDate = async (date_string: string): Promise => { is_loading.value = true; @@ -85,6 +99,21 @@ export const useTimesheetStore = defineStore('timesheet', () => { is_loading.value = false; }; + const getTimesheetByNumberAndEmail = async (employee_email: string) => { + is_loading.value = true; + + try{ + const response = await timesheetTempService.getTimesheetsByNumberAndEmail( + current_timesheet.value.timesheet_id, + employee_email + ); + timesheet_employee_details.value = response; + }catch (error) { + console.error('There was an error retrieving timesheet details for this employee: ', error); + } + is_loading.value = false; + } + const getTimesheetsByPayPeriodAndEmail = async (employee_email: string) => { is_loading.value = true; @@ -126,8 +155,10 @@ export const useTimesheetStore = defineStore('timesheet', () => { pay_period_overview_employees, pay_period_overview_employee_approval_statuses, pay_period_employee_details, + current_timesheet, is_loading, getPayPeriodByDate, + getTimesheetByNumberAndEmail, getPayPeriodByYearAndPeriodNumber, getTimesheetApprovalPayPeriodEmployeeOverviews, getTimesheetsByPayPeriodAndEmail, From d20f97095843f5dbc682c554e1e818a9a0a372df Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 2 Sep 2025 14:30:46 -0400 Subject: [PATCH 2/7] feat(timesheet): added timesheet page, created timsheet-interface and setup store and api --- .../composables/use-timesheet-api.ts | 33 +++++++++++++++++++ .../timesheets/pages/timesheet-temp-page.vue | 24 +++++++++----- .../timesheets/services/timesheet-services.ts | 6 ++-- .../timesheets/types/timesheet-interface.ts | 23 +++++++++++-- src/stores/timesheet-store.ts | 24 +++++++------- 5 files changed, 85 insertions(+), 25 deletions(-) diff --git a/src/modules/timesheets/composables/use-timesheet-api.ts b/src/modules/timesheets/composables/use-timesheet-api.ts index e69de29..f04ecf4 100644 --- a/src/modules/timesheets/composables/use-timesheet-api.ts +++ b/src/modules/timesheets/composables/use-timesheet-api.ts @@ -0,0 +1,33 @@ +import { useAuthStore } from "src/stores/auth-store"; +import { useTimesheetStore } from "src/stores/timesheet-store" +import { ref } from "vue"; +import { timesheetTempService } from "../services/timesheet-services"; + + +export const useTimesheetApi = () => { + const timesheet_store = useTimesheetStore(); + const auth_store = useAuthStore(); + const week_offset = ref(0); + + const fetch_week = async (offset = week_offset.value) => { + const email = auth_store.user?.email; + if(!email) return; + try{ + timesheet_store.is_loading = true; + const timesheet = await timesheetTempService.getTimesheetsByEmail(email, offset); + timesheet_store.current_timesheet = timesheet; + week_offset.value = offset; + }catch (err) { + console.error('fetch week error', err); + timesheet_store.current_timesheet = { ...timesheet_store.current_timesheet, shifts: [], expenses: [] }; + } finally { + timesheet_store.is_loading = false; + } + }; + + const this_week = async () => fetch_week(0); + const next_week = async () => fetch_week(week_offset.value + 1); + const previous_week = async () => fetch_week(week_offset.value - 1); + + return { week_offset, this_week, next_week, previous_week, fetch_week}; +} \ No newline at end of file diff --git a/src/modules/timesheets/pages/timesheet-temp-page.vue b/src/modules/timesheets/pages/timesheet-temp-page.vue index d030193..1b5d365 100644 --- a/src/modules/timesheets/pages/timesheet-temp-page.vue +++ b/src/modules/timesheets/pages/timesheet-temp-page.vue @@ -1,20 +1,28 @@ + + \ No newline at end of file diff --git a/src/modules/timesheets/components/timesheet/time-sheet-shift-list-row.vue b/src/modules/timesheets/components/timesheet/time-sheet-shift-list-row.vue deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/timesheets/components/timesheet/time-sheet-shift-list.vue b/src/modules/timesheets/components/timesheet/time-sheet-shift-list.vue deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/timesheets/components/timesheet/time-sheet.vue b/src/modules/timesheets/components/timesheet/time-sheet.vue deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/timesheets/components/shift/time-sheet-shift-list-item.vue b/src/modules/timesheets/components/timesheet/timesheet-expense-list.vue similarity index 100% rename from src/modules/timesheets/components/shift/time-sheet-shift-list-item.vue rename to src/modules/timesheets/components/timesheet/timesheet-expense-list.vue diff --git a/src/modules/timesheets/components/timesheet/timesheet-shift-form.vue b/src/modules/timesheets/components/timesheet/timesheet-shift-form.vue new file mode 100644 index 0000000..8f2946c --- /dev/null +++ b/src/modules/timesheets/components/timesheet/timesheet-shift-form.vue @@ -0,0 +1,116 @@ + + + + + \ No newline at end of file diff --git a/src/modules/timesheets/pages/timesheet-temp-page.vue b/src/modules/timesheets/pages/timesheet-temp-page.vue index 1b5d365..39d59b3 100644 --- a/src/modules/timesheets/pages/timesheet-temp-page.vue +++ b/src/modules/timesheets/pages/timesheet-temp-page.vue @@ -4,6 +4,7 @@ import { useTimesheetStore } from 'src/stores/timesheet-store'; import { useTimesheetApi } from '../composables/use-timesheet-api'; import { computed, onMounted } from 'vue'; import { useI18n } from 'vue-i18n'; +import TimesheetShiftForm from '../components/timesheet/timesheet-shift-form.vue'; const { locale } = useI18n(); @@ -45,18 +46,17 @@ import { useI18n } from 'vue-i18n';
{{ $t('pageTitles.timeSheets') }}
-
-
- {{ timesheet_label.start_date }} -
-
- {{ $t('timeSheet.dateRangesTo') }} -
-
- {{ timesheet_label.end_date }} -
+
+
+ {{ timesheet_label.start_date }}
- - +
+ {{ $t('timeSheet.dateRangesTo') }} +
+
+ {{ timesheet_label.end_date }} +
+
+ \ No newline at end of file diff --git a/src/modules/timesheets/timesheet-constants.ts b/src/modules/timesheets/timesheet-constants.ts index e69de29..7ca8ae2 100644 --- a/src/modules/timesheets/timesheet-constants.ts +++ b/src/modules/timesheets/timesheet-constants.ts @@ -0,0 +1 @@ +//mock data \ No newline at end of file From 29f5760c62420891c8074fe31e94baa45647d26b Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 5 Sep 2025 13:14:01 -0400 Subject: [PATCH 4/7] feat(timesheet): added navigation and save btns --- .../timesheet/timesheet-navigation.vue | 104 +++++++++ .../timesheet/timesheet-shift-form.vue | 199 ++++++++++++++---- .../composables/use-timesheet-api.ts | 39 +++- .../timesheets/pages/timesheet-temp-page.vue | 13 +- .../timesheets/services/timesheet-services.ts | 9 + .../timesheets/types/timesheet-interface.ts | 1 - .../types/timesheet-shift-interface.ts | 14 +- src/stores/timesheet-store.ts | 16 +- 8 files changed, 345 insertions(+), 50 deletions(-) create mode 100644 src/modules/timesheets/components/timesheet/timesheet-navigation.vue diff --git a/src/modules/timesheets/components/timesheet/timesheet-navigation.vue b/src/modules/timesheets/components/timesheet/timesheet-navigation.vue new file mode 100644 index 0000000..2183ed9 --- /dev/null +++ b/src/modules/timesheets/components/timesheet/timesheet-navigation.vue @@ -0,0 +1,104 @@ + + + \ No newline at end of file diff --git a/src/modules/timesheets/components/timesheet/timesheet-shift-form.vue b/src/modules/timesheets/components/timesheet/timesheet-shift-form.vue index 8f2946c..bef6a3a 100644 --- a/src/modules/timesheets/components/timesheet/timesheet-shift-form.vue +++ b/src/modules/timesheets/components/timesheet/timesheet-shift-form.vue @@ -2,8 +2,13 @@ - \ No newline at end of file diff --git a/src/modules/timesheets/services/timesheet-services.ts b/src/modules/timesheets/services/timesheet-services.ts index 1b5803b..d96bef5 100644 --- a/src/modules/timesheets/services/timesheet-services.ts +++ b/src/modules/timesheets/services/timesheet-services.ts @@ -1,9 +1,18 @@ import { api } from "src/boot/axios"; import type {Timesheet} from "src/modules/timesheets/types/timesheet-interface"; +import type { CreateShiftPayload, CreateWeekShiftPayload } from "../types/timesheet-shift-interface"; export const timesheetTempService = { + //GET getTimesheetsByEmail: async ( email: string, offset = 0): Promise => { const response = await api.get(`/timesheets/${encodeURIComponent(email)}`, {params: offset ? { offset } : undefined}); return response.data as Timesheet; }, + + //POST + createTimesheetShifts: async ( email: string, shifts: CreateShiftPayload[], offset = 0): Promise => { + const payload: CreateWeekShiftPayload = { shifts }; + const response = await api.post(`/timesheets/shifts/${encodeURIComponent(email)}`, payload, { params: offset ? { offset }: undefined }); + return response.data as Timesheet; + } }; \ No newline at end of file diff --git a/src/modules/timesheets/types/timesheet-interface.ts b/src/modules/timesheets/types/timesheet-interface.ts index 9986fbd..8fa1a02 100644 --- a/src/modules/timesheets/types/timesheet-interface.ts +++ b/src/modules/timesheets/types/timesheet-interface.ts @@ -16,7 +16,6 @@ type Shifts = { is_approved: boolean; } - type Expenses = { bank_type: string; date: string; diff --git a/src/modules/timesheets/types/timesheet-shift-interface.ts b/src/modules/timesheets/types/timesheet-shift-interface.ts index 474b532..1811d97 100644 --- a/src/modules/timesheets/types/timesheet-shift-interface.ts +++ b/src/modules/timesheets/types/timesheet-shift-interface.ts @@ -1,4 +1,16 @@ -export interface Shift { +export type CreateShiftPayload = { + date: string; + type: string; + start_time: string; + end_time: string; + description?: string; +}; + +export type CreateWeekShiftPayload = { + shifts: CreateShiftPayload[]; +} + +export type Shift = { is_approved: boolean; start: string; end: string; diff --git a/src/stores/timesheet-store.ts b/src/stores/timesheet-store.ts index 1ef2b28..143a67c 100644 --- a/src/stores/timesheet-store.ts +++ b/src/stores/timesheet-store.ts @@ -1,12 +1,13 @@ import { defineStore } from 'pinia'; import { ref } from 'vue'; import { timesheetApprovalService } from 'src/modules/timesheet-approval/services/services-timesheet-approval'; +import { timesheetTempService } from 'src/modules/timesheets/services/timesheet-services'; import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface'; import type { PayPeriodOverviewEmployee } from "src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface"; import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface'; import type { PayPeriodReportFilters } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-report-interface'; import type { Timesheet } from 'src/modules/timesheets/types/timesheet-interface'; -import { timesheetTempService } from 'src/modules/timesheets/services/timesheet-services'; +import type { CreateShiftPayload } from 'src/modules/timesheets/types/timesheet-shift-interface'; const default_pay_period: PayPeriod = { pay_period_no: -1, @@ -113,6 +114,18 @@ export const useTimesheetStore = defineStore('timesheet', () => { is_loading.value = false; } } + //employee timesheet + const createTimesheetShifts = async (employee_email: string, shifts: CreateShiftPayload[], offset = 0) => { + is_loading.value = true; + try{ + const timesheet = await timesheetTempService.createTimesheetShifts(employee_email, shifts, offset); + current_timesheet.value = timesheet; + } catch (err) { + console.error('createTimesheetShifts error: ', err); + } finally { + is_loading.value = false; + } + }; const getTimesheetsByPayPeriodAndEmail = async (employee_email: string) => { is_loading.value = true; @@ -159,6 +172,7 @@ export const useTimesheetStore = defineStore('timesheet', () => { is_loading, getPayPeriodByDate, getTimesheetByEmail, + createTimesheetShifts, getPayPeriodByYearAndPeriodNumber, getTimesheetApprovalPayPeriodEmployeeOverviews, getTimesheetsByPayPeriodAndEmail, From d0b0f2df6c6f034c38b6df192673c0ce57647612 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Fri, 5 Sep 2025 16:07:29 -0400 Subject: [PATCH 5/7] feat(timesheet): i18n for timesheet page --- src/i18n/en-ca/index.ts | 26 ++++++++- src/i18n/fr-ca/index.ts | 26 ++++++++- .../timesheet/timesheet-navigation.vue | 10 ++-- .../timesheet/timesheet-shift-form.vue | 58 ++++++++++++------- .../timesheets/pages/timesheet-temp-page.vue | 2 +- 5 files changed, 92 insertions(+), 30 deletions(-) diff --git a/src/i18n/en-ca/index.ts b/src/i18n/en-ca/index.ts index 836035f..2e94179 100644 --- a/src/i18n/en-ca/index.ts +++ b/src/i18n/en-ca/index.ts @@ -249,7 +249,31 @@ export default { timeSheets: 'Time sheet', timeSheetValidations: 'Time sheet', }, - timeSheet: { + timesheet: { + //employee's timesheet page + days: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + nav_button: { + calendar_date_picker:'Calendar', + current_week:'This week', + next_week:'Next week', + previous_week:'Previous week', + }, + save_button:'Save', + shift_types: 'Shift`s Type', + shiftTypes: { + EMERGENCY: 'Emergency', + EVENING: 'Evening', + HOLIDAY: 'Holiday', + REGULAR: 'Regular', + SICK: 'Sick Leave', + VACATION: 'Vacation', + WORK_FROM_HOME: 'Work from home', + }, + fields: { + start:'Start', + end:'End', + }, + //rest timeSheetTab_1: 'Shifts', timeSheetTab_2: 'Expenses', templateButton: 'Apply Templates', diff --git a/src/i18n/fr-ca/index.ts b/src/i18n/fr-ca/index.ts index 11dee1a..3067ace 100644 --- a/src/i18n/fr-ca/index.ts +++ b/src/i18n/fr-ca/index.ts @@ -299,7 +299,31 @@ export default { noResultsLabel: 'Le filtre n’a révélé aucun résultat', noDataLabel: 'Je n’ai rien trouvé pour toi', }, - timeSheet: { + timesheet: { + //employee's timesheet page + days: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], + nav_button: { + calendar_date_picker:'Calendrier', + current_week:'Semaine actuelle', + next_week:'Prochaine semaine', + previous_week:'Semaine précédente', + }, + save_button:'Enregistrer', + shift_types: 'Type de quart', + shiftTypes: { + EMERGENCY: 'Urgence', + EVENING: 'Soir', + HOLIDAY: 'Férier', + SICK: 'Absence', + REGULAR: 'Régulier', + VACATION: 'Vacance', + WORK_FROM_HOME: 'Télétravail', + }, + fields: { + start:'Entrée', + end:'Sortie', + }, + //rest timeSheetTab_1: 'Quarts de travail', timeSheetTab_2: 'Dépenses', templateButton: 'Appliquer le modèle', diff --git a/src/modules/timesheets/components/timesheet/timesheet-navigation.vue b/src/modules/timesheets/components/timesheet/timesheet-navigation.vue index 2183ed9..635c5d0 100644 --- a/src/modules/timesheets/components/timesheet/timesheet-navigation.vue +++ b/src/modules/timesheets/components/timesheet/timesheet-navigation.vue @@ -27,7 +27,7 @@