Add support for archiving lists and namespaces (#73)
continuous-integration/drone/push Build is passing Details

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: #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>
</div>
<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>
<template v-for="n in namespaces">
<div :key="n.id">
@ -122,14 +134,23 @@
</span>
</router-link>
<label class="menu-label" v-tooltip="n.name + ' (' + n.lists.length + ')'" :for="n.id + 'checker'">
{{n.name}} ({{n.lists.length}})
<span>
{{n.name}} ({{n.lists.length}})
</span>
<span class="is-archived" v-if="n.is_archived">
Archived
</span>
</label>
</div>
<input :key="n.id + 'checker'" type="checkbox" checked="checked" :id="n.id + 'checker'" class="checkinput"/>
<div class="more-container" :key="n.id + 'child'">
<ul class="menu-list can-be-hidden" >
<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>
</li>
</ul>
@ -220,6 +241,7 @@
authTypes: authTypes,
isOnline: true,
motd: '',
showArchived: false,
// Service Worker stuff
updateAvailable: false,
@ -280,7 +302,7 @@
},
loadNamespaces() {
this.namespaceService = new NamespaceService()
this.namespaceService.getAll()
this.namespaceService.getAll({}, {is_archived: this.showArchived})
.then(r => {
this.$set(this, 'namespaces', r)
})

View File

@ -1,5 +1,9 @@
<template>
<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">
<header class="card-header">
<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>
</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>
<div class="columns bigbuttons">

View File

@ -5,6 +5,10 @@
<icon icon="cog" size="2x"/>
</router-link>
<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">
<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>

View File

@ -1,5 +1,9 @@
<template>
<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">
<header class="card-header">
<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>
</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>
<div class="columns bigbuttons">
@ -62,7 +83,7 @@
import NamespaceService from '../../services/namespace'
import NamespaceModel from '../../models/namespace'
export default {
name: "EditNamespace",
data() {

View File

@ -32,7 +32,7 @@
</button>
</div>
<div class="field task-add">
<div class="field task-add" v-if="!list.is_archived">
<div class="field is-grouped">
<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()"/>
@ -59,8 +59,8 @@
<div class="tasks" v-if="tasks && tasks.length > 0" :class="{'short': isTaskEdit}">
<div class="task" v-for="l in tasks" :key="l.id">
<span>
<div class="fancycheckbox">
<input @change="markAsDone" type="checkbox" :id="l.id" :checked="l.done" style="display: none;">
<div class="fancycheckbox" :class="{'is-disabled': list.is_archived}">
<input @change="markAsDone" type="checkbox" :id="l.id" :checked="l.done" style="display: none;" :disabled="list.is_archived">
<label :for="l.id" class="check">
<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>
@ -92,7 +92,7 @@
<priority-label :priority="l.priority"/>
</router-link>
</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"/>
</div>
</div>

View File

@ -27,6 +27,7 @@ export default class ListModel extends AbstractModel {
owner: UserModel,
tasks: [],
namespaceID: 0,
is_archived: false,
created: null,
updated: null,

View File

@ -23,6 +23,7 @@ export default class NamespaceModel extends AbstractModel {
description: '',
owner: UserModel,
lists: [],
is_archived: false,
created: null,
updated: null,

View File

@ -1,10 +1,13 @@
// Fancy Checkboxes
.fancycheckbox {
display: inline-block;
padding-right: 5px;
padding-top: 3px;
input[type=checkbox] {
display: none;
}
&.is-block {
margin: .5em .2em;
}
@ -50,6 +53,10 @@
}
}
&.is-disabled .check:hover svg {
stroke: #c8ccd4;
}
input[type=checkbox]:checked + .check svg {
stroke: $primary;
}

View File

@ -98,6 +98,15 @@
}
}
.show-archived-check {
width: 100%;
text-align: right;
span {
vertical-align: super;
}
}
.menu{
.menu-label {
font-size: 1em;
@ -116,6 +125,34 @@
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{
float: right;
padding: 10px 0.3em 0;
@ -164,7 +201,7 @@
}
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;
-webkit-border-radius: 0;
@ -173,7 +210,6 @@
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inline-block;
width: 100%;
border-left: $vikunja-nav-selected-width solid transparent;
@ -197,6 +233,7 @@
background: $white;
border-left: $vikunja-nav-selected-width solid darken($primary, 3%);
}
}
}
@ -220,6 +257,7 @@
a {
padding-left: 2em;
display: inline-block;
}
}
}