targo-frontend/src/modules/timesheets/components/expenses/timesheet-details-expenses.vue

164 lines
5.2 KiB
Vue

<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { computed, ref } from 'vue';
import { useExpenseForm } from '../../composables/use-expense-form';
import { useExpenseDraft } from '../../composables/use-expense-draft';
import { useExpenseItems } from '../../composables/use-expense-items';
import { COMMENT_MAX_LENGTH } from '../../constants/expense.constants';
import { useToggle } from 'src/modules/shared/composables/use-toggle';
import ExpenseList from './expense-list.vue';
import ExpenseForm from './expense-form.vue';
import {
buildExpenseTypeOptions,
computeExpenseTotals,
makeExpenseRules,
buildExpenseSavePayload
} from '../../utils/expense.util';
import { EXPENSE_TYPE } from '../../types/expense.types';
import { ExpensesValidationError } from '../../types/expense-validation.interface';
import type { TimesheetExpense } from '../../types/expense.interfaces';
/* eslint-disable */
const { t , locale } = useI18n();
const rules = makeExpenseRules(t, COMMENT_MAX_LENGTH);
//------------------ props ------------------
const props = defineProps<{
pay_period_no: number;
pay_year: number;
email: string;
is_approved?: boolean;
initial_expenses?: TimesheetExpense[];
}>();
//------------------ emits ------------------
const emit = defineEmits<{
(e:'close'): void;
(e: 'save', payload: {
pay_period_no: number;
pay_year: number;
email: string;
expenses: TimesheetExpense[];
}): void;
(e: 'error', err: ExpensesValidationError): void;
}>();
//------------------ q-select mapper ------------------
const type_options = computed(()=> {
void locale.value;
return buildExpenseTypeOptions(EXPENSE_TYPE, t);
})
//------------------ refs and computed ------------------
const files = ref<File[] | null>(null);
const is_readonly = computed(()=> !!props.is_approved);
const { state: is_open_date_picker } = useToggle();
const { draft, setType, reset, showAmount } = useExpenseDraft();
const { validateAnd } = useExpenseForm();
const { items, addFromDraft, removeAt, validateAll, payload } = useExpenseItems({
initial_expenses: props.initial_expenses,
draft,
is_approved: is_readonly,
});
const totals = computed(()=> computeExpenseTotals(items.value));
//------------------ actions ------------------
const onSave = () => {
try {
validateAll();
reset();
emit('save', buildExpenseSavePayload({
pay_period_no: props.pay_period_no,
pay_year: props.pay_year,
email: props.email,
expenses: payload(),
}));
} catch(err: any) {
const e = err instanceof ExpensesValidationError
? err
: new ExpensesValidationError({
status_code: 400,
message: String(err?.message || err)
});
emit('error', e);
}
};
const onFormSubmit = async () => {
try {
await validateAnd(async () => {
addFromDraft();
reset();
});
} catch (err: any) {
const e = err instanceof ExpensesValidationError
? err
: new ExpensesValidationError({
status_code: 400,
message: String(err?.message || err)
});
emit('error', e);
}
};
const onClose = () => emit('close');
</script>
<template>
<div>
<!-- header (title with totals)-->
<q-item class="row justify-between">
<q-item-label header class="text-h6 col-auto">
{{ $t('timesheet.expense.title') }}
</q-item-label>
<q-item-section class="items-center col-auto">
<q-badge lines="1" class="q-pa-sm q-px-md" :label="$t('timesheet.expense.total_amount') + ': ' + totals.amount.toFixed(2)"/>
<q-separator spaced/>
<q-badge lines="2" class="q-pa-sm q-px-md" :label="$t('timesheet.expense.total_mileage') + ': ' + totals.mileage.toFixed(1)"/>
</q-item-section>
</q-item>
<ExpenseList
:items="items"
:is_readonly="is_readonly"
@remove="removeAt"
/>
<ExpenseForm
v-model:draft="draft"
v-model:files="files"
v-model:date-picker-open="is_open_date_picker"
:type_options="type_options"
:show_amount="showAmount"
:is_readonly="is_readonly"
:rules="rules"
:comment_max_length="COMMENT_MAX_LENGTH"
:set-type="setType"
@submit="onFormSubmit"
/>
<q-separator spaced/>
<div class="row col-auto justify-end">
<!-- close btn -->
<q-btn
flat
class="q-mr-sm"
color="primary"
:label="$t('timesheet.cancel_button')"
@click="onClose"
/>
<!-- save btn -->
<q-btn
color="primary"
unelevated
push
:disable="is_readonly || items.length === 0"
:label="$t('timesheet.save_button')"
@click="onSave"
/>
</div>
</div>
</template>