Merge pull request 'feat(approvals): install chartJS and vue-chartJS for graphical representation of employee details, configuring bar graph.' (#10) from dev/nicolas/approvals-overview-details into main
Reviewed-on: Targo/targo_frontend#10
This commit is contained in:
commit
83ebf02182
30
package-lock.json
generated
30
package-lock.json
generated
|
|
@ -11,10 +11,12 @@
|
|||
"dependencies": {
|
||||
"@quasar/extras": "^1.17.0",
|
||||
"axios": "^1.11.0",
|
||||
"chart.js": "^4.5.0",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.4.1",
|
||||
"quasar": "^2.18.2",
|
||||
"vue": "^3.5.18",
|
||||
"vue-chartjs": "^5.3.2",
|
||||
"vue-i18n": "^11.1.11",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
|
|
@ -1173,6 +1175,12 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@kurkle/color": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
|
@ -3457,6 +3465,18 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
|
||||
"integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/check-error": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
|
||||
|
|
@ -10134,6 +10154,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-chartjs": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz",
|
||||
"integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"chart.js": "^4.1.1",
|
||||
"vue": "^3.0.0-0 || ^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-component-type-helpers": {
|
||||
"version": "2.2.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz",
|
||||
|
|
|
|||
22
package.json
22
package.json
|
|
@ -16,31 +16,33 @@
|
|||
"dependencies": {
|
||||
"@quasar/extras": "^1.17.0",
|
||||
"axios": "^1.11.0",
|
||||
"chart.js": "^4.5.0",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.4.1",
|
||||
"quasar": "^2.18.2",
|
||||
"vue": "^3.5.18",
|
||||
"vue-chartjs": "^5.3.2",
|
||||
"vue-i18n": "^11.1.11",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"cypress": "^14.5.2",
|
||||
"@eslint/js": "^9.14.0",
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
"@quasar/app-vite": "^2.1.0",
|
||||
"@types/node": "^20.5.9",
|
||||
"@vue/eslint-config-typescript": "^14.4.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"cypress": "^14.5.2",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-plugin-vue": "^10.3.0",
|
||||
"globals": "^15.12.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.1.2",
|
||||
"vitest": "^3.2.4",
|
||||
"vue-tsc": "^2.0.29",
|
||||
"@vue/eslint-config-typescript": "^14.4.0",
|
||||
"typescript": "~5.5.3",
|
||||
"vite-plugin-checker": "^0.9.0",
|
||||
"@types/node": "^20.5.9",
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
"@quasar/app-vite": "^2.1.0",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"typescript": "~5.5.3"
|
||||
"vitest": "^3.2.4",
|
||||
"vue-tsc": "^2.0.29"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^28 || ^26 || ^24 || ^22 || ^20 || ^18",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ $dark: #000;
|
|||
$dark-page: #323232;
|
||||
|
||||
$positive: #21ba45;
|
||||
$negative: #ff586c71;
|
||||
$info: #31ccec;
|
||||
$warning: #ffde82c2;
|
||||
$negative: #df6674;
|
||||
$info: #54bbdd;
|
||||
$warning: #eec964;
|
||||
$white: white;
|
||||
|
|
|
|||
|
|
@ -327,6 +327,11 @@ export default {
|
|||
consumedVacationTotalValidation: 'Consumed with vacation must be positive.',
|
||||
maxVacationPerYearValidation: 'Max Vacation Per Year must be positive.',
|
||||
resteVacationTotal: 'Rest of vacation',
|
||||
hoursWorkedChartTitle: 'Hours worked',
|
||||
hoursWorkedRegular: 'regular',
|
||||
hoursWorkedEvening: 'evening',
|
||||
hoursWorkedEmergency: 'emergency',
|
||||
hoursWorkedOvertime: 'overtime',
|
||||
tooltipTimeline: 'Daily breakdown',
|
||||
tooltipTimesheet: 'Open timesheet',
|
||||
reportFilterCategoryCompany: 'Company',
|
||||
|
|
|
|||
|
|
@ -374,6 +374,11 @@ export default {
|
|||
consumedVacationTotalValidation: 'Vacances utilisées doit être positif',
|
||||
maxVacationPerYearValidation: 'Maximum vacances annuel doit être positif.',
|
||||
resteVacationTotal: 'Reste des vacances',
|
||||
hoursWorkedChartTitle: 'Heures travaillées',
|
||||
hoursWorkedRegular: 'régulier',
|
||||
hoursWorkedEvening: 'soir',
|
||||
hoursWorkedEmergency: 'urgence',
|
||||
hoursWorkedOvertime: 'supplémentaire',
|
||||
tooltipTimeline: 'Vue journalière',
|
||||
tooltipTimesheet: 'Feuille de temps',
|
||||
reportFilterCategoryCompany: 'Compagnie',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { Bar } from 'vue-chartjs';
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, type ChartData, type ChartOptions, type Plugin } from 'chart.js';
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale);
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
data: ChartData<"bar">;
|
||||
options?: ChartOptions<"bar"> | undefined;
|
||||
plugins?: Plugin<"bar">[] | undefined;
|
||||
}>(), {
|
||||
options: () => ({}),
|
||||
plugins: () => [],
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Bar
|
||||
:data="props.data"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import type { Shift } from "src/modules/timesheets/types/timesheet-shift-interface";
|
||||
import { date } from 'quasar';
|
||||
|
||||
const total_minutes = 24 * 60;
|
||||
const props = defineProps<{
|
||||
weekdayShifts: (Shift | null)[];
|
||||
}>();
|
||||
|
||||
const toMinutes = (time: string): number => {
|
||||
const parsed_time = date.extractDate(time, 'HH:mm');
|
||||
return parsed_time.getHours() * 60 + parsed_time.getMinutes();
|
||||
};
|
||||
|
||||
const bars = computed(() => {
|
||||
return props.weekdayShifts
|
||||
.filter((s): s is Shift => s !== null) // skip null shifts
|
||||
.map((s) => {
|
||||
const start = toMinutes(s.start);
|
||||
const end = toMinutes(s.end);
|
||||
const hover = ref<boolean>(false);
|
||||
const start_percent = (start / total_minutes) * 100;
|
||||
const width_percent = ((end - start) / total_minutes) * 100;
|
||||
|
||||
return { start_percent, width_percent, s, hover };
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative bg-grey-5 rounded-10 no-wrap" style="height: 4px; margin: 6px 0;">
|
||||
<div
|
||||
v-for="(bar, index) in bars"
|
||||
:key="index"
|
||||
class="absolute bg-primary no-wrap"
|
||||
:style="{
|
||||
left: bar.start_percent + '%',
|
||||
width: bar.width_percent + '%',
|
||||
height: '10px',
|
||||
transform: 'translateY(-3px)',
|
||||
}"
|
||||
@mouseenter="bar.hover.value = true"
|
||||
@mouseleave="bar.hover.value = false"
|
||||
>
|
||||
<!-- hoverable timestamps -->
|
||||
<transition-group
|
||||
appear
|
||||
enter-active-class="animated fadeIn"
|
||||
leave-active-class="animated fadeOut"
|
||||
>
|
||||
<q-badge
|
||||
v-if="bar.hover.value"
|
||||
style="transform: translate(-110%, -40%);"
|
||||
>
|
||||
{{ bar.s.start }}
|
||||
</q-badge>
|
||||
<q-badge
|
||||
v-if="bar.hover.value"
|
||||
floating style="transform: translate(100%, 5%);"
|
||||
>
|
||||
{{ bar.s.end }}
|
||||
</q-badge>
|
||||
</transition-group>
|
||||
|
||||
<!-- total hours worked per day -->
|
||||
<!-- <q-badge> {{ bar.s.end }}</q-badge> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<!-- <script setup lang="ts">
|
||||
import type { Shift } from 'src/modules/timesheets/types/timesheet-shift-interface';
|
||||
import { date } from 'quasar';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
weekdayShifts: Shift[];
|
||||
}>();
|
||||
|
||||
const extractTimeValue = (time: string): number => {
|
||||
const parsed_time = date.extractDate(time, 'HH:mm');
|
||||
return parsed_time.getHours() + (parsed_time.getMinutes() / 60);
|
||||
};
|
||||
|
||||
const slider_model = ref<number>(0);
|
||||
const shifts = ref<Shift[]>(props.weekdayShifts);
|
||||
const ranges = ref(
|
||||
shifts.value.map( shift =>
|
||||
shift
|
||||
? {
|
||||
min: extractTimeValue(shift.start_time),
|
||||
max: extractTimeValue(shift.end_time)
|
||||
}
|
||||
: {
|
||||
min: 0,
|
||||
max: 0
|
||||
}
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative-position q-pa-none q-ma-none col">
|
||||
<div class="absolute-full row justify-between">
|
||||
<div></div>
|
||||
<q-separator vertical color="accent" />
|
||||
<q-separator vertical color="accent" />
|
||||
<q-separator vertical color="accent" />
|
||||
<q-separator vertical color="accent" />
|
||||
<q-separator vertical color="accent" />
|
||||
<div></div>
|
||||
</div>
|
||||
<q-slider
|
||||
v-model="slider_model"
|
||||
track-color="grey-6"
|
||||
track-size="8px"
|
||||
thumb-size="0px"
|
||||
:max="24"
|
||||
readonly
|
||||
class="absolute-full q-pt-xs"
|
||||
/>
|
||||
<div
|
||||
v-for="(_range, index) in ranges"
|
||||
:key="index"
|
||||
class="absolute-full"
|
||||
>
|
||||
<q-range
|
||||
v-model="ranges[index]"
|
||||
track-color="transparent"
|
||||
inner-track-color="transparent"
|
||||
color="primary"
|
||||
:max="24"
|
||||
thumb-size="0px"
|
||||
track-size="16px"
|
||||
label-color="accent"
|
||||
label-text-color="primary"
|
||||
label
|
||||
:left-label-value="shifts[index]?.start_time"
|
||||
:right-label-value="shifts[index]?.end_time"
|
||||
readonly
|
||||
class="z-top"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template> -->
|
||||
|
|
@ -1,12 +1,62 @@
|
|||
<script setup lang="ts">
|
||||
import type { PayPeriodEmployeeDetails } from '../types/timesheet-approval-pay-period-employee-details-interface';
|
||||
import ShiftPreviewBar from 'src/modules/timesheet-approval/components/shifts/shift-preview-bar.vue';
|
||||
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 { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
isLoading: boolean;
|
||||
employeeName: string;
|
||||
employeeDetails: PayPeriodEmployeeDetails | undefined;
|
||||
employeeDetails: PayPeriodEmployeeDetails | undefined;
|
||||
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>
|
||||
|
||||
<template>
|
||||
|
|
@ -27,85 +77,13 @@
|
|||
</q-card-section>
|
||||
|
||||
<!-- employee name -->
|
||||
<q-card-section class="text-h5 text-weight-bolder text-center full-width text-primary q-pt-none">
|
||||
<q-card-section v-if="!props.isLoading" class="text-h5 text-weight-bolder text-center full-width text-primary q-pt-none">
|
||||
{{ props.employeeName }}
|
||||
</q-card-section>
|
||||
|
||||
<!-- employee timesheet details -->
|
||||
<q-card-section v-if="!props.isLoading" class="q-pa-none">
|
||||
<div class="relative column col no-wrap bg-transparent">
|
||||
<div class="row text-center full-width text-grey-5 text-weight-bolder text-caption no-wrap">
|
||||
<div class="col"></div>
|
||||
<div class="">4</div>
|
||||
<div class="col"></div>
|
||||
<div class="">8</div>
|
||||
<div class="col"></div>
|
||||
<div class="">12</div>
|
||||
<div class="col"></div>
|
||||
<div class="">4</div>
|
||||
<div class="col"></div>
|
||||
<div class="">8</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
<ShiftPreviewBar
|
||||
v-for="(shifts, index) in employeeDetails?.week1.shifts"
|
||||
:key="index"
|
||||
:weekday-shifts="shifts"
|
||||
class="q-mt-xs z-top"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<q-separator class="q-mx-xl q-my-sm" style="height: 3px;"/>
|
||||
|
||||
<div class="relative column col">
|
||||
<ShiftPreviewBar
|
||||
v-for="(shifts, index) in employeeDetails?.week2.shifts"
|
||||
:key="index"
|
||||
:weekday-shifts="shifts"
|
||||
class="q-mt-xs z-top"
|
||||
/>
|
||||
<div class="row text-center full-width text-grey-5 text-caption no-wrap">
|
||||
<div class="col"></div>
|
||||
<div class="">4</div>
|
||||
<div class="col"></div>
|
||||
<div class="">8</div>
|
||||
<div class="col"></div>
|
||||
<div class="">12</div>
|
||||
<div class="col"></div>
|
||||
<div class="">4</div>
|
||||
<div class="col"></div>
|
||||
<div class="">8</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column absolute-full q-py-lg" style="z-index: 0;">
|
||||
<div class="row col">
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
<div class="row col q-mt-lg">
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
<q-separator vertical />
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</div>
|
||||
<q-card-section class="q-pa-none justify-center" :class="$q.screen.lt.lg? 'column': 'row'">
|
||||
<TimesheetApprovalEmployeeDetailsHoursWorkedChart :key="props.updateKey" :data="getHoursWorkedData()" />
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
const emit = defineEmits<{
|
||||
clickDetails: [email: string];
|
||||
'update:modelValue': [value: boolean | null];
|
||||
updateApproval: [ value: boolean ];
|
||||
}>();
|
||||
|
||||
const card_buttons: CardButton[] = [
|
||||
|
|
@ -168,7 +168,7 @@
|
|||
unchecked-icon="lock_open"
|
||||
:color="props.initialState ? 'white' : 'primary'" keep-color
|
||||
:model-value="props.initialState"
|
||||
@update:model-value="val => emit('update:modelValue', val)"
|
||||
@update:model-value="val => emit('updateApproval', val)"
|
||||
:label="props.initialState ? $t('timeSheetValidations.timeSheetStatusVerified') : $t('timeSheetValidations.timeSheetStatusUnverified')"
|
||||
class="text-uppercase"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
const report_filter_company = ref<boolean[]>([true, true]);
|
||||
const report_filter_type = ref<boolean[]>([true, true, true, true]);
|
||||
const clicked_employee_name = ref<string>('');
|
||||
const update_key = ref<number>(0);
|
||||
|
||||
const columns = computed((): QTableColumn<PayPeriodOverviewEmployee>[] => [
|
||||
{
|
||||
|
|
@ -98,6 +99,21 @@
|
|||
timesheet_store.current_pay_period.pay_period_no <= 1;
|
||||
});
|
||||
|
||||
const getEmployeeApprovalStatusReference = (email: string): boolean => {
|
||||
const approval_status = timesheet_store.pay_period_overview_employee_approval_statuses?.find( status => status.key === email);
|
||||
if (approval_status) {
|
||||
return approval_status.value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const updateEmployeeApprovalStatus = (email: string, value: boolean) => {
|
||||
const approval_status = timesheet_store.pay_period_overview_employee_approval_statuses?.find( status => status.key === email);
|
||||
if (approval_status) {
|
||||
approval_status.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
const onDateSelected = async (date_string: string) => {
|
||||
await timesheet_approval_api.getPayPeriodOverviewByDate(date_string);
|
||||
};
|
||||
|
|
@ -106,6 +122,7 @@
|
|||
clicked_employee_name.value = name;
|
||||
is_showing_details.value = true;
|
||||
await timesheet_approval_api.getTimesheetsByPayPeriodAndEmail(email);
|
||||
console.log('current employee details: ', timesheet_store.pay_period_employee_details);
|
||||
};
|
||||
|
||||
const onClickPrintReport = async () => {
|
||||
|
|
@ -125,14 +142,16 @@
|
|||
<template>
|
||||
<q-dialog
|
||||
v-model="is_showing_details"
|
||||
transition-show="scale"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-down"
|
||||
@show="() => update_key += 1"
|
||||
>
|
||||
<TimesheetApprovalEmployeeDetails
|
||||
:is-loading="timesheet_store.is_loading"
|
||||
:employee-name="clicked_employee_name"
|
||||
:employee-details="timesheet_store.pay_period_employee_details"
|
||||
style="min-width: 300px;"
|
||||
:update-key="update_key"
|
||||
class="full-width"
|
||||
/>
|
||||
</q-dialog>
|
||||
<div class="q-pa-md">
|
||||
|
|
@ -217,13 +236,15 @@
|
|||
<!-- Template for individual employee cards -->
|
||||
<template v-slot:item="props: {
|
||||
cols: (QTableColumn<PayPeriodOverviewEmployee> & { value: unknown })[],
|
||||
row: PayPeriodOverviewEmployee
|
||||
row: PayPeriodOverviewEmployee,
|
||||
key: string,
|
||||
}">
|
||||
<TimesheetApprovalEmployeeOverviewListItem
|
||||
:cols="props.cols"
|
||||
:row="props.row"
|
||||
:initial-state="props.row.is_approved"
|
||||
:initial-state="getEmployeeApprovalStatusReference(props.key)"
|
||||
@click-details="email => onClickedDetails(email, props.row.employee_name)"
|
||||
@update-approval="value => updateEmployeeApprovalStatus(props.key, value)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { TimesheetDetailsWeek } from "src/modules/timesheets/types/timesheet-details-interface";
|
||||
|
||||
export interface PayPeriodEmployeeDetails {
|
||||
// is_approved: boolean;
|
||||
week1: TimesheetDetailsWeek;
|
||||
week2: TimesheetDetailsWeek;
|
||||
};
|
||||
|
|
@ -2,8 +2,8 @@ import type { Shift } from "./timesheet-shift-interface";
|
|||
|
||||
export interface TimesheetDetailsWeek {
|
||||
is_approved: boolean;
|
||||
shifts: WeekDay<TimesheetDetailsWeekDayShifts>;
|
||||
expenses: WeekDay<TimesheetDetailsWeekDayExpenses>;
|
||||
shifts: WeekDay<TimesheetDetailsDailySchedule>;
|
||||
expenses: WeekDay<TimesheetDetailsDailyExpenses>;
|
||||
}
|
||||
|
||||
type WeekDay<T> = {
|
||||
|
|
@ -16,9 +16,17 @@ type WeekDay<T> = {
|
|||
sat: T;
|
||||
}
|
||||
|
||||
type TimesheetDetailsWeekDayShifts = Shift[];
|
||||
interface TimesheetDetailsDailySchedule {
|
||||
shifts: Shift[];
|
||||
regular_hours: number;
|
||||
evening_hours: number;
|
||||
emergency_hours: number;
|
||||
overtime_hours: number;
|
||||
short_date: string; // ex. 08/24
|
||||
break_duration?: number;
|
||||
}
|
||||
|
||||
interface TimesheetDetailsWeekDayExpenses {
|
||||
interface TimesheetDetailsDailyExpenses {
|
||||
costs: Expense[];
|
||||
mileage: Expense[];
|
||||
[otherType: string]: Expense[]; //for possible future types of expenses
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
|||
const is_loading = ref<boolean>(false);
|
||||
const current_pay_period = ref<PayPeriod>(default_pay_period);
|
||||
const pay_period_overview_employees = ref<PayPeriodOverviewEmployee[]>([]);
|
||||
const pay_period_overview_employee_approval_statuses = ref<{key: string, value: boolean}[] | undefined>();
|
||||
const pay_period_employee_details = ref<PayPeriodEmployeeDetails | undefined>();
|
||||
const pay_period_report = ref();
|
||||
|
||||
|
|
@ -74,6 +75,7 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
|||
supervisor_email
|
||||
);
|
||||
pay_period_overview_employees.value = response.employees_overview;
|
||||
pay_period_overview_employee_approval_statuses.value = response.employees_overview.map( employee => ({ key: employee.email, value: employee.is_approved }) );
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving Employee Pay Period overviews: ', error);
|
||||
pay_period_overview_employees.value = [];
|
||||
|
|
@ -122,6 +124,7 @@ export const useTimesheetStore = defineStore('timesheet', () => {
|
|||
return {
|
||||
current_pay_period,
|
||||
pay_period_overview_employees,
|
||||
pay_period_overview_employee_approval_statuses,
|
||||
pay_period_employee_details,
|
||||
is_loading,
|
||||
getPayPeriodByDate,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user