feat: change hr zone to bar chart

- return transform_activity_streams to  get_public_activity_stream_by_type
-
This commit is contained in:
Zuhdi
2025-06-17 22:57:52 +07:00
parent c503c35607
commit 9748f7776f
5 changed files with 128 additions and 5 deletions

View File

@@ -313,7 +313,7 @@ def get_public_activity_stream_by_type(activity_id: int, stream_type: int, db: S
return None
# Return the activity stream
return activity_stream
return transform_activity_streams(activity_stream, activity, db)
except Exception as err:
# Log the exception
core_logger.print_to_log(

View File

@@ -15,6 +15,7 @@
"@fortawesome/vue-fontawesome": "^3.0.8",
"bootstrap": "^5.3.3",
"chart.js": "^4.4.6",
"chartjs-plugin-datalabels": "^2.2.0",
"flag-icons": "^7.2.3",
"leaflet": "^1.9.4",
"luxon": "^3.5.0",
@@ -3864,6 +3865,15 @@
"pnpm": ">=8"
}
},
"node_modules/chartjs-plugin-datalabels": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
"integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
"license": "MIT",
"peerDependencies": {
"chart.js": ">=3.0.0"
}
},
"node_modules/check-error": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",

View File

@@ -19,6 +19,7 @@
"@fortawesome/vue-fontawesome": "^3.0.8",
"bootstrap": "^5.3.3",
"chart.js": "^4.4.6",
"chartjs-plugin-datalabels": "^2.2.0",
"flag-icons": "^7.2.3",
"leaflet": "^1.9.4",
"luxon": "^3.5.0",

View File

@@ -200,10 +200,14 @@
<br>
<span>{{ $t("activitySummaryComponent.activityAvgHR") }}: {{ formatHr(activity.average_hr) }}</span> <br>
<span>{{ $t("activitySummaryComponent.activityMaxHR") }}: {{ formatHr(activity.max_hr) }}</span> <br><br>
<span v-for="(value, zone, index) in hrZones" :key="zone"
:style="{ color: getZoneColor(index) }">
{{ $t("activitySummaryComponent.activityHRZone") }} {{ index + 1 }} ({{ value.hr }}) : {{ value.percent }}%<br>
</span>
<BarChartComponent
v-if="Object.values(hrZones).length > 0"
:labels="getHrBarChartData().labels"
:values="getHrBarChartData().values"
:barColors="getHrBarChartData().barColors"
:datalabelsFormatter="(value) => `${Math.round(value)}%`"
:title="$t('activitySummaryComponent.activityHRZone')"
/>
</div>
</div>
</div>
@@ -224,6 +228,7 @@ import LoadingComponent from "@/components/GeneralComponents/LoadingComponent.vu
import UserAvatarComponent from "@/components/Users/UserAvatarComponent.vue";
import EditActivityModalComponent from "@/components/Activities/Modals/EditActivityModalComponent.vue";
import ModalComponent from "@/components/Modals/ModalComponent.vue";
import BarChartComponent from '@/components/GeneralComponents/BarChartComponent.vue';
// Importing the services
import { users } from "@/services/usersService";
import { activities } from "@/services/activitiesService";
@@ -336,4 +341,14 @@ function getZoneColor(index) {
];
return colors[index] || '#000';
}
function getHrBarChartData() {
const zones = Object.values(hrZones.value);
return {
labels: zones.map((z, i) => `${t('activitySummaryComponent.activityHRZone')} ${i + 1} (${z.hr || ''})`),
// values: zones.map(z => `${z.percent ?? 0}%`),
values: zones.map(z => z.percent ?? 0),
barColors: zones.map((_, i) => getZoneColor(i)),
};
}
</script>

View File

@@ -0,0 +1,97 @@
<template>
<div>
<canvas ref="barChartCanvas"></canvas>
</div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
import { Chart, registerables } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
Chart.register(...registerables);
const props = defineProps({
labels: {
type: Array,
required: true
},
values: {
type: Array,
required: true
},
barColors: {
type: Array,
default: () => ['#1e90ff', '#28a745', '#ffc107', '#fd7e14', '#dc3545']
},
title: {
type: String,
default: ''
},
datalabelsFormatter: {
type: Function,
default: null
}
});
const barChartCanvas = ref(null);
let chartInstance = null;
function renderChart() {
if (chartInstance) {
chartInstance.destroy();
}
chartInstance = new Chart(barChartCanvas.value, {
type: 'bar',
data: {
labels: props.labels,
datasets: [
{
label: props.title,
data: props.values,
backgroundColor: props.barColors,
}
]
},
options: {
indexAxis: 'y',
responsive: true,
plugins: {
legend: { display: false },
title: {
display: !!props.title,
text: props.title
},
tooltip: { enabled: false },
datalabels: {
backgroundColor: function(context) {
return "black";
},
borderRadius: 4,
color: 'white',
font: {
weight: 'bold'
},
align: 'start', // Align datalabels to the start of the bar
anchor: 'start', // Anchor datalabels to the start of the bar
formatter: props.datalabelsFormatter || undefined,
padding: 6
}
},
scales: {
y: {
beginAtZero: true,
ticks: { stepSize: 10 }
}
}
},
plugins: [ChartDataLabels]
});
}
onMounted(() => {
renderChart();
});
watch(() => [props.labels, props.values, props.barColors, props.title], renderChart, { deep: true });
</script>