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:
parent
649714e8a9
commit
e0be77d88f
54 changed files with 1773 additions and 974 deletions
|
@ -21,7 +21,10 @@ describe('Lists', () => {
|
|||
|
||||
it('Should create a new list', () => {
|
||||
cy.visit('/')
|
||||
cy.get('.namespace-title a[href="/namespaces/1/list"]')
|
||||
cy.get('.namespace-title .dropdown-trigger')
|
||||
.click()
|
||||
cy.get('.namespace-title .dropdown .dropdown-item')
|
||||
.contains('New list')
|
||||
.click()
|
||||
cy.url()
|
||||
.should('contain', '/namespaces/1/list')
|
||||
|
@ -58,9 +61,8 @@ describe('Lists', () => {
|
|||
.should('contain', '/lists/1/list')
|
||||
cy.get('.list-title h1')
|
||||
.should('contain', 'First List')
|
||||
cy.get('.list-title a.icon')
|
||||
.should('have.attr', 'href')
|
||||
.and('include', '/lists/1/edit')
|
||||
cy.get('.list-title .dropdown')
|
||||
.should('exist')
|
||||
cy.get('p')
|
||||
.contains('This list is currently empty.')
|
||||
.should('exist')
|
||||
|
@ -363,6 +365,7 @@ describe('Lists', () => {
|
|||
|
||||
cy.getAttached('.kanban .bucket .tasks .task')
|
||||
.contains(tasks[0].title)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
cy.url()
|
||||
|
|
|
@ -64,25 +64,7 @@
|
|||
{{ n.title }} ({{ n.lists.filter(l => !l.isArchived).length }})
|
||||
</span>
|
||||
</label>
|
||||
<div class="actions">
|
||||
<router-link
|
||||
:key="n.id + 'list.create'"
|
||||
:to="{ name: 'list.create', params: { id: n.id} }"
|
||||
v-if="n.id > 0"
|
||||
v-tooltip="'Add a new list in the ' + n.title + ' namespace'">
|
||||
<span class="icon">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{name: 'namespace.edit', params: {id: n.id} }"
|
||||
v-if="n.id > 0"
|
||||
v-tooltip="'Settings'">
|
||||
<span class="icon">
|
||||
<icon icon="cog"/>
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<namespace-settings-dropdown :namespace="n" v-if="n.id > 0"/>
|
||||
</div>
|
||||
<input
|
||||
:id="n.id + 'checker'"
|
||||
|
@ -118,6 +100,7 @@
|
|||
<icon :icon="['far', 'star']" v-else/>
|
||||
</span>
|
||||
</router-link>
|
||||
<list-settings-dropdown :list="l"/>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
|
@ -134,9 +117,15 @@
|
|||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
import {CURRENT_LIST, MENU_ACTIVE, LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
||||
import ListSettingsDropdown from '@/components/list/list-settings-dropdown'
|
||||
import NamespaceSettingsDropdown from '@/components/namespace/namespace-settings-dropdown.vue'
|
||||
|
||||
export default {
|
||||
name: 'navigation',
|
||||
components: {
|
||||
ListSettingsDropdown,
|
||||
NamespaceSettingsDropdown,
|
||||
},
|
||||
computed: mapState({
|
||||
namespaces(state) {
|
||||
return state.namespaces.namespaces.filter(n => !n.isArchived)
|
||||
|
|
|
@ -31,58 +31,49 @@
|
|||
class="title">
|
||||
{{ currentList.title === '' ? 'Loading...' : currentList.title }}
|
||||
</h1>
|
||||
<router-link
|
||||
:to="{ name: 'list.edit', params: { id: currentList.id } }"
|
||||
class="icon"
|
||||
v-if="canWriteCurrentList">
|
||||
<icon icon="cog" size="2x"/>
|
||||
</router-link>
|
||||
|
||||
<list-settings-dropdown v-if="canWriteCurrentList" :list="currentList"/>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<update/>
|
||||
<div class="user">
|
||||
<img :src="userAvatar" alt="" class="avatar"/>
|
||||
<div class="dropdown is-right is-active">
|
||||
<div class="dropdown-trigger">
|
||||
<x-button
|
||||
@click.stop="userMenuActive = !userMenuActive"
|
||||
type="secondary"
|
||||
<dropdown class="is-right">
|
||||
<template v-slot:trigger>
|
||||
<x-button
|
||||
type="secondary"
|
||||
:shadow="false">
|
||||
<span class="username">{{ userInfo.name !== '' ? userInfo.name : userInfo.username }}</span>
|
||||
<span class="icon is-small">
|
||||
<icon icon="chevron-down"/>
|
||||
</span>
|
||||
</x-button>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div class="dropdown-menu" v-if="userMenuActive">
|
||||
<div class="dropdown-content">
|
||||
<router-link :to="{name: 'user.settings'}" class="dropdown-item">
|
||||
Settings
|
||||
</router-link>
|
||||
<a
|
||||
:href="imprintUrl"
|
||||
class="dropdown-item"
|
||||
target="_blank"
|
||||
v-if="imprintUrl">
|
||||
Imprint
|
||||
</a>
|
||||
<a
|
||||
:href="privacyPolicyUrl"
|
||||
class="dropdown-item"
|
||||
target="_blank"
|
||||
v-if="privacyPolicyUrl">
|
||||
Privacy policy
|
||||
</a>
|
||||
<a @click="$store.commit('keyboardShortcutsActive', true)" class="dropdown-item">Keyboard
|
||||
Shortcuts</a>
|
||||
<a @click="logout()" class="dropdown-item">
|
||||
Logout
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<router-link :to="{name: 'user.settings'}" class="dropdown-item">
|
||||
Settings
|
||||
</router-link>
|
||||
<a
|
||||
:href="imprintUrl"
|
||||
class="dropdown-item"
|
||||
target="_blank"
|
||||
v-if="imprintUrl">
|
||||
Imprint
|
||||
</a>
|
||||
<a
|
||||
:href="privacyPolicyUrl"
|
||||
class="dropdown-item"
|
||||
target="_blank"
|
||||
v-if="privacyPolicyUrl">
|
||||
Privacy policy
|
||||
</a>
|
||||
<a @click="$store.commit('keyboardShortcutsActive', true)" class="dropdown-item">Keyboard
|
||||
Shortcuts</a>
|
||||
<a @click="logout()" class="dropdown-item">
|
||||
Logout
|
||||
</a>
|
||||
</dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -93,21 +84,16 @@ import {mapState} from 'vuex'
|
|||
import {CURRENT_LIST} from '@/store/mutation-types'
|
||||
import Rights from '@/models/rights.json'
|
||||
import Update from '@/components/home/update'
|
||||
import ListSettingsDropdown from '@/components/list/list-settings-dropdown'
|
||||
import Dropdown from '@/components/misc/dropdown'
|
||||
|
||||
export default {
|
||||
name: 'topNavigation',
|
||||
data() {
|
||||
return {
|
||||
userMenuActive: false,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Dropdown,
|
||||
ListSettingsDropdown,
|
||||
Update,
|
||||
},
|
||||
created() {
|
||||
// This will hide the menu once clicked outside of it
|
||||
this.$nextTick(() => document.addEventListener('click', () => this.userMenuActive = false))
|
||||
},
|
||||
computed: mapState({
|
||||
userInfo: state => state.auth.info,
|
||||
userAvatar: state => state.auth.avatarUrl,
|
||||
|
|
106
src/components/list/list-settings-dropdown.vue
Normal file
106
src/components/list/list-settings-dropdown.vue
Normal file
|
@ -0,0 +1,106 @@
|
|||
<template>
|
||||
<dropdown>
|
||||
<template v-if="isSavedFilter">
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.edit`, params: { listId: list.id } }"
|
||||
icon="pen"
|
||||
>
|
||||
Edit
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.delete`, params: { listId: list.id } }"
|
||||
icon="trash-alt"
|
||||
>
|
||||
Delete
|
||||
</dropdown-item>
|
||||
</template>
|
||||
<template v-else-if="list.isArchived">
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.archive`, params: { listId: list.id } }"
|
||||
icon="archive"
|
||||
>
|
||||
Un-Archive
|
||||
</dropdown-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.edit`, params: { listId: list.id } }"
|
||||
icon="pen"
|
||||
>
|
||||
Edit
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.background`, params: { listId: list.id } }"
|
||||
v-if="backgroundsEnabled"
|
||||
icon="image"
|
||||
>
|
||||
Set background
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.share`, params: { listId: list.id } }"
|
||||
icon="share-alt"
|
||||
>
|
||||
Share
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.duplicate`, params: { listId: list.id } }"
|
||||
icon="paste"
|
||||
>
|
||||
Duplicate
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.archive`, params: { listId: list.id } }"
|
||||
icon="archive"
|
||||
>
|
||||
Archive
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: `${listRoutePrefix}.settings.delete`, params: { listId: list.id } }"
|
||||
icon="trash-alt"
|
||||
class="has-text-danger"
|
||||
>
|
||||
Delete
|
||||
</dropdown-item>
|
||||
</template>
|
||||
</dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getSavedFilterIdFromListId} from '@/helpers/savedFilter'
|
||||
import Dropdown from '@/components/misc/dropdown'
|
||||
import DropdownItem from '@/components/misc/dropdown-item'
|
||||
|
||||
export default {
|
||||
name: 'list-settings-dropdown',
|
||||
components: {
|
||||
DropdownItem,
|
||||
Dropdown,
|
||||
},
|
||||
props: {
|
||||
list: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
backgroundsEnabled() {
|
||||
return this.$store.state.config.enabledBackgroundProviders.length > 0
|
||||
},
|
||||
listRoutePrefix() {
|
||||
let name = 'list'
|
||||
|
||||
if (this.$route.name.startsWith('list.')) {
|
||||
name = this.$route.name
|
||||
}
|
||||
|
||||
if (this.isSavedFilter) {
|
||||
name = name.replace('list.', 'filter.')
|
||||
}
|
||||
|
||||
return name
|
||||
},
|
||||
isSavedFilter() {
|
||||
return getSavedFilterIdFromListId(this.list.id) > 0
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -10,7 +10,7 @@
|
|||
</span>
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content" :class="{'p-0': !padding}">
|
||||
<div class="card-content loader-container" :class="{'p-0': !padding, 'is-loading': loading}">
|
||||
<div :class="{'content': hasContent}">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
@ -46,6 +46,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
85
src/components/misc/create-edit.vue
Normal file
85
src/components/misc/create-edit.vue
Normal file
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<modal @close="$router.back()" :overflow="true" :wide="wide">
|
||||
<card
|
||||
:title="title"
|
||||
:shadow="false"
|
||||
:padding="false"
|
||||
class="has-text-left has-overflow"
|
||||
:has-close="true"
|
||||
close-icon="times"
|
||||
@close="$router.back()"
|
||||
:loading="loading"
|
||||
>
|
||||
<div class="p-4">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<footer class="modal-card-foot is-flex is-justify-content-flex-end">
|
||||
<x-button
|
||||
:shadow="false"
|
||||
type="tertary"
|
||||
@click.prevent.stop="$emit('tertary')"
|
||||
v-if="tertary !== ''"
|
||||
>
|
||||
{{ tertary }}
|
||||
</x-button>
|
||||
<x-button
|
||||
type="secondary"
|
||||
@click.prevent.stop="$router.back()"
|
||||
>
|
||||
Cancel
|
||||
</x-button>
|
||||
<x-button
|
||||
type="primary"
|
||||
@click.prevent.stop="primary"
|
||||
:icon="primaryIcon"
|
||||
:disabled="primaryDisabled"
|
||||
v-if="primaryLabel !== ''"
|
||||
>
|
||||
{{ primaryLabel }}
|
||||
</x-button>
|
||||
</footer>
|
||||
</card>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'create-edit',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
primaryLabel: {
|
||||
type: String,
|
||||
default: 'Create',
|
||||
},
|
||||
primaryIcon: {
|
||||
type: String,
|
||||
default: 'plus',
|
||||
},
|
||||
primaryDisabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
tertary: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
wide: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
primary() {
|
||||
this.$emit('create')
|
||||
this.$emit('primary')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,55 +0,0 @@
|
|||
<template>
|
||||
<modal @close="$router.back()" :overflow="true">
|
||||
<card
|
||||
:title="title"
|
||||
:shadow="false"
|
||||
:padding="false"
|
||||
class="has-text-left has-overflow"
|
||||
:has-close="true"
|
||||
close-icon="times"
|
||||
@close="$router.back()"
|
||||
>
|
||||
<div class="p-4">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<footer class="modal-card-foot is-flex is-justify-content-flex-end">
|
||||
<x-button
|
||||
:shadow="false"
|
||||
type="secondary"
|
||||
@click.prevent.stop="$router.back()"
|
||||
>
|
||||
Cancel
|
||||
</x-button>
|
||||
<x-button
|
||||
:shadow="false"
|
||||
type="primary"
|
||||
@click.prevent.stop="$emit('create')"
|
||||
icon="plus"
|
||||
:disabled="createDisabled"
|
||||
>
|
||||
{{ createLabel }}
|
||||
</x-button>
|
||||
</footer>
|
||||
</card>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'create',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
createLabel: {
|
||||
type: String,
|
||||
default: 'Create',
|
||||
},
|
||||
createDisabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
28
src/components/misc/dropdown-item.vue
Normal file
28
src/components/misc/dropdown-item.vue
Normal file
|
@ -0,0 +1,28 @@
|
|||
<template>
|
||||
<router-link
|
||||
:to="to"
|
||||
class="dropdown-item">
|
||||
<span class="icon" v-if="icon !== ''">
|
||||
<icon :icon="icon"/>
|
||||
</span>
|
||||
<span>
|
||||
<slot></slot>
|
||||
</span>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'dropdown-item',
|
||||
props: {
|
||||
to: {
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
50
src/components/misc/dropdown.vue
Normal file
50
src/components/misc/dropdown.vue
Normal file
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<div class="dropdown is-right is-active" ref="dropdown">
|
||||
<div class="dropdown-trigger" @click="open = !open">
|
||||
<slot name="trigger">
|
||||
<icon :icon="triggerIcon" class="icon"/>
|
||||
</slot>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div class="dropdown-menu" v-if="open">
|
||||
<div class="dropdown-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
|
||||
export default {
|
||||
name: 'dropdown',
|
||||
data() {
|
||||
return {
|
||||
open: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.hide)
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('click', this.hide)
|
||||
},
|
||||
props: {
|
||||
triggerIcon: {
|
||||
type: String,
|
||||
default: 'ellipsis-h',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hide(e) {
|
||||
if (this.open) {
|
||||
closeWhenClickedOutside(e, this.$refs.dropdown, () => {
|
||||
this.open = false
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
11
src/components/misc/nothing.vue
Normal file
11
src/components/misc/nothing.vue
Normal file
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<p class="has-text-centered has-text-grey is-italic p-4 mb-4">
|
||||
<slot></slot>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'nothing'
|
||||
}
|
||||
</script>
|
|
@ -2,7 +2,7 @@
|
|||
<transition name="modal">
|
||||
<div class="modal-mask">
|
||||
<div class="modal-container" @click.self.prevent.stop="$emit('close')">
|
||||
<div class="modal-content" :class="{'has-overflow': overflow}">
|
||||
<div class="modal-content" :class="{'has-overflow': overflow, 'is-wide': wide}">
|
||||
<slot>
|
||||
<div class="header">
|
||||
<slot name="header"></slot>
|
||||
|
@ -49,6 +49,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
wide: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
63
src/components/namespace/namespace-settings-dropdown.vue
Normal file
63
src/components/namespace/namespace-settings-dropdown.vue
Normal file
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<dropdown>
|
||||
<template v-if="namespace.isArchived">
|
||||
<dropdown-item
|
||||
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
|
||||
icon="archive"
|
||||
>
|
||||
Un-Archive
|
||||
</dropdown-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<dropdown-item
|
||||
:to="{ name: 'namespace.settings.edit', params: { id: namespace.id } }"
|
||||
icon="pen"
|
||||
>
|
||||
Edit
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: 'namespace.settings.share', params: { id: namespace.id } }"
|
||||
icon="share-alt"
|
||||
>
|
||||
Share
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: 'list.create', params: { id: namespace.id } }"
|
||||
icon="plus"
|
||||
>
|
||||
New list
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: 'namespace.settings.archive', params: { id: namespace.id } }"
|
||||
icon="archive"
|
||||
>
|
||||
Archive
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
:to="{ name: 'namespace.settings.delete', params: { id: namespace.id } }"
|
||||
icon="trash-alt"
|
||||
class="has-text-danger"
|
||||
>
|
||||
Delete
|
||||
</dropdown-item>
|
||||
</template>
|
||||
</dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from '@/components/misc/dropdown'
|
||||
import DropdownItem from '@/components/misc/dropdown-item'
|
||||
|
||||
export default {
|
||||
name: 'namespace-settings-dropdown',
|
||||
components: {
|
||||
DropdownItem,
|
||||
Dropdown,
|
||||
},
|
||||
props: {
|
||||
namespace: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<card title="Share links" class="is-fullwidth" :padding="false">
|
||||
<div>
|
||||
<p class="has-text-weight-bold">Share Links</p>
|
||||
<div class="sharables-list">
|
||||
<div class="p-4">
|
||||
<p>Share with a link:</p>
|
||||
|
@ -21,7 +22,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<table
|
||||
class="table is-striped is-hoverable is-fullwidth link-share-list"
|
||||
class="table has-actions is-striped is-hoverable is-fullwidth link-share-list"
|
||||
v-if="linkShares.length > 0"
|
||||
>
|
||||
<thead>
|
||||
|
@ -112,7 +113,7 @@
|
|||
</p>
|
||||
</modal>
|
||||
</transition>
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<template>
|
||||
<card class="is-fullwidth has-overflow" :title="`Shared with these ${shareType}s`" :padding="false">
|
||||
<div class="p-4" v-if="userIsAdmin">
|
||||
<div>
|
||||
<p class="has-text-weight-bold">Shared with these {{ shareType }}s</p>
|
||||
<div v-if="userIsAdmin">
|
||||
<div class="field has-addons">
|
||||
<p
|
||||
class="control is-expanded"
|
||||
v-bind:class="{ 'is-loading': searchService.loading }"
|
||||
:class="{ 'is-loading': searchService.loading }"
|
||||
>
|
||||
<multiselect
|
||||
:loading="searchService.loading"
|
||||
|
@ -20,7 +21,8 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table is-striped is-hoverable is-fullwidth">
|
||||
|
||||
<table class="table has-actions is-striped is-hoverable is-fullwidth mb-4" v-if="sharables.length > 0">
|
||||
<tbody>
|
||||
<tr :key="s.id" v-for="s in sharables">
|
||||
<template v-if="shareType === 'user'">
|
||||
|
@ -105,6 +107,10 @@
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<nothing v-else>
|
||||
Not shared with any {{ shareType }} yet.
|
||||
</nothing>
|
||||
|
||||
<transition name="modal">
|
||||
<modal
|
||||
@close="showDeleteModal = false"
|
||||
|
@ -121,7 +127,7 @@
|
|||
</p>
|
||||
</modal>
|
||||
</transition>
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -143,6 +149,7 @@ import TeamModel from '../../models/team'
|
|||
|
||||
import rights from '../../models/rights'
|
||||
import Multiselect from '@/components/input/multiselect'
|
||||
import Nothing from '@/components/misc/nothing'
|
||||
|
||||
export default {
|
||||
name: 'userTeamShare',
|
||||
|
@ -182,6 +189,7 @@ export default {
|
|||
}
|
||||
},
|
||||
components: {
|
||||
Nothing,
|
||||
Multiselect,
|
||||
},
|
||||
computed: mapState({
|
||||
|
|
|
@ -12,6 +12,8 @@ export default {
|
|||
pages: [],
|
||||
currentPage: 0,
|
||||
|
||||
loadedList: null,
|
||||
|
||||
showTaskSearch: false,
|
||||
searchTerm: '',
|
||||
|
||||
|
@ -53,6 +55,17 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
const list = {listId: parseInt(this.$route.params.listId)}
|
||||
|
||||
const currentList = {
|
||||
id: list.listId,
|
||||
params: params,
|
||||
search: search,
|
||||
}
|
||||
if (JSON.stringify(currentList) === JSON.stringify(this.loadedList)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$set(this, 'tasks', [])
|
||||
|
||||
if (params === null) {
|
||||
|
@ -62,7 +75,8 @@ export default {
|
|||
if (search !== '') {
|
||||
params.s = search
|
||||
}
|
||||
this.taskCollectionService.getAll({listId: this.$route.params.listId}, params, page)
|
||||
|
||||
this.taskCollectionService.getAll(list, params, page)
|
||||
.then(r => {
|
||||
this.$set(this, 'tasks', r)
|
||||
this.$set(this, 'pages', [])
|
||||
|
@ -95,6 +109,8 @@ export default {
|
|||
isEllipsis: false,
|
||||
})
|
||||
}
|
||||
|
||||
this.loadedList = currentList
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
export const saveListView = (listId, routeName) => {
|
||||
if(routeName.includes('settings.')) {
|
||||
return
|
||||
}
|
||||
|
||||
const savedListView = localStorage.getItem('listView')
|
||||
let savedListViewJson = false
|
||||
if (savedListView !== null) {
|
||||
|
|
|
@ -57,6 +57,10 @@ import {
|
|||
faChessKnight,
|
||||
faCoffee,
|
||||
faCocktail,
|
||||
faEllipsisH,
|
||||
faArchive,
|
||||
faShareAlt,
|
||||
faImage,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import {faCalendarAlt, faClock, faComments, faSave, faStar, faTimesCircle, faSun} from '@fortawesome/free-regular-svg-icons'
|
||||
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
|
||||
|
@ -144,6 +148,10 @@ library.add(faSun)
|
|||
library.add(faChessKnight)
|
||||
library.add(faCoffee)
|
||||
library.add(faCocktail)
|
||||
library.add(faEllipsisH)
|
||||
library.add(faArchive)
|
||||
library.add(faShareAlt)
|
||||
library.add(faImage)
|
||||
|
||||
Vue.component('icon', FontAwesomeIcon)
|
||||
|
||||
|
|
|
@ -29,6 +29,20 @@ import Kanban from '../views/list/views/Kanban'
|
|||
import List from '../views/list/views/List'
|
||||
import Gantt from '../views/list/views/Gantt'
|
||||
import Table from '../views/list/views/Table'
|
||||
// List Settings
|
||||
import ListSettingEdit from '@/views/list/settings/edit'
|
||||
import ListSettingBackground from '@/views/list/settings/background'
|
||||
import ListSettingDuplicate from '@/views/list/settings/duplicate'
|
||||
import ListSettingShare from '@/views/list/settings/share'
|
||||
import ListSettingDelete from '@/views/list/settings/delete'
|
||||
import ListSettingArchive from '@/views/list/settings/archive'
|
||||
import FilterSettingEdit from '@/views/filters/settings/edit'
|
||||
import FilterSettingDelete from '@/views/filters/settings/delete'
|
||||
// Namespace Settings
|
||||
import NamespaceSettingEdit from '@/views/namespaces/settings/edit'
|
||||
import NamespaceSettingShare from '@/views/namespaces/settings/share'
|
||||
import NamespaceSettingArchive from '@/views/namespaces/settings/archive'
|
||||
import NamespaceSettingDelete from '@/views/namespaces/settings/delete'
|
||||
// Saved Filters
|
||||
import CreateSavedFilter from '@/views/filters/CreateSavedFilter'
|
||||
|
||||
|
@ -57,12 +71,6 @@ const NewListComponent = () => ({
|
|||
error: ErrorComponent,
|
||||
timeout: 60000,
|
||||
})
|
||||
const EditListComponent = () => ({
|
||||
component: import(/* webpackChunkName: "settings" */'../views/list/EditListView'),
|
||||
loading: LoadingComponent,
|
||||
error: ErrorComponent,
|
||||
timeout: 60000,
|
||||
})
|
||||
// Namespace Handling
|
||||
const NewNamespaceComponent = () => ({
|
||||
component: import(/* webpackChunkName: "settings" */'../views/namespaces/NewNamespace'),
|
||||
|
@ -70,12 +78,6 @@ const NewNamespaceComponent = () => ({
|
|||
error: ErrorComponent,
|
||||
timeout: 60000,
|
||||
})
|
||||
const EditNamespaceComponent = () => ({
|
||||
component: import(/* webpackChunkName: "settings" */'../views/namespaces/EditNamespace'),
|
||||
loading: LoadingComponent,
|
||||
error: ErrorComponent,
|
||||
timeout: 60000,
|
||||
})
|
||||
|
||||
const EditTeamComponent = () => ({
|
||||
component: import(/* webpackChunkName: "settings" */'../views/teams/EditTeam'),
|
||||
|
@ -163,11 +165,6 @@ export default new Router({
|
|||
popup: NewNamespaceComponent,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/namespaces/:id/edit',
|
||||
name: 'namespace.edit',
|
||||
component: EditNamespaceComponent,
|
||||
},
|
||||
{
|
||||
path: '/namespaces/:id/list',
|
||||
name: 'list.create',
|
||||
|
@ -176,9 +173,32 @@ export default new Router({
|
|||
}
|
||||
},
|
||||
{
|
||||
path: '/lists/:id/edit',
|
||||
name: 'list.edit',
|
||||
component: EditListComponent,
|
||||
path: '/namespaces/:id/settings/edit',
|
||||
name: 'namespace.settings.edit',
|
||||
components: {
|
||||
popup: NamespaceSettingEdit,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/namespaces/:id/settings/share',
|
||||
name: 'namespace.settings.share',
|
||||
components: {
|
||||
popup: NamespaceSettingShare,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/namespaces/:id/settings/archive',
|
||||
name: 'namespace.settings.archive',
|
||||
components: {
|
||||
popup: NamespaceSettingArchive,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/namespaces/:id/settings/delete',
|
||||
name: 'namespace.settings.delete',
|
||||
components: {
|
||||
popup: NamespaceSettingDelete,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/tasks/:id',
|
||||
|
@ -190,6 +210,62 @@ export default new Router({
|
|||
name: 'tasks.range',
|
||||
component: ShowTasksInRangeComponent,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'list.settings.edit',
|
||||
components: {
|
||||
popup: ListSettingEdit,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/background',
|
||||
name: 'list.settings.background',
|
||||
components: {
|
||||
popup: ListSettingBackground,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/duplicate',
|
||||
name: 'list.settings.duplicate',
|
||||
components: {
|
||||
popup: ListSettingDuplicate,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/share',
|
||||
name: 'list.settings.share',
|
||||
components: {
|
||||
popup: ListSettingShare,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'list.settings.delete',
|
||||
components: {
|
||||
popup: ListSettingDelete,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/archive',
|
||||
name: 'list.settings.archive',
|
||||
components: {
|
||||
popup: ListSettingArchive,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'filter.settings.edit',
|
||||
components: {
|
||||
popup: FilterSettingEdit,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'filter.settings.delete',
|
||||
components: {
|
||||
popup: FilterSettingDelete,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId',
|
||||
name: 'list.index',
|
||||
|
@ -205,6 +281,46 @@ export default new Router({
|
|||
name: 'task.list.detail',
|
||||
component: TaskDetailViewModal,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'list.list.settings.edit',
|
||||
component: ListSettingEdit,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/background',
|
||||
name: 'list.list.settings.background',
|
||||
component: ListSettingBackground,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/duplicate',
|
||||
name: 'list.list.settings.duplicate',
|
||||
component: ListSettingDuplicate,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/share',
|
||||
name: 'list.list.settings.share',
|
||||
component: ListSettingShare,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'list.list.settings.delete',
|
||||
component: ListSettingDelete,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/archive',
|
||||
name: 'list.list.settings.archive',
|
||||
component: ListSettingArchive,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'filter.list.settings.edit',
|
||||
component: FilterSettingEdit,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'filter.list.settings.delete',
|
||||
component: FilterSettingDelete,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -217,12 +333,94 @@ export default new Router({
|
|||
name: 'task.gantt.detail',
|
||||
component: TaskDetailViewModal,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'list.gantt.settings.edit',
|
||||
component: ListSettingEdit,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/background',
|
||||
name: 'list.gantt.settings.background',
|
||||
component: ListSettingBackground,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/duplicate',
|
||||
name: 'list.gantt.settings.duplicate',
|
||||
component: ListSettingDuplicate,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/share',
|
||||
name: 'list.gantt.settings.share',
|
||||
component: ListSettingShare,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'list.gantt.settings.delete',
|
||||
component: ListSettingDelete,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/archive',
|
||||
name: 'list.gantt.settings.archive',
|
||||
component: ListSettingArchive,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'filter.gantt.settings.edit',
|
||||
component: FilterSettingEdit,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'filter.gantt.settings.delete',
|
||||
component: FilterSettingDelete,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/table',
|
||||
name: 'list.table',
|
||||
component: Table,
|
||||
children: [
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'list.table.settings.edit',
|
||||
component: ListSettingEdit,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/background',
|
||||
name: 'list.table.settings.background',
|
||||
component: ListSettingBackground,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/duplicate',
|
||||
name: 'list.table.settings.duplicate',
|
||||
component: ListSettingDuplicate,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/share',
|
||||
name: 'list.table.settings.share',
|
||||
component: ListSettingShare,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'list.table.settings.delete',
|
||||
component: ListSettingDelete,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/archive',
|
||||
name: 'list.table.settings.archive',
|
||||
component: ListSettingArchive,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'filter.table.settings.edit',
|
||||
component: FilterSettingEdit,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'filter.table.settings.delete',
|
||||
component: FilterSettingDelete,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/kanban',
|
||||
|
@ -234,6 +432,46 @@ export default new Router({
|
|||
name: 'task.kanban.detail',
|
||||
component: TaskDetailViewModal,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'list.kanban.settings.edit',
|
||||
component: ListSettingEdit,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/background',
|
||||
name: 'list.kanban.settings.background',
|
||||
component: ListSettingBackground,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/duplicate',
|
||||
name: 'list.kanban.settings.duplicate',
|
||||
component: ListSettingDuplicate,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/share',
|
||||
name: 'list.kanban.settings.share',
|
||||
component: ListSettingShare,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'list.kanban.settings.delete',
|
||||
component: ListSettingDelete,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/archive',
|
||||
name: 'list.kanban.settings.archive',
|
||||
component: ListSettingArchive,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/edit',
|
||||
name: 'filter.kanban.settings.edit',
|
||||
component: FilterSettingEdit,
|
||||
},
|
||||
{
|
||||
path: '/lists/:listId/settings/delete',
|
||||
name: 'filter.kanban.settings.delete',
|
||||
component: FilterSettingDelete,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -106,7 +106,7 @@ export const store = new Vuex.Store({
|
|||
// Server updates don't return the right. Therefore the right is reset after updating the list which is
|
||||
// confusing because all the buttons will disappear in that case. To prevent this, we're keeping the right
|
||||
// when updating the list in global state.
|
||||
if(typeof state.currentList.maxRight !== 'undefined') {
|
||||
if (typeof state.currentList.maxRight !== 'undefined') {
|
||||
currentList.maxRight = state.currentList.maxRight
|
||||
}
|
||||
state.currentList = currentList
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
border-bottom: 1px solid $grey-200;
|
||||
border-radius: $radius $radius 0 0;
|
||||
}
|
||||
|
||||
.modal-card-foot {
|
||||
background: $grey-50;
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.box, .card {
|
||||
|
|
|
@ -250,10 +250,6 @@ $filter-container-height: '1rem - #{$switch-view-height}';
|
|||
}
|
||||
}
|
||||
|
||||
.dropdown-trigger {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.title.input {
|
||||
height: auto;
|
||||
padding: .4rem .5rem;
|
||||
|
@ -261,6 +257,11 @@ $filter-container-height: '1rem - #{$switch-view-height}';
|
|||
}
|
||||
}
|
||||
|
||||
.dropdown-trigger {
|
||||
cursor: pointer;
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.bucket-footer {
|
||||
padding: .5rem;
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
}
|
||||
|
||||
.is-load-more-button {
|
||||
margin: 0 auto;
|
||||
margin: 1rem auto 0 !important;
|
||||
display: block;
|
||||
width: 200px;
|
||||
}
|
||||
|
|
|
@ -8,24 +8,6 @@
|
|||
.content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.table {
|
||||
border-top: 1px solid $grey-100;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
td.type, td.actions {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
td.actions {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-add {
|
||||
|
@ -51,11 +33,12 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
.dropdown-trigger {
|
||||
color: $grey-400;
|
||||
margin-left: 1rem;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +46,10 @@
|
|||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
$filter-container-top-default: -59px;
|
||||
$filter-container-top-link-share-gantt: -138px;
|
||||
$filter-container-top-link-share-list: -47px;
|
||||
|
||||
.filter-container {
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
|
@ -70,7 +57,7 @@
|
|||
max-width: 180px;
|
||||
position: absolute;
|
||||
right: 1.5rem;
|
||||
margin-top: -59px;
|
||||
margin-top: $filter-container-top-default;
|
||||
z-index: 4;
|
||||
|
||||
.items {
|
||||
|
@ -146,20 +133,42 @@
|
|||
}
|
||||
}
|
||||
|
||||
.list-namespace-title {
|
||||
color: $grey-500;
|
||||
}
|
||||
|
||||
.link-share-container .gantt-chart-container .filter-container,
|
||||
.gantt-chart-container .filter-container {
|
||||
margin-top: calc(-138px - 2rem);
|
||||
right: 0;
|
||||
margin-top: calc(#{$filter-container-top-link-share-gantt} - 2rem);
|
||||
}
|
||||
|
||||
.link-share-container .list-view .filter-container {
|
||||
margin-top: -47px;
|
||||
margin-top: $filter-container-top-link-share-list;
|
||||
}
|
||||
|
||||
.link-share-container .filter-container {
|
||||
right: 9rem;
|
||||
margin-top: -59px;
|
||||
margin-top: $filter-container-top-default;
|
||||
}
|
||||
|
||||
.list-namespace-title {
|
||||
color: $grey-500;
|
||||
.is-archived {
|
||||
$notification-height: 1.25rem + 1.25rem + 1.5rem + 1.5rem;
|
||||
|
||||
.filter-container {
|
||||
margin-top: calc(#{$filter-container-top-default} - #{$notification-height});
|
||||
}
|
||||
|
||||
.link-share-container .gantt-chart-container .filter-container,
|
||||
.gantt-chart-container .filter-container {
|
||||
margin-top: calc(#{$filter-container-top-link-share-gantt} - 2rem - #{$notification-height});
|
||||
}
|
||||
|
||||
.link-share-container .list-view .filter-container {
|
||||
margin-top: calc(#{$filter-container-top-link-share-list} - #{$notification-height});
|
||||
}
|
||||
|
||||
.link-share-container .filter-container {
|
||||
margin-top: calc(#{$filter-container-top-default} - #{$notification-height});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,12 @@
|
|||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
margin: 0;
|
||||
top: 25%;
|
||||
transform: translate(-50%, -25%);
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
|
@ -56,6 +62,11 @@
|
|||
color: $white;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.is-wide {
|
||||
max-width: $desktop;
|
||||
width: calc(100% - 2rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,3 +64,17 @@
|
|||
.underline-none {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.table.has-actions {
|
||||
border-top: 1px solid $grey-100;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
td.actions {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,11 @@
|
|||
|
||||
.field.has-addons .button {
|
||||
height: 2.5rem;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.field.has-addons .select select {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.input,
|
||||
|
|
|
@ -179,13 +179,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
flex: 0 0 auto;
|
||||
a:not(.dropdown-item) {
|
||||
color: $vikunja-nav-color;
|
||||
padding: 0 .25rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $vikunja-nav-color;
|
||||
padding: 0 .25rem;
|
||||
}
|
||||
.dropdown-trigger {
|
||||
padding: .5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,15 +224,36 @@
|
|||
&.can-be-hidden {
|
||||
transition: all $transition;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
//overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.dropdown-trigger {
|
||||
opacity: 0;
|
||||
padding: .5rem;
|
||||
cursor: pointer;
|
||||
transition: $transition;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $white;
|
||||
|
||||
.dropdown-trigger {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span.list-menu-link, a {
|
||||
a:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
span.list-menu-link, li > a {
|
||||
padding: 0.75rem .5rem 0.75rem $navbar-padding * 1.5;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
|
@ -261,10 +283,8 @@
|
|||
}
|
||||
|
||||
&:hover {
|
||||
background: $white;
|
||||
border-left: $vikunja-nav-selected-width solid $primary;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,7 +322,7 @@
|
|||
font-family: $vikunja-font;
|
||||
}
|
||||
|
||||
span.list-menu-link, a {
|
||||
span.list-menu-link, li > a {
|
||||
padding-left: 2rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -342,12 +362,6 @@
|
|||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
.dropdown-content {
|
||||
box-shadow: $shadow-md;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-hide-button, .menu-show-button {
|
||||
|
|
|
@ -72,11 +72,26 @@ button.table {
|
|||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.dropdown-item.is-disabled {
|
||||
cursor: not-allowed;
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left !important;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
.icon {
|
||||
padding-right: .5rem;
|
||||
color: $grey-300 !important;
|
||||
}
|
||||
|
||||
&.has-text-danger .icon {
|
||||
color: $danger !important;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,3 +135,9 @@ button.table {
|
|||
border-radius: 100%;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
.dropdown-content {
|
||||
box-shadow: $shadow-md;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
$grey-50: #F9FAFB;
|
||||
$grey-100: #f3f4f6;
|
||||
$grey-200: #E5E7EB;
|
||||
$grey-300: #D1D5DB;
|
||||
|
|
|
@ -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>
|
44
src/views/filters/settings/delete.vue
Normal file
44
src/views/filters/settings/delete.vue
Normal 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>
|
128
src/views/filters/settings/edit.vue
Normal file
128
src/views/filters/settings/edit.vue
Normal 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>
|
|
@ -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() {
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
52
src/views/list/settings/archive.vue
Normal file
52
src/views/list/settings/archive.vue
Normal 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>
|
|
@ -1,9 +1,11 @@
|
|||
<template>
|
||||
<card
|
||||
:class="{ 'is-loading': backgroundService.loading}"
|
||||
class="list-background-setting loader-container"
|
||||
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
|
||||
<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
|
||||
|
@ -51,24 +53,21 @@
|
|||
type="secondary"
|
||||
v-if="backgroundSearchResult.length > 0"
|
||||
>
|
||||
<template v-if="backgroundService.loading">
|
||||
Loading...
|
||||
</template>
|
||||
<template v-else>
|
||||
Load more photos
|
||||
</template>
|
||||
{{ backgroundService.loading ? 'Loading...' : 'Load more photos'}}
|
||||
</x-button>
|
||||
</template>
|
||||
</card>
|
||||
</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: 'background-settings',
|
||||
name: 'list-setting-background',
|
||||
components: {CreateEdit},
|
||||
data() {
|
||||
return {
|
||||
backgroundSearchTerm: '',
|
||||
|
@ -81,12 +80,6 @@ export default {
|
|||
backgroundUploadService: null,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
listId: {
|
||||
default: 0,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
unsplashBackgroundEnabled() {
|
||||
return this.$store.state.config.enabledBackgroundProviders.includes('unsplash')
|
||||
|
@ -98,6 +91,7 @@ export default {
|
|||
created() {
|
||||
this.backgroundService = new BackgroundUnsplashService()
|
||||
this.backgroundUploadService = new BackgroundUploadService()
|
||||
this.setTitle('Set a list background')
|
||||
// Show the default collection of backgrounds
|
||||
this.newBackgroundSearch()
|
||||
},
|
||||
|
@ -142,7 +136,7 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
this.backgroundService.update({id: backgroundId, listId: this.listId})
|
||||
this.backgroundService.update({id: backgroundId, listId: this.$route.params.listId})
|
||||
.then(l => {
|
||||
this.$store.commit(CURRENT_LIST, l)
|
||||
this.$store.commit('namespaces/setListInNamespaceById', l)
|
||||
|
@ -157,7 +151,7 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
this.backgroundUploadService.create(this.listId, this.$refs.backgroundUploadInput.files[0])
|
||||
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)
|
43
src/views/list/settings/delete.vue
Normal file
43
src/views/list/settings/delete.vue
Normal 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>
|
58
src/views/list/settings/duplicate.vue
Normal file
58
src/views/list/settings/duplicate.vue
Normal 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>
|
127
src/views/list/settings/edit.vue
Normal file
127
src/views/list/settings/edit.vue
Normal 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>
|
78
src/views/list/settings/share.vue
Normal file
78
src/views/list/settings/share.vue
Normal 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>
|
|
@ -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 === '') {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
|
@ -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()
|
||||
|
|
52
src/views/namespaces/settings/archive.vue
Normal file
52
src/views/namespaces/settings/archive.vue
Normal 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>
|
44
src/views/namespaces/settings/delete.vue
Normal file
44
src/views/namespaces/settings/delete.vue
Normal 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>
|
137
src/views/namespaces/settings/edit.vue
Normal file
137
src/views/namespaces/settings/edit.vue
Normal 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>
|
77
src/views/namespaces/settings/share.vue
Normal file
77
src/views/namespaces/settings/share.vue
Normal 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>
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue