Removed old pages, added users search.
This commit is contained in:
parent
f92577b76a
commit
08db8b718c
|
@ -15,4 +15,7 @@ export interface GymSearchResult {
|
||||||
export interface UserSearchResult {
|
export interface UserSearchResult {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
submissionCount: number;
|
||||||
|
accountPrivate: boolean;
|
||||||
|
locale: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,12 @@
|
||||||
{{ submission.rawWeight }} {{ WeightUnitUtil.toAbbreviation(submission.weightUnit) }}
|
{{ submission.rawWeight }} {{ WeightUnitUtil.toAbbreviation(submission.weightUnit) }}
|
||||||
{{ submission.exercise.displayName }}
|
{{ submission.exercise.displayName }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item-label caption>
|
<q-item-label caption v-if="showName">
|
||||||
{{ submission.user.name }}
|
{{ submission.user.name }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
|
<q-item-label caption v-if="showGym">
|
||||||
|
{{ submission.gym.displayName }}
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section side top>
|
<q-item-section side top>
|
||||||
{{ submission.performedAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
{{ submission.performedAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
||||||
|
@ -21,8 +24,13 @@ import { DateTime } from 'luxon';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
submission: ExerciseSubmission;
|
submission: ExerciseSubmission;
|
||||||
|
showName?: boolean;
|
||||||
|
showGym?: boolean;
|
||||||
}
|
}
|
||||||
defineProps<Props>();
|
withDefaults(defineProps<Props>(), {
|
||||||
|
showName: true,
|
||||||
|
showGym: true,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<q-item :to="`/users/${user.id}`">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{ user.name }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side top>
|
||||||
|
<q-badge color="primary" :label="submissionCountLabel"/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {UserSearchResult} from 'src/api/search/models';
|
||||||
|
import {computed} from 'vue';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
user: UserSearchResult;
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
const submissionCountLabel = computed(() => {
|
||||||
|
const c = props.user.submissionCount;
|
||||||
|
if (c < 1000) return '' + c;
|
||||||
|
if (c < 1000000) return Math.floor(c / 1000) + 'k';
|
||||||
|
return Math.floor(c / 1000000) + 'm';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -48,6 +48,9 @@ export default {
|
||||||
accountPrivate: 'This account is private.',
|
accountPrivate: 'This account is private.',
|
||||||
recentLifts: 'Recent Lifts',
|
recentLifts: 'Recent Lifts',
|
||||||
},
|
},
|
||||||
|
userSearchPage: {
|
||||||
|
searchHint: 'Search for a user'
|
||||||
|
},
|
||||||
userSettingsPage: {
|
userSettingsPage: {
|
||||||
title: 'Account Settings',
|
title: 'Account Settings',
|
||||||
password: 'Password',
|
password: 'Password',
|
||||||
|
|
|
@ -48,6 +48,9 @@ export default {
|
||||||
accountPrivate: 'Dit account is privaat.',
|
accountPrivate: 'Dit account is privaat.',
|
||||||
recentLifts: 'Recente liften',
|
recentLifts: 'Recente liften',
|
||||||
},
|
},
|
||||||
|
userSearchPage: {
|
||||||
|
searchHint: 'Zoek een gebruiker'
|
||||||
|
},
|
||||||
userSettingsPage: {
|
userSettingsPage: {
|
||||||
title: 'Account instellingen',
|
title: 'Account instellingen',
|
||||||
password: 'Wachtwoord',
|
password: 'Wachtwoord',
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
<q-item clickable to="/">Gyms</q-item>
|
<q-item clickable to="/">Gyms</q-item>
|
||||||
<q-item clickable>Global Leaderboard</q-item>
|
<q-item clickable>Global Leaderboard</q-item>
|
||||||
<q-item clickable to="/testing">Testing Page</q-item>
|
<q-item clickable to="/users">Users</q-item>
|
||||||
<q-item clickable to="/about">About</q-item>
|
<q-item clickable to="/about">About</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-drawer>
|
</q-drawer>
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
<template>
|
|
||||||
<StandardCenteredPage>
|
|
||||||
<h3 class="text-center">About Gymboard</h3>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Error fugit quia
|
|
||||||
laboriosam eaque? Deserunt, accusantium dicta assumenda debitis incidunt
|
|
||||||
eius provident magnam, est quasi officia voluptas, nam neque omnis
|
|
||||||
reiciendis.
|
|
||||||
</p>
|
|
||||||
</StandardCenteredPage>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import StandardCenteredPage from 'src/components/StandardCenteredPage.vue';
|
|
||||||
</script>
|
|
|
@ -1,33 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-page>
|
|
||||||
<StandardCenteredPage>
|
|
||||||
<h3>Testing Page</h3>
|
|
||||||
<p>
|
|
||||||
Use this page to test new functionality, before adding it to the main
|
|
||||||
app. This page should be hidden on production.
|
|
||||||
</p>
|
|
||||||
<div style="border: 3px solid red">
|
|
||||||
<h4>Auth Test</h4>
|
|
||||||
<q-btn label="Do auth" @click="doAuth()" />
|
|
||||||
<q-btn label="Logout" @click="api.auth.logout(authStore)" />
|
|
||||||
</div>
|
|
||||||
</StandardCenteredPage>
|
|
||||||
</q-page>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import StandardCenteredPage from 'src/components/StandardCenteredPage.vue';
|
|
||||||
import api from 'src/api/main';
|
|
||||||
import { useAuthStore } from 'stores/auth-store';
|
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
|
||||||
|
|
||||||
async function doAuth() {
|
|
||||||
await api.auth.login(authStore, {
|
|
||||||
email: 'andrew.lalis@example.com',
|
|
||||||
password: 'testpass',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
|
@ -19,6 +19,7 @@
|
||||||
v-for="sub in recentSubmissions"
|
v-for="sub in recentSubmissions"
|
||||||
:submission="sub"
|
:submission="sub"
|
||||||
:key="sub.id"
|
:key="sub.id"
|
||||||
|
:show-name="false"
|
||||||
/>
|
/>
|
||||||
</q-list>
|
</q-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<template>
|
||||||
|
<q-page>
|
||||||
|
<StandardCenteredPage>
|
||||||
|
<q-input
|
||||||
|
v-model="searchQuery"
|
||||||
|
:label="$t('userSearchPage.searchHint')"
|
||||||
|
clearable
|
||||||
|
:loading="searchBarLoadingState"
|
||||||
|
@update:modelValue="onSearchQueryUpdated"
|
||||||
|
class="q-mt-lg"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="search"/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<q-list>
|
||||||
|
<UserSearchResultListItem
|
||||||
|
v-for="result in searchResults"
|
||||||
|
:user="result"
|
||||||
|
:key="result.id"
|
||||||
|
/>
|
||||||
|
</q-list>
|
||||||
|
</StandardCenteredPage>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
||||||
|
import {onMounted, Ref, ref} from 'vue';
|
||||||
|
import {UserSearchResult} from 'src/api/search/models';
|
||||||
|
import {useRoute, useRouter} from 'vue-router';
|
||||||
|
import {sleep} from 'src/utils';
|
||||||
|
import searchApi from 'src/api/search';
|
||||||
|
import UserSearchResultListItem from 'components/UserSearchResultListItem.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const searchQuery = ref('');
|
||||||
|
const searchBarLoadingState = ref(false);
|
||||||
|
const searchResults: Ref<UserSearchResult[]> = ref([]);
|
||||||
|
|
||||||
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
const SEARCH_TIMEOUT = 500;
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (route.query.search_query) {
|
||||||
|
searchQuery.value = route.query.search_query as string;
|
||||||
|
searchBarLoadingState.value = true;
|
||||||
|
await sleep(500);
|
||||||
|
await doSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onSearchQueryUpdated() {
|
||||||
|
if (timer !== null) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
timer = setTimeout(doSearch, SEARCH_TIMEOUT);
|
||||||
|
searchBarLoadingState.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doSearch() {
|
||||||
|
const searchQueryText = searchQuery.value;
|
||||||
|
let query = {};
|
||||||
|
if (searchQueryText && searchQueryText.length > 0) {
|
||||||
|
query = { search_query: searchQueryText };
|
||||||
|
}
|
||||||
|
await router.push({ path: '/users', query: query });
|
||||||
|
try {
|
||||||
|
searchResults.value = await searchApi.searchUsers(searchQueryText);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
} finally {
|
||||||
|
searchBarLoadingState.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -34,6 +34,7 @@
|
||||||
v-for="sub in recentSubmissions"
|
v-for="sub in recentSubmissions"
|
||||||
:submission="sub"
|
:submission="sub"
|
||||||
:key="sub.id"
|
:key="sub.id"
|
||||||
|
:show-gym="false"
|
||||||
/>
|
/>
|
||||||
</q-list>
|
</q-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
v-for="sub in submissions"
|
v-for="sub in submissions"
|
||||||
:submission="sub"
|
:submission="sub"
|
||||||
:key="sub.id"
|
:key="sub.id"
|
||||||
|
:show-gym="false"
|
||||||
/>
|
/>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-page>
|
</q-page>
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { RouteRecordRaw } from 'vue-router';
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
import MainLayout from 'layouts/MainLayout.vue';
|
import MainLayout from 'layouts/MainLayout.vue';
|
||||||
import IndexPage from 'pages/IndexPage.vue';
|
import GymSearchPage from 'pages/GymSearchPage.vue';
|
||||||
import AboutPage from 'pages/AboutPage.vue';
|
|
||||||
import GymPage from 'pages/gym/GymPage.vue';
|
import GymPage from 'pages/gym/GymPage.vue';
|
||||||
import GymSubmissionPage from 'pages/gym/GymSubmissionPage.vue';
|
import GymSubmissionPage from 'pages/gym/GymSubmissionPage.vue';
|
||||||
import GymHomePage from 'pages/gym/GymHomePage.vue';
|
import GymHomePage from 'pages/gym/GymHomePage.vue';
|
||||||
import GymLeaderboardsPage from 'pages/gym/GymLeaderboardsPage.vue';
|
import GymLeaderboardsPage from 'pages/gym/GymLeaderboardsPage.vue';
|
||||||
import TestingPage from 'pages/TestingPage.vue';
|
|
||||||
import LoginPage from 'pages/auth/LoginPage.vue';
|
import LoginPage from 'pages/auth/LoginPage.vue';
|
||||||
import RegisterPage from 'pages/auth/RegisterPage.vue';
|
import RegisterPage from 'pages/auth/RegisterPage.vue';
|
||||||
import RegistrationSuccessPage from 'pages/auth/RegistrationSuccessPage.vue';
|
import RegistrationSuccessPage from 'pages/auth/RegistrationSuccessPage.vue';
|
||||||
|
@ -14,6 +12,7 @@ import ActivationPage from 'pages/auth/ActivationPage.vue';
|
||||||
import SubmissionPage from 'pages/SubmissionPage.vue';
|
import SubmissionPage from 'pages/SubmissionPage.vue';
|
||||||
import UserPage from 'pages/UserPage.vue';
|
import UserPage from 'pages/UserPage.vue';
|
||||||
import UserSettingsPage from 'pages/auth/UserSettingsPage.vue';
|
import UserSettingsPage from 'pages/auth/UserSettingsPage.vue';
|
||||||
|
import UserSearchPage from 'pages/UserSearchPage.vue';
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
// Auth-related pages, which live outside the main layout.
|
// Auth-related pages, which live outside the main layout.
|
||||||
|
@ -27,8 +26,8 @@ const routes: RouteRecordRaw[] = [
|
||||||
path: '/',
|
path: '/',
|
||||||
component: MainLayout,
|
component: MainLayout,
|
||||||
children: [
|
children: [
|
||||||
{ path: '', component: IndexPage },
|
{ path: '', component: GymSearchPage },
|
||||||
{ path: 'testing', component: TestingPage },
|
{ path: 'users', component: UserSearchPage },
|
||||||
{
|
{
|
||||||
path: 'users/:userId',
|
path: 'users/:userId',
|
||||||
children: [
|
children: [
|
||||||
|
@ -48,7 +47,6 @@ const routes: RouteRecordRaw[] = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ path: 'submissions/:submissionId', component: SubmissionPage },
|
{ path: 'submissions/:submissionId', component: SubmissionPage },
|
||||||
{ path: 'about', component: AboutPage },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ SELECT
|
||||||
(
|
(
|
||||||
SELECT COUNT(id)
|
SELECT COUNT(id)
|
||||||
FROM submission
|
FROM submission
|
||||||
WHERE submission.id = u.id
|
WHERE submission.user_id = u.id
|
||||||
) as submission_count,
|
) as submission_count,
|
||||||
p.account_private as account_private,
|
p.account_private as account_private,
|
||||||
p.locale as locale
|
p.locale as locale
|
||||||
|
|
Loading…
Reference in New Issue