From 67132857acf27b5edfb7cd2a5347bbd355278093 Mon Sep 17 00:00:00 2001 From: Nic D Date: Tue, 24 Feb 2026 08:32:10 -0500 Subject: [PATCH 1/2] feat(timesheet-approval): allow creation of expenses from employee details window --- .../components/details-dialog.vue | 61 ++++++++++++++++++- .../components/expense-dialog-form.vue | 15 ++++- .../components/shift-list-day-row.vue | 2 +- .../timesheets/composables/use-expense-api.ts | 2 +- .../timesheets/services/expense-service.ts | 4 +- src/stores/expense-store.ts | 2 +- 6 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/modules/timesheet-approval/components/details-dialog.vue b/src/modules/timesheet-approval/components/details-dialog.vue index 1cb8f9f..0c3f08a 100644 --- a/src/modules/timesheet-approval/components/details-dialog.vue +++ b/src/modules/timesheet-approval/components/details-dialog.vue @@ -10,13 +10,18 @@ import DetailsDialogChartExpenses from 'src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue'; import TimesheetWrapper from 'src/modules/timesheets/components/timesheet-wrapper.vue'; import ExpenseDialogList from 'src/modules/timesheets/components/expense-dialog-list.vue'; + import ExpenseDialogForm from 'src/modules/timesheets/components/expense-dialog-form.vue'; import { useTimesheetApprovalApi } from '../composables/use-timesheet-approval-api'; import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api'; + import { useExpensesStore } from 'src/stores/expense-store'; + import { Expense } from 'src/modules/timesheets/models/expense.models'; + import { date } from 'quasar'; // ========== state ======================================== const { t } = useI18n(); const timesheetStore = useTimesheetStore(); + const expenseStore = useExpensesStore(); const timesheetApprovalApi = useTimesheetApprovalApi(); const shiftApi = useShiftApi(); const isDialogOpen = ref(false); @@ -24,11 +29,14 @@ // ========== computed ======================================== const isApproved = computed(() => timesheetStore.timesheets.every(timesheet => timesheet.is_approved)); + const approveButtonLabel = computed(() => isApproved.value ? t('shared.label.unlock') : t('shared.label.lock') ); + const approveButtonIcon = computed(() => isApproved.value ? 'las la-lock' : 'las la-unlock'); + const hasExpenses = computed(() => timesheetStore.timesheets.some(timesheet => Object.values(timesheet.weekly_expenses).some(hours => hours > 0)) ); @@ -49,6 +57,15 @@ const onClickSaveTimesheets = async () => { await shiftApi.saveShiftChanges(timesheetStore.current_pay_period_overview?.email); + expenseStore.is_showing_create_form = false; + } + + const onClickExpenseCreate = () => { + expenseStore.mode = 'create'; + if (timesheetStore.pay_period) + expenseStore.current_expense = new Expense(timesheetStore.pay_period.period_start); + else + expenseStore.current_expense = new Expense(date.formatDate(new Date(), 'YYYY-MM-DD')); } @@ -118,6 +135,7 @@
@@ -129,12 +147,51 @@ />
-
+
+ + + + + + + + + +
-
+
({ default: new Expense(new Date().toISOString().slice(0, 10)) }) const file = defineModel('file'); + const {email} = defineProps<{ + email?: string | undefined; + }>(); + const { t } = useI18n(); const ui_store = useUiStore(); const timesheet_store = useTimesheetStore(); @@ -60,9 +64,16 @@ const requestExpenseCreationOrUpdate = async () => { if (file.value) - await expenses_api.upsertExpense(expenses_store.current_expense, employeeEmail ?? auth_store.user?.email ?? 'MISSING_EMAIL', file.value); + await expenses_api.upsertExpense( + expenses_store.current_expense, + email ?? employeeEmail ?? auth_store.user?.email ?? 'MISSING_EMAIL', + file.value + ); else - await expenses_api.upsertExpense(expenses_store.current_expense, employeeEmail ?? auth_store.user?.email ?? 'MISSING_EMAIL'); + await expenses_api.upsertExpense( + expenses_store.current_expense, + email ?? employeeEmail ?? auth_store.user?.email ?? 'MISSING_EMAIL' + ); expenses_store.is_showing_create_form = true; expenses_store.mode = 'create'; diff --git a/src/modules/timesheets/components/shift-list-day-row.vue b/src/modules/timesheets/components/shift-list-day-row.vue index 73cbe08..35142f7 100644 --- a/src/modules/timesheets/components/shift-list-day-row.vue +++ b/src/modules/timesheets/components/shift-list-day-row.vue @@ -211,7 +211,7 @@ backdrop-filter="blur(6px)" >
{{ diff --git a/src/modules/timesheets/composables/use-expense-api.ts b/src/modules/timesheets/composables/use-expense-api.ts index 7190bea..2165807 100644 --- a/src/modules/timesheets/composables/use-expense-api.ts +++ b/src/modules/timesheets/composables/use-expense-api.ts @@ -19,7 +19,7 @@ export const useExpensesApi = () => { expense.attachment_name = file.name; } } - + console.log('employee email provided for expense: ', employee_email) const success = await expenses_store.upsertExpense(expense, employee_email); if (success) { diff --git a/src/modules/timesheets/services/expense-service.ts b/src/modules/timesheets/services/expense-service.ts index 2473bb2..4a4e462 100644 --- a/src/modules/timesheets/services/expense-service.ts +++ b/src/modules/timesheets/services/expense-service.ts @@ -3,8 +3,8 @@ import type { BackendResponse } from "src/modules/shared/models/backend-response import type { AttachmentPresignedURLResponse, Expense } from "src/modules/timesheets/models/expense.models"; export const ExpenseService = { - createExpense: async (expense: Expense): Promise<{ success: boolean, data: Expense, error?: unknown }> => { - const response = await api.post('expense/create', expense); + createExpense: async (expense: Expense, email?: string): Promise<{ success: boolean, data: Expense, error?: unknown }> => { + const response = await api.post(`expense/create${email ? '?employee_email=' + email : ''}`, expense); return response.data; }, diff --git a/src/stores/expense-store.ts b/src/stores/expense-store.ts index 9bf61e5..64911c9 100644 --- a/src/stores/expense-store.ts +++ b/src/stores/expense-store.ts @@ -36,7 +36,7 @@ export const useExpensesStore = defineStore('expenses', () => { const upsertExpense = async (expense: Expense, email?: string): Promise => { try { if (expense.id < 0) { - const data = await ExpenseService.createExpense(expense); + const data = await ExpenseService.createExpense(expense, email); return data.success; } const data = await ExpenseService.updateExpense(expense, email); From 883e6f8a3d09ae6f59f89b2914dbd1a53fb65eec Mon Sep 17 00:00:00 2001 From: Nic D Date: Tue, 24 Feb 2026 11:04:24 -0500 Subject: [PATCH 2/2] fix(chatbot): correct formatting issue with text display, now displays markdown properly as html --- src/boot/axios.ts | 3 +- src/i18n/en-ca/index.ts | 1 + src/i18n/fr-ca/index.ts | 3 +- .../components/login-connection-panel.vue | 131 ++++++++++-------- .../chatbot/components/dialogue-phrase.vue | 53 +++---- 5 files changed, 96 insertions(+), 95 deletions(-) diff --git a/src/boot/axios.ts b/src/boot/axios.ts index 3ac8c4c..612d74e 100644 --- a/src/boot/axios.ts +++ b/src/boot/axios.ts @@ -16,7 +16,8 @@ declare module 'vue' { // for each client) const api = axios.create({ baseURL: import.meta.env.VITE_TARGO_BACKEND_URL, - withCredentials: true + withCredentials: true, + timeout: 5 * 60 * 1000, }); export default defineBoot(({ app }) => { diff --git a/src/i18n/en-ca/index.ts b/src/i18n/en-ca/index.ts index 3bba422..4f4be80 100644 --- a/src/i18n/en-ca/index.ts +++ b/src/i18n/en-ca/index.ts @@ -7,6 +7,7 @@ export default { error: { NO_REPLY_RECEIVED: "encountered an error while waiting for chatbot to reply", SEND_MESSAGE_FAILED: "unable to send message to chatbot", + GENERIC_RESPONSE_ERROR: "An error was encountered while waiting for the chatbot to reply, please try again.", }, }, diff --git a/src/i18n/fr-ca/index.ts b/src/i18n/fr-ca/index.ts index 7fe01c2..0252bb6 100644 --- a/src/i18n/fr-ca/index.ts +++ b/src/i18n/fr-ca/index.ts @@ -6,7 +6,8 @@ export default { chat_thinking: "Réflexion en cours...", error: { NO_REPLY_RECEIVED: "Une erreur est survenu lors de la réception de la réponse du chatbot", - SEND_MESSAGE_FAILED: "Une erreur est survenu lors de l'envoi de votre message", + SEND_MESSAGE_FAILED: "Une erreur est survenue lors de l'envoi de votre message", + GENERIC_RESPONSE_ERROR: "Le chatbot ne réponds pas pour le moment, veuillez réessayer.", }, }, diff --git a/src/modules/auth/components/login-connection-panel.vue b/src/modules/auth/components/login-connection-panel.vue index 0dd7755..dfeb369 100644 --- a/src/modules/auth/components/login-connection-panel.vue +++ b/src/modules/auth/components/login-connection-panel.vue @@ -12,6 +12,15 @@ // const is_remembered = ref(false); const is_employee_email = computed(() => email.value.includes('@targ')); const is_game_time = computed(() => email.value.includes('allumette')); + + const onSubmitConnectionRequest = () => { + console.log('submit requested'); + if (is_employee_email.value) return; + } + + const onClickEmployeeConnect = () => { + auth_api.oidcLogin(); + }