Merge pull request #334 from jat255/filter_workouts_by_name
Add filter for workouts by title
This commit is contained in:
commit
89fd4e27f4
2
fittrackee/dist/index.html
vendored
2
fittrackee/dist/index.html
vendored
@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"/><link rel="stylesheet" href="/static/css/leaflet.css"/><title>FitTrackee</title><script defer="defer" src="/static/js/chunk-vendors.01cb48d3.js"></script><script defer="defer" src="/static/js/app.fbd9f0c5.js"></script><link href="/static/css/app.5f8309dc.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.01cb48d3.js"></script><script defer="defer" src="/static/js/app.d68a5506.js"></script><link href="/static/css/app.5f8309dc.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>
|
2
fittrackee/dist/service-worker.js
vendored
2
fittrackee/dist/service-worker.js
vendored
File diff suppressed because one or more lines are too long
2
fittrackee/dist/service-worker.js.map
vendored
2
fittrackee/dist/service-worker.js.map
vendored
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/css/workouts.3be4ceac.css
vendored
Normal file
1
fittrackee/dist/static/css/workouts.3be4ceac.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/app.d68a5506.js.map
vendored
Normal file
1
fittrackee/dist/static/js/app.d68a5506.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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(436);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.3b4abef2.js.map
|
||||
//# sourceMappingURL=statistics.1a300331.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/workouts.53fbbd65.js.map
vendored
Normal file
1
fittrackee/dist/static/js/workouts.53fbbd65.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -116,6 +116,7 @@ def seven_workouts_user_1() -> List[Workout]:
|
||||
distance=5,
|
||||
duration=datetime.timedelta(seconds=1024),
|
||||
)
|
||||
workout_1.title = "Workout 1 of 7"
|
||||
update_workout(workout_1)
|
||||
workout_1.ascent = 120
|
||||
workout_1.descent = 200
|
||||
@ -130,6 +131,7 @@ def seven_workouts_user_1() -> List[Workout]:
|
||||
distance=10,
|
||||
duration=datetime.timedelta(seconds=3456),
|
||||
)
|
||||
workout_2.title = "Workout 2 of 7"
|
||||
update_workout(workout_2)
|
||||
workout_2.ascent = 100
|
||||
workout_2.descent = 80
|
||||
@ -144,6 +146,7 @@ def seven_workouts_user_1() -> List[Workout]:
|
||||
distance=10,
|
||||
duration=datetime.timedelta(seconds=1024),
|
||||
)
|
||||
workout_3.title = "Workout 3 of 7"
|
||||
update_workout(workout_3)
|
||||
workout_3.ascent = 80
|
||||
workout_3.descent = 100
|
||||
@ -160,6 +163,7 @@ def seven_workouts_user_1() -> List[Workout]:
|
||||
distance=1,
|
||||
duration=datetime.timedelta(seconds=600),
|
||||
)
|
||||
workout_4.title = "Workout 4 of 7"
|
||||
update_workout(workout_4)
|
||||
workout_4.ascent = 120
|
||||
workout_4.descent = 180
|
||||
@ -174,6 +178,7 @@ def seven_workouts_user_1() -> List[Workout]:
|
||||
distance=10,
|
||||
duration=datetime.timedelta(seconds=1000),
|
||||
)
|
||||
workout_5.title = "Workout 5 of 7"
|
||||
update_workout(workout_5)
|
||||
workout_5.ascent = 100
|
||||
workout_5.descent = 200
|
||||
@ -188,6 +193,7 @@ def seven_workouts_user_1() -> List[Workout]:
|
||||
distance=8,
|
||||
duration=datetime.timedelta(seconds=6000),
|
||||
)
|
||||
workout_6.title = "Workout 6 of 7"
|
||||
update_workout(workout_6)
|
||||
workout_6.ascent = 40
|
||||
workout_6.descent = 20
|
||||
@ -202,6 +208,7 @@ def seven_workouts_user_1() -> List[Workout]:
|
||||
distance=10,
|
||||
duration=datetime.timedelta(seconds=3000),
|
||||
)
|
||||
workout_7.title = "Workout 7 of 7"
|
||||
update_workout(workout_7)
|
||||
db.session.add(workout_7)
|
||||
db.session.commit()
|
||||
|
@ -950,6 +950,52 @@ class TestGetWorkoutsWithFilters(ApiTestCaseMixin):
|
||||
'total': 1,
|
||||
}
|
||||
|
||||
def test_it_gets_one_workout_with_title_filter(
|
||||
self,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
sport_1_cycling: Sport,
|
||||
seven_workouts_user_1: List[Workout],
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
'/api/workouts?title=3 of 7',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
data = json.loads(response.data.decode())
|
||||
assert response.status_code == 200
|
||||
assert 'success' in data['status']
|
||||
workouts = data['data']['workouts']
|
||||
assert len(workouts) == 1
|
||||
assert 'Workout 3 of 7' == workouts[0]['title']
|
||||
assert 'Mon, 01 Jan 2018 00:00:00 GMT' == workouts[0]['workout_date']
|
||||
|
||||
def test_it_gets_no_workouts_with_title_filter(
|
||||
self,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
sport_1_cycling: Sport,
|
||||
seven_workouts_user_1: List[Workout],
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
'/api/workouts?title=no_such_title',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
data = json.loads(response.data.decode())
|
||||
assert response.status_code == 200
|
||||
assert 'success' in data['status']
|
||||
workouts = data['data']['workouts']
|
||||
assert len(workouts) == 0
|
||||
|
||||
|
||||
class TestGetWorkoutsWithFiltersAndPagination(ApiTestCaseMixin):
|
||||
def test_it_gets_page_2_with_date_filter(
|
||||
@ -1024,6 +1070,38 @@ class TestGetWorkoutsWithFiltersAndPagination(ApiTestCaseMixin):
|
||||
'total': 7,
|
||||
}
|
||||
|
||||
def test_it_gets_all_workouts_with_title_filter(
|
||||
self,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
sport_1_cycling: Sport,
|
||||
seven_workouts_user_1: List[Workout],
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
'/api/workouts?title=of 7',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
data = json.loads(response.data.decode())
|
||||
assert response.status_code == 200
|
||||
assert 'success' in data['status']
|
||||
assert len(data['data']['workouts']) == 5
|
||||
assert (
|
||||
'Wed, 09 May 2018 00:00:00 GMT'
|
||||
== data['data']['workouts'][0]['workout_date']
|
||||
)
|
||||
assert data['pagination'] == {
|
||||
'has_next': True,
|
||||
'has_prev': False,
|
||||
'page': 1,
|
||||
'pages': 2,
|
||||
'total': 7,
|
||||
}
|
||||
|
||||
|
||||
class TestGetWorkout(ApiTestCaseMixin):
|
||||
def test_it_gets_a_workout(
|
||||
|
@ -187,6 +187,8 @@ def get_workouts(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
:query integer per_page: number of workouts per page
|
||||
(default: 5, max: 100)
|
||||
:query integer sport_id: sport id
|
||||
:quert string title: any part (or all) of the workout title;
|
||||
title matching is case-insensitive
|
||||
:query string from: start date (format: ``%Y-%m-%d``)
|
||||
:query string to: end date (format: ``%Y-%m-%d``)
|
||||
:query float distance_from: minimal distance
|
||||
@ -230,6 +232,7 @@ def get_workouts(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
)
|
||||
order = params.get('order', 'desc')
|
||||
sport_id = params.get('sport_id')
|
||||
title = params.get('title')
|
||||
per_page = int(params.get('per_page', DEFAULT_WORKOUTS_PER_PAGE))
|
||||
if per_page > MAX_WORKOUTS_PER_PAGE:
|
||||
per_page = MAX_WORKOUTS_PER_PAGE
|
||||
@ -237,6 +240,7 @@ def get_workouts(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
Workout.query.filter(
|
||||
Workout.user_id == auth_user.id,
|
||||
Workout.sport_id == sport_id if sport_id else True,
|
||||
Workout.title.ilike(f"%{title}%") if title else True,
|
||||
Workout.workout_date >= date_from if date_from else True,
|
||||
Workout.workout_date < date_to + timedelta(seconds=1)
|
||||
if date_to
|
||||
|
@ -1,160 +1,184 @@
|
||||
<template>
|
||||
<div class="workouts-filters">
|
||||
<div class="box">
|
||||
<div class="form">
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.FROM') }}: </label>
|
||||
<input
|
||||
name="from"
|
||||
type="date"
|
||||
:value="$route.query.from"
|
||||
@change="handleFilterChange"
|
||||
/>
|
||||
<form v-on:submit.prevent="onSubmit" class="form">
|
||||
<div class="form-all-items">
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.FROM') }}: </label>
|
||||
<input
|
||||
name="from"
|
||||
type="date"
|
||||
:value="$route.query.from"
|
||||
@change="handleFilterChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.TO') }}: </label>
|
||||
<input
|
||||
name="to"
|
||||
type="date"
|
||||
:value="$route.query.to"
|
||||
@change="handleFilterChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.TO') }}: </label>
|
||||
<input
|
||||
name="to"
|
||||
type="date"
|
||||
:value="$route.query.to"
|
||||
@change="handleFilterChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.SPORT', 1) }}:</label>
|
||||
<select
|
||||
name="sport_id"
|
||||
:value="$route.query.sport_id"
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<option value="" />
|
||||
<option
|
||||
v-for="sport in translatedSports.filter((s) =>
|
||||
authUser.sports_list.includes(s.id)
|
||||
)"
|
||||
:value="sport.id"
|
||||
:key="sport.id"
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.SPORT', 1) }}:</label>
|
||||
<select
|
||||
name="sport_id"
|
||||
:value="$route.query.sport_id"
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
{{ sport.translatedLabel }}
|
||||
</option>
|
||||
</select>
|
||||
<option value="" />
|
||||
<option
|
||||
v-for="sport in translatedSports.filter((s) =>
|
||||
authUser.sports_list.includes(s.id)
|
||||
)"
|
||||
:value="sport.id"
|
||||
:key="sport.id"
|
||||
>
|
||||
{{ sport.translatedLabel }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-item form-item-title">
|
||||
<label> {{ $t('workouts.TITLE', 1) }}:</label>
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
class="title"
|
||||
name="title"
|
||||
:value="$route.query.title"
|
||||
@change="handleFilterChange"
|
||||
placeholder=""
|
||||
type="text"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.DISTANCE') }} ({{ toUnit }}): </label>
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
name="distance_from"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.1"
|
||||
:value="$route.query.distance_from"
|
||||
@change="handleFilterChange"
|
||||
/>
|
||||
<span>{{ $t('workouts.TO') }}</span>
|
||||
<input
|
||||
name="distance_to"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.1"
|
||||
:value="$route.query.distance_to"
|
||||
@change="handleFilterChange"
|
||||
/>
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.DISTANCE') }} ({{ toUnit }}): </label>
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
name="distance_from"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.1"
|
||||
:value="$route.query.distance_from"
|
||||
@change="handleFilterChange"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
<span>{{ $t('workouts.TO') }}</span>
|
||||
<input
|
||||
name="distance_to"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.1"
|
||||
:value="$route.query.distance_to"
|
||||
@change="handleFilterChange"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.DURATION') }}: </label>
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
name="duration_from"
|
||||
:value="$route.query.duration_from"
|
||||
@change="handleFilterChange"
|
||||
pattern="^([0-9]*[0-9]):([0-5][0-9])$"
|
||||
placeholder="hh:mm"
|
||||
type="text"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
<span>{{ $t('workouts.TO') }}</span>
|
||||
<input
|
||||
name="duration_to"
|
||||
:value="$route.query.duration_to"
|
||||
@change="handleFilterChange"
|
||||
pattern="^([0-9]*[0-9]):([0-5][0-9])$"
|
||||
placeholder="hh:mm"
|
||||
type="text"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.AVE_SPEED') }} ({{ toUnit }}/h): </label>
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
min="0"
|
||||
name="ave_speed_from"
|
||||
:value="$route.query.ave_speed_from"
|
||||
@change="handleFilterChange"
|
||||
step="0.1"
|
||||
type="number"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
<span>{{ $t('workouts.TO') }}</span>
|
||||
<input
|
||||
min="0"
|
||||
name="ave_speed_to"
|
||||
:value="$route.query.ave_speed_to"
|
||||
@change="handleFilterChange"
|
||||
step="0.1"
|
||||
type="number"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.MAX_SPEED') }} ({{ toUnit }}/h): </label>
|
||||
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
min="0"
|
||||
name="max_speed_from"
|
||||
:value="$route.query.max_speed_from"
|
||||
@change="handleFilterChange"
|
||||
step="0.1"
|
||||
type="number"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
<span>{{ $t('workouts.TO') }}</span>
|
||||
<input
|
||||
min="0"
|
||||
name="max_speed_to"
|
||||
:value="$route.query.max_speed_to"
|
||||
@change="handleFilterChange"
|
||||
step="0.1"
|
||||
type="number"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.DURATION') }}: </label>
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
name="duration_from"
|
||||
:value="$route.query.duration_from"
|
||||
@change="handleFilterChange"
|
||||
pattern="^([0-9]*[0-9]):([0-5][0-9])$"
|
||||
placeholder="hh:mm"
|
||||
type="text"
|
||||
/>
|
||||
<span>{{ $t('workouts.TO') }}</span>
|
||||
<input
|
||||
name="duration_to"
|
||||
:value="$route.query.duration_to"
|
||||
@change="handleFilterChange"
|
||||
pattern="^([0-9]*[0-9]):([0-5][0-9])$"
|
||||
placeholder="hh:mm"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-button">
|
||||
<button type="submit" class="confirm" @click="onFilter">
|
||||
{{ $t('buttons.FILTER') }}
|
||||
</button>
|
||||
<button class="confirm" @click="onClearFilter">
|
||||
{{ $t('buttons.CLEAR_FILTER') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.AVE_SPEED') }} ({{ toUnit }}/h): </label>
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
min="0"
|
||||
name="ave_speed_from"
|
||||
:value="$route.query.ave_speed_from"
|
||||
@change="handleFilterChange"
|
||||
step="0.1"
|
||||
type="number"
|
||||
/>
|
||||
<span>{{ $t('workouts.TO') }}</span>
|
||||
<input
|
||||
min="0"
|
||||
name="ave_speed_to"
|
||||
:value="$route.query.ave_speed_to"
|
||||
@change="handleFilterChange"
|
||||
step="0.1"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-items-group">
|
||||
<div class="form-item">
|
||||
<label> {{ $t('workouts.MAX_SPEED') }} ({{ toUnit }}/h): </label>
|
||||
|
||||
<div class="form-inputs-group">
|
||||
<input
|
||||
min="0"
|
||||
name="max_speed_from"
|
||||
:value="$route.query.max_speed_from"
|
||||
@change="handleFilterChange"
|
||||
step="0.1"
|
||||
type="number"
|
||||
/>
|
||||
<span>{{ $t('workouts.TO') }}</span>
|
||||
<input
|
||||
min="0"
|
||||
name="max_speed_to"
|
||||
:value="$route.query.max_speed_to"
|
||||
@change="handleFilterChange"
|
||||
step="0.1"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-button">
|
||||
<button class="confirm" @click="onFilter">
|
||||
{{ $t('buttons.FILTER') }}
|
||||
</button>
|
||||
<button class="confirm" @click="onClearFilter">
|
||||
{{ $t('buttons.CLEAR_FILTER') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -223,40 +247,48 @@
|
||||
|
||||
.workouts-filters {
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 0;
|
||||
|
||||
.form-items-group {
|
||||
.form-all-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: $default-padding * 0.5;
|
||||
padding-top: 0;
|
||||
|
||||
.form-item {
|
||||
.form-items-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: $default-padding * 0.5;
|
||||
|
||||
.form-inputs-group {
|
||||
.form-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.form-inputs-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
width: 34%;
|
||||
}
|
||||
span {
|
||||
padding: $default-padding * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width: 34%;
|
||||
height: 16px;
|
||||
}
|
||||
span {
|
||||
padding: $default-padding * 0.5;
|
||||
|
||||
select {
|
||||
height: 38px;
|
||||
padding: 0 $default-padding * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
select {
|
||||
height: 36px;
|
||||
padding: 0 $default-padding * 0.5;
|
||||
.form-item-title {
|
||||
padding-top: $default-padding;
|
||||
input.title {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -276,26 +308,35 @@
|
||||
|
||||
@media screen and (max-width: $medium-limit) {
|
||||
.form {
|
||||
flex-direction: row;
|
||||
padding-top: $default-padding * 0.5;
|
||||
.form-all-items {
|
||||
flex-direction: row;
|
||||
padding-top: $default-padding * 0.5;
|
||||
|
||||
.form-items-group {
|
||||
padding: 0 $default-padding * 0.5;
|
||||
height: 100%;
|
||||
.form-items-group {
|
||||
padding: 0 $default-padding * 0.5;
|
||||
height: 100%;
|
||||
|
||||
.form-item {
|
||||
label {
|
||||
font-size: 0.9em;
|
||||
.form-item {
|
||||
label, span {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.form-inputs-group {
|
||||
flex-direction: column;
|
||||
justify-content: normal;
|
||||
padding: 0;
|
||||
|
||||
input {
|
||||
width: 85%;
|
||||
}
|
||||
span {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-inputs-group {
|
||||
flex-direction: column;
|
||||
justify-content: normal;
|
||||
padding: 0;
|
||||
|
||||
input {
|
||||
width: 75%;
|
||||
}
|
||||
.form-item-title {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,26 +352,30 @@
|
||||
}
|
||||
@media screen and (max-width: $small-limit) {
|
||||
.form {
|
||||
flex-direction: column;
|
||||
padding-top: 0;
|
||||
.form-all-items {
|
||||
flex-direction: column;
|
||||
padding-top: 0;
|
||||
|
||||
.form-items-group {
|
||||
padding: $default-padding * 0.5;
|
||||
.form-items-group {
|
||||
padding: $default-padding * 0.5;
|
||||
|
||||
.form-item {
|
||||
label {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.form-inputs-group {
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
input {
|
||||
width: 50%;
|
||||
.form-item {
|
||||
label {
|
||||
font-size: 1em;
|
||||
}
|
||||
span {
|
||||
padding: $default-padding * 0.5;
|
||||
|
||||
.form-inputs-group {
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
span {
|
||||
padding: $default-padding * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -348,6 +393,20 @@
|
||||
.form-button {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.form {
|
||||
.form-all-items {
|
||||
|
||||
.form-items-group {
|
||||
.form-item-title {
|
||||
padding-top: $default-padding;
|
||||
|
||||
input.title {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -66,6 +66,7 @@ export const workoutsPayloadKeys = [
|
||||
'duration_from',
|
||||
'duration_to',
|
||||
'sport_id',
|
||||
'title'
|
||||
]
|
||||
|
||||
const getRange = (stop: number, start = 1): number[] => {
|
||||
|
Loading…
Reference in New Issue
Block a user