Client - init stats chart (wip)
This commit is contained in:
		| @@ -11,10 +11,12 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "axios": "^0.21.1", | ||||
|     "chart.js": "^3.5.1", | ||||
|     "core-js": "^3.6.5", | ||||
|     "date-fns": "^2.23.0", | ||||
|     "register-service-worker": "^1.7.1", | ||||
|     "vue": "^3.0.0", | ||||
|     "vue-chart-3": "^0.5.8", | ||||
|     "vue-i18n": "^9.1.0", | ||||
|     "vue-router": "^4.0.0-0", | ||||
|     "vuex": "^4.0.0-0" | ||||
|   | ||||
							
								
								
									
										69
									
								
								fittrackee_client/src/components/Common/StatsChart/Chart.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								fittrackee_client/src/components/Common/StatsChart/Chart.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| <template> | ||||
|   <div class="chart"> | ||||
|     <BarChart v-bind="barChartProps" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
|   import { Chart, ChartData, ChartOptions, registerables } from 'chart.js' | ||||
|   import { ComputedRef, PropType, computed, defineComponent } from 'vue' | ||||
|   import { BarChart, useBarChart } from 'vue-chart-3' | ||||
|  | ||||
|   import { IStatisticsChartDataset } from '@/types/statistics' | ||||
|  | ||||
|   Chart.register(...registerables) | ||||
|  | ||||
|   export default defineComponent({ | ||||
|     name: 'Chart', | ||||
|     components: { | ||||
|       BarChart, | ||||
|     }, | ||||
|     props: { | ||||
|       datasets: { | ||||
|         type: Object as PropType<IStatisticsChartDataset[]>, | ||||
|         required: true, | ||||
|       }, | ||||
|       labels: { | ||||
|         type: Object as PropType<unknown[]>, | ||||
|         required: true, | ||||
|       }, | ||||
|     }, | ||||
|     setup(props) { | ||||
|       let chartData: ComputedRef<ChartData<'bar'>> = computed(() => ({ | ||||
|         labels: props.labels, | ||||
|         datasets: props.datasets, | ||||
|       })) | ||||
|       const options = computed<ChartOptions<'bar'>>(() => ({ | ||||
|         responsive: true, | ||||
|         maintainAspectRatio: true, | ||||
|         layout: { | ||||
|           padding: 5, | ||||
|         }, | ||||
|         scales: { | ||||
|           x: { | ||||
|             stacked: true, | ||||
|             grid: { | ||||
|               display: false, | ||||
|             }, | ||||
|           }, | ||||
|           y: { | ||||
|             stacked: true, | ||||
|             grid: { | ||||
|               display: false, | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|         plugins: { | ||||
|           legend: { | ||||
|             display: false, | ||||
|           }, | ||||
|         }, | ||||
|       })) | ||||
|       const { barChartProps } = useBarChart({ | ||||
|         chartData, | ||||
|         options, | ||||
|       }) | ||||
|       return { barChartProps } | ||||
|     }, | ||||
|   }) | ||||
| </script> | ||||
							
								
								
									
										65
									
								
								fittrackee_client/src/components/Common/StatsChart/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								fittrackee_client/src/components/Common/StatsChart/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| <template> | ||||
|   <div class="stat-chart"> | ||||
|     <Chart :datasets="datasets" :labels="labels" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
|   import { ComputedRef, PropType, computed, defineComponent } from 'vue' | ||||
|  | ||||
|   import Chart from '@/components/Common/StatsChart/Chart.vue' | ||||
|   import { ISport } from '@/types/sports' | ||||
|   import { | ||||
|     IStatisticsChartData, | ||||
|     IStatisticsDateParams, | ||||
|     TDatasetKeys, | ||||
|     TStatisticsFromApi, | ||||
|   } from '@/types/statistics' | ||||
|   import { formatStats } from '@/utils/statistics' | ||||
|  | ||||
|   export default defineComponent({ | ||||
|     name: 'StatsChart', | ||||
|     components: { | ||||
|       Chart, | ||||
|     }, | ||||
|     props: { | ||||
|       statistics: { | ||||
|         type: Object as PropType<TStatisticsFromApi>, | ||||
|         required: true, | ||||
|       }, | ||||
|       displayedData: { | ||||
|         type: String as PropType<TDatasetKeys>, | ||||
|         required: true, | ||||
|       }, | ||||
|       params: { | ||||
|         type: Object as PropType<IStatisticsDateParams>, | ||||
|         required: true, | ||||
|       }, | ||||
|       sports: { | ||||
|         type: Object as PropType<ISport[]>, | ||||
|         required: true, | ||||
|       }, | ||||
|       weekStartingMonday: { | ||||
|         type: Boolean, | ||||
|         required: true, | ||||
|       }, | ||||
|     }, | ||||
|     setup(props) { | ||||
|       const formattedStats: ComputedRef<IStatisticsChartData> = computed(() => | ||||
|         formatStats( | ||||
|           props.params, | ||||
|           props.weekStartingMonday, | ||||
|           props.sports, | ||||
|           [], | ||||
|           props.statistics | ||||
|         ) | ||||
|       ) | ||||
|       return { | ||||
|         datasets: computed( | ||||
|           () => formattedStats.value.datasets[props.displayedData] | ||||
|         ), | ||||
|         labels: computed(() => formattedStats.value.labels), | ||||
|       } | ||||
|     }, | ||||
|   }) | ||||
| </script> | ||||
| @@ -1,3 +1,145 @@ | ||||
| <template> | ||||
|   <div>Month stats</div> | ||||
|   <div> | ||||
|     <Card :without-title="false"> | ||||
|       <template #title>{{ $t('dashboard.THIS_MONTH') }}</template> | ||||
|       <template #content> | ||||
|         <div class="chart-radio"> | ||||
|           <label class=""> | ||||
|             <input | ||||
|               type="radio" | ||||
|               name="total_distance" | ||||
|               :checked="displayedData === 'total_distance'" | ||||
|               @click="updateDisplayData" | ||||
|             /> | ||||
|             {{ t('workouts.DISTANCE') }} | ||||
|           </label> | ||||
|           <label class=""> | ||||
|             <input | ||||
|               type="radio" | ||||
|               name="total_duration" | ||||
|               :checked="displayedData === 'total_duration'" | ||||
|               @click="updateDisplayData" | ||||
|             /> | ||||
|             {{ t('workouts.DURATION') }} | ||||
|           </label> | ||||
|           <label class=""> | ||||
|             <input | ||||
|               type="radio" | ||||
|               name="nb_workouts" | ||||
|               :checked="displayedData === 'nb_workouts'" | ||||
|               @click="updateDisplayData" | ||||
|             /> | ||||
|             {{ t('workouts.WORKOUT', 2) }} | ||||
|           </label> | ||||
|         </div> | ||||
|         <Chart | ||||
|           :displayedData="displayedData" | ||||
|           :params="chartParams" | ||||
|           :statistics="statistics" | ||||
|           :sports="sports" | ||||
|           :week-starting-monday="weekStartingMonday" | ||||
|           v-if="statistics && weekStartingMonday !== undefined" | ||||
|       /></template> | ||||
|     </Card> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
|   import { startOfMonth, endOfMonth, format } from 'date-fns' | ||||
|   import { | ||||
|     ComputedRef, | ||||
|     PropType, | ||||
|     computed, | ||||
|     defineComponent, | ||||
|     ref, | ||||
|     watch, | ||||
|   } from 'vue' | ||||
|   import { useI18n } from 'vue-i18n' | ||||
|  | ||||
|   import Card from '@/components/Common/Card.vue' | ||||
|   import Chart from '@/components/Common/StatsChart/index.vue' | ||||
|   import { STATS_STORE, SPORTS_STORE, USER_STORE } from '@/store/constants' | ||||
|   import { ISport } from '@/types/sports' | ||||
|   import { IStatisticsDateParams, TStatisticsFromApi } from '@/types/statistics' | ||||
|   import { IAuthUserProfile } from '@/types/user' | ||||
|   import { useStore } from '@/use/useStore' | ||||
|  | ||||
|   export default defineComponent({ | ||||
|     name: 'UserMonthStats', | ||||
|     components: { | ||||
|       Card, | ||||
|       Chart, | ||||
|     }, | ||||
|     props: { | ||||
|       user: { | ||||
|         type: Object as PropType<IAuthUserProfile>, | ||||
|         required: true, | ||||
|       }, | ||||
|     }, | ||||
|     setup(props) { | ||||
|       const store = useStore() | ||||
|       const date = new Date() | ||||
|       const { t } = useI18n() | ||||
|       const dateFormat = 'yyyy-MM-dd' | ||||
|       const chartParams: IStatisticsDateParams = { | ||||
|         duration: 'week', | ||||
|         start: startOfMonth(date), | ||||
|         end: endOfMonth(date), | ||||
|       } | ||||
|       const apiParams = { | ||||
|         from: format(chartParams.start, dateFormat), | ||||
|         to: format(chartParams.end, dateFormat), | ||||
|         time: `week${props.user.weekm ? 'm' : ''}`, | ||||
|       } | ||||
|       const statistics: ComputedRef<TStatisticsFromApi> = computed( | ||||
|         () => store.getters[STATS_STORE.GETTERS.USER_STATS] | ||||
|       ) | ||||
|       const sports: ComputedRef<ISport[]> = computed( | ||||
|         () => store.getters[SPORTS_STORE.GETTERS.SPORTS] | ||||
|       ) | ||||
|       const authUser: ComputedRef<IAuthUserProfile> = computed( | ||||
|         () => store.getters[USER_STORE.GETTERS.AUTH_USER_PROFILE] | ||||
|       ) | ||||
|       let displayedData = ref('total_distance') | ||||
|  | ||||
|       function updateDisplayData(event: Event & { target: HTMLInputElement }) { | ||||
|         displayedData.value = event.target.name | ||||
|       } | ||||
|  | ||||
|       watch( | ||||
|         () => props.user.username, | ||||
|         async () => { | ||||
|           store.dispatch(STATS_STORE.ACTIONS.GET_USER_STATS, { | ||||
|             username: props.user.username, | ||||
|             filterType: 'by_time', | ||||
|             params: apiParams, | ||||
|           }) | ||||
|         } | ||||
|       ) | ||||
|       return { | ||||
|         chartParams, | ||||
|         displayedData, | ||||
|         sports, | ||||
|         statistics, | ||||
|         t, | ||||
|         updateDisplayData, | ||||
|         weekStartingMonday: computed<boolean>(() => authUser.value.weekm), | ||||
|       } | ||||
|     }, | ||||
|   }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
|   @import '~@/scss/base'; | ||||
|  | ||||
|   .chart-radio { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     padding: $default-padding; | ||||
|  | ||||
|     label { | ||||
|       font-size: 0.9em; | ||||
|       font-weight: normal; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| { | ||||
|   "DASHBOARD": "Dashboard" | ||||
|   "DASHBOARD": "Dashboard", | ||||
|   "THIS_MONTH": "This month" | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| { | ||||
|   "ADD_WORKOUT": "Add workout", | ||||
|   "DISTANCE": "distance", | ||||
|   "DURATION": "duration", | ||||
|   "KM": "km", | ||||
|   "SPORT": "sport | sports", | ||||
|   "WORKOUT": "workout | workouts" | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| { | ||||
|   "DASHBOARD": "Tableau de Bord" | ||||
|   "DASHBOARD": "Tableau de Bord", | ||||
|   "THIS_MONTH": "Ce mois" | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| { | ||||
|   "ADD_WORKOUT": "Ajouter une séance", | ||||
|   "DISTANCE": "distance", | ||||
|   "DURATION": "durée", | ||||
|   "KM": "km", | ||||
|   "SPORT": "sport | sports", | ||||
|   "WORKOUT": "séance | séances" | ||||
|   | ||||
| @@ -31,7 +31,7 @@ export type TStatisticsFromApi = { | ||||
| export interface IStatisticsChartDataset { | ||||
|   label: string | ||||
|   backgroundColor: string[] | ||||
|   data: (number | null)[] | ||||
|   data: number[] | ||||
| } | ||||
|  | ||||
| export type TStatisticsDatasets = Record< | ||||
| @@ -40,6 +40,6 @@ export type TStatisticsDatasets = Record< | ||||
| > | ||||
|  | ||||
| export interface IStatisticsChartData { | ||||
|   labels: string[] | ||||
|   labels: unknown[] | ||||
|   datasets: TStatisticsDatasets | ||||
| } | ||||
|   | ||||
| @@ -36,14 +36,24 @@ export const getDateKeys = ( | ||||
|   return days | ||||
| } | ||||
|  | ||||
| export const sportColors: Record<string, string> = { | ||||
|   'Cycling (Sport)': '#55A8A3', | ||||
|   'Cycling (Transport)': '#98C3A9', | ||||
|   Hiking: '#D0838A', | ||||
|   'Mountain Biking': '#ECC77E', | ||||
|   Running: '#926692', | ||||
|   Walking: '#929292', | ||||
| } | ||||
|  | ||||
| const getStatisticsChartDataset = ( | ||||
|   sportLabel: string | ||||
|   sportLabel: string, | ||||
|   color: string | ||||
| ): IStatisticsChartDataset => | ||||
|   Object.assign( | ||||
|     {}, | ||||
|     { | ||||
|       label: sportLabel, | ||||
|       backgroundColor: [], | ||||
|       backgroundColor: [color], | ||||
|       data: [], | ||||
|     } | ||||
|   ) | ||||
| @@ -55,9 +65,10 @@ export const getDatasets = (displayedSports: ISport[]): TStatisticsDatasets => { | ||||
|     total_duration: [], | ||||
|   } | ||||
|   displayedSports.map((sport) => { | ||||
|     datasets.nb_workouts.push(getStatisticsChartDataset(sport.label)) | ||||
|     datasets.total_distance.push(getStatisticsChartDataset(sport.label)) | ||||
|     datasets.total_duration.push(getStatisticsChartDataset(sport.label)) | ||||
|     const color = sportColors[sport.label] | ||||
|     datasets.nb_workouts.push(getStatisticsChartDataset(sport.label, color)) | ||||
|     datasets.total_distance.push(getStatisticsChartDataset(sport.label, color)) | ||||
|     datasets.total_duration.push(getStatisticsChartDataset(sport.label, color)) | ||||
|   }) | ||||
|   return datasets | ||||
| } | ||||
| @@ -85,17 +96,17 @@ export const formatStats = ( | ||||
|     const date: string = format(key, dateFormat) | ||||
|     labels.push(date) | ||||
|     data.map((datasetKey) => { | ||||
|       datasets[datasetKey].map((dataset) => | ||||
|       datasets[datasetKey].map((dataset) => { | ||||
|         dataset.data.push( | ||||
|           apiStats !== {} && | ||||
|             date in apiStats && | ||||
|             sportsId[dataset.label] in apiStats[date] | ||||
|             ? apiStats[date][sportsId[dataset.label]][datasetKey] | ||||
|             : null | ||||
|         ) | ||||
|             : 0 | ||||
|         ) | ||||
|       }) | ||||
|     }) | ||||
|   }) | ||||
|   return { | ||||
|     labels, | ||||
|     datasets, | ||||
|   | ||||
| @@ -164,51 +164,51 @@ describe('getDatasets', () => { | ||||
|       nb_workouts: [ | ||||
|         { | ||||
|           label: 'Cycling (Sport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#55A8A3'], | ||||
|           data: [], | ||||
|         }, | ||||
|         { | ||||
|           label: 'Cycling (Transport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#98C3A9'], | ||||
|           data: [], | ||||
|         }, | ||||
|         { | ||||
|           label: 'Hiking', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#D0838A'], | ||||
|           data: [], | ||||
|         }, | ||||
|       ], | ||||
|       total_distance: [ | ||||
|         { | ||||
|           label: 'Cycling (Sport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#55A8A3'], | ||||
|           data: [], | ||||
|         }, | ||||
|         { | ||||
|           label: 'Cycling (Transport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#98C3A9'], | ||||
|           data: [], | ||||
|         }, | ||||
|         { | ||||
|           label: 'Hiking', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#D0838A'], | ||||
|           data: [], | ||||
|         }, | ||||
|       ], | ||||
|       total_duration: [ | ||||
|         { | ||||
|           label: 'Cycling (Sport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#55A8A3'], | ||||
|           data: [], | ||||
|         }, | ||||
|         { | ||||
|           label: 'Cycling (Transport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#98C3A9'], | ||||
|           data: [], | ||||
|         }, | ||||
|         { | ||||
|           label: 'Hiking', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#D0838A'], | ||||
|           data: [], | ||||
|         }, | ||||
|       ], | ||||
| @@ -220,21 +220,21 @@ describe('getDatasets', () => { | ||||
|       nb_workouts: [ | ||||
|         { | ||||
|           label: 'Cycling (Transport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#98C3A9'], | ||||
|           data: [], | ||||
|         }, | ||||
|       ], | ||||
|       total_distance: [ | ||||
|         { | ||||
|           label: 'Cycling (Transport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#98C3A9'], | ||||
|           data: [], | ||||
|         }, | ||||
|       ], | ||||
|       total_duration: [ | ||||
|         { | ||||
|           label: 'Cycling (Transport)', | ||||
|           backgroundColor: [], | ||||
|           backgroundColor: ['#98C3A9'], | ||||
|           data: [], | ||||
|         }, | ||||
|       ], | ||||
| @@ -257,52 +257,52 @@ describe('formatStats', () => { | ||||
|         nb_workouts: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Hiking', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#D0838A'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|         ], | ||||
|         total_distance: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Hiking', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#D0838A'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|         ], | ||||
|         total_duration: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Hiking', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#D0838A'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
| @@ -326,22 +326,22 @@ describe('formatStats', () => { | ||||
|         nb_workouts: [ | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|         ], | ||||
|         total_distance: [ | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|         ], | ||||
|         total_duration: [ | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 0, 0], | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
| @@ -392,52 +392,52 @@ describe('formatStats', () => { | ||||
|         nb_workouts: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [1, 1, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [1, 1, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, 2, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 2, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Hiking', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, 2], | ||||
|             backgroundColor: ['#D0838A'], | ||||
|             data: [0, 0, 2], | ||||
|           }, | ||||
|         ], | ||||
|         total_distance: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [10, 15, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [10, 15, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, 20, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 20, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Hiking', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, 12], | ||||
|             backgroundColor: ['#D0838A'], | ||||
|             data: [0, 0, 12], | ||||
|           }, | ||||
|         ], | ||||
|         total_duration: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [3000, 3500, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [3000, 3500, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Cycling (Transport)', | ||||
|             backgroundColor: [], | ||||
|             data: [null, 3000, null], | ||||
|             backgroundColor: ['#98C3A9'], | ||||
|             data: [0, 3000, 0], | ||||
|           }, | ||||
|           { | ||||
|             label: 'Hiking', | ||||
|             backgroundColor: [], | ||||
|             data: [null, null, 5000], | ||||
|             backgroundColor: ['#D0838A'], | ||||
|             data: [0, 0, 5000], | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
| @@ -488,22 +488,22 @@ describe('formatStats', () => { | ||||
|         nb_workouts: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [1, 1, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [1, 1, 0], | ||||
|           }, | ||||
|         ], | ||||
|         total_distance: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [10, 15, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [10, 15, 0], | ||||
|           }, | ||||
|         ], | ||||
|         total_duration: [ | ||||
|           { | ||||
|             label: 'Cycling (Sport)', | ||||
|             backgroundColor: [], | ||||
|             data: [3000, 3500, null], | ||||
|             backgroundColor: ['#55A8A3'], | ||||
|             data: [3000, 3500, 0], | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|   | ||||
| @@ -1718,6 +1718,13 @@ | ||||
|   dependencies: | ||||
|     "@vue/shared" "3.1.5" | ||||
|  | ||||
| "@vue/reactivity@3.2.8": | ||||
|   version "3.2.8" | ||||
|   resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.8.tgz#b27200ccfaa06f312ac467b12a38161377c557ed" | ||||
|   integrity sha512-/Hj3Uz28SG+xB5SDWPOXUs0emvHkq82EmTgk44/plTVFeswCZ3i3Hd7WmsrPT4rGajlDKd5uqMmW0ith1ED0FA== | ||||
|   dependencies: | ||||
|     "@vue/shared" "3.2.8" | ||||
|  | ||||
| "@vue/runtime-core@3.1.5": | ||||
|   version "3.1.5" | ||||
|   resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.1.5.tgz#a545b7f146092929cb5e833e85439150f17ac87b" | ||||
| @@ -1726,6 +1733,14 @@ | ||||
|     "@vue/reactivity" "3.1.5" | ||||
|     "@vue/shared" "3.1.5" | ||||
|  | ||||
| "@vue/runtime-core@3.2.8", "@vue/runtime-core@latest": | ||||
|   version "3.2.8" | ||||
|   resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.8.tgz#8a2342c0baa0fee192f819a3bdc19547d7430b88" | ||||
|   integrity sha512-hwzXLGw1njBEY5JSyRXIIdCtzMFFF6F38WcKMmoIE3p7da30jEbWt8EwwrBomjT8ZbqzElOGlewBcnXNOiiIUg== | ||||
|   dependencies: | ||||
|     "@vue/reactivity" "3.2.8" | ||||
|     "@vue/shared" "3.2.8" | ||||
|  | ||||
| "@vue/runtime-dom@3.1.5": | ||||
|   version "3.1.5" | ||||
|   resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.1.5.tgz#4fa28947d408aa368fa17ea0edc1beb9af1472a1" | ||||
| @@ -1735,11 +1750,25 @@ | ||||
|     "@vue/shared" "3.1.5" | ||||
|     csstype "^2.6.8" | ||||
|  | ||||
| "@vue/runtime-dom@latest": | ||||
|   version "3.2.8" | ||||
|   resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.8.tgz#c6631b507049d39844b0434e81df1aa79efcc6cb" | ||||
|   integrity sha512-A/aRrlGLJ5y4Z7eNbnO/xHwx2RiPijQo7D3OIwESroG3HNP+dpuoqamajo5TXS9ZGjbMOih82COoe7xb9P4BZw== | ||||
|   dependencies: | ||||
|     "@vue/runtime-core" "3.2.8" | ||||
|     "@vue/shared" "3.2.8" | ||||
|     csstype "^2.6.8" | ||||
|  | ||||
| "@vue/shared@3.1.5": | ||||
|   version "3.1.5" | ||||
|   resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.1.5.tgz#74ee3aad995d0a3996a6bb9533d4d280514ede03" | ||||
|   integrity sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA== | ||||
|  | ||||
| "@vue/shared@3.2.8": | ||||
|   version "3.2.8" | ||||
|   resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.8.tgz#2f918e330aeb3f56ab1031ca60a5b30672512457" | ||||
|   integrity sha512-E2DQQnG7Qr4GwTs3GlfPPlHliGVADoufTnhpwfoViw7JlyLMmYtjfnTwM6nXAwvSJWiF7D+7AxpnWBBT3VWo6Q== | ||||
|  | ||||
| "@vue/test-utils@^2.0.0-0": | ||||
|   version "2.0.0-rc.10" | ||||
|   resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-rc.10.tgz#9ed689cd7d5a1c9ef6693806010e464d2ecc13b2" | ||||
| @@ -2802,6 +2831,11 @@ chardet@^0.7.0: | ||||
|   resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" | ||||
|   integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== | ||||
|  | ||||
| chart.js@^3.5.1: | ||||
|   version "3.5.1" | ||||
|   resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.5.1.tgz#73e24d23a4134a70ccdb5e79a917f156b6f3644a" | ||||
|   integrity sha512-m5kzt72I1WQ9LILwQC4syla/LD/N413RYv2Dx2nnTkRS9iv/ey1xLTt0DnPc/eWV4zI+BgEgDYBIzbQhZHc/PQ== | ||||
|  | ||||
| check-error@^1.0.2: | ||||
|   version "1.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" | ||||
| @@ -3521,6 +3555,11 @@ csstype@^2.6.8: | ||||
|   resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e" | ||||
|   integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== | ||||
|  | ||||
| csstype@latest: | ||||
|   version "3.0.8" | ||||
|   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" | ||||
|   integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== | ||||
|  | ||||
| cyclist@^1.0.1: | ||||
|   version "1.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" | ||||
| @@ -6330,7 +6369,7 @@ lodash.uniq@^4.5.0: | ||||
|   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" | ||||
|   integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= | ||||
|  | ||||
| lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3: | ||||
| lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@latest: | ||||
|   version "4.17.21" | ||||
|   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" | ||||
|   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== | ||||
| @@ -6752,6 +6791,11 @@ nanoid@^3.1.23: | ||||
|   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" | ||||
|   integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== | ||||
|  | ||||
| nanoid@latest: | ||||
|   version "3.1.25" | ||||
|   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" | ||||
|   integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== | ||||
|  | ||||
| nanomatch@^1.2.9: | ||||
|   version "1.2.13" | ||||
|   resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" | ||||
| @@ -9742,6 +9786,18 @@ vm-browserify@^1.0.1: | ||||
|   resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" | ||||
|   integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== | ||||
|  | ||||
| vue-chart-3@^0.5.8: | ||||
|   version "0.5.8" | ||||
|   resolved "https://registry.yarnpkg.com/vue-chart-3/-/vue-chart-3-0.5.8.tgz#1b3b6d1160f0696f8757d614eb94a720fb321a9d" | ||||
|   integrity sha512-VJEBTdMgWOaYqekXtz4LVBIeYyIx3qDlQnFyY4Ao1GwizokYZBycCeRN3oKDcYbbZi5yxYqTy6+Tm+m+SOPUPA== | ||||
|   dependencies: | ||||
|     "@vue/runtime-core" latest | ||||
|     "@vue/runtime-dom" latest | ||||
|     csstype latest | ||||
|     lodash latest | ||||
|     nanoid latest | ||||
|     vue-demi "^0.10.1" | ||||
|  | ||||
| vue-cli-plugin-i18n@~2.1.1: | ||||
|   version "2.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/vue-cli-plugin-i18n/-/vue-cli-plugin-i18n-2.1.1.tgz#aa2579cae810ca641662aee33c13b28f11f12d31" | ||||
| @@ -9756,6 +9812,11 @@ vue-cli-plugin-i18n@~2.1.1: | ||||
|     vue-i18n "^8.17.0" | ||||
|     vue-i18n-extract "1.0.2" | ||||
|  | ||||
| vue-demi@^0.10.1: | ||||
|   version "0.10.1" | ||||
|   resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.10.1.tgz#229b81395510f02f4ee255344557a12cc0120930" | ||||
|   integrity sha512-L6Oi+BvmMv6YXvqv5rJNCFHEKSVu7llpWWJczqmAQYOdmPPw5PNYoz1KKS//Fxhi+4QP64dsPjtmvnYGo1jemA== | ||||
|  | ||||
| vue-eslint-parser@^7.0.0, vue-eslint-parser@^7.9.0: | ||||
|   version "7.9.0" | ||||
|   resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.9.0.tgz#5eeedc71f22ebc7b18b957d1ab171acf29a41e64" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user