update js dependencies
This commit is contained in:
parent
3d66f6dd70
commit
c7024e6692
@ -6,8 +6,8 @@
|
||||
[![Flask Version](https://img.shields.io/badge/flask-2.0-brightgreen.svg)](http://flask.pocoo.org/)
|
||||
[![code style: black](https://img.shields.io/badge/code%20style-black-black)](https://github.com/psf/black)
|
||||
[![type check: mypy](https://img.shields.io/badge/type%20check-mypy-blue)](http://mypy-lang.org/)
|
||||
[![Vue Version](https://img.shields.io/badge/vue-3.2-brightgreen.svg)](https://v3.vuejs.org/)
|
||||
[![Typescript Version](https://img.shields.io/npm/types/typescript)](https://www.typescriptlang.org/)
|
||||
[![Vue Version](https://img.shields.io/badge/vue-3.1-brightgreen.svg)](https://v3.vuejs.org/)
|
||||
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
|
||||
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/290a285f22e94132904dc13b4dd19d1d)](https://www.codacy.com/app/SamR1/FitTrackee)
|
||||
[![pipeline status](https://gitlab.com/SamR1/FitTrackee/badges/master/pipeline.svg)](https://gitlab.com/SamR1/FitTrackee/-/commits/master)
|
||||
|
2
fittrackee/dist/index.html
vendored
2
fittrackee/dist/index.html
vendored
@ -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.9f104fd3.css" rel="prefetch"><link href="/static/css/main.37fe4113.css" rel="prefetch"><link href="/static/css/main~workouts.539c93e4.css" rel="prefetch"><link href="/static/css/profile.1e84a078.css" rel="prefetch"><link href="/static/css/reset.d2d36969.css" rel="prefetch"><link href="/static/css/workouts.0c5cfad5.css" rel="prefetch"><link href="/static/js/admin.4f80b9a8.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.2b9e3dee.js" rel="prefetch"><link href="/static/js/main~workouts.896585f2.js" rel="prefetch"><link href="/static/js/profile.459d43b2.js" rel="prefetch"><link href="/static/js/reset.8adc8d46.js" rel="prefetch"><link href="/static/js/workouts.8d9e217b.js" rel="prefetch"><link href="/static/css/app.b50ad746.css" rel="preload" as="style"><link href="/static/js/app.52839b95.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.61765c73.js" rel="preload" as="script"><link href="/static/css/app.b50ad746.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.61765c73.js"></script><script src="/static/js/app.52839b95.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.9f104fd3.css" rel="prefetch"><link href="/static/css/main.37fe4113.css" rel="prefetch"><link href="/static/css/main~workouts.539c93e4.css" rel="prefetch"><link href="/static/css/profile.1e84a078.css" rel="prefetch"><link href="/static/css/reset.d2d36969.css" rel="prefetch"><link href="/static/css/workouts.0c5cfad5.css" rel="prefetch"><link href="/static/js/admin.4047df15.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.0da63bb9.js" rel="prefetch"><link href="/static/js/main~workouts.aa540c70.js" rel="prefetch"><link href="/static/js/profile.7e87449f.js" rel="prefetch"><link href="/static/js/reset.89dff43e.js" rel="prefetch"><link href="/static/js/workouts.f98d0408.js" rel="prefetch"><link href="/static/css/app.b50ad746.css" rel="preload" as="style"><link href="/static/js/app.e4597b67.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.c7e5773d.js" rel="preload" as="script"><link href="/static/css/app.b50ad746.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.c7e5773d.js"></script><script src="/static/js/app.e4597b67.js"></script></body></html>
|
@ -64,7 +64,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
|
||||
"url": "/img/workouts/mountains.svg"
|
||||
},
|
||||
{
|
||||
"revision": "520d1945082dadc2fc1e3715b5e92f81",
|
||||
"revision": "e437b90bb612d1a1f473098ba660a943",
|
||||
"url": "/index.html"
|
||||
},
|
||||
{
|
||||
@ -76,11 +76,11 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
|
||||
"url": "/robots.txt"
|
||||
},
|
||||
{
|
||||
"revision": "e60fe33b28eb8d18be84",
|
||||
"revision": "926aa13be38925ea26b0",
|
||||
"url": "/static/css/admin.9f104fd3.css"
|
||||
},
|
||||
{
|
||||
"revision": "13f5c85f139f92eb9298",
|
||||
"revision": "08865ce798ce3accdbc7",
|
||||
"url": "/static/css/app.b50ad746.css"
|
||||
},
|
||||
{
|
||||
@ -92,23 +92,23 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
|
||||
"url": "/static/css/leaflet.css"
|
||||
},
|
||||
{
|
||||
"revision": "ce9711136bd312e20159",
|
||||
"revision": "a702baca6025f6f01708",
|
||||
"url": "/static/css/main.37fe4113.css"
|
||||
},
|
||||
{
|
||||
"revision": "1bb79b46d79d1d317530",
|
||||
"revision": "5e298b1182cc624e462b",
|
||||
"url": "/static/css/main~workouts.539c93e4.css"
|
||||
},
|
||||
{
|
||||
"revision": "12f7f5dc1ad7050cc484",
|
||||
"revision": "c16a070d51720bb1c776",
|
||||
"url": "/static/css/profile.1e84a078.css"
|
||||
},
|
||||
{
|
||||
"revision": "07d0f54ecab2e0bd8eee",
|
||||
"revision": "cd09dcb62fde404b4c77",
|
||||
"url": "/static/css/reset.d2d36969.css"
|
||||
},
|
||||
{
|
||||
"revision": "4ffc14773cab41b5e035",
|
||||
"revision": "a1a8fe166d947249a434",
|
||||
"url": "/static/css/workouts.0c5cfad5.css"
|
||||
},
|
||||
{
|
||||
@ -192,12 +192,12 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
|
||||
"url": "/static/img/pt-sans-v9-latin-regular.f1f73e45.svg"
|
||||
},
|
||||
{
|
||||
"revision": "e60fe33b28eb8d18be84",
|
||||
"url": "/static/js/admin.4f80b9a8.js"
|
||||
"revision": "926aa13be38925ea26b0",
|
||||
"url": "/static/js/admin.4047df15.js"
|
||||
},
|
||||
{
|
||||
"revision": "13f5c85f139f92eb9298",
|
||||
"url": "/static/js/app.52839b95.js"
|
||||
"revision": "08865ce798ce3accdbc7",
|
||||
"url": "/static/js/app.e4597b67.js"
|
||||
},
|
||||
{
|
||||
"revision": "bd7d183c9f68e5f4027d",
|
||||
@ -220,27 +220,27 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
|
||||
"url": "/static/js/chunk-2d22523a.4b710d99.js"
|
||||
},
|
||||
{
|
||||
"revision": "6c05e0c377c41a911291",
|
||||
"url": "/static/js/chunk-vendors.61765c73.js"
|
||||
"revision": "8a5827b5b86b68554e15",
|
||||
"url": "/static/js/chunk-vendors.c7e5773d.js"
|
||||
},
|
||||
{
|
||||
"revision": "ce9711136bd312e20159",
|
||||
"url": "/static/js/main.2b9e3dee.js"
|
||||
"revision": "a702baca6025f6f01708",
|
||||
"url": "/static/js/main.0da63bb9.js"
|
||||
},
|
||||
{
|
||||
"revision": "1bb79b46d79d1d317530",
|
||||
"url": "/static/js/main~workouts.896585f2.js"
|
||||
"revision": "5e298b1182cc624e462b",
|
||||
"url": "/static/js/main~workouts.aa540c70.js"
|
||||
},
|
||||
{
|
||||
"revision": "12f7f5dc1ad7050cc484",
|
||||
"url": "/static/js/profile.459d43b2.js"
|
||||
"revision": "c16a070d51720bb1c776",
|
||||
"url": "/static/js/profile.7e87449f.js"
|
||||
},
|
||||
{
|
||||
"revision": "07d0f54ecab2e0bd8eee",
|
||||
"url": "/static/js/reset.8adc8d46.js"
|
||||
"revision": "cd09dcb62fde404b4c77",
|
||||
"url": "/static/js/reset.89dff43e.js"
|
||||
},
|
||||
{
|
||||
"revision": "4ffc14773cab41b5e035",
|
||||
"url": "/static/js/workouts.8d9e217b.js"
|
||||
"revision": "a1a8fe166d947249a434",
|
||||
"url": "/static/js/workouts.f98d0408.js"
|
||||
}
|
||||
]);
|
2
fittrackee/dist/service-worker.js
vendored
2
fittrackee/dist/service-worker.js
vendored
@ -14,7 +14,7 @@
|
||||
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
|
||||
|
||||
importScripts(
|
||||
"/precache-manifest.064a986893c5d436e33ba3b972260c2e.js"
|
||||
"/precache-manifest.e101202b5f7df36b064e1a0ba13db6a2.js"
|
||||
);
|
||||
|
||||
workbox.core.setCacheNameDetails({prefix: "fittrackee_client"});
|
||||
|
2
fittrackee/dist/static/js/admin.4047df15.js
vendored
Normal file
2
fittrackee/dist/static/js/admin.4047df15.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["admin"],{"726e":function(e,t,n){},8185:function(e,t,n){"use strict";n("a27b")},"89b3":function(e,t,n){"use strict";n.r(t);var o=n("7a23"),c=function(e){return Object(o["pushScopeId"])("data-v-2fbe41f1"),e=e(),Object(o["popScopeId"])(),e},r={id:"admin",class:"view"},a={key:0,class:"container"},i=c((function(){return Object(o["createElementVNode"])("div",{id:"bottom"},null,-1)}));function u(e,t,n,c,u,p){var s=Object(o["resolveComponent"])("router-view"),b=Object(o["resolveComponent"])("NotFound");return Object(o["openBlock"])(),Object(o["createElementBlock"])("div",r,[e.userLoading?Object(o["createCommentVNode"])("",!0):(Object(o["openBlock"])(),Object(o["createElementBlock"])("div",a,[e.isAuthUserAmin?(Object(o["openBlock"])(),Object(o["createBlock"])(s,{key:0,appConfig:e.appConfig,appStatistics:e.appStatistics},null,8,["appConfig","appStatistics"])):(Object(o["openBlock"])(),Object(o["createBlock"])(b,{key:1})),i]))])}var p=n("f7f9"),s=n("dad5"),b=n("2906"),d=Object(o["defineComponent"])({name:"Admin",components:{NotFound:p["a"]},setup:function(){var e=Object(b["a"])();Object(o["onBeforeMount"])((function(){return e.dispatch(s["b"].ACTIONS.GET_APPLICATION_STATS)}));var t=Object(o["computed"])((function(){return e.getters[s["b"].GETTERS.APP_LOADING]})),n=Object(o["computed"])((function(){return e.getters[s["b"].GETTERS.APP_CONFIG]})),c=Object(o["computed"])((function(){return e.getters[s["b"].GETTERS.APP_STATS]})),r=Object(o["computed"])((function(){return e.getters[s["a"].GETTERS.IS_ADMIN]})),a=Object(o["computed"])((function(){return e.getters[s["a"].GETTERS.USER_LOADING]}));return{appConfig:n,appLoading:t,appStatistics:c,isAuthUserAmin:r,userLoading:a}}}),l=(n("d14e"),n("6b0d")),O=n.n(l);const m=O()(d,[["render",u],["__scopeId","data-v-2fbe41f1"]]);t["default"]=m},a27b:function(e,t,n){},d14e:function(e,t,n){"use strict";n("726e")},f7f9:function(e,t,n){"use strict";var o=n("7a23");function c(e,t,n,c,r,a){var i=Object(o["resolveComponent"])("Error");return Object(o["openBlock"])(),Object(o["createBlock"])(i,{title:"404",message:e.$t("error.NOT_FOUND.".concat(e.target)),"button-text":e.$t("common.HOME")},null,8,["message","button-text"])}var r={id:"error"},a={class:"error-content"};function i(e,t,n,c,i,u){return Object(o["openBlock"])(),Object(o["createElementBlock"])("div",r,[Object(o["createElementVNode"])("div",a,[Object(o["createElementVNode"])("h1",null,Object(o["toDisplayString"])(e.title),1),Object(o["createElementVNode"])("p",null,Object(o["toDisplayString"])(e.message),1),e.buttonText?(Object(o["openBlock"])(),Object(o["createElementBlock"])("button",{key:0,onClick:t[0]||(t[0]=function(t){return e.$router.push(e.path)}),class:"upper"},Object(o["toDisplayString"])(e.buttonText),1)):Object(o["createCommentVNode"])("",!0)])])}var u=Object(o["defineComponent"])({name:"Error",props:{title:{type:String,required:!0},message:{type:String},buttonText:{type:String},path:{type:String,default:"/"}}}),p=(n("8185"),n("6b0d")),s=n.n(p);const b=s()(u,[["render",i],["__scopeId","data-v-58e20d75"]]);var d=b,l=Object(o["defineComponent"])({name:"NotFound",components:{Error:d},props:{target:{type:String,default:"PAGE"}}});const O=s()(l,[["render",c]]);t["a"]=O}}]);
|
||||
//# sourceMappingURL=admin.4047df15.js.map
|
1
fittrackee/dist/static/js/admin.4047df15.js.map
vendored
Normal file
1
fittrackee/dist/static/js/admin.4047df15.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/admin.4f80b9a8.js
vendored
2
fittrackee/dist/static/js/admin.4f80b9a8.js
vendored
@ -1,2 +0,0 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["admin"],{"726e":function(e,t,o){},8185:function(e,t,o){"use strict";o("a27b")},"89b3":function(e,t,o){"use strict";o.r(t);var n=o("7a23"),c=Object(n["withScopeId"])("data-v-2fbe41f1");Object(n["pushScopeId"])("data-v-2fbe41f1");var r={id:"admin",class:"view"},a={key:0,class:"container"},i=Object(n["createVNode"])("div",{id:"bottom"},null,-1);Object(n["popScopeId"])();var p=c((function(e,t,o,c,p,u){var s=Object(n["resolveComponent"])("router-view"),d=Object(n["resolveComponent"])("NotFound");return Object(n["openBlock"])(),Object(n["createBlock"])("div",r,[e.userLoading?Object(n["createCommentVNode"])("",!0):(Object(n["openBlock"])(),Object(n["createBlock"])("div",a,[e.isAuthUserAmin?(Object(n["openBlock"])(),Object(n["createBlock"])(s,{key:0,appConfig:e.appConfig,appStatistics:e.appStatistics},null,8,["appConfig","appStatistics"])):(Object(n["openBlock"])(),Object(n["createBlock"])(d,{key:1})),i]))])})),u=o("f7f9"),s=o("dad5"),d=o("2906"),b=Object(n["defineComponent"])({name:"Admin",components:{NotFound:u["a"]},setup:function(){var e=Object(d["a"])();Object(n["onBeforeMount"])((function(){return e.dispatch(s["b"].ACTIONS.GET_APPLICATION_STATS)}));var t=Object(n["computed"])((function(){return e.getters[s["b"].GETTERS.APP_LOADING]})),o=Object(n["computed"])((function(){return e.getters[s["b"].GETTERS.APP_CONFIG]})),c=Object(n["computed"])((function(){return e.getters[s["b"].GETTERS.APP_STATS]})),r=Object(n["computed"])((function(){return e.getters[s["a"].GETTERS.IS_ADMIN]})),a=Object(n["computed"])((function(){return e.getters[s["a"].GETTERS.USER_LOADING]}));return{appConfig:o,appLoading:t,appStatistics:c,isAuthUserAmin:r,userLoading:a}}});o("d14e");b.render=p,b.__scopeId="data-v-2fbe41f1";t["default"]=b},a27b:function(e,t,o){},d14e:function(e,t,o){"use strict";o("726e")},f7f9:function(e,t,o){"use strict";var n=o("7a23");function c(e,t,o,c,r,a){var i=Object(n["resolveComponent"])("Error");return Object(n["openBlock"])(),Object(n["createBlock"])(i,{title:"404",message:e.$t("error.NOT_FOUND.".concat(e.target)),"button-text":e.$t("common.HOME")},null,8,["message","button-text"])}var r=Object(n["withScopeId"])("data-v-58e20d75");Object(n["pushScopeId"])("data-v-58e20d75");var a={id:"error"},i={class:"error-content"};Object(n["popScopeId"])();var p=r((function(e,t,o,c,r,p){return Object(n["openBlock"])(),Object(n["createBlock"])("div",a,[Object(n["createVNode"])("div",i,[Object(n["createVNode"])("h1",null,Object(n["toDisplayString"])(e.title),1),Object(n["createVNode"])("p",null,Object(n["toDisplayString"])(e.message),1),e.buttonText?(Object(n["openBlock"])(),Object(n["createBlock"])("button",{key:0,onClick:t[1]||(t[1]=function(t){return e.$router.push(e.path)}),class:"upper"},Object(n["toDisplayString"])(e.buttonText),1)):Object(n["createCommentVNode"])("",!0)])])})),u=Object(n["defineComponent"])({name:"Error",props:{title:{type:String,required:!0},message:{type:String},buttonText:{type:String},path:{type:String,default:"/"}}});o("8185");u.render=p,u.__scopeId="data-v-58e20d75";var s=u,d=Object(n["defineComponent"])({name:"NotFound",components:{Error:s},props:{target:{type:String,default:"PAGE"}}});d.render=c;t["a"]=d}}]);
|
||||
//# sourceMappingURL=admin.4f80b9a8.js.map
|
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/app.52839b95.js
vendored
2
fittrackee/dist/static/js/app.52839b95.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/app.e4597b67.js
vendored
Normal file
2
fittrackee/dist/static/js/app.e4597b67.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/app.e4597b67.js.map
vendored
Normal file
1
fittrackee/dist/static/js/app.e4597b67.js.map
vendored
Normal file
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
52
fittrackee/dist/static/js/chunk-vendors.c7e5773d.js
vendored
Normal file
52
fittrackee/dist/static/js/chunk-vendors.c7e5773d.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/chunk-vendors.c7e5773d.js.map
vendored
Normal file
1
fittrackee/dist/static/js/chunk-vendors.c7e5773d.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/main.0da63bb9.js
vendored
Normal file
2
fittrackee/dist/static/js/main.0da63bb9.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/main.0da63bb9.js.map
vendored
Normal file
1
fittrackee/dist/static/js/main.0da63bb9.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/main.2b9e3dee.js
vendored
2
fittrackee/dist/static/js/main.2b9e3dee.js
vendored
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
2
fittrackee/dist/static/js/main~workouts.aa540c70.js
vendored
Normal file
2
fittrackee/dist/static/js/main~workouts.aa540c70.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/main~workouts.aa540c70.js.map
vendored
Normal file
1
fittrackee/dist/static/js/main~workouts.aa540c70.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,2 +0,0 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["profile"],{"0ab6":function(e,t,r){},"36e8":function(e,t,r){"use strict";r.r(t);var c=r("7a23"),n=Object(c["withScopeId"])("data-v-37d55f74");Object(c["pushScopeId"])("data-v-37d55f74");var o={key:0,id:"profile",class:"container view"};Object(c["popScopeId"])();var a=n((function(e,t,r,n,a,s){var u=Object(c["resolveComponent"])("router-view");return e.authUser.username?(Object(c["openBlock"])(),Object(c["createBlock"])("div",o,[Object(c["createVNode"])(u,{user:e.authUser},null,8,["user"])])):Object(c["createCommentVNode"])("",!0)})),s=r("dad5"),u=r("2906"),d=Object(c["defineComponent"])({name:"ProfileView",setup:function(){var e=Object(u["a"])(),t=Object(c["computed"])((function(){return e.getters[s["a"].GETTERS.AUTH_USER_PROFILE]}));return{authUser:t}}});r("a6f2");d.render=a,d.__scopeId="data-v-37d55f74";t["default"]=d},"7ffc":function(e,t,r){"use strict";r("b288")},a6f2:function(e,t,r){"use strict";r("0ab6")},ad3d:function(e,t,r){"use strict";r.r(t);var c=r("7a23"),n=Object(c["withScopeId"])("data-v-82f4bbf6");Object(c["pushScopeId"])("data-v-82f4bbf6");var o={key:0,id:"user",class:"view"},a={class:"box"};Object(c["popScopeId"])();var s=n((function(e,t,r,n,s,u){var d=Object(c["resolveComponent"])("UserHeader"),f=Object(c["resolveComponent"])("UserInfos");return e.user.username?(Object(c["openBlock"])(),Object(c["createBlock"])("div",o,[Object(c["createVNode"])(d,{user:e.user},null,8,["user"]),Object(c["createVNode"])("div",a,[Object(c["createVNode"])(f,{user:e.user,"from-admin":!0},null,8,["user"])])])):Object(c["createCommentVNode"])("",!0)})),u=r("6c02"),d=r("3c44"),f=r("71a7"),i=r("dad5"),b=r("2906"),p=Object(c["defineComponent"])({name:"UserView",components:{UserHeader:d["a"],UserInfos:f["a"]},setup:function(){var e=Object(u["c"])(),t=Object(b["a"])(),r=Object(c["computed"])((function(){return t.getters[i["e"].GETTERS.USER]}));return Object(c["onBeforeMount"])((function(){e.params.username&&"string"===typeof e.params.username&&t.dispatch(i["e"].ACTIONS.GET_USER,e.params.username)})),Object(c["onBeforeUnmount"])((function(){t.dispatch(i["e"].ACTIONS.EMPTY_USER)})),{user:r}}});r("7ffc");p.render=s,p.__scopeId="data-v-82f4bbf6";t["default"]=p},b288:function(e,t,r){}}]);
|
||||
//# sourceMappingURL=profile.459d43b2.js.map
|
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/profile.7e87449f.js
vendored
Normal file
2
fittrackee/dist/static/js/profile.7e87449f.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["profile"],{"0ab6":function(e,t,n){},"36e8":function(e,t,n){"use strict";n.r(t);var r=n("7a23"),c={key:0,id:"profile",class:"container view"};function o(e,t,n,o,a,s){var u=Object(r["resolveComponent"])("router-view");return e.authUser.username?(Object(r["openBlock"])(),Object(r["createElementBlock"])("div",c,[Object(r["createVNode"])(u,{user:e.authUser},null,8,["user"])])):Object(r["createCommentVNode"])("",!0)}var a=n("dad5"),s=n("2906"),u=Object(r["defineComponent"])({name:"ProfileView",setup:function(){var e=Object(s["a"])(),t=Object(r["computed"])((function(){return e.getters[a["a"].GETTERS.AUTH_USER_PROFILE]}));return{authUser:t}}}),i=(n("a6f2"),n("6b0d")),d=n.n(i);const f=d()(u,[["render",o],["__scopeId","data-v-37d55f74"]]);t["default"]=f},"7ffc":function(e,t,n){"use strict";n("b288")},a6f2:function(e,t,n){"use strict";n("0ab6")},ad3d:function(e,t,n){"use strict";n.r(t);var r=n("7a23"),c={key:0,id:"user",class:"view"},o={class:"box"};function a(e,t,n,a,s,u){var i=Object(r["resolveComponent"])("UserHeader"),d=Object(r["resolveComponent"])("UserInfos");return e.user.username?(Object(r["openBlock"])(),Object(r["createElementBlock"])("div",c,[Object(r["createVNode"])(i,{user:e.user},null,8,["user"]),Object(r["createElementVNode"])("div",o,[Object(r["createVNode"])(d,{user:e.user,"from-admin":!0},null,8,["user"])])])):Object(r["createCommentVNode"])("",!0)}var s=n("6c02"),u=n("3c44"),i=n("71a7"),d=n("dad5"),f=n("2906"),b=Object(r["defineComponent"])({name:"UserView",components:{UserHeader:u["a"],UserInfos:i["a"]},setup:function(){var e=Object(s["c"])(),t=Object(f["a"])(),n=Object(r["computed"])((function(){return t.getters[d["e"].GETTERS.USER]}));return Object(r["onBeforeMount"])((function(){e.params.username&&"string"===typeof e.params.username&&t.dispatch(d["e"].ACTIONS.GET_USER,e.params.username)})),Object(r["onBeforeUnmount"])((function(){t.dispatch(d["e"].ACTIONS.EMPTY_USER)})),{user:n}}}),m=(n("7ffc"),n("6b0d")),p=n.n(m);const O=p()(b,[["render",a],["__scopeId","data-v-82f4bbf6"]]);t["default"]=O},b288:function(e,t,n){}}]);
|
||||
//# sourceMappingURL=profile.7e87449f.js.map
|
1
fittrackee/dist/static/js/profile.7e87449f.js.map
vendored
Normal file
1
fittrackee/dist/static/js/profile.7e87449f.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/reset.89dff43e.js
vendored
Normal file
2
fittrackee/dist/static/js/reset.89dff43e.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/reset.89dff43e.js.map
vendored
Normal file
1
fittrackee/dist/static/js/reset.89dff43e.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/reset.8adc8d46.js
vendored
2
fittrackee/dist/static/js/reset.8adc8d46.js
vendored
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
2
fittrackee/dist/static/js/workouts.f98d0408.js
vendored
Normal file
2
fittrackee/dist/static/js/workouts.f98d0408.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/workouts.f98d0408.js.map
vendored
Normal file
1
fittrackee/dist/static/js/workouts.f98d0408.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -5,7 +5,6 @@
|
||||
"quoteProps": "as-needed",
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"printWidth": 80,
|
||||
"endOfLine": "auto",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fittrackee_client",
|
||||
"version": "0.1.0",
|
||||
"version": "0.5.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
@ -13,24 +13,24 @@
|
||||
"dependencies": {
|
||||
"@tmcw/togeojson": "^4.5.0",
|
||||
"@vue-leaflet/vue-leaflet": "^0.6.1",
|
||||
"axios": "^0.21.1",
|
||||
"chart.js": "^3.5.1",
|
||||
"axios": "^0.24.0",
|
||||
"chart.js": "^3.6.0",
|
||||
"chartjs-plugin-datalabels": "^2.0.0",
|
||||
"core-js": "^3.6.5",
|
||||
"date-fns": "^2.23.0",
|
||||
"core-js": "^3.19.1",
|
||||
"date-fns": "^2.25.0",
|
||||
"date-fns-tz": "^1.1.6",
|
||||
"leaflet": "^1.7.1",
|
||||
"register-service-worker": "^1.7.1",
|
||||
"vue": "^3.0.0",
|
||||
"vue-chart-3": "^0.5.8",
|
||||
"vue-chart-3": "^0.5.11",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vuex": "^4.0.0-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@intlify/vue-i18n-loader": "^2.1.0",
|
||||
"@intlify/vue-i18n-loader": "^3.3.0",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/mocha": "^5.2.4",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
@ -41,7 +41,6 @@
|
||||
"@vue/cli-plugin-unit-mocha": "~4.5.0",
|
||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/compiler-sfc": "^3.0.0",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^7.0.0",
|
||||
"@vue/test-utils": "^2.0.0-0",
|
||||
@ -54,8 +53,8 @@
|
||||
"prettier": "^2.2.1",
|
||||
"sass": "^1.26.5",
|
||||
"sass-loader": "^8.0.2",
|
||||
"typescript": "~4.1.5",
|
||||
"vue-cli-plugin-i18n": "~2.1.1"
|
||||
"typescript": "~4.4.4",
|
||||
"vue-cli-plugin-i18n": "~2.3.1"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
@ -13,7 +13,7 @@ authApi.interceptors.request.use(
|
||||
const authToken = store.getters[AUTH_USER_STORE.GETTERS.AUTH_TOKEN]
|
||||
if (authToken) {
|
||||
const auth = `Bearer ${authToken}`
|
||||
if (config.headers.Authorization !== auth) {
|
||||
if (config.headers && config.headers.Authorization !== auth) {
|
||||
config.headers.Authorization = `Bearer ${authToken}`
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { Module } from 'vuex'
|
||||
import { actions } from '@/store/modules/authUser/actions'
|
||||
import { getters } from '@/store/modules/authUser/getters'
|
||||
import { mutations } from '@/store/modules/authUser/mutations'
|
||||
import { authUserState } from '@/store/modules/authUser/state.ts'
|
||||
import { authUserState } from '@/store/modules/authUser/state'
|
||||
import { IAuthUserState } from '@/store/modules/authUser/types'
|
||||
import { IRootState } from '@/store/modules/root/types'
|
||||
|
||||
|
@ -4,7 +4,7 @@ import authUserModule from '@/store/modules/authUser'
|
||||
import { actions } from '@/store/modules/root/actions'
|
||||
import { getters } from '@/store/modules/root/getters'
|
||||
import { mutations } from '@/store/modules/root/mutations'
|
||||
import { state } from '@/store/modules/root/state.ts'
|
||||
import { state } from '@/store/modules/root/state'
|
||||
import { IRootState } from '@/store/modules/root/types'
|
||||
import sportsModule from '@/store/modules/sports'
|
||||
import statsModule from '@/store/modules/statistics'
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
||||
FROM node:16
|
||||
|
||||
MAINTAINER SamR1@users.noreply.github.com
|
||||
|
||||
# set working directory
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# add `/usr/src/app/node_modules/.bin` to $PATH
|
||||
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
|
||||
# add environment variables
|
||||
ARG NODE_ENV
|
||||
ARG REACT_APP_API_URL
|
||||
ENV NODE_ENV $NODE_ENV
|
||||
ENV REACT_APP_API_URL $REACT_APP_API_URL
|
||||
|
||||
# install dependencies
|
||||
COPY package.json /usr/src/app/package.json
|
||||
RUN yarn install --silent
|
||||
RUN yarn global add react-scripts
|
||||
|
||||
# copy source
|
||||
COPY . /usr/src/app/
|
@ -1,51 +0,0 @@
|
||||
{
|
||||
"name": "fittrackee_client",
|
||||
"version": "0.4.9",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@mapbox/togeojson": "^0.16.0",
|
||||
"connected-react-router": "^6.9.1",
|
||||
"date-fns": "^2.23.0",
|
||||
"history": "^4.10.1",
|
||||
"i18next": "^20.6.0",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"i18next-xhr-backend": "^3.2.2",
|
||||
"leaflet": "^1.7.1",
|
||||
"luxon": "^2.0.2",
|
||||
"object-hash": "^2.2.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^11.11.0",
|
||||
"react-leaflet": "^3.2.2",
|
||||
"react-redux": "^7.2.5",
|
||||
"react-router-dom": "^5.3.0",
|
||||
"react-scripts": "^4.0.3",
|
||||
"react-timezone": "^2.4.0",
|
||||
"recharts": "^1.8.5",
|
||||
"redux": "^4.1.1",
|
||||
"redux-thunk": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "PORT=$CLIENT_PORT react-scripts start",
|
||||
"build": "NODE_ENV=production react-scripts build && rm -rf ../fittrackee/dist/* && cp -a build/. ../fittrackee/dist",
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "node_modules/.bin/eslint --cache --ext .jsx --ext .js src",
|
||||
"lint-fix": "node_modules/.bin/eslint --cache --ext .jsx --ext .js src --fix"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
],
|
||||
"devDependencies": {
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-testcafe": "^0.2.1",
|
||||
"prettier": "^2.4.1"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 9.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 8.1 KiB |
@ -1,826 +0,0 @@
|
||||
html {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #eaeaea;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.App {
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #222;
|
||||
height: 150px;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-title {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.App-intro {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.App-nav-profile-img {
|
||||
max-width: 32px;
|
||||
max-height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.App-profile-img-small {
|
||||
max-width: 150px;
|
||||
max-height: 150px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
a {
|
||||
color: #40578a;
|
||||
}
|
||||
|
||||
input[type="text"], textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.add-workout {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.add-workout-radio {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.admin-img {
|
||||
max-width: 35px;
|
||||
max-height: 35px;
|
||||
}
|
||||
|
||||
.admin-items {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
.admin-message {
|
||||
color: #7c7c7d;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.app-config-form label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.card {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.chart {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.chart-workouts {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.chart-arrows {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.chart-filters {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.chart-info {
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.chart-radio {
|
||||
display: flex;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.chart-radio label {
|
||||
/* display: flex; */
|
||||
}
|
||||
|
||||
.chart-radio input {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.chart-stats {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.col-workout-logo{
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.custom-modal {
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
max-width: 500px;
|
||||
margin: 20% auto;
|
||||
z-index: 1250;
|
||||
}
|
||||
|
||||
.custom-modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
padding: 50px;
|
||||
z-index: 1240;
|
||||
}
|
||||
|
||||
.custom-fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.custom-fa-small {
|
||||
font-size: 0.8em;
|
||||
margin-left: -0.8em;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
.custom-fa-small {
|
||||
font-size: 0.6em;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tooltip {
|
||||
background-color: #fff;
|
||||
border: 1px solid lightgrey;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.custom-tooltip p {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.custom-tooltip-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
height: 100%;
|
||||
}
|
||||
.dashboard, .history {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.dropdown-wrapper {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.dropdown-list {
|
||||
background-color: #f8f9fa;
|
||||
padding: 5px 0;
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
cursor: default;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.dropdown-item-selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dropdown-item-selected::after {
|
||||
content: " ✔";
|
||||
}
|
||||
|
||||
.error-message {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.fa-as-link {
|
||||
cursor:pointer;
|
||||
color: #40578a;
|
||||
}
|
||||
|
||||
.fa-as-link:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.fa-question-circle {
|
||||
color: #6c757d;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.fa-trophy {
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
.fa-color {
|
||||
color: #405976;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #f8f9fa;
|
||||
bottom: 0;
|
||||
color: #8b8c8c;
|
||||
font-size: 0.9em;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Chrome, Safari, Edge, Opera */
|
||||
.form-disabled .form-group input::-webkit-outer-spin-button,
|
||||
.form-disabled .form-group input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
.form-disabled .form-group input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.form-disabled .form-group input{
|
||||
border: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.gpx-file {
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.huge {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.i18n-flag svg {
|
||||
height: 100%;
|
||||
opacity: .9;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.inactive-link {
|
||||
color: lightgrey;
|
||||
}
|
||||
|
||||
.leaflet-container {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.loader {
|
||||
animation: spin 2s linear infinite;
|
||||
border: 8px solid #f3f3f3;
|
||||
border-top: 8px solid #3498db;
|
||||
border-radius: 50%;
|
||||
height: 60px;
|
||||
margin-left: 41%;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.map-attribution {
|
||||
bottom: 0;
|
||||
font-size: 11px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.map-attribution-text {
|
||||
background-color: rgba(255, 255, 255, .5);
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.no-picture {
|
||||
color: #405976;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 2em;
|
||||
margin: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.password-forget {
|
||||
margin: 10px;
|
||||
font-size: .9em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.radioLabel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.record-logo {
|
||||
margin-right: 5px;
|
||||
max-width: 25px;
|
||||
max-height: 25px;
|
||||
}
|
||||
|
||||
.record-table table, .record-table th, .record-table td{
|
||||
font-size: 0.85em;
|
||||
padding: 0.1em;
|
||||
}
|
||||
@media only screen and (min-width: 1200px) {
|
||||
.record-table table, .record-table th, .record-table td{
|
||||
font-size: 0.9em;
|
||||
padding: 0.1em;
|
||||
}
|
||||
}
|
||||
|
||||
.remaining-chars {
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sport-img {
|
||||
max-width: 35px;
|
||||
max-height: 35px;
|
||||
}
|
||||
|
||||
.sport-img-medium {
|
||||
max-width: 45px;
|
||||
max-height: 45px;
|
||||
}
|
||||
|
||||
.stats-disabled {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
fill: #405976;
|
||||
height: 70px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.time-frames {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.time-frame label {
|
||||
float: left;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.time-frame label input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.time-frame label span {
|
||||
border: #a9a9a9 solid 1px;
|
||||
border-radius: 9%;
|
||||
color: #7b7b7b;
|
||||
display: block;
|
||||
font-size: 0.9em;
|
||||
padding: 2px 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.time-frame input:checked + span {
|
||||
background-color: #a9a9a9;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.timezone-custom {
|
||||
font-size: .9em !important;
|
||||
height: inherit !important;
|
||||
}
|
||||
|
||||
.timezone-custom input {
|
||||
border: 0 !important;
|
||||
padding: 5px 1px !important;
|
||||
}
|
||||
|
||||
.timezone-custom ul {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.timezone-picker {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.timezone-picker-textfield {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.unlink {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.user-bio, .workout-notes {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.user-filters {
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.user-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.weather-img {
|
||||
max-width: 35px;
|
||||
max-height: 35px;
|
||||
}
|
||||
|
||||
.weather-img-small {
|
||||
max-width: 20px;
|
||||
max-height: 20px;
|
||||
}
|
||||
|
||||
.weather-table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.weather-table table, .weather-table th, .weather-table td{
|
||||
font-size: 0.9em;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.workouts-result {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.workout-card {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.col-with-map {
|
||||
font-size: .87em;
|
||||
}
|
||||
@media only screen and (min-width: 1200px) {
|
||||
.col-with-map {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.workout-details {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
.workout-date {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.workout-filter {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.workout-filter .col-2, .col-5{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.workout-label {
|
||||
font-size: 0.8em;
|
||||
color: #666
|
||||
}
|
||||
|
||||
.workout-logo {
|
||||
margin: 0 5px;
|
||||
max-width: 20px;
|
||||
max-height: 20px;
|
||||
}
|
||||
|
||||
.workout-map {
|
||||
background-color: #eaeaea;
|
||||
height: 225px;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.workout-no-map {
|
||||
background-color: #eaeaea;
|
||||
color: #666666;
|
||||
font-style: italic;
|
||||
height: 400px;
|
||||
line-height: 400px;
|
||||
}
|
||||
|
||||
.workout-notes, .actvity-segments {
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
margin-top: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.workout-page {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.workout-segments-list {
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
.workout-sport {
|
||||
margin-right: 1px;
|
||||
max-width: 18px;
|
||||
max-height: 18px;
|
||||
}
|
||||
|
||||
.workout-title img, .workout-title .map-attribution-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.workout-title img {
|
||||
border: 1px solid lightgrey;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
display: none;
|
||||
margin-left: 20px;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.workout-title .map-attribution-list {
|
||||
display: none;
|
||||
font-size: 11px;
|
||||
margin-left: 20px;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.workout-title:hover img, .workout-title:hover .map-attribution-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* responsive table */
|
||||
/* adapted from https://uglyduck.ca/making-tables-responsive-with-minimal-css/ */
|
||||
.heading-span,
|
||||
.heading-span-absolute {
|
||||
background: #eee;
|
||||
color: dimgrey;
|
||||
display: none;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
text-transform: uppercase;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.heading-span-absolute {
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media(max-width: 1024px) {
|
||||
table thead {
|
||||
left: -9999px;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
table tr {
|
||||
border-bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
table td {
|
||||
border: 1px solid lightgrey;
|
||||
margin: 0 -1px -1px 0;
|
||||
padding-top: 30px !important;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
}
|
||||
.record-tr {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.record-td {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
.heading-span, .heading-span-absolute {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* calendar */
|
||||
:root {
|
||||
--main-color: #1a8fff;
|
||||
--text-color: #777;
|
||||
--text-color-light: #ccc;
|
||||
--border-color: #eee;
|
||||
--bg-color: #f9f9f9;
|
||||
--neutral-color: #fff;
|
||||
}
|
||||
|
||||
.calendar .col-start {
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.calendar .col-center {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar .col-end {
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: var(--neutral-color);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.calendar .header {
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
/*font-size: 115%;*/
|
||||
padding: 0.5em 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.calendar .header .icon {
|
||||
cursor: pointer;
|
||||
transition: .15s ease-out;
|
||||
}
|
||||
|
||||
.calendar .header .icon:hover {
|
||||
transform: scale(1.75);
|
||||
transition: .25s ease-out;
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
.calendar .header .icon:first-of-type {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.calendar .header .icon:last-of-type {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.calendar .days {
|
||||
text-transform: uppercase;
|
||||
font-weight: 400;
|
||||
color: var(--text-color-light);
|
||||
font-size: 70%;
|
||||
padding: .75em 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.calendar .body .cell {
|
||||
position: relative;
|
||||
height: 3em;
|
||||
border-right: 1px solid var(--border-color);
|
||||
background: var(--neutral-color);
|
||||
}
|
||||
|
||||
.calendar .body .cell:hover {
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.calendar .body .selected {
|
||||
border-left: 10px solid transparent;
|
||||
border-image: linear-gradient(45deg, #1a8fff 0%,#53cbf1 40%);
|
||||
}
|
||||
|
||||
.calendar .body .row {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.calendar .body .row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.calendar .body .cell:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.calendar .body .cell .number {
|
||||
position: absolute;
|
||||
font-size: 82.5%;
|
||||
line-height: 1;
|
||||
top: .75em;
|
||||
right: .75em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.calendar .body .disabled {
|
||||
color: var(--text-color-light);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.calendar .body .col {
|
||||
flex-grow: 0;
|
||||
flex-basis: calc(100%/7);
|
||||
width: calc(100%/7);
|
||||
}
|
||||
|
||||
.calendar .body .img-disabled {
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
.calendar .body .weekend {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.calendar .body .today {
|
||||
background: #eff1f3;
|
||||
}
|
||||
|
||||
.calendar-workout,
|
||||
.calendar-more {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.calendar-more {
|
||||
color: #405976;
|
||||
font-size: .7em;
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
|
||||
.calendar-display-more {
|
||||
background: whitesmoke;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
margin-bottom: 10px;
|
||||
padding: 10px 15px;
|
||||
position: absolute;
|
||||
min-width: 52px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.calendar-workout-more {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
|
||||
.calendar-workout:nth-child(-n+2),
|
||||
.calendar-workout:nth-child(n+3) ~ .calendar-more,
|
||||
.calendar-workout-more:nth-child(n+3) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 992px) and (max-width: 1200px) {
|
||||
|
||||
.calendar-workout:nth-child(-n+4),
|
||||
.calendar-workout:nth-child(n+5) ~ .calendar-more,
|
||||
.calendar-workout-more:nth-child(n+5) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1200px) {
|
||||
|
||||
.calendar-workout:nth-child(-n+6),
|
||||
.calendar-workout:nth-child(n+7) ~ .calendar-more,
|
||||
.calendar-workout-more:nth-child(n+7) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import { formatDuration } from '../../../utils/stats'
|
||||
|
||||
const formatValue = (displayedData, value) =>
|
||||
displayedData === 'duration'
|
||||
? formatDuration(value, true)
|
||||
: ['distance', 'ascent', 'descent'].includes(displayedData)
|
||||
? value.toFixed(2)
|
||||
: value
|
||||
|
||||
/**
|
||||
* @return {null}
|
||||
*/
|
||||
export default function CustomTooltip(props) {
|
||||
const { active } = props
|
||||
if (active) {
|
||||
const { displayedData, payload, label } = props
|
||||
let total = 0
|
||||
payload.map(p => (total += p.value))
|
||||
return (
|
||||
<div className="custom-tooltip">
|
||||
<p className="custom-tooltip-label">{label}</p>
|
||||
{payload.map(p => (
|
||||
<p key={p.name} style={{ color: p.fill }}>
|
||||
{p.name}: {formatValue(displayedData, p.value)} {p.unit}
|
||||
</p>
|
||||
))}
|
||||
{payload.length > 0 && (
|
||||
<p>Total: {formatValue(displayedData, total)}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts'
|
||||
|
||||
import { formatValue } from '../../../utils/stats'
|
||||
import { workoutColors } from '../../../utils/workouts'
|
||||
import CustomTooltip from './CustomTooltip'
|
||||
import CustomLabel from './CustomLabel'
|
||||
|
||||
export default class StatsCharts extends React.PureComponent {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = {
|
||||
displayedData: 'distance',
|
||||
}
|
||||
}
|
||||
handleRadioChange(changeEvent) {
|
||||
this.setState({
|
||||
displayedData: changeEvent.target.name,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { displayedData } = this.state
|
||||
const { sports, stats, t, withElevation } = this.props
|
||||
if (Object.keys(stats).length === 0) {
|
||||
return t('common:No workouts.')
|
||||
}
|
||||
return (
|
||||
<div className="chart-stats">
|
||||
<div className="row chart-radio">
|
||||
<label className="radioLabel col">
|
||||
<input
|
||||
type="radio"
|
||||
name="distance"
|
||||
checked={displayedData === 'distance'}
|
||||
onChange={e => this.handleRadioChange(e)}
|
||||
/>
|
||||
{t('statistics:distance')}
|
||||
</label>
|
||||
<label className="radioLabel col">
|
||||
<input
|
||||
type="radio"
|
||||
name="duration"
|
||||
checked={displayedData === 'duration'}
|
||||
onChange={e => this.handleRadioChange(e)}
|
||||
/>
|
||||
{t('statistics:duration')}
|
||||
</label>
|
||||
{withElevation && (
|
||||
<>
|
||||
<label className="radioLabel col">
|
||||
<input
|
||||
type="radio"
|
||||
name="ascent"
|
||||
checked={displayedData === 'ascent'}
|
||||
onChange={e => this.handleRadioChange(e)}
|
||||
/>
|
||||
{t('statistics:ascent')}
|
||||
</label>
|
||||
<label className="radioLabel col">
|
||||
<input
|
||||
type="radio"
|
||||
name="descent"
|
||||
checked={displayedData === 'descent'}
|
||||
onChange={e => this.handleRadioChange(e)}
|
||||
/>
|
||||
{t('statistics:descent')}
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
<label className="radioLabel col">
|
||||
<input
|
||||
type="radio"
|
||||
name="workouts"
|
||||
checked={displayedData === 'workouts'}
|
||||
onChange={e => this.handleRadioChange(e)}
|
||||
/>
|
||||
{t('statistics:workouts')}
|
||||
</label>
|
||||
</div>
|
||||
<ResponsiveContainer height={300}>
|
||||
<BarChart data={stats[displayedData]} margin={{ top: 15, bottom: 0 }}>
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
interval={0} // to force to display all ticks
|
||||
/>
|
||||
<YAxis tickFormatter={value => formatValue(displayedData, value)} />
|
||||
<Tooltip
|
||||
content={<CustomTooltip displayedData={displayedData} />}
|
||||
/>
|
||||
{sports.map((s, i) => (
|
||||
<Bar
|
||||
// disable for now due to problems w/ CustomLabel
|
||||
// see https://github.com/recharts/recharts/issues/829
|
||||
isAnimationActive={false}
|
||||
key={s.id}
|
||||
dataKey={s.label}
|
||||
stackId="a"
|
||||
fill={workoutColors[i]}
|
||||
label={
|
||||
i === sports.length - 1 ? (
|
||||
<CustomLabel displayedData={displayedData} />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
name={t(`sports:${s.label}`)}
|
||||
/>
|
||||
))}
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
import { format } from 'date-fns'
|
||||
import React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
|
||||
import { getStats } from '../../../actions/stats'
|
||||
import { formatStats } from '../../../utils/stats'
|
||||
import StatsChart from './StatsChart'
|
||||
|
||||
class Statistics extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
this.updateData()
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
(this.props.user.username &&
|
||||
this.props.user.username !== prevProps.user.username) ||
|
||||
this.props.statsParams !== prevProps.statsParams
|
||||
) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
updateData() {
|
||||
if (this.props.user.username) {
|
||||
this.props.loadWorkouts(
|
||||
this.props.user.username,
|
||||
this.props.user.weekm,
|
||||
this.props.statsParams
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
displayedSports,
|
||||
sports,
|
||||
statistics,
|
||||
statsParams,
|
||||
displayEmpty,
|
||||
t,
|
||||
user,
|
||||
withElevation,
|
||||
} = this.props
|
||||
if (!displayEmpty && Object.keys(statistics).length === 0) {
|
||||
return <span>{t('common:No workouts.')}</span>
|
||||
}
|
||||
const stats = formatStats(
|
||||
statistics,
|
||||
sports,
|
||||
statsParams,
|
||||
displayedSports,
|
||||
user.weekm
|
||||
)
|
||||
return (
|
||||
<StatsChart
|
||||
sports={sports}
|
||||
stats={stats}
|
||||
t={t}
|
||||
withElevation={withElevation}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => ({
|
||||
sports: state.sports.data,
|
||||
statistics: state.statistics.data,
|
||||
user: state.user,
|
||||
}),
|
||||
dispatch => ({
|
||||
loadWorkouts: (userName, weekm, data) => {
|
||||
const dateFormat = 'yyyy-MM-dd'
|
||||
// depends on user config (first day of week)
|
||||
const time =
|
||||
data.duration === 'week'
|
||||
? `${data.duration}${weekm ? 'm' : ''}`
|
||||
: data.duration
|
||||
const params = {
|
||||
from: format(data.start, dateFormat),
|
||||
to: format(data.end, dateFormat),
|
||||
time: time,
|
||||
}
|
||||
dispatch(getStats(userName, data.type, params))
|
||||
},
|
||||
})
|
||||
)(Statistics)
|
@ -1,34 +0,0 @@
|
||||
import { endOfMonth, startOfMonth } from 'date-fns'
|
||||
import React from 'react'
|
||||
|
||||
import Stats from '../Common/Stats'
|
||||
|
||||
export default class Statistics extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
const date = new Date()
|
||||
this.state = {
|
||||
start: startOfMonth(date),
|
||||
end: endOfMonth(date),
|
||||
duration: 'week',
|
||||
type: 'by_time',
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { t } = this.props
|
||||
return (
|
||||
<div className="card workout-card">
|
||||
<div className="card-header">{t('dashboard:This month')}</div>
|
||||
<div className="card-body">
|
||||
<Stats
|
||||
displayEmpty={false}
|
||||
statsParams={this.state}
|
||||
t={t}
|
||||
withElevation={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
import { format } from 'date-fns'
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import StaticMap from '../Common/StaticMap'
|
||||
import { getDateWithTZ } from '../../utils'
|
||||
|
||||
export default function WorkoutCard(props) {
|
||||
const { sports, t, user, workout } = props
|
||||
|
||||
return (
|
||||
<div className="card workout-card text-center">
|
||||
<div className="card-header">
|
||||
<Link to={`/workouts/${workout.id}`}>
|
||||
{sports
|
||||
.filter(sport => sport.id === workout.sport_id)
|
||||
.map(sport => t(`sports:${sport.label}`))}{' '}
|
||||
-{' '}
|
||||
{format(
|
||||
getDateWithTZ(workout.workout_date, user.timezone),
|
||||
'dd/MM/yyyy HH:mm'
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="row">
|
||||
{workout.map && (
|
||||
<div className="col">
|
||||
<StaticMap workout={workout} />
|
||||
</div>
|
||||
)}
|
||||
<div className={`col${workout.map ? ' col-with-map' : ''}`}>
|
||||
<p>
|
||||
<i className="fa fa-clock-o" aria-hidden="true" />{' '}
|
||||
{t('workouts:Duration')}: {workout.moving}
|
||||
{workout.map ? (
|
||||
<span>
|
||||
<br />
|
||||
<br />
|
||||
</span>
|
||||
) : (
|
||||
' - '
|
||||
)}
|
||||
<i className="fa fa-road" aria-hidden="true" />{' '}
|
||||
{t('workouts:Distance')}: {workout.distance} km
|
||||
<br />
|
||||
</p>
|
||||
{workout.min_alt && workout.max_alt && (
|
||||
<p>
|
||||
<i className="fi-mountains custom-fa" />
|
||||
{t('workouts:Min. altitude')}: {workout.min_alt}m
|
||||
<br />
|
||||
{t('workouts:Max. altitude')}: {workout.max_alt}m
|
||||
<br />
|
||||
</p>
|
||||
)}
|
||||
{workout.ascent && workout.descent && (
|
||||
<p>
|
||||
<i className="fa fa-location-arrow custom-fa" />
|
||||
{t('workouts:Ascent')}: {workout.ascent}m
|
||||
<br />
|
||||
{t('workouts:Descent')}: {workout.descent}m
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
import {
|
||||
endOfMonth,
|
||||
endOfWeek,
|
||||
endOfYear,
|
||||
startOfMonth,
|
||||
startOfYear,
|
||||
startOfWeek,
|
||||
addMonths,
|
||||
addWeeks,
|
||||
addYears,
|
||||
subMonths,
|
||||
subWeeks,
|
||||
subYears,
|
||||
} from 'date-fns'
|
||||
import React from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { withTranslation } from 'react-i18next'
|
||||
import { connect } from 'react-redux'
|
||||
|
||||
import NoWorkouts from '../Common/NoWorkouts'
|
||||
import Stats from '../Common/Stats'
|
||||
import { workoutColors, translateSports } from '../../utils/workouts'
|
||||
|
||||
const durations = ['week', 'month', 'year']
|
||||
|
||||
class Statistics extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
const date = new Date()
|
||||
this.state = {
|
||||
displayedSports: props.sports.map(sport => sport.id),
|
||||
statsParams: {
|
||||
start: startOfMonth(subMonths(date, 11)),
|
||||
end: endOfMonth(date),
|
||||
duration: 'month',
|
||||
type: 'by_time',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.sports !== prevProps.sports) {
|
||||
this.updateDisplayedSports()
|
||||
}
|
||||
}
|
||||
|
||||
updateDisplayedSports() {
|
||||
const { sports } = this.props
|
||||
this.setState({ displayedSports: sports.map(sport => sport.id) })
|
||||
}
|
||||
|
||||
handleOnChangeDuration(e) {
|
||||
const duration = e.target.name
|
||||
|
||||
const date = new Date()
|
||||
const start =
|
||||
duration === 'year'
|
||||
? startOfYear(subYears(date, 9))
|
||||
: duration === 'week'
|
||||
? startOfMonth(subMonths(date, 2))
|
||||
: startOfMonth(subMonths(date, 11))
|
||||
const end =
|
||||
duration === 'year'
|
||||
? endOfYear(date)
|
||||
: duration === 'week'
|
||||
? endOfWeek(date)
|
||||
: endOfMonth(date)
|
||||
this.setState({ statsParams: { duration, end, start, type: 'by_time' } })
|
||||
}
|
||||
|
||||
handleOnChangeSports(sportId) {
|
||||
const { displayedSports } = this.state
|
||||
if (displayedSports.includes(sportId)) {
|
||||
this.setState({
|
||||
displayedSports: displayedSports.filter(s => s !== sportId),
|
||||
})
|
||||
} else {
|
||||
this.setState({ displayedSports: displayedSports.concat([sportId]) })
|
||||
}
|
||||
}
|
||||
|
||||
handleOnClickArrows(forward) {
|
||||
const { start, end, duration } = this.state.statsParams
|
||||
let newStart, newEnd
|
||||
if (forward) {
|
||||
newStart =
|
||||
duration === 'year'
|
||||
? startOfYear(subYears(start, 1))
|
||||
: duration === 'week'
|
||||
? startOfWeek(subWeeks(start, 1))
|
||||
: startOfMonth(subMonths(start, 1))
|
||||
newEnd =
|
||||
duration === 'year'
|
||||
? endOfYear(subYears(end, 1))
|
||||
: duration === 'week'
|
||||
? endOfWeek(subWeeks(end, 1))
|
||||
: endOfMonth(subMonths(end, 1))
|
||||
} else {
|
||||
newStart =
|
||||
duration === 'year'
|
||||
? startOfYear(addYears(start, 1))
|
||||
: duration === 'week'
|
||||
? startOfWeek(addWeeks(start, 1))
|
||||
: startOfMonth(addMonths(start, 1))
|
||||
newEnd =
|
||||
duration === 'year'
|
||||
? endOfYear(addYears(end, 1))
|
||||
: duration === 'week'
|
||||
? endOfWeek(addWeeks(end, 1))
|
||||
: endOfMonth(addMonths(end, 1))
|
||||
}
|
||||
this.setState({
|
||||
statsParams: { duration, end: newEnd, start: newStart, type: 'by_time' },
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { displayedSports, statsParams } = this.state
|
||||
const { sports, t, user } = this.props
|
||||
const translatedSports = translateSports(
|
||||
sports.filter(sport => user.sports_list.includes(sport.id)),
|
||||
t
|
||||
)
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>FitTrackee - {t('statistics:Statistics')}</title>
|
||||
</Helmet>
|
||||
<div className="container dashboard">
|
||||
<div className="card workout-card">
|
||||
<div className="card-header">{t('statistics:Statistics')}</div>
|
||||
<div
|
||||
className={`card-body${
|
||||
user.nb_workouts === 0 ? ' stats-disabled' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="chart-filters row">
|
||||
<div className="col chart-arrows">
|
||||
<p className="text-center">
|
||||
<i
|
||||
className="fa fa-chevron-left"
|
||||
aria-hidden="true"
|
||||
onClick={() => this.handleOnClickArrows(true)}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-md-3 time-frames justify-content-around">
|
||||
{durations.map(d => (
|
||||
<div className="time-frame" key={d}>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
id={d}
|
||||
name={d}
|
||||
checked={d === statsParams.duration}
|
||||
onChange={e => this.handleOnChangeDuration(e)}
|
||||
/>
|
||||
<span>{t(`statistics:${d}`)}</span>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="col chart-arrows">
|
||||
<p className="text-center">
|
||||
<i
|
||||
className="fa fa-chevron-right"
|
||||
aria-hidden="true"
|
||||
onClick={() => this.handleOnClickArrows(false)}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Stats
|
||||
displayEmpty
|
||||
displayedSports={displayedSports}
|
||||
statsParams={statsParams}
|
||||
t={t}
|
||||
withElevation
|
||||
/>
|
||||
<div className="row chart-workouts">
|
||||
{translatedSports.map(sport => (
|
||||
<label className="col workout-label" key={sport.id}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={displayedSports.includes(sport.id)}
|
||||
name={sport.label}
|
||||
onChange={() => this.handleOnChangeSports(sport.id)}
|
||||
/>
|
||||
<span style={{ color: workoutColors[sport.id - 1] }}>
|
||||
{` ${sport.label}`}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{user.nb_workouts === 0 && <NoWorkouts t={t} />}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(
|
||||
connect(state => ({
|
||||
sports: state.sports.data,
|
||||
user: state.user,
|
||||
}))(Statistics)
|
||||
)
|
@ -1,111 +0,0 @@
|
||||
import { format } from 'date-fns'
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import StaticMap from '../Common/StaticMap'
|
||||
import { getDateWithTZ } from '../../utils'
|
||||
|
||||
export default class WorkoutsList extends React.PureComponent {
|
||||
render() {
|
||||
const { loading, sports, t, user, workouts } = this.props
|
||||
return (
|
||||
<div className="card workout-card">
|
||||
<div className="card-body">
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" />
|
||||
<th scope="col">{t('common:Workout')}</th>
|
||||
<th scope="col">{t('workouts:Date')}</th>
|
||||
<th scope="col">{t('workouts:Distance')}</th>
|
||||
<th scope="col">{t('workouts:Duration')}</th>
|
||||
<th scope="col">{t('workouts:Ave. speed')}</th>
|
||||
<th scope="col">{t('workouts:Max. speed')}</th>
|
||||
<th scope="col">{t('workouts:Ascent')}</th>
|
||||
<th scope="col">{t('workouts:Descent')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!loading &&
|
||||
sports &&
|
||||
workouts.map((workout, idx) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<tr key={idx}>
|
||||
<td>
|
||||
<span className="heading-span-absolute">
|
||||
{t('common:Sport')}
|
||||
</span>
|
||||
<img
|
||||
className="workout-sport"
|
||||
src={sports
|
||||
.filter(s => s.id === workout.sport_id)
|
||||
.map(s => s.img)}
|
||||
alt="workout sport logo"
|
||||
/>
|
||||
</td>
|
||||
<td className="workout-title">
|
||||
<span className="heading-span-absolute">
|
||||
{t('common:Workout')}
|
||||
</span>
|
||||
<Link to={`/workouts/${workout.id}`}>
|
||||
{workout.title}
|
||||
</Link>
|
||||
{workout.map && (
|
||||
<StaticMap workout={workout} display="list" />
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<span className="heading-span-absolute">
|
||||
{t('workouts:Date')}
|
||||
</span>
|
||||
{format(
|
||||
getDateWithTZ(workout.workout_date, user.timezone),
|
||||
'dd/MM/yyyy HH:mm'
|
||||
)}
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="heading-span-absolute">
|
||||
{t('workouts:Distance')}
|
||||
</span>
|
||||
{Number(workout.distance).toFixed(2)} km
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="heading-span-absolute">
|
||||
{t('workouts:Duration')}
|
||||
</span>
|
||||
{workout.moving}
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="heading-span-absolute">
|
||||
{t('workouts:Ave. speed')}
|
||||
</span>
|
||||
{workout.ave_speed} km/h
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="heading-span-absolute">
|
||||
{t('workouts:Max. speed')}
|
||||
</span>
|
||||
{workout.max_speed} km/h
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="heading-span-absolute">
|
||||
{t('workouts:Ascent')}
|
||||
</span>
|
||||
{workout.ascent === null ? '' : `${workout.ascent} m`}
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="heading-span-absolute">
|
||||
{t('workouts:Descent')}
|
||||
</span>
|
||||
{workout.descent === null ? '' : `${workout.descent} m`}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{loading && <div className="loader" />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"Cycling (Sport)": "Cycling (Sport)",
|
||||
"Cycling (Transport)": "Cycling (Transport)",
|
||||
"Hiking": "Hiking",
|
||||
"Mountain Biking": "Mountain Biking",
|
||||
"Mountain Biking (Electric)": "Mountain Biking (Electric)",
|
||||
"Running": "Running",
|
||||
"Walking": "Walking",
|
||||
"Trail" : "Trail",
|
||||
"Skiing (Alpine)" : "Skiing (Alpine)",
|
||||
"Skiing (Cross Country)" : "Skiing (Cross Country)",
|
||||
"Rowing" : "Rowing"
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"workouts": "workouts",
|
||||
"distance": "distance",
|
||||
"duration": "duration",
|
||||
"ascent": "ascent",
|
||||
"descent": "descent",
|
||||
"month": "month",
|
||||
"Statistics": "Statistics",
|
||||
"year": "year",
|
||||
"week": "week"
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"Cycling (Sport)": "Vélo (Sport)",
|
||||
"Cycling (Transport)": "Vélo (Transport)",
|
||||
"Hiking": "Randonnée",
|
||||
"Mountain Biking": "VTT",
|
||||
"Mountain Biking (Electric)": "VTT (Electrique)",
|
||||
"Running": "Course",
|
||||
"Walking": "Marche",
|
||||
"Trail" : "Trail",
|
||||
"Skiing (Alpine)" : "Ski (Alpin)",
|
||||
"Skiing (Cross Country)" : "Ski (Randonnée)",
|
||||
"Rowing" : "Aviron"
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"workouts": "séances",
|
||||
"distance": "distance",
|
||||
"duration": "durée",
|
||||
"ascent": "dénivelé +",
|
||||
"descent": "dénivelé -",
|
||||
"month": "mois",
|
||||
"Statistics": "Statistiques",
|
||||
"year": "année",
|
||||
"week": "semaine"
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
{
|
||||
"Workouts": "Séances",
|
||||
"Workout": "Séance",
|
||||
"Workout Date": "Date de l'séance",
|
||||
"Add a workout": "Ajouter une séance",
|
||||
"Are you sure you want to delete this workout?": "Etes-vous sûr de vouloir supprimer cette séance ?",
|
||||
"Ave. speed": "Vitesse moyenne",
|
||||
"Ascent": "Dénivelé +",
|
||||
"Average speed": "Vitesse moyenne",
|
||||
"Chart": "Analyse",
|
||||
"data from gpx, without any cleaning": "données issues du fichier gpx, sans correction",
|
||||
"Date": "Date",
|
||||
"Delete workout": "Supprimer l'séance",
|
||||
"Descent": "Dénivelé -",
|
||||
"Distance": "Distance",
|
||||
"distance": "distance",
|
||||
"Duration": "Durée",
|
||||
"duration": "durée",
|
||||
"Edit a workout": "Editer une séance",
|
||||
"Edit workout": "Editer une workout",
|
||||
"elevation": "altitude",
|
||||
"End": "Arrivée",
|
||||
"Farest distance": "Distance la + longue",
|
||||
"Filter": "Filtrer",
|
||||
"From": "A partir de",
|
||||
"gpxFile": "fichier <strong>gpx</strong>",
|
||||
"Longest duration": "Durée la + longue",
|
||||
"Max. altitude" : "Altitude max",
|
||||
"Max. speed": "Vitesse max",
|
||||
"Min. altitude": "Altitude min",
|
||||
"no folder inside": "pas de répertoire",
|
||||
"files max": " fichiers max",
|
||||
"max size": "taille max",
|
||||
"No data to display": "Pas de données à afficher",
|
||||
"No Map": "Pas de carte",
|
||||
"No next workout": "Pas d'séance suivante",
|
||||
"No next segment": "Pas de segment suivant",
|
||||
"No notes": "Pas de notes",
|
||||
"No previous workout": "Pas d'séance précédente",
|
||||
"No previous segment": "Pas de segment précédent",
|
||||
"Notes": "Notes",
|
||||
"pauses": "pauses",
|
||||
"Personal records": "Records personnels",
|
||||
"See next workout": "Voir l'séance suivante",
|
||||
"See next segment": "Voir le segment suivant",
|
||||
"See previous workout": "Voir l'séance précédente",
|
||||
"See previous segment": "Voir le segment précédent",
|
||||
"segment": "segment",
|
||||
"Segments": "Segments",
|
||||
"Start": "Départ",
|
||||
"speed": "vitesse",
|
||||
"Title": "Titre",
|
||||
"To": "Jusqu'au",
|
||||
"total duration": "durée totale",
|
||||
"with gpx file": "avec un fichier gpx",
|
||||
"without gpx file": "sans fichier gpx",
|
||||
"zipFile": "ou un fichier <strong> zip</strong> contenant des fichiers <strong>gpx</strong>"
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
import {
|
||||
addDays,
|
||||
addMonths,
|
||||
addYears,
|
||||
format,
|
||||
startOfMonth,
|
||||
startOfWeek,
|
||||
startOfYear,
|
||||
} from 'date-fns'
|
||||
|
||||
const xAxisFormats = [
|
||||
{ duration: 'week', dateFormat: 'yyyy-MM-dd', xAxis: 'dd/MM' },
|
||||
{ duration: 'month', dateFormat: 'yyyy-MM', xAxis: 'MM/yyyy' },
|
||||
{ duration: 'year', dateFormat: 'yyyy', xAxis: 'yyyy' },
|
||||
]
|
||||
|
||||
export const formatDuration = (totalSeconds, formatWithDay = false) => {
|
||||
let days = '0'
|
||||
if (formatWithDay) {
|
||||
days = String(Math.floor(totalSeconds / 86400))
|
||||
totalSeconds %= 86400
|
||||
}
|
||||
const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0')
|
||||
totalSeconds %= 3600
|
||||
const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0')
|
||||
const seconds = String(totalSeconds % 60).padStart(2, '0')
|
||||
if (formatWithDay) {
|
||||
return `${days === '0' ? '' : `${days}d:`}${
|
||||
hours === '00' ? '' : `${hours}h:`
|
||||
}${minutes}m:${seconds}s`
|
||||
}
|
||||
return `${hours === '00' ? '' : `${hours}:`}${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
export const formatValue = (displayedData, value) =>
|
||||
value === 0
|
||||
? ''
|
||||
: displayedData === 'distance'
|
||||
? `${value.toFixed(2)} km`
|
||||
: displayedData === 'duration'
|
||||
? formatDuration(value)
|
||||
: displayedData === 'ascent'
|
||||
? `${value.toFixed(2)} km`
|
||||
: displayedData === 'descent'
|
||||
? `${value.toFixed(2)} km`
|
||||
: value
|
||||
|
||||
const dateIncrement = (duration, day) => {
|
||||
switch (duration) {
|
||||
case 'week':
|
||||
return addDays(day, 7)
|
||||
case 'year':
|
||||
return addYears(day, 1)
|
||||
case 'month':
|
||||
default:
|
||||
return addMonths(day, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const startDate = (duration, day, weekm) => {
|
||||
switch (duration) {
|
||||
case 'week':
|
||||
return startOfWeek(day, { weekStartsOn: weekm ? 1 : 0 })
|
||||
case 'year':
|
||||
return startOfYear(day)
|
||||
case 'month':
|
||||
default:
|
||||
return startOfMonth(day)
|
||||
}
|
||||
}
|
||||
|
||||
export const formatStats = (stats, sports, params, displayedSports, weekm) => {
|
||||
const nbWorkoutsStats = []
|
||||
const distanceStats = []
|
||||
const durationStats = []
|
||||
const ascentStats = []
|
||||
const descentStats = []
|
||||
|
||||
for (
|
||||
let day = startDate(params.duration, params.start, weekm);
|
||||
day <= params.end;
|
||||
day = dateIncrement(params.duration, day)
|
||||
) {
|
||||
const [xAxisFormat] = xAxisFormats.filter(
|
||||
x => x.duration === params.duration
|
||||
)
|
||||
const date = format(day, xAxisFormat.dateFormat)
|
||||
const xAxis = format(day, xAxisFormat.xAxis)
|
||||
const dataNbWorkouts = { date: xAxis }
|
||||
const dataDistance = { date: xAxis }
|
||||
const dataDuration = { date: xAxis }
|
||||
const dataAscent = { date: xAxis }
|
||||
const dataDescent = { date: xAxis }
|
||||
|
||||
if (stats[date]) {
|
||||
Object.keys(stats[date])
|
||||
.filter(sportId =>
|
||||
displayedSports ? displayedSports.includes(+sportId) : true
|
||||
)
|
||||
.map(sportId => {
|
||||
const sportLabel = sports.filter(s => s.id === +sportId)[0].label
|
||||
dataNbWorkouts[sportLabel] = stats[date][sportId].nb_workouts
|
||||
dataDistance[sportLabel] = stats[date][sportId].total_distance
|
||||
dataDuration[sportLabel] = stats[date][sportId].total_duration
|
||||
dataAscent[sportLabel] = stats[date][sportId].total_ascent / 1000
|
||||
dataDescent[sportLabel] = stats[date][sportId].total_descent / 1000
|
||||
return null
|
||||
})
|
||||
}
|
||||
nbWorkoutsStats.push(dataNbWorkouts)
|
||||
distanceStats.push(dataDistance)
|
||||
durationStats.push(dataDuration)
|
||||
ascentStats.push(dataAscent)
|
||||
descentStats.push(dataDescent)
|
||||
}
|
||||
|
||||
return {
|
||||
workouts: nbWorkoutsStats,
|
||||
distance: distanceStats,
|
||||
duration: durationStats,
|
||||
ascent: ascentStats,
|
||||
descent: descentStats,
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
import { format, subHours } from 'date-fns'
|
||||
import togeojson from '@mapbox/togeojson'
|
||||
|
||||
import { getDateWithTZ } from './index'
|
||||
|
||||
export const workoutColors = [
|
||||
'#55a8a3', // Cycling (sport)
|
||||
'#98C3A9', // Cycling (transport)
|
||||
'#D0838A', // Hiking
|
||||
'#ECC77E', // Mountain bike
|
||||
'#926692', // Running
|
||||
'#929292', // Walking
|
||||
'#f7af88', // Mountain Biking (Electric)
|
||||
'#fbc2a6', // Trail
|
||||
'#67a4bd', // Skiing (Alpine)
|
||||
'#9498d0', // Skiing (Cross Country)
|
||||
'#fad783', // Rowing
|
||||
]
|
||||
|
||||
export const recordsLabels = [
|
||||
{ record_type: 'AS', label: 'Ave. speed' },
|
||||
{ record_type: 'FD', label: 'Farest distance' },
|
||||
{ record_type: 'LD', label: 'Longest duration' },
|
||||
{ record_type: 'MS', label: 'Max. speed' },
|
||||
]
|
||||
|
||||
export const getGeoJson = gpxContent => {
|
||||
let jsonData
|
||||
if (gpxContent) {
|
||||
const gpx = new DOMParser().parseFromString(gpxContent, 'text/xml')
|
||||
jsonData = togeojson.gpx(gpx)
|
||||
}
|
||||
return { jsonData }
|
||||
}
|
||||
|
||||
export const formatWorkoutDate = (
|
||||
dateTime,
|
||||
dateFormat = null,
|
||||
timeFormat = null
|
||||
) => {
|
||||
if (!dateFormat) {
|
||||
dateFormat = 'yyyy/MM/dd'
|
||||
}
|
||||
if (!timeFormat) {
|
||||
timeFormat = 'HH:mm'
|
||||
}
|
||||
return {
|
||||
workout_date: dateTime ? format(dateTime, dateFormat) : null,
|
||||
workout_time: dateTime ? format(dateTime, timeFormat) : null,
|
||||
}
|
||||
}
|
||||
|
||||
export const formatWorkoutDuration = seconds => {
|
||||
let newDate = new Date(0)
|
||||
newDate = subHours(newDate.setSeconds(seconds), 1)
|
||||
return newDate.getTime()
|
||||
}
|
||||
|
||||
export const formatChartData = chartData => {
|
||||
for (let i = 0; i < chartData.length; i++) {
|
||||
chartData[i].time = new Date(chartData[i].time).getTime()
|
||||
chartData[i].duration = formatWorkoutDuration(chartData[i].duration)
|
||||
}
|
||||
return chartData
|
||||
}
|
||||
|
||||
export const formatRecord = (record, tz) => {
|
||||
let value
|
||||
switch (record.record_type) {
|
||||
case 'AS':
|
||||
case 'MS':
|
||||
value = `${record.value} km/h`
|
||||
break
|
||||
case 'FD':
|
||||
value = `${record.value} km`
|
||||
break
|
||||
default:
|
||||
// 'LD'
|
||||
value = record.value // eslint-disable-line prefer-destructuring
|
||||
}
|
||||
const [recordType] = recordsLabels.filter(
|
||||
r => r.record_type === record.record_type
|
||||
)
|
||||
return {
|
||||
workout_date: formatWorkoutDate(getDateWithTZ(record.workout_date, tz))
|
||||
.workout_date,
|
||||
workout_id: record.workout_id,
|
||||
id: record.id,
|
||||
record_type: recordType.label,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
const sortSports = (a, b) => {
|
||||
const sportALabel = a.label.toLowerCase()
|
||||
const sportBLabel = b.label.toLowerCase()
|
||||
return sportALabel > sportBLabel ? 1 : sportALabel < sportBLabel ? -1 : 0
|
||||
}
|
||||
|
||||
export const translateSports = (sports, t, onlyActive = false) =>
|
||||
sports
|
||||
.filter(sport => (onlyActive ? sport.is_active : true))
|
||||
.map(sport => ({
|
||||
...sport,
|
||||
label: t(`sports:${sport.label}`),
|
||||
}))
|
||||
.sort(sortSports)
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user