Client - refacto + display fix and improvements

This commit is contained in:
Sam 2021-10-02 16:16:58 +02:00
parent 3d56eb3c93
commit 60a5df70a9
38 changed files with 140 additions and 127 deletions

View File

@ -1,20 +1,13 @@
<template> <template>
<div id="about"> <div id="about">
<img class="bike-img" v-bind:src="bike_image_url" alt="mountain bike" /> <img class="bike-img" :src="'/img/bike.svg'" alt="mountain bike" />
</div> </div>
</template> </template>
<script lang="ts"> <script>
import { defineComponent } from 'vue' export default {
export default defineComponent({
name: 'About', name: 'About',
setup() {
return {
bike_image_url: './img/bike.svg',
} }
},
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -15,9 +15,7 @@
}, },
setup() { setup() {
const { t } = useI18n() const { t } = useI18n()
return { return { t }
t,
}
}, },
}) })
</script> </script>

View File

@ -16,7 +16,7 @@
props: { props: {
withoutTitle: { withoutTitle: {
type: Boolean, type: Boolean,
default: true, default: false,
}, },
}, },
}) })

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="dropdown-wrapper"> <div class="dropdown-wrapper">
<div class="dropdown-selected" @click="openDropdown"> <div class="dropdown-selected" @click="toggleDropdown">
<slot></slot> <slot></slot>
</div> </div>
<ul class="dropdown-list" v-if="isOpen"> <ul class="dropdown-list" v-if="isOpen">
@ -41,25 +41,19 @@
let isOpen = ref(false) let isOpen = ref(false)
let dropdownOptions = props.options.map((option) => option) let dropdownOptions = props.options.map((option) => option)
function openDropdown() { function toggleDropdown() {
isOpen.value = true isOpen.value = !isOpen.value
} }
function updateSelected(option: IDropdownOption) { function updateSelected(option: IDropdownOption) {
emit('selected', option) emit('selected', option)
isOpen.value = false isOpen.value = false
} }
function getSelectedLabel(selectedValue: string) {
return props.options.filter(
(option: IDropdownOption) => option.value === selectedValue
)[0].label
}
return { return {
dropdownOptions, dropdownOptions,
updateSelected,
getSelectedLabel,
isOpen, isOpen,
openDropdown, toggleDropdown,
updateSelected,
} }
}, },
}) })

View File

@ -1,5 +1,5 @@
<template> <template>
<div id="error404"> <div id="error">
<div class="error-content"> <div class="error-content">
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<p>{{ message }}</p> <p>{{ message }}</p>
@ -37,7 +37,7 @@
<style scoped lang="scss"> <style scoped lang="scss">
@import '~@/scss/base'; @import '~@/scss/base';
#error404 { #error {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="error-message"> <div class="error-message">
<ul v-if="Array.isArray(message)"> <ul v-if="Array.isArray(message)">
<li v-for="(submessage, index) in message" :key="index"> <li v-for="(subMessage, index) in message" :key="index">
{{ t(submessage) }} {{ t(subMessage) }}
</li> </li>
</ul> </ul>
<div v-else>{{ t(message) }}</div> <div v-else>{{ t(message) }}</div>
@ -20,9 +20,7 @@
}, },
setup() { setup() {
const { t } = useI18n() const { t } = useI18n()
return { return { t }
t,
}
}, },
}) })
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div id="modal"> <div id="modal">
<div class="custom-modal"> <div class="custom-modal">
<Card :without-title="false"> <Card>
<template #title> <template #title>
{{ title }} {{ title }}
</template> </template>
@ -79,6 +79,15 @@
margin: 25% auto; margin: 25% auto;
z-index: 1250; z-index: 1250;
@media screen and (max-width: $medium-limit) {
margin: 15% auto;
width: 100%;
}
@media screen and (max-width: $small-limit) {
margin: 50% 0;
width: 100%;
}
::v-deep(.card) { ::v-deep(.card) {
border: 0; border: 0;
margin: 0; margin: 0;
@ -102,12 +111,5 @@
} }
} }
} }
@media screen and (max-width: $small-limit) {
.custom-modal {
margin: 50% 0;
width: 100%;
}
}
} }
</style> </style>

View File

@ -13,7 +13,7 @@
import Error from '@/components/Common/Error.vue' import Error from '@/components/Common/Error.vue'
export default defineComponent({ export default defineComponent({
name: 'NotFoundView', name: 'NotFound',
components: { components: {
Error, Error,
}, },

View File

@ -17,12 +17,12 @@
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import CyclingSport from '@/components/Common/Sports/CyclingSport.vue' import CyclingSport from '@/components/Common/SportImage/CyclingSport.vue'
import CyclingTransport from '@/components/Common/Sports/CyclingTransport.vue' import CyclingTransport from '@/components/Common/SportImage/CyclingTransport.vue'
import Hiking from '@/components/Common/Sports/Hiking.vue' import Hiking from '@/components/Common/SportImage/Hiking.vue'
import MountainBiking from '@/components/Common/Sports/MountainBiking.vue' import MountainBiking from '@/components/Common/SportImage/MountainBiking.vue'
import Running from '@/components/Common/Sports/Running.vue' import Running from '@/components/Common/SportImage/Running.vue'
import Walking from '@/components/Common/Sports/Walking.vue' import Walking from '@/components/Common/SportImage/Walking.vue'
import { sportColors } from '@/utils/sports' import { sportColors } from '@/utils/sports'
export default defineComponent({ export default defineComponent({

View File

@ -10,7 +10,7 @@
<span class="map-attribution-text">©</span> <span class="map-attribution-text">©</span>
<a <a
class="map-attribution-text" class="map-attribution-text"
href="http://www.openstreetmap.org/copyright" href="https://www.openstreetmap.org/copyright"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >

View File

@ -35,11 +35,11 @@
}, },
setup(props) { setup(props) {
const { t } = useI18n() const { t } = useI18n()
// eslint-disable-next-line // eslint-disable-next-line @typescript-eslint/no-explicit-any
function getNumber(value: any): number { function getNumber(value: any): number {
return isNaN(value) ? 0 : +value return isNaN(value) ? 0 : +value
} }
// eslint-disable-next-line // eslint-disable-next-line @typescript-eslint/no-explicit-any
function getSum(total: any, value: any): number { function getSum(total: any, value: any): number {
return getNumber(total) + getNumber(value) return getNumber(total) + getNumber(value)
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="timeline-workout"> <div class="timeline-workout">
<Card> <Card :without-title="true">
<template #content> <template #content>
<div class="workout-user-date"> <div class="workout-user-date">
<div class="workout-user"> <div class="workout-user">
@ -83,7 +83,7 @@
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import Card from '@/components/Common/Card.vue' import Card from '@/components/Common/Card.vue'
import SportImage from '@/components/Common/Sports/SportImage.vue' import SportImage from '@/components/Common/SportImage/index.vue'
import StaticMap from '@/components/Common/StaticMap.vue' import StaticMap from '@/components/Common/StaticMap.vue'
import { ROOT_STORE } from '@/store/constants' import { ROOT_STORE } from '@/store/constants'
import { ISport } from '@/types/sports' import { ISport } from '@/types/sports'

View File

@ -1,6 +1,14 @@
<template> <template>
<div id="timeline"> <div id="timeline">
<div class="section-title">{{ t('workouts.LATEST_WORKOUTS') }}</div> <div class="section-title">{{ t('workouts.LATEST_WORKOUTS') }}</div>
<div v-if="user.nb_workouts > 0 && workouts.length === 0">
<WorkoutCard
v-for="index in [...Array(initWorkoutsCount).keys()]"
:user="user"
:key="index"
/>
</div>
<div v-else>
<WorkoutCard <WorkoutCard
v-for="workout in workouts" v-for="workout in workouts"
:workout="workout" :workout="workout"
@ -21,6 +29,7 @@
</button> </button>
</div> </div>
</div> </div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -56,12 +65,14 @@
required: true, required: true,
}, },
}, },
setup() { setup(props) {
const store = useStore() const store = useStore()
const { t } = useI18n() const { t } = useI18n()
let page = ref(1) let page = ref(1)
const per_page = 5 const per_page = 5
const initWorkoutsCount =
props.user.nb_workouts >= per_page ? per_page : props.user.nb_workouts
onBeforeMount(() => loadWorkouts()) onBeforeMount(() => loadWorkouts())
const workouts: ComputedRef<IWorkout[]> = computed( const workouts: ComputedRef<IWorkout[]> = computed(
@ -85,6 +96,7 @@
} }
return { return {
initWorkoutsCount,
moreWorkoutsExist, moreWorkoutsExist,
per_page, per_page,
workouts, workouts,

View File

@ -23,6 +23,7 @@
<script lang="ts"> <script lang="ts">
import { format } from 'date-fns' import { format } from 'date-fns'
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
export default defineComponent({ export default defineComponent({
name: 'CalendarHeader', name: 'CalendarHeader',
props: { props: {

View File

@ -25,11 +25,11 @@
import { defineComponent, PropType } from 'vue' import { defineComponent, PropType } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import SportImage from '@/components/Common/Sports/SportImage.vue' import SportImage from '@/components/Common/SportImage/index.vue'
import { IWorkout } from '@/types/workouts' import { IWorkout } from '@/types/workouts'
export default defineComponent({ export default defineComponent({
name: 'CalendarWorkouts', name: 'CalendarWorkout',
components: { components: {
SportImage, SportImage,
}, },

View File

@ -1,6 +1,6 @@
<template> <template>
<div id="user-calendar"> <div id="user-calendar">
<Card class="calendar-card"> <Card class="calendar-card" :without-title="true">
<template #content> <template #content>
<CalendarHeader <CalendarHeader
:day="day" :day="day"

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="user-month-stats"> <div class="user-month-stats">
<Card :without-title="false"> <Card>
<template #title>{{ $t('dashboard.THIS_MONTH') }}</template> <template #title>{{ $t('dashboard.THIS_MONTH') }}</template>
<template #content> <template #content>
<div v-if="Object.keys(statistics).length === 0"> <div v-if="Object.keys(statistics).length === 0">

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="records-card"> <div class="records-card">
<Card :without-title="false"> <Card>
<template #title> <template #title>
<SportImage :sport-label="records.label" /> <SportImage :sport-label="records.label" />
{{ sportTranslatedLabel }} {{ sportTranslatedLabel }}
@ -31,7 +31,7 @@
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import Card from '@/components/Common/Card.vue' import Card from '@/components/Common/Card.vue'
import SportImage from '@/components/Common/Sports/SportImage.vue' import SportImage from '@/components/Common/SportImage/index.vue'
import { IRecord } from '@/types/workouts' import { IRecord } from '@/types/workouts'
export default defineComponent({ export default defineComponent({
@ -93,7 +93,7 @@
} }
} }
} }
@media screen and (max-width: $small-limit) { @media screen and (max-width: $medium-limit) {
font-size: 1em; font-size: 1em;
.card-title { .card-title {
font-size: 1em; font-size: 1em;

View File

@ -52,10 +52,7 @@
props.user.timezone props.user.timezone
) )
) )
return { return { recordsBySport, t }
recordsBySport,
t,
}
}, },
}) })
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="user-stat-card"> <div class="user-stat-card">
<Card> <Card :without-title="true">
<template #content> <template #content>
<div class="stat-content"> <div class="stat-content">
<div class="stat-icon"> <div class="stat-icon">

View File

@ -7,6 +7,10 @@
disabled: registration_disabled, disabled: registration_disabled,
}" }"
> >
<AlertMessage
message="user.REGISTER_DISABLED"
v-if="registration_disabled"
/>
<form @submit.prevent="onSubmit(action)"> <form @submit.prevent="onSubmit(action)">
<div class="form-items"> <div class="form-items">
<input <input
@ -48,10 +52,6 @@
</button> </button>
</form> </form>
<ErrorMessage :message="errorMessages" v-if="errorMessages" /> <ErrorMessage :message="errorMessages" v-if="errorMessages" />
<AlertMessage
message="user.REGISTER_DISABLED"
v-if="registration_disabled"
/>
</div> </div>
</div> </div>
</div> </div>
@ -133,9 +133,9 @@
buttonText, buttonText,
errorMessages, errorMessages,
formData, formData,
onSubmit,
registration_disabled, registration_disabled,
router, router,
onSubmit,
} }
}, },
}) })
@ -162,15 +162,16 @@
border-color: var(--disabled-color); border-color: var(--disabled-color);
} }
} }
@media screen and (max-width: $medium-limit) {
margin-top: $default-margin;
width: 100%;
}
} }
@media screen and (max-width: $medium-limit) { @media screen and (max-width: $medium-limit) {
height: auto; height: auto;
margin-bottom: 50px; margin-bottom: 50px;
#user-form {
margin-top: $default-margin;
width: 100%;
}
} }
} }
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<div id="workout-chart"> <div id="workout-chart">
<Card :without-title="false"> <Card>
<template #title>{{ t('workouts.ANALYSIS') }} </template> <template #title>{{ t('workouts.ANALYSIS') }} </template>
<template #content> <template #content>
<div class="chart-radio"> <div class="chart-radio">

View File

@ -84,7 +84,7 @@
import { PropType, defineComponent } from 'vue' import { PropType, defineComponent } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import SportImage from '@/components/Common/Sports/SportImage.vue' import SportImage from '@/components/Common/SportImage/index.vue'
import { ISport } from '@/types/sports' import { ISport } from '@/types/sports'
import { IWorkoutObject } from '@/types/workouts' import { IWorkoutObject } from '@/types/workouts'

View File

@ -28,7 +28,11 @@
class="workout-data" class="workout-data"
v-if="workoutObject.maxAlt !== null && workoutObject.minAlt !== null" v-if="workoutObject.maxAlt !== null && workoutObject.minAlt !== null"
> >
<img class="mountains" src="/img/workouts/mountains.svg" /> <img
class="mountains"
src="/img/workouts/mountains.svg"
:alt="t('workouts.ELEVATION')"
/>
{{ t('workouts.MIN_ALTITUDE') }}: <span>{{ workoutObject.minAlt }} m</span {{ t('workouts.MIN_ALTITUDE') }}: <span>{{ workoutObject.minAlt }} m</span
><br /> ><br />
{{ t('workouts.MAX_ALTITUDE') }}: {{ t('workouts.MAX_ALTITUDE') }}:

View File

@ -5,7 +5,7 @@
<div class="leaflet-container" v-if="workoutData.workout.with_gpx"> <div class="leaflet-container" v-if="workoutData.workout.with_gpx">
<LMap <LMap
v-if="geoJson.jsonData && center && bounds.length === 2" v-if="geoJson.jsonData && center && bounds.length === 2"
:zoom="options.zoom" :zoom="13"
:center="center" :center="center"
:bounds="bounds" :bounds="bounds"
ref="workoutMap" ref="workoutMap"
@ -108,17 +108,18 @@
const appConfig: ComputedRef<IAppConfig> = computed( const appConfig: ComputedRef<IAppConfig> = computed(
() => store.getters[ROOT_STORE.GETTERS.APP_CONFIG] () => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]
) )
const center = computed(() => getCenter(bounds))
return { const geoJson = computed(() =>
appConfig,
bounds: bounds,
center: computed(() => getCenter(bounds)),
geoJson: computed(() =>
props.workoutData && props.workoutData.gpx props.workoutData && props.workoutData.gpx
? getGeoJson(props.workoutData.gpx) ? getGeoJson(props.workoutData.gpx)
: {} : {}
), )
options: { zoom: 13 },
return {
appConfig,
bounds,
center,
geoJson,
t, t,
workoutMap, workoutMap,
fitBounds, fitBounds,

View File

@ -7,7 +7,7 @@
@confirmAction="deleteWorkout(workoutObject.workoutId)" @confirmAction="deleteWorkout(workoutObject.workoutId)"
@cancelAction="updateDisplayModal(false)" @cancelAction="updateDisplayModal(false)"
/> />
<Card :without-title="false"> <Card>
<template #title> <template #title>
<WorkoutCardTitle <WorkoutCardTitle
:sport="sport" :sport="sport"
@ -213,7 +213,7 @@
.card-content { .card-content {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@media screen and (max-width: $small-limit) { @media screen and (max-width: $medium-limit) {
flex-direction: column; flex-direction: column;
} }
} }

View File

@ -1,6 +1,9 @@
<template> <template>
<div id="workout-edition"> <div
<Card :without-title="false"> id="workout-edition"
:class="{ 'center-form': workout && workout.with_gpx }"
>
<Card>
<template #title>{{ <template #title>{{
t(`workouts.${isCreation ? 'ADD' : 'EDIT'}_WORKOUT`) t(`workouts.${isCreation ? 'ADD' : 'EDIT'}_WORKOUT`)
}}</template> }}</template>
@ -424,10 +427,15 @@
#workout-edition { #workout-edition {
margin: 100px auto; margin: 100px auto;
width: 700px; width: 700px;
@media screen and (max-width: $small-limit) { @media screen and (max-width: $medium-limit) {
width: 100%; width: 100%;
margin: 0 auto 50px auto; margin: 0 auto 50px auto;
} }
@media screen and (max-width: $small-limit) {
&.center-form {
margin: 50px auto;
}
}
::v-deep(.card) { ::v-deep(.card) {
.card-title { .card-title {
@ -436,7 +444,7 @@
} }
.card-content { .card-content {
@media screen and (max-width: $small-limit) { @media screen and (max-width: $medium-limit) {
padding: $default-padding 0; padding: $default-padding 0;
} }
@ -454,7 +462,7 @@
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
@media screen and (max-width: $small-limit) { @media screen and (max-width: $medium-limit) {
flex-direction: column; flex-direction: column;
} }
} }
@ -481,7 +489,7 @@
justify-content: space-around; justify-content: space-around;
label { label {
font-weight: normal; font-weight: normal;
@media screen and (max-width: $small-limit) { @media screen and (max-width: $medium-limit) {
font-size: 0.9em; font-size: 0.9em;
} }
} }
@ -513,7 +521,7 @@
padding: $default-padding; padding: $default-padding;
div { div {
display: flex; display: flex;
@media screen and (max-width: $small-limit) { @media screen and (max-width: $medium-limit) {
flex-direction: column; flex-direction: column;
} }
ul { ul {

View File

@ -1,6 +1,6 @@
<template> <template>
<div id="workout-note"> <div id="workout-note">
<Card :without-title="false"> <Card>
<template #title>{{ t('workouts.NOTES') }}</template> <template #title>{{ t('workouts.NOTES') }}</template>
<template #content> <template #content>
{{ notes && notes !== '' ? notes : t('workouts.NO_NOTES') }}</template {{ notes && notes !== '' ? notes : t('workouts.NO_NOTES') }}</template

View File

@ -1,6 +1,6 @@
<template> <template>
<div id="workout-segments"> <div id="workout-segments">
<Card :without-title="false"> <Card>
<template #title>{{ t('workouts.SEGMENT', 2) }}</template> <template #title>{{ t('workouts.SEGMENT', 2) }}</template>
<template #content> <template #content>
<ul> <ul>

View File

@ -1,7 +1,7 @@
<template> <template>
<div id="dashboard" v-if="authUser.username && sports.length > 0"> <div id="dashboard" v-if="authUser.username && sports.length > 0">
<div class="container mobile-menu"> <div class="container mobile-menu">
<Card> <Card :without-title="true">
<template #content> <template #content>
<button <button
class="mobile-menu-item" class="mobile-menu-item"
@ -145,7 +145,7 @@
display: none; display: none;
} }
@media screen and (max-width: $small-limit) { @media screen and (max-width: $medium-limit) {
padding-bottom: 60px; padding-bottom: 60px;
.dashboard-container { .dashboard-container {
display: flex; display: flex;

View File

@ -66,7 +66,3 @@
}, },
}) })
</script> </script>
<style lang="scss" scoped>
@import '~@/scss/base';
</style>

View File

@ -36,28 +36,36 @@
@import '~@/scss/base'; @import '~@/scss/base';
#loginOrRegister { #loginOrRegister {
display: flex;
height: 100%; height: 100%;
.container { .container {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-evenly; justify-content: space-evenly;
height: 100%;
margin-bottom: $default-margin * 2; margin-bottom: $default-margin * 2;
width: 100%;
.container-sub { .container-sub {
min-width: 50%; min-width: 50%;
height: 100%;
}
} }
@media screen and (max-width: $medium-limit) { @media screen and (max-width: $medium-limit) {
display: block; height: auto;
.container {
.container-sub { .container-sub {
align-items: center;
.bike-img { .bike-img {
margin-top: $default-margin * 1.5; max-width: 60%;
} }
} }
} }
} }
@media screen and (max-width: $small-limit) {
.container {
flex-direction: column;
}
}
} }
</style> </style>