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 {
|
||||
id: string;
|
||||
name: string;
|
||||
submissionCount: number;
|
||||
accountPrivate: boolean;
|
||||
locale: string;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,12 @@
|
|||
{{ submission.rawWeight }} {{ WeightUnitUtil.toAbbreviation(submission.weightUnit) }}
|
||||
{{ submission.exercise.displayName }}
|
||||
</q-item-label>
|
||||
<q-item-label caption>
|
||||
<q-item-label caption v-if="showName">
|
||||
{{ submission.user.name }}
|
||||
</q-item-label>
|
||||
<q-item-label caption v-if="showGym">
|
||||
{{ submission.gym.displayName }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side top>
|
||||
{{ submission.performedAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
||||
|
@ -21,8 +24,13 @@ import { DateTime } from 'luxon';
|
|||
|
||||
interface Props {
|
||||
submission: ExerciseSubmission;
|
||||
showName?: boolean;
|
||||
showGym?: boolean;
|
||||
}
|
||||
defineProps<Props>();
|
||||
withDefaults(defineProps<Props>(), {
|
||||
showName: true,
|
||||
showGym: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<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.',
|
||||
recentLifts: 'Recent Lifts',
|
||||
},
|
||||
userSearchPage: {
|
||||
searchHint: 'Search for a user'
|
||||
},
|
||||
userSettingsPage: {
|
||||
title: 'Account Settings',
|
||||
password: 'Password',
|
||||
|
|
|
@ -48,6 +48,9 @@ export default {
|
|||
accountPrivate: 'Dit account is privaat.',
|
||||
recentLifts: 'Recente liften',
|
||||
},
|
||||
userSearchPage: {
|
||||
searchHint: 'Zoek een gebruiker'
|
||||
},
|
||||
userSettingsPage: {
|
||||
title: 'Account instellingen',
|
||||
password: 'Wachtwoord',
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</q-item-label>
|
||||
<q-item clickable to="/">Gyms</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-list>
|
||||
</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"
|
||||
:submission="sub"
|
||||
:key="sub.id"
|
||||
:show-name="false"
|
||||
/>
|
||||
</q-list>
|
||||
</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"
|
||||
:submission="sub"
|
||||
:key="sub.id"
|
||||
:show-gym="false"
|
||||
/>
|
||||
</q-list>
|
||||
</div>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
v-for="sub in submissions"
|
||||
:submission="sub"
|
||||
:key="sub.id"
|
||||
:show-gym="false"
|
||||
/>
|
||||
</q-list>
|
||||
</q-page>
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import { RouteRecordRaw } from 'vue-router';
|
||||
import MainLayout from 'layouts/MainLayout.vue';
|
||||
import IndexPage from 'pages/IndexPage.vue';
|
||||
import AboutPage from 'pages/AboutPage.vue';
|
||||
import GymSearchPage from 'pages/GymSearchPage.vue';
|
||||
import GymPage from 'pages/gym/GymPage.vue';
|
||||
import GymSubmissionPage from 'pages/gym/GymSubmissionPage.vue';
|
||||
import GymHomePage from 'pages/gym/GymHomePage.vue';
|
||||
import GymLeaderboardsPage from 'pages/gym/GymLeaderboardsPage.vue';
|
||||
import TestingPage from 'pages/TestingPage.vue';
|
||||
import LoginPage from 'pages/auth/LoginPage.vue';
|
||||
import RegisterPage from 'pages/auth/RegisterPage.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 UserPage from 'pages/UserPage.vue';
|
||||
import UserSettingsPage from 'pages/auth/UserSettingsPage.vue';
|
||||
import UserSearchPage from 'pages/UserSearchPage.vue';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
// Auth-related pages, which live outside the main layout.
|
||||
|
@ -27,8 +26,8 @@ const routes: RouteRecordRaw[] = [
|
|||
path: '/',
|
||||
component: MainLayout,
|
||||
children: [
|
||||
{ path: '', component: IndexPage },
|
||||
{ path: 'testing', component: TestingPage },
|
||||
{ path: '', component: GymSearchPage },
|
||||
{ path: 'users', component: UserSearchPage },
|
||||
{
|
||||
path: 'users/:userId',
|
||||
children: [
|
||||
|
@ -48,7 +47,6 @@ const routes: RouteRecordRaw[] = [
|
|||
],
|
||||
},
|
||||
{ path: 'submissions/:submissionId', component: SubmissionPage },
|
||||
{ path: 'about', component: AboutPage },
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ SELECT
|
|||
(
|
||||
SELECT COUNT(id)
|
||||
FROM submission
|
||||
WHERE submission.id = u.id
|
||||
WHERE submission.user_id = u.id
|
||||
) as submission_count,
|
||||
p.account_private as account_private,
|
||||
p.locale as locale
|
||||
|
|
Loading…
Reference in New Issue