fix(approval): fix issue where employee email was not properly propagated to shifts and expenses in approval page
This commit is contained in:
parent
9a520d94bf
commit
7267efc258
|
|
@ -52,7 +52,11 @@
|
||||||
|
|
||||||
<!-- list of shifts -->
|
<!-- list of shifts -->
|
||||||
<div class="col-auto column no-wrap">
|
<div class="col-auto column no-wrap">
|
||||||
<TimesheetWrapper mode="approval" class="col-auto"/>
|
<TimesheetWrapper
|
||||||
|
mode="approval"
|
||||||
|
:employee-email="timesheet_store.current_pay_period_overview?.email"
|
||||||
|
class="col-auto"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
>
|
>
|
||||||
import { date } from 'quasar';
|
import { date } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, inject, onMounted, ref } from 'vue';
|
||||||
import { useUiStore } from 'src/stores/ui-store';
|
import { useUiStore } from 'src/stores/ui-store';
|
||||||
import { useExpensesStore } from 'src/stores/expense-store';
|
import { useExpensesStore } from 'src/stores/expense-store';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
|
|
@ -12,35 +12,39 @@
|
||||||
import { getExpenseIcon, useExpenseRules } from 'src/modules/timesheets/utils/expense.util';
|
import { getExpenseIcon, useExpenseRules } from 'src/modules/timesheets/utils/expense.util';
|
||||||
import { Expense, type ExpenseType, TYPES_WITH_AMOUNT_ONLY } from 'src/modules/timesheets/models/expense.models';
|
import { Expense, type ExpenseType, TYPES_WITH_AMOUNT_ONLY } from 'src/modules/timesheets/models/expense.models';
|
||||||
|
|
||||||
|
// ================= state ======================
|
||||||
|
|
||||||
interface ExpenseOption {
|
interface ExpenseOption {
|
||||||
label: string;
|
label: string;
|
||||||
value: ExpenseType;
|
value: ExpenseType;
|
||||||
icon: string;
|
icon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const COMMENT_MAX_LENGTH = 280;
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ui_store = useUiStore();
|
const ui_store = useUiStore();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
const expenses_store = useExpensesStore();
|
const expenses_store = useExpensesStore();
|
||||||
const expenses_api = useExpensesApi();
|
const expenses_api = useExpensesApi();
|
||||||
const files = defineModel<File[] | null>('files');
|
const files = defineModel<File[] | null>('files');
|
||||||
|
|
||||||
const is_navigator_open = ref(false);
|
const is_navigator_open = ref(false);
|
||||||
|
|
||||||
const COMMENT_MAX_LENGTH = 280;
|
|
||||||
const rules = useExpenseRules(t);
|
const rules = useExpenseRules(t);
|
||||||
|
|
||||||
const period_start_date = computed(() => timesheet_store.pay_period?.period_start.replaceAll('-', '/') ?? '');
|
|
||||||
const period_end_date = computed(() => timesheet_store.pay_period?.period_end.replaceAll('-', '/') ?? '');
|
|
||||||
|
|
||||||
const expense_options: ExpenseOption[] = [
|
const expense_options: ExpenseOption[] = [
|
||||||
{ label: t('timesheet.expense.types.PER_DIEM'), value: 'PER_DIEM', icon: getExpenseIcon('PER_DIEM') },
|
{ label: t('timesheet.expense.types.PER_DIEM'), value: 'PER_DIEM', icon: getExpenseIcon('PER_DIEM') },
|
||||||
{ label: t('timesheet.expense.types.EXPENSES'), value: 'EXPENSES', icon: getExpenseIcon('EXPENSES') },
|
{ label: t('timesheet.expense.types.EXPENSES'), value: 'EXPENSES', icon: getExpenseIcon('EXPENSES') },
|
||||||
{ label: t('timesheet.expense.types.MILEAGE'), value: 'MILEAGE', icon: getExpenseIcon('MILEAGE') },
|
{ label: t('timesheet.expense.types.MILEAGE'), value: 'MILEAGE', icon: getExpenseIcon('MILEAGE') },
|
||||||
{ label: t('timesheet.expense.types.ON_CALL'), value: 'ON_CALL', icon: getExpenseIcon('ON_CALL') },
|
{ label: t('timesheet.expense.types.ON_CALL'), value: 'ON_CALL', icon: getExpenseIcon('ON_CALL') },
|
||||||
]
|
]
|
||||||
|
|
||||||
const expense_selected = ref<ExpenseOption | undefined>();
|
const expense_selected = ref<ExpenseOption | undefined>();
|
||||||
|
const employeeEmail = inject<string>('employeeEmail');
|
||||||
|
|
||||||
|
// ================== computed ===================
|
||||||
|
|
||||||
|
const period_start_date = computed(() => timesheet_store.pay_period?.period_start.replaceAll('-', '/') ?? '');
|
||||||
|
const period_end_date = computed(() => timesheet_store.pay_period?.period_end.replaceAll('-', '/') ?? '');
|
||||||
|
|
||||||
|
// ==================== method =======================
|
||||||
|
|
||||||
const openDatePicker = () => {
|
const openDatePicker = () => {
|
||||||
is_navigator_open.value = true;
|
is_navigator_open.value = true;
|
||||||
|
|
@ -55,7 +59,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestExpenseCreationOrUpdate = async () => {
|
const requestExpenseCreationOrUpdate = async () => {
|
||||||
await expenses_api.upsertExpense(expenses_store.current_expense);
|
await expenses_api.upsertExpense(expenses_store.current_expense, employeeEmail);
|
||||||
|
|
||||||
expenses_store.is_showing_create_form = true;
|
expenses_store.is_showing_create_form = true;
|
||||||
expenses_store.mode = 'create';
|
expenses_store.mode = 'create';
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,31 @@
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
import { computed } from 'vue';
|
import { computed, inject } from 'vue';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import ExpenseDialogListItem from 'src/modules/timesheets/components/expense-dialog-list-item.vue';
|
import ExpenseDialogListItem from 'src/modules/timesheets/components/expense-dialog-list-item.vue';
|
||||||
import ExpenseDialogListItemMobile from 'src/modules/timesheets/components/mobile/expense-dialog-list-item-mobile.vue';
|
import ExpenseDialogListItemMobile from 'src/modules/timesheets/components/mobile/expense-dialog-list-item-mobile.vue';
|
||||||
|
|
||||||
|
// ================== state =======================
|
||||||
|
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
||||||
const { mode = 'normal' } = defineProps<{
|
const { mode = 'normal' } = defineProps<{
|
||||||
mode?: 'approval' | 'normal';
|
mode?: 'approval' | 'normal';
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
// ========================== computed ==========================
|
||||||
|
|
||||||
const expenses_list = computed(() => {
|
const expenses_list = computed(() => {
|
||||||
if (timesheet_store.timesheets !== undefined) {
|
if (timesheet_store.timesheets !== undefined) {
|
||||||
return timesheet_store.timesheets.flatMap(week => week.days).flatMap(day => day.expenses);
|
return timesheet_store.timesheets.flatMap(week => week.days).flatMap(day => day.expenses);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ==================== methods ========================
|
||||||
|
|
||||||
|
inject( 'employeeEmail', mode === 'approval' ? timesheet_store.current_pay_period_overview?.email : undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,16 @@
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { QSelect, QInput, useQuasar } from 'quasar';
|
import { QSelect, QInput, useQuasar, QSelectProps } from 'quasar';
|
||||||
import { useUiStore } from 'src/stores/ui-store';
|
import { useUiStore } from 'src/stores/ui-store';
|
||||||
import { SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
|
import { SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
|
||||||
import type { Shift } from 'src/modules/timesheets/models/shift.models';
|
import type { Shift } from 'src/modules/timesheets/models/shift.models';
|
||||||
|
|
||||||
const q = useQuasar();
|
// ================== State ==================
|
||||||
const { t } = useI18n();
|
|
||||||
const ui_store = useUiStore();
|
|
||||||
|
|
||||||
const COMMENT_LENGTH_MAX = 280;
|
const COMMENT_LENGTH_MAX = 280;
|
||||||
|
|
||||||
const shift = defineModel<Shift>('shift', { required: true });
|
|
||||||
const shift_type_selected = ref(SHIFT_OPTIONS.find(option => option.value == shift.value.type));
|
|
||||||
const select_ref = ref<QSelect | null>(null);
|
|
||||||
const error_message = ref<string | undefined>();
|
|
||||||
const is_showing_delete_confirm = ref(false);
|
|
||||||
|
|
||||||
const { errorMessage = undefined, isTimesheetApproved = false, holiday = false } = defineProps<{
|
const { errorMessage = undefined, isTimesheetApproved = false, holiday = false } = defineProps<{
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
isTimesheetApproved?: boolean;
|
isTimesheetApproved?: boolean;
|
||||||
|
|
@ -28,7 +20,25 @@
|
||||||
holiday?: boolean | undefined;
|
holiday?: boolean | undefined;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const time_input_props = {
|
const emit = defineEmits<{
|
||||||
|
'requestDelete': [void];
|
||||||
|
'onTimeFieldBlur': [void];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const shift = defineModel<Shift>('shift', { required: true });
|
||||||
|
|
||||||
|
const q = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const ui_store = useUiStore();
|
||||||
|
|
||||||
|
const shiftTypeSelected = ref(SHIFT_OPTIONS.find(option => option.value == shift.value.type));
|
||||||
|
const selectRef = ref<QSelect | null>(null);
|
||||||
|
const shiftErrorMessage = ref<string | undefined>();
|
||||||
|
const is_showing_delete_confirm = ref(false);
|
||||||
|
|
||||||
|
// ================== Computed ==================
|
||||||
|
|
||||||
|
const timeInputProps = computed(() => ({
|
||||||
dense: true,
|
dense: true,
|
||||||
borderless: shift.value.is_approved && isTimesheetApproved,
|
borderless: shift.value.is_approved && isTimesheetApproved,
|
||||||
readonly: shift.value.is_approved && isTimesheetApproved,
|
readonly: shift.value.is_approved && isTimesheetApproved,
|
||||||
|
|
@ -38,32 +48,46 @@
|
||||||
noErrorIcon: true,
|
noErrorIcon: true,
|
||||||
hideBottomSpace: true,
|
hideBottomSpace: true,
|
||||||
error: shift.value.has_error,
|
error: shift.value.has_error,
|
||||||
errorMessage: errorMessage ? t(errorMessage) : (error_message.value ? t(error_message.value) : undefined),
|
errorMessage: errorMessage ? t(errorMessage) : (shiftErrorMessage.value ? t(shiftErrorMessage.value) : undefined),
|
||||||
labelColor: shift.value.is_approved ? 'white' : (holiday ? 'purple-5' : 'accent'),
|
labelColor: shift.value.is_approved ? 'white' : (holiday ? 'purple-5' : 'accent'),
|
||||||
class: `col rounded-5 bg-dark q-mx-xs ${shift.value.id === -2 ? 'bg-negative' : ''} ${shift.value.is_approved || isTimesheetApproved ? 'cursor-not-allowed inset-shadow' : ''}`,
|
class: `col rounded-5 bg-dark q-mx-xs ${shift.value.id === -2 ? 'bg-negative' : ''} ${shift.value.is_approved || isTimesheetApproved ? 'cursor-not-allowed inset-shadow' : ''}`,
|
||||||
inputClass: `text-weight-medium ${shift.value.id === -2 ? 'text-white ' : ' '} ${shift.value.is_approved ? 'text-white cursor-not-allowed q-px-sm' : ''}`,
|
inputClass: `text-weight-medium ${shift.value.id === -2 ? 'text-white ' : ' '} ${shift.value.is_approved ? 'text-white cursor-not-allowed q-px-sm' : ''}`,
|
||||||
style: shift.value.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : '',
|
style: shift.value.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : '',
|
||||||
inputStyle: "font-size: 1.2em;"
|
inputStyle: "font-size: 1.2em;"
|
||||||
}
|
}));
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const shiftTypeSelectProps = computed<Partial<QSelectProps>>(() => ({
|
||||||
'requestDelete': [void];
|
standout: q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9',
|
||||||
'onTimeFieldBlur': [void];
|
dense: true,
|
||||||
}>();
|
borderless: shift.value.is_approved && isTimesheetApproved,
|
||||||
|
readonly: shift.value.is_approved && isTimesheetApproved,
|
||||||
|
optionsDense: !ui_store.is_mobile_mode,
|
||||||
|
hideDropdownIcon: true,
|
||||||
|
menuOffset: [0, 10],
|
||||||
|
menuAnchor: "bottom middle",
|
||||||
|
menuSelf: "top middle",
|
||||||
|
options: SHIFT_OPTIONS,
|
||||||
|
class: `col rounded-5 q-mx-xs bg-dark ${!shift.value.is_approved && !isTimesheetApproved ? '' : 'inset-shadow'}`,
|
||||||
|
popupContentClass: "text-uppercase text-weight-bold text-center rounded-5",
|
||||||
|
style: shift.value.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : '',
|
||||||
|
popupContentStyle: "border: 2px solid var(--q-accent)",
|
||||||
|
}));
|
||||||
|
|
||||||
|
// ================== Methods ==================
|
||||||
|
|
||||||
const onTimeFieldBlur = (time_string: string) => {
|
const onTimeFieldBlur = (time_string: string) => {
|
||||||
if (time_string.length < 1 || !time_string) {
|
if (time_string.length < 1 || !time_string) {
|
||||||
shift.value.has_error = true;
|
shift.value.has_error = true;
|
||||||
error_message.value = 'timesheet.errors.SHIFT_TIME_REQUIRED'
|
shiftErrorMessage.value = 'timesheet.errors.SHIFT_TIME_REQUIRED'
|
||||||
} else {
|
} else {
|
||||||
shift.value.has_error = false;
|
shift.value.has_error = false;
|
||||||
error_message.value = undefined;
|
shiftErrorMessage.value = undefined;
|
||||||
emit('onTimeFieldBlur');
|
emit('onTimeFieldBlur');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onBlurShiftTypeSelect = () => {
|
const onBlurShiftTypeSelect = () => {
|
||||||
if (shift_type_selected.value === undefined) {
|
if (shiftTypeSelected.value === undefined) {
|
||||||
shift.value.type = 'REGULAR';
|
shift.value.type = 'REGULAR';
|
||||||
shift.value.id = 0;
|
shift.value.id = 0;
|
||||||
emit('requestDelete');
|
emit('requestDelete');
|
||||||
|
|
@ -81,18 +105,22 @@
|
||||||
return 'negative';
|
return 'negative';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleIsShowingDeleteConfirm = (state: boolean) => {
|
||||||
|
is_showing_delete_confirm.value = state;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (ui_store.focus_next_component) {
|
if (ui_store.focus_next_component) {
|
||||||
select_ref.value?.focus();
|
selectRef.value?.focus();
|
||||||
select_ref.value?.showPopup();
|
selectRef.value?.showPopup();
|
||||||
shift_type_selected.value = undefined;
|
shiftTypeSelected.value = undefined;
|
||||||
ui_store.focus_next_component = false;
|
ui_store.focus_next_component = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="ui_store.is_mobile_mode ? 'column' : 'row'">
|
<div class="row">
|
||||||
<!-- delete shift confirmation dialog -->
|
<!-- delete shift confirmation dialog -->
|
||||||
<q-dialog
|
<q-dialog
|
||||||
v-model="is_showing_delete_confirm"
|
v-model="is_showing_delete_confirm"
|
||||||
|
|
@ -110,7 +138,7 @@
|
||||||
color="negative"
|
color="negative"
|
||||||
:label="$t('shared.misc.no')"
|
:label="$t('shared.misc.no')"
|
||||||
class="col"
|
class="col"
|
||||||
@click="is_showing_delete_confirm = false"
|
@click="toggleIsShowingDeleteConfirm(false)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-btn
|
<q-btn
|
||||||
|
|
@ -130,23 +158,9 @@
|
||||||
>
|
>
|
||||||
<!-- shift type -->
|
<!-- shift type -->
|
||||||
<q-select
|
<q-select
|
||||||
ref="select_ref"
|
ref="selectRef"
|
||||||
v-model="shift_type_selected"
|
v-model="shiftTypeSelected"
|
||||||
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
v-bind="shiftTypeSelectProps"
|
||||||
dense
|
|
||||||
:borderless="(shift.is_approved && isTimesheetApproved)"
|
|
||||||
:readonly="(shift.is_approved && isTimesheetApproved)"
|
|
||||||
:options-dense="!ui_store.is_mobile_mode"
|
|
||||||
hide-dropdown-icon
|
|
||||||
:menu-offset="[0, 10]"
|
|
||||||
menu-anchor="bottom middle"
|
|
||||||
menu-self="top middle"
|
|
||||||
:options="SHIFT_OPTIONS"
|
|
||||||
class="col rounded-5 q-mx-xs bg-dark"
|
|
||||||
:class="!shift.is_approved && !isTimesheetApproved ? '' : 'inset-shadow'"
|
|
||||||
popup-content-class="text-uppercase text-weight-bold text-center rounded-5"
|
|
||||||
:style="shift.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : ''"
|
|
||||||
popup-content-style="border: 2px solid var(--q-accent)"
|
|
||||||
@blur="onBlurShiftTypeSelect"
|
@blur="onBlurShiftTypeSelect"
|
||||||
@update:model-value="option => shift.type = option.value"
|
@update:model-value="option => shift.type = option.value"
|
||||||
>
|
>
|
||||||
|
|
@ -173,6 +187,22 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #option="scope">
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-bind="scope.itemProps"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon :name="scope.opt.icon" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section class="text-left">
|
||||||
|
{{ $t(scope.label) }}
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- work-from-home toggle -->
|
||||||
<template #after>
|
<template #after>
|
||||||
<q-icon
|
<q-icon
|
||||||
v-if="shift.is_approved"
|
v-if="shift.is_approved"
|
||||||
|
|
@ -214,21 +244,6 @@
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-toggle>
|
</q-toggle>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #option="scope">
|
|
||||||
<q-item
|
|
||||||
clickable
|
|
||||||
v-bind="scope.itemProps"
|
|
||||||
>
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-icon :name="scope.opt.icon" />
|
|
||||||
</q-item-section>
|
|
||||||
|
|
||||||
<q-item-section class="text-left">
|
|
||||||
{{ $t(scope.label) }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
</q-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -237,7 +252,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
ref="start_time"
|
ref="start_time"
|
||||||
v-model="shift.start_time"
|
v-model="shift.start_time"
|
||||||
v-bind="time_input_props"
|
v-bind="timeInputProps"
|
||||||
type="time"
|
type="time"
|
||||||
@blur="onTimeFieldBlur(shift.start_time)"
|
@blur="onTimeFieldBlur(shift.start_time)"
|
||||||
>
|
>
|
||||||
|
|
@ -254,7 +269,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
ref="end_time"
|
ref="end_time"
|
||||||
v-model="shift.end_time"
|
v-model="shift.end_time"
|
||||||
v-bind="time_input_props"
|
v-bind="timeInputProps"
|
||||||
type="time"
|
type="time"
|
||||||
@blur="onTimeFieldBlur(shift.end_time)"
|
@blur="onTimeFieldBlur(shift.end_time)"
|
||||||
>
|
>
|
||||||
|
|
@ -267,12 +282,11 @@
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<!-- comment and delete buttons -->
|
|
||||||
<div
|
<div
|
||||||
class="row full-height"
|
class="row full-height"
|
||||||
:class="ui_store.is_mobile_mode ? 'col-12' : 'col-auto flex-center'"
|
:class="ui_store.is_mobile_mode ? 'col-12' : 'col-auto flex-center'"
|
||||||
>
|
>
|
||||||
<!-- desktop comment button -->
|
<!-- comment button -->
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="!ui_store.is_mobile_mode"
|
v-if="!ui_store.is_mobile_mode"
|
||||||
push
|
push
|
||||||
|
|
@ -291,6 +305,7 @@
|
||||||
class="text-blue-9 text-weight-bolder"
|
class="text-blue-9 text-weight-bolder"
|
||||||
>!!</q-badge>
|
>!!</q-badge>
|
||||||
|
|
||||||
|
<!-- popup to edit comment, with visual indicator of character limit -->
|
||||||
<q-popup-edit
|
<q-popup-edit
|
||||||
v-model="shift.comment"
|
v-model="shift.comment"
|
||||||
v-slot="scope"
|
v-slot="scope"
|
||||||
|
|
@ -339,6 +354,7 @@
|
||||||
</q-popup-edit>
|
</q-popup-edit>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
|
||||||
|
<!-- delete button -->
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="!shift.is_approved"
|
v-if="!shift.is_approved"
|
||||||
flat
|
flat
|
||||||
|
|
@ -350,14 +366,15 @@
|
||||||
class="col"
|
class="col"
|
||||||
size="1.2em"
|
size="1.2em"
|
||||||
:class="shift.is_approved ? 'invisible' : ''"
|
:class="shift.is_approved ? 'invisible' : ''"
|
||||||
@click="is_showing_delete_confirm = true"
|
@click="toggleIsShowingDeleteConfirm(true)"
|
||||||
>
|
/>
|
||||||
</q-btn>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- styling the error message component to ressemble a red tab that
|
||||||
|
drops down, rather than the standard floating red text only -->
|
||||||
<style scoped>
|
<style scoped>
|
||||||
:deep(.q-field--error) {
|
:deep(.q-field--error) {
|
||||||
background-color: var(--q-negative) !important;
|
background-color: var(--q-negative) !important;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
import ShiftListDayRow from 'src/modules/timesheets/components/shift-list-day-row.vue';
|
import ShiftListDayRow from 'src/modules/timesheets/components/shift-list-day-row.vue';
|
||||||
import ShiftListDayRowMobile from 'src/modules/timesheets/components/mobile/shift-list-day-row-mobile.vue';
|
import ShiftListDayRowMobile from 'src/modules/timesheets/components/mobile/shift-list-day-row-mobile.vue';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { inject, ref } from 'vue';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api';
|
import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api';
|
||||||
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
||||||
|
|
@ -13,27 +13,30 @@
|
||||||
import type { TimesheetDay } from 'src/modules/timesheets/models/timesheet.models';
|
import type { TimesheetDay } from 'src/modules/timesheets/models/timesheet.models';
|
||||||
import { isShiftOverlap } from 'src/modules/timesheets/utils/shift.util';
|
import { isShiftOverlap } from 'src/modules/timesheets/utils/shift.util';
|
||||||
|
|
||||||
const shift_api = useShiftApi();
|
// ================== State ==================
|
||||||
const timesheet_api = useTimesheetApi();
|
|
||||||
const timesheet_store = useTimesheetStore();
|
|
||||||
const shift_error_message = ref<string | undefined>();
|
|
||||||
|
|
||||||
const { day, dense = false, approved = false, holiday = false, employeeEmail } = defineProps<{
|
const { day, dense = false, approved = false, holiday = false } = defineProps<{
|
||||||
timesheetId: number;
|
timesheetId: number;
|
||||||
weekDayIndex: number;
|
weekDayIndex: number;
|
||||||
day: TimesheetDay;
|
day: TimesheetDay;
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
approved?: boolean;
|
approved?: boolean;
|
||||||
holiday?: boolean;
|
holiday?: boolean;
|
||||||
employeeEmail?: string;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const preset_mouseover = ref(false);
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'deleteUnsavedShift': [void];
|
'deleteUnsavedShift': [void];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const shift_api = useShiftApi();
|
||||||
|
const timesheet_api = useTimesheetApi();
|
||||||
|
const timesheet_store = useTimesheetStore();
|
||||||
|
const preset_mouseover = ref(false);
|
||||||
|
const shift_error_message = ref<string | undefined>();
|
||||||
|
const employeeEmail = inject<string>('employeeEmail');
|
||||||
|
|
||||||
|
// ================== Methods ==================
|
||||||
|
|
||||||
const deleteCurrentShift = async (shift: Shift) => {
|
const deleteCurrentShift = async (shift: Shift) => {
|
||||||
if (shift.id <= 0) {
|
if (shift.id <= 0) {
|
||||||
shift.id = 0;
|
shift.id = 0;
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,27 @@
|
||||||
import ShiftListWeeklyOverview from 'src/modules/timesheets/components/shift-list-weekly-overview.vue';
|
import ShiftListWeeklyOverview from 'src/modules/timesheets/components/shift-list-weekly-overview.vue';
|
||||||
import ShiftListWeeklyOverviewMobile from 'src/modules/timesheets/components/mobile/shift-list-weekly-overview-mobile.vue';
|
import ShiftListWeeklyOverviewMobile from 'src/modules/timesheets/components/mobile/shift-list-weekly-overview-mobile.vue';
|
||||||
|
|
||||||
import { computed, onMounted } from 'vue';
|
import { date } from 'quasar';
|
||||||
|
import { computed, onMounted, provide } from 'vue';
|
||||||
import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api';
|
import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api';
|
||||||
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
||||||
import { useExpensesStore } from 'src/stores/expense-store';
|
import { useExpensesStore } from 'src/stores/expense-store';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { date } from 'quasar';
|
|
||||||
|
|
||||||
|
// ================= state ====================
|
||||||
|
|
||||||
|
const { mode = 'normal', employeeEmail } = defineProps<{
|
||||||
|
mode?: 'approval' | 'normal';
|
||||||
|
employeeEmail?: string | undefined;
|
||||||
|
}>();
|
||||||
|
|
||||||
const expenses_store = useExpensesStore();
|
const expenses_store = useExpensesStore();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
const timesheet_api = useTimesheetApi();
|
const timesheet_api = useTimesheetApi();
|
||||||
const shift_api = useShiftApi();
|
const shift_api = useShiftApi();
|
||||||
|
|
||||||
|
// ================== computed ====================
|
||||||
|
|
||||||
const has_shift_errors = computed(() => timesheet_store.all_current_shifts.filter(shift => shift.has_error === true).length > 0);
|
const has_shift_errors = computed(() => timesheet_store.all_current_shifts.filter(shift => shift.has_error === true).length > 0);
|
||||||
|
|
||||||
const is_timesheets_approved = computed(() => timesheet_store.timesheets.every(timesheet => timesheet.is_approved))
|
const is_timesheets_approved = computed(() => timesheet_store.timesheets.every(timesheet => timesheet.is_approved))
|
||||||
|
|
@ -44,9 +53,9 @@
|
||||||
0) //initial value
|
0) //initial value
|
||||||
);
|
);
|
||||||
|
|
||||||
const { mode = 'normal' } = defineProps<{
|
// =================== methods ==========================
|
||||||
mode?: 'approval' | 'normal';
|
|
||||||
}>();
|
provide('employeeEmail', employeeEmail);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (mode === 'normal')
|
if (mode === 'normal')
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ export const useExpensesApi = () => {
|
||||||
const expenses_store = useExpensesStore();
|
const expenses_store = useExpensesStore();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
||||||
const upsertExpense = async (expense: Expense): Promise<void> => {
|
const upsertExpense = async (expense: Expense, employee_email?: string): Promise<void> => {
|
||||||
const success = await expenses_store.upsertExpense(expense);
|
const success = await expenses_store.upsertExpense(expense, employee_email);
|
||||||
if (success) {
|
if (success) {
|
||||||
expenses_store.current_expense = new Expense(date.formatDate( new Date(), 'YYYY-MM-DD'));
|
expenses_store.current_expense = new Expense(date.formatDate( new Date(), 'YYYY-MM-DD'));
|
||||||
timesheet_store.getTimesheetsByOptionalEmployeeEmail();
|
timesheet_store.getTimesheetsByOptionalEmployeeEmail();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user