fix(approvals): refactor to work with staging db which has partial user and employee info.
This commit is contained in:
parent
39ce63603e
commit
82bf7d5282
|
|
@ -1,4 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script
|
||||||
|
setup
|
||||||
|
lang="ts"
|
||||||
|
>
|
||||||
import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||||
|
|
||||||
// const getEmployeeAvatar = (first_name: string, last_name: string) => {
|
// const getEmployeeAvatar = (first_name: string, last_name: string) => {
|
||||||
|
|
@ -6,8 +9,9 @@
|
||||||
// return first_name.charAt(0) + last_name.charAt(0);
|
// return first_name.charAt(0) + last_name.charAt(0);
|
||||||
// };
|
// };
|
||||||
|
|
||||||
const { row } = defineProps<{
|
const { row, index = 0 } = defineProps<{
|
||||||
row: EmployeeProfile
|
row: EmployeeProfile
|
||||||
|
index?: number
|
||||||
}>()
|
}>()
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
onProfileClick: [email: string]
|
onProfileClick: [email: string]
|
||||||
|
|
@ -15,37 +19,47 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-card
|
<transition
|
||||||
v-ripple
|
appear
|
||||||
class="column col-xs-6 col-sm-4 col-md-3 col-lg-2 no-wrap rounded-15 cursor-pointer q-ma-sm"
|
enter-active-class="animated fadeInUp slow"
|
||||||
style="max-width: 230px;"
|
mode="out-in"
|
||||||
@click="emit('onProfileClick', row.email)"
|
|
||||||
>
|
>
|
||||||
<q-card-section class="col-6 text-center">
|
<q-card
|
||||||
<q-avatar
|
v-ripple
|
||||||
color="primary"
|
class="column col-xs-6 col-sm-4 col-md-3 col-lg-2 no-wrap rounded-15 cursor-pointer q-ma-sm"
|
||||||
size="8em"
|
style="max-width: 230px;"
|
||||||
class="shadow-3 q-mb-md"
|
:style="`animation-delay: ${index / 25}s;`"
|
||||||
>
|
@click="emit('onProfileClick', row.email)"
|
||||||
<img
|
|
||||||
src="src/assets/targo-default-avatar.png"
|
|
||||||
alt="employee avatar"
|
|
||||||
class="q-pa-xs"
|
|
||||||
>
|
|
||||||
</q-avatar>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-section
|
|
||||||
class="col-grow text-center text-h6 text-weight-medium text-uppercase q-pb-none"
|
|
||||||
style="line-height: 0.8em;"
|
|
||||||
>
|
>
|
||||||
<div class="ellipsis text-primary"> {{ row.first_name }} {{ row.last_name }} </div>
|
<q-card-section class="col-6 text-center">
|
||||||
<q-separator color="primary" class="q-mx-sm q-mt-xs" />
|
<q-avatar
|
||||||
<div class=" ellipsis-2-lines text-caption"> {{ row.job_title }} </div>
|
:color="row.last_work_day === undefined ? 'accent' : 'negative'"
|
||||||
</q-card-section>
|
size="8em"
|
||||||
|
class="shadow-3 q-mb-md"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="src/assets/targo-default-avatar.png"
|
||||||
|
alt="employee avatar"
|
||||||
|
class="q-pa-xs"
|
||||||
|
>
|
||||||
|
</q-avatar>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
<q-card-section class="bg-primary text-white text-caption text-center q-py-none col-2 content-center ">
|
<q-card-section
|
||||||
<div> {{ row.email }} </div>
|
class="col-grow text-center text-h6 text-weight-medium text-uppercase q-pb-none"
|
||||||
</q-card-section>
|
style="line-height: 0.8em;"
|
||||||
</q-card>
|
>
|
||||||
|
<div class="ellipsis" :class="row.last_work_day === undefined ? 'text-accent' : 'text-negative'"> {{ row.first_name }} {{ row.last_name }} </div>
|
||||||
|
<q-separator
|
||||||
|
color="accent"
|
||||||
|
class="q-mx-sm q-mt-xs"
|
||||||
|
/>
|
||||||
|
<div class=" ellipsis-2-lines text-caption"> {{ row.job_title }} </div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-section class="bg-primary text-white text-caption text-center q-py-none col-2 content-center">
|
||||||
|
<div> {{ row.email }} </div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1,31 +1,21 @@
|
||||||
<script setup lang="ts">
|
<script
|
||||||
import { computed, onMounted, ref } from 'vue';
|
setup
|
||||||
|
lang="ts"
|
||||||
|
>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-employee-api';
|
import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-employee-api';
|
||||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import EmployeeListTableItem from 'src/modules/employee-list/components/employee-list-table-item.vue';
|
import EmployeeListTableItem from 'src/modules/employee-list/components/employee-list-table-item.vue';
|
||||||
import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
import { employee_list_columns } from 'src/modules/employee-list/models/employee-profile.models';
|
||||||
import type { QTableColumn } from 'quasar';
|
|
||||||
|
|
||||||
const employee_list_api = useEmployeeListApi();
|
const employee_list_api = useEmployeeListApi();
|
||||||
const employee_store = useEmployeeStore();
|
const employee_store = useEmployeeStore();
|
||||||
const is_loading_list = ref<boolean>(true);
|
const is_loading_list = ref<boolean>(true);
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const filter = ref("");
|
const filter = ref("");
|
||||||
const is_grid_mode = ref(true);
|
const is_grid_mode = ref(true);
|
||||||
const pagination = ref({ rowsPerPage: 0 });
|
|
||||||
|
|
||||||
const employee_list_columns = computed((): QTableColumn<EmployeeProfile>[] => [
|
onMounted(async () => {
|
||||||
{name: 'first_name', label: t('employee_list.table.first_name'), field: 'first_name', align: 'left'},
|
|
||||||
{name: 'last_name', label: t('employee_list.table.last_name'), field: 'last_name', align: 'left'},
|
|
||||||
{name: 'email', label: t('employee_list.table.email'), field: 'email', align: 'left'},
|
|
||||||
{name: 'supervisor_full_name', label: t('employee_list.table.supervisor'), field: 'supervisor_full_name', align: 'left'},
|
|
||||||
{name: 'company_name', label: t('employee_list.table.company'), field: 'company_name', align: 'left'},
|
|
||||||
{name: 'job_title', label: t('employee_list.table.role'), field: 'job_title', align: 'left'},
|
|
||||||
]);
|
|
||||||
|
|
||||||
onMounted( async () => {
|
|
||||||
is_loading_list.value = true;
|
is_loading_list.value = true;
|
||||||
await employee_list_api.getEmployeeList();
|
await employee_list_api.getEmployeeList();
|
||||||
is_loading_list.value = false;
|
is_loading_list.value = false;
|
||||||
|
|
@ -41,18 +31,17 @@
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
title=" "
|
title=" "
|
||||||
card-style="max-height: 70vh;"
|
card-style="max-height: 70vh;"
|
||||||
:rows="employee_store.employeeList"
|
:rows="employee_store.employee_list"
|
||||||
:columns="employee_list_columns"
|
:columns="employee_list_columns"
|
||||||
row-key="name"
|
row-key="name"
|
||||||
v-model:pagination="pagination"
|
|
||||||
:rows-per-page-options="[0]"
|
:rows-per-page-options="[0]"
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
class="q-pa-md bg-transparent"
|
class="q-pa-md bg-transparent"
|
||||||
:class="is_grid_mode ? '': 'sticky-header-table'"
|
:class="is_grid_mode ? '' : 'sticky-header-table'"
|
||||||
:style="$q.screen.lt.md ? '' : 'width: 80vw;'"
|
:style="$q.screen.lt.md ? '' : 'width: 80vw;'"
|
||||||
:table-class="$q.dark.isActive ? 'q-px-md q-py-none q-mx-md rounded-10 bg-dark' : 'q-px-md q-py-none q-mx-md rounded-10 bg-white'"
|
:table-class="$q.dark.isActive ? 'q-px-md q-py-none q-mx-md rounded-10 bg-dark' : 'q-px-md q-py-none q-mx-md rounded-10 bg-white'"
|
||||||
color="primary"
|
color="accent"
|
||||||
table-header-class="text-primary text-uppercase"
|
table-header-class="text-accent text-uppercase"
|
||||||
card-container-class="justify-center"
|
card-container-class="justify-center"
|
||||||
:grid="is_grid_mode"
|
:grid="is_grid_mode"
|
||||||
:loading="is_loading_list"
|
:loading="is_loading_list"
|
||||||
|
|
@ -61,48 +50,69 @@
|
||||||
:loading-label="$t('shared.label.loading')"
|
:loading-label="$t('shared.label.loading')"
|
||||||
@row-click="() => console.log('click!')"
|
@row-click="() => console.log('click!')"
|
||||||
>
|
>
|
||||||
|
<template #header="props">
|
||||||
|
<q-tr
|
||||||
|
:props="props"
|
||||||
|
class="bg-accent"
|
||||||
|
>
|
||||||
|
<q-th
|
||||||
|
v-for="col in props.cols"
|
||||||
|
:key="col.name"
|
||||||
|
:props="props"
|
||||||
|
>
|
||||||
|
<span class="text-uppercase text-weight-bolder text-white">
|
||||||
|
{{ $t(col.label) }}
|
||||||
|
</span>
|
||||||
|
</q-th>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-slot:item="props">
|
<template v-slot:item="props">
|
||||||
<EmployeeListTableItem :row="props.row"/>
|
<EmployeeListTableItem
|
||||||
|
:row="props.row"
|
||||||
|
:index="props.rowIndex"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:top>
|
<template v-slot:top>
|
||||||
<div class="row full-width q-mb-sm">
|
<div class="row full-width q-mb-sm">
|
||||||
<q-btn
|
<q-btn
|
||||||
push
|
push
|
||||||
color="primary"
|
color="accent"
|
||||||
icon="person_add"
|
icon="person_add"
|
||||||
:label="$t('shared.label.add')"
|
:label="$t('shared.label.add')"
|
||||||
class="text-uppercase"
|
class="text-uppercase"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-space />
|
<q-space />
|
||||||
|
|
||||||
<q-btn-toggle
|
<q-btn-toggle
|
||||||
v-model="is_grid_mode"
|
v-model="is_grid_mode"
|
||||||
push
|
push
|
||||||
color="white"
|
rounded
|
||||||
text-color="primary"
|
color="white"
|
||||||
toggle-color="primary"
|
text-color="accent"
|
||||||
class="q-mr-md"
|
toggle-color="accent"
|
||||||
|
class="q-mr-md"
|
||||||
:options="[
|
:options="[
|
||||||
{icon: 'grid_view', value: true},
|
{ icon: 'grid_view', value: true },
|
||||||
{icon: 'view_list', value: false},
|
{ icon: 'view_list', value: false },
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<q-input
|
<q-input
|
||||||
v-model="filter"
|
v-model="filter"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
rounded
|
rounded
|
||||||
color="primary"
|
color="accent"
|
||||||
bg-color="white"
|
bg-color="white"
|
||||||
label-color="primary"
|
label-color="accent"
|
||||||
:label="$t('shared.label.search')"
|
:label="$t('shared.label.search')"
|
||||||
>
|
>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<q-icon
|
<q-icon
|
||||||
name="search"
|
name="search"
|
||||||
color="primary"
|
color="accent"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
@ -111,13 +121,13 @@
|
||||||
|
|
||||||
<!-- Template for custome failed-to-load state -->
|
<!-- Template for custome failed-to-load state -->
|
||||||
<template v-slot:no-data="{ message, filter }">
|
<template v-slot:no-data="{ message, filter }">
|
||||||
<div class="full-width column items-center text-primary q-gutter-sm">
|
<div class="full-width column items-center text-accent q-gutter-sm">
|
||||||
<span class="text-h6 q-mt-xl">
|
<span class="text-h6 q-mt-xl">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</span>
|
</span>
|
||||||
<q-icon
|
<q-icon
|
||||||
size="4em"
|
size="4em"
|
||||||
:name="filter ? 'filter_alt_off' : 'error_outline'"
|
:name="filter ? 'filter_alt_off' : 'error_outline'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import type { QTableColumn } from "quasar";
|
||||||
|
|
||||||
export interface EmployeeProfile {
|
export interface EmployeeProfile {
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
|
|
@ -24,4 +26,43 @@ export const default_employee_profile: EmployeeProfile = {
|
||||||
last_work_day: '',
|
last_work_day: '',
|
||||||
residence: '',
|
residence: '',
|
||||||
birth_date: '',
|
birth_date: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const employee_list_columns: QTableColumn<EmployeeProfile>[] = [
|
||||||
|
{
|
||||||
|
name: 'first_name',
|
||||||
|
label: 'employee_list.table.first_name',
|
||||||
|
field: 'first_name',
|
||||||
|
align: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'last_name',
|
||||||
|
label: 'employee_list.table.last_name',
|
||||||
|
field: 'last_name',
|
||||||
|
align: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'email',
|
||||||
|
label: 'employee_list.table.email',
|
||||||
|
field: 'email',
|
||||||
|
align: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'supervisor_full_name',
|
||||||
|
label: 'employee_list.table.supervisor',
|
||||||
|
field: 'supervisor_full_name',
|
||||||
|
align: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'company_name',
|
||||||
|
label: 'employee_list.table.company',
|
||||||
|
field: 'company_name',
|
||||||
|
align: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'job_title',
|
||||||
|
label: 'employee_list.table.role',
|
||||||
|
field: 'job_title',
|
||||||
|
align: 'left'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -3,8 +3,8 @@ import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-
|
||||||
|
|
||||||
export const EmployeeListService = {
|
export const EmployeeListService = {
|
||||||
getEmployeeList: async (): Promise<EmployeeProfile[]> => {
|
getEmployeeList: async (): Promise<EmployeeProfile[]> => {
|
||||||
const response = await api.get<EmployeeProfile[]>('/employees/employee-list')
|
const response = await api.get<{success: boolean, data: EmployeeProfile[], error?: string }>('/employees/employee-list')
|
||||||
return response.data;
|
return response.data.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
getEmployeeDetails: async (email: string): Promise<EmployeeProfile> => {
|
getEmployeeDetails: async (email: string): Promise<EmployeeProfile> => {
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,12 @@
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, 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 ChartData, type ChartDataset } from 'chart.js';
|
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale, type ChartDataset } from 'chart.js';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
@ -27,18 +25,20 @@
|
||||||
const expenses_dataset = ref<ChartDataset<'bar'>[]>([]);
|
const expenses_dataset = ref<ChartDataset<'bar'>[]>([]);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
expenses_dataset.value = [
|
setTimeout(() => {
|
||||||
{
|
expenses_dataset.value = [
|
||||||
label: t('timesheet_approvals.table.expenses'),
|
{
|
||||||
data: all_days.map(day => (day.expenses + day.on_call + day.per_diem)),
|
label: t('timesheet_approvals.table.expenses'),
|
||||||
backgroundColor: colors.getPaletteColor('accent'),
|
data: all_days.map(day => (day.expenses + day.on_call + day.per_diem)),
|
||||||
},
|
backgroundColor: colors.getPaletteColor('accent'),
|
||||||
{
|
},
|
||||||
label: t('timesheet_approvals.table.mileage'),
|
{
|
||||||
data: all_days.map(day => day.mileage),
|
label: t('timesheet_approvals.table.mileage'),
|
||||||
backgroundColor: colors.getPaletteColor('info'),
|
data: all_days.map(day => day.mileage),
|
||||||
}
|
backgroundColor: colors.getPaletteColor('info'),
|
||||||
]
|
}
|
||||||
|
]
|
||||||
|
}, 100)
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -55,7 +55,6 @@
|
||||||
:options="({
|
:options="({
|
||||||
indexAxis: $q.screen.lt.md ? 'y' : 'x',
|
indexAxis: $q.screen.lt.md ? 'y' : 'x',
|
||||||
plugins: {
|
plugins: {
|
||||||
|
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: t('timesheet_approvals.chart.expenses_title'),
|
text: t('timesheet_approvals.chart.expenses_title'),
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { colors, useQuasar } from 'quasar';
|
import { colors, useQuasar } from 'quasar';
|
||||||
import { Bar } from 'vue-chartjs';
|
import { Bar } from 'vue-chartjs';
|
||||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, type ChartData, type ChartDataset } from 'chart.js';
|
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale, type ChartDataset } 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';
|
||||||
|
|
||||||
|
|
@ -19,14 +19,14 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
||||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale);
|
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, TimeScale);
|
||||||
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
ChartJS.defaults.font.family = '"Roboto", sans-serif';
|
||||||
// ChartJS.defaults.maintainAspectRatio = false;
|
ChartJS.defaults.maintainAspectRatio = false;
|
||||||
ChartJS.defaults.color = $q.dark.isActive ? '#F5F5F5' : '#616161';
|
ChartJS.defaults.color = $q.dark.isActive ? '#F5F5F5' : '#616161';
|
||||||
|
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
||||||
const all_days = computed(() => timesheet_store.timesheets.flatMap(week => week.days));
|
const all_days = timesheet_store.timesheets.flatMap(week => week.days);
|
||||||
|
|
||||||
const datasetConfig: ChartConfigHoursWorked[] = [
|
const datasetConfig: ChartConfigHoursWorked[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -42,36 +42,38 @@
|
||||||
{
|
{
|
||||||
key: 'emergency',
|
key: 'emergency',
|
||||||
label: t('shared.shift_type.emergency'),
|
label: t('shared.shift_type.emergency'),
|
||||||
color: getComputedStyle(document.body).getPropertyValue('--q-warning').trim(),
|
color: colors.getPaletteColor('warning'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'overtime',
|
key: 'overtime',
|
||||||
label: t('shared.shift_type.overtime'),
|
label: t('shared.shift_type.overtime'),
|
||||||
color: getComputedStyle(document.body).getPropertyValue('--q-negative').trim(),
|
color: colors.getPaletteColor('negative'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const hours_worked_labels = ref<string[]>(all_days.value.map(day => day.date.slice(-5,)));
|
const hours_worked_labels = ref<string[]>(all_days.map(day => day.date.slice(-5,)));
|
||||||
const hours_worked_dataset = ref<ChartDataset<'bar'>[]>([]);
|
const hours_worked_dataset = ref<ChartDataset<'bar'>[]>([]);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
hours_worked_dataset.value = datasetConfig.map(cfg => ({
|
setTimeout(() => {
|
||||||
label: cfg.label,
|
hours_worked_dataset.value = datasetConfig.map(cfg => ({
|
||||||
data: all_days.value.map(day => day.daily_hours[cfg.key]),
|
label: cfg.label,
|
||||||
backgroundColor: cfg.color,
|
data: all_days.map(day => day.daily_hours[cfg.key]),
|
||||||
}));
|
backgroundColor: cfg.color,
|
||||||
|
}));
|
||||||
|
}, 100);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="bg-dark rounded-10 q-pa-sm"
|
class="bg-dark rounded-10 q-pa-sm"
|
||||||
:style="`min-height: ${$q.screen.lt.md ? '450px;' : '200px'}`"
|
:style="`min-height: ${$q.screen.lt.md ? '350px;' : '200px'}`"
|
||||||
>
|
>
|
||||||
<Bar
|
<Bar
|
||||||
:data="{
|
:data="{
|
||||||
labels: hours_worked_labels,
|
|
||||||
datasets: hours_worked_dataset,
|
datasets: hours_worked_dataset,
|
||||||
|
labels: hours_worked_labels,
|
||||||
}"
|
}"
|
||||||
:options="({
|
:options="({
|
||||||
indexAxis: $q.screen.lt.md ? 'y' : 'x',
|
indexAxis: $q.screen.lt.md ? 'y' : 'x',
|
||||||
|
|
|
||||||
|
|
@ -29,20 +29,23 @@
|
||||||
const shift_type_totals = ref<ChartDataset<'doughnut'>[]>([]);
|
const shift_type_totals = ref<ChartDataset<'doughnut'>[]>([]);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
shift_type_totals.value = [{
|
setTimeout(() => {
|
||||||
data: [
|
shift_type_totals.value = [{
|
||||||
timesheet_store.current_pay_period_overview!.regular_hours,
|
data: [
|
||||||
timesheet_store.current_pay_period_overview!.other_hours.evening_hours,
|
timesheet_store.current_pay_period_overview!.regular_hours,
|
||||||
timesheet_store.current_pay_period_overview!.other_hours.emergency_hours,
|
timesheet_store.current_pay_period_overview!.other_hours.evening_hours,
|
||||||
timesheet_store.current_pay_period_overview!.other_hours.overtime_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
|
backgroundColor: [
|
||||||
colors.getPaletteColor('green-10'), // Evening
|
colors.getPaletteColor('accent'), // Regular
|
||||||
colors.getPaletteColor('warning'), // Emergency
|
colors.getPaletteColor('green-10'), // Evening
|
||||||
colors.getPaletteColor('negative'), // Overtime
|
colors.getPaletteColor('warning'), // Emergency
|
||||||
]
|
colors.getPaletteColor('negative'), // Overtime
|
||||||
}]
|
]
|
||||||
|
}]
|
||||||
|
|
||||||
|
}, 100);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import type { PayPeriodOverviewResponse } from "src/modules/timesheet-approval/m
|
||||||
|
|
||||||
export const timesheetApprovalService = {
|
export const timesheetApprovalService = {
|
||||||
getPayPeriodOverviews: async (year: number, period_number: number): Promise<PayPeriodOverviewResponse> => {
|
getPayPeriodOverviews: async (year: number, period_number: number): Promise<PayPeriodOverviewResponse> => {
|
||||||
const response = await api.get(`pay-periods/crew/${year}/${period_number}`);
|
const response = await api.get(`pay-periods/overview/${year}/${period_number}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { default_employee_profile, type EmployeeProfile } from "src/modules/empl
|
||||||
|
|
||||||
export const useEmployeeStore = defineStore('employee', () => {
|
export const useEmployeeStore = defineStore('employee', () => {
|
||||||
const employee = ref<EmployeeProfile>( default_employee_profile );
|
const employee = ref<EmployeeProfile>( default_employee_profile );
|
||||||
const employeeList = ref<EmployeeProfile[]>([]);
|
const employee_list = ref<EmployeeProfile[]>([]);
|
||||||
const isShowingEmployeeAddModifyWindow = ref<boolean>(false);
|
const isShowingEmployeeAddModifyWindow = ref<boolean>(false);
|
||||||
const isLoadingEmployeeProfile = ref(false);
|
const isLoadingEmployeeProfile = ref(false);
|
||||||
const isLoadingEmployeeList = ref(false);
|
const isLoadingEmployeeList = ref(false);
|
||||||
|
|
@ -14,7 +14,7 @@ export const useEmployeeStore = defineStore('employee', () => {
|
||||||
isLoadingEmployeeList.value = true;
|
isLoadingEmployeeList.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await EmployeeListService.getEmployeeList();
|
const response = await EmployeeListService.getEmployeeList();
|
||||||
employeeList.value = response;
|
employee_list.value = response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Ran into an error fetching employee list: ", error);
|
console.error("Ran into an error fetching employee list: ", error);
|
||||||
//TODO: trigger an alert window with an error message here!
|
//TODO: trigger an alert window with an error message here!
|
||||||
|
|
@ -35,6 +35,6 @@ export const useEmployeeStore = defineStore('employee', () => {
|
||||||
isLoadingEmployeeProfile.value = false;
|
isLoadingEmployeeProfile.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { employee, employeeList, isShowingEmployeeAddModifyWindow, isLoadingEmployeeList, isLoadingEmployeeProfile, getEmployeeList, getEmployeeDetails };
|
return { employee, employee_list, isShowingEmployeeAddModifyWindow, isLoadingEmployeeList, isLoadingEmployeeProfile, getEmployeeList, getEmployeeDetails };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user