Add target lines and labels to health charts

Enhanced Health dashboard, steps, and weight components to display user target values with visual indicators and updated i18n labels. Steps and weight charts now show dashed target lines if targets are set, and card footers display units and icons for progress direction.
This commit is contained in:
João Vitória Silva
2025-11-25 12:22:32 +00:00
parent 2cc06345e6
commit dba386990f
7 changed files with 105 additions and 41 deletions

View File

@@ -16,9 +16,14 @@
<h1 v-else>{{ $t('generalItems.labelNotApplicable') }}</h1>
</div>
<div class="card-footer text-body-secondary">
<span v-if="userHealthTargets && userHealthTargets['weight']">{{
userHealthTargets.weight
}}</span>
<font-awesome-icon :icon="['fas', 'angle-down']" class="me-1" v-if="currentWeight > userHealthTargets.weight" />
<font-awesome-icon :icon="['fas', 'angle-up']" class="me-1" v-else/>
<span v-if="userHealthTargets && userHealthTargets['weight'] && Number(authStore?.user?.units) === 1">
{{ userHealthTargets.weight }} {{ $t('generalItems.unitsKg') }}
</span>
<span v-else-if="userHealthTargets && userHealthTargets['weight'] && Number(authStore?.user?.units) === 2">
{{ kgToLbs(userHealthTargets.weight) }} {{ $t('generalItems.unitsLbs') }}
</span>
<span v-else>{{ $t('healthDashboardZoneComponent.noWeightTarget') }}</span>
</div>
</div>
@@ -51,9 +56,11 @@
<h1 v-else>{{ $t('generalItems.labelNotApplicable') }}</h1>
</div>
<div class="card-footer text-body-secondary">
<span v-if="userHealthTargets && userHealthTargets['steps']">{{
userHealthTargets.steps
}}</span>
<span v-if="userHealthTargets && userHealthTargets['steps']">
<font-awesome-icon :icon="['fas', 'angle-down']" class="me-1" v-if="todaySteps < userHealthTargets.steps" />
<font-awesome-icon :icon="['fas', 'angle-up']" class="me-1" v-else/>
{{ userHealthTargets.steps }} {{ $t('healthDashboardZoneComponent.stepsTargetLabel') }}
</span>
<span v-else>{{ $t('healthDashboardZoneComponent.noStepsTarget') }}</span>
</div>
</div>

View File

@@ -24,7 +24,7 @@
class="mt-3 p-3 bg-body-tertiary rounded shadow-sm"
>
<!-- show graph -->
<HealthStepsBarChartComponent :userHealthSteps="dataWithSteps" :isLoading="isLoading" />
<HealthStepsBarChartComponent :userHealthTargets="userHealthTargets":userHealthSteps="dataWithSteps" :isLoading="isLoading" />
<br />
<p>

View File

@@ -14,6 +14,10 @@ import zoomPlugin from 'chartjs-plugin-zoom'
Chart.register(...registerables, zoomPlugin)
const props = defineProps({
userHealthTargets: {
type: [Object, null],
required: true
},
userHealthSteps: {
type: Object,
required: true
@@ -73,20 +77,38 @@ function updatedSortedArray() {
`${createdAt.getDate()}/${createdAt.getMonth() + 1}/${createdAt.getFullYear()}`
)
}
const datasets = [
{
label: t('generalItems.labelSteps'),
data: data,
backgroundColor: function (context) {
const chart = context.chart
const { ctx, chartArea } = chart
return 'rgba(59, 130, 246, 0.4)'
},
borderColor: 'rgba(59, 130, 246, 0.8)', // Blue border
borderWidth: 1
}
]
// Add target line if steps target exists
if (props.userHealthTargets?.steps != null) {
datasets.push({
label: t('generalItems.labelStepsTarget'),
data: Array(labels.length).fill(props.userHealthTargets.steps),
type: 'line',
borderColor: 'rgba(107, 114, 128, 0.9)',
borderWidth: 2,
borderDash: [5, 5],
fill: false,
pointRadius: 0,
pointHoverRadius: 0
})
}
return {
datasets: [
{
label: t('healthStepsListComponent.labelSteps'),
data: data,
backgroundColor: function (context) {
const chart = context.chart
const { ctx, chartArea } = chart
return 'rgba(59, 130, 246, 0.4)'
},
borderColor: 'rgba(59, 130, 246, 0.8)', // Blue border
borderWidth: 1
}
],
datasets: datasets,
labels: labels
}
})
@@ -167,7 +189,7 @@ onMounted(() => {
}
// Format weight with 1 decimal place
return `${label}: ${value.toFixed(1)}`
return `${label}: ${value}`
}
}
},

View File

@@ -24,7 +24,7 @@
class="mt-3 p-3 bg-body-tertiary rounded shadow-sm"
>
<!-- show graph -->
<HealthWeightLineChartComponent :userHealthWeight="dataWithWeight" :isLoading="isLoading" />
<HealthWeightLineChartComponent :userHealthTargets="userHealthTargets" :userHealthWeight="dataWithWeight" :isLoading="isLoading" />
<br />
<p>

View File

@@ -17,6 +17,10 @@ import zoomPlugin from 'chartjs-plugin-zoom'
Chart.register(...registerables, zoomPlugin)
const props = defineProps({
userHealthTargets: {
type: [Object, null],
required: true
},
userHealthWeight: {
type: Object,
required: true
@@ -100,25 +104,51 @@ function updatedSortedArray() {
label = t('generalItems.labelWeightInLbs')
}
const datasets = [
{
label: label,
data: data,
backgroundColor: function (context) {
const chart = context.chart
const { ctx, chartArea } = chart
if (!chartArea) {
return 'rgba(59, 130, 246, 0.4)'
}
return createGradient(ctx, chartArea)
},
borderColor: 'rgba(59, 130, 246, 0.8)', // Blue border
fill: true,
pointHoverRadius: 4,
pointHoverBackgroundColor: 'rgba(59, 130, 246, 0.8)'
}
]
// Add target line if weight target exists
if (props.userHealthTargets?.weight != null) {
const targetWeight =
Number(authStore?.user?.units) === 1
? props.userHealthTargets.weight
: kgToLbs(props.userHealthTargets.weight)
const targetLabel =
Number(authStore?.user?.units) === 1
? t('generalItems.labelWeightTargetInKg')
: t('generalItems.labelWeightTargetInLbs')
datasets.push({
label: targetLabel,
data: Array(labels.length).fill(targetWeight),
borderColor: 'rgba(107, 114, 128, 0.9)',
borderWidth: 2,
borderDash: [5, 5],
fill: false,
pointRadius: 0,
pointHoverRadius: 0
})
}
return {
datasets: [
{
label: label,
data: data,
backgroundColor: function (context) {
const chart = context.chart
const { ctx, chartArea } = chart
if (!chartArea) {
return 'rgba(59, 130, 246, 0.4)'
}
return createGradient(ctx, chartArea)
},
borderColor: 'rgba(59, 130, 246, 0.8)', // Blue border
fill: true,
pointHoverRadius: 4,
pointHoverBackgroundColor: 'rgba(59, 130, 246, 0.8)'
}
],
datasets: datasets,
labels: labels
}
})

View File

@@ -11,5 +11,6 @@
"bmiObesityClass2": "Obesity (Class 2)",
"bmiObesityClass3": "Extreme Obesity (Class 3)",
"steps": "Today's steps",
"stepsTargetLabel": "steps",
"noStepsTarget": "No steps target"
}

View File

@@ -36,6 +36,7 @@
"unitsKmH": "km/h",
"unitsKg": "kg",
"labelWeightInKg": "Weight in kg",
"labelWeightTargetInKg": "Weight target in kg",
"unitsInches": "inches",
"unitsFeet": "feet",
"unitsFeetShort": "ft",
@@ -45,6 +46,9 @@
"unitsMph": "mph",
"unitsLbs": "lbs",
"labelWeightInLbs": "Weight in lbs",
"labelWeightTargetInLbs": "Weight target in lbs",
"labelSteps": "Steps",
"labelStepsTarget": "Steps target",
"unitsCalories": "kcal",
"unitsBpm": "bpm",
"labelHRinBpm": "Heart rate in bpm",
@@ -79,4 +83,4 @@
"genderUnspecified": "Unspecified",
"labelAverage": "Average",
"labelMaximum": "Maximum"
}
}