refactor(timesheet): fix delete shift functionality which had stopped working due to complete frontend refactor

This commit is contained in:
Nicolas Drolet 2025-10-14 14:18:32 -04:00
parent 7f43341629
commit 702a977fce
9 changed files with 102 additions and 82 deletions

View File

@ -22,6 +22,8 @@
const employeeEmail = defineModel();
const visible_columns = ref<string[]>(['REGULAR', 'email']);
const emit = defineEmits<{
'clickedDetailsButton': [email: string];
}>();
@ -56,6 +58,7 @@
<template>
<div class="q-pa-md">
<q-table
:visible-columns="visible_columns"
:rows="overview_rows"
:columns="pay_period_overview_columns"
row-key="email"
@ -131,7 +134,7 @@
v-if="(props.value > 0 && typeof props.value !== 'boolean') || typeof props.value === 'string'"
:class="getListModeTextColor(props.col.name)"
>{{ props.value }}</span>
<q-icon
<q-icon
v-if="typeof props.value === 'boolean'"
:name="props.value ? 'verified' : 'fiber_manual_record'"
:color="props.value ? 'primary' : 'grey-5'"

View File

@ -1,4 +1,7 @@
<script setup lang="ts">
<script
setup
lang="ts"
>
import { useShiftStore } from 'src/stores/shift-store';
import { SHIFT_TYPES } from 'src/modules/timesheets/models/shift.models';
@ -12,68 +15,68 @@
<template>
<div>
<div class="col-xs-6 col-sm-4 col-md-3 row q-mx-xs q-my-none">
<q-select
v-model="shift_store.current_shift.type"
options-dense
:options="SHIFT_TYPES"
:label="$t('timesheet.shift.types.label')"
class="col q-pa-none"
color="primary"
outlined
dense
square
hide-dropdown-icon
emit-value
map-options
/>
<q-select
v-model="shift_store.current_shift.type"
options-dense
:options="SHIFT_TYPES"
:label="$t('timesheet.shift.types.label')"
class="col q-pa-none"
color="primary"
outlined
dense
square
hide-dropdown-icon
emit-value
map-options
/>
<div class="col-auto column items-center">
<span
class="text-caption q-pa-none q-ma-none"
style="line-height: 0.7em; font-size: 0.7em;"
>{{ $t('timesheet.shift.types.REMOTE') }}</span>
<q-toggle
v-model="shift_store.current_shift.is_remote"
class="q-pa-none q-ma-none"
/>
</div>
</div>
<div class="col-auto row q-mx-xs">
<q-input
v-model="shift_store.current_shift.start_time"
:label="$t('timesheet.shift.fields.start')"
outlined
dense
square
inputmode="numeric"
mask="##:##"
class="col-auto q-mx-xs"
/>
<q-input
v-model="shift_store.current_shift.end_time"
:label="$t('timesheet.shift.fields.end')"
outlined
dense
square
inputmode="numeric"
mask="##:##"
class="col-auto q-mx-xs"
/>
</div>
<q-input
v-model="shift_store.current_shift.comment"
type="textarea"
autogrow
filled
dense
square
:label="$t('timesheet.shift.fields.header_comment')"
:counter="true"
:maxlength="512"
class="col-auto"
<div class="col-auto column items-center">
<span
class="text-caption q-pa-none q-ma-none"
style="line-height: 0.7em; font-size: 0.7em;"
>{{ $t('timesheet.shift.types.REMOTE') }}</span>
<q-toggle
v-model="shift_store.current_shift.is_remote"
class="q-pa-none q-ma-none"
/>
</div>
</div>
<div class="col-auto row q-mx-xs">
<q-input
v-model="shift_store.current_shift.start_time"
:label="$t('timesheet.shift.fields.start')"
outlined
dense
square
inputmode="numeric"
mask="##:##"
class="col-auto q-mx-xs"
/>
<q-input
v-model="shift_store.current_shift.end_time"
:label="$t('timesheet.shift.fields.end')"
outlined
dense
square
inputmode="numeric"
mask="##:##"
class="col-auto q-mx-xs"
/>
</div>
<q-input
v-model="shift_store.current_shift.comment"
type="textarea"
autogrow
filled
dense
square
:label="$t('timesheet.shift.fields.header_comment')"
:counter="true"
:maxlength="512"
class="col-auto"
/>
</div>
</template>

View File

@ -5,6 +5,7 @@
import { computed, ref } from 'vue';
import { useShiftStore } from 'src/stores/shift-store';
import { useShiftApi } from 'src/modules/timesheets/composables/api/use-shift-api';
import ShiftCrudDialogAddUpdateShift from 'src/modules/timesheets/components/shift-crud-dialog-add-update-shift.vue';
const shift_store = useShiftStore();
const shift_api = useShiftApi();
@ -69,7 +70,7 @@
v-if="shift_store.mode !== 'delete'"
class="row no-wrap items-start justify-center"
>
<ShiftCrudDialogAddUpdateShift />
</div>
<div

View File

@ -16,7 +16,7 @@ export const useShiftApi = () => {
start_time: shift.start_time,
end_time: shift.end_time,
type: shift.type,
is_approved: false,
is_approved: shift.is_approved,
is_remote: shift.is_remote,
comment: comment,
};

View File

@ -10,7 +10,7 @@ export const SHIFT_TYPES = [
export type ShiftType = 'REGULAR' | 'EVENING' | 'EMERGENCY' | 'OVERTIME' | 'HOLIDAY' | 'VACATION' | 'SICK' ;
export type UpsertAction = 'create' | 'update' | 'delete';
export type CrudAction = 'create' | 'update' | 'delete';
export type ShiftLegendItem = {
type: ShiftType;
@ -30,7 +30,7 @@ export interface Shift {
}
export interface UpsertShiftsResponse {
action: UpsertAction;
action: CrudAction;
day: Shift[];
}

View File

@ -1,5 +1,5 @@
import { api } from "src/boot/axios";
import type { UpsertShift } from "src/modules/timesheets/models/shift.models";
import type { CrudAction, UpsertShift } from "src/modules/timesheets/models/shift.models";
import type { PayPeriod } from "src/modules/shared/models/pay-period.models";
import type { PayPeriodDetails } from "src/modules/timesheets/models/pay-period-details.models";
import type { PayPeriodOverview } from "src/modules/timesheet-approval/models/pay-period-overview.models";
@ -36,15 +36,21 @@ export const timesheetService = {
return response.data;
},
upsertOrDeleteShiftsByDateAndEmployeeEmail: async (email: string, payload: UpsertShift): Promise<PayPeriodDetails> => {
const response = await api.put(`/shifts/upsert/${email}`, payload);
upsertShiftsByEmployeeEmailAndAction: async (email: string, payload: UpsertShift, action: CrudAction): Promise<PayPeriodDetails> => {
const response = await api.put(`/shifts/upsert/${email}?action=${action}`, payload);
return response.data;
},
deleteShiftsByEmployeeEmailAndDate: async (email: string, date: string, payload: UpsertShift) => {
console.log('sent old shift: ', payload);
const response = await api.delete(`/shifts/delete/${email}/${date}`, { data: payload });
return response;
},
upsertOrDeleteExpensesByPayPeriodAndEmployeeEmail: async (email: string, date: string, payload: UpsertExpense): Promise<Expense[]> => {
const headers = { 'Content-Type': 'application/json' }
const response = await api.put(`/expenses/upsert/${email}/${date}`, payload, { headers });
return response.data;
}
},
};

View File

@ -6,7 +6,7 @@
const auth_store = useAuthStore();
const employee_roles = [ 'SUPERVISOR', 'EMPLOYEE', 'ADMIN', 'HR', 'ACCOUNTING' ];
const { employeeProfile } = defineProps<{
defineProps<{
employeeProfile?: EmployeeProfile | undefined;
}>();
</script>

View File

@ -4,7 +4,7 @@ import { useTimesheetStore } from "src/stores/timesheet-store";
import { default_expense, default_pay_period_expenses, type UpsertExpense, type Expense, type PayPeriodExpenses } from "src/modules/timesheets/models/expense.models";
import { timesheetService } from "src/modules/timesheets/services/timesheet-service";
import { ExpensesApiError, type GenericApiError } from "src/modules/timesheets/models/expense.validation";
import type { UpsertAction } from "src/modules/timesheets/models/shift.models";
import type { CrudAction } from "src/modules/timesheets/models/shift.models";
@ -12,7 +12,7 @@ export const useExpensesStore = defineStore('expenses', () => {
const timesheet_store = useTimesheetStore();
const is_open = ref(false);
const is_loading = ref(false);
const mode = ref<UpsertAction>('create');
const mode = ref<CrudAction>('create');
const pay_period_expenses = ref<PayPeriodExpenses>(default_pay_period_expenses);
const current_expense = ref<Expense>(default_expense);
const initial_expense = ref<Expense>(default_expense);

View File

@ -3,18 +3,18 @@ import { defineStore } from "pinia";
import { unwrapAndClone } from "src/utils/unwrap-and-clone";
import { timesheetService } from "src/modules/timesheets/services/timesheet-service";
import { useTimesheetStore } from "src/stores/timesheet-store";
import { default_shift, type UpsertAction, type Shift, type UpsertShift } from "src/modules/timesheets/models/shift.models";
import { default_shift, type CrudAction, type Shift, type UpsertShift } from "src/modules/timesheets/models/shift.models";
export const useShiftStore = defineStore('shift', () => {
const is_open = ref(false);
const mode = ref<UpsertAction>('create');
const mode = ref<CrudAction>('create');
const date_iso = ref<string>('');
const current_shift = ref<Shift>(default_shift);
const initial_shift = ref<Shift>(default_shift);
const timesheet_store = useTimesheetStore();
const open = (next_mode: UpsertAction, date: string, current: Shift, initial: Shift) => {
const open = (next_mode: CrudAction, date: string, current: Shift, initial: Shift) => {
mode.value = next_mode;
date_iso.value = date;
current_shift.value = current; // new shift
@ -38,19 +38,26 @@ export const useShiftStore = defineStore('shift', () => {
is_open.value = false;
mode.value = 'create';
date_iso.value = '';
current_shift.value = default_shift;
initial_shift.value = default_shift;
};
const upsertOrDeleteShiftByEmployeeEmail = async (employee_email: string, upsert_shift: UpsertShift) => {
const encoded_email = encodeURIComponent(employee_email);
try {
const result = await timesheetService.upsertOrDeleteShiftsByDateAndEmployeeEmail(encoded_email, upsert_shift);
timesheet_store.pay_period_details = result;
if (mode.value === 'delete') {
const result = await timesheetService.deleteShiftsByEmployeeEmailAndDate(encoded_email, upsert_shift.old_shift?.date ?? '', upsert_shift);
console.log('result: ', result);
if (result.status === 200) {
await timesheet_store.getPayPeriodDetailsByEmployeeEmail(employee_email);
}
} else {
const result = await timesheetService.upsertShiftsByEmployeeEmailAndAction(encoded_email, upsert_shift, mode.value);
timesheet_store.pay_period_details = result;
}
close();
} catch (err) {
console.log('error doing thing: ', err)
console.log('error doing thing: ', err);
// const status_code: number = err?.response?.status ?? 500;
// const data = err?.response?.data ?? {};
// throw new GenericApiError({