Removed old pages, added users search.

This commit is contained in:
Andrew Lalis 2023-02-16 19:06:44 +01:00
parent f92577b76a
commit 08db8b718c
15 changed files with 142 additions and 57 deletions

View File

@ -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;
} }

View File

@ -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>

View File

@ -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>

View File

@ -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',

View File

@ -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',

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 },
], ],
}, },

View File

@ -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