Add timeframe selection and improve goal progress calculations in DepartmentGoals.vue

- Introduced timeframe selection tabs for viewing goals by different periods.
- Updated `getGoalProgress` and department progress calculations to incorporate selected timeframe.
- Enhanced goal progress display to reflect accurate departmental and overall progress based on current timeframe.
This commit is contained in:
Jeppe Bundgaard
2026-02-25 15:41:01 +01:00
parent 732707cc7e
commit 745d0787ab
@@ -16,6 +16,10 @@ const loading = ref(true);
const activeTab = ref('active');
const toast = useToast();
const timeframe = ref('all');
const currentTimeframe = computed(() => timeframe.value);
const fetchGoals = async () => {
loading.value = true;
try {
@@ -79,10 +83,18 @@ const getGoalIconColor = (type) => {
}
};
const getGoalProgress = (goal) => {
const getGoalProgress = (goal, deptId = null) => {
const key = currentTimeframe.value;
// Use the .value to divided by .target and multiply by 100
const value = parseFloat(goal.progress?.all?.count);
const target = parseFloat(goal.progress?.all?.target);
if (deptId && goal.progress?.departmental_distribution?.[deptId]?.[key]) {
const deptValue = parseFloat(goal.progress.departmental_distribution[deptId][key].count);
const deptTarget = getDailyGoalDepartmentTarget(goal, deptId, true);
if (isNaN(deptValue) || isNaN(deptTarget) || deptTarget === 0) return 0;
const deptProgress = (deptValue / deptTarget) * 100;
return Math.min(Math.round(deptProgress), 100);
}
const value = parseFloat(goal.progress?.[key]?.count);
const target = parseFloat(goal.progress?.[key]?.target);
if (isNaN(value) || isNaN(target) || target === 0) return 0;
const progress = (value / target) * 100;
return Math.min(Math.round(progress), 100);
@@ -162,9 +174,13 @@ const getDailyGoalDepartmentTarget = (goal, deptId, progressUntilNow = false) =>
return isNaN(x) ? 0 : x;
};
const getEvenSplitPerDay = () => {
if (daysTotal === 0) return goal.criteria.target; // Avoid division by zero
if (daysTotal === 0) return goal.progress?.departmental_distribution?.[deptId]?.[currentTimeframe]?.target || 0;
const deptCount = Math.max(1, goal.departments.length || 1);
return (normalizeInt(goal.criteria.target) / deptCount) / daysTotal;
// If the department has a custom daily target, use that, otherwise use the even split
if (hasOverride) {
return normalizeInt(overrides[String(deptId)]);
}
return (normalizeInt(goal.progress?.departmental_distribution?.[deptId]?.[currentTimeframe]?.target) || (normalizeInt(goal.criteria.target) / deptCount)) / daysTotal;
};
const dailyTarget = hasOverride ? normalizeInt(overrides[String(deptId)]) : getEvenSplitPerDay();
@@ -255,6 +271,36 @@ const getGoalApplicableDaysInPeriod = (goal, fromDate, toDate) => {
</ul>
</div>
<!-- Timeframe -->
<div class="tabs is-toggle is-fullwidth mb-5">
<ul>
<li :class="{ 'is-active': timeframe === 'all' }" @click="timeframe = 'all'">
<a>
<span class="icon is-small"><i class="fas fa-list"></i></span>
<span>Alle</span>
</a>
</li>
<li :class="{ 'is-active': timeframe === 'today' }" @click="timeframe = 'today'">
<a>
<span class="icon is-small"><i class="fas fa-calendar-day"></i></span>
<span>I dag</span>
</a>
</li>
<li :class="{ 'is-active': timeframe === 'week' }" @click="timeframe = 'week'">
<a>
<span class="icon is-small"><i class="fas fa-calendar-week"></i></span>
<span>Denne uge</span>
</a>
</li>
<li :class="{ 'is-active': timeframe === 'month' }" @click="timeframe = 'month'">
<a>
<span class="icon is-small"><i class="fas fa-calendar-alt"></i></span>
<span>Denne måned</span>
</a>
</li>
</ul>
</div>
<!-- Content -->
<div v-if="loading" class="section has-text-centered py-6">
<span class="icon is-large">
@@ -317,12 +363,12 @@ const getGoalApplicableDaysInPeriod = (goal, fromDate, toDate) => {
<div class="mb-5">
<div class="is-flex is-justify-content-space-between mb-2">
<span class="is-size-7 has-text-weight-semibold">Fremskridt</span>
<span class="is-size-7 has-text-weight-bold"><small>{{ goal.progress.all?.count || 0 }} / {{ goal.criteria.target }}</small> - <strong>{{ getGoalProgress(goal) }}%</strong></span>
<span class="is-size-7 has-text-weight-bold"><small>{{ goal.progress[currentTimeframe]?.count || 0 }} / {{ Math.round(goal.progress[currentTimeframe]?.target || goal.progress[currentTimeframe]?.target || goal.criteria.target) }}</small> - <strong>{{ getGoalProgress(goal) }}%</strong></span>
</div>
<progress
class="progress is-small mb-0"
:value="goal.progress.all?.count || 0"
:max="goal.criteria.target"
:value="goal.progress[currentTimeframe]?.count || 0"
:max="goal.progress[currentTimeframe]?.target || goal.criteria.target"
:class="getProgressClass(getGoalProgress(goal))"
>
{{ getGoalProgress(goal) }}%
@@ -332,7 +378,7 @@ const getGoalApplicableDaysInPeriod = (goal, fromDate, toDate) => {
<div class="columns is-mobile">
<div class="column">
<p class="heading mb-1">Mål</p>
<p class="subtitle is-6 has-text-weight-bold">{{ goal.criteria.type === 'REVENUE' ? SessionUser.functions.currency.toLocal(goal.criteria.target) : goal.criteria.target }}</p>
<p class="subtitle is-6 has-text-weight-bold">{{ goal.criteria.type === 'REVENUE' ? SessionUser.functions.currency.toLocal(goal.progress[currentTimeframe]?.target || goal.criteria.target) : goal.progress[currentTimeframe]?.target || goal.criteria.target }}</p>
</div>
<div class="column">
<p class="heading mb-1">Periode</p>
@@ -349,20 +395,20 @@ const getGoalApplicableDaysInPeriod = (goal, fromDate, toDate) => {
<b-tooltip position="is-left" type="is-dark">
<template v-slot:content>
<div>
<!-- Until today -->
<div class="divider mt-0">Fremskridt til dd.</div>
<!-- Progress in selected timeframe for this department -->
<div class="divider mt-0">Valgt tidsramme</div>
<!-- Departmental progress and then % with 2 decimal places -->
<template v-if="goal.departments.length > 1" v-for="tmpDeptId in goal.departments" :key="tmpDeptId">
<!-- The target for this department until today -->
<div class="is-flex is-justify-content-space-between mb-1">
<strong :class="{'has-text-warning': tmpDeptId === deptId, 'has-text-grey': tmpDeptId !== deptId}">{{ getDepartmentName(tmpDeptId) }}</strong>
<span :class="{'has-text-warning': tmpDeptId === deptId, 'has-text-grey': tmpDeptId !== deptId}">{{ goal.progress.departmental_distribution[tmpDeptId]?.all?.count || 0 }} / {{ getDailyGoalDepartmentTarget(goal, tmpDeptId, true) }} - <strong :class="{'has-text-warning': tmpDeptId === deptId, 'has-text-grey': tmpDeptId !== deptId}">{{ Math.min(Math.round(((goal.progress.departmental_distribution[tmpDeptId]?.all?.count || 0) / getDailyGoalDepartmentTarget(goal, tmpDeptId, true)) * 100, true), 100) }}%</strong></span>
<span :class="{'has-text-warning': tmpDeptId === deptId, 'has-text-grey': tmpDeptId !== deptId}">{{ goal.progress.departmental_distribution[tmpDeptId]?.[currentTimeframe]?.count || 0 }} / {{ getDailyGoalDepartmentTarget(goal, tmpDeptId, true) }} - <strong :class="{'has-text-warning': tmpDeptId === deptId, 'has-text-grey': tmpDeptId !== deptId}">{{ Math.min(Math.round(((goal.progress.departmental_distribution[tmpDeptId]?.[currentTimeframe]?.count || 0) / getDailyGoalDepartmentTarget(goal, tmpDeptId, true)) * 100, true), 100) }}%</strong></span>
</div>
</template>
<!-- Total progress until now for all departments -->
<div class="is-flex is-justify-content-space-between mb-1">
<strong class="has-text-white">I alt</strong>
<span>{{ goal.progress.all?.count || 0 }} / {{ getDailyGoalDepartmentTarget(goal, deptId, true) * goal.departments.length }} - <strong class="has-text-white">{{ getGoalProgress(goal) }}%</strong></span>
<span>{{ goal.progress[currentTimeframe]?.count || 0 }} / {{ goal.progress[currentTimeframe]?.target || (getDailyGoalDepartmentTarget(goal, deptId, true) * goal.departments.length) }} - <strong class="has-text-white">{{ getGoalProgress(goal) }}%</strong></span>
</div>
<!-- Total divider -->
<div class="divider">I alt</div>
@@ -370,7 +416,7 @@ const getGoalApplicableDaysInPeriod = (goal, fromDate, toDate) => {
<div class="is-flex is-justify-content-space-between mb-1">
<strong class="has-text-white">Mål</strong>
<!-- Overall goal progress and then % with 2 decimal places -->
<span>{{ goal.progress.all?.count || 0 }} / {{ goal.criteria.target }} - <strong class="has-text-white">{{ getGoalProgress(goal) }}%</strong></span>
<span>{{ goal.progress[currentTimeframe]?.count || 0 }} / {{ goal.progress[currentTimeframe]?.target || goal.criteria.target }} - <strong class="has-text-white">{{ getGoalProgress(goal) }}%</strong></span>
</div>
<!-- Days since -->
<div class="is-flex is-justify-content-space-between mb-1">
@@ -385,18 +431,18 @@ const getGoalApplicableDaysInPeriod = (goal, fromDate, toDate) => {
</template>
<template v-slot:default>
<span>
<strong>{{ Math.min(Math.round(((goal.progress.departmental_distribution[deptId]?.all?.count || 0) / getDailyGoalDepartmentTarget(goal, deptId, true)) * 100, true), 100) }}%</strong>
<strong>{{ Math.min(Math.round(((goal.progress.departmental_distribution[deptId]?.[currentTimeframe]?.count || 0) / goal.progress.departmental_distribution[deptId]?.[currentTimeframe]?.target) * 100, true), 100) }}%</strong>
</span>
</template>
</b-tooltip>
</div>
<progress
class="progress is-small mb-0"
:value="goal.progress.departmental_distribution[deptId]?.all?.count || 0"
:max="getDailyGoalDepartmentTarget(goal, deptId, true)"
:class="getProgressClass(getDailyGoalDepartmentTarget(goal, deptId, true) === 0 ? 0 : Math.min(Math.round(((goal.progress.departmental_distribution[deptId]?.all?.count || 0) / getDailyGoalDepartmentTarget(goal, deptId, true)) * 100, true), 100))"
:value="goal.progress.departmental_distribution[deptId]?.[currentTimeframe]?.count || 0"
:max="goal.progress.departmental_distribution[deptId]?.[currentTimeframe]?.target || getDailyGoalDepartmentTarget(goal, deptId, true)"
:class="getProgressClass(getDailyGoalDepartmentTarget(goal, deptId, true) === 0 ? 0 : Math.min(Math.round(((goal.progress.departmental_distribution[deptId]?.[currentTimeframe]?.count || 0) / goal.progress.departmental_distribution[deptId]?.[currentTimeframe]?.target) * 100, true), 100))"
>
{{ Math.min(Math.round(((goal.progress.departmental_distribution[deptId]?.all?.count || 0) / getDailyGoalDepartmentTarget(goal, deptId)) * 100, true), 100) }}%
{{ Math.min(Math.round(((goal.progress.departmental_distribution[deptId]?.[currentTimeframe]?.count || 0) / goal.progress.departmental_distribution[deptId]?.[currentTimeframe]?.target) * 100, true), 100) }}%
</progress>
</div>
</div>