236 lines
8.7 KiB
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> |