Merge pull request #245 from jat255/add_date_formats
Add ability to customize format used to display dates throughout the app
This commit is contained in:
@@ -42,6 +42,9 @@ clean-install: clean
|
||||
rm -rf dist/
|
||||
|
||||
## Docker commands for evaluation purposes
|
||||
docker-bandit:
|
||||
docker-compose -f docker-compose-dev.yml exec fittrackee $(DOCKER_BANDIT) -r fittrackee -c pyproject.toml
|
||||
|
||||
docker-build:
|
||||
docker-compose -f docker-compose-dev.yml build fittrackee
|
||||
|
||||
@@ -50,11 +53,15 @@ docker-build-all: docker-build docker-build-client
|
||||
docker-build-client:
|
||||
docker-compose -f docker-compose-dev.yml build fittrackee_client
|
||||
|
||||
docker-check-all: docker-bandit docker-lint-all docker-type-check docker-test-client docker-test-python
|
||||
|
||||
docker-init: docker-run docker-init-db docker-restart docker-run-workers
|
||||
|
||||
docker-init-db:
|
||||
docker-compose -f docker-compose-dev.yml exec fittrackee docker/init-database.sh
|
||||
|
||||
docker-lint-all: docker-lint-client docker-lint-python
|
||||
|
||||
docker-lint-client:
|
||||
docker-compose -f docker-compose-dev.yml up -d fittrackee_client
|
||||
docker-compose -f docker-compose-dev.yml exec fittrackee_client yarn lint
|
||||
@@ -65,12 +72,18 @@ docker-lint-python: docker-run
|
||||
docker-logs:
|
||||
docker-compose -f docker-compose-dev.yml logs --follow
|
||||
|
||||
docker-migrate-db:
|
||||
docker-compose -f docker-compose-dev.yml exec fittrackee $(DOCKER_FLASK) db migrate --directory $(DOCKER_MIGRATIONS)
|
||||
|
||||
docker-rebuild:
|
||||
docker-compose -f docker-compose-dev.yml build --no-cache
|
||||
|
||||
docker-restart:
|
||||
docker-compose -f docker-compose-dev.yml restart fittrackee
|
||||
|
||||
docker-revision:
|
||||
docker-compose -f docker-compose-dev.yml exec fittrackee $(DOCKER_FLASK) db revision --directory $(DOCKER_MIGRATIONS) --message $(MIGRATION_MESSAGE)
|
||||
|
||||
docker-run-all: docker-run docker-run-workers
|
||||
|
||||
docker-run:
|
||||
@@ -104,9 +117,16 @@ docker-test-e2e: docker-run
|
||||
docker-test-python: docker-run
|
||||
docker-compose -f docker-compose-dev.yml exec fittrackee docker/test-python.sh $(PYTEST_ARGS)
|
||||
|
||||
docker-type-check:
|
||||
echo 'Running mypy in docker...'
|
||||
docker-compose -f docker-compose-dev.yml exec fittrackee $(DOCKER_MYPY) fittrackee
|
||||
|
||||
docker-up:
|
||||
docker-compose -f docker-compose-dev.yml up fittrackee
|
||||
|
||||
docker-upgrade-db:
|
||||
docker-compose -f docker-compose-dev.yml exec fittrackee $(DOCKER_FTCLI) db upgrade
|
||||
|
||||
downgrade-db:
|
||||
$(FLASK) db downgrade --directory $(MIGRATIONS)
|
||||
|
||||
|
||||
@@ -27,6 +27,14 @@ BANDIT = $(VENV)/bin/bandit
|
||||
PYBABEL = $(VENV)/bin/pybabel
|
||||
FTCLI = $(VENV)/bin/ftcli
|
||||
|
||||
# Docker env
|
||||
export DOCKER_APP_DIR = /usr/src/app
|
||||
export DOCKER_MIGRATIONS = $(DOCKER_APP_DIR)/fittrackee/migrations
|
||||
export DOCKER_FLASK = /usr/local/bin/flask
|
||||
export DOCKER_FTCLI = /usr/local/bin/ftcli
|
||||
export DOCKER_BANDIT = /usr/local/bin/bandit
|
||||
export DOCKER_MYPY = /usr/local/bin/mypy
|
||||
|
||||
# Node env
|
||||
NODE_MODULES = $(PWD)/fittrackee_client/node_modules
|
||||
NPM ?= yarn
|
||||
|
||||
Vendored
+1
-1
@@ -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><script defer="defer" src="/static/js/chunk-vendors.e9c5b3a5.js"></script><script defer="defer" src="/static/js/app.f492102f.js"></script><link href="/static/css/app.eee1934d.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></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><script defer="defer" src="/static/js/chunk-vendors.a306e708.js"></script><script defer="defer" src="/static/js/app.2c35ea55.js"></script><link href="/static/css/app.a67ca8d9.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></body></html>
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,2 +1,2 @@
|
||||
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[328],{6e3:function(t,e,i){i.r(e),i.d(e,{default:function(){return _}});var a=i(6252),n=i(2262),s=i(8273),c=i(5801),r=i(9917);const S=t=>((0,a.dD)("data-v-64629971"),t=t(),(0,a.Cn)(),t),l={id:"admin",class:"view"},p={key:0,class:"container"},u=S((()=>(0,a._)("div",{id:"bottom"},null,-1)));var T=(0,a.aZ)({__name:"AdminView",setup(t){const e=(0,r.o)(),i=(0,a.Fl)((()=>e.getters[c.SY.GETTERS.APP_CONFIG])),S=(0,a.Fl)((()=>e.getters[c.SY.GETTERS.APP_STATS])),T=(0,a.Fl)((()=>e.getters[c.YN.GETTERS.IS_ADMIN])),d=(0,a.Fl)((()=>e.getters[c.YN.GETTERS.USER_LOADING]));return(0,a.wF)((()=>e.dispatch(c.SY.ACTIONS.GET_APPLICATION_STATS))),(t,e)=>{const c=(0,a.up)("router-view");return(0,a.wg)(),(0,a.iD)("div",l,[(0,n.SU)(d)?(0,a.kq)("",!0):((0,a.wg)(),(0,a.iD)("div",p,[(0,n.SU)(T)?((0,a.wg)(),(0,a.j4)(c,{key:0,appConfig:(0,n.SU)(i),appStatistics:(0,n.SU)(S)},null,8,["appConfig","appStatistics"])):((0,a.wg)(),(0,a.j4)(s.Z,{key:1})),u]))])}}}),d=i(3744);const o=(0,d.Z)(T,[["__scopeId","data-v-64629971"]]);var _=o}}]);
|
||||
//# sourceMappingURL=admin.ab9e5f5f.js.map
|
||||
//# sourceMappingURL=admin.3b0b84c0.js.map
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"static/js/admin.ab9e5f5f.js","mappings":"mOAGA,MAAMA,EAAeC,KAAMC,EAAAA,EAAAA,IAAa,mBAAmBD,EAAEA,KAAIE,EAAAA,EAAAA,MAAcF,GACzEG,EAAa,CACjBC,GAAI,QACJC,MAAO,QAEHC,EAAa,CACjBC,IAAK,EACLF,MAAO,aAEHG,EAA2BT,GAAa,KAAmBU,EAAAA,EAAAA,GAAoB,MAAO,CAAEL,GAAI,UAAY,MAAO,KAUrH,OAA4BM,EAAAA,EAAAA,IAAiB,CAC3CC,OAAQ,YACRC,MAAMC,GAEN,MAAMC,GAAQC,EAAAA,EAAAA,KAERC,GAAqCC,EAAAA,EAAAA,KACzC,IAAMH,EAAMI,QAAQC,EAAAA,GAAAA,QAAAA,cAEhBC,GAA6CH,EAAAA,EAAAA,KACjD,IAAMH,EAAMI,QAAQC,EAAAA,GAAAA,QAAAA,aAEhBE,GAAuCJ,EAAAA,EAAAA,KAC3C,IAAMH,EAAMI,QAAQI,EAAAA,GAAAA,QAAAA,YAEhBC,GAAoCN,EAAAA,EAAAA,KACxC,IAAMH,EAAMI,QAAQI,EAAAA,GAAAA,QAAAA,gBAKxB,OAFEE,EAAAA,EAAAA,KAAc,IAAMV,EAAMW,SAASN,EAAAA,GAAAA,QAAAA,yBAE9B,CAACO,EAAUC,KAChB,MAAMC,GAAyBC,EAAAA,EAAAA,IAAkB,eAEjD,OAAQC,EAAAA,EAAAA,OAAcC,EAAAA,EAAAA,IAAoB,MAAO5B,EAAY,EACzD6B,EAAAA,EAAAA,IAAOT,IAWLU,EAAAA,EAAAA,IAAoB,IAAI,KAVvBH,EAAAA,EAAAA,OAAcC,EAAAA,EAAAA,IAAoB,MAAOzB,EAAY,EACnD0B,EAAAA,EAAAA,IAAOX,KACHS,EAAAA,EAAAA,OAAcI,EAAAA,EAAAA,IAAaN,EAAwB,CAClDrB,IAAK,EACLS,WAAWgB,EAAAA,EAAAA,IAAOhB,GAClBI,eAAeY,EAAAA,EAAAA,IAAOZ,IACrB,KAAM,EAAG,CAAC,YAAa,qBACzBU,EAAAA,EAAAA,OAAcI,EAAAA,EAAAA,IAAaC,EAAAA,EAAU,CAAE5B,IAAK,KACjDC,MAVR,CAeD,I,UCvDD,MAAM4B,GAA2B,OAAgB,EAAQ,CAAC,CAAC,YAAY,qBAEvE,O","sources":["webpack://fittrackee_client/./src/views/AdminView.vue?67de","webpack://fittrackee_client/./src/views/AdminView.vue"],"sourcesContent":["import { defineComponent as _defineComponent } from 'vue'\nimport { unref as _unref, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock, createCommentVNode as _createCommentVNode, createElementVNode as _createElementVNode, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \"vue\"\n\nconst _withScopeId = n => (_pushScopeId(\"data-v-64629971\"),n=n(),_popScopeId(),n)\nconst _hoisted_1 = {\n id: \"admin\",\n class: \"view\"\n}\nconst _hoisted_2 = {\n key: 0,\n class: \"container\"\n}\nconst _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(\"div\", { id: \"bottom\" }, null, -1))\n\nimport { computed, ComputedRef, onBeforeMount } from 'vue'\n\n import NotFound from '@/components/Common/NotFound.vue'\n import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'\n import { TAppConfig, IAppStatistics } from '@/types/application'\n import { useStore } from '@/use/useStore'\n\n \nexport default /*#__PURE__*/_defineComponent({\n __name: 'AdminView',\n setup(__props) {\n\n const store = useStore()\n\n const appConfig: ComputedRef<TAppConfig> = computed(\n () => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]\n )\n const appStatistics: ComputedRef<IAppStatistics> = computed(\n () => store.getters[ROOT_STORE.GETTERS.APP_STATS]\n )\n const isAuthUserAmin: ComputedRef<boolean> = computed(\n () => store.getters[AUTH_USER_STORE.GETTERS.IS_ADMIN]\n )\n const userLoading: ComputedRef<boolean> = computed(\n () => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]\n )\n\n onBeforeMount(() => store.dispatch(ROOT_STORE.ACTIONS.GET_APPLICATION_STATS))\n\nreturn (_ctx: any,_cache: any) => {\n const _component_router_view = _resolveComponent(\"router-view\")!\n\n return (_openBlock(), _createElementBlock(\"div\", _hoisted_1, [\n (!_unref(userLoading))\n ? (_openBlock(), _createElementBlock(\"div\", _hoisted_2, [\n (_unref(isAuthUserAmin))\n ? (_openBlock(), _createBlock(_component_router_view, {\n key: 0,\n appConfig: _unref(appConfig),\n appStatistics: _unref(appStatistics)\n }, null, 8, [\"appConfig\", \"appStatistics\"]))\n : (_openBlock(), _createBlock(NotFound, { key: 1 })),\n _hoisted_3\n ]))\n : _createCommentVNode(\"\", true)\n ]))\n}\n}\n\n})","import script from \"./AdminView.vue?vue&type=script&setup=true&lang=ts\"\nexport * from \"./AdminView.vue?vue&type=script&setup=true&lang=ts\"\n\nimport \"./AdminView.vue?vue&type=style&index=0&id=64629971&lang=scss&scoped=true\"\n\nimport exportComponent from \"/mnt/data-lnx/Devs/00_Perso/FitTrackee/fittrackee_client/node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['__scopeId',\"data-v-64629971\"]])\n\nexport default __exports__"],"names":["_withScopeId","n","_pushScopeId","_popScopeId","_hoisted_1","id","class","_hoisted_2","key","_hoisted_3","_createElementVNode","_defineComponent","__name","setup","__props","store","useStore","appConfig","computed","getters","ROOT_STORE","appStatistics","isAuthUserAmin","AUTH_USER_STORE","userLoading","onBeforeMount","dispatch","_ctx","_cache","_component_router_view","_resolveComponent","_openBlock","_createElementBlock","_unref","_createCommentVNode","_createBlock","NotFound","__exports__"],"sourceRoot":""}
|
||||
{"version":3,"file":"static/js/admin.3b0b84c0.js","mappings":"mOAGA,MAAMA,EAAeC,KAAMC,EAAAA,EAAAA,IAAa,mBAAmBD,EAAEA,KAAIE,EAAAA,EAAAA,MAAcF,GACzEG,EAAa,CACjBC,GAAI,QACJC,MAAO,QAEHC,EAAa,CACjBC,IAAK,EACLF,MAAO,aAEHG,EAA2BT,GAAa,KAAmBU,EAAAA,EAAAA,GAAoB,MAAO,CAAEL,GAAI,UAAY,MAAO,KAUrH,OAA4BM,EAAAA,EAAAA,IAAiB,CAC3CC,OAAQ,YACRC,MAAMC,GAEN,MAAMC,GAAQC,EAAAA,EAAAA,KAERC,GAAqCC,EAAAA,EAAAA,KACzC,IAAMH,EAAMI,QAAQC,EAAAA,GAAAA,QAAAA,cAEhBC,GAA6CH,EAAAA,EAAAA,KACjD,IAAMH,EAAMI,QAAQC,EAAAA,GAAAA,QAAAA,aAEhBE,GAAuCJ,EAAAA,EAAAA,KAC3C,IAAMH,EAAMI,QAAQI,EAAAA,GAAAA,QAAAA,YAEhBC,GAAoCN,EAAAA,EAAAA,KACxC,IAAMH,EAAMI,QAAQI,EAAAA,GAAAA,QAAAA,gBAKxB,OAFEE,EAAAA,EAAAA,KAAc,IAAMV,EAAMW,SAASN,EAAAA,GAAAA,QAAAA,yBAE9B,CAACO,EAAUC,KAChB,MAAMC,GAAyBC,EAAAA,EAAAA,IAAkB,eAEjD,OAAQC,EAAAA,EAAAA,OAAcC,EAAAA,EAAAA,IAAoB,MAAO5B,EAAY,EACzD6B,EAAAA,EAAAA,IAAOT,IAWLU,EAAAA,EAAAA,IAAoB,IAAI,KAVvBH,EAAAA,EAAAA,OAAcC,EAAAA,EAAAA,IAAoB,MAAOzB,EAAY,EACnD0B,EAAAA,EAAAA,IAAOX,KACHS,EAAAA,EAAAA,OAAcI,EAAAA,EAAAA,IAAaN,EAAwB,CAClDrB,IAAK,EACLS,WAAWgB,EAAAA,EAAAA,IAAOhB,GAClBI,eAAeY,EAAAA,EAAAA,IAAOZ,IACrB,KAAM,EAAG,CAAC,YAAa,qBACzBU,EAAAA,EAAAA,OAAcI,EAAAA,EAAAA,IAAaC,EAAAA,EAAU,CAAE5B,IAAK,KACjDC,MAVR,CAeD,I,UCvDD,MAAM4B,GAA2B,OAAgB,EAAQ,CAAC,CAAC,YAAY,qBAEvE,O","sources":["webpack://fittrackee_client/./src/views/AdminView.vue?67de","webpack://fittrackee_client/./src/views/AdminView.vue"],"sourcesContent":["import { defineComponent as _defineComponent } from 'vue'\nimport { unref as _unref, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock, createCommentVNode as _createCommentVNode, createElementVNode as _createElementVNode, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \"vue\"\n\nconst _withScopeId = n => (_pushScopeId(\"data-v-64629971\"),n=n(),_popScopeId(),n)\nconst _hoisted_1 = {\n id: \"admin\",\n class: \"view\"\n}\nconst _hoisted_2 = {\n key: 0,\n class: \"container\"\n}\nconst _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(\"div\", { id: \"bottom\" }, null, -1))\n\nimport { computed, ComputedRef, onBeforeMount } from 'vue'\n\n import NotFound from '@/components/Common/NotFound.vue'\n import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'\n import { TAppConfig, IAppStatistics } from '@/types/application'\n import { useStore } from '@/use/useStore'\n\n \nexport default /*#__PURE__*/_defineComponent({\n __name: 'AdminView',\n setup(__props) {\n\n const store = useStore()\n\n const appConfig: ComputedRef<TAppConfig> = computed(\n () => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]\n )\n const appStatistics: ComputedRef<IAppStatistics> = computed(\n () => store.getters[ROOT_STORE.GETTERS.APP_STATS]\n )\n const isAuthUserAmin: ComputedRef<boolean> = computed(\n () => store.getters[AUTH_USER_STORE.GETTERS.IS_ADMIN]\n )\n const userLoading: ComputedRef<boolean> = computed(\n () => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]\n )\n\n onBeforeMount(() => store.dispatch(ROOT_STORE.ACTIONS.GET_APPLICATION_STATS))\n\nreturn (_ctx: any,_cache: any) => {\n const _component_router_view = _resolveComponent(\"router-view\")!\n\n return (_openBlock(), _createElementBlock(\"div\", _hoisted_1, [\n (!_unref(userLoading))\n ? (_openBlock(), _createElementBlock(\"div\", _hoisted_2, [\n (_unref(isAuthUserAmin))\n ? (_openBlock(), _createBlock(_component_router_view, {\n key: 0,\n appConfig: _unref(appConfig),\n appStatistics: _unref(appStatistics)\n }, null, 8, [\"appConfig\", \"appStatistics\"]))\n : (_openBlock(), _createBlock(NotFound, { key: 1 })),\n _hoisted_3\n ]))\n : _createCommentVNode(\"\", true)\n ]))\n}\n}\n\n})","import script from \"./AdminView.vue?vue&type=script&setup=true&lang=ts\"\nexport * from \"./AdminView.vue?vue&type=script&setup=true&lang=ts\"\n\nimport \"./AdminView.vue?vue&type=style&index=0&id=64629971&lang=scss&scoped=true\"\n\nimport exportComponent from \"/mnt/data-lnx/Devs/00_Perso/FitTrackee/fittrackee_client/node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['__scopeId',\"data-v-64629971\"]])\n\nexport default __exports__"],"names":["_withScopeId","n","_pushScopeId","_popScopeId","_hoisted_1","id","class","_hoisted_2","key","_hoisted_3","_createElementVNode","_defineComponent","__name","setup","__props","store","useStore","appConfig","computed","getters","ROOT_STORE","appStatistics","isAuthUserAmin","AUTH_USER_STORE","userLoading","onBeforeMount","dispatch","_ctx","_cache","_component_router_view","_resolveComponent","_openBlock","_createElementBlock","_unref","_createCommentVNode","_createBlock","NotFound","__exports__"],"sourceRoot":""}
|
||||
+2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -1,2 +1,2 @@
|
||||
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[845],{4264:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});r(7658);var n=r(6252),a=r(2262),s=r(3577),u=r(2201),o=r(7167),i=r(5801),c=r(9917);const l={key:0,id:"account-confirmation",class:"center-card with-margin"},E={class:"error-message"};var _=(0,n.aZ)({__name:"AccountConfirmationView",setup(e){const t=(0,u.yj)(),r=(0,u.tv)(),_=(0,c.o)(),d=(0,n.Fl)((()=>_.getters[i.SY.GETTERS.ERROR_MESSAGES])),S=(0,n.Fl)((()=>t.query.token));function m(){S.value?_.dispatch(i.YN.ACTIONS.CONFIRM_ACCOUNT,{token:S.value}):r.push("/")}return(0,n.wF)((()=>m())),(0,n.Ah)((()=>_.commit(i.SY.MUTATIONS.EMPTY_ERROR_MESSAGES))),(e,t)=>{const r=(0,n.up)("router-link");return(0,a.SU)(d)?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(o.Z),(0,n._)("p",E,[(0,n._)("span",null,(0,s.zw)(e.$t("error.SOMETHING_WRONG"))+".",1),(0,n.Wm)(r,{class:"links",to:"/account-confirmation/resend"},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.$t("buttons.ACCOUNT-CONFIRMATION-RESEND"))+"? ",1)])),_:1})])])):(0,n.kq)("",!0)}}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-785df978"]]);var m=S},8160:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});r(7658);var n=r(6252),a=r(2262),s=r(3577),u=r(2201),o=r(7167),i=r(5801),c=r(9917);const l={key:0,id:"email-update",class:"center-card with-margin"},E={class:"error-message"};var _=(0,n.aZ)({__name:"EmailUpdateView",setup(e){const t=(0,u.yj)(),r=(0,u.tv)(),_=(0,c.o)(),d=(0,n.Fl)((()=>_.getters[i.YN.GETTERS.AUTH_USER_PROFILE])),S=(0,n.Fl)((()=>_.getters[i.YN.GETTERS.IS_AUTHENTICATED])),m=(0,n.Fl)((()=>_.getters[i.SY.GETTERS.ERROR_MESSAGES])),p=(0,n.Fl)((()=>t.query.token));function R(){p.value?_.dispatch(i.YN.ACTIONS.CONFIRM_EMAIL,{token:p.value,refreshUser:S.value}):r.push("/")}return(0,n.wF)((()=>R())),(0,n.Ah)((()=>_.commit(i.SY.MUTATIONS.EMPTY_ERROR_MESSAGES))),(0,n.YP)((()=>m.value),(e=>{d.value.username&&e&&r.push("/")})),(e,t)=>{const r=(0,n.up)("router-link"),u=(0,n.up)("i18n-t");return(0,a.SU)(m)&&!(0,a.SU)(d).username?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(o.Z),(0,n._)("p",E,[(0,n._)("span",null,(0,s.zw)(e.$t("error.SOMETHING_WRONG"))+".",1),(0,n._)("span",null,[(0,n.Wm)(u,{keypath:"user.PROFILE.ERRORED_EMAIL_UPDATE"},{default:(0,n.w5)((()=>[(0,n.Wm)(r,{to:"/login"},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.$t("user.LOG_IN")),1)])),_:1})])),_:1})])])])):(0,n.kq)("",!0)}}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-8c2ec9ce"]]);var m=S},6266:function(e,t,r){r.r(t),r.d(t,{default:function(){return d}});var n=r(6252),a=r(2262),s=r(5801),u=r(9917);const o=e=>((0,n.dD)("data-v-05463732"),e=e(),(0,n.Cn)(),e),i={key:0,id:"profile",class:"container view"},c=o((()=>(0,n._)("div",{id:"bottom"},null,-1)));var l=(0,n.aZ)({__name:"ProfileView",setup(e){const t=(0,u.o)(),r=(0,n.Fl)((()=>t.getters[s.YN.GETTERS.AUTH_USER_PROFILE]));return(e,t)=>{const s=(0,n.up)("router-view");return(0,a.SU)(r).username?((0,n.wg)(),(0,n.iD)("div",i,[(0,n.Wm)(s,{user:(0,a.SU)(r)},null,8,["user"]),c])):(0,n.kq)("",!0)}}}),E=r(3744);const _=(0,E.Z)(l,[["__scopeId","data-v-05463732"]]);var d=_},9453:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});var n=r(6252),a=r(2262),s=r(2201),u=r(2179),o=r(7408),i=r(5801),c=r(9917);const l={key:0,id:"user",class:"view"},E={class:"box"};var _=(0,n.aZ)({__name:"UserView",props:{fromAdmin:{type:Boolean}},setup(e){const t=e,{fromAdmin:r}=(0,a.BK)(t),_=(0,s.yj)(),d=(0,c.o)(),S=(0,n.Fl)((()=>d.getters[i.RT.GETTERS.USER]));return(0,n.wF)((()=>{_.params.username&&"string"===typeof _.params.username&&d.dispatch(i.RT.ACTIONS.GET_USER,_.params.username)})),(0,n.Jd)((()=>{d.dispatch(i.RT.ACTIONS.EMPTY_USER)})),(e,t)=>(0,a.SU)(S).username?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(u.Z,{user:(0,a.SU)(S)},null,8,["user"]),(0,n._)("div",E,[(0,n.Wm)(o.Z,{user:(0,a.SU)(S),"from-admin":(0,a.SU)(r)},null,8,["user","from-admin"])])])):(0,n.kq)("",!0)}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-af7007f4"]]);var m=S}}]);
|
||||
//# sourceMappingURL=profile.dd30724d.js.map
|
||||
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[845],{4264:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});r(7658);var n=r(6252),a=r(2262),s=r(3577),u=r(2201),o=r(7167),i=r(5801),c=r(9917);const l={key:0,id:"account-confirmation",class:"center-card with-margin"},E={class:"error-message"};var _=(0,n.aZ)({__name:"AccountConfirmationView",setup(e){const t=(0,u.yj)(),r=(0,u.tv)(),_=(0,c.o)(),d=(0,n.Fl)((()=>_.getters[i.SY.GETTERS.ERROR_MESSAGES])),S=(0,n.Fl)((()=>t.query.token));function m(){S.value?_.dispatch(i.YN.ACTIONS.CONFIRM_ACCOUNT,{token:S.value}):r.push("/")}return(0,n.wF)((()=>m())),(0,n.Ah)((()=>_.commit(i.SY.MUTATIONS.EMPTY_ERROR_MESSAGES))),(e,t)=>{const r=(0,n.up)("router-link");return(0,a.SU)(d)?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(o.Z),(0,n._)("p",E,[(0,n._)("span",null,(0,s.zw)(e.$t("error.SOMETHING_WRONG"))+".",1),(0,n.Wm)(r,{class:"links",to:"/account-confirmation/resend"},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.$t("buttons.ACCOUNT-CONFIRMATION-RESEND"))+"? ",1)])),_:1})])])):(0,n.kq)("",!0)}}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-785df978"]]);var m=S},8160:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});r(7658);var n=r(6252),a=r(2262),s=r(3577),u=r(2201),o=r(7167),i=r(5801),c=r(9917);const l={key:0,id:"email-update",class:"center-card with-margin"},E={class:"error-message"};var _=(0,n.aZ)({__name:"EmailUpdateView",setup(e){const t=(0,u.yj)(),r=(0,u.tv)(),_=(0,c.o)(),d=(0,n.Fl)((()=>_.getters[i.YN.GETTERS.AUTH_USER_PROFILE])),S=(0,n.Fl)((()=>_.getters[i.YN.GETTERS.IS_AUTHENTICATED])),m=(0,n.Fl)((()=>_.getters[i.SY.GETTERS.ERROR_MESSAGES])),p=(0,n.Fl)((()=>t.query.token));function R(){p.value?_.dispatch(i.YN.ACTIONS.CONFIRM_EMAIL,{token:p.value,refreshUser:S.value}):r.push("/")}return(0,n.wF)((()=>R())),(0,n.Ah)((()=>_.commit(i.SY.MUTATIONS.EMPTY_ERROR_MESSAGES))),(0,n.YP)((()=>m.value),(e=>{d.value.username&&e&&r.push("/")})),(e,t)=>{const r=(0,n.up)("router-link"),u=(0,n.up)("i18n-t");return(0,a.SU)(m)&&!(0,a.SU)(d).username?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(o.Z),(0,n._)("p",E,[(0,n._)("span",null,(0,s.zw)(e.$t("error.SOMETHING_WRONG"))+".",1),(0,n._)("span",null,[(0,n.Wm)(u,{keypath:"user.PROFILE.ERRORED_EMAIL_UPDATE"},{default:(0,n.w5)((()=>[(0,n.Wm)(r,{to:"/login"},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.$t("user.LOG_IN")),1)])),_:1})])),_:1})])])])):(0,n.kq)("",!0)}}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-8c2ec9ce"]]);var m=S},6266:function(e,t,r){r.r(t),r.d(t,{default:function(){return d}});var n=r(6252),a=r(2262),s=r(5801),u=r(9917);const o=e=>((0,n.dD)("data-v-05463732"),e=e(),(0,n.Cn)(),e),i={key:0,id:"profile",class:"container view"},c=o((()=>(0,n._)("div",{id:"bottom"},null,-1)));var l=(0,n.aZ)({__name:"ProfileView",setup(e){const t=(0,u.o)(),r=(0,n.Fl)((()=>t.getters[s.YN.GETTERS.AUTH_USER_PROFILE]));return(e,t)=>{const s=(0,n.up)("router-view");return(0,a.SU)(r).username?((0,n.wg)(),(0,n.iD)("div",i,[(0,n.Wm)(s,{user:(0,a.SU)(r)},null,8,["user"]),c])):(0,n.kq)("",!0)}}}),E=r(3744);const _=(0,E.Z)(l,[["__scopeId","data-v-05463732"]]);var d=_},9453:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});var n=r(6252),a=r(2262),s=r(2201),u=r(2179),o=r(1585),i=r(5801),c=r(9917);const l={key:0,id:"user",class:"view"},E={class:"box"};var _=(0,n.aZ)({__name:"UserView",props:{fromAdmin:{type:Boolean}},setup(e){const t=e,{fromAdmin:r}=(0,a.BK)(t),_=(0,s.yj)(),d=(0,c.o)(),S=(0,n.Fl)((()=>d.getters[i.RT.GETTERS.USER]));return(0,n.wF)((()=>{_.params.username&&"string"===typeof _.params.username&&d.dispatch(i.RT.ACTIONS.GET_USER,_.params.username)})),(0,n.Jd)((()=>{d.dispatch(i.RT.ACTIONS.EMPTY_USER)})),(e,t)=>(0,a.SU)(S).username?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(u.Z,{user:(0,a.SU)(S)},null,8,["user"]),(0,n._)("div",E,[(0,n.Wm)(o.Z,{user:(0,a.SU)(S),"from-admin":(0,a.SU)(r)},null,8,["user","from-admin"])])])):(0,n.kq)("",!0)}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-af7007f4"]]);var m=S}}]);
|
||||
//# sourceMappingURL=profile.23749cd8.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[193],{7885:function(e,s,t){t.r(s),t.d(s,{default:function(){return A}});var a=t(6252),r=t(2262),l=t(3577),o=(t(7658),t(9150)),n=t(4998);const c={class:"chart-menu"},i={class:"chart-arrow"},u={class:"time-frames custom-checkboxes-group"},d={class:"time-frames-checkboxes custom-checkboxes"},p=["id","name","checked","onInput"],m={class:"chart-arrow"};var v=(0,a.aZ)({__name:"StatsMenu",emits:["arrowClick","timeFrameUpdate"],setup(e,{emit:s}){const t=(0,r.iH)("month"),o=["week","month","year"];function n(e){t.value=e,s("timeFrameUpdate",e)}return(e,r)=>((0,a.wg)(),(0,a.iD)("div",c,[(0,a._)("div",i,[(0,a._)("i",{class:"fa fa-chevron-left","aria-hidden":"true",onClick:r[0]||(r[0]=e=>s("arrowClick",!0))})]),(0,a._)("div",u,[(0,a._)("div",d,[((0,a.wg)(),(0,a.iD)(a.HY,null,(0,a.Ko)(o,(s=>(0,a._)("div",{class:"time-frame custom-checkbox",key:s},[(0,a._)("label",null,[(0,a._)("input",{type:"radio",id:s,name:s,checked:t.value===s,onInput:e=>n(s)},null,40,p),(0,a._)("span",null,(0,l.zw)(e.$t(`statistics.TIME_FRAMES.${s}`)),1)])]))),64))])]),(0,a._)("div",m,[(0,a._)("i",{class:"fa fa-chevron-right","aria-hidden":"true",onClick:r[1]||(r[1]=e=>s("arrowClick",!1))})])]))}}),k=t(3744);const _=(0,k.Z)(v,[["__scopeId","data-v-22d55de2"]]);var S=_,w=t(631);const f={class:"sports-menu"},h=["id","name","checked","onInput"],U={class:"sport-label"};var b=(0,a.aZ)({__name:"StatsSportsMenu",props:{userSports:null,selectedSportIds:{default:()=>[]}},emits:["selectedSportIdsUpdate"],setup(e,{emit:s}){const t=e,{t:n}=(0,o.QT)(),c=(0,a.f3)("sportColors"),{selectedSportIds:i}=(0,r.BK)(t),u=(0,a.Fl)((()=>(0,w.xH)(t.userSports,n)));function d(e){s("selectedSportIdsUpdate",e)}return(e,s)=>{const t=(0,a.up)("SportImage");return(0,a.wg)(),(0,a.iD)("div",f,[((0,a.wg)(!0),(0,a.iD)(a.HY,null,(0,a.Ko)((0,r.SU)(u),(e=>((0,a.wg)(),(0,a.iD)("label",{type:"checkbox",key:e.id,style:(0,l.j5)({color:e.color?e.color:(0,r.SU)(c)[e.label]})},[(0,a._)("input",{type:"checkbox",id:e.id,name:e.label,checked:(0,r.SU)(i).includes(e.id),onInput:s=>d(e.id)},null,40,h),(0,a.Wm)(t,{"sport-label":e.label,color:e.color},null,8,["sport-label","color"]),(0,a._)("span",U,(0,l.zw)(e.translatedLabel),1)],4)))),128))])}}});const I=b;var g=I,T=t(9318);const y={key:0,id:"user-statistics"};var C=(0,a.aZ)({__name:"index",props:{sports:null,user:null},setup(e){const s=e,{t:t}=(0,o.QT)(),{sports:l,user:c}=(0,r.BK)(s),i=(0,r.iH)("month"),u=(0,r.iH)(v(i.value)),d=(0,a.Fl)((()=>(0,w.xH)(s.sports,t))),p=(0,r.iH)(_(s.sports));function m(e){i.value=e,u.value=v(i.value)}function v(e){return(0,T.aZ)(new Date,e,s.user.weekm)}function k(e){u.value=(0,T.FN)(u.value,e,s.user.weekm)}function _(e){return e.map((e=>e.id))}function f(e){p.value.includes(e)?p.value=p.value.filter((s=>s!==e)):p.value.push(e)}return(0,a.YP)((()=>s.sports),(e=>{p.value=_(e)})),(e,s)=>(0,r.SU)(d)?((0,a.wg)(),(0,a.iD)("div",y,[(0,a.Wm)(S,{onTimeFrameUpdate:m,onArrowClick:k}),(0,a.Wm)(n.Z,{sports:(0,r.SU)(l),user:(0,r.SU)(c),chartParams:u.value,"displayed-sport-ids":p.value,fullStats:!0},null,8,["sports","user","chartParams","displayed-sport-ids"]),(0,a.Wm)(g,{"selected-sport-ids":p.value,"user-sports":(0,r.SU)(l),onSelectedSportIdsUpdate:f},null,8,["selected-sport-ids","user-sports"])])):(0,a.kq)("",!0)}});const F=(0,k.Z)(C,[["__scopeId","data-v-30799d13"]]);var Z=F,x=t(5630),D=t(5801),H=t(9917);const E={id:"statistics",class:"view"},R={key:0,class:"container"};var W=(0,a.aZ)({__name:"StatisticsView",setup(e){const s=(0,H.o)(),t=(0,a.Fl)((()=>s.getters[D.YN.GETTERS.AUTH_USER_PROFILE])),o=(0,a.Fl)((()=>s.getters[D.O8.GETTERS.SPORTS].filter((e=>t.value.sports_list.includes(e.id)))));return(e,s)=>{const n=(0,a.up)("Card");return(0,a.wg)(),(0,a.iD)("div",E,[(0,r.SU)(t).username?((0,a.wg)(),(0,a.iD)("div",R,[(0,a.Wm)(n,null,{title:(0,a.w5)((()=>[(0,a.Uk)((0,l.zw)(e.$t("statistics.STATISTICS")),1)])),content:(0,a.w5)((()=>[(0,a.Wm)(Z,{class:(0,l.C_)({"stats-disabled":0===(0,r.SU)(t).nb_workouts}),user:(0,r.SU)(t),sports:(0,r.SU)(o)},null,8,["class","user","sports"])])),_:1}),0===(0,r.SU)(t).nb_workouts?((0,a.wg)(),(0,a.j4)(x.Z,{key:0})):(0,a.kq)("",!0)])):(0,a.kq)("",!0)])}}});const P=(0,k.Z)(W,[["__scopeId","data-v-2e341d4e"]]);var A=P}}]);
|
||||
//# sourceMappingURL=statistics.9cd652fd.js.map
|
||||
//# sourceMappingURL=statistics.eaf4afd3.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,31 @@
|
||||
"""Add date_format for date display to user preferences in DB
|
||||
|
||||
Revision ID: bf13b8f5589d
|
||||
Revises: 84d840ce853b
|
||||
Create Date: 2022-10-25 18:53:59.378423
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'bf13b8f5589d'
|
||||
down_revision = '5b936821326d'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column(
|
||||
'users', sa.Column('date_format', sa.String(length=50), nullable=True)
|
||||
)
|
||||
op.execute("UPDATE users SET date_format = 'MM/dd/yyyy'")
|
||||
op.alter_column('users', 'date_format', nullable=False)
|
||||
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'date_format')
|
||||
# ### end Alembic commands ###
|
||||
@@ -235,6 +235,27 @@ class TestUserRegistration(ApiTestCaseMixin):
|
||||
assert data['status'] == 'success'
|
||||
assert 'auth_token' not in data
|
||||
|
||||
def test_it_creates_user_with_default_date_format(
|
||||
self, app: Flask
|
||||
) -> None:
|
||||
client = app.test_client()
|
||||
username = self.random_string()
|
||||
|
||||
client.post(
|
||||
'/api/auth/register',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
username=username,
|
||||
email=self.random_email(),
|
||||
password=self.random_string(),
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
new_user = User.query.filter_by(username=username).first()
|
||||
assert new_user.date_format == 'MM/dd/yyyy'
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'input_language,expected_language',
|
||||
[('en', 'en'), ('fr', 'fr'), ('invalid', 'en'), (None, 'en')],
|
||||
@@ -1394,6 +1415,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
|
||||
language=input_language,
|
||||
imperial_units=True,
|
||||
display_ascent=False,
|
||||
date_format='yyyy-MM-dd',
|
||||
)
|
||||
),
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
@@ -1407,6 +1429,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
|
||||
assert data['data']['imperial_units'] is True
|
||||
assert data['data']['language'] == expected_language
|
||||
assert data['data']['timezone'] == 'America/New_York'
|
||||
assert data['data']['date_format'] == 'yyyy-MM-dd'
|
||||
assert data['data']['weekm'] is True
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -173,6 +173,7 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
|
||||
if not user:
|
||||
new_user = User(username=username, email=email, password=password)
|
||||
new_user.timezone = 'Europe/Paris'
|
||||
new_user.date_format = 'MM/dd/yyyy'
|
||||
new_user.confirmation_token = secrets.token_urlsafe(30)
|
||||
new_user.language = language
|
||||
db.session.add(new_user)
|
||||
@@ -780,6 +781,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
"bio": null,
|
||||
"birth_date": null,
|
||||
"created_at": "Sun, 14 Jul 2019 14:09:58 GMT",
|
||||
"date_format": "MM/dd/yyyy",
|
||||
"display_ascent": true,
|
||||
"email": "sam@example.com",
|
||||
"first_name": null,
|
||||
@@ -854,6 +856,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
}
|
||||
|
||||
:<json boolean display_ascent: display highest ascent records and total
|
||||
:<json string date_format: the format used to display dates in the app
|
||||
:<json boolean imperial_units: display distance in imperial units
|
||||
:<json string language: language preferences
|
||||
:<json string timezone: user time zone
|
||||
@@ -874,6 +877,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
# get post data
|
||||
post_data = request.get_json()
|
||||
user_mandatory_data = {
|
||||
'date_format',
|
||||
'display_ascent',
|
||||
'imperial_units',
|
||||
'language',
|
||||
@@ -883,6 +887,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
if not post_data or not post_data.keys() >= user_mandatory_data:
|
||||
return InvalidPayloadErrorResponse()
|
||||
|
||||
date_format = post_data.get('date_format')
|
||||
display_ascent = post_data.get('display_ascent')
|
||||
imperial_units = post_data.get('imperial_units')
|
||||
language = get_language(post_data.get('language'))
|
||||
@@ -890,6 +895,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
weekm = post_data.get('weekm')
|
||||
|
||||
try:
|
||||
auth_user.date_format = date_format
|
||||
auth_user.display_ascent = display_ascent
|
||||
auth_user.imperial_units = imperial_units
|
||||
auth_user.language = language
|
||||
|
||||
@@ -33,6 +33,7 @@ class User(BaseModel):
|
||||
bio = db.Column(db.String(200), nullable=True)
|
||||
picture = db.Column(db.String(255), nullable=True)
|
||||
timezone = db.Column(db.String(50), nullable=True)
|
||||
date_format = db.Column(db.String(50), nullable=True)
|
||||
# does the week start Monday?
|
||||
weekm = db.Column(db.Boolean, default=False, nullable=False)
|
||||
workouts = db.relationship(
|
||||
@@ -190,6 +191,7 @@ class User(BaseModel):
|
||||
serialized_user = {
|
||||
**serialized_user,
|
||||
**{
|
||||
'date_format': self.date_format,
|
||||
'display_ascent': self.display_ascent,
|
||||
'imperial_units': self.imperial_units,
|
||||
'language': self.language,
|
||||
|
||||
@@ -62,9 +62,10 @@
|
||||
{{ $t('user.PROFILE.REGISTRATION_DATE') }}
|
||||
</span>
|
||||
{{
|
||||
format(
|
||||
getDateWithTZ(user.created_at, authUser.timezone),
|
||||
'dd/MM/yyyy HH:mm'
|
||||
formatDate(
|
||||
user.created_at,
|
||||
authUser.timezone,
|
||||
authUser.date_format
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
@@ -130,7 +131,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import {
|
||||
ComputedRef,
|
||||
Ref,
|
||||
@@ -152,7 +152,7 @@
|
||||
import { IAuthUserProfile, IUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { getQuery, sortList } from '@/utils/api'
|
||||
import { getDateWithTZ } from '@/utils/dates'
|
||||
import { formatDate } from '@/utils/dates'
|
||||
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
span {
|
||||
padding: 2px 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
.record-type {
|
||||
flex-grow: 1;
|
||||
@@ -94,7 +94,12 @@
|
||||
.record-value {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
padding-right: $default-padding * 2;
|
||||
padding-right: $default-padding;
|
||||
}
|
||||
.record-date {
|
||||
white-space: nowrap;
|
||||
min-width: 30%;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
translateSports(props.sports, t),
|
||||
props.user.timezone,
|
||||
props.user.imperial_units,
|
||||
props.user.display_ascent
|
||||
props.user.display_ascent,
|
||||
props.user.date_format
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -130,6 +130,8 @@
|
||||
import { TAppConfig } from '@/types/application'
|
||||
import { IAuthUserProfile, IUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { formatDate, getDateFormat } from '@/utils/dates'
|
||||
import { localeFromLanguage } from '@/utils/locales'
|
||||
|
||||
interface Props {
|
||||
user: IUserProfile
|
||||
@@ -142,17 +144,28 @@
|
||||
const store = useStore()
|
||||
|
||||
const { user, fromAdmin } = toRefs(props)
|
||||
const language: ComputedRef<string> = computed(
|
||||
() => store.getters[ROOT_STORE.GETTERS.LANGUAGE]
|
||||
)
|
||||
const authUser: ComputedRef<IAuthUserProfile> = computed(
|
||||
() => store.getters[AUTH_USER_STORE.GETTERS.AUTH_USER_PROFILE]
|
||||
)
|
||||
const registrationDate = computed(() =>
|
||||
props.user.created_at
|
||||
? format(new Date(props.user.created_at), 'dd/MM/yyyy HH:mm')
|
||||
? formatDate(
|
||||
props.user.created_at,
|
||||
authUser.value.timezone,
|
||||
authUser.value.date_format
|
||||
)
|
||||
: ''
|
||||
)
|
||||
const birthDate = computed(() =>
|
||||
props.user.birth_date
|
||||
? format(new Date(props.user.birth_date), 'dd/MM/yyyy')
|
||||
? format(
|
||||
new Date(props.user.birth_date),
|
||||
`${getDateFormat(authUser.value.date_format, language.value)}`,
|
||||
{ locale: localeFromLanguage[language.value] }
|
||||
)
|
||||
: ''
|
||||
)
|
||||
const isSuccess = computed(
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
<div id="user-preferences" class="description-list">
|
||||
<dl>
|
||||
<dt>{{ $t('user.PROFILE.LANGUAGE') }}:</dt>
|
||||
<dd>{{ language }}</dd>
|
||||
<dd>{{ userLanguage }}</dd>
|
||||
<dt>{{ $t('user.PROFILE.TIMEZONE') }}:</dt>
|
||||
<dd>{{ timezone }}</dd>
|
||||
<dt>{{ $t('user.PROFILE.DATE_FORMAT') }}:</dt>
|
||||
<dd>{{ getDateFormat(date_format, appLanguage) }}</dd>
|
||||
<dt>{{ $t('user.PROFILE.FIRST_DAY_OF_WEEK') }}:</dt>
|
||||
<dd>{{ $t(`user.PROFILE.${fistDayOfWeek}`) }}</dd>
|
||||
<dt>{{ $t('user.PROFILE.UNITS.LABEL') }}:</dt>
|
||||
@@ -28,9 +30,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, ComputedRef } from 'vue'
|
||||
|
||||
import { ROOT_STORE } from '@/store/constants'
|
||||
import { IAuthUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { getDateFormat } from '@/utils/dates'
|
||||
import { languageLabels } from '@/utils/locales'
|
||||
|
||||
interface Props {
|
||||
@@ -38,7 +43,12 @@
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const language = computed(() =>
|
||||
const store = useStore()
|
||||
|
||||
const appLanguage: ComputedRef<string> = computed(
|
||||
() => store.getters[ROOT_STORE.GETTERS.LANGUAGE]
|
||||
)
|
||||
const userLanguage = computed(() =>
|
||||
props.user.language
|
||||
? languageLabels[props.user.language]
|
||||
: languageLabels['en']
|
||||
@@ -47,6 +57,9 @@
|
||||
const timezone = computed(() =>
|
||||
props.user.timezone ? props.user.timezone : 'Europe/Paris'
|
||||
)
|
||||
const date_format = computed(() =>
|
||||
props.user.date_format ? props.user.date_format : 'MM/dd/yyyy'
|
||||
)
|
||||
const display_ascent = computed(() =>
|
||||
props.user.display_ascent ? 'DISPLAYED' : 'HIDDEN'
|
||||
)
|
||||
|
||||
@@ -65,11 +65,12 @@
|
||||
import { ComputedRef, computed, reactive, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'
|
||||
import { IUserProfile, IUserPayload } from '@/types/user'
|
||||
import { IUserProfile, IUserPayload, IAuthUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { formatDate } from '@/utils/dates'
|
||||
|
||||
interface Props {
|
||||
user: IUserProfile
|
||||
user: IAuthUserProfile
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
@@ -84,7 +85,11 @@
|
||||
})
|
||||
const registrationDate = computed(() =>
|
||||
props.user.created_at
|
||||
? format(new Date(props.user.created_at), 'dd/MM/yyyy HH:mm')
|
||||
? formatDate(
|
||||
props.user.created_at,
|
||||
props.user.timezone,
|
||||
props.user.date_format
|
||||
)
|
||||
: ''
|
||||
)
|
||||
const loading = computed(
|
||||
|
||||
@@ -23,6 +23,22 @@
|
||||
@updateTimezone="updateTZ"
|
||||
/>
|
||||
</label>
|
||||
<label class="form-items">
|
||||
{{ $t('user.PROFILE.DATE_FORMAT') }}
|
||||
<select
|
||||
id="date_format"
|
||||
v-model="userForm.date_format"
|
||||
:disabled="loading"
|
||||
>
|
||||
<option
|
||||
v-for="dateFormat in dateFormatOptions"
|
||||
:value="dateFormat.value"
|
||||
:key="dateFormat.value"
|
||||
>
|
||||
{{ dateFormat.label }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
<div class="form-items form-checkboxes">
|
||||
<span class="checkboxes-label">
|
||||
{{ $t('user.PROFILE.FIRST_DAY_OF_WEEK') }}
|
||||
@@ -106,6 +122,7 @@
|
||||
import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'
|
||||
import { IUserPreferencesPayload, IAuthUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { availableDateFormatOptions } from '@/utils/dates'
|
||||
import { availableLanguages } from '@/utils/locales'
|
||||
|
||||
interface Props {
|
||||
@@ -120,6 +137,7 @@
|
||||
imperial_units: false,
|
||||
language: '',
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'dd/MM/yyyy',
|
||||
weekm: false,
|
||||
})
|
||||
const weekStart = [
|
||||
@@ -158,6 +176,13 @@
|
||||
const errorMessages: ComputedRef<string | string[] | null> = computed(
|
||||
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
|
||||
)
|
||||
const dateFormatOptions = computed(() =>
|
||||
availableDateFormatOptions(
|
||||
new Date().toUTCString(),
|
||||
props.user.timezone,
|
||||
userForm.language
|
||||
)
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
if (props.user) {
|
||||
@@ -170,6 +195,7 @@
|
||||
userForm.imperial_units = user.imperial_units ? user.imperial_units : false
|
||||
userForm.language = user.language ? user.language : 'en'
|
||||
userForm.timezone = user.timezone ? user.timezone : 'Europe/Paris'
|
||||
userForm.date_format = user.date_format ? user.date_format : 'dd/MM/yyyy'
|
||||
userForm.weekm = user.weekm ? user.weekm : false
|
||||
}
|
||||
function updateProfile() {
|
||||
|
||||
@@ -50,9 +50,10 @@
|
||||
<dt>{{ capitalize($t('oauth2.APP.ISSUE_AT')) }}:</dt>
|
||||
<dd>
|
||||
{{
|
||||
format(
|
||||
getDateWithTZ(client.issued_at, authUser.timezone),
|
||||
'dd/MM/yyyy HH:mm'
|
||||
formatDate(
|
||||
client.issued_at,
|
||||
authUser.timezone,
|
||||
authUser.date_format
|
||||
)
|
||||
}}
|
||||
</dd>
|
||||
@@ -105,7 +106,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import {
|
||||
ComputedRef,
|
||||
Ref,
|
||||
@@ -124,7 +124,7 @@
|
||||
import { IOAuth2Client } from '@/types/oauth'
|
||||
import { IAuthUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { getDateWithTZ } from '@/utils/dates'
|
||||
import { formatDate } from '@/utils/dates'
|
||||
|
||||
interface Props {
|
||||
authUser: IAuthUserProfile
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
<span class="app-issued-at">
|
||||
{{ $t('oauth2.APP.ISSUE_AT') }}
|
||||
{{
|
||||
format(
|
||||
getDateWithTZ(client.issued_at, authUser.timezone),
|
||||
'dd/MM/yyyy HH:mm'
|
||||
formatDate(
|
||||
client.issued_at,
|
||||
authUser.timezone,
|
||||
authUser.date_format
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
@@ -34,7 +35,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import { ComputedRef, computed, onBeforeMount, toRefs, watch } from 'vue'
|
||||
import { LocationQuery, useRoute } from 'vue-router'
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
import { IAuthUserProfile } from '@/types/user'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { defaultPage, getNumberQueryValue } from '@/utils/api'
|
||||
import { getDateWithTZ } from '@/utils/dates'
|
||||
import { formatDate } from '@/utils/dates'
|
||||
|
||||
interface Props {
|
||||
authUser: IAuthUserProfile
|
||||
|
||||
@@ -29,10 +29,7 @@
|
||||
class="workout-date"
|
||||
v-if="workout.workout_date && user"
|
||||
:title="
|
||||
format(
|
||||
getDateWithTZ(workout.workout_date, user.timezone),
|
||||
'dd/MM/yyyy HH:mm'
|
||||
)
|
||||
formatDate(workout.workout_date, user.timezone, user.date_format)
|
||||
"
|
||||
>
|
||||
{{
|
||||
@@ -141,7 +138,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Locale, format, formatDistance } from 'date-fns'
|
||||
import { Locale, formatDistance } from 'date-fns'
|
||||
import { ComputedRef, computed, toRefs, withDefaults } from 'vue'
|
||||
|
||||
import StaticMap from '@/components/Common/StaticMap.vue'
|
||||
@@ -151,7 +148,7 @@
|
||||
import { IUserProfile } from '@/types/user'
|
||||
import { IWorkout } from '@/types/workouts'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { getDateWithTZ } from '@/utils/dates'
|
||||
import { formatDate } from '@/utils/dates'
|
||||
|
||||
interface Props {
|
||||
user: IUserProfile
|
||||
|
||||
@@ -131,7 +131,8 @@
|
||||
getDateWithTZ(
|
||||
props.workoutData.workout.workout_date,
|
||||
props.authUser.timezone
|
||||
)
|
||||
),
|
||||
props.authUser.date_format
|
||||
)
|
||||
return {
|
||||
ascent: segment ? segment.ascent : workout.ascent,
|
||||
|
||||
@@ -84,9 +84,10 @@
|
||||
{{ $t('workouts.DATE') }}
|
||||
</span>
|
||||
{{
|
||||
format(
|
||||
getDateWithTZ(workout.workout_date, user.timezone),
|
||||
'dd/MM/yyyy HH:mm'
|
||||
formatDate(
|
||||
workout.workout_date,
|
||||
user.timezone,
|
||||
user.date_format
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
@@ -162,7 +163,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import {
|
||||
ComputedRef,
|
||||
Ref,
|
||||
@@ -186,7 +186,7 @@
|
||||
import { IWorkout, TWorkoutsPayload } from '@/types/workouts'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import { getQuery, sortList, workoutsPayloadKeys } from '@/utils/api'
|
||||
import { getDateWithTZ } from '@/utils/dates'
|
||||
import { formatDate } from '@/utils/dates'
|
||||
import { getSportColor, getSportLabel } from '@/utils/sports'
|
||||
import { convertDistance } from '@/utils/units'
|
||||
import { defaultOrder } from '@/utils/workouts'
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"BACK_TO_PROFILE": "Zurück zum Profil",
|
||||
"BIO": "Biographie",
|
||||
"BIRTH_DATE": "Geburtsdatum",
|
||||
"DATE_FORMAT": "Datumsanzeigeformat",
|
||||
"EDIT": "Profil bearbeiten",
|
||||
"EDIT_PREFERENCES": "Einstellungen ändern",
|
||||
"EDIT_SPORTS_PREFERENCES": "Einstellungen für Sportarten ändern",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"BACK_TO_PROFILE": "Back to profile",
|
||||
"BIO": "Bio",
|
||||
"BIRTH_DATE": "Birth date",
|
||||
"DATE_FORMAT": "Date display format",
|
||||
"EDIT": "Edit profile",
|
||||
"EDIT_PREFERENCES": "Edit preferences",
|
||||
"EDIT_SPORTS_PREFERENCES": "Edit sports preferences",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"BACK_TO_PROFILE": "Revenir au profil",
|
||||
"BIO": "Bio",
|
||||
"BIRTH_DATE": "Date de naissance",
|
||||
"DATE_FORMAT": "Format d'affichage de la date",
|
||||
"EDIT": "Modifier le profil",
|
||||
"EDIT_PREFERENCES": "Modifier les préférences",
|
||||
"EDIT_SPORTS_PREFERENCES": "Modifier les préférences des sports",
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface IAuthUserProfile extends IUserProfile {
|
||||
imperial_units: boolean
|
||||
language: string | null
|
||||
timezone: string
|
||||
date_format: string
|
||||
weekm: boolean
|
||||
}
|
||||
|
||||
@@ -64,6 +65,7 @@ export interface IUserPreferencesPayload {
|
||||
imperial_units: boolean
|
||||
language: string
|
||||
timezone: string
|
||||
date_format: string
|
||||
weekm: boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@ import {
|
||||
} from 'date-fns'
|
||||
import { utcToZonedTime } from 'date-fns-tz'
|
||||
|
||||
import createI18n from '@/i18n'
|
||||
import { localeFromLanguage } from '@/utils/locales'
|
||||
|
||||
const { locale } = createI18n.global
|
||||
|
||||
export const getStartDate = (
|
||||
duration: string,
|
||||
day: Date,
|
||||
@@ -70,11 +75,70 @@ export const formatWorkoutDate = (
|
||||
if (!dateFormat) {
|
||||
dateFormat = 'yyyy/MM/dd'
|
||||
}
|
||||
dateFormat = getDateFormat(dateFormat, locale.value)
|
||||
if (!timeFormat) {
|
||||
timeFormat = 'HH:mm'
|
||||
}
|
||||
return {
|
||||
workout_date: format(dateTime, dateFormat),
|
||||
workout_date: format(dateTime, dateFormat, {
|
||||
locale: localeFromLanguage[locale.value],
|
||||
}),
|
||||
workout_time: format(dateTime, timeFormat),
|
||||
}
|
||||
}
|
||||
|
||||
const availableDateFormats = [
|
||||
'MM/dd/yyyy',
|
||||
'dd/MM/yyyy',
|
||||
'yyyy-MM-dd',
|
||||
'date_string',
|
||||
]
|
||||
const dateStringFormats: Record<string, string> = {
|
||||
de: 'do MMM yyyy',
|
||||
en: 'MMM. do, yyyy',
|
||||
fr: 'd MMM yyyy',
|
||||
}
|
||||
|
||||
export const getDateFormat = (dateFormat: string, language: string): string => {
|
||||
return dateFormat === 'date_string' ? dateStringFormats[language] : dateFormat
|
||||
}
|
||||
|
||||
export const formatDate = (
|
||||
dateString: string,
|
||||
timezone: string,
|
||||
dateFormat: string,
|
||||
withTime = true,
|
||||
language: string | null = null
|
||||
): string => {
|
||||
if (!language) {
|
||||
language = locale.value
|
||||
}
|
||||
return format(
|
||||
getDateWithTZ(dateString, timezone),
|
||||
`${getDateFormat(dateFormat, language)}${withTime ? ' HH:mm' : ''}`,
|
||||
{ locale: localeFromLanguage[language] }
|
||||
)
|
||||
}
|
||||
|
||||
export const availableDateFormatOptions = (
|
||||
inputDate: string,
|
||||
timezone: string,
|
||||
language: string | null = null
|
||||
) => {
|
||||
const l: string = language ? language : locale.value
|
||||
const options: Record<string, string>[] = []
|
||||
availableDateFormats.map((df) => {
|
||||
const dateFormat = getDateFormat(df, l)
|
||||
options.push({
|
||||
label: `${dateFormat} - ${formatDate(
|
||||
inputDate,
|
||||
timezone,
|
||||
dateFormat,
|
||||
false,
|
||||
l
|
||||
)}`,
|
||||
value: df,
|
||||
})
|
||||
})
|
||||
return options
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import createI18n from '@/i18n'
|
||||
import { ITranslatedSport } from '@/types/sports'
|
||||
import { TUnit } from '@/types/units'
|
||||
import { ICardRecord, IRecord, IRecordsBySports } from '@/types/workouts'
|
||||
import { formatWorkoutDate, getDateWithTZ } from '@/utils/dates'
|
||||
import { formatDate, getDateFormat } from '@/utils/dates'
|
||||
import { convertDistance, units } from '@/utils/units'
|
||||
|
||||
const { locale } = createI18n.global
|
||||
|
||||
export const formatRecord = (
|
||||
record: IRecord,
|
||||
tz: string,
|
||||
useImperialUnits: boolean
|
||||
useImperialUnits: boolean,
|
||||
date_format: string
|
||||
): Record<string, string | number> => {
|
||||
const distanceUnitFrom: TUnit = 'km'
|
||||
const distanceUnitTo: TUnit = useImperialUnits
|
||||
@@ -53,8 +57,7 @@ export const formatRecord = (
|
||||
)
|
||||
}
|
||||
return {
|
||||
workout_date: formatWorkoutDate(getDateWithTZ(record.workout_date, tz))
|
||||
.workout_date,
|
||||
workout_date: formatDate(record.workout_date, tz, date_format, false),
|
||||
workout_id: record.workout_id,
|
||||
id: record.id,
|
||||
record_type: record.record_type,
|
||||
@@ -73,9 +76,11 @@ export const getRecordsBySports = (
|
||||
translatedSports: ITranslatedSport[],
|
||||
tz: string,
|
||||
useImperialUnits: boolean,
|
||||
display_ascent: boolean
|
||||
): IRecordsBySports =>
|
||||
records
|
||||
display_ascent: boolean,
|
||||
date_format: string
|
||||
): IRecordsBySports => {
|
||||
date_format = getDateFormat(date_format, locale.value)
|
||||
return records
|
||||
.filter((r) => (display_ascent ? true : r.record_type !== 'HA'))
|
||||
.reduce((sportList: IRecordsBySports, record) => {
|
||||
const sport = translatedSports.find((s) => s.id === record.sport_id)
|
||||
@@ -88,8 +93,9 @@ export const getRecordsBySports = (
|
||||
}
|
||||
}
|
||||
sportList[sport.translatedLabel].records.push(
|
||||
formatRecord(record, tz, useImperialUnits)
|
||||
formatRecord(record, tz, useImperialUnits, date_format)
|
||||
)
|
||||
}
|
||||
return sportList
|
||||
}, {})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import {
|
||||
incrementDate,
|
||||
getStartDate,
|
||||
formatWorkoutDate,
|
||||
formatDate,
|
||||
availableDateFormatOptions,
|
||||
getDateFormat,
|
||||
} from '@/utils/dates'
|
||||
|
||||
describe('startDate (week starting Sunday)', () => {
|
||||
@@ -240,3 +243,158 @@ describe('formatWorkoutDate', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatDate', () => {
|
||||
const dateString = 'Tue, 01 Nov 2022 00:00:00 GMT'
|
||||
|
||||
const testsParams = [
|
||||
{
|
||||
description:
|
||||
'format date for "Europe/Paris" timezone and "dd/MM/yyyy" format (with time)',
|
||||
inputParams: {
|
||||
timezone: 'Europe/Paris',
|
||||
dateFormat: 'dd/MM/yyyy',
|
||||
withTime: true,
|
||||
},
|
||||
expectedDate: '01/11/2022 01:00',
|
||||
},
|
||||
{
|
||||
description:
|
||||
'format date for "America/New_York" timezone and "MM/dd/yyyy" format (w/o time)',
|
||||
inputParams: {
|
||||
timezone: 'America/New_York',
|
||||
dateFormat: 'MM/dd/yyyy',
|
||||
withTime: false,
|
||||
},
|
||||
expectedDate: '10/31/2022',
|
||||
},
|
||||
]
|
||||
testsParams.map((testParams) => {
|
||||
it(testParams.description, () => {
|
||||
assert.deepEqual(
|
||||
formatDate(
|
||||
dateString,
|
||||
testParams.inputParams.timezone,
|
||||
testParams.inputParams.dateFormat,
|
||||
testParams.inputParams.withTime
|
||||
),
|
||||
testParams.expectedDate
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatDate (w/ default value)', () => {
|
||||
it('format date for "Europe/Paris" timezone and "dd/MM/yyyy" format', () => {
|
||||
assert.deepEqual(
|
||||
formatDate('Tue, 01 Nov 2022 00:00:00 GMT', 'Europe/Paris', 'yyyy-MM-dd'),
|
||||
'2022-11-01 01:00'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getDateFormat', () => {
|
||||
const testsParams = [
|
||||
{
|
||||
inputParams: {
|
||||
dateFormat: 'dd/MM/yyyy',
|
||||
language: 'en',
|
||||
},
|
||||
expectedFormat: 'dd/MM/yyyy',
|
||||
},
|
||||
{
|
||||
inputParams: {
|
||||
dateFormat: 'MM/dd/yyyy',
|
||||
language: 'en',
|
||||
},
|
||||
expectedFormat: 'MM/dd/yyyy',
|
||||
},
|
||||
{
|
||||
inputParams: {
|
||||
dateFormat: 'yyyy-MM-dd',
|
||||
language: 'en',
|
||||
},
|
||||
expectedFormat: 'yyyy-MM-dd',
|
||||
},
|
||||
{
|
||||
inputParams: {
|
||||
dateFormat: 'date_string',
|
||||
language: 'en',
|
||||
},
|
||||
expectedFormat: 'MMM. do, yyyy',
|
||||
},
|
||||
{
|
||||
inputParams: {
|
||||
dateFormat: 'date_string',
|
||||
language: 'fr',
|
||||
},
|
||||
expectedFormat: 'd MMM yyyy',
|
||||
},
|
||||
{
|
||||
inputParams: {
|
||||
dateFormat: 'date_string',
|
||||
language: 'de',
|
||||
},
|
||||
expectedFormat: 'do MMM yyyy',
|
||||
},
|
||||
]
|
||||
testsParams.map((testParams) => {
|
||||
it(`get date format for "${testParams.inputParams.language}" and "${testParams.inputParams.dateFormat}" `, () => {
|
||||
assert.deepEqual(
|
||||
getDateFormat(
|
||||
testParams.inputParams.dateFormat,
|
||||
testParams.inputParams.language
|
||||
),
|
||||
testParams.expectedFormat
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('availableDateFormatOptions', () => {
|
||||
const inputDate = `Sun, 9 Oct 2022 18:18:41 GMT`
|
||||
const inputTimezone = `Europe/Paris`
|
||||
|
||||
const testsParams = [
|
||||
{
|
||||
inputLanguage: 'en',
|
||||
expectedOptions: [
|
||||
{ label: 'MM/dd/yyyy - 10/09/2022', value: 'MM/dd/yyyy' },
|
||||
{ label: 'dd/MM/yyyy - 09/10/2022', value: 'dd/MM/yyyy' },
|
||||
{ label: 'yyyy-MM-dd - 2022-10-09', value: 'yyyy-MM-dd' },
|
||||
{ label: 'MMM. do, yyyy - Oct. 9th, 2022', value: 'date_string' },
|
||||
],
|
||||
},
|
||||
{
|
||||
inputLanguage: 'fr',
|
||||
expectedOptions: [
|
||||
{ label: 'MM/dd/yyyy - 10/09/2022', value: 'MM/dd/yyyy' },
|
||||
{ label: 'dd/MM/yyyy - 09/10/2022', value: 'dd/MM/yyyy' },
|
||||
{ label: 'yyyy-MM-dd - 2022-10-09', value: 'yyyy-MM-dd' },
|
||||
{ label: 'd MMM yyyy - 9 oct. 2022', value: 'date_string' },
|
||||
],
|
||||
},
|
||||
{
|
||||
inputLanguage: 'de',
|
||||
expectedOptions: [
|
||||
{ label: 'MM/dd/yyyy - 10/09/2022', value: 'MM/dd/yyyy' },
|
||||
{ label: 'dd/MM/yyyy - 09/10/2022', value: 'dd/MM/yyyy' },
|
||||
{ label: 'yyyy-MM-dd - 2022-10-09', value: 'yyyy-MM-dd' },
|
||||
{ label: 'do MMM yyyy - 9. Okt. 2022', value: 'date_string' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
testsParams.map((testParams) => {
|
||||
it(`returns available options for ${testParams.inputLanguage} locale`, () => {
|
||||
assert.deepEqual(
|
||||
availableDateFormatOptions(
|
||||
inputDate,
|
||||
inputTimezone,
|
||||
testParams.inputLanguage
|
||||
),
|
||||
testParams.expectedOptions
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,6 +19,7 @@ describe('formatRecord', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
id: 9,
|
||||
@@ -41,6 +42,7 @@ describe('formatRecord', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'yyyy/MM/dd'
|
||||
},
|
||||
expected: {
|
||||
id: 10,
|
||||
@@ -63,6 +65,7 @@ describe('formatRecord', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'yyyy/MM/dd'
|
||||
},
|
||||
expected: {
|
||||
id: 11,
|
||||
@@ -85,12 +88,13 @@ describe('formatRecord', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'dd/MM/yyyy'
|
||||
},
|
||||
expected: {
|
||||
id: 12,
|
||||
record_type: 'MS',
|
||||
value: '18 km/h',
|
||||
workout_date: '2019/07/08',
|
||||
workout_date: '08/07/2019',
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
},
|
||||
@@ -107,12 +111,13 @@ describe('formatRecord', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'MMM. do, yyyy'
|
||||
},
|
||||
expected: {
|
||||
id: 13,
|
||||
record_type: 'HA',
|
||||
value: '100 m',
|
||||
workout_date: '2019/07/07',
|
||||
workout_date: 'Jul. 7th, 2019',
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
},
|
||||
@@ -123,7 +128,8 @@ describe('formatRecord', () => {
|
||||
formatRecord(
|
||||
testParams.inputParams.record,
|
||||
testParams.inputParams.timezone,
|
||||
false
|
||||
false,
|
||||
testParams.inputParams.date_format
|
||||
),
|
||||
testParams.expected
|
||||
)
|
||||
@@ -146,6 +152,7 @@ describe('formatRecord after conversion', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
id: 9,
|
||||
@@ -168,12 +175,13 @@ describe('formatRecord after conversion', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
id: 10,
|
||||
record_type: 'FD',
|
||||
value: '11.185 mi',
|
||||
workout_date: '2019/07/08',
|
||||
workout_date: '2019/08/07',
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
},
|
||||
@@ -190,6 +198,7 @@ describe('formatRecord after conversion', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
id: 11,
|
||||
@@ -212,12 +221,13 @@ describe('formatRecord after conversion', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
id: 12,
|
||||
record_type: 'MS',
|
||||
value: '11.18 mi/h',
|
||||
workout_date: '2019/07/08',
|
||||
workout_date: '2019/08/07',
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
},
|
||||
@@ -234,6 +244,7 @@ describe('formatRecord after conversion', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
timezone: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
id: 13,
|
||||
@@ -250,7 +261,8 @@ describe('formatRecord after conversion', () => {
|
||||
formatRecord(
|
||||
testParams.inputParams.record,
|
||||
testParams.inputParams.timezone,
|
||||
true
|
||||
true,
|
||||
testParams.inputParams.date_format
|
||||
),
|
||||
testParams.expected
|
||||
)
|
||||
@@ -272,7 +284,8 @@ describe('formatRecord (invalid record type)', () => {
|
||||
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
|
||||
},
|
||||
'Europe/Paris',
|
||||
false
|
||||
false,
|
||||
'yyyy/dd/MM'
|
||||
)
|
||||
).to.throw(
|
||||
'Invalid record type, expected: "AS", "FD", "HA", "LD", "MD", got: "M"'
|
||||
@@ -287,6 +300,7 @@ describe('getRecordsBySports', () => {
|
||||
input: {
|
||||
records: [],
|
||||
tz: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {},
|
||||
},
|
||||
@@ -305,6 +319,7 @@ describe('getRecordsBySports', () => {
|
||||
},
|
||||
],
|
||||
tz: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
'Cycling (Sport)': {
|
||||
@@ -355,6 +370,7 @@ describe('getRecordsBySports', () => {
|
||||
},
|
||||
],
|
||||
tz: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
'Cycling (Sport)': {
|
||||
@@ -385,7 +401,7 @@ describe('getRecordsBySports', () => {
|
||||
id: 10,
|
||||
record_type: 'FD',
|
||||
value: '18 km',
|
||||
workout_date: '2019/07/08',
|
||||
workout_date: '2019/08/07',
|
||||
workout_id: 'n6JcLPQt3QtZWFfiSnYm4C',
|
||||
},
|
||||
],
|
||||
@@ -401,7 +417,8 @@ describe('getRecordsBySports', () => {
|
||||
translatedSports,
|
||||
testParams.input.tz,
|
||||
false,
|
||||
true
|
||||
true,
|
||||
testParams.input.date_format
|
||||
),
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
@@ -418,6 +435,7 @@ describe('getRecordsBySports after conversion', () => {
|
||||
input: {
|
||||
records: [],
|
||||
tz: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {},
|
||||
},
|
||||
@@ -436,6 +454,7 @@ describe('getRecordsBySports after conversion', () => {
|
||||
},
|
||||
],
|
||||
tz: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
'Cycling (Sport)': {
|
||||
@@ -486,6 +505,7 @@ describe('getRecordsBySports after conversion', () => {
|
||||
},
|
||||
],
|
||||
tz: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
'Cycling (Sport)': {
|
||||
@@ -516,7 +536,7 @@ describe('getRecordsBySports after conversion', () => {
|
||||
id: 10,
|
||||
record_type: 'FD',
|
||||
value: '11.185 mi',
|
||||
workout_date: '2019/07/08',
|
||||
workout_date: '2019/08/07',
|
||||
workout_id: 'n6JcLPQt3QtZWFfiSnYm4C',
|
||||
},
|
||||
],
|
||||
@@ -532,7 +552,8 @@ describe('getRecordsBySports after conversion', () => {
|
||||
translatedSports,
|
||||
testParams.input.tz,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
testParams.input.date_format
|
||||
),
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
@@ -549,6 +570,7 @@ describe('getRecordsBySports with HA record', () => {
|
||||
input: {
|
||||
records: [],
|
||||
tz: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {},
|
||||
},
|
||||
@@ -576,6 +598,7 @@ describe('getRecordsBySports with HA record', () => {
|
||||
},
|
||||
],
|
||||
tz: 'Europe/Paris',
|
||||
date_format: 'yyyy/dd/MM'
|
||||
},
|
||||
expected: {
|
||||
'Cycling (Sport)': {
|
||||
@@ -602,7 +625,8 @@ describe('getRecordsBySports with HA record', () => {
|
||||
translatedSports,
|
||||
testParams.input.tz,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
testParams.input.date_format
|
||||
),
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
|
||||
Reference in New Issue
Block a user