targo-frontend/src/modules/timesheets/components/expense-dialog-form.vue

236 lines
8.7 KiB
Vue

<script
setup
lang="ts"
>
import { inject, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useExpensesStore } from 'src/stores/expense-store';
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 { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
import { useTimesheetStore } from 'src/stores/timesheet-store';
import { date } from 'quasar';
const { t } = useI18n();
const timesheet_store = useTimesheetStore();
const expenses_store = useExpensesStore();
const expenses_api = useExpensesApi();
const files = defineModel<File[] | null>('files');
const is_navigator_open = ref(false);
const COMMENT_MAX_LENGTH = 280;
const employee_email = inject<string>('employeeEmail');
const rules = useExpenseRules(t);
const openDatePicker = () => {
is_navigator_open.value = true;
if (expenses_store.current_expense.date === '') {
expenses_store.current_expense.date = date.formatDate(new Date(), 'YYYY-MM-DD');
}
};
const cancelUpdateMode = () => {
expenses_store.current_expense = new Expense(date.formatDate(new Date(), 'YYYY-MM-DD'));
expenses_store.initial_expense = new Expense(date.formatDate(new Date(), 'YYYY-MM-DD'));
expenses_store.mode = 'create';
};
const requestExpenseCreationOrUpdate = async () => {
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 ?? '');
};
const getExpenseCalendarRange = (current_date: string) => {
const period = timesheet_store.pay_period;
if (period !== undefined) return current_date >= period.period_start && current_date <= period.period_end;
return false;
}
</script>
<template>
<q-form
v-if="!timesheet_store.timesheets?.every(timesheet => timesheet.is_approved)"
:key="expenses_store.current_expense.id"
flat
@submit.prevent="requestExpenseCreationOrUpdate"
>
<div class="text-uppercase text-weight-medium q-pt-sm q-px-lg">
{{ $t('timesheet.expense.add_expense') }}
</div>
<div class="row justify-between items-start rounded-5 q-px-lg q-pb-sm">
<!-- date selection input -->
<q-input
v-model="expenses_store.current_expense.date"
dense
outlined
readonly
stack-label
class="col q-px-xs"
color="primary"
:label="$t('timesheet.expense.date')"
>
<template #prepend>
<q-btn
push
dense
icon="event"
color="accent"
class="q-mr-sm"
@click="openDatePicker"
/>
<q-dialog
v-model="is_navigator_open"
transition-show="jump-right"
transition-hide="jump-right"
>
<q-date
v-model="expenses_store.current_expense.date"
mask="YYYY-MM-DD"
event-color="accent"
:options="getExpenseCalendarRange"
@update:model-value="is_navigator_open = false"
/>
</q-dialog>
</template>
</q-input>
<!-- expenses type selection -->
<q-select
v-model="expenses_store.current_expense.type"
:options="EXPENSE_TYPE"
standout="bg-blue-grey-9"
dense
emit-value
map-options
hide-dropdown-icon
class="col q-px-xs"
color="primary"
:label="$t('timesheet.expense.type')"
:menu-offset="[0, 10]"
menu-anchor="bottom middle"
menu-self="top middle"
popup-content-class="text-uppercase text-weight-bold text-center rounded-5"
popup-content-style="border: 2px solid var(--q-accent)"
:rules="[rules.typeRequired]"
:option-label="label => $t(`timesheet.expense.types.${label}`)"
/>
<!-- amount input -->
<div v-if="TYPES_WITH_AMOUNT_ONLY.includes(expenses_store.current_expense?.type ?? 'EXPENSES')">
<q-input
key="amount"
v-model.number="expenses_store.current_expense.amount"
filled
input-class="text-right"
dense
stack-label
color="primary"
class="col q-px-xs"
label-slot
suffix="$"
lazy-rules="ondemand"
:rules="[rules.amountRequired]"
>
<template #label>
<span class="text-weight-bold text-accent text-uppercase text-caption">
{{ $t('timesheet.expense.amount') }}
</span>
</template>
</q-input>
</div>
<!-- mileage input -->
<div v-else>
<q-input
key="mileage"
v-model.number="expenses_store.current_expense.mileage"
filled
input-class="text-right"
dense
stack-label
clearable
color="primary"
class="col q-px-xs"
label-slot
suffix="km"
lazy-rules="ondemand"
:rules="[rules.mileageRequired]"
>
<template #label>
<span class="text-weight-bold text-accent text-uppercase text-caption">
{{ $t('timesheet.expense.mileage') }}
</span>
</template>
</q-input>
</div>
<!-- employee comment input -->
<q-input
v-model="expenses_store.current_expense.comment"
filled
dense
stack-label
label-slot
color="primary"
type="text"
class="col q-px-sm"
:counter="true"
:maxlength="COMMENT_MAX_LENGTH"
lazy-rules="ondemand"
:rules="[rules.commentRequired]"
>
<template #label>
<span class="text-weight-bold text-accent text-uppercase text-caption">
{{ $t('timesheet.expense.employee_comment') }}
</span>
</template>
</q-input>
<!-- import attach file section -->
<q-file
v-model="files"
dense
filled
use-chips
multiple
stack-label
:label="$t('timesheet.expense.hints.attach_file')"
class="col"
style="max-width: 300px;"
>
<template #prepend>
<q-icon
name="attach_file"
size="sm"
color="primary"
/>
</template>
</q-file>
</div>
<div class="col row full-width items-center">
<q-space />
<q-btn
v-if="expenses_store.mode === 'update'"
flat
dense
class="col-auto q-ml-sm"
icon="clear"
color="negative"
:label="$q.screen.gt.sm ? $t('shared.label.cancel') : ''"
@click="cancelUpdateMode"
/>
<q-btn
push
color="accent"
:icon="expenses_store.mode === 'update' ? 'save' : 'upload'"
:label="$q.screen.gt.sm ? (expenses_store.mode === 'update' ? $t('shared.label.update') : $t('shared.label.add')) : ''"
class="q-px-sm q-mb-sm q-mx-lg"
type="submit"
/>
</div>
</q-form>
</template>