Client - add workout details
This commit is contained in:
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div id="workout-info">
|
||||
<div class="workout-data">
|
||||
<i class="fa fa-clock-o" aria-hidden="true" />
|
||||
{{ t('workouts.DURATION') }}: <span>{{ workout.moving }}</span>
|
||||
<WorkoutRecord :workout="workout" record_type="LD" />
|
||||
<div v-if="withPause">
|
||||
({{ t('workouts.PAUSES') }}: <span>{{ workout.pauses }}</span> -
|
||||
{{ t('workouts.TOTAL_DURATION') }}: <span>{{ workout.duration }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workout-data">
|
||||
<i class="fa fa-road" aria-hidden="true" />
|
||||
{{ t('workouts.DISTANCE') }}: <span>{{ workout.distance }} km</span>
|
||||
<WorkoutRecord :workout="workout" record_type="FD" />
|
||||
</div>
|
||||
<div class="workout-data">
|
||||
<i class="fa fa-tachometer" aria-hidden="true" />
|
||||
{{ t('workouts.AVERAGE_SPEED') }}:
|
||||
<span>{{ workout.ave_speed }} km/h</span
|
||||
><WorkoutRecord :workout="workout" record_type="AS" /><br />
|
||||
{{ t('workouts.MAX_SPEED') }}: <span>{{ workout.max_speed }} km/h</span>
|
||||
<WorkoutRecord :workout="workout" record_type="MS" />
|
||||
</div>
|
||||
<div class="workout-data">
|
||||
<img class="mountains" src="/img/misc/mountains.svg" />
|
||||
{{ t('workouts.MIN_ALTITUDE') }}: <span>{{ workout.min_alt }} m</span
|
||||
><br />
|
||||
{{ t('workouts.MAX_ALTITUDE') }}: <span>{{ workout.max_alt }} m</span>
|
||||
</div>
|
||||
<div class="workout-data">
|
||||
<i class="fa fa-location-arrow" aria-hidden="true" />
|
||||
{{ t('workouts.ASCENT') }}: <span>{{ workout.ascent }} m</span><br />
|
||||
{{ t('workouts.DESCENT') }}: <span>{{ workout.descent }} m</span>
|
||||
</div>
|
||||
<WorkoutWeather :workout="workout" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import WorkoutRecord from '@/components/Workout/WorkoutDetail/WorkoutRecord.vue'
|
||||
import WorkoutWeather from '@/components/Workout/WorkoutDetail/WorkoutWeather.vue'
|
||||
import { IWorkout } from '@/types/workouts'
|
||||
export default defineComponent({
|
||||
name: 'WorkoutData',
|
||||
components: {
|
||||
WorkoutRecord,
|
||||
WorkoutWeather,
|
||||
},
|
||||
props: {
|
||||
workout: {
|
||||
type: Object as PropType<IWorkout>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { t } = useI18n()
|
||||
return {
|
||||
withPause: computed(
|
||||
() =>
|
||||
props.workout.pauses !== '0:00:00' && props.workout.pauses !== null
|
||||
),
|
||||
t,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/scss/base';
|
||||
#workout-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: $default-padding $default-padding * 2;
|
||||
width: 100%;
|
||||
.mountains {
|
||||
margin-bottom: -3px;
|
||||
height: 16px;
|
||||
filter: var(--workout-img-color);
|
||||
}
|
||||
.workout-data {
|
||||
text-transform: capitalize;
|
||||
padding: $default-padding * 0.5 0;
|
||||
|
||||
span {
|
||||
font-weight: bold;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-limit) {
|
||||
padding: $default-padding;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -105,6 +105,7 @@
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/scss/base';
|
||||
#workout-map {
|
||||
padding: $default-padding 0;
|
||||
.leaflet-container {
|
||||
height: 400px;
|
||||
width: 600px;
|
||||
|
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<span
|
||||
class="workout-record"
|
||||
v-if="
|
||||
workout.records &&
|
||||
workout.records.find((record) => record.record_type === record_type)
|
||||
"
|
||||
>
|
||||
<sup>
|
||||
<i class="fa fa-trophy" aria-hidden="true" />
|
||||
</sup>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
import { IWorkout } from '@/types/workouts'
|
||||
export default defineComponent({
|
||||
name: 'WorkoutRecord',
|
||||
props: {
|
||||
workout: {
|
||||
type: Object as PropType<IWorkout>,
|
||||
required: true,
|
||||
},
|
||||
record_type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div id="workout-weather" v-if="workout.weather_start && workout.weather_end">
|
||||
<table class="weather-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th>
|
||||
<div class="weather-th">
|
||||
{{ t('workouts.START') }}
|
||||
<img
|
||||
class="weather-img"
|
||||
:src="`/img/weather/${workout.weather_start.icon}.svg`"
|
||||
:alt="
|
||||
t(`workouts.WEATHER.DARK_SKY.${workout.weather_start.icon}`)
|
||||
"
|
||||
:title="
|
||||
t(`workouts.WEATHER.DARK_SKY.${workout.weather_start.icon}`)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div class="weather-th">
|
||||
{{ t('workouts.END') }}
|
||||
<img
|
||||
class="weather-img"
|
||||
:src="`/img/weather/${workout.weather_end.icon}.svg`"
|
||||
:alt="
|
||||
t(`workouts.WEATHER.DARK_SKY.${workout.weather_end.icon}`)
|
||||
"
|
||||
:title="
|
||||
t(`workouts.WEATHER.DARK_SKY.${workout.weather_end.icon}`)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<img
|
||||
class="weather-img weather-img-small"
|
||||
src="/img/weather/temperature.svg"
|
||||
:alt="t(`workouts.WEATHER.TEMPERATURE`)"
|
||||
:title="t(`workouts.WEATHER.TEMPERATURE`)"
|
||||
/>
|
||||
</td>
|
||||
<td>{{ Number(workout.weather_start.temperature).toFixed(1) }}°C</td>
|
||||
<td>{{ Number(workout.weather_end.temperature).toFixed(1) }}°C</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img
|
||||
class="weather-img weather-img-small"
|
||||
src="/img/weather/pour-rain.svg"
|
||||
:alt="t(`workouts.WEATHER.HUMIDITY`)"
|
||||
:title="t(`workouts.WEATHER.HUMIDITY`)"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{{ Number(workout.weather_start.humidity * 100).toFixed(1) }}%
|
||||
</td>
|
||||
<td>{{ Number(workout.weather_end.humidity * 100).toFixed(1) }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img
|
||||
class="weather-img weather-img-small"
|
||||
src="/img/weather/breeze.svg"
|
||||
:alt="t(`workouts.WEATHER.WIND`)"
|
||||
:title="t(`workouts.WEATHER.WIND`)"
|
||||
/>
|
||||
</td>
|
||||
<td>{{ Number(workout.weather_start.wind).toFixed(1) }}m/s</td>
|
||||
<td>{{ Number(workout.weather_end.wind).toFixed(1) }}m/s</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { IWorkout } from '@/types/workouts'
|
||||
export default defineComponent({
|
||||
name: 'WorkoutWeather',
|
||||
props: {
|
||||
workout: {
|
||||
type: Object as PropType<IWorkout>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
return { t }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/scss/base';
|
||||
#workout-weather {
|
||||
padding-top: $default-padding;
|
||||
.weather-img {
|
||||
height: 30px;
|
||||
filter: var(--workout-img-color);
|
||||
}
|
||||
.weather-img-small {
|
||||
height: 20px;
|
||||
}
|
||||
.weather-table {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
.weather-th {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
tbody {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -54,6 +54,7 @@
|
||||
</template>
|
||||
<template #content>
|
||||
<WorkoutMap :workout="workout" />
|
||||
<WorkoutData :workout="workout.workout" />
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
@ -65,6 +66,7 @@
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import Card from '@/components/Common/Card.vue'
|
||||
import WorkoutData from '@/components/Workout/WorkoutDetail/WorkoutData.vue'
|
||||
import WorkoutMap from '@/components/Workout/WorkoutDetail/WorkoutMap.vue'
|
||||
import { WORKOUTS_STORE } from '@/store/constants'
|
||||
import { ISport } from '@/types/sports'
|
||||
@ -77,6 +79,7 @@
|
||||
name: 'WorkoutDetail',
|
||||
components: {
|
||||
Card,
|
||||
WorkoutData,
|
||||
WorkoutMap,
|
||||
},
|
||||
props: {
|
||||
@ -158,6 +161,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@media screen and (max-width: $small-limit) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,14 +1,22 @@
|
||||
{
|
||||
"ADD_WORKOUT": "Add workout",
|
||||
"ASCENT": "ascent",
|
||||
"AVERAGE_SPEED": "average speed",
|
||||
"DESCENT": "descent",
|
||||
"DISTANCE": "distance",
|
||||
"DURATION": "duration",
|
||||
"END": "end",
|
||||
"KM": "km",
|
||||
"LATEST_WORKOUTS": "Latest workouts",
|
||||
"MAX_ALTITUDE": "max. altitude",
|
||||
"MAX_SPEED": "max. speed",
|
||||
"MIN_ALTITUDE": "min. altitude",
|
||||
"NEXT_WORKOUT": "Next workout",
|
||||
"NO_NEXT_WORKOUT": "No next workout",
|
||||
"NO_PREVIOUS_WORKOUT": "No previous workout",
|
||||
"NO_RECORDS": "No records.",
|
||||
"NO_WORKOUTS": "No workouts.",
|
||||
"PAUSES": "pauses",
|
||||
"PREVIOUS_WORKOUT": "Previous workout",
|
||||
"RECORD": "record | records",
|
||||
"RECORD_AS": "Ave. speed",
|
||||
@ -16,5 +24,24 @@
|
||||
"RECORD_LD": "Longest duration",
|
||||
"RECORD_MS": "Max. speed",
|
||||
"SPORT": "sport | sports",
|
||||
"START": "start",
|
||||
"TOTAL_DURATION": "total duration",
|
||||
"WEATHER": {
|
||||
"HUMIDITY": "humidity",
|
||||
"TEMPERATURE": "temperature",
|
||||
"WIND": "wind",
|
||||
"DARK_SKY": {
|
||||
"clear-day": "clear day",
|
||||
"clear-night": "clear night",
|
||||
"cloudy": "cloudy",
|
||||
"fog": "fog",
|
||||
"partly-cloudy-day": "partly cloudy day",
|
||||
"partly-cloudy-night": "partly cloudy night",
|
||||
"rain": "rain",
|
||||
"sleet": "sleet",
|
||||
"snow": "snow",
|
||||
"wind": "wind"
|
||||
}
|
||||
},
|
||||
"WORKOUT": "workout | workouts"
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
{
|
||||
"ADD_WORKOUT": "Ajouter une séance",
|
||||
"ASCENT": "dénivelé positif",
|
||||
"AVERAGE_SPEED": "vitesse moyenne",
|
||||
"DESCENT": "dénivelé négatif",
|
||||
"DISTANCE": "distance",
|
||||
"DURATION": "durée",
|
||||
"END": "fin",
|
||||
"KM": "km",
|
||||
"LATEST_WORKOUTS": "Séances récentes",
|
||||
"MAX_ALTITUDE": "altitude max",
|
||||
"MAX_SPEED": "vitesse max",
|
||||
"MIN_ALTITUDE": "altitude min",
|
||||
"NEXT_WORKOUT": "Séance suivante",
|
||||
"NO_NEXT_WORKOUT": "Pas de séance suivante",
|
||||
"NO_PREVIOUS_WORKOUT": "Pas de séances précédente",
|
||||
"NO_RECORDS": "Pas de records.",
|
||||
"NO_WORKOUTS": "Pas de séances.",
|
||||
"PAUSES": "pauses",
|
||||
"PREVIOUS_WORKOUT": "Séance précédente",
|
||||
"RECORD": "record | records",
|
||||
"RECORD_AS": "Vitesse moy.",
|
||||
@ -16,5 +24,24 @@
|
||||
"RECORD_LD": "Durée la + longue",
|
||||
"RECORD_MS": "Vitesse max.",
|
||||
"SPORT": "sport | sports",
|
||||
"START": "début",
|
||||
"TOTAL_DURATION": "durée totale",
|
||||
"WEATHER": {
|
||||
"HUMIDITY": "humidité",
|
||||
"TEMPERATURE": "température",
|
||||
"WIND": "vent",
|
||||
"DARK_SKY": {
|
||||
"clear-day": "ensoleillé",
|
||||
"clear-night": "nuit claire",
|
||||
"cloudy": "nuageux",
|
||||
"fog": "brouillard",
|
||||
"partly-cloudy-day": "partiellement nuageux",
|
||||
"partly-cloudy-night": "nuit partiellement nuageuse",
|
||||
"rain": "pluie",
|
||||
"sleet": "neige fondue",
|
||||
"snow": "neige",
|
||||
"wind": "venteux"
|
||||
}
|
||||
},
|
||||
"WORKOUT": "séance | séances"
|
||||
}
|
||||
|
@ -32,4 +32,6 @@
|
||||
--disabled-color: #a3a3a3;
|
||||
|
||||
--workout-trophy-color: #daa520;
|
||||
--workout-img-color: invert(22%) sepia(25%) saturate(646%) hue-rotate(169deg)
|
||||
brightness(97%) contrast(96%);
|
||||
}
|
Reference in New Issue
Block a user