Merge pull request #167 from Fmstrat/ascent-record

Added ascent record to Dashboard
This commit is contained in:
Sam 2022-07-23 17:54:43 +02:00 committed by GitHub
commit ab432d6895
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 532 additions and 102 deletions

View File

@ -45,6 +45,7 @@ Workouts
- User records by sports:
- average speed
- farthest distance
- highest ascent (**new in 0.6.11**, can be hidden, see user preferences)
- longest duration
- maximum speed
@ -71,6 +72,7 @@ Account & preferences
- A user can reset his password (*new in 0.3.0*)
- A user can change his email address (*new in 0.6.0*)
- A user can choose between metric system and imperial system for distance, elevation and speed display (*new in 0.5.0*)
- A user can choose to display or hide ascent records (*new in 0.6.11*)
- A user can set sport preferences (*new in 0.5.0*):
- change sport color (used for sport image and charts)
- can override stopped speed threshold (for next uploaded gpx files)

View File

@ -347,6 +347,7 @@ character “_” allowed</p></li>
<span class="w"> </span><span class="nt">&quot;bio&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;birth_date&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;created_at&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sun, 14 Jul 2019 14:09:58 GMT&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;display_ascent&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;email&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;sam@example.com&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;first_name&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;imperial_units&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
@ -377,6 +378,15 @@ character “_” allowed</p></li>
<span class="w"> </span><span class="nt">&quot;workout_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;hvYBqYBRa7wwXpaStWR4V2&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">13</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;record_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;HA&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;sport_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;user&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sam&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;value&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">43.97</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;workout_date&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sun, 07 Jul 2019 08:00:00 GMT&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;workout_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;hvYBqYBRa7wwXpaStWR4V2&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">11</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;record_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;LD&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;sport_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
@ -449,6 +459,7 @@ character “_” allowed</p></li>
<span class="w"> </span><span class="nt">&quot;bio&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;birth_date&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;created_at&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sun, 14 Jul 2019 14:09:58 GMT&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;display_ascent&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;email&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;sam@example.com&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;first_name&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;imperial_units&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
@ -479,6 +490,15 @@ character “_” allowed</p></li>
<span class="w"> </span><span class="nt">&quot;workout_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;hvYBqYBRa7wwXpaStWR4V2&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">13</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;record_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;HA&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;sport_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;user&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sam&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;value&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">43.97</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;workout_date&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sun, 07 Jul 2019 08:00:00 GMT&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;workout_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;hvYBqYBRa7wwXpaStWR4V2&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">11</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;record_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;LD&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;sport_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
@ -566,6 +586,7 @@ character “_” allowed</p></li>
<span class="w"> </span><span class="nt">&quot;bio&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;birth_date&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;created_at&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sun, 14 Jul 2019 14:09:58 GMT&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;display_ascent&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;email&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;sam@example.com&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;first_name&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;imperial_units&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
@ -596,6 +617,15 @@ character “_” allowed</p></li>
<span class="w"> </span><span class="nt">&quot;workout_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;hvYBqYBRa7wwXpaStWR4V2&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">13</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;record_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;HA&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;sport_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;user&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sam&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;value&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">43.97</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;workout_date&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sun, 07 Jul 2019 08:00:00 GMT&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;workout_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;hvYBqYBRa7wwXpaStWR4V2&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">11</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;record_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;LD&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;sport_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
@ -633,10 +663,11 @@ character “_” allowed</p></li>
<dl class="field-list simple">
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>display_ascent</strong> (<em>boolean</em>) display highest ascent records</p></li>
<li><p><strong>imperial_units</strong> (<em>boolean</em>) display distance in imperial units</p></li>
<li><p><strong>language</strong> (<em>string</em>) language preferences</p></li>
<li><p><strong>timezone</strong> (<em>string</em>) user time zone</p></li>
<li><p><strong>weekm</strong> (<em>boolean</em>) does week start on Monday?</p></li>
<li><p><strong>language</strong> (<em>string</em>) language preferences</p></li>
<li><p><strong>imperial_units</strong> (<em>boolean</em>) display distance in imperial units</p></li>
</ul>
</dd>
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
@ -924,6 +955,7 @@ character “_” allowed</p></li>
<span class="w"> </span><span class="nt">&quot;bio&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;birth_date&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;created_at&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sun, 14 Jul 2019 14:09:58 GMT&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;display_ascent&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;email&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;sam@example.com&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;first_name&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;imperial_units&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
@ -954,6 +986,15 @@ character “_” allowed</p></li>
<span class="w"> </span><span class="nt">&quot;workout_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;hvYBqYBRa7wwXpaStWR4V2&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">13</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;record_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;HA&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;sport_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;user&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sam&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;value&quot;</span><span class="p">:</span><span class="w"> </span><span class="mf">43.97</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;workout_date&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Sun, 07 Jul 2019 08:00:00 GMT&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;workout_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;hvYBqYBRa7wwXpaStWR4V2&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">11</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;record_type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;LD&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;sport_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>

View File

@ -212,6 +212,7 @@
<dt>User records by sports:</dt><dd><ul class="simple">
<li><p>average speed</p></li>
<li><p>farthest distance</p></li>
<li><p>highest ascent (<strong>new in 0.6.11</strong>, can be hidden, see user preferences)</p></li>
<li><p>longest duration</p></li>
<li><p>maximum speed</p></li>
</ul>
@ -248,6 +249,7 @@ A user with an inactive account cannot log in. (<em>new in 0.6.0</em>)</p></li>
<li><p>A user can reset his password (<em>new in 0.3.0</em>)</p></li>
<li><p>A user can change his email address (<em>new in 0.6.0</em>)</p></li>
<li><p>A user can choose between metric system and imperial system for distance, elevation and speed display (<em>new in 0.5.0</em>)</p></li>
<li><p>A user can choose to display or hide ascent records (<em>new in 0.6.11</em>)</p></li>
<li><dl class="simple">
<dt>A user can set sport preferences (<em>new in 0.5.0</em>):</dt><dd><ul>
<li><p>change sport color (used for sport image and charts)</p></li>

File diff suppressed because one or more lines are too long

View File

@ -45,6 +45,7 @@ Workouts
- User records by sports:
- average speed
- farthest distance
- highest ascent (**new in 0.6.11**, can be hidden, see user preferences)
- longest duration
- maximum speed
@ -71,6 +72,7 @@ Account & preferences
- A user can reset his password (*new in 0.3.0*)
- A user can change his email address (*new in 0.6.0*)
- A user can choose between metric system and imperial system for distance, elevation and speed display (*new in 0.5.0*)
- A user can choose to display or hide ascent records (*new in 0.6.11*)
- A user can set sport preferences (*new in 0.5.0*):
- change sport color (used for sport image and charts)
- can override stopped speed threshold (for next uploaded gpx files)

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><script defer="defer" src="/static/js/chunk-vendors.7132edc6.js"></script><script defer="defer" src="/static/js/app.bf1d4e1c.js"></script><link href="/static/css/app.32d0ced1.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.7132edc6.js"></script><script defer="defer" src="/static/js/app.ac1e5052.js"></script><link href="/static/css/app.f768a44b.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>

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 +1,2 @@
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[193],{9161:function(e,s,t){t.r(s),t.d(s,{default:function(){return A}});t(6699);var a=t(6252),r=t(2262),l=t(3577),o=t(3324),n=t(9996);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-d693c7da"]]);var Z=F,x=t(5630),D=t(8602),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.1ad194e3.js.map
//# sourceMappingURL=statistics.440cd8b2.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,45 @@
"""add ascent record
Revision ID: cd0e6cf83207
Revises: 5e3a3a31c432
Create Date: 2022-03-22 20:21:13.661883
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'cd0e6cf83207'
down_revision = '5e3a3a31c432'
branch_labels = None
depends_on = None
def upgrade():
op.execute(
"""
ALTER TYPE record_types ADD VALUE 'HA';
"""
)
op.add_column(
'users', sa.Column('display_ascent', sa.Boolean(), nullable=True)
)
op.execute("UPDATE users SET display_ascent = true")
op.alter_column('users', 'display_ascent', nullable=False)
def downgrade():
op.drop_column('users', 'display_ascent')
op.execute("DELETE FROM records WHERE record_type = 'HA';")
op.execute("ALTER TYPE record_types RENAME TO record_types_old")
op.execute("CREATE TYPE record_types AS ENUM('AS', 'FD', 'LD', 'MS')")
op.execute(
"""
ALTER TABLE records ALTER COLUMN record_type TYPE record_types
USING record_type::text::record_types
"""
)
op.execute("DROP TYPE record_types_old")

View File

@ -1272,6 +1272,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
weekm=True,
language=input_language,
imperial_units=True,
display_ascent=False,
)
),
headers=dict(Authorization=f'Bearer {auth_token}'),
@ -1281,8 +1282,11 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
data = json.loads(response.data.decode())
assert data['status'] == 'success'
assert data['message'] == 'user preferences updated'
assert data['data']['display_ascent'] is False
assert data['data']['imperial_units'] is True
assert data['data']['language'] == expected_language
assert data['data'] == jsonify_dict(user_1.serialize(user_1))
assert data['data']['timezone'] == 'America/New_York'
assert data['data']['weekm'] is True
class TestUserSportPreferencesUpdate(ApiTestCaseMixin):

View File

@ -66,6 +66,7 @@ class TestUserSerializeAsAuthUser(UserModelAssertMixin):
assert serialized_user['language'] == user_1.language
assert serialized_user['timezone'] == user_1.timezone
assert serialized_user['weekm'] == user_1.weekm
assert serialized_user['display_ascent'] == user_1.display_ascent
def test_it_returns_workouts_infos(self, app: Flask, user_1: User) -> None:
serialized_user = user_1.serialize(user_1)

View File

@ -56,7 +56,7 @@ def assert_workout_data_with_gpx(data: Dict) -> None:
assert segment['pauses'] is None
records = data['data']['workouts'][0]['records']
assert len(records) == 4
assert len(records) == 5
assert records[0]['sport_id'] == 1
assert records[0]['workout_id'] == data['data']['workouts'][0]['id']
assert records[0]['record_type'] == 'MS'
@ -69,14 +69,19 @@ def assert_workout_data_with_gpx(data: Dict) -> None:
assert records[1]['value'] == '0:04:10'
assert records[2]['sport_id'] == 1
assert records[2]['workout_id'] == data['data']['workouts'][0]['id']
assert records[2]['record_type'] == 'FD'
assert records[2]['record_type'] == 'HA'
assert records[2]['value'] == 0.4
assert records[2]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[2]['value'] == 0.32
assert records[3]['sport_id'] == 1
assert records[3]['workout_id'] == data['data']['workouts'][0]['id']
assert records[3]['record_type'] == 'AS'
assert records[3]['record_type'] == 'FD'
assert records[3]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[3]['value'] == 4.61
assert records[3]['value'] == 0.32
assert records[4]['sport_id'] == 1
assert records[4]['workout_id'] == data['data']['workouts'][0]['id']
assert records[4]['record_type'] == 'AS'
assert records[4]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[4]['value'] == 4.61
def assert_workout_data_with_gpx_segments(data: Dict) -> None:
@ -133,7 +138,7 @@ def assert_workout_data_with_gpx_segments(data: Dict) -> None:
assert segment['pauses'] is None
records = data['data']['workouts'][0]['records']
assert len(records) == 4
assert len(records) == 5
assert records[0]['sport_id'] == 1
assert records[0]['workout_id'] == data['data']['workouts'][0]['id']
assert records[0]['record_type'] == 'MS'
@ -146,14 +151,18 @@ def assert_workout_data_with_gpx_segments(data: Dict) -> None:
assert records[1]['value'] == '0:03:55'
assert records[2]['sport_id'] == 1
assert records[2]['workout_id'] == data['data']['workouts'][0]['id']
assert records[2]['record_type'] == 'FD'
assert records[2]['record_type'] == 'HA'
assert records[2]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[2]['value'] == 0.3
assert records[3]['sport_id'] == 1
assert records[3]['workout_id'] == data['data']['workouts'][0]['id']
assert records[3]['record_type'] == 'AS'
assert records[3]['record_type'] == 'FD'
assert records[3]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[3]['value'] == 4.59
assert records[3]['value'] == 0.3
assert records[4]['sport_id'] == 1
assert records[4]['workout_id'] == data['data']['workouts'][0]['id']
assert records[4]['record_type'] == 'AS'
assert records[4]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[4]['value'] == 4.59
def assert_workout_data_wo_gpx(data: Dict) -> None:
@ -252,6 +261,39 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin):
assert 'just a workout' == data['data']['workouts'][0]['title']
assert_workout_data_with_gpx(data)
def test_it_returns_ha_record_when_a_workout_without_gpx_exists(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
gpx_file: str,
workout_cycling_user_1: Workout,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.post(
'/api/workouts',
data=dict(
file=(BytesIO(str.encode(gpx_file)), 'example.gpx'),
data='{"sport_id": 1}',
),
headers=dict(
content_type='multipart/form-data',
Authorization=f'Bearer {auth_token}',
),
)
data = json.loads(response.data.decode())
records = data['data']['workouts'][0]['records']
assert len(records) == 1
assert records[0]['sport_id'] == 1
assert records[0]['workout_id'] == data['data']['workouts'][0]['id']
assert records[0]['record_type'] == 'HA'
assert records[0]['value'] == 0.4
assert records[0]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
def test_it_creates_workout_with_expecting_gpx_path(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None:

View File

@ -32,7 +32,7 @@ def assert_workout_data_with_gpx(data: Dict, sport_id: int) -> None:
assert data['data']['workouts'][0]['with_gpx'] is True
records = data['data']['workouts'][0]['records']
assert len(records) == 4
assert len(records) == 5
assert records[0]['sport_id'] == sport_id
assert records[0]['workout_id'] == data['data']['workouts'][0]['id']
assert records[0]['record_type'] == 'MS'
@ -45,14 +45,18 @@ def assert_workout_data_with_gpx(data: Dict, sport_id: int) -> None:
assert records[1]['value'] == '0:04:10'
assert records[2]['sport_id'] == sport_id
assert records[2]['workout_id'] == data['data']['workouts'][0]['id']
assert records[2]['record_type'] == 'FD'
assert records[2]['record_type'] == 'HA'
assert records[2]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[2]['value'] == 0.32
assert records[3]['sport_id'] == sport_id
assert records[3]['workout_id'] == data['data']['workouts'][0]['id']
assert records[3]['record_type'] == 'AS'
assert records[3]['record_type'] == 'FD'
assert records[3]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[3]['value'] == 4.61
assert records[3]['value'] == 0.32
assert records[4]['sport_id'] == sport_id
assert records[4]['workout_id'] == data['data']['workouts'][0]['id']
assert records[4]['record_type'] == 'AS'
assert records[4]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[4]['value'] == 4.61
class TestEditWorkoutWithGpx(ApiTestCaseMixin):

View File

@ -290,6 +290,7 @@ def get_authenticated_user_profile(
"bio": null,
"birth_date": null,
"created_at": "Sun, 14 Jul 2019 14:09:58 GMT",
"display_ascent": true,
"email": "sam@example.com",
"first_name": null,
"imperial_units": false,
@ -319,6 +320,15 @@ def get_authenticated_user_profile(
"workout_date": "Sun, 07 Jul 2019 08:00:00 GMT",
"workout_id": "hvYBqYBRa7wwXpaStWR4V2"
},
{
"id": 13,
"record_type": "HA",
"sport_id": 1,
"user": "Sam",
"value": 43.97,
"workout_date": "Sun, 07 Jul 2019 08:00:00 GMT",
"workout_id": "hvYBqYBRa7wwXpaStWR4V2"
},
{
"id": 11,
"record_type": "LD",
@ -390,6 +400,7 @@ def edit_user(auth_user: User) -> Union[Dict, HttpResponse]:
"bio": null,
"birth_date": null,
"created_at": "Sun, 14 Jul 2019 14:09:58 GMT",
"display_ascent": true,
"email": "sam@example.com",
"first_name": null,
"imperial_units": false,
@ -419,6 +430,15 @@ def edit_user(auth_user: User) -> Union[Dict, HttpResponse]:
"workout_date": "Sun, 07 Jul 2019 08:00:00 GMT",
"workout_id": "hvYBqYBRa7wwXpaStWR4V2"
},
{
"id": 13,
"record_type": "HA",
"sport_id": 1,
"user": "Sam",
"value": 43.97,
"workout_date": "Sun, 07 Jul 2019 08:00:00 GMT",
"workout_id": "hvYBqYBRa7wwXpaStWR4V2"
},
{
"id": 11,
"record_type": "LD",
@ -546,6 +566,7 @@ def update_user_account(auth_user: User) -> Union[Dict, HttpResponse]:
"bio": null,
"birth_date": null,
"created_at": "Sun, 14 Jul 2019 14:09:58 GMT",
"display_ascent": true,
"email": "sam@example.com",
"first_name": null,
"imperial_units": false,
@ -575,6 +596,15 @@ def update_user_account(auth_user: User) -> Union[Dict, HttpResponse]:
"workout_date": "Sun, 07 Jul 2019 08:00:00 GMT",
"workout_id": "hvYBqYBRa7wwXpaStWR4V2"
},
{
"id": 13,
"record_type": "HA",
"sport_id": 1,
"user": "Sam",
"value": 43.97,
"workout_date": "Sun, 07 Jul 2019 08:00:00 GMT",
"workout_id": "hvYBqYBRa7wwXpaStWR4V2"
},
{
"id": 11,
"record_type": "LD",
@ -746,6 +776,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",
"display_ascent": true,
"email": "sam@example.com",
"first_name": null,
"imperial_units": false,
@ -775,6 +806,15 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
"workout_date": "Sun, 07 Jul 2019 08:00:00 GMT",
"workout_id": "hvYBqYBRa7wwXpaStWR4V2"
},
{
"id": 13,
"record_type": "HA",
"sport_id": 1,
"user": "Sam",
"value": 43.97,
"workout_date": "Sun, 07 Jul 2019 08:00:00 GMT",
"workout_id": "hvYBqYBRa7wwXpaStWR4V2"
},
{
"id": 11,
"record_type": "LD",
@ -809,10 +849,11 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
"status": "success"
}
:<json boolean display_ascent: display highest ascent records
:<json boolean imperial_units: display distance in imperial units
:<json string language: language preferences
:<json string timezone: user time zone
:<json boolean weekm: does week start on Monday?
:<json string language: language preferences
:<json boolean imperial_units: display distance in imperial units
:reqheader Authorization: OAuth 2.0 Bearer Token
@ -830,6 +871,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
# get post data
post_data = request.get_json()
user_mandatory_data = {
'display_ascent',
'imperial_units',
'language',
'timezone',
@ -838,12 +880,14 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
if not post_data or not post_data.keys() >= user_mandatory_data:
return InvalidPayloadErrorResponse()
display_ascent = post_data.get('display_ascent')
imperial_units = post_data.get('imperial_units')
language = get_language(post_data.get('language'))
timezone = post_data.get('timezone')
weekm = post_data.get('weekm')
try:
auth_user.display_ascent = display_ascent
auth_user.imperial_units = imperial_units
auth_user.language = language
auth_user.timezone = timezone

View File

@ -50,6 +50,7 @@ class User(BaseModel):
is_active = db.Column(db.Boolean, default=False, nullable=False)
email_to_confirm = db.Column(db.String(255), nullable=True)
confirmation_token = db.Column(db.String(255), nullable=True)
display_ascent = db.Column(db.Boolean, default=True, nullable=False)
def __repr__(self) -> str:
return f'<User {self.username!r}>'
@ -170,6 +171,7 @@ class User(BaseModel):
serialized_user = {
**serialized_user,
**{
'display_ascent': self.display_ascent,
'imperial_units': self.imperial_units,
'language': self.language,
'timezone': self.timezone,

View File

@ -22,6 +22,7 @@ BaseModel: DeclarativeMeta = db.Model
record_types = [
'AS', # 'Best Average Speed'
'FD', # 'Farthest Distance'
'HA', # 'Highest Ascent'
'LD', # 'Longest Duration'
'MS', # 'Max speed'
]
@ -319,9 +320,14 @@ class Workout(BaseModel):
def get_user_workout_records(
cls, user_id: int, sport_id: int, as_integer: Optional[bool] = False
) -> Dict:
"""
Note:
Values for ascent are null for workouts without gpx
"""
record_types_columns = {
'AS': 'ave_speed', # 'Average speed'
'FD': 'distance', # 'Farthest Distance'
'HA': 'ascent', # 'Highest Ascent'
'LD': 'moving', # 'Longest Duration'
'MS': 'max_speed', # 'Max speed'
}
@ -329,7 +335,11 @@ class Workout(BaseModel):
for record_type, column in record_types_columns.items():
column_sorted = getattr(getattr(Workout, column), 'desc')()
record_workout = (
Workout.query.filter_by(user_id=user_id, sport_id=sport_id)
Workout.query.filter(
Workout.user_id == user_id,
Workout.sport_id == sport_id,
getattr(Workout, column) != None, # noqa
)
.order_by(column_sorted, Workout.workout_date)
.first()
)
@ -481,7 +491,7 @@ class Record(BaseModel):
return datetime.timedelta(seconds=self._value)
elif self.record_type in ['AS', 'MS']:
return float(self._value / 100)
else: # 'FD'
else: # 'FD' or 'HA'
return float(self._value / 1000)
@value.setter # type: ignore
@ -491,7 +501,7 @@ class Record(BaseModel):
def serialize(self) -> Dict:
if self.value is None:
value = None
elif self.record_type in ['AS', 'FD', 'MS']:
elif self.record_type in ['AS', 'FD', 'HA', 'MS']:
value = float(self.value) # type: ignore
else: # 'LD'
value = str(self.value) # type: ignore

View File

@ -42,7 +42,8 @@
props.user.records,
translateSports(props.sports, t),
props.user.timezone,
props.user.imperial_units
props.user.imperial_units,
props.user.display_ascent
)
)
</script>

View File

@ -15,6 +15,8 @@
)
}}
</dd>
<dt>{{ $t('user.PROFILE.ASCENT_DATA') }}:</dt>
<dd>{{ $t(`common.${display_ascent}`) }}</dd>
</dl>
<div class="profile-buttons">
<button @click="$router.push('/profile/edit/preferences')">
@ -28,11 +30,11 @@
<script setup lang="ts">
import { computed } from 'vue'
import { IUserProfile } from '@/types/user'
import { IAuthUserProfile } from '@/types/user'
import { languageLabels } from '@/utils/locales'
interface Props {
user: IUserProfile
user: IAuthUserProfile
}
const props = defineProps<Props>()
@ -45,4 +47,7 @@
const timezone = computed(() =>
props.user.timezone ? props.user.timezone : 'Europe/Paris'
)
const display_ascent = computed(() =>
props.user.display_ascent ? 'DISPLAYED' : 'HIDDEN'
)
</script>

View File

@ -23,34 +23,66 @@
@updateTimezone="updateTZ"
/>
</label>
<label class="form-items">
{{ $t('user.PROFILE.FIRST_DAY_OF_WEEK') }}
<select id="weekm" v-model="userForm.weekm" :disabled="loading">
<option
v-for="start in weekStart"
:value="start.value"
:key="start.value"
>
{{ $t(`user.PROFILE.${start.label}`) }}
</option>
</select>
</label>
<label class="form-items">
{{ $t('user.PROFILE.UNITS.LABEL') }}
<select
id="imperial_units"
v-model="userForm.imperial_units"
:disabled="loading"
>
<option
v-for="unit in imperialUnits"
:value="unit.value"
:key="unit.value"
>
{{ $t(`user.PROFILE.UNITS.${unit.label}`) }}
</option>
</select>
</label>
<div class="form-items form-checkboxes">
<span class="checkboxes-label">
{{ $t('user.PROFILE.FIRST_DAY_OF_WEEK') }}
</span>
<div class="checkboxes">
<label v-for="start in weekStart" :key="start.label">
<input
type="radio"
:id="start.label"
:name="start.label"
:checked="start.value === userForm.weekm"
:disabled="loading"
@input="updateWeekM(start.value)"
/>
<span class="checkbox-label">
{{ $t(`user.PROFILE.${start.label}`) }}
</span>
</label>
</div>
</div>
<div class="form-items form-checkboxes">
<span class="checkboxes-label">
{{ $t('user.PROFILE.UNITS.LABEL') }}
</span>
<div class="checkboxes">
<label v-for="unit in imperialUnits" :key="unit.label">
<input
type="radio"
:id="unit.label"
:name="unit.label"
:checked="unit.value === userForm.imperial_units"
:disabled="loading"
@input="updateImperialUnit(unit.value)"
/>
<span class="checkbox-label">
{{ $t(`user.PROFILE.UNITS.${unit.label}`) }}
</span>
</label>
</div>
</div>
<div class="form-items form-checkboxes">
<span class="checkboxes-label">
{{ $t('user.PROFILE.ASCENT_DATA') }}
</span>
<div class="checkboxes">
<label v-for="status in ascentData" :key="status.label">
<input
type="radio"
:id="status.label"
:name="status.label"
:checked="status.value === userForm.display_ascent"
:disabled="loading"
@input="updateAscentDisplay(status.value)"
/>
<span class="checkbox-label">
{{ $t(`common.${status.label}`) }}
</span>
</label>
</div>
</div>
<div class="form-buttons">
<button class="confirm" type="submit">
{{ $t('buttons.SUBMIT') }}
@ -72,40 +104,51 @@
import TimezoneDropdown from '@/components/User/ProfileEdition/TimezoneDropdown.vue'
import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'
import { IUserProfile, IUserPreferencesPayload } from '@/types/user'
import { IUserPreferencesPayload, IAuthUserProfile } from '@/types/user'
import { useStore } from '@/use/useStore'
import { availableLanguages } from '@/utils/locales'
interface Props {
user: IUserProfile
user: IAuthUserProfile
}
const props = defineProps<Props>()
const store = useStore()
const userForm: IUserPreferencesPayload = reactive({
display_ascent: true,
imperial_units: false,
language: '',
timezone: 'Europe/Paris',
weekm: false,
})
const weekStart = [
{
label: 'MONDAY',
value: true,
},
{
label: 'SUNDAY',
value: false,
},
{
label: 'MONDAY',
value: true,
},
]
const imperialUnits = [
{
label: 'METRIC',
value: false,
},
{
label: 'IMPERIAL',
value: true,
},
]
const ascentData = [
{
label: 'METRIC',
label: 'DISPLAYED',
value: true,
},
{
label: 'HIDDEN',
value: false,
},
]
@ -122,7 +165,8 @@
}
})
function updateUserForm(user: IUserProfile) {
function updateUserForm(user: IAuthUserProfile) {
userForm.display_ascent = user.display_ascent
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'
@ -134,8 +178,43 @@
function updateTZ(value: string) {
userForm.timezone = value
}
function updateAscentDisplay(value: boolean) {
userForm.display_ascent = value
}
function updateImperialUnit(value: boolean) {
userForm.imperial_units = value
}
function updateWeekM(value: boolean) {
userForm.weekm = value
}
onUnmounted(() => {
store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
})
</script>
<style lang="scss" scoped>
@import '~@/scss/vars.scss';
#user-preferences-edition {
.form-items {
padding-top: $default-padding * 0.5;
}
.form-checkboxes {
.checkboxes-label {
font-weight: bold;
}
.checkboxes {
display: flex;
gap: $default-padding;
flex-wrap: wrap;
.checkbox-label {
padding-left: $default-padding * 0.5;
}
label {
font-weight: normal;
}
}
}
}
</style>

View File

@ -3,9 +3,11 @@
"CONFIRMATION": "Confirmation",
"CONTACT": "contact",
"DAY": "day | days",
"DISPLAYED": "Displayed",
"DOCUMENTATION": "documentation",
"HOME": "Home",
"HERE": "here",
"HIDDEN": "Hidden",
"SELECTS": {
"ORDER_BY": {
"LABEL": "order by"

View File

@ -48,6 +48,7 @@
"PASSWORD_UPDATED": "Your password have been updated. Click {0} to log in.",
"PROFILE": {
"ACCOUNT_EDITION": "Account edition",
"ASCENT_DATA": "Ascent-related data (records, total)",
"BACK_TO_PROFILE": "Back to profile",
"BIO": "Bio",
"BIRTH_DATE": "Birth date",

View File

@ -43,6 +43,7 @@
"RECORD": "record | records",
"RECORD_AS": "Ave. speed",
"RECORD_FD": "Farthest distance",
"RECORD_HA": "Highest ascent",
"RECORD_LD": "Longest duration",
"RECORD_MS": "Max. speed",
"REMAINING_CHARS": "remaining characters",

View File

@ -3,9 +3,11 @@
"CONFIRMATION": "Confirmation",
"CONTACT": "contact",
"DAY": "jour | jours",
"DISPLAYED": "Affiché",
"DOCUMENTATION": "documentation (en)",
"HOME": "Accueil",
"HERE": "ici",
"HIDDEN": "Masqué",
"SELECTS": {
"ORDER_BY": {
"LABEL": "trier par "

View File

@ -48,6 +48,7 @@
"PASSWORD_UPDATED": "Votre mot de passe a été mis à jour. Cliquez {0} pour vous connecter.",
"PROFILE": {
"ACCOUNT_EDITION": "Mise à jour du compte",
"ASCENT_DATA": "Données relatives au dénivelé positif (records, total)",
"BACK_TO_PROFILE": "Revenir au profil",
"BIO": "Bio",
"BIRTH_DATE": "Date de naissance",

View File

@ -43,6 +43,7 @@
"RECORD": "record | records",
"RECORD_AS": "Vitesse moy.",
"RECORD_FD": "Distance la + longue",
"RECORD_HA": "Dénivelé positif le + élevé",
"RECORD_LD": "Durée la + longue",
"RECORD_MS": "Vitesse max.",
"REMAINING_CHARS": "nombre de caractères restants ",

View File

@ -24,6 +24,7 @@ export interface IUserProfile {
}
export interface IAuthUserProfile extends IUserProfile {
display_ascent: boolean
imperial_units: boolean
language: string | null
timezone: string
@ -58,6 +59,7 @@ export interface IAdminUserPayload {
}
export interface IUserPreferencesPayload {
display_ascent: boolean
imperial_units: boolean
language: string
timezone: string

View File

@ -9,30 +9,47 @@ export const formatRecord = (
tz: string,
useImperialUnits: boolean
): Record<string, string | number> => {
const unitFrom: TUnit = 'km'
const unitTo: TUnit = useImperialUnits
? units[unitFrom].defaultTarget
: unitFrom
const distanceUnitFrom: TUnit = 'km'
const distanceUnitTo: TUnit = useImperialUnits
? units[distanceUnitFrom].defaultTarget
: distanceUnitFrom
const ascentUnitFrom: TUnit = 'm'
const ascentUnitTo: TUnit = useImperialUnits
? units[ascentUnitFrom].defaultTarget
: ascentUnitFrom
let value
switch (record.record_type) {
case 'AS':
case 'MS':
value = `${convertDistance(
+record.value,
unitFrom,
unitTo,
distanceUnitFrom,
distanceUnitTo,
2
)} ${unitTo}/h`
)} ${distanceUnitTo}/h`
break
case 'FD':
value = `${convertDistance(+record.value, unitFrom, unitTo, 3)} ${unitTo}`
value = `${convertDistance(
+record.value,
distanceUnitFrom,
distanceUnitTo,
3
)} ${distanceUnitTo}`
break
case 'HA':
value = `${convertDistance(
+record.value,
ascentUnitFrom,
ascentUnitTo,
2
)} ${ascentUnitTo}`
break
case 'LD':
value = record.value
break
default:
throw new Error(
`Invalid record type, expected: "AS", "FD", "LD", "MD", got: "${record.record_type}"`
`Invalid record type, expected: "AS", "FD", "HA", "LD", "MD", got: "${record.record_type}"`
)
}
return {
@ -55,21 +72,24 @@ export const getRecordsBySports = (
records: IRecord[],
translatedSports: ITranslatedSport[],
tz: string,
useImperialUnits: boolean
useImperialUnits: boolean,
display_ascent: boolean
): IRecordsBySports =>
records.reduce((sportList: IRecordsBySports, record) => {
const sport = translatedSports.find((s) => s.id === record.sport_id)
if (sport && sport.label) {
if (sportList[sport.translatedLabel] === void 0) {
sportList[sport.translatedLabel] = {
label: sport.label,
color: sport.color,
records: [],
records
.filter((r) => (display_ascent ? true : r.record_type !== 'HA'))
.reduce((sportList: IRecordsBySports, record) => {
const sport = translatedSports.find((s) => s.id === record.sport_id)
if (sport && sport.label) {
if (sportList[sport.translatedLabel] === void 0) {
sportList[sport.translatedLabel] = {
label: sport.label,
color: sport.color,
records: [],
}
}
sportList[sport.translatedLabel].records.push(
formatRecord(record, tz, useImperialUnits)
)
}
sportList[sport.translatedLabel].records.push(
formatRecord(record, tz, useImperialUnits)
)
}
return sportList
}, {})
return sportList
}, {})

View File

@ -94,6 +94,28 @@ describe('formatRecord', () => {
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
},
{
description: "return formatted record for 'Highest ascent'",
inputParams: {
record: {
id: 13,
record_type: 'HA',
sport_id: 1,
user: 'admin',
value: 100,
workout_date: 'Sun, 07 Jul 2019 08:00:00 GMT',
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
timezone: 'Europe/Paris',
},
expected: {
id: 13,
record_type: 'HA',
value: '100 m',
workout_date: '2019/07/07',
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
},
]
testsParams.map((testParams) => {
it(testParams.description, () => {
@ -199,6 +221,28 @@ describe('formatRecord after conversion', () => {
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
},
{
description: "return formatted record for 'Highest ascent'",
inputParams: {
record: {
id: 13,
record_type: 'HA',
sport_id: 1,
user: 'admin',
value: 100,
workout_date: 'Sun, 07 Jul 2019 08:00:00 GMT',
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
timezone: 'Europe/Paris',
},
expected: {
id: 13,
record_type: 'HA',
value: '328.08 ft',
workout_date: '2019/07/07',
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
},
]
testsParams.map((testParams) => {
it(testParams.description, () => {
@ -231,7 +275,7 @@ describe('formatRecord (invalid record type)', () => {
false
)
).to.throw(
'Invalid record type, expected: "AS", "FD", "LD", "MD", got: "M"'
'Invalid record type, expected: "AS", "FD", "HA", "LD", "MD", got: "M"'
)
})
})
@ -356,7 +400,8 @@ describe('getRecordsBySports', () => {
testParams.input.records,
translatedSports,
testParams.input.tz,
false
false,
true
),
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@ -486,6 +531,7 @@ describe('getRecordsBySports after conversion', () => {
testParams.input.records,
translatedSports,
testParams.input.tz,
true,
true
),
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -495,3 +541,73 @@ describe('getRecordsBySports after conversion', () => {
})
)
})
describe('getRecordsBySports with HA record', () => {
const testsParams = [
{
description: 'returns empty object if no records',
input: {
records: [],
tz: 'Europe/Paris',
},
expected: {},
},
{
description: 'returns records except HA record',
input: {
records: [
{
id: 9,
record_type: 'AS',
sport_id: 1,
user: 'admin',
value: 18,
workout_date: 'Sun, 07 Jul 2019 08:00:00 GMT',
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
{
id: 9,
record_type: 'HA',
sport_id: 1,
user: 'admin',
value: 235,
workout_date: 'Sun, 07 Jul 2019 08:00:00 GMT',
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
],
tz: 'Europe/Paris',
},
expected: {
'Cycling (Sport)': {
color: null,
label: 'Cycling (Sport)',
records: [
{
id: 9,
record_type: 'AS',
value: '18 km/h',
workout_date: '2019/07/07',
workout_id: 'hvYBqYBRa7wwXpaStWR4V2',
},
],
},
},
},
]
testsParams.map((testParams) =>
it(testParams.description, () => {
assert.deepEqual(
getRecordsBySports(
testParams.input.records,
translatedSports,
testParams.input.tz,
false,
false
),
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testParams.expected
)
})
)
})