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"> <script lang="ts">
import { ChartData, ChartOptions } from 'chart.js' 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 { LineChart, useLineChart } from 'vue-chart-3'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@ -70,6 +70,7 @@
emits: ['getCoordinates'], emits: ['getCoordinates'],
setup(props, { emit }) { setup(props, { emit }) {
const { t } = useI18n() const { t } = useI18n()
let displayDistance = ref(true) let displayDistance = ref(true)
const datasets: ComputedRef<IWorkoutChartData> = computed(() => const datasets: ComputedRef<IWorkoutChartData> = computed(() =>
getDatasets(props.workoutData.chartData, t) getDatasets(props.workoutData.chartData, t)
@ -90,6 +91,7 @@
) )
const options = computed<ChartOptions<'line'>>(() => ({ const options = computed<ChartOptions<'line'>>(() => ({
responsive: true, responsive: true,
maintainAspectRatio: true,
animation: false, animation: false,
layout: { layout: {
padding: { padding: {
@ -221,7 +223,6 @@
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
label { label {
padding: 0 $default-padding; padding: 0 $default-padding;
} }
@ -231,6 +232,15 @@
font-style: italic; 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> </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> </template>
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent, computed } from 'vue' import { PropType, computed, defineComponent } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import WorkoutRecord from '@/components/Workout/WorkoutDetail/WorkoutRecord.vue' import WorkoutRecord from '@/components/Workout/WorkoutDetail/WorkoutRecord.vue'
import WorkoutWeather from '@/components/Workout/WorkoutDetail/WorkoutWeather.vue' import WorkoutWeather from '@/components/Workout/WorkoutDetail/WorkoutWeather.vue'
import { IWorkoutObject } from '@/types/workouts' import { IWorkoutObject } from '@/types/workouts'
export default defineComponent({ export default defineComponent({
name: 'WorkoutData', name: 'WorkoutData',
components: { components: {

View File

@ -1,15 +1,10 @@
<template> <template>
<div id="workout-map"> <div id="workout-map">
<div <div v-if="workoutData.loading" class="leaflet-container" />
class="leaflet-container" <div v-else>
v-if=" <div class="leaflet-container" v-if="workoutData.workout.with_gpx">
workoutData.workout.with_gpx &&
geoJson.jsonData &&
center &&
bounds.length === 2
"
>
<LMap <LMap
v-if="geoJson.jsonData && center && bounds.length === 2"
:zoom="options.zoom" :zoom="options.zoom"
:center="center" :center="center"
:bounds="bounds" :bounds="bounds"
@ -30,6 +25,7 @@
</div> </div>
<div v-else class="no-map">{{ t('workouts.NO_MAP') }}</div> <div v-else class="no-map">{{ t('workouts.NO_MAP') }}</div>
</div> </div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -39,6 +35,7 @@
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { ROOT_STORE } from '@/store/constants' import { ROOT_STORE } from '@/store/constants'
import { IAppConfig } from '@/types/application'
import { GeoJSONData } from '@/types/geojson' import { GeoJSONData } from '@/types/geojson'
import { IWorkoutData, TCoordinates } from '@/types/workouts' import { IWorkoutData, TCoordinates } from '@/types/workouts'
import { useStore } from '@/use/useStore' import { useStore } from '@/use/useStore'
@ -64,9 +61,7 @@
setup(props) { setup(props) {
const { t } = useI18n() const { t } = useI18n()
const store = useStore() const store = useStore()
const workoutMap = ref<null | {
leafletObject: { fitBounds: (bounds: number[][]) => null }
}>(null)
function getGeoJson(gpxContent: string): GeoJSONData { function getGeoJson(gpxContent: string): GeoJSONData {
if (!gpxContent || gpxContent !== '') { if (!gpxContent || gpxContent !== '') {
try { try {
@ -93,6 +88,9 @@
} }
} }
const workoutMap = ref<null | {
leafletObject: { fitBounds: (bounds: number[][]) => null }
}>(null)
const bounds = computed(() => const bounds = computed(() =>
props.workoutData props.workoutData
? [ ? [
@ -107,7 +105,7 @@
] ]
: [] : []
) )
const appConfig = computed( const appConfig: ComputedRef<IAppConfig> = computed(
() => store.getters[ROOT_STORE.GETTERS.APP_CONFIG] () => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]
) )

View File

@ -16,17 +16,28 @@
import { defineComponent, PropType } from 'vue' import { defineComponent, PropType } from 'vue'
import { IWorkoutObject } from '@/types/workouts' import { IWorkoutObject } from '@/types/workouts'
export default defineComponent({ export default defineComponent({
name: 'WorkoutRecord', name: 'WorkoutRecord',
props: { props: {
workoutObject: {
type: Object as PropType<IWorkoutObject>,
required: true,
},
record_type: { record_type: {
type: String, type: String,
required: true, required: true,
}, },
workoutObject: {
type: Object as PropType<IWorkoutObject>,
required: true,
},
}, },
}) })
</script> </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"> <div class="workout-detail">
<Card :without-title="false"> <Card :without-title="false">
<template #title> <template #title>
<div <WorkoutCardTitle :sport="sport" :workoutObject="workoutObject" />
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>
</template> </template>
<template #content> <template #content>
<WorkoutMap <WorkoutMap
@ -79,8 +20,8 @@
ComputedRef, ComputedRef,
PropType, PropType,
Ref, Ref,
defineComponent,
computed, computed,
defineComponent,
ref, ref,
watch, watch,
} from 'vue' } from 'vue'
@ -88,6 +29,7 @@
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import Card from '@/components/Common/Card.vue' import Card from '@/components/Common/Card.vue'
import WorkoutCardTitle from '@/components/Workout/WorkoutDetail/WorkoutCardTitle.vue'
import WorkoutData from '@/components/Workout/WorkoutDetail/WorkoutData.vue' import WorkoutData from '@/components/Workout/WorkoutDetail/WorkoutData.vue'
import WorkoutMap from '@/components/Workout/WorkoutDetail/WorkoutMap.vue' import WorkoutMap from '@/components/Workout/WorkoutDetail/WorkoutMap.vue'
import { ISport } from '@/types/sports' import { ISport } from '@/types/sports'
@ -105,6 +47,7 @@
name: 'WorkoutDetail', name: 'WorkoutDetail',
components: { components: {
Card, Card,
WorkoutCardTitle,
WorkoutData, WorkoutData,
WorkoutMap, WorkoutMap,
}, },
@ -113,6 +56,10 @@
type: Object as PropType<IAuthUserProfile>, type: Object as PropType<IAuthUserProfile>,
required: true, required: true,
}, },
displaySegment: {
type: Boolean,
required: true,
},
markerCoordinates: { markerCoordinates: {
type: Object as PropType<TCoordinates>, type: Object as PropType<TCoordinates>,
required: false, required: false,
@ -124,10 +71,6 @@
type: Object as PropType<IWorkoutData>, type: Object as PropType<IWorkoutData>,
required: true, required: true,
}, },
displaySegment: {
type: Boolean,
required: true,
},
}, },
setup(props) { setup(props) {
const route = useRoute() const route = useRoute()
@ -239,38 +182,6 @@
display: flex; display: flex;
::v-deep(.card) { ::v-deep(.card) {
width: 100%; 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 { .card-content {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

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

View File

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

View File

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

View File

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

View File

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