Client - add a workout + fix
This commit is contained in:
parent
991a7acc03
commit
6bfcb24133
@ -4,6 +4,7 @@
|
|||||||
:id="name"
|
:id="name"
|
||||||
:name="name"
|
:name="name"
|
||||||
:maxLenght="charLimit"
|
:maxLenght="charLimit"
|
||||||
|
:disabled="disabled"
|
||||||
v-model="text"
|
v-model="text"
|
||||||
@input="updateText"
|
@input="updateText"
|
||||||
/>
|
/>
|
||||||
@ -24,6 +25,10 @@
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 500,
|
default: 500,
|
||||||
},
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
input: {
|
input: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
<div id="timeline">
|
<div id="timeline">
|
||||||
<div class="section-title">{{ t('workouts.LATEST_WORKOUTS') }}</div>
|
<div class="section-title">{{ t('workouts.LATEST_WORKOUTS') }}</div>
|
||||||
<WorkoutCard
|
<WorkoutCard
|
||||||
v-for="index in [...Array(workoutsToDisplayCount()).keys()]"
|
v-for="workout in workouts"
|
||||||
:workout="workouts.length > 0 ? workouts[index] : null"
|
:workout="workout"
|
||||||
:sport="
|
:sport="
|
||||||
workouts.length > 0
|
workouts.length > 0
|
||||||
? sports.filter((s) => s.id === workouts[index].sport_id)[0]
|
? sports.filter((s) => s.id === workout.sport_id)[0]
|
||||||
: null
|
: null
|
||||||
"
|
"
|
||||||
:user="user"
|
:user="user"
|
||||||
:key="index"
|
:key="workout.id"
|
||||||
/>
|
/>
|
||||||
<div v-if="workouts.length === 0" class="no-workouts">
|
<div v-if="workouts.length === 0" class="no-workouts">
|
||||||
{{ t('workouts.NO_WORKOUTS') }}
|
{{ t('workouts.NO_WORKOUTS') }}
|
||||||
@ -56,7 +56,7 @@
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup() {
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
@ -64,9 +64,6 @@
|
|||||||
const per_page = 5
|
const per_page = 5
|
||||||
onBeforeMount(() => loadWorkouts())
|
onBeforeMount(() => loadWorkouts())
|
||||||
|
|
||||||
const initWorkoutsCount =
|
|
||||||
props.user.nb_workouts >= per_page ? per_page : props.user.nb_workouts
|
|
||||||
|
|
||||||
const workouts: ComputedRef<IWorkout[]> = computed(
|
const workouts: ComputedRef<IWorkout[]> = computed(
|
||||||
() => store.getters[WORKOUTS_STORE.GETTERS.USER_WORKOUTS]
|
() => store.getters[WORKOUTS_STORE.GETTERS.USER_WORKOUTS]
|
||||||
)
|
)
|
||||||
@ -86,20 +83,13 @@
|
|||||||
page.value += 1
|
page.value += 1
|
||||||
loadWorkouts()
|
loadWorkouts()
|
||||||
}
|
}
|
||||||
function workoutsToDisplayCount() {
|
|
||||||
return workouts.value.length > initWorkoutsCount
|
|
||||||
? workouts.value.length
|
|
||||||
: initWorkoutsCount
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initWorkoutsCount,
|
|
||||||
moreWorkoutsExist,
|
moreWorkoutsExist,
|
||||||
per_page,
|
per_page,
|
||||||
workouts,
|
workouts,
|
||||||
t,
|
t,
|
||||||
loadMoreWorkouts,
|
loadMoreWorkouts,
|
||||||
workoutsToDisplayCount,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -28,7 +28,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="nav-item">{{ t('statistics.STATISTICS') }}</div>
|
<div class="nav-item">{{ t('statistics.STATISTICS') }}</div>
|
||||||
<div class="nav-item">{{ t('administration.ADMIN') }}</div>
|
<div class="nav-item">{{ t('administration.ADMIN') }}</div>
|
||||||
<div class="nav-item">{{ t('workouts.ADD_WORKOUT') }}</div>
|
<router-link class="nav-item" to="/workouts/add">
|
||||||
|
{{ t('workouts.ADD_WORKOUT') }}
|
||||||
|
</router-link>
|
||||||
<div class="nav-item nav-separator" />
|
<div class="nav-item nav-separator" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,14 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="workout-edition">
|
<div id="workout-edition">
|
||||||
<Card :without-title="false">
|
<Card :without-title="false">
|
||||||
<template #title>{{ t('workouts.EDIT_WORKOUT') }}</template>
|
<template #title>{{
|
||||||
|
t(`workouts.${isCreation ? 'ADD' : 'EDIT'}_WORKOUT`)
|
||||||
|
}}</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div id="workout-form">
|
<div id="workout-form">
|
||||||
<form @submit.prevent="updateWorkout">
|
<form @submit.prevent="updateWorkout">
|
||||||
<div class="form-items">
|
<div class="form-items">
|
||||||
|
<div class="form-item-radio" v-if="isCreation">
|
||||||
|
<div class="radio">
|
||||||
|
<input
|
||||||
|
id="withGpx"
|
||||||
|
type="radio"
|
||||||
|
:checked="withGpx"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="updateWithGpx"
|
||||||
|
/>
|
||||||
|
<label for="withGpx">{{ t('workouts.WITH_GPX') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<input
|
||||||
|
id="withoutGpx"
|
||||||
|
type="radio"
|
||||||
|
:checked="!withGpx"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="updateWithGpx"
|
||||||
|
/>
|
||||||
|
<label for="withoutGpx">{{
|
||||||
|
t('workouts.WITHOUT_GPX')
|
||||||
|
}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<label> {{ t('workouts.SPORT', 1) }}: </label>
|
<label> {{ t('workouts.SPORT', 1) }}: </label>
|
||||||
<select id="sport" v-model="workoutDataObject.sport_id">
|
<select
|
||||||
|
id="sport"
|
||||||
|
required
|
||||||
|
:disabled="loading"
|
||||||
|
v-model="workoutDataObject.sport_id"
|
||||||
|
>
|
||||||
<option
|
<option
|
||||||
v-for="sport in translatedSports"
|
v-for="sport in translatedSports"
|
||||||
:value="sport.id"
|
:value="sport.id"
|
||||||
@ -18,38 +49,133 @@
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-item">
|
<div class="form-item" v-if="isCreation && withGpx">
|
||||||
|
<label for="gpxFile">
|
||||||
|
{{ t('workouts.GPX_FILE') }}
|
||||||
|
<sup>
|
||||||
|
<i class="fa fa-question-circle" aria-hidden="true" />
|
||||||
|
</sup>
|
||||||
|
{{ t('workouts.ZIP_FILE') }}
|
||||||
|
<sup
|
||||||
|
><i class="fa fa-question-circle" aria-hidden="true" /></sup
|
||||||
|
>:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="gpxFile"
|
||||||
|
name="gpxFile"
|
||||||
|
type="file"
|
||||||
|
accept=".gpx, .zip"
|
||||||
|
:disabled="loading"
|
||||||
|
@input="updateFile"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-item" v-else>
|
||||||
<label for="title"> {{ t('workouts.TITLE') }}: </label>
|
<label for="title"> {{ t('workouts.TITLE') }}: </label>
|
||||||
<input
|
<input
|
||||||
id="title"
|
id="title"
|
||||||
name="title"
|
name="title"
|
||||||
type="text"
|
type="text"
|
||||||
|
:required="!isCreation"
|
||||||
|
:disabled="loading"
|
||||||
v-model="workoutDataObject.title"
|
v-model="workoutDataObject.title"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!withGpx">
|
||||||
|
<div class="workout-date-duration">
|
||||||
|
<div class="form-item">
|
||||||
|
<label>{{ t('workouts.WORKOUT_DATE') }}:</label>
|
||||||
|
<div class="workout-date-time">
|
||||||
|
<input
|
||||||
|
id="workout-date"
|
||||||
|
name="workout-date"
|
||||||
|
type="date"
|
||||||
|
required
|
||||||
|
:disabled="loading"
|
||||||
|
v-model="workoutDataObject.workoutDate"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
id="workout-time"
|
||||||
|
name="workout-time"
|
||||||
|
class="workout-time"
|
||||||
|
type="time"
|
||||||
|
required
|
||||||
|
:disabled="loading"
|
||||||
|
v-model="workoutDataObject.workoutTime"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label>{{ t('workouts.DURATION') }}:</label>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id="workout-duration-hour"
|
||||||
|
name="workout-duration-hour"
|
||||||
|
class="workout-duration"
|
||||||
|
type="text"
|
||||||
|
placeholder="HH"
|
||||||
|
pattern="^([0-9]*[0-9])$"
|
||||||
|
required
|
||||||
|
:disabled="loading"
|
||||||
|
v-model="workoutDataObject.workoutDurationHour"
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<input
|
||||||
|
id="workout-duration-minutes"
|
||||||
|
name="workout-duration-minutes"
|
||||||
|
class="workout-duration"
|
||||||
|
type="text"
|
||||||
|
pattern="^([0-5][0-9])$"
|
||||||
|
placeholder="MM"
|
||||||
|
required
|
||||||
|
:disabled="loading"
|
||||||
|
v-model="workoutDataObject.workoutDurationMinutes"
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<input
|
||||||
|
id="workout-duration-seconds"
|
||||||
|
name="workout-duration-seconds"
|
||||||
|
class="workout-duration"
|
||||||
|
type="text"
|
||||||
|
pattern="^([0-5][0-9])$"
|
||||||
|
placeholder="SS"
|
||||||
|
required
|
||||||
|
:disabled="loading"
|
||||||
|
v-model="workoutDataObject.workoutDurationSeconds"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-item">
|
||||||
|
<label>{{ t('workouts.DISTANCE') }} (km):</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.1"
|
||||||
|
required
|
||||||
|
:disabled="loading"
|
||||||
|
v-model="workoutDataObject.workoutDistance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<label> {{ t('workouts.NOTES') }}: </label>
|
<label> {{ t('workouts.NOTES') }}: </label>
|
||||||
<CustomTextArea
|
<CustomTextArea
|
||||||
name="notes"
|
name="notes"
|
||||||
:input="workoutDataObject.notes"
|
:input="workoutDataObject.notes"
|
||||||
|
:disabled="loading"
|
||||||
@updateValue="updateNotes"
|
@updateValue="updateNotes"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ErrorMessage :message="errorMessages" v-if="errorMessages" />
|
<ErrorMessage :message="errorMessages" v-if="errorMessages" />
|
||||||
<div class="form-buttons">
|
<div v-if="loading">
|
||||||
<button class="confirm" type="submit">
|
<Loader />
|
||||||
|
</div>
|
||||||
|
<div v-else class="form-buttons">
|
||||||
|
<button class="confirm" type="submit" :disabled="loading">
|
||||||
{{ t('buttons.SUBMIT') }}
|
{{ t('buttons.SUBMIT') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button class="cancel" @click.prevent="onCancel">
|
||||||
class="cancel"
|
|
||||||
@click="
|
|
||||||
$router.push({
|
|
||||||
name: 'Workout',
|
|
||||||
params: { workoutId: workout.id },
|
|
||||||
})
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ t('buttons.CANCEL') }}
|
{{ t('buttons.CANCEL') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -67,18 +193,24 @@
|
|||||||
defineComponent,
|
defineComponent,
|
||||||
computed,
|
computed,
|
||||||
reactive,
|
reactive,
|
||||||
|
ref,
|
||||||
watch,
|
watch,
|
||||||
|
onMounted,
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
import Card from '@/components/Common/Card.vue'
|
import Card from '@/components/Common/Card.vue'
|
||||||
import CustomTextArea from '@/components/Common/CustomTextArea.vue'
|
import CustomTextArea from '@/components/Common/CustomTextArea.vue'
|
||||||
import ErrorMessage from '@/components/Common/ErrorMessage.vue'
|
import ErrorMessage from '@/components/Common/ErrorMessage.vue'
|
||||||
|
import Loader from '@/components/Common/Loader.vue'
|
||||||
import { ROOT_STORE, WORKOUTS_STORE } from '@/store/constants'
|
import { ROOT_STORE, WORKOUTS_STORE } from '@/store/constants'
|
||||||
import { ISport } from '@/types/sports'
|
import { ISport } from '@/types/sports'
|
||||||
import { IWorkout } from '@/types/workouts'
|
import { IAuthUserProfile } from '@/types/user'
|
||||||
|
import { IWorkout, IWorkoutForm } from '@/types/workouts'
|
||||||
import { useStore } from '@/use/useStore'
|
import { useStore } from '@/use/useStore'
|
||||||
|
import { formatWorkoutDate, getDateWithTZ } from '@/utils/dates'
|
||||||
import { translateSports } from '@/utils/sports'
|
import { translateSports } from '@/utils/sports'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -87,20 +219,40 @@
|
|||||||
Card,
|
Card,
|
||||||
CustomTextArea,
|
CustomTextArea,
|
||||||
ErrorMessage,
|
ErrorMessage,
|
||||||
|
Loader,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
authUser: {
|
||||||
|
type: Object as PropType<IAuthUserProfile>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isCreation: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
sports: {
|
sports: {
|
||||||
type: Object as PropType<ISport[]>,
|
type: Object as PropType<ISport[]>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
workout: {
|
workout: {
|
||||||
type: Object as PropType<IWorkout>,
|
type: Object as PropType<IWorkout>,
|
||||||
required: true,
|
required: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.workout && props.workout.id) {
|
||||||
|
formatWorkoutForm(props.workout)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const translatedSports: ComputedRef<ISport[]> = computed(() =>
|
const translatedSports: ComputedRef<ISport[]> = computed(() =>
|
||||||
translateSports(props.sports, t)
|
translateSports(props.sports, t)
|
||||||
@ -109,30 +261,112 @@
|
|||||||
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
|
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
|
||||||
)
|
)
|
||||||
const workoutForm = reactive({
|
const workoutForm = reactive({
|
||||||
sport_id: 0,
|
sport_id: '',
|
||||||
title: '',
|
title: '',
|
||||||
notes: '',
|
notes: '',
|
||||||
|
workoutDate: '',
|
||||||
|
workoutTime: '',
|
||||||
|
workoutDurationHour: '',
|
||||||
|
workoutDurationMinutes: '',
|
||||||
|
workoutDurationSeconds: '',
|
||||||
|
workoutDistance: '',
|
||||||
})
|
})
|
||||||
|
let withGpx = ref(
|
||||||
|
props.workout ? props.workout.with_gpx : props.isCreation
|
||||||
|
)
|
||||||
|
let gpxFile: File | null = null
|
||||||
|
|
||||||
function updateNotes(value: string) {
|
function updateNotes(value: string) {
|
||||||
workoutForm.notes = value
|
workoutForm.notes = value
|
||||||
}
|
}
|
||||||
|
function updateWithGpx() {
|
||||||
|
withGpx.value = !withGpx.value
|
||||||
|
}
|
||||||
|
function updateFile(event: Event & { target: HTMLInputElement }) {
|
||||||
|
if (event.target.files) {
|
||||||
|
gpxFile = event.target.files[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function formatWorkoutForm(workout: IWorkout) {
|
||||||
|
workoutForm.sport_id = `${workout.sport_id}`
|
||||||
|
workoutForm.title = workout.title
|
||||||
|
workoutForm.notes = workout.notes
|
||||||
|
if (!workout.with_gpx) {
|
||||||
|
const workoutDateTime = formatWorkoutDate(
|
||||||
|
getDateWithTZ(workout.workout_date, props.authUser.timezone),
|
||||||
|
'yyyy-MM-dd'
|
||||||
|
)
|
||||||
|
const duration = workout.duration.split(':')
|
||||||
|
workoutForm.workoutDistance = `${workout.distance}`
|
||||||
|
workoutForm.workoutDate = workoutDateTime.workout_date
|
||||||
|
workoutForm.workoutTime = workoutDateTime.workout_time
|
||||||
|
workoutForm.workoutDurationHour = duration[0]
|
||||||
|
workoutForm.workoutDurationMinutes = duration[1]
|
||||||
|
workoutForm.workoutDurationSeconds = duration[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function formatPayload(payload: IWorkoutForm) {
|
||||||
|
payload.title = workoutForm.title
|
||||||
|
payload.distance = +workoutForm.workoutDistance
|
||||||
|
payload.duration =
|
||||||
|
+workoutForm.workoutDurationHour * 3600 +
|
||||||
|
+workoutForm.workoutDurationMinutes * 60 +
|
||||||
|
+workoutForm.workoutDurationSeconds
|
||||||
|
payload.workout_date = `${workoutForm.workoutDate} ${workoutForm.workoutTime}`
|
||||||
|
}
|
||||||
function updateWorkout() {
|
function updateWorkout() {
|
||||||
|
const payload: IWorkoutForm = {
|
||||||
|
sport_id: +workoutForm.sport_id,
|
||||||
|
notes: workoutForm.notes,
|
||||||
|
}
|
||||||
if (props.workout) {
|
if (props.workout) {
|
||||||
|
if (props.workout.with_gpx) {
|
||||||
store.dispatch(WORKOUTS_STORE.ACTIONS.EDIT_WORKOUT, {
|
store.dispatch(WORKOUTS_STORE.ACTIONS.EDIT_WORKOUT, {
|
||||||
workoutId: props.workout.id,
|
workoutId: props.workout.id,
|
||||||
data: workoutForm,
|
data: payload,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
formatPayload(payload)
|
||||||
|
store.dispatch(
|
||||||
|
WORKOUTS_STORE.ACTIONS.ADD_WORKOUT_WITHOUT_GPX,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (withGpx.value) {
|
||||||
|
if (!gpxFile) {
|
||||||
|
throw new Error('No file provided !!')
|
||||||
|
}
|
||||||
|
payload.file = gpxFile
|
||||||
|
store.dispatch(WORKOUTS_STORE.ACTIONS.ADD_WORKOUT, payload)
|
||||||
|
} else {
|
||||||
|
formatPayload(payload)
|
||||||
|
store.dispatch(
|
||||||
|
WORKOUTS_STORE.ACTIONS.ADD_WORKOUT_WITHOUT_GPX,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onCancel() {
|
||||||
|
if (props.workout) {
|
||||||
|
router.push({
|
||||||
|
name: 'Workout',
|
||||||
|
params: { workoutId: props.workout.id },
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
router.go(-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.workout,
|
() => props.workout,
|
||||||
async (newWorkout: IWorkout | undefined) => {
|
async (
|
||||||
if (newWorkout && newWorkout.id) {
|
newWorkout: IWorkout | undefined,
|
||||||
workoutForm.sport_id = newWorkout.sport_id
|
previousWorkout: IWorkout | undefined
|
||||||
workoutForm.title = newWorkout.title
|
) => {
|
||||||
workoutForm.notes = newWorkout.notes
|
if (newWorkout !== previousWorkout && newWorkout && newWorkout.id) {
|
||||||
|
formatWorkoutForm(newWorkout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -143,8 +377,12 @@
|
|||||||
errorMessages,
|
errorMessages,
|
||||||
t,
|
t,
|
||||||
translatedSports,
|
translatedSports,
|
||||||
|
withGpx,
|
||||||
workoutDataObject: workoutForm,
|
workoutDataObject: workoutForm,
|
||||||
|
onCancel,
|
||||||
|
updateFile,
|
||||||
updateNotes,
|
updateNotes,
|
||||||
|
updateWithGpx,
|
||||||
updateWorkout,
|
updateWorkout,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -155,29 +393,75 @@
|
|||||||
@import '~@/scss/base';
|
@import '~@/scss/base';
|
||||||
|
|
||||||
#workout-edition {
|
#workout-edition {
|
||||||
margin: 25% auto;
|
margin: 100px auto;
|
||||||
width: 700px;
|
width: 700px;
|
||||||
|
@media screen and (max-width: $small-limit) {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto 50px auto;
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep(.card) {
|
::v-deep(.card) {
|
||||||
.card-title {
|
.card-title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-content {
|
.card-content {
|
||||||
|
@media screen and (max-width: $small-limit) {
|
||||||
|
padding: $default-padding 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workout-form {
|
||||||
.form-items {
|
.form-items {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workout-date-duration {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
@media screen and (max-width: $small-limit) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-item {
|
.form-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: $default-padding;
|
padding: $default-padding;
|
||||||
|
|
||||||
|
.workout-date-time {
|
||||||
|
display: flex;
|
||||||
|
#workout-date {
|
||||||
|
margin-right: $default-margin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.workout-duration {
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item-radio {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
.radio {
|
||||||
label {
|
label {
|
||||||
text-transform: capitalize;
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin-top: -2px;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-buttons {
|
.form-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
@ -187,10 +471,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $small-limit) {
|
|
||||||
width: 100%;
|
|
||||||
margin: 15% auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"ADD_WORKOUT": "Add workout",
|
"ADD_WORKOUT": "Add a workout",
|
||||||
"ANALYSIS": "analysis",
|
"ANALYSIS": "analysis",
|
||||||
"ASCENT": "ascent",
|
"ASCENT": "ascent",
|
||||||
"AVERAGE_SPEED": "average speed",
|
"AVERAGE_SPEED": "average speed",
|
||||||
@ -10,6 +10,7 @@
|
|||||||
"EDIT_WORKOUT": "Edit the workout",
|
"EDIT_WORKOUT": "Edit the workout",
|
||||||
"ELEVATION": "elevation",
|
"ELEVATION": "elevation",
|
||||||
"END": "end",
|
"END": "end",
|
||||||
|
"GPX_FILE": "fichier .gpx",
|
||||||
"KM": "km",
|
"KM": "km",
|
||||||
"LATEST_WORKOUTS": "Latest workouts",
|
"LATEST_WORKOUTS": "Latest workouts",
|
||||||
"LOAD_MORE_WORKOUT": "Load more workouts",
|
"LOAD_MORE_WORKOUT": "Load more workouts",
|
||||||
@ -27,7 +28,7 @@
|
|||||||
"NO_PREVIOUS_WORKOUT": "No previous workout",
|
"NO_PREVIOUS_WORKOUT": "No previous workout",
|
||||||
"NO_RECORDS": "No records.",
|
"NO_RECORDS": "No records.",
|
||||||
"NO_WORKOUTS": "No workouts.",
|
"NO_WORKOUTS": "No workouts.",
|
||||||
"NOTES": "Notes",
|
"NOTES": "notes",
|
||||||
"PAUSES": "pauses",
|
"PAUSES": "pauses",
|
||||||
"PREVIOUS_SEGMENT": "Previous segment",
|
"PREVIOUS_SEGMENT": "Previous segment",
|
||||||
"PREVIOUS_WORKOUT": "Previous workout",
|
"PREVIOUS_WORKOUT": "Previous workout",
|
||||||
@ -60,6 +61,10 @@
|
|||||||
"wind": "wind"
|
"wind": "wind"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"WITH_GPX": "with .gpx file",
|
||||||
|
"WITHOUT_GPX": "without .gpx file",
|
||||||
"WORKOUT": "workout | workouts",
|
"WORKOUT": "workout | workouts",
|
||||||
"WORKOUT_DELETION_CONFIRMATION": "Are you sure you want to delete this workout?"
|
"WORKOUT_DATE": "workout date",
|
||||||
|
"WORKOUT_DELETION_CONFIRMATION": "Are you sure you want to delete this workout?",
|
||||||
|
"ZIP_FILE": "or .zip file containing .gpx files"
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"EDIT_WORKOUT": "Modifier la séance",
|
"EDIT_WORKOUT": "Modifier la séance",
|
||||||
"ELEVATION": "altitude",
|
"ELEVATION": "altitude",
|
||||||
"END": "fin",
|
"END": "fin",
|
||||||
|
"GPX_FILE": ".gpx file",
|
||||||
"KM": "km",
|
"KM": "km",
|
||||||
"LATEST_WORKOUTS": "Séances récentes",
|
"LATEST_WORKOUTS": "Séances récentes",
|
||||||
"LOAD_MORE_WORKOUT": "Charger les séances suivantes",
|
"LOAD_MORE_WORKOUT": "Charger les séances suivantes",
|
||||||
@ -27,7 +28,7 @@
|
|||||||
"NO_PREVIOUS_WORKOUT": "Pas de séance précédente",
|
"NO_PREVIOUS_WORKOUT": "Pas de séance précédente",
|
||||||
"NO_RECORDS": "Pas de records.",
|
"NO_RECORDS": "Pas de records.",
|
||||||
"NO_WORKOUTS": "Pas de séances.",
|
"NO_WORKOUTS": "Pas de séances.",
|
||||||
"NOTES": "Notes",
|
"NOTES": "notes",
|
||||||
"PAUSES": "pauses",
|
"PAUSES": "pauses",
|
||||||
"PREVIOUS_SEGMENT": "Segment précédent",
|
"PREVIOUS_SEGMENT": "Segment précédent",
|
||||||
"PREVIOUS_WORKOUT": "Séance précédente",
|
"PREVIOUS_WORKOUT": "Séance précédente",
|
||||||
@ -60,6 +61,10 @@
|
|||||||
"wind": "venteux"
|
"wind": "venteux"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"WITH_GPX": "avec un fichier .gpx",
|
||||||
|
"WITHOUT_GPX": "sans fichier .gpx",
|
||||||
"WORKOUT": "séance | séances",
|
"WORKOUT": "séance | séances",
|
||||||
"WORKOUT_DELETION_CONFIRMATION": "Etes-vous sûr de vouloir supprimer cette séance ?"
|
"WORKOUT_DATE": "date de la séance",
|
||||||
|
"WORKOUT_DELETION_CONFIRMATION": "Etes-vous sûr de vouloir supprimer cette séance ?",
|
||||||
|
"ZIP_FILE": "ou une archive .zip contenant des fichiers .gpx"
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
|
|||||||
|
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import { USER_STORE } from '@/store/constants'
|
import { USER_STORE } from '@/store/constants'
|
||||||
|
import AddWorkout from '@/views/AddWorkout.vue'
|
||||||
import Dashboard from '@/views/DashBoard.vue'
|
import Dashboard from '@/views/DashBoard.vue'
|
||||||
import EditWorkout from '@/views/EditWorkout.vue'
|
import EditWorkout from '@/views/EditWorkout.vue'
|
||||||
import LoginOrRegister from '@/views/LoginOrRegister.vue'
|
import LoginOrRegister from '@/views/LoginOrRegister.vue'
|
||||||
@ -43,6 +44,11 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: Workout,
|
component: Workout,
|
||||||
props: { displaySegment: true },
|
props: { displaySegment: true },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/workouts/add',
|
||||||
|
name: 'AddWorkout',
|
||||||
|
component: AddWorkout,
|
||||||
|
},
|
||||||
{ path: '/:pathMatch(.*)*', name: 'not-found', component: NotFoundView },
|
{ path: '/:pathMatch(.*)*', name: 'not-found', component: NotFoundView },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ input, textarea, select {
|
|||||||
padding: $default-padding;
|
padding: $default-padding;
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
|
background-color: var(--disabled-background-color);
|
||||||
border-color: var(--disabled-color);
|
border-color: var(--disabled-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,18 @@ import { ActionContext, ActionTree } from 'vuex'
|
|||||||
|
|
||||||
import authApi from '@/api/authApi'
|
import authApi from '@/api/authApi'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { ROOT_STORE, WORKOUTS_STORE } from '@/store/constants'
|
import { ROOT_STORE, USER_STORE, WORKOUTS_STORE } from '@/store/constants'
|
||||||
import { IRootState } from '@/store/modules/root/types'
|
import { IRootState } from '@/store/modules/root/types'
|
||||||
import {
|
import {
|
||||||
IWorkoutsActions,
|
IWorkoutsActions,
|
||||||
IWorkoutsState,
|
IWorkoutsState,
|
||||||
} from '@/store/modules/workouts/types'
|
} from '@/store/modules/workouts/types'
|
||||||
import { IWorkout, IWorkoutPayload, IWorkoutsPayload } from '@/types/workouts'
|
import {
|
||||||
|
IWorkout,
|
||||||
|
IWorkoutForm,
|
||||||
|
IWorkoutPayload,
|
||||||
|
IWorkoutsPayload,
|
||||||
|
} from '@/types/workouts'
|
||||||
import { handleError } from '@/utils'
|
import { handleError } from '@/utils'
|
||||||
|
|
||||||
const getWorkouts = (
|
const getWorkouts = (
|
||||||
@ -113,37 +118,108 @@ export const actions: ActionTree<IWorkoutsState, IRootState> &
|
|||||||
payload: IWorkoutPayload
|
payload: IWorkoutPayload
|
||||||
): void {
|
): void {
|
||||||
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||||
|
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true)
|
||||||
authApi
|
authApi
|
||||||
.delete(`workouts/${payload.workoutId}`)
|
.delete(`workouts/${payload.workoutId}`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
context.commit(WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUT)
|
context.commit(WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUT)
|
||||||
|
context.dispatch(USER_STORE.ACTIONS.GET_USER_PROFILE)
|
||||||
router.push('/')
|
router.push('/')
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
handleError(context, error)
|
handleError(context, error)
|
||||||
})
|
})
|
||||||
|
.finally(() =>
|
||||||
|
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, false)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
[WORKOUTS_STORE.ACTIONS.EDIT_WORKOUT](
|
[WORKOUTS_STORE.ACTIONS.EDIT_WORKOUT](
|
||||||
context: ActionContext<IWorkoutsState, IRootState>,
|
context: ActionContext<IWorkoutsState, IRootState>,
|
||||||
payload: IWorkoutPayload
|
payload: IWorkoutPayload
|
||||||
): void {
|
): void {
|
||||||
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||||
|
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true)
|
||||||
authApi
|
authApi
|
||||||
.patch(`workouts/${payload.workoutId}`, payload.data)
|
.patch(`workouts/${payload.workoutId}`, payload.data)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
context.dispatch(USER_STORE.ACTIONS.GET_USER_PROFILE)
|
||||||
context
|
context
|
||||||
.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, {
|
.dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, {
|
||||||
workoutId: payload.workoutId,
|
workoutId: payload.workoutId,
|
||||||
})
|
})
|
||||||
.then(() =>
|
.then(() => {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'Workout',
|
name: 'Workout',
|
||||||
params: { workoutId: payload.workoutId },
|
params: { workoutId: payload.workoutId },
|
||||||
})
|
})
|
||||||
)
|
})
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
handleError(context, error)
|
handleError(context, error)
|
||||||
})
|
})
|
||||||
|
.finally(() =>
|
||||||
|
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, false)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[WORKOUTS_STORE.ACTIONS.ADD_WORKOUT](
|
||||||
|
context: ActionContext<IWorkoutsState, IRootState>,
|
||||||
|
payload: IWorkoutForm
|
||||||
|
): void {
|
||||||
|
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||||
|
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true)
|
||||||
|
if (!payload.file) {
|
||||||
|
throw new Error('No gpx file provided')
|
||||||
|
}
|
||||||
|
const form = new FormData()
|
||||||
|
form.append('file', payload.file)
|
||||||
|
form.append(
|
||||||
|
'data',
|
||||||
|
`{"sport_id": ${payload.sport_id}, "notes": "${payload.notes}"}`
|
||||||
|
)
|
||||||
|
authApi
|
||||||
|
.post('workouts', form, {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data.status === 'created') {
|
||||||
|
context.dispatch(USER_STORE.ACTIONS.GET_USER_PROFILE)
|
||||||
|
const workout: IWorkout = res.data.data.workouts[0]
|
||||||
|
router.push(
|
||||||
|
res.data.data.workouts.length === 1
|
||||||
|
? `/workouts/${workout.id}`
|
||||||
|
: '/'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
handleError(context, error)
|
||||||
|
})
|
||||||
|
.finally(() =>
|
||||||
|
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, false)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[WORKOUTS_STORE.ACTIONS.ADD_WORKOUT_WITHOUT_GPX](
|
||||||
|
context: ActionContext<IWorkoutsState, IRootState>,
|
||||||
|
payload: IWorkoutForm
|
||||||
|
): void {
|
||||||
|
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
|
||||||
|
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true)
|
||||||
|
authApi
|
||||||
|
.post('workouts/no_gpx', payload)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data.status === 'created') {
|
||||||
|
context.dispatch(USER_STORE.ACTIONS.GET_USER_PROFILE)
|
||||||
|
const workout: IWorkout = res.data.data.workouts[0]
|
||||||
|
router.push(`/workouts/${workout.id}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
handleError(context, error)
|
||||||
|
})
|
||||||
|
.finally(() =>
|
||||||
|
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, false)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
export enum WorkoutsActions {
|
export enum WorkoutsActions {
|
||||||
|
ADD_WORKOUT = 'ADD_WORKOUT',
|
||||||
|
ADD_WORKOUT_WITHOUT_GPX = 'ADD_WORKOUT_WITHOUT_GPX',
|
||||||
DELETE_WORKOUT = 'DELETE_WORKOUT',
|
DELETE_WORKOUT = 'DELETE_WORKOUT',
|
||||||
EDIT_WORKOUT = 'EDIT_WORKOUT',
|
EDIT_WORKOUT = 'EDIT_WORKOUT',
|
||||||
GET_CALENDAR_WORKOUTS = 'GET_CALENDAR_WORKOUTS',
|
GET_CALENDAR_WORKOUTS = 'GET_CALENDAR_WORKOUTS',
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
IWorkoutsPayload,
|
IWorkoutsPayload,
|
||||||
IWorkoutData,
|
IWorkoutData,
|
||||||
IWorkoutPayload,
|
IWorkoutPayload,
|
||||||
|
IWorkoutForm,
|
||||||
} from '@/types/workouts'
|
} from '@/types/workouts'
|
||||||
|
|
||||||
export interface IWorkoutsState {
|
export interface IWorkoutsState {
|
||||||
@ -42,6 +43,14 @@ export interface IWorkoutsActions {
|
|||||||
context: ActionContext<IWorkoutsState, IRootState>,
|
context: ActionContext<IWorkoutsState, IRootState>,
|
||||||
payload: IWorkoutPayload
|
payload: IWorkoutPayload
|
||||||
): void
|
): void
|
||||||
|
[WORKOUTS_STORE.ACTIONS.ADD_WORKOUT](
|
||||||
|
context: ActionContext<IWorkoutsState, IRootState>,
|
||||||
|
payload: IWorkoutForm
|
||||||
|
): void
|
||||||
|
[WORKOUTS_STORE.ACTIONS.ADD_WORKOUT_WITHOUT_GPX](
|
||||||
|
context: ActionContext<IWorkoutsState, IRootState>,
|
||||||
|
payload: IWorkoutForm
|
||||||
|
): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWorkoutsGetters {
|
export interface IWorkoutsGetters {
|
||||||
|
@ -99,8 +99,12 @@ export interface IWorkoutObject {
|
|||||||
|
|
||||||
export interface IWorkoutForm {
|
export interface IWorkoutForm {
|
||||||
sport_id: number | null
|
sport_id: number | null
|
||||||
title: string
|
|
||||||
notes: string
|
notes: string
|
||||||
|
title?: string
|
||||||
|
workout_date?: string
|
||||||
|
distance?: number
|
||||||
|
duration?: number
|
||||||
|
file?: Blob
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWorkoutPayload {
|
export interface IWorkoutPayload {
|
||||||
|
47
fittrackee_client/src/views/AddWorkout.vue
Normal file
47
fittrackee_client/src/views/AddWorkout.vue
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<div id="add-workout">
|
||||||
|
<div class="container">
|
||||||
|
<WorkoutEdition
|
||||||
|
:authUser="authUser"
|
||||||
|
:sports="sports"
|
||||||
|
:isCreation="true"
|
||||||
|
:loading="workoutData.loading"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, ComputedRef } from 'vue'
|
||||||
|
|
||||||
|
import WorkoutEdition from '@/components/Workout/WorkoutEdition.vue'
|
||||||
|
import { SPORTS_STORE, USER_STORE, WORKOUTS_STORE } from '@/store/constants'
|
||||||
|
import { ISport } from '@/types/sports'
|
||||||
|
import { IAuthUserProfile } from '@/types/user'
|
||||||
|
import { IWorkoutData } from '@/types/workouts'
|
||||||
|
import { useStore } from '@/use/useStore'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'AddWorkout',
|
||||||
|
components: {
|
||||||
|
WorkoutEdition,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const store = useStore()
|
||||||
|
const sports: ComputedRef<ISport[]> = computed(
|
||||||
|
() => store.getters[SPORTS_STORE.GETTERS.SPORTS]
|
||||||
|
)
|
||||||
|
const authUser: ComputedRef<IAuthUserProfile> = computed(
|
||||||
|
() => store.getters[USER_STORE.GETTERS.AUTH_USER_PROFILE]
|
||||||
|
)
|
||||||
|
const workoutData: ComputedRef<IWorkoutData> = computed(
|
||||||
|
() => store.getters[WORKOUTS_STORE.GETTERS.WORKOUT_DATA]
|
||||||
|
)
|
||||||
|
return { authUser, sports, workoutData }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~@/scss/base';
|
||||||
|
</style>
|
@ -1,7 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="edit-workout">
|
<div id="edit-workout">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<WorkoutEdition :sports="sports" :workout="workoutData.workout" />
|
<WorkoutEdition
|
||||||
|
:authUser="authUser"
|
||||||
|
:sports="sports"
|
||||||
|
:workout="workoutData.workout"
|
||||||
|
:loading="workoutData.loading"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -17,8 +22,9 @@
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
import WorkoutEdition from '@/components/Workout/WorkoutEdition.vue'
|
import WorkoutEdition from '@/components/Workout/WorkoutEdition.vue'
|
||||||
import { SPORTS_STORE, WORKOUTS_STORE } from '@/store/constants'
|
import { SPORTS_STORE, USER_STORE, WORKOUTS_STORE } from '@/store/constants'
|
||||||
import { ISport } from '@/types/sports'
|
import { ISport } from '@/types/sports'
|
||||||
|
import { IAuthUserProfile } from '@/types/user'
|
||||||
import { IWorkoutData } from '@/types/workouts'
|
import { IWorkoutData } from '@/types/workouts'
|
||||||
import { useStore } from '@/use/useStore'
|
import { useStore } from '@/use/useStore'
|
||||||
|
|
||||||
@ -37,6 +43,9 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const authUser: ComputedRef<IAuthUserProfile> = computed(
|
||||||
|
() => store.getters[USER_STORE.GETTERS.AUTH_USER_PROFILE]
|
||||||
|
)
|
||||||
const sports: ComputedRef<ISport[]> = computed(
|
const sports: ComputedRef<ISport[]> = computed(
|
||||||
() => store.getters[SPORTS_STORE.GETTERS.SPORTS]
|
() => store.getters[SPORTS_STORE.GETTERS.SPORTS]
|
||||||
)
|
)
|
||||||
@ -53,7 +62,7 @@
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return { sports, workoutData }
|
return { authUser, sports, workoutData }
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user