mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge branch '5344-add-group-members-management' of github.com:Ocelot-Social-Community/Ocelot-Social into 5059-groups/5318-implement-content-menu-group-profile
# Conflicts: # webapp/components/Group/GroupContentMenu.vue
This commit is contained in:
commit
178c64a49d
@ -268,6 +268,7 @@ $size-avatar-large: 114px;
|
||||
* @presenter Spacing
|
||||
*/
|
||||
|
||||
$size-button-large: 50px;
|
||||
$size-button-base: 36px;
|
||||
$size-button-small: 26px;
|
||||
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-container class="group-card">
|
||||
<ds-page-title heading="Groups"></ds-page-title>
|
||||
<ds-card v-for="item in items" :key="item.id" space="xx-small">
|
||||
<nuxt-link :to="`/group/${item.id}`">{{ item.name }}</nuxt-link>
|
||||
{{ item.categories ? item.categories.map((category) => category.name) : [] }}
|
||||
<div>{{ item }}</div>
|
||||
<ds-space>
|
||||
<base-button
|
||||
v-if="item.myRole === 'owner'"
|
||||
icon="trash"
|
||||
@click="deleteGroup(item)"
|
||||
></base-button>
|
||||
<base-button
|
||||
v-if="item.myRole === 'pending'"
|
||||
icon="question-circle"
|
||||
@click="removePending(item)"
|
||||
></base-button>
|
||||
<base-button
|
||||
v-if="item.myRole === 'owner'"
|
||||
icon="edit"
|
||||
@click="editGroup(item)"
|
||||
></base-button>
|
||||
<base-button
|
||||
v-if="item.myRole === 'usual'"
|
||||
icon="close"
|
||||
@click="unfollowGroup(item)"
|
||||
></base-button>
|
||||
<base-button
|
||||
v-if="item.myRole === null"
|
||||
icon="plus"
|
||||
@click="addMemeberToGroup(item)"
|
||||
></base-button>
|
||||
</ds-space>
|
||||
</ds-card>
|
||||
</ds-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GroupList',
|
||||
props: {
|
||||
items: { type: Array, default: () => [] },
|
||||
},
|
||||
methods: {
|
||||
removePending() {
|
||||
alert('removePending group')
|
||||
},
|
||||
editGroup(item) {
|
||||
this.$router.push({ path: `/group/edit/${item.id}` })
|
||||
},
|
||||
deleteGroup() {
|
||||
alert('delete group')
|
||||
},
|
||||
unfollowGroup() {
|
||||
alert('unfollow group')
|
||||
},
|
||||
addMemeberToGroup() {
|
||||
alert('addMemeberToGroup group')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -40,7 +40,13 @@ export default {
|
||||
Dropdown,
|
||||
},
|
||||
props: {
|
||||
placement: { type: String, default: 'bottom-end' },
|
||||
usage: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: (value) => {
|
||||
return value.match(/(groupTeaser|groupProfile)/)
|
||||
},
|
||||
},
|
||||
resource: { type: Object, required: true },
|
||||
resourceType: {
|
||||
type: String,
|
||||
@ -49,6 +55,7 @@ export default {
|
||||
return value.match(/(group)/)
|
||||
},
|
||||
},
|
||||
placement: { type: String, default: 'bottom-end' },
|
||||
},
|
||||
computed: {
|
||||
routes() {
|
||||
|
||||
39
webapp/components/Group/GroupForm.spec.js
Normal file
39
webapp/components/Group/GroupForm.spec.js
Normal file
@ -0,0 +1,39 @@
|
||||
import { config, mount } from '@vue/test-utils'
|
||||
import GroupForm from './GroupForm.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
||||
|
||||
const propsData = {
|
||||
update: false,
|
||||
group: {},
|
||||
}
|
||||
|
||||
describe('GroupForm', () => {
|
||||
let wrapper
|
||||
let mocks
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$env: {
|
||||
CATEGORIES_ACTIVE: true,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('mount', () => {
|
||||
const Wrapper = () => {
|
||||
return mount(GroupForm, { propsData, mocks, localVue })
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.findAll('.group-form')).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,68 +1,132 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-container>
|
||||
<ds-form
|
||||
class="group-form"
|
||||
ref="groupForm"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="submit"
|
||||
<ds-form
|
||||
class="group-form"
|
||||
ref="groupForm"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="submit"
|
||||
>
|
||||
<ds-input
|
||||
:label="$t('group.name')"
|
||||
v-model="formData.name"
|
||||
:placeholder="`${$t('group.name')} …`"
|
||||
></ds-input>
|
||||
<ds-input
|
||||
v-if="update"
|
||||
:label="$t('group.labelSlug')"
|
||||
v-model="formData.slug"
|
||||
icon="at"
|
||||
:placeholder="`${$t('group.labelSlug')} …`"
|
||||
></ds-input>
|
||||
<!-- groupType -->
|
||||
<!-- TODO: change it has to be implemented later -->
|
||||
<!-- TODO: move 'ds-select' from styleguide to main code and implement missen translation etc. functionality -->
|
||||
<!-- <ds-select
|
||||
id="groupType"
|
||||
:label="$t('group.type')"
|
||||
:v-model="formData.groupType"
|
||||
:label-prop="'label'"
|
||||
:options="groupTypeOptions"
|
||||
icon="user"
|
||||
:placeholder="$t('group.type') + ' …'"
|
||||
></ds-select> -->
|
||||
<ds-text class="select-label">
|
||||
{{ $t('group.type') }}
|
||||
</ds-text>
|
||||
<ds-icon class="select-icon" name="user" />
|
||||
<select
|
||||
class="select"
|
||||
:options="groupTypeOptions"
|
||||
:value="formData.groupType"
|
||||
:disabled="update"
|
||||
@change="changeGroupType($event)"
|
||||
>
|
||||
<ds-input
|
||||
v-model="formData.name"
|
||||
label="Gruppenname"
|
||||
placeholder="Your name ..."
|
||||
></ds-input>
|
||||
|
||||
<ds-select
|
||||
icon="user"
|
||||
v-model="formData.groupType"
|
||||
label="Sichtbarkeit"
|
||||
:options="['public', 'closed', 'hidden']"
|
||||
placeholder="Status ..."
|
||||
></ds-select>
|
||||
|
||||
<ds-input v-model="formData.about" label="Kurzbeschreibung" rows="3"></ds-input>
|
||||
|
||||
<ds-input
|
||||
v-model="formData.description"
|
||||
label="Beschreibung"
|
||||
type="textarea"
|
||||
rows="3"
|
||||
></ds-input>
|
||||
|
||||
<ds-select
|
||||
icon="card"
|
||||
v-model="formData.actionRadius"
|
||||
label="Radius"
|
||||
<option v-for="groupType in groupTypeOptions" :key="groupType" :value="groupType">
|
||||
{{ $t(`group.types.${groupType}`) }}
|
||||
</option>
|
||||
</select>
|
||||
<!-- goal -->
|
||||
<ds-input
|
||||
:label="$t('group.goal')"
|
||||
v-model="formData.about"
|
||||
:placeholder="$t('group.goal') + ' …'"
|
||||
rows="3"
|
||||
></ds-input>
|
||||
<!-- description -->
|
||||
<ds-input
|
||||
:label="$t('group.description')"
|
||||
v-model="formData.description"
|
||||
:placeholder="$t('group.description') + ' …'"
|
||||
type="textarea"
|
||||
rows="3"
|
||||
></ds-input>
|
||||
<ds-space margin-top="large">
|
||||
<!-- actionRadius -->
|
||||
<!-- TODO: move 'ds-select' from styleguide to main code and implement missen translation etc. functionality -->
|
||||
<!-- <ds-select
|
||||
id="actionRadius"
|
||||
:label="$t('group.actionRadius')"
|
||||
v-model="formData"
|
||||
model="actionRadius"
|
||||
:options="['regional', 'national', 'continental', 'global']"
|
||||
placeholder="Radius ..."
|
||||
></ds-select>
|
||||
icon="globe"
|
||||
:placeholder="`${$t('group.actionRadius')} …`"
|
||||
></ds-select> -->
|
||||
<ds-text class="select-label">
|
||||
{{ $t('group.actionRadius') }}
|
||||
</ds-text>
|
||||
<ds-icon class="select-icon" name="globe" />
|
||||
<select
|
||||
class="select"
|
||||
:options="actionRadiusOptions"
|
||||
:value="formData.actionRadius"
|
||||
@change="changeActionRadius($event)"
|
||||
>
|
||||
<option
|
||||
v-for="actionRadius in actionRadiusOptions"
|
||||
:key="actionRadius"
|
||||
:value="actionRadius"
|
||||
>
|
||||
{{ $t(`group.actionRadii.${actionRadius}`) }}
|
||||
</option>
|
||||
</select>
|
||||
<!-- location -->
|
||||
<ds-select
|
||||
id="city"
|
||||
:label="$t('settings.data.labelCity')"
|
||||
v-model="formData.locationName"
|
||||
:options="cities"
|
||||
icon="map-marker"
|
||||
:placeholder="$t('settings.data.labelCity') + ' …'"
|
||||
:loading="loadingGeo"
|
||||
@input.native="handleCityInput"
|
||||
/>
|
||||
<!-- TODO: implement clear button -->
|
||||
<!-- <base-button icon="close" circle ghost size="small" :disabled="formData.locationName.length === 0" @click="clear" /> -->
|
||||
</ds-space>
|
||||
<ds-space margin-top="large">
|
||||
<categories-select
|
||||
v-if="categoriesActive"
|
||||
model="categoryIds"
|
||||
:existingCategoryIds="formData.categoryIds"
|
||||
/>
|
||||
|
||||
<div>{{ formData }}</div>
|
||||
|
||||
<ds-space margin-top="large">
|
||||
<ds-button @click.prevent="reset()">Reset form</ds-button>
|
||||
<ds-button
|
||||
type="submit"
|
||||
@click.prevent="submit()"
|
||||
icon="save"
|
||||
:disabled="disabled"
|
||||
primary
|
||||
>
|
||||
{{ update ? $t('group.update') : $t('group.save') }}
|
||||
</ds-button>
|
||||
</ds-space>
|
||||
</ds-form>
|
||||
<ds-space centered v-show="!update">
|
||||
<nuxt-link to="/my-groups">zurück</nuxt-link>
|
||||
</ds-space>
|
||||
</ds-container>
|
||||
<ds-space margin-top="large">
|
||||
<nuxt-link to="/my-groups">
|
||||
<ds-button>{{ $t('actions.cancel') }}</ds-button>
|
||||
</nuxt-link>
|
||||
<ds-button
|
||||
type="submit"
|
||||
icon="save"
|
||||
:disabled="update ? submitDisableEdit : submitDisable"
|
||||
primary
|
||||
@click.prevent="submit()"
|
||||
>
|
||||
{{ update ? $t('group.update') : $t('group.save') }}
|
||||
</ds-button>
|
||||
</ds-space>
|
||||
</ds-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -70,6 +134,9 @@
|
||||
import CategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect'
|
||||
import { CATEGORIES_MIN, CATEGORIES_MAX } from '~/constants/categories.js'
|
||||
import { NAME_LENGTH_MIN, NAME_LENGTH_MAX } from '~/constants/groups.js'
|
||||
import { queryLocations } from '~/graphql/location'
|
||||
|
||||
let timeout
|
||||
|
||||
export default {
|
||||
name: 'GroupForm',
|
||||
@ -89,24 +156,33 @@ export default {
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const { name, groupType, about, description, actionRadius, categories } = this.group
|
||||
const { name, slug, groupType, about, description, actionRadius, locationName, categories } =
|
||||
this.group
|
||||
return {
|
||||
categoriesActive: this.$env.CATEGORIES_ACTIVE,
|
||||
disabled: false,
|
||||
groupTypeOptions: ['public', 'closed', 'hidden'],
|
||||
actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
|
||||
loadingGeo: false,
|
||||
cities: [],
|
||||
formData: {
|
||||
name: name || '',
|
||||
slug: slug || '',
|
||||
groupType: groupType || '',
|
||||
about: about || '',
|
||||
description: description || '',
|
||||
locationName: locationName || '',
|
||||
actionRadius: actionRadius || '',
|
||||
categoryIds: categories ? categories.map((category) => category.id) : [],
|
||||
},
|
||||
formSchema: {
|
||||
name: { required: true, min: NAME_LENGTH_MIN, max: NAME_LENGTH_MAX },
|
||||
slug: { required: false },
|
||||
groupType: { required: true },
|
||||
about: { required: true },
|
||||
description: { required: true },
|
||||
actionRadius: { required: true },
|
||||
locationName: { required: false },
|
||||
categoryIds: {
|
||||
type: 'array',
|
||||
required: this.categoriesActive,
|
||||
@ -123,16 +199,54 @@ export default {
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
submitDisable() {
|
||||
return (
|
||||
this.formData.name === '' ||
|
||||
this.formData.groupType === '' ||
|
||||
// this.formData.about === '' || // not mandatory
|
||||
this.formData.description === '' ||
|
||||
this.formData.actionRadius === '' ||
|
||||
// this.formData.locationName === '' || // not mandatory
|
||||
this.formData.categoryIds.length === 0
|
||||
)
|
||||
},
|
||||
submitDisableEdit() {
|
||||
return (
|
||||
this.formData.name === this.group.name &&
|
||||
this.formData.slug === this.group.slug &&
|
||||
// this.formData.groupType === this.group.groupType && // can not be changed for now
|
||||
this.formData.about === this.group.about &&
|
||||
this.formData.description === this.group.description &&
|
||||
this.formData.actionRadius === this.group.actionRadius &&
|
||||
this.formData.locationName === (this.group.locationName ? this.group.locationName : '') &&
|
||||
this.sameCategories
|
||||
)
|
||||
},
|
||||
sameCategories() {
|
||||
const formDataCategories = this.formData.categoryIds.map((id) => id).sort()
|
||||
const groupDataCategories = this.group.categories.map((category) => category.id).sort()
|
||||
let equal = true
|
||||
|
||||
if (formDataCategories.length !== groupDataCategories.length) return false
|
||||
|
||||
formDataCategories.forEach((id, index) => {
|
||||
equal = equal && id === groupDataCategories[index]
|
||||
})
|
||||
return equal
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
const { name, about, description, groupType, actionRadius, categoryIds } = this.formData
|
||||
const { name, about, description, groupType, actionRadius, locationName, categoryIds } =
|
||||
this.formData
|
||||
const variables = {
|
||||
name,
|
||||
about,
|
||||
description,
|
||||
groupType,
|
||||
actionRadius,
|
||||
locationName: locationName.label,
|
||||
categoryIds,
|
||||
}
|
||||
this.update
|
||||
@ -142,9 +256,64 @@ export default {
|
||||
})
|
||||
: this.$emit('createGroup', variables)
|
||||
},
|
||||
reset() {
|
||||
alert('reset')
|
||||
changeGroupType(event) {
|
||||
this.formData.groupType = event.target.value
|
||||
},
|
||||
changeActionRadius(event) {
|
||||
this.formData.actionRadius = event.target.value
|
||||
},
|
||||
handleCityInput(value) {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => this.requestGeoData(value), 500)
|
||||
},
|
||||
processLocationsResult(places) {
|
||||
if (!places.length) {
|
||||
return []
|
||||
}
|
||||
const result = []
|
||||
places.forEach((place) => {
|
||||
result.push({
|
||||
label: place.place_name,
|
||||
value: place.place_name,
|
||||
id: place.id,
|
||||
})
|
||||
})
|
||||
|
||||
return result
|
||||
},
|
||||
async requestGeoData(e) {
|
||||
const value = e.target ? e.target.value.trim() : ''
|
||||
if (value === '') {
|
||||
this.cities = []
|
||||
return
|
||||
}
|
||||
this.loadingGeo = true
|
||||
|
||||
const place = encodeURIComponent(value)
|
||||
const lang = this.$i18n.locale()
|
||||
|
||||
const {
|
||||
data: { queryLocations: res },
|
||||
} = await this.$apollo.query({ query: queryLocations(), variables: { place, lang } })
|
||||
|
||||
this.cities = this.processLocationsResult(res)
|
||||
this.loadingGeo = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.select-label {
|
||||
margin-bottom: 3pt;
|
||||
color: $text-color-soft;
|
||||
}
|
||||
.select-icon {
|
||||
margin-right: 4pt;
|
||||
color: $text-color-disabled;
|
||||
}
|
||||
.select {
|
||||
margin-bottom: $space-small;
|
||||
color: $text-color-base;
|
||||
}
|
||||
</style>
|
||||
|
||||
21
webapp/components/Group/GroupList.vue
Normal file
21
webapp/components/Group/GroupList.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-space margin-bottom="small" v-for="group in groups" :key="group.id">
|
||||
<group-teaser :group="group" />
|
||||
</ds-space>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GroupTeaser from '~/components/Group/GroupTeaser'
|
||||
|
||||
export default {
|
||||
name: 'GroupList',
|
||||
components: {
|
||||
GroupTeaser,
|
||||
},
|
||||
props: {
|
||||
groups: { type: Array, default: () => [] },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,56 +1,148 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-space><h3>Members</h3></ds-space>
|
||||
<ds-table :data="GroupMembers" :fields="tableFields">
|
||||
<template slot="avatar">
|
||||
<ds-avatar online size="small" name="Hans Peter"></ds-avatar>
|
||||
<ds-table :fields="tableFields" :data="groupMembers" condensed>
|
||||
<template #avatar="scope">
|
||||
<nuxt-link
|
||||
:to="{
|
||||
name: 'profile-id-slug',
|
||||
params: { id: scope.row.id, slug: scope.row.slug },
|
||||
}"
|
||||
>
|
||||
<ds-avatar online size="small" :name="scope.row.name"></ds-avatar>
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<template slot="loves" slot-scope="scope">
|
||||
{{ scope.row.name }} loves {{ scope.row.loves }}
|
||||
<template #name="scope">
|
||||
<nuxt-link
|
||||
:to="{
|
||||
name: 'profile-id-slug',
|
||||
params: { id: scope.row.id, slug: scope.row.slug },
|
||||
}"
|
||||
>
|
||||
<ds-text>
|
||||
<b>{{ scope.row.name | truncate(20) }}</b>
|
||||
</ds-text>
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<template slot="edit" slot-scope="scope">
|
||||
<ds-button size="small" @click="deleteRow(scope.row)">delete</ds-button>
|
||||
<template #slug="scope">
|
||||
<nuxt-link
|
||||
:to="{
|
||||
name: 'profile-id-slug',
|
||||
params: { id: scope.row.id, slug: scope.row.slug },
|
||||
}"
|
||||
>
|
||||
<ds-text>
|
||||
<b>{{ `@${scope.row.slug}` | truncate(20) }}</b>
|
||||
</ds-text>
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<template #roleInGroup="scope">
|
||||
<select
|
||||
v-if="scope.row.myRoleInGroup !== 'owner'"
|
||||
:options="['pending', 'usual', 'admin', 'owner']"
|
||||
:value="`${scope.row.myRoleInGroup}`"
|
||||
@change="changeMemberRole(scope.row.id, $event)"
|
||||
>
|
||||
<option v-for="role in ['pending', 'usual', 'admin', 'owner']" :key="role" :value="role">
|
||||
{{ $t(`group.roles.${role}`) }}
|
||||
</option>
|
||||
</select>
|
||||
<ds-chip v-else color="primary">
|
||||
{{ $t(`group.roles.${scope.row.myRoleInGroup}`) }}
|
||||
</ds-chip>
|
||||
</template>
|
||||
<template #edit="scope">
|
||||
<ds-button size="small" primary :disabled="true" @click="openModal(scope.row)">
|
||||
<!-- TODO: implement removal of group members -->
|
||||
<!-- :disabled="scope.row.myRoleInGroup === 'owner'"
|
||||
-->
|
||||
{{ $t('group.removeMemberButton') }}
|
||||
</ds-button>
|
||||
</template>
|
||||
</ds-table>
|
||||
<!-- TODO: implement removal of group members -->
|
||||
<!-- TODO: change to ocelot.social modal -->
|
||||
<!-- <ds-modal
|
||||
v-if="isOpen"
|
||||
v-model="isOpen"
|
||||
:title="`${$t('group.removeMember')}`"
|
||||
force
|
||||
extended
|
||||
:confirm-label="$t('group.removeMember')"
|
||||
:cancel-label="$t('actions.cancel')"
|
||||
@confirm="deleteMember(memberId)"
|
||||
/> -->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { changeGroupMemberRoleMutation } from '~/graphql/groups.js'
|
||||
|
||||
export default {
|
||||
name: 'GroupMember',
|
||||
props: {
|
||||
groupId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
groupMembers: {
|
||||
type: Array,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableFields: ['avatar', 'name', 'type', 'loves', 'edit'],
|
||||
GroupMembers: [
|
||||
{
|
||||
name: 'Rengar',
|
||||
type: 'Jungler',
|
||||
loves: 'Hide and seek',
|
||||
},
|
||||
{
|
||||
name: 'Renekton',
|
||||
type: 'Toplaner',
|
||||
loves: 'Slice and dice',
|
||||
},
|
||||
{
|
||||
name: 'Twitch',
|
||||
type: 'ADC',
|
||||
loves: 'Spray and pray',
|
||||
},
|
||||
{
|
||||
name: 'Blitz',
|
||||
type: 'Support',
|
||||
loves: 'Hook you up',
|
||||
},
|
||||
],
|
||||
isOpen: false,
|
||||
memberId: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteRow(row) {
|
||||
const index = this.tableData.indexOf(row)
|
||||
if (index > -1) {
|
||||
this.tableData.splice(index, 1)
|
||||
computed: {
|
||||
tableFields() {
|
||||
return {
|
||||
avatar: {
|
||||
label: this.$t('group.membersAdministrationList.avatar'),
|
||||
align: 'left',
|
||||
},
|
||||
name: {
|
||||
label: this.$t('group.membersAdministrationList.name'),
|
||||
align: 'left',
|
||||
},
|
||||
slug: {
|
||||
label: this.$t('group.membersAdministrationList.slug'),
|
||||
align: 'left',
|
||||
},
|
||||
roleInGroup: {
|
||||
label: this.$t('group.membersAdministrationList.roleInGroup'),
|
||||
align: 'left',
|
||||
},
|
||||
edit: {
|
||||
label: '',
|
||||
align: 'left',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async changeMemberRole(id, event) {
|
||||
const newRole = event.target.value
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: { groupId: this.groupId, userId: id, roleInGroup: newRole },
|
||||
})
|
||||
this.$toast.success(
|
||||
this.$t('group.changeMemberRole', { role: this.$t(`group.roles.${newRole}`) }),
|
||||
)
|
||||
} catch (error) {
|
||||
this.$toast.error(error.message)
|
||||
}
|
||||
},
|
||||
// TODO: implement removal of group members
|
||||
// openModal(row) {
|
||||
// this.isOpen = true
|
||||
// this.memberId = row.id
|
||||
// },
|
||||
// deleteMember(id) {
|
||||
// alert('deleteMember: ' + id)
|
||||
// },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,26 +1,183 @@
|
||||
<template>
|
||||
<div class="group-teaser">
|
||||
<ds-grid-item :row-span="2" column-span="fullWidth">
|
||||
<ds-space centered>
|
||||
<nuxt-link :to="{ name: 'group-create' }">
|
||||
<base-button
|
||||
<nuxt-link
|
||||
class="group-teaser"
|
||||
:to="{ name: 'group-id-slug', params: { id: group.id, slug: group.slug } }"
|
||||
>
|
||||
<base-card
|
||||
:class="{
|
||||
'disabled-content': group.disabled,
|
||||
}"
|
||||
>
|
||||
<h2 class="title hyphenate-text">{{ group.name }}</h2>
|
||||
<div class="slug-location">
|
||||
<!-- group slug -->
|
||||
<div>
|
||||
<ds-text color="soft">
|
||||
<base-icon name="at" />
|
||||
{{ group.slug }}
|
||||
</ds-text>
|
||||
</div>
|
||||
<!-- group location -->
|
||||
<div class="location-item">
|
||||
<ds-text v-if="group && group.location" color="soft">
|
||||
<base-icon name="map-marker" />
|
||||
{{ group && group.location ? group.location.name : '' }}
|
||||
</ds-text>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div class="content hyphenate-text" v-html="descriptionExcerpt" />
|
||||
<footer class="footer">
|
||||
<div>
|
||||
<!-- group my role in group -->
|
||||
<ds-chip color="primary">
|
||||
{{ group && group.myRole ? $t('group.roles.' + group.myRole) : '' }}
|
||||
</ds-chip>
|
||||
<!-- group type -->
|
||||
<ds-chip color="primary">
|
||||
{{ group && group.groupType ? $t('group.types.' + group.groupType) : '' }}
|
||||
</ds-chip>
|
||||
<!-- group action radius -->
|
||||
<ds-chip color="primary">
|
||||
{{ group && group.actionRadius ? $t('group.actionRadii.' + group.actionRadius) : '' }}
|
||||
</ds-chip>
|
||||
</div>
|
||||
<!-- group categories -->
|
||||
<div class="categories" v-if="categoriesActive">
|
||||
<category
|
||||
v-for="category in group.categories"
|
||||
:key="category.id"
|
||||
v-tooltip="{
|
||||
content: $t('group.newGroup'),
|
||||
placement: 'left',
|
||||
content: $t(`contribution.category.description.${category.slug}`),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
:path="{ name: 'group-create' }"
|
||||
class="profile-post-add-button"
|
||||
icon="plus"
|
||||
circle
|
||||
filled
|
||||
:icon="category.icon"
|
||||
/>
|
||||
</nuxt-link>
|
||||
</ds-space>
|
||||
</ds-grid-item>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="categories-placeholder"></div>
|
||||
<!-- group context menu -->
|
||||
<client-only>
|
||||
<group-content-menu
|
||||
:usage="'groupProfile'"
|
||||
:resourceType="'group'"
|
||||
:resource="group"
|
||||
:group="group"
|
||||
/>
|
||||
</client-only>
|
||||
</footer>
|
||||
<footer class="footer">
|
||||
<!-- group goal -->
|
||||
<div class="labeled-chip">
|
||||
<ds-text class="label-text hyphenate-text" color="soft" size="small">
|
||||
{{ $t('group.goal') }}
|
||||
</ds-text>
|
||||
<div class="chip">
|
||||
<ds-chip v-if="group && group.about">{{ group ? group.about : '' }}</ds-chip>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</base-card>
|
||||
</nuxt-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Category from '~/components/Category'
|
||||
import GroupContentMenu from '~/components/Group/GroupContentMenu'
|
||||
|
||||
export default {
|
||||
name: 'GroupTeaser',
|
||||
components: {
|
||||
Category,
|
||||
GroupContentMenu,
|
||||
},
|
||||
props: {
|
||||
group: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
width: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
categoriesActive: this.$env.CATEGORIES_ACTIVE,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
descriptionExcerpt() {
|
||||
return this.$filters.removeLinks(this.group.descriptionExcerpt)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.group-teaser,
|
||||
.group-teaser:hover,
|
||||
.group-teaser:active {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 100%;
|
||||
color: $text-color-base;
|
||||
|
||||
> .ribbon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: -7px;
|
||||
}
|
||||
}
|
||||
|
||||
.group-teaser > .base-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
> .title {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
> .slug-location {
|
||||
display: flex;
|
||||
margin-bottom: $space-small;
|
||||
|
||||
> .location-item {
|
||||
margin-left: $space-small;
|
||||
}
|
||||
}
|
||||
|
||||
> .content {
|
||||
flex-grow: 1;
|
||||
margin-bottom: $space-small;
|
||||
}
|
||||
|
||||
> .footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
> .categories-placeholder {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
> .labeled-chip {
|
||||
margin-top: $space-xx-small;
|
||||
|
||||
> .chip {
|
||||
margin-top: -$space-small + $space-x-small;
|
||||
}
|
||||
}
|
||||
|
||||
> .content-menu {
|
||||
position: relative;
|
||||
z-index: $z-index-post-teaser-link;
|
||||
}
|
||||
}
|
||||
|
||||
.user-teaser {
|
||||
margin-bottom: $space-small;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -19,9 +19,8 @@
|
||||
</client-only>
|
||||
<h2 class="title hyphenate-text">{{ post.title }}</h2>
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div class="content hyphenate-text" v-html="excerpt" />
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<footer
|
||||
class="footer"
|
||||
v-observe-visibility="(isVisible, entry) => visibilityChanged(isVisible, entry, post.id)"
|
||||
|
||||
@ -46,7 +46,7 @@ export default {
|
||||
type: String,
|
||||
default: 'regular',
|
||||
validator(value) {
|
||||
return value.match(/(small|regular)/)
|
||||
return value.match(/(small|regular|large)/)
|
||||
},
|
||||
},
|
||||
type: {
|
||||
@ -70,6 +70,7 @@ export default {
|
||||
if (this.danger) buttonClass += ' --danger'
|
||||
if (this.loading) buttonClass += ' --loading'
|
||||
if (this.size === 'small') buttonClass += ' --small'
|
||||
if (this.size === 'large') buttonClass += ' --large'
|
||||
|
||||
if (this.filled) buttonClass += ' --filled'
|
||||
else if (this.ghost) buttonClass += ' --ghost'
|
||||
@ -127,6 +128,15 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
&.--large {
|
||||
height: $size-button-large;
|
||||
font-size: $font-size-large;
|
||||
|
||||
&.--circle {
|
||||
width: $size-button-large;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.--icon-only) > .base-icon {
|
||||
margin-right: $space-xx-small;
|
||||
}
|
||||
|
||||
@ -91,6 +91,9 @@
|
||||
<client-only v-if="SHOW_GROUP_BUTTON_IN_HEADER">
|
||||
<group-button />
|
||||
</client-only>
|
||||
<client-only>
|
||||
<group-button />
|
||||
</client-only>
|
||||
<client-only>
|
||||
<avatar-menu placement="top" />
|
||||
</client-only>
|
||||
|
||||
@ -405,23 +405,49 @@
|
||||
},
|
||||
"actionRadius": "Aktionsradius",
|
||||
"categories": "Thema ::: Themen",
|
||||
"changeMemberRole": "Die Rolle wurde auf „{role}“ geändert!",
|
||||
"contentMenu": {
|
||||
"visitGroupPage": "Gruppe anzeigen"
|
||||
},
|
||||
"createNewGroup": {
|
||||
"title": "Erstelle eine neue Gruppe",
|
||||
"tooltip": "Erstelle eine neue Gruppe"
|
||||
},
|
||||
"description": "Beschreibung",
|
||||
"editGroupSettings": {
|
||||
"groupName": "Einstellungen für „{name}“",
|
||||
"title": "Meine Gruppe ändern"
|
||||
},
|
||||
"follow": "Folge",
|
||||
"foundation": "Gründung",
|
||||
"general": "Allgemein",
|
||||
"goal": "Ziel der Gruppe",
|
||||
"group-created": "Die Gruppe wurde angelegt!",
|
||||
"group-updated": "Die Gruppendaten wurden geändert!",
|
||||
"groupCreated": "Die Gruppe wurde angelegt!",
|
||||
"joinLeaveButton": {
|
||||
"iAmMember": "Bin Mitglied",
|
||||
"join": "Beitreten"
|
||||
},
|
||||
"labelSlug": "Eindeutiger Gruppenname",
|
||||
"leaveModal": {
|
||||
"confirmButton": "Verlassen",
|
||||
"message": "Eine Gruppe zu verlassen ist möglicherweise nicht rückgängig zu machen!<br>Gruppe <b>„{name}“</b> verlassen!",
|
||||
"title": "Möchtest du wirklich die Gruppe verlassen?"
|
||||
},
|
||||
"members": "Mitglieder",
|
||||
"membersAdministrationList": {
|
||||
"avatar": "Avatar",
|
||||
"name": "Name",
|
||||
"roleInGroup": "Rolle",
|
||||
"slug": "Eindeutiger Name"
|
||||
},
|
||||
"membersCount": "Mitglied ::: Mitglieder",
|
||||
"membersListTitle": "Gruppenmitglieder",
|
||||
"membersListTitleNotAllowedSeeingGroupMembers": "Gruppenmitglieder unsichtbar",
|
||||
"newGroup": "Erstelle eine neue Gruppe",
|
||||
"myGroups": "Meine Gruppen",
|
||||
"name": "Gruppenname",
|
||||
"radius": "Radius",
|
||||
"removeMember": "Mitglied aus der Gruppe entfernen?",
|
||||
"removeMemberButton": "Entfernen",
|
||||
"role": "Deine Rolle in der Gruppe",
|
||||
"roles": {
|
||||
"admin": "Administrator",
|
||||
@ -436,7 +462,8 @@
|
||||
"hidden": "Versteckte Gruppe",
|
||||
"public": "Öffentliche Gruppe"
|
||||
},
|
||||
"update": "Änderung speichern"
|
||||
"update": "Änderung speichern",
|
||||
"updatedGroup": "Die Gruppendaten wurden geändert!"
|
||||
},
|
||||
"hashtags-filter": {
|
||||
"clearSearch": "Suche löschen",
|
||||
@ -818,7 +845,6 @@
|
||||
"unmute": "Stummschaltung aufheben",
|
||||
"unmuted": "{name} ist nicht mehr stummgeschaltet"
|
||||
},
|
||||
"myGroups": "Meine Gruppen",
|
||||
"name": "Einstellungen",
|
||||
"notifications": {
|
||||
"name": "Benachrichtigungen",
|
||||
|
||||
@ -405,23 +405,49 @@
|
||||
},
|
||||
"actionRadius": "Action radius",
|
||||
"categories": "Topic ::: Topics",
|
||||
"changeMemberRole": "The role has been changed to “{role}”!",
|
||||
"contentMenu": {
|
||||
"visitGroupPage": "Show group"
|
||||
},
|
||||
"createNewGroup": {
|
||||
"title": "Create A New Group",
|
||||
"tooltip": "Create a new group"
|
||||
},
|
||||
"description": "Description",
|
||||
"editGroupSettings": {
|
||||
"groupName": "Settings Of “{name}”",
|
||||
"title": "Edit My Group"
|
||||
},
|
||||
"follow": "Follow",
|
||||
"foundation": "Foundation",
|
||||
"general": "General",
|
||||
"goal": "Goal of group",
|
||||
"group-created": "The group was created!",
|
||||
"group-updated": "The group data has been changed.",
|
||||
"groupCreated": "The group was created!",
|
||||
"joinLeaveButton": {
|
||||
"iAmMember": "I'm a member",
|
||||
"join": "Join"
|
||||
},
|
||||
"labelSlug": "Unique group name",
|
||||
"leaveModal": {
|
||||
"confirmButton": "Leave",
|
||||
"message": "Leaving a group may be irreversible!<br>Leave group <b>“{name}”</b>!",
|
||||
"title": "Do you really want to leave the group?"
|
||||
},
|
||||
"members": "Members",
|
||||
"membersAdministrationList": {
|
||||
"avatar": "Avatar",
|
||||
"name": "Name",
|
||||
"roleInGroup": "Role",
|
||||
"slug": "Unique name"
|
||||
},
|
||||
"membersCount": "Member ::: Members",
|
||||
"membersListTitle": "Group Members",
|
||||
"membersListTitleNotAllowedSeeingGroupMembers": "Group Members invisible",
|
||||
"newGroup": "Create a new Group",
|
||||
"myGroups": "My Groups",
|
||||
"name": "Group name",
|
||||
"radius": "Radius",
|
||||
"removeMember": "Remove member",
|
||||
"removeMemberButton": "Remove",
|
||||
"role": "Your role in the group",
|
||||
"roles": {
|
||||
"admin": "Administrator",
|
||||
@ -436,7 +462,8 @@
|
||||
"hidden": "Hidden Group",
|
||||
"public": "Public Group"
|
||||
},
|
||||
"update": "Save change"
|
||||
"update": "Save change",
|
||||
"updatedGroup": "The group data has been changed."
|
||||
},
|
||||
"hashtags-filter": {
|
||||
"clearSearch": "Clear search",
|
||||
@ -818,7 +845,6 @@
|
||||
"unmute": "Unmute user",
|
||||
"unmuted": "{name} is unmuted again"
|
||||
},
|
||||
"myGroups": "My Groups",
|
||||
"name": "Settings",
|
||||
"notifications": {
|
||||
"name": "Notifications",
|
||||
|
||||
@ -238,7 +238,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@yoga-practice')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('yoga-practice')
|
||||
})
|
||||
|
||||
describe('displays no(!) group location – because is "null"', () => {
|
||||
@ -385,7 +386,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@yoga-practice')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('yoga-practice')
|
||||
})
|
||||
|
||||
describe('displays no(!) group location – because is "null"', () => {
|
||||
@ -492,7 +494,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@yoga-practice')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('yoga-practice')
|
||||
})
|
||||
|
||||
describe('displays no(!) group location – because is "null"', () => {
|
||||
@ -599,7 +602,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@yoga-practice')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('yoga-practice')
|
||||
})
|
||||
|
||||
describe('displays no(!) group location – because is "null"', () => {
|
||||
@ -710,7 +714,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@school-for-citizens')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('school-for-citizens')
|
||||
})
|
||||
|
||||
describe('displays group location', () => {
|
||||
@ -821,7 +826,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@school-for-citizens')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('school-for-citizens')
|
||||
})
|
||||
|
||||
describe('displays group location', () => {
|
||||
@ -932,7 +938,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@school-for-citizens')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('school-for-citizens')
|
||||
})
|
||||
|
||||
describe('displays group location', () => {
|
||||
@ -1043,7 +1050,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@school-for-citizens')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('school-for-citizens')
|
||||
})
|
||||
|
||||
describe('displays group location', () => {
|
||||
@ -1158,7 +1166,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@investigative-journalism')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('investigative-journalism')
|
||||
})
|
||||
|
||||
describe('displays group location', () => {
|
||||
@ -1272,7 +1281,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has group slug', () => {
|
||||
expect(wrapper.text()).toContain('@investigative-journalism')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('investigative-journalism')
|
||||
})
|
||||
|
||||
describe('displays group location', () => {
|
||||
@ -1386,7 +1396,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has no(!) group slug', () => {
|
||||
expect(wrapper.text()).not.toContain('@investigative-journalism')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(false)
|
||||
expect(wrapper.text()).not.toContain('investigative-journalism')
|
||||
})
|
||||
|
||||
describe('displays not(!) group location', () => {
|
||||
@ -1488,7 +1499,8 @@ describe('GroupProfileSlug', () => {
|
||||
})
|
||||
|
||||
it('has no(!) group slug', () => {
|
||||
expect(wrapper.text()).not.toContain('@investigative-journalism')
|
||||
expect(wrapper.find('[data-test="at"]').exists()).toBe(false)
|
||||
expect(wrapper.text()).not.toContain('investigative-journalism')
|
||||
})
|
||||
|
||||
describe('displays not(!) group location', () => {
|
||||
|
||||
@ -18,10 +18,11 @@
|
||||
<!-- Menu -->
|
||||
<client-only>
|
||||
<group-content-menu
|
||||
placement="bottom-end"
|
||||
class="group-content-menu"
|
||||
:usage="'groupProfile'"
|
||||
:resourceType="'group'"
|
||||
:resource="group || {}"
|
||||
class="group-content-menu"
|
||||
placement="bottom-end"
|
||||
/>
|
||||
<!-- TODO: implement later on -->
|
||||
<!-- @mute="muteUser"
|
||||
@ -31,26 +32,27 @@
|
||||
@delete="deleteUser" -->
|
||||
</client-only>
|
||||
<ds-space margin="small">
|
||||
<!-- Group name -->
|
||||
<!-- group name -->
|
||||
<ds-heading tag="h3" align="center" no-margin>
|
||||
{{ groupName }}
|
||||
</ds-heading>
|
||||
<!-- Group slug -->
|
||||
<!-- group slug -->
|
||||
<ds-text align="center" color="soft">
|
||||
<base-icon name="at" data-test="at" />
|
||||
{{ groupSlug }}
|
||||
</ds-text>
|
||||
<!-- Group location -->
|
||||
<!-- group location -->
|
||||
<ds-text v-if="group && group.location" align="center" color="soft" size="small">
|
||||
<base-icon name="map-marker" data-test="map-marker" />
|
||||
{{ group && group.location ? group.location.name : '' }}
|
||||
</ds-text>
|
||||
<!-- Group created at -->
|
||||
<!-- group created at -->
|
||||
<ds-text align="center" color="soft" size="small">
|
||||
{{ $t('group.foundation') }} {{ group.createdAt | date('MMMM yyyy') }}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
<ds-flex v-if="isAllowedSeeingGroupMembers">
|
||||
<!-- Group members count -->
|
||||
<!-- group members count -->
|
||||
<ds-flex-item v-if="isAllowedSeeingGroupMembers">
|
||||
<client-only>
|
||||
<ds-number :label="$t('group.membersCount', {}, groupMembers.length)">
|
||||
@ -110,7 +112,7 @@
|
||||
</div>
|
||||
<hr />
|
||||
<ds-space margin-top="small" margin-bottom="small">
|
||||
<!-- Group my role in group -->
|
||||
<!-- group my role in group -->
|
||||
<template v-if="isGroupMember">
|
||||
<ds-text class="centered-text hyphenate-text" color="soft" size="small">
|
||||
{{ $t('group.role') }}
|
||||
@ -121,7 +123,7 @@
|
||||
</ds-chip>
|
||||
</div>
|
||||
</template>
|
||||
<!-- Group type -->
|
||||
<!-- group type -->
|
||||
<ds-text class="centered-text hyphenate-text" color="soft" size="small">
|
||||
{{ $t('group.type') }}
|
||||
</ds-text>
|
||||
@ -130,7 +132,7 @@
|
||||
{{ group && group.groupType ? $t('group.types.' + group.groupType) : '' }}
|
||||
</ds-chip>
|
||||
</div>
|
||||
<!-- Group action radius -->
|
||||
<!-- group action radius -->
|
||||
<ds-text class="centered-text hyphenate-text" color="soft" size="small">
|
||||
{{ $t('group.actionRadius') }}
|
||||
</ds-text>
|
||||
@ -143,7 +145,7 @@
|
||||
</div>
|
||||
<ds-space margin="x-small" />
|
||||
</ds-space>
|
||||
<!-- Group categories -->
|
||||
<!-- group categories -->
|
||||
<template v-if="categoriesActive">
|
||||
<hr />
|
||||
<ds-space margin-top="small" margin-bottom="small">
|
||||
@ -176,7 +178,7 @@
|
||||
</div>
|
||||
</ds-space>
|
||||
</template>
|
||||
<!-- Group goal -->
|
||||
<!-- group goal -->
|
||||
<template v-if="group && group.about">
|
||||
<hr />
|
||||
<ds-space margin-top="small" margin-bottom="small">
|
||||
@ -230,14 +232,13 @@
|
||||
<ds-space>
|
||||
<base-card class="group-description">
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div
|
||||
v-if="isDescriptionCollapsed"
|
||||
class="content hyphenate-text"
|
||||
v-html="groupDescriptionExcerpt"
|
||||
/>
|
||||
<content-viewer v-else class="content hyphenate-text" :content="group.description" />
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<base-button
|
||||
class="collaps-button"
|
||||
size="small"
|
||||
@ -401,7 +402,7 @@ export default {
|
||||
},
|
||||
groupSlug() {
|
||||
const { slug } = this.group || {}
|
||||
return slug && `@${slug}`
|
||||
return slug
|
||||
},
|
||||
groupDescriptionExcerpt() {
|
||||
return this.group ? this.$filters.removeLinks(this.group.descriptionExcerpt) : ''
|
||||
|
||||
@ -1,26 +1,33 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2>Create Groupe</h2>
|
||||
<ds-flex :width="{ base: '100%' }" gutter="base">
|
||||
<ds-flex-item :width="{ base: '100%', md: 5 }">
|
||||
<group-form @createGroup="createGroup" />
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', md: 1 }"> </ds-flex-item>
|
||||
</ds-flex>
|
||||
<hr />
|
||||
<group-member />
|
||||
<ds-space margin="small">
|
||||
<ds-heading tag="h1">{{ $t('group.createNewGroup.title') }}</ds-heading>
|
||||
</ds-space>
|
||||
<ds-space margin="large" />
|
||||
<ds-container>
|
||||
<base-card>
|
||||
<ds-space margin="large">
|
||||
<ds-flex :width="{ base: '100%' }" gutter="base">
|
||||
<ds-flex-item :width="{ base: '100%', md: 5 }">
|
||||
<ds-container>
|
||||
<group-form @createGroup="createGroup" />
|
||||
</ds-container>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', md: 1 }"> </ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-space>
|
||||
</base-card>
|
||||
</ds-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GroupForm from '~/components/Group/GroupForm'
|
||||
import GroupMember from '~/components/Group/GroupMember'
|
||||
import { createGroupMutation } from '~/graphql/groups.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GroupForm,
|
||||
GroupMember,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -29,14 +36,33 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async createGroup(value) {
|
||||
const { name, about, description, groupType, actionRadius, categoryIds } = value
|
||||
const variables = { name, about, description, groupType, actionRadius, categoryIds }
|
||||
const { name, about, description, groupType, actionRadius, locationName, categoryIds } = value
|
||||
const variables = {
|
||||
name,
|
||||
about,
|
||||
description,
|
||||
groupType,
|
||||
actionRadius,
|
||||
locationName,
|
||||
categoryIds,
|
||||
}
|
||||
let responseId, responseSlug
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: createGroupMutation(),
|
||||
variables,
|
||||
update: (_store, { data }) => {
|
||||
const { id: groupId, slug: groupSlug } = data.CreateGroup
|
||||
responseId = groupId
|
||||
responseSlug = groupSlug
|
||||
},
|
||||
})
|
||||
this.$toast.success(this.$t('group.groupCreated'))
|
||||
// this.$router.history.push('/my-groups')
|
||||
this.$router.history.push({
|
||||
name: 'group-id-slug',
|
||||
params: { id: responseId, slug: responseSlug },
|
||||
})
|
||||
this.$toast.success(this.$t('group.group-created'))
|
||||
} catch (error) {
|
||||
this.$toast.error(error.message)
|
||||
}
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-page-title heading="Group Setting"></ds-page-title>
|
||||
<ds-space margin="small">
|
||||
<ds-heading tag="h1">{{ $t('group.editGroupSettings.title') }}</ds-heading>
|
||||
<ds-heading tag="h2">
|
||||
{{ $t('group.editGroupSettings.groupName', { name: group.name }) }}
|
||||
</ds-heading>
|
||||
</ds-space>
|
||||
<ds-space margin="large" />
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item :width="{ base: '100%', md: '200px' }">
|
||||
<ds-menu :routes="routes" :is-exact="() => true" />
|
||||
@ -11,9 +17,6 @@
|
||||
</transition>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
<ds-space centered>
|
||||
<nuxt-link to="/my-groups">zurück</nuxt-link>
|
||||
</ds-space>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -29,11 +32,11 @@ export default {
|
||||
routes() {
|
||||
return [
|
||||
{
|
||||
name: 'General',
|
||||
name: this.$t('group.general'),
|
||||
path: `/group/edit/${this.group.id}`,
|
||||
},
|
||||
{
|
||||
name: 'Members',
|
||||
name: this.$t('group.members'),
|
||||
path: `/group/edit/${this.group.id}/members`,
|
||||
},
|
||||
]
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-container>
|
||||
<base-card>
|
||||
<ds-heading tag="h3">{{ $t('group.general') }}</ds-heading>
|
||||
<ds-space margin="large" />
|
||||
<group-form :group="group" :update="true" @updateGroup="updateGroup" />
|
||||
</ds-container>
|
||||
</base-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -23,14 +25,44 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async updateGroup(value) {
|
||||
const { id, name, about, description, groupType, actionRadius, categoryIds } = value
|
||||
const variables = { id, name, about, description, groupType, actionRadius, categoryIds }
|
||||
const {
|
||||
id,
|
||||
slug,
|
||||
name,
|
||||
about,
|
||||
description,
|
||||
groupType,
|
||||
actionRadius,
|
||||
locationName,
|
||||
categoryIds,
|
||||
} = value
|
||||
const variables = {
|
||||
id,
|
||||
name,
|
||||
slug,
|
||||
about,
|
||||
description,
|
||||
groupType,
|
||||
actionRadius,
|
||||
locationName,
|
||||
categoryIds,
|
||||
}
|
||||
let responseId, responseSlug
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: updateGroupMutation(),
|
||||
variables,
|
||||
update: (_store, { data }) => {
|
||||
const { id: groupId, slug: groupSlug } = data.UpdateGroup
|
||||
responseId = groupId
|
||||
responseSlug = groupSlug
|
||||
},
|
||||
})
|
||||
this.$toast.success(this.$t('group.updatedGroup'))
|
||||
this.$router.history.push({
|
||||
name: 'group-id-slug',
|
||||
params: { id: responseId, slug: responseSlug },
|
||||
})
|
||||
this.$toast.success(this.$t('group.group-updated'))
|
||||
} catch (error) {
|
||||
this.$toast.error(error.message)
|
||||
}
|
||||
|
||||
@ -1,16 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-container>
|
||||
<group-member />
|
||||
</ds-container>
|
||||
<base-card>
|
||||
<ds-heading tag="h3">{{ $t('group.members') }}</ds-heading>
|
||||
<ds-space margin="large" />
|
||||
<group-member :groupId="group.id" :groupMembers="groupMembers" />
|
||||
</base-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GroupMember from '~/components/Group/GroupMember'
|
||||
import { groupMembersQuery } from '~/graphql/groups.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GroupMember,
|
||||
},
|
||||
props: {
|
||||
group: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
GroupMembers: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
groupMembers() {
|
||||
return this.GroupMembers ? this.GroupMembers : []
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
GroupMembers: {
|
||||
query() {
|
||||
return groupMembersQuery()
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
id: this.group.id,
|
||||
}
|
||||
},
|
||||
error(error) {
|
||||
this.GroupMembers = []
|
||||
this.$toast.error(error.message)
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,45 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>my groups</div>
|
||||
<group-teaser />
|
||||
<br />
|
||||
<br />
|
||||
<group-card :items="responseGroupListQuery" />
|
||||
<ds-space margin="small">
|
||||
<ds-heading tag="h1">{{ $t('group.myGroups') }}</ds-heading>
|
||||
</ds-space>
|
||||
<ds-space margin="large" />
|
||||
<ds-container>
|
||||
<!-- create group -->
|
||||
<ds-space centered>
|
||||
<nuxt-link :to="{ name: 'group-create' }">
|
||||
<base-button
|
||||
class="group-add-button"
|
||||
icon="plus"
|
||||
size="large"
|
||||
circle
|
||||
filled
|
||||
v-tooltip="{
|
||||
content: $t('group.createNewGroup.tooltip'),
|
||||
placement: 'left',
|
||||
}"
|
||||
/>
|
||||
</nuxt-link>
|
||||
</ds-space>
|
||||
<!-- group list -->
|
||||
<group-list :groups="myGroups" />
|
||||
</ds-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GroupTeaser from '~/components/Group/GroupTeaser.vue'
|
||||
import GroupCard from '~/components/Group/GroupCard.vue'
|
||||
import GroupList from '~/components/Group/GroupList'
|
||||
import { groupQuery } from '~/graphql/groups.js'
|
||||
|
||||
export default {
|
||||
name: 'MyGroups',
|
||||
components: {
|
||||
GroupTeaser,
|
||||
GroupCard,
|
||||
GroupList,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
responseGroupListQuery: [],
|
||||
Group: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async groupListQuery() {
|
||||
try {
|
||||
const response = await this.$apollo.query({
|
||||
query: groupQuery(this.$i18n),
|
||||
})
|
||||
this.responseGroupListQuery = response.data.Group
|
||||
} catch (error) {
|
||||
this.responseGroupListQuery = []
|
||||
this.$toast.error(error.message)
|
||||
} finally {
|
||||
this.pending = false
|
||||
}
|
||||
computed: {
|
||||
myGroups() {
|
||||
return this.Group ? this.Group : []
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.groupListQuery()
|
||||
apollo: {
|
||||
Group: {
|
||||
query() {
|
||||
return groupQuery(this.$i18n)
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
isMember: true,
|
||||
}
|
||||
},
|
||||
error(error) {
|
||||
this.Group = []
|
||||
this.$toast.error(error.message)
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.group-add-button {
|
||||
box-shadow: $box-shadow-x-large;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
{{ userName }}
|
||||
</ds-heading>
|
||||
<ds-text align="center" color="soft">
|
||||
<base-icon name="at" />
|
||||
{{ userSlug }}
|
||||
</ds-text>
|
||||
<ds-text v-if="user.location" align="center" color="soft" size="small">
|
||||
@ -253,7 +254,7 @@ export default {
|
||||
},
|
||||
userSlug() {
|
||||
const { slug } = this.user || {}
|
||||
return slug && `@${slug}`
|
||||
return slug
|
||||
},
|
||||
tabOptions() {
|
||||
return [
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-heading tag="h1">{{ $t('settings.name') }}</ds-heading>
|
||||
<ds-space margin="small">
|
||||
<ds-heading tag="h1">{{ $t('settings.name') }}</ds-heading>
|
||||
</ds-space>
|
||||
<ds-space margin="large" />
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item :width="{ base: '100%', md: '200px' }">
|
||||
<ds-menu :routes="routes" :is-exact="() => true" />
|
||||
@ -39,10 +42,6 @@ export default {
|
||||
name: this.$t('settings.social-media.name'),
|
||||
path: `/settings/my-social-media`,
|
||||
},
|
||||
{
|
||||
name: this.$t('settings.myGroups'),
|
||||
path: `/my-groups`,
|
||||
},
|
||||
{
|
||||
name: this.$t('settings.muted-users.name'),
|
||||
path: `/settings/muted-users`,
|
||||
|
||||
@ -3,7 +3,7 @@ import VTooltip from 'v-tooltip'
|
||||
|
||||
Vue.use(VTooltip, {
|
||||
defaultDelay: {
|
||||
show: 1500,
|
||||
show: 750,
|
||||
hide: 50,
|
||||
},
|
||||
defaultOffset: 2,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user