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:
konrad 2020-03-22 20:40:13 +00:00
parent ce80fa2dbd
commit 7d2bd192ab
9 changed files with 118 additions and 11 deletions

View file

@ -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)
}) })

View file

@ -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">

View file

@ -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>

View file

@ -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">

View file

@ -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>

View file

@ -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,

View file

@ -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,

View file

@ -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;
} }

View file

@ -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;
} }
} }
} }