diff --git a/fittrackee_client/package.json b/fittrackee_client/package.json index 68e23e8f..0d380f5f 100644 --- a/fittrackee_client/package.json +++ b/fittrackee_client/package.json @@ -10,12 +10,15 @@ "i18n:report": "vue-cli-service i18n:report --src \"./src/**/*.?(js|vue)\" --locales \"./src/locales/**/*.json\"" }, "dependencies": { + "@tmcw/togeojson": "^4.5.0", + "@vue-leaflet/vue-leaflet": "^0.6.1", "axios": "^0.21.1", "chart.js": "^3.5.1", "chartjs-plugin-datalabels": "^2.0.0", "core-js": "^3.6.5", "date-fns": "^2.23.0", "date-fns-tz": "^1.1.6", + "leaflet": "^1.7.1", "register-service-worker": "^1.7.1", "vue": "^3.0.0", "vue-chart-3": "^0.5.8", diff --git a/fittrackee_client/public/index.html b/fittrackee_client/public/index.html index 672dac29..4835c894 100644 --- a/fittrackee_client/public/index.html +++ b/fittrackee_client/public/index.html @@ -6,6 +6,7 @@ + <%= htmlWebpackPlugin.options.title %> diff --git a/fittrackee_client/public/static/css/leaflet.css b/fittrackee_client/public/static/css/leaflet.css new file mode 100644 index 00000000..601476fe --- /dev/null +++ b/fittrackee_client/public/static/css/leaflet.css @@ -0,0 +1,640 @@ +/* required styles */ + +.leaflet-pane, +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-tile-container, +.leaflet-pane > svg, +.leaflet-pane > canvas, +.leaflet-zoom-box, +.leaflet-image-layer, +.leaflet-layer { + position: absolute; + left: 0; + top: 0; + } +.leaflet-container { + overflow: hidden; + } +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-user-drag: none; + } +/* Prevents IE11 from highlighting tiles in blue */ +.leaflet-tile::selection { + background: transparent; +} +/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ +.leaflet-safari .leaflet-tile { + image-rendering: -webkit-optimize-contrast; + } +/* hack that prevents hw layers "stretching" when loading new tiles */ +.leaflet-safari .leaflet-tile-container { + width: 1600px; + height: 1600px; + -webkit-transform-origin: 0 0; + } +.leaflet-marker-icon, +.leaflet-marker-shadow { + display: block; + } +/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ +/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container .leaflet-overlay-pane svg, +.leaflet-container .leaflet-marker-pane img, +.leaflet-container .leaflet-shadow-pane img, +.leaflet-container .leaflet-tile-pane img, +.leaflet-container img.leaflet-image-layer, +.leaflet-container .leaflet-tile { + max-width: none !important; + max-height: none !important; + } + +.leaflet-container.leaflet-touch-zoom { + -ms-touch-action: pan-x pan-y; + touch-action: pan-x pan-y; + } +.leaflet-container.leaflet-touch-drag { + -ms-touch-action: pinch-zoom; + /* Fallback for FF which doesn't support pinch-zoom */ + touch-action: none; + touch-action: pinch-zoom; +} +.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { + -ms-touch-action: none; + touch-action: none; +} +.leaflet-container { + -webkit-tap-highlight-color: transparent; +} +.leaflet-container a { + -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); +} +.leaflet-tile { + filter: inherit; + visibility: hidden; + } +.leaflet-tile-loaded { + visibility: inherit; + } +.leaflet-zoom-box { + width: 0; + height: 0; + -moz-box-sizing: border-box; + box-sizing: border-box; + z-index: 800; + } +/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ +.leaflet-overlay-pane svg { + -moz-user-select: none; + } + +.leaflet-pane { z-index: 400; } + +.leaflet-tile-pane { z-index: 200; } +.leaflet-overlay-pane { z-index: 400; } +.leaflet-shadow-pane { z-index: 500; } +.leaflet-marker-pane { z-index: 600; } +.leaflet-tooltip-pane { z-index: 650; } +.leaflet-popup-pane { z-index: 700; } + +.leaflet-map-pane canvas { z-index: 100; } +.leaflet-map-pane svg { z-index: 200; } + +.leaflet-vml-shape { + width: 1px; + height: 1px; + } +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; + } + + +/* control positioning */ + +.leaflet-control { + position: relative; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } +.leaflet-top, +.leaflet-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.leaflet-top { + top: 0; + } +.leaflet-right { + right: 0; + } +.leaflet-bottom { + bottom: 0; + } +.leaflet-left { + left: 0; + } +.leaflet-control { + float: left; + clear: both; + } +.leaflet-right .leaflet-control { + float: right; + } +.leaflet-top .leaflet-control { + margin-top: 10px; + } +.leaflet-bottom .leaflet-control { + margin-bottom: 10px; + } +.leaflet-left .leaflet-control { + margin-left: 10px; + } +.leaflet-right .leaflet-control { + margin-right: 10px; + } + + +/* zoom and fade animations */ + +.leaflet-fade-anim .leaflet-tile { + will-change: opacity; + } +.leaflet-fade-anim .leaflet-popup { + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + } +.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; + } +.leaflet-zoom-animated { + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + } +.leaflet-zoom-anim .leaflet-zoom-animated { + will-change: transform; + } +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); + -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); + transition: transform 0.25s cubic-bezier(0,0,0.25,1); + } +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile { + -webkit-transition: none; + -moz-transition: none; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* cursors */ + +.leaflet-interactive { + cursor: pointer; + } +.leaflet-grab { + cursor: -webkit-grab; + cursor: -moz-grab; + cursor: grab; + } +.leaflet-crosshair, +.leaflet-crosshair .leaflet-interactive { + cursor: crosshair; + } +.leaflet-popup-pane, +.leaflet-control { + cursor: auto; + } +.leaflet-dragging .leaflet-grab, +.leaflet-dragging .leaflet-grab .leaflet-interactive, +.leaflet-dragging .leaflet-marker-draggable { + cursor: move; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + cursor: grabbing; + } + +/* marker & overlays interactivity */ +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-image-layer, +.leaflet-pane > svg path, +.leaflet-tile-container { + pointer-events: none; + } + +.leaflet-marker-icon.leaflet-interactive, +.leaflet-image-layer.leaflet-interactive, +.leaflet-pane > svg path.leaflet-interactive, +svg.leaflet-image-layer.leaflet-interactive path { + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } + +/* visual tweaks */ + +.leaflet-container { + background: #ddd; + outline: 0; + } +.leaflet-container a { + color: #0078A8; + } +.leaflet-container a.leaflet-active { + outline: 2px solid orange; + } +.leaflet-zoom-box { + border: 2px dotted #38f; + background: rgba(255,255,255,0.5); + } + + +/* general typography */ +.leaflet-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + } + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.leaflet-bar a, +.leaflet-bar a:hover { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.leaflet-bar a, +.leaflet-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.leaflet-bar a:hover { + background-color: #f4f4f4; + } +.leaflet-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.leaflet-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.leaflet-bar a.leaflet-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + +.leaflet-touch .leaflet-bar a { + width: 30px; + height: 30px; + line-height: 30px; + } +.leaflet-touch .leaflet-bar a:first-child { + border-top-left-radius: 2px; + border-top-right-radius: 2px; + } +.leaflet-touch .leaflet-bar a:last-child { + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + } + +/* zoom control */ + +.leaflet-control-zoom-in, +.leaflet-control-zoom-out { + font: bold 18px 'Lucida Console', Monaco, monospace; + text-indent: 1px; + } + +.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { + font-size: 22px; + } + + +/* layers control */ + +.leaflet-control-layers { + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; + } +.leaflet-control-layers-toggle { + background-image: url(images/layers.png); + width: 36px; + height: 36px; + } +.leaflet-retina .leaflet-control-layers-toggle { + background-image: url(images/layers-2x.png); + background-size: 26px 26px; + } +.leaflet-touch .leaflet-control-layers-toggle { + width: 44px; + height: 44px; + } +.leaflet-control-layers .leaflet-control-layers-list, +.leaflet-control-layers-expanded .leaflet-control-layers-toggle { + display: none; + } +.leaflet-control-layers-expanded .leaflet-control-layers-list { + display: block; + position: relative; + } +.leaflet-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.leaflet-control-layers-scrollbar { + overflow-y: scroll; + overflow-x: hidden; + padding-right: 5px; + } +.leaflet-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.leaflet-control-layers label { + display: block; + } +.leaflet-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + +/* Default icon URLs */ +.leaflet-default-icon-path { + background-image: url(images/marker-icon.png); + } + + +/* attribution and scale controls */ + +.leaflet-container .leaflet-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.7); + margin: 0; + } +.leaflet-control-attribution, +.leaflet-control-scale-line { + padding: 0 5px; + color: #333; + } +.leaflet-control-attribution a { + text-decoration: none; + } +.leaflet-control-attribution a:hover { + text-decoration: underline; + } +.leaflet-container .leaflet-control-attribution, +.leaflet-container .leaflet-control-scale { + font-size: 11px; + } +.leaflet-left .leaflet-control-scale { + margin-left: 5px; + } +.leaflet-bottom .leaflet-control-scale { + margin-bottom: 5px; + } +.leaflet-control-scale-line { + border: 2px solid #777; + border-top: none; + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + white-space: nowrap; + overflow: hidden; + -moz-box-sizing: border-box; + box-sizing: border-box; + + background: #fff; + background: rgba(255, 255, 255, 0.5); + } +.leaflet-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + } +.leaflet-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.leaflet-touch .leaflet-control-attribution, +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + box-shadow: none; + } +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; + } + + +/* popup */ + +.leaflet-popup { + position: absolute; + text-align: center; + margin-bottom: 20px; + } +.leaflet-popup-content-wrapper { + padding: 1px; + text-align: left; + border-radius: 12px; + } +.leaflet-popup-content { + margin: 13px 19px; + line-height: 1.4; + } +.leaflet-popup-content p { + margin: 18px 0; + } +.leaflet-popup-tip-container { + width: 40px; + height: 20px; + position: absolute; + left: 50%; + margin-left: -20px; + overflow: hidden; + pointer-events: none; + } +.leaflet-popup-tip { + width: 17px; + height: 17px; + padding: 1px; + + margin: -10px auto 0; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + } +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { + background: white; + color: #333; + box-shadow: 0 3px 14px rgba(0,0,0,0.4); + } +.leaflet-container a.leaflet-popup-close-button { + position: absolute; + top: 0; + right: 0; + padding: 4px 4px 0 0; + border: none; + text-align: center; + width: 18px; + height: 14px; + font: 16px/14px Tahoma, Verdana, sans-serif; + color: #c3c3c3; + text-decoration: none; + font-weight: bold; + background: transparent; + } +.leaflet-container a.leaflet-popup-close-button:hover { + color: #999; + } +.leaflet-popup-scrolled { + overflow: auto; + border-bottom: 1px solid #ddd; + border-top: 1px solid #ddd; + } + +.leaflet-oldie .leaflet-popup-content-wrapper { + -ms-zoom: 1; + } +.leaflet-oldie .leaflet-popup-tip { + width: 24px; + margin: 0 auto; + + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); + } +.leaflet-oldie .leaflet-popup-tip-container { + margin-top: -1px; + } + +.leaflet-oldie .leaflet-control-zoom, +.leaflet-oldie .leaflet-control-layers, +.leaflet-oldie .leaflet-popup-content-wrapper, +.leaflet-oldie .leaflet-popup-tip { + border: 1px solid #999; + } + + +/* div icon */ + +.leaflet-div-icon { + background: #fff; + border: 1px solid #666; + } + + +/* Tooltip */ +/* Base styles for the element that has a tooltip */ +.leaflet-tooltip { + position: absolute; + padding: 6px; + background-color: #fff; + border: 1px solid #fff; + border-radius: 3px; + color: #222; + white-space: nowrap; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + box-shadow: 0 1px 3px rgba(0,0,0,0.4); + } +.leaflet-tooltip.leaflet-clickable { + cursor: pointer; + pointer-events: auto; + } +.leaflet-tooltip-top:before, +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + position: absolute; + pointer-events: none; + border: 6px solid transparent; + background: transparent; + content: ""; + } + +/* Directions */ + +.leaflet-tooltip-bottom { + margin-top: 6px; +} +.leaflet-tooltip-top { + margin-top: -6px; +} +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-top:before { + left: 50%; + margin-left: -6px; + } +.leaflet-tooltip-top:before { + bottom: 0; + margin-bottom: -12px; + border-top-color: #fff; + } +.leaflet-tooltip-bottom:before { + top: 0; + margin-top: -12px; + margin-left: -6px; + border-bottom-color: #fff; + } +.leaflet-tooltip-left { + margin-left: -6px; +} +.leaflet-tooltip-right { + margin-left: 6px; +} +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + top: 50%; + margin-top: -6px; + } +.leaflet-tooltip-left:before { + right: 0; + margin-right: -12px; + border-left-color: #fff; + } +.leaflet-tooltip-right:before { + left: 0; + margin-left: -12px; + border-right-color: #fff; + } diff --git a/fittrackee_client/src/components/Workout/WorkoutDetail/WorkoutMap.vue b/fittrackee_client/src/components/Workout/WorkoutDetail/WorkoutMap.vue new file mode 100644 index 00000000..54afbebb --- /dev/null +++ b/fittrackee_client/src/components/Workout/WorkoutDetail/WorkoutMap.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/fittrackee_client/src/components/Workout/WorkoutDetail/index.vue b/fittrackee_client/src/components/Workout/WorkoutDetail/index.vue new file mode 100644 index 00000000..0182bbc9 --- /dev/null +++ b/fittrackee_client/src/components/Workout/WorkoutDetail/index.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/fittrackee_client/src/locales/en/error.json b/fittrackee_client/src/locales/en/error.json index 4e676adb..d767a02f 100644 --- a/fittrackee_client/src/locales/en/error.json +++ b/fittrackee_client/src/locales/en/error.json @@ -2,6 +2,7 @@ "UNKNOWN": "Error. Please try again or contact the administrator.", "APP_ERROR": "The application seems encounter some issues.
Please try later or contact the administrator.", "NOT_FOUND": { - "PAGE": "Page not found" + "PAGE": "Page not found", + "WORKOUT": "Workout not found" } } \ No newline at end of file diff --git a/fittrackee_client/src/locales/fr/error.json b/fittrackee_client/src/locales/fr/error.json index 76051115..5da3e91c 100644 --- a/fittrackee_client/src/locales/fr/error.json +++ b/fittrackee_client/src/locales/fr/error.json @@ -2,6 +2,7 @@ "UNKNOWN": "Erreur. Veuillez réessayer ou contacter l'administrateur.", "APP_ERROR": "L'application semble rencontrer quelques problèmes.
Veuillez réessayer plus tard ou contacter l'administrateur.", "NOT_FOUND": { - "PAGE": "Page introuvable" + "PAGE": "Page introuvable", + "WORKOUT": "Séance introuvable" } } \ No newline at end of file diff --git a/fittrackee_client/src/router/index.ts b/fittrackee_client/src/router/index.ts index 6e3bf13b..8d8f6771 100644 --- a/fittrackee_client/src/router/index.ts +++ b/fittrackee_client/src/router/index.ts @@ -5,6 +5,7 @@ import { USER_STORE } from '@/store/constants' import Dashboard from '@/views/DashBoard.vue' import LoginOrRegister from '@/views/LoginOrRegister.vue' import NotFoundView from '@/views/NotFoundView.vue' +import Workout from '@/views/Workout.vue' const routes: Array = [ { @@ -24,6 +25,11 @@ const routes: Array = [ component: LoginOrRegister, props: { action: 'register' }, }, + { + path: '/workouts/:workoutId', + name: 'Workout', + component: Workout, + }, { path: '/:pathMatch(.*)*', name: 'not-found', component: NotFoundView }, ] diff --git a/fittrackee_client/src/store/modules/workouts/actions.ts b/fittrackee_client/src/store/modules/workouts/actions.ts index e2ce5e9d..36a42d0a 100644 --- a/fittrackee_client/src/store/modules/workouts/actions.ts +++ b/fittrackee_client/src/store/modules/workouts/actions.ts @@ -49,4 +49,37 @@ export const actions: ActionTree & ): void { getWorkouts(context, payload, 'USER_WORKOUTS') }, + [WORKOUTS_STORE.ACTIONS.GET_WORKOUT]( + context: ActionContext, + workoutId: string + ): void { + context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES) + context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true) + authApi + .get(`workouts/${workoutId}`) + .then((res) => { + if (res.data.status === 'success') { + context.commit( + WORKOUTS_STORE.MUTATIONS.SET_WORKOUT, + res.data.data.workouts[0] + ) + if (res.data.data.workouts[0].with_gpx) { + authApi.get(`workouts/${workoutId}/gpx`).then((res) => { + if (res.data.status === 'success') { + context.commit( + WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_GPX, + res.data.data.gpx + ) + } + }) + } + } else { + handleError(context, null) + } + }) + .catch((error) => handleError(context, error)) + .finally(() => + context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, false) + ) + }, } diff --git a/fittrackee_client/src/store/modules/workouts/enums.ts b/fittrackee_client/src/store/modules/workouts/enums.ts index b86b12f7..57986e74 100644 --- a/fittrackee_client/src/store/modules/workouts/enums.ts +++ b/fittrackee_client/src/store/modules/workouts/enums.ts @@ -1,15 +1,20 @@ export enum WorkoutsActions { GET_CALENDAR_WORKOUTS = 'GET_CALENDAR_WORKOUTS', GET_USER_WORKOUTS = 'GET_USER_WORKOUTS', + GET_WORKOUT = 'GET_WORKOUT', } export enum WorkoutsGetters { CALENDAR_WORKOUTS = 'CALENDAR_WORKOUTS', USER_WORKOUTS = 'USER_WORKOUTS', + WORKOUT = 'WORKOUT', } export enum WorkoutsMutations { EMPTY_WORKOUTS = 'EMPTY_WORKOUTS', SET_CALENDAR_WORKOUTS = 'SET_CALENDAR_WORKOUTS', SET_USER_WORKOUTS = 'SET_USER_WORKOUTS', + SET_WORKOUT = 'SET_WORKOUT', + SET_WORKOUT_GPX = 'SET_WORKOUT_GPX', + SET_WORKOUT_LOADING = 'SET_WORKOUT_LOADING', } diff --git a/fittrackee_client/src/store/modules/workouts/getters.ts b/fittrackee_client/src/store/modules/workouts/getters.ts index 0e48ca85..79391b71 100644 --- a/fittrackee_client/src/store/modules/workouts/getters.ts +++ b/fittrackee_client/src/store/modules/workouts/getters.ts @@ -15,4 +15,7 @@ export const getters: GetterTree & [WORKOUTS_STORE.GETTERS.USER_WORKOUTS]: (state: IWorkoutsState) => { return state.user_workouts }, + [WORKOUTS_STORE.GETTERS.WORKOUT]: (state: IWorkoutsState) => { + return state.workout + }, } diff --git a/fittrackee_client/src/store/modules/workouts/mutations.ts b/fittrackee_client/src/store/modules/workouts/mutations.ts index 65ef2574..5bc6ec67 100644 --- a/fittrackee_client/src/store/modules/workouts/mutations.ts +++ b/fittrackee_client/src/store/modules/workouts/mutations.ts @@ -20,6 +20,24 @@ export const mutations: MutationTree & TWorkoutsMutations = { ) { state.user_workouts = workouts }, + [WORKOUTS_STORE.MUTATIONS.SET_WORKOUT]( + state: IWorkoutsState, + workout: IWorkout + ) { + state.workout.workout = workout + }, + [WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING]( + state: IWorkoutsState, + loading: boolean + ) { + state.workout.loading = loading + }, + [WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_GPX]( + state: IWorkoutsState, + gpx: string + ) { + state.workout.gpx = gpx + }, [WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUTS](state: IWorkoutsState) { state.calendar_workouts = [] state.user_workouts = [] diff --git a/fittrackee_client/src/store/modules/workouts/state.ts b/fittrackee_client/src/store/modules/workouts/state.ts index 7898b295..52193f69 100644 --- a/fittrackee_client/src/store/modules/workouts/state.ts +++ b/fittrackee_client/src/store/modules/workouts/state.ts @@ -1,6 +1,12 @@ import { IWorkoutsState } from '@/store/modules/workouts/types' +import { IWorkout } from '@/types/workouts' export const workoutsState: IWorkoutsState = { calendar_workouts: [], user_workouts: [], + workout: { + gpx: '', + loading: false, + workout: {}, + }, } diff --git a/fittrackee_client/src/store/modules/workouts/types.ts b/fittrackee_client/src/store/modules/workouts/types.ts index 25dad1a6..0a64adda 100644 --- a/fittrackee_client/src/store/modules/workouts/types.ts +++ b/fittrackee_client/src/store/modules/workouts/types.ts @@ -7,11 +7,12 @@ import { import { WORKOUTS_STORE } from '@/store/constants' import { IRootState } from '@/store/modules/root/types' -import { IWorkout, IWorkoutsPayload } from '@/types/workouts' +import { IWorkout, IWorkoutsPayload, IWorkoutState } from '@/types/workouts' export interface IWorkoutsState { user_workouts: IWorkout[] calendar_workouts: IWorkout[] + workout: IWorkoutState } export interface IWorkoutsActions { @@ -23,11 +24,16 @@ export interface IWorkoutsActions { context: ActionContext, payload: IWorkoutsPayload ): void + [WORKOUTS_STORE.ACTIONS.GET_WORKOUT]( + context: ActionContext, + workoutId: string | string[] + ): void } export interface IWorkoutsGetters { [WORKOUTS_STORE.GETTERS.CALENDAR_WORKOUTS](state: IWorkoutsState): IWorkout[] [WORKOUTS_STORE.GETTERS.USER_WORKOUTS](state: IWorkoutsState): IWorkout[] + [WORKOUTS_STORE.GETTERS.WORKOUT](state: IWorkoutsState): IWorkoutState } export type TWorkoutsMutations = { @@ -39,6 +45,12 @@ export type TWorkoutsMutations = { state: S, workouts: IWorkout[] ): void + [WORKOUTS_STORE.MUTATIONS.SET_WORKOUT](state: S, workout: IWorkout): void + [WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_GPX](state: S, gpx: string): void + [WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING]( + state: S, + loading: boolean + ): void [WORKOUTS_STORE.MUTATIONS.EMPTY_WORKOUTS](state: S): void } diff --git a/fittrackee_client/src/togeojson.d.ts b/fittrackee_client/src/togeojson.d.ts new file mode 100644 index 00000000..66dbf54a --- /dev/null +++ b/fittrackee_client/src/togeojson.d.ts @@ -0,0 +1 @@ +declare module '@tmcw/togeojson' diff --git a/fittrackee_client/src/types/geojson.ts b/fittrackee_client/src/types/geojson.ts new file mode 100644 index 00000000..61c6d2b3 --- /dev/null +++ b/fittrackee_client/src/types/geojson.ts @@ -0,0 +1,3 @@ +export interface GeoJSONData { + jsonData?: Record +} diff --git a/fittrackee_client/src/types/workouts.ts b/fittrackee_client/src/types/workouts.ts index 646e4366..82027579 100644 --- a/fittrackee_client/src/types/workouts.ts +++ b/fittrackee_client/src/types/workouts.ts @@ -78,3 +78,9 @@ export interface IWorkoutsPayload { per_page?: number page?: number } + +export interface IWorkoutState { + gpx: string + loading: boolean + workout: IWorkout +} diff --git a/fittrackee_client/src/views/Workout.vue b/fittrackee_client/src/views/Workout.vue new file mode 100644 index 00000000..1d634538 --- /dev/null +++ b/fittrackee_client/src/views/Workout.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/fittrackee_client/src/vue-leaflet.d.ts b/fittrackee_client/src/vue-leaflet.d.ts new file mode 100644 index 00000000..f0fa4d07 --- /dev/null +++ b/fittrackee_client/src/vue-leaflet.d.ts @@ -0,0 +1,11 @@ +declare module '@vue-leaflet/vue-leaflet' { + import type { DefineComponent } from 'vue' + export const LMap: DefineComponent + export const LIcon: DefineComponent + export const LTileLayer: DefineComponent + export const LMarker: DefineComponent + export const LGeoJson: DefineComponent + export const LPolyline: DefineComponent + export const LPolygon: DefineComponent + export const LRectangle: DefineComponent +} diff --git a/fittrackee_client/yarn.lock b/fittrackee_client/yarn.lock index 5affad44..ea1b69f8 100644 --- a/fittrackee_client/yarn.lock +++ b/fittrackee_client/yarn.lock @@ -1085,6 +1085,11 @@ resolved "https://registry.yarnpkg.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz#a53515db25d8038374381b73af20bb4f2e508d87" integrity sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w== +"@tmcw/togeojson@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@tmcw/togeojson/-/togeojson-4.5.0.tgz#9b5c7bdd8c5ad3b9c504824d3cdef9b60edbd206" + integrity sha512-lNuuhW7nvN1T7xII9eRTi9zuPwYfFl43/1u/Xgi88tedX4ePfwJB5dqc31N7z6sWeR+7EES274ESNrK1gsW53A== + "@types/body-parser@*": version "1.19.1" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" @@ -1338,6 +1343,11 @@ "@typescript-eslint/types" "4.28.4" eslint-visitor-keys "^2.0.0" +"@vue-leaflet/vue-leaflet@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@vue-leaflet/vue-leaflet/-/vue-leaflet-0.6.1.tgz#d731a5d2256d049e345f58330616180191d88b12" + integrity sha512-/sm0bdrdftXh5nSGEPsoKrJI1D/GtKiEsBo9X/TA2yu4lYTDcaem6U4t1Ea5CoLleiZRCNUrZr9PG/xHdUPXYA== + "@vue/babel-helper-vue-jsx-merge-props@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.2.1.tgz#31624a7a505fb14da1d58023725a4c5f270e6a81" @@ -6227,6 +6237,11 @@ launch-editor@^2.2.1: chalk "^2.3.0" shell-quote "^1.6.1" +leaflet@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.7.1.tgz#10d684916edfe1bf41d688a3b97127c0322a2a19" + integrity sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw== + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"