1
0

Move list edit/namespace to separate pages and in a menu (#397)

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/397
Co-authored-by: konrad <konrad@kola-entertainments.de>
Co-committed-by: konrad <konrad@kola-entertainments.de>
This commit is contained in:
konrad
2021-01-30 16:17:04 +00:00
parent 649714e8a9
commit e0be77d88f
54 changed files with 1773 additions and 974 deletions

View File

@ -1,159 +0,0 @@
<template>
<div :class="{ 'is-loading': filterService.loading}" class="loader-container edit-list is-max-width-desktop">
<card title="Edit Saved Filter">
<form @submit.prevent="save()">
<div class="field">
<label class="label" for="listtext">Filter Name</label>
<div class="control">
<input
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading"
@keyup.enter="save"
class="input"
id="listtext"
placeholder="The list title goes here..."
type="text"
v-focus
v-model="filter.title"/>
</div>
</div>
<div class="field">
<label class="label" for="listdescription">Description</label>
<div class="control">
<editor
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading"
:preview-is-default="false"
id="listdescription"
placeholder="The lists description goes here..."
v-model="filter.description"
/>
</div>
</div>
<div class="field">
<label class="label" for="filters">Filters</label>
<div class="control">
<filters
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading"
class="has-no-shadow has-no-border"
v-model="filters"
/>
</div>
</div>
</form>
<div class="field has-addons mt-4">
<div class="control is-fullwidth">
<x-button
@click="save()"
:loading="filterService.loading"
class="is-fullwidth">
Save
</x-button>
</div>
<div class="control">
<x-button
@click="showDeleteModal = true"
:loading="filterService.loading"
class="is-danger"
icon="trash-alt"
/>
</div>
</div>
</card>
<modal
@close="showDeleteModal = false"
@submit="() => deleteSavedFilter()"
v-if="showDeleteModal">
<span slot="header">Delete this saved filter</span>
<p slot="text">
Are you sure you want to delete this saved filter?
</p>
</modal>
</div>
</template>
<script>
import ErrorComponent from '../../components/misc/error'
import LoadingComponent from '../../components/misc/loading'
import SavedFilterModel from '@/models/savedFilter'
import SavedFilterService from '@/services/savedFilter'
import ListModel from '@/models/list'
import Filters from '@/components/list/partials/filters'
import {objectToSnakeCase} from '@/helpers/case'
export default {
name: 'EditFilter',
data() {
return {
filter: SavedFilterModel,
filterService: SavedFilterService,
filters: {
sort_by: ['done', 'id'],
order_by: ['asc', 'desc'],
filter_by: ['done'],
filter_value: ['false'],
filter_comparator: ['equals'],
filter_concat: 'and',
filter_include_nulls: true,
},
showDeleteModal: false,
}
},
components: {
Filters,
editor: () => ({
component: import(/* webpackChunkName: "editor" */ '../../components/input/editor'),
loading: LoadingComponent,
error: ErrorComponent,
timeout: 60000,
}),
},
created() {
this.filterService = new SavedFilterService()
this.loadSavedFilter()
},
watch: {
// call again the method if the route changes
'$route': 'loadSavedFilter',
},
methods: {
loadSavedFilter() {
// We assume the listId in the route is the pseudolist
const list = new ListModel({id: this.$route.params.id})
this.filter = new SavedFilterModel({id: list.getSavedFilterId()})
this.filterService.get(this.filter)
.then(r => {
this.filter = r
this.filters = objectToSnakeCase(this.filter.filters)
})
.catch(e => this.error(e, this))
},
save() {
this.filter.filters = this.filters
this.filterService.update(this.filter)
.then(r => {
this.$store.dispatch('namespaces/loadNamespaces')
this.success({message: 'The filter was saved successfully.'}, this)
this.filter = r
this.filters = objectToSnakeCase(this.filter.filters)
})
.catch(e => this.error(e, this))
},
deleteSavedFilter() {
this.filterService.delete(this.filter)
.then(() => {
this.$store.dispatch('namespaces/loadNamespaces')
this.success({message: 'The filter was deleted successfully.'}, this)
this.$router.push({name: 'namespaces.index'})
})
.catch(e => this.error(e, this))
},
},
}
</script>

View File

@ -0,0 +1,44 @@
<template>
<modal
@close="$router.back()"
@submit="deleteSavedFilter()"
>
<span slot="header">Delete this saved filter</span>
<p slot="text">
Are you sure you want to delete this saved filter?
</p>
</modal>
</template>
<script>
import SavedFilterModel from '@/models/savedFilter'
import SavedFilterService from '@/services/savedFilter'
import ListModel from '@/models/list'
export default {
name: 'filter-settings-delete',
data() {
return {
filterService: SavedFilterService,
}
},
created() {
this.filterService = new SavedFilterService()
},
methods: {
deleteSavedFilter() {
// We assume the listId in the route is the pseudolist
const list = new ListModel({id: this.$route.params.listId})
const filter = new SavedFilterModel({id: list.getSavedFilterId()})
this.filterService.delete(filter)
.then(() => {
this.$store.dispatch('namespaces/loadNamespaces')
this.success({message: 'The filter was deleted successfully.'}, this)
this.$router.push({name: 'namespaces.index'})
})
.catch(e => this.error(e, this))
},
},
}
</script>

View File

@ -0,0 +1,128 @@
<template>
<create-edit
title="Edit This Saved Filter"
primary-icon=""
primary-label="Save"
@primary="save"
tertary="Delete"
@tertary="$router.push({ name: 'filter.list.settings.delete', params: { id: $route.params.listId } })"
>
<form @submit.prevent="save()">
<div class="field">
<label class="label" for="title">Filter Title</label>
<div class="control">
<input
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading"
@keyup.enter="save"
class="input"
id="title"
placeholder="The title goes here..."
type="text"
v-focus
v-model="filter.title"/>
</div>
</div>
<div class="field">
<label class="label" for="description">Description</label>
<div class="control">
<editor
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading"
:preview-is-default="false"
id="description"
placeholder="The description goes here..."
v-model="filter.description"
/>
</div>
</div>
<div class="field">
<label class="label" for="filters">Filters</label>
<div class="control">
<filters
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading"
class="has-no-shadow has-no-border"
v-model="filters"
/>
</div>
</div>
</form>
</create-edit>
</template>
<script>
import ErrorComponent from '@/components/misc/error'
import LoadingComponent from '@/components/misc/loading'
import CreateEdit from '@/components/misc/create-edit'
import SavedFilterModel from '@/models/savedFilter'
import SavedFilterService from '@/services/savedFilter'
import ListModel from '@/models/list'
import Filters from '@/components/list/partials/filters'
import {objectToSnakeCase} from '@/helpers/case'
export default {
name: 'filter-settings-edit',
data() {
return {
filter: SavedFilterModel,
filterService: SavedFilterService,
filters: {
sort_by: ['done', 'id'],
order_by: ['asc', 'desc'],
filter_by: ['done'],
filter_value: ['false'],
filter_comparator: ['equals'],
filter_concat: 'and',
filter_include_nulls: true,
},
showDeleteModal: false,
}
},
components: {
CreateEdit,
Filters,
editor: () => ({
component: import(/* webpackChunkName: "editor" */ '@/components/input/editor'),
loading: LoadingComponent,
error: ErrorComponent,
timeout: 60000,
}),
},
created() {
this.filterService = new SavedFilterService()
this.loadSavedFilter()
},
watch: {
// call again the method if the route changes
'$route': 'loadSavedFilter',
},
methods: {
loadSavedFilter() {
// We assume the listId in the route is the pseudolist
const list = new ListModel({id: this.$route.params.listId})
this.filter = new SavedFilterModel({id: list.getSavedFilterId()})
this.filterService.get(this.filter)
.then(r => {
this.filter = r
this.filters = objectToSnakeCase(this.filter.filters)
})
.catch(e => this.error(e, this))
},
save() {
this.filter.filters = this.filters
this.filterService.update(this.filter)
.then(r => {
this.$store.dispatch('namespaces/loadNamespaces')
this.success({message: 'The filter was saved successfully.'}, this)
this.filter = r
this.filters = objectToSnakeCase(this.filter.filters)
})
.catch(e => this.error(e, this))
},
},
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<create
<create-edit
title="Create a new label"
@create="newLabel()"
:create-disabled="label.title === ''"
@ -31,7 +31,7 @@
<color-picker v-model="label.hexColor" />
</div>
</div>
</create>
</create-edit>
</template>
<script>
@ -39,7 +39,7 @@ import labelModel from '../../models/label'
import labelService from '../../services/label'
import LabelModel from '../../models/label'
import LabelService from '../../services/label'
import Create from '@/components/misc/create'
import CreateEdit from '@/components/misc/create-edit'
import ColorPicker from '../../components/input/colorPicker'
export default {
@ -52,7 +52,7 @@ export default {
}
},
components: {
Create,
CreateEdit,
ColorPicker,
},
created() {

View File

@ -1,266 +0,0 @@
<template>
<div :class="{ 'is-loading': listService.loading}" class="loader-container edit-list is-max-width-desktop">
<div class="notification is-warning" v-if="list.isArchived">
This list is archived.
It is not possible to create new or edit tasks or it.
</div>
<card title="Edit List">
<form @submit.prevent="submit()">
<div class="field">
<label class="label" for="listtext">List Name</label>
<div class="control">
<input
:class="{ 'disabled': listService.loading}"
:disabled="listService.loading"
@keyup.enter="submit"
class="input"
id="listtext"
placeholder="The list title goes here..."
type="text"
v-focus
v-model="list.title"/>
</div>
</div>
<div class="field">
<label
class="label"
for="listtext"
v-tooltip="'The list identifier can be used to uniquely identify a task across lists. You can set it to empty to disable it.'">
List Identifier
</label>
<div class="control">
<input
:class="{ 'disabled': listService.loading}"
:disabled="listService.loading"
@keyup.enter="submit"
class="input"
id="listtext"
placeholder="The list identifier goes here..."
type="text"
v-focus
v-model="list.identifier"/>
</div>
</div>
<div class="field">
<label class="label" for="listdescription">Description</label>
<div class="control">
<editor
:class="{ 'disabled': listService.loading}"
:disabled="listService.loading"
:preview-is-default="false"
id="listdescription"
placeholder="The lists description goes here..."
v-model="list.description"
/>
</div>
</div>
<div class="field">
<label class="label" for="isArchivedCheck">Is Archived</label>
<div class="control">
<fancycheckbox
v-model="list.isArchived"
v-tooltip="'If a list is archived, you cannot create new tasks or edit the list or existing tasks.'">
This list is archived
</fancycheckbox>
</div>
</div>
<div class="field">
<label class="label">Color</label>
<div class="control">
<color-picker v-model="list.hexColor"/>
</div>
</div>
</form>
<div class="field has-addons mt-4">
<div class="control is-fullwidth">
<x-button
@click="submit()"
:loading="listService.loading"
class="is-fullwidth">
Save
</x-button>
</div>
<div class="control">
<x-button
@click="showDeleteModal = true"
:locading="listService.loading"
icon="trash-alt"
class="is-danger"
/>
</div>
</div>
</card>
<!-- Duplicate list -->
<card class="has-overflow" title="Duplicate this list">
<p>Select a namespace which should hold the duplicated list:</p>
<div class="field has-addons">
<div class="control is-expanded">
<namespace-search @selected="selectNamespace"/>
</div>
<div class="control">
<x-button
:loading="listDuplicateService.loading"
@click="duplicateList"
>
Duplicate
</x-button>
</div>
</div>
</card>
<background :list-id="$route.params.id"/>
<component
:id="list.id"
:is="manageUsersComponent"
:userIsAdmin="userIsAdmin"
shareType="user"
type="list"/>
<component
:id="list.id"
:is="manageTeamsComponent"
:userIsAdmin="userIsAdmin"
shareType="team"
type="list"/>
<link-sharing :list-id="$route.params.id" v-if="linkSharingEnabled"/>
<transition name="modal">
<modal
@close="showDeleteModal = false"
@submit="deleteList()"
v-if="showDeleteModal">
<span slot="header">Delete the list</span>
<p slot="text">Are you sure you want to delete this list and all of its contents?
<br/>This includes all tasks and <b>CANNOT BE UNDONE!</b></p>
</modal>
</transition>
</div>
</template>
<script>
import router from '../../router'
import manageSharing from '../../components/sharing/userTeam'
import LinkSharing from '../../components/sharing/linkSharing'
import ListModel from '../../models/list'
import ListService from '../../services/list'
import Fancycheckbox from '../../components/input/fancycheckbox'
import Background from '../../components/list/partials/background-settings'
import {CURRENT_LIST} from '@/store/mutation-types'
import ColorPicker from '../../components/input/colorPicker'
import NamespaceSearch from '../../components/namespace/namespace-search'
import ListDuplicateService from '../../services/listDuplicateService'
import ListDuplicateModel from '../../models/listDuplicateModel'
import LoadingComponent from '../../components/misc/loading'
import ErrorComponent from '../../components/misc/error'
export default {
name: 'EditList',
data() {
return {
list: ListModel,
listService: ListService,
showDeleteModal: false,
manageUsersComponent: '',
manageTeamsComponent: '',
listDuplicateService: ListDuplicateService,
selectedNamespace: null,
}
},
components: {
NamespaceSearch,
ColorPicker,
Background,
Fancycheckbox,
LinkSharing,
manageSharing,
editor: () => ({
component: import(/* webpackChunkName: "editor" */ '../../components/input/editor'),
loading: LoadingComponent,
error: ErrorComponent,
timeout: 60000,
}),
},
created() {
this.listService = new ListService()
this.listDuplicateService = new ListDuplicateService()
this.loadList()
},
watch: {
// call again the method if the route changes
'$route': 'loadList',
},
computed: {
linkSharingEnabled() {
return this.$store.state.config.linkSharingEnabled
},
userIsAdmin() {
return this.list.owner && this.list.owner.id === this.$store.state.auth.info.id
},
},
methods: {
loadList() {
let list = new ListModel({id: this.$route.params.id})
this.listService.get(list)
.then(r => {
this.$set(this, 'list', r)
this.$store.commit(CURRENT_LIST, r)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'manageSharing'
this.manageUsersComponent = 'manageSharing'
this.setTitle(`Edit ${this.list.title}`)
})
.catch(e => {
this.error(e, this)
})
},
submit() {
this.listService.update(this.list)
.then(r => {
this.$store.commit('namespaces/setListInNamespaceById', r)
this.success({message: 'The list was successfully updated.'}, this)
})
.catch(e => {
this.error(e, this)
})
},
deleteList() {
this.listService.delete(this.list)
.then(() => {
this.$store.commit('namespaces/removeListFromNamespaceById', this.list)
this.success({message: 'The list was successfully deleted.'}, this)
router.push({name: 'home'})
})
.catch(e => {
this.error(e, this)
})
},
selectNamespace(namespace) {
this.selectedNamespace = namespace
},
duplicateList() {
const listDuplicate = new ListDuplicateModel({
listId: this.list.id,
namespaceId: this.selectedNamespace.id,
})
this.listDuplicateService.create(listDuplicate)
.then(r => {
this.$store.commit('namespaces/addListToNamespace', r.list)
this.$store.commit('lists/addList', r.list)
this.success({message: 'The list was successfully duplicated.'}, this)
router.push({name: 'list.index', params: {listId: r.list.id}})
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -1,25 +0,0 @@
<template>
<div>
<edit-filter v-if="isSavedFilter"/>
<edit-list v-else/>
</div>
</template>
<script>
import EditList from '@/views/list/EditList'
import EditFilter from '@/views/filters/EditSavedFilter'
import {mapState} from 'vuex'
import {getSavedFilterIdFromListId} from '@/helpers/savedFilter'
export default {
name: 'EditListView',
components: {
EditFilter,
EditList,
},
computed: mapState({
isSavedFilter: state => getSavedFilterIdFromListId(state.currentList.id) > 0
})
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<create title="Create a new list" @create="newList()" :create-disabled="list.title === ''">
<create-edit title="Create a new list" @create="newList()" :create-disabled="list.title === ''">
<div class="field">
<label class="label" for="listTitle">List Title</label>
<div
@ -28,13 +28,13 @@
<color-picker v-model="list.hexColor" />
</div>
</div>
</create>
</create-edit>
</template>
<script>
import ListService from '../../services/list'
import ListModel from '../../models/list'
import Create from '@/components/misc/create'
import CreateEdit from '@/components/misc/create-edit'
import ColorPicker from '../../components/input/colorPicker'
export default {
@ -47,7 +47,7 @@ export default {
}
},
components: {
Create,
CreateEdit,
ColorPicker,
},
created() {

View File

@ -1,36 +1,38 @@
<template>
<div
:class="{ 'is-loading': listService.loading}"
:class="{ 'is-loading': listService.loading, 'is-archived': currentList.isArchived}"
class="loader-container"
>
<div class="switch-view-container">
<div class="switch-view">
<router-link
:class="{'is-active': $route.name === 'list.list'}"
:class="{'is-active': $route.name.includes('list.list')}"
:to="{ name: 'list.list', params: { listId: listId } }">
List
</router-link>
<router-link
:class="{'is-active': $route.name === 'list.gantt'}"
:class="{'is-active': $route.name.includes('list.gantt')}"
:to="{ name: 'list.gantt', params: { listId: listId } }">
Gantt
</router-link>
<router-link
:class="{'is-active': $route.name === 'list.table'}"
:class="{'is-active': $route.name.includes('list.table')}"
:to="{ name: 'list.table', params: { listId: listId } }">
Table
</router-link>
<router-link
:class="{'is-active': $route.name === 'list.kanban'}"
:class="{'is-active': $route.name.includes('list.kanban')}"
:to="{ name: 'list.kanban', params: { listId: listId } }">
Kanban
</router-link>
</div>
</div>
<div class="notification is-warning" v-if="list.isArchived">
This list is archived.
It is not possible to create new or edit tasks or it.
</div>
<transition name="fade">
<div class="notification is-warning" v-if="currentList.isArchived">
This list is archived.
It is not possible to create new or edit tasks or it.
</div>
</transition>
<router-view/>
</div>
@ -75,6 +77,7 @@ export default {
return typeof this.$store.state.currentList === 'undefined' ? {
id: 0,
title: '',
isArchived: false,
} : this.$store.state.currentList
},
},
@ -86,6 +89,10 @@ export default {
return
},
loadList() {
if(this.$route.name.includes('.settings.')) {
return
}
this.setTitle(this.currentList.title)
// This invalidates the loaded list at the kanban board which lets it reload its content when

View File

@ -0,0 +1,52 @@
<template>
<modal
@close="$router.back()"
@submit="archiveList()"
>
<span slot="header">{{ list.isArchived ? 'Un-' : '' }}Archive this list</span>
<p slot="text" v-if="list.isArchived">
You will be able to create new tasks or edit it.
</p>
<p slot="text" v-else>
You won't be able to edit this list or create new tasks until you un-archive it.
</p>
</modal>
</template>
<script>
import ListService from '@/services/list'
export default {
name: 'list-setting-archive',
data() {
return {
listService: ListService,
list: null,
}
},
created() {
this.listService = new ListService()
this.list = this.$store.getters['lists/getListById'](this.$route.params.listId)
this.setTitle(`Archive "${this.list.title}"`)
},
methods: {
archiveList() {
this.list.isArchived = !this.list.isArchived
this.listService.update(this.list)
.then(r => {
this.$store.commit('currentList', r)
this.$store.commit('namespaces/setListInNamespaceById', r)
this.success({message: 'The list was successfully archived.'}, this)
})
.catch(e => {
this.error(e, this)
})
.finally(() => {
this.$router.back()
})
},
},
}
</script>

View File

@ -0,0 +1,166 @@
<template>
<create-edit
title="Set list background"
primary-label=""
:loading="backgroundService.loading"
class="list-background-setting"
:wide="true"
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
>
<div class="mb-4" v-if="uploadBackgroundEnabled">
<input
@change="uploadBackground"
accept="image/*"
class="is-hidden"
ref="backgroundUploadInput"
type="file"
/>
<x-button
:loading="backgroundUploadService.loading"
@click="$refs.backgroundUploadInput.click()"
type="primary"
>
Choose a background from your pc
</x-button>
</div>
<template v-if="unsplashBackgroundEnabled">
<input
:class="{'is-loading': backgroundService.loading}"
@keyup="() => newBackgroundSearch()"
class="input is-expanded"
placeholder="Search for a background..."
type="text"
v-model="backgroundSearchTerm"
/>
<p class="unsplash-link"><a href="https://unsplash.com" target="_blank">Powered by Unsplash</a></p>
<div class="image-search-result">
<a
:key="im.id"
:style="{'background-image': `url(${backgroundThumbs[im.id]})`}"
@click="() => setBackground(im.id)"
class="image"
v-for="im in backgroundSearchResult">
<a :href="`https://unsplash.com/@${im.info.author}`" target="_blank" class="info">
{{ im.info.authorName }}
</a>
</a>
</div>
<x-button
:disabled="backgroundService.loading"
@click="() => searchBackgrounds(currentPage + 1)"
class="is-load-more-button mt-4"
:shadow="false"
type="secondary"
v-if="backgroundSearchResult.length > 0"
>
{{ backgroundService.loading ? 'Loading...' : 'Load more photos'}}
</x-button>
</template>
</create-edit>
</template>
<script>
import BackgroundUnsplashService from '../../../services/backgroundUnsplash'
import BackgroundUploadService from '../../../services/backgroundUpload'
import {CURRENT_LIST} from '@/store/mutation-types'
import CreateEdit from '@/components/misc/create-edit'
export default {
name: 'list-setting-background',
components: {CreateEdit},
data() {
return {
backgroundSearchTerm: '',
backgroundSearchResult: [],
backgroundService: null,
backgroundThumbs: {},
currentPage: 1,
backgroundSearchTimeout: null,
backgroundUploadService: null,
}
},
computed: {
unsplashBackgroundEnabled() {
return this.$store.state.config.enabledBackgroundProviders.includes('unsplash')
},
uploadBackgroundEnabled() {
return this.$store.state.config.enabledBackgroundProviders.includes('upload')
},
},
created() {
this.backgroundService = new BackgroundUnsplashService()
this.backgroundUploadService = new BackgroundUploadService()
this.setTitle('Set a list background')
// Show the default collection of backgrounds
this.newBackgroundSearch()
},
methods: {
newBackgroundSearch() {
if (!this.unsplashBackgroundEnabled) {
return
}
// This is an extra method to reset a few things when searching to not break loading more photos.
this.$set(this, 'backgroundSearchResult', [])
this.$set(this, 'backgroundThumbs', {})
this.searchBackgrounds()
},
searchBackgrounds(page = 1) {
if (this.backgroundSearchTimeout !== null) {
clearTimeout(this.backgroundSearchTimeout)
}
// We're using the timeout to not search on every keypress but with a 300ms delay.
// If another key is pressed within these 300ms, the last search request is dropped and a new one is scheduled.
this.backgroundSearchTimeout = setTimeout(() => {
this.currentPage = page
this.backgroundService.getAll({}, {s: this.backgroundSearchTerm, p: page})
.then(r => {
this.backgroundSearchResult = this.backgroundSearchResult.concat(r)
r.forEach(b => {
this.backgroundService.thumb(b)
.then(t => {
this.$set(this.backgroundThumbs, b.id, t)
})
})
})
.catch(e => {
this.error(e, this)
})
}, 300)
},
setBackground(backgroundId) {
// Don't set a background if we're in the process of setting one
if (this.backgroundService.loading) {
return
}
this.backgroundService.update({id: backgroundId, listId: this.$route.params.listId})
.then(l => {
this.$store.commit(CURRENT_LIST, l)
this.$store.commit('namespaces/setListInNamespaceById', l)
this.success({message: 'The background has been set successfully!'}, this)
})
.catch(e => {
this.error(e, this)
})
},
uploadBackground() {
if (this.$refs.backgroundUploadInput.files.length === 0) {
return
}
this.backgroundUploadService.create(this.$route.params.listId, this.$refs.backgroundUploadInput.files[0])
.then(l => {
this.$store.commit(CURRENT_LIST, l)
this.$store.commit('namespaces/setListInNamespaceById', l)
this.success({message: 'The background has been set successfully!'}, this)
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -0,0 +1,43 @@
<template>
<modal
@close="$router.back()"
@submit="deleteList()"
>
<span slot="header">Delete this list</span>
<p slot="text">Are you sure you want to delete this list and all of its contents?
<br/>This includes all tasks and <b>CANNOT BE UNDONE!</b></p>
</modal>
</template>
<script>
import ListService from '@/services/list'
export default {
name: 'list-setting-delete',
data() {
return {
listService: ListService,
}
},
created() {
this.listService = new ListService()
const list = this.$store.getters['lists/getListById'](this.$route.params.listId)
this.setTitle(`Delete "${list.title}"`)
},
methods: {
deleteList() {
const list = this.$store.getters['lists/getListById'](this.$route.params.listId)
this.listService.delete(list)
.then(() => {
this.$store.commit('namespaces/removeListFromNamespaceById', list)
this.success({message: 'The list was successfully deleted.'}, this)
this.$router.push({name: 'home'})
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -0,0 +1,58 @@
<template>
<create-edit
title="Duplicate this list"
primary-icon="paste"
primary-label="Duplicate"
@primary="duplicateList"
:loading="listDuplicateService.loading"
>
<p>Select a namespace which should hold the duplicated list:</p>
<namespace-search @selected="selectNamespace"/>
</create-edit>
</template>
<script>
import ListDuplicateService from '@/services/listDuplicateService'
import NamespaceSearch from '@/components/namespace/namespace-search'
import ListDuplicateModel from '@/models/listDuplicateModel'
import CreateEdit from '@/components/misc/create-edit'
export default {
name: 'list-setting-duplicate',
data() {
return {
listDuplicateService: ListDuplicateService,
selectedNamespace: null,
}
},
components: {
CreateEdit,
NamespaceSearch,
},
created() {
this.listDuplicateService = new ListDuplicateService()
this.setTitle('Duplicate List')
},
methods: {
selectNamespace(namespace) {
this.selectedNamespace = namespace
},
duplicateList() {
const listDuplicate = new ListDuplicateModel({
listId: this.$route.params.listId,
namespaceId: this.selectedNamespace.id,
})
this.listDuplicateService.create(listDuplicate)
.then(r => {
this.$store.commit('namespaces/addListToNamespace', r.list)
this.$store.commit('lists/addList', r.list)
this.success({message: 'The list was successfully duplicated.'}, this)
this.$router.push({name: 'list.index', params: {listId: r.list.id}})
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -0,0 +1,127 @@
<template>
<create-edit
title="Edit This List"
primary-icon=""
primary-label="Save"
@primary="save"
tertary="Delete"
@tertary="$router.push({ name: 'list.list.settings.delete', params: { id: $route.params.listId } })"
>
<div class="field">
<label class="label" for="listtext">List Name</label>
<div class="control">
<input
:class="{ 'disabled': listService.loading}"
:disabled="listService.loading"
@keyup.enter="save"
class="input"
id="listtext"
placeholder="The list title goes here..."
type="text"
v-focus
v-model="list.title"/>
</div>
</div>
<div class="field">
<label
class="label"
for="listtext"
v-tooltip="'The list identifier can be used to uniquely identify a task across lists. You can set it to empty to disable it.'">
List Identifier
</label>
<div class="control">
<input
:class="{ 'disabled': listService.loading}"
:disabled="listService.loading"
@keyup.enter="save"
class="input"
id="listtext"
placeholder="The list identifier goes here..."
type="text"
v-focus
v-model="list.identifier"/>
</div>
</div>
<div class="field">
<label class="label" for="listdescription">Description</label>
<div class="control">
<editor
:class="{ 'disabled': listService.loading}"
:disabled="listService.loading"
:preview-is-default="false"
id="listdescription"
placeholder="The lists description goes here..."
v-model="list.description"
/>
</div>
</div>
<div class="field">
<label class="label">Color</label>
<div class="control">
<color-picker v-model="list.hexColor"/>
</div>
</div>
</create-edit>
</template>
<script>
import ListModel from '@/models/list'
import ListService from '@/services/list'
import ColorPicker from '@/components/input/colorPicker'
import LoadingComponent from '@/components/misc/loading'
import ErrorComponent from '@/components/misc/error'
import ListDuplicateService from '@/services/listDuplicateService'
import {CURRENT_LIST} from '@/store/mutation-types'
import CreateEdit from '@/components/misc/create-edit'
export default {
name: 'list-setting-edit',
data() {
return {
list: ListModel,
listService: ListService,
}
},
components: {
CreateEdit,
ColorPicker,
editor: () => ({
component: import(/* webpackChunkName: "editor" */ '@/components/input/editor'),
loading: LoadingComponent,
error: ErrorComponent,
timeout: 60000,
}),
},
created() {
this.listService = new ListService()
this.listDuplicateService = new ListDuplicateService()
this.loadList()
},
methods: {
loadList() {
const list = new ListModel({id: this.$route.params.listId})
this.listService.get(list)
.then(r => {
this.$set(this, 'list', r)
this.$store.commit(CURRENT_LIST, r)
this.setTitle(`Edit "${this.list.title}"`)
})
.catch(e => {
this.error(e, this)
})
},
save() {
this.listService.update(this.list)
.then(r => {
this.$store.commit('namespaces/setListInNamespaceById', r)
this.success({message: 'The list was successfully updated.'}, this)
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -0,0 +1,78 @@
<template>
<create-edit
title="Share this list"
primary-label=""
>
<component
:id="list.id"
:is="manageUsersComponent"
:userIsAdmin="userIsAdmin"
shareType="user"
type="list"/>
<component
:id="list.id"
:is="manageTeamsComponent"
:userIsAdmin="userIsAdmin"
shareType="team"
type="list"/>
<link-sharing :list-id="$route.params.listId" v-if="linkSharingEnabled" class="mt-4"/>
</create-edit>
</template>
<script>
import ListService from '@/services/list'
import ListModel from '@/models/list'
import {CURRENT_LIST} from '@/store/mutation-types'
import CreateEdit from '@/components/misc/create-edit'
import LinkSharing from '@/components/sharing/linkSharing'
import userTeam from '@/components/sharing/userTeam'
export default {
name: 'list-setting-share',
data() {
return {
list: ListModel,
listService: ListService,
manageUsersComponent: '',
manageTeamsComponent: '',
}
},
components: {
CreateEdit,
LinkSharing,
userTeam,
},
computed: {
linkSharingEnabled() {
return this.$store.state.config.linkSharingEnabled
},
userIsAdmin() {
return this.list.owner && this.list.owner.id === this.$store.state.auth.info.id
},
},
created() {
this.listService = new ListService()
this.loadList()
},
methods: {
loadList() {
const list = new ListModel({id: this.$route.params.listId})
this.listService.get(list)
.then(r => {
this.$set(this, 'list', r)
this.$store.commit(CURRENT_LIST, r)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'userTeam'
this.manageUsersComponent = 'userTeam'
this.setTitle(`Share "${this.list.title}"`)
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -32,57 +32,47 @@
v-if="bucket.limit > 0">
{{ bucket.tasks.length }}/{{ bucket.limit }}
</span>
<div
class="dropdown is-right is-active options"
<dropdown
class="is-right options"
v-if="canWrite"
trigger-icon="ellipsis-v"
>
<div @click.stop="toggleBucketDropdown(bucket.id)" class="dropdown-trigger">
<span class="icon">
<icon icon="ellipsis-v"/>
</span>
</div>
<transition name="fade">
<div class="dropdown-menu" role="menu" v-if="bucketOptionsDropDownActive[bucket.id]">
<div class="dropdown-content">
<a
@click.stop="showSetLimitInput = true"
class="dropdown-item"
>
<div class="field has-addons" v-if="showSetLimitInput">
<div class="control">
<input
@change="() => updateBucket(bucket)"
@keyup.enter="() => updateBucket(bucket)"
class="input"
type="number"
v-focus.always
v-model="bucket.limit"
/>
</div>
<div class="control">
<x-button
:icon="['far', 'save']"
:shadow="false"
/>
</div>
</div>
<template v-else>
Limit: {{ bucket.limit > 0 ? bucket.limit : 'Not set' }}
</template>
</a>
<a
:class="{'is-disabled': buckets.length <= 1}"
@click="() => deleteBucketModal(bucket.id)"
class="dropdown-item has-text-danger"
v-tooltip="buckets.length <= 1 ? 'You cannot remove the last bucket.' : ''"
>
<span class="icon is-small"><icon icon="trash-alt"/></span>
Delete
</a>
<a
@click.stop="showSetLimitInput = true"
class="dropdown-item"
>
<div class="field has-addons" v-if="showSetLimitInput">
<div class="control">
<input
@change="() => updateBucket(bucket)"
@keyup.enter="() => updateBucket(bucket)"
class="input"
type="number"
v-focus.always
v-model="bucket.limit"
/>
</div>
<div class="control">
<x-button
:icon="['far', 'save']"
:shadow="false"
/>
</div>
</div>
</transition>
</div>
<template v-else>
Limit: {{ bucket.limit > 0 ? bucket.limit : 'Not set' }}
</template>
</a>
<a
:class="{'is-disabled': buckets.length <= 1}"
@click="() => deleteBucketModal(bucket.id)"
class="dropdown-item has-text-danger"
v-tooltip="buckets.length <= 1 ? 'You cannot remove the last bucket.' : ''"
>
<span class="icon is-small"><icon icon="trash-alt"/></span>
Delete
</a>
</dropdown>
</div>
<div :ref="`tasks-container${bucket.id}`" class="tasks">
<!-- Make the component either a div or a draggable component based on the user rights -->
@ -272,12 +262,14 @@ import {applyDrag} from '@/helpers/applyDrag'
import {mapState} from 'vuex'
import {saveListView} from '@/helpers/saveListView'
import Rights from '../../../models/rights.json'
import {LOADING, LOADING_MODULE} from '../../../store/mutation-types'
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
import FilterPopup from '@/components/list/partials/filter-popup'
import Dropdown from '@/components/misc/dropdown'
export default {
name: 'Kanban',
components: {
Dropdown,
FilterPopup,
Container,
Draggable,
@ -295,7 +287,6 @@ export default {
showOnTop: true,
},
sourceBucket: 0,
bucketOptionsDropDownActive: {},
showBucketDeleteModal: false,
bucketToDelete: 0,
@ -324,7 +315,6 @@ export default {
created() {
this.taskService = new TaskService()
this.loadBuckets()
this.$nextTick(() => document.addEventListener('click', this.closeBucketDropdowns))
// Save the current list view to local storage
// We use local storage and not vuex here to make it persistent across reloads.
@ -449,17 +439,6 @@ export default {
toggleShowNewTaskInput(bucket) {
this.$set(this.showNewTaskInput, bucket, !this.showNewTaskInput[bucket])
},
toggleBucketDropdown(bucketId) {
const oldState = this.bucketOptionsDropDownActive[bucketId]
this.closeBucketDropdowns() // Close all eventually open dropdowns
this.$set(this.bucketOptionsDropDownActive, bucketId, !oldState)
},
closeBucketDropdowns() {
this.showSetLimitInput = false
for (const bucketId in this.bucketOptionsDropDownActive) {
this.bucketOptionsDropDownActive[bucketId] = false
}
},
addTaskToBucket(bucketId) {
if (this.newTaskText === '') {

View File

@ -84,12 +84,10 @@
</p>
</div>
<p
class="has-text-centered has-text-grey is-italic p-4 mb-4"
v-if="ctaVisible && tasks.length === 0 && !taskCollectionService.loading">
<nothing v-if="ctaVisible && tasks.length === 0 && !taskCollectionService.loading">
This list is currently empty.
<a @click="$refs.newTaskInput.focus()">Create a new task.</a>
</p>
</nothing>
<div class="tasks-container">
<div :class="{'short': isTaskEdit}" class="tasks mt-0" v-if="tasks && tasks.length > 0">
@ -174,6 +172,7 @@ import Rights from '../../../models/rights.json'
import {mapState} from 'vuex'
import FilterPopup from '@/components/list/partials/filter-popup'
import {HAS_TASKS} from '@/store/mutation-types'
import Nothing from '@/components/misc/nothing'
export default {
name: 'List',
@ -195,6 +194,7 @@ export default {
taskList,
],
components: {
Nothing,
FilterPopup,
SingleTaskInList,
EditTask,

View File

@ -42,7 +42,7 @@
</div>
<card :padding="false" :has-content="false">
<table class="table is-hoverable is-fullwidth mb-0">
<table class="table has-actions is-hoverable is-fullwidth mb-0">
<thead>
<tr>
<th v-if="activeColumns.id">

View File

@ -1,201 +0,0 @@
<template>
<div class="loader-container is-max-width-desktop" v-bind:class="{ 'is-loading': namespaceService.loading}">
<div class="notification is-warning" v-if="namespace.isArchived">
This namespace is archived.
It is not possible to create new lists or edit it.
</div>
<card title="Edit Namespace">
<form @submit.prevent="submit()">
<div class="field">
<label class="label" for="namespacetext">Namespace Name</label>
<div class="control">
<input
:class="{ 'disabled': namespaceService.loading}"
:disabled="namespaceService.loading"
class="input"
id="namespacetext"
placeholder="The namespace text is here..."
type="text"
v-focus
v-model="namespace.title"/>
</div>
</div>
<div class="field">
<label class="label" for="namespacedescription">Description</label>
<div class="control">
<editor
:class="{ 'disabled': namespaceService.loading}"
:disabled="namespaceService.loading"
:preview-is-default="false"
id="namespacedescription"
placeholder="The namespaces description goes here..."
v-if="editorActive"
v-model="namespace.description"
/>
</div>
</div>
<div class="field">
<label class="label" for="isArchivedCheck">Is Archived</label>
<div class="control">
<fancycheckbox
v-model="namespace.isArchived"
v-tooltip="'If a namespace is archived, you cannot create new lists or edit it.'">
This namespace is archived
</fancycheckbox>
</div>
</div>
<div class="field">
<label class="label">Color</label>
<div class="control">
<color-picker v-model="namespace.hexColor"/>
</div>
</div>
</form>
<div class="field has-addons mt-4">
<div class="control is-fullwidth">
<x-button
@click="submit()"
:loading="namespaceService.loading"
class="is-fullwidth"
>
Save
</x-button>
</div>
<div class="control">
<x-button
@click="showDeleteModal = true"
:loading="namespaceService.loading"
class="is-danger"
icon="trash-alt"
/>
</div>
</div>
</card>
<component
:id="namespace.id"
:is="manageUsersComponent"
:userIsAdmin="userIsAdmin"
shareType="user"
type="namespace"/>
<component
:id="namespace.id"
:is="manageTeamsComponent"
:userIsAdmin="userIsAdmin"
shareType="team"
type="namespace"/>
<transition name="modal">
<modal
@close="showDeleteModal = false"
v-if="showDeleteModal"
@submit="deleteNamespace()">
<span slot="header">Delete the namespace</span>
<p slot="text">Are you sure you want to delete this namespace and all of its contents?
<br/>This includes lists & tasks and <b>CANNOT BE UNDONE!</b></p>
</modal>
</transition>
</div>
</template>
<script>
import router from '../../router'
import manageSharing from '../../components/sharing/userTeam'
import NamespaceService from '../../services/namespace'
import NamespaceModel from '../../models/namespace'
import Fancycheckbox from '../../components/input/fancycheckbox'
import ColorPicker from '../../components/input/colorPicker'
import LoadingComponent from '../../components/misc/loading'
import ErrorComponent from '../../components/misc/error'
export default {
name: 'EditNamespace',
data() {
return {
namespaceService: NamespaceService,
manageUsersComponent: '',
manageTeamsComponent: '',
namespace: NamespaceModel,
showDeleteModal: false,
editorActive: false,
}
},
components: {
ColorPicker,
Fancycheckbox,
manageSharing,
editor: () => ({
component: import(/* webpackChunkName: "editor" */ '../../components/input/editor'),
loading: LoadingComponent,
error: ErrorComponent,
timeout: 60000,
}),
},
beforeMount() {
this.namespace.id = this.$route.params.id
},
created() {
this.namespaceService = new NamespaceService()
this.namespace = new NamespaceModel()
this.loadNamespace()
},
watch: {
// call again the method if the route changes
'$route': 'loadNamespace',
},
computed: {
userIsAdmin() {
return this.namespace.owner && this.namespace.owner.id === this.$store.state.auth.info.id
},
},
methods: {
loadNamespace() {
// This makes the editor trigger its mounted function again which makes it forget every input
// it currently has in its textarea. This is a counter-hack to a hack inside of vue-easymde
// which made it impossible to detect change from the outside. Therefore the component would
// not update if new content from the outside was made available.
// See https://github.com/NikulinIlya/vue-easymde/issues/3
this.editorActive = false
this.$nextTick(() => this.editorActive = true)
let namespace = new NamespaceModel({id: this.$route.params.id})
this.namespaceService.get(namespace)
.then(r => {
this.$set(this, 'namespace', r)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'manageSharing'
this.manageUsersComponent = 'manageSharing'
this.setTitle(`Edit ${this.namespace.title}`)
})
.catch(e => {
this.error(e, this)
})
},
submit() {
this.namespaceService.update(this.namespace)
.then(r => {
// Update the namespace in the parent
this.$store.commit('namespaces/setNamespaceById', r)
this.success({message: 'The namespace was successfully updated.'}, this)
})
.catch(e => {
this.error(e, this)
})
},
deleteNamespace() {
this.namespaceService.delete(this.namespace)
.then(() => {
this.$store.commit('namespaces/removeNamespaceById', this.namespace.id)
this.success({message: 'The namespace was successfully deleted.'}, this)
router.push({name: 'home'})
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<create
<create-edit
title="Create a new namespace"
@create="newNamespace()"
:create-disabled="namespace.title === ''"
@ -39,13 +39,13 @@
>
What's a namespace?
</p>
</create>
</create-edit>
</template>
<script>
import NamespaceModel from '../../models/namespace'
import NamespaceService from '../../services/namespace'
import Create from '@/components/misc/create'
import CreateEdit from '@/components/misc/create-edit'
import ColorPicker from '../../components/input/colorPicker'
export default {
@ -59,7 +59,7 @@ export default {
},
components: {
ColorPicker,
Create,
CreateEdit,
},
created() {
this.namespace = new NamespaceModel()

View File

@ -0,0 +1,52 @@
<template>
<modal
@close="$router.back()"
@submit="archiveNamespace()"
>
<span slot="header">{{ namespace.isArchived ? 'Un-' : '' }}Archive this namespace</span>
<p slot="text" v-if="namespace.isArchived">
You will be able to create new lists or edit it.
</p>
<p slot="text" v-else>
You won't be able to edit this namespace or create new list until you un-archive it.<br/>
This will also archive all lists in this namespace.
</p>
</modal>
</template>
<script>
import NamespaceService from '@/services/namespace'
export default {
name: 'namespace-setting-archive',
data() {
return {
namespaceService: NamespaceService,
namespace: null,
}
},
created() {
this.namespaceService = new NamespaceService()
this.namespace = this.$store.getters['namespaces/getNamespaceById'](this.$route.params.id)
this.setTitle(`Archive "${this.namespace.title}"`)
},
methods: {
archiveNamespace() {
this.namespace.isArchived = !this.namespace.isArchived
this.namespaceService.update(this.namespace)
.then(r => {
this.$store.commit('namespaces/setNamespaceById', r)
this.success({message: 'The namespace was successfully archived.'}, this)
})
.catch(e => {
this.error(e, this)
})
.finally(() => {
this.$router.back()
})
},
},
}
</script>

View File

@ -0,0 +1,44 @@
<template>
<modal
@close="$router.back()"
@submit="deleteNamespace()"
>
<span slot="header">Delete this namespace</span>
<p slot="text">Are you sure you want to delete this namespace and all of its contents?
<br/>This includes all tasks and <b>CANNOT BE UNDONE!</b></p>
</modal>
</template>
<script>
import NamespaceService from '@/services/namespace'
export default {
name: 'namespace-setting-delete',
data() {
return {
namespaceService: NamespaceService,
}
},
created() {
this.namespaceService = new NamespaceService()
const namespace = this.$store.getters['namespaces/getNamespaceById'](this.$route.params.id)
this.setTitle(`Delete "${namespace.title}"`)
},
methods: {
deleteNamespace() {
const namespace = this.$store.getters['namespaces/getNamespaceById'](this.$route.params.id)
this.namespaceService.delete(namespace)
.then(() => {
this.$store.commit('namespaces/removeNamespaceFromNamespaceById', namespace)
this.success({message: 'The namespace was successfully deleted.'}, this)
this.$router.push({name: 'home'})
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -0,0 +1,137 @@
<template>
<create-edit
title="Edit This Namespace"
primary-icon=""
primary-label="Save"
@primary="save"
tertary="Delete"
@tertary="$router.push({ name: 'namespace.settings.delete', params: { id: $route.params.id } })"
>
<form @submit.prevent="save()">
<div class="field">
<label class="label" for="namespacetext">Namespace Name</label>
<div class="control">
<input
:class="{ 'disabled': namespaceService.loading}"
:disabled="namespaceService.loading"
class="input"
id="namespacetext"
placeholder="The namespace text is here..."
type="text"
v-focus
v-model="namespace.title"/>
</div>
</div>
<div class="field">
<label class="label" for="namespacedescription">Description</label>
<div class="control">
<editor
:class="{ 'disabled': namespaceService.loading}"
:disabled="namespaceService.loading"
:preview-is-default="false"
id="namespacedescription"
placeholder="The namespaces description goes here..."
v-if="editorActive"
v-model="namespace.description"
/>
</div>
</div>
<div class="field">
<label class="label" for="isArchivedCheck">Is Archived</label>
<div class="control">
<fancycheckbox
v-model="namespace.isArchived"
v-tooltip="'If a namespace is archived, you cannot create new lists or edit it.'">
This namespace is archived
</fancycheckbox>
</div>
</div>
<div class="field">
<label class="label">Color</label>
<div class="control">
<color-picker v-model="namespace.hexColor"/>
</div>
</div>
</form>
</create-edit>
</template>
<script>
import NamespaceService from '@/services/namespace'
import NamespaceModel from '@/models/namespace'
import Fancycheckbox from '@/components/input/fancycheckbox'
import ColorPicker from '@/components/input/colorPicker'
import LoadingComponent from '@/components/misc/loading'
import ErrorComponent from '@/components/misc/error'
import CreateEdit from '@/components/misc/create-edit'
export default {
name: 'namespace-setting-edit',
data() {
return {
namespaceService: NamespaceService,
namespace: NamespaceModel,
editorActive: false,
}
},
components: {
CreateEdit,
ColorPicker,
Fancycheckbox,
editor: () => ({
component: import(/* webpackChunkName: "editor" */ '@/components/input/editor'),
loading: LoadingComponent,
error: ErrorComponent,
timeout: 60000,
}),
},
beforeMount() {
this.namespace.id = this.$route.params.id
},
created() {
this.namespaceService = new NamespaceService()
this.namespace = new NamespaceModel()
this.loadNamespace()
},
watch: {
// call again the method if the route changes
'$route': 'loadNamespace',
},
methods: {
loadNamespace() {
// This makes the editor trigger its mounted function again which makes it forget every input
// it currently has in its textarea. This is a counter-hack to a hack inside of vue-easymde
// which made it impossible to detect change from the outside. Therefore the component would
// not update if new content from the outside was made available.
// See https://github.com/NikulinIlya/vue-easymde/issues/3
this.editorActive = false
this.$nextTick(() => this.editorActive = true)
const namespace = new NamespaceModel({id: this.$route.params.id})
this.namespaceService.get(namespace)
.then(r => {
this.$set(this, 'namespace', r)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'manageSharing'
this.manageUsersComponent = 'manageSharing'
this.setTitle(`Edit "${r.title}"`)
})
.catch(e => {
this.error(e, this)
})
},
save() {
this.namespaceService.update(this.namespace)
.then(r => {
// Update the namespace in the parent
this.$store.commit('namespaces/setNamespaceById', r)
this.success({message: 'The namespace was successfully updated.'}, this)
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -0,0 +1,77 @@
<template>
<create-edit
title="Share this Namespace"
primary-label=""
>
<component
:id="namespace.id"
:is="manageUsersComponent"
:userIsAdmin="userIsAdmin"
shareType="user"
type="namespace"/>
<component
:id="namespace.id"
:is="manageTeamsComponent"
:userIsAdmin="userIsAdmin"
shareType="team"
type="namespace"/>
</create-edit>
</template>
<script>
import manageSharing from '@/components/sharing/userTeam'
import CreateEdit from '@/components/misc/create-edit'
import NamespaceService from '@/services/namespace'
import NamespaceModel from '@/models/namespace'
export default {
name: 'namespace-setting-share',
data() {
return {
namespaceService: NamespaceService,
manageUsersComponent: '',
manageTeamsComponent: '',
namespace: NamespaceModel,
}
},
components: {
CreateEdit,
manageSharing,
},
beforeMount() {
this.namespace.id = this.$route.params.id
},
created() {
this.namespaceService = new NamespaceService()
this.namespace = new NamespaceModel()
this.loadNamespace()
},
watch: {
// call again the method if the route changes
'$route': 'loadNamespace',
},
computed: {
userIsAdmin() {
return this.namespace.owner && this.namespace.owner.id === this.$store.state.auth.info.id
},
},
methods: {
loadNamespace() {
const namespace = new NamespaceModel({id: this.$route.params.id})
this.namespaceService.get(namespace)
.then(r => {
this.$set(this, 'namespace', r)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'manageSharing'
this.manageUsersComponent = 'manageSharing'
this.setTitle(`Share "${this.namespace.title}"`)
})
.catch(e => {
this.error(e, this)
})
},
},
}
</script>

View File

@ -88,7 +88,7 @@
</div>
</div>
</div>
<table class="table is-striped is-hoverable is-fullwidth">
<table class="table has-actions is-striped is-hoverable is-fullwidth">
<tbody>
<tr :key="m.id" v-for="m in team.members">
<td>{{ m.getDisplayName() }}</td>

View File

@ -1,5 +1,5 @@
<template>
<create
<create-edit
title="Create a new team"
@create="newTeam()"
:create-disabled="team.name === ''"
@ -25,14 +25,14 @@
<p class="help is-danger" v-if="showError && team.name === ''">
Please specify a name.
</p>
</create>
</create-edit>
</template>
<script>
import router from '../../router'
import TeamModel from '../../models/team'
import TeamService from '../../services/team'
import Create from '@/components/misc/create'
import CreateEdit from '@/components/misc/create-edit'
export default {
name: 'NewTeam',
@ -44,7 +44,7 @@ export default {
}
},
components: {
Create,
CreateEdit,
},
created() {
this.teamService = new TeamService()