Merge pull request 'fix(approvals): fix sizing issue, multiple ui bugs in details window, separate scrollable timesheet from static one' (#38) from dev/nicolas/staging-prep into main
Reviewed-on: Targo/targo_frontend#38
This commit is contained in:
commit
7abbfe8208
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
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import ShiftListDay from 'src/modules/timesheets/components/shift-list-day.vue';
|
||||
import ShiftListDateWidget from 'src/modules/timesheets/components/shift-list-date-widget.vue';
|
||||
|
||||
import { date, useQuasar } from 'quasar';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { Shift } from 'src/modules/timesheets/models/shift.models';
|
||||
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
||||
import type { QScrollArea, TouchSwipeValue } from 'quasar';
|
||||
import type { TimesheetDay } from 'src/modules/timesheets/models/timesheet.models';
|
||||
|
||||
const CURRENT_DATE_STRING = new Date().toISOString().slice(0, 10);
|
||||
|
||||
const { extractDate } = date;
|
||||
const q = useQuasar();
|
||||
|
||||
const ui_store = useUiStore();
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const timesheet_api = useTimesheetApi();
|
||||
|
||||
const { mode = 'normal' } = defineProps<{
|
||||
mode: 'normal' | 'approval';
|
||||
}>();
|
||||
|
||||
const mobile_animation_direction = ref('fadeInLeft');
|
||||
const animation_style = computed(() => ui_store.is_mobile_mode ? mobile_animation_direction.value : 'fadeInDown');
|
||||
|
||||
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 timesheet_container = ref<HTMLElement | null>(null);
|
||||
const scroll_area_height = ref(0);
|
||||
|
||||
const addNewShift = (day_shifts: Shift[], date: string, timesheet_id: number) => {
|
||||
ui_store.focus_next_component = true;
|
||||
const new_shift = new Shift;
|
||||
new_shift.date = date;
|
||||
new_shift.timesheet_id = timesheet_id;
|
||||
day_shifts.push(new_shift);
|
||||
};
|
||||
|
||||
const deleteUnsavedShift = (timesheet_index: number, day_index: number) => {
|
||||
if (timesheet_store.timesheets !== undefined) {
|
||||
const day = timesheet_store.timesheets[timesheet_index]!.days[day_index]!;
|
||||
const shifts_without_deleted_shift = day.shifts.filter(shift => shift.id !== 0);
|
||||
day.shifts = shifts_without_deleted_shift;
|
||||
}
|
||||
};
|
||||
|
||||
const getDayApproval = (day: TimesheetDay) => {
|
||||
if (day.shifts.length < 1) return false;
|
||||
return day.shifts.every(shift => shift.is_approved === true);
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
const getMobileDayRef = (iso_date_string: string): string => {
|
||||
return iso_date_string === CURRENT_DATE_STRING ? 'currentDayComponent' : '';
|
||||
};
|
||||
|
||||
watch(currentDayComponentWatcher, () => {
|
||||
if (currentDayComponent.value && timesheet_page.value && q.platform.is.mobile) {
|
||||
console.log('setting scroll position to offsetTop of currentDayComponent: ', currentDayComponent.value[0]!.offsetTop);
|
||||
timesheet_page.value.setScrollPosition('vertical', currentDayComponent.value[0]!.offsetTop, 800);
|
||||
return;
|
||||
}
|
||||
|
||||
if (timesheet_container.value !== null && mode === 'approval') {
|
||||
scroll_area_height.value = timesheet_container.value.offsetHeight
|
||||
}
|
||||
})
|
||||
</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"
|
||||
:style="mode === 'approval' ? `height: ${scroll_area_height}px;` : ''"
|
||||
: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 -->
|
||||
<div
|
||||
v-else
|
||||
ref="timesheet_container"
|
||||
class="col fit"
|
||||
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||
>
|
||||
<div
|
||||
v-for="timesheet, timesheet_index of timesheet_store.timesheets"
|
||||
:key="timesheet.timesheet_id"
|
||||
class="col column fit flex-center"
|
||||
>
|
||||
<transition
|
||||
appear
|
||||
enter-active-class="animated fadeInDown"
|
||||
leave-active-class="animated fadeOutUp"
|
||||
>
|
||||
<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)"
|
||||
flat
|
||||
dense
|
||||
:label="$t('timesheet.apply_preset_week')"
|
||||
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-icon
|
||||
name="las la-calendar-week"
|
||||
color="accent"
|
||||
size="md"
|
||||
/>
|
||||
</q-btn>
|
||||
</transition>
|
||||
|
||||
<transition-group
|
||||
appear
|
||||
:enter-active-class="`animated ${animation_style}`"
|
||||
>
|
||||
<div
|
||||
v-for="day, day_index in timesheet.days"
|
||||
:key="day.date"
|
||||
:ref="getMobileDayRef(day.date)"
|
||||
class="col-auto row q-pa-sm fit"
|
||||
:style="`animation-delay: ${day_index / 15}s;`"
|
||||
>
|
||||
<!-- mobile version in portrait mode -->
|
||||
<div
|
||||
v-if="$q.platform.is.mobile && ($q.screen.width < $q.screen.height)"
|
||||
class="col column full-width q-px-md q-py-sm"
|
||||
>
|
||||
<q-card
|
||||
class="mobile-rounded-10 shadow-12"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent' : 'bg-dark'"
|
||||
>
|
||||
|
||||
<q-card-section
|
||||
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;"
|
||||
>
|
||||
<span> {{ $d(extractDate(day.date, 'YYYY-MM-DD'), {
|
||||
weekday: 'long', day: 'numeric', month:
|
||||
'long'
|
||||
}) }}</span>
|
||||
|
||||
<q-icon
|
||||
v-if="(getDayApproval(day) || timesheet.is_approved)"
|
||||
name="verified"
|
||||
size="3em"
|
||||
color="white"
|
||||
class="absolute-top-left z-top"
|
||||
style="top: -0.2em; left: 0px;"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section
|
||||
v-if="day.shifts.filter(shift => shift.id !== 0).length > 0"
|
||||
class="q-pa-none transparent"
|
||||
>
|
||||
<ShiftListDay
|
||||
outlined
|
||||
:timesheet-id="timesheet.timesheet_id"
|
||||
:week-day-index="day_index"
|
||||
:animation-delay-multiplier="day_index"
|
||||
:approved="(getDayApproval(day) || timesheet.is_approved)"
|
||||
:day="day"
|
||||
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-btn
|
||||
v-if="!(getDayApproval(day) || timesheet.is_approved)"
|
||||
square
|
||||
dense
|
||||
size="xl"
|
||||
color="accent"
|
||||
icon="more_time"
|
||||
class="full-width"
|
||||
style="border-radius: 0 0 10px 10px;"
|
||||
@click="addNewShift(day.shifts, day.date, timesheet.timesheet_id)"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<!-- desktop version -->
|
||||
<div
|
||||
v-else
|
||||
class="col row full-width rounded-10 ellipsis shadow-10"
|
||||
>
|
||||
<div
|
||||
class="col row"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent' : 'bg-dark'"
|
||||
>
|
||||
<!-- 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;
|
||||
}
|
||||
}
|
||||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import ShiftListDay from 'src/modules/timesheets/components/shift-list-day.vue';
|
||||
import ShiftListDateWidget from 'src/modules/timesheets/components/shift-list-date-widget.vue';
|
||||
|
||||
import { ref, computed } from 'vue';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { Shift } from 'src/modules/timesheets/models/shift.models';
|
||||
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
|
||||
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 { extractDate } = date;
|
||||
|
||||
const ui_store = useUiStore();
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const timesheet_api = useTimesheetApi();
|
||||
|
||||
const mobile_animation_direction = ref('fadeInLeft');
|
||||
|
||||
const animation_style = computed(() => ui_store.is_mobile_mode ? mobile_animation_direction.value : 'fadeInDown');
|
||||
|
||||
const addNewShift = (day_shifts: Shift[], date: string, timesheet_id: number) => {
|
||||
ui_store.focus_next_component = true;
|
||||
const new_shift = new Shift;
|
||||
new_shift.date = date;
|
||||
new_shift.timesheet_id = timesheet_id;
|
||||
day_shifts.push(new_shift);
|
||||
};
|
||||
|
||||
const deleteUnsavedShift = (timesheet_index: number, day_index: number) => {
|
||||
if (timesheet_store.timesheets !== undefined) {
|
||||
const day = timesheet_store.timesheets[timesheet_index]!.days[day_index]!;
|
||||
const shifts_without_deleted_shift = day.shifts.filter(shift => shift.id !== 0);
|
||||
day.shifts = shifts_without_deleted_shift;
|
||||
}
|
||||
};
|
||||
|
||||
const getDayApproval = (day: TimesheetDay) => {
|
||||
if (day.shifts.length < 1) return false;
|
||||
return day.shifts.every(shift => shift.is_approved === true);
|
||||
};
|
||||
|
||||
const getMobileDayRef = (iso_date_string: string): string => {
|
||||
return iso_date_string === CURRENT_DATE_STRING ? 'currentDayComponent' : '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fit"
|
||||
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||
>
|
||||
<div
|
||||
v-for="timesheet, timesheet_index of timesheet_store.timesheets"
|
||||
:key="timesheet.timesheet_id"
|
||||
class="col column fit items-center"
|
||||
>
|
||||
<transition
|
||||
appear
|
||||
enter-active-class="animated fadeInDown"
|
||||
leave-active-class="animated fadeOutUp"
|
||||
>
|
||||
<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)"
|
||||
flat
|
||||
dense
|
||||
:label="$t('timesheet.apply_preset_week')"
|
||||
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-icon
|
||||
name="las la-calendar-week"
|
||||
color="accent"
|
||||
size="md"
|
||||
/>
|
||||
</q-btn>
|
||||
</transition>
|
||||
|
||||
<transition-group
|
||||
appear
|
||||
:enter-active-class="`animated ${animation_style}`"
|
||||
>
|
||||
<div
|
||||
v-for="day, day_index in timesheet.days"
|
||||
:key="day.date"
|
||||
:ref="getMobileDayRef(day.date)"
|
||||
class="col-auto row q-pa-sm full-width"
|
||||
:style="`animation-delay: ${day_index / 15}s;`"
|
||||
>
|
||||
<!-- mobile version in portrait mode -->
|
||||
<div
|
||||
v-if="$q.platform.is.mobile && ($q.screen.width < $q.screen.height)"
|
||||
class="col column full-width q-px-md q-py-sm"
|
||||
>
|
||||
<q-card
|
||||
class="shadow-12"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent rounded-10' : 'bg-dark mobile-rounded-10'"
|
||||
>
|
||||
|
||||
<q-card-section
|
||||
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;"
|
||||
>
|
||||
<span> {{ $d(extractDate(day.date, 'YYYY-MM-DD'), {
|
||||
weekday: 'long', day: 'numeric', month:
|
||||
'long'
|
||||
}) }}</span>
|
||||
|
||||
<q-icon
|
||||
v-if="(getDayApproval(day) || timesheet.is_approved)"
|
||||
name="verified"
|
||||
size="3em"
|
||||
color="white"
|
||||
class="absolute-top-left z-top"
|
||||
style="top: -0.2em; left: 0px;"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section
|
||||
v-if="day.shifts.filter(shift => shift.id !== 0).length > 0"
|
||||
class="q-pa-none transparent"
|
||||
>
|
||||
<ShiftListDay
|
||||
outlined
|
||||
:timesheet-id="timesheet.timesheet_id"
|
||||
:week-day-index="day_index"
|
||||
:animation-delay-multiplier="day_index"
|
||||
:approved="(getDayApproval(day) || timesheet.is_approved)"
|
||||
:day="day"
|
||||
@delete-unsaved-shift="deleteUnsavedShift(timesheet_index, day_index)"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-btn
|
||||
v-if="!(getDayApproval(day) || timesheet.is_approved)"
|
||||
square
|
||||
dense
|
||||
size="xl"
|
||||
color="accent"
|
||||
icon="more_time"
|
||||
class="full-width"
|
||||
style="border-radius: 0 0 10px 10px;"
|
||||
@click="addNewShift(day.shifts, day.date, timesheet.timesheet_id)"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<!-- desktop version -->
|
||||
<div
|
||||
v-else
|
||||
class="col row full-width rounded-10 ellipsis shadow-10"
|
||||
>
|
||||
<div
|
||||
class="col row"
|
||||
:class="(getDayApproval(day) || timesheet.is_approved) ? 'bg-accent' : 'bg-dark'"
|
||||
>
|
||||
<!-- 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>
|
||||
</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>
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
>
|
||||
/* eslint-disable */
|
||||
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 ExpenseDialog from 'src/modules/timesheets/components/expense-dialog.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 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<{
|
||||
mode?: 'approval' | 'normal';
|
||||
}>();
|
||||
|
|
@ -46,7 +62,7 @@
|
|||
<span
|
||||
v-if="mode === 'approval'"
|
||||
class="col-auto text-uppercase text-bold text-h5"
|
||||
>
|
||||
>
|
||||
{{ $t('timesheet.page_header') }}
|
||||
</span>
|
||||
|
||||
|
|
@ -54,8 +70,15 @@
|
|||
<!-- weekly overview -->
|
||||
<div class="col-auto row q-px-lg full-width">
|
||||
<!-- supervisor weekly overview -->
|
||||
<div class="col-xs-6 col-md-4 col-xl-3 q-pa-md">
|
||||
<ShiftListWeeklyOverview mode="total-hours" />
|
||||
<div
|
||||
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>
|
||||
|
||||
<PageHeaderTemplate
|
||||
|
|
@ -66,10 +89,13 @@
|
|||
class="col"
|
||||
/>
|
||||
|
||||
<q-space v-else />
|
||||
<q-space v-if="!$q.platform.is.mobile && mode === 'approval'" />
|
||||
|
||||
<!-- 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" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -77,7 +103,7 @@
|
|||
<!-- top menu -->
|
||||
<div
|
||||
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 -->
|
||||
<PayPeriodNavigator
|
||||
|
|
@ -133,12 +159,40 @@
|
|||
<!-- mobile weekly overview widget -->
|
||||
<ShiftListWeeklyOverviewMobile class="col-auto" />
|
||||
|
||||
<ShiftList
|
||||
<!-- standard scrollable shift list for user input -->
|
||||
<ShiftListScrollable
|
||||
v-if="mode === 'normal'"
|
||||
:mode="mode"
|
||||
: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
|
||||
v-if="$q.platform.is.mobile && $q.screen.width < $q.screen.height"
|
||||
square
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
const headerComponent = ref<HTMLElement | null>(null);
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user