Add support for archiving lists and namespaces (#73)
Use fancy checkbox for archiving namespace Show is archived badge for namespaces Fix is archived badge in navigation bar Add check to filter out archived lists or namespaces Show if a list is archived in menu Hide edit task if the list is archived Hide marking tasks as done if the list is archived Show is archived message on list Add archiving a list Add archiving a namespace Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/73
This commit is contained in:
parent
ce80fa2dbd
commit
7d2bd192ab
9 changed files with 118 additions and 11 deletions
26
src/App.vue
26
src/App.vue
|
@ -104,6 +104,18 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<aside class="menu namespaces-lists">
|
<aside class="menu namespaces-lists">
|
||||||
|
<div class="fancycheckbox show-archived-check">
|
||||||
|
<input type="checkbox" v-model="showArchived" @change="loadNamespaces()" style="display: none;" id="showArchivedCheckbox"/>
|
||||||
|
<label class="check" for="showArchivedCheckbox">
|
||||||
|
<svg width="18px" height="18px" viewBox="0 0 18 18">
|
||||||
|
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
|
||||||
|
<polyline points="1 9 7 14 15 4"></polyline>
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
Show Archived
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="spinner" :class="{ 'is-loading': namespaceService.loading}"></div>
|
<div class="spinner" :class="{ 'is-loading': namespaceService.loading}"></div>
|
||||||
<template v-for="n in namespaces">
|
<template v-for="n in namespaces">
|
||||||
<div :key="n.id">
|
<div :key="n.id">
|
||||||
|
@ -122,14 +134,23 @@
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<label class="menu-label" v-tooltip="n.name + ' (' + n.lists.length + ')'" :for="n.id + 'checker'">
|
<label class="menu-label" v-tooltip="n.name + ' (' + n.lists.length + ')'" :for="n.id + 'checker'">
|
||||||
|
<span>
|
||||||
{{n.name}} ({{n.lists.length}})
|
{{n.name}} ({{n.lists.length}})
|
||||||
|
</span>
|
||||||
|
<span class="is-archived" v-if="n.is_archived">
|
||||||
|
Archived
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<input :key="n.id + 'checker'" type="checkbox" checked="checked" :id="n.id + 'checker'" class="checkinput"/>
|
<input :key="n.id + 'checker'" type="checkbox" checked="checked" :id="n.id + 'checker'" class="checkinput"/>
|
||||||
<div class="more-container" :key="n.id + 'child'">
|
<div class="more-container" :key="n.id + 'child'">
|
||||||
<ul class="menu-list can-be-hidden" >
|
<ul class="menu-list can-be-hidden" >
|
||||||
<li v-for="l in n.lists" :key="l.id">
|
<li v-for="l in n.lists" :key="l.id">
|
||||||
<router-link :to="{ name: 'showList', params: { id: l.id} }">{{l.title}}
|
<router-link :to="{ name: 'showList', params: { id: l.id} }">
|
||||||
|
<span>{{l.title}}</span>
|
||||||
|
<span class="is-archived" v-if="l.is_archived">
|
||||||
|
Archived
|
||||||
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -220,6 +241,7 @@
|
||||||
authTypes: authTypes,
|
authTypes: authTypes,
|
||||||
isOnline: true,
|
isOnline: true,
|
||||||
motd: '',
|
motd: '',
|
||||||
|
showArchived: false,
|
||||||
|
|
||||||
// Service Worker stuff
|
// Service Worker stuff
|
||||||
updateAvailable: false,
|
updateAvailable: false,
|
||||||
|
@ -280,7 +302,7 @@
|
||||||
},
|
},
|
||||||
loadNamespaces() {
|
loadNamespaces() {
|
||||||
this.namespaceService = new NamespaceService()
|
this.namespaceService = new NamespaceService()
|
||||||
this.namespaceService.getAll()
|
this.namespaceService.getAll({}, {is_archived: this.showArchived})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
this.$set(this, 'namespaces', r)
|
this.$set(this, 'namespaces', r)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="loader-container" :class="{ 'is-loading': listService.loading}">
|
<div class="loader-container" :class="{ 'is-loading': listService.loading}">
|
||||||
|
<div class="notification is-warning" v-if="list.is_archived">
|
||||||
|
This list is archived.
|
||||||
|
It is not possible to create new or edit tasks or it.
|
||||||
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<header class="card-header">
|
<header class="card-header">
|
||||||
<p class="card-header-title">
|
<p class="card-header-title">
|
||||||
|
@ -21,6 +25,15 @@
|
||||||
<textarea :class="{ 'disabled': listService.loading}" :disabled="listService.loading" class="textarea" placeholder="The lists description goes here..." id="listdescription" v-model="list.description"></textarea>
|
<textarea :class="{ 'disabled': listService.loading}" :disabled="listService.loading" class="textarea" placeholder="The lists description goes here..." id="listdescription" v-model="list.description"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="isArchivedCheck">Is Archived</label>
|
||||||
|
<div class="control">
|
||||||
|
<label class="checkbox" v-tooltip="'If a list is archived, you cannot create new tasks or edit the list or existing tasks.'">
|
||||||
|
<input type="checkbox" id="isArchivedCheck" v-model="list.is_archived"/>
|
||||||
|
This list is archived
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="columns bigbuttons">
|
<div class="columns bigbuttons">
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
<icon icon="cog" size="2x"/>
|
<icon icon="cog" size="2x"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<h1 :style="{ 'opacity': list.title === '' ? '0': '1' }">{{ list.title === '' ? 'Loading...': list.title}}</h1>
|
<h1 :style="{ 'opacity': list.title === '' ? '0': '1' }">{{ list.title === '' ? 'Loading...': list.title}}</h1>
|
||||||
|
<div class="notification is-warning" v-if="list.is_archived">
|
||||||
|
This list is archived.
|
||||||
|
It is not possible to create new or edit tasks or it.
|
||||||
|
</div>
|
||||||
<div class="switch-view">
|
<div class="switch-view">
|
||||||
<router-link :to="{ name: 'showList', params: { id: list.id } }" :class="{'is-active': $route.params.type !== 'gantt'}">List</router-link>
|
<router-link :to="{ name: 'showList', params: { id: list.id } }" :class="{'is-active': $route.params.type !== 'gantt'}">List</router-link>
|
||||||
<router-link :to="{ name: 'showListWithType', params: { id: list.id, type: 'gantt' } }" :class="{'is-active': $route.params.type === 'gantt'}">Gantt</router-link>
|
<router-link :to="{ name: 'showListWithType', params: { id: list.id, type: 'gantt' } }" :class="{'is-active': $route.params.type === 'gantt'}">Gantt</router-link>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="loader-container" v-bind:class="{ 'is-loading': namespaceService.loading}">
|
<div class="loader-container" v-bind:class="{ 'is-loading': namespaceService.loading}">
|
||||||
|
<div class="notification is-warning" v-if="namespace.is_archived">
|
||||||
|
This namespace is archived.
|
||||||
|
It is not possible to create new lists or edit it.
|
||||||
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<header class="card-header">
|
<header class="card-header">
|
||||||
<p class="card-header-title">
|
<p class="card-header-title">
|
||||||
|
@ -21,6 +25,23 @@
|
||||||
<textarea :class="{ 'disabled': namespaceService.loading}" :disabled="namespaceService.loading" class="textarea" placeholder="The namespaces description goes here..." id="namespacedescription" v-model="namespace.description"></textarea>
|
<textarea :class="{ 'disabled': namespaceService.loading}" :disabled="namespaceService.loading" class="textarea" placeholder="The namespaces description goes here..." id="namespacedescription" v-model="namespace.description"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="isArchivedCheck">Is Archived</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="fancycheckbox" v-tooltip="'If a namespace is archived, you cannot create new lists or edit it.'">
|
||||||
|
<input type="checkbox" id="isArchivedCheck" v-model="namespace.is_archived"/>
|
||||||
|
<label class="check" for="isArchivedCheck">
|
||||||
|
<svg width="18px" height="18px" viewBox="0 0 18 18">
|
||||||
|
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
|
||||||
|
<polyline points="1 9 7 14 15 4"></polyline>
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
This namespace is archived
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="columns bigbuttons">
|
<div class="columns bigbuttons">
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field task-add">
|
<div class="field task-add" v-if="!list.is_archived">
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<p class="control has-icons-left is-expanded" :class="{ 'is-loading': taskService.loading}">
|
<p class="control has-icons-left is-expanded" :class="{ 'is-loading': taskService.loading}">
|
||||||
<input v-focus class="input" :class="{ 'disabled': taskService.loading}" v-model="newTaskText" type="text" placeholder="Add a new task..." @keyup.enter="addTask()"/>
|
<input v-focus class="input" :class="{ 'disabled': taskService.loading}" v-model="newTaskText" type="text" placeholder="Add a new task..." @keyup.enter="addTask()"/>
|
||||||
|
@ -59,8 +59,8 @@
|
||||||
<div class="tasks" v-if="tasks && tasks.length > 0" :class="{'short': isTaskEdit}">
|
<div class="tasks" v-if="tasks && tasks.length > 0" :class="{'short': isTaskEdit}">
|
||||||
<div class="task" v-for="l in tasks" :key="l.id">
|
<div class="task" v-for="l in tasks" :key="l.id">
|
||||||
<span>
|
<span>
|
||||||
<div class="fancycheckbox">
|
<div class="fancycheckbox" :class="{'is-disabled': list.is_archived}">
|
||||||
<input @change="markAsDone" type="checkbox" :id="l.id" :checked="l.done" style="display: none;">
|
<input @change="markAsDone" type="checkbox" :id="l.id" :checked="l.done" style="display: none;" :disabled="list.is_archived">
|
||||||
<label :for="l.id" class="check">
|
<label :for="l.id" class="check">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18">
|
<svg width="18px" height="18px" viewBox="0 0 18 18">
|
||||||
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
|
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
|
||||||
|
@ -92,7 +92,7 @@
|
||||||
<priority-label :priority="l.priority"/>
|
<priority-label :priority="l.priority"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</span>
|
</span>
|
||||||
<div @click="editTask(l.id)" class="icon settings">
|
<div @click="editTask(l.id)" class="icon settings" v-if="!list.is_archived">
|
||||||
<icon icon="pencil-alt"/>
|
<icon icon="pencil-alt"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,6 +27,7 @@ export default class ListModel extends AbstractModel {
|
||||||
owner: UserModel,
|
owner: UserModel,
|
||||||
tasks: [],
|
tasks: [],
|
||||||
namespaceID: 0,
|
namespaceID: 0,
|
||||||
|
is_archived: false,
|
||||||
|
|
||||||
created: null,
|
created: null,
|
||||||
updated: null,
|
updated: null,
|
||||||
|
|
|
@ -23,6 +23,7 @@ export default class NamespaceModel extends AbstractModel {
|
||||||
description: '',
|
description: '',
|
||||||
owner: UserModel,
|
owner: UserModel,
|
||||||
lists: [],
|
lists: [],
|
||||||
|
is_archived: false,
|
||||||
|
|
||||||
created: null,
|
created: null,
|
||||||
updated: null,
|
updated: null,
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
|
||||||
// Fancy Checkboxes
|
// Fancy Checkboxes
|
||||||
.fancycheckbox {
|
.fancycheckbox {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
|
|
||||||
|
input[type=checkbox] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-block {
|
&.is-block {
|
||||||
margin: .5em .2em;
|
margin: .5em .2em;
|
||||||
}
|
}
|
||||||
|
@ -50,6 +53,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-disabled .check:hover svg {
|
||||||
|
stroke: #c8ccd4;
|
||||||
|
}
|
||||||
|
|
||||||
input[type=checkbox]:checked + .check svg {
|
input[type=checkbox]:checked + .check svg {
|
||||||
stroke: $primary;
|
stroke: $primary;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.show-archived-check {
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
span {
|
||||||
|
vertical-align: super;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.menu{
|
.menu{
|
||||||
.menu-label {
|
.menu-label {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
|
@ -116,6 +125,34 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-label, .menu-list a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
span {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-archived {
|
||||||
|
font-size: 0.75em;
|
||||||
|
border: 1px solid $grey;
|
||||||
|
color: $grey !important;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: $vikunja-font;
|
||||||
|
min-width: 60px;
|
||||||
|
display: block;
|
||||||
|
margin-left: 3px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-label .is-archived {
|
||||||
|
min-width: 85px;
|
||||||
|
}
|
||||||
|
|
||||||
.nsettings{
|
.nsettings{
|
||||||
float: right;
|
float: right;
|
||||||
padding: 10px 0.3em 0;
|
padding: 10px 0.3em 0;
|
||||||
|
@ -164,7 +201,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
padding: 0.75em 1em 0.75em $navbar-padding * 1.5;
|
padding: 0.75em .5em 0.75em $navbar-padding * 1.5;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
-webkit-border-radius: 0;
|
-webkit-border-radius: 0;
|
||||||
|
@ -173,7 +210,6 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-left: $vikunja-nav-selected-width solid transparent;
|
border-left: $vikunja-nav-selected-width solid transparent;
|
||||||
|
|
||||||
|
@ -197,6 +233,7 @@
|
||||||
background: $white;
|
background: $white;
|
||||||
border-left: $vikunja-nav-selected-width solid darken($primary, 3%);
|
border-left: $vikunja-nav-selected-width solid darken($primary, 3%);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +257,7 @@
|
||||||
|
|
||||||
a {
|
a {
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue