Add pagination to search

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2020-09-22 11:45:54 +02:00
parent 1dbb53cacc
commit 6d47ce393d
3 changed files with 77 additions and 29 deletions

View File

@ -8,6 +8,8 @@ export const SEARCH_EVENTS = gql`
$term: String $term: String
$beginsOn: DateTime $beginsOn: DateTime
$endsOn: DateTime $endsOn: DateTime
$page: Int
$limit: Int
) { ) {
searchEvents( searchEvents(
location: $location location: $location
@ -16,6 +18,8 @@ export const SEARCH_EVENTS = gql`
term: $term term: $term
beginsOn: $beginsOn beginsOn: $beginsOn
endsOn: $endsOn endsOn: $endsOn
page: $page
limit: $limit
) { ) {
total total
elements { elements {
@ -36,8 +40,8 @@ export const SEARCH_EVENTS = gql`
`; `;
export const SEARCH_GROUPS = gql` export const SEARCH_GROUPS = gql`
query SearchGroups($term: String, $location: String, $radius: Float) { query SearchGroups($term: String, $location: String, $radius: Float, $page: Int, $limit: Int) {
searchGroups(term: $term, location: $location, radius: $radius) { searchGroups(term: $term, location: $location, radius: $radius, page: $page, limit: $limit) {
total total
elements { elements {
avatar { avatar {

View File

@ -15,3 +15,8 @@ export interface SearchPerson {
total: number; total: number;
elements: IPerson[]; elements: IPerson[];
} }
export enum SearchTabs {
EVENTS = 0,
GROUPS = 1,
}

View File

@ -71,13 +71,27 @@
<b-tag rounded>{{ searchEvents.total }}</b-tag> <b-tag rounded>{{ searchEvents.total }}</b-tag>
</span> </span>
</template> </template>
<div v-if="searchEvents.total > 0" class="columns is-multiline"> <div v-if="searchEvents.total > 0">
<div <div class="columns is-multiline">
class="column is-one-quarter-desktop" <div
v-for="event in searchEvents.elements" class="column is-one-quarter-desktop"
:key="event.uuid" v-for="event in searchEvents.elements"
> :key="event.uuid"
<EventCard :event="event" /> >
<EventCard :event="event" />
</div>
</div>
<div class="pagination">
<b-pagination
:total="searchEvents.total"
v-model="eventPage"
:per-page="EVENT_PAGE_LIMIT"
:aria-next-label="$t('Next page')"
:aria-previous-label="$t('Previous page')"
:aria-page-label="$t('Page')"
:aria-current-label="$t('Current page')"
>
</b-pagination>
</div> </div>
</div> </div>
<b-message v-else-if="$apollo.loading === false" type="is-danger">{{ <b-message v-else-if="$apollo.loading === false" type="is-danger">{{
@ -91,13 +105,27 @@
{{ $t("Groups") }} <b-tag rounded>{{ searchGroups.total }}</b-tag> {{ $t("Groups") }} <b-tag rounded>{{ searchGroups.total }}</b-tag>
</span> </span>
</template> </template>
<div v-if="searchGroups.total > 0" class="columns is-multiline"> <div v-if="searchGroups.total > 0">
<div <div class="columns is-multiline">
class="column is-one-quarter-desktop" <div
v-for="group in searchGroups.elements" class="column is-one-quarter-desktop"
:key="group.uuid" v-for="group in searchGroups.elements"
> :key="group.uuid"
<group-card :group="group" /> >
<group-card :group="group" />
</div>
</div>
<div class="pagination">
<b-pagination
:total="searchGroups.total"
v-model="groupPage"
:per-page="GROUP_PAGE_LIMIT"
:aria-next-label="$t('Next page')"
:aria-previous-label="$t('Previous page')"
:aria-page-label="$t('Page')"
:aria-current-label="$t('Current page')"
>
</b-pagination>
</div> </div>
</div> </div>
<b-message v-else-if="$apollo.loading === false" type="is-danger"> <b-message v-else-if="$apollo.loading === false" type="is-danger">
@ -129,10 +157,10 @@ import { FETCH_EVENTS } from "../graphql/event";
import { IEvent } from "../types/event.model"; import { IEvent } from "../types/event.model";
import RouteName from "../router/name"; import RouteName from "../router/name";
import { IAddress, Address } from "../types/address.model"; import { IAddress, Address } from "../types/address.model";
import { SearchEvent, SearchGroup } from "../types/search.model";
import AddressAutoComplete from "../components/Event/AddressAutoComplete.vue"; import AddressAutoComplete from "../components/Event/AddressAutoComplete.vue";
import { SEARCH_EVENTS, SEARCH_GROUPS } from "../graphql/search"; import { SEARCH_EVENTS, SEARCH_GROUPS } from "../graphql/search";
import { Paginate } from "../types/paginate"; import { Paginate } from "../types/paginate";
import { SearchTabs } from "../types/search.model";
import { IGroup } from "../types/actor"; import { IGroup } from "../types/actor";
import GroupCard from "../components/Group/GroupCard.vue"; import GroupCard from "../components/Group/GroupCard.vue";
import { CONFIG } from "../graphql/config"; import { CONFIG } from "../graphql/config";
@ -143,16 +171,15 @@ interface ISearchTimeOption {
end?: Date | null; end?: Date | null;
} }
enum SearchTabs {
EVENTS = 0,
GROUPS = 1,
}
const tabsName: { events: number; groups: number } = { const tabsName: { events: number; groups: number } = {
events: SearchTabs.EVENTS, events: SearchTabs.EVENTS,
groups: SearchTabs.GROUPS, groups: SearchTabs.GROUPS,
}; };
const EVENT_PAGE_LIMIT = 1;
const GROUP_PAGE_LIMIT = 10;
@Component({ @Component({
components: { components: {
EventCard, EventCard,
@ -173,6 +200,8 @@ const tabsName: { events: number; groups: number } = {
beginsOn: this.start, beginsOn: this.start,
endsOn: this.end, endsOn: this.end,
radius: this.radius, radius: this.radius,
page: this.eventPage,
limit: EVENT_PAGE_LIMIT,
}; };
}, },
debounce: 300, debounce: 300,
@ -188,6 +217,8 @@ const tabsName: { events: number; groups: number } = {
term: this.search, term: this.search,
location: this.geohash, location: this.geohash,
radius: this.radius, radius: this.radius,
page: this.groupPage,
limit: GROUP_PAGE_LIMIT,
}; };
}, },
skip() { skip() {
@ -213,6 +244,10 @@ export default class Search extends Vue {
searchGroups: Paginate<IGroup> = { total: 0, elements: [] }; searchGroups: Paginate<IGroup> = { total: 0, elements: [] };
eventPage = 1;
groupPage = 1;
search: string = (this.$route.query.term as string) || ""; search: string = (this.$route.query.term as string) || "";
activeTab: SearchTabs = tabsName[this.$route.query.searchType as "events" | "groups"] || 0; activeTab: SearchTabs = tabsName[this.$route.query.searchType as "events" | "groups"] || 0;
@ -268,23 +303,27 @@ export default class Search extends Vue {
end: null, end: null,
}; };
radiusString = (radius: number | null) => { EVENT_PAGE_LIMIT = EVENT_PAGE_LIMIT;
GROUP_PAGE_LIMIT = GROUP_PAGE_LIMIT;
radiusString = (radius: number | null): string => {
if (radius) { if (radius) {
return this.$tc("{nb} km", radius, { nb: radius }); return this.$tc("{nb} km", radius, { nb: radius }) as string;
} }
return this.$t("any distance"); return this.$t("any distance") as string;
}; };
radiusOptions: (number | null)[] = [1, 5, 10, 25, 50, 100, 150, null]; radiusOptions: (number | null)[] = [1, 5, 10, 25, 50, 100, 150, null];
radius = 50; radius = 50;
submit() { submit(): void {
this.$apollo.queries.searchEvents.refetch(); this.$apollo.queries.searchEvents.refetch();
} }
@Watch("search") @Watch("search")
updateSearchTerm() { updateSearchTerm(): void {
this.$router.push({ this.$router.push({
name: RouteName.SEARCH, name: RouteName.SEARCH,
query: { ...this.$route.query, term: this.search }, query: { ...this.$route.query, term: this.search },
@ -292,7 +331,7 @@ export default class Search extends Vue {
} }
@Watch("activeTab") @Watch("activeTab")
updateActiveTab() { updateActiveTab(): void {
const searchType = this.activeTab === tabsName.events ? "events" : "groups"; const searchType = this.activeTab === tabsName.events ? "events" : "groups";
this.$router.push({ this.$router.push({
name: RouteName.SEARCH, name: RouteName.SEARCH,
@ -308,7 +347,7 @@ export default class Search extends Vue {
return { start: startOfDay(start), end: endOfDay(end) }; return { start: startOfDay(start), end: endOfDay(end) };
} }
get geohash() { get geohash(): string | undefined {
if (this.location && this.location.geom) { if (this.location && this.location.geom) {
const [lon, lat] = this.location.geom.split(";"); const [lon, lat] = this.location.geom.split(";");
return ngeohash.encode(lat, lon, 6); return ngeohash.encode(lat, lon, 6);