Client - init workout chart (WIP)

This commit is contained in:
Sam
2021-09-26 08:59:17 +02:00
parent 146899c269
commit c50e74143e
19 changed files with 467 additions and 37 deletions

View File

@ -5,25 +5,15 @@
</template>
<script lang="ts">
import {
Chart,
ChartData,
ChartOptions,
LayoutItem,
registerables,
} from 'chart.js'
import ChartDataLabels from 'chartjs-plugin-datalabels'
import { ChartData, ChartOptions, LayoutItem } from 'chart.js'
import { ComputedRef, PropType, computed, defineComponent } from 'vue'
import { BarChart, useBarChart } from 'vue-chart-3'
import { useI18n } from 'vue-i18n'
import { IChartDataset } from '@/types/chart'
import { TDatasetKeys } from '@/types/statistics'
import { TStatisticsDatasetKeys } from '@/types/statistics'
import { formatTooltipValue } from '@/utils/tooltip'
Chart.register(...registerables)
Chart.register(ChartDataLabels)
export default defineComponent({
name: 'Chart',
components: {
@ -39,7 +29,7 @@
required: true,
},
displayedData: {
type: String as PropType<TDatasetKeys>,
type: String as PropType<TStatisticsDatasetKeys>,
required: true,
},
},

View File

@ -16,7 +16,7 @@
import {
IStatisticsChartData,
IStatisticsDateParams,
TDatasetKeys,
TStatisticsDatasetKeys,
TStatisticsFromApi,
} from '@/types/statistics'
import { formatStats } from '@/utils/statistics'
@ -32,7 +32,7 @@
required: true,
},
displayedData: {
type: String as PropType<TDatasetKeys>,
type: String as PropType<TStatisticsDatasetKeys>,
required: true,
},
params: {

View File

@ -0,0 +1,188 @@
<template>
<div id="workout-chart">
<Card :without-title="false">
<template #title>{{ t('workouts.ANALYSIS') }} </template>
<template #content>
<div class="chart-radio">
<label>
<input
type="radio"
name="distance"
:checked="displayDistance"
@click="updateDisplayDistance"
/>
{{ t('workouts.DISTANCE') }}
</label>
<label>
<input
type="radio"
name="duration"
:checked="!displayDistance"
@click="updateDisplayDistance"
/>
{{ t('workouts.DURATION') }}
</label>
</div>
<LineChart v-bind="lineChartProps" class="line-chart" />
<div class="no-data-cleaning">
{{ t('workouts.NO_DATA_CLEANING') }}
</div>
</template>
</Card>
</div>
</template>
<script lang="ts">
import { ChartData, ChartOptions } from 'chart.js'
import { PropType, defineComponent, ref, ComputedRef, computed } from 'vue'
import { LineChart, useLineChart } from 'vue-chart-3'
import { useI18n } from 'vue-i18n'
import Card from '@/components/Common/Card.vue'
import { IAuthUserProfile } from '@/types/user'
import { IWorkoutChartData, IWorkoutState } from '@/types/workouts'
import { getDatasets } from '@/utils/workouts'
export default defineComponent({
name: 'WorkoutChart',
components: {
Card,
LineChart,
},
props: {
authUser: {
type: Object as PropType<IAuthUserProfile>,
required: true,
},
workout: {
type: Object as PropType<IWorkoutState>,
required: true,
},
},
setup(props) {
const { t } = useI18n()
let displayDistance = ref(true)
const datasets: ComputedRef<IWorkoutChartData> = computed(() =>
getDatasets(props.workout.chartData, t)
)
let chartData: ComputedRef<ChartData<'line'>> = computed(() => ({
labels: displayDistance.value
? datasets.value.distance_labels
: datasets.value.duration_labels,
datasets: JSON.parse(
JSON.stringify([
datasets.value.datasets.speed,
datasets.value.datasets.elevation,
])
),
}))
const options = computed<ChartOptions<'line'>>(() => ({
responsive: true,
animation: false,
layout: {
padding: {
top: 22,
},
},
scales: {
x: {
grid: {
drawOnChartArea: false,
},
ticks: {
count: 10,
callback: function (value) {
return displayDistance.value
? Number(value).toFixed(2)
: new Date(+value * 1000).toISOString().substr(11, 8)
},
},
type: 'linear',
bounds: 'data',
title: {
display: true,
text: displayDistance.value
? t('workouts.DISTANCE') + ' (km)'
: t('workouts.DURATION'),
},
},
ySpeed: {
grid: {
drawOnChartArea: false,
},
position: 'left',
title: {
display: true,
text: t('workouts.SPEED') + ' (km/h)',
},
},
yElevation: {
beginAtZero: true,
grid: {
drawOnChartArea: false,
},
position: 'right',
title: {
display: true,
text: t('workouts.ELEVATION') + ' (m)',
},
},
},
elements: {
point: {
pointStyle: 'circle',
pointRadius: 0,
},
},
plugins: {
datalabels: {
display: false,
},
},
}))
function updateDisplayDistance() {
displayDistance.value = !displayDistance.value
}
const { lineChartProps } = useLineChart({
chartData,
options,
})
return {
displayDistance,
lineChartProps,
t,
updateDisplayDistance,
}
},
})
</script>
<style lang="scss" scoped>
@import '~@/scss/base';
#workout-chart {
::v-deep(.card) {
.card-title {
text-transform: capitalize;
}
.card-content {
display: flex;
flex-direction: column;
.chart-radio {
width: 100%;
display: flex;
justify-content: center;
label {
padding: 0 $default-padding;
}
}
.no-data-cleaning {
font-size: 0.85em;
font-style: italic;
}
}
}
}
</style>