update js dependencies

This commit is contained in:
Sam 2021-11-06 21:27:23 +01:00
parent 3d66f6dd70
commit c7024e6692
65 changed files with 1353 additions and 15211 deletions

View File

@ -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)

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.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>

View File

@ -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"
}
]);

View File

@ -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"});

View 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

File diff suppressed because one or more lines are too long

View File

@ -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

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

@ -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

View 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

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

@ -5,7 +5,6 @@
"quoteProps": "as-needed",
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"printWidth": 80,
"endOfLine": "auto",

View File

@ -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,

View File

@ -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}`
}
}

View File

@ -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'

View File

@ -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

View File

@ -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/

View File

@ -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

View File

@ -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;
}
}

View File

@ -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
}

View File

@ -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>
)
}
}

View File

@ -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)

View File

@ -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>
)
}
}

View File

@ -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>
)
}

View File

@ -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)
)

View File

@ -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>
)
}
}

View File

@ -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"
}

View File

@ -1,11 +0,0 @@
{
"workouts": "workouts",
"distance": "distance",
"duration": "duration",
"ascent": "ascent",
"descent": "descent",
"month": "month",
"Statistics": "Statistics",
"year": "year",
"week": "week"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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>"
}

View File

@ -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,
}
}

View File

@ -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