feat(details): Adjust shift display UI, DRY code in details components, prep for modifying UI
This commit is contained in:
parent
b8c9b8ed76
commit
072e0931a1
|
|
@ -26,5 +26,5 @@ $dark-page: #323232;
|
||||||
$positive: #21ba45;
|
$positive: #21ba45;
|
||||||
$negative: #e6364b;
|
$negative: #e6364b;
|
||||||
$info: #6bb9e7;
|
$info: #6bb9e7;
|
||||||
$warning: #eec964;
|
$warning: #e4a944;
|
||||||
$white: white;
|
$white: white;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { colors } from 'quasar';
|
||||||
import { Bar } from 'vue-chartjs';
|
import { Bar } from 'vue-chartjs';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, type ChartData, type ChartOptions, type Plugin, type ChartDataset } from 'chart.js';
|
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, type ChartData, type ChartOptions, type Plugin, type ChartDataset } from 'chart.js';
|
||||||
|
|
@ -12,14 +13,11 @@
|
||||||
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
||||||
ChartJS.defaults.maintainAspectRatio = false;
|
ChartJS.defaults.maintainAspectRatio = false;
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = defineProps<{
|
||||||
rawData: PayPeriodEmployeeDetails | undefined;
|
rawData: PayPeriodEmployeeDetails | undefined;
|
||||||
options?: ChartOptions<"bar"> | undefined;
|
options?: ChartOptions<"bar"> | undefined;
|
||||||
plugins?: Plugin<"bar">[] | undefined;
|
plugins?: Plugin<"bar">[] | undefined;
|
||||||
}>(), {
|
}>();
|
||||||
options: () => ({}),
|
|
||||||
plugins: () => [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const hours_worked_labels = ref<string[]>([]);
|
const hours_worked_labels = ref<string[]>([]);
|
||||||
const hours_worked_dataset = ref<ChartDataset<'bar'>[]>([]);
|
const hours_worked_dataset = ref<ChartDataset<'bar'>[]>([]);
|
||||||
|
|
@ -28,34 +26,36 @@
|
||||||
if (props.rawData) {
|
if (props.rawData) {
|
||||||
const all_weeks = [props.rawData.week1, props.rawData.week2];
|
const all_weeks = [props.rawData.week1, props.rawData.week2];
|
||||||
const all_days = all_weeks.flatMap( week => Object.values(week.shifts));
|
const all_days = all_weeks.flatMap( week => Object.values(week.shifts));
|
||||||
const regular_hours = all_days.map( day => day.regular_hours);
|
const datasetConfig = [
|
||||||
const evening_hours = all_days.map( day => day.evening_hours);
|
|
||||||
const emergency_hours = all_days.map( day => day.emergency_hours);
|
|
||||||
const overtime_hours = all_days.map( day => day.overtime_hours);
|
|
||||||
|
|
||||||
hours_worked_dataset.value = [
|
|
||||||
{
|
{
|
||||||
|
key: 'regular_hours',
|
||||||
label: t('timeSheetValidations.hoursWorkedRegular'),
|
label: t('timeSheetValidations.hoursWorkedRegular'),
|
||||||
data: regular_hours,
|
color: colors.getPaletteColor('green-5'),
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-primary').trim(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: 'evening_hours',
|
||||||
label: t('timeSheetValidations.hoursWorkedEvening'),
|
label: t('timeSheetValidations.hoursWorkedEvening'),
|
||||||
data: evening_hours,
|
color: colors.getPaletteColor('green-9'),
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-info').trim(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: 'emergency_hours',
|
||||||
label: t('timeSheetValidations.hoursWorkedEmergency'),
|
label: t('timeSheetValidations.hoursWorkedEmergency'),
|
||||||
data: emergency_hours,
|
color: getComputedStyle(document.body).getPropertyValue('--q-warning').trim(),
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-accent').trim(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: 'overtime_hours',
|
||||||
label: t('timeSheetValidations.hoursWorkedOvertime'),
|
label: t('timeSheetValidations.hoursWorkedOvertime'),
|
||||||
data: overtime_hours,
|
color: getComputedStyle(document.body).getPropertyValue('--q-negative').trim(),
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-negative').trim(),
|
|
||||||
},
|
},
|
||||||
]
|
] as const;
|
||||||
hours_worked_labels.value = all_days.map( day => day.short_date);
|
|
||||||
|
hours_worked_dataset.value = datasetConfig.map(cfg => ({
|
||||||
|
label: cfg.label,
|
||||||
|
data: all_days.map(day => day[ cfg.key ]),
|
||||||
|
backgroundColor: cfg.color,
|
||||||
|
}));
|
||||||
|
|
||||||
|
hours_worked_labels.value = all_days.map(day => day.short_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -69,6 +69,31 @@
|
||||||
<template>
|
<template>
|
||||||
<Bar
|
<Bar
|
||||||
:data="getHoursWorkedData()"
|
:data="getHoursWorkedData()"
|
||||||
:options="props.options"
|
:options="({
|
||||||
|
indexAxis: $q.screen.lt.md? 'y' : 'x',
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
boxWidth: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: t('timeSheetValidations.hoursWorkedChartTitle'),
|
||||||
|
color: '#616161'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true,
|
||||||
|
suggestedMin: 0,
|
||||||
|
suggestedMax: 10,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { Doughnut } from 'vue-chartjs';
|
import { colors } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { Doughnut } from 'vue-chartjs';
|
||||||
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale, LinearScale, type ChartData, type ChartOptions, type Plugin, type ChartDataset } from 'chart.js';
|
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale, LinearScale, type ChartData, type ChartOptions, type Plugin, type ChartDataset } from 'chart.js';
|
||||||
import type { PayPeriodOverviewEmployee } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface';
|
import type { PayPeriodOverviewEmployee } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface';
|
||||||
|
|
||||||
|
|
@ -12,14 +13,9 @@
|
||||||
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
||||||
ChartJS.defaults.maintainAspectRatio = false;
|
ChartJS.defaults.maintainAspectRatio = false;
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = defineProps<{
|
||||||
rawData: PayPeriodOverviewEmployee | undefined;
|
rawData: PayPeriodOverviewEmployee | undefined;
|
||||||
options?: ChartOptions<"doughnut"> | undefined;
|
}>();
|
||||||
plugins?: Plugin<"doughnut">[] | undefined;
|
|
||||||
}>(), {
|
|
||||||
options: () => ({}),
|
|
||||||
plugins: () => [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const shift_type_labels = ref<string[]>([]);
|
const shift_type_labels = ref<string[]>([]);
|
||||||
const shift_type_totals = ref<ChartDataset<'doughnut'>[]>([{ data: [40, 0, 2, 5], }]);
|
const shift_type_totals = ref<ChartDataset<'doughnut'>[]>([{ data: [40, 0, 2, 5], }]);
|
||||||
|
|
@ -33,10 +29,10 @@
|
||||||
props.rawData.overtime_hours,
|
props.rawData.overtime_hours,
|
||||||
],
|
],
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
getComputedStyle(document.body).getPropertyValue('--q-primary').trim(),
|
colors.getPaletteColor('green-5'), // Regular
|
||||||
getComputedStyle(document.body).getPropertyValue('--q-info').trim(),
|
colors.getPaletteColor('green-9'), // Evening
|
||||||
getComputedStyle(document.body).getPropertyValue('--q-accent').trim(),
|
getComputedStyle(document.body).getPropertyValue('--q-warning').trim(), // Emergency
|
||||||
getComputedStyle(document.body).getPropertyValue('--q-negative').trim(),
|
getComputedStyle(document.body).getPropertyValue('--q-negative').trim(), // Overtime
|
||||||
]
|
]
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
@ -58,6 +54,14 @@
|
||||||
<template>
|
<template>
|
||||||
<Doughnut
|
<Doughnut
|
||||||
:data="data"
|
:data="data"
|
||||||
:options="props.options"
|
:options="({
|
||||||
|
plugins:{
|
||||||
|
legend:{
|
||||||
|
labels:{
|
||||||
|
boxWidth: 15,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -13,14 +13,9 @@
|
||||||
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
||||||
ChartJS.defaults.maintainAspectRatio = false;
|
ChartJS.defaults.maintainAspectRatio = false;
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = defineProps<{
|
||||||
rawData: PayPeriodEmployeeDetails | undefined;
|
rawData: PayPeriodEmployeeDetails | undefined;
|
||||||
options?: ChartOptions<"bar"> | undefined;
|
}>();
|
||||||
plugins?: Plugin<"bar">[] | undefined;
|
|
||||||
}>(), {
|
|
||||||
options: () => ({}),
|
|
||||||
plugins: () => [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const expenses_dataset = ref<ChartDataset<'bar'>[]>([]);
|
const expenses_dataset = ref<ChartDataset<'bar'>[]>([]);
|
||||||
const expenses_labels = ref<string[]>([]);
|
const expenses_labels = ref<string[]>([]);
|
||||||
|
|
@ -71,6 +66,31 @@
|
||||||
<template>
|
<template>
|
||||||
<Bar
|
<Bar
|
||||||
:data="getExpensesData()"
|
:data="getExpensesData()"
|
||||||
:options="props.options"
|
:options="({
|
||||||
|
indexAxis: $q.screen.lt.md? 'y' : 'x',
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: t('timeSheetValidations.reportFilterExpenses'),
|
||||||
|
color: '#616161'
|
||||||
|
},
|
||||||
|
legend:{
|
||||||
|
labels:{
|
||||||
|
boxWidth: 15,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
suggestedMin: 0,
|
||||||
|
suggestedMax: 100,
|
||||||
|
stacked: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})"
|
||||||
|
:style="$q.screen.lt.md ? 'min-height: 300px;': '' "
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1,59 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- This header component is placed in absolute top in order to keep the timestamps
|
|
||||||
below it vertically centered in the row. Since being absolute top makes it no longer
|
|
||||||
follow the alignment of the shift rows below it, it must containt the same elements
|
|
||||||
as them to remain aligned. -->
|
|
||||||
<q-card-section
|
<q-card-section
|
||||||
horizontal
|
horizontal
|
||||||
class="text-uppercase text-center items-center q-px-xs"
|
class="text-uppercase text-center items-center q-pa-none"
|
||||||
>
|
>
|
||||||
<!-- Date widget -->
|
|
||||||
<q-card-section class="q-px-xs q-py-none col-auto"> <div class="q-px-xs q-py-none" style="width: 75px;"></div></q-card-section>
|
|
||||||
|
|
||||||
<!-- shift row itself -->
|
<!-- shift row itself -->
|
||||||
<q-card-section class="col q-pa-none">
|
<q-card-section class="col q-pa-none">
|
||||||
<q-card-section horizontal class="col q-px-xs">
|
<q-card-section horizontal class="col q-pa-none">
|
||||||
<!-- punch-in timestamps -->
|
<!-- punch-in timestamps -->
|
||||||
<q-card-section class="col q-px-xs q-py-none">
|
<q-card-section class="col q-pa-none">
|
||||||
<q-item-label class="text-weight-bolder text-primary">
|
<q-item-label class="text-weight-bolder text-primary" style="font-size: 0.7em;">
|
||||||
{{ $t('shiftColumns.labelIn') }}
|
{{ $t('shiftColumns.labelIn') }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<!-- arrows pointing to punch-out timestamps -->
|
<!-- arrows pointing to punch-out timestamps -->
|
||||||
<q-card-section class="col-auto q-pa-none q-mx-sm">
|
<q-card-section class="col q-py-none q-px-sm">
|
||||||
<q-icon
|
|
||||||
name="double_arrow"
|
|
||||||
color="transparent"
|
|
||||||
size="24px"
|
|
||||||
style="transform: translateX(5px);"
|
|
||||||
/>
|
|
||||||
<q-icon
|
|
||||||
name="double_arrow"
|
|
||||||
color="transparent"
|
|
||||||
size="24px"
|
|
||||||
style="transform: translateX(-5px);"
|
|
||||||
/>
|
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<!-- punch-out timestamps -->
|
<!-- punch-out timestamps -->
|
||||||
<q-card-section class="col q-px-xs q-py-none">
|
<q-card-section class="col q-pa-none">
|
||||||
<q-item-label class="text-weight-bolder text-primary">
|
<q-item-label class="text-weight-bolder text-primary" style="font-size: 0.7em;">
|
||||||
{{ $t('shiftColumns.labelOut') }}
|
{{ $t('shiftColumns.labelOut') }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<!-- shift type badge -->
|
|
||||||
<q-card-section class="col column q-pa-none q-gutter-md"><div style="width: 80px;"></div></q-card-section>
|
|
||||||
|
|
||||||
<!-- comment button -->
|
<!-- comment button -->
|
||||||
<q-card-section class="col-auto column q-pa-none">
|
<q-card-section class="col column q-pa-none">
|
||||||
<q-btn
|
|
||||||
icon="chat_bubble_outline"
|
|
||||||
color="transparent"
|
|
||||||
flat
|
|
||||||
class="q-pa-sm col"
|
|
||||||
/>
|
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
|
||||||
|
|
@ -1,113 +1,103 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import type { Shift } from 'src/modules/timesheets/types/timesheet-shift-interface';
|
||||||
import { default_shift } from 'src/modules/timesheets/types/timesheet-shift-interface';
|
|
||||||
import type { TimesheetDetailsDailySchedule } from 'src/modules/timesheets/types/timesheet-details-interface';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
dayData: TimesheetDetailsDailySchedule;
|
shift: Shift;
|
||||||
}>();
|
}>();
|
||||||
const shifts_or_placeholder = computed(() => {
|
|
||||||
return props.dayData.shifts.length > 0 ? props.dayData.shifts : [default_shift];
|
|
||||||
})
|
|
||||||
|
|
||||||
const getShiftColor = (type: string): string => {
|
const getShiftColor = (type: string): string => {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case 'REGULAR': return 'primary';
|
case 'REGULAR': return 'secondary';
|
||||||
case 'EVENING': return 'info';
|
case 'EVENING': return 'warning';
|
||||||
case 'EMERGENCY': return 'teal-14';
|
case 'EMERGENCY': return 'amber-10';
|
||||||
case 'OVERTIME': return 'negative';
|
case 'OVERTIME': return 'negative';
|
||||||
case 'VACATION': return 'purple-10';
|
case 'VACATION': return 'purple-10';
|
||||||
case 'HOLIDAY': return 'indigo-13';
|
case 'HOLIDAY': return 'purple-10';
|
||||||
case 'SICK': return 'grey-9';
|
case 'SICK': return 'grey-8';
|
||||||
default : return 'transparent';
|
default : return 'transparent';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTextColor = (type: string): string => {
|
||||||
|
switch(type) {
|
||||||
|
case 'REGULAR': return 'grey-8';
|
||||||
|
case '': return 'transparent';
|
||||||
|
default: return 'white';
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-card-section
|
<q-card-section
|
||||||
horizontal
|
horizontal
|
||||||
class="q-pa-xs text-uppercase text-center items-center"
|
class="q-pa-none text-uppercase text-center items-center cursor-pointer rounded-10"
|
||||||
|
style="line-height: 1;"
|
||||||
>
|
>
|
||||||
<!-- punch-in timestamps -->
|
<!-- punch-in timestamps -->
|
||||||
<q-card-section class="q-pa-none col">
|
<q-card-section class="q-pa-none col">
|
||||||
<q-item-label
|
<q-item-label
|
||||||
v-for="shift, index in props.dayData.shifts"
|
class="text-weight-bolder q-pa-xs rounded-5"
|
||||||
:key="index"
|
:class="'bg-' + getShiftColor(props.shift.type) + ' text-' + getTextColor(props.shift.type)"
|
||||||
class="text-weight-bolder text-grey-8 bg-secondary q-pa-xs rounded-5"
|
style="font-size: 1.5em; line-height: 80% !important;"
|
||||||
style="font-size: 1.75em; line-height: 80% !important;"
|
|
||||||
>
|
>
|
||||||
{{ shift.start_time }}
|
{{ props.shift.start_time }}
|
||||||
</q-item-label>
|
|
||||||
<q-item-label v-if="props.dayData.shifts.length === 0"
|
|
||||||
class="text-weight-bolder text-grey-5"
|
|
||||||
style="font-size: 1.75em; line-height: 80% !important;"
|
|
||||||
>
|
|
||||||
---
|
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<!-- arrows pointing to punch-out timestamps -->
|
<!-- arrows pointing to punch-out timestamps -->
|
||||||
<q-card-section
|
<q-card-section
|
||||||
horizontal class="items-center justify-center q-ma-sm col-auto">
|
horizontal
|
||||||
<q-icon
|
class="items-center justify-center q-mx-sm col"
|
||||||
name="double_arrow"
|
>
|
||||||
:color=" props.dayData.shifts.length > 0 ? 'accent' : 'transparent'"
|
<div
|
||||||
size="24px"
|
v-for="icon_data, index in [
|
||||||
style="transform: translateX(5px);"
|
{ transform: 'transform: translateX(5px);', color: 'accent' },
|
||||||
/>
|
{ transform: 'transform: translateX(-5px);', color: 'primary' }]"
|
||||||
<q-icon
|
:key="index"
|
||||||
name="double_arrow"
|
>
|
||||||
:color=" props.dayData.shifts.length > 0 ? 'primary' : 'transparent'"
|
<q-icon
|
||||||
size="24px"
|
v-if="props.shift.type !== ''"
|
||||||
style="transform: translateX(-5px);"
|
name="double_arrow"
|
||||||
/>
|
:color="icon_data.color"
|
||||||
|
size="24px"
|
||||||
|
:style="icon_data.transform"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<!-- punch-out timestamps -->
|
<!-- punch-out timestamps -->
|
||||||
<q-card-section class="q-pa-none col">
|
<q-card-section class="q-pa-none col">
|
||||||
<q-item-label
|
<q-item-label
|
||||||
v-for="shift, index in props.dayData.shifts"
|
class="text-weight-bolder text-white q-pa-xs rounded-5"
|
||||||
:key="index"
|
:class="'bg-' + getShiftColor(props.shift.type) + ' text-' + getTextColor(props.shift.type)"
|
||||||
class="text-weight-bolder text-grey-8 bg-secondary q-pa-xs rounded-5"
|
style="font-size: 1.5em; line-height: 80% !important;"
|
||||||
style="font-size: 1.75em; line-height: 80% !important;"
|
|
||||||
>
|
>
|
||||||
{{ shift.end_time }}
|
{{ props.shift.end_time }}
|
||||||
</q-item-label>
|
|
||||||
<q-item-label v-if="props.dayData.shifts.length === 0"
|
|
||||||
class="text-weight-bolder text-grey-5"
|
|
||||||
style="font-size: 1.75em; line-height: 80% !important;"
|
|
||||||
>
|
|
||||||
---
|
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<!-- shift type badge -->
|
<!-- comment and expenses buttons -->
|
||||||
<q-card-section class="col column q-pa-none items-center q-gutter-md">
|
|
||||||
<q-badge
|
|
||||||
v-for="shift, index in shifts_or_placeholder"
|
|
||||||
:key="index"
|
|
||||||
:color="shift.type ? getShiftColor(shift.type) : 'transparent'"
|
|
||||||
:label="shift.type || ''"
|
|
||||||
class="text-weight-medium justify-center col"
|
|
||||||
style="width: 80px; font-size: 0.8em;"
|
|
||||||
/>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<!-- comment button -->
|
|
||||||
<q-card-section
|
<q-card-section
|
||||||
class="col-auto column q-pa-none items-end"
|
class="col q-pa-none text-right"
|
||||||
>
|
>
|
||||||
<q-space />
|
<!-- chat_bubble_outline or announcement -->
|
||||||
<q-btn
|
<q-btn
|
||||||
v-for="(shift, index) in shifts_or_placeholder"
|
v-if="props.shift.type !== ''"
|
||||||
:key="index"
|
|
||||||
flat
|
flat
|
||||||
dense
|
dense
|
||||||
:disable="shift.type === ''"
|
color='grey-8'
|
||||||
icon="chat_bubble_outline"
|
icon="chat_bubble_outline"
|
||||||
:color="shift.type === '' ? 'grey-5' : 'grey-8'"
|
class="q-pa-none"
|
||||||
class="q-pa-xs col"
|
/>
|
||||||
|
|
||||||
|
<!-- insert_drive_file or request_quote -->
|
||||||
|
<q-btn
|
||||||
|
v-if="props.shift.type !== ''"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
color='grey-8'
|
||||||
|
icon="attach_money"
|
||||||
|
class="q-pa-none q-mx-xs"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import TimesheetApprovalEmployeeDetailsShiftsRow from 'src/modules/timesheet-approval/components/timesheet-approval-employee-details-shifts-row.vue';
|
import TimesheetApprovalEmployeeDetailsShiftsRow from 'src/modules/timesheet-approval/components/timesheet-approval-employee-details-shifts-row.vue';
|
||||||
import TimesheetApprovalEmployeeDetailsShiftsRowHeader from 'src/modules/timesheet-approval/components/timesheet-approval-employee-details-shifts-row-header.vue';
|
import TimesheetApprovalEmployeeDetailsShiftsRowHeader from 'src/modules/timesheet-approval/components/timesheet-approval-employee-details-shifts-row-header.vue';
|
||||||
import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
import type { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
|
import { default_shift, type Shift } from 'src/modules/timesheets/types/timesheet-shift-interface';
|
||||||
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface';
|
import type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
@ -9,6 +10,10 @@
|
||||||
currentPayPeriod: PayPeriod;
|
currentPayPeriod: PayPeriod;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const shifts_or_placeholder = (shifts: Shift[]): Shift[] => {
|
||||||
|
return shifts.length > 0 ? shifts : [default_shift];
|
||||||
|
};
|
||||||
|
|
||||||
const getDate = (shift_date: string): Date => {
|
const getDate = (shift_date: string): Date => {
|
||||||
return new Date(props.currentPayPeriod.pay_year.toString() + '/' + shift_date);
|
return new Date(props.currentPayPeriod.pay_year.toString() + '/' + shift_date);
|
||||||
};
|
};
|
||||||
|
|
@ -27,29 +32,39 @@
|
||||||
bordered
|
bordered
|
||||||
class="row items-center rounded-10 q-mb-xs"
|
class="row items-center rounded-10 q-mb-xs"
|
||||||
>
|
>
|
||||||
<TimesheetApprovalEmployeeDetailsShiftsRowHeader class="absolute-top" />
|
|
||||||
<q-card-section class="col-auto q-pa-xs text-white">
|
<q-card-section class="col-auto q-pa-xs text-white">
|
||||||
<div
|
<div
|
||||||
class="bg-primary rounded-10 q-pa-xs text-center"
|
class="bg-primary rounded-10 q-pa-xs text-center"
|
||||||
style="width: 75px;"
|
:style="$q.screen.lt.md ? '' : 'width: 75px;'"
|
||||||
>
|
>
|
||||||
<q-item-label
|
<q-item-label
|
||||||
style="font-size: 0.8em;"
|
style="font-size: 0.7em;"
|
||||||
class="text-uppercase"
|
class="text-uppercase"
|
||||||
>{{ $d(getDate(day.short_date), {weekday: 'long'}) }}</q-item-label>
|
>{{ $d(getDate(day.short_date), {weekday: $q.screen.lt.md ? 'short' : 'long'}) }}</q-item-label>
|
||||||
<q-item-label
|
<q-item-label
|
||||||
class="text-weight-bolder"
|
class="text-weight-bolder"
|
||||||
style="font-size: 3em; line-height: 90% !important;"
|
style="font-size: 2.5em; line-height: 90% !important;"
|
||||||
>{{ day.short_date.split('/')[1] }}</q-item-label>
|
>{{ day.short_date.split('/')[1] }}</q-item-label>
|
||||||
<q-item-label
|
<q-item-label
|
||||||
style="font-size: 0.8em;"
|
style="font-size: 0.7em;"
|
||||||
class="text-uppercase"
|
class="text-uppercase"
|
||||||
>{{ $d(getDate(day.short_date), {month: 'long'}) }}</q-item-label>
|
>{{ $d(getDate(day.short_date), {month: $q.screen.lt.md ? 'short' : 'long'}) }}</q-item-label>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="col q-pa-none">
|
<q-card-section class="col q-pa-none">
|
||||||
|
<TimesheetApprovalEmployeeDetailsShiftsRowHeader />
|
||||||
<TimesheetApprovalEmployeeDetailsShiftsRow
|
<TimesheetApprovalEmployeeDetailsShiftsRow
|
||||||
:day-data="day"
|
v-for="shift, shift_index in shifts_or_placeholder(day.shifts)"
|
||||||
|
:key="shift_index"
|
||||||
|
:shift="shift"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="q-pr-xs col-auto">
|
||||||
|
<q-btn
|
||||||
|
push
|
||||||
|
color="primary"
|
||||||
|
icon="more_time"
|
||||||
|
class="q-pa-sm"
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
import type { PayPeriodOverviewEmployee } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface';
|
import type { PayPeriodOverviewEmployee } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-overview-employee-interface';
|
||||||
import type { PayPeriodEmployeeDetails } from '../types/timesheet-approval-pay-period-employee-details-interface';
|
import type { PayPeriodEmployeeDetails } from '../types/timesheet-approval-pay-period-employee-details-interface';
|
||||||
import { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
import { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
|
import { colors } from 'quasar';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
|
@ -21,6 +22,53 @@ import { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const is_showing_graph = ref<boolean>(true);
|
const is_showing_graph = ref<boolean>(true);
|
||||||
|
|
||||||
|
// case 'REGULAR': return 'green-5';
|
||||||
|
// case 'EVENING': return 'green-9';
|
||||||
|
// case 'EMERGENCY': return 'warning';
|
||||||
|
// case 'OVERTIME': return 'negative';
|
||||||
|
// case 'VACATION': return 'purple-10';
|
||||||
|
// case 'HOLIDAY': return 'purple-10';
|
||||||
|
// case 'SICK': return 'grey-9';
|
||||||
|
// default : return 'transparent';
|
||||||
|
|
||||||
|
type shiftColor = {
|
||||||
|
type: string;
|
||||||
|
color: string;
|
||||||
|
text_color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shift_type_legend: shiftColor[] = [
|
||||||
|
{
|
||||||
|
type: t('timeSheet.shiftRegular'),
|
||||||
|
color: 'secondary',
|
||||||
|
text_color: 'grey-8',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: t('timeSheet.shiftEvening'),
|
||||||
|
color: 'warning',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: t('timeSheet.shiftEmergency'),
|
||||||
|
color: 'amber-10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: t('timeSheetValidations.hoursWorkedOvertime'),
|
||||||
|
color: 'negative',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: t('timeSheet.shiftVacation'),
|
||||||
|
color: 'purple-10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: t('timeSheet.shiftHoliday'),
|
||||||
|
color: 'purple-8',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: t('timeSheet.shiftSick'),
|
||||||
|
color: 'grey-8',
|
||||||
|
},
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -51,7 +99,7 @@ import { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
{{ props.employeeName }}
|
{{ props.employeeName }}
|
||||||
|
|
||||||
<q-separator class="q-mb-sm" color="accent" size="2px" />
|
<q-separator class="q-mb-sm" color="accent" size="2px" />
|
||||||
<q-card-actions align="center">
|
<q-card-actions align="center" class="q-pa-none">
|
||||||
<q-card flat class="bg-secondary rounded-5 q-pa-xs">
|
<q-card flat class="bg-secondary rounded-5 q-pa-xs">
|
||||||
<q-btn-toggle
|
<q-btn-toggle
|
||||||
color="white"
|
color="white"
|
||||||
|
|
@ -70,13 +118,30 @@ import { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
<!-- employee timesheet details edit -->
|
<!-- employee timesheet details edit -->
|
||||||
<q-card-section
|
<q-card-section
|
||||||
v-if="!props.isLoading && !is_showing_graph"
|
v-if="!props.isLoading && !is_showing_graph"
|
||||||
:horizontal="$q.screen.gt.sm"
|
class="q-pa-none"
|
||||||
class="q-pa-none bg-secondary rounded-10"
|
|
||||||
>
|
>
|
||||||
<TimesheetApprovalEmployeeDetailsShifts
|
<!-- shift type color legend -->
|
||||||
:raw-data="props.employeeDetails"
|
<q-card-section class="q-py-xs q-px-none text-center q-my-s">
|
||||||
:current-pay-period="props.currentPayPeriod"
|
<q-badge
|
||||||
/>
|
v-for="shift_type in shift_type_legend"
|
||||||
|
:color="shift_type.color"
|
||||||
|
:label="shift_type.type"
|
||||||
|
:text-color="shift_type.text_color || 'white'"
|
||||||
|
class="q-px-md q-py-xs q-mx-xs q-my-none text-uppercase text-weight-bolder justify-center"
|
||||||
|
style="width: 120px; font-size: 0.8em;"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<!-- list of shifts, broken down into weekly columns -->
|
||||||
|
<q-card-section
|
||||||
|
:horizontal="$q.screen.gt.sm"
|
||||||
|
class="q-pa-none bg-secondary rounded-10"
|
||||||
|
>
|
||||||
|
<TimesheetApprovalEmployeeDetailsShifts
|
||||||
|
:raw-data="props.employeeDetails"
|
||||||
|
:current-pay-period="props.currentPayPeriod"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<!-- employee timesheet details, but look at these graphs -->
|
<!-- employee timesheet details, but look at these graphs -->
|
||||||
|
|
@ -84,32 +149,6 @@ import { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
<q-card-section class="q-pa-none col no-wrap" style="min-height: 300px;">
|
<q-card-section class="q-pa-none col no-wrap" style="min-height: 300px;">
|
||||||
<TimesheetApprovalEmployeeDetailsHoursWorkedChart
|
<TimesheetApprovalEmployeeDetailsHoursWorkedChart
|
||||||
:raw-data="props.employeeDetails"
|
:raw-data="props.employeeDetails"
|
||||||
:options="({
|
|
||||||
indexAxis: $q.screen.lt.md? 'y' : 'x',
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
labels: {
|
|
||||||
boxWidth: 15,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: t('timeSheetValidations.hoursWorkedChartTitle'),
|
|
||||||
color: '#616161'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
stacked: true,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
stacked: true,
|
|
||||||
suggestedMin: 0,
|
|
||||||
suggestedMax: 10,
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})"
|
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
|
|
@ -122,15 +161,6 @@ import { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
<q-card-section class="q-pa-none q-ma-none col-4">
|
<q-card-section class="q-pa-none q-ma-none col-4">
|
||||||
<TimesheetApprovalEmployeeDetailsShiftTypesChart
|
<TimesheetApprovalEmployeeDetailsShiftTypesChart
|
||||||
:raw-data="props.employeeOverview"
|
:raw-data="props.employeeOverview"
|
||||||
:options="({
|
|
||||||
plugins:{
|
|
||||||
legend:{
|
|
||||||
labels:{
|
|
||||||
boxWidth: 15,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})"
|
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
|
|
@ -138,33 +168,7 @@ import { PayPeriod } from 'src/modules/shared/types/pay-period-interface';
|
||||||
|
|
||||||
<q-card-section class="q-pa-none q-ma-none col" :class="$q.screen.lt.md ? 'full-width' : ''">
|
<q-card-section class="q-pa-none q-ma-none col" :class="$q.screen.lt.md ? 'full-width' : ''">
|
||||||
<TimesheetApprovalEmployeeExpensesChart
|
<TimesheetApprovalEmployeeExpensesChart
|
||||||
:style="$q.screen.lt.md ? 'min-height: 300px;': '' "
|
|
||||||
:raw-data="props.employeeDetails"
|
:raw-data="props.employeeDetails"
|
||||||
:options="({
|
|
||||||
indexAxis: $q.screen.lt.md? 'y' : 'x',
|
|
||||||
plugins: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: t('timeSheetValidations.reportFilterExpenses'),
|
|
||||||
color: '#616161'
|
|
||||||
},
|
|
||||||
legend:{
|
|
||||||
labels:{
|
|
||||||
boxWidth: 15,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
stacked: true
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
suggestedMin: 0,
|
|
||||||
suggestedMax: 100,
|
|
||||||
stacked: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})"
|
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ export interface Shift {
|
||||||
|
|
||||||
export const default_shift: Shift = {
|
export const default_shift: Shift = {
|
||||||
date: '',
|
date: '',
|
||||||
start_time: '',
|
start_time: '--:--',
|
||||||
end_time: '',
|
end_time: '--:--',
|
||||||
type: '',
|
type: '',
|
||||||
is_approved: false,
|
is_approved: false,
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user