Client - add a workout + fix
This commit is contained in:
		@@ -4,6 +4,7 @@
 | 
			
		||||
      :id="name"
 | 
			
		||||
      :name="name"
 | 
			
		||||
      :maxLenght="charLimit"
 | 
			
		||||
      :disabled="disabled"
 | 
			
		||||
      v-model="text"
 | 
			
		||||
      @input="updateText"
 | 
			
		||||
    />
 | 
			
		||||
@@ -24,6 +25,10 @@
 | 
			
		||||
        type: Number,
 | 
			
		||||
        default: 500,
 | 
			
		||||
      },
 | 
			
		||||
      disabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        default: false,
 | 
			
		||||
      },
 | 
			
		||||
      input: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        default: '',
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,15 @@
 | 
			
		||||
  <div id="timeline">
 | 
			
		||||
    <div class="section-title">{{ t('workouts.LATEST_WORKOUTS') }}</div>
 | 
			
		||||
    <WorkoutCard
 | 
			
		||||
      v-for="index in [...Array(workoutsToDisplayCount()).keys()]"
 | 
			
		||||
      :workout="workouts.length > 0 ? workouts[index] : null"
 | 
			
		||||
      v-for="workout in workouts"
 | 
			
		||||
      :workout="workout"
 | 
			
		||||
      :sport="
 | 
			
		||||
        workouts.length > 0
 | 
			
		||||
          ? sports.filter((s) => s.id === workouts[index].sport_id)[0]
 | 
			
		||||
          ? sports.filter((s) => s.id === workout.sport_id)[0]
 | 
			
		||||
          : null
 | 
			
		||||
      "
 | 
			
		||||
      :user="user"
 | 
			
		||||
      :key="index"
 | 
			
		||||
      :key="workout.id"
 | 
			
		||||
    />
 | 
			
		||||
    <div v-if="workouts.length === 0" class="no-workouts">
 | 
			
		||||
      {{ t('workouts.NO_WORKOUTS') }}
 | 
			
		||||
@@ -56,7 +56,7 @@
 | 
			
		||||
        required: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    setup(props) {
 | 
			
		||||
    setup() {
 | 
			
		||||
      const store = useStore()
 | 
			
		||||
      const { t } = useI18n()
 | 
			
		||||
 | 
			
		||||
@@ -64,9 +64,6 @@
 | 
			
		||||
      const per_page = 5
 | 
			
		||||
      onBeforeMount(() => loadWorkouts())
 | 
			
		||||
 | 
			
		||||
      const initWorkoutsCount =
 | 
			
		||||
        props.user.nb_workouts >= per_page ? per_page : props.user.nb_workouts
 | 
			
		||||
 | 
			
		||||
      const workouts: ComputedRef<IWorkout[]> = computed(
 | 
			
		||||
        () => store.getters[WORKOUTS_STORE.GETTERS.USER_WORKOUTS]
 | 
			
		||||
      )
 | 
			
		||||
@@ -86,20 +83,13 @@
 | 
			
		||||
        page.value += 1
 | 
			
		||||
        loadWorkouts()
 | 
			
		||||
      }
 | 
			
		||||
      function workoutsToDisplayCount() {
 | 
			
		||||
        return workouts.value.length > initWorkoutsCount
 | 
			
		||||
          ? workouts.value.length
 | 
			
		||||
          : initWorkoutsCount
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        initWorkoutsCount,
 | 
			
		||||
        moreWorkoutsExist,
 | 
			
		||||
        per_page,
 | 
			
		||||
        workouts,
 | 
			
		||||
        t,
 | 
			
		||||
        loadMoreWorkouts,
 | 
			
		||||
        workoutsToDisplayCount,
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  })
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,9 @@
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="nav-item">{{ t('statistics.STATISTICS') }}</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>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,45 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div id="workout-edition">
 | 
			
		||||
    <Card :without-title="false">
 | 
			
		||||
      <template #title>{{ t('workouts.EDIT_WORKOUT') }}</template>
 | 
			
		||||
      <template #title>{{
 | 
			
		||||
        t(`workouts.${isCreation ? 'ADD' : 'EDIT'}_WORKOUT`)
 | 
			
		||||
      }}</template>
 | 
			
		||||
      <template #content>
 | 
			
		||||
        <div id="workout-form">
 | 
			
		||||
          <form @submit.prevent="updateWorkout">
 | 
			
		||||
            <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">
 | 
			
		||||
                <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
 | 
			
		||||
                    v-for="sport in translatedSports"
 | 
			
		||||
                    :value="sport.id"
 | 
			
		||||
@@ -18,38 +49,133 @@
 | 
			
		||||
                  </option>
 | 
			
		||||
                </select>
 | 
			
		||||
              </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>
 | 
			
		||||
                <input
 | 
			
		||||
                  id="title"
 | 
			
		||||
                  name="title"
 | 
			
		||||
                  type="text"
 | 
			
		||||
                  :required="!isCreation"
 | 
			
		||||
                  :disabled="loading"
 | 
			
		||||
                  v-model="workoutDataObject.title"
 | 
			
		||||
                />
 | 
			
		||||
              </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">
 | 
			
		||||
                <label> {{ t('workouts.NOTES') }}: </label>
 | 
			
		||||
                <CustomTextArea
 | 
			
		||||
                  name="notes"
 | 
			
		||||
                  :input="workoutDataObject.notes"
 | 
			
		||||
                  :disabled="loading"
 | 
			
		||||
                  @updateValue="updateNotes"
 | 
			
		||||
                />
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <ErrorMessage :message="errorMessages" v-if="errorMessages" />
 | 
			
		||||
            <div class="form-buttons">
 | 
			
		||||
              <button class="confirm" type="submit">
 | 
			
		||||
            <div v-if="loading">
 | 
			
		||||
              <Loader />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div v-else class="form-buttons">
 | 
			
		||||
              <button class="confirm" type="submit" :disabled="loading">
 | 
			
		||||
                {{ t('buttons.SUBMIT') }}
 | 
			
		||||
              </button>
 | 
			
		||||
              <button
 | 
			
		||||
                class="cancel"
 | 
			
		||||
                @click="
 | 
			
		||||
                  $router.push({
 | 
			
		||||
                    name: 'Workout',
 | 
			
		||||
                    params: { workoutId: workout.id },
 | 
			
		||||
                  })
 | 
			
		||||
                "
 | 
			
		||||
              >
 | 
			
		||||
              <button class="cancel" @click.prevent="onCancel">
 | 
			
		||||
                {{ t('buttons.CANCEL') }}
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
@@ -67,18 +193,24 @@
 | 
			
		||||
    defineComponent,
 | 
			
		||||
    computed,
 | 
			
		||||
    reactive,
 | 
			
		||||
    ref,
 | 
			
		||||
    watch,
 | 
			
		||||
    onMounted,
 | 
			
		||||
    onUnmounted,
 | 
			
		||||
  } from 'vue'
 | 
			
		||||
  import { useI18n } from 'vue-i18n'
 | 
			
		||||
  import { useRouter } from 'vue-router'
 | 
			
		||||
 | 
			
		||||
  import Card from '@/components/Common/Card.vue'
 | 
			
		||||
  import CustomTextArea from '@/components/Common/CustomTextArea.vue'
 | 
			
		||||
  import ErrorMessage from '@/components/Common/ErrorMessage.vue'
 | 
			
		||||
  import Loader from '@/components/Common/Loader.vue'
 | 
			
		||||
  import { ROOT_STORE, WORKOUTS_STORE } from '@/store/constants'
 | 
			
		||||
  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 { formatWorkoutDate, getDateWithTZ } from '@/utils/dates'
 | 
			
		||||
  import { translateSports } from '@/utils/sports'
 | 
			
		||||
 | 
			
		||||
  export default defineComponent({
 | 
			
		||||
@@ -87,20 +219,40 @@
 | 
			
		||||
      Card,
 | 
			
		||||
      CustomTextArea,
 | 
			
		||||
      ErrorMessage,
 | 
			
		||||
      Loader,
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
      authUser: {
 | 
			
		||||
        type: Object as PropType<IAuthUserProfile>,
 | 
			
		||||
        required: true,
 | 
			
		||||
      },
 | 
			
		||||
      isCreation: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        default: false,
 | 
			
		||||
      },
 | 
			
		||||
      loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        default: false,
 | 
			
		||||
      },
 | 
			
		||||
      sports: {
 | 
			
		||||
        type: Object as PropType<ISport[]>,
 | 
			
		||||
        required: true,
 | 
			
		||||
      },
 | 
			
		||||
      workout: {
 | 
			
		||||
        type: Object as PropType<IWorkout>,
 | 
			
		||||
        required: true,
 | 
			
		||||
        required: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    setup(props) {
 | 
			
		||||
      const { t } = useI18n()
 | 
			
		||||
      const store = useStore()
 | 
			
		||||
      const router = useRouter()
 | 
			
		||||
 | 
			
		||||
      onMounted(() => {
 | 
			
		||||
        if (props.workout && props.workout.id) {
 | 
			
		||||
          formatWorkoutForm(props.workout)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const translatedSports: ComputedRef<ISport[]> = computed(() =>
 | 
			
		||||
        translateSports(props.sports, t)
 | 
			
		||||
@@ -109,30 +261,112 @@
 | 
			
		||||
        () => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
 | 
			
		||||
      )
 | 
			
		||||
      const workoutForm = reactive({
 | 
			
		||||
        sport_id: 0,
 | 
			
		||||
        sport_id: '',
 | 
			
		||||
        title: '',
 | 
			
		||||
        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) {
 | 
			
		||||
        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() {
 | 
			
		||||
        const payload: IWorkoutForm = {
 | 
			
		||||
          sport_id: +workoutForm.sport_id,
 | 
			
		||||
          notes: workoutForm.notes,
 | 
			
		||||
        }
 | 
			
		||||
        if (props.workout) {
 | 
			
		||||
          store.dispatch(WORKOUTS_STORE.ACTIONS.EDIT_WORKOUT, {
 | 
			
		||||
            workoutId: props.workout.id,
 | 
			
		||||
            data: workoutForm,
 | 
			
		||||
          if (props.workout.with_gpx) {
 | 
			
		||||
            store.dispatch(WORKOUTS_STORE.ACTIONS.EDIT_WORKOUT, {
 | 
			
		||||
              workoutId: props.workout.id,
 | 
			
		||||
              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(
 | 
			
		||||
        () => props.workout,
 | 
			
		||||
        async (newWorkout: IWorkout | undefined) => {
 | 
			
		||||
          if (newWorkout && newWorkout.id) {
 | 
			
		||||
            workoutForm.sport_id = newWorkout.sport_id
 | 
			
		||||
            workoutForm.title = newWorkout.title
 | 
			
		||||
            workoutForm.notes = newWorkout.notes
 | 
			
		||||
        async (
 | 
			
		||||
          newWorkout: IWorkout | undefined,
 | 
			
		||||
          previousWorkout: IWorkout | undefined
 | 
			
		||||
        ) => {
 | 
			
		||||
          if (newWorkout !== previousWorkout && newWorkout && newWorkout.id) {
 | 
			
		||||
            formatWorkoutForm(newWorkout)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
@@ -143,8 +377,12 @@
 | 
			
		||||
        errorMessages,
 | 
			
		||||
        t,
 | 
			
		||||
        translatedSports,
 | 
			
		||||
        withGpx,
 | 
			
		||||
        workoutDataObject: workoutForm,
 | 
			
		||||
        onCancel,
 | 
			
		||||
        updateFile,
 | 
			
		||||
        updateNotes,
 | 
			
		||||
        updateWithGpx,
 | 
			
		||||
        updateWorkout,
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
@@ -155,42 +393,84 @@
 | 
			
		||||
  @import '~@/scss/base';
 | 
			
		||||
 | 
			
		||||
  #workout-edition {
 | 
			
		||||
    margin: 25% auto;
 | 
			
		||||
    margin: 100px auto;
 | 
			
		||||
    width: 700px;
 | 
			
		||||
    @media screen and (max-width: $small-limit) {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      margin: 0 auto 50px auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ::v-deep(.card) {
 | 
			
		||||
      .card-title {
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        text-transform: uppercase;
 | 
			
		||||
      }
 | 
			
		||||
      .card-content {
 | 
			
		||||
        .form-items {
 | 
			
		||||
          display: flex;
 | 
			
		||||
          flex-direction: column;
 | 
			
		||||
 | 
			
		||||
          .form-item {
 | 
			
		||||
      .card-content {
 | 
			
		||||
        @media screen and (max-width: $small-limit) {
 | 
			
		||||
          padding: $default-padding 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #workout-form {
 | 
			
		||||
          .form-items {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            flex-direction: column;
 | 
			
		||||
            padding: $default-padding;
 | 
			
		||||
 | 
			
		||||
            label {
 | 
			
		||||
              text-transform: capitalize;
 | 
			
		||||
            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 {
 | 
			
		||||
              display: flex;
 | 
			
		||||
              flex-direction: column;
 | 
			
		||||
              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 {
 | 
			
		||||
                  font-weight: normal;
 | 
			
		||||
                }
 | 
			
		||||
                input {
 | 
			
		||||
                  margin-top: -2px;
 | 
			
		||||
                  vertical-align: middle;
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        .form-buttons {
 | 
			
		||||
          display: flex;
 | 
			
		||||
          justify-content: flex-end;
 | 
			
		||||
          button {
 | 
			
		||||
            margin: $default-padding * 0.5;
 | 
			
		||||
 | 
			
		||||
          .form-buttons {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            justify-content: flex-end;
 | 
			
		||||
            button {
 | 
			
		||||
              margin: $default-padding * 0.5;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media screen and (max-width: $small-limit) {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      margin: 15% auto;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
  "ADD_WORKOUT": "Add workout",
 | 
			
		||||
  "ADD_WORKOUT": "Add a workout",
 | 
			
		||||
  "ANALYSIS": "analysis",
 | 
			
		||||
  "ASCENT": "ascent",
 | 
			
		||||
  "AVERAGE_SPEED": "average speed",
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
  "EDIT_WORKOUT": "Edit the workout",
 | 
			
		||||
  "ELEVATION": "elevation",
 | 
			
		||||
  "END": "end",
 | 
			
		||||
  "GPX_FILE": "fichier .gpx",
 | 
			
		||||
  "KM": "km",
 | 
			
		||||
  "LATEST_WORKOUTS": "Latest workouts",
 | 
			
		||||
  "LOAD_MORE_WORKOUT": "Load more workouts",
 | 
			
		||||
@@ -27,7 +28,7 @@
 | 
			
		||||
  "NO_PREVIOUS_WORKOUT": "No previous workout",
 | 
			
		||||
  "NO_RECORDS": "No records.",
 | 
			
		||||
  "NO_WORKOUTS": "No workouts.",
 | 
			
		||||
  "NOTES": "Notes",
 | 
			
		||||
  "NOTES": "notes",
 | 
			
		||||
  "PAUSES": "pauses",
 | 
			
		||||
  "PREVIOUS_SEGMENT": "Previous segment",
 | 
			
		||||
  "PREVIOUS_WORKOUT": "Previous workout",
 | 
			
		||||
@@ -60,6 +61,10 @@
 | 
			
		||||
      "wind":  "wind"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "WITH_GPX": "with .gpx file",
 | 
			
		||||
  "WITHOUT_GPX": "without .gpx file",
 | 
			
		||||
  "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",
 | 
			
		||||
  "ELEVATION": "altitude",
 | 
			
		||||
  "END": "fin",
 | 
			
		||||
  "GPX_FILE": ".gpx file",
 | 
			
		||||
  "KM": "km",
 | 
			
		||||
  "LATEST_WORKOUTS": "Séances récentes",
 | 
			
		||||
  "LOAD_MORE_WORKOUT": "Charger les séances suivantes",
 | 
			
		||||
@@ -27,7 +28,7 @@
 | 
			
		||||
  "NO_PREVIOUS_WORKOUT": "Pas de séance précédente",
 | 
			
		||||
  "NO_RECORDS": "Pas de records.",
 | 
			
		||||
  "NO_WORKOUTS": "Pas de séances.",
 | 
			
		||||
  "NOTES": "Notes",
 | 
			
		||||
  "NOTES": "notes",
 | 
			
		||||
  "PAUSES": "pauses",
 | 
			
		||||
  "PREVIOUS_SEGMENT": "Segment précédent",
 | 
			
		||||
  "PREVIOUS_WORKOUT": "Séance précédente",
 | 
			
		||||
@@ -60,6 +61,10 @@
 | 
			
		||||
      "wind":  "venteux"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "WITH_GPX": "avec un fichier .gpx",
 | 
			
		||||
  "WITHOUT_GPX": "sans fichier .gpx",
 | 
			
		||||
  "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 { USER_STORE } from '@/store/constants'
 | 
			
		||||
import AddWorkout from '@/views/AddWorkout.vue'
 | 
			
		||||
import Dashboard from '@/views/DashBoard.vue'
 | 
			
		||||
import EditWorkout from '@/views/EditWorkout.vue'
 | 
			
		||||
import LoginOrRegister from '@/views/LoginOrRegister.vue'
 | 
			
		||||
@@ -43,6 +44,11 @@ const routes: Array<RouteRecordRaw> = [
 | 
			
		||||
    component: Workout,
 | 
			
		||||
    props: { displaySegment: true },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: '/workouts/add',
 | 
			
		||||
    name: 'AddWorkout',
 | 
			
		||||
    component: AddWorkout,
 | 
			
		||||
  },
 | 
			
		||||
  { path: '/:pathMatch(.*)*', name: 'not-found', component: NotFoundView },
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ input, textarea, select {
 | 
			
		||||
  padding: $default-padding;
 | 
			
		||||
 | 
			
		||||
  &:disabled {
 | 
			
		||||
    background-color:  var(--disabled-background-color);
 | 
			
		||||
    border-color: var(--disabled-color);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,18 @@ import { ActionContext, ActionTree } from 'vuex'
 | 
			
		||||
 | 
			
		||||
import authApi from '@/api/authApi'
 | 
			
		||||
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 {
 | 
			
		||||
  IWorkoutsActions,
 | 
			
		||||
  IWorkoutsState,
 | 
			
		||||
} from '@/store/modules/workouts/types'
 | 
			
		||||
import { IWorkout, IWorkoutPayload, IWorkoutsPayload } from '@/types/workouts'
 | 
			
		||||
import {
 | 
			
		||||
  IWorkout,
 | 
			
		||||
  IWorkoutForm,
 | 
			
		||||
  IWorkoutPayload,
 | 
			
		||||
  IWorkoutsPayload,
 | 
			
		||||
} from '@/types/workouts'
 | 
			
		||||
import { handleError } from '@/utils'
 | 
			
		||||
 | 
			
		||||
const getWorkouts = (
 | 
			
		||||
@@ -113,37 +118,108 @@ export const actions: ActionTree<IWorkoutsState, IRootState> &
 | 
			
		||||
    payload: IWorkoutPayload
 | 
			
		||||
  ): void {
 | 
			
		||||
    context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
 | 
			
		||||
    context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true)
 | 
			
		||||
    authApi
 | 
			
		||||
      .delete(`workouts/${payload.workoutId}`)
 | 
			
		||||
      .then(() => {
 | 
			
		||||
        context.commit(WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUT)
 | 
			
		||||
        context.dispatch(USER_STORE.ACTIONS.GET_USER_PROFILE)
 | 
			
		||||
        router.push('/')
 | 
			
		||||
      })
 | 
			
		||||
      .catch((error) => {
 | 
			
		||||
        handleError(context, error)
 | 
			
		||||
      })
 | 
			
		||||
      .finally(() =>
 | 
			
		||||
        context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, false)
 | 
			
		||||
      )
 | 
			
		||||
  },
 | 
			
		||||
  [WORKOUTS_STORE.ACTIONS.EDIT_WORKOUT](
 | 
			
		||||
    context: ActionContext<IWorkoutsState, IRootState>,
 | 
			
		||||
    payload: IWorkoutPayload
 | 
			
		||||
  ): void {
 | 
			
		||||
    context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
 | 
			
		||||
    context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true)
 | 
			
		||||
    authApi
 | 
			
		||||
      .patch(`workouts/${payload.workoutId}`, payload.data)
 | 
			
		||||
      .then(() => {
 | 
			
		||||
        context.dispatch(USER_STORE.ACTIONS.GET_USER_PROFILE)
 | 
			
		||||
        context
 | 
			
		||||
          .dispatch(WORKOUTS_STORE.ACTIONS.GET_WORKOUT_DATA, {
 | 
			
		||||
            workoutId: payload.workoutId,
 | 
			
		||||
          })
 | 
			
		||||
          .then(() =>
 | 
			
		||||
          .then(() => {
 | 
			
		||||
            router.push({
 | 
			
		||||
              name: 'Workout',
 | 
			
		||||
              params: { workoutId: payload.workoutId },
 | 
			
		||||
            })
 | 
			
		||||
          )
 | 
			
		||||
          })
 | 
			
		||||
      })
 | 
			
		||||
      .catch((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 {
 | 
			
		||||
  ADD_WORKOUT = 'ADD_WORKOUT',
 | 
			
		||||
  ADD_WORKOUT_WITHOUT_GPX = 'ADD_WORKOUT_WITHOUT_GPX',
 | 
			
		||||
  DELETE_WORKOUT = 'DELETE_WORKOUT',
 | 
			
		||||
  EDIT_WORKOUT = 'EDIT_WORKOUT',
 | 
			
		||||
  GET_CALENDAR_WORKOUTS = 'GET_CALENDAR_WORKOUTS',
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import {
 | 
			
		||||
  IWorkoutsPayload,
 | 
			
		||||
  IWorkoutData,
 | 
			
		||||
  IWorkoutPayload,
 | 
			
		||||
  IWorkoutForm,
 | 
			
		||||
} from '@/types/workouts'
 | 
			
		||||
 | 
			
		||||
export interface IWorkoutsState {
 | 
			
		||||
@@ -42,6 +43,14 @@ export interface IWorkoutsActions {
 | 
			
		||||
    context: ActionContext<IWorkoutsState, IRootState>,
 | 
			
		||||
    payload: IWorkoutPayload
 | 
			
		||||
  ): 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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -99,8 +99,12 @@ export interface IWorkoutObject {
 | 
			
		||||
 | 
			
		||||
export interface IWorkoutForm {
 | 
			
		||||
  sport_id: number | null
 | 
			
		||||
  title: string
 | 
			
		||||
  notes: string
 | 
			
		||||
  title?: string
 | 
			
		||||
  workout_date?: string
 | 
			
		||||
  distance?: number
 | 
			
		||||
  duration?: number
 | 
			
		||||
  file?: Blob
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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>
 | 
			
		||||
  <div id="edit-workout">
 | 
			
		||||
    <div class="container">
 | 
			
		||||
      <WorkoutEdition :sports="sports" :workout="workoutData.workout" />
 | 
			
		||||
      <WorkoutEdition
 | 
			
		||||
        :authUser="authUser"
 | 
			
		||||
        :sports="sports"
 | 
			
		||||
        :workout="workoutData.workout"
 | 
			
		||||
        :loading="workoutData.loading"
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -17,8 +22,9 @@
 | 
			
		||||
  import { useRoute } from 'vue-router'
 | 
			
		||||
 | 
			
		||||
  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 { IAuthUserProfile } from '@/types/user'
 | 
			
		||||
  import { IWorkoutData } from '@/types/workouts'
 | 
			
		||||
  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(
 | 
			
		||||
        () => store.getters[SPORTS_STORE.GETTERS.SPORTS]
 | 
			
		||||
      )
 | 
			
		||||
@@ -53,7 +62,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      return { sports, workoutData }
 | 
			
		||||
      return { authUser, sports, workoutData }
 | 
			
		||||
    },
 | 
			
		||||
  })
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user