Merge pull request 'fix(details): complete chart implementation for worked hours, stacking bars, proper mobile scaling' (#11) from dev/nicolas/approvals-overview-details into main
Reviewed-on: Targo/targo_frontend#11
This commit is contained in:
commit
61d2b96c32
|
|
@ -24,7 +24,7 @@ $dark: #000;
|
||||||
$dark-page: #323232;
|
$dark-page: #323232;
|
||||||
|
|
||||||
$positive: #21ba45;
|
$positive: #21ba45;
|
||||||
$negative: #df6674;
|
$negative: #ff576ba9;
|
||||||
$info: #54bbdd;
|
$info: #54bbdd;
|
||||||
$warning: #eec964;
|
$warning: #eec964;
|
||||||
$white: white;
|
$white: white;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
import { Bar } from 'vue-chartjs';
|
import { Bar } from 'vue-chartjs';
|
||||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, type ChartData, type ChartOptions, type Plugin } from 'chart.js';
|
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 type { PayPeriodEmployeeDetails } from 'src/modules/timesheet-approval/types/timesheet-approval-pay-period-employee-details-interface';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale);
|
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale);
|
||||||
|
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
||||||
|
ChartJS.defaults.maintainAspectRatio = false;
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
data: ChartData<"bar">;
|
rawData: PayPeriodEmployeeDetails | undefined;
|
||||||
options?: ChartOptions<"bar"> | undefined;
|
options?: ChartOptions<"bar"> | undefined;
|
||||||
plugins?: Plugin<"bar">[] | undefined;
|
plugins?: Plugin<"bar">[] | undefined;
|
||||||
}>(), {
|
}>(), {
|
||||||
|
|
@ -13,11 +20,73 @@
|
||||||
plugins: () => [],
|
plugins: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const hours_worked_labels = ref<string[]>([]);
|
||||||
|
const hours_worked_dataset = ref<ChartDataset<'bar'>[]>([]);
|
||||||
|
|
||||||
|
|
||||||
|
const getHoursWorkedData = (): ChartData<'bar'> => {
|
||||||
|
if (props.rawData) {
|
||||||
|
const all_weeks = [props.rawData.week1, props.rawData.week2];
|
||||||
|
const all_days = all_weeks.flatMap( week => Object.values(week.shifts));
|
||||||
|
const regular_hours = all_days.map( day => day.regular_hours);
|
||||||
|
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 = [
|
||||||
|
{
|
||||||
|
label: t('timeSheetValidations.hoursWorkedRegular'),
|
||||||
|
data: regular_hours,
|
||||||
|
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-primary').trim(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('timeSheetValidations.hoursWorkedEvening'),
|
||||||
|
data: evening_hours,
|
||||||
|
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-info').trim(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('timeSheetValidations.hoursWorkedEmergency'),
|
||||||
|
data: emergency_hours,
|
||||||
|
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-warning').trim(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('timeSheetValidations.hoursWorkedOvertime'),
|
||||||
|
data: overtime_hours,
|
||||||
|
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-negative').trim(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
hours_worked_labels.value = all_days.map( day => day.short_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
labels: hours_worked_labels.value,
|
||||||
|
datasets: hours_worked_dataset.value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Bar
|
<Bar
|
||||||
:data="props.data"
|
:data="getHoursWorkedData()"
|
||||||
|
:options="({
|
||||||
|
indexAxis: $q.screen.lt.md? 'y' : 'x',
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: t('timeSheetValidations.hoursWorkedChartTitle'),
|
||||||
|
color: '#616161'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
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 type { ChartData, ChartDataset } from 'chart.js';
|
|
||||||
import TimesheetApprovalEmployeeDetailsHoursWorkedChart from 'src/modules/timesheet-approval/components/graphs/timesheet-approval-employee-details-hours-worked-chart.vue';
|
import TimesheetApprovalEmployeeDetailsHoursWorkedChart from 'src/modules/timesheet-approval/components/graphs/timesheet-approval-employee-details-hours-worked-chart.vue';
|
||||||
import { ref } from 'vue';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
|
@ -13,50 +8,6 @@
|
||||||
employeeDetails: PayPeriodEmployeeDetails | undefined;
|
employeeDetails: PayPeriodEmployeeDetails | undefined;
|
||||||
updateKey: number;
|
updateKey: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const hours_worked_labels = ref<string[]>([]);
|
|
||||||
const hours_worked_dataset = ref<ChartDataset<'bar'>[]>([]);
|
|
||||||
|
|
||||||
|
|
||||||
const getHoursWorkedData = (): ChartData<'bar'> => {
|
|
||||||
if (props.employeeDetails) {
|
|
||||||
const all_weeks = [props.employeeDetails.week1, props.employeeDetails.week2];
|
|
||||||
const all_days = all_weeks.flatMap( week => Object.values(week.shifts));
|
|
||||||
const regular_hours = all_days.map( day => day.regular_hours);
|
|
||||||
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 = [
|
|
||||||
{
|
|
||||||
label: t('timeSheetValidations.hoursWorkedRegular'),
|
|
||||||
data: regular_hours,
|
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-primary').trim(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('timeSheetValidations.hoursWorkedEvening'),
|
|
||||||
data: evening_hours,
|
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-info').trim(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('timeSheetValidations.hoursWorkedEmergency'),
|
|
||||||
data: emergency_hours,
|
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-warning').trim(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('timeSheetValidations.hoursWorkedOvertime'),
|
|
||||||
data: overtime_hours,
|
|
||||||
backgroundColor: getComputedStyle(document.body).getPropertyValue('--q-negative').trim(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
hours_worked_labels.value = all_days.map( day => day.short_date);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
labels: hours_worked_labels.value,
|
|
||||||
datasets: hours_worked_dataset.value,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -82,8 +33,16 @@
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<!-- employee timesheet details -->
|
<!-- employee timesheet details -->
|
||||||
<q-card-section class="q-pa-none justify-center" :class="$q.screen.lt.lg? 'column': 'row'">
|
<q-card-section
|
||||||
<TimesheetApprovalEmployeeDetailsHoursWorkedChart :key="props.updateKey" :data="getHoursWorkedData()" />
|
v-if="!props.isLoading"
|
||||||
|
class="q-pa-none justify-center"
|
||||||
|
:class="$q.screen.lt.lg? 'column': 'row'"
|
||||||
|
>
|
||||||
|
<TimesheetApprovalEmployeeDetailsHoursWorkedChart
|
||||||
|
:key="props.updateKey"
|
||||||
|
:raw-data="props.employeeDetails"
|
||||||
|
style="min-height: 300px;"
|
||||||
|
/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -122,12 +122,10 @@
|
||||||
clicked_employee_name.value = name;
|
clicked_employee_name.value = name;
|
||||||
is_showing_details.value = true;
|
is_showing_details.value = true;
|
||||||
await timesheet_approval_api.getTimesheetsByPayPeriodAndEmail(email);
|
await timesheet_approval_api.getTimesheetsByPayPeriodAndEmail(email);
|
||||||
console.log('current employee details: ', timesheet_store.pay_period_employee_details);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickPrintReport = async () => {
|
const onClickPrintReport = async () => {
|
||||||
await timesheet_approval_api.getTimesheetApprovalCSVReport(report_filter_company.value, report_filter_type.value);
|
await timesheet_approval_api.getTimesheetApprovalCSVReport(report_filter_company.value, report_filter_type.value);
|
||||||
console.log('current filter selections: [', report_filter_company.value.toLocaleString(),'], [', report_filter_type.value.toLocaleString(), ']');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted( async () => {
|
onMounted( async () => {
|
||||||
|
|
@ -144,7 +142,7 @@
|
||||||
v-model="is_showing_details"
|
v-model="is_showing_details"
|
||||||
transition-show="jump-down"
|
transition-show="jump-down"
|
||||||
transition-hide="jump-down"
|
transition-hide="jump-down"
|
||||||
@show="() => update_key += 1"
|
@before-show="() => update_key += 1"
|
||||||
>
|
>
|
||||||
<TimesheetApprovalEmployeeDetails
|
<TimesheetApprovalEmployeeDetails
|
||||||
:is-loading="timesheet_store.is_loading"
|
:is-loading="timesheet_store.is_loading"
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,13 @@ export const timesheetApprovalService = {
|
||||||
getPayPeriodEmployeeOverviews: async (year: number, period_number: number, supervisor_email: string): Promise<PayPeriodOverview> => {
|
getPayPeriodEmployeeOverviews: async (year: number, period_number: number, supervisor_email: string): Promise<PayPeriodOverview> => {
|
||||||
// TODO: REMOVE MOCK DATA PEFORE PUSHING TO PROD
|
// TODO: REMOVE MOCK DATA PEFORE PUSHING TO PROD
|
||||||
const response = await api.get(`pay-periods/${year}/${period_number}/${supervisor_email}`);
|
const response = await api.get(`pay-periods/${year}/${period_number}/${supervisor_email}`);
|
||||||
|
console.log('all employee data: ', response.data);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
getTimesheetsByPayPeriodAndEmail: async (year: number, period_no: number, email: string): Promise<PayPeriodEmployeeDetails> => {
|
getTimesheetsByPayPeriodAndEmail: async (year: number, period_no: number, email: string): Promise<PayPeriodEmployeeDetails> => {
|
||||||
const response = await api.get('timesheets', { params: { year, period_no, email, }});
|
const response = await api.get('timesheets', { params: { year, period_no, email, }});
|
||||||
|
console.log('employee details: ', response.data);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user