fix(approvals): fix sizing issue, multiple ui bugs in details window, separate scrollable timesheet from static one
This commit is contained in:
parent
07b52c854f
commit
ec466bf6f2
102
src/modules/timesheets/components/shift-list-scrollable.vue
Normal file
102
src/modules/timesheets/components/shift-list-scrollable.vue
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
<script
|
||||||
|
setup
|
||||||
|
lang="ts"
|
||||||
|
>
|
||||||
|
import ShiftList from 'src/modules/timesheets/components/shift-list.vue';
|
||||||
|
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
|
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
||||||
|
import type { QScrollArea, TouchSwipeValue } from 'quasar';
|
||||||
|
|
||||||
|
const q = useQuasar();
|
||||||
|
|
||||||
|
const timesheet_store = useTimesheetStore();
|
||||||
|
const timesheet_api = useTimesheetApi();
|
||||||
|
|
||||||
|
const { mode = 'normal' } = defineProps<{
|
||||||
|
mode: 'normal' | 'approval';
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const mobile_animation_direction = ref('fadeInLeft');
|
||||||
|
|
||||||
|
const timesheet_page = ref<QScrollArea | null>(null);
|
||||||
|
const currentDayComponent = ref<HTMLElement[] | null>(null);
|
||||||
|
const currentDayComponentWatcher = ref(currentDayComponent);
|
||||||
|
|
||||||
|
const scroll_y = computed(() => timesheet_page.value?.getScrollPosition().top ?? 0);
|
||||||
|
|
||||||
|
const handleSwipe: TouchSwipeValue = (details) => {
|
||||||
|
mobile_animation_direction.value = details.direction === 'left' ? 'fadeInRight' : 'fadeInLeft';
|
||||||
|
if (details.distance && details.distance.x && Math.abs(details.distance.x) > 10) {
|
||||||
|
timesheet_api.getTimesheetsBySwiping(details.direction === 'left' ? 1 : -1).catch(error => console.error(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(currentDayComponentWatcher, () => {
|
||||||
|
if (currentDayComponent.value && timesheet_page.value && q.platform.is.mobile) {
|
||||||
|
timesheet_page.value.setScrollPosition('vertical', currentDayComponent.value[0]!.offsetTop, 800);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="column fit relative-position"
|
||||||
|
:style="$q.platform.is.mobile && $q.screen.width < $q.screen.height ? 'margin-bottom: 40px' : ''"
|
||||||
|
v-touch-swipe="handleSwipe"
|
||||||
|
>
|
||||||
|
<q-scroll-area
|
||||||
|
ref="timesheet_page"
|
||||||
|
:horizontal-offset="[0, 3]"
|
||||||
|
class="col absolute-full hide-scrollbar"
|
||||||
|
:thumb-style="{ opacity: '0' }"
|
||||||
|
:bar-style="{ opacity: '0' }"
|
||||||
|
>
|
||||||
|
<!-- Show if no timesheets found (further than one month from present) -->
|
||||||
|
<div
|
||||||
|
v-if="timesheet_store.timesheets.length < 1 && !timesheet_store.is_loading"
|
||||||
|
class="col-auto column flex-center fit q-py-lg"
|
||||||
|
style="min-height: 20vh;"
|
||||||
|
>
|
||||||
|
<span class="text-uppercase text-weight-bolder text-center">{{ $t('shared.error.no_data_found')
|
||||||
|
}}</span>
|
||||||
|
<q-icon
|
||||||
|
name="las la-calendar"
|
||||||
|
color="accent"
|
||||||
|
size="10em"
|
||||||
|
class="absolute"
|
||||||
|
style="opacity: 0.2;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Else show timesheets if found -->
|
||||||
|
<ShiftList />
|
||||||
|
</q-scroll-area>
|
||||||
|
|
||||||
|
<q-page-sticky
|
||||||
|
v-if="mode === 'normal'"
|
||||||
|
position="bottom-right"
|
||||||
|
:offset="$q.screen.width > $q.screen.height ? [15, 15] : [15, 65]"
|
||||||
|
class="z-top"
|
||||||
|
>
|
||||||
|
<transition
|
||||||
|
appear
|
||||||
|
enter-active-class="animated zoomIn"
|
||||||
|
leave-active-class="animated zoomOut"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
v-if="scroll_y > 400"
|
||||||
|
fab
|
||||||
|
icon="las la-chevron-up"
|
||||||
|
color="white"
|
||||||
|
text-color="accent"
|
||||||
|
class="shadow-12"
|
||||||
|
@click="timesheet_page!.setScrollPosition('vertical', 0, 300)"
|
||||||
|
/>
|
||||||
|
</transition>
|
||||||
|
</q-page-sticky>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -1,323 +1,233 @@
|
||||||
<script
|
<script
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
import ShiftListDay from 'src/modules/timesheets/components/shift-list-day.vue';
|
import ShiftListDay from 'src/modules/timesheets/components/shift-list-day.vue';
|
||||||
import ShiftListDateWidget from 'src/modules/timesheets/components/shift-list-date-widget.vue';
|
import ShiftListDateWidget from 'src/modules/timesheets/components/shift-list-date-widget.vue';
|
||||||
|
|
||||||
import { date, useQuasar } from 'quasar';
|
import { ref, computed } from 'vue';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { useUiStore } from 'src/stores/ui-store';
|
||||||
import { useUiStore } from 'src/stores/ui-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { Shift } from 'src/modules/timesheets/models/shift.models';
|
||||||
import { Shift } from 'src/modules/timesheets/models/shift.models';
|
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
||||||
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
import type { TimesheetDay } from 'src/modules/timesheets/models/timesheet.models';
|
||||||
import type { QScrollArea, TouchSwipeValue } from 'quasar';
|
|
||||||
import type { TimesheetDay } from 'src/modules/timesheets/models/timesheet.models';
|
import { date } from 'quasar';
|
||||||
|
|
||||||
const CURRENT_DATE_STRING = new Date().toISOString().slice(0, 10);
|
const CURRENT_DATE_STRING = new Date().toISOString().slice(0, 10);
|
||||||
|
|
||||||
const { extractDate } = date;
|
const { extractDate } = date;
|
||||||
const q = useQuasar();
|
|
||||||
|
const ui_store = useUiStore();
|
||||||
const ui_store = useUiStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_api = useTimesheetApi();
|
||||||
const timesheet_api = useTimesheetApi();
|
|
||||||
|
const mobile_animation_direction = ref('fadeInLeft');
|
||||||
const { mode = 'normal' } = defineProps<{
|
|
||||||
mode: 'normal' | 'approval';
|
const animation_style = computed(() => ui_store.is_mobile_mode ? mobile_animation_direction.value : 'fadeInDown');
|
||||||
}>();
|
|
||||||
|
const addNewShift = (day_shifts: Shift[], date: string, timesheet_id: number) => {
|
||||||
const mobile_animation_direction = ref('fadeInLeft');
|
ui_store.focus_next_component = true;
|
||||||
const animation_style = computed(() => ui_store.is_mobile_mode ? mobile_animation_direction.value : 'fadeInDown');
|
const new_shift = new Shift;
|
||||||
|
new_shift.date = date;
|
||||||
const timesheet_page = ref<QScrollArea | null>(null);
|
new_shift.timesheet_id = timesheet_id;
|
||||||
const currentDayComponent = ref<HTMLElement[] | null>(null);
|
day_shifts.push(new_shift);
|
||||||
const currentDayComponentWatcher = ref(currentDayComponent);
|
};
|
||||||
|
|
||||||
const scroll_y = computed(() => timesheet_page.value?.getScrollPosition().top ?? 0);
|
const deleteUnsavedShift = (timesheet_index: number, day_index: number) => {
|
||||||
const timesheet_container = ref<HTMLElement | null>(null);
|
if (timesheet_store.timesheets !== undefined) {
|
||||||
const scroll_area_height = ref(0);
|
const day = timesheet_store.timesheets[timesheet_index]!.days[day_index]!;
|
||||||
|
const shifts_without_deleted_shift = day.shifts.filter(shift => shift.id !== 0);
|
||||||
const addNewShift = (day_shifts: Shift[], date: string, timesheet_id: number) => {
|
day.shifts = shifts_without_deleted_shift;
|
||||||
ui_store.focus_next_component = true;
|
}
|
||||||
const new_shift = new Shift;
|
};
|
||||||
new_shift.date = date;
|
|
||||||
new_shift.timesheet_id = timesheet_id;
|
const getDayApproval = (day: TimesheetDay) => {
|
||||||
day_shifts.push(new_shift);
|
if (day.shifts.length < 1) return false;
|
||||||
};
|
return day.shifts.every(shift => shift.is_approved === true);
|
||||||
|
};
|
||||||
const deleteUnsavedShift = (timesheet_index: number, day_index: number) => {
|
|
||||||
if (timesheet_store.timesheets !== undefined) {
|
const getMobileDayRef = (iso_date_string: string): string => {
|
||||||
const day = timesheet_store.timesheets[timesheet_index]!.days[day_index]!;
|
return iso_date_string === CURRENT_DATE_STRING ? 'currentDayComponent' : '';
|
||||||
const shifts_without_deleted_shift = day.shifts.filter(shift => shift.id !== 0);
|
};
|
||||||
day.shifts = shifts_without_deleted_shift;
|
</script>
|
||||||
}
|
|
||||||
};
|
<template>
|
||||||
|
<div
|
||||||
const getDayApproval = (day: TimesheetDay) => {
|
class="fit"
|
||||||
if (day.shifts.length < 1) return false;
|
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||||
return day.shifts.every(shift => shift.is_approved === true);
|
>
|
||||||
};
|
<div
|
||||||
|
v-for="timesheet, timesheet_index of timesheet_store.timesheets"
|
||||||
const handleSwipe: TouchSwipeValue = (details) => {
|
:key="timesheet.timesheet_id"
|
||||||
mobile_animation_direction.value = details.direction === 'left' ? 'fadeInRight' : 'fadeInLeft';
|
class="col column fit items-center"
|
||||||
if (details.distance && details.distance.x && Math.abs(details.distance.x) > 10) {
|
>
|
||||||
timesheet_api.getTimesheetsBySwiping(details.direction === 'left' ? 1 : -1).catch(error => console.error(error));
|
<transition
|
||||||
}
|
appear
|
||||||
};
|
enter-active-class="animated fadeInDown"
|
||||||
|
leave-active-class="animated fadeOutUp"
|
||||||
const getMobileDayRef = (iso_date_string: string): string => {
|
>
|
||||||
return iso_date_string === CURRENT_DATE_STRING ? 'currentDayComponent' : '';
|
<q-btn
|
||||||
};
|
v-if="!$q.platform.is.mobile && timesheet.days.every(day => day.shifts.length < 1) && timesheet_store.has_timesheet_preset"
|
||||||
|
:disable="!timesheet.days.every(day => day.shifts.length < 1)"
|
||||||
watch(currentDayComponentWatcher, () => {
|
flat
|
||||||
if (currentDayComponent.value && timesheet_page.value && q.platform.is.mobile) {
|
dense
|
||||||
console.log('setting scroll position to offsetTop of currentDayComponent: ', currentDayComponent.value[0]!.offsetTop);
|
:label="$t('timesheet.apply_preset_week')"
|
||||||
timesheet_page.value.setScrollPosition('vertical', currentDayComponent.value[0]!.offsetTop, 800);
|
class="col-auto text-uppercase text-weight-bold text-accent q-mx-lg q-py-none rounded-5"
|
||||||
return;
|
@click="timesheet_api.applyPreset(timesheet.timesheet_id)"
|
||||||
}
|
>
|
||||||
|
<q-icon
|
||||||
if (timesheet_container.value !== null && mode === 'approval') {
|
name="las la-calendar-week"
|
||||||
scroll_area_height.value = timesheet_container.value.offsetHeight
|
color="accent"
|
||||||
}
|
size="md"
|
||||||
})
|
/>
|
||||||
</script>
|
</q-btn>
|
||||||
|
</transition>
|
||||||
<template>
|
|
||||||
<div
|
<transition-group
|
||||||
class="column fit relative-position"
|
appear
|
||||||
:style="$q.platform.is.mobile && $q.screen.width < $q.screen.height ? 'margin-bottom: 40px' : ''"
|
:enter-active-class="`animated ${animation_style}`"
|
||||||
v-touch-swipe="handleSwipe"
|
>
|
||||||
>
|
<div
|
||||||
<q-scroll-area
|
v-for="day, day_index in timesheet.days"
|
||||||
ref="timesheet_page"
|
:key="day.date"
|
||||||
:horizontal-offset="[0, 3]"
|
:ref="getMobileDayRef(day.date)"
|
||||||
class="col absolute-full hide-scrollbar"
|
class="col-auto row q-pa-sm full-width"
|
||||||
:style="mode === 'approval' ? `height: ${scroll_area_height}px;` : ''"
|
:style="`animation-delay: ${day_index / 15}s;`"
|
||||||
:thumb-style="{ opacity: '0' }"
|
>
|
||||||
:bar-style="{ opacity: '0' }"
|
<!-- mobile version in portrait mode -->
|
||||||
>
|
<div
|
||||||
<!-- Show if no timesheets found (further than one month from present) -->
|
v-if="$q.platform.is.mobile && ($q.screen.width < $q.screen.height)"
|
||||||
<div
|
class="col column full-width q-px-md q-py-sm"
|
||||||
v-if="timesheet_store.timesheets.length < 1 && !timesheet_store.is_loading"
|
>
|
||||||
class="col-auto column flex-center fit q-py-lg"
|
<q-card
|
||||||
style="min-height: 20vh;"
|
class="shadow-12"
|
||||||
>
|
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent rounded-10' : 'bg-dark mobile-rounded-10'"
|
||||||
<span class="text-uppercase text-weight-bolder text-center">{{ $t('shared.error.no_data_found')
|
>
|
||||||
}}</span>
|
|
||||||
<q-icon
|
<q-card-section
|
||||||
name="las la-calendar"
|
class="text-weight-bolder text-uppercase text-h6 q-py-sm text-center relative-position"
|
||||||
color="accent"
|
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent text-white' : 'bg-primary text-white'"
|
||||||
size="10em"
|
style="line-height: 1em;"
|
||||||
class="absolute"
|
>
|
||||||
style="opacity: 0.2;"
|
<span> {{ $d(extractDate(day.date, 'YYYY-MM-DD'), {
|
||||||
/>
|
weekday: 'long', day: 'numeric', month:
|
||||||
</div>
|
'long'
|
||||||
|
}) }}</span>
|
||||||
<!-- Else show timesheets if found -->
|
|
||||||
<div
|
<q-icon
|
||||||
v-else
|
v-if="(getDayApproval(day) || timesheet.is_approved)"
|
||||||
ref="timesheet_container"
|
name="verified"
|
||||||
class="col fit"
|
size="3em"
|
||||||
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
color="white"
|
||||||
>
|
class="absolute-top-left z-top"
|
||||||
<div
|
style="top: -0.2em; left: 0px;"
|
||||||
v-for="timesheet, timesheet_index of timesheet_store.timesheets"
|
/>
|
||||||
:key="timesheet.timesheet_id"
|
</q-card-section>
|
||||||
class="col column fit flex-center"
|
|
||||||
>
|
<q-card-section
|
||||||
<transition
|
v-if="day.shifts.filter(shift => shift.id !== 0).length > 0"
|
||||||
appear
|
class="q-pa-none transparent"
|
||||||
enter-active-class="animated fadeInDown"
|
>
|
||||||
leave-active-class="animated fadeOutUp"
|
<ShiftListDay
|
||||||
>
|
outlined
|
||||||
<q-btn
|
:timesheet-id="timesheet.timesheet_id"
|
||||||
v-if="!$q.platform.is.mobile && timesheet.days.every(day => day.shifts.length < 1) && timesheet_store.has_timesheet_preset"
|
:week-day-index="day_index"
|
||||||
:disable="!timesheet.days.every(day => day.shifts.length < 1)"
|
:animation-delay-multiplier="day_index"
|
||||||
flat
|
:approved="(getDayApproval(day) || timesheet.is_approved)"
|
||||||
dense
|
:day="day"
|
||||||
:label="$t('timesheet.apply_preset_week')"
|
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
||||||
class="col-auto text-uppercase text-weight-bold text-accent q-mx-lg q-py-none rounded-5"
|
/>
|
||||||
@click="timesheet_api.applyPreset(timesheet.timesheet_id)"
|
</q-card-section>
|
||||||
>
|
|
||||||
<q-icon
|
<q-card-section class="q-pa-none">
|
||||||
name="las la-calendar-week"
|
<q-btn
|
||||||
color="accent"
|
v-if="!(getDayApproval(day) || timesheet.is_approved)"
|
||||||
size="md"
|
square
|
||||||
/>
|
dense
|
||||||
</q-btn>
|
size="xl"
|
||||||
</transition>
|
color="accent"
|
||||||
|
icon="more_time"
|
||||||
<transition-group
|
class="full-width"
|
||||||
appear
|
style="border-radius: 0 0 10px 10px;"
|
||||||
:enter-active-class="`animated ${animation_style}`"
|
@click="addNewShift(day.shifts, day.date, timesheet.timesheet_id)"
|
||||||
>
|
/>
|
||||||
<div
|
</q-card-section>
|
||||||
v-for="day, day_index in timesheet.days"
|
</q-card>
|
||||||
:key="day.date"
|
</div>
|
||||||
:ref="getMobileDayRef(day.date)"
|
|
||||||
class="col-auto row q-pa-sm fit"
|
<!-- desktop version -->
|
||||||
:style="`animation-delay: ${day_index / 15}s;`"
|
<div
|
||||||
>
|
v-else
|
||||||
<!-- mobile version in portrait mode -->
|
class="col row full-width rounded-10 ellipsis shadow-10"
|
||||||
<div
|
>
|
||||||
v-if="$q.platform.is.mobile && ($q.screen.width < $q.screen.height)"
|
<div
|
||||||
class="col column full-width q-px-md q-py-sm"
|
class="col row"
|
||||||
>
|
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent' : 'bg-dark'"
|
||||||
<q-card
|
>
|
||||||
class="mobile-rounded-10 shadow-12"
|
<!-- Date block -->
|
||||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent' : 'bg-dark'"
|
<ShiftListDateWidget
|
||||||
>
|
:display-date="day.date"
|
||||||
|
:approved="(getDayApproval(day) || timesheet.is_approved)"
|
||||||
<q-card-section
|
class="col-auto"
|
||||||
class="text-weight-bolder text-uppercase text-h6 q-py-sm text-center relative-position"
|
/>
|
||||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent text-white' : 'bg-primary text-white'"
|
|
||||||
style="line-height: 1em;"
|
<ShiftListDay
|
||||||
>
|
:timesheet-id="timesheet.timesheet_id"
|
||||||
<span> {{ $d(extractDate(day.date, 'YYYY-MM-DD'), {
|
:week-day-index="day_index"
|
||||||
weekday: 'long', day: 'numeric', month:
|
:day="day"
|
||||||
'long'
|
:approved="getDayApproval(day) || timesheet.is_approved"
|
||||||
}) }}</span>
|
class="col"
|
||||||
|
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
||||||
<q-icon
|
/>
|
||||||
v-if="(getDayApproval(day) || timesheet.is_approved)"
|
</div>
|
||||||
name="verified"
|
|
||||||
size="3em"
|
|
||||||
color="white"
|
<div class="col-auto self-stretch">
|
||||||
class="absolute-top-left z-top"
|
<q-icon
|
||||||
style="top: -0.2em; left: 0px;"
|
v-if="(getDayApproval(day) || timesheet.is_approved)"
|
||||||
/>
|
name="verified"
|
||||||
</q-card-section>
|
color="white"
|
||||||
|
size="xl"
|
||||||
<q-card-section
|
class="full-height"
|
||||||
v-if="day.shifts.filter(shift => shift.id !== 0).length > 0"
|
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent' : ''"
|
||||||
class="q-pa-none transparent"
|
/>
|
||||||
>
|
|
||||||
<ShiftListDay
|
<q-btn
|
||||||
outlined
|
v-else
|
||||||
:timesheet-id="timesheet.timesheet_id"
|
:dense="!$q.platform.is.mobile"
|
||||||
:week-day-index="day_index"
|
square
|
||||||
:animation-delay-multiplier="day_index"
|
icon="more_time"
|
||||||
:approved="(getDayApproval(day) || timesheet.is_approved)"
|
size="lg"
|
||||||
:day="day"
|
color="accent"
|
||||||
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
text-color="white"
|
||||||
/>
|
class="full-height"
|
||||||
</q-card-section>
|
:class="$q.platform.is.mobile ? 'q-px-xs' : ''"
|
||||||
|
@click="addNewShift(day.shifts, day.date, timesheet.timesheet_id)"
|
||||||
<q-card-section class="q-pa-none">
|
/>
|
||||||
<q-btn
|
</div>
|
||||||
v-if="!(getDayApproval(day) || timesheet.is_approved)"
|
</div>
|
||||||
square
|
</div>
|
||||||
dense
|
</transition-group>
|
||||||
size="xl"
|
</div>
|
||||||
color="accent"
|
</div>
|
||||||
icon="more_time"
|
</template>
|
||||||
class="full-width"
|
|
||||||
style="border-radius: 0 0 10px 10px;"
|
<style
|
||||||
@click="addNewShift(day.shifts, day.date, timesheet.timesheet_id)"
|
scoped
|
||||||
/>
|
lang="scss"
|
||||||
</q-card-section>
|
>
|
||||||
</q-card>
|
@each $size in (1, 2, 3, 4, 5, 10, 15, 20, 25, 50, 75, 100, 200) {
|
||||||
</div>
|
.mobile-rounded-#{$size} {
|
||||||
|
border-radius: #{$size}px !important;
|
||||||
<!-- desktop version -->
|
}
|
||||||
<div
|
|
||||||
v-else
|
.mobile-rounded-#{$size}>div:first-child {
|
||||||
class="col row full-width rounded-10 ellipsis shadow-10"
|
border-radius: #{$size}px #{$size}px 0 0 !important;
|
||||||
>
|
}
|
||||||
<div
|
|
||||||
class="col row"
|
.mobile-rounded-#{$size}>div:last-child {
|
||||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent' : 'bg-dark'"
|
border-radius: 0 0 #{$size}px #{$size}px !important;
|
||||||
>
|
}
|
||||||
<!-- Date block -->
|
}
|
||||||
<ShiftListDateWidget
|
|
||||||
:display-date="day.date"
|
|
||||||
:approved="(getDayApproval(day) || timesheet.is_approved)"
|
|
||||||
class="col-auto"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ShiftListDay
|
|
||||||
:timesheet-id="timesheet.timesheet_id"
|
|
||||||
:week-day-index="day_index"
|
|
||||||
:day="day"
|
|
||||||
:approved="getDayApproval(day) || timesheet.is_approved"
|
|
||||||
class="col"
|
|
||||||
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="col-auto self-stretch">
|
|
||||||
<q-icon
|
|
||||||
v-if="(getDayApproval(day) || timesheet.is_approved)"
|
|
||||||
name="verified"
|
|
||||||
color="white"
|
|
||||||
size="xl"
|
|
||||||
class="full-height"
|
|
||||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent' : ''"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<q-btn
|
|
||||||
v-else
|
|
||||||
:dense="!$q.platform.is.mobile"
|
|
||||||
square
|
|
||||||
icon="more_time"
|
|
||||||
size="lg"
|
|
||||||
color="accent"
|
|
||||||
text-color="white"
|
|
||||||
class="full-height"
|
|
||||||
:class="$q.platform.is.mobile ? 'q-px-xs' : ''"
|
|
||||||
@click="addNewShift(day.shifts, day.date, timesheet.timesheet_id)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition-group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-scroll-area>
|
|
||||||
|
|
||||||
<q-page-sticky
|
|
||||||
v-if="mode === 'normal'"
|
|
||||||
position="bottom-right"
|
|
||||||
:offset="$q.screen.width > $q.screen.height ? [15, 15] : [15, 65]"
|
|
||||||
class="z-top"
|
|
||||||
>
|
|
||||||
<transition
|
|
||||||
appear
|
|
||||||
enter-active-class="animated zoomIn"
|
|
||||||
leave-active-class="animated zoomOut"
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
v-if="scroll_y > 400"
|
|
||||||
fab
|
|
||||||
icon="las la-chevron-up"
|
|
||||||
color="white"
|
|
||||||
text-color="accent"
|
|
||||||
class="shadow-12"
|
|
||||||
@click="timesheet_page!.setScrollPosition('vertical', 0, 300)"
|
|
||||||
/>
|
|
||||||
</transition>
|
|
||||||
</q-page-sticky>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style
|
|
||||||
scoped
|
|
||||||
lang="scss"
|
|
||||||
>
|
|
||||||
@each $size in (1, 2, 3, 4, 5, 10, 15, 20, 25, 50, 75, 100, 200) {
|
|
||||||
.mobile-rounded-#{$size} {
|
|
||||||
border-radius: #{$size}px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-rounded-#{$size}>div:first-child {
|
|
||||||
border-radius: #{$size}px #{$size}px 0 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-rounded-#{$size}>div:last-child {
|
|
||||||
border-radius: 0 0 #{$size}px #{$size}px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
>
|
>
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import ShiftList from 'src/modules/timesheets/components/shift-list.vue';
|
import ShiftList from 'src/modules/timesheets/components/shift-list.vue';
|
||||||
|
import ShiftListScrollable from 'src/modules/timesheets/components/shift-list-scrollable.vue';
|
||||||
import LoadingOverlay from 'src/modules/shared/components/loading-overlay.vue';
|
import LoadingOverlay from 'src/modules/shared/components/loading-overlay.vue';
|
||||||
import ExpenseDialog from 'src/modules/timesheets/components/expense-dialog.vue';
|
import ExpenseDialog from 'src/modules/timesheets/components/expense-dialog.vue';
|
||||||
import PageHeaderTemplate from 'src/modules/shared/components/page-header-template.vue';
|
import PageHeaderTemplate from 'src/modules/shared/components/page-header-template.vue';
|
||||||
|
|
@ -28,6 +29,21 @@
|
||||||
|
|
||||||
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))
|
||||||
|
|
||||||
|
const total_hours = computed(() => timesheet_store.timesheets.reduce((sum, timesheet) =>
|
||||||
|
sum + timesheet.weekly_hours.regular
|
||||||
|
+ timesheet.weekly_hours.evening
|
||||||
|
+ timesheet.weekly_hours.emergency
|
||||||
|
+ timesheet.weekly_hours.overtime,
|
||||||
|
0) //initial value
|
||||||
|
);
|
||||||
|
|
||||||
|
const total_expenses = computed(() => timesheet_store.timesheets.reduce((sum, timesheet) =>
|
||||||
|
sum + timesheet.weekly_expenses.expenses
|
||||||
|
+ timesheet.weekly_expenses.on_call
|
||||||
|
+ timesheet.weekly_expenses.per_diem,
|
||||||
|
0) //initial value
|
||||||
|
);
|
||||||
|
|
||||||
const { mode = 'normal' } = defineProps<{
|
const { mode = 'normal' } = defineProps<{
|
||||||
mode?: 'approval' | 'normal';
|
mode?: 'approval' | 'normal';
|
||||||
}>();
|
}>();
|
||||||
|
|
@ -46,7 +62,7 @@
|
||||||
<span
|
<span
|
||||||
v-if="mode === 'approval'"
|
v-if="mode === 'approval'"
|
||||||
class="col-auto text-uppercase text-bold text-h5"
|
class="col-auto text-uppercase text-bold text-h5"
|
||||||
>
|
>
|
||||||
{{ $t('timesheet.page_header') }}
|
{{ $t('timesheet.page_header') }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
@ -54,8 +70,15 @@
|
||||||
<!-- weekly overview -->
|
<!-- weekly overview -->
|
||||||
<div class="col-auto row q-px-lg full-width">
|
<div class="col-auto row q-px-lg full-width">
|
||||||
<!-- supervisor weekly overview -->
|
<!-- supervisor weekly overview -->
|
||||||
<div class="col-xs-6 col-md-4 col-xl-3 q-pa-md">
|
<div
|
||||||
<ShiftListWeeklyOverview mode="total-hours" />
|
v-if="!$q.platform.is.mobile"
|
||||||
|
class="col-xs-6 col-md-4 col-xl-3 q-pa-md"
|
||||||
|
>
|
||||||
|
<ShiftListWeeklyOverview
|
||||||
|
mode="total-hours"
|
||||||
|
:total-hours="total_hours"
|
||||||
|
:total-expenses="total_expenses"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PageHeaderTemplate
|
<PageHeaderTemplate
|
||||||
|
|
@ -66,10 +89,13 @@
|
||||||
class="col"
|
class="col"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-space v-else />
|
<q-space v-if="!$q.platform.is.mobile && mode === 'approval'" />
|
||||||
|
|
||||||
<!-- employee weekly overview -->
|
<!-- employee weekly overview -->
|
||||||
<div class="col-xs-6 col-md-4 col-xl-3 q-pa-md">
|
<div
|
||||||
|
v-if="!$q.platform.is.mobile"
|
||||||
|
class="col-xs-6 col-md-4 col-xl-3 q-pa-md"
|
||||||
|
>
|
||||||
<ShiftListWeeklyOverview mode="off-hours" />
|
<ShiftListWeeklyOverview mode="off-hours" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -77,7 +103,7 @@
|
||||||
<!-- top menu -->
|
<!-- top menu -->
|
||||||
<div
|
<div
|
||||||
class="col-auto row items-center full-width"
|
class="col-auto row items-center full-width"
|
||||||
:class="$q.platform.is.mobile && ($q.screen.width < $q.screen.height) ? 'justify-between' : 'q-pb-sm q-px-md'"
|
:class="$q.platform.is.mobile && ($q.screen.width < $q.screen.height) ? 'justify-between q-px-md' : 'q-pb-sm q-px-xl'"
|
||||||
>
|
>
|
||||||
<!-- navigation btn -->
|
<!-- navigation btn -->
|
||||||
<PayPeriodNavigator
|
<PayPeriodNavigator
|
||||||
|
|
@ -133,12 +159,40 @@
|
||||||
<!-- mobile weekly overview widget -->
|
<!-- mobile weekly overview widget -->
|
||||||
<ShiftListWeeklyOverviewMobile class="col-auto" />
|
<ShiftListWeeklyOverviewMobile class="col-auto" />
|
||||||
|
|
||||||
<ShiftList
|
<!-- standard scrollable shift list for user input -->
|
||||||
|
<ShiftListScrollable
|
||||||
|
v-if="mode === 'normal'"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:class="mode === 'normal' ? 'col' : 'col-auto'"
|
:class="mode === 'normal' ? 'col' : 'col-auto'"
|
||||||
:style="mode === 'normal' ? '' : 'min-height: 100vh'"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- full shift list for timesheet approval details dialog -->
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="col-auto column full-width"
|
||||||
|
:style="$q.platform.is.mobile && $q.screen.width < $q.screen.height ? 'margin-bottom: 40px' : ''"
|
||||||
|
>
|
||||||
|
<!-- Show if no timesheets found (further than one month from present) -->
|
||||||
|
<div
|
||||||
|
v-if="timesheet_store.timesheets.length < 1 && !timesheet_store.is_loading"
|
||||||
|
class="col-auto column flex-center fit q-py-lg"
|
||||||
|
style="min-height: 20vh;"
|
||||||
|
>
|
||||||
|
<span class="text-uppercase text-weight-bolder text-center">{{ $t('shared.error.no_data_found')
|
||||||
|
}}</span>
|
||||||
|
<q-icon
|
||||||
|
name="las la-calendar"
|
||||||
|
color="accent"
|
||||||
|
size="10em"
|
||||||
|
class="absolute"
|
||||||
|
style="opacity: 0.2;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Else show timesheets if found -->
|
||||||
|
<ShiftList class="col" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="$q.platform.is.mobile && $q.screen.width < $q.screen.height"
|
v-if="$q.platform.is.mobile && $q.screen.width < $q.screen.height"
|
||||||
square
|
square
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
const headerComponent = ref<HTMLElement | null>(null);
|
const headerComponent = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const table_max_height = computed(() => {
|
const table_max_height = computed(() => {
|
||||||
const height = page_height.value - (headerComponent.value?.clientHeight ?? 0);
|
const height = page_height.value - Math.min(headerComponent.value?.clientHeight ?? 0, headerComponent.value?.offsetHeight ?? 0);
|
||||||
return height;
|
return height;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user