255 lines
9.8 KiB
Vue
255 lines
9.8 KiB
Vue
<script
|
|
setup
|
|
lang="ts"
|
|
>
|
|
import ExpenseDialogForm from 'src/modules/timesheets/components/expense-dialog-form.vue';
|
|
|
|
import { computed, ref, toRaw } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { date, Notify } from 'quasar';
|
|
import { useExpensesStore } from 'src/stores/expense-store';
|
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
|
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
|
import { getExpenseIcon } from 'src/modules/timesheets/utils/expense.util';
|
|
import type { Expense } from 'src/modules/timesheets/models/expense.models';
|
|
|
|
// ================== state =====================
|
|
|
|
const expense = defineModel<Expense>({ required: true });
|
|
|
|
const { mode = 'normal' } = defineProps<{
|
|
mode?: 'approval' | 'normal';
|
|
}>();
|
|
|
|
const { t } = useI18n();
|
|
const expensesApi = useExpensesApi();
|
|
const expenseStore = useExpensesStore();
|
|
const timesheetStore = useTimesheetStore();
|
|
const isShowingUpdateForm = ref(false);
|
|
|
|
// ========== computed ===================================
|
|
|
|
const attachmentButtonColor = computed(() => expense.value.attachment_name ?
|
|
(expense.value.is_approved ? 'white' : 'accent') :
|
|
'grey-5');
|
|
|
|
// ===================== methods =========================
|
|
|
|
const requestExpenseDeletion = async () => {
|
|
await expensesApi.deleteExpenseById(expense.value.id);
|
|
}
|
|
|
|
const onClickExpenseUpdate = () => {
|
|
if (expense.value.is_approved) return;
|
|
|
|
expenseStore.mode = 'update';
|
|
expenseStore.current_expense = structuredClone(toRaw(expense.value));
|
|
expenseStore.initial_expense = structuredClone(toRaw(expense.value));
|
|
}
|
|
|
|
const onClickApproval = async () => {
|
|
expenseStore.current_expense = structuredClone(toRaw(expense.value));
|
|
expenseStore.current_expense.is_approved = !expenseStore.current_expense.is_approved;
|
|
|
|
const success = await expenseStore.upsertExpense(
|
|
expenseStore.current_expense,
|
|
timesheetStore.current_pay_period_overview?.email
|
|
);
|
|
|
|
if (success) {
|
|
expense.value.is_approved = !expense.value.is_approved;
|
|
} else {
|
|
expenseStore.current_expense.is_approved = !expenseStore.current_expense.is_approved;
|
|
Notify.create({
|
|
message: t('timesheet.errors.UPDATE_ERROR'),
|
|
color: "negative"
|
|
});
|
|
}
|
|
}
|
|
|
|
const getEmployeeEmail = () => {
|
|
if (mode === 'approval')
|
|
return timesheetStore.current_pay_period_overview?.email;
|
|
}
|
|
|
|
const onClickAttachment = async () => {
|
|
expenseStore.isShowingAttachmentDialog = true;
|
|
await expenseStore.getAttachmentURL(expense.value.attachment_key);
|
|
}
|
|
|
|
const hideUpdateForm = () => {
|
|
isShowingUpdateForm.value = false;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<q-expansion-item
|
|
v-model="isShowingUpdateForm"
|
|
hide-expand-icon
|
|
dense
|
|
group="expenses"
|
|
class="shadow-3 rounded-5 bg-dark q-my-sm"
|
|
:class="expense.is_approved ? ' bg-accent text-white' : ''"
|
|
@before-show="onClickExpenseUpdate()"
|
|
>
|
|
<template #header>
|
|
<div class="col row items-center full-width">
|
|
<!-- avatar type icon section -->
|
|
<div class="col-auto">
|
|
<q-icon
|
|
:name="getExpenseIcon(expense.type)"
|
|
size="lg"
|
|
class="q-pr-md"
|
|
/>
|
|
|
|
<q-tooltip
|
|
anchor="top middle"
|
|
self="center middle"
|
|
:offset="[0, 20]"
|
|
class="bg-accent text-uppercase text-weight-bold"
|
|
>
|
|
{{ $t(`timesheet.expense.types.${expense.type}`) }}
|
|
</q-tooltip>
|
|
</div>
|
|
|
|
<!-- amount or mileage section -->
|
|
<div class="col-auto column q-pr-md">
|
|
<span
|
|
class="text-weight-bolder"
|
|
:class="expense.is_approved ? ' bg-accent text-white' : ''"
|
|
style="font-size: 1.3em;"
|
|
>
|
|
{{ expense.type === 'MILEAGE' ? `${Number(expense.mileage).toFixed(1)} km` : `$
|
|
${Number(expense.amount).toFixed(2)}` }}
|
|
</span>
|
|
|
|
<!-- date label -->
|
|
<span
|
|
class="text-uppercase text-weight-light text-caption"
|
|
:class="expense.is_approved ? ' bg-accent text-white' : ''"
|
|
>
|
|
{{ $d(date.extractDate(expense.date, 'YYYY-MM-DD'), {
|
|
month: 'long', day: 'numeric', weekday: 'long'
|
|
}) }}
|
|
</span>
|
|
</div>
|
|
|
|
<q-separator
|
|
vertical
|
|
spaced
|
|
class="q-my-xs"
|
|
/>
|
|
|
|
<!-- comments section -->
|
|
<div class="col column">
|
|
<div class="col row items-center">
|
|
<span
|
|
class="col-auto text-weight-medium text-accent text-uppercase q-pr-md"
|
|
style="font-size: 1.2em;"
|
|
>
|
|
{{ $t('timesheet.expense.employee_comment') }} :
|
|
</span>
|
|
|
|
<span
|
|
class="col"
|
|
:class="expense.is_approved ? ' bg-accent text-white' : ''"
|
|
>
|
|
{{ expense.comment }}
|
|
</span>
|
|
</div>
|
|
|
|
<q-separator class="q-mr-md" />
|
|
|
|
<div class="col row items-center">
|
|
<span
|
|
class="col-auto text-weight-medium text-accent text-uppercase q-pr-md"
|
|
style="font-size: 1.2em; "
|
|
:style="expense.supervisor_comment ? '' : 'filter: grayscale(1);'"
|
|
>
|
|
{{ $t('timesheet.expense.supervisor_comment') }} :
|
|
</span>
|
|
|
|
<span
|
|
class="col"
|
|
:class="expense.is_approved ? ' bg-accent text-white' : ''"
|
|
>
|
|
{{ expense.supervisor_comment }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- attachment -->
|
|
<div class="col-auto">
|
|
<q-btn
|
|
flat
|
|
size="lg"
|
|
:disable="expense.attachment_name === undefined"
|
|
:color="attachmentButtonColor"
|
|
class="col-auto q-px-sm q-mr-sm"
|
|
:icon="expense.attachment_key ? 'image' : 'hide_image'"
|
|
@click.stop="onClickAttachment"
|
|
>
|
|
<q-tooltip
|
|
anchor="top middle"
|
|
self="center middle"
|
|
:offset="[0, 20]"
|
|
class="bg-accent text-uppercase text-weight-bold"
|
|
>
|
|
{{ expense.attachment_name ?? $t('timesheet.expense.no_attachment') }}
|
|
</q-tooltip>
|
|
</q-btn>
|
|
</div>
|
|
|
|
<!-- buttons -->
|
|
<div class="col-auto">
|
|
<q-btn
|
|
v-if="mode === 'approval'"
|
|
flat
|
|
size="lg"
|
|
:icon="expense.is_approved ? 'lock' : 'lock_open'"
|
|
:color="expense.is_approved ? 'white' : 'accent'"
|
|
class="relative-position"
|
|
@click.stop="onClickApproval"
|
|
>
|
|
<q-tooltip
|
|
anchor="top middle"
|
|
self="center middle"
|
|
:offset="[0, 20]"
|
|
class="bg-accent text-uppercase text-weight-bold"
|
|
>
|
|
{{ expense.is_approved ? $t('timesheet_approvals.tooltip.unapprove') :
|
|
$t('timesheet_approvals.tooltip.approve') }}
|
|
</q-tooltip>
|
|
</q-btn>
|
|
|
|
<q-icon
|
|
v-if="expense.is_approved"
|
|
name="verified"
|
|
color="white"
|
|
size="2.5em"
|
|
class="q-px-sm"
|
|
/>
|
|
|
|
<q-btn
|
|
v-else-if="!expense.is_approved && mode === 'normal'"
|
|
flat
|
|
dense
|
|
size="lg"
|
|
icon="close"
|
|
color="negative"
|
|
class="q-py-xs q-px-sm"
|
|
@click.stop="requestExpenseDeletion"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<ExpenseDialogForm
|
|
:key="isShowingUpdateForm ? 1 : 2"
|
|
:expense-type="expense.type"
|
|
:email="getEmployeeEmail()"
|
|
:mode="mode"
|
|
@click-save="hideUpdateForm"
|
|
/>
|
|
</q-expansion-item>
|
|
</template> |