Added icons, better navigation, and working translation.
| 
						 | 
					@ -15,7 +15,10 @@
 | 
				
			||||||
   version="1.1"
 | 
					   version="1.1"
 | 
				
			||||||
   id="svg8"
 | 
					   id="svg8"
 | 
				
			||||||
   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
 | 
					   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
 | 
				
			||||||
   sodipodi:docname="icon.svg">
 | 
					   sodipodi:docname="icon.svg"
 | 
				
			||||||
 | 
					   inkscape:export-filename="/home/andrew/github-andrewlalis/Gymboard/design/icon_128.png"
 | 
				
			||||||
 | 
					   inkscape:export-xdpi="384"
 | 
				
			||||||
 | 
					   inkscape:export-ydpi="384">
 | 
				
			||||||
  <defs
 | 
					  <defs
 | 
				
			||||||
     id="defs2" />
 | 
					     id="defs2" />
 | 
				
			||||||
  <sodipodi:namedview
 | 
					  <sodipodi:namedview
 | 
				
			||||||
| 
						 | 
					@ -26,15 +29,15 @@
 | 
				
			||||||
     inkscape:pageopacity="0.0"
 | 
					     inkscape:pageopacity="0.0"
 | 
				
			||||||
     inkscape:pageshadow="2"
 | 
					     inkscape:pageshadow="2"
 | 
				
			||||||
     inkscape:zoom="22.4"
 | 
					     inkscape:zoom="22.4"
 | 
				
			||||||
     inkscape:cx="16.756057"
 | 
					     inkscape:cx="2.8035079"
 | 
				
			||||||
     inkscape:cy="13.672431"
 | 
					     inkscape:cy="15.519297"
 | 
				
			||||||
     inkscape:document-units="px"
 | 
					     inkscape:document-units="px"
 | 
				
			||||||
     inkscape:current-layer="layer1"
 | 
					     inkscape:current-layer="layer1"
 | 
				
			||||||
     showgrid="false"
 | 
					     showgrid="false"
 | 
				
			||||||
     units="px"
 | 
					     units="px"
 | 
				
			||||||
     inkscape:window-width="1920"
 | 
					     inkscape:window-width="1920"
 | 
				
			||||||
     inkscape:window-height="1009"
 | 
					     inkscape:window-height="1049"
 | 
				
			||||||
     inkscape:window-x="0"
 | 
					     inkscape:window-x="1920"
 | 
				
			||||||
     inkscape:window-y="0"
 | 
					     inkscape:window-y="0"
 | 
				
			||||||
     inkscape:window-maximized="1" />
 | 
					     inkscape:window-maximized="1" />
 | 
				
			||||||
  <metadata
 | 
					  <metadata
 | 
				
			||||||
| 
						 | 
					@ -45,7 +48,7 @@
 | 
				
			||||||
        <dc:format>image/svg+xml</dc:format>
 | 
					        <dc:format>image/svg+xml</dc:format>
 | 
				
			||||||
        <dc:type
 | 
					        <dc:type
 | 
				
			||||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
					           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
				
			||||||
        <dc:title></dc:title>
 | 
					        <dc:title />
 | 
				
			||||||
      </cc:Work>
 | 
					      </cc:Work>
 | 
				
			||||||
    </rdf:RDF>
 | 
					    </rdf:RDF>
 | 
				
			||||||
  </metadata>
 | 
					  </metadata>
 | 
				
			||||||
| 
						 | 
					@ -55,55 +58,55 @@
 | 
				
			||||||
     id="layer1"
 | 
					     id="layer1"
 | 
				
			||||||
     transform="translate(0,-288.53333)">
 | 
					     transform="translate(0,-288.53333)">
 | 
				
			||||||
    <rect
 | 
					    <rect
 | 
				
			||||||
       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.96639419;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
				
			||||||
       id="rect815"
 | 
					       id="rect815"
 | 
				
			||||||
       width="1.5535041"
 | 
					       width="1.8913983"
 | 
				
			||||||
       height="2.5136223"
 | 
					       height="3.0603466"
 | 
				
			||||||
       x="0.8639006"
 | 
					       x="0.13103379"
 | 
				
			||||||
       y="290.76736"
 | 
					       y="290.25571"
 | 
				
			||||||
       rx="0.26458335" />
 | 
					       rx="0.32213143" />
 | 
				
			||||||
    <rect
 | 
					    <rect
 | 
				
			||||||
       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.96639419;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
				
			||||||
       id="rect819"
 | 
					       id="rect819"
 | 
				
			||||||
       width="3.1289527"
 | 
					       width="3.809514"
 | 
				
			||||||
       height="0.90185839"
 | 
					       height="1.0980167"
 | 
				
			||||||
       x="2.6688571"
 | 
					       x="2.3285766"
 | 
				
			||||||
       y="291.52304" />
 | 
					       y="291.17575" />
 | 
				
			||||||
    <rect
 | 
					    <rect
 | 
				
			||||||
       rx="0.26458335"
 | 
					       rx="0.32213143"
 | 
				
			||||||
       y="290.76736"
 | 
					       y="290.25571"
 | 
				
			||||||
       x="6.049262"
 | 
					       x="6.4442344"
 | 
				
			||||||
       height="2.5136223"
 | 
					       height="3.0603466"
 | 
				
			||||||
       width="1.5535041"
 | 
					       width="1.8913983"
 | 
				
			||||||
       id="rect821"
 | 
					       id="rect821"
 | 
				
			||||||
       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
 | 
					       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.96639419;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
 | 
				
			||||||
    <rect
 | 
					    <rect
 | 
				
			||||||
       y="292.4281"
 | 
					       y="292.27765"
 | 
				
			||||||
       x="2.8526049"
 | 
					       x="2.5522902"
 | 
				
			||||||
       height="0.58447266"
 | 
					       height="0.71159816"
 | 
				
			||||||
       width="2.761457"
 | 
					       width="3.3620865"
 | 
				
			||||||
       id="rect823"
 | 
					       id="rect823"
 | 
				
			||||||
       style="opacity:1;fill:#646464;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
 | 
					       style="opacity:1;fill:#646464;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.96639419;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
 | 
				
			||||||
    <rect
 | 
					    <rect
 | 
				
			||||||
       style="opacity:1;fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					       style="opacity:1;fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.96639419;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
				
			||||||
       id="rect825"
 | 
					       id="rect825"
 | 
				
			||||||
       width="2.4071047"
 | 
					       width="2.9306607"
 | 
				
			||||||
       height="0.58447266"
 | 
					       height="0.71159816"
 | 
				
			||||||
       x="3.0297811"
 | 
					       x="2.7680032"
 | 
				
			||||||
       y="293.01257" />
 | 
					       y="292.98926" />
 | 
				
			||||||
    <rect
 | 
					    <rect
 | 
				
			||||||
       y="293.59705"
 | 
					       y="293.70084"
 | 
				
			||||||
       x="3.201052"
 | 
					       x="2.9765263"
 | 
				
			||||||
       height="0.58447266"
 | 
					       height="0.71159816"
 | 
				
			||||||
       width="2.064563"
 | 
					       width="2.5136149"
 | 
				
			||||||
       id="rect827"
 | 
					       id="rect827"
 | 
				
			||||||
       style="opacity:1;fill:#a8a8a8;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
 | 
					       style="opacity:1;fill:#a8a8a8;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.96639419;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
 | 
				
			||||||
    <rect
 | 
					    <rect
 | 
				
			||||||
       style="opacity:1;fill:#d2d2d2;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					       style="opacity:1;fill:#d2d2d2;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.96639419;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
				
			||||||
       id="rect829"
 | 
					       id="rect829"
 | 
				
			||||||
       width="1.6865873"
 | 
					       width="2.0534277"
 | 
				
			||||||
       height="0.58447266"
 | 
					       height="0.71159816"
 | 
				
			||||||
       x="3.3900397"
 | 
					       x="3.2066195"
 | 
				
			||||||
       y="294.18152" />
 | 
					       y="294.41245" />
 | 
				
			||||||
  </g>
 | 
					  </g>
 | 
				
			||||||
</svg>
 | 
					</svg>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.2 KiB  | 
| 
		 Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 99 KiB  | 
| 
		 Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 1.0 KiB  | 
| 
		 Before Width: | Height: | Size: 859 B After Width: | Height: | Size: 375 B  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 516 B  | 
| 
		 After Width: | Height: | Size: 723 B  | 
| 
		 Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 872 B  | 
| 
						 | 
					@ -4,12 +4,6 @@ const api = axios.create({
 | 
				
			||||||
  baseURL: 'http://localhost:8080',
 | 
					  baseURL: 'http://localhost:8080',
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GymIdentifiable {
 | 
					 | 
				
			||||||
  countryCode: string;
 | 
					 | 
				
			||||||
  cityShortName: string;
 | 
					 | 
				
			||||||
  shortName: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type Gym = {
 | 
					export type Gym = {
 | 
				
			||||||
  countryCode: string;
 | 
					  countryCode: string;
 | 
				
			||||||
  countryName: string;
 | 
					  countryName: string;
 | 
				
			||||||
| 
						 | 
					@ -48,7 +42,3 @@ export async function getGym(
 | 
				
			||||||
    streetAddress: d.streetAddress,
 | 
					    streetAddress: d.streetAddress,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export function getGymRoute(gym: GymIdentifiable): string {
 | 
					 | 
				
			||||||
  return `/g/${gym.countryCode}/${gym.cityShortName}/${gym.shortName}`;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,8 +12,8 @@
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import {getGymRoute} from 'src/api/gymboard-api';
 | 
					 | 
				
			||||||
import {GymSearchResult} from 'src/api/gymboard-search';
 | 
					import {GymSearchResult} from 'src/api/gymboard-search';
 | 
				
			||||||
 | 
					import {getGymRoute} from 'src/router/gym-routing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
  gym: GymSearchResult
 | 
					  gym: GymSearchResult
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="row justify-center">
 | 
				
			||||||
 | 
					    <div class="col-xs-12 col-sm-8 col-md-6 col-lg-4">
 | 
				
			||||||
 | 
					      <slot>
 | 
				
			||||||
 | 
					        <p>Form content.</p>
 | 
				
			||||||
 | 
					      </slot>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,18 @@
 | 
				
			||||||
// This is just an example,
 | 
					 | 
				
			||||||
// so you can safely delete all default props below
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  failed: 'Action failed',
 | 
					  mainLayout: {
 | 
				
			||||||
  success: 'Action was successful'
 | 
					    language: 'Language',
 | 
				
			||||||
 | 
					    pages: 'Pages'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  gymPage: {
 | 
				
			||||||
 | 
					    home: 'Home',
 | 
				
			||||||
 | 
					    submit: 'Submit',
 | 
				
			||||||
 | 
					    leaderboard: 'Leaderboard',
 | 
				
			||||||
 | 
					    submitPage: {
 | 
				
			||||||
 | 
					      exercise: 'Exercise',
 | 
				
			||||||
 | 
					      weight: 'Weight',
 | 
				
			||||||
 | 
					      reps: 'Repetitions',
 | 
				
			||||||
 | 
					      date: 'Date',
 | 
				
			||||||
 | 
					      submit: 'Submit'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
import enUS from './en-US';
 | 
					import enUS from './en-US';
 | 
				
			||||||
 | 
					import nlNL from './nl-NL';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  'en-US': enUS
 | 
					  'en-US': enUS,
 | 
				
			||||||
 | 
					  'nl-NL': nlNL
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  mainLayout: {
 | 
				
			||||||
 | 
					    language: 'Taal',
 | 
				
			||||||
 | 
					    pages: 'Pagina\'s'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  gymPage: {
 | 
				
			||||||
 | 
					    home: 'Thuis',
 | 
				
			||||||
 | 
					    submit: 'Indienen',
 | 
				
			||||||
 | 
					    leaderboard: 'Scorebord',
 | 
				
			||||||
 | 
					    submitPage: {
 | 
				
			||||||
 | 
					      exercise: 'Oefening',
 | 
				
			||||||
 | 
					      weight: 'Gewicht',
 | 
				
			||||||
 | 
					      reps: 'Repetities',
 | 
				
			||||||
 | 
					      date: 'Datum',
 | 
				
			||||||
 | 
					      submit: 'Sturen'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,23 @@
 | 
				
			||||||
        <q-toolbar-title>
 | 
					        <q-toolbar-title>
 | 
				
			||||||
          <router-link to="/" style="text-decoration: none; color: inherit;">Gymboard</router-link>
 | 
					          <router-link to="/" style="text-decoration: none; color: inherit;">Gymboard</router-link>
 | 
				
			||||||
        </q-toolbar-title>
 | 
					        </q-toolbar-title>
 | 
				
			||||||
 | 
					        <q-select
 | 
				
			||||||
 | 
					          v-model="i18n.locale.value"
 | 
				
			||||||
 | 
					          :options="localeOptions"
 | 
				
			||||||
 | 
					          :label="$t('mainLayout.language')"
 | 
				
			||||||
 | 
					          dense
 | 
				
			||||||
 | 
					          borderless
 | 
				
			||||||
 | 
					          emit-value
 | 
				
			||||||
 | 
					          map-options
 | 
				
			||||||
 | 
					          options-dense
 | 
				
			||||||
 | 
					          filled
 | 
				
			||||||
 | 
					          hide-bottom-space
 | 
				
			||||||
 | 
					          dark
 | 
				
			||||||
 | 
					          options-dark
 | 
				
			||||||
 | 
					          label-color="white"
 | 
				
			||||||
 | 
					          options-selected-class="text-grey"
 | 
				
			||||||
 | 
					          style="min-width: 150px"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
      </q-toolbar>
 | 
					      </q-toolbar>
 | 
				
			||||||
    </q-header>
 | 
					    </q-header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +43,7 @@
 | 
				
			||||||
        <q-item-label
 | 
					        <q-item-label
 | 
				
			||||||
          header
 | 
					          header
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          Pages
 | 
					          {{ $t('mainLayout.pages') }}
 | 
				
			||||||
        </q-item-label>
 | 
					        </q-item-label>
 | 
				
			||||||
        <q-item clickable>Gyms</q-item>
 | 
					        <q-item clickable>Gyms</q-item>
 | 
				
			||||||
        <q-item clickable>Global Leaderboard</q-item>
 | 
					        <q-item clickable>Global Leaderboard</q-item>
 | 
				
			||||||
| 
						 | 
					@ -39,21 +56,19 @@
 | 
				
			||||||
  </q-layout>
 | 
					  </q-layout>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { defineComponent, ref } from 'vue';
 | 
					import { ref } from 'vue';
 | 
				
			||||||
 | 
					import {useI18n} from 'vue-i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineComponent({
 | 
					const i18n = useI18n({useScope: 'global'});
 | 
				
			||||||
  name: 'MainLayout',
 | 
					const localeOptions = [
 | 
				
			||||||
 | 
					  { value: 'en-US', label: 'English' },
 | 
				
			||||||
 | 
					  { value: 'nl-NL', label: 'Nederlands' }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setup () {
 | 
					const leftDrawerOpen = ref(false);
 | 
				
			||||||
    const leftDrawerOpen = ref(false)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					function toggleLeftDrawer () {
 | 
				
			||||||
      leftDrawerOpen,
 | 
					  leftDrawerOpen.value = !leftDrawerOpen.value
 | 
				
			||||||
      toggleLeftDrawer () {
 | 
					}
 | 
				
			||||||
        leftDrawerOpen.value = !leftDrawerOpen.value
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,13 @@
 | 
				
			||||||
  <q-page>
 | 
					  <q-page>
 | 
				
			||||||
    <h3>Gym Home Page</h3>
 | 
					    <h3>Gym Home Page</h3>
 | 
				
			||||||
    <p>
 | 
					    <p>
 | 
				
			||||||
      Put stuff here.
 | 
					      Maybe put an image of the gym here?
 | 
				
			||||||
 | 
					    </p>
 | 
				
			||||||
 | 
					    <p>
 | 
				
			||||||
 | 
					      Put a description of the gym here?
 | 
				
			||||||
 | 
					    </p>
 | 
				
			||||||
 | 
					    <p>
 | 
				
			||||||
 | 
					      Maybe show a snapshot of some recent lifts?
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
  </q-page>
 | 
					  </q-page>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,30 +1,38 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <StandardCenteredPage v-if="gym">
 | 
					  <StandardCenteredPage v-if="gym">
 | 
				
			||||||
    <h3 class="q-my-sm">{{ gym.displayName }}</h3>
 | 
					    <h3 class="q-my-md text-center">{{ gym.displayName }}</h3>
 | 
				
			||||||
    <q-btn-group>
 | 
					    <q-btn-group spread square push>
 | 
				
			||||||
      <q-btn label="Home" :to="baseGymPath"/>
 | 
					      <q-btn
 | 
				
			||||||
      <q-btn label="Submit" :to="baseGymPath + '/submit'"/>
 | 
					        :label="$t('gymPage.home')"
 | 
				
			||||||
      <q-btn label="Leaderboard" :to="baseGymPath + '/lb'"/>
 | 
					        :to="getGymRoute(gym)"
 | 
				
			||||||
 | 
					        :color="homePageSelected ? 'primary' : 'secondary'"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <q-btn
 | 
				
			||||||
 | 
					        :label="$t('gymPage.submit')"
 | 
				
			||||||
 | 
					        :to="getGymRoute(gym) + '/submit'"
 | 
				
			||||||
 | 
					        :color="submitPageSelected ? 'primary' : 'secondary'"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <q-btn
 | 
				
			||||||
 | 
					        :label="$t('gymPage.leaderboard')"
 | 
				
			||||||
 | 
					        :to="getGymRoute(gym) + '/leaderboard'"
 | 
				
			||||||
 | 
					        :color="leaderboardPageSelected ? 'primary' : 'secondary'"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
    </q-btn-group>
 | 
					    </q-btn-group>
 | 
				
			||||||
    <router-view/>
 | 
					    <router-view/>
 | 
				
			||||||
  </StandardCenteredPage>
 | 
					  </StandardCenteredPage>
 | 
				
			||||||
  <q-page v-if="notFound">
 | 
					 | 
				
			||||||
    <h3>Gym not found! Oh no!!!</h3>
 | 
					 | 
				
			||||||
    <router-link to="/">Back</router-link>
 | 
					 | 
				
			||||||
  </q-page>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { onMounted, ref, Ref } from 'vue';
 | 
					import {computed, onMounted, ref, Ref} from 'vue';
 | 
				
			||||||
import { getGym, Gym } from 'src/api/gymboard-api';
 | 
					import { getGym, Gym } from 'src/api/gymboard-api';
 | 
				
			||||||
import { useRoute } from 'vue-router';
 | 
					import {useRoute, useRouter} from 'vue-router';
 | 
				
			||||||
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
 | 
					import StandardCenteredPage from 'components/StandardCenteredPage.vue';
 | 
				
			||||||
 | 
					import {getGymRoute} from 'src/router/gym-routing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const route = useRoute();
 | 
					const route = useRoute();
 | 
				
			||||||
const baseGymPath = `/g/${route.params.countryCode}/${route.params.cityShortName}/${route.params.gymShortName}`;
 | 
					const router = useRouter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const gym: Ref<Gym | undefined> = ref<Gym>();
 | 
					const gym: Ref<Gym | undefined> = ref<Gym>();
 | 
				
			||||||
const notFound: Ref<boolean | undefined> = ref<boolean>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Once the component is mounted, load the gym that we're at.
 | 
					// Once the component is mounted, load the gym that we're at.
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
| 
						 | 
					@ -34,10 +42,13 @@ onMounted(async () => {
 | 
				
			||||||
      route.params.cityShortName as string,
 | 
					      route.params.cityShortName as string,
 | 
				
			||||||
      route.params.gymShortName as string
 | 
					      route.params.gymShortName as string
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    notFound.value = false;
 | 
					 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    console.error(error);
 | 
					    console.error(error);
 | 
				
			||||||
    notFound.value = true;
 | 
					    await router.push('/');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const homePageSelected = computed(() => gym.value && getGymRoute(gym.value) === route.fullPath);
 | 
				
			||||||
 | 
					const submitPageSelected = computed(() => gym.value && route.fullPath === getGymRoute(gym.value) + '/submit');
 | 
				
			||||||
 | 
					const leaderboardPageSelected = computed(() => gym.value && route.fullPath === getGymRoute(gym.value) + '/leaderboard');
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,31 +1,91 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <q-page v-if="gym">
 | 
					  <q-page v-if="gym">
 | 
				
			||||||
    <h3>Submit Your Lift for {{ gym.displayName }}</h3>
 | 
					    <q-form @submit="onSubmitted">
 | 
				
			||||||
    <q-form>
 | 
					      <SlimForm>
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					          <q-select
 | 
				
			||||||
 | 
					            :options="exercises"
 | 
				
			||||||
 | 
					            v-model="submissionModel.exercise"
 | 
				
			||||||
 | 
					            :label="$t('gymPage.submitPage.exercise')"
 | 
				
			||||||
 | 
					            class="col-12"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					          <q-input
 | 
				
			||||||
 | 
					            :label="$t('gymPage.submitPage.weight')"
 | 
				
			||||||
 | 
					            type="number"
 | 
				
			||||||
 | 
					            v-model="submissionModel.weight"
 | 
				
			||||||
 | 
					            class="col-8"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <q-select
 | 
				
			||||||
 | 
					            :options="weightUnits"
 | 
				
			||||||
 | 
					            v-model="submissionModel.weightUnit"
 | 
				
			||||||
 | 
					            class="col-4 q-pl-sm"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					          <q-input
 | 
				
			||||||
 | 
					            :label="$t('gymPage.submitPage.reps')"
 | 
				
			||||||
 | 
					            type="number"
 | 
				
			||||||
 | 
					            v-model="submissionModel.repCount"
 | 
				
			||||||
 | 
					            class="col-12"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					          <q-input
 | 
				
			||||||
 | 
					            v-model="submissionModel.date"
 | 
				
			||||||
 | 
					            type="date"
 | 
				
			||||||
 | 
					            :label="$t('gymPage.submitPage.date')"
 | 
				
			||||||
 | 
					            class="col-12"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					          <q-btn
 | 
				
			||||||
 | 
					            :label="$t('gymPage.submitPage.submit')"
 | 
				
			||||||
 | 
					            color="primary"
 | 
				
			||||||
 | 
					            type="submit"
 | 
				
			||||||
 | 
					            class="q-mt-sm"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </SlimForm>
 | 
				
			||||||
    </q-form>
 | 
					    </q-form>
 | 
				
			||||||
  </q-page>
 | 
					  </q-page>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import {onMounted, ref, Ref} from 'vue';
 | 
					import {onMounted, ref, Ref} from 'vue';
 | 
				
			||||||
import {getGym, Gym} from 'src/api/gymboard-api';
 | 
					import {Gym} from 'src/api/gymboard-api';
 | 
				
			||||||
import {useRoute} from 'vue-router';
 | 
					import {getGymFromRoute} from 'src/router/gym-routing';
 | 
				
			||||||
 | 
					import SlimForm from 'components/SlimForm.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// interface Props {
 | 
				
			||||||
 | 
					//   gym: Gym
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// const props = defineProps<Props>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const route = useRoute();
 | 
					 | 
				
			||||||
const gym: Ref<Gym | undefined> = ref<Gym>();
 | 
					const gym: Ref<Gym | undefined> = ref<Gym>();
 | 
				
			||||||
 | 
					let submissionModel = ref({
 | 
				
			||||||
 | 
					  exercise: null,
 | 
				
			||||||
 | 
					  weight: null,
 | 
				
			||||||
 | 
					  weightUnit: 'Kg',
 | 
				
			||||||
 | 
					  repCount: 1,
 | 
				
			||||||
 | 
					  date: new Date().toLocaleDateString('en-CA')
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					const weightUnits = ['Kg', 'Lbs'];
 | 
				
			||||||
 | 
					const exercises = ['Bench Press', 'Squat', 'Deadlift'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Make it possible to pass the gym to this via props instead.
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    gym.value = await getGym(
 | 
					    gym.value = await getGymFromRoute();
 | 
				
			||||||
      route.params.countryCode as string,
 | 
					 | 
				
			||||||
      route.params.cityShortName as string,
 | 
					 | 
				
			||||||
      route.params.gymShortName as string
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    console.error(error);
 | 
					    console.error(error);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
})
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function onSubmitted() {
 | 
				
			||||||
 | 
					  console.log('submitted');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped>
 | 
					<style scoped>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					import {useRoute} from 'vue-router';
 | 
				
			||||||
 | 
					import {getGym, Gym} from 'src/api/gymboard-api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Any object that contains the properties needed to identify a single gym.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export interface GymRoutable {
 | 
				
			||||||
 | 
					  countryCode: string;
 | 
				
			||||||
 | 
					  cityShortName: string;
 | 
				
			||||||
 | 
					  shortName: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Gets the route that can be used to navigate to a particular gym's page.
 | 
				
			||||||
 | 
					 * @param gym The gym to get the route for.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function getGymRoute(gym: GymRoutable): string {
 | 
				
			||||||
 | 
					  return `/gyms/${gym.countryCode}/${gym.cityShortName}/${gym.shortName}`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Gets the gym that's referred to by the current route's path params.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function getGymFromRoute(): Promise<Gym> {
 | 
				
			||||||
 | 
					  const route = useRoute();
 | 
				
			||||||
 | 
					  return await getGym(
 | 
				
			||||||
 | 
					    route.params.countryCode as string,
 | 
				
			||||||
 | 
					    route.params.cityShortName as string,
 | 
				
			||||||
 | 
					    route.params.gymShortName as string
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -13,12 +13,12 @@ const routes: RouteRecordRaw[] = [
 | 
				
			||||||
    children: [
 | 
					    children: [
 | 
				
			||||||
      { path: '', component: IndexPage },
 | 
					      { path: '', component: IndexPage },
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        path: 'g/:countryCode/:cityShortName/:gymShortName',
 | 
					        path: 'gyms/:countryCode/:cityShortName/:gymShortName',
 | 
				
			||||||
        component: GymPage,
 | 
					        component: GymPage,
 | 
				
			||||||
        children: [
 | 
					        children: [
 | 
				
			||||||
          { path: '', component: GymHomePage },
 | 
					          { path: '', component: GymHomePage },
 | 
				
			||||||
          { path: 'submit', component: GymSubmissionPage },
 | 
					          { path: 'submit', component: GymSubmissionPage },
 | 
				
			||||||
          { path: 'lb', component: GymLeaderboardsPage }
 | 
					          { path: 'leaderboard', component: GymLeaderboardsPage }
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||