Client - add full statistics chart

This commit is contained in:
Sam 2021-10-03 19:23:17 +02:00
parent 60a5df70a9
commit 1502e97211
12 changed files with 579 additions and 250 deletions

View File

@ -32,6 +32,14 @@
type: String as PropType<TStatisticsDatasetKeys>, type: String as PropType<TStatisticsDatasetKeys>,
required: true, required: true,
}, },
displayedSportIds: {
type: Array as PropType<number[]>,
required: true,
},
fullStats: {
type: Boolean,
required: true,
},
}, },
setup(props) { setup(props) {
const { t } = useI18n() const { t } = useI18n()
@ -54,7 +62,7 @@
animation: false, animation: false,
layout: { layout: {
padding: { padding: {
top: 22, top: props.fullStats ? 40 : 22,
}, },
}, },
scales: { scales: {
@ -76,7 +84,7 @@
}, },
}, },
afterFit: function (scale: LayoutItem) { afterFit: function (scale: LayoutItem) {
scale.width = 60 scale.width = props.fullStats ? 75 : 60
}, },
}, },
}, },
@ -84,13 +92,22 @@
datalabels: { datalabels: {
anchor: 'end', anchor: 'end',
align: 'end', align: 'end',
rotation: function (context) {
return props.fullStats && context.chart.chartArea.width < 580
? 310
: 0
},
display: function (context) {
return !(props.fullStats && context.chart.chartArea.width < 300)
},
formatter: function (value, context) { formatter: function (value, context) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
const total: number = context.chart.data.datasets const total: number = context.chart.data.datasets
.map((d) => d.data[context.dataIndex]) .map((d) => d.data[context.dataIndex])
.reduce((total, value) => getSum(total, value), 0) .reduce((total, value) => getSum(total, value), 0)
return context.datasetIndex === 5 && total > 0 return context.datasetIndex ===
props.displayedSportIds.length - 1 && total > 0
? formatTooltipValue(props.displayedData, total, false) ? formatTooltipValue(props.displayedData, total, false)
: null : null
}, },

View File

@ -1,68 +1,170 @@
<template> <template>
<div class="stat-chart"> <div class="start-chart">
<Chart <div v-if="labels.length === 0">
:datasets="datasets" {{ t('workouts.NO_WORKOUTS') }}
:labels="labels" </div>
:displayedData="displayedData" <div v-else>
/> <div class="chart-radio">
<label>
<input
type="radio"
name="total_distance"
:checked="displayedData === 'total_distance'"
@click="updateDisplayData"
/>
{{ t('workouts.DISTANCE') }}
</label>
<label>
<input
type="radio"
name="total_duration"
:checked="displayedData === 'total_duration'"
@click="updateDisplayData"
/>
{{ t('workouts.DURATION') }}
</label>
<label>
<input
type="radio"
name="nb_workouts"
:checked="displayedData === 'nb_workouts'"
@click="updateDisplayData"
/>
{{ t('workouts.WORKOUT', 2) }}
</label>
</div>
<Chart
v-if="labels.length > 0"
:datasets="datasets"
:labels="labels"
:displayedData="displayedData"
:displayedSportIds="displayedSportIds"
:fullStats="fullStats"
/>
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { ComputedRef, PropType, computed, defineComponent } from 'vue' import { format } from 'date-fns'
import {
ComputedRef,
PropType,
Ref,
computed,
defineComponent,
ref,
watch,
onBeforeMount,
} from 'vue'
import { useI18n } from 'vue-i18n'
import Chart from '@/components/Common/StatsChart/Chart.vue' import Chart from '@/components/Common/StatsChart/Chart.vue'
import { STATS_STORE } from '@/store/constants'
import { ISport } from '@/types/sports' import { ISport } from '@/types/sports'
import { import {
IStatisticsChartData, IStatisticsChartData,
IStatisticsDateParams,
TStatisticsDatasetKeys, TStatisticsDatasetKeys,
IStatisticsDateParams,
TStatisticsFromApi, TStatisticsFromApi,
IStatisticsParams,
} from '@/types/statistics' } from '@/types/statistics'
import { IAuthUserProfile } from '@/types/user'
import { useStore } from '@/use/useStore'
import { formatStats } from '@/utils/statistics' import { formatStats } from '@/utils/statistics'
export default defineComponent({ export default defineComponent({
name: 'StatsChart', name: 'UserMonthStats',
components: { components: {
Chart, Chart,
}, },
props: { props: {
statistics: {
type: Object as PropType<TStatisticsFromApi>,
required: true,
},
displayedData: {
type: String as PropType<TStatisticsDatasetKeys>,
required: true,
},
params: {
type: Object as PropType<IStatisticsDateParams>,
required: true,
},
sports: { sports: {
type: Object as PropType<ISport[]>, type: Object as PropType<ISport[]>,
required: true, required: true,
}, },
weekStartingMonday: { user: {
type: Boolean, type: Object as PropType<IAuthUserProfile>,
required: true, required: true,
}, },
chartParams: {
type: Object as PropType<IStatisticsDateParams>,
required: true,
},
displayedSportIds: {
type: Array as PropType<number[]>,
default: () => [],
},
fullStats: {
type: Boolean,
default: false,
},
}, },
setup(props) { setup(props) {
const store = useStore()
const { t } = useI18n()
let displayedData: Ref<TStatisticsDatasetKeys> = ref('total_distance')
const statistics: ComputedRef<TStatisticsFromApi> = computed(
() => store.getters[STATS_STORE.GETTERS.USER_STATS]
)
const formattedStats: ComputedRef<IStatisticsChartData> = computed(() => const formattedStats: ComputedRef<IStatisticsChartData> = computed(() =>
formatStats( formatStats(
props.params, props.chartParams,
props.weekStartingMonday, props.user.weekm,
props.sports, props.sports,
[], props.displayedSportIds,
props.statistics statistics.value
) )
) )
onBeforeMount(() =>
getStatistics(getApiParams(props.chartParams, props.user))
)
function getStatistics(apiParams: IStatisticsParams) {
store.dispatch(STATS_STORE.ACTIONS.GET_USER_STATS, {
username: props.user.username,
filterType: 'by_time',
params: apiParams,
})
}
function updateDisplayData(
event: Event & {
target: HTMLInputElement & { name: TStatisticsDatasetKeys }
}
) {
displayedData.value = event.target.name
}
function getApiParams(
chartParams: IStatisticsDateParams,
user: IAuthUserProfile
): IStatisticsParams {
return {
from: format(chartParams.start, 'yyyy-MM-dd'),
to: format(chartParams.end, 'yyyy-MM-dd'),
time:
chartParams.duration === 'week'
? `week${user.weekm ? 'm' : ''}`
: chartParams.duration,
}
}
watch(
() => props.chartParams,
async (newParams) => {
getStatistics(getApiParams(newParams, props.user))
}
)
return { return {
datasets: computed( datasets: computed(
() => formattedStats.value.datasets[props.displayedData] () => formattedStats.value.datasets[displayedData.value]
), ),
labels: computed(() => formattedStats.value.labels), labels: computed(() => formattedStats.value.labels),
displayedData,
t,
updateDisplayData,
} }
}, },
}) })
@ -70,10 +172,16 @@
<style lang="scss" scoped> <style lang="scss" scoped>
@import '~@/scss/base'; @import '~@/scss/base';
.stat-chart { .start-chart {
.chart { .chart-radio {
display: flex;
justify-content: space-between;
padding: $default-padding; padding: $default-padding;
max-height: 100%;
label {
font-size: 0.85em;
font-weight: normal;
}
} }
} }
</style> </style>

View File

@ -3,78 +3,32 @@
<Card> <Card>
<template #title>{{ $t('dashboard.THIS_MONTH') }}</template> <template #title>{{ $t('dashboard.THIS_MONTH') }}</template>
<template #content> <template #content>
<div v-if="Object.keys(statistics).length === 0"> <StatChart
{{ t('workouts.NO_WORKOUTS') }} :sports="sports"
</div> :user="user"
<div v-else> :chart-params="chartParams"
<div class="chart-radio"> :displayed-sport-ids="selectedSportIds"
<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"
/>
</div>
</template> </template>
</Card> </Card>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { endOfMonth, format, startOfMonth } from 'date-fns' import { endOfMonth, startOfMonth } from 'date-fns'
import { import { PropType, defineComponent } from 'vue'
ComputedRef,
PropType,
computed,
defineComponent,
ref,
onBeforeMount,
} from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import Card from '@/components/Common/Card.vue' import Card from '@/components/Common/Card.vue'
import Chart from '@/components/Common/StatsChart/index.vue' import StatChart from '@/components/Common/StatsChart/index.vue'
import { STATS_STORE } from '@/store/constants'
import { ISport } from '@/types/sports' import { ISport } from '@/types/sports'
import { IStatisticsDateParams, TStatisticsFromApi } from '@/types/statistics'
import { IAuthUserProfile } from '@/types/user' import { IAuthUserProfile } from '@/types/user'
import { useStore } from '@/use/useStore'
export default defineComponent({ export default defineComponent({
name: 'UserMonthStats', name: 'UserMonthStats',
components: { components: {
Card, Card,
Chart, StatChart,
}, },
props: { props: {
sports: { sports: {
@ -87,46 +41,16 @@
}, },
}, },
setup(props) { setup(props) {
const store = useStore()
const { t } = useI18n() const { t } = useI18n()
onBeforeMount(() => getStatistics())
const date = new Date() const date = new Date()
const dateFormat = 'yyyy-MM-dd'
let displayedData = ref('total_distance')
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]
)
function updateDisplayData(event: Event & { target: HTMLInputElement }) {
displayedData.value = event.target.name
}
function getStatistics() {
store.dispatch(STATS_STORE.ACTIONS.GET_USER_STATS, {
username: props.user.username,
filterType: 'by_time',
params: apiParams,
})
}
return { return {
weekStartingMonday: computed<boolean>(() => props.user.weekm), chartParams: {
chartParams, duration: 'week',
displayedData, start: startOfMonth(date),
statistics, end: endOfMonth(date),
},
selectedSportIds: props.sports.map((sport) => sport.id),
t, t,
updateDisplayData,
} }
}, },
}) })
@ -138,23 +62,6 @@
.user-month-stats { .user-month-stats {
::v-deep(.card-content) { ::v-deep(.card-content) {
padding: $default-padding; padding: $default-padding;
.stat-chart {
.chart {
.bar-chart {
height: 100%;
}
}
}
.chart-radio {
display: flex;
justify-content: space-between;
padding: $default-padding;
label {
font-size: 0.85em;
font-weight: normal;
}
}
} }
} }
</style> </style>

View File

@ -26,7 +26,9 @@
<div class="nav-item"> <div class="nav-item">
{{ capitalize(t('workouts.WORKOUT', 2)) }} {{ capitalize(t('workouts.WORKOUT', 2)) }}
</div> </div>
<div class="nav-item">{{ t('statistics.STATISTICS') }}</div> <router-link class="nav-item" to="/statistics">
{{ t('statistics.STATISTICS') }}
</router-link>
<div class="nav-item">{{ t('administration.ADMIN') }}</div> <div class="nav-item">{{ t('administration.ADMIN') }}</div>
<router-link class="nav-item" to="/workouts/add"> <router-link class="nav-item" to="/workouts/add">
{{ t('workouts.ADD_WORKOUT') }} {{ t('workouts.ADD_WORKOUT') }}

View File

@ -0,0 +1,330 @@
<template>
<div id="user-statistics">
<Card v-if="translatedSports">
<template #title>{{ $t('statistics.STATISTICS') }}</template>
<template #content>
<div class="chart-filters">
<div class="chart-arrow">
<i
class="fa fa-chevron-left"
aria-hidden="true"
@click="handleOnClickArrows(true)"
/>
</div>
<div class="time-frames">
<div class="time-frames-checkboxes">
<div v-for="frame in timeFrames" class="time-frame" :key="frame">
<label>
<input
type="radio"
:id="frame"
:name="frame"
:checked="selectedTimeFrame === frame"
@input="updateTimeFrame(frame)"
/>
<span>{{ t(`statistics.TIME_FRAMES.${frame}`) }}</span>
</label>
</div>
</div>
</div>
<div class="chart-arrow">
<i
class="fa fa-chevron-right"
aria-hidden="true"
@click="handleOnClickArrows(false)"
/>
</div>
</div>
<StatChart
:sports="sports"
:user="user"
:chartParams="chartParams"
:displayed-sport-ids="selectedSportIds"
:fullStats="true"
/>
<div class="sports">
<label
v-for="sport in translatedSports"
type="checkbox"
:key="sport.id"
:style="{ color: sportColors[sport.label] }"
>
<input
type="checkbox"
:id="sport.id"
:name="sport.label"
:checked="selectedSportIds.includes(sport.id)"
@input="updateSelectedSportIds(sport.id)"
/>
<SportImage :sport-label="sport.label" />
{{ sport.translatedLabel }}
</label>
</div>
</template>
</Card>
</div>
</template>
<script lang="ts">
import {
addMonths,
addWeeks,
addYears,
endOfMonth,
endOfWeek,
endOfYear,
startOfMonth,
startOfWeek,
startOfYear,
subMonths,
subWeeks,
subYears,
} from 'date-fns'
import {
ComputedRef,
PropType,
Ref,
computed,
defineComponent,
ref,
watch,
} from 'vue'
import { useI18n } from 'vue-i18n'
import Card from '@/components/Common/Card.vue'
import SportImage from '@/components/Common/SportImage/index.vue'
import StatChart from '@/components/Common/StatsChart/index.vue'
import { ISport, ITranslatedSport } from '@/types/sports'
import { IStatisticsDateParams } from '@/types/statistics'
import { IAuthUserProfile } from '@/types/user'
import { translateSports, sportColors } from '@/utils/sports'
export default defineComponent({
name: 'UserMonthStats',
components: {
Card,
SportImage,
StatChart,
},
props: {
sports: {
type: Object as PropType<ISport[]>,
required: true,
},
user: {
type: Object as PropType<IAuthUserProfile>,
required: true,
},
},
setup(props) {
const { t } = useI18n()
let selectedTimeFrame = ref('month')
const timeFrames = ['week', 'month', 'year']
const chartParams: Ref<IStatisticsDateParams> = ref(
getChartParams(selectedTimeFrame.value)
)
const translatedSports: ComputedRef<ITranslatedSport[]> = computed(() =>
translateSports(props.sports, t)
)
const selectedSportIds: Ref<number[]> = ref(getSports(props.sports))
function updateTimeFrame(timeFrame: string) {
selectedTimeFrame.value = timeFrame
chartParams.value = getChartParams(selectedTimeFrame.value)
}
function getChartParams(timeFrame: string): IStatisticsDateParams {
const date = new Date()
const start =
timeFrame === 'year'
? startOfYear(subYears(date, 9))
: selectedTimeFrame.value === 'week'
? startOfMonth(subMonths(date, 2))
: startOfMonth(subMonths(date, 11))
const end =
timeFrame === 'year'
? endOfYear(date)
: timeFrame === 'week'
? endOfWeek(date)
: endOfMonth(date)
return {
duration: timeFrame,
end,
start,
}
}
function handleOnClickArrows(backward: boolean) {
chartParams.value = {
duration: selectedTimeFrame.value,
end:
selectedTimeFrame.value === 'year'
? startOfYear(
backward
? endOfYear(subYears(chartParams.value.end, 1))
: endOfYear(addYears(chartParams.value.end, 1))
)
: selectedTimeFrame.value === 'week'
? startOfMonth(
backward
? endOfWeek(subWeeks(chartParams.value.end, 1))
: endOfWeek(addWeeks(chartParams.value.end, 1))
)
: startOfMonth(
backward
? endOfMonth(subMonths(chartParams.value.end, 1))
: endOfMonth(addMonths(chartParams.value.end, 1))
),
start:
selectedTimeFrame.value === 'year'
? startOfYear(
backward
? startOfYear(subYears(chartParams.value.start, 1))
: startOfYear(addYears(chartParams.value.start, 1))
)
: selectedTimeFrame.value === 'week'
? startOfMonth(
backward
? startOfWeek(subWeeks(chartParams.value.start, 1))
: startOfWeek(addWeeks(chartParams.value.start, 1))
)
: startOfMonth(
backward
? startOfMonth(subMonths(chartParams.value.start, 1))
: startOfMonth(addMonths(chartParams.value.start, 1))
),
}
}
function getSports(sports: ISport[]) {
return sports.map((sport) => sport.id)
}
function updateSelectedSportIds(sportId: number) {
if (selectedSportIds.value.includes(sportId)) {
selectedSportIds.value = selectedSportIds.value.filter(
(id) => id !== sportId
)
} else {
selectedSportIds.value.push(sportId)
}
}
watch(
() => props.sports,
(newSports) => {
selectedSportIds.value = getSports(newSports)
}
)
return {
chartParams,
selectedTimeFrame,
sportColors,
t,
timeFrames,
translatedSports,
selectedSportIds,
handleOnClickArrows,
updateSelectedSportIds,
updateTimeFrame,
}
},
})
</script>
<style lang="scss" scoped>
@import '~@/scss/base';
#user-statistics {
display: flex;
width: 100%;
margin-bottom: 30px;
::v-deep(.card) {
width: 100%;
.card-content {
.chart-filters {
display: flex;
.chart-arrow,
.time-frames {
flex-grow: 1;
text-align: center;
}
.chart-arrow {
cursor: pointer;
}
.time-frames {
display: flex;
justify-content: space-around;
.time-frames-checkboxes {
display: inline-flex;
.time-frame {
label {
font-weight: normal;
float: left;
padding: 0 5px;
cursor: pointer;
}
label input {
display: none;
}
label span {
border: solid 1px var(--time-frame-border-color);
border-radius: 9%;
display: block;
font-size: 0.9em;
padding: 2px 6px;
text-align: center;
}
input:checked + span {
background-color: var(--time-frame-checked-bg-color);
color: var(--time-frame-checked-color);
}
}
}
}
}
.chart-radio {
justify-content: space-around;
padding: $default-padding * 2 $default-padding;
}
.sports {
display: flex;
justify-content: space-between;
padding: $default-padding * 2 $default-padding;
@media screen and (max-width: $medium-limit) {
justify-content: normal;
flex-wrap: wrap;
}
label {
display: flex;
align-items: center;
font-size: 0.9em;
font-weight: normal;
min-width: 120px;
padding: $default-padding;
@media screen and (max-width: $medium-limit) {
min-width: 100px;
}
@media screen and (max-width: $x-small-limit) {
width: 100%;
}
}
.sport-img {
padding: 3px;
width: 20px;
height: 20px;
}
}
}
}
}
</style>

View File

@ -1,4 +1,9 @@
{ {
"STATISTICS": "Statistics", "STATISTICS": "Statistics",
"TOTAL": "Total" "TOTAL": "Total",
"TIME_FRAMES": {
"week": "week",
"month": "month",
"year": "year"
}
} }

View File

@ -1,4 +1,9 @@
{ {
"STATISTICS": "Statistiques", "STATISTICS": "Statistiques",
"TOTAL": "Total" "TOTAL": "Total",
"TIME_FRAMES": {
"week": "semaine",
"month": "mois",
"year": "année"
}
} }

View File

@ -7,6 +7,7 @@ import Dashboard from '@/views/DashBoard.vue'
import EditWorkout from '@/views/EditWorkout.vue' import EditWorkout from '@/views/EditWorkout.vue'
import LoginOrRegister from '@/views/LoginOrRegister.vue' import LoginOrRegister from '@/views/LoginOrRegister.vue'
import NotFoundView from '@/views/NotFoundView.vue' import NotFoundView from '@/views/NotFoundView.vue'
import StatisticsView from '@/views/StatisticsView.vue'
import Workout from '@/views/Workout.vue' import Workout from '@/views/Workout.vue'
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
@ -27,6 +28,11 @@ const routes: Array<RouteRecordRaw> = [
component: LoginOrRegister, component: LoginOrRegister,
props: { action: 'register' }, props: { action: 'register' },
}, },
{
path: '/statistics',
name: 'Statistics',
component: StatisticsView,
},
{ {
path: '/workouts/:workoutId', path: '/workouts/:workoutId',
name: 'Workout', name: 'Workout',

View File

@ -16,6 +16,10 @@
--card-border-color: #c4c7cf; --card-border-color: #c4c7cf;
--input-border-color: #9da3af; --input-border-color: #9da3af;
--time-frame-border-color: #9da3af;
--time-frame-checked-bg-color: #9da3af;
--time-frame-checked-color: #FFFFFF;
--calendar-border-color: #c4c7cf; --calendar-border-color: #c4c7cf;
--calendar-week-end-color: #f5f5f5; --calendar-week-end-color: #f5f5f5;
--calendar-today-color: #eff1f3; --calendar-today-color: #eff1f3;

View File

@ -76,7 +76,7 @@ export const formatStats = (
const dayKeys = getDateKeys(params, weekStartingMonday) const dayKeys = getDateKeys(params, weekStartingMonday)
const dateFormat = dateFormats[params.duration] const dateFormat = dateFormats[params.duration]
const displayedSports = sports.filter((sport) => const displayedSports = sports.filter((sport) =>
displayedSportsId.length == 0 ? true : displayedSportsId.includes(sport.id) displayedSportsId.includes(sport.id)
) )
const labels: string[] = [] const labels: string[] = []
const datasets = getDatasets(displayedSports) const datasets = getDatasets(displayedSports)

View File

@ -0,0 +1,41 @@
<template>
<div id="statistics">
<div class="container" v-if="authUser.username">
<Statistics :user="authUser" :sports="sports" />
</div>
</div>
</template>
<script lang="ts">
import { ComputedRef, computed, defineComponent } from 'vue'
import Statistics from '@/components/Statistics/index.vue'
import { USER_STORE, SPORTS_STORE } from '@/store/constants'
import { ISport } from '@/types/sports'
import { IAuthUserProfile } from '@/types/user'
import { useStore } from '@/use/useStore'
export default defineComponent({
name: 'StatisticsView',
components: {
Statistics,
},
setup() {
const store = useStore()
const authUser: ComputedRef<IAuthUserProfile> = computed(
() => store.getters[USER_STORE.GETTERS.AUTH_USER_PROFILE]
)
const sports: ComputedRef<ISport[]> = computed(
() => store.getters[SPORTS_STORE.GETTERS.SPORTS]
)
return { authUser, sports }
},
})
</script>
<style lang="scss" scoped>
@import '~@/scss/base';
#statistics {
height: 100%;
}
</style>

View File

@ -254,57 +254,9 @@ describe('formatStats', () => {
const expected: IStatisticsChartData = { const expected: IStatisticsChartData = {
labels: ['2021-05', '2021-06', '2021-07'], labels: ['2021-05', '2021-06', '2021-07'],
datasets: { datasets: {
nb_workouts: [ nb_workouts: [],
{ total_distance: [],
label: 'Cycling (Sport)', total_duration: [],
backgroundColor: ['#4c9792'],
data: [0, 0, 0],
},
{
label: 'Cycling (Transport)',
backgroundColor: ['#88af98'],
data: [0, 0, 0],
},
{
label: 'Hiking',
backgroundColor: ['#bb757c'],
data: [0, 0, 0],
},
],
total_distance: [
{
label: 'Cycling (Sport)',
backgroundColor: ['#4c9792'],
data: [0, 0, 0],
},
{
label: 'Cycling (Transport)',
backgroundColor: ['#88af98'],
data: [0, 0, 0],
},
{
label: 'Hiking',
backgroundColor: ['#bb757c'],
data: [0, 0, 0],
},
],
total_duration: [
{
label: 'Cycling (Sport)',
backgroundColor: ['#4c9792'],
data: [0, 0, 0],
},
{
label: 'Cycling (Transport)',
backgroundColor: ['#88af98'],
data: [0, 0, 0],
},
{
label: 'Hiking',
backgroundColor: ['#bb757c'],
data: [0, 0, 0],
},
],
}, },
} }
assert.deepEqual( assert.deepEqual(
@ -352,7 +304,7 @@ describe('formatStats', () => {
) )
}) })
it('returns empty datasets if data and no displayed sport provided', () => { it('returns empty datasets if data provided but no displayed sport', () => {
const inputStats: TStatisticsFromApi = { const inputStats: TStatisticsFromApi = {
'2021-05': { '2021-05': {
1: { 1: {
@ -389,57 +341,9 @@ describe('formatStats', () => {
const expected: IStatisticsChartData = { const expected: IStatisticsChartData = {
labels: ['2021-05', '2021-06', '2021-07'], labels: ['2021-05', '2021-06', '2021-07'],
datasets: { datasets: {
nb_workouts: [ nb_workouts: [],
{ total_distance: [],
label: 'Cycling (Sport)', total_duration: [],
backgroundColor: ['#4c9792'],
data: [1, 1, 0],
},
{
label: 'Cycling (Transport)',
backgroundColor: ['#88af98'],
data: [0, 2, 0],
},
{
label: 'Hiking',
backgroundColor: ['#bb757c'],
data: [0, 0, 2],
},
],
total_distance: [
{
label: 'Cycling (Sport)',
backgroundColor: ['#4c9792'],
data: [10, 15, 0],
},
{
label: 'Cycling (Transport)',
backgroundColor: ['#88af98'],
data: [0, 20, 0],
},
{
label: 'Hiking',
backgroundColor: ['#bb757c'],
data: [0, 0, 12],
},
],
total_duration: [
{
label: 'Cycling (Sport)',
backgroundColor: ['#4c9792'],
data: [3000, 3500, 0],
},
{
label: 'Cycling (Transport)',
backgroundColor: ['#88af98'],
data: [0, 3000, 0],
},
{
label: 'Hiking',
backgroundColor: ['#bb757c'],
data: [0, 0, 5000],
},
],
}, },
} }
assert.deepEqual( assert.deepEqual(