Add group search

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2020-08-05 14:39:17 +02:00
parent 214400aaea
commit 3bae65374f
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
9 changed files with 126 additions and 39 deletions

View File

@ -9,21 +9,21 @@
</div> </div>
<div class="media-content"> <div class="media-content">
<router-link <router-link
:to="{ name: RouteName.GROUP, params: { preferredUsername: groupFullUsername } }" :to="{
name: RouteName.GROUP,
params: { preferredUsername: usernameWithDomain(group) },
}"
> >
<h3>{{ member.parent.name }}</h3> <h3>{{ group.name }}</h3>
<p class="is-6 has-text-grey"> <p class="is-6 has-text-grey">
<span v-if="member.parent.domain">{{ <span v-if="group.domain">{{ `@${group.preferredUsername}@${group.domain}` }}</span>
`@${member.parent.preferredUsername}@${member.parent.domain}` <span v-else>{{ `@${group.preferredUsername}` }}</span>
}}</span>
<span v-else>{{ `@${member.parent.preferredUsername}` }}</span>
</p> </p>
<b-tag type="is-info">{{ member.role }}</b-tag>
</router-link> </router-link>
</div> </div>
</div> </div>
<div class="content"> <div class="content">
<p>{{ member.parent.summary }}</p> <p>{{ group.summary }}</p>
</div> </div>
</div> </div>
</div> </div>
@ -31,20 +31,15 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { IGroup, IMember } from "@/types/actor"; import { IGroup, usernameWithDomain } from "@/types/actor";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
@Component @Component
export default class GroupCard extends Vue { export default class GroupCard extends Vue {
@Prop({ required: true }) member!: IMember; @Prop({ required: true }) group!: IGroup;
RouteName = RouteName; RouteName = RouteName;
get groupFullUsername() { usernameWithDomain = usernameWithDomain;
if (this.member.parent.domain) {
return `${this.member.parent.preferredUsername}@${this.member.parent.domain}`;
}
return this.member.parent.preferredUsername;
}
} }
</script> </script>

View File

@ -0,0 +1,48 @@
<template>
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-48x48">
<img src="https://bulma.io/images/placeholders/96x96.png" alt="Placeholder image" />
</figure>
</div>
<div class="media-content">
<router-link
:to="{
name: RouteName.GROUP,
params: { preferredUsername: usernameWithDomain(member.parent) },
}"
>
<h3>{{ member.parent.name }}</h3>
<p class="is-6 has-text-grey">
<span v-if="member.parent.domain">{{
`@${member.parent.preferredUsername}@${member.parent.domain}`
}}</span>
<span v-else>{{ `@${member.parent.preferredUsername}` }}</span>
</p>
<b-tag type="is-info">{{ member.role }}</b-tag>
</router-link>
</div>
</div>
<div class="content">
<p>{{ member.parent.summary }}</p>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import { IMember, usernameWithDomain } from "@/types/actor";
import RouteName from "../../router/name";
@Component
export default class GroupMemberCard extends Vue {
@Prop({ required: true }) member!: IMember;
RouteName = RouteName;
usernameWithDomain = usernameWithDomain;
}
</script>

View File

@ -137,9 +137,7 @@ import RouteName from "../router/name";
this.handleErrors(graphQLErrors); this.handleErrors(graphQLErrors);
}, },
}, },
config: { config: CONFIG,
query: CONFIG,
},
}, },
components: { components: {
Logo, Logo,

View File

@ -8,7 +8,7 @@
type="search" type="search"
rounded rounded
:placeholder="defaultPlaceHolder" :placeholder="defaultPlaceHolder"
v-model="searchText" v-model="search"
@keyup.native.enter="enter" @keyup.native.enter="enter"
/> />
</label> </label>
@ -21,12 +21,12 @@ import RouteName from "../router/name";
export default class SearchField extends Vue { export default class SearchField extends Vue {
@Prop({ type: String, required: false }) placeholder!: string; @Prop({ type: String, required: false }) placeholder!: string;
searchText = ""; search: string = "";
enter() { enter() {
this.$router.push({ this.$router.push({
name: RouteName.SEARCH, name: RouteName.SEARCH,
params: { searchTerm: this.searchText }, query: { term: this.search },
}); });
} }

View File

@ -49,7 +49,7 @@ const router = new Router({
...discussionRoutes, ...discussionRoutes,
...errorRoutes, ...errorRoutes,
{ {
path: "/search/:searchTerm?/:searchType?", path: "/search",
name: RouteName.SEARCH, name: RouteName.SEARCH,
component: Search, component: Search,
props: true, props: true,

View File

@ -3,7 +3,7 @@
<h1>{{ $t("Group List") }} ({{ groups.total }})</h1> <h1>{{ $t("Group List") }} ({{ groups.total }})</h1>
<b-loading :active.sync="$apollo.loading" /> <b-loading :active.sync="$apollo.loading" />
<div class="columns"> <div class="columns">
<GroupCard <GroupMemberCard
v-for="group in groups.elements" v-for="group in groups.elements"
:key="group.uuid" :key="group.uuid"
:group="group" :group="group"
@ -20,7 +20,7 @@
import { Component, Vue } from "vue-property-decorator"; import { Component, Vue } from "vue-property-decorator";
import { LIST_GROUPS } from "@/graphql/actor"; import { LIST_GROUPS } from "@/graphql/actor";
import { Group, IGroup } from "@/types/actor"; import { Group, IGroup } from "@/types/actor";
import GroupCard from "@/components/Group/GroupCard.vue"; import GroupMemberCard from "@/components/Group/GroupMemberCard.vue";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
@Component({ @Component({
@ -30,7 +30,7 @@ import RouteName from "../../router/name";
}, },
}, },
components: { components: {
GroupCard, GroupMemberCard,
}, },
}) })
export default class GroupList extends Vue { export default class GroupList extends Vue {

View File

@ -12,7 +12,7 @@
/> />
</section> </section>
<section v-if="memberships && memberships.length > 0"> <section v-if="memberships && memberships.length > 0">
<GroupCard v-for="member in memberships" :key="member.id" :member="member" /> <GroupMemberCard v-for="member in memberships" :key="member.id" :member="member" />
</section> </section>
<b-message v-if="$apollo.loading === false && memberships.length === 0" type="is-danger"> <b-message v-if="$apollo.loading === false && memberships.length === 0" type="is-danger">
{{ $t("No groups found") }} {{ $t("No groups found") }}
@ -23,7 +23,7 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-property-decorator"; import { Component, Vue } from "vue-property-decorator";
import { LOGGED_USER_MEMBERSHIPS } from "@/graphql/actor"; import { LOGGED_USER_MEMBERSHIPS } from "@/graphql/actor";
import GroupCard from "@/components/Group/GroupCard.vue"; import GroupMemberCard from "@/components/Group/GroupMemberCard.vue";
import InvitationCard from "@/components/Group/InvitationCard.vue"; import InvitationCard from "@/components/Group/InvitationCard.vue";
import { Paginate } from "@/types/paginate"; import { Paginate } from "@/types/paginate";
import { IGroup, IMember, MemberRole } from "@/types/actor"; import { IGroup, IMember, MemberRole } from "@/types/actor";
@ -32,7 +32,7 @@ import { ACCEPT_INVITATION } from "../../graphql/member";
@Component({ @Component({
components: { components: {
GroupCard, GroupMemberCard,
InvitationCard, InvitationCard,
}, },
apollo: { apollo: {

View File

@ -48,7 +48,7 @@ export default class PageNotFound extends Vue {
enter() { enter() {
this.$router.push({ this.$router.push({
name: RouteName.SEARCH, name: RouteName.SEARCH,
params: { searchTerm: this.searchText }, query: { term: this.searchText },
}); });
} }
} }

View File

@ -34,7 +34,7 @@
</b-select> </b-select>
</b-field> </b-field>
<b-field :label="$t('Date')" label-for="date"> <b-field :label="$t('Date')" label-for="date">
<b-select v-model="when" id="date"> <b-select v-model="when" id="date" :disabled="activeTab !== 0">
<option v-for="(option, index) in options" :key="index" :value="option">{{ <option v-for="(option, index) in options" :key="index" :value="option">{{
option.label option.label
}}</option> }}</option>
@ -78,6 +78,26 @@
$t("No events found") $t("No events found")
}}</b-message> }}</b-message>
</b-tab-item> </b-tab-item>
<b-tab-item v-if="config && config.features.groups">
<template slot="header">
<b-icon icon="account-multiple"></b-icon>
<span>
{{ $t("Groups") }} <b-tag rounded>{{ searchGroups.total }}</b-tag>
</span>
</template>
<div v-if="searchGroups.total > 0" class="columns is-multiline">
<div
class="column is-one-quarter-desktop"
v-for="group in searchGroups.elements"
:key="group.uuid"
>
<group-card :group="group" />
</div>
</div>
<b-message v-else-if="$apollo.loading === false" type="is-danger">
{{ $t("No groups found") }}
</b-message>
</b-tab-item>
</b-tabs> </b-tabs>
</div> </div>
</template> </template>
@ -107,6 +127,9 @@ import {
startOfMonth, startOfMonth,
eachWeekendOfInterval, eachWeekendOfInterval,
} from "date-fns"; } from "date-fns";
import { IGroup } from "../types/actor";
import GroupCard from "../components/Group/GroupCard.vue";
import { CONFIG } from "../graphql/config";
interface ISearchTimeOption { interface ISearchTimeOption {
label: string; label: string;
@ -128,8 +151,10 @@ const tabsName: { events: number; groups: number } = {
components: { components: {
EventCard, EventCard,
AddressAutoComplete, AddressAutoComplete,
GroupCard,
}, },
apollo: { apollo: {
config: CONFIG,
events: FETCH_EVENTS, events: FETCH_EVENTS,
searchEvents: { searchEvents: {
query: SEARCH_EVENTS, query: SEARCH_EVENTS,
@ -148,6 +173,17 @@ const tabsName: { events: number; groups: number } = {
return !this.search && !this.actualTag && !this.geohash && this.end === null; return !this.search && !this.actualTag && !this.geohash && this.end === null;
}, },
}, },
searchGroups: {
query: SEARCH_GROUPS,
variables() {
return {
searchText: this.search,
};
},
skip() {
return this.search == null || this.search == "";
},
},
}, },
metaInfo() { metaInfo() {
return { return {
@ -159,16 +195,14 @@ const tabsName: { events: number; groups: number } = {
}, },
}) })
export default class Search extends Vue { export default class Search extends Vue {
@Prop({ type: String, required: false, default: "" }) searchTerm!: string;
@Prop({ type: String, required: false, default: "events" }) searchType!: "events" | "groups";
events: IEvent[] = []; events: IEvent[] = [];
searchEvents: Paginate<IEvent> & { initial: boolean } = { total: 0, elements: [], initial: true }; searchEvents: Paginate<IEvent> & { initial: boolean } = { total: 0, elements: [], initial: true };
searchGroups: Paginate<IGroup> = { total: 0, elements: [] };
search = this.searchTerm; search: string = (this.$route.query.term as string) || "";
activeTab: SearchTabs = tabsName[this.searchType]; activeTab: SearchTabs = tabsName[this.$route.query.searchType as "events" | "groups"] || 0;
location: IAddress = new Address(); location: IAddress = new Address();
@ -230,15 +264,27 @@ export default class Search extends Vue {
radiusOptions: (number | null)[] = [1, 5, 10, 25, 50, 100, 150, null]; radiusOptions: (number | null)[] = [1, 5, 10, 25, 50, 100, 150, null];
radius: number | undefined = undefined; radius: number | null = null;
submit() { submit() {
this.$apollo.queries.searchEvents.refetch(); this.$apollo.queries.searchEvents.refetch();
} }
@Watch("searchTerm") @Watch("search")
updateSearchTerm() { updateSearchTerm() {
this.search = this.searchTerm; this.$router.push({
name: RouteName.SEARCH,
query: Object.assign({}, this.$route.query, { term: this.search }),
});
}
@Watch("activeTab")
updateActiveTab() {
const searchType = this.activeTab === tabsName.events ? "events" : "groups";
this.$router.push({
name: RouteName.SEARCH,
query: Object.assign({}, this.$route.query, { searchType }),
});
} }
get weekend(): { start: Date; end: Date } { get weekend(): { start: Date; end: Date } {