feat(timesheet-approval): Add menu that contains shift options, approval-only feature
This commit is contained in:
parent
83dd3a4de4
commit
57946dbadd
|
|
@ -12,6 +12,7 @@
|
||||||
noTopPadding?: boolean;
|
noTopPadding?: boolean;
|
||||||
backgroundColor?: 'bg-secondary' | 'bg-dark';
|
backgroundColor?: 'bg-secondary' | 'bg-dark';
|
||||||
appendContent?: string | number;
|
appendContent?: string | number;
|
||||||
|
autoFocus?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
|
|
@ -28,6 +29,7 @@
|
||||||
v-model="model"
|
v-model="model"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
dense
|
dense
|
||||||
|
:autofocus="autoFocus"
|
||||||
borderless
|
borderless
|
||||||
color="accent"
|
color="accent"
|
||||||
label-color="white"
|
label-color="white"
|
||||||
|
|
@ -89,8 +91,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
:deep(.q-field--dense.q-field--float .q-field__label) {
|
:deep(.q-field--dense.q-field--float .q-field__label) {
|
||||||
transform: translate(-17px, -60%) scale(0.75);
|
transform: translate(-17px, -60%) scale(0.75);
|
||||||
border-radius: 10px 10px 10px 0px;
|
border-radius: 10px 10px 10px 0px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
<script
|
||||||
|
setup
|
||||||
|
lang="ts"
|
||||||
|
>
|
||||||
|
import TargoInput from 'src/modules/shared/components/targo-input.vue';
|
||||||
|
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import type { Shift } from 'src/modules/timesheets/models/shift.models';
|
||||||
|
|
||||||
|
const shift = defineModel<Shift>({ required: true });
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
'clickToggleApproval': [void];
|
||||||
|
'clickDelete': [void];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const isCommentDialogOpen = ref(false);
|
||||||
|
|
||||||
|
const approvalOptionState = computed<{ icon: string, label: string }>(() => shift.value.is_approved ?
|
||||||
|
{ icon: 'las la-unlock', label: 'shared.label.unlock' } :
|
||||||
|
{ icon: 'las la-lock', label: 'shared.label.lock' }
|
||||||
|
)
|
||||||
|
|
||||||
|
const hasComment = computed(() => shift.value.comment && shift.value.comment.length > 0)
|
||||||
|
|
||||||
|
const onClickViewComments = () => {
|
||||||
|
isCommentDialogOpen.value = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="row full-height flex-center">
|
||||||
|
<q-dialog
|
||||||
|
v-model="isCommentDialogOpen"
|
||||||
|
full-width
|
||||||
|
backdrop-filter="blur(4px)"
|
||||||
|
>
|
||||||
|
<div class="row flex-center full-width">
|
||||||
|
<div class="col-xs-12 col-sm-10 col-md-8 col-lg-6">
|
||||||
|
<TargoInput
|
||||||
|
v-model="shift.comment"
|
||||||
|
auto-focus
|
||||||
|
:label="$t('timesheet.expense.employee_comment')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="more_vert"
|
||||||
|
color="accent"
|
||||||
|
class="col-auto q-px-md"
|
||||||
|
>
|
||||||
|
<q-badge
|
||||||
|
v-if="hasComment"
|
||||||
|
rounded
|
||||||
|
floating
|
||||||
|
color="negative"
|
||||||
|
style="transform:translate(-10px, 0px)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-menu
|
||||||
|
auto-close
|
||||||
|
transition-show="jump-down"
|
||||||
|
transition-hide="jump-up"
|
||||||
|
transition-duration="200"
|
||||||
|
>
|
||||||
|
|
||||||
|
<q-list dense>
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
@click="$emit('clickToggleApproval')"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar :icon="approvalOptionState.icon" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>
|
||||||
|
{{ $t(approvalOptionState.label) }}
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
@click="onClickViewComments"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar icon="las la-power-off" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>
|
||||||
|
<div class="row items-center">
|
||||||
|
<span class="col">{{ $t('timesheet.expense.employee_comment') }}</span>
|
||||||
|
|
||||||
|
<div class="col-auto q-pl-sm">
|
||||||
|
<q-badge
|
||||||
|
v-if="hasComment"
|
||||||
|
rounded
|
||||||
|
color="negative"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
@click="$emit('clickDelete')"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar
|
||||||
|
icon="las la-trash"
|
||||||
|
text-color="negative"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>
|
||||||
|
{{ $t('shared.label.remove') }}
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -2,9 +2,11 @@
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
|
import DetailsDialogShiftMenu from 'src/modules/timesheet-approval/components/details-dialog-shift-menu.vue';
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { computed, inject, onMounted, ref } from 'vue';
|
import { computed, inject, onMounted, ref } from 'vue';
|
||||||
import { QSelect, QInput, useQuasar, type QSelectProps, QPopupProxy } from 'quasar';
|
import { QSelect, QInput, useQuasar, type QSelectProps } from 'quasar';
|
||||||
import { useUiStore } from 'src/stores/ui-store';
|
import { useUiStore } from 'src/stores/ui-store';
|
||||||
import { useAuthStore } from 'src/stores/auth-store';
|
import { useAuthStore } from 'src/stores/auth-store';
|
||||||
import { getCurrentDailyMinutesWorked, getShiftOptions, getTimeStringFromMinutes, SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
|
import { getCurrentDailyMinutesWorked, getShiftOptions, getTimeStringFromMinutes, SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
|
||||||
|
|
@ -51,7 +53,6 @@
|
||||||
const shiftErrorMessage = ref<string | undefined>();
|
const shiftErrorMessage = ref<string | undefined>();
|
||||||
const is_showing_delete_confirm = ref(false);
|
const is_showing_delete_confirm = ref(false);
|
||||||
const isShowingPredefinedTime = ref(shift.value.type === 'HOLIDAY');
|
const isShowingPredefinedTime = ref(shift.value.type === 'HOLIDAY');
|
||||||
const popupProxyRef = ref<QPopupProxy | null>(null);
|
|
||||||
const predefinedHoursString = ref('');
|
const predefinedHoursString = ref('');
|
||||||
const predefinedHoursBgColor = ref(`bg-${shiftTypeSelected.value?.icon_color ?? ''}`);
|
const predefinedHoursBgColor = ref(`bg-${shiftTypeSelected.value?.icon_color ?? ''}`);
|
||||||
|
|
||||||
|
|
@ -59,12 +60,6 @@
|
||||||
|
|
||||||
const hasPTO = computed(() => currentShifts.some(shift => SHIFT_TYPES_WITH_PREDEFINED_TIMES.includes(shift.type)));
|
const hasPTO = computed(() => currentShifts.some(shift => SHIFT_TYPES_WITH_PREDEFINED_TIMES.includes(shift.type)));
|
||||||
|
|
||||||
const rightClickMenuIcon = computed(() => shift.value.is_approved ? 'lock_open' : 'lock');
|
|
||||||
|
|
||||||
const rightClickMenuLabel = computed(() => shift.value.is_approved ?
|
|
||||||
t('timesheet_approvals.tooltip.unapprove') :
|
|
||||||
t('timesheet_approvals.tooltip.approve'));
|
|
||||||
|
|
||||||
const timeInputProps = computed(() => ({
|
const timeInputProps = computed(() => ({
|
||||||
dense: true,
|
dense: true,
|
||||||
borderless: shift.value.is_approved && isTimesheetApproved,
|
borderless: shift.value.is_approved && isTimesheetApproved,
|
||||||
|
|
@ -136,12 +131,9 @@
|
||||||
is_showing_delete_confirm.value = state;
|
is_showing_delete_confirm.value = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRightClickApprove = () => {
|
const onclickToogleApproval = () => {
|
||||||
if (authStore.user?.user_module_access.includes('timesheets_approval'))
|
if (authStore.user?.user_module_access.includes('timesheets_approval'))
|
||||||
shift.value.is_approved = !shift.value.is_approved;
|
shift.value.is_approved = !shift.value.is_approved;
|
||||||
|
|
||||||
if (popupProxyRef.value)
|
|
||||||
popupProxyRef.value.hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onShiftTypeChange = (option: ShiftOption) => {
|
const onShiftTypeChange = (option: ShiftOption) => {
|
||||||
|
|
@ -179,31 +171,6 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- right-click to approve shift only (if in approval mode) -->
|
|
||||||
<q-popup-proxy
|
|
||||||
v-if="mode === 'approval'"
|
|
||||||
ref="popupProxyRef"
|
|
||||||
context-menu
|
|
||||||
class="rounded-5 q-px-md shadow-24 cursor-pointer"
|
|
||||||
style="border: 3px solid var(--q-primary);"
|
|
||||||
>
|
|
||||||
<q-banner
|
|
||||||
dense
|
|
||||||
class="cursor-pointer q-px-lg"
|
|
||||||
@click="onRightClickApprove"
|
|
||||||
>
|
|
||||||
<template v-slot:avatar>
|
|
||||||
<q-icon
|
|
||||||
:name="rightClickMenuIcon"
|
|
||||||
color="accent"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<span class="text-weight-bold text-accent text-uppercase">
|
|
||||||
{{ rightClickMenuLabel }}
|
|
||||||
</span>
|
|
||||||
</q-banner>
|
|
||||||
</q-popup-proxy>
|
|
||||||
|
|
||||||
<!-- delete shift confirmation dialog -->
|
<!-- delete shift confirmation dialog -->
|
||||||
<q-dialog
|
<q-dialog
|
||||||
v-model="is_showing_delete_confirm"
|
v-model="is_showing_delete_confirm"
|
||||||
|
|
@ -348,7 +315,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Else show input fields for in-out timestamps -->
|
<!-- Else show input fields for in-out timestamps -->
|
||||||
<div v-else class="col row items-start text-uppercase rounded-5 q-pa-xs">
|
<div
|
||||||
|
v-else
|
||||||
|
class="col row items-start text-uppercase rounded-5 q-pa-xs"
|
||||||
|
>
|
||||||
<q-input
|
<q-input
|
||||||
ref="start_time"
|
ref="start_time"
|
||||||
v-model="shift.start_time"
|
v-model="shift.start_time"
|
||||||
|
|
@ -384,8 +354,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="row full-height"
|
v-if="mode === 'normal'"
|
||||||
:class="$q.platform.is.mobile ? 'col-12' : 'col-auto flex-center'"
|
class="row col-auto flex-center full-height"
|
||||||
>
|
>
|
||||||
<!-- comment button -->
|
<!-- comment button -->
|
||||||
<q-btn
|
<q-btn
|
||||||
|
|
@ -470,6 +440,17 @@
|
||||||
@click="toggleIsShowingDeleteConfirm(true)"
|
@click="toggleIsShowingDeleteConfirm(true)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="col-auto"
|
||||||
|
>
|
||||||
|
<DetailsDialogShiftMenu
|
||||||
|
v-model="shift"
|
||||||
|
@click-toggle-approval="onclickToogleApproval"
|
||||||
|
@click-delete="toggleIsShowingDeleteConfirm(true)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user