fix(approvals): adjust scripts to handle timesheet modifications made through approvals page

This commit is contained in:
Nic D 2026-01-22 10:38:08 -05:00
parent 388e002dda
commit b9613889f5
7 changed files with 71 additions and 60 deletions

View File

@ -199,7 +199,7 @@
/>
<q-btn
v-else
v-else-if="!expense.is_approved && mode === 'normal'"
flat
dense
size="lg"

View File

@ -2,12 +2,15 @@
setup
lang="ts"
>
import { useI18n } from 'vue-i18n';
import { onMounted, ref } from 'vue';
import { QSelect, QInput } from 'quasar';
import { QSelect, QInput, useQuasar } from 'quasar';
import { useUiStore } from 'src/stores/ui-store';
import { SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
import type { Shift } from 'src/modules/timesheets/models/shift.models';
const q = useQuasar();
const { t } = useI18n();
const ui_store = useUiStore();
const COMMENT_LENGTH_MAX = 280;
@ -24,6 +27,24 @@
holiday?: boolean | undefined;
}>();
const time_input_props = {
dense: true,
borderless: shift.value.is_approved && isTimesheetApproved,
readonly: shift.value.is_approved && isTimesheetApproved,
standout: q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9',
labelSlot: true,
lazyRules: true,
noErrorIcon: true,
hideBottomSpace: true,
error: shift.value.has_error,
errorMessage: errorMessage ? t(errorMessage) : (error_message.value ? t(error_message.value) : undefined),
labelColor: shift.value.is_approved ? 'white' : (holiday ? 'purple-5' : 'accent'),
class: `col rounded-5 bg-dark q-mx-xs ${shift.value.id === -2 ? 'bg-negative' : ''} ${shift.value.is_approved || isTimesheetApproved ? 'cursor-not-allowed inset-shadow' : ''}`,
inputClass: `text-weight-medium ${shift.value.id === -2 ? 'text-white ' : ' '} ${shift.value.is_approved ? 'text-white cursor-not-allowed q-px-sm' : ''}`,
style: shift.value.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : '',
inputStyle: "font-size: 1.2em;"
}
const emit = defineEmits<{
'requestDelete': [void];
'onTimeFieldBlur': [void];
@ -37,7 +58,7 @@
shift.value.has_error = false;
error_message.value = undefined;
emit('onTimeFieldBlur');
}
}
}
const onBlurShiftTypeSelect = () => {
@ -141,7 +162,7 @@
dense
keep-color
size="3em"
:color="holiday? 'purple-5' : 'accent'"
:color="holiday ? 'purple-5' : 'accent'"
icon="las la-building"
checked-icon="las la-laptop"
>
@ -158,13 +179,14 @@
</template>
<template #option="scope">
<q-item clickable v-bind="scope.itemProps">
<q-item
clickable
v-bind="scope.itemProps"
>
<q-item-section avatar>
<q-icon
:name="scope.opt.icon"
/>
<q-icon :name="scope.opt.icon" />
</q-item-section>
<q-item-section class="text-left">
{{ $t(scope.label) }}
</q-item-section>
@ -178,23 +200,8 @@
<q-input
ref="start_time"
v-model="shift.start_time"
dense
:borderless="(shift.is_approved && isTimesheetApproved)"
:readonly="(shift.is_approved && isTimesheetApproved)"
v-bind="time_input_props"
type="time"
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
label-slot
lazy-rules
no-error-icon
hide-bottom-space
:error="shift.has_error"
:error-message="errorMessage ? $t(errorMessage) : (error_message ? $t(error_message) : undefined)"
:label-color="!shift.is_approved ? (holiday? 'purple-5' : 'accent') : 'white'"
class="col rounded-5 bg-dark q-mx-xs"
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (!shift.is_approved && !isTimesheetApproved ? '' : 'cursor-not-allowed inset-shadow')"
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'text-white cursor-not-allowed q-px-sm' : '')"
input-style="font-size: 1.2em;"
:style="shift.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : ''"
@blur="onTimeFieldBlur(shift.start_time)"
>
<template #label>
@ -210,22 +217,8 @@
<q-input
ref="end_time"
v-model="shift.end_time"
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
dense
:borderless="(shift.is_approved && isTimesheetApproved)"
:readonly="(shift.is_approved && isTimesheetApproved)"
v-bind="time_input_props"
type="time"
label-slot
no-error-icon
hide-bottom-space
:error="shift.has_error"
:error-message="errorMessage ? $t(errorMessage) : (error_message ? $t(error_message) : undefined)"
:label-color="!shift.is_approved ? (holiday? 'purple-5' : 'accent') : 'white'"
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'text-white cursor-not-allowed q-px-sm' : '')"
input-style="font-size: 1.2em;"
class="col rounded-5 bg-dark q-mx-xs"
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (shift.is_approved ? 'cursor-not-allowed q-px-xs transparent inset-shadow' : (isTimesheetApproved ? 'inset-shadow' : ''))"
:style="shift.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : ''"
@blur="onTimeFieldBlur(shift.end_time)"
>
<template #label>
@ -247,9 +240,9 @@
v-if="!ui_store.is_mobile_mode"
push
dense
:color="shift.is_approved ? 'white' : (holiday? 'purple-5' : 'accent')"
:color="shift.is_approved ? 'white' : (holiday ? 'purple-5' : 'accent')"
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
:text-color="shift.is_approved ? (holiday? 'purple-5' : 'accent') : 'white'"
:text-color="shift.is_approved ? (holiday ? 'purple-5' : 'accent') : 'white'"
class="col"
:class="ui_store.is_mobile_mode ? 'q-mt-xs bg-dark' : ''"
>

View File

@ -18,13 +18,14 @@
const timesheet_store = useTimesheetStore();
const shift_error_message = ref<string | undefined>();
const { day, dense = false, approved = false, holiday = false } = defineProps<{
const { day, dense = false, approved = false, holiday = false, employeeEmail } = defineProps<{
timesheetId: number;
weekDayIndex: number;
day: TimesheetDay;
dense?: boolean;
approved?: boolean;
holiday?: boolean;
employeeEmail?: string;
}>();
const preset_mouseover = ref(false);
@ -38,7 +39,7 @@
shift.id = 0;
emit('deleteUnsavedShift');
} else {
await shift_api.deleteShiftById(shift.id);
await shift_api.deleteShiftById(shift.id, employeeEmail);
}
if (day.shifts.length < 2 && shift_error_message.value !== undefined) {

View File

@ -7,9 +7,9 @@ export const useShiftApi = () => {
const shift_store = useShiftStore();
const auth_store = useAuthStore();
const deleteShiftById = async (shift_id: number) => {
const deleteShiftById = async (shift_id: number, employee_email?: string | undefined) => {
timesheet_store.is_loading = true;
const success = await shift_store.deleteShiftById(shift_id);
const success = await shift_store.deleteShiftById(shift_id, employee_email);
if (success) {
await timesheet_store.getTimesheetsByOptionalEmployeeEmail(auth_store.user?.email ?? '');

View File

@ -3,17 +3,32 @@ import type { BackendResponse } from "src/modules/shared/models/backend-response
import type { Shift } from "src/modules/timesheets/models/shift.models";
export const ShiftService = {
deleteShiftById: async (shift_id: number) => {
deleteShiftById: async (shift_id: number, employee_email?: string | undefined) => {
if (employee_email) {
const response = await api.delete(`/shift/${shift_id}/${employee_email}`);
return response.data;
}
const response = await api.delete(`/shift/${shift_id}`);
return response.data;
},
createNewShifts: async (new_shifts: Shift[]):Promise<BackendResponse<Shift>> => {
createNewShifts: async (new_shifts: Shift[], employee_email?: string | undefined):Promise<BackendResponse<Shift>> => {
if (employee_email) {
const response = await api.post(`/shift/create/${employee_email}`);
return response.data;
}
const response = await api.post(`/shift/create`, new_shifts);
return response.data;
},
updateShifts: async (existing_shifts: Shift[]):Promise<BackendResponse<Shift>> => {
updateShifts: async (existing_shifts: Shift[], employee_email?: string | undefined):Promise<BackendResponse<Shift>> => {
if (employee_email) {
const response = await api.patch(`/shift/update/${employee_email}`);
return response.data;
}
const response = await api.patch(`/shift/update`, existing_shifts);
return response.data;
}

View File

@ -34,6 +34,12 @@
onUnmounted(() => {
timesheet_store.unsubscribeToPayPeriodObservable();
})
const details_dialog_props = {
'is-loading': timesheet_store.is_loading,
'employee-overview': timesheet_store.current_pay_period_overview,
timesheets: timesheet_store.timesheets,
}
</script>
<template>
@ -41,11 +47,7 @@
class="bg-secondary"
:style-fn="tableStyleFunction"
>
<DetailsDialog
:is-loading="timesheet_store.is_loading"
:employee-overview="timesheet_store.current_pay_period_overview"
:timesheets="timesheet_store.timesheets"
/>
<DetailsDialog v-bind="details_dialog_props" />
<OverviewReport />

View File

@ -9,9 +9,9 @@ export const useShiftStore = defineStore('shift_store', () => {
const timesheet_store = useTimesheetStore();
const shift_errors = ref<string[]>([]);
const deleteShiftById = async (shift_id: number): Promise<boolean> => {
const deleteShiftById = async (shift_id: number, employee_email?: string | undefined): Promise<boolean> => {
try {
await ShiftService.deleteShiftById(shift_id);
await ShiftService.deleteShiftById(shift_id, employee_email);
return true;
} catch (error) {
console.error('DEV ERROR || error while deleting shift: ', error);
@ -19,7 +19,7 @@ export const useShiftStore = defineStore('shift_store', () => {
}
};
const createNewShifts = async (): Promise<boolean> => {
const createNewShifts = async (employee_email?: string | undefined): Promise<boolean> => {
if (timesheet_store.timesheets === undefined) return false;
try {
@ -27,7 +27,7 @@ export const useShiftStore = defineStore('shift_store', () => {
const new_shifts = days.flatMap(day => day.shifts).filter(shift => shift.id < 0);
if (new_shifts?.length > 0) {
const response = await ShiftService.createNewShifts(new_shifts);
const response = await ShiftService.createNewShifts(new_shifts, employee_email);
if (response.success) {
return true;
}
@ -40,14 +40,14 @@ export const useShiftStore = defineStore('shift_store', () => {
}
};
const updateShifts = async (): Promise<boolean> => {
const updateShifts = async (employee_email?: string | undefined): Promise<boolean> => {
if (timesheet_store.timesheets === undefined) return false;
try {
const existing_shifts = timesheet_store.timesheets.flatMap(week => week.days).flatMap(day => day.shifts).filter(shift => shift.id > 0);
if (existing_shifts?.length > 0) {
const response = await ShiftService.updateShifts(existing_shifts);
const response = await ShiftService.updateShifts(existing_shifts, employee_email);
if (response.success) {
return true;