Add group search
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
214400aaea
commit
3bae65374f
@ -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>
|
||||||
|
48
js/src/components/Group/GroupMemberCard.vue
Normal file
48
js/src/components/Group/GroupMemberCard.vue
Normal 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>
|
@ -137,9 +137,7 @@ import RouteName from "../router/name";
|
|||||||
this.handleErrors(graphQLErrors);
|
this.handleErrors(graphQLErrors);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config: {
|
config: CONFIG,
|
||||||
query: CONFIG,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Logo,
|
Logo,
|
||||||
|
@ -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 },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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: {
|
||||||
|
@ -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 },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 } {
|
||||||
|
Loading…
Reference in New Issue
Block a user