Cleanup code & make sure it has a common code style
This commit is contained in:
parent
4a8b15e7be
commit
a8a7f70a3c
132 changed files with 6821 additions and 6595 deletions
131
src/App.vue
131
src/App.vue
|
@ -4,20 +4,20 @@
|
|||
<!-- This is a workaround to get the sw to "see" the to-be-cached version of the offline background image -->
|
||||
<div class="offline" style="height: 0;width: 0;"></div>
|
||||
<nav
|
||||
class="navbar main-theme is-fixed-top"
|
||||
:class="{'has-background': background}"
|
||||
role="navigation"
|
||||
aria-label="main navigation"
|
||||
class="navbar main-theme is-fixed-top"
|
||||
role="navigation"
|
||||
v-if="userAuthenticated && (userInfo && userInfo.type === authTypes.USER)">
|
||||
<div class="navbar-brand">
|
||||
<router-link :to="{name: 'home'}" class="navbar-item logo">
|
||||
<img src="/images/logo-full-pride.svg" alt="Vikunja" v-if="(new Date()).getMonth() === 5"/>
|
||||
<img src="/images/logo-full.svg" alt="Vikunja" v-else/>
|
||||
<img alt="Vikunja" src="/images/logo-full-pride.svg" v-if="(new Date()).getMonth() === 5"/>
|
||||
<img alt="Vikunja" src="/images/logo-full.svg" v-else/>
|
||||
</router-link>
|
||||
<a
|
||||
:class="{'is-visible': !menuActive}"
|
||||
@click="menuActive = true"
|
||||
class="menu-show-button"
|
||||
:class="{'is-visible': !menuActive}"
|
||||
>
|
||||
<icon icon="bars"></icon>
|
||||
</a>
|
||||
|
@ -30,8 +30,8 @@
|
|||
</a>
|
||||
<div class="list-title" v-if="currentList.id">
|
||||
<h1
|
||||
class="title"
|
||||
:style="{ 'opacity': currentList.title === '' ? '0': '1' }">
|
||||
:style="{ 'opacity': currentList.title === '' ? '0': '1' }"
|
||||
class="title">
|
||||
{{ currentList.title === '' ? 'Loading...' : currentList.title }}
|
||||
</h1>
|
||||
<router-link
|
||||
|
@ -42,15 +42,15 @@
|
|||
</router-link>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div v-if="updateAvailable" class="update-notification">
|
||||
<div class="update-notification" v-if="updateAvailable">
|
||||
<p>There is an update for Vikunja available!</p>
|
||||
<a @click="refreshApp()" class="button is-primary noshadow">Update Now</a>
|
||||
</div>
|
||||
<div class="user">
|
||||
<img :src="userAvatar" class="avatar" alt=""/>
|
||||
<img :src="userAvatar" alt="" class="avatar"/>
|
||||
<div class="dropdown is-right is-active">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button noshadow" @click.stop="userMenuActive = !userMenuActive">
|
||||
<button @click.stop="userMenuActive = !userMenuActive" class="button noshadow">
|
||||
<span class="username">{{ userInfo.username }}</span>
|
||||
<span class="icon is-small">
|
||||
<icon icon="chevron-down"/>
|
||||
|
@ -63,9 +63,16 @@
|
|||
<router-link :to="{name: 'user.settings'}" class="dropdown-item">
|
||||
Settings
|
||||
</router-link>
|
||||
<a :href="imprintUrl" v-if="imprintUrl" class="dropdown-item" target="_blank">Imprint</a>
|
||||
<a :href="privacyPolicyUrl" v-if="privacyPolicyUrl" class="dropdown-item" target="_blank">Privacy policy</a>
|
||||
<a @click="keyboardShortcutsActive = true" class="dropdown-item">Keyboard Shortcuts</a>
|
||||
<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="keyboardShortcutsActive = true" class="dropdown-item">Keyboard
|
||||
Shortcuts</a>
|
||||
<a @click="logout()" class="dropdown-item">
|
||||
Logout
|
||||
</a>
|
||||
|
@ -81,14 +88,14 @@
|
|||
<icon icon="times"></icon>
|
||||
</a>
|
||||
<div
|
||||
class="app-container"
|
||||
:class="{'has-background': background}"
|
||||
:style="{'background-image': `url(${background})`}"
|
||||
class="app-container"
|
||||
>
|
||||
<div class="namespace-container" :class="{'is-active': menuActive}">
|
||||
<div :class="{'is-active': menuActive}" class="namespace-container">
|
||||
<div class="menu top-menu">
|
||||
<router-link :to="{name: 'home'}" class="logo">
|
||||
<img src="/images/logo-full.svg" alt="Vikunja"/>
|
||||
<img alt="Vikunja" src="/images/logo-full.svg"/>
|
||||
</router-link>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
|
@ -142,67 +149,71 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<a @click="menuActive = false" class="collapse-menu-button" v-shortkey="['ctrl', 'e']" @shortkey="() => menuActive = !menuActive">
|
||||
<a
|
||||
@click="menuActive = false"
|
||||
@shortkey="() => menuActive = !menuActive"
|
||||
class="collapse-menu-button"
|
||||
v-shortkey="['ctrl', 'e']">
|
||||
Collapse Menu
|
||||
</a>
|
||||
|
||||
<aside class="menu namespaces-lists">
|
||||
<div class="spinner" :class="{ 'is-loading': namespaceService.loading}"></div>
|
||||
<div :class="{ 'is-loading': namespaceService.loading}" class="spinner"></div>
|
||||
<template v-for="n in namespaces">
|
||||
<div :key="n.id">
|
||||
<router-link
|
||||
v-tooltip.right="'Settings'"
|
||||
:to="{name: 'namespace.edit', params: {id: n.id} }"
|
||||
class="nsettings"
|
||||
v-if="n.id > 0">
|
||||
v-if="n.id > 0"
|
||||
v-tooltip.right="'Settings'">
|
||||
<span class="icon">
|
||||
<icon icon="cog"/>
|
||||
</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
v-tooltip="'Add a new list in the ' + n.title + ' namespace'"
|
||||
:key="n.id + 'list.create'"
|
||||
:to="{ name: 'list.create', params: { id: n.id} }"
|
||||
class="nsettings"
|
||||
:key="n.id + 'list.create'"
|
||||
v-if="n.id > 0">
|
||||
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>
|
||||
<label
|
||||
:for="n.id + 'checker'"
|
||||
class="menu-label"
|
||||
v-tooltip="n.title + ' (' + n.lists.length + ')'"
|
||||
:for="n.id + 'checker'">
|
||||
v-tooltip="n.title + ' (' + n.lists.length + ')'">
|
||||
<span class="name">
|
||||
<span
|
||||
:style="{ backgroundColor: n.hexColor }"
|
||||
class="color-bubble"
|
||||
v-if="n.hexColor !== ''"
|
||||
:style="{ backgroundColor: n.hexColor }">
|
||||
v-if="n.hexColor !== ''">
|
||||
</span>
|
||||
{{ n.title }} ({{ n.lists.length }})
|
||||
</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'">
|
||||
:key="n.id + 'checker'"
|
||||
checked="checked"
|
||||
class="checkinput"
|
||||
type="checkbox"/>
|
||||
<div :key="n.id + 'child'" class="more-container">
|
||||
<ul class="menu-list can-be-hidden">
|
||||
<template v-for="l in n.lists">
|
||||
<!-- This is a bit ugly but vue wouldn't want to let me filter this - probably because the lists
|
||||
are nested inside of the namespaces makes it a lot harder.-->
|
||||
<li v-if="!l.isArchived" :key="l.id">
|
||||
<li :key="l.id" v-if="!l.isArchived">
|
||||
<router-link
|
||||
:to="{ name: 'list.index', params: { listId: l.id} }"
|
||||
:class="{'router-link-exact-active': currentList.id === l.id}">
|
||||
:class="{'router-link-exact-active': currentList.id === l.id}"
|
||||
:to="{ name: 'list.index', params: { listId: l.id} }">
|
||||
<span class="name">
|
||||
<span
|
||||
:style="{ backgroundColor: l.hexColor }"
|
||||
class="color-bubble"
|
||||
v-if="l.hexColor !== ''"
|
||||
:style="{ backgroundColor: l.hexColor }">
|
||||
v-if="l.hexColor !== ''">
|
||||
</span>
|
||||
{{ l.title }}
|
||||
</span>
|
||||
|
@ -210,16 +221,15 @@
|
|||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<label class="hidden-hint" :for="n.id + 'checker'">
|
||||
<label :for="n.id + 'checker'" class="hidden-hint">
|
||||
Show hidden lists ({{ n.lists.length }})...
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
</aside>
|
||||
<a class="menu-bottom-link" target="_blank" href="https://vikunja.io">Powered by Vikunja</a>
|
||||
<a class="menu-bottom-link" href="https://vikunja.io" target="_blank">Powered by Vikunja</a>
|
||||
</div>
|
||||
<div
|
||||
class="app-content"
|
||||
:class="[
|
||||
{
|
||||
'fullpage-overlay': fullpage,
|
||||
|
@ -227,29 +237,30 @@
|
|||
},
|
||||
$route.name,
|
||||
]"
|
||||
class="app-content"
|
||||
>
|
||||
<a class="mobile-overlay" v-if="menuActive" @click="menuActive = false"></a>
|
||||
<a @click="menuActive = false" class="mobile-overlay" v-if="menuActive"></a>
|
||||
<transition name="fade">
|
||||
<router-view/>
|
||||
</transition>
|
||||
<a class="keyboard-shortcuts-button" @click="keyboardShortcutsActive = true">
|
||||
<a @click="keyboardShortcutsActive = true" class="keyboard-shortcuts-button">
|
||||
<icon icon="keyboard"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="userAuthenticated && (userInfo && userInfo.type === authTypes.LINK_SHARE)"
|
||||
class="link-share-container"
|
||||
:class="{'has-background': background}"
|
||||
:style="{'background-image': `url(${background})`}"
|
||||
class="link-share-container"
|
||||
v-else-if="userAuthenticated && (userInfo && userInfo.type === authTypes.LINK_SHARE)"
|
||||
>
|
||||
<div class="container has-text-centered link-share-view">
|
||||
<div class="column is-10 is-offset-1">
|
||||
<img src="/images/logo-full.svg" alt="Vikunja" class="logo"/>
|
||||
<img alt="Vikunja" class="logo" src="/images/logo-full.svg"/>
|
||||
<h1
|
||||
class="title"
|
||||
:style="{ 'opacity': currentList.title === '' ? '0': '1' }">
|
||||
:style="{ 'opacity': currentList.title === '' ? '0': '1' }"
|
||||
class="title">
|
||||
{{ currentList.title === '' ? 'Loading...' : currentList.title }}
|
||||
</h1>
|
||||
<div class="box has-text-left view">
|
||||
|
@ -268,7 +279,7 @@
|
|||
</div>
|
||||
<div v-else>
|
||||
<div class="noauth-container">
|
||||
<img src="/images/logo-full.svg" alt="Vikunja"/>
|
||||
<img alt="Vikunja" src="/images/logo-full.svg"/>
|
||||
<div class="message is-info" v-if="motd !== ''">
|
||||
<div class="message-header">
|
||||
<p>Info</p>
|
||||
|
@ -290,7 +301,7 @@
|
|||
</div>
|
||||
|
||||
<transition name="fade">
|
||||
<keyboard-shortcuts v-if="keyboardShortcutsActive" @close="keyboardShortcutsActive = false"/>
|
||||
<keyboard-shortcuts @close="keyboardShortcutsActive = false" v-if="keyboardShortcutsActive"/>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -332,8 +343,8 @@
|
|||
beforeMount() {
|
||||
// Check if the user is offline, show a message then
|
||||
this.$store.commit(ONLINE, navigator.onLine)
|
||||
window.addEventListener('online', () => this.$store.commit(ONLINE, navigator.onLine));
|
||||
window.addEventListener('offline', () => this.$store.commit(ONLINE, navigator.onLine));
|
||||
window.addEventListener('online', () => this.$store.commit(ONLINE, navigator.onLine))
|
||||
window.addEventListener('offline', () => this.$store.commit(ONLINE, navigator.onLine))
|
||||
|
||||
// Password reset
|
||||
if (this.$route.query.userPasswordReset !== undefined) {
|
||||
|
@ -377,10 +388,10 @@
|
|||
if (navigator && navigator.serviceWorker) {
|
||||
navigator.serviceWorker.addEventListener(
|
||||
'controllerchange', () => {
|
||||
if (this.refreshing) return;
|
||||
this.refreshing = true;
|
||||
window.location.reload();
|
||||
}
|
||||
if (this.refreshing) return
|
||||
this.refreshing = true
|
||||
window.location.reload()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -486,16 +497,16 @@
|
|||
},
|
||||
showRefreshUI(e) {
|
||||
console.log('recieved refresh event', e)
|
||||
this.registration = e.detail;
|
||||
this.updateAvailable = true;
|
||||
this.registration = e.detail
|
||||
this.updateAvailable = true
|
||||
},
|
||||
refreshApp() {
|
||||
this.updateExists = false;
|
||||
this.updateExists = false
|
||||
if (!this.registration || !this.registration.waiting) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
// Notify the service worker to actually do the update
|
||||
this.registration.waiting.postMessage('skipWaiting');
|
||||
this.registration.waiting.postMessage('skipWaiting')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -5,65 +5,65 @@
|
|||
workbox.routing.registerRoute(
|
||||
// This regexp matches all files in precache-manifest
|
||||
new RegExp('.+\\.(css|json|js|eot|svg|ttf|woff|woff2|png|html|txt)$'),
|
||||
new workbox.strategies.StaleWhileRevalidate()
|
||||
);
|
||||
new workbox.strategies.StaleWhileRevalidate(),
|
||||
)
|
||||
|
||||
// Always send api reqeusts through the network
|
||||
workbox.routing.registerRoute(
|
||||
new RegExp('api\\/v1\\/.*$'),
|
||||
new workbox.strategies.NetworkOnly()
|
||||
);
|
||||
new workbox.strategies.NetworkOnly(),
|
||||
)
|
||||
|
||||
// This code listens for the user's confirmation to update the app.
|
||||
self.addEventListener('message', (e) => {
|
||||
if (!e.data) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
switch (e.data) {
|
||||
case 'skipWaiting':
|
||||
self.skipWaiting();
|
||||
break;
|
||||
self.skipWaiting()
|
||||
break
|
||||
default:
|
||||
// NOOP
|
||||
break;
|
||||
break
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const getBearerToken = async () => {
|
||||
// we can't get a client that sent the current request, therefore we need
|
||||
// to ask any controlled page for auth token
|
||||
const allClients = await self.clients.matchAll();
|
||||
const client = allClients.filter(client => client.type === 'window')[0];
|
||||
const allClients = await self.clients.matchAll()
|
||||
const client = allClients.filter(client => client.type === 'window')[0]
|
||||
|
||||
// if there is no page in scope, we can't get any token
|
||||
// and we indicate it with null value
|
||||
if (!client) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
// to communicate with a page we will use MessageChannels
|
||||
// they expose pipe-like interface, where a receiver of
|
||||
// a message uses one end of a port for messaging and
|
||||
// we use the other end for listening
|
||||
const channel = new MessageChannel();
|
||||
const channel = new MessageChannel()
|
||||
|
||||
client.postMessage({
|
||||
'action': 'getBearerToken'
|
||||
}, [channel.port1]);
|
||||
'action': 'getBearerToken',
|
||||
}, [channel.port1])
|
||||
|
||||
// ports support only onmessage callback which
|
||||
// is cumbersome to use, so we wrap it with Promise
|
||||
return new Promise((resolve, reject) => {
|
||||
channel.port2.onmessage = event => {
|
||||
if (event.data.error) {
|
||||
console.error('Port error', event.error);
|
||||
reject(event.data.error);
|
||||
console.error('Port error', event.error)
|
||||
reject(event.data.error)
|
||||
}
|
||||
|
||||
resolve(event.data.authToken);
|
||||
resolve(event.data.authToken)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// Notification action
|
||||
|
@ -88,7 +88,7 @@ self.addEventListener('notificationclick', function(event) {
|
|||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({id: taskId, done: true})
|
||||
body: JSON.stringify({id: taskId, done: true}),
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(r => {
|
||||
|
@ -106,7 +106,7 @@ self.addEventListener('notificationclick', function(event) {
|
|||
}
|
||||
})
|
||||
|
||||
workbox.core.clientsClaim();
|
||||
workbox.core.clientsClaim()
|
||||
// The precaching code provided by Workbox.
|
||||
self.__precacheManifest = [].concat(self.__precacheManifest || []);
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
|
||||
self.__precacheManifest = [].concat(self.__precacheManifest || [])
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest, {})
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div class="color-picker-container">
|
||||
<verte
|
||||
v-model="color"
|
||||
:menuPosition="menuPosition"
|
||||
picker="square"
|
||||
model="hex"
|
||||
:enableAlpha="false"
|
||||
:rgbSliders="true"/>
|
||||
:menuPosition="menuPosition"
|
||||
:rgbSliders="true"
|
||||
model="hex"
|
||||
picker="square"
|
||||
v-model="color"/>
|
||||
<a @click="reset" class="reset">
|
||||
Reset Color
|
||||
</a>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="editor" :class="{'is-pulled-up': isEditEnabled}">
|
||||
<div :class="{'is-pulled-up': isEditEnabled}" class="editor">
|
||||
<div class="tabs is-right" v-if="hasPreview && isEditEnabled">
|
||||
<ul>
|
||||
<li :class="{'is-active': isPreviewActive}" v-if="isEditActive">
|
||||
|
@ -11,9 +11,15 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<vue-easymde v-model="text" :configs="config" @change="bubble" class="content" v-if="isEditActive" @input="handleInput"/>
|
||||
<vue-easymde
|
||||
:configs="config"
|
||||
@change="bubble"
|
||||
@input="handleInput"
|
||||
class="content"
|
||||
v-if="isEditActive"
|
||||
v-model="text"/>
|
||||
|
||||
<div class="preview content" v-if="isPreviewActive" v-html="preview">
|
||||
<div class="preview content" v-html="preview" v-if="isPreviewActive">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -30,7 +36,7 @@
|
|||
export default {
|
||||
name: 'editor',
|
||||
components: {
|
||||
VueEasymde
|
||||
VueEasymde,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
|
@ -199,7 +205,7 @@
|
|||
title: 'Guide',
|
||||
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M19.4999 2.3999H6.4999C5.0699 2.3999 3.8999 3.5699 3.8999 4.9999V18.9999C3.8999 20.4299 5.0699 21.5999 6.4999 21.5999H19.4999C19.8299 21.5999 20.0999 21.3299 20.0999 20.9999V16.9999V2.9999C20.0999 2.6699 19.8299 2.3999 19.4999 2.3999ZM5.0999 4.9999V16.8118C5.50468 16.5513 5.98546 16.3999 6.4999 16.3999H18.8999V3.5999H6.4999C5.7299 3.5999 5.0999 4.2299 5.0999 4.9999ZM6.4999 17.5999H18.8999V20.3999H6.4999C5.7299 20.3999 5.0999 19.7699 5.0999 18.9999C5.0999 18.2299 5.7299 17.5999 6.4999 17.5999ZM8.4999 8.5999H15.4999C15.8299 8.5999 16.0999 8.3299 16.0999 7.9999C16.0999 7.6699 15.8299 7.3999 15.4999 7.3999H8.4999C8.1699 7.3999 7.8999 7.6699 7.8999 7.9999C7.8999 8.3299 8.1699 8.5999 8.4999 8.5999ZM15.4999 11.3999H8.4999C8.1699 11.3999 7.8999 11.6699 7.8999 11.9999C7.8999 12.3299 8.1699 12.5999 8.4999 12.5999H15.4999C15.8299 12.5999 16.0999 12.3299 16.0999 11.9999C16.0999 11.6699 15.8299 11.3999 15.4999 11.3999Z"/></svg>',
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -248,7 +254,7 @@
|
|||
}, timeout)
|
||||
},
|
||||
replaceAt(str, index, replacement) {
|
||||
return str.substr(0, index) + replacement + str.substr(index + replacement.length);
|
||||
return str.substr(0, index) + replacement + str.substr(index + replacement.length)
|
||||
},
|
||||
findNthIndex(str, n) {
|
||||
|
||||
|
@ -305,7 +311,7 @@
|
|||
checkboxNum++
|
||||
return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checked} class="text-checkbox-${this._uid}"/>`
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
this.preview = DOMPurify.sanitize(marked(this.text))
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
<template>
|
||||
<div class="fancycheckbox" :class="{'is-disabled': disabled}">
|
||||
<div :class="{'is-disabled': disabled}" class="fancycheckbox">
|
||||
<input
|
||||
@change="updateData"
|
||||
type="checkbox"
|
||||
:id="checkBoxId"
|
||||
:checked="checked"
|
||||
:disabled="disabled"
|
||||
:id="checkBoxId"
|
||||
@change="updateData"
|
||||
style="display: none;"
|
||||
:disabled="disabled"/>
|
||||
type="checkbox"/>
|
||||
<label :for="checkBoxId" 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>
|
||||
<svg height="18px" viewBox="0 0 18 18" width="18px">
|
||||
<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>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div
|
||||
:class="{ 'is-loading': backgroundService.loading}"
|
||||
class="card list-background-setting loader-container"
|
||||
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
|
||||
:class="{ 'is-loading': backgroundService.loading}">
|
||||
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Set list background
|
||||
|
@ -11,47 +11,47 @@
|
|||
<div class="card-content">
|
||||
<div class="content" v-if="uploadBackgroundEnabled">
|
||||
<input
|
||||
type="file"
|
||||
ref="backgroundUploadInput"
|
||||
@change="uploadBackground"
|
||||
class="is-hidden"
|
||||
accept="image/*"
|
||||
class="is-hidden"
|
||||
ref="backgroundUploadInput"
|
||||
type="file"
|
||||
/>
|
||||
<a
|
||||
class="button is-primary"
|
||||
:class="{'is-loading': backgroundUploadService.loading}"
|
||||
@click="$refs.backgroundUploadInput.click()"
|
||||
class="button is-primary"
|
||||
>
|
||||
Choose a background from your pc
|
||||
</a>
|
||||
</div>
|
||||
<div class="content" v-if="unsplashBackgroundEnabled">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search for a background..."
|
||||
class="input is-expanded"
|
||||
v-model="backgroundSearchTerm"
|
||||
@keyup="() => newBackgroundSearch()"
|
||||
:class="{'is-loading': backgroundService.loading}"
|
||||
@keyup="() => newBackgroundSearch()"
|
||||
class="input is-expanded"
|
||||
placeholder="Search for a background..."
|
||||
type="text"
|
||||
v-model="backgroundSearchTerm"
|
||||
/>
|
||||
<p class="unsplash-link"><a href="https://unsplash.com" target="_blank">Powered by Unsplash</a></p>
|
||||
<div class="image-search-result">
|
||||
<a
|
||||
:key="im.id"
|
||||
:style="{'background-image': `url(${backgroundThumbs[im.id]})`}"
|
||||
@click="() => setBackground(im.id)"
|
||||
class="image"
|
||||
v-for="im in backgroundSearchResult"
|
||||
:style="{'background-image': `url(${backgroundThumbs[im.id]})`}"
|
||||
:key="im.id">
|
||||
<a class="info" :href="`https://unsplash.com/@${im.info.author}`">
|
||||
v-for="im in backgroundSearchResult">
|
||||
<a :href="`https://unsplash.com/@${im.info.author}`" class="info">
|
||||
{{ im.info.authorName }}
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
v-if="backgroundSearchResult.length > 0"
|
||||
class="button is-primary is-centered is-load-more-button is-outlined noshadow"
|
||||
@click="() => searchBackgrounds(currentPage + 1)"
|
||||
:disabled="backgroundService.loading"
|
||||
@click="() => searchBackgrounds(currentPage + 1)"
|
||||
class="button is-primary is-centered is-load-more-button is-outlined noshadow"
|
||||
v-if="backgroundSearchResult.length > 0"
|
||||
>
|
||||
<template v-if="backgroundService.loading">
|
||||
Loading...
|
||||
|
@ -68,7 +68,7 @@
|
|||
<script>
|
||||
import BackgroundUnsplashService from '../../../services/backgroundUnsplash'
|
||||
import BackgroundUploadService from '../../../services/backgroundUpload'
|
||||
import {CURRENT_LIST} from '../../../store/mutation-types'
|
||||
import {CURRENT_LIST} from '@/store/mutation-types'
|
||||
|
||||
export default {
|
||||
name: 'background-settings',
|
||||
|
@ -88,7 +88,7 @@
|
|||
listId: {
|
||||
default: 0,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
unsplashBackgroundEnabled() {
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
<label class="label">Due Date</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
class="input"
|
||||
:config="flatPickerConfig"
|
||||
@on-close="setDueDateFilter"
|
||||
class="input"
|
||||
placeholder="Due Date Range"
|
||||
v-model="filters.dueDate"
|
||||
@on-close="setDueDateFilter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -66,13 +66,13 @@
|
|||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.$set(this, 'params', newVal)
|
||||
this.prepareDone()
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
change() {
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
<template v-if="isMigrating === false && message === '' && lastMigrationDate === null">
|
||||
<p>To authorize Vikunja to access your {{ name }} Account, click the button below.</p>
|
||||
<a
|
||||
:href="authUrl"
|
||||
class="button is-primary"
|
||||
:class="{'is-loading': migrationService.loading}"
|
||||
:disabled="migrationService.loading">
|
||||
:disabled="migrationService.loading"
|
||||
:href="authUrl"
|
||||
class="button is-primary">
|
||||
Get Started
|
||||
</a>
|
||||
</template>
|
||||
|
@ -16,7 +16,7 @@
|
|||
class="migration-in-progress-container"
|
||||
v-else-if="isMigrating === true && message === '' && lastMigrationDate === null">
|
||||
<div class="migration-in-progress">
|
||||
<img :src="`/images/migration/${identifier}.png`" :alt="name"/>
|
||||
<img :alt="name" :src="`/images/migration/${identifier}.png`"/>
|
||||
<div class="progress-dots">
|
||||
<span></span>
|
||||
<span></span>
|
||||
|
@ -27,7 +27,7 @@
|
|||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<img src="/images/logo.svg" alt="Vikunja">
|
||||
<img alt="Vikunja" src="/images/logo.svg">
|
||||
</div>
|
||||
<p>Importing in progress, hang tight...</p>
|
||||
</div>
|
||||
|
@ -38,7 +38,7 @@
|
|||
Are you sure?
|
||||
</p>
|
||||
<div class="buttons">
|
||||
<button class="button is-primary" @click="migrate">I am sure, please start migrating now!</button>
|
||||
<button @click="migrate" class="button is-primary">I am sure, please start migrating now!</button>
|
||||
<router-link :to="{name: 'home'}" class="button is-danger is-outlined">Cancel</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
name: 'error'
|
||||
name: 'error',
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="modal-mask keyboard-shortcuts-modal">
|
||||
<div class="modal-container" @click.self="close()">
|
||||
<div @click.self="close()" class="modal-container">
|
||||
<div class="modal-content">
|
||||
<div class="card has-background-white has-no-shadow">
|
||||
<header class="card-header">
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="legal-links">
|
||||
<a :href="imprintUrl" v-if="imprintUrl" target="_blank">Imprint</a>
|
||||
<a :href="imprintUrl" target="_blank" v-if="imprintUrl">Imprint</a>
|
||||
<span v-if="imprintUrl && privacyPolicyUrl"> | </span>
|
||||
<a :href="privacyPolicyUrl" v-if="privacyPolicyUrl" target="_blank">Privacy policy</a>
|
||||
<a :href="privacyPolicyUrl" target="_blank" v-if="privacyPolicyUrl">Privacy policy</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -14,6 +14,6 @@
|
|||
computed: mapState({
|
||||
imprintUrl: state => state.config.legal.imprintUrl,
|
||||
privacyPolicyUrl: state => state.config.legal.privacyPolicyUrl,
|
||||
})
|
||||
}),
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
name: 'loading'
|
||||
name: 'loading',
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<template slot="body" slot-scope="props">
|
||||
<div :class="['vue-notification-template', 'vue-notification', props.item.type]" @click="close(props)">
|
||||
<div
|
||||
v-if="props.item.title"
|
||||
class="notification-title"
|
||||
v-html="props.item.title"
|
||||
v-if="props.item.title"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
|
@ -13,11 +13,13 @@
|
|||
v-html="props.item.text"
|
||||
>
|
||||
</div>
|
||||
<div class="buttons is-right" v-if="props.item.data && props.item.data.actions && props.item.data.actions.length > 0">
|
||||
<div
|
||||
class="buttons is-right"
|
||||
v-if="props.item.data && props.item.data.actions && props.item.data.actions.length > 0">
|
||||
<button
|
||||
class="button noshadow is-small"
|
||||
:key="'action_'+i"
|
||||
@click="action.callback"
|
||||
v-for="(action, i) in props.item.data.actions" :key="'action_'+i">
|
||||
class="button noshadow is-small" v-for="(action, i) in props.item.data.actions">
|
||||
{{ action.title }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
<template>
|
||||
<div class="user" :class="{'is-inline': isInline}">
|
||||
<img :src="user.getAvatarUrl(avatarSize)" class="avatar" alt="" v-tooltip="user.username" :width="avatarSize" :height="avatarSize"/>
|
||||
<span v-if="showUsername" class="username">{{ user.username }}</span>
|
||||
<div :class="{'is-inline': isInline}" class="user">
|
||||
<img
|
||||
:height="avatarSize"
|
||||
:src="user.getAvatarUrl(avatarSize)"
|
||||
:width="avatarSize"
|
||||
alt=""
|
||||
class="avatar"
|
||||
v-tooltip="user.username"/>
|
||||
<span class="username" v-if="showUsername">{{ user.username }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
<slot name="text"></slot>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="button is-danger is-inverted noshadow" @click="$emit('close')">Cancel</button>
|
||||
<button class="button is-success noshadow" @click="$emit('submit')">Do it!</button>
|
||||
<button @click="$emit('close')" class="button is-danger is-inverted noshadow">Cancel</button>
|
||||
<button @click="$emit('submit')" class="button is-success noshadow">Do it!</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -29,6 +29,6 @@
|
|||
this.$emit('close')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<template>
|
||||
<multiselect
|
||||
v-model="namespace"
|
||||
:options="namespaces"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:loading="namespaceService.loading"
|
||||
:internal-search="true"
|
||||
:loading="namespaceService.loading"
|
||||
:multiple="false"
|
||||
:options="namespaces"
|
||||
:searchable="true"
|
||||
:showNoOptions="false"
|
||||
@search-change="findNamespaces"
|
||||
@select="select"
|
||||
placeholder="Search for a namespace..."
|
||||
:showNoOptions="false"
|
||||
label="title"
|
||||
track-by="id">
|
||||
placeholder="Search for a namespace..."
|
||||
track-by="id"
|
||||
v-model="namespace">
|
||||
<template slot="clear" slot-scope="props">
|
||||
<div
|
||||
class="multiselect__clear" v-if="namespace.id !== 0"
|
||||
@mousedown.prevent.stop="clearAll(props.search)"></div>
|
||||
@mousedown.prevent.stop="clearAll(props.search)" class="multiselect__clear"
|
||||
v-if="namespace.id !== 0"></div>
|
||||
</template>
|
||||
<span slot="noResult">No namespace found. Consider changing the search query.</span>
|
||||
</multiselect>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-success">
|
||||
<button class="button is-success" type="submit">
|
||||
Share
|
||||
</button>
|
||||
</div>
|
||||
|
@ -36,14 +36,14 @@
|
|||
<th>Delete</th>
|
||||
</tr>
|
||||
<template v-if="linkShares.length > 0">
|
||||
<tr v-for="s in linkShares" :key="s.id">
|
||||
<tr :key="s.id" v-for="s in linkShares">
|
||||
<td>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input class="input" type="text" :value="getShareLink(s.hash)" readonly/>
|
||||
<input :value="getShareLink(s.hash)" class="input" readonly type="text"/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-success noshadow" @click="copy(getShareLink(s.hash))">
|
||||
<a @click="copy(getShareLink(s.hash))" class="button is-success noshadow">
|
||||
<span class="icon">
|
||||
<icon icon="paste"/>
|
||||
</span>
|
||||
|
@ -75,7 +75,8 @@
|
|||
</template>
|
||||
</td>
|
||||
<td class="actions">
|
||||
<button @click="() => {linkIdToDelete = s.id; showDeleteModal = true}" class="button is-danger icon-only">
|
||||
<button @click="() => {linkIdToDelete = s.id; showDeleteModal = true}"
|
||||
class="button is-danger icon-only">
|
||||
<span class="icon">
|
||||
<icon icon="trash-alt"/>
|
||||
</span>
|
||||
|
@ -88,9 +89,9 @@
|
|||
</div>
|
||||
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
@submit="remove()">
|
||||
@submit="remove()"
|
||||
v-if="showDeleteModal">
|
||||
<span slot="header">Remove a link share</span>
|
||||
<p slot="text">Are you sure you want to remove this link share?<br/>
|
||||
It will no longer be possible to access this list with this link share.<br/>
|
||||
|
@ -137,7 +138,7 @@
|
|||
watch: {
|
||||
listId: () => { // watch it
|
||||
this.load()
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: mapState({
|
||||
frontendUrl: state => state.config.frontendUrl,
|
||||
|
|
|
@ -10,25 +10,30 @@
|
|||
<div class="field is-grouped">
|
||||
<p class="control is-expanded" v-bind:class="{ 'is-loading': searchService.loading}">
|
||||
<multiselect
|
||||
v-model="sharable"
|
||||
:options="found"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:loading="searchService.loading"
|
||||
:internal-search="true"
|
||||
:label="searchLabel"
|
||||
:loading="searchService.loading"
|
||||
:multiple="false"
|
||||
:options="found"
|
||||
:searchable="true"
|
||||
:showNoOptions="false"
|
||||
@search-change="find"
|
||||
placeholder="Type to search..."
|
||||
:showNoOptions="false"
|
||||
:label="searchLabel"
|
||||
track-by="id">
|
||||
track-by="id"
|
||||
v-model="sharable">
|
||||
<template slot="clear" slot-scope="props">
|
||||
<div class="multiselect__clear" v-if="sharable.id !== 0" @mousedown.prevent.stop="clearAll(props.search)"></div>
|
||||
<div
|
||||
@mousedown.prevent.stop="clearAll(props.search)"
|
||||
class="multiselect__clear"
|
||||
v-if="sharable.id !== 0"></div>
|
||||
</template>
|
||||
<span slot="noResult">Oops! No {{shareType}} found. Consider changing the search query.</span>
|
||||
<span slot="noResult">
|
||||
Oops! No {{ shareType }} found. Consider changing the search query.
|
||||
</span>
|
||||
</multiselect>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button type="submit" class="button is-success">
|
||||
<button class="button is-success" type="submit">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
|
@ -39,7 +44,7 @@
|
|||
</form>
|
||||
<table class="table is-striped is-hoverable is-fullwidth">
|
||||
<tbody>
|
||||
<tr v-for="s in sharables" :key="s.id">
|
||||
<tr :key="s.id" v-for="s in sharables">
|
||||
<template v-if="shareType === 'user'">
|
||||
<td>{{ s.username }}</td>
|
||||
<td>
|
||||
|
@ -77,13 +82,16 @@
|
|||
</td>
|
||||
<td class="actions" v-if="userIsAdmin">
|
||||
<div class="select">
|
||||
<select @change="toggleType(s)" v-model="selectedRight[s.id]" class="button buttonright">
|
||||
<option :value="rights.READ" :selected="s.right === rights.READ">Read only</option>
|
||||
<option :value="rights.READ_WRITE" :selected="s.right === rights.READ_WRITE">Read & write</option>
|
||||
<option :value="rights.ADMIN" :selected="s.right === rights.ADMIN">Admin</option>
|
||||
<select @change="toggleType(s)" class="button buttonright" v-model="selectedRight[s.id]">
|
||||
<option :selected="s.right === rights.READ" :value="rights.READ">Read only</option>
|
||||
<option :selected="s.right === rights.READ_WRITE" :value="rights.READ_WRITE">Read &
|
||||
write
|
||||
</option>
|
||||
<option :selected="s.right === rights.ADMIN" :value="rights.ADMIN">Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
<button @click="() => {sharable = s; showDeleteModal = true}" class="button is-danger icon-only">
|
||||
<button @click="() => {sharable = s; showDeleteModal = true}"
|
||||
class="button is-danger icon-only">
|
||||
<span class="icon is-small">
|
||||
<icon icon="trash-alt"/>
|
||||
</span>
|
||||
|
@ -95,9 +103,9 @@
|
|||
</div>
|
||||
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
@submit="deleteSharable()">
|
||||
@submit="deleteSharable()"
|
||||
v-if="showDeleteModal">
|
||||
<span slot="header">Remove a {{ shareType }} from the {{ typeString }}</span>
|
||||
<p slot="text">Are you sure you want to remove this {{ shareType }} from the {{ typeString }}?<br/>
|
||||
<b>This CANNOT BE UNDONE!</b></p>
|
||||
|
@ -172,7 +180,7 @@
|
|||
}),
|
||||
},
|
||||
computed: mapState({
|
||||
userInfo: state => state.auth.info
|
||||
userInfo: state => state.auth.info,
|
||||
}),
|
||||
created() {
|
||||
|
||||
|
@ -192,8 +200,7 @@
|
|||
} else {
|
||||
throw new Error('Unknown type: ' + this.type)
|
||||
}
|
||||
}
|
||||
else if (this.shareType === 'team') {
|
||||
} else if (this.shareType === 'team') {
|
||||
this.searchService = new TeamService()
|
||||
this.sharable = new TeamModel()
|
||||
this.searchLabel = 'name'
|
||||
|
|
|
@ -4,45 +4,45 @@
|
|||
<label class="label" for="tasktext">Task Text</label>
|
||||
<div class="control">
|
||||
<input
|
||||
v-focus
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
:disabled="taskService.loading"
|
||||
@change="editTaskSubmit()"
|
||||
class="input"
|
||||
type="text"
|
||||
id="tasktext"
|
||||
placeholder="The task text is here..."
|
||||
v-model="taskEditTask.title"
|
||||
@change="editTaskSubmit()"/>
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="taskEditTask.title"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="taskdescription">Description</label>
|
||||
<div class="control">
|
||||
<editor
|
||||
placeholder="The tasks description goes here..."
|
||||
id="taskdescription"
|
||||
v-model="taskEditTask.description"
|
||||
:preview-is-default="false"
|
||||
id="taskdescription"
|
||||
placeholder="The tasks description goes here..."
|
||||
v-if="editorActive"
|
||||
v-model="taskEditTask.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b>Reminder Dates</b>
|
||||
<reminders v-model="taskEditTask.reminderDates" @change="editTaskSubmit()"/>
|
||||
<reminders @change="editTaskSubmit()" v-model="taskEditTask.reminderDates"/>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="taskduedate">Due Date</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading"
|
||||
v-model="taskEditTask.dueDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading"
|
||||
@on-close="editTaskSubmit()"
|
||||
class="input"
|
||||
id="taskduedate"
|
||||
placeholder="The tasks due date is here...">
|
||||
placeholder="The tasks due date is here..."
|
||||
v-model="taskEditTask.dueDate">
|
||||
</flat-pickr>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -53,25 +53,25 @@
|
|||
<div class="column">
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading"
|
||||
v-model="taskEditTask.startDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading"
|
||||
@on-close="editTaskSubmit()"
|
||||
class="input"
|
||||
id="taskduedate"
|
||||
placeholder="Start date">
|
||||
placeholder="Start date"
|
||||
v-model="taskEditTask.startDate">
|
||||
</flat-pickr>
|
||||
</div>
|
||||
<div class="column">
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading"
|
||||
v-model="taskEditTask.endDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading"
|
||||
@on-close="editTaskSubmit()"
|
||||
class="input"
|
||||
id="taskduedate"
|
||||
placeholder="End date">
|
||||
placeholder="End date"
|
||||
v-model="taskEditTask.endDate">
|
||||
</flat-pickr>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -79,20 +79,20 @@
|
|||
|
||||
<div class="field">
|
||||
<label class="label" for="">Repeat after</label>
|
||||
<repeat-after v-model="taskEditTask.repeatAfter" @change="editTaskSubmit()"/>
|
||||
<repeat-after @change="editTaskSubmit()" v-model="taskEditTask.repeatAfter"/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="">Priority</label>
|
||||
<div class="control priority-select">
|
||||
<priority-select v-model="taskEditTask.priority" @change="editTaskSubmit()"/>
|
||||
<priority-select @change="editTaskSubmit()" v-model="taskEditTask.priority"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Percent Done</label>
|
||||
<div class="control">
|
||||
<percent-done-select v-model="taskEditTask.percentDone" @change="editTaskSubmit()"/>
|
||||
<percent-done-select @change="editTaskSubmit()" v-model="taskEditTask.percentDone"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -106,7 +106,7 @@
|
|||
<div class="field">
|
||||
<label class="label" for="">Assignees</label>
|
||||
<ul class="assingees">
|
||||
<li v-for="(a, index) in taskEditTask.assignees" :key="a.id">
|
||||
<li :key="a.id" v-for="(a, index) in taskEditTask.assignees">
|
||||
{{ a.username }}
|
||||
<a @click="deleteAssigneeByIndex(index)">
|
||||
<icon icon="times"/>
|
||||
|
@ -118,9 +118,9 @@
|
|||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<edit-assignees
|
||||
:task-id="taskEditTask.id"
|
||||
:initial-assignees="taskEditTask.assignees"
|
||||
:list-id="taskEditTask.listId"
|
||||
:initial-assignees="taskEditTask.assignees"/>
|
||||
:task-id="taskEditTask.id"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -132,13 +132,13 @@
|
|||
</div>
|
||||
|
||||
<related-tasks
|
||||
class="is-narrow"
|
||||
:task-id="task.id"
|
||||
:list-id="task.listId"
|
||||
:initial-related-tasks="task.relatedTasks"
|
||||
:list-id="task.listId"
|
||||
:task-id="task.id"
|
||||
class="is-narrow"
|
||||
/>
|
||||
|
||||
<button type="submit" class="button is-success is-fullwidth" :class="{ 'is-loading': taskService.loading}">
|
||||
<button :class="{ 'is-loading': taskService.loading}" class="button is-success is-fullwidth" type="submit">
|
||||
Save
|
||||
</button>
|
||||
|
||||
|
@ -209,13 +209,13 @@
|
|||
task: {
|
||||
type: TaskModel,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
task() {
|
||||
this.taskEditTask = this.task
|
||||
this.initTaskFields()
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.listService = new ListService()
|
||||
|
|
|
@ -2,16 +2,17 @@
|
|||
<div class="gantt-chart box">
|
||||
<div class="dates">
|
||||
<template v-for="(y, yk) in days">
|
||||
<div class="months" :key="yk + 'year'">
|
||||
<div class="month" v-for="(m, mk) in days[yk]" :key="mk + 'month'">
|
||||
{{new Date((new Date(yk)).setMonth(mk)).toLocaleString('en-us', { month: 'long' })}}, {{(new Date(yk)).getFullYear()}}
|
||||
<div :key="yk + 'year'" class="months">
|
||||
<div :key="mk + 'month'" class="month" v-for="(m, mk) in days[yk]">
|
||||
{{ new Date((new Date(yk)).setMonth(mk)).toLocaleString('en-us', {month: 'long'}) }},
|
||||
{{ (new Date(yk)).getFullYear() }}
|
||||
<div class="days">
|
||||
<div
|
||||
class="day"
|
||||
v-for="(d, dk) in days[yk][mk]"
|
||||
:class="{'today': d.toDateString() === now.toDateString()}"
|
||||
:key="dk + 'day'"
|
||||
:style="{'width': dayWidth + 'px'}"
|
||||
:class="{'today': d.toDateString() === now.toDateString()}">
|
||||
class="day"
|
||||
v-for="(d, dk) in days[yk][mk]">
|
||||
<span class="theday" v-if="dayWidth > 25">
|
||||
{{ d.getDate() }}
|
||||
</span>
|
||||
|
@ -24,32 +25,36 @@
|
|||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="tasks" :style="{'width': fullWidth + 'px'}">
|
||||
<div class="row" v-for="(t, k) in theTasks" :key="t.id" :style="{background: 'repeating-linear-gradient(90deg, #ededed, #ededed 1px, ' + (k % 2 === 0 ? '#fafafa 1px, #fafafa ' : '#fff 1px, #fff ') + dayWidth + 'px)'}">
|
||||
<div :style="{'width': fullWidth + 'px'}" class="tasks">
|
||||
<div
|
||||
:key="t.id"
|
||||
:style="{background: 'repeating-linear-gradient(90deg, #ededed, #ededed 1px, ' + (k % 2 === 0 ? '#fafafa 1px, #fafafa ' : '#fff 1px, #fff ') + dayWidth + 'px)'}"
|
||||
class="row"
|
||||
v-for="(t, k) in theTasks">
|
||||
<VueDragResize
|
||||
class="task"
|
||||
:class="{
|
||||
'done': t.done,
|
||||
'is-current-edit': taskToEdit !== null && taskToEdit.id === t.id,
|
||||
'has-light-text': !colorIsDark(t.hexColor),
|
||||
'has-dark-text': colorIsDark(t.hexColor)
|
||||
}"
|
||||
:style="{'border-color': t.hexColor, 'background-color': t.hexColor}"
|
||||
:isActive="canWrite"
|
||||
:x="t.offsetDays * dayWidth - 6"
|
||||
:y="0"
|
||||
:w="t.durationDays * dayWidth"
|
||||
:h="31"
|
||||
:minw="dayWidth"
|
||||
:snapToGrid="true"
|
||||
:gridX="dayWidth"
|
||||
:sticks="['mr', 'ml']"
|
||||
axis="x"
|
||||
:h="31"
|
||||
:isActive="canWrite"
|
||||
:minw="dayWidth"
|
||||
:parentLimitation="true"
|
||||
:parentW="fullWidth"
|
||||
@resizestop="resizeTask"
|
||||
@dragstop="resizeTask"
|
||||
:snapToGrid="true"
|
||||
:sticks="['mr', 'ml']"
|
||||
:style="{'border-color': t.hexColor, 'background-color': t.hexColor}"
|
||||
:w="t.durationDays * dayWidth"
|
||||
:x="t.offsetDays * dayWidth - 6"
|
||||
:y="0"
|
||||
@clicked="setTaskDragged(t)"
|
||||
@dragstop="resizeTask"
|
||||
@resizestop="resizeTask"
|
||||
axis="x"
|
||||
class="task"
|
||||
>
|
||||
<span :class="{
|
||||
'has-high-priority': t.priority >= priorities.HIGH,
|
||||
|
@ -64,23 +69,27 @@
|
|||
</VueDragResize>
|
||||
</div>
|
||||
<template v-if="showTaskswithoutDates">
|
||||
<div class="row" v-for="(t, k) in tasksWithoutDates" :key="t.id" :style="{background: 'repeating-linear-gradient(90deg, #ededed, #ededed 1px, ' + (k % 2 === 0 ? '#fafafa 1px, #fafafa ' : '#fff 1px, #fff ') + dayWidth + 'px)'}">
|
||||
<div
|
||||
:key="t.id"
|
||||
:style="{background: 'repeating-linear-gradient(90deg, #ededed, #ededed 1px, ' + (k % 2 === 0 ? '#fafafa 1px, #fafafa ' : '#fff 1px, #fff ') + dayWidth + 'px)'}"
|
||||
class="row"
|
||||
v-for="(t, k) in tasksWithoutDates">
|
||||
<VueDragResize
|
||||
class="task nodate"
|
||||
:isActive="canWrite"
|
||||
:x="dayOffsetUntilToday * dayWidth - 6"
|
||||
:y="0"
|
||||
:h="31"
|
||||
:minw="dayWidth"
|
||||
:snapToGrid="true"
|
||||
:gridX="dayWidth"
|
||||
:sticks="['mr', 'ml']"
|
||||
axis="x"
|
||||
:h="31"
|
||||
:isActive="canWrite"
|
||||
:minw="dayWidth"
|
||||
:parentLimitation="true"
|
||||
:parentW="fullWidth"
|
||||
@resizestop="resizeTask"
|
||||
@dragstop="resizeTask"
|
||||
:snapToGrid="true"
|
||||
:sticks="['mr', 'ml']"
|
||||
:x="dayOffsetUntilToday * dayWidth - 6"
|
||||
:y="0"
|
||||
@clicked="setTaskDragged(t)"
|
||||
@dragstop="resizeTask"
|
||||
@resizestop="resizeTask"
|
||||
axis="x"
|
||||
class="task nodate"
|
||||
v-tooltip="'This task has no dates set.'"
|
||||
>
|
||||
<span>{{ t.title }}</span>
|
||||
|
@ -91,16 +100,16 @@
|
|||
<form @submit.prevent="addNewTask()" class="add-new-task" v-if="canWrite">
|
||||
<transition name="width">
|
||||
<input
|
||||
type="text"
|
||||
v-model="newTaskTitle"
|
||||
class="input"
|
||||
v-if="newTaskFieldActive"
|
||||
ref="newTaskTitleField"
|
||||
@keyup.esc="newTaskFieldActive = false"
|
||||
@blur="hideCrateNewTask"
|
||||
@keyup.esc="newTaskFieldActive = false"
|
||||
class="input"
|
||||
ref="newTaskTitleField"
|
||||
type="text"
|
||||
v-if="newTaskFieldActive"
|
||||
v-model="newTaskTitle"
|
||||
/>
|
||||
</transition>
|
||||
<button class="button is-primary noshadow" @click="showCreateNewTask">
|
||||
<button @click="showCreateNewTask" class="button is-primary noshadow">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
|
@ -113,7 +122,7 @@
|
|||
<p class="card-header-title">
|
||||
Edit Task
|
||||
</p>
|
||||
<a class="card-header-icon" @click="() => {isTaskEdit = false; taskToEdit = null}">
|
||||
<a @click="() => {isTaskEdit = false; taskToEdit = null}" class="card-header-icon">
|
||||
<span class="icon">
|
||||
<icon icon="times"/>
|
||||
</span>
|
||||
|
@ -158,16 +167,16 @@
|
|||
default: false,
|
||||
},
|
||||
dateFrom: {
|
||||
default: new Date((new Date()).setDate((new Date()).getDate() - 15))
|
||||
default: new Date((new Date()).setDate((new Date()).getDate() - 15)),
|
||||
},
|
||||
dateTo: {
|
||||
default: new Date((new Date()).setDate((new Date()).getDate() + 30))
|
||||
default: new Date((new Date()).setDate((new Date()).getDate() + 30)),
|
||||
},
|
||||
// The width of a day in pixels, used to calculate all sorts of things.
|
||||
dayWidth: {
|
||||
type: Number,
|
||||
default: 35,
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -220,7 +229,7 @@
|
|||
},
|
||||
prepareGanttDays() {
|
||||
// Layout: years => [months => [days]]
|
||||
let years = {};
|
||||
let years = {}
|
||||
for (let d = this.startDate; d <= this.endDate; d.setDate(d.getDate() + 1)) {
|
||||
let date = new Date(d)
|
||||
if (years[date.getFullYear() + ''] === undefined) {
|
||||
|
|
|
@ -28,5 +28,5 @@ export default {
|
|||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
|
@ -137,7 +137,7 @@ export default {
|
|||
|
||||
this.$router.push({
|
||||
name: 'list.list',
|
||||
query: {search: this.searchTerm}
|
||||
query: {search: this.searchTerm},
|
||||
})
|
||||
},
|
||||
hideSearchBar() {
|
||||
|
@ -154,12 +154,12 @@ export default {
|
|||
return {
|
||||
name: 'list.' + type,
|
||||
params: {
|
||||
type: type
|
||||
type: type,
|
||||
},
|
||||
query: {
|
||||
page: page,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
|
@ -6,17 +6,30 @@
|
|||
</span>
|
||||
Attachments
|
||||
<a
|
||||
v-if="editEnabled"
|
||||
class="button is-primary is-outlined is-small noshadow"
|
||||
:disabled="attachmentService.loading"
|
||||
@click="$refs.files.click()"
|
||||
:disabled="attachmentService.loading">
|
||||
class="button is-primary is-outlined is-small noshadow"
|
||||
v-if="editEnabled">
|
||||
<span class="icon is-small"><icon icon="cloud-upload-alt"/></span>
|
||||
Upload attachment
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<input type="file" id="files" ref="files" multiple @change="uploadNewAttachment()" :disabled="attachmentService.loading" v-if="editEnabled"/>
|
||||
<progress v-if="attachmentService.uploadProgress > 0" class="progress is-primary" :value="attachmentService.uploadProgress" max="100">{{ attachmentService.uploadProgress }}%</progress>
|
||||
<input
|
||||
:disabled="attachmentService.loading"
|
||||
@change="uploadNewAttachment()"
|
||||
id="files"
|
||||
multiple
|
||||
ref="files"
|
||||
type="file"
|
||||
v-if="editEnabled"/>
|
||||
<progress
|
||||
:value="attachmentService.uploadProgress"
|
||||
class="progress is-primary"
|
||||
max="100"
|
||||
v-if="attachmentService.uploadProgress > 0">
|
||||
{{ attachmentService.uploadProgress }}%
|
||||
</progress>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -27,22 +40,30 @@
|
|||
<th>Created By</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
<tr class="attachment" v-for="a in attachments" :key="a.id">
|
||||
<tr :key="a.id" class="attachment" v-for="a in attachments">
|
||||
<td>
|
||||
{{ a.file.name }}
|
||||
</td>
|
||||
<td>{{ a.file.getHumanSize() }}</td>
|
||||
<td>{{ a.file.mime }}</td>
|
||||
<td v-tooltip="formatDate(a.created)">{{ formatDateSince(a.created) }}</td>
|
||||
<td><user :user="a.createdBy" :avatar-size="30"/></td>
|
||||
<td>
|
||||
<user :avatar-size="30" :user="a.createdBy"/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="buttons has-addons">
|
||||
<a class="button is-primary noshadow" @click="downloadAttachment(a)" v-tooltip="'Download this attachment'">
|
||||
<a
|
||||
@click="downloadAttachment(a)"
|
||||
class="button is-primary noshadow"
|
||||
v-tooltip="'Download this attachment'">
|
||||
<span class="icon">
|
||||
<icon icon="cloud-download-alt"/>
|
||||
</span>
|
||||
</a>
|
||||
<a v-if="editEnabled" class="button is-danger noshadow" v-tooltip="'Delete this attachment'" @click="() => {attachmentToDelete = a; showDeleteModal = true}">
|
||||
<a
|
||||
@click="() => {attachmentToDelete = a; showDeleteModal = true}"
|
||||
class="button is-danger noshadow" v-if="editEnabled"
|
||||
v-tooltip="'Delete this attachment'">
|
||||
<span class="icon">
|
||||
<icon icon="trash-alt"/>
|
||||
</span>
|
||||
|
@ -53,7 +74,7 @@
|
|||
</table>
|
||||
|
||||
<!-- Dropzone -->
|
||||
<div class="dropzone" :class="{ 'hidden': !showDropzone }" v-if="editEnabled">
|
||||
<div :class="{ 'hidden': !showDropzone }" class="dropzone" v-if="editEnabled">
|
||||
<div class="drop-hint">
|
||||
<div class="icon">
|
||||
<icon icon="cloud-upload-alt"/>
|
||||
|
@ -66,8 +87,8 @@
|
|||
|
||||
<!-- Delete modal -->
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
v-if="showDeleteModal"
|
||||
v-on:submit="deleteAttachment()">
|
||||
<span slot="header">Delete attachment</span>
|
||||
<p slot="text">Are you sure you want to delete the attachment {{ attachmentToDelete.file.name }}?<br/>
|
||||
|
@ -112,26 +133,26 @@
|
|||
this.attachmentService = new AttachmentService()
|
||||
},
|
||||
computed: mapState({
|
||||
attachments: state => state.attachments.attachments
|
||||
attachments: state => state.attachments.attachments,
|
||||
}),
|
||||
mounted() {
|
||||
document.addEventListener('dragenter', e => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.showDropzone = true
|
||||
});
|
||||
})
|
||||
|
||||
window.addEventListener('dragleave', e => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.showDropzone = false
|
||||
});
|
||||
})
|
||||
|
||||
document.addEventListener('dragover', e => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.showDropzone = true
|
||||
});
|
||||
})
|
||||
|
||||
document.addEventListener('drop', e => {
|
||||
e.stopPropagation()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="content details" :class="{'has-top-border': canWrite || comments.length > 0}">
|
||||
<div :class="{'has-top-border': canWrite || comments.length > 0}" class="content details">
|
||||
<h1 v-if="canWrite || comments.length > 0">
|
||||
<span class="icon is-grey">
|
||||
<icon :icon="['far', 'comments']"/>
|
||||
|
@ -10,24 +10,24 @@
|
|||
<progress class="progress is-small is-info" max="100" v-if="taskCommentService.loading">Loading
|
||||
comments...
|
||||
</progress>
|
||||
<div class="media comment" v-for="c in comments" :key="c.id">
|
||||
<div :key="c.id" class="media comment" v-for="c in comments">
|
||||
<figure class="media-left">
|
||||
<img class="image is-avatar" :src="c.author.getAvatarUrl(48)" alt="" width="48" height="48"/>
|
||||
<img :src="c.author.getAvatarUrl(48)" alt="" class="image is-avatar" height="48" width="48"/>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
<div class="comment-info" :class="{'is-pulled-up': canWrite}">
|
||||
<div :class="{'is-pulled-up': canWrite}" class="comment-info">
|
||||
<strong>{{ c.author.username }}</strong>
|
||||
<small v-tooltip="formatDate(c.created)">{{ formatDateSince(c.created) }}</small>
|
||||
<small v-if="+new Date(c.created) !== +new Date(c.updated)" v-tooltip="formatDate(c.updated)"> ·
|
||||
edited {{ formatDateSince(c.updated) }}</small>
|
||||
</div>
|
||||
<editor
|
||||
v-model="c.comment"
|
||||
:has-preview="true"
|
||||
@change="() => {toggleEdit(c);editComment()}"
|
||||
:upload-enabled="true"
|
||||
:upload-callback="attachmentUpload"
|
||||
:is-edit-enabled="canWrite"
|
||||
:upload-callback="attachmentUpload"
|
||||
:upload-enabled="true"
|
||||
@change="() => {toggleEdit(c);editComment()}"
|
||||
v-model="c.comment"
|
||||
/>
|
||||
<div class="comment-actions" v-if="canWrite">
|
||||
<a @click="toggleDelete(c.id)">Remove</a>
|
||||
|
@ -36,25 +36,25 @@
|
|||
</div>
|
||||
<div class="media comment" v-if="canWrite">
|
||||
<figure class="media-left">
|
||||
<img class="image is-avatar" :src="userAvatar" alt="" width="48" height="48"/>
|
||||
<img :src="userAvatar" alt="" class="image is-avatar" height="48" width="48"/>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
<div class="form">
|
||||
<div class="field">
|
||||
<editor
|
||||
placeholder="Add your comment..."
|
||||
:class="{'is-loading': taskCommentService.loading && !isCommentEdit}"
|
||||
v-model="newComment.comment"
|
||||
:has-preview="false"
|
||||
:upload-enabled="true"
|
||||
:upload-callback="attachmentUpload"
|
||||
:upload-enabled="true"
|
||||
placeholder="Add your comment..."
|
||||
v-if="editorActive"
|
||||
v-model="newComment.comment"
|
||||
/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="button is-primary"
|
||||
:class="{'is-loading': taskCommentService.loading && !isCommentEdit}"
|
||||
@click="addComment()" :disabled="newComment.comment === ''">Comment
|
||||
<button :class="{'is-loading': taskCommentService.loading && !isCommentEdit}"
|
||||
:disabled="newComment.comment === ''"
|
||||
@click="addComment()" class="button is-primary">Comment
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -62,9 +62,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
@submit="deleteComment()">
|
||||
@submit="deleteComment()"
|
||||
v-if="showDeleteModal">
|
||||
<span slot="header">Delete this comment</span>
|
||||
<p slot="text">Are you sure you want to delete this comment?
|
||||
<br/>This <b>CANNOT BE UNDONE!</b></p>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
date: {
|
||||
type: Date,
|
||||
default: 0,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<div class="defer-task loading-container" :class="{'is-loading': taskService.loading}">
|
||||
<div :class="{'is-loading': taskService.loading}" class="defer-task loading-container">
|
||||
<label class="label">Defer due date</label>
|
||||
<div class="defer-days">
|
||||
<button class="button is-outlined is-primary has-no-shadow" @click="() => deferDays(1)">1 day</button>
|
||||
<button class="button is-outlined is-primary has-no-shadow" @click="() => deferDays(3)">3 days</button>
|
||||
<button class="button is-outlined is-primary has-no-shadow" @click="() => deferDays(7)">1 week</button>
|
||||
<button @click="() => deferDays(1)" class="button is-outlined is-primary has-no-shadow">1 day</button>
|
||||
<button @click="() => deferDays(3)" class="button is-outlined is-primary has-no-shadow">3 days</button>
|
||||
<button @click="() => deferDays(7)" class="button is-outlined is-primary has-no-shadow">1 week</button>
|
||||
</div>
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading"
|
||||
v-model="dueDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading"
|
||||
class="input"
|
||||
v-model="dueDate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -47,7 +47,7 @@
|
|||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.taskService = new TaskService()
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
<template>
|
||||
<multiselect
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="true"
|
||||
:options-limit="300"
|
||||
:close-on-select="false"
|
||||
:disabled="disabled"
|
||||
:hide-selected="true"
|
||||
v-model="assignees"
|
||||
:options="foundUsers"
|
||||
:searchable="true"
|
||||
:loading="listUserService.loading"
|
||||
:internal-search="true"
|
||||
:loading="listUserService.loading"
|
||||
:multiple="true"
|
||||
:options="foundUsers"
|
||||
:options-limit="300"
|
||||
:searchable="true"
|
||||
:showNoOptions="false"
|
||||
@search-change="findUser"
|
||||
@select="addAssignee"
|
||||
placeholder="Type to assign a user..."
|
||||
label="username"
|
||||
track-by="id"
|
||||
placeholder="Type to assign a user..."
|
||||
select-label="Assign this user"
|
||||
:showNoOptions="false"
|
||||
:disabled="disabled"
|
||||
track-by="id"
|
||||
v-model="assignees"
|
||||
>
|
||||
<template slot="tag" slot-scope="{ option }">
|
||||
<user :user="option" :show-username="false" :avatar-size="30"/>
|
||||
<user :avatar-size="30" :show-username="false" :user="option"/>
|
||||
<a @click="removeAssignee(option)" class="remove-assignee" v-if="!disabled">
|
||||
<icon icon="times"/>
|
||||
</a>
|
||||
</template>
|
||||
<template slot="clear" slot-scope="props">
|
||||
<div class="multiselect__clear" v-if="newAssignee !== null && newAssignee.id !== 0"
|
||||
@mousedown.prevent.stop="clearAllFoundUsers(props.search)"></div>
|
||||
<div
|
||||
@mousedown.prevent.stop="clearAllFoundUsers(props.search)"
|
||||
class="multiselect__clear"
|
||||
v-if="newAssignee !== null && newAssignee.id !== 0"></div>
|
||||
</template>
|
||||
<span slot="noResult">No user found. Consider changing the search query.</span>
|
||||
</multiselect>
|
||||
|
@ -89,7 +91,7 @@
|
|||
watch: {
|
||||
initialAssignees(newVal) {
|
||||
this.assignees = newVal
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addAssignee(user) {
|
||||
|
|
|
@ -1,36 +1,41 @@
|
|||
<template>
|
||||
<multiselect
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="true"
|
||||
:options-limit="300"
|
||||
:close-on-select="false"
|
||||
:disabled="disabled"
|
||||
:hide-selected="true"
|
||||
v-model="labels"
|
||||
:options="foundLabels"
|
||||
:searchable="true"
|
||||
:loading="labelService.loading || labelTaskService.loading"
|
||||
:internal-search="true"
|
||||
:loading="labelService.loading || labelTaskService.loading"
|
||||
:multiple="true"
|
||||
:options="foundLabels"
|
||||
:options-limit="300"
|
||||
:searchable="true"
|
||||
:showNoOptions="false"
|
||||
:taggable="true"
|
||||
@search-change="findLabel"
|
||||
@select="addLabel"
|
||||
placeholder="Type to add a new label..."
|
||||
label="title"
|
||||
track-by="id"
|
||||
:taggable="true"
|
||||
:showNoOptions="false"
|
||||
@tag="createAndAddLabel"
|
||||
label="title"
|
||||
placeholder="Type to add a new label..."
|
||||
tag-placeholder="Add this as new label"
|
||||
:disabled="disabled"
|
||||
track-by="id"
|
||||
v-model="labels"
|
||||
>
|
||||
<template slot="tag" slot-scope="{ option }">
|
||||
<span class="tag"
|
||||
:style="{'background': option.hexColor, 'color': option.textColor}">
|
||||
<template
|
||||
slot="tag"
|
||||
slot-scope="{ option }">
|
||||
<span
|
||||
:style="{'background': option.hexColor, 'color': option.textColor}"
|
||||
class="tag">
|
||||
<span>{{ option.title }}</span>
|
||||
<a class="delete is-small" @click="removeLabel(option)"></a>
|
||||
<a @click="removeLabel(option)" class="delete is-small"></a>
|
||||
</span>
|
||||
</template>
|
||||
<template slot="clear" slot-scope="props">
|
||||
<div class="multiselect__clear" v-if="labels.length"
|
||||
@mousedown.prevent.stop="clearAllLabels(props.search)"></div>
|
||||
<div
|
||||
@mousedown.prevent.stop="clearAllLabels(props.search)"
|
||||
class="multiselect__clear"
|
||||
v-if="labels.length"></div>
|
||||
</template>
|
||||
</multiselect>
|
||||
</template>
|
||||
|
@ -80,7 +85,7 @@
|
|||
watch: {
|
||||
value(newLabels) {
|
||||
this.labels = newLabels
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.labelService = new LabelService()
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
<template>
|
||||
<div class="label-wrapper">
|
||||
<span class="tag" v-for="label in labels" :style="{'background': label.hexColor, 'color': label.textColor}" :key="label.id">
|
||||
<span
|
||||
:key="label.id"
|
||||
:style="{'background': label.hexColor, 'color': label.textColor}"
|
||||
class="tag"
|
||||
v-for="label in labels">
|
||||
<span>{{ label.title }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -12,8 +16,8 @@
|
|||
props: {
|
||||
labels: {
|
||||
required: true,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
<template>
|
||||
<multiselect
|
||||
v-model="list"
|
||||
:options="foundLists"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:loading="listSerivce.loading"
|
||||
:internal-search="true"
|
||||
:loading="listSerivce.loading"
|
||||
:multiple="false"
|
||||
:options="foundLists"
|
||||
:searchable="true"
|
||||
:showNoOptions="false"
|
||||
@search-change="findLists"
|
||||
@select="select"
|
||||
placeholder="Type to search for a list..."
|
||||
label="title"
|
||||
track-by="id"
|
||||
:showNoOptions="false"
|
||||
class="control is-expanded"
|
||||
label="title"
|
||||
placeholder="Type to search for a list..."
|
||||
track-by="id"
|
||||
v-focus
|
||||
v-model="list"
|
||||
>
|
||||
<template slot="clear" slot-scope="props">
|
||||
<div class="multiselect__clear" v-if="list !== null && list.id !== 0" @mousedown.prevent.stop="clearAll(props.search)"></div>
|
||||
<div
|
||||
@mousedown.prevent.stop="clearAll(props.search)"
|
||||
class="multiselect__clear"
|
||||
v-if="list !== null && list.id !== 0"></div>
|
||||
</template>
|
||||
<template slot="option" slot-scope="props">
|
||||
<span class="list-namespace-title">{{ namespace(props.option.namespaceId) }} ></span>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="select">
|
||||
<select v-model.number="percentDone" @change="updateData" :disabled="disabled">
|
||||
<select :disabled="disabled" @change="updateData" v-model.number="percentDone">
|
||||
<option value="0">0%</option>
|
||||
<option value="0.1">10%</option>
|
||||
<option value="0.2">20%</option>
|
||||
|
@ -46,7 +46,7 @@
|
|||
updateData() {
|
||||
this.$emit('input', this.percentDone)
|
||||
this.$emit('change')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<template>
|
||||
<span v-if="showAll || priority >= priorities.HIGH" :class="{'not-so-high': priority === priorities.HIGH, 'high-priority': priority >= priorities.HIGH}">
|
||||
<span
|
||||
:class="{'not-so-high': priority === priorities.HIGH, 'high-priority': priority >= priorities.HIGH}"
|
||||
v-if="showAll || priority >= priorities.HIGH">
|
||||
<span class="icon" v-if="priority >= priorities.HIGH">
|
||||
<icon icon="exclamation"/>
|
||||
</span>
|
||||
|
@ -34,7 +36,7 @@
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="select">
|
||||
<select v-model="priority" @change="updateData" :disabled="disabled">
|
||||
<select :disabled="disabled" @change="updateData" v-model="priority">
|
||||
<option :value="priorities.UNSET">Unset</option>
|
||||
<option :value="priorities.LOW">Low</option>
|
||||
<option :value="priorities.MEDIUM">Medium</option>
|
||||
|
@ -44,7 +44,7 @@
|
|||
updateData() {
|
||||
this.$emit('input', this.priority)
|
||||
this.$emit('change')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -4,26 +4,26 @@
|
|||
<label class="label">New Task Relation</label>
|
||||
<div class="field">
|
||||
<multiselect
|
||||
v-model="newTaskRelationTask"
|
||||
:options="foundTasks"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:loading="taskService.loading"
|
||||
:internal-search="true"
|
||||
@search-change="findTasks"
|
||||
placeholder="Type search for a new task to add as related..."
|
||||
label="title"
|
||||
track-by="id"
|
||||
:taggable="true"
|
||||
:loading="taskService.loading"
|
||||
:multiple="false"
|
||||
:options="foundTasks"
|
||||
:searchable="true"
|
||||
:showNoOptions="false"
|
||||
:taggable="true"
|
||||
@search-change="findTasks"
|
||||
@tag="createAndRelateTask"
|
||||
label="title"
|
||||
placeholder="Type search for a new task to add as related..."
|
||||
tag-placeholder="Add this as new related task"
|
||||
track-by="id"
|
||||
v-model="newTaskRelationTask"
|
||||
>
|
||||
<template slot="clear" slot-scope="props">
|
||||
<div
|
||||
@mousedown.prevent.stop="clearAllFoundTasks(props.search)"
|
||||
class="multiselect__clear"
|
||||
v-if="newTaskRelationTask !== null && newTaskRelationTask.id !== 0"
|
||||
@mousedown.prevent.stop="clearAllFoundTasks(props.search)"></div>
|
||||
v-if="newTaskRelationTask !== null && newTaskRelationTask.id !== 0"></div>
|
||||
</template>
|
||||
<span slot="noResult">No task found. Consider changing the search query.</span>
|
||||
</multiselect>
|
||||
|
@ -33,53 +33,55 @@
|
|||
<div class="select is-fullwidth has-defaults">
|
||||
<select v-model="newTaskRelationKind">
|
||||
<option value="unset">Select a relation kind</option>
|
||||
<option v-for="(label, rk) in relationKinds" :key="rk" :value="rk">
|
||||
<option :key="rk" :value="rk" v-for="(label, rk) in relationKinds">
|
||||
{{ label[0] }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary" @click="addTaskRelation()">Add task Relation</a>
|
||||
<a @click="addTaskRelation()" class="button is-primary">Add task Relation</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="related-tasks" v-for="(rts, kind ) in relatedTasks" :key="kind">
|
||||
<div :key="kind" class="related-tasks" v-for="(rts, kind ) in relatedTasks">
|
||||
<template v-if="rts.length > 0">
|
||||
<span class="title">{{ relationKindTitle(kind, rts.length) }}</span>
|
||||
<div class="tasks noborder">
|
||||
<div class="task" v-for="t in rts" :key="t.id">
|
||||
<div :key="t.id" class="task" v-for="t in rts">
|
||||
<router-link :to="{ name: $route.name, params: { id: t.id } }">
|
||||
<span class="tasktext" :class="{ 'done': t.done}">
|
||||
<span :class="{ 'done': t.done}" class="tasktext">
|
||||
<span
|
||||
v-if="t.listId !== listId"
|
||||
class="different-list"
|
||||
v-if="t.listId !== listId"
|
||||
v-tooltip="'This task belongs to a different list.'">
|
||||
{{ $store.getters['lists/getListById'](t.listId) === null ? '' : $store.getters['lists/getListById'](t.listId).title }} >
|
||||
{{
|
||||
$store.getters['lists/getListById'](t.listId) === null ? '' : $store.getters['lists/getListById'](t.listId).title
|
||||
}} >
|
||||
</span>
|
||||
{{ t.title }}
|
||||
</span>
|
||||
</router-link>
|
||||
<a
|
||||
v-if="editEnabled"
|
||||
@click="() => {showDeleteModal = true; relationToDelete = {relationKind: kind, otherTaskId: t.id}}"
|
||||
class="remove"
|
||||
@click="() => {showDeleteModal = true; relationToDelete = {relationKind: kind, otherTaskId: t.id}}">
|
||||
v-if="editEnabled">
|
||||
<icon icon="trash-alt"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<p v-if="showNoRelationsNotice && Object.keys(relatedTasks).length === 0" class="none">
|
||||
<p class="none" v-if="showNoRelationsNotice && Object.keys(relatedTasks).length === 0">
|
||||
No task relations yet.
|
||||
</p>
|
||||
|
||||
<!-- Delete modal -->
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
@submit="removeTaskRelation()">
|
||||
@submit="removeTaskRelation()"
|
||||
v-if="showDeleteModal">
|
||||
<span slot="header">Delete Task Relation</span>
|
||||
<p slot="text">Are you sure you want to delete this task relation?<br/>
|
||||
<b>This CANNOT BE UNDONE!</b></p>
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
<template>
|
||||
<div class="reminders">
|
||||
<div class="reminder-input"
|
||||
<div
|
||||
:class="{ 'overdue': (r < nowUnix && index !== (reminders.length - 1))}"
|
||||
v-for="(r, index) in reminders" :key="index">
|
||||
:key="index"
|
||||
class="reminder-input"
|
||||
v-for="(r, index) in reminders">
|
||||
<flat-pickr
|
||||
:v-model="reminders"
|
||||
:config="flatPickerConfig"
|
||||
:id="'taskreminderdate' + index"
|
||||
:value="r"
|
||||
:data-index="index"
|
||||
placeholder="Add a new reminder..."
|
||||
:disabled="disabled"
|
||||
:id="'taskreminderdate' + index"
|
||||
:v-model="reminders"
|
||||
:value="r"
|
||||
placeholder="Add a new reminder..."
|
||||
>
|
||||
</flat-pickr>
|
||||
<a v-if="index !== (reminders.length - 1) && !disabled" @click="removeReminderByIndex(index)">
|
||||
<a @click="removeReminderByIndex(index)" v-if="index !== (reminders.length - 1) && !disabled">
|
||||
<icon icon="times"></icon>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
<div class="control">
|
||||
<input
|
||||
:disabled="disabled"
|
||||
@change="updateData"
|
||||
class="input"
|
||||
placeholder="Specify an amount..."
|
||||
v-model="repeatAfter.amount"
|
||||
@change="updateData"/>
|
||||
v-model="repeatAfter.amount"/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select v-model="repeatAfter.type" @change="updateData" :disabled="disabled">
|
||||
<select :disabled="disabled" @change="updateData" v-model="repeatAfter.type">
|
||||
<option value="hours">Hours</option>
|
||||
<option value="days">Days</option>
|
||||
<option value="weeks">Weeks</option>
|
||||
|
@ -26,8 +26,8 @@
|
|||
</div>
|
||||
<fancycheckbox
|
||||
:disabled="disabled"
|
||||
class="column"
|
||||
@change="updateData"
|
||||
class="column"
|
||||
v-model="task.repeatFromCurrentDate"
|
||||
v-tooltip="'When marking the task as done, all dates will be set relative to the current date rather than the date they had before.'"
|
||||
>
|
||||
|
@ -80,12 +80,12 @@
|
|||
this.task.repeatAfter = this.repeatAfter
|
||||
this.$emit('input', this.task)
|
||||
this.$emit('change')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
p {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<div class="task loader-container" :class="{'is-loading': taskService.loading}">
|
||||
<fancycheckbox v-model="task.done" @change="markAsDone" :disabled="isArchived || disabled"/>
|
||||
<span class="tasktext" :class="{ 'done': task.done}">
|
||||
<div :class="{'is-loading': taskService.loading}" class="task loader-container">
|
||||
<fancycheckbox :disabled="isArchived || disabled" @change="markAsDone" v-model="task.done"/>
|
||||
<span :class="{ 'done': task.done}" class="tasktext">
|
||||
<router-link :to="{ name: taskDetailRoute, params: { id: task.id } }">
|
||||
<router-link
|
||||
v-if="showList && $store.getters['lists/getListById'](task.listId) !== null"
|
||||
v-tooltip="`This task belongs to list '${$store.getters['lists/getListById'](task.listId).title}'`"
|
||||
:to="{ name: 'list.list', params: { listId: task.listId } }"
|
||||
class="task-list">
|
||||
class="task-list"
|
||||
v-if="showList && $store.getters['lists/getListById'](task.listId) !== null"
|
||||
v-tooltip="`This task belongs to list '${$store.getters['lists/getListById'](task.listId).title}'`">
|
||||
{{ $store.getters['lists/getListById'](task.listId).title }}
|
||||
</router-link>
|
||||
|
||||
|
@ -23,37 +23,37 @@
|
|||
|
||||
<labels :labels="task.labels"/>
|
||||
<user
|
||||
:user="a"
|
||||
:avatar-size="27"
|
||||
:show-username="false"
|
||||
:is-inline="true"
|
||||
v-for="(a, i) in task.assignees"
|
||||
:key="task.id + 'assignee' + a.id + i"
|
||||
:show-username="false"
|
||||
:user="a"
|
||||
v-for="(a, i) in task.assignees"
|
||||
/>
|
||||
<i
|
||||
v-if="+new Date(task.dueDate) > 0"
|
||||
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
|
||||
v-tooltip="formatDate(task.dueDate)"
|
||||
@click.stop="showDefer = !showDefer"
|
||||
v-if="+new Date(task.dueDate) > 0"
|
||||
v-tooltip="formatDate(task.dueDate)"
|
||||
>
|
||||
- Due {{ formatDateSince(task.dueDate) }}
|
||||
</i>
|
||||
<transition name="fade">
|
||||
<defer-task v-model="task" v-if="+new Date(task.dueDate) > 0 && showDefer"/>
|
||||
<defer-task v-if="+new Date(task.dueDate) > 0 && showDefer" v-model="task"/>
|
||||
</transition>
|
||||
<priority-label :priority="task.priority"/>
|
||||
</span>
|
||||
<router-link
|
||||
v-if="currentList.id !== task.listId && $store.getters['lists/getListById'](task.listId) !== null"
|
||||
v-tooltip="`This task belongs to list '${$store.getters['lists/getListById'](task.listId).title}'`"
|
||||
:to="{ name: 'list.list', params: { listId: task.listId } }"
|
||||
class="task-list">
|
||||
class="task-list"
|
||||
v-if="currentList.id !== task.listId && $store.getters['lists/getListById'](task.listId) !== null"
|
||||
v-tooltip="`This task belongs to list '${$store.getters['lists/getListById'](task.listId).title}'`">
|
||||
{{ $store.getters['lists/getListById'](task.listId).title }}
|
||||
</router-link>
|
||||
<a
|
||||
class="favorite"
|
||||
:class="{'is-favorite': task.isFavorite}"
|
||||
@click="toggleFavorite">
|
||||
@click="toggleFavorite"
|
||||
class="favorite">
|
||||
<icon icon="star" v-if="task.isFavorite"/>
|
||||
<icon :icon="['far', 'star']" v-else/>
|
||||
</a>
|
||||
|
@ -97,7 +97,7 @@
|
|||
},
|
||||
taskDetailRoute: {
|
||||
type: String,
|
||||
default: 'task.list.detail'
|
||||
default: 'task.list.detail',
|
||||
},
|
||||
showList: {
|
||||
type: Boolean,
|
||||
|
@ -122,7 +122,10 @@
|
|||
},
|
||||
computed: {
|
||||
currentList() {
|
||||
return typeof this.$store.state.currentList === 'undefined' ? {id: 0, title: ''} : this.$store.state.currentList
|
||||
return typeof this.$store.state.currentList === 'undefined' ? {
|
||||
id: 0,
|
||||
title: '',
|
||||
} : this.$store.state.currentList
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -139,10 +142,10 @@
|
|||
title: 'Undo',
|
||||
callback: () => this.markAsDone({
|
||||
target: {
|
||||
checked: !checked
|
||||
}
|
||||
checked: !checked,
|
||||
},
|
||||
}),
|
||||
}]
|
||||
}],
|
||||
)
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -151,7 +154,7 @@
|
|||
}
|
||||
|
||||
if (checked) {
|
||||
setTimeout(updateFunc, 300); // Delay it to show the animation when marking a task as done
|
||||
setTimeout(updateFunc, 300) // Delay it to show the animation when marking a task as done
|
||||
} else {
|
||||
updateFunc() // Don't delay it when un-marking it as it doesn't have an animation the other way around
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<a @click="click">
|
||||
<icon icon="sort-up" v-if="order === 'asc'"/>
|
||||
<icon icon="sort-up" v-else-if="order === 'desc'" rotation="180"/>
|
||||
<icon icon="sort-up" rotation="180" v-else-if="order === 'desc'"/>
|
||||
<icon icon="sort" v-else/>
|
||||
</a>
|
||||
</template>
|
||||
|
|
|
@ -8,57 +8,57 @@
|
|||
<div class="card-content">
|
||||
<div class="control mb-4">
|
||||
<label class="radio">
|
||||
<input type="radio" name="avatarProvider" v-model="avatarProvider" value="default"/>
|
||||
<input name="avatarProvider" type="radio" v-model="avatarProvider" value="default"/>
|
||||
Default
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="avatarProvider" v-model="avatarProvider" value="initials"/>
|
||||
<input name="avatarProvider" type="radio" v-model="avatarProvider" value="initials"/>
|
||||
Initials
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="avatarProvider" v-model="avatarProvider" value="gravatar"/>
|
||||
<input name="avatarProvider" type="radio" v-model="avatarProvider" value="gravatar"/>
|
||||
Gravatar
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="avatarProvider" v-model="avatarProvider" value="upload"/>
|
||||
<input name="avatarProvider" type="radio" v-model="avatarProvider" value="upload"/>
|
||||
Upload
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<template v-if="avatarProvider === 'upload'">
|
||||
<input
|
||||
type="file"
|
||||
ref="avatarUploadInput"
|
||||
@change="cropAvatar"
|
||||
class="is-hidden"
|
||||
accept="image/*"
|
||||
class="is-hidden"
|
||||
ref="avatarUploadInput"
|
||||
type="file"
|
||||
/>
|
||||
<a
|
||||
v-if="!isCropAvatar"
|
||||
class="button is-primary"
|
||||
:class="{ 'is-loading': avatarService.loading || loading}"
|
||||
@click="$refs.avatarUploadInput.click()"
|
||||
:class="{ 'is-loading': avatarService.loading || loading}">
|
||||
class="button is-primary"
|
||||
v-if="!isCropAvatar">
|
||||
Upload Avatar
|
||||
</a>
|
||||
<template v-else>
|
||||
<cropper
|
||||
:src="avatarToCrop"
|
||||
class="mb-4"
|
||||
@ready="() => loading = false"
|
||||
:stencil-props="{aspectRatio: 1}"
|
||||
@ready="() => loading = false"
|
||||
class="mb-4"
|
||||
ref="cropper"/>
|
||||
<a
|
||||
class="button is-primary"
|
||||
:class="{ 'is-loading': avatarService.loading || loading}"
|
||||
@click="uploadAvatar"
|
||||
:class="{ 'is-loading': avatarService.loading || loading}">
|
||||
class="button is-primary">
|
||||
Upload Avatar
|
||||
</a>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<div class="bigbuttons" v-if="avatarProvider !== 'upload'">
|
||||
<button @click="updateAvatarStatus()" class="button is-primary is-fullwidth"
|
||||
:class="{ 'is-loading': avatarService.loading || loading}">
|
||||
<button :class="{ 'is-loading': avatarService.loading || loading}" @click="updateAvatarStatus()"
|
||||
class="button is-primary is-fullwidth">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
export const applyDrag = (arr, dragResult) => {
|
||||
const {removedIndex, addedIndex, payload} = dragResult
|
||||
if (removedIndex === null && addedIndex === null) return arr
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
export const filterObject = (obj, fn) => {
|
||||
let key
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
export const saveListView = (listId, routeName) => {
|
||||
const savedListView = localStorage.getItem('listView')
|
||||
let savedListViewJson = false
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
export const setTitle = title => {
|
||||
if (typeof title === 'undefined' || title === '') {
|
||||
document.title = 'Vikunja'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import axios from 'axios'
|
||||
|
||||
export const HTTP = axios.create({
|
||||
baseURL: window.API_URL
|
||||
baseURL: window.API_URL,
|
||||
})
|
||||
|
|
151
src/main.js
151
src/main.js
|
@ -3,6 +3,74 @@ import App from './App.vue'
|
|||
import router from './router'
|
||||
|
||||
import {VERSION} from './version.json'
|
||||
// Register the modal
|
||||
import Modal from './components/modal/modal'
|
||||
// Add CSS
|
||||
import './styles/vikunja.scss'
|
||||
// Notifications
|
||||
import Notifications from 'vue-notification'
|
||||
// Icons
|
||||
import {library} from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faAlignLeft,
|
||||
faAngleRight,
|
||||
faBars,
|
||||
faCalendar,
|
||||
faCalendarWeek,
|
||||
faCheck,
|
||||
faCheckDouble,
|
||||
faChevronDown,
|
||||
faCloudDownloadAlt,
|
||||
faCloudUploadAlt,
|
||||
faCog,
|
||||
faEllipsisV,
|
||||
faExclamation,
|
||||
faFillDrip,
|
||||
faFilter,
|
||||
faHistory,
|
||||
faKeyboard,
|
||||
faLayerGroup,
|
||||
faList,
|
||||
faListOl,
|
||||
faLock,
|
||||
faPaperclip,
|
||||
faPaste,
|
||||
faPen,
|
||||
faPencilAlt,
|
||||
faPercent,
|
||||
faPlus,
|
||||
faPowerOff,
|
||||
faSearch,
|
||||
faSignOutAlt,
|
||||
faSort,
|
||||
faSortUp,
|
||||
faStar as faStarSolid,
|
||||
faTachometerAlt,
|
||||
faTags,
|
||||
faTasks,
|
||||
faTh,
|
||||
faTimes,
|
||||
faTrashAlt,
|
||||
faUser,
|
||||
faUsers,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import {faCalendarAlt, faClock, faComments, faSave, faStar, faTimesCircle} from '@fortawesome/free-regular-svg-icons'
|
||||
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
|
||||
// Tooltip
|
||||
import VTooltip from 'v-tooltip'
|
||||
// PWA
|
||||
import './registerServiceWorker'
|
||||
|
||||
// Shortcuts
|
||||
import vueShortkey from 'vue-shortkey'
|
||||
// Mixins
|
||||
import message from './message'
|
||||
import {format, formatDistance} from 'date-fns'
|
||||
import {colorIsDark} from './helpers/colorIsDark'
|
||||
import {setTitle} from './helpers/setTitle'
|
||||
// Vuex
|
||||
import {store} from './store'
|
||||
|
||||
console.info(`Vikunja frontend version ${VERSION}`)
|
||||
|
||||
// Make sure the api url does not contain a / at the end
|
||||
|
@ -10,70 +78,12 @@ if(window.API_URL.substr(window.API_URL.length - 1, window.API_URL.length) === '
|
|||
window.API_URL = window.API_URL.substr(0, window.API_URL.length - 1)
|
||||
}
|
||||
|
||||
// Register the modal
|
||||
import Modal from './components/modal/modal'
|
||||
Vue.component('modal', Modal)
|
||||
|
||||
// Add CSS
|
||||
import './styles/vikunja.scss'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// Notifications
|
||||
import Notifications from 'vue-notification'
|
||||
Vue.use(Notifications)
|
||||
|
||||
// Icons
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faSignOutAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faListOl } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faTasks } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCog } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faAngleRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faLayerGroup } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faUsers } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faUser } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faLock } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faPen } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faTachometerAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCalendar } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faBars } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faPowerOff } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCalendarWeek } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faExclamation } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faTags } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faChevronDown } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faPaste } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faTimesCircle } from '@fortawesome/free-regular-svg-icons'
|
||||
import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons'
|
||||
import { faCloudDownloadAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faPercent } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faStar as faStarSolid } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faStar } from '@fortawesome/free-regular-svg-icons'
|
||||
import { faAlignLeft } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faPaperclip } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faClock } from '@fortawesome/free-regular-svg-icons'
|
||||
import { faHistory } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCheckDouble } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faTh } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSort } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSortUp } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faList } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faFilter } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faFillDrip } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faKeyboard } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faComments } from '@fortawesome/free-regular-svg-icons'
|
||||
import { faSave } from '@fortawesome/free-regular-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
|
||||
library.add(faSignOutAlt)
|
||||
library.add(faPlus)
|
||||
library.add(faListOl)
|
||||
|
@ -124,15 +134,8 @@ library.add(faStarSolid)
|
|||
|
||||
Vue.component('icon', FontAwesomeIcon)
|
||||
|
||||
// Tooltip
|
||||
import VTooltip from 'v-tooltip'
|
||||
Vue.use(VTooltip, {defaultHtml: false})
|
||||
|
||||
// PWA
|
||||
import './registerServiceWorker'
|
||||
|
||||
// Shortcuts
|
||||
import vueShortkey from 'vue-shortkey'
|
||||
Vue.use(vueShortkey)
|
||||
|
||||
// Set focus
|
||||
|
@ -146,14 +149,9 @@ Vue.directive('focus', {
|
|||
if (window.innerWidth > 769 || (typeof modifiers.always !== 'undefined' && modifiers.always)) {
|
||||
el.focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// Mixins
|
||||
import message from './message'
|
||||
import {format, formatDistance} from 'date-fns'
|
||||
import {colorIsDark} from './helpers/colorIsDark'
|
||||
import {setTitle} from './helpers/setTitle'
|
||||
Vue.mixin({
|
||||
methods: {
|
||||
formatDateSince: date => {
|
||||
|
@ -161,7 +159,7 @@ Vue.mixin({
|
|||
date = new Date(date)
|
||||
}
|
||||
const currentDate = new Date()
|
||||
let formatted = '';
|
||||
let formatted = ''
|
||||
if (date > currentDate) {
|
||||
formatted += 'in '
|
||||
}
|
||||
|
@ -170,7 +168,7 @@ Vue.mixin({
|
|||
formatted += ' ago'
|
||||
}
|
||||
|
||||
return formatted;
|
||||
return formatted
|
||||
},
|
||||
formatDate: date => {
|
||||
if (typeof date === 'string') {
|
||||
|
@ -182,14 +180,11 @@ Vue.mixin({
|
|||
success: (s, context, actions = []) => message.success(s, context, actions),
|
||||
colorIsDark: colorIsDark,
|
||||
setTitle: setTitle,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// Vuex
|
||||
import {store} from './store'
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
render: h => h(App),
|
||||
}).$mount('#app')
|
||||
|
|
|
@ -2,11 +2,11 @@ export default {
|
|||
setLoading(context) {
|
||||
const timeout = setTimeout(function () {
|
||||
context.loading = true
|
||||
}, 100);
|
||||
}, 100)
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
context.loading = false
|
||||
};
|
||||
}
|
||||
},
|
||||
error(e, context, actions = []) {
|
||||
// Build the notification text from error response
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {defaults, omitBy, isNil} from 'lodash'
|
||||
import {objectToCamelCase} from '../helpers/case'
|
||||
import {defaults, isNil, omitBy} from 'lodash'
|
||||
import {objectToCamelCase} from '@/helpers/case'
|
||||
|
||||
export default class AbstractModel {
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import AbstractModel from './abstractModel'
|
||||
import UserModel from './user'
|
||||
import {colorIsDark} from '../helpers/colorIsDark'
|
||||
import {colorIsDark} from '@/helpers/colorIsDark'
|
||||
|
||||
export default class LabelModel extends AbstractModel {
|
||||
constructor(data) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import AbstractModel from "./abstractModel";
|
||||
import AbstractModel from './abstractModel'
|
||||
|
||||
export default class LabelTask extends AbstractModel {
|
||||
defaults() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import AbstractModel from "./abstractModel";
|
||||
import AbstractModel from './abstractModel'
|
||||
|
||||
export default class PasswordResetModel extends AbstractModel {
|
||||
constructor(data) {
|
||||
|
|
|
@ -7,7 +7,7 @@ export default class TeamListModel extends TeamShareBaseModel {
|
|||
super.defaults(),
|
||||
{
|
||||
listId: 0,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ export default class TeamMemberModel extends UserModel {
|
|||
{
|
||||
admin: false,
|
||||
teamId: 0,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ export default class TeamNamespaceModel extends TeamShareBaseModel {
|
|||
super.defaults(),
|
||||
{
|
||||
namespaceId: 0,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ export default class TeamShareBaseModel extends AbstractModel {
|
|||
right: 0,
|
||||
|
||||
created: null,
|
||||
updated: null
|
||||
updated: null,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ export default class UserListModel extends UserShareBaseModel {
|
|||
super.defaults(),
|
||||
{
|
||||
listId: 0,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import UserShareBaseModel from "./userShareBase";
|
||||
import UserShareBaseModel from './userShareBase'
|
||||
import {merge} from 'lodash'
|
||||
|
||||
// This class extends the user share model with a 'rights' parameter which is used in sharing
|
||||
|
@ -8,7 +8,7 @@ export default class UserNamespaceModel extends UserShareBaseModel {
|
|||
super.defaults(),
|
||||
{
|
||||
namespaceId: 0,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ if (process.env.NODE_ENV === 'production') {
|
|||
console.log('New content is available; please refresh.')
|
||||
// Send an event with the updated info
|
||||
document.dispatchEvent(
|
||||
new CustomEvent(swEvents.SW_UPDATED, { detail:registration })
|
||||
new CustomEvent(swEvents.SW_UPDATED, {detail: registration}),
|
||||
)
|
||||
},
|
||||
offline() {
|
||||
|
@ -29,7 +29,7 @@ if (process.env.NODE_ENV === 'production') {
|
|||
},
|
||||
error(error) {
|
||||
console.error('Error during service worker registration:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -37,21 +37,21 @@ if(navigator && navigator.serviceWorker) {
|
|||
navigator.serviceWorker.addEventListener('message', event => {
|
||||
// for every message we expect an action field
|
||||
// determining operation that we should perform
|
||||
const { action } = event.data;
|
||||
const {action} = event.data
|
||||
// we use 2nd port provided by the message channel
|
||||
const port = event.ports[0];
|
||||
const port = event.ports[0]
|
||||
|
||||
if (action === 'getBearerToken') {
|
||||
console.debug('Token request from sw');
|
||||
console.debug('Token request from sw')
|
||||
port.postMessage({
|
||||
authToken: localStorage.getItem('token'),
|
||||
})
|
||||
} else {
|
||||
console.error('Unknown event', event);
|
||||
console.error('Unknown event', event)
|
||||
port.postMessage({
|
||||
error: 'Unknown request',
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,25 @@ import ErrorComponent from '../components/misc/error'
|
|||
// User Handling
|
||||
import LoginComponent from '../views/user/Login'
|
||||
import RegisterComponent from '../views/user/Register'
|
||||
// Tasks
|
||||
import ShowTasksInRangeComponent from '../views/tasks/ShowTasksInRange'
|
||||
import LinkShareAuthComponent from '../views/sharing/LinkSharingAuth'
|
||||
import TaskDetailViewModal from '../views/tasks/TaskDetailViewModal'
|
||||
import TaskDetailView from '../views/tasks/TaskDetailView'
|
||||
import ListNamespaces from '../views/namespaces/ListNamespaces'
|
||||
// Team Handling
|
||||
import ListTeamsComponent from '../views/teams/ListTeams'
|
||||
// Label Handling
|
||||
import ListLabelsComponent from '../views/labels/ListLabels'
|
||||
// Migration
|
||||
import MigrationComponent from '../views/migrator/Migrate'
|
||||
import MigrateServiceComponent from '../views/migrator/MigrateService'
|
||||
// List Views
|
||||
import ShowListComponent from '../views/list/ShowList'
|
||||
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'
|
||||
|
||||
const PasswordResetComponent = () => ({
|
||||
component: import(/* webpackPrefetch: true *//* webpackChunkName: "user-settings" */'../views/user/PasswordReset'),
|
||||
|
@ -40,11 +59,6 @@ const EditListComponent = () => ({
|
|||
error: ErrorComponent,
|
||||
timeout: 60000,
|
||||
})
|
||||
// Tasks
|
||||
import ShowTasksInRangeComponent from '../views/tasks/ShowTasksInRange'
|
||||
import LinkShareAuthComponent from '../views/sharing/LinkSharingAuth'
|
||||
import TaskDetailViewModal from '../views/tasks/TaskDetailViewModal'
|
||||
import TaskDetailView from '../views/tasks/TaskDetailView'
|
||||
// Namespace Handling
|
||||
const NewNamespaceComponent = () => ({
|
||||
component: import(/* webpackPrefetch: true *//* webpackChunkName: "settings" */'../views/namespaces/NewNamespace'),
|
||||
|
@ -58,9 +72,6 @@ const EditNamespaceComponent = () => ({
|
|||
error: ErrorComponent,
|
||||
timeout: 60000,
|
||||
})
|
||||
import ListNamespaces from '../views/namespaces/ListNamespaces'
|
||||
// Team Handling
|
||||
import ListTeamsComponent from '../views/teams/ListTeams'
|
||||
|
||||
const EditTeamComponent = () => ({
|
||||
component: import(/* webpackPrefetch: true *//* webpackChunkName: "settings" */'../views/teams/EditTeam'),
|
||||
|
@ -74,17 +85,6 @@ const NewTeamComponent = () => ({
|
|||
error: ErrorComponent,
|
||||
timeout: 60000,
|
||||
})
|
||||
// Label Handling
|
||||
import ListLabelsComponent from '../views/labels/ListLabels'
|
||||
// Migration
|
||||
import MigrationComponent from '../views/migrator/Migrate'
|
||||
import MigrateServiceComponent from '../views/migrator/MigrateService'
|
||||
// List Views
|
||||
import ShowListComponent from '../views/list/ShowList'
|
||||
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'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
|
@ -99,7 +99,7 @@ export default new Router({
|
|||
// Scroll to anchor should still work
|
||||
if (to.hash) {
|
||||
return {
|
||||
selector: to.hash
|
||||
selector: to.hash,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ export default new Router({
|
|||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeComponent
|
||||
component: HomeComponent,
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
|
@ -120,22 +120,22 @@ export default new Router({
|
|||
{
|
||||
path: '/login',
|
||||
name: 'user.login',
|
||||
component: LoginComponent
|
||||
component: LoginComponent,
|
||||
},
|
||||
{
|
||||
path: '/get-password-reset',
|
||||
name: 'user.password-reset.request',
|
||||
component: GetPasswordResetComponent
|
||||
component: GetPasswordResetComponent,
|
||||
},
|
||||
{
|
||||
path: '/password-reset',
|
||||
name: 'user.password-reset.reset',
|
||||
component: PasswordResetComponent
|
||||
component: PasswordResetComponent,
|
||||
},
|
||||
{
|
||||
path: '/register',
|
||||
name: 'user.register',
|
||||
component: RegisterComponent
|
||||
component: RegisterComponent,
|
||||
},
|
||||
{
|
||||
path: '/user/settings',
|
||||
|
@ -145,7 +145,7 @@ export default new Router({
|
|||
{
|
||||
path: '/share/:share/auth',
|
||||
name: 'link-share.auth',
|
||||
component: LinkShareAuthComponent
|
||||
component: LinkShareAuthComponent,
|
||||
},
|
||||
{
|
||||
path: '/namespaces',
|
||||
|
@ -155,22 +155,22 @@ export default new Router({
|
|||
{
|
||||
path: '/namespaces/new',
|
||||
name: 'namespace.create',
|
||||
component: NewNamespaceComponent
|
||||
component: NewNamespaceComponent,
|
||||
},
|
||||
{
|
||||
path: '/namespaces/:id/edit',
|
||||
name: 'namespace.edit',
|
||||
component: EditNamespaceComponent
|
||||
component: EditNamespaceComponent,
|
||||
},
|
||||
{
|
||||
path: '/namespaces/:id/list',
|
||||
name: 'list.create',
|
||||
component: NewListComponent
|
||||
component: NewListComponent,
|
||||
},
|
||||
{
|
||||
path: '/lists/:id/edit',
|
||||
name: 'list.edit',
|
||||
component: EditListComponent
|
||||
component: EditListComponent,
|
||||
},
|
||||
{
|
||||
path: '/tasks/:id',
|
||||
|
@ -228,27 +228,27 @@ export default new Router({
|
|||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/teams',
|
||||
name: 'teams.index',
|
||||
component: ListTeamsComponent
|
||||
component: ListTeamsComponent,
|
||||
},
|
||||
{
|
||||
path: '/teams/new',
|
||||
name: 'teams.create',
|
||||
component: NewTeamComponent
|
||||
component: NewTeamComponent,
|
||||
},
|
||||
{
|
||||
path: '/teams/:id/edit',
|
||||
name: 'teams.edit',
|
||||
component: EditTeamComponent
|
||||
component: EditTeamComponent,
|
||||
},
|
||||
{
|
||||
path: '/labels',
|
||||
name: 'labels.index',
|
||||
component: ListLabelsComponent
|
||||
component: ListLabelsComponent,
|
||||
},
|
||||
{
|
||||
path: '/migrate',
|
||||
|
@ -260,5 +260,5 @@ export default new Router({
|
|||
name: 'migrate.service',
|
||||
component: MigrateServiceComponent,
|
||||
},
|
||||
]
|
||||
],
|
||||
})
|
|
@ -1,6 +1,6 @@
|
|||
import axios from 'axios'
|
||||
import {reduce, replace} from 'lodash'
|
||||
import {objectToSnakeCase} from '../helpers/case'
|
||||
import {objectToSnakeCase} from '@/helpers/case'
|
||||
|
||||
export default class AbstractService {
|
||||
|
||||
|
|
|
@ -38,17 +38,17 @@ export default class AttachmentService extends AbstractService {
|
|||
method: 'GET',
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
return window.URL.createObjectURL(new Blob([response.data]));
|
||||
return window.URL.createObjectURL(new Blob([response.data]))
|
||||
})
|
||||
}
|
||||
|
||||
download(model) {
|
||||
this.getBlobUrl(model).then(url => {
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute('download', model.file.name);
|
||||
link.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', model.file.name)
|
||||
link.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -62,12 +62,12 @@ export default class AttachmentService extends AbstractService {
|
|||
const data = new FormData()
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
// TODO: Validation of file size
|
||||
data.append('files', new Blob([files[i]]), files[i].name);
|
||||
data.append('files', new Blob([files[i]]), files[i].name)
|
||||
}
|
||||
|
||||
return this.uploadFormData(
|
||||
this.getReplacedRoute(this.paths.create, model),
|
||||
data
|
||||
data,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ export default class BackgroundUploadService extends AbstractService {
|
|||
return this.uploadFile(
|
||||
this.getReplacedRoute(this.paths.create, {listId: listId}),
|
||||
file,
|
||||
'background'
|
||||
'background',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import AbstractService from "./abstractService";
|
||||
import LabelTask from "../models/labelTask";
|
||||
import AbstractService from './abstractService'
|
||||
import LabelTask from '../models/labelTask'
|
||||
|
||||
export default class LabelTaskService extends AbstractService {
|
||||
constructor() {
|
||||
|
|
|
@ -5,7 +5,7 @@ import {formatISO} from 'date-fns'
|
|||
export default class ListUserService extends AbstractService {
|
||||
constructor() {
|
||||
super({
|
||||
getAll: '/lists/{listId}/listusers'
|
||||
getAll: '/lists/{listId}/listusers',
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ export default class NamespaceService extends AbstractService {
|
|||
getAll: '/namespaces',
|
||||
update: '/namespaces/{id}',
|
||||
delete: '/namespaces/{id}',
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
processModel(model) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import AbstractService from './abstractService'
|
|||
export default class PasswordUpdateService extends AbstractService {
|
||||
constructor() {
|
||||
super({
|
||||
update: '/user/password'
|
||||
update: '/user/password',
|
||||
})
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ export default class TaskService extends AbstractService {
|
|||
get: '/tasks/{id}',
|
||||
update: '/tasks/{id}',
|
||||
delete: '/tasks/{id}',
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
modelFactory(data) {
|
||||
|
|
|
@ -10,7 +10,7 @@ export default class TeamService extends AbstractService {
|
|||
getAll: '/teams',
|
||||
update: '/teams/{id}',
|
||||
delete: '/teams/{id}',
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
processModel(model) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import AbstractService from './abstractService'
|
||||
import TotpModel from "../models/totp";
|
||||
import TotpModel from '../models/totp'
|
||||
|
||||
export default class TotpService extends AbstractService {
|
||||
urlPrefix = '/user/settings/totp'
|
||||
|
|
|
@ -5,7 +5,7 @@ import {formatISO} from 'date-fns'
|
|||
export default class UserService extends AbstractService {
|
||||
constructor() {
|
||||
super({
|
||||
getAll: '/users'
|
||||
getAll: '/users',
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
import {CURRENT_LIST, ERROR_MESSAGE, HAS_TASKS, IS_FULLPAGE, LOADING, ONLINE} from './mutation-types'
|
||||
import config from './modules/config'
|
||||
import auth from './modules/auth'
|
||||
|
@ -13,7 +10,9 @@ import lists from './modules/lists'
|
|||
import attachments from './modules/attachments'
|
||||
|
||||
import ListService from '../services/list'
|
||||
import {setTitle} from '../helpers/setTitle'
|
||||
import {setTitle} from '@/helpers/setTitle'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export const store = new Vuex.Store({
|
||||
modules: {
|
||||
|
@ -97,6 +96,6 @@ export const store = new Vuex.Store({
|
|||
},
|
||||
[HAS_TASKS](state, hasTasks) {
|
||||
state.hasTasks = hasTasks
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
|
@ -1,6 +1,6 @@
|
|||
import {HTTP} from '../../http-common'
|
||||
import {ERROR_MESSAGE, LOADING} from "../mutation-types";
|
||||
import UserModel from "../../models/user";
|
||||
import {HTTP} from '@/http-common'
|
||||
import {ERROR_MESSAGE, LOADING} from '../mutation-types'
|
||||
import UserModel from '../../models/user'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
|
@ -39,7 +39,7 @@ export default {
|
|||
|
||||
const data = {
|
||||
username: credentials.username,
|
||||
password: credentials.password
|
||||
password: credentials.password,
|
||||
}
|
||||
|
||||
if (credentials.totpPasscode) {
|
||||
|
@ -81,7 +81,7 @@ export default {
|
|||
return HTTP.post('register', {
|
||||
username: credentials.username,
|
||||
email: credentials.email,
|
||||
password: credentials.password
|
||||
password: credentials.password,
|
||||
})
|
||||
.then(() => {
|
||||
return ctx.dispatch('login', credentials)
|
||||
|
@ -135,7 +135,7 @@ export default {
|
|||
HTTP.post('user/token', null, {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + localStorage.getItem('token'),
|
||||
}
|
||||
},
|
||||
})
|
||||
.then(r => {
|
||||
localStorage.setItem('token', r.data.token)
|
||||
|
@ -149,6 +149,6 @@ export default {
|
|||
logout(ctx) {
|
||||
localStorage.removeItem('token')
|
||||
ctx.dispatch('checkAuth')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import {CONFIG} from '../mutation-types'
|
||||
import {HTTP} from '../../http-common'
|
||||
import {HTTP} from '@/http-common'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
|
@ -18,7 +18,7 @@ export default {
|
|||
legal: {
|
||||
imprintUrl: '',
|
||||
privacyPolicyUrl: '',
|
||||
}
|
||||
},
|
||||
}),
|
||||
mutations: {
|
||||
[CONFIG](state, config) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Vue from 'vue'
|
||||
|
||||
import BucketService from '../../services/bucket'
|
||||
import {filterObject} from '../../helpers/filterObject'
|
||||
import {filterObject} from '@/helpers/filterObject'
|
||||
import {setLoading} from '../helper'
|
||||
|
||||
/**
|
||||
|
@ -90,7 +90,7 @@ export default {
|
|||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
getTaskById: state => id => {
|
||||
|
|
|
@ -25,10 +25,12 @@ fieldset[disabled] .multiselect {
|
|||
border-width: 2px;
|
||||
box-shadow: 0 0 0 1px transparent;
|
||||
}
|
||||
|
||||
&:before {
|
||||
animation: spinning 2.4s cubic-bezier(0.41, 0.26, 0.2, 0.62);
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
&:after {
|
||||
animation: spinning 2.4s cubic-bezier(0.51, 0.09, 0.21, 0.8);
|
||||
animation-iteration-count: infinite;
|
||||
|
@ -58,9 +60,11 @@ fieldset[disabled] .multiselect {
|
|||
min-height: 40px;
|
||||
text-align: left;
|
||||
color: $text;
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
@ -73,12 +77,14 @@ fieldset[disabled] .multiselect {
|
|||
|
||||
.multiselect--active {
|
||||
z-index: 50;
|
||||
|
||||
&:not(.multiselect--above) {
|
||||
.multiselect__current, .multiselect__input, .multiselect__tags {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.multiselect__select {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
|
@ -131,6 +137,7 @@ fieldset[disabled] .multiselect {
|
|||
border-color: $primary;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
padding-left: 5px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
@ -184,14 +191,17 @@ fieldset[disabled] .multiselect {
|
|||
line-height: 22px;
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 5px;
|
||||
|
||||
&:after {
|
||||
content: "×";
|
||||
color: darken($multiselect-highlight, 20);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&:focus, &:hover {
|
||||
background: lighten($multiselect-highlight, 10);
|
||||
}
|
||||
|
||||
&:focus:after, &:hover:after {
|
||||
color: $white;
|
||||
}
|
||||
|
@ -227,6 +237,7 @@ fieldset[disabled] .multiselect {
|
|||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&:before {
|
||||
position: relative;
|
||||
right: 0;
|
||||
|
@ -305,6 +316,7 @@ fieldset[disabled] .multiselect {
|
|||
position: relative;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&:after {
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
@ -320,6 +332,7 @@ fieldset[disabled] .multiselect {
|
|||
background: $multiselect-highlight;
|
||||
outline: none;
|
||||
color: $white;
|
||||
|
||||
&:after {
|
||||
content: attr(data-select);
|
||||
background: $multiselect-highlight;
|
||||
|
@ -331,13 +344,16 @@ fieldset[disabled] .multiselect {
|
|||
background: darken($white, 10);
|
||||
color: $multiselect-dark;
|
||||
font-weight: bold;
|
||||
|
||||
&:after {
|
||||
content: attr(data-selected);
|
||||
color: silver;
|
||||
}
|
||||
|
||||
&.multiselect__option--highlight {
|
||||
background: $multiselect-highlight-negative;
|
||||
color: $white;
|
||||
|
||||
&:after {
|
||||
background: $multiselect-highlight-negative;
|
||||
content: attr(data-deselect);
|
||||
|
@ -348,6 +364,7 @@ fieldset[disabled] .multiselect {
|
|||
|
||||
.multiselect--disabled {
|
||||
pointer-events: none;
|
||||
|
||||
.multiselect__current, .multiselect__select {
|
||||
background: $multiselect-disabled;
|
||||
color: darken($multiselect-disabled, 40);
|
||||
|
@ -364,9 +381,11 @@ fieldset[disabled] .multiselect {
|
|||
.multiselect__option--group {
|
||||
background: $multiselect-disabled;
|
||||
color: $multiselect-dark;
|
||||
|
||||
&.multiselect__option--highlight {
|
||||
background: $multiselect-dark;
|
||||
color: $white;
|
||||
|
||||
&:after {
|
||||
background: $multiselect-dark;
|
||||
}
|
||||
|
@ -380,6 +399,7 @@ fieldset[disabled] .multiselect {
|
|||
.multiselect__option--group-selected.multiselect__option--highlight {
|
||||
background: $multiselect-highlight-negative;
|
||||
color: $white;
|
||||
|
||||
&:after {
|
||||
background: $multiselect-highlight-negative;
|
||||
content: attr(data-deselect);
|
||||
|
@ -406,24 +426,30 @@ fieldset[disabled] .multiselect {
|
|||
.multiselect {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.multiselect__select {
|
||||
right: auto;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.multiselect__tags {
|
||||
padding: 8px 8px 0px 40px;
|
||||
}
|
||||
|
||||
.multiselect__content {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.multiselect__option:after {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.multiselect__clear {
|
||||
right: auto;
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.multiselect__spinner {
|
||||
right: auto;
|
||||
left: 1px;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
// Chrome
|
||||
::-webkit-scrollbar {
|
||||
width: $scrollbar-height;
|
||||
|
|
|
@ -47,14 +47,37 @@
|
|||
animation: wave 2s ease infinite;
|
||||
margin-right: 5px;
|
||||
|
||||
&:nth-child(1){ animation-delay: 0; }
|
||||
&:nth-child(2){ animation-delay: 100ms; }
|
||||
&:nth-child(3){ animation-delay: 200ms; }
|
||||
&:nth-child(4){ animation-delay: 300ms; }
|
||||
&:nth-child(5){ animation-delay: 400ms; }
|
||||
&:nth-child(6){ animation-delay: 500ms; }
|
||||
&:nth-child(7){ animation-delay: 600ms; }
|
||||
&:nth-child(8){ animation-delay: 700ms; }
|
||||
&:nth-child(1) {
|
||||
animation-delay: 0;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
animation-delay: 100ms;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
animation-delay: 200ms;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
animation-delay: 300ms;
|
||||
}
|
||||
|
||||
&:nth-child(5) {
|
||||
animation-delay: 400ms;
|
||||
}
|
||||
|
||||
&:nth-child(6) {
|
||||
animation-delay: 500ms;
|
||||
}
|
||||
|
||||
&:nth-child(7) {
|
||||
animation-delay: 600ms;
|
||||
}
|
||||
|
||||
&:nth-child(8) {
|
||||
animation-delay: 700ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
position: relative;
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
|
||||
&:after {
|
||||
@include loader;
|
||||
position: absolute;
|
||||
|
@ -18,6 +19,7 @@
|
|||
.spinner {
|
||||
&.is-loading {
|
||||
pointer-events: none;
|
||||
|
||||
&:after {
|
||||
@include loader;
|
||||
width: 2em;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
@-webkit-keyframes bounce {
|
||||
from,
|
||||
20%,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity $transition-duration;
|
||||
}
|
||||
|
||||
.fade-enter, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
@ -8,6 +9,7 @@
|
|||
.width-enter-active, .width-leave-active {
|
||||
transition: width $transition-duration;
|
||||
}
|
||||
|
||||
.width-enter, .width-leave-to {
|
||||
width: 0;
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"VERSION": "0.12"
|
||||
"VERSION": "dev"
|
||||
}
|
|
@ -4,8 +4,8 @@
|
|||
<template v-if="!hasTasks">
|
||||
<p>Click on a list or namespace on the left to get started.</p>
|
||||
<router-link
|
||||
class="button is-primary is-right noshadow is-outlined"
|
||||
:to="{name: 'migrate.start'}"
|
||||
class="button is-primary is-right noshadow is-outlined"
|
||||
v-if="migratorsEnabled"
|
||||
>
|
||||
Import your data into Vikunja
|
||||
|
@ -28,7 +28,7 @@
|
|||
return {
|
||||
loading: false,
|
||||
currentDate: new Date(),
|
||||
tasks: []
|
||||
tasks: [],
|
||||
}
|
||||
},
|
||||
computed: mapState({
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="loader-container content" :class="{ 'is-loading': labelService.loading}">
|
||||
<div :class="{ 'is-loading': labelService.loading}" class="loader-container content">
|
||||
<h1>Manage labels</h1>
|
||||
<p>
|
||||
Click on a label to edit it.
|
||||
|
@ -9,10 +9,10 @@
|
|||
<div class="columns">
|
||||
<div class="labels-list column">
|
||||
<span
|
||||
v-for="l in labels" :key="l.id"
|
||||
class="tag"
|
||||
:class="{'disabled': userInfo.id !== l.createdBy.id}"
|
||||
:class="{'disabled': userInfo.id !== l.createdBy.id}" :key="l.id"
|
||||
:style="{'background': l.hexColor, 'color': l.textColor}"
|
||||
class="tag"
|
||||
v-for="l in labels"
|
||||
>
|
||||
<span
|
||||
v-if="userInfo.id !== l.createdBy.id"
|
||||
|
@ -20,12 +20,12 @@
|
|||
{{ l.title }}
|
||||
</span>
|
||||
<a
|
||||
@click="editLabel(l)"
|
||||
:style="{'color': l.textColor}"
|
||||
@click="editLabel(l)"
|
||||
v-else>
|
||||
{{ l.title }}
|
||||
</a>
|
||||
<a class="delete is-small" @click="deleteLabel(l)" v-if="userInfo.id === l.createdBy.id"></a>
|
||||
<a @click="deleteLabel(l)" class="delete is-small" v-if="userInfo.id === l.createdBy.id"></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="column is-4" v-if="isLabelEdit">
|
||||
|
@ -34,7 +34,7 @@
|
|||
<span class="card-header-title">
|
||||
Edit Label
|
||||
</span>
|
||||
<a class="card-header-icon" @click="isLabelEdit = false">
|
||||
<a @click="isLabelEdit = false" class="card-header-icon">
|
||||
<span class="icon">
|
||||
<icon icon="times"/>
|
||||
</span>
|
||||
|
@ -47,8 +47,8 @@
|
|||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Label title"
|
||||
type="text"
|
||||
v-model="labelEditLabel.title"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -56,10 +56,10 @@
|
|||
<label class="label">Description</label>
|
||||
<div class="control">
|
||||
<editor
|
||||
placeholder="Label description"
|
||||
v-model="labelEditLabel.description"
|
||||
:preview-is-default="false"
|
||||
placeholder="Label description"
|
||||
v-if="editorActive"
|
||||
v-model="labelEditLabel.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -71,15 +71,15 @@
|
|||
</div>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<button type="submit" class="button is-fullwidth is-primary"
|
||||
:class="{ 'is-loading': labelService.loading}">
|
||||
<button :class="{ 'is-loading': labelService.loading}" class="button is-fullwidth is-primary"
|
||||
type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a
|
||||
class="button has-icon is-danger"
|
||||
@click="() => {deleteLabel(labelEditLabel);isLabelEdit = false}">
|
||||
@click="() => {deleteLabel(labelEditLabel);isLabelEdit = false}"
|
||||
class="button has-icon is-danger">
|
||||
<span class="icon">
|
||||
<icon icon="trash-alt"/>
|
||||
</span>
|
||||
|
@ -132,7 +132,7 @@
|
|||
this.setTitle('Labels')
|
||||
},
|
||||
computed: mapState({
|
||||
userInfo: state => state.auth.info
|
||||
userInfo: state => state.auth.info,
|
||||
}),
|
||||
methods: {
|
||||
loadLabels() {
|
||||
|
@ -204,7 +204,7 @@
|
|||
// See https://github.com/NikulinIlya/vue-easymde/issues/3
|
||||
this.editorActive = false
|
||||
this.$nextTick(() => this.editorActive = true)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="loader-container edit-list is-max-width-desktop" :class="{ 'is-loading': listService.loading}">
|
||||
<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.
|
||||
|
@ -17,14 +17,14 @@
|
|||
<label class="label" for="listtext">List Name</label>
|
||||
<div class="control">
|
||||
<input
|
||||
v-focus
|
||||
:class="{ 'disabled': listService.loading}"
|
||||
:disabled="listService.loading"
|
||||
@keyup.enter="submit"
|
||||
class="input"
|
||||
type="text"
|
||||
id="listtext"
|
||||
placeholder="The list title goes here..."
|
||||
@keyup.enter="submit"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="list.title"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -37,14 +37,14 @@
|
|||
</label>
|
||||
<div class="control">
|
||||
<input
|
||||
v-focus
|
||||
:class="{ 'disabled': listService.loading}"
|
||||
:disabled="listService.loading"
|
||||
@keyup.enter="submit"
|
||||
class="input"
|
||||
type="text"
|
||||
id="listtext"
|
||||
placeholder="The list identifier goes here..."
|
||||
@keyup.enter="submit"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="list.identifier"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -54,10 +54,10 @@
|
|||
<editor
|
||||
:class="{ 'disabled': listService.loading}"
|
||||
:disabled="listService.loading"
|
||||
placeholder="The lists description goes here..."
|
||||
id="listdescription"
|
||||
v-model="list.description"
|
||||
:preview-is-default="false"
|
||||
id="listdescription"
|
||||
placeholder="The lists description goes here..."
|
||||
v-model="list.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -81,14 +81,14 @@
|
|||
|
||||
<div class="columns bigbuttons">
|
||||
<div class="column">
|
||||
<button @click="submit()" class="button is-primary is-fullwidth"
|
||||
:class="{ 'is-loading': listService.loading}">
|
||||
<button :class="{ 'is-loading': listService.loading}" @click="submit()"
|
||||
class="button is-primary is-fullwidth">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-1">
|
||||
<button @click="showDeleteModal = true" class="button is-danger is-fullwidth"
|
||||
:class="{ 'is-loading': listService.loading}">
|
||||
<button :class="{ 'is-loading': listService.loading}" @click="showDeleteModal = true"
|
||||
class="button is-danger is-fullwidth">
|
||||
<span class="icon is-small">
|
||||
<icon icon="trash-alt"/>
|
||||
</span>
|
||||
|
@ -114,10 +114,10 @@
|
|||
</p>
|
||||
<p class="control">
|
||||
<button
|
||||
type="submit"
|
||||
class="button is-success"
|
||||
:class="{'is-loading': listDuplicateService.loading}"
|
||||
@click="duplicateList"
|
||||
:class="{'is-loading': listDuplicateService.loading}">
|
||||
class="button is-success"
|
||||
type="submit">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
|
@ -132,24 +132,24 @@
|
|||
<background :list-id="$route.params.id"/>
|
||||
|
||||
<component
|
||||
:id="list.id"
|
||||
:is="manageUsersComponent"
|
||||
:id="list.id"
|
||||
type="list"
|
||||
:userIsAdmin="userIsAdmin"
|
||||
shareType="user"
|
||||
:userIsAdmin="userIsAdmin"/>
|
||||
type="list"/>
|
||||
<component
|
||||
:is="manageTeamsComponent"
|
||||
:id="list.id"
|
||||
type="list"
|
||||
:is="manageTeamsComponent"
|
||||
:userIsAdmin="userIsAdmin"
|
||||
shareType="team"
|
||||
:userIsAdmin="userIsAdmin"/>
|
||||
type="list"/>
|
||||
|
||||
<link-sharing :list-id="$route.params.id" v-if="linkSharingEnabled"/>
|
||||
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
@submit="deleteList()">
|
||||
@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>
|
||||
|
@ -166,7 +166,7 @@
|
|||
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 {CURRENT_LIST} from '@/store/mutation-types'
|
||||
import ColorPicker from '../../components/input/colorPicker'
|
||||
import NamespaceSearch from '../../components/namespace/namespace-search'
|
||||
import ListDuplicateService from '../../services/listDuplicateService'
|
||||
|
@ -211,7 +211,7 @@
|
|||
},
|
||||
watch: {
|
||||
// call again the method if the route changes
|
||||
'$route': 'loadList'
|
||||
'$route': 'loadList',
|
||||
},
|
||||
computed: {
|
||||
linkSharingEnabled() {
|
||||
|
@ -275,6 +275,6 @@
|
|||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
<template>
|
||||
<div class="fullpage">
|
||||
<a class="close" @click="back()">
|
||||
<a @click="back()" class="close">
|
||||
<icon :icon="['far', 'times-circle']">
|
||||
</icon>
|
||||
</a>
|
||||
<h3>Create a new list</h3>
|
||||
<div class="field is-grouped">
|
||||
<p class="control is-expanded" :class="{ 'is-loading': listService.loading}">
|
||||
<input v-focus
|
||||
class="input"
|
||||
<p :class="{ 'is-loading': listService.loading}" class="control is-expanded">
|
||||
<input
|
||||
:class="{ 'disabled': listService.loading}"
|
||||
v-model="list.title"
|
||||
type="text"
|
||||
placeholder="The list's name goes here..."
|
||||
@keyup.enter="newList()"
|
||||
@keyup.esc="back()"
|
||||
@keyup.enter="newList()"/>
|
||||
class="input"
|
||||
placeholder="The list's name goes here..."
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="list.title"/>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-success noshadow" @click="newList()" :disabled="list.title === ''">
|
||||
<button :disabled="list.title === ''" @click="newList()" class="button is-success noshadow">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
|
@ -35,10 +36,10 @@
|
|||
import router from '../../router'
|
||||
import ListService from '../../services/list'
|
||||
import ListModel from '../../models/list'
|
||||
import {IS_FULLPAGE} from '../../store/mutation-types'
|
||||
import {IS_FULLPAGE} from '@/store/mutation-types'
|
||||
|
||||
export default {
|
||||
name: "NewList",
|
||||
name: 'NewList',
|
||||
data() {
|
||||
return {
|
||||
showError: false,
|
||||
|
@ -77,6 +78,6 @@
|
|||
back() {
|
||||
router.go(-1)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,27 +1,27 @@
|
|||
<template>
|
||||
<div
|
||||
class="loader-container"
|
||||
:class="{ 'is-loading': listService.loading}"
|
||||
class="loader-container"
|
||||
>
|
||||
<div class="switch-view">
|
||||
<router-link
|
||||
:to="{ name: 'list.list', params: { listId: listId } }"
|
||||
:class="{'is-active': $route.name === 'list.list'}">
|
||||
:class="{'is-active': $route.name === 'list.list'}"
|
||||
:to="{ name: 'list.list', params: { listId: listId } }">
|
||||
List
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{ name: 'list.gantt', params: { listId: listId } }"
|
||||
:class="{'is-active': $route.name === 'list.gantt'}">
|
||||
:class="{'is-active': $route.name === 'list.gantt'}"
|
||||
:to="{ name: 'list.gantt', params: { listId: listId } }">
|
||||
Gantt
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{ name: 'list.table', params: { listId: listId } }"
|
||||
:class="{'is-active': $route.name === 'list.table'}">
|
||||
:class="{'is-active': $route.name === 'list.table'}"
|
||||
:to="{ name: 'list.table', params: { listId: listId } }">
|
||||
Table
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{ name: 'list.kanban', params: { listId: listId } }"
|
||||
:class="{'is-active': $route.name === 'list.kanban'}">
|
||||
:class="{'is-active': $route.name === 'list.kanban'}"
|
||||
:to="{ name: 'list.kanban', params: { listId: listId } }">
|
||||
Kanban
|
||||
</router-link>
|
||||
</div>
|
||||
|
@ -39,8 +39,8 @@
|
|||
|
||||
import ListModel from '../../models/list'
|
||||
import ListService from '../../services/list'
|
||||
import {CURRENT_LIST} from '../../store/mutation-types'
|
||||
import {getListView} from '../../helpers/saveListView'
|
||||
import {CURRENT_LIST} from '@/store/mutation-types'
|
||||
import {getListView} from '@/helpers/saveListView'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -70,7 +70,10 @@
|
|||
return this.$store.state.background
|
||||
},
|
||||
currentList() {
|
||||
return typeof this.$store.state.currentList === 'undefined' ? {id: 0, title: ''} : this.$store.state.currentList
|
||||
return typeof this.$store.state.currentList === 'undefined' ? {
|
||||
id: 0,
|
||||
title: '',
|
||||
} : this.$store.state.currentList
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -136,6 +139,6 @@
|
|||
this.listLoaded = this.$route.params.listId
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="gantt-chart-container">
|
||||
<div class="gantt-options">
|
||||
<fancycheckbox v-model="showTaskswithoutDates" class="is-block">
|
||||
<fancycheckbox class="is-block" v-model="showTaskswithoutDates">
|
||||
Show tasks which don't have dates set
|
||||
</fancycheckbox>
|
||||
<div class="range-picker">
|
||||
|
@ -21,11 +21,11 @@
|
|||
<label class="label" for="fromDate">From</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
class="input"
|
||||
v-model="dateFrom"
|
||||
:config="flatPickerConfig"
|
||||
class="input"
|
||||
id="fromDate"
|
||||
placeholder="From"
|
||||
v-model="dateFrom"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,22 +33,22 @@
|
|||
<label class="label" for="toDate">To</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
class="input"
|
||||
v-model="dateTo"
|
||||
:config="flatPickerConfig"
|
||||
class="input"
|
||||
id="toDate"
|
||||
placeholder="To"
|
||||
v-model="dateTo"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<gantt-chart
|
||||
:list-id="Number($route.params.listId)"
|
||||
:show-taskswithout-dates="showTaskswithoutDates"
|
||||
:date-from="dateFrom"
|
||||
:date-to="dateTo"
|
||||
:day-width="dayWidth"
|
||||
:list-id="Number($route.params.listId)"
|
||||
:show-taskswithout-dates="showTaskswithoutDates"
|
||||
/>
|
||||
|
||||
<!-- This router view is used to show the task popup while keeping the gantt chart itself -->
|
||||
|
@ -63,14 +63,14 @@
|
|||
import GanttChart from '../../../components/tasks/gantt-component'
|
||||
import flatPickr from 'vue-flatpickr-component'
|
||||
import Fancycheckbox from '../../../components/input/fancycheckbox'
|
||||
import {saveListView} from '../../../helpers/saveListView'
|
||||
import {saveListView} from '@/helpers/saveListView'
|
||||
|
||||
export default {
|
||||
name: 'Gantt',
|
||||
components: {
|
||||
Fancycheckbox,
|
||||
flatPickr,
|
||||
GanttChart
|
||||
GanttChart,
|
||||
},
|
||||
created() {
|
||||
// Save the current list view to local storage
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
<template>
|
||||
<div class="kanban loader-container" :class="{ 'is-loading': loading}">
|
||||
<div v-for="bucket in buckets" :key="`bucket${bucket.id}`" class="bucket">
|
||||
<div :class="{ 'is-loading': loading}" class="kanban loader-container">
|
||||
<div :key="`bucket${bucket.id}`" class="bucket" v-for="bucket in buckets">
|
||||
<div class="bucket-header">
|
||||
<h2
|
||||
:ref="`bucket${bucket.id}title`"
|
||||
@focusout="() => saveBucketTitle(bucket.id)"
|
||||
@keyup.ctrl.enter="() => saveBucketTitle(bucket.id)"
|
||||
class="title input"
|
||||
contenteditable="true"
|
||||
spellcheck="false"
|
||||
@focusout="() => saveBucketTitle(bucket.id)"
|
||||
:ref="`bucket${bucket.id}title`"
|
||||
@keyup.ctrl.enter="() => saveBucketTitle(bucket.id)">{{ bucket.title }}</h2>
|
||||
spellcheck="false">{{ bucket.title }}</h2>
|
||||
<span
|
||||
class="limit"
|
||||
:class="{'is-max': bucket.tasks.length >= bucket.limit}"
|
||||
class="limit"
|
||||
v-if="bucket.limit > 0">
|
||||
{{ bucket.tasks.length }}/{{ bucket.limit }}
|
||||
</span>
|
||||
<div
|
||||
class="dropdown is-right options"
|
||||
:class="{ 'is-active': bucketOptionsDropDownActive[bucket.id] }"
|
||||
class="dropdown is-right options"
|
||||
v-if="canWrite"
|
||||
>
|
||||
<div class="dropdown-trigger" @click.stop="toggleBucketDropdown(bucket.id)">
|
||||
<div @click.stop="toggleBucketDropdown(bucket.id)" class="dropdown-trigger">
|
||||
<span class="icon">
|
||||
<icon icon="ellipsis-v"/>
|
||||
</span>
|
||||
|
@ -28,18 +28,18 @@
|
|||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click.stop="showSetLimitInput = true"
|
||||
class="dropdown-item"
|
||||
>
|
||||
<div class="field has-addons" v-if="showSetLimitInput">
|
||||
<div class="control">
|
||||
<input
|
||||
type="number"
|
||||
@change="() => updateBucket(bucket)"
|
||||
@keyup.enter="() => updateBucket(bucket)"
|
||||
class="input"
|
||||
type="number"
|
||||
v-focus.always
|
||||
v-model="bucket.limit"
|
||||
@keyup.enter="() => updateBucket(bucket)"
|
||||
@change="() => updateBucket(bucket)"
|
||||
/>
|
||||
</div>
|
||||
<div class="control">
|
||||
|
@ -55,9 +55,9 @@
|
|||
</template>
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item has-text-danger"
|
||||
@click="() => deleteBucketModal(bucket.id)"
|
||||
: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>
|
||||
|
@ -67,28 +67,27 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tasks" :ref="`tasks-container${bucket.id}`">
|
||||
<div :ref="`tasks-container${bucket.id}`" class="tasks">
|
||||
<!-- Make the component either a div or a draggable component based on the user rights -->
|
||||
<component
|
||||
:is="canWrite ? 'Container' : 'div'"
|
||||
@drop="e => onDrop(bucket.id, e)"
|
||||
group-name="buckets"
|
||||
:get-child-payload="getTaskPayload(bucket.id)"
|
||||
:drop-placeholder="dropPlaceholderOptions"
|
||||
:animation-duration="150"
|
||||
:drop-placeholder="dropPlaceholderOptions"
|
||||
:get-child-payload="getTaskPayload(bucket.id)"
|
||||
:is="canWrite ? 'Container' : 'div'"
|
||||
:should-accept-drop="() => shouldAcceptDrop(bucket)"
|
||||
@drop="e => onDrop(bucket.id, e)"
|
||||
drag-class="ghost-task"
|
||||
drag-class-drop="ghost-task-drop"
|
||||
drag-handle-selector=".task.draggable"
|
||||
group-name="buckets"
|
||||
>
|
||||
<!-- Make the component either a div or a draggable component based on the user rights -->
|
||||
<component
|
||||
v-for="task in bucket.tasks"
|
||||
:key="`bucket${bucket.id}-task${task.id}`"
|
||||
:is="canWrite ? 'Draggable' : 'div'"
|
||||
:key="`bucket${bucket.id}-task${task.id}`"
|
||||
v-for="task in bucket.tasks"
|
||||
>
|
||||
<div
|
||||
class="task loader-container draggable"
|
||||
:class="{
|
||||
'is-loading': taskService.loading && taskUpdating[task.id],
|
||||
'draggable': !taskService.loading || !taskUpdating[task.id],
|
||||
|
@ -96,8 +95,9 @@
|
|||
}"
|
||||
:style="{'background-color': task.hexColor !== '#' && task.hexColor !== `#${task.defaultColor}` ? task.hexColor : false}"
|
||||
@click.ctrl="() => markTaskAsDone(task)"
|
||||
@click.meta="() => markTaskAsDone(task)"
|
||||
@click.exact="() => $router.push({ name: 'task.kanban.detail', params: { id: task.id } })"
|
||||
@click.meta="() => markTaskAsDone(task)"
|
||||
class="task loader-container draggable"
|
||||
>
|
||||
<span class="task-id">
|
||||
<span class="is-done" v-if="task.done">Done</span>
|
||||
|
@ -109,9 +109,9 @@
|
|||
</template>
|
||||
</span>
|
||||
<span
|
||||
v-if="task.dueDate > 0"
|
||||
class="due-date"
|
||||
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
|
||||
class="due-date"
|
||||
v-if="task.dueDate > 0"
|
||||
v-tooltip="formatDate(task.dueDate)">
|
||||
<span class="icon">
|
||||
<icon :icon="['far', 'calendar-alt']"/>
|
||||
|
@ -127,22 +127,22 @@
|
|||
<priority-label :priority="task.priority" class="priority-label"/>
|
||||
<div class="assignees" v-if="task.assignees.length > 0">
|
||||
<user
|
||||
v-for="u in task.assignees"
|
||||
:key="task.id + 'assignee' + u.id"
|
||||
:user="u"
|
||||
:show-username="false"
|
||||
:avatar-size="24"
|
||||
:key="task.id + 'assignee' + u.id"
|
||||
:show-username="false"
|
||||
:user="u"
|
||||
v-for="u in task.assignees"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="icon" v-if="task.attachments.length > 0">
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="none" rx="0" ry="0"></rect>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M19.86 8.29994C19.8823 8.27664 19.9026 8.25201 19.9207 8.22634C20.5666 7.53541 20.93 6.63567 20.93 5.68001C20.93 4.69001 20.55 3.76001 19.85 3.06001C18.45 1.66001 16.02 1.66001 14.62 3.06001L9.88002 7.80001C9.86705 7.81355 9.85481 7.82753 9.8433 7.8419L4.58 13.1C3.6 14.09 3.06 15.39 3.06 16.78C3.06 18.17 3.6 19.48 4.58 20.46C5.6 21.47 6.93 21.98 8.26 21.98C9.59 21.98 10.92 21.47 11.94 20.46L17.74 14.66C17.97 14.42 17.98 14.04 17.74 13.81C17.5 13.58 17.12 13.58 16.89 13.81L11.09 19.61C10.33 20.36 9.33 20.78 8.26 20.78C7.19 20.78 6.19 20.37 5.43 19.61C4.68 18.85 4.26 17.85 4.26 16.78C4.26 15.72 4.68 14.71 5.43 13.96L15.47 3.91996C15.4962 3.89262 15.5195 3.86346 15.54 3.83292C16.4992 2.95103 18.0927 2.98269 19.01 3.90001C19.48 4.37001 19.74 5.00001 19.74 5.67001C19.74 6.34001 19.48 6.97001 19.01 7.44001L14.27 12.18C14.2571 12.1935 14.2448 12.2075 14.2334 12.2218L8.96 17.4899C8.59 17.8699 7.93 17.8699 7.55 17.4899C7.36 17.2999 7.26 17.0399 7.26 16.7799C7.26 16.5199 7.36 16.2699 7.55 16.0699L15.47 8.14994C15.7 7.90994 15.71 7.52994 15.47 7.29994C15.23 7.06994 14.85 7.06994 14.62 7.29994L6.7 15.2199C6.29 15.6399 6.06 16.1899 6.06 16.7799C6.06 17.3699 6.29 17.9199 6.7 18.3399C7.12 18.7499 7.67 18.9799 8.26 18.9799C8.85 18.9799 9.4 18.7599 9.82 18.3399L19.86 8.29994Z"></path>
|
||||
d="M19.86 8.29994C19.8823 8.27664 19.9026 8.25201 19.9207 8.22634C20.5666 7.53541 20.93 6.63567 20.93 5.68001C20.93 4.69001 20.55 3.76001 19.85 3.06001C18.45 1.66001 16.02 1.66001 14.62 3.06001L9.88002 7.80001C9.86705 7.81355 9.85481 7.82753 9.8433 7.8419L4.58 13.1C3.6 14.09 3.06 15.39 3.06 16.78C3.06 18.17 3.6 19.48 4.58 20.46C5.6 21.47 6.93 21.98 8.26 21.98C9.59 21.98 10.92 21.47 11.94 20.46L17.74 14.66C17.97 14.42 17.98 14.04 17.74 13.81C17.5 13.58 17.12 13.58 16.89 13.81L11.09 19.61C10.33 20.36 9.33 20.78 8.26 20.78C7.19 20.78 6.19 20.37 5.43 19.61C4.68 18.85 4.26 17.85 4.26 16.78C4.26 15.72 4.68 14.71 5.43 13.96L15.47 3.91996C15.4962 3.89262 15.5195 3.86346 15.54 3.83292C16.4992 2.95103 18.0927 2.98269 19.01 3.90001C19.48 4.37001 19.74 5.00001 19.74 5.67001C19.74 6.34001 19.48 6.97001 19.01 7.44001L14.27 12.18C14.2571 12.1935 14.2448 12.2075 14.2334 12.2218L8.96 17.4899C8.59 17.8699 7.93 17.8699 7.55 17.4899C7.36 17.2999 7.26 17.0399 7.26 16.7799C7.26 16.5199 7.36 16.2699 7.55 16.0699L15.47 8.14994C15.7 7.90994 15.71 7.52994 15.47 7.29994C15.23 7.06994 14.85 7.06994 14.62 7.29994L6.7 15.2199C6.29 15.6399 6.06 16.1899 6.06 16.7799C6.06 17.3699 6.29 17.9199 6.7 18.3399C7.12 18.7499 7.67 18.9799 8.26 18.9799C8.85 18.9799 9.4 18.7599 9.82 18.3399L19.86 8.29994Z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -156,16 +156,16 @@
|
|||
<div class="control">
|
||||
<input
|
||||
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Enter the new task text..."
|
||||
v-focus.always
|
||||
@focusout="toggleShowNewTaskInput(bucket.id)"
|
||||
@keyup.esc="toggleShowNewTaskInput(bucket.id)"
|
||||
@keyup.enter="addTaskToBucket(bucket.id)"
|
||||
v-model="newTaskText"
|
||||
:disabled="taskService.loading"
|
||||
:class="{'is-loading': taskService.loading}"
|
||||
:disabled="taskService.loading"
|
||||
@focusout="toggleShowNewTaskInput(bucket.id)"
|
||||
@keyup.enter="addTaskToBucket(bucket.id)"
|
||||
@keyup.esc="toggleShowNewTaskInput(bucket.id)"
|
||||
class="input"
|
||||
placeholder="Enter the new task text..."
|
||||
type="text"
|
||||
v-focus.always
|
||||
v-model="newTaskText"
|
||||
/>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="newTaskError[bucket.id] && newTaskText === ''">
|
||||
|
@ -173,8 +173,8 @@
|
|||
</p>
|
||||
</div>
|
||||
<a
|
||||
class="button noshadow is-transparent is-fullwidth has-text-centered"
|
||||
@click="toggleShowNewTaskInput(bucket.id)"
|
||||
class="button noshadow is-transparent is-fullwidth has-text-centered"
|
||||
v-if="!showNewTaskInput[bucket.id]">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
|
@ -191,21 +191,21 @@
|
|||
|
||||
<div class="bucket new-bucket" v-if="!loading && canWrite">
|
||||
<input
|
||||
v-if="showNewBucketInput"
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Enter the new bucket title..."
|
||||
v-focus.always
|
||||
@focusout="() => showNewBucketInput = false"
|
||||
@keyup.esc="() => showNewBucketInput = false"
|
||||
@keyup.enter="createNewBucket"
|
||||
v-model="newBucketTitle"
|
||||
:disabled="loading"
|
||||
:class="{'is-loading': loading}"
|
||||
:disabled="loading"
|
||||
@focusout="() => showNewBucketInput = false"
|
||||
@keyup.enter="createNewBucket"
|
||||
@keyup.esc="() => showNewBucketInput = false"
|
||||
class="input"
|
||||
placeholder="Enter the new bucket title..."
|
||||
type="text"
|
||||
v-focus.always
|
||||
v-if="showNewBucketInput"
|
||||
v-model="newBucketTitle"
|
||||
/>
|
||||
<a
|
||||
class="button noshadow is-transparent is-fullwidth has-text-centered"
|
||||
@click="() => showNewBucketInput = true" v-if="!showNewBucketInput">
|
||||
@click="() => showNewBucketInput = true"
|
||||
class="button noshadow is-transparent is-fullwidth has-text-centered" v-if="!showNewBucketInput">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
|
@ -221,9 +221,9 @@
|
|||
</transition>
|
||||
|
||||
<modal
|
||||
v-if="showBucketDeleteModal"
|
||||
@close="showBucketDeleteModal = false"
|
||||
@submit="deleteBucket()">
|
||||
@submit="deleteBucket()"
|
||||
v-if="showBucketDeleteModal">
|
||||
<span slot="header">Delete the bucket</span>
|
||||
<p slot="text">
|
||||
Are you sure you want to delete this bucket?<br/>
|
||||
|
@ -244,11 +244,11 @@
|
|||
import User from '../../../components/misc/user'
|
||||
import Labels from '../../../components/tasks/partials/labels'
|
||||
|
||||
import {filterObject} from '../../../helpers/filterObject'
|
||||
import {applyDrag} from '../../../helpers/applyDrag'
|
||||
import {filterObject} from '@/helpers/filterObject'
|
||||
import {applyDrag} from '@/helpers/applyDrag'
|
||||
import {mapState} from 'vuex'
|
||||
import {LOADING} from '../../../store/mutation-types'
|
||||
import {saveListView} from '../../../helpers/saveListView'
|
||||
import {LOADING} from '@/store/mutation-types'
|
||||
import {saveListView} from '@/helpers/saveListView'
|
||||
import Rights from '../../../models/rights.json'
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
<template>
|
||||
<div class="loader-container is-max-width-desktop" :class="{ 'is-loading': taskCollectionService.loading}">
|
||||
<div :class="{ 'is-loading': taskCollectionService.loading}" class="loader-container is-max-width-desktop">
|
||||
<div class="filter-container">
|
||||
<div class="items">
|
||||
<div class="search">
|
||||
<div class="field has-addons" :class="{ 'hidden': !showTaskSearch }">
|
||||
<div :class="{ 'hidden': !showTaskSearch }" class="field has-addons">
|
||||
<div class="control has-icons-left has-icons-right">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
v-focus
|
||||
v-model="searchTerm"
|
||||
@blur="hideSearchBar()"
|
||||
@keyup.enter="searchTasks"
|
||||
@blur="hideSearchBar()"/>
|
||||
class="input"
|
||||
placeholder="Search"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="searchTerm"/>
|
||||
<span class="icon is-left">
|
||||
<icon icon="search"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button
|
||||
class="button noshadow is-primary"
|
||||
:class="{'is-loading': taskCollectionService.loading}"
|
||||
@click="searchTasks"
|
||||
:class="{'is-loading': taskCollectionService.loading}">
|
||||
class="button noshadow is-primary">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="button" @click="showTaskSearch = !showTaskSearch" v-if="!showTaskSearch">
|
||||
<button @click="showTaskSearch = !showTaskSearch" class="button" v-if="!showTaskSearch">
|
||||
<span class="icon">
|
||||
<icon icon="search"/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<button class="button" @click="showTaskFilter = !showTaskFilter">
|
||||
<button @click="showTaskFilter = !showTaskFilter" class="button">
|
||||
<span class="icon is-small">
|
||||
<icon icon="filter"/>
|
||||
</span>
|
||||
|
@ -41,30 +41,30 @@
|
|||
</div>
|
||||
<transition name="fade">
|
||||
<filters
|
||||
@change="loadTasks(1)"
|
||||
v-if="showTaskFilter"
|
||||
v-model="params"
|
||||
@change="loadTasks(1)"
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<div class="field task-add" v-if="!list.isArchived && canWrite">
|
||||
<div class="field is-grouped">
|
||||
<p class="control has-icons-left is-expanded" :class="{ 'is-loading': taskService.loading}">
|
||||
<p :class="{ 'is-loading': taskService.loading}" class="control has-icons-left is-expanded">
|
||||
<input
|
||||
v-focus
|
||||
class="input"
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
v-model="newTaskText"
|
||||
type="text"
|
||||
@keyup.enter="addTask()"
|
||||
class="input"
|
||||
placeholder="Add a new task..."
|
||||
@keyup.enter="addTask()"/>
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="newTaskText"/>
|
||||
<span class="icon is-small is-left">
|
||||
<icon icon="tasks"/>
|
||||
</span>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-success" :disabled="newTaskText.length === 0" @click="addTask()">
|
||||
<button :disabled="newTaskText.length === 0" @click="addTask()" class="button is-success">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
|
@ -77,20 +77,20 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<p v-if="tasks.length === 0" class="list-is-empty-notice">
|
||||
<p class="list-is-empty-notice" v-if="tasks.length === 0">
|
||||
This list is currently empty.
|
||||
</p>
|
||||
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="tasks" v-if="tasks && tasks.length > 0" :class="{'short': isTaskEdit}">
|
||||
<div :class="{'short': isTaskEdit}" class="tasks" v-if="tasks && tasks.length > 0">
|
||||
<single-task-in-list
|
||||
v-for="t in tasks"
|
||||
:disabled="!canWrite"
|
||||
:key="t.id"
|
||||
:the-task="t"
|
||||
@taskUpdated="updateTasks"
|
||||
task-detail-route="task.detail"
|
||||
:disabled="!canWrite"
|
||||
v-for="t in tasks"
|
||||
>
|
||||
<div @click="editTask(t.id)" class="icon settings" v-if="!list.isArchived && canWrite">
|
||||
<icon icon="pencil-alt"/>
|
||||
|
@ -104,7 +104,7 @@
|
|||
<p class="card-header-title">
|
||||
Edit Task
|
||||
</p>
|
||||
<a class="card-header-icon" @click="isTaskEdit = false">
|
||||
<a @click="isTaskEdit = false" class="card-header-icon">
|
||||
<span class="icon">
|
||||
<icon icon="angle-right"/>
|
||||
</span>
|
||||
|
@ -120,22 +120,22 @@
|
|||
</div>
|
||||
|
||||
<nav
|
||||
aria-label="pagination"
|
||||
class="pagination is-centered"
|
||||
role="navigation"
|
||||
aria-label="pagination"
|
||||
v-if="taskCollectionService.totalPages > 1">
|
||||
<router-link
|
||||
class="pagination-previous"
|
||||
:disabled="currentPage === 1"
|
||||
:to="getRouteForPagination(currentPage - 1)"
|
||||
tag="button"
|
||||
:disabled="currentPage === 1">
|
||||
class="pagination-previous"
|
||||
tag="button">
|
||||
Previous
|
||||
</router-link>
|
||||
<router-link
|
||||
class="pagination-next"
|
||||
:disabled="currentPage === taskCollectionService.totalPages"
|
||||
:to="getRouteForPagination(currentPage + 1)"
|
||||
tag="button"
|
||||
:disabled="currentPage === taskCollectionService.totalPages">
|
||||
class="pagination-next"
|
||||
tag="button">
|
||||
Next page
|
||||
</router-link>
|
||||
<ul class="pagination-list">
|
||||
|
@ -143,10 +143,10 @@
|
|||
<li :key="'page'+i" v-if="p.isEllipsis"><span class="pagination-ellipsis">…</span></li>
|
||||
<li :key="'page'+i" v-else>
|
||||
<router-link
|
||||
:to="getRouteForPagination(p.number)"
|
||||
:aria-label="'Goto page ' + p.number"
|
||||
:class="{'is-current': p.number === currentPage}"
|
||||
class="pagination-link"
|
||||
:aria-label="'Goto page ' + p.number">
|
||||
:to="getRouteForPagination(p.number)"
|
||||
class="pagination-link">
|
||||
{{ p.number }}
|
||||
</router-link>
|
||||
</li>
|
||||
|
@ -173,7 +173,7 @@
|
|||
import EditTask from '../../../components/tasks/edit-task'
|
||||
import SingleTaskInList from '../../../components/tasks/partials/singleTaskInList'
|
||||
import taskList from '../../../components/tasks/mixins/taskList'
|
||||
import {saveListView} from '../../../helpers/saveListView'
|
||||
import {saveListView} from '@/helpers/saveListView'
|
||||
import Filters from '../../../components/list/partials/filters'
|
||||
import Rights from '../../../models/rights.json'
|
||||
import {mapState} from 'vuex'
|
||||
|
@ -358,6 +358,6 @@
|
|||
}
|
||||
this.sortTasks()
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,14 +1,16 @@
|
|||
<template>
|
||||
<div class="table-view loader-container" :class="{'is-loading': taskCollectionService.loading}">
|
||||
<div :class="{'is-loading': taskCollectionService.loading}" class="table-view loader-container">
|
||||
<div class="filter-container">
|
||||
<div class="items">
|
||||
<button class="button" @click="() => {showActiveColumnsFilter = !showActiveColumnsFilter; showTaskFilter = false}">
|
||||
<button @click="() => {showActiveColumnsFilter = !showActiveColumnsFilter; showTaskFilter = false}"
|
||||
class="button">
|
||||
<span class="icon is-small">
|
||||
<icon icon="th"/>
|
||||
</span>
|
||||
Columns
|
||||
</button>
|
||||
<button class="button" @click="() => {showTaskFilter = !showTaskFilter; showActiveColumnsFilter = false}">
|
||||
<button @click="() => {showTaskFilter = !showTaskFilter; showActiveColumnsFilter = false}"
|
||||
class="button">
|
||||
<span class="icon is-small">
|
||||
<icon icon="filter"/>
|
||||
</span>
|
||||
|
@ -21,22 +23,29 @@
|
|||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.id">#</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.done">Done</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.title">Title</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.priority">Priority</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.priority">Priority
|
||||
</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.labels">Labels</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.assignees">Assignees</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.dueDate">Due Date</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.startDate">Start Date</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.endDate">End Date</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.percentDone">% Done</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.assignees">Assignees
|
||||
</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.dueDate">Due Date
|
||||
</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.startDate">Start Date
|
||||
</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.endDate">End Date
|
||||
</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.percentDone">% Done
|
||||
</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.created">Created</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.updated">Updated</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.createdBy">Created By</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.createdBy">Created By
|
||||
</fancycheckbox>
|
||||
</div>
|
||||
</div>
|
||||
<filters
|
||||
@change="loadTasks(1)"
|
||||
v-if="showTaskFilter"
|
||||
v-model="params"
|
||||
@change="loadTasks(1)"
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
|
@ -96,7 +105,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="t in tasks" :key="t.id">
|
||||
<tr :key="t.id" v-for="t in tasks">
|
||||
<td v-if="activeColumns.id">
|
||||
<router-link :to="{name: 'task.detail', params: { id: t.id }}">
|
||||
<template v-if="t.identifier === ''">
|
||||
|
@ -121,12 +130,12 @@
|
|||
</td>
|
||||
<td v-if="activeColumns.assignees">
|
||||
<user
|
||||
:user="a"
|
||||
:avatar-size="27"
|
||||
:show-username="false"
|
||||
:is-inline="true"
|
||||
v-for="(a, i) in t.assignees"
|
||||
:key="t.id + 'assignee' + a.id + i"
|
||||
:show-username="false"
|
||||
:user="a"
|
||||
v-for="(a, i) in t.assignees"
|
||||
/>
|
||||
</td>
|
||||
<date-table-cell :date="t.dueDate" v-if="activeColumns.dueDate"/>
|
||||
|
@ -137,22 +146,43 @@
|
|||
<date-table-cell :date="t.updated" v-if="activeColumns.updated"/>
|
||||
<td v-if="activeColumns.createdBy">
|
||||
<user
|
||||
:user="t.createdBy"
|
||||
:avatar-size="27"
|
||||
:show-username="false"
|
||||
:avatar-size="27"/>
|
||||
:user="t.createdBy"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<nav class="pagination is-centered" role="navigation" aria-label="pagination" v-if="taskCollectionService.totalPages > 1">
|
||||
<router-link class="pagination-previous" :to="getRouteForPagination(currentPage - 1, 'table')" tag="button" :disabled="currentPage === 1">Previous</router-link>
|
||||
<router-link class="pagination-next" :to="getRouteForPagination(currentPage + 1, 'table')" tag="button" :disabled="currentPage === taskCollectionService.totalPages">Next page</router-link>
|
||||
<nav
|
||||
aria-label="pagination"
|
||||
class="pagination is-centered"
|
||||
role="navigation"
|
||||
v-if="taskCollectionService.totalPages > 1">
|
||||
<router-link
|
||||
:disabled="currentPage === 1"
|
||||
:to="getRouteForPagination(currentPage - 1, 'table')"
|
||||
class="pagination-previous"
|
||||
tag="button">
|
||||
Previous
|
||||
</router-link>
|
||||
<router-link
|
||||
:disabled="currentPage === taskCollectionService.totalPages"
|
||||
:to="getRouteForPagination(currentPage + 1, 'table')"
|
||||
class="pagination-next"
|
||||
tag="button">
|
||||
Next page
|
||||
</router-link>
|
||||
<ul class="pagination-list">
|
||||
<template v-for="(p, i) in pages">
|
||||
<li :key="'page'+i" v-if="p.isEllipsis"><span class="pagination-ellipsis">…</span></li>
|
||||
<li :key="'page'+i" v-else>
|
||||
<router-link :to="getRouteForPagination(p.number, 'table')" :class="{'is-current': p.number === currentPage}" class="pagination-link" :aria-label="'Goto page ' + p.number">{{ p.number }}</router-link>
|
||||
<router-link
|
||||
:aria-label="'Goto page ' + p.number"
|
||||
:class="{'is-current': p.number === currentPage}"
|
||||
:to="getRouteForPagination(p.number, 'table')"
|
||||
class="pagination-link">{{ p.number }}
|
||||
</router-link>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
|
@ -174,7 +204,7 @@
|
|||
import DateTableCell from '../../../components/tasks/partials/date-table-cell'
|
||||
import Fancycheckbox from '../../../components/input/fancycheckbox'
|
||||
import Sort from '../../../components/tasks/partials/sort'
|
||||
import {saveListView} from '../../../helpers/saveListView'
|
||||
import {saveListView} from '@/helpers/saveListView'
|
||||
import Filters from '../../../components/list/partials/filters'
|
||||
|
||||
export default {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<h1>Import your data from other services to Vikunja</h1>
|
||||
<p>Click on the logo of one of the third-party services below to get started.</p>
|
||||
<div class="migration-services-overview">
|
||||
<router-link :to="{name: 'migrate.service', params: {service: m}}" v-for="m in availableMigrators" :key="m">
|
||||
<img :src="`/images/migration/${m}.png`" :alt="m"/>
|
||||
<router-link :key="m" :to="{name: 'migrate.service', params: {service: m}}" v-for="m in availableMigrators">
|
||||
<img :alt="m" :src="`/images/migration/${m}.png`"/>
|
||||
{{ m }}
|
||||
</router-link>
|
||||
</div>
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
<label class="label" for="namespacetext">Namespace Name</label>
|
||||
<div class="control">
|
||||
<input
|
||||
v-focus
|
||||
:class="{ 'disabled': namespaceService.loading}"
|
||||
:disabled="namespaceService.loading"
|
||||
class="input"
|
||||
type="text"
|
||||
id="namespacetext"
|
||||
placeholder="The namespace text is here..."
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="namespace.title"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,11 +33,11 @@
|
|||
<editor
|
||||
:class="{ 'disabled': namespaceService.loading}"
|
||||
:disabled="namespaceService.loading"
|
||||
placeholder="The namespaces description goes here..."
|
||||
id="namespacedescription"
|
||||
v-model="namespace.description"
|
||||
:preview-is-default="false"
|
||||
id="namespacedescription"
|
||||
placeholder="The namespaces description goes here..."
|
||||
v-if="editorActive"
|
||||
v-model="namespace.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -61,14 +61,14 @@
|
|||
|
||||
<div class="columns bigbuttons">
|
||||
<div class="column">
|
||||
<button @click="submit()" class="button is-primary is-fullwidth"
|
||||
:class="{ 'is-loading': namespaceService.loading}">
|
||||
<button :class="{ 'is-loading': namespaceService.loading}" @click="submit()"
|
||||
class="button is-primary is-fullwidth">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-1">
|
||||
<button @click="showDeleteModal = true" class="button is-danger is-fullwidth"
|
||||
:class="{ 'is-loading': namespaceService.loading}">
|
||||
<button :class="{ 'is-loading': namespaceService.loading}" @click="showDeleteModal = true"
|
||||
class="button is-danger is-fullwidth">
|
||||
<span class="icon is-small">
|
||||
<icon icon="trash-alt"/>
|
||||
</span>
|
||||
|
@ -80,21 +80,21 @@
|
|||
</div>
|
||||
|
||||
<component
|
||||
:id="namespace.id"
|
||||
:is="manageUsersComponent"
|
||||
:id="namespace.id"
|
||||
type="namespace"
|
||||
:userIsAdmin="userIsAdmin"
|
||||
shareType="user"
|
||||
:userIsAdmin="userIsAdmin"/>
|
||||
type="namespace"/>
|
||||
<component
|
||||
:is="manageTeamsComponent"
|
||||
:id="namespace.id"
|
||||
type="namespace"
|
||||
:is="manageTeamsComponent"
|
||||
:userIsAdmin="userIsAdmin"
|
||||
shareType="team"
|
||||
:userIsAdmin="userIsAdmin"/>
|
||||
type="namespace"/>
|
||||
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
v-if="showDeleteModal"
|
||||
v-on: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?
|
||||
|
@ -115,7 +115,7 @@
|
|||
import ErrorComponent from '../../components/misc/error'
|
||||
|
||||
export default {
|
||||
name: "EditNamespace",
|
||||
name: 'EditNamespace',
|
||||
data() {
|
||||
return {
|
||||
namespaceService: NamespaceService,
|
||||
|
@ -148,7 +148,7 @@
|
|||
},
|
||||
watch: {
|
||||
// call again the method if the route changes
|
||||
'$route': 'loadNamespace'
|
||||
'$route': 'loadNamespace',
|
||||
},
|
||||
computed: {
|
||||
userIsAdmin() {
|
||||
|
@ -198,7 +198,7 @@
|
|||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -7,11 +7,11 @@
|
|||
Create new namespace
|
||||
</router-link>
|
||||
|
||||
<fancycheckbox v-model="showArchived" class="show-archived-check">
|
||||
<fancycheckbox class="show-archived-check" v-model="showArchived">
|
||||
Show Archived
|
||||
</fancycheckbox>
|
||||
|
||||
<div class="namespace" v-for="n in namespaces" :key="`n${n.id}`">
|
||||
<div :key="`n${n.id}`" class="namespace" v-for="n in namespaces">
|
||||
<h1>
|
||||
<span>{{ n.title }}</span>
|
||||
<span class="is-archived" v-if="n.isArchived">
|
||||
|
@ -22,18 +22,18 @@
|
|||
<div class="lists">
|
||||
<template v-for="l in n.lists">
|
||||
<router-link
|
||||
:to="{ name: 'list.index', params: { listId: l.id} }"
|
||||
class="list"
|
||||
:key="`l${l.id}`"
|
||||
v-if="showArchived ? true : !l.isArchived"
|
||||
:style="{
|
||||
'background-color': l.hexColor,
|
||||
'background-image': typeof backgrounds[l.id] !== 'undefined' ? `url(${backgrounds[l.id]})` : false,
|
||||
}"
|
||||
:class="{
|
||||
'has-light-text': !colorIsDark(l.hexColor),
|
||||
'has-background': typeof backgrounds[l.id] !== 'undefined',
|
||||
}"
|
||||
:key="`l${l.id}`"
|
||||
:style="{
|
||||
'background-color': l.hexColor,
|
||||
'background-image': typeof backgrounds[l.id] !== 'undefined' ? `url(${backgrounds[l.id]})` : false,
|
||||
}"
|
||||
:to="{ name: 'list.index', params: { listId: l.id} }"
|
||||
class="list"
|
||||
v-if="showArchived ? true : !l.isArchived"
|
||||
>
|
||||
<div class="is-archived-container">
|
||||
<span class="is-archived" v-if="l.isArchived">
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
<template>
|
||||
<div class="fullpage">
|
||||
<a class="close" @click="back()">
|
||||
<a @click="back()" class="close">
|
||||
<icon :icon="['far', 'times-circle']">
|
||||
</icon>
|
||||
</a>
|
||||
<h3>Create a new namespace</h3>
|
||||
<div class="field is-grouped">
|
||||
<p class="control is-expanded" v-bind:class="{ 'is-loading': namespaceService.loading}">
|
||||
<input v-focus
|
||||
class="input"
|
||||
v-bind:class="{ 'disabled': namespaceService.loading}"
|
||||
v-model="namespace.title"
|
||||
type="text"
|
||||
<input
|
||||
@keyup.enter="newNamespace()"
|
||||
@keyup.esc="back()"
|
||||
placeholder="The namespace's name goes here..."/>
|
||||
class="input"
|
||||
placeholder="The namespace's name goes here..."
|
||||
type="text"
|
||||
:class="{ 'disabled': namespaceService.loading}"
|
||||
v-focus
|
||||
v-model="namespace.title"/>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-success noshadow" @click="newNamespace()" :disabled="namespace.title === ''">
|
||||
<button :disabled="namespace.title === ''" @click="newNamespace()" class="button is-success noshadow">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
|
@ -28,19 +29,21 @@
|
|||
<p class="help is-danger" v-if="showError && namespace.title === ''">
|
||||
Please specify a title.
|
||||
</p>
|
||||
<p class="small" v-tooltip.bottom="'A namespace is a collection of lists you can share and use to organize your lists with. In fact, every list belongs to a namepace.'">
|
||||
<p
|
||||
class="small"
|
||||
v-tooltip.bottom="'A namespace is a collection of lists you can share and use to organize your lists with. In fact, every list belongs to a namepace.'">
|
||||
What's a namespace?</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import router from '../../router'
|
||||
import NamespaceModel from "../../models/namespace";
|
||||
import NamespaceService from "../../services/namespace";
|
||||
import {IS_FULLPAGE} from '../../store/mutation-types'
|
||||
import NamespaceModel from '../../models/namespace'
|
||||
import NamespaceService from '../../services/namespace'
|
||||
import {IS_FULLPAGE} from '@/store/mutation-types'
|
||||
|
||||
export default {
|
||||
name: "NewNamespace",
|
||||
name: 'NewNamespace',
|
||||
data() {
|
||||
return {
|
||||
showError: false,
|
||||
|
@ -76,7 +79,7 @@
|
|||
},
|
||||
back() {
|
||||
router.go(-1)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div class="is-max-width-desktop show-tasks">
|
||||
<fancycheckbox
|
||||
@change="loadPendingTasks"
|
||||
class="is-pulled-right"
|
||||
v-if="!showAll"
|
||||
v-model="showNulls"
|
||||
@change="loadPendingTasks"
|
||||
>
|
||||
Show tasks without dates
|
||||
</fancycheckbox>
|
||||
|
@ -13,30 +13,30 @@
|
|||
Tasks from
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading"
|
||||
v-model="cStartDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading"
|
||||
@on-close="loadPendingTasks"
|
||||
class="input"
|
||||
v-model="cStartDate"
|
||||
/>
|
||||
until
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading"
|
||||
v-model="cEndDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading"
|
||||
@on-close="loadPendingTasks"
|
||||
class="input"
|
||||
v-model="cEndDate"
|
||||
/>
|
||||
</h3>
|
||||
<template v-if="!taskService.loading && (!hasUndoneTasks || !tasks || tasks.length === 0)">
|
||||
<h3 class="nothing">Nothing to do - Have a nice day!</h3>
|
||||
<img src="/images/cool.svg" alt=""/>
|
||||
<img alt="" src="/images/cool.svg"/>
|
||||
</template>
|
||||
<div class="spinner" :class="{ 'is-loading': taskService.loading}"></div>
|
||||
<div :class="{ 'is-loading': taskService.loading}" class="spinner"></div>
|
||||
<div class="tasks" v-if="tasks && tasks.length > 0">
|
||||
<div class="task" v-for="t in tasks" :key="t.id">
|
||||
<single-task-in-list :the-task="t" @taskUpdated="updateTasks" :show-list="true"/>
|
||||
<div :key="t.id" class="task" v-for="t in tasks">
|
||||
<single-task-in-list :show-list="true" :the-task="t" @taskUpdated="updateTasks"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,7 +44,7 @@
|
|||
<script>
|
||||
import TaskService from '../../services/task'
|
||||
import SingleTaskInList from '../../components/tasks/partials/singleTaskInList'
|
||||
import {HAS_TASKS} from '../../store/mutation-types'
|
||||
import {HAS_TASKS} from '@/store/mutation-types'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
import flatPickr from 'vue-flatpickr-component'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="content has-text-centered">
|
||||
<ShowTasks
|
||||
:start-date="startDate"
|
||||
:end-date="endDate"
|
||||
:start-date="startDate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -23,24 +23,24 @@
|
|||
},
|
||||
watch: {
|
||||
// call again the method if the route changes
|
||||
'$route': 'setDates'
|
||||
'$route': 'setDates',
|
||||
},
|
||||
created() {
|
||||
this.setDates();
|
||||
this.setDates()
|
||||
},
|
||||
methods: {
|
||||
setDates() {
|
||||
switch (this.$route.params.type) {
|
||||
case 'week':
|
||||
this.startDate = new Date();
|
||||
this.endDate = new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000);
|
||||
break;
|
||||
this.startDate = new Date()
|
||||
this.endDate = new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
break
|
||||
case 'month':
|
||||
this.startDate = new Date();
|
||||
this.endDate = new Date((new Date()).setMonth((new Date()).getMonth() + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.startDate = new Date()
|
||||
this.endDate = new Date((new Date()).setMonth((new Date()).getMonth() + 1))
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="loader-container task-view-container" :class="{ 'is-loading': taskService.loading}">
|
||||
<div :class="{ 'is-loading': taskService.loading}" class="loader-container task-view-container">
|
||||
<div class="task-view">
|
||||
<div class="heading">
|
||||
<h1 class="title task-id" v-if="task.identifier === ''">
|
||||
|
@ -9,8 +9,8 @@
|
|||
{{ task.identifier }}
|
||||
</h1>
|
||||
<div class="is-done" v-if="task.done">Done</div>
|
||||
<h1 class="title input" contenteditable="true" @focusout="saveTaskOnChange()" ref="taskTitle"
|
||||
@keyup.ctrl.enter="saveTaskOnChange()">{{ task.title }}</h1>
|
||||
<h1 @focusout="saveTaskOnChange()" @keyup.ctrl.enter="saveTaskOnChange()" class="title input" contenteditable="true"
|
||||
ref="taskTitle">{{ task.title }}</h1>
|
||||
</div>
|
||||
<h6 class="subtitle" v-if="parent && parent.namespace && parent.list">
|
||||
{{ parent.namespace.title }} >
|
||||
|
@ -22,7 +22,7 @@
|
|||
<!-- Content and buttons -->
|
||||
<div class="columns">
|
||||
<!-- Content -->
|
||||
<div class="column" :class="{'is-two-thirds': canWrite}">
|
||||
<div :class="{'is-two-thirds': canWrite}" class="column">
|
||||
<div class="columns details">
|
||||
<div class="column assignees" v-if="activeFields.assignees">
|
||||
<!-- Assignees -->
|
||||
|
@ -31,11 +31,11 @@
|
|||
Assignees
|
||||
</div>
|
||||
<edit-assignees
|
||||
:task-id="task.id"
|
||||
:list-id="task.listId"
|
||||
:initial-assignees="task.assignees"
|
||||
ref="assignees"
|
||||
:disabled="!canWrite"
|
||||
:initial-assignees="task.assignees"
|
||||
:list-id="task.listId"
|
||||
:task-id="task.id"
|
||||
ref="assignees"
|
||||
/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.priority">
|
||||
|
@ -45,10 +45,10 @@
|
|||
Priority
|
||||
</div>
|
||||
<priority-select
|
||||
v-model="task.priority"
|
||||
:disabled="!canWrite"
|
||||
@change="saveTask"
|
||||
ref="priority"
|
||||
:disabled="!canWrite"/>
|
||||
v-model="task.priority"/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.dueDate">
|
||||
<!-- Due Date -->
|
||||
|
@ -59,16 +59,16 @@
|
|||
<div class="date-input">
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
v-model="dueDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
@on-close="saveTask"
|
||||
class="input"
|
||||
placeholder="Click here to set a due date"
|
||||
ref="dueDate"
|
||||
v-model="dueDate"
|
||||
>
|
||||
</flat-pickr>
|
||||
<a v-if="dueDate && canWrite" @click="() => {dueDate = task.dueDate = null;saveTask()}">
|
||||
<a @click="() => {dueDate = task.dueDate = null;saveTask()}" v-if="dueDate && canWrite">
|
||||
<span class="icon is-small">
|
||||
<icon icon="times"></icon>
|
||||
</span>
|
||||
|
@ -82,10 +82,10 @@
|
|||
Percent Done
|
||||
</div>
|
||||
<percent-done-select
|
||||
v-model="task.percentDone"
|
||||
:disabled="!canWrite"
|
||||
@change="saveTask"
|
||||
ref="percentDone"
|
||||
:disabled="!canWrite"/>
|
||||
v-model="task.percentDone"/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.startDate">
|
||||
<!-- Start Date -->
|
||||
|
@ -96,16 +96,16 @@
|
|||
<div class="date-input">
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
v-model="task.startDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
@on-close="saveTask"
|
||||
class="input"
|
||||
placeholder="Click here to set a start date"
|
||||
ref="startDate"
|
||||
v-model="task.startDate"
|
||||
>
|
||||
</flat-pickr>
|
||||
<a v-if="task.startDate && canWrite" @click="() => {task.startDate = null;saveTask()}">
|
||||
<a @click="() => {task.startDate = null;saveTask()}" v-if="task.startDate && canWrite">
|
||||
<span class="icon is-small">
|
||||
<icon icon="times"></icon>
|
||||
</span>
|
||||
|
@ -121,16 +121,16 @@
|
|||
<div class="date-input">
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
class="input"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
v-model="task.endDate"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
@on-close="saveTask"
|
||||
class="input"
|
||||
placeholder="Click here to set an end date"
|
||||
ref="endDate"
|
||||
v-model="task.endDate"
|
||||
>
|
||||
</flat-pickr>
|
||||
<a v-if="task.endDate && canWrite" @click="() => {task.endDate = null;saveTask()}">
|
||||
<a @click="() => {task.endDate = null;saveTask()}" v-if="task.endDate && canWrite">
|
||||
<span class="icon is-small">
|
||||
<icon icon="times"></icon>
|
||||
</span>
|
||||
|
@ -144,10 +144,10 @@
|
|||
Reminders
|
||||
</div>
|
||||
<reminders
|
||||
v-model="task.reminderDates"
|
||||
:disabled="!canWrite"
|
||||
@change="saveTask"
|
||||
ref="reminders"
|
||||
:disabled="!canWrite"/>
|
||||
v-model="task.reminderDates"/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.repeatAfter">
|
||||
<!-- Repeat after -->
|
||||
|
@ -156,10 +156,10 @@
|
|||
Repeat
|
||||
</div>
|
||||
<repeat-after
|
||||
v-model="task"
|
||||
@change="saveTask"
|
||||
:disabled="!canWrite"
|
||||
ref="repeatAfter"/>
|
||||
@change="saveTask"
|
||||
ref="repeatAfter"
|
||||
v-model="task"/>
|
||||
</div>
|
||||
<div class="column" v-if="activeFields.color">
|
||||
<!-- Color -->
|
||||
|
@ -168,10 +168,10 @@
|
|||
Color
|
||||
</div>
|
||||
<color-picker
|
||||
v-model="taskColor"
|
||||
menu-position="bottom"
|
||||
@change="saveTask"
|
||||
ref="color"/>
|
||||
menu-position="bottom"
|
||||
ref="color"
|
||||
v-model="taskColor"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -183,11 +183,11 @@
|
|||
</span>
|
||||
Labels
|
||||
</div>
|
||||
<edit-labels :task-id="taskId" v-model="task.labels" ref="labels" :disabled="!canWrite"/>
|
||||
<edit-labels :disabled="!canWrite" :task-id="taskId" ref="labels" v-model="task.labels"/>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="details content description" :class="{ 'has-top-border': activeFields.labels }">
|
||||
<div :class="{ 'has-top-border': activeFields.labels }" class="details content description">
|
||||
<h3>
|
||||
<span class="icon is-grey">
|
||||
<icon icon="align-left"/>
|
||||
|
@ -195,20 +195,20 @@
|
|||
Description
|
||||
</h3>
|
||||
<editor
|
||||
v-model="task.description"
|
||||
@change="saveTask"
|
||||
:upload-enabled="true"
|
||||
:upload-callback="attachmentUpload"
|
||||
:is-edit-enabled="canWrite"
|
||||
placeholder="Click here to enter a description..."/>
|
||||
:upload-callback="attachmentUpload"
|
||||
:upload-enabled="true"
|
||||
@change="saveTask"
|
||||
placeholder="Click here to enter a description..."
|
||||
v-model="task.description"/>
|
||||
</div>
|
||||
|
||||
<!-- Attachments -->
|
||||
<div class="content attachments has-top-border" v-if="activeFields.attachments">
|
||||
<attachments
|
||||
:edit-enabled="canWrite"
|
||||
:task-id="taskId"
|
||||
ref="attachments"
|
||||
:edit-enabled="canWrite"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -221,12 +221,12 @@
|
|||
Related Tasks
|
||||
</h3>
|
||||
<related-tasks
|
||||
:task-id="taskId"
|
||||
:list-id="task.listId"
|
||||
:initial-related-tasks="task.relatedTasks"
|
||||
:show-no-relations-notice="true"
|
||||
ref="relatedTasks"
|
||||
:edit-enabled="canWrite"
|
||||
:initial-related-tasks="task.relatedTasks"
|
||||
:list-id="task.listId"
|
||||
:show-no-relations-notice="true"
|
||||
:task-id="taskId"
|
||||
ref="relatedTasks"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -246,13 +246,13 @@
|
|||
</div>
|
||||
|
||||
<!-- Comments -->
|
||||
<comments :task-id="taskId" :can-write="canWrite"/>
|
||||
<comments :can-write="canWrite" :task-id="taskId"/>
|
||||
</div>
|
||||
<div class="column is-one-third action-buttons" v-if="canWrite">
|
||||
<a
|
||||
class="button is-outlined noshadow has-no-border"
|
||||
:class="{'is-success': !task.done}"
|
||||
@click="toggleTaskDone()">
|
||||
@click="toggleTaskDone()"
|
||||
class="button is-outlined noshadow has-no-border">
|
||||
<span class="icon is-small"><icon icon="check-double"/></span>
|
||||
<template v-if="task.done">
|
||||
Mark as undone
|
||||
|
@ -262,78 +262,78 @@
|
|||
</template>
|
||||
</a>
|
||||
<a
|
||||
class="button"
|
||||
@click="setFieldActive('assignees')"
|
||||
v-shortkey="['ctrl', 'shift', 'a']"
|
||||
@shortkey="setFieldActive('assignees')">
|
||||
@shortkey="setFieldActive('assignees')"
|
||||
class="button"
|
||||
v-shortkey="['ctrl', 'shift', 'a']">
|
||||
<span class="icon is-small"><icon icon="users"/></span>
|
||||
Assign this task to a user
|
||||
</a>
|
||||
<a
|
||||
class="button"
|
||||
@click="setFieldActive('labels')"
|
||||
v-shortkey="['ctrl', 'shift', 'l']"
|
||||
@shortkey="setFieldActive('labels')">
|
||||
@shortkey="setFieldActive('labels')"
|
||||
class="button"
|
||||
v-shortkey="['ctrl', 'shift', 'l']">
|
||||
<span class="icon is-small"><icon icon="tags"/></span>
|
||||
Add labels
|
||||
</a>
|
||||
<a class="button" @click="setFieldActive('reminders')">
|
||||
<a @click="setFieldActive('reminders')" class="button">
|
||||
<span class="icon is-small"><icon icon="history"/></span>
|
||||
Set Reminders
|
||||
</a>
|
||||
<a
|
||||
class="button"
|
||||
@click="setFieldActive('dueDate')"
|
||||
v-shortkey="['ctrl', 'shift', 'd']"
|
||||
@shortkey="setFieldActive('dueDate')">
|
||||
@shortkey="setFieldActive('dueDate')"
|
||||
class="button"
|
||||
v-shortkey="['ctrl', 'shift', 'd']">
|
||||
<span class="icon is-small"><icon icon="calendar"/></span>
|
||||
Set Due Date
|
||||
</a>
|
||||
<a class="button" @click="setFieldActive('startDate')">
|
||||
<a @click="setFieldActive('startDate')" class="button">
|
||||
<span class="icon is-small"><icon icon="calendar-week"/></span>
|
||||
Set a Start Date
|
||||
</a>
|
||||
<a class="button" @click="setFieldActive('endDate')">
|
||||
<a @click="setFieldActive('endDate')" class="button">
|
||||
<span class="icon is-small"><icon icon="calendar-week"/></span>
|
||||
Set an End Date
|
||||
</a>
|
||||
<a class="button" @click="setFieldActive('repeatAfter')">
|
||||
<a @click="setFieldActive('repeatAfter')" class="button">
|
||||
<span class="icon is-small"><icon :icon="['far', 'clock']"/></span>
|
||||
Set a repeating interval
|
||||
</a>
|
||||
<a class="button" @click="setFieldActive('priority')">
|
||||
<a @click="setFieldActive('priority')" class="button">
|
||||
<span class="icon is-small"><icon :icon="['far', 'star']"/></span>
|
||||
Set Priority
|
||||
</a>
|
||||
<a class="button" @click="setFieldActive('percentDone')">
|
||||
<a @click="setFieldActive('percentDone')" class="button">
|
||||
<span class="icon is-small"><icon icon="percent"/></span>
|
||||
Set Percent Done
|
||||
</a>
|
||||
<a
|
||||
class="button"
|
||||
@click="setFieldActive('attachments')"
|
||||
v-shortkey="['ctrl', 'shift', 'f']"
|
||||
@shortkey="setFieldActive('attachments')">
|
||||
@shortkey="setFieldActive('attachments')"
|
||||
class="button"
|
||||
v-shortkey="['ctrl', 'shift', 'f']">
|
||||
<span class="icon is-small"><icon icon="paperclip"/></span>
|
||||
Add attachments
|
||||
</a>
|
||||
<a
|
||||
class="button"
|
||||
@click="setFieldActive('relatedTasks')"
|
||||
v-shortkey="['ctrl', 'shift', 'r']"
|
||||
@shortkey="setFieldActive('relatedTasks')">
|
||||
@shortkey="setFieldActive('relatedTasks')"
|
||||
class="button"
|
||||
v-shortkey="['ctrl', 'shift', 'r']">
|
||||
<span class="icon is-small"><icon icon="tasks"/></span>
|
||||
Add task relations
|
||||
</a>
|
||||
<a class="button" @click="setFieldActive('moveList')">
|
||||
<a @click="setFieldActive('moveList')" class="button">
|
||||
<span class="icon is-small"><icon icon="list"/></span>
|
||||
Move task
|
||||
</a>
|
||||
<a class="button" @click="setFieldActive('color')">
|
||||
<a @click="setFieldActive('color')" class="button">
|
||||
<span class="icon is-small"><icon icon="fill-drip"/></span>
|
||||
Set task color
|
||||
</a>
|
||||
<a class="button is-danger is-outlined noshadow has-no-border" @click="showDeleteModal = true">
|
||||
<a @click="showDeleteModal = true" class="button is-danger is-outlined noshadow has-no-border">
|
||||
<span class="icon is-small"><icon icon="trash-alt"/></span>
|
||||
Delete task
|
||||
</a>
|
||||
|
@ -344,9 +344,9 @@
|
|||
</div>
|
||||
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
@submit="deleteTask()">
|
||||
@submit="deleteTask()"
|
||||
v-if="showDeleteModal">
|
||||
<span slot="header">Delete this task</span>
|
||||
<p slot="text">
|
||||
Are you sure you want to remove this task? <br/>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="modal-mask">
|
||||
<div class="modal-container" @click.self="close()">
|
||||
<div @click.self="close()" class="modal-container">
|
||||
<div class="scrolling-content">
|
||||
<a @click="close()" class="close">
|
||||
<icon icon="times"/>
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
<label class="label" for="teamtext">Team Name</label>
|
||||
<div class="control">
|
||||
<input
|
||||
v-focus
|
||||
:class="{ 'disabled': teamMemberService.loading}"
|
||||
:disabled="teamMemberService.loading"
|
||||
class="input"
|
||||
type="text"
|
||||
id="teamtext"
|
||||
placeholder="The team text is here..."
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="team.name"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,10 +32,10 @@
|
|||
<editor
|
||||
:class="{ 'disabled': teamService.loading}"
|
||||
:disabled="teamService.loading"
|
||||
placeholder="The teams description goes here..."
|
||||
id="teamdescription"
|
||||
v-model="team.description"
|
||||
:preview-is-default="false"
|
||||
id="teamdescription"
|
||||
placeholder="The teams description goes here..."
|
||||
v-model="team.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -43,14 +43,14 @@
|
|||
|
||||
<div class="columns bigbuttons">
|
||||
<div class="column">
|
||||
<button @click="submit()" class="button is-primary is-fullwidth"
|
||||
:class="{ 'is-loading': teamService.loading}">
|
||||
<button :class="{ 'is-loading': teamService.loading}" @click="submit()"
|
||||
class="button is-primary is-fullwidth">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-1">
|
||||
<button @click="showDeleteModal = true" class="button is-danger is-fullwidth"
|
||||
:class="{ 'is-loading': teamService.loading}">
|
||||
<button :class="{ 'is-loading': teamService.loading}" @click="showDeleteModal = true"
|
||||
class="button is-danger is-fullwidth">
|
||||
<span class="icon is-small">
|
||||
<icon icon="trash-alt"/>
|
||||
</span>
|
||||
|
@ -70,31 +70,31 @@
|
|||
<form @submit.prevent="addUser()" class="add-member-form" v-if="userIsAdmin">
|
||||
<div class="field is-grouped">
|
||||
<p
|
||||
class="control has-icons-left is-expanded"
|
||||
:class="{ 'is-loading': teamMemberService.loading}">
|
||||
:class="{ 'is-loading': teamMemberService.loading}"
|
||||
class="control has-icons-left is-expanded">
|
||||
<multiselect
|
||||
v-model="newMember"
|
||||
:options="foundUsers"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:loading="userService.loading"
|
||||
:internal-search="true"
|
||||
@search-change="findUser"
|
||||
placeholder="Type to search..."
|
||||
:loading="userService.loading"
|
||||
:multiple="false"
|
||||
:options="foundUsers"
|
||||
:searchable="true"
|
||||
:showNoOptions="false"
|
||||
@search-change="findUser"
|
||||
label="username"
|
||||
track-by="id">
|
||||
placeholder="Type to search..."
|
||||
track-by="id"
|
||||
v-model="newMember">
|
||||
<template slot="clear" slot-scope="props">
|
||||
<div
|
||||
class="multiselect__clear" v-if="newMember !== null && newMember.id !== 0"
|
||||
@mousedown.prevent.stop="clearAll(props.search)">
|
||||
@mousedown.prevent.stop="clearAll(props.search)" class="multiselect__clear"
|
||||
v-if="newMember !== null && newMember.id !== 0">
|
||||
</div>
|
||||
</template>
|
||||
<span slot="noResult">Oops! No user found. Consider changing the search query.</span>
|
||||
</multiselect>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button type="submit" class="button is-success">
|
||||
<button class="button is-success" type="submit">
|
||||
<span class="icon is-small">
|
||||
<icon icon="plus"/>
|
||||
</span>
|
||||
|
@ -105,7 +105,7 @@
|
|||
</form>
|
||||
<table class="table is-striped is-hoverable is-fullwidth">
|
||||
<tbody>
|
||||
<tr v-for="m in team.members" :key="m.id">
|
||||
<tr :key="m.id" v-for="m in team.members">
|
||||
<td>{{ m.username }}</td>
|
||||
<td>
|
||||
<template v-if="m.id === userInfo.id">
|
||||
|
@ -127,8 +127,8 @@
|
|||
</template>
|
||||
</td>
|
||||
<td class="actions" v-if="userIsAdmin">
|
||||
<button @click="toggleUserType(m)" class="button buttonright is-primary"
|
||||
:class="{'is-loading': teamMemberService.loading}"
|
||||
<button :class="{'is-loading': teamMemberService.loading}" @click="toggleUserType(m)"
|
||||
class="button buttonright is-primary"
|
||||
v-if="m.id !== userInfo.id">
|
||||
Make
|
||||
<template v-if="!m.admin">
|
||||
|
@ -138,8 +138,8 @@
|
|||
Member
|
||||
</template>
|
||||
</button>
|
||||
<button @click="() => {member = m; showUserDeleteModal = true}" class="button is-danger"
|
||||
:class="{'is-loading': teamMemberService.loading}"
|
||||
<button :class="{'is-loading': teamMemberService.loading}" @click="() => {member = m; showUserDeleteModal = true}"
|
||||
class="button is-danger"
|
||||
v-if="m.id !== userInfo.id">
|
||||
<span class="icon is-small">
|
||||
<icon icon="trash-alt"/>
|
||||
|
@ -154,9 +154,9 @@
|
|||
|
||||
<!-- Team delete modal -->
|
||||
<modal
|
||||
v-if="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
@submit="deleteTeam()">
|
||||
@submit="deleteTeam()"
|
||||
v-if="showDeleteModal">
|
||||
<span slot="header">Delete the team</span>
|
||||
<p slot="text">Are you sure you want to delete this team and all of its members?<br/>
|
||||
All team members will loose access to lists and namespaces shared with this team.<br/>
|
||||
|
@ -164,9 +164,9 @@
|
|||
</modal>
|
||||
<!-- User delete modal -->
|
||||
<modal
|
||||
v-if="showUserDeleteModal"
|
||||
@close="showUserDeleteModal = false"
|
||||
@submit="deleteUser()">
|
||||
@submit="deleteUser()"
|
||||
v-if="showUserDeleteModal">
|
||||
<span slot="header">Remove a user from the team</span>
|
||||
<p slot="text">Are you sure you want to remove this user from the team?<br/>
|
||||
They will loose access to all lists and namespaces this team has access to.<br/>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</router-link>
|
||||
<h1>Teams</h1>
|
||||
<ul class="teams box">
|
||||
<li v-for="t in teams" :key="t.id">
|
||||
<li :key="t.id" v-for="t in teams">
|
||||
<router-link :to="{name: 'teams.edit', params: {id: t.id}}">
|
||||
{{ t.name }}
|
||||
</router-link>
|
||||
|
@ -45,6 +45,6 @@
|
|||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue