fix(timesheet-approval): overview list now updates when closing details dialog.
Graphs at top of details dialog now update dynamically if user makes changes to employee timesheet.
This commit is contained in:
parent
1cac8966be
commit
b0de761645
|
|
@ -2,12 +2,12 @@
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { Bar } from 'vue-chartjs';
|
import { Bar } from 'vue-chartjs';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar, colors } from 'quasar';
|
import { useQuasar, colors } from 'quasar';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale, type ChartDataset } from 'chart.js';
|
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale } from 'chart.js';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
@ -19,27 +19,21 @@
|
||||||
|
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
||||||
const all_days = timesheet_store.timesheets.flatMap(week => week.days.flatMap(day => day.daily_expenses));
|
const all_days = computed(() => timesheet_store.timesheets.flatMap(week => week.days.flatMap(day => day.daily_expenses)));
|
||||||
|
|
||||||
const expenses_labels = ref<string[]>(timesheet_store.timesheets.flatMap(week => week.days.map(day => day.date.slice(-5,))));
|
const expenses_labels = ref<string[]>(timesheet_store.timesheets.flatMap(week => week.days.map(day => day.date.slice(-5,))));
|
||||||
const expenses_dataset = ref<ChartDataset<'bar'>[]>([]);
|
const expenses_dataset = computed(() => [
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
expenses_dataset.value = [
|
|
||||||
{
|
{
|
||||||
label: t('timesheet_approvals.table.expenses'),
|
label: t('timesheet_approvals.table.expenses'),
|
||||||
data: all_days.map(day => (day.expenses + day.on_call + day.per_diem)),
|
data: all_days.value.map(day => (day.expenses + day.on_call + day.per_diem)),
|
||||||
backgroundColor: colors.getPaletteColor('accent'),
|
backgroundColor: colors.getPaletteColor('accent'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('timesheet_approvals.table.mileage'),
|
label: t('timesheet_approvals.table.mileage'),
|
||||||
data: all_days.map(day => day.mileage),
|
data: all_days.value.map(day => day.mileage),
|
||||||
backgroundColor: colors.getPaletteColor('info'),
|
backgroundColor: colors.getPaletteColor('info'),
|
||||||
}
|
}
|
||||||
]
|
]);
|
||||||
}, 100)
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,17 @@
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { colors, date, useQuasar } from 'quasar';
|
import { colors, date, useQuasar } from 'quasar';
|
||||||
import { Bar } from 'vue-chartjs';
|
import { Bar } from 'vue-chartjs';
|
||||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale, type ChartDataset } from 'chart.js';
|
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale } from 'chart.js';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import type { TotalHours } from 'src/modules/timesheets/models/timesheet.models';
|
import type { TotalHours } from 'src/modules/timesheets/models/timesheet.models';
|
||||||
import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-utils';
|
import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-utils';
|
||||||
|
|
||||||
interface ChartConfigHoursWorked {
|
interface ChartConfigHoursWorked {
|
||||||
key: keyof Pick<TotalHours, 'regular' | 'evening' | 'emergency' | 'overtime'| 'vacation' | 'holiday'>;
|
key: keyof Pick<TotalHours, 'regular' | 'evening' | 'emergency' | 'overtime' | 'vacation' | 'holiday'>;
|
||||||
label: string;
|
label: string;
|
||||||
color: string;
|
color: string;
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
|
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
||||||
const all_days = timesheet_store.timesheets.flatMap(week => week.days);
|
const all_days = computed(() => timesheet_store.timesheets.flatMap(week => week.days));
|
||||||
|
|
||||||
const datasetConfig: ChartConfigHoursWorked[] = [
|
const datasetConfig: ChartConfigHoursWorked[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -62,18 +62,12 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const hours_worked_labels = ref<string[]>(all_days.map(day => day.date.slice(-5,)));
|
const hours_worked_labels = ref<string[]>(all_days.value.map(day => day.date.slice(-5,)));
|
||||||
const hours_worked_dataset = ref<ChartDataset<'bar'>[]>([]);
|
const hours_worked_datasets = computed(() => datasetConfig.map(cfg => ({
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
hours_worked_dataset.value = datasetConfig.map(cfg => ({
|
|
||||||
label: cfg.label,
|
label: cfg.label,
|
||||||
data: all_days.map(day => day.daily_hours[cfg.key]),
|
data: all_days.value.map(day => day.daily_hours[cfg.key]),
|
||||||
backgroundColor: cfg.color,
|
backgroundColor: cfg.color,
|
||||||
}));
|
})))
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -83,7 +77,7 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
>
|
>
|
||||||
<Bar
|
<Bar
|
||||||
:data="{
|
:data="{
|
||||||
datasets: hours_worked_dataset,
|
datasets: hours_worked_datasets,
|
||||||
labels: hours_worked_labels,
|
labels: hours_worked_labels,
|
||||||
}"
|
}"
|
||||||
:options="({
|
:options="({
|
||||||
|
|
@ -92,7 +86,7 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
title: function (context) {
|
title: function (context) {
|
||||||
return $d(date.extractDate(`2025-${context[0]!.label}`, 'YYYY-MM-DD'), {month: 'long', day: 'numeric'});
|
return $d(date.extractDate(`2025-${context[0]!.label}`, 'YYYY-MM-DD'), { month: 'long', day: 'numeric' });
|
||||||
},
|
},
|
||||||
label: function (context) {
|
label: function (context) {
|
||||||
return getHoursMinutesStringFromHoursFloat(context.parsed.y);
|
return getHoursMinutesStringFromHoursFloat(context.parsed.y);
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
/* eslint-disable */
|
import { computed } from 'vue';
|
||||||
import { onMounted, ref } from 'vue';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { colors } from 'quasar';
|
import { colors } from 'quasar';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { Doughnut } from 'vue-chartjs';
|
import { Doughnut } from 'vue-chartjs';
|
||||||
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale, LinearScale, type ChartDataset } from 'chart.js';
|
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale, LinearScale } from 'chart.js';
|
||||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||||
import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-utils';
|
import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-utils';
|
||||||
|
import type { Timesheet, TotalHours } from 'src/modules/timesheets/models/timesheet.models';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
@ -22,33 +22,27 @@
|
||||||
|
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
||||||
const shift_type_labels = ref<string[]>([
|
const TRACKABLE_SHIFT_TYPES: (keyof TotalHours)[] = ['regular', 'evening', 'emergency', 'overtime', 'holiday', 'vacation']
|
||||||
|
|
||||||
|
const SHIFT_TYPE_LABELS = [
|
||||||
t('shared.shift_type.regular'),
|
t('shared.shift_type.regular'),
|
||||||
t('shared.shift_type.evening'),
|
t('shared.shift_type.evening'),
|
||||||
t('shared.shift_type.emergency'),
|
t('shared.shift_type.emergency'),
|
||||||
t('shared.shift_type.overtime'),
|
t('shared.shift_type.overtime'),
|
||||||
]);
|
t('shared.shift_type.holiday'),
|
||||||
|
t('shared.shift_type.vacation'),
|
||||||
|
];
|
||||||
|
|
||||||
const shift_type_totals = ref<ChartDataset<'doughnut'>[]>([]);
|
const shift_type_data = computed(() => {
|
||||||
|
const initial_totals = new Array(TRACKABLE_SHIFT_TYPES.length).fill(0);
|
||||||
|
|
||||||
onMounted(() => {
|
return timesheet_store.timesheets.reduce((accumulator: number[], timesheet: Timesheet) => {
|
||||||
setTimeout(() => {
|
TRACKABLE_SHIFT_TYPES.forEach( (trackable_shift_type, index) => {
|
||||||
shift_type_totals.value = [{
|
accumulator[index]! += timesheet.weekly_hours[trackable_shift_type] ?? 0;
|
||||||
data: [
|
});
|
||||||
timesheet_store.current_pay_period_overview!.regular_hours,
|
|
||||||
timesheet_store.current_pay_period_overview!.other_hours.evening_hours,
|
|
||||||
timesheet_store.current_pay_period_overview!.other_hours.emergency_hours,
|
|
||||||
timesheet_store.current_pay_period_overview!.other_hours.overtime_hours,
|
|
||||||
],
|
|
||||||
backgroundColor: [
|
|
||||||
colors.getPaletteColor('accent'), // Regular
|
|
||||||
colors.getPaletteColor('green-10'), // Evening
|
|
||||||
colors.getPaletteColor('warning'), // Emergency
|
|
||||||
colors.getPaletteColor('negative'), // Overtime
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
|
|
||||||
}, 100);
|
return accumulator;
|
||||||
|
}, initial_totals);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -59,8 +53,16 @@
|
||||||
>
|
>
|
||||||
<Doughnut
|
<Doughnut
|
||||||
:data="{
|
:data="{
|
||||||
labels: shift_type_labels,
|
labels: SHIFT_TYPE_LABELS,
|
||||||
datasets: shift_type_totals,
|
datasets: [{
|
||||||
|
data: shift_type_data,
|
||||||
|
backgroundColor: [
|
||||||
|
colors.getPaletteColor('accent'), // Regular
|
||||||
|
colors.getPaletteColor('green-10'), // Evening
|
||||||
|
colors.getPaletteColor('warning'), // Emergency
|
||||||
|
colors.getPaletteColor('negative'), // Overtime
|
||||||
|
]
|
||||||
|
}],
|
||||||
}"
|
}"
|
||||||
:options="({
|
:options="({
|
||||||
plugins: {
|
plugins: {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
backdrop-filter="blur(6px)"
|
backdrop-filter="blur(6px)"
|
||||||
@show="is_dialog_open = true"
|
@show="is_dialog_open = true"
|
||||||
@hide="is_dialog_open = false"
|
@hide="is_dialog_open = false"
|
||||||
|
@before-hide="timesheet_store.getTimesheetOverviews"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="column bg-secondary hide-scrollbar shadow-12 rounded-15 q-pa-sm no-wrap"
|
class="column bg-secondary hide-scrollbar shadow-12 rounded-15 q-pa-sm no-wrap"
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,6 @@
|
||||||
expense_selected.value = expense_options.find(expense_option => expense_option.value === expense.value.type);
|
expense_selected.value = expense_options.find(expense_option => expense_option.value === expense.value.type);
|
||||||
else
|
else
|
||||||
expense_selected.value = expense_options[1];
|
expense_selected.value = expense_options[1];
|
||||||
|
|
||||||
console.log('expense mount triggered: current expense type is ', expenses_store.current_expense.type, ', matching option is: ', expense_selected.value);
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user