refactor(timesheet): more work on plugging in backend, managing expenses

This commit is contained in:
Nicolas Drolet 2025-10-31 17:03:21 -04:00
parent 18aa4c08f4
commit f0ef88a16c
12 changed files with 164 additions and 121 deletions

View File

@ -104,7 +104,6 @@ export default defineConfig((ctx) => {
config: { config: {
notify: { notify: {
color: 'primary', color: 'primary',
avatar: 'https://cdn.quasar.dev/img/boy-avatar.png',
}, },
dark: false, dark: false,
}, },

View File

@ -14,7 +14,7 @@
$primary : #019547; $primary : #019547;
$secondary : #DAE0E7; $secondary : #DAE0E7;
$accent : #AAD5C4; $accent : #83f29f7d;
$dark-shadow-color : #00220f; $dark-shadow-color : #00220f;

View File

@ -44,12 +44,28 @@
</template> </template>
</q-input> </q-input>
<q-card-section class="q-ma-none q-pa-none text-uppercase text-caption text-weight-medium"> <q-card-section
horizontal
class="q-mb-md q-pa-none text-uppercase text-caption text-weight-medium"
>
<q-toggle <q-toggle
v-model="is_remembered" v-model="is_remembered"
size="sm"
color="primary" color="primary"
:label="$t('login.button.remember_me')" class="col-auto"
/> />
<transition
enter-active-class="animated rubberBand slow"
leave-active-class=""
mode="out-in"
>
<span
:key="is_remembered ? 'yep' : 'nope'"
class="col-auto text-weight-bold self-center q-ml-sm"
:class="is_remembered ? 'text-primary' : ''"
>{{ $t('login.button.remember_me') }}</span>
</transition>
</q-card-section> </q-card-section>
<q-card-actions> <q-card-actions>

View File

@ -3,10 +3,10 @@
lang="ts" lang="ts"
> >
/* eslint-disable */ /* eslint-disable */
import { inject, ref } from 'vue'; import { computed, inject, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useExpensesStore } from 'src/stores/expense-store'; import { useExpensesStore } from 'src/stores/expense-store';
import { empty_expense, EXPENSE_TYPE, TYPES_WITH_AMOUNT_ONLY } from 'src/modules/timesheets/models/expense.models'; import { Expense, EXPENSE_TYPE, TYPES_WITH_AMOUNT_ONLY } from 'src/modules/timesheets/models/expense.models';
import { useExpenseRules } from 'src/modules/timesheets/utils/expense.util'; import { useExpenseRules } from 'src/modules/timesheets/utils/expense.util';
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api'; import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
import { useTimesheetStore } from 'src/stores/timesheet-store'; import { useTimesheetStore } from 'src/stores/timesheet-store';
@ -18,45 +18,44 @@
const expenses_api = useExpensesApi(); const expenses_api = useExpensesApi();
const files = defineModel<File[] | null>('files'); const files = defineModel<File[] | null>('files');
const is_navigator_open = ref(false); const is_navigator_open = ref(false);
const mode = ref<'create' | 'update' | 'delete'>('create');
const COMMENT_MAX_LENGTH = 280; const COMMENT_MAX_LENGTH = 280;
const employee_email = inject<string>('employeeEmail'); const employee_email = inject<string>('employeeEmail');
const rules = useExpenseRules(t); const rules = useExpenseRules(t);
const background_color = computed(() => expenses_store.mode === 'update' ? 'accent' : '');
const cancelUpdateMode = () => { const cancelUpdateMode = () => {
expenses_store.current_expense = empty_expense; expenses_store.current_expense = new Expense;
expenses_store.initial_expense = empty_expense; expenses_store.initial_expense = new Expense;
} }
const requestExpenseCreationOrUpdate = async () => { const requestExpenseCreationOrUpdate = async () => {
if (mode.value === 'create') await expenses_api.createExpenseByEmployeeEmail(employee_email ?? '', expenses_store.current_expense?.date ?? ''); if (expenses_store.mode === 'create') await expenses_api.createExpenseByEmployeeEmail(employee_email ?? '', expenses_store.current_expense?.date ?? '');
else await expenses_api.updateExpenseByEmployeeEmail(employee_email ?? '', expenses_store.current_expense?.date ?? ''); else await expenses_api.updateExpenseByEmployeeEmail(employee_email ?? '', expenses_store.current_expense?.date ?? '');
} }
</script> </script>
<template> <template>
<q-form <q-form
flat
v-if="!timesheet_store.timesheets?.every(timesheet => timesheet.is_approved)" v-if="!timesheet_store.timesheets?.every(timesheet => timesheet.is_approved)"
:key="expenses_store.mode"
flat
@submit.prevent="requestExpenseCreationOrUpdate" @submit.prevent="requestExpenseCreationOrUpdate"
> >
<div class="text-subtitle2 q-py-sm"> <div class="text-subtitle2 q-py-sm">
{{ $t('timesheet.expense.add_expense') }} {{ $t('timesheet.expense.add_expense') }}
</div> </div>
<div <div class="row justify-between rounded-5">
class="row justify-between rounded-5"
:class="mode === 'update' ? 'bg-accent' : ''"
>
<!-- date selection input --> <!-- date selection input -->
<q-input <q-input
v-model="expenses_store.current_expense.date" v-model="expenses_store.current_expense.date"
dense dense
filled outlined
readonly readonly
stack-label stack-label
class="col q-px-xs" class="col q-px-xs"
:bg-color="background_color"
color="primary" color="primary"
:label="$t('timesheet.expense.date')" :label="$t('timesheet.expense.date')"
> >
@ -85,6 +84,7 @@
filled filled
dense dense
class="col q-px-xs" class="col q-px-xs"
:bg-color="background_color"
color="primary" color="primary"
emit-value emit-value
map-options map-options
@ -105,6 +105,7 @@
clearable clearable
color="primary" color="primary"
class="col q-px-xs" class="col q-px-xs"
:bg-color="background_color"
:label="$t('timesheet.expense.amount')" :label="$t('timesheet.expense.amount')"
suffix="$" suffix="$"
lazy-rules="ondemand" lazy-rules="ondemand"
@ -124,6 +125,7 @@
clearable clearable
color="primary" color="primary"
class="col q-px-xs" class="col q-px-xs"
:bg-color="background_color"
:label="$t('timesheet.expense.mileage')" :label="$t('timesheet.expense.mileage')"
suffix="km" suffix="km"
lazy-rules="ondemand" lazy-rules="ondemand"
@ -135,12 +137,13 @@
<q-input <q-input
v-model="expenses_store.current_expense.comment" v-model="expenses_store.current_expense.comment"
filled filled
color="primary"
type="text"
class="col q-px-sm"
dense dense
stack-label stack-label
clearable clearable
color="primary"
type="text"
class="col q-px-sm"
:bg-color="background_color"
:counter="true" :counter="true"
:maxlength="COMMENT_MAX_LENGTH" :maxlength="COMMENT_MAX_LENGTH"
lazy-rules="ondemand" lazy-rules="ondemand"
@ -156,14 +159,15 @@
<!-- import attach file section --> <!-- import attach file section -->
<q-file <q-file
v-model="files" v-model="files"
:label="$t('timesheet.expense.hints.attach_file')" dense
filled filled
use-chips use-chips
multiple multiple
stack-label stack-label
:label="$t('timesheet.expense.hints.attach_file')"
class="col" class="col"
:bg-color="background_color"
style="max-width: 300px;" style="max-width: 300px;"
dense
> >
<template #prepend> <template #prepend>
<q-icon <q-icon
@ -175,13 +179,14 @@
</q-file> </q-file>
<!-- add btn section --> <!-- add btn section -->
<div> <div class="col-auto column">
<q-btn <q-btn
v-if="mode === 'update'" v-if="expenses_store.mode === 'update'"
flat push
dense dense
size="sm" size="sm"
class="q-mt-sm q-ml-sm" class="col q-ml-sm"
icon="cancel"
@click="cancelUpdateMode" @click="cancelUpdateMode"
/> />
@ -189,9 +194,10 @@
push push
dense dense
color="primary" color="primary"
icon="add" :icon="expenses_store.mode === 'update' ? 'save' : 'add'"
:label="$q.screen.gt.sm ? (expenses_store.mode === 'update' ? $t('shared.label.update') : $t('shared.label.add')) : ''"
size="sm" size="sm"
class="q-mt-sm q-ml-sm" class="col q-mx-xs q-my-sm q-pr-sm"
type="submit" type="submit"
/> />
</div> </div>

View File

@ -2,43 +2,37 @@
setup setup
lang="ts" lang="ts"
> >
/* eslint-disable */
import { computed, inject, ref } from 'vue'; import { computed, inject, ref } from 'vue';
import { unwrapAndClone } from 'src/utils/unwrap-and-clone'; import { unwrapAndClone } from 'src/utils/unwrap-and-clone';
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api'; import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
import { useExpensesStore } from 'src/stores/expense-store'; import { useExpensesStore } from 'src/stores/expense-store';
import { useTimesheetStore } from 'src/stores/timesheet-store';
import { getExpenseIcon } from 'src/modules/timesheets/utils/expense.util'; import { getExpenseIcon } from 'src/modules/timesheets/utils/expense.util';
import { useAuthStore } from 'src/stores/auth-store'; import { useAuthStore } from 'src/stores/auth-store';
import { CAN_APPROVE_PAY_PERIODS } from 'src/modules/shared/models/user.models'; import { CAN_APPROVE_PAY_PERIODS } from 'src/modules/shared/models/user.models';
import { empty_expense, type Expense } from 'src/modules/timesheets/models/expense.models'; import { Expense } from 'src/modules/timesheets/models/expense.models';
import { deepEqual } from 'src/utils/deep-equal';
const { expense, horizontal = false } = defineProps<{ const { expense, horizontal = false } = defineProps<{
expense: Expense; expense: Expense;
index: number; index: number;
horizontal?: boolean; horizontal?: boolean;
}>(); }>();
const is_approved = defineModel<boolean>({ required: true });
const timesheet_store = useTimesheetStore();
const expenses_store = useExpensesStore(); const expenses_store = useExpensesStore();
const auth_store = useAuthStore(); const auth_store = useAuthStore();
const expenses_api = useExpensesApi(); const expenses_api = useExpensesApi();
const employee_email = inject<string>('employeeEmail') ?? '';
const is_approved = defineModel<boolean>({ required: true });
const is_selected = ref(false);
const refresh_key = ref(1); const refresh_key = ref(1);
const background_class = computed(() => deepEqual(expense, expenses_store.current_expense) ? 'bg-accent' : '');
const approved_class = computed(() => horizontal ? ' q-mx-xs q-pa-xs cursor-pointer' : '')
const expense_item_style = computed(() => is_approved.value ? 'border: solid 2px var(--q-primary);' : 'border: solid 2px grey;');
const is_authorized_to_approve = computed(() => CAN_APPROVE_PAY_PERIODS.includes(auth_store.user?.role ?? 'GUEST')) const is_authorized_to_approve = computed(() => CAN_APPROVE_PAY_PERIODS.includes(auth_store.user?.role ?? 'GUEST'))
const expenseItemStyle = computed(() => is_approved.value ? 'border: solid 2px var(--q-primary);' : 'border: solid 2px grey;'); const setExpenseToUpdate = () => {
// const highlightClass = computed(() => (expenses_store.mode === 'update' && is_selected) ? 'bg-accent' : ''); expenses_store.mode = 'update';
const approvedClass = computed(() => horizontal ? ' q-mx-xs q-pa-xs cursor-pointer' : '') // if (expense.is_approved) return;
const employeeEmail = inject<string>('employeeEmail') ?? '';
const setExpenseToModify = () => {
// expenses_store.mode = 'update';
expenses_store.current_expense = expense; expenses_store.current_expense = expense;
expenses_store.initial_expense = unwrapAndClone(expense); expenses_store.initial_expense = unwrapAndClone(expense);
}; };
@ -46,16 +40,26 @@
const requestExpenseDeletion = async () => { const requestExpenseDeletion = async () => {
// expenses_store.mode = 'delete'; // expenses_store.mode = 'delete';
expenses_store.initial_expense = expense; expenses_store.initial_expense = expense;
expenses_store.current_expense = empty_expense; expenses_store.current_expense = new Expense;
await expenses_api.deleteExpenseByEmployeeEmail(employeeEmail, expenses_store.initial_expense.date); await expenses_api.deleteExpenseByEmployeeEmail(employee_email, expenses_store.initial_expense.date);
} }
function onExpenseClicked() { const onExpenseClicked = () => {
if (is_authorized_to_approve.value) { if (is_authorized_to_approve.value) {
is_approved.value = !is_approved.value; is_approved.value = !is_approved.value;
refresh_key.value += 1; refresh_key.value += 1;
} }
} }
const onUpdateClicked = () => {
if (expenses_store.mode === 'update') {
expenses_store.mode = 'create';
expenses_store.current_expense = new Expense;
expenses_store.initial_expense = new Expense;
return;
}
setExpenseToUpdate();
}
</script> </script>
<template> <template>
@ -67,7 +71,8 @@
:key="refresh_key" :key="refresh_key"
:clickable="horizontal" :clickable="horizontal"
class="row col-4 q-ma-xs shadow-2" class="row col-4 q-ma-xs shadow-2"
:style="expenseItemStyle + approvedClass" :class="background_class + approved_class"
:style="expense_item_style"
@click="onExpenseClicked" @click="onExpenseClicked"
> >
<q-badge <q-badge
@ -104,7 +109,7 @@
</q-item-section> </q-item-section>
<!-- amount or mileage section --> <!-- amount or mileage section -->
<q-item-section class="col-auto"> <q-item-section class="col col-md-2">
<q-item-label v-if="String(expense.type).trim().toUpperCase() === 'MILEAGE'"> <q-item-label v-if="String(expense.type).trim().toUpperCase() === 'MILEAGE'">
<template v-if="typeof expense.mileage === 'number'"> <template v-if="typeof expense.mileage === 'number'">
{{ expense.mileage?.toFixed(1) }} km {{ expense.mileage?.toFixed(1) }} km
@ -121,9 +126,9 @@
<q-item-label <q-item-label
caption caption
lines="1" lines="1"
class="text-uppercase"
> >
<!-- {{ $d(new Date(expense.date), { year: 'numeric', month: 'short', day: 'numeric', weekday: 'short' }) }} --> {{ $d(new Date(expense.date), { month: 'short', day: 'numeric', weekday: 'long' }) }}
{{ expense.date }}
</q-item-label> </q-item-label>
</q-item-section> </q-item-section>
@ -174,24 +179,21 @@
</q-item-label> </q-item-label>
</q-item-section> </q-item-section>
<q-item-section <q-item-section :side="$q.screen.gt.sm">
side
class="q-pa-none"
>
<q-btn <q-btn
push push
dense dense
size="xs"
color="primary" color="primary"
icon="edit" icon="edit"
class="q-mb-xs z-top" class="z-top"
@click.stop="setExpenseToModify" @click.stop="onUpdateClicked"
/> />
</q-item-section>
<q-item-section :side="$q.screen.gt.sm">
<q-btn <q-btn
push push
dense dense
size="xs"
color="negative" color="negative"
icon="close" icon="close"
class="z-top" class="z-top"

View File

@ -35,14 +35,21 @@
<ExpenseDialogHeader /> <ExpenseDialogHeader />
<ExpenseDialogList /> <ExpenseDialogList />
<ExpenseDialogForm v-if="!expense_store.current_expense.is_approved" /> <transition
<q-icon appear
v-else enter-active-class="animated fadeInDown faster"
name="block" leave-active-class="animated fadeOutDown faster"
color="negative" mode="out-in"
size="lg" >
/> <ExpenseDialogForm v-if="!expense_store.current_expense.is_approved" />
<q-icon
v-else
name="block"
color="negative"
size="lg"
/>
</transition>
<q-separator spaced /> <q-separator spaced />

View File

@ -46,19 +46,27 @@
time_picker_model.value = time; time_picker_model.value = time;
}; };
const onBlurShiftTypeSelect = () => {
if (shift_type_selected.value === undefined) {
shift.value.shift_id = 0;
}
}
onMounted(() => { onMounted(() => {
if (ui_store.focus_next_component) { if (ui_store.focus_next_component) {
select_ref.value?.focus(); select_ref.value?.focus();
select_ref.value?.showPopup(); select_ref.value?.showPopup();
shift_type_selected.value = undefined;
ui_store.focus_next_component = false; ui_store.focus_next_component = false;
} }
}); });
</script> </script>
<template> <template>
<div <div
v-if="shift.shift_id !== 0" v-if="shift.shift_id !== 0"
class="col row flex-center text-uppercase rounded-10" class="row col flex-center text-uppercase rounded-10"
:class="$q.screen.lt.md ? 'q-pa-xs' : ''"
> >
<!-- shift type --> <!-- shift type -->
<q-select <q-select
@ -66,19 +74,22 @@
v-model="shift_type_selected" v-model="shift_type_selected"
standout="bg-blue-grey-9" standout="bg-blue-grey-9"
dense dense
options-dense :options-dense="!ui_store.is_mobile_mode"
hide-dropdown-icon hide-dropdown-icon
:menu-offset="[0, 10]" :menu-offset="[0, 10]"
menu-anchor="bottom middle"
menu-self="top middle"
:options="options" :options="options"
class="rounded-5 q-mx-xs shadow-1" class="rounded-5 shadow-1"
:class="ui_store.is_mobile_mode ? 'col-auto' : 'col'" :class="ui_store.is_mobile_mode ? 'col-auto q-mx-xs' : 'col q-mx-xs'"
popup-content-class="text-uppercase text-weight-bold text-center rounded-5" popup-content-class="text-uppercase text-weight-bold text-center rounded-5"
popup-content-style="border: 2px solid var(--q-primary)" popup-content-style="border: 2px solid var(--q-primary)"
@blur="onBlurShiftTypeSelect"
> >
<template #selected-item="scope"> <template #selected-item="scope">
<div <div
class="row text-weight-bold q-ma-none q-pa-none no-wrap ellipsis" class="row text-weight-bold q-ma-none q-pa-none no-wrap ellipsis"
:class="ui_store.is_mobile_mode ? 'items-center' : 'flex-center'" :class="ui_store.is_mobile_mode ? 'items-center full-height' : 'flex-center'"
:tabindex="scope.tabindex" :tabindex="scope.tabindex"
> >
<q-icon <q-icon
@ -96,7 +107,7 @@
</template> </template>
</q-select> </q-select>
<!-- punch-in timestamp --> <!-- punch in field -->
<q-input <q-input
v-model="shift.start_time" v-model="shift.start_time"
dense dense
@ -127,7 +138,7 @@
</template> </template>
</q-input> </q-input>
<!-- punch-out timestamps --> <!-- punch out field -->
<q-input <q-input
v-model="shift.end_time" v-model="shift.end_time"
dense dense
@ -169,7 +180,7 @@
:name="shift.comment ? 'comment' : ''" :name="shift.comment ? 'comment' : ''"
color="primary" color="primary"
:size="dense ? 'xs' : 'sm'" :size="dense ? 'xs' : 'sm'"
class="col-auto q-pa-none q-mr-xs" class="col-auto q-pa-none"
/> />
<q-btn <q-btn
@ -178,7 +189,7 @@
dense dense
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'" :icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
:text-color="shift.comment ? 'primary' : 'grey-8'" :text-color="shift.comment ? 'primary' : 'grey-8'"
class="col-auto q-ma-none q-pl-md full-height" class="col-auto q-ma-none full-height"
/> />
<q-btn <q-btn

View File

@ -44,7 +44,10 @@
</script> </script>
<template> <template>
<div :class="$q.screen.lt.md ? 'column' : 'row'"> <div
:class="$q.screen.lt.md ? 'column full-width' : 'row'"
:style="$q.screen.lt.md ? 'width: 90vw !important;' : ''"
>
<div <div
v-for="timesheet in timesheet_store.timesheets" v-for="timesheet in timesheet_store.timesheets"
:key="timesheet.timesheet_id" :key="timesheet.timesheet_id"

View File

@ -5,7 +5,6 @@
import ShiftList from 'src/modules/timesheets/components/shift-list.vue'; import ShiftList from 'src/modules/timesheets/components/shift-list.vue';
import ExpenseDialog from 'src/modules/timesheets/components/expense-dialog.vue'; import ExpenseDialog from 'src/modules/timesheets/components/expense-dialog.vue';
import PayPeriodNavigator from 'src/modules/shared/components/pay-period-navigator.vue'; import PayPeriodNavigator from 'src/modules/shared/components/pay-period-navigator.vue';
// import ShiftListLegend from 'src/modules/timesheets/components/shift-list-legend.vue';
import { useTimesheetStore } from 'src/stores/timesheet-store'; import { useTimesheetStore } from 'src/stores/timesheet-store';
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api'; import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
import { useExpensesStore } from 'src/stores/expense-store'; import { useExpensesStore } from 'src/stores/expense-store';

View File

@ -4,44 +4,45 @@ export const EXPENSE_TYPE: ExpenseType[] = ['PER_DIEM', 'MILEAGE', 'EXPENSES', '
export const TYPES_WITH_MILEAGE_ONLY: Readonly<ExpenseType[]> = ['MILEAGE']; export const TYPES_WITH_MILEAGE_ONLY: Readonly<ExpenseType[]> = ['MILEAGE'];
export const TYPES_WITH_AMOUNT_ONLY: Readonly<ExpenseType[]> = ['PER_DIEM', 'EXPENSES', 'ON_CALL',]; export const TYPES_WITH_AMOUNT_ONLY: Readonly<ExpenseType[]> = ['PER_DIEM', 'EXPENSES', 'ON_CALL',];
export interface Expense { export class Expense {
id: number; id: number;
date: string; //YYYY-MM-DD date: string; //YYYY-MM-DD
type: ExpenseType; type: ExpenseType;
amount: number; amount: number;
mileage?: number; mileage?: number;
comment: string; comment: string;
supervisor_comment?: string; supervisor_comment?: string;
is_approved: boolean; is_approved: boolean;
constructor() {
this.id = -1;
this.date = '';
this.type = 'EXPENSES';
this.amount = 0;
this.comment = '';
this.is_approved = false;
};
}; };
export const empty_expense: Expense = {
id: -1,
date: '',
type: 'EXPENSES',
amount: 0,
comment: '',
is_approved: false,
};
export const test_expenses: Expense[] = [ export const test_expenses: Expense[] = [
{ {
id: 201, id: 201,
date: '2025-01-06', date: '2025-01-06',
type: 'EXPENSES', type: 'EXPENSES',
amount: 15.5, amount: 15.5,
comment: 'Lunch receipt', comment: 'Lunch receipt',
is_approved: false, is_approved: false,
}, },
{ {
id: 202, id: 202,
date: '2025-01-07', date: '2025-01-07',
type: 'MILEAGE', type: 'MILEAGE',
amount: 0, amount: 0,
mileage: 32.4, mileage: 32.4,
comment: 'Travel to client site', comment: 'Travel to client site',
is_approved: true, is_approved: true,
}, },
]; ];

View File

@ -20,7 +20,7 @@ export const useAuthStore = defineStore('auth', () => {
void handleAuthMessage(event); void handleAuthMessage(event);
}); });
const oidc_popup = window.open(`${import.meta.env.VITE_TARGO_BACKEND_AUTH_URL}auth/v1/login`, 'authPopup', 'width=600,height=800'); const oidc_popup = window.open(`${import.meta.env.VITE_TARGO_BACKEND_URL}auth/v1/login`, 'authPopup', 'width=600,height=800');
if (!oidc_popup) if (!oidc_popup)
Notify.create({ Notify.create({

View File

@ -1,7 +1,7 @@
import { ref } from "vue"; import { ref } from "vue";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { ExpensesApiError, type GenericApiError } from "src/modules/timesheets/models/expense-validation.models"; import { ExpensesApiError, type GenericApiError } from "src/modules/timesheets/models/expense-validation.models";
import { empty_expense, test_expenses, type Expense } from "src/modules/timesheets/models/expense.models"; import { test_expenses, Expense } from "src/modules/timesheets/models/expense.models";
import { ExpenseService } from "src/modules/timesheets/services/expense-service"; import { ExpenseService } from "src/modules/timesheets/services/expense-service";
@ -9,9 +9,10 @@ import { ExpenseService } from "src/modules/timesheets/services/expense-service"
export const useExpensesStore = defineStore('expenses', () => { export const useExpensesStore = defineStore('expenses', () => {
const is_open = ref(false); const is_open = ref(false);
const is_loading = ref(false); const is_loading = ref(false);
const mode = ref<'create' | 'update' | 'delete'>('create');
const pay_period_expenses = ref<Expense[]>(test_expenses); const pay_period_expenses = ref<Expense[]>(test_expenses);
const current_expense = ref<Expense>(empty_expense); const current_expense = ref<Expense>(new Expense);
const initial_expense = ref<Expense>(empty_expense); const initial_expense = ref<Expense>(new Expense);
const error = ref<string | null>(null); const error = ref<string | null>(null);
// const setErrorFrom = (err: unknown) => { // const setErrorFrom = (err: unknown) => {
@ -21,13 +22,10 @@ export const useExpensesStore = defineStore('expenses', () => {
const open = (): void => { const open = (): void => {
is_open.value = true; is_open.value = true;
is_loading.value = true;
error.value = null; error.value = null;
current_expense.value = empty_expense; current_expense.value = new Expense;
initial_expense.value = empty_expense; initial_expense.value = new Expense;
mode.value = 'create';
// await getPayPeriodExpensesByTimesheetId(timesheet_id);
is_loading.value = false;
} }
const close = () => { const close = () => {
@ -80,6 +78,7 @@ export const useExpensesStore = defineStore('expenses', () => {
return { return {
is_open, is_open,
is_loading, is_loading,
mode,
pay_period_expenses, pay_period_expenses,
current_expense, current_expense,
initial_expense, initial_expense,