Merge pull request 'dev/matthieu/timesheet-form' (#18) from dev/matthieu/timesheet-form into main
Reviewed-on: Targo/targo_frontend#18
This commit is contained in:
commit
4470c855cf
|
|
@ -160,10 +160,12 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expense: {
|
expense: {
|
||||||
add_expense:"Add Expense",
|
add_expense:'Add Expense',
|
||||||
amount:"Amount",
|
amount:'Amount',
|
||||||
date:"Date",
|
date:'Date',
|
||||||
empty_list:"No registered expenses",
|
empty_list:'No registered expenses',
|
||||||
|
employee_comment:'Comment',
|
||||||
|
supervisor_comment:'Supervisor note',
|
||||||
errors: {
|
errors: {
|
||||||
date_required_or_invalid:"the date is missing or invalid",
|
date_required_or_invalid:"the date is missing or invalid",
|
||||||
comment_required:"A comment required",
|
comment_required:"A comment required",
|
||||||
|
|
|
||||||
|
|
@ -160,10 +160,12 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expense: {
|
expense: {
|
||||||
add_expense:"Ajouter une dépense",
|
add_expense:'Ajouter une dépense',
|
||||||
amount:"Montant",
|
amount:'Montant',
|
||||||
date:"Date",
|
date:'Date',
|
||||||
empty_list:"Aucun dépense enregistrée",
|
empty_list:'Aucun dépense enregistrée',
|
||||||
|
employee_comment:'Commentaire',
|
||||||
|
supervisor_comment:'Note du Superviseur',
|
||||||
errors: {
|
errors: {
|
||||||
date_required_or_invalid:"La date est manquante ou invalide",
|
date_required_or_invalid:"La date est manquante ou invalide",
|
||||||
comment_required:"un commentaire est requis",
|
comment_required:"un commentaire est requis",
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,10 @@
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { EXPENSE_TYPE, type ExpenseType, type TimesheetExpense } from '../../types/timesheet-expenses-interface';
|
import { EXPENSE_TYPE, type ExpenseType, type TimesheetExpense } from '../../types/timesheet-expenses-interface';
|
||||||
import { compute_expense_totals, ExpensesValidationError, normalize_expense, validate_expense_UI } from '../../utils/timesheet-expenses-validators';
|
import { compute_expense_totals, ExpensesValidationError, normalize_expense, validate_expense_UI } from '../../utils/timesheet-expenses-validators';
|
||||||
import { useI18n } from 'vue-i18n';
|
// import { date } from 'quasar';
|
||||||
import { COMMENT_MAX_LENGTH } from '../../composables/use-shift-api';
|
import { COMMENT_MAX_LENGTH } from '../../composables/use-shift-api';
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
//props
|
//props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
@ -31,27 +30,28 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
//q-select mapper
|
//q-select mapper
|
||||||
const type_options = computed(()=> EXPENSE_TYPE.map((val)=> ({
|
const type_options = computed(()=> EXPENSE_TYPE.map( val => ({ label: val, value: val })));
|
||||||
label: t(`timesheet.expense.types.${val}`, val),
|
|
||||||
value: val,
|
|
||||||
})));
|
|
||||||
|
|
||||||
//refs & states
|
//refs & states
|
||||||
const items = ref<TimesheetExpense[]>(Array.isArray(props.initial_expenses) ? props.initial_expenses.map(normalize_expense): []);
|
const items = ref<TimesheetExpense[]>(Array.isArray(props.initial_expenses) ? props.initial_expenses.map(normalize_expense): []);
|
||||||
|
const formRef = ref<InstanceType<any> | null>(null);
|
||||||
|
const triedSubmit = ref(false);
|
||||||
|
|
||||||
|
const DEFAULT_TYPE: ExpenseType = 'EXPENSES'
|
||||||
|
|
||||||
const draft = ref<Partial<TimesheetExpense>>({
|
const draft = ref<Partial<TimesheetExpense>>({
|
||||||
date:'',
|
date:'',
|
||||||
type: 'EXPENSES',
|
type: DEFAULT_TYPE,
|
||||||
comment:'',
|
comment:'',
|
||||||
});
|
});
|
||||||
|
|
||||||
// computeds
|
// computeds
|
||||||
const totals = computed(()=> compute_expense_totals(items.value));
|
const totals = computed(()=> compute_expense_totals(items.value));
|
||||||
const remaining_comment_chars = computed(()=> {
|
const is_readonly = computed(()=> !!props.is_approved);
|
||||||
const comment = String(draft.value.comment ?? '');
|
const showMileage = computed(()=> (draft.value.type as string) === 'MILEAGE');
|
||||||
return COMMENT_MAX_LENGTH - comment.length;
|
const showAmount = computed(()=> !showMileage.value);
|
||||||
});
|
|
||||||
|
|
||||||
//actions
|
//helpers
|
||||||
const reset_draft = () => {
|
const reset_draft = () => {
|
||||||
draft.value.date = '';
|
draft.value.date = '';
|
||||||
draft.value.type = 'EXPENSES';
|
draft.value.type = 'EXPENSES';
|
||||||
|
|
@ -60,10 +60,20 @@ const reset_draft = () => {
|
||||||
draft.value.comment = '';
|
draft.value.comment = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const set_draft_type = (value: ExpenseType) => {
|
||||||
|
draft.value.type = value;
|
||||||
|
if (value === 'MILEAGE') {
|
||||||
|
delete draft.value.amount;
|
||||||
|
} else {
|
||||||
|
delete draft.value.mileage;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//actions
|
||||||
const add_draft_as_item = () => {
|
const add_draft_as_item = () => {
|
||||||
const candidate: TimesheetExpense = normalize_expense({
|
const candidate: TimesheetExpense = normalize_expense({
|
||||||
date: String(draft.value.date ?? '').trim(),
|
date: draft.value.date,
|
||||||
type: String(draft.value.type ?? '').trim(),
|
type: normType(draft.value.type),
|
||||||
...(typeof draft.value.amount === 'number' ? { amount: draft.value.amount }: {}),
|
...(typeof draft.value.amount === 'number' ? { amount: draft.value.amount }: {}),
|
||||||
...(typeof draft.value.mileage === 'number' ? { mileage: draft.value.mileage }: {}),
|
...(typeof draft.value.mileage === 'number' ? { mileage: draft.value.mileage }: {}),
|
||||||
comment: String(draft.value.comment ?? '').trim(),
|
comment: String(draft.value.comment ?? '').trim(),
|
||||||
|
|
@ -117,217 +127,270 @@ const on_save = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const on_form_submit = async () => {
|
||||||
|
triedSubmit.value = true;
|
||||||
|
const ok = await formRef.value?.validate(true);
|
||||||
|
if(!ok) return;
|
||||||
|
add_draft_as_item();
|
||||||
|
};
|
||||||
|
|
||||||
const on_close = () => emit('close');
|
const on_close = () => emit('close');
|
||||||
|
|
||||||
|
//icons managament
|
||||||
//read-only guard for supervisor comment and approved expenses
|
type ExpensesType = 'MILEAGE' | 'EXPENSES' | 'PER_DIEM' | 'PRIME_GARDE' | string;
|
||||||
const is_readonly = computed(()=> !!props.is_approved);
|
const normType = (type: unknown) => String(type ?? '').trim().toUpperCase();
|
||||||
|
const expenseTypeIcon = (type: ExpensesType) => {
|
||||||
|
const t = normType(type);
|
||||||
const set_draft_type = (value: ExpenseType) => (draft.value.type = value);
|
const map: Record<string, string> = {
|
||||||
const set_draft_amount = (value: number | null) => {
|
MILEAGE: 'time_to_leave',
|
||||||
if(value === null || value === undefined || Number.isNaN(Number(value))) {
|
EXPENSES: 'receipt_long',
|
||||||
delete draft.value.amount;
|
PER_DIEM: 'hotel',
|
||||||
} else {
|
PRIME_GARDE: 'admin_panel_settings',
|
||||||
draft.value.amount = Number(value);
|
};
|
||||||
}
|
return map[String(t)] ?? 'help_outline';
|
||||||
};
|
|
||||||
const set_draft_mileage = (value: number | null) => {
|
|
||||||
if(value === null || value === undefined || Number.isNaN(Number(value))) {
|
|
||||||
delete draft.value.mileage;
|
|
||||||
} else {
|
|
||||||
draft.value.mileage = Number(value);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-card class="q-pa-md q-gutter-md" flat bordered>
|
<div >
|
||||||
<!-- header (title with totals)-->
|
<!-- header (title with totals)-->
|
||||||
<div class="row items-center justify-between">
|
<q-item class="row justify-between">
|
||||||
<div class="text-h6"> {{ $t('timesheet.expense.title') }}</div>
|
<q-item-label header class="text-h6 col-auto">
|
||||||
<div class="row items-center q-gutter-sm">
|
{{ $t('timesheet.expense.title') }}
|
||||||
<q-badge :label="$t('timesheet.expense.total_amount') + ': ' + totals.amount.toFixed(2)"/>
|
</q-item-label>
|
||||||
<q-badge :label="$t('timesheet.expense.total_mileage') + ': ' + totals.mileage.toFixed(2)"/>
|
<q-item-section class="items-center col-auto">
|
||||||
</div>
|
<q-badge lines="1" class="q-pa-sm q-px-md" :label="$t('timesheet.expense.total_amount') + ': ' + totals.amount.toFixed(2)"/>
|
||||||
</div>
|
<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-separator/>
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
<!-- liste des dépenses pré existantes -->
|
<!-- liste des dépenses pré existantes -->
|
||||||
<div class="column q-gutter-sm">
|
<q-list
|
||||||
<div
|
padding
|
||||||
v-if="items.length === 0"
|
bordered
|
||||||
class="text-italic text-secondary"
|
class="rounded-borders"
|
||||||
>{{ $t('timesheet.expense.empty_list') }}
|
>
|
||||||
</div>
|
<q-item-label v-if="items.length === 0" class="text-italic q-px-sm">
|
||||||
|
{{ $t('timesheet.expense.empty_list') }}
|
||||||
<q-card
|
</q-item-label>
|
||||||
v-for="(expense, index) in items"
|
<q-item
|
||||||
:key="index"
|
style="border: solid 1px lightgrey; border-radius: 7px;"
|
||||||
flat
|
v-for="(expense, index) in items" :key="index"
|
||||||
bordered
|
class="q-my-xs shadow-1"
|
||||||
class="q-pa-sm"
|
|
||||||
>
|
>
|
||||||
<!-- date section -->
|
<!-- avatar type icon section -->
|
||||||
<div class="row items-start q-col-gutter-md">
|
<q-item-section avatar>
|
||||||
<div class="col-12 col-md-2">
|
<q-icon :name="expenseTypeIcon(expense.type)" color="primary"/>
|
||||||
<div class="text-caption text-secondary">{{ $t('timesheet.expense.date') }} </div>
|
</q-item-section>
|
||||||
<div class="text-body2"> {{ expense.date }}</div>
|
|
||||||
</div>
|
<!-- amount or mileage section -->
|
||||||
</div>
|
<q-item-section top>
|
||||||
<!-- expense type section -->
|
<q-item-label v-if="String(expense.type).trim().toUpperCase() === 'MILEAGE'">
|
||||||
<div class="row items-start q-col-gutter-md">
|
{{ expense.mileage?.toFixed(1) }} km
|
||||||
<div class="col-12 col-md-2">
|
</q-item-label>
|
||||||
<div class="text-body2"> {{ $t('timesheet.expense.types') + expense.type, expense.type }} </div>
|
<q-item-label v-else>
|
||||||
<div class="text-body2">
|
{{ expense.amount?.toFixed(2) }} $
|
||||||
{{ $t('timesheet.expense.types') + expense.type, expense.type }}
|
</q-item-label>
|
||||||
</div>
|
|
||||||
</div>
|
<!-- date label -->
|
||||||
<div class="col-12 col-sm-3">
|
<q-item-label caption lines="2">
|
||||||
<q-select
|
{{ $d(new Date(expense.date + 'T00:00:00'), { year:'numeric', month:'short', day: 'numeric', weekday: 'short'}) }}
|
||||||
v-model="draft.type"
|
</q-item-label>
|
||||||
:options="type_options"
|
</q-item-section>
|
||||||
filled
|
|
||||||
color="primary"
|
<!-- attachment file icon -->
|
||||||
emit-value
|
<q-item-section side>
|
||||||
map-options
|
<q-btn
|
||||||
:label="$t('timesheet.expense.type')"
|
push
|
||||||
:rules="[ val => !! val || $t('timesheet.expense.errors.type_required')]"
|
dense
|
||||||
@update:model-value="val => set_draft_type(val as ExpenseType)"
|
size="md"
|
||||||
/>
|
color="primary"
|
||||||
</div>
|
class="q-mx-lg"
|
||||||
</div>
|
icon="attach_file"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
<!-- amount section -->
|
|
||||||
<div class="col-6 col-md-2">
|
|
||||||
<div class="text-caption text-secondary"> {{ $t('timesheet.expense.amount') }}</div>
|
|
||||||
<div class="text-body2">
|
|
||||||
<span v-if="typeof expense.amount === 'number'">{{ expense.amount.toFixed(2) }}</span>
|
|
||||||
<span v-else class="text-grey-6">-</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- comment section -->
|
<!-- comment section -->
|
||||||
<div class="col-12 col-md">
|
<q-item-section top>
|
||||||
<div class="text-caption text-secondary">{{ $t('timesheet.expense.employee_comment') }}</div>
|
<q-item-label lines="1">
|
||||||
<div class="text-body2">{{ expense.comment }}</div>
|
{{ $t('timesheet.expense.employee_comment') }}
|
||||||
</div>
|
</q-item-label>
|
||||||
|
<q-item-label caption lines="2">
|
||||||
|
{{ expense.comment }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
<!-- supervisor comment section -->
|
<!-- supervisor comment section -->
|
||||||
<div class="col-12 col-md">
|
<q-item-section top>
|
||||||
<div class="text-caption text-secondary">{{ $t('timesheet.expense.supervisor_comment') }}</div>
|
<q-item-label lines="1">
|
||||||
<div class="text-body2">
|
{{ $t('timesheet.expense.supervisor_comment') }}
|
||||||
<span v-if="expense.supervisor_comment">{{ expense.supervisor_comment }}</span>
|
</q-item-label>
|
||||||
<span v-else class="text-grey-6">-</span>
|
<q-item-label v-if="expense.supervisor_comment" caption lines="2">
|
||||||
</div>
|
{{ expense.supervisor_comment }}
|
||||||
</div>
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
<!-- delete btn -->
|
<!-- delete btn -->
|
||||||
<div class="col-auto q-ml-auto">
|
<q-item-section side>
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="!is_readonly"
|
v-if="!is_readonly"
|
||||||
flat
|
push
|
||||||
round
|
dense
|
||||||
size="sm"
|
size="xs"
|
||||||
color="negative"
|
color="negative"
|
||||||
icon="delete"
|
icon="close"
|
||||||
:aria-label="$t('timesheet.delete_button')"
|
|
||||||
@click="remove_item_at(index)"
|
@click="remove_item_at(index)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</q-item-section>
|
||||||
</q-card>
|
</q-item>
|
||||||
</div>
|
</q-list>
|
||||||
|
|
||||||
<q-separator spaced/>
|
<q-form
|
||||||
|
ref="formRef"
|
||||||
|
flat
|
||||||
|
v-if="!is_readonly"
|
||||||
|
@submit.prevent="on_form_submit"
|
||||||
|
>
|
||||||
|
<div class="text-subtitle2 q-py-sm">
|
||||||
|
{{ $t('timesheet.expense.add_expense')}}
|
||||||
|
</div>
|
||||||
|
<div class="row justify-between">
|
||||||
|
|
||||||
<div v-if="!is_readonly" class="column q-gutter-sm">
|
<!-- date selection input -->
|
||||||
<div class="text-subtitle2">{{ $t('timesheet.expense.add_expense')}}</div>
|
<q-input
|
||||||
<!-- add a new expense btn -->
|
v-model="draft.date"
|
||||||
<div class="row q-col-gutter-md">
|
type="date"
|
||||||
<div class="col-12 col-sm-3">
|
dense
|
||||||
<!-- date selection input -->
|
filled
|
||||||
|
class="col- q-px-xs"
|
||||||
|
color="primary"
|
||||||
|
:label="$t('timesheet.expense.date')"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<!-- expenses type selection -->
|
||||||
|
<q-select
|
||||||
|
v-model="draft.type"
|
||||||
|
:options="type_options"
|
||||||
|
:option-label="opt => $t(`timesheet.expense.types.${opt.label}`)"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
class="col-auto q-px-xs"
|
||||||
|
color="primary"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
:label="$t('timesheet.expense.type')"
|
||||||
|
:rules="[ val => !! val || $t('timesheet.expense.errors.type_required')]"
|
||||||
|
@update:model-value="val => set_draft_type(val as ExpenseType)"
|
||||||
|
/>
|
||||||
|
<!-- amount input -->
|
||||||
|
<template v-if="showAmount">
|
||||||
<q-input
|
<q-input
|
||||||
v-model="draft.date"
|
key="amount"
|
||||||
|
v-model.number="draft.amount"
|
||||||
filled
|
filled
|
||||||
|
input-class="text-right"
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
color="primary"
|
color="primary"
|
||||||
type="date"
|
class="col-auto q-px-xs"
|
||||||
:label="$t('timesheet.expense.date')"
|
|
||||||
:rules="[ value =>!!value || $t('timesheet.expense.errors.date_required_or_invalid')]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-sm-3">
|
|
||||||
<!-- amount input -->
|
|
||||||
<q-input
|
|
||||||
:model-value="draft.amount"
|
|
||||||
@update:model-value="val => set_draft_amount(val as any)"
|
|
||||||
filled
|
|
||||||
color="primary"
|
|
||||||
type="number"
|
|
||||||
step="0.01"
|
|
||||||
min="0"
|
|
||||||
:disable="(draft.type as string) === 'MILEAGE'"
|
|
||||||
:label="$t('timesheet.expense.amount')"
|
:label="$t('timesheet.expense.amount')"
|
||||||
:hint="$t('timesheet.expense.hints.amount_or_mileage')"
|
suffix="$"
|
||||||
/>
|
lazy-rules="ondemand"
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-sm-3">
|
|
||||||
<!-- mileage input -->
|
|
||||||
<q-input
|
|
||||||
:model-value="draft.mileage"
|
|
||||||
@update:model-value="val => set_draft_mileage(val as any)"
|
|
||||||
filled
|
|
||||||
color="primary"
|
|
||||||
type="number"
|
|
||||||
step="0.1"
|
|
||||||
min="0"
|
|
||||||
:disable="(draft.type as string) !== 'MILEAGE'"
|
|
||||||
:label="$t('timesheet.expense.mileage')"
|
|
||||||
:hint="$t('timesheet.expense.hints.amount_or_mileage')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12">
|
|
||||||
<!-- employee comment input -->
|
|
||||||
<q-input
|
|
||||||
v-model="draft.comment"
|
|
||||||
filled
|
|
||||||
color="primary"
|
|
||||||
type="textarea"
|
|
||||||
:label="$t('timesheet.expense.employee_comment')"
|
|
||||||
:counter="true"
|
|
||||||
:maxlength="COMMENT_MAX_LENGTH"
|
|
||||||
:rules="[
|
:rules="[
|
||||||
value => (value && String(value).trim().length) || $t('timesheet.expense.errors.comment_required'),
|
value => (value !== undefined && value !== null && value !== '')
|
||||||
value => String(value || '').length <= COMMENT_MAX_LENGTH || $t('timesheet.expense.errors.comment_too_long')
|
|| $t('timesheet.expense.errors.amount_required_for_type')
|
||||||
]"
|
]"
|
||||||
:hint="$t('timesheet.expense.hints.comment_required')"
|
|
||||||
/>
|
/>
|
||||||
<div class="text-right text-caption text-secondary q-mt-xs">
|
</template>
|
||||||
{{ remaining_comment_chars }} {{ $t('general.chars_left') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12">
|
<!-- mileage input -->
|
||||||
<!-- add expense btn -->
|
<template v-else>
|
||||||
<q-btn
|
<q-input
|
||||||
|
key="mileage"
|
||||||
|
v-model.number="draft.mileage"
|
||||||
|
filled
|
||||||
|
input-class="text-right"
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
color="primary"
|
color="primary"
|
||||||
unelevated
|
class="col-auto q-px-xs"
|
||||||
:label="$t('timesheet.expense.add_expense')"
|
:label="$t('timesheet.expense.mileage')"
|
||||||
icon="add"
|
suffix="km"
|
||||||
@click="add_draft_as_item"
|
lazy-rules="ondemand"
|
||||||
|
:rules="[
|
||||||
|
value => (value !== undefined && value !== null && value !== '')
|
||||||
|
|| $t('timesheet.expense.errors.mileage_required_for_type')
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- employee comment input -->
|
||||||
|
<q-input
|
||||||
|
v-model="draft.comment"
|
||||||
|
filled
|
||||||
|
color="primary"
|
||||||
|
type="text"
|
||||||
|
class="col q-px-sm"
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
|
:label="$t('timesheet.expense.employee_comment')"
|
||||||
|
:counter="true"
|
||||||
|
:maxlength="COMMENT_MAX_LENGTH"
|
||||||
|
lazy-rules="ondemand"
|
||||||
|
:rules="[
|
||||||
|
value => (value && String(value).trim().length) || $t('timesheet.expense.errors.comment_required'),
|
||||||
|
value => String(value || '').length <= COMMENT_MAX_LENGTH || $t('timesheet.expense.errors.comment_too_long')
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
color="primary"
|
||||||
|
class="q-px-sm row"
|
||||||
|
>
|
||||||
|
<div class="row column">
|
||||||
|
<q-input
|
||||||
|
v-model="draft.comment"
|
||||||
|
type="text"
|
||||||
|
readonly
|
||||||
|
filled
|
||||||
|
class="col-auto justify-end"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
name="attach_file"
|
||||||
|
size="sm"
|
||||||
|
class="col-auto justify-start"
|
||||||
|
/>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<!-- add btn section -->
|
||||||
|
<div>
|
||||||
|
<q-btn
|
||||||
|
push
|
||||||
|
dense
|
||||||
|
color="primary"
|
||||||
|
icon="add"
|
||||||
|
size="sm"
|
||||||
|
class="q-mt-sm q-ml-sm"
|
||||||
|
type="submit"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
</q-form>
|
||||||
|
|
||||||
<q-separator spaced/>
|
<q-separator spaced/>
|
||||||
|
|
||||||
<div class="row justify-end q-gutter-sm">
|
<div class="row col-auto justify-end">
|
||||||
<!-- close btn -->
|
<!-- close btn -->
|
||||||
<q-btn
|
<q-btn
|
||||||
flat
|
flat
|
||||||
color="primary"
|
color="primary"
|
||||||
:label="$t('timesheet.cancel_button')"
|
:label="$t('timesheet.cancel_button')"
|
||||||
@click="on_close"
|
@click="on_close"
|
||||||
|
|
@ -341,5 +404,5 @@ const set_draft_mileage = (value: number | null) => {
|
||||||
@click="on_save"
|
@click="on_save"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -261,19 +261,19 @@ const on_request_delete = async ({ date, shift }: { date: string; shift: any })
|
||||||
persistent
|
persistent
|
||||||
>
|
>
|
||||||
<q-card
|
<q-card
|
||||||
class="q-pa=md"
|
class="q-pa-md column"
|
||||||
style="min-width:900px; max-width: 95vw;"
|
style=" min-width: 70vw;"
|
||||||
>
|
>
|
||||||
<q-inner-loading :showing="is_loading_expenses">
|
<q-inner-loading :showing="is_loading_expenses">
|
||||||
<q-spinner size="32px"/>
|
<q-spinner size="32px"/>
|
||||||
</q-inner-loading>
|
</q-inner-loading>
|
||||||
<q-banner
|
<!-- <q-banner
|
||||||
v-if="expenses_error"
|
v-if="expenses_error"
|
||||||
dense
|
dense
|
||||||
class="bg-red-2 text-negative q-mt-sm"
|
class="bg-red-2 col-auto text-negative q-mt-sm"
|
||||||
>
|
>
|
||||||
{{ expenses_error }}
|
{{ expenses_error }}
|
||||||
</q-banner>
|
</q-banner> -->
|
||||||
|
|
||||||
<TimesheetDetailsExpenses
|
<TimesheetDetailsExpenses
|
||||||
v-if="expenses_data"
|
v-if="expenses_data"
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ export const EXPENSE_TYPE = [
|
||||||
'PER_DIEM',
|
'PER_DIEM',
|
||||||
'MILEAGE',
|
'MILEAGE',
|
||||||
'EXPENSES',
|
'EXPENSES',
|
||||||
'PRIME_DISPO',
|
'PRIME_GARDE',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type ExpenseType = typeof EXPENSE_TYPE[number];
|
export type ExpenseType = typeof EXPENSE_TYPE[number];
|
||||||
|
|
||||||
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', 'PRIME_DISPO']
|
export const TYPES_WITH_AMOUNT_ONLY: Readonly<ExpenseType[]> = ['PER_DIEM', 'EXPENSES', 'PRIME_GARDE']
|
||||||
Loading…
Reference in New Issue
Block a user