feat(dashboard, timesheet): Add weekly hours to timesheet overview, overhaul dashboard links
BIN
src/assets/links/facturation-transparent.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/links/facturation_bg.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/links/google_bg.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
BIN
src/assets/links/hydroQC_bg.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
src/assets/links/hydroQC_icon.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
BIN
src/assets/links/intranet_logo.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/links/intranet_targo_bg.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
src/assets/links/logo_gmail.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
src/assets/links/logo_gmail_lockup_default_1x_r5.png
Normal file
|
After Width: | Height: | Size: 1006 B |
BIN
src/assets/links/map-icon.png
Normal file
|
After Width: | Height: | Size: 504 KiB |
BIN
src/assets/links/map_targo_banner.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
|
@ -2,10 +2,10 @@
|
|||
setup
|
||||
lang="ts"
|
||||
>
|
||||
const { imageSource = "", title = "", description = "", route = "" } = defineProps<{
|
||||
imageSource?: string,
|
||||
title?: string,
|
||||
description?: string,
|
||||
const { route = "" } = defineProps<{
|
||||
iconImageSource: string,
|
||||
bgImageSource: string,
|
||||
name: string,
|
||||
route?: string,
|
||||
}>();
|
||||
|
||||
|
|
@ -15,28 +15,50 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<q-card
|
||||
class="shortcut-card cursor-pointer shadow-12"
|
||||
<div
|
||||
class="full-width cursor-pointer bg-dark shadow-2 rounded-15 q-pa-xs position-relative"
|
||||
style="border: solid 1px var(--q-accent);"
|
||||
@click="onClickExternalShortcut"
|
||||
>
|
||||
<q-img
|
||||
:src="imageSource"
|
||||
fit="contain"
|
||||
<span
|
||||
v-if="$q.platform.is.mobile"
|
||||
class="col text-uppercase text-bold text-accent absolute"
|
||||
style="transform: translate(20px, -20px);"
|
||||
>
|
||||
<div class="absolute-bottom text-uppercase text-weight-bolder text-center">{{ title }}</div>
|
||||
</q-img>
|
||||
{{ name }}
|
||||
</span>
|
||||
|
||||
<q-card-section v-if="description">
|
||||
<span>{{ description }}</span>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<div
|
||||
class="row items-center q-px-lg q-py-sm link-card rounded-10 inset-shadow"
|
||||
:style="`background-image: url(${bgImageSource}); background-size: ${$q.platform.is.mobile ? 'cover' : 'contain'};`"
|
||||
>
|
||||
<q-icon
|
||||
round
|
||||
color="dark"
|
||||
size="md"
|
||||
:name="`img:${iconImageSource}`"
|
||||
class="col-auto q-pr-md"
|
||||
/>
|
||||
|
||||
<span
|
||||
v-if="!$q.platform.is.mobile"
|
||||
class="col text-uppercase text-bold"
|
||||
>
|
||||
{{ name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style
|
||||
lang="sass"
|
||||
scoped
|
||||
lang="css"
|
||||
>
|
||||
.shortcut-card
|
||||
width: 100%
|
||||
max-width: 250px
|
||||
.link-card {
|
||||
background-blend-mode: multiply;
|
||||
background-position: bottom right;
|
||||
background-repeat: no-repeat;
|
||||
background-color: var(--q-dark);
|
||||
background-size: contain;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,10 +2,18 @@
|
|||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { ref } from 'vue';
|
||||
import { RouteNames } from 'src/router/router-constants';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const slide = ref<string>('welcome');
|
||||
const autoplayTimer = ref(9001);
|
||||
|
||||
const onCarouselMouseEvent = (state: 'enter' | 'exit') => {
|
||||
if (state === 'enter')
|
||||
autoplayTimer.value = 0
|
||||
else
|
||||
autoplayTimer.value = 9001
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -17,10 +25,12 @@ import { ref } from 'vue';
|
|||
animated
|
||||
infinite
|
||||
arrows
|
||||
:autoplay="9001"
|
||||
:autoplay="autoplayTimer"
|
||||
control-color="accent"
|
||||
control-type="outline"
|
||||
class="bg-dark full-width rounded-15 shadow-18"
|
||||
class="bg-dark fit rounded-15 shadow-18"
|
||||
@mouseenter="onCarouselMouseEvent('enter')"
|
||||
@mouseleave="onCarouselMouseEvent('exit')"
|
||||
>
|
||||
<!-- welcome slide -->
|
||||
<q-carousel-slide
|
||||
|
|
@ -39,7 +49,7 @@ import { ref } from 'vue';
|
|||
</div>
|
||||
</q-img>
|
||||
|
||||
<div class="col column flex-center q-px-md">
|
||||
<div class="col column flex-center q-px-md text-h5 text-weight-light">
|
||||
<span class="col-auto text-center">{{ $t('dashboard.carousel.welcome_message') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -63,7 +73,7 @@ import { ref } from 'vue';
|
|||
</div>
|
||||
</q-img>
|
||||
|
||||
<div class="col column flex-center q-px-md">
|
||||
<div class="col column flex-center q-px-md text-h5 text-weight-light">
|
||||
<span class="col-auto text-center">{{ $t('dashboard.carousel.help_message') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
const { mode = 'totals', timesheetMode = 'normal', totalHours = 0, totalExpenses = 0 } = defineProps<{
|
||||
mode: 'total-hours' | 'off-hours';
|
||||
timesheetMode: 'approval' | 'normal';
|
||||
weeklyHours?: number[];
|
||||
totalHours?: number;
|
||||
totalExpenses?: number;
|
||||
}>();
|
||||
|
|
@ -18,8 +19,8 @@
|
|||
const timesheetStore = useTimesheetStore();
|
||||
const is_management = auth_store.user?.user_module_access.includes('timesheets_approval');
|
||||
|
||||
const vacationHours = computed(() => timesheetStore.paid_time_off_totals.vacation_hours);
|
||||
const sickHours = computed(() => timesheetStore.paid_time_off_totals.sick_hours);
|
||||
const vacationHours = computed(() => timesheetStore.paid_time_off_totals.vacation_hours);
|
||||
const sickHours = computed(() => timesheetStore.paid_time_off_totals.sick_hours);
|
||||
const bankedHours = computed(() => timesheetStore.paid_time_off_totals.banked_hours);
|
||||
|
||||
onMounted(async () => {
|
||||
|
|
@ -37,6 +38,18 @@
|
|||
v-if="mode === 'total-hours'"
|
||||
class="col column full-width"
|
||||
>
|
||||
<div
|
||||
v-for="hours, index in weeklyHours"
|
||||
:key="index"
|
||||
class="col row full-width"
|
||||
>
|
||||
<span class="col-auto text-uppercase text-caption text-bold text-accent">
|
||||
{{ $t(`timesheet_approvals.table.weekly_hours_${index + 1}`) }}
|
||||
</span>
|
||||
|
||||
<span class="col text-right">{{ getHoursMinutesStringFromHoursFloat(hours) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="col row full-width">
|
||||
<span class="col-auto text-uppercase text-caption text-bold text-accent">
|
||||
{{ $t('timesheet.total_hours') }}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,15 @@ import { RouteNames } from 'src/router/router-constants';
|
|||
const hasShiftErrors = computed(() => timesheetStore.all_current_shifts.filter(shift => shift.has_error === true).length > 0);
|
||||
const isTimesheetsApproved = computed(() => timesheetStore.timesheets.every(timesheet => timesheet.is_approved));
|
||||
|
||||
const weeklyHours = computed(() => timesheetStore.timesheets.map(timesheet =>
|
||||
Object.values(timesheet.weekly_hours).reduce((sum, hoursPerType) => sum += hoursPerType, 0) - timesheet.weekly_hours.sick
|
||||
));
|
||||
const totalHours = computed(() => timesheetStore.timesheets.reduce((sum, timesheet) =>
|
||||
sum += timesheet.weekly_hours.regular
|
||||
+ timesheet.weekly_hours.evening
|
||||
+ timesheet.weekly_hours.emergency
|
||||
+ timesheet.weekly_hours.vacation
|
||||
+ timesheet.weekly_hours.holiday
|
||||
+ timesheet.weekly_hours.overtime,
|
||||
0 //initial value
|
||||
));
|
||||
|
|
@ -124,6 +129,7 @@ import { RouteNames } from 'src/router/router-constants';
|
|||
<ShiftListWeeklyOverview
|
||||
mode="total-hours"
|
||||
:timesheet-mode="mode"
|
||||
:weekly-hours="weeklyHours"
|
||||
:total-hours="totalHours"
|
||||
:total-expenses="totalExpenses"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -12,68 +12,84 @@
|
|||
class="q-pa-md justify-center items-stretch bg-secondary"
|
||||
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||
>
|
||||
<!-- left column -->
|
||||
<div class="column col flex-center q-pa-md">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- center column -->
|
||||
<div class="column col-xs-12 col-md-8 col-xl-6 items-center q-pa-md">
|
||||
<div class="col-auto full-width q-py-md">
|
||||
<div class="column col items-center q-pa-md">
|
||||
<div class="col-8 fit q-py-md">
|
||||
<MainCarousel />
|
||||
</div>
|
||||
|
||||
<span class="col-auto text-uppercase text-weight-bold self-start q-pt-md">{{ $t('dashboard.useful_links') }}</span>
|
||||
|
||||
<div class="col row full-width justify-evenly items-start q-py-md">
|
||||
<div class="col-3 q-pa-sm">
|
||||
<ShortcutCard
|
||||
image-source="src/assets/google_thumbnail.png"
|
||||
title="Google Workspace"
|
||||
route="https://mail.google.com/mail/u/0/#inbox"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-3 q-pa-sm">
|
||||
<ShortcutCard
|
||||
image-source="src/assets/facturation_thumbnail.png"
|
||||
title="Facturation"
|
||||
route="https://facturation.targo.ca/facturation/accueil.php?menu=ticket_open"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-3 q-pa-sm">
|
||||
<ShortcutCard
|
||||
image-source="src/assets/map_targo_banner.png"
|
||||
title="Map Targo"
|
||||
route="https://map.targointernet.com/infrastructure/map.php"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-3 q-pa-sm">
|
||||
<ShortcutCard
|
||||
image-source="src/assets/info-pannes.png"
|
||||
title="Info Pannes"
|
||||
route="https://infopannes.solutions.hydroquebec.com/info-pannes/pannes/pannes-en-cours"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- right column -->
|
||||
<div class="column col items-center">
|
||||
<div class="column col-lg-4 items-center" :class="$q.platform.is.mobile ? 'q-px-md' : 'q-px-xl'">
|
||||
<span
|
||||
v-if="!$q.platform.is.mobile"
|
||||
class="col-auto text-uppercase text-weight-bold self-start q-px-md q-pt-lg"
|
||||
>
|
||||
{{ $t('dashboard.useful_links') }}
|
||||
</span>
|
||||
|
||||
<div class="col-auto full-width"
|
||||
:class="$q.platform.is.mobile ? 'q-py-md' : 'q-py-sm'">
|
||||
<ShortcutCard
|
||||
icon-image-source="src/assets/links/logo_gmail.png"
|
||||
bg-image-source="src/assets/links/google_bg.png"
|
||||
name="Messagerie"
|
||||
route="https://mail.google.com/mail/u/0/#inbox"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto full-width"
|
||||
:class="$q.platform.is.mobile ? 'q-py-md' : 'q-py-sm'">
|
||||
<ShortcutCard
|
||||
icon-image-source="src/assets/links/facturation-transparent.png"
|
||||
bg-image-source="src/assets/links/facturation_bg.png"
|
||||
name="Facturation"
|
||||
route="https://facturation.targo.ca/facturation/accueil.php?menu=ticket_open"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto full-width"
|
||||
:class="$q.platform.is.mobile ? 'q-py-md' : 'q-py-sm'">
|
||||
<ShortcutCard
|
||||
icon-image-source="src/assets/links/map-icon.png"
|
||||
bg-image-source="src/assets/links/map_targo_banner.png"
|
||||
name="Map Targo"
|
||||
route="https://map.targointernet.com/infrastructure/map"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto full-width"
|
||||
:class="$q.platform.is.mobile ? 'q-py-md' : 'q-py-sm'">
|
||||
<ShortcutCard
|
||||
icon-image-source="src/assets/links/hydroQC_icon.png"
|
||||
bg-image-source="src/assets/links/hydroQC_bg.png"
|
||||
name="Info Pannes"
|
||||
route="https://infopannes.solutions.hydroquebec.com/info-pannes/pannes/pannes-en-cours"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto full-width"
|
||||
:class="$q.platform.is.mobile ? 'q-py-md' : 'q-py-sm'">
|
||||
<ShortcutCard
|
||||
icon-image-source="src/assets/links/intranet_logo.png"
|
||||
bg-image-source="src/assets/links/intranet_targo_bg.png"
|
||||
name="Intranet"
|
||||
route="https://intranet.facturation.targo.ca/"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col"></div>
|
||||
|
||||
<div
|
||||
class="col-auto row full-width within-iframe"
|
||||
:class="$q.platform.is.mobile ? 'justify-center' : 'justify-end q-pl-md'"
|
||||
style="height: 50vh;"
|
||||
:class="$q.platform.is.mobile ? 'justify-center q-pt-lg' : 'justify-end'"
|
||||
>
|
||||
<iframe
|
||||
title="Environment Canada Weather"
|
||||
height="400px"
|
||||
height="200px"
|
||||
src="https://weather.gc.ca/wxlink/wxlink.html?coords=45.159%2C-73.676&lang=f"
|
||||
allowtransparency="true"
|
||||
style="border: 0;"
|
||||
class="col-auto"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||