Client - display segment
This commit is contained in:
parent
de32c136b6
commit
b7b2eb0daf
@ -2,44 +2,48 @@
|
||||
<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" />
|
||||
{{ t('workouts.DURATION') }}: <span>{{ workoutObject.moving }}</span>
|
||||
<WorkoutRecord :workoutObject="workoutObject" record_type="LD" />
|
||||
<div v-if="withPause">
|
||||
({{ t('workouts.PAUSES') }}: <span>{{ workout.pauses }}</span> -
|
||||
{{ t('workouts.TOTAL_DURATION') }}: <span>{{ workout.duration }})</span>
|
||||
({{ t('workouts.PAUSES') }}: <span>{{ workoutObject.pauses }}</span> -
|
||||
{{ t('workouts.TOTAL_DURATION') }}:
|
||||
<span>{{ workoutObject.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" />
|
||||
{{ t('workouts.DISTANCE') }}: <span>{{ workoutObject.distance }} km</span>
|
||||
<WorkoutRecord :workoutObject="workoutObject" 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" />
|
||||
<span>{{ workoutObject.aveSpeed }} km/h</span
|
||||
><WorkoutRecord :workoutObject="workoutObject" record_type="AS" /><br />
|
||||
{{ t('workouts.MAX_SPEED') }}:
|
||||
<span>{{ workoutObject.maxSpeed }} km/h</span>
|
||||
<WorkoutRecord :workoutObject="workoutObject" record_type="MS" />
|
||||
</div>
|
||||
<div
|
||||
class="workout-data"
|
||||
v-if="workout.max_alt !== null && workout.min_alt !== null"
|
||||
v-if="workoutObject.maxAlt !== null && workoutObject.minAlt !== null"
|
||||
>
|
||||
<img class="mountains" src="/img/misc/mountains.svg" />
|
||||
{{ t('workouts.MIN_ALTITUDE') }}: <span>{{ workout.min_alt }} m</span
|
||||
{{ t('workouts.MIN_ALTITUDE') }}: <span>{{ workoutObject.minAlt }} m</span
|
||||
><br />
|
||||
{{ t('workouts.MAX_ALTITUDE') }}: <span>{{ workout.max_alt }} m</span>
|
||||
{{ t('workouts.MAX_ALTITUDE') }}:
|
||||
<span>{{ workoutObject.maxAlt }} m</span>
|
||||
</div>
|
||||
<div
|
||||
class="workout-data"
|
||||
v-if="workout.ascent !== null && workout.descent !== null"
|
||||
v-if="workoutObject.ascent !== null && workoutObject.descent !== null"
|
||||
>
|
||||
<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>
|
||||
{{ t('workouts.ASCENT') }}: <span>{{ workoutObject.ascent }} m</span
|
||||
><br />
|
||||
{{ t('workouts.DESCENT') }}: <span>{{ workoutObject.descent }} m</span>
|
||||
</div>
|
||||
<WorkoutWeather :workout="workout" />
|
||||
<WorkoutWeather :workoutObject="workoutObject" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -49,7 +53,7 @@
|
||||
|
||||
import WorkoutRecord from '@/components/Workout/WorkoutDetail/WorkoutRecord.vue'
|
||||
import WorkoutWeather from '@/components/Workout/WorkoutDetail/WorkoutWeather.vue'
|
||||
import { IWorkout } from '@/types/workouts'
|
||||
import { IWorkoutObject } from '@/types/workouts'
|
||||
export default defineComponent({
|
||||
name: 'WorkoutData',
|
||||
components: {
|
||||
@ -57,8 +61,8 @@
|
||||
WorkoutWeather,
|
||||
},
|
||||
props: {
|
||||
workout: {
|
||||
type: Object as PropType<IWorkout>,
|
||||
workoutObject: {
|
||||
type: Object as PropType<IWorkoutObject>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
@ -67,7 +71,8 @@
|
||||
return {
|
||||
withPause: computed(
|
||||
() =>
|
||||
props.workout.pauses !== '0:00:00' && props.workout.pauses !== null
|
||||
props.workoutObject.pauses !== '0:00:00' &&
|
||||
props.workoutObject.pauses !== null
|
||||
),
|
||||
t,
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
<span
|
||||
class="workout-record"
|
||||
v-if="
|
||||
workout.records &&
|
||||
workout.records.find((record) => record.record_type === record_type)
|
||||
workoutObject.records &&
|
||||
workoutObject.records.find((record) => record.record_type === record_type)
|
||||
"
|
||||
>
|
||||
<sup>
|
||||
@ -15,12 +15,12 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
import { IWorkout } from '@/types/workouts'
|
||||
import { IWorkoutObject } from '@/types/workouts'
|
||||
export default defineComponent({
|
||||
name: 'WorkoutRecord',
|
||||
props: {
|
||||
workout: {
|
||||
type: Object as PropType<IWorkout>,
|
||||
workoutObject: {
|
||||
type: Object as PropType<IWorkoutObject>,
|
||||
required: true,
|
||||
},
|
||||
record_type: {
|
||||
|
@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div id="workout-weather" v-if="workout.weather_start && workout.weather_end">
|
||||
<div
|
||||
id="workout-weather"
|
||||
v-if="workoutObject.weatherStart && workoutObject.weatherEnd"
|
||||
>
|
||||
<table class="weather-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -9,12 +12,16 @@
|
||||
{{ t('workouts.START') }}
|
||||
<img
|
||||
class="weather-img"
|
||||
:src="`/img/weather/${workout.weather_start.icon}.svg`"
|
||||
:src="`/img/weather/${workoutObject.weatherStart.icon}.svg`"
|
||||
:alt="
|
||||
t(`workouts.WEATHER.DARK_SKY.${workout.weather_start.icon}`)
|
||||
t(
|
||||
`workouts.WEATHER.DARK_SKY.${workoutObject.weatherStart.icon}`
|
||||
)
|
||||
"
|
||||
:title="
|
||||
t(`workouts.WEATHER.DARK_SKY.${workout.weather_start.icon}`)
|
||||
t(
|
||||
`workouts.WEATHER.DARK_SKY.${workoutObject.weatherStart.icon}`
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
@ -24,12 +31,16 @@
|
||||
{{ t('workouts.END') }}
|
||||
<img
|
||||
class="weather-img"
|
||||
:src="`/img/weather/${workout.weather_end.icon}.svg`"
|
||||
:src="`/img/weather/${workoutObject.weatherEnd.icon}.svg`"
|
||||
:alt="
|
||||
t(`workouts.WEATHER.DARK_SKY.${workout.weather_end.icon}`)
|
||||
t(
|
||||
`workouts.WEATHER.DARK_SKY.${workoutObject.weatherEnd.icon}`
|
||||
)
|
||||
"
|
||||
:title="
|
||||
t(`workouts.WEATHER.DARK_SKY.${workout.weather_end.icon}`)
|
||||
t(
|
||||
`workouts.WEATHER.DARK_SKY.${workoutObject.weatherEnd.icon}`
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
@ -46,8 +57,12 @@
|
||||
: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>
|
||||
<td>
|
||||
{{ Number(workoutObject.weatherStart.temperature).toFixed(1) }}°C
|
||||
</td>
|
||||
<td>
|
||||
{{ Number(workoutObject.weatherEnd.temperature).toFixed(1) }}°C
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
@ -59,9 +74,11 @@
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{{ Number(workout.weather_start.humidity * 100).toFixed(1) }}%
|
||||
{{ Number(workoutObject.weatherStart.humidity * 100).toFixed(1) }}%
|
||||
</td>
|
||||
<td>
|
||||
{{ Number(workoutObject.weatherEnd.humidity * 100).toFixed(1) }}%
|
||||
</td>
|
||||
<td>{{ Number(workout.weather_end.humidity * 100).toFixed(1) }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
@ -72,8 +89,8 @@
|
||||
: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>
|
||||
<td>{{ Number(workoutObject.weatherStart.wind).toFixed(1) }}m/s</td>
|
||||
<td>{{ Number(workoutObject.weatherEnd.wind).toFixed(1) }}m/s</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -84,12 +101,12 @@
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { IWorkout } from '@/types/workouts'
|
||||
import { IWorkoutObject } from '@/types/workouts'
|
||||
export default defineComponent({
|
||||
name: 'WorkoutWeather',
|
||||
props: {
|
||||
workout: {
|
||||
type: Object as PropType<IWorkout>,
|
||||
workoutObject: {
|
||||
type: Object as PropType<IWorkoutObject>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
@ -4,18 +4,15 @@
|
||||
<template #title>
|
||||
<div
|
||||
class="workout-previous workout-arrow"
|
||||
:class="{ inactive: !workoutData.workout.previous_workout }"
|
||||
:class="{ inactive: !workoutObject.previousUrl }"
|
||||
:title="
|
||||
workoutData.workout.previous_workout
|
||||
? t('workouts.PREVIOUS_WORKOUT')
|
||||
: t('workouts.NO_PREVIOUS_WORKOUT')
|
||||
workoutObject.previousUrl
|
||||
? t(`workouts.PREVIOUS_${workoutObject.type}`)
|
||||
: t(`workouts.NO_PREVIOUS_${workoutObject.type}`)
|
||||
"
|
||||
@click="
|
||||
workoutData.workout.previous_workout
|
||||
? $router.push({
|
||||
name: 'Workout',
|
||||
params: { workoutId: workoutData.workout.previous_workout },
|
||||
})
|
||||
workoutObject.previousUrl
|
||||
? $router.push(workoutObject.previousUrl)
|
||||
: null
|
||||
"
|
||||
>
|
||||
@ -26,27 +23,41 @@
|
||||
<img alt="workout sport logo" :src="sport.img" />
|
||||
</div>
|
||||
<div class="workout-title-date">
|
||||
<div class="workout-title">{{ workoutData.workout.title }}</div>
|
||||
<div class="workout-title" v-if="workoutObject.type === 'WORKOUT'">
|
||||
{{ workoutObject.title }}
|
||||
</div>
|
||||
<div class="workout-title" v-else>
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'Workout',
|
||||
params: { workoutId: workoutObject.workoutId },
|
||||
}"
|
||||
>
|
||||
{{ workoutObject.title }}
|
||||
</router-link>
|
||||
—
|
||||
<span class="workout-segment">
|
||||
<i class="fa fa-map-marker" aria-hidden="true" />
|
||||
{{ t('workouts.SEGMENT') }}
|
||||
{{ workoutObject.segmentId + 1 }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="workout-date">
|
||||
{{ workoutDate.workout_date }} - {{ workoutDate.workout_time }}
|
||||
{{ workoutObject.workoutDate }} -
|
||||
{{ workoutObject.workoutTime }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="workout-next workout-arrow"
|
||||
:class="{ inactive: !workoutData.workout.next_workout }"
|
||||
:class="{ inactive: !workoutObject.nextUrl }"
|
||||
:title="
|
||||
workoutData.workout.next_workout
|
||||
? t('workouts.NEXT_WORKOUT')
|
||||
: t('workouts.NO_NEXT_WORKOUT')
|
||||
workoutObject.nextUrl
|
||||
? t(`workouts.NEXT_${workoutObject.type}`)
|
||||
: t(`workouts.NO_NEXT_${workoutObject.type}`)
|
||||
"
|
||||
@click="
|
||||
workoutData.workout.next_workout
|
||||
? $router.push({
|
||||
name: 'Workout',
|
||||
params: { workoutId: workoutData.workout.next_workout },
|
||||
})
|
||||
: null
|
||||
workoutObject.nextUrl ? $router.push(workoutObject.nextUrl) : null
|
||||
"
|
||||
>
|
||||
<i class="fa fa-chevron-right" aria-hidden="true" />
|
||||
@ -57,25 +68,37 @@
|
||||
:workoutData="workoutData"
|
||||
:markerCoordinates="markerCoordinates"
|
||||
/>
|
||||
<WorkoutData :workout="workoutData.workout" />
|
||||
<WorkoutData :workoutObject="workoutObject" />
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent, computed, watch } from 'vue'
|
||||
import {
|
||||
ComputedRef,
|
||||
PropType,
|
||||
Ref,
|
||||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
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'
|
||||
import { IAuthUserProfile } from '@/types/user'
|
||||
import { IWorkoutData, TCoordinates } from '@/types/workouts'
|
||||
import { useStore } from '@/use/useStore'
|
||||
import {
|
||||
IWorkout,
|
||||
IWorkoutData,
|
||||
IWorkoutObject,
|
||||
IWorkoutSegment,
|
||||
TCoordinates,
|
||||
} from '@/types/workouts'
|
||||
import { formatWorkoutDate, getDateWithTZ } from '@/utils/dates'
|
||||
|
||||
export default defineComponent({
|
||||
@ -101,17 +124,98 @@
|
||||
type: Object as PropType<IWorkoutData>,
|
||||
required: true,
|
||||
},
|
||||
displaySegment: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const route = useRoute()
|
||||
const store = useStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
function getWorkoutObjectUrl(
|
||||
workout: IWorkout,
|
||||
displaySegment: boolean,
|
||||
segmentId: number | null
|
||||
): Record<string, string | null> {
|
||||
const previousUrl =
|
||||
displaySegment && segmentId && segmentId !== 1
|
||||
? `/workouts/${workout.id}/segment/${segmentId - 1}`
|
||||
: !displaySegment && workout.previous_workout
|
||||
? `/workouts/${workout.previous_workout}`
|
||||
: null
|
||||
const nextUrl =
|
||||
displaySegment && segmentId && segmentId < workout.segments.length
|
||||
? `/workouts/${workout.id}/segment/${segmentId + 1}`
|
||||
: !displaySegment && workout.next_workout
|
||||
? `/workouts/${workout.next_workout}`
|
||||
: null
|
||||
return {
|
||||
previousUrl,
|
||||
nextUrl,
|
||||
}
|
||||
}
|
||||
function getWorkoutObject(
|
||||
workout: IWorkout,
|
||||
segment: IWorkoutSegment | null
|
||||
): IWorkoutObject {
|
||||
const urls = getWorkoutObjectUrl(
|
||||
workout,
|
||||
props.displaySegment,
|
||||
segmentId.value ? +segmentId.value : null
|
||||
)
|
||||
const workoutDate = formatWorkoutDate(
|
||||
getDateWithTZ(
|
||||
props.workoutData.workout.workout_date,
|
||||
props.authUser.timezone
|
||||
)
|
||||
)
|
||||
return {
|
||||
ascent: segment ? segment.ascent : workout.ascent,
|
||||
aveSpeed: segment ? segment.ave_speed : workout.ave_speed,
|
||||
distance: segment ? segment.distance : workout.distance,
|
||||
descent: segment ? segment.descent : workout.descent,
|
||||
duration: segment ? segment.duration : workout.duration,
|
||||
maxAlt: segment ? segment.max_alt : workout.max_alt,
|
||||
maxSpeed: segment ? segment.max_speed : workout.max_speed,
|
||||
minAlt: segment ? segment.min_alt : workout.min_alt,
|
||||
moving: segment ? segment.moving : workout.moving,
|
||||
nextUrl: urls.nextUrl,
|
||||
pauses: segment ? segment.pauses : workout.pauses,
|
||||
previousUrl: urls.previousUrl,
|
||||
records: segment ? [] : workout.records,
|
||||
segmentId: segment ? segment.segment_id : null,
|
||||
title: workout.title,
|
||||
type: props.displaySegment ? 'SEGMENT' : 'WORKOUT',
|
||||
workoutDate: workoutDate.workout_date,
|
||||
weatherEnd: segment ? null : workout.weather_end,
|
||||
workoutId: workout.id,
|
||||
weatherStart: segment ? null : workout.weather_start,
|
||||
workoutTime: workoutDate.workout_time,
|
||||
}
|
||||
}
|
||||
|
||||
const workout: ComputedRef<IWorkout> = computed(
|
||||
() => props.workoutData.workout
|
||||
)
|
||||
let segmentId: Ref<number | null> = ref(
|
||||
route.params.workoutId ? +route.params.segmentId : null
|
||||
)
|
||||
const segment: ComputedRef<IWorkoutSegment | null> = computed(() =>
|
||||
workout.value.segments.length > 0 && segmentId.value
|
||||
? workout.value.segments[+segmentId.value - 1]
|
||||
: null
|
||||
)
|
||||
|
||||
watch(
|
||||
() => route.params.workoutId,
|
||||
async (newWorkoutId) => {
|
||||
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, newWorkoutId)
|
||||
() => route.params.segmentId,
|
||||
async (newSegmentId) => {
|
||||
if (newSegmentId) {
|
||||
segmentId.value = +newSegmentId
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
sport: computed(() =>
|
||||
props.sports
|
||||
@ -120,13 +224,8 @@
|
||||
)
|
||||
: {}
|
||||
),
|
||||
workoutDate: computed(() =>
|
||||
formatWorkoutDate(
|
||||
getDateWithTZ(
|
||||
props.workoutData.workout.workout_date,
|
||||
props.authUser.timezone
|
||||
)
|
||||
)
|
||||
workoutObject: computed(() =>
|
||||
getWorkoutObject(workout.value, segment.value)
|
||||
),
|
||||
t,
|
||||
}
|
||||
@ -166,6 +265,10 @@
|
||||
font-size: 0.8em;
|
||||
font-weight: normal;
|
||||
}
|
||||
.workout-segment {
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
.card-content {
|
||||
|
72
fittrackee_client/src/components/Workout/WorkoutSegments.vue
Normal file
72
fittrackee_client/src/components/Workout/WorkoutSegments.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div id="workout-segments">
|
||||
<Card :without-title="false">
|
||||
<template #title>{{ t('workouts.SEGMENT', 2) }}</template>
|
||||
<template #content>
|
||||
<ul>
|
||||
<li v-for="(segment, index) in segments" :key="segment.segment_id">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'WorkoutSegment',
|
||||
params: {
|
||||
workoutId: segment.workout_id,
|
||||
segmentId: index + 1,
|
||||
},
|
||||
}"
|
||||
>{{ t('workouts.SEGMENT', 1) }} {{ index + 1 }}</router-link
|
||||
>
|
||||
({{ t('workouts.DISTANCE') }}: {{ segment.distance }} km,
|
||||
{{ t('workouts.DURATION') }}: {{ segment.duration }})
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Card from '@/components/Common/Card.vue'
|
||||
import { IWorkoutSegment } from '@/types/workouts'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WorkoutSegments',
|
||||
components: {
|
||||
Card,
|
||||
},
|
||||
props: {
|
||||
segments: {
|
||||
type: Object as PropType<IWorkoutSegment[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
return { t }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/scss/base';
|
||||
#workout-segments {
|
||||
::v-deep(.card) {
|
||||
.card-title {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.card-content {
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
a {
|
||||
font-weight: bold;
|
||||
}
|
||||
ul {
|
||||
padding: 0 $default-padding;
|
||||
list-style: square;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -13,22 +13,27 @@
|
||||
"MAX_ALTITUDE": "max. altitude",
|
||||
"MAX_SPEED": "max. speed",
|
||||
"MIN_ALTITUDE": "min. altitude",
|
||||
"NEXT_SEGMENT": "No next segment",
|
||||
"NEXT_WORKOUT": "Next workout",
|
||||
"NO_DATA_CLEANING": "data from gpx, without any cleaning",
|
||||
"NO_MAP": "No map",
|
||||
"NO_NEXT_SEGMENT": "No next segment",
|
||||
"NO_NEXT_WORKOUT": "No next workout",
|
||||
"NO_NOTES": "No notes",
|
||||
"NO_PREVIOUS_SEGMENT": "No previous segment",
|
||||
"NO_PREVIOUS_WORKOUT": "No previous workout",
|
||||
"NO_RECORDS": "No records.",
|
||||
"NO_WORKOUTS": "No workouts.",
|
||||
"NOTES": "Notes",
|
||||
"PAUSES": "pauses",
|
||||
"PREVIOUS_SEGMENT": "Previous segment",
|
||||
"PREVIOUS_WORKOUT": "Previous workout",
|
||||
"RECORD": "record | records",
|
||||
"RECORD_AS": "Ave. speed",
|
||||
"RECORD_FD": "Farest distance",
|
||||
"RECORD_LD": "Longest duration",
|
||||
"RECORD_MS": "Max. speed",
|
||||
"SEGMENT": "segment | segments",
|
||||
"SPEED": "speed",
|
||||
"SPORT": "sport | sports",
|
||||
"START": "start",
|
||||
|
@ -13,22 +13,27 @@
|
||||
"MAX_ALTITUDE": "altitude max",
|
||||
"MAX_SPEED": "vitesse max",
|
||||
"MIN_ALTITUDE": "altitude min",
|
||||
"NEXT_SEGMENT": "Segment suivant",
|
||||
"NEXT_WORKOUT": "Séance suivante",
|
||||
"NO_DATA_CLEANING": "données issues du fichier gpx, sans correction",
|
||||
"NO_MAP": "Pas de carte",
|
||||
"NO_NEXT_SEGMENT": "Pas de segment suivant",
|
||||
"NO_NEXT_WORKOUT": "Pas de séance suivante",
|
||||
"NO_NOTES": "Pas de notes",
|
||||
"NO_PREVIOUS_WORKOUT": "Pas de séances précédente",
|
||||
"NO_PREVIOUS_SEGMENT": "Pas de segment précédent",
|
||||
"NO_PREVIOUS_WORKOUT": "Pas de séance précédente",
|
||||
"NO_RECORDS": "Pas de records.",
|
||||
"NO_WORKOUTS": "Pas de séances.",
|
||||
"NOTES": "Notes",
|
||||
"PAUSES": "pauses",
|
||||
"PREVIOUS_SEGMENT": "Segment précédent",
|
||||
"PREVIOUS_WORKOUT": "Séance précédente",
|
||||
"RECORD": "record | records",
|
||||
"RECORD_AS": "Vitesse moy.",
|
||||
"RECORD_FD": "Distance la + longue",
|
||||
"RECORD_LD": "Durée la + longue",
|
||||
"RECORD_MS": "Vitesse max.",
|
||||
"SEGMENT": "segment | segments",
|
||||
"SPEED": "vitesse",
|
||||
"SPORT": "sport | sports",
|
||||
"START": "début",
|
||||
|
@ -29,6 +29,13 @@ const routes: Array<RouteRecordRaw> = [
|
||||
path: '/workouts/:workoutId',
|
||||
name: 'Workout',
|
||||
component: Workout,
|
||||
props: { displaySegment: false },
|
||||
},
|
||||
{
|
||||
path: '/workouts/:workoutId/segment/:segmentId',
|
||||
name: 'WorkoutSegment',
|
||||
component: Workout,
|
||||
props: { displaySegment: true },
|
||||
},
|
||||
{ path: '/:pathMatch(.*)*', name: 'not-found', component: NotFoundView },
|
||||
]
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
IWorkoutsActions,
|
||||
IWorkoutsState,
|
||||
} from '@/store/modules/workouts/types'
|
||||
import { IWorkoutsPayload } from '@/types/workouts'
|
||||
import { IWorkout, IWorkoutPayload, IWorkoutsPayload } from '@/types/workouts'
|
||||
import { handleError } from '@/utils'
|
||||
|
||||
const getWorkouts = (
|
||||
@ -51,41 +51,58 @@ export const actions: ActionTree<IWorkoutsState, IRootState> &
|
||||
},
|
||||
[WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA](
|
||||
context: ActionContext<IWorkoutsState, IRootState>,
|
||||
workoutId: string
|
||||
payload: IWorkoutPayload
|
||||
): void {
|
||||
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true)
|
||||
const segmentUrl = payload.segmentId ? `/segment/${payload.segmentId}` : ''
|
||||
authApi
|
||||
.get(`workouts/${workoutId}`)
|
||||
.get(`workouts/${payload.workoutId}`)
|
||||
.then((res) => {
|
||||
const workout: IWorkout = res.data.data.workouts[0]
|
||||
if (res.data.status === 'success') {
|
||||
if (
|
||||
payload.segmentId &&
|
||||
(workout.segments.length === 0 ||
|
||||
!workout.segments[+payload.segmentId - 1])
|
||||
) {
|
||||
throw new Error('WORKOUT_NOT_FOUND')
|
||||
}
|
||||
context.commit(
|
||||
WORKOUTS_STORE.MUTATIONS.SET_WORKOUT,
|
||||
res.data.data.workouts[0]
|
||||
)
|
||||
if (res.data.data.workouts[0].with_gpx) {
|
||||
authApi.get(`workouts/${workoutId}/chart_data`).then((res) => {
|
||||
if (res.data.status === 'success') {
|
||||
context.commit(
|
||||
WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_CHART_DATA,
|
||||
res.data.data.chart_data
|
||||
)
|
||||
}
|
||||
})
|
||||
authApi.get(`workouts/${workoutId}/gpx`).then((res) => {
|
||||
if (res.data.status === 'success') {
|
||||
context.commit(
|
||||
WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_GPX,
|
||||
res.data.data.gpx
|
||||
)
|
||||
}
|
||||
})
|
||||
authApi
|
||||
.get(`workouts/${payload.workoutId}/chart_data${segmentUrl}`)
|
||||
.then((res) => {
|
||||
if (res.data.status === 'success') {
|
||||
context.commit(
|
||||
WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_CHART_DATA,
|
||||
res.data.data.chart_data
|
||||
)
|
||||
}
|
||||
})
|
||||
authApi
|
||||
.get(`workouts/${payload.workoutId}/gpx${segmentUrl}`)
|
||||
.then((res) => {
|
||||
if (res.data.status === 'success') {
|
||||
context.commit(
|
||||
WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_GPX,
|
||||
res.data.data.gpx
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
context.commit(WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUT)
|
||||
handleError(context, null)
|
||||
}
|
||||
})
|
||||
.catch((error) => handleError(context, error))
|
||||
.catch((error) => {
|
||||
context.commit(WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUT)
|
||||
handleError(context, error)
|
||||
})
|
||||
.finally(() =>
|
||||
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, false)
|
||||
)
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
IWorkoutApiChartData,
|
||||
IWorkoutsPayload,
|
||||
IWorkoutData,
|
||||
IWorkoutPayload,
|
||||
} from '@/types/workouts'
|
||||
|
||||
export interface IWorkoutsState {
|
||||
@ -31,7 +32,7 @@ export interface IWorkoutsActions {
|
||||
): void
|
||||
[WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA](
|
||||
context: ActionContext<IWorkoutsState, IRootState>,
|
||||
workoutId: string | string[]
|
||||
payload: IWorkoutPayload
|
||||
): void
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,35 @@ export interface IWorkout {
|
||||
workout_date: string
|
||||
}
|
||||
|
||||
export interface IWorkoutObject {
|
||||
ascent: number | null
|
||||
aveSpeed: number
|
||||
descent: number | null
|
||||
distance: number
|
||||
duration: string
|
||||
maxAlt: number | null
|
||||
maxSpeed: number
|
||||
minAlt: number | null
|
||||
moving: string
|
||||
nextUrl: string | null
|
||||
pauses: string | null
|
||||
previousUrl: string | null
|
||||
records: IRecord[]
|
||||
segmentId: number | null
|
||||
title: string
|
||||
type: string
|
||||
workoutDate: string
|
||||
weatherEnd: IWeather | null
|
||||
workoutId: string
|
||||
weatherStart: IWeather | null
|
||||
workoutTime: string
|
||||
}
|
||||
|
||||
export interface IWorkoutPayload {
|
||||
workoutId: string | string[]
|
||||
segmentId?: string | string[]
|
||||
}
|
||||
|
||||
export interface IWorkoutsPayload {
|
||||
from?: string
|
||||
to?: string
|
||||
|
@ -14,6 +14,7 @@
|
||||
:sports="sports"
|
||||
:authUser="authUser"
|
||||
:markerCoordinates="markerCoordinates"
|
||||
:displaySegment="displaySegment"
|
||||
/>
|
||||
<WorkoutChart
|
||||
v-if="
|
||||
@ -21,9 +22,17 @@
|
||||
"
|
||||
:workoutData="workoutData"
|
||||
:authUser="authUser"
|
||||
:displaySegment="displaySegment"
|
||||
@getCoordinates="updateCoordinates"
|
||||
/>
|
||||
<WorkoutNotes :notes="workoutData.workout.notes" />
|
||||
<WorkoutSegments
|
||||
v-if="!displaySegment && workoutData.workout.segments.length > 1"
|
||||
:segments="workoutData.workout.segments"
|
||||
></WorkoutSegments>
|
||||
<WorkoutNotes
|
||||
v-if="!displaySegment"
|
||||
:notes="workoutData.workout.notes"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<NotFound target="WORKOUT" />
|
||||
@ -40,6 +49,7 @@
|
||||
computed,
|
||||
defineComponent,
|
||||
ref,
|
||||
watch,
|
||||
onBeforeMount,
|
||||
onUnmounted,
|
||||
} from 'vue'
|
||||
@ -50,9 +60,10 @@
|
||||
import WorkoutChart from '@/components/Workout/WorkoutChart/index.vue'
|
||||
import WorkoutDetail from '@/components/Workout/WorkoutDetail/index.vue'
|
||||
import WorkoutNotes from '@/components/Workout/WorkoutNotes.vue'
|
||||
import WorkoutSegments from '@/components/Workout/WorkoutSegments.vue'
|
||||
import { SPORTS_STORE, USER_STORE, WORKOUTS_STORE } from '@/store/constants'
|
||||
import { IAuthUserProfile } from '@/types/user'
|
||||
import { IWorkoutData, TCoordinates } from '@/types/workouts'
|
||||
import { IWorkoutData, IWorkoutPayload, TCoordinates } from '@/types/workouts'
|
||||
import { useStore } from '@/use/useStore'
|
||||
|
||||
export default defineComponent({
|
||||
@ -63,16 +74,25 @@
|
||||
WorkoutChart,
|
||||
WorkoutDetail,
|
||||
WorkoutNotes,
|
||||
WorkoutSegments,
|
||||
},
|
||||
setup() {
|
||||
props: {
|
||||
displaySegment: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const route = useRoute()
|
||||
const store = useStore()
|
||||
onBeforeMount(() =>
|
||||
store.dispatch(
|
||||
WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA,
|
||||
route.params.workoutId
|
||||
)
|
||||
)
|
||||
|
||||
onBeforeMount(() => {
|
||||
const payload: IWorkoutPayload = { workoutId: route.params.workoutId }
|
||||
if (props.displaySegment) {
|
||||
payload.segmentId = route.params.segmentId
|
||||
}
|
||||
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, payload)
|
||||
})
|
||||
|
||||
const workoutData: ComputedRef<IWorkoutData> = computed(
|
||||
() => store.getters[WORKOUTS_STORE.GETTERS.WORKOUT_DATA]
|
||||
@ -93,9 +113,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.workoutId,
|
||||
async (newWorkoutId) => {
|
||||
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, {
|
||||
workoutId: newWorkoutId,
|
||||
})
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => route.params.segmentId,
|
||||
async (newSegmentId) => {
|
||||
const payload: IWorkoutPayload = {
|
||||
workoutId: route.params.workoutId,
|
||||
}
|
||||
if (newSegmentId) {
|
||||
payload.segmentId = newSegmentId
|
||||
}
|
||||
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, payload)
|
||||
}
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
store.commit(WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUT)
|
||||
})
|
||||
|
||||
return {
|
||||
authUser,
|
||||
markerCoordinates,
|
||||
|
Loading…
Reference in New Issue
Block a user