Client - add user sports preferences

+ minor refactor
This commit is contained in:
Sam 2021-11-12 18:52:08 +01:00
parent 7afdd04d7d
commit 7c49fd31ad
67 changed files with 500 additions and 101 deletions

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"><link rel="stylesheet" href="/static/css/leaflet.css"><title>FitTrackee</title><link href="/static/css/admin.04e24276.css" rel="prefetch"><link href="/static/css/main.c790adb1.css" rel="prefetch"><link href="/static/css/main~workouts.66c5ef05.css" rel="prefetch"><link href="/static/css/profile.b52bc193.css" rel="prefetch"><link href="/static/css/reset.bd9657a8.css" rel="prefetch"><link href="/static/css/workouts.d0767062.css" rel="prefetch"><link href="/static/js/admin.2f1d393d.js" rel="prefetch"><link href="/static/js/chunk-2d0c9189.c81458cc.js" rel="prefetch"><link href="/static/js/chunk-2d0cf391.020c75ea.js" rel="prefetch"><link href="/static/js/chunk-2d0da8f3.c8c3e7e8.js" rel="prefetch"><link href="/static/js/chunk-2d2248b6.d84473c1.js" rel="prefetch"><link href="/static/js/chunk-2d22523a.4b710d99.js" rel="prefetch"><link href="/static/js/main.e5da50b8.js" rel="prefetch"><link href="/static/js/main~workouts.a74990d7.js" rel="prefetch"><link href="/static/js/profile.6a786c1d.js" rel="prefetch"><link href="/static/js/reset.518e646f.js" rel="prefetch"><link href="/static/js/workouts.1c22fd12.js" rel="prefetch"><link href="/static/css/app.534b9c5c.css" rel="preload" as="style"><link href="/static/js/app.9ada5ac5.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.71654064.js" rel="preload" as="script"><link href="/static/css/app.534b9c5c.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.71654064.js"></script><script src="/static/js/app.9ada5ac5.js"></script></body></html> <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"><link rel="stylesheet" href="/static/css/leaflet.css"><title>FitTrackee</title><link href="/static/css/admin.dc8b6d66.css" rel="prefetch"><link href="/static/css/main.411e7bd3.css" rel="prefetch"><link href="/static/css/main~workouts.c8c5694b.css" rel="prefetch"><link href="/static/css/profile.314b1d45.css" rel="prefetch"><link href="/static/css/reset.a71577d5.css" rel="prefetch"><link href="/static/css/workouts.773dfff0.css" rel="prefetch"><link href="/static/js/admin.2f1d393d.js" rel="prefetch"><link href="/static/js/chunk-2d0c9189.c81458cc.js" rel="prefetch"><link href="/static/js/chunk-2d0cf391.020c75ea.js" rel="prefetch"><link href="/static/js/chunk-2d0da8f3.c8c3e7e8.js" rel="prefetch"><link href="/static/js/chunk-2d2248b6.d84473c1.js" rel="prefetch"><link href="/static/js/chunk-2d22523a.4b710d99.js" rel="prefetch"><link href="/static/js/main.88fa3c28.js" rel="prefetch"><link href="/static/js/main~workouts.a74990d7.js" rel="prefetch"><link href="/static/js/profile.62578012.js" rel="prefetch"><link href="/static/js/reset.518e646f.js" rel="prefetch"><link href="/static/js/workouts.46dd8fa5.js" rel="prefetch"><link href="/static/css/app.97115085.css" rel="preload" as="style"><link href="/static/js/app.38e0c4d5.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.71654064.js" rel="preload" as="script"><link href="/static/css/app.97115085.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.71654064.js"></script><script src="/static/js/app.38e0c4d5.js"></script></body></html>

View File

@ -64,7 +64,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/img/workouts/mountains.svg" "url": "/img/workouts/mountains.svg"
}, },
{ {
"revision": "04e22b3a72bc3a810319eb7a512e4b23", "revision": "59c5cdf1d1708e7f936a0a30db0bbffb",
"url": "/index.html" "url": "/index.html"
}, },
{ {
@ -76,12 +76,12 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/robots.txt" "url": "/robots.txt"
}, },
{ {
"revision": "ca5244fc3bcfc65816b9", "revision": "55e1f50bd31cac2908e3",
"url": "/static/css/admin.04e24276.css" "url": "/static/css/admin.dc8b6d66.css"
}, },
{ {
"revision": "fe2e967e1efad7b13e67", "revision": "9ae7710525db019efc86",
"url": "/static/css/app.534b9c5c.css" "url": "/static/css/app.97115085.css"
}, },
{ {
"revision": "82c1118c918377daaa71a320ab8eea42", "revision": "82c1118c918377daaa71a320ab8eea42",
@ -92,24 +92,24 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/css/leaflet.css" "url": "/static/css/leaflet.css"
}, },
{ {
"revision": "d3cf46cfc6340753d540", "revision": "12cfbc42bf674769c6f0",
"url": "/static/css/main.c790adb1.css" "url": "/static/css/main.411e7bd3.css"
}, },
{ {
"revision": "68364924c988a1f11b42", "revision": "ce60ed388b792b0e9a0e",
"url": "/static/css/main~workouts.66c5ef05.css" "url": "/static/css/main~workouts.c8c5694b.css"
}, },
{ {
"revision": "3438ac3f32223591afd9", "revision": "74137feddeb35e2de067",
"url": "/static/css/profile.b52bc193.css" "url": "/static/css/profile.314b1d45.css"
}, },
{ {
"revision": "688d813785d3c55a7d33", "revision": "6066a5f13daad652feea",
"url": "/static/css/reset.bd9657a8.css" "url": "/static/css/reset.a71577d5.css"
}, },
{ {
"revision": "5e13fc66c78986a630a0", "revision": "9a0901331d45e214aa27",
"url": "/static/css/workouts.d0767062.css" "url": "/static/css/workouts.773dfff0.css"
}, },
{ {
"revision": "e719f9244c69e28e7d00e725ca1e280e", "revision": "e719f9244c69e28e7d00e725ca1e280e",
@ -192,12 +192,12 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/img/pt-sans-v9-latin-regular.f1f73e45.svg" "url": "/static/img/pt-sans-v9-latin-regular.f1f73e45.svg"
}, },
{ {
"revision": "ca5244fc3bcfc65816b9", "revision": "55e1f50bd31cac2908e3",
"url": "/static/js/admin.2f1d393d.js" "url": "/static/js/admin.2f1d393d.js"
}, },
{ {
"revision": "fe2e967e1efad7b13e67", "revision": "9ae7710525db019efc86",
"url": "/static/js/app.9ada5ac5.js" "url": "/static/js/app.38e0c4d5.js"
}, },
{ {
"revision": "bd7d183c9f68e5f4027d", "revision": "bd7d183c9f68e5f4027d",
@ -224,23 +224,23 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/js/chunk-vendors.71654064.js" "url": "/static/js/chunk-vendors.71654064.js"
}, },
{ {
"revision": "d3cf46cfc6340753d540", "revision": "12cfbc42bf674769c6f0",
"url": "/static/js/main.e5da50b8.js" "url": "/static/js/main.88fa3c28.js"
}, },
{ {
"revision": "68364924c988a1f11b42", "revision": "ce60ed388b792b0e9a0e",
"url": "/static/js/main~workouts.a74990d7.js" "url": "/static/js/main~workouts.a74990d7.js"
}, },
{ {
"revision": "3438ac3f32223591afd9", "revision": "74137feddeb35e2de067",
"url": "/static/js/profile.6a786c1d.js" "url": "/static/js/profile.62578012.js"
}, },
{ {
"revision": "688d813785d3c55a7d33", "revision": "6066a5f13daad652feea",
"url": "/static/js/reset.518e646f.js" "url": "/static/js/reset.518e646f.js"
}, },
{ {
"revision": "5e13fc66c78986a630a0", "revision": "9a0901331d45e214aa27",
"url": "/static/js/workouts.1c22fd12.js" "url": "/static/js/workouts.46dd8fa5.js"
} }
]); ]);

View File

@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts( importScripts(
"/precache-manifest.200309f56fa439f700e940d169066203.js" "/precache-manifest.94b00578e2690280739258ebb12c465f.js"
); );
workbox.core.setCacheNameDetails({prefix: "fittrackee_client"}); workbox.core.setCacheNameDetails({prefix: "fittrackee_client"});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["profile"],{"0bb3":function(e,t,n){},"36e8":function(e,t,n){"use strict";n.r(t);var c=n("7a23"),r=n("dad5"),o=n("2906"),u=function(e){return Object(c["pushScopeId"])("data-v-d342b648"),e=e(),Object(c["popScopeId"])(),e},a={key:0,id:"profile",class:"container view"},s=u((function(){return Object(c["createElementVNode"])("div",{id:"bottom"},null,-1)})),d=Object(c["defineComponent"])({setup:function(e){var t=Object(o["a"])(),n=Object(c["computed"])((function(){return t.getters[r["a"].GETTERS.AUTH_USER_PROFILE]}));return function(e,t){var r=Object(c["resolveComponent"])("router-view");return Object(c["unref"])(n).username?(Object(c["openBlock"])(),Object(c["createElementBlock"])("div",a,[Object(c["createVNode"])(r,{user:Object(c["unref"])(n)},null,8,["user"]),s])):Object(c["createCommentVNode"])("",!0)}}}),b=(n("6171"),n("6b0d")),i=n.n(b);const f=i()(d,[["__scopeId","data-v-d342b648"]]);t["default"]=f},6171:function(e,t,n){"use strict";n("0bb3")},"9b98":function(e,t,n){"use strict";n("d332")},ad3d:function(e,t,n){"use strict";n.r(t);var c=n("7a23"),r=n("6c02"),o=n("3c44"),u=n("71a7"),a=n("dad5"),s=n("2906"),d={key:0,id:"user",class:"view"},b={class:"box"},i=Object(c["defineComponent"])({setup:function(e){var t=Object(r["c"])(),n=Object(s["a"])(),i=Object(c["computed"])((function(){return n.getters[a["e"].GETTERS.USER]}));return Object(c["onBeforeMount"])((function(){t.params.username&&"string"===typeof t.params.username&&n.dispatch(a["e"].ACTIONS.GET_USER,t.params.username)})),Object(c["onBeforeUnmount"])((function(){n.dispatch(a["e"].ACTIONS.EMPTY_USER)})),function(e,t){return Object(c["unref"])(i).username?(Object(c["openBlock"])(),Object(c["createElementBlock"])("div",d,[Object(c["createVNode"])(o["a"],{user:Object(c["unref"])(i)},null,8,["user"]),Object(c["createElementVNode"])("div",b,[Object(c["createVNode"])(u["a"],{user:Object(c["unref"])(i),"from-admin":!0},null,8,["user"])])])):Object(c["createCommentVNode"])("",!0)}}}),f=(n("9b98"),n("6b0d")),O=n.n(f);const p=O()(i,[["__scopeId","data-v-218f8f1e"]]);t["default"]=p},d332:function(e,t,n){}}]);
//# sourceMappingURL=profile.62578012.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["profile"],{"36e8":function(e,t,n){"use strict";n.r(t);var c=n("7a23"),r=n("dad5"),o=n("2906"),u={key:0,id:"profile",class:"container view"},a=Object(c["defineComponent"])({setup:function(e){var t=Object(o["a"])(),n=Object(c["computed"])((function(){return t.getters[r["a"].GETTERS.AUTH_USER_PROFILE]}));return function(e,t){var r=Object(c["resolveComponent"])("router-view");return Object(c["unref"])(n).username?(Object(c["openBlock"])(),Object(c["createElementBlock"])("div",u,[Object(c["createVNode"])(r,{user:Object(c["unref"])(n)},null,8,["user"])])):Object(c["createCommentVNode"])("",!0)}}}),s=(n("44ab"),n("6b0d")),b=n.n(s);const i=b()(a,[["__scopeId","data-v-bb090bfa"]]);t["default"]=i},"44ab":function(e,t,n){"use strict";n("4fe6")},"4fe6":function(e,t,n){},"9b98":function(e,t,n){"use strict";n("d332")},ad3d:function(e,t,n){"use strict";n.r(t);var c=n("7a23"),r=n("6c02"),o=n("3c44"),u=n("71a7"),a=n("dad5"),s=n("2906"),b={key:0,id:"user",class:"view"},i={class:"box"},d=Object(c["defineComponent"])({setup:function(e){var t=Object(r["c"])(),n=Object(s["a"])(),d=Object(c["computed"])((function(){return n.getters[a["e"].GETTERS.USER]}));return Object(c["onBeforeMount"])((function(){t.params.username&&"string"===typeof t.params.username&&n.dispatch(a["e"].ACTIONS.GET_USER,t.params.username)})),Object(c["onBeforeUnmount"])((function(){n.dispatch(a["e"].ACTIONS.EMPTY_USER)})),function(e,t){return Object(c["unref"])(d).username?(Object(c["openBlock"])(),Object(c["createElementBlock"])("div",b,[Object(c["createVNode"])(o["a"],{user:Object(c["unref"])(d)},null,8,["user"]),Object(c["createElementVNode"])("div",i,[Object(c["createVNode"])(u["a"],{user:Object(c["unref"])(d),"from-admin":!0},null,8,["user"])])])):Object(c["createCommentVNode"])("",!0)}}}),f=(n("9b98"),n("6b0d")),O=n.n(f);const j=O()(d,[["__scopeId","data-v-218f8f1e"]]);t["default"]=j},d332:function(e,t,n){}}]);
//# sourceMappingURL=profile.6a786c1d.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -35,6 +35,7 @@
<SportImage <SportImage
:title="sport.translatedLabel" :title="sport.translatedLabel"
:sport-label="sport.label" :sport-label="sport.label"
:color="sport.color"
/> />
</td> </td>
<td class="sport-label"> <td class="sport-label">
@ -127,9 +128,6 @@
font-style: italic; font-style: italic;
padding: 0 $default-padding; padding: 0 $default-padding;
} }
.text-left {
text-align: left;
}
.sport-action { .sport-action {
padding-left: $default-padding * 4; padding-left: $default-padding * 4;
} }

View File

@ -1,7 +1,7 @@
<template> <template>
<div <div
class="sport-img" class="sport-img"
:style="{ fill: sportColors[sportLabel] }" :style="{ fill: color ? color : sportColors[sportLabel] }"
:title="title ? title : $t(`sports.${sportLabel}.LABEL`)" :title="title ? title : $t(`sports.${sportLabel}.LABEL`)"
> >
<CyclingSport v-if="sportLabel === 'Cycling (Sport)'" /> <CyclingSport v-if="sportLabel === 'Cycling (Sport)'" />
@ -37,12 +37,13 @@
interface Props { interface Props {
sportLabel: string sportLabel: string
color: string | null
title?: string title?: string
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
title: '', title: '',
}) })
const { sportLabel, title } = toRefs(props) const { color, sportLabel, title } = toRefs(props)
const sportColors = inject('sportColors') const sportColors = inject('sportColors')
</script> </script>

View File

@ -5,7 +5,11 @@
$router.push({ name: 'Workout', params: { workoutId: workout.id } }) $router.push({ name: 'Workout', params: { workoutId: workout.id } })
" "
> >
<SportImage :sport-label="sportLabel" :title="workout.title" /> <SportImage
:sport-label="sportLabel"
:title="workout.title"
:color="sportColor"
/>
<sup> <sup>
<i <i
v-if="workout.records.length > 0" v-if="workout.records.length > 0"
@ -28,6 +32,7 @@
interface Props { interface Props {
workout: IWorkout workout: IWorkout
sportLabel: string sportLabel: string
sportColor: string | null
} }
const props = defineProps<Props>() const props = defineProps<Props>()

View File

@ -10,6 +10,7 @@
:key="index" :key="index"
:workout="workout" :workout="workout"
:sportLabel="getSportLabel(workout, sports)" :sportLabel="getSportLabel(workout, sports)"
:sportColor="getSportColor(workout, sports)"
/> />
</div> </div>
<div v-else class="donut-display"> <div v-else class="donut-display">
@ -41,7 +42,7 @@
import CalendarWorkoutsChart from '@/components/Dashboard/UserCalendar/CalendarWorkoutsChart.vue' import CalendarWorkoutsChart from '@/components/Dashboard/UserCalendar/CalendarWorkoutsChart.vue'
import { ISport } from '@/types/sports' import { ISport } from '@/types/sports'
import { IWorkout } from '@/types/workouts' import { IWorkout } from '@/types/workouts'
import { getSportLabel, sportIdColors } from '@/utils/sports' import { getSportColor, getSportLabel, sportIdColors } from '@/utils/sports'
import { getDonutDatasets } from '@/utils/workouts' import { getDonutDatasets } from '@/utils/workouts'
interface Props { interface Props {

View File

@ -2,7 +2,7 @@
<div class="records-card"> <div class="records-card">
<Card> <Card>
<template #title> <template #title>
<SportImage :sport-label="records.label" /> <SportImage :sport-label="records.label" :color="records.color" />
{{ sportTranslatedLabel }} {{ sportTranslatedLabel }}
</template> </template>
<template #content> <template #content>

View File

@ -13,7 +13,7 @@
:checked="selectedSportIds.includes(sport.id)" :checked="selectedSportIds.includes(sport.id)"
@input="updateSelectedSportIds(sport.id)" @input="updateSelectedSportIds(sport.id)"
/> />
<SportImage :sport-label="sport.label" /> <SportImage :sport-label="sport.label" :color="sport.color" />
<span class="sport-label">{{ sport.translatedLabel }}</span> <span class="sport-label">{{ sport.translatedLabel }}</span>
</label> </label>
</div> </div>

View File

@ -91,9 +91,5 @@
.user-bio { .user-bio {
white-space: pre-wrap; white-space: pre-wrap;
} }
.profile-buttons {
display: flex;
gap: $default-padding;
}
} }
</style> </style>

View File

@ -38,13 +38,3 @@
props.user.timezone ? props.user.timezone : 'Europe/Paris' props.user.timezone ? props.user.timezone : 'Europe/Paris'
) )
</script> </script>
<style lang="scss" scoped>
@import '~@/scss/base.scss';
#user-preferences {
.profile-buttons {
display: flex;
gap: $default-padding;
}
}
</style>

View File

@ -22,7 +22,7 @@
const props = defineProps<Props>() const props = defineProps<Props>()
const { user, tab } = toRefs(props) const { user, tab } = toRefs(props)
const tabs = ['PROFILE', 'PREFERENCES'] const tabs = ['PROFILE', 'PREFERENCES', 'SPORTS']
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -34,7 +34,7 @@
const store = useStore() const store = useStore()
const { user, tab } = toRefs(props) const { user, tab } = toRefs(props)
const tabs = ['PROFILE', 'PICTURE', 'PREFERENCES'] const tabs = ['PROFILE', 'PICTURE', 'PREFERENCES', 'SPORTS']
const loading = computed( const loading = computed(
() => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING] () => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]
) )

View File

@ -38,7 +38,10 @@
case 'PICTURE': case 'PICTURE':
return '/profile/edit/picture' return '/profile/edit/picture'
case 'PREFERENCES': case 'PREFERENCES':
return `/profile${props.edition ? '/edit' : ''}/preferences` case 'SPORTS':
return `/profile${
props.edition ? '/edit' : ''
}/${tab.toLocaleLowerCase()}`
default: default:
case 'PROFILE': case 'PROFILE':
return `/profile${props.edition ? '/edit' : ''}` return `/profile${props.edition ? '/edit' : ''}`

View File

@ -0,0 +1,280 @@
<template>
<div id="user-sport-preferences">
<div class="responsive-table" v-if="sports.length > 0">
<div class="mobile-display">
<div v-if="isEdition" class="profile-buttons mobile-display">
<button
class="cancel"
@click.prevent="$router.push('/profile/sports')"
>
{{ $t('buttons.BACK') }}
</button>
</div>
<div v-else class="profile-buttons">
<button @click="$router.push('/profile/edit/sports')">
{{ $t('user.PROFILE.EDIT_SPORTS_PREFERENCES') }}
</button>
<button @click="$router.push('/')">{{ $t('common.HOME') }}</button>
</div>
</div>
<table>
<thead>
<tr>
<th>{{ $t('user.PROFILE.SPORT.COLOR') }}</th>
<th class="text-left">{{ $t('workouts.SPORT', 0) }}</th>
<th>{{ $t('user.PROFILE.SPORT.IS_ACTIVE') }}</th>
<th>{{ $t('user.PROFILE.SPORT.STOPPED_SPEED_THRESHOLD') }}</th>
<th v-if="isEdition">{{ $t('user.PROFILE.SPORT.ACTION') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="sport in translatedSports" :key="sport.id">
<td>
<span class="cell-heading">
{{ $t('user.PROFILE.SPORT.COLOR') }}
</span>
<input
v-if="isSportInEdition(sport.id)"
class="sport-color"
type="color"
:value="sportPayload.color"
@input="updateColor"
/>
<SportImage
v-else
:title="sport.translatedLabel"
:sport-label="sport.label"
:color="sport.color ? sport.color : sportColors[sport.label]"
/>
</td>
<td class="sport-label">
<span class="cell-heading">
{{ $t('user.PROFILE.SPORT.LABEL') }}
</span>
{{ sport.translatedLabel }}
<i
v-if="loading && isSportInEdition(sport.id)"
class="fa fa-refresh fa-spin fa-fw"
/>
<ErrorMessage
:message="errorMessages"
v-if="errorMessages && sportPayload.sport_id === sport.id"
/>
</td>
<td class="text-center">
<span class="cell-heading">
{{ $t('user.PROFILE.SPORT.IS_ACTIVE') }}
</span>
<input
v-if="isSportInEdition(sport.id)"
type="checkbox"
:checked="sport.is_active_for_user"
@change="updateIsActive"
/>
<i
v-else
:class="`fa fa${
sport.is_active_for_user ? '-check' : ''
}-square-o`"
aria-hidden="true"
/>
</td>
<td class="text-center">
<span class="cell-heading">
{{ $t('user.PROFILE.SPORT.STOPPED_SPEED_THRESHOLD') }}
</span>
<input
class="threshold-input"
v-if="isSportInEdition(sport.id)"
type="number"
min="0"
step="0.1"
:value="sportPayload.stopped_speed_threshold"
@input="updateThreshold"
/>
<span v-else>
{{ sport.stopped_speed_threshold }}
</span>
</td>
<td v-if="isEdition" class="action-buttons">
<span class="cell-heading">
{{ $t('user.PROFILE.SPORT.ACTION') }}
</span>
<button
v-if="sportPayload.sport_id === 0"
@click="updateSportInEdition(sport)"
>
{{ $t('buttons.EDIT') }}
</button>
<div v-if="isSportInEdition(sport.id)" class="edition-buttons">
<button :disabled="loading" @click="updateSport">
{{ $t('buttons.SUBMIT') }}
</button>
<button :disabled="loading" @click="updateSportInEdition(null)">
{{ $t('buttons.CANCEL') }}
</button>
</div>
</td>
</tr>
</tbody>
</table>
<div v-if="isEdition" class="profile-buttons">
<button class="cancel" @click.prevent="$router.push('/profile/sports')">
{{ $t('buttons.BACK') }}
</button>
</div>
<div v-else class="profile-buttons">
<button @click="$router.push('/profile/edit/sports')">
{{ $t('user.PROFILE.EDIT_SPORTS_PREFERENCES') }}
</button>
<button @click="$router.push('/')">{{ $t('common.HOME') }}</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ComputedRef, computed, inject, reactive, toRefs, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { AUTH_USER_STORE, ROOT_STORE, SPORTS_STORE } from '@/store/constants'
import { ISport, ITranslatedSport } from '@/types/sports'
import { IUserSportPreferencesPayload } from '@/types/user'
import { useStore } from '@/use/useStore'
import { translateSports } from '@/utils/sports'
interface Props {
isEdition: boolean
}
const props = defineProps<Props>()
const store = useStore()
const { t } = useI18n()
const { isEdition } = toRefs(props)
const sportColors = inject('sportColors')
const sports: ComputedRef<ISport[]> = computed(
() => store.getters[SPORTS_STORE.GETTERS.SPORTS]
)
const translatedSports: ComputedRef<ITranslatedSport[]> = computed(() =>
translateSports(sports.value, t)
)
const loading = computed(
() => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]
)
const errorMessages: ComputedRef<string | string[] | null> = computed(
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
)
const sportPayload: IUserSportPreferencesPayload = reactive({
sport_id: 0,
color: null,
is_active: true,
stopped_speed_threshold: 1,
})
function updateSportInEdition(sport: ISport | null) {
if (sport !== null) {
sportPayload.sport_id = sport.id
sportPayload.color = sport.color ? sport.color : sportColors[sport.label]
sportPayload.is_active = sport.is_active_for_user
sportPayload.stopped_speed_threshold = sport.stopped_speed_threshold
} else {
resetSportPayload()
}
}
function isSportInEdition(sportId: number) {
return sportPayload.sport_id === sportId
}
function updateColor(event: Event & { target: HTMLInputElement }) {
sportPayload.color = event.target.value
}
function updateThreshold(event: Event & { target: HTMLInputElement }) {
sportPayload.stopped_speed_threshold = parseFloat(event.target.value)
}
function updateIsActive(event: Event & { target: HTMLInputElement }) {
sportPayload.is_active = event.target.checked
}
function resetSportPayload() {
sportPayload.sport_id = 0
sportPayload.color = null
sportPayload.is_active = true
sportPayload.stopped_speed_threshold = 1
store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
}
function updateSport(event: Event) {
event.preventDefault()
store.dispatch(
AUTH_USER_STORE.ACTIONS.UPDATE_USER_SPORT_PREFERENCES,
sportPayload
)
}
watch(
() => loading.value,
(newIsLoading) => {
if (!newIsLoading && !errorMessages.value) {
resetSportPayload()
}
}
)
</script>
<style lang="scss" scoped>
@import '~@/scss/base.scss';
#user-sport-preferences {
.sport-img {
height: 35px;
width: 35px;
margin: 0 auto;
}
.sport-color {
border: none;
margin: 6px 1px 6px 0;
padding: 0;
width: 40px;
}
.sport-label {
width: 170px;
}
.action-buttons {
width: 70px;
}
.edition-buttons {
display: flex;
flex-wrap: wrap;
gap: $default-padding * 0.5;
line-height: 1.3em;
button {
text-align: center;
min-width: 80px;
}
}
.threshold-input {
padding: $default-padding * 0.5;
width: 50px;
}
.mobile-display {
display: none;
}
div.error-message {
margin: 0;
}
@media screen and (max-width: $small-limit) {
.sport-label {
width: 100%;
}
.action-buttons {
width: 100%;
}
.edition-buttons {
justify-content: center;
}
.mobile-display {
display: flex;
margin: $default-margin * 2 0 $default-margin;
}
}
}
</style>

View File

@ -75,7 +75,11 @@
" "
> >
<div class="img"> <div class="img">
<SportImage v-if="sport.label" :sport-label="sport.label" /> <SportImage
v-if="sport.label"
:sport-label="sport.label"
:color="sport.color"
/>
</div> </div>
<div class="data"> <div class="data">
<i class="fa fa-clock-o" aria-hidden="true" /> <i class="fa fa-clock-o" aria-hidden="true" />

View File

@ -17,7 +17,7 @@
<i class="fa fa-chevron-left" aria-hidden="true" /> <i class="fa fa-chevron-left" aria-hidden="true" />
</div> </div>
<div class="workout-card-title"> <div class="workout-card-title">
<SportImage :sport-label="sport.label" /> <SportImage :sport-label="sport.label" :color="sport.color" />
<div class="workout-title-date"> <div class="workout-title-date">
<div class="workout-title" v-if="workoutObject.type === 'WORKOUT'"> <div class="workout-title" v-if="workoutObject.type === 'WORKOUT'">
{{ workoutObject.title }} {{ workoutObject.title }}

View File

@ -46,7 +46,7 @@
v-model="workoutForm.sport_id" v-model="workoutForm.sport_id"
> >
<option <option
v-for="sport in translatedSports.filter((s) => s.is_active)" v-for="sport in translatedSports"
:value="sport.id" :value="sport.id"
:key="sport.id" :key="sport.id"
> >
@ -259,7 +259,7 @@
const { workout, isCreation, loading } = toRefs(props) const { workout, isCreation, loading } = toRefs(props)
const translatedSports: ComputedRef<ISport[]> = computed(() => const translatedSports: ComputedRef<ISport[]> = computed(() =>
translateSports(props.sports, t) translateSports(props.sports, t, true)
) )
const appConfig: ComputedRef<TAppConfig> = computed( const appConfig: ComputedRef<TAppConfig> = computed(
() => store.getters[ROOT_STORE.GETTERS.APP_CONFIG] () => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]

View File

@ -52,6 +52,9 @@
:sport-label=" :sport-label="
sports.filter((s) => s.id === workout.sport_id)[0].label sports.filter((s) => s.id === workout.sport_id)[0].label
" "
:color="
sports.filter((s) => s.id === workout.sport_id)[0].color
"
/> />
</td> </td>
<td <td

View File

@ -23,6 +23,7 @@
"BIRTH_DATE": "Birth date", "BIRTH_DATE": "Birth date",
"EDIT": "Edit profile", "EDIT": "Edit profile",
"EDIT_PREFERENCES": "Edit preferences", "EDIT_PREFERENCES": "Edit preferences",
"EDIT_SPORTS_PREFERENCES": "Edit sports preferences",
"FIRST_NAME": "First name", "FIRST_NAME": "First name",
"FIRST_DAY_OF_WEEK": "First day of week", "FIRST_DAY_OF_WEEK": "First day of week",
"LANGUAGE": "Language", "LANGUAGE": "Language",
@ -36,11 +37,20 @@
"PREFERENCES_EDITION": "Preferences edition", "PREFERENCES_EDITION": "Preferences edition",
"PROFILE_EDITION": "Profile edition", "PROFILE_EDITION": "Profile edition",
"REGISTRATION_DATE": "Registration date", "REGISTRATION_DATE": "Registration date",
"SPORTS_EDITION": "Sports preferences edition",
"SUNDAY": "Sunday", "SUNDAY": "Sunday",
"TABS": { "TABS": {
"PICTURE": "picture", "PICTURE": "picture",
"PREFERENCES": "preferences", "PREFERENCES": "preferences",
"PROFILE": "profile" "PROFILE": "profile",
"SPORTS": "sports"
},
"SPORT": {
"ACTION": "action",
"COLOR": "color",
"IS_ACTIVE": "active",
"LABEL": "label",
"STOPPED_SPEED_THRESHOLD": "stopped speed threshold"
}, },
"TIMEZONE": "Timezone" "TIMEZONE": "Timezone"
}, },

View File

@ -23,6 +23,7 @@
"BIRTH_DATE": "Date de naissance", "BIRTH_DATE": "Date de naissance",
"EDIT": "Modifier le profil", "EDIT": "Modifier le profil",
"EDIT_PREFERENCES": "Modifier les préférences", "EDIT_PREFERENCES": "Modifier les préférences",
"EDIT_SPORTS_PREFERENCES": "Modifier les préférences des sports",
"FIRST_DAY_OF_WEEK": "Premier jour de la semaine", "FIRST_DAY_OF_WEEK": "Premier jour de la semaine",
"FIRST_NAME": "Prénom", "FIRST_NAME": "Prénom",
"LANGUAGE": "Langue", "LANGUAGE": "Langue",
@ -36,11 +37,20 @@
"PREFERENCES_EDITION": "Mise à jour des préférences", "PREFERENCES_EDITION": "Mise à jour des préférences",
"PROFILE_EDITION": "Mise à jour du profil", "PROFILE_EDITION": "Mise à jour du profil",
"REGISTRATION_DATE": "Date d'inscription", "REGISTRATION_DATE": "Date d'inscription",
"SPORTS_EDITION": "Mise à jour des préférences des sports",
"SUNDAY": "Dimanche", "SUNDAY": "Dimanche",
"TABS": { "TABS": {
"PICTURE": "image", "PICTURE": "image",
"PREFERENCES": "préférences", "PREFERENCES": "préférences",
"PROFILE": "profil" "PROFILE": "profil",
"SPORTS": "sports"
},
"SPORT": {
"ACTION": "action",
"COLOR": "couleur",
"IS_ACTIVE": "actif",
"LABEL": "label",
"STOPPED_SPEED_THRESHOLD": "seuil de vitesse arrêtée"
}, },
"TIMEZONE": "Fuseau horaire" "TIMEZONE": "Fuseau horaire"
}, },

View File

@ -11,6 +11,7 @@ import ProfileEdition from '@/components/User/ProfileEdition/index.vue'
import UserInfosEdition from '@/components/User/ProfileEdition/UserInfosEdition.vue' import UserInfosEdition from '@/components/User/ProfileEdition/UserInfosEdition.vue'
import UserPictureEdition from '@/components/User/ProfileEdition/UserPictureEdition.vue' import UserPictureEdition from '@/components/User/ProfileEdition/UserPictureEdition.vue'
import UserPreferencesEdition from '@/components/User/ProfileEdition/UserPreferencesEdition.vue' import UserPreferencesEdition from '@/components/User/ProfileEdition/UserPreferencesEdition.vue'
import UserSportPreferences from '@/components/User/UserSportPreferences.vue'
import store from '@/store' import store from '@/store'
import { AUTH_USER_STORE } from '@/store/constants' import { AUTH_USER_STORE } from '@/store/constants'
@ -101,6 +102,12 @@ const routes: Array<RouteRecordRaw> = [
name: 'UserPreferences', name: 'UserPreferences',
component: UserPreferences, component: UserPreferences,
}, },
{
path: 'sports',
name: 'UserSportPreferences',
component: UserSportPreferences,
props: { isEdition: false },
},
], ],
}, },
{ {
@ -126,6 +133,12 @@ const routes: Array<RouteRecordRaw> = [
name: 'UserPreferencesEdition', name: 'UserPreferencesEdition',
component: UserPreferencesEdition, component: UserPreferencesEdition,
}, },
{
path: 'sports',
name: 'UserSportPreferencesEdition',
component: UserSportPreferences,
props: { isEdition: true },
},
], ],
}, },
], ],

View File

@ -254,9 +254,12 @@ button {
} }
} }
.center-text { .text-center {
text-align: center; text-align: center;
} }
.text-left {
text-align: left;
}
.responsive-table { .responsive-table {
margin-bottom: 15px; margin-bottom: 15px;
@ -337,3 +340,14 @@ button {
} }
} }
} }
.profile-buttons {
display: flex;
gap: $default-padding;
}
.medium-sport-img {
height: 35px;
width: 35px;
margin: 0 auto;
}

View File

@ -25,6 +25,7 @@ import {
IUserPayload, IUserPayload,
IUserPicturePayload, IUserPicturePayload,
IUserPreferencesPayload, IUserPreferencesPayload,
IUserSportPreferencesPayload,
} from '@/types/user' } from '@/types/user'
import { handleError } from '@/utils' import { handleError } from '@/utils'
@ -172,6 +173,26 @@ export const actions: ActionTree<IAuthUserState, IRootState> &
context.commit(AUTH_USER_STORE.MUTATIONS.UPDATE_USER_LOADING, false) context.commit(AUTH_USER_STORE.MUTATIONS.UPDATE_USER_LOADING, false)
) )
}, },
[AUTH_USER_STORE.ACTIONS.UPDATE_USER_SPORT_PREFERENCES](
context: ActionContext<IAuthUserState, IRootState>,
payload: IUserSportPreferencesPayload
): void {
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
context.commit(AUTH_USER_STORE.MUTATIONS.UPDATE_USER_LOADING, true)
authApi
.post('auth/profile/edit/sports', payload)
.then((res) => {
if (res.data.status === 'success') {
context.dispatch(SPORTS_STORE.ACTIONS.GET_SPORTS)
} else {
handleError(context, null)
}
})
.catch((error) => {
handleError(context, error)
context.commit(AUTH_USER_STORE.MUTATIONS.UPDATE_USER_LOADING, false)
})
},
[AUTH_USER_STORE.ACTIONS.UPDATE_USER_PICTURE]( [AUTH_USER_STORE.ACTIONS.UPDATE_USER_PICTURE](
context: ActionContext<IAuthUserState, IRootState>, context: ActionContext<IAuthUserState, IRootState>,
payload: IUserPicturePayload payload: IUserPicturePayload

View File

@ -10,6 +10,7 @@ export enum AuthUserActions {
UPDATE_USER_PICTURE = 'UPDATE_USER_PICTURE', UPDATE_USER_PICTURE = 'UPDATE_USER_PICTURE',
UPDATE_USER_PROFILE = 'UPDATE_USER_PROFILE', UPDATE_USER_PROFILE = 'UPDATE_USER_PROFILE',
UPDATE_USER_PREFERENCES = 'UPDATE_USER_PREFERENCES', UPDATE_USER_PREFERENCES = 'UPDATE_USER_PREFERENCES',
UPDATE_USER_SPORT_PREFERENCES = 'UPDATE_USER_SPORT_PREFERENCES',
} }
export enum AuthUserGetters { export enum AuthUserGetters {

View File

@ -16,6 +16,7 @@ import {
IUserPayload, IUserPayload,
IUserPicturePayload, IUserPicturePayload,
IUserPreferencesPayload, IUserPreferencesPayload,
IUserSportPreferencesPayload,
} from '@/types/user' } from '@/types/user'
export interface IAuthUserState { export interface IAuthUserState {
@ -52,6 +53,11 @@ export interface IAuthUserActions {
payload: IUserPreferencesPayload payload: IUserPreferencesPayload
): void ): void
[AUTH_USER_STORE.ACTIONS.UPDATE_USER_SPORT_PREFERENCES](
context: ActionContext<IAuthUserState, IRootState>,
payload: IUserSportPreferencesPayload
): void
[AUTH_USER_STORE.ACTIONS.UPDATE_USER_PICTURE]( [AUTH_USER_STORE.ACTIONS.UPDATE_USER_PICTURE](
context: ActionContext<IAuthUserState, IRootState>, context: ActionContext<IAuthUserState, IRootState>,
payload: IUserPicturePayload payload: IUserPicturePayload

View File

@ -1,7 +1,7 @@
import { ActionContext, ActionTree } from 'vuex' import { ActionContext, ActionTree } from 'vuex'
import authApi from '@/api/authApi' import authApi from '@/api/authApi'
import { ROOT_STORE, SPORTS_STORE } from '@/store/constants' import { AUTH_USER_STORE, ROOT_STORE, SPORTS_STORE } from '@/store/constants'
import { IRootState } from '@/store/modules/root/types' import { IRootState } from '@/store/modules/root/types'
import { ISportsActions, ISportsState } from '@/store/modules/sports/types' import { ISportsActions, ISportsState } from '@/store/modules/sports/types'
import { ISportPayload } from '@/types/sports' import { ISportPayload } from '@/types/sports'
@ -20,6 +20,7 @@ export const actions: ActionTree<ISportsState, IRootState> & ISportsActions = {
SPORTS_STORE.MUTATIONS.SET_SPORTS, SPORTS_STORE.MUTATIONS.SET_SPORTS,
res.data.data.sports res.data.data.sports
) )
context.commit(AUTH_USER_STORE.MUTATIONS.UPDATE_USER_LOADING, false)
} else { } else {
handleError(context, null) handleError(context, null)
} }

View File

@ -1,9 +1,12 @@
export interface ISport { export interface ISport {
color: string | null
has_workouts: boolean has_workouts: boolean
id: number id: number
img: string img: string
is_active: boolean is_active: boolean
is_active_for_user: boolean
label: string label: string
stopped_speed_threshold: number
} }
export interface ITranslatedSport extends ISport { export interface ITranslatedSport extends ISport {

View File

@ -45,6 +45,13 @@ export interface IUserPreferencesPayload {
weekm: boolean weekm: boolean
} }
export interface IUserSportPreferencesPayload {
sport_id: number
color: string | null
is_active: boolean
stopped_speed_threshold: number
}
export interface IUserPicturePayload { export interface IUserPicturePayload {
picture: File picture: File
} }

View File

@ -27,8 +27,9 @@ export interface IRecord {
} }
export interface IRecordsBySport { export interface IRecordsBySport {
[key: string]: string | Record<string, string | number>[] [key: string]: string | Record<string, string | number>[] | null
label: string label: string
color: string | null
records: Record<string, string | number>[] records: Record<string, string | number>[]
} }

View File

@ -44,6 +44,7 @@ export const getRecordsBySports = (
if (sportList[sport.translatedLabel] === void 0) { if (sportList[sport.translatedLabel] === void 0) {
sportList[sport.translatedLabel] = { sportList[sport.translatedLabel] = {
label: sport.label, label: sport.label,
color: sport.color,
records: [], records: [],
} }
} }

View File

@ -38,7 +38,7 @@ export const translateSports = (
onlyActive = false onlyActive = false
): ITranslatedSport[] => ): ITranslatedSport[] =>
sports sports
.filter((sport) => (onlyActive ? sport.is_active : true)) .filter((sport) => (onlyActive ? sport.is_active_for_user : true))
.map((sport) => ({ .map((sport) => ({
...sport, ...sport,
translatedLabel: t(`sports.${sport.label}.LABEL`), translatedLabel: t(`sports.${sport.label}.LABEL`),
@ -50,3 +50,12 @@ export const getSportLabel = (workout: IWorkout, sports: ISport[]): string => {
.filter((sport) => sport.id === workout.sport_id) .filter((sport) => sport.id === workout.sport_id)
.map((sport) => sport.label)[0] .map((sport) => sport.label)[0]
} }
export const getSportColor = (
workout: IWorkout,
sports: ISport[]
): string | null => {
return sports
.filter((sport) => sport.id === workout.sport_id)
.map((sport) => sport.color)[0]
}

View File

@ -1,6 +1,7 @@
<template> <template>
<div id="profile" class="container view" v-if="authUser.username"> <div id="profile" class="container view" v-if="authUser.username">
<router-view :user="authUser"></router-view> <router-view :user="authUser"></router-view>
<div id="bottom" />
</div> </div>
</template> </template>

View File

@ -5,25 +5,34 @@ import { translateSports } from '@/utils/sports'
const { t } = createI18n.global const { t } = createI18n.global
export const sports: ISport[] = [ export const sports: ISport[] = [
{ {
color: null,
has_workouts: false, has_workouts: false,
id: 1, id: 1,
img: '/img/sports/cycling-sport.png', img: '/img/sports/cycling-sport.png',
is_active: true, is_active: true,
is_active_for_user: true,
label: 'Cycling (Sport)', label: 'Cycling (Sport)',
stopped_speed_threshold: 1,
}, },
{ {
color: '#000000',
has_workouts: false, has_workouts: false,
id: 2, id: 2,
img: '/img/sports/cycling-transport.png', img: '/img/sports/cycling-transport.png',
is_active: false, is_active: false,
is_active_for_user: false,
label: 'Cycling (Transport)', label: 'Cycling (Transport)',
stopped_speed_threshold: 1,
}, },
{ {
color: null,
has_workouts: true, has_workouts: true,
id: 3, id: 3,
img: '/img/sports/hiking.png', img: '/img/sports/hiking.png',
is_active: true, is_active: true,
is_active_for_user: false,
label: 'Hiking', label: 'Hiking',
stopped_speed_threshold: 0.1,
}, },
] ]

View File

@ -157,6 +157,7 @@ describe('getRecordsBySports', () => {
}, },
expected: { expected: {
'Cycling (Sport)': { 'Cycling (Sport)': {
color: null,
label: 'Cycling (Sport)', label: 'Cycling (Sport)',
records: [ records: [
{ {
@ -206,6 +207,7 @@ describe('getRecordsBySports', () => {
}, },
expected: { expected: {
'Cycling (Sport)': { 'Cycling (Sport)': {
color: null,
label: 'Cycling (Sport)', label: 'Cycling (Sport)',
records: [ records: [
{ {
@ -225,6 +227,7 @@ describe('getRecordsBySports', () => {
], ],
}, },
'Cycling (Transport)': { 'Cycling (Transport)': {
color: '#000000',
label: 'Cycling (Transport)', label: 'Cycling (Transport)',
records: [ records: [
{ {

View File

@ -18,27 +18,36 @@ describe('sortSports', () => {
}, },
expected: [ expected: [
{ {
color: null,
has_workouts: false, has_workouts: false,
id: 1, id: 1,
img: '/img/sports/cycling-sport.png', img: '/img/sports/cycling-sport.png',
is_active: true, is_active: true,
is_active_for_user: true,
label: 'Cycling (Sport)', label: 'Cycling (Sport)',
stopped_speed_threshold: 1,
translatedLabel: 'Cycling (Sport)', translatedLabel: 'Cycling (Sport)',
}, },
{ {
color: '#000000',
has_workouts: false, has_workouts: false,
id: 2, id: 2,
img: '/img/sports/cycling-transport.png', img: '/img/sports/cycling-transport.png',
is_active: false, is_active: false,
is_active_for_user: false,
label: 'Cycling (Transport)', label: 'Cycling (Transport)',
stopped_speed_threshold: 1,
translatedLabel: 'Cycling (Transport)', translatedLabel: 'Cycling (Transport)',
}, },
{ {
color: null,
has_workouts: true, has_workouts: true,
id: 3, id: 3,
img: '/img/sports/hiking.png', img: '/img/sports/hiking.png',
is_active: true, is_active: true,
is_active_for_user: false,
label: 'Hiking', label: 'Hiking',
stopped_speed_threshold: 0.1,
translatedLabel: 'Hiking', translatedLabel: 'Hiking',
}, },
], ],
@ -53,21 +62,16 @@ describe('sortSports', () => {
}, },
expected: [ expected: [
{ {
color: null,
has_workouts: false, has_workouts: false,
id: 1, id: 1,
img: '/img/sports/cycling-sport.png', img: '/img/sports/cycling-sport.png',
is_active: true, is_active: true,
is_active_for_user: true,
label: 'Cycling (Sport)', label: 'Cycling (Sport)',
stopped_speed_threshold: 1,
translatedLabel: 'Cycling (Sport)', translatedLabel: 'Cycling (Sport)',
}, },
{
has_workouts: true,
id: 3,
img: '/img/sports/hiking.png',
is_active: true,
label: 'Hiking',
translatedLabel: 'Hiking',
},
], ],
}, },
{ {
@ -88,27 +92,36 @@ describe('sortSports', () => {
}, },
expected: [ expected: [
{ {
color: null,
has_workouts: true, has_workouts: true,
id: 3, id: 3,
img: '/img/sports/hiking.png', img: '/img/sports/hiking.png',
is_active: true, is_active: true,
is_active_for_user: false,
label: 'Hiking', label: 'Hiking',
stopped_speed_threshold: 0.1,
translatedLabel: 'Randonnée', translatedLabel: 'Randonnée',
}, },
{ {
color: null,
has_workouts: false, has_workouts: false,
id: 1, id: 1,
img: '/img/sports/cycling-sport.png', img: '/img/sports/cycling-sport.png',
is_active: true, is_active: true,
is_active_for_user: true,
label: 'Cycling (Sport)', label: 'Cycling (Sport)',
stopped_speed_threshold: 1,
translatedLabel: 'Vélo (Sport)', translatedLabel: 'Vélo (Sport)',
}, },
{ {
color: '#000000',
has_workouts: false, has_workouts: false,
id: 2, id: 2,
img: '/img/sports/cycling-transport.png', img: '/img/sports/cycling-transport.png',
is_active: false, is_active: false,
is_active_for_user: false,
label: 'Cycling (Transport)', label: 'Cycling (Transport)',
stopped_speed_threshold: 1,
translatedLabel: 'Vélo (Transport)', translatedLabel: 'Vélo (Transport)',
}, },
], ],
@ -123,19 +136,14 @@ describe('sortSports', () => {
}, },
expected: [ expected: [
{ {
has_workouts: true, color: null,
id: 3,
img: '/img/sports/hiking.png',
is_active: true,
label: 'Hiking',
translatedLabel: 'Randonnée',
},
{
has_workouts: false, has_workouts: false,
id: 1, id: 1,
img: '/img/sports/cycling-sport.png', img: '/img/sports/cycling-sport.png',
is_active: true, is_active: true,
is_active_for_user: true,
label: 'Cycling (Sport)', label: 'Cycling (Sport)',
stopped_speed_threshold: 1,
translatedLabel: 'Vélo (Sport)', translatedLabel: 'Vélo (Sport)',
}, },
], ],