Client - refactoring & display fix

This commit is contained in:
Sam 2021-09-27 11:49:17 +02:00
parent b7b2eb0daf
commit 42c16a9680
11 changed files with 220 additions and 158 deletions

View File

@ -38,7 +38,7 @@
<script lang="ts">
import { ChartData, ChartOptions } from 'chart.js'
import { PropType, defineComponent, ref, ComputedRef, computed } from 'vue'
import { ComputedRef, PropType, computed, defineComponent, ref } from 'vue'
import { LineChart, useLineChart } from 'vue-chart-3'
import { useI18n } from 'vue-i18n'
@ -70,6 +70,7 @@
emits: ['getCoordinates'],
setup(props, { emit }) {
const { t } = useI18n()
let displayDistance = ref(true)
const datasets: ComputedRef<IWorkoutChartData> = computed(() =>
getDatasets(props.workoutData.chartData, t)
@ -90,6 +91,7 @@
)
const options = computed<ChartOptions<'line'>>(() => ({
responsive: true,
maintainAspectRatio: true,
animation: false,
layout: {
padding: {
@ -221,7 +223,6 @@
width: 100%;
display: flex;
justify-content: center;
label {
padding: 0 $default-padding;
}
@ -231,6 +232,15 @@
font-style: italic;
}
}
@media screen and (max-width: $small-limit) {
.card-content {
padding: $default-padding 0;
.no-data-cleaning {
padding: 0 $default-padding * 2;
}
}
}
}
}
</style>

View File

@ -0,0 +1,134 @@
<template>
<div id="workout-card-title">
<div
class="workout-previous workout-arrow"
:class="{ inactive: !workoutObject.previousUrl }"
:title="
workoutObject.previousUrl
? t(`workouts.PREVIOUS_${workoutObject.type}`)
: t(`workouts.NO_PREVIOUS_${workoutObject.type}`)
"
@click="
workoutObject.previousUrl
? $router.push(workoutObject.previousUrl)
: null
"
>
<i class="fa fa-chevron-left" aria-hidden="true" />
</div>
<div class="workout-card-title">
<div class="sport-img">
<img alt="workout sport logo" :src="sport.img" />
</div>
<div class="workout-title-date">
<div class="workout-title" v-if="workoutObject.type === 'WORKOUT'">
{{ workoutObject.title }}
</div>
<div class="workout-title" v-else>
{{ workoutObject.title }}
<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">
{{ workoutObject.workoutDate }} -
{{ workoutObject.workoutTime }}
<span class="workout-link">
<router-link
v-if="workoutObject.type === 'SEGMENT'"
:to="{
name: 'Workout',
params: { workoutId: workoutObject.workoutId },
}"
>
> {{ t('workouts.BACK_TO_WORKOUT') }}
</router-link></span
>
</div>
</div>
</div>
<div
class="workout-next workout-arrow"
:class="{ inactive: !workoutObject.nextUrl }"
:title="
workoutObject.nextUrl
? t(`workouts.NEXT_${workoutObject.type}`)
: t(`workouts.NO_NEXT_${workoutObject.type}`)
"
@click="
workoutObject.nextUrl ? $router.push(workoutObject.nextUrl) : null
"
>
<i class="fa fa-chevron-right" aria-hidden="true" />
</div>
</div>
</template>
<script lang="ts">
import { PropType, defineComponent } from 'vue'
import { useI18n } from 'vue-i18n'
import { ISport } from '@/types/sports'
import { IWorkoutObject } from '@/types/workouts'
export default defineComponent({
name: 'WorkoutCardTitle',
props: {
sport: {
type: Object as PropType<ISport>,
required: true,
},
workoutObject: {
type: Object as PropType<IWorkoutObject>,
required: true,
},
},
setup() {
const { t } = useI18n()
return { t }
},
})
</script>
<style lang="scss" scoped>
@import '~@/scss/base';
#workout-card-title {
display: flex;
justify-content: space-between;
align-items: center;
.workout-arrow {
cursor: pointer;
&.inactive {
color: var(--disabled-color);
cursor: default;
}
}
.workout-card-title {
display: flex;
flex-grow: 1;
.sport-img {
img {
height: 35px;
width: 35px;
padding: 0 $default-padding;
}
}
.workout-date {
font-size: 0.8em;
font-weight: normal;
}
.workout-segment {
font-weight: normal;
}
.workout-link {
padding-left: $default-padding;
}
}
}
</style>

View File

@ -48,12 +48,13 @@
</template>
<script lang="ts">
import { PropType, defineComponent, computed } from 'vue'
import { PropType, computed, defineComponent } from 'vue'
import { useI18n } from 'vue-i18n'
import WorkoutRecord from '@/components/Workout/WorkoutDetail/WorkoutRecord.vue'
import WorkoutWeather from '@/components/Workout/WorkoutDetail/WorkoutWeather.vue'
import { IWorkoutObject } from '@/types/workouts'
export default defineComponent({
name: 'WorkoutData',
components: {

View File

@ -1,34 +1,30 @@
<template>
<div id="workout-map">
<div
class="leaflet-container"
v-if="
workoutData.workout.with_gpx &&
geoJson.jsonData &&
center &&
bounds.length === 2
"
>
<LMap
:zoom="options.zoom"
:center="center"
:bounds="bounds"
ref="workoutMap"
@ready="fitBounds(bounds)"
>
<LTileLayer
:url="`${getApiUrl()}workouts/map_tile/{s}/{z}/{x}/{y}.png`"
:attribution="appConfig.map_attribution"
<div v-if="workoutData.loading" class="leaflet-container" />
<div v-else>
<div class="leaflet-container" v-if="workoutData.workout.with_gpx">
<LMap
v-if="geoJson.jsonData && center && bounds.length === 2"
:zoom="options.zoom"
:center="center"
:bounds="bounds"
/>
<LGeoJson :geojson="geoJson.jsonData" />
<LMarker
v-if="markerCoordinates.latitude"
:lat-lng="[markerCoordinates.latitude, markerCoordinates.longitude]"
/>
</LMap>
ref="workoutMap"
@ready="fitBounds(bounds)"
>
<LTileLayer
:url="`${getApiUrl()}workouts/map_tile/{s}/{z}/{x}/{y}.png`"
:attribution="appConfig.map_attribution"
:bounds="bounds"
/>
<LGeoJson :geojson="geoJson.jsonData" />
<LMarker
v-if="markerCoordinates.latitude"
:lat-lng="[markerCoordinates.latitude, markerCoordinates.longitude]"
/>
</LMap>
</div>
<div v-else class="no-map">{{ t('workouts.NO_MAP') }}</div>
</div>
<div v-else class="no-map">{{ t('workouts.NO_MAP') }}</div>
</div>
</template>
@ -39,6 +35,7 @@
import { useI18n } from 'vue-i18n'
import { ROOT_STORE } from '@/store/constants'
import { IAppConfig } from '@/types/application'
import { GeoJSONData } from '@/types/geojson'
import { IWorkoutData, TCoordinates } from '@/types/workouts'
import { useStore } from '@/use/useStore'
@ -64,9 +61,7 @@
setup(props) {
const { t } = useI18n()
const store = useStore()
const workoutMap = ref<null | {
leafletObject: { fitBounds: (bounds: number[][]) => null }
}>(null)
function getGeoJson(gpxContent: string): GeoJSONData {
if (!gpxContent || gpxContent !== '') {
try {
@ -93,6 +88,9 @@
}
}
const workoutMap = ref<null | {
leafletObject: { fitBounds: (bounds: number[][]) => null }
}>(null)
const bounds = computed(() =>
props.workoutData
? [
@ -107,7 +105,7 @@
]
: []
)
const appConfig = computed(
const appConfig: ComputedRef<IAppConfig> = computed(
() => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]
)

View File

@ -16,17 +16,28 @@
import { defineComponent, PropType } from 'vue'
import { IWorkoutObject } from '@/types/workouts'
export default defineComponent({
name: 'WorkoutRecord',
props: {
workoutObject: {
type: Object as PropType<IWorkoutObject>,
required: true,
},
record_type: {
type: String,
required: true,
},
workoutObject: {
type: Object as PropType<IWorkoutObject>,
required: true,
},
},
})
</script>
<style lang="scss" scoped>
@import '~@/scss/base';
.workout-record {
sup {
font-size: 75%;
line-height: 0;
}
}
</style>

View File

@ -2,66 +2,7 @@
<div class="workout-detail">
<Card :without-title="false">
<template #title>
<div
class="workout-previous workout-arrow"
:class="{ inactive: !workoutObject.previousUrl }"
:title="
workoutObject.previousUrl
? t(`workouts.PREVIOUS_${workoutObject.type}`)
: t(`workouts.NO_PREVIOUS_${workoutObject.type}`)
"
@click="
workoutObject.previousUrl
? $router.push(workoutObject.previousUrl)
: null
"
>
<i class="fa fa-chevron-left" aria-hidden="true" />
</div>
<div class="workout-card-title">
<div class="sport-img">
<img alt="workout sport logo" :src="sport.img" />
</div>
<div class="workout-title-date">
<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">
{{ workoutObject.workoutDate }} -
{{ workoutObject.workoutTime }}
</div>
</div>
</div>
<div
class="workout-next workout-arrow"
:class="{ inactive: !workoutObject.nextUrl }"
:title="
workoutObject.nextUrl
? t(`workouts.NEXT_${workoutObject.type}`)
: t(`workouts.NO_NEXT_${workoutObject.type}`)
"
@click="
workoutObject.nextUrl ? $router.push(workoutObject.nextUrl) : null
"
>
<i class="fa fa-chevron-right" aria-hidden="true" />
</div>
<WorkoutCardTitle :sport="sport" :workoutObject="workoutObject" />
</template>
<template #content>
<WorkoutMap
@ -79,8 +20,8 @@
ComputedRef,
PropType,
Ref,
defineComponent,
computed,
defineComponent,
ref,
watch,
} from 'vue'
@ -88,6 +29,7 @@
import { useRoute } from 'vue-router'
import Card from '@/components/Common/Card.vue'
import WorkoutCardTitle from '@/components/Workout/WorkoutDetail/WorkoutCardTitle.vue'
import WorkoutData from '@/components/Workout/WorkoutDetail/WorkoutData.vue'
import WorkoutMap from '@/components/Workout/WorkoutDetail/WorkoutMap.vue'
import { ISport } from '@/types/sports'
@ -105,6 +47,7 @@
name: 'WorkoutDetail',
components: {
Card,
WorkoutCardTitle,
WorkoutData,
WorkoutMap,
},
@ -113,6 +56,10 @@
type: Object as PropType<IAuthUserProfile>,
required: true,
},
displaySegment: {
type: Boolean,
required: true,
},
markerCoordinates: {
type: Object as PropType<TCoordinates>,
required: false,
@ -124,10 +71,6 @@
type: Object as PropType<IWorkoutData>,
required: true,
},
displaySegment: {
type: Boolean,
required: true,
},
},
setup(props) {
const route = useRoute()
@ -239,38 +182,6 @@
display: flex;
::v-deep(.card) {
width: 100%;
.card-title {
display: flex;
justify-content: space-between;
align-items: center;
.workout-arrow {
cursor: pointer;
&.inactive {
color: var(--disabled-color);
cursor: default;
}
}
.workout-card-title {
display: flex;
flex-grow: 1;
.sport-img {
img {
height: 35px;
width: 35px;
padding: 0 $default-padding;
}
}
.workout-date {
font-size: 0.8em;
font-weight: normal;
}
.workout-segment {
font-style: italic;
font-weight: normal;
}
}
}
.card-content {
display: flex;
flex-direction: row;

View File

@ -35,7 +35,6 @@
<style lang="scss" scoped>
@import '~@/scss/base.scss';
#workout-note {
::v-deep(.card-content) {
font-style: italic;

View File

@ -25,7 +25,7 @@
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue'
import { PropType, defineComponent } from 'vue'
import { useI18n } from 'vue-i18n'
import Card from '@/components/Common/Card.vue'

View File

@ -3,6 +3,7 @@
"ANALYSIS": "analysis",
"ASCENT": "ascent",
"AVERAGE_SPEED": "average speed",
"BACK_TO_WORKOUT": "back to workout",
"DESCENT": "descent",
"DISTANCE": "distance",
"DURATION": "duration",

View File

@ -3,6 +3,7 @@
"ANALYSIS": "analyse",
"ASCENT": "dénivelé positif",
"AVERAGE_SPEED": "vitesse moyenne",
"BACK_TO_WORKOUT": "revenir à la séance",
"DESCENT": "dénivelé négatif",
"DISTANCE": "distance",
"DURATION": "durée",

View File

@ -1,15 +1,9 @@
<template>
<div id="workout">
<div class="container">
<div class="workout-loading" v-if="workoutData.loading">
<div class="loading">
<Loader />
</div>
</div>
<div v-else class="workout-container">
<div class="workout-container" v-if="sports.length > 0">
<div v-if="workoutData.workout.id">
<WorkoutDetail
v-if="sports.length > 0"
:workoutData="workoutData"
:sports="sports"
:authUser="authUser"
@ -28,14 +22,14 @@
<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" />
<NotFound v-if="!workoutData.loading" target="WORKOUT" />
</div>
</div>
</div>
@ -55,9 +49,8 @@
} from 'vue'
import { useRoute } from 'vue-router'
import Loader from '@/components/Common/Loader.vue'
import NotFound from '@/components/Common/NotFound.vue'
import WorkoutChart from '@/components/Workout/WorkoutChart/index.vue'
import WorkoutChart from '@/components/Workout/WorkoutChart.vue'
import WorkoutDetail from '@/components/Workout/WorkoutDetail/index.vue'
import WorkoutNotes from '@/components/Workout/WorkoutNotes.vue'
import WorkoutSegments from '@/components/Workout/WorkoutSegments.vue'
@ -69,7 +62,6 @@
export default defineComponent({
name: 'Workout',
components: {
Loader,
NotFound,
WorkoutChart,
WorkoutDetail,
@ -116,21 +108,25 @@
watch(
() => route.params.workoutId,
async (newWorkoutId) => {
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, {
workoutId: newWorkoutId,
})
if (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 (route.params.workoutId) {
const payload: IWorkoutPayload = {
workoutId: route.params.workoutId,
}
if (newSegmentId) {
payload.segmentId = newSegmentId
}
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, payload)
}
if (newSegmentId) {
payload.segmentId = newSegmentId
}
store.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, payload)
}
)