2020-08-27 11:53:24 +02:00
< template >
< div v-if = "group" class="section" >
< nav class = "breadcrumb" aria -label = " breadcrumbs " >
< ul >
< li >
< router-link : to = "{ name: RouteName.ADMIN }" > { { $t ( "Admin" ) } } < / router-link >
< / li >
< li >
< router-link
: to = " {
name : RouteName . ADMIN _GROUPS ,
} "
> { { $t ( "Groups" ) } } < / r o u t e r - l i n k
>
< / li >
< li class = "is-active" >
< router-link
: to = " {
name : RouteName . PROFILES ,
params : { id : group . id } ,
} "
> { { group . name || group . preferredUsername } } < / r o u t e r - l i n k
>
< / li >
< / ul >
< / nav >
< div class = "actor-card" >
< router-link
: to = "{ name: RouteName.GROUP, params: { preferredUsername: usernameWithDomain(group) } }"
>
< actor-card :actor = "group" :full = "true" :popover = "false" :limit = "false" / >
< / router-link >
< / div >
< table v-if = "metadata.length > 0" class="table is-fullwidth" >
< tbody >
< tr v-for ="{ key, value, link } in metadata" :key ="key" >
< td > { { key } } < / td >
< td v-if = "link" >
< router-link :to = "link" >
{ { value } }
< / router-link >
< / td >
< td v-else > {{ value }} < / td >
< / tr >
< / tbody >
< / table >
< div class = "buttons" >
< b-button @click ="confirmSuspendProfile" v-if = "!group.suspended" type="is-primary" > {{
$t ( "Suspend" )
} } < / b-button >
< b-button @click ="unsuspendProfile" v-if = "group.suspended" type="is-primary" > {{
$t ( "Unsuspend" )
} } < / b-button >
< b-button @click ="refreshProfile" v-if = "group.domain" type="is-primary" outlined > { {
$t ( "Refresh profile" )
} } < / b-button >
< / div >
< section >
< h2 class = "subtitle" >
{ {
$tc ( "{number} members" , group . members . total , {
number : group . members . total ,
} )
} }
< / h2 >
< b-table
: data = "group.members.elements"
: loading = "$apollo.queries.group.loading"
paginated
backend - pagination
: total = "group.members.total"
: per - page = "EVENTS_PER_PAGE"
@ page - change = "onMembersPageChange"
>
< b-table-column field = "actor.preferredUsername" :label = "$t('Member')" v-slot = "props" >
< article class = "media" >
< figure class = "media-left image is-48x48" v-if = "props.row.actor.avatar" >
< img class = "is-rounded" :src = "props.row.actor.avatar.url" alt = "" / >
< / figure >
< b-icon class = "media-left" v -else size = "is-large" icon = "account-circle" / >
< div class = "media-content" >
< div class = "content" >
< span v-if = "props.row.actor.name" > {{ props.row.actor.name }} < / span
> < br / >
< span class = "is-size-7 has-text-grey"
> @ { { usernameWithDomain ( props . row . actor ) } } < / s p a n
>
< / div >
< / div >
< / article >
< / b-table-column >
< b-table-column field = "role" :label = "$t('Role')" v-slot = "props" >
< b-tag type = "is-primary" v-if = "props.row.role === MemberRole.ADMINISTRATOR" >
{ { $t ( "Administrator" ) } }
< / b-tag >
< b-tag type = "is-primary" v -else -if = " props.row.role = = = MemberRole.MODERATOR " >
{ { $t ( "Moderator" ) } }
< / b-tag >
< b-tag v -else -if = " props.row.role = = = MemberRole.MEMBER " >
{ { $t ( "Member" ) } }
< / b-tag >
< b-tag type = "is-warning" v -else -if = " props.row.role = = = MemberRole.NOT_APPROVED " >
{ { $t ( "Not approved" ) } }
< / b-tag >
< b-tag type = "is-danger" v -else -if = " props.row.role = = = MemberRole.REJECTED " >
{ { $t ( "Rejected" ) } }
< / b-tag >
< b-tag type = "is-danger" v -else -if = " props.row.role = = = MemberRole.INVITED " >
{ { $t ( "Invited" ) } }
< / b-tag >
< / b-table-column >
< b-table-column field = "insertedAt" :label = "$t('Date')" v-slot = "props" >
< span class = "has-text-centered" >
{ { props . row . insertedAt | formatDateString } } < br / > { {
props . row . insertedAt | formatTimeString
} }
< / span >
< / b-table-column >
< template slot = "empty" >
< section class = "section" >
< div class = "content has-text-grey has-text-centered" >
< p > { { $t ( "Nothing to see here" ) } } < / p >
< / div >
< / section >
< / template >
< / b-table >
< / section >
< section >
< h2 class = "subtitle" >
{ {
$tc ( "{number} organized events" , group . organizedEvents . total , {
number : group . organizedEvents . total ,
} )
} }
< / h2 >
< b-table
: data = "group.organizedEvents.elements"
: loading = "$apollo.queries.group.loading"
paginated
backend - pagination
: total = "group.organizedEvents.total"
: per - page = "EVENTS_PER_PAGE"
@ page - change = "onOrganizedEventsPageChange"
>
< b-table-column field = "title" :label = "$t('Title')" v-slot = "props" >
< router-link : to = "{ name: RouteName.EVENT, params: { uuid: props.row.uuid } }" >
{ { props . row . title } }
< / router-link >
< / b-table-column >
< b-table-column field = "beginsOn" : label = "$t('Begins on')" v-slot = "props" >
{ { props . row . beginsOn | formatDateTimeString } }
< / b-table-column >
< template slot = "empty" >
< section class = "section" >
< div class = "content has-text-grey has-text-centered" >
< p > { { $t ( "Nothing to see here" ) } } < / p >
< / div >
< / section >
< / template >
< / b-table >
< / section >
< section >
< h2 class = "subtitle" >
{ {
$tc ( "{number} posts" , group . posts . total , {
number : group . posts . total ,
} )
} }
< / h2 >
< b-table
: data = "group.posts.elements"
: loading = "$apollo.queries.group.loading"
paginated
backend - pagination
: total = "group.posts.total"
: per - page = "EVENTS_PER_PAGE"
@ page - change = "onPostsPageChange"
>
< b-table-column field = "title" :label = "$t('Title')" v-slot = "props" >
< router-link : to = "{ name: RouteName.POST, params: { slug: props.row.slug } }" >
{ { props . row . title } }
< / router-link >
< / b-table-column >
< b-table-column field = "publishAt" : label = "$t('Publication date')" v-slot = "props" >
{ { props . row . publishAt | formatDateTimeString } }
< / b-table-column >
< template slot = "empty" >
< section class = "section" >
< div class = "content has-text-grey has-text-centered" >
< p > { { $t ( "Nothing to see here" ) } } < / p >
< / div >
< / section >
< / template >
< / b-table >
< / section >
< / div >
< / template >
< script lang = "ts" >
import { Component , Vue , Prop } from "vue-property-decorator" ;
2020-08-31 12:40:30 +02:00
import { GET _GROUP , REFRESH _PROFILE } from "@/graphql/group" ;
2020-11-23 16:58:50 +01:00
import { formatBytes } from "@/utils/datetime" ;
2020-11-27 19:27:44 +01:00
import { MemberRole } from "@/types/enums" ;
2020-08-27 11:53:24 +02:00
import { SUSPEND _PROFILE , UNSUSPEND _PROFILE } from "../../graphql/actor" ;
2020-11-27 19:27:44 +01:00
import { IGroup } from "../../types/actor" ;
2020-08-27 11:53:24 +02:00
import { usernameWithDomain , IActor } from "../../types/actor/actor.model" ;
import RouteName from "../../router/name" ;
import ActorCard from "../../components/Account/ActorCard.vue" ;
const EVENTS _PER _PAGE = 10 ;
@ Component ( {
apollo : {
group : {
query : GET _GROUP ,
fetchPolicy : "cache-and-network" ,
variables ( ) {
return {
id : this . id ,
organizedEventsPage : 1 ,
organizedEventsLimit : EVENTS _PER _PAGE ,
} ;
} ,
skip ( ) {
return ! this . id ;
} ,
update : ( data ) => data . getGroup ,
} ,
} ,
components : {
ActorCard ,
} ,
} )
export default class AdminGroupProfile extends Vue {
@ Prop ( { required : true } ) id ! : string ;
group ! : IGroup ;
usernameWithDomain = usernameWithDomain ;
RouteName = RouteName ;
EVENTS _PER _PAGE = EVENTS _PER _PAGE ;
organizedEventsPage = 1 ;
membersPage = 1 ;
postsPage = 1 ;
MemberRole = MemberRole ;
2020-09-29 09:53:48 +02:00
get metadata ( ) : Array < Record < string , string > > {
2020-08-27 11:53:24 +02:00
if ( ! this . group ) return [ ] ;
2020-09-29 09:53:48 +02:00
const res : Record < string , string > [ ] = [
2020-08-27 11:53:24 +02:00
{
key : this . $t ( "Status" ) as string ,
2020-09-29 09:53:48 +02:00
value : ( this . group . suspended ? this . $t ( "Suspended" ) : this . $t ( "Active" ) ) as string ,
2020-08-27 11:53:24 +02:00
} ,
{
key : this . $t ( "Domain" ) as string ,
2020-09-29 09:53:48 +02:00
value : ( this . group . domain ? this . group . domain : this . $t ( "Local" ) ) as string ,
2020-08-27 11:53:24 +02:00
} ,
2020-11-23 16:58:50 +01:00
{
key : this . $i18n . t ( "Uploaded media size" ) as string ,
value : formatBytes ( this . group . mediaSize ) ,
} ,
2020-08-27 11:53:24 +02:00
] ;
return res ;
}
2020-09-29 09:53:48 +02:00
confirmSuspendProfile ( ) : void {
2020-08-27 11:53:24 +02:00
const message = ( this . group . domain
? this . $t (
"Are you sure you want to <b>suspend</b> this group? As this group originates from instance {instance}, this will only remove local members and delete the local data, as well as rejecting all the future data." ,
{ instance : this . group . domain }
)
: this . $t (
"Are you sure you want to <b>suspend</b> this group? All members - including remote ones - will be notified and removed from the group, and <b>all of the group data (events, posts, discussions, todos…) will be irretrievably destroyed</b>."
) ) as string ;
this . $buefy . dialog . confirm ( {
title : this . $t ( "Suspend group" ) as string ,
message ,
confirmText : this . $t ( "Suspend group" ) as string ,
cancelText : this . $t ( "Cancel" ) as string ,
type : "is-danger" ,
hasIcon : true ,
onConfirm : ( ) => this . suspendProfile ( ) ,
} ) ;
}
2020-09-29 09:53:48 +02:00
async suspendProfile ( ) : Promise < void > {
2020-08-27 11:53:24 +02:00
this . $apollo . mutate < { suspendProfile : { id : string } } > ( {
mutation : SUSPEND _PROFILE ,
variables : {
id : this . id ,
} ,
update : ( store , { data } ) => {
if ( data == null ) return ;
const profileId = this . id ;
const profileData = store . readQuery < { getGroup : IGroup } > ( {
query : GET _GROUP ,
variables : {
id : profileId ,
} ,
} ) ;
if ( ! profileData ) return ;
const { getGroup : group } = profileData ;
group . suspended = true ;
group . avatar = null ;
group . name = "" ;
group . summary = "" ;
store . writeQuery ( {
query : GET _GROUP ,
variables : {
id : profileId ,
} ,
data : { getGroup : group } ,
} ) ;
} ,
} ) ;
}
2020-09-29 09:53:48 +02:00
async unsuspendProfile ( ) : Promise < void > {
2020-08-27 11:53:24 +02:00
const profileID = this . id ;
2020-09-29 09:53:48 +02:00
await this . $apollo . mutate < { unsuspendProfile : { id : string } } > ( {
2020-08-27 11:53:24 +02:00
mutation : UNSUSPEND _PROFILE ,
variables : {
id : this . id ,
} ,
refetchQueries : [
{
query : GET _GROUP ,
variables : {
id : profileID ,
} ,
} ,
] ,
} ) ;
}
2020-09-29 09:53:48 +02:00
async refreshProfile ( ) : Promise < void > {
2020-08-27 11:53:24 +02:00
this . $apollo . mutate < { refreshProfile : IActor } > ( {
mutation : REFRESH _PROFILE ,
variables : {
actorId : this . id ,
} ,
} ) ;
}
2020-09-29 09:53:48 +02:00
async onOrganizedEventsPageChange ( page : number ) : Promise < void > {
2020-08-27 11:53:24 +02:00
this . organizedEventsPage = page ;
await this . $apollo . queries . group . fetchMore ( {
variables : {
actorId : this . id ,
organizedEventsPage : this . organizedEventsPage ,
organizedEventsLimit : EVENTS _PER _PAGE ,
} ,
updateQuery : ( previousResult , { fetchMoreResult } ) => {
if ( ! fetchMoreResult ) return previousResult ;
const newOrganizedEvents = fetchMoreResult . group . organizedEvents . elements ;
return {
group : {
... previousResult . group ,
organizedEvents : {
_ _typename : previousResult . group . organizedEvents . _ _typename ,
total : previousResult . group . organizedEvents . total ,
elements : [ ... previousResult . group . organizedEvents . elements , ... newOrganizedEvents ] ,
} ,
} ,
} ;
} ,
} ) ;
}
2020-09-29 09:53:48 +02:00
async onMembersPageChange ( page : number ) : Promise < void > {
2020-08-27 11:53:24 +02:00
this . membersPage = page ;
await this . $apollo . queries . group . fetchMore ( {
variables : {
actorId : this . id ,
memberPage : this . membersPage ,
memberLimit : EVENTS _PER _PAGE ,
} ,
updateQuery : ( previousResult , { fetchMoreResult } ) => {
if ( ! fetchMoreResult ) return previousResult ;
const newMembers = fetchMoreResult . group . members . elements ;
return {
group : {
... previousResult . group ,
members : {
_ _typename : previousResult . group . members . _ _typename ,
total : previousResult . group . members . total ,
elements : [ ... previousResult . group . members . elements , ... newMembers ] ,
} ,
} ,
} ;
} ,
} ) ;
}
2020-09-29 09:53:48 +02:00
async onPostsPageChange ( page : number ) : Promise < void > {
2020-08-27 11:53:24 +02:00
this . postsPage = page ;
await this . $apollo . queries . group . fetchMore ( {
variables : {
actorId : this . id ,
postsPage : this . postsPage ,
postLimit : EVENTS _PER _PAGE ,
} ,
updateQuery : ( previousResult , { fetchMoreResult } ) => {
if ( ! fetchMoreResult ) return previousResult ;
const newPosts = fetchMoreResult . group . posts . elements ;
return {
group : {
... previousResult . group ,
posts : {
_ _typename : previousResult . group . posts . _ _typename ,
total : previousResult . group . posts . total ,
elements : [ ... previousResult . group . posts . elements , ... newPosts ] ,
} ,
} ,
} ;
} ,
} ) ;
}
}
< / script >
< style lang = "scss" scoped >
table ,
section {
margin : 2 rem 0 ;
}
. actor - card {
background : # fff ;
padding : 1.5 rem ;
border - radius : 10 px ;
}
< / style >