1
0

feat(teams): add public flags to teams to allow easier sharing with other teams (#2179)

Resolves #2173
Co-authored-by: Daniel Herrmann <daniel.herrmann1@gmail.com>
Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2179
Reviewed-by: konrad <k@knt.li>
Co-authored-by: waza-ari <daniel.herrmann@makerspace-darmstadt.de>
Co-committed-by: waza-ari <daniel.herrmann@makerspace-darmstadt.de>
This commit is contained in:
waza-ari
2024-03-10 14:04:32 +00:00
committed by konrad
parent d7fdefcead
commit ffa82556e0
22 changed files with 392 additions and 45 deletions

View File

@ -172,6 +172,7 @@ import Multiselect from '@/components/input/multiselect.vue'
import Nothing from '@/components/misc/nothing.vue'
import {success} from '@/message'
import {useAuthStore} from '@/stores/auth'
import {useConfigStore} from '@/stores/config'
// FIXME: I think this whole thing can now only manage user/team sharing for projects? Maybe remove a little generalization?
@ -210,8 +211,8 @@ const selectedRight = ref({})
const sharables = ref([])
const showDeleteModal = ref(false)
const authStore = useAuthStore()
const configStore = useConfigStore()
const userInfo = computed(() => authStore.info)
function createShareTypeNameComputed(count: number) {
@ -360,7 +361,15 @@ async function find(query: string) {
found.value = []
return
}
const results = await searchService.getAll({}, {s: query})
// Include public teams here if we are sharing with teams and its enabled in the config
let results = []
if (props.shareType === 'team' && configStore.publicTeamsEnabled) {
results = await searchService.getAll({}, {s: query, includePublic: true})
} else {
results = await searchService.getAll({}, {s: query})
}
found.value = results
.filter(m => {
if(props.shareType === 'user' && m.id === currentUserId.value) {

View File

@ -986,7 +986,9 @@
"description": "Description",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
"member": "Member",
"isPublic": "Public Team",
"isPublicDescription": "Make the team publicly discoverable. When enabled, anyone can share projects with this team even when not being a direct member."
}
},
"keyboardShortcuts": {

View File

@ -10,6 +10,7 @@ export interface ITeam extends IAbstract {
members: ITeamMember[]
right: Right
oidcId: string
isPublic: boolean
createdBy: IUser
created: Date

View File

@ -14,6 +14,7 @@ export default class TeamModel extends AbstractModel<ITeam> implements ITeam {
members: ITeamMember[] = []
right: Right = RIGHTS.READ
oidcId = ''
isPublic: boolean = false
createdBy: IUser = {} // FIXME: seems wrong
created: Date = null

View File

@ -37,6 +37,7 @@ export interface ConfigState {
providers: IProvider[],
},
},
publicTeamsEnabled: boolean,
}
export const useConfigStore = defineStore('config', () => {
@ -70,6 +71,7 @@ export const useConfigStore = defineStore('config', () => {
providers: [],
},
},
publicTeamsEnabled: false,
})
const migratorsEnabled = computed(() => state.availableMigrators?.length > 0)

View File

@ -33,6 +33,27 @@
>
{{ $t('team.attributes.nameRequired') }}
</p>
<div
v-if="configStore.publicTeamsEnabled"
class="field"
>
<label
class="label"
for="teamIsPublic"
>{{ $t('team.attributes.isPublic') }}</label>
<div
class="control is-expanded"
:class="{ 'is-loading': teamService.loading }"
>
<Fancycheckbox
v-model="team.isPublic"
:disabled="teamMemberService.loading || undefined"
:class="{ 'disabled': teamService.loading }"
>
{{ $t('team.attributes.isPublicDescription') }}
</Fancycheckbox>
</div>
</div>
<div class="field">
<label
class="label"
@ -242,6 +263,7 @@ import {useI18n} from 'vue-i18n'
import {useRoute, useRouter} from 'vue-router'
import Editor from '@/components/input/AsyncEditor'
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
import Multiselect from '@/components/input/multiselect.vue'
import User from '@/components/misc/user.vue'
@ -254,12 +276,14 @@ import {RIGHTS as Rights} from '@/constants/rights'
import {useTitle} from '@/composables/useTitle'
import {success} from '@/message'
import {useAuthStore} from '@/stores/auth'
import {useConfigStore} from '@/stores/config'
import type {ITeam} from '@/modelTypes/ITeam'
import type {IUser} from '@/modelTypes/IUser'
import type {ITeamMember} from '@/modelTypes/ITeamMember'
const authStore = useAuthStore()
const configStore = useConfigStore()
const route = useRoute()
const router = useRouter()
const {t} = useI18n({useScope: 'global'})

View File

@ -25,6 +25,26 @@
>
</div>
</div>
<div
v-if="configStore.publicTeamsEnabled"
class="field"
>
<label
class="label"
for="teamIsPublic"
>{{ $t('team.attributes.isPublic') }}</label>
<div
class="control is-expanded"
:class="{ 'is-loading': teamService.loading }"
>
<Fancycheckbox
v-model="team.isPublic"
:class="{ 'disabled': teamService.loading }"
>
{{ $t('team.attributes.isPublicDescription') }}
</Fancycheckbox>
</div>
</div>
<p
v-if="showError && team.name === ''"
class="help is-danger"
@ -46,11 +66,14 @@ import TeamModel from '@/models/team'
import TeamService from '@/services/team'
import CreateEdit from '@/components/misc/create-edit.vue'
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
import {useTitle} from '@/composables/useTitle'
import {useRouter} from 'vue-router'
import {success} from '@/message'
import {useConfigStore} from '@/stores/config'
const {t} = useI18n()
const title = computed(() => t('team.create.title'))
useTitle(title)
@ -60,6 +83,8 @@ const teamService = shallowReactive(new TeamService())
const team = reactive(new TeamModel())
const showError = ref(false)
const configStore = useConfigStore()
async function newTeam() {
if (team.name === '') {
showError.value = true