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
165
src/App.vue
165
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 -->
|
<!-- 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>
|
<div class="offline" style="height: 0;width: 0;"></div>
|
||||||
<nav
|
<nav
|
||||||
class="navbar main-theme is-fixed-top"
|
|
||||||
:class="{'has-background': background}"
|
:class="{'has-background': background}"
|
||||||
role="navigation"
|
|
||||||
aria-label="main navigation"
|
aria-label="main navigation"
|
||||||
|
class="navbar main-theme is-fixed-top"
|
||||||
|
role="navigation"
|
||||||
v-if="userAuthenticated && (userInfo && userInfo.type === authTypes.USER)">
|
v-if="userAuthenticated && (userInfo && userInfo.type === authTypes.USER)">
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<router-link :to="{name: 'home'}" class="navbar-item logo">
|
<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 alt="Vikunja" src="/images/logo-full-pride.svg" v-if="(new Date()).getMonth() === 5"/>
|
||||||
<img src="/images/logo-full.svg" alt="Vikunja" v-else/>
|
<img alt="Vikunja" src="/images/logo-full.svg" v-else/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<a
|
<a
|
||||||
|
:class="{'is-visible': !menuActive}"
|
||||||
@click="menuActive = true"
|
@click="menuActive = true"
|
||||||
class="menu-show-button"
|
class="menu-show-button"
|
||||||
:class="{'is-visible': !menuActive}"
|
|
||||||
>
|
>
|
||||||
<icon icon="bars"></icon>
|
<icon icon="bars"></icon>
|
||||||
</a>
|
</a>
|
||||||
|
@ -30,9 +30,9 @@
|
||||||
</a>
|
</a>
|
||||||
<div class="list-title" v-if="currentList.id">
|
<div class="list-title" v-if="currentList.id">
|
||||||
<h1
|
<h1
|
||||||
class="title"
|
:style="{ 'opacity': currentList.title === '' ? '0': '1' }"
|
||||||
:style="{ 'opacity': currentList.title === '' ? '0': '1' }">
|
class="title">
|
||||||
{{ currentList.title === '' ? 'Loading...': currentList.title}}
|
{{ currentList.title === '' ? 'Loading...' : currentList.title }}
|
||||||
</h1>
|
</h1>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'list.edit', params: { id: currentList.id } }"
|
:to="{ name: 'list.edit', params: { id: currentList.id } }"
|
||||||
|
@ -42,16 +42,16 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-end">
|
<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>
|
<p>There is an update for Vikunja available!</p>
|
||||||
<a @click="refreshApp()" class="button is-primary noshadow">Update Now</a>
|
<a @click="refreshApp()" class="button is-primary noshadow">Update Now</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="user">
|
<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 is-right is-active">
|
||||||
<div class="dropdown-trigger">
|
<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="username">{{ userInfo.username }}</span>
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<icon icon="chevron-down"/>
|
<icon icon="chevron-down"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -63,9 +63,16 @@
|
||||||
<router-link :to="{name: 'user.settings'}" class="dropdown-item">
|
<router-link :to="{name: 'user.settings'}" class="dropdown-item">
|
||||||
Settings
|
Settings
|
||||||
</router-link>
|
</router-link>
|
||||||
<a :href="imprintUrl" v-if="imprintUrl" class="dropdown-item" target="_blank">Imprint</a>
|
<a :href="imprintUrl" class="dropdown-item" target="_blank" v-if="imprintUrl">Imprint</a>
|
||||||
<a :href="privacyPolicyUrl" v-if="privacyPolicyUrl" class="dropdown-item" target="_blank">Privacy policy</a>
|
<a
|
||||||
<a @click="keyboardShortcutsActive = true" class="dropdown-item">Keyboard Shortcuts</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">
|
<a @click="logout()" class="dropdown-item">
|
||||||
Logout
|
Logout
|
||||||
</a>
|
</a>
|
||||||
|
@ -81,14 +88,14 @@
|
||||||
<icon icon="times"></icon>
|
<icon icon="times"></icon>
|
||||||
</a>
|
</a>
|
||||||
<div
|
<div
|
||||||
class="app-container"
|
|
||||||
:class="{'has-background': background}"
|
:class="{'has-background': background}"
|
||||||
:style="{'background-image': `url(${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">
|
<div class="menu top-menu">
|
||||||
<router-link :to="{name: 'home'}" class="logo">
|
<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>
|
</router-link>
|
||||||
<ul class="menu-list">
|
<ul class="menu-list">
|
||||||
<li>
|
<li>
|
||||||
|
@ -142,84 +149,87 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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
|
Collapse Menu
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<aside class="menu namespaces-lists">
|
<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">
|
<template v-for="n in namespaces">
|
||||||
<div :key="n.id">
|
<div :key="n.id">
|
||||||
<router-link
|
<router-link
|
||||||
v-tooltip.right="'Settings'"
|
|
||||||
:to="{name: 'namespace.edit', params: {id: n.id} }"
|
:to="{name: 'namespace.edit', params: {id: n.id} }"
|
||||||
class="nsettings"
|
class="nsettings"
|
||||||
v-if="n.id > 0">
|
v-if="n.id > 0"
|
||||||
|
v-tooltip.right="'Settings'">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<icon icon="cog"/>
|
<icon icon="cog"/>
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<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} }"
|
:to="{ name: 'list.create', params: { id: n.id} }"
|
||||||
class="nsettings"
|
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">
|
<span class="icon">
|
||||||
<icon icon="plus"/>
|
<icon icon="plus"/>
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<label
|
<label
|
||||||
|
:for="n.id + 'checker'"
|
||||||
class="menu-label"
|
class="menu-label"
|
||||||
v-tooltip="n.title + ' (' + n.lists.length + ')'"
|
v-tooltip="n.title + ' (' + n.lists.length + ')'">
|
||||||
:for="n.id + 'checker'">
|
|
||||||
<span class="name">
|
<span class="name">
|
||||||
<span
|
<span
|
||||||
|
:style="{ backgroundColor: n.hexColor }"
|
||||||
class="color-bubble"
|
class="color-bubble"
|
||||||
v-if="n.hexColor !== ''"
|
v-if="n.hexColor !== ''">
|
||||||
:style="{ backgroundColor: n.hexColor }">
|
|
||||||
</span>
|
</span>
|
||||||
{{n.title}} ({{n.lists.length}})
|
{{ n.title }} ({{ n.lists.length }})
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
:key="n.id + 'checker'"
|
|
||||||
type="checkbox"
|
|
||||||
checked="checked"
|
|
||||||
:id="n.id + 'checker'"
|
:id="n.id + 'checker'"
|
||||||
class="checkinput"/>
|
:key="n.id + 'checker'"
|
||||||
<div class="more-container" :key="n.id + 'child'">
|
checked="checked"
|
||||||
|
class="checkinput"
|
||||||
|
type="checkbox"/>
|
||||||
|
<div :key="n.id + 'child'" class="more-container">
|
||||||
<ul class="menu-list can-be-hidden">
|
<ul class="menu-list can-be-hidden">
|
||||||
<template v-for="l in n.lists">
|
<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
|
<!-- 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.-->
|
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
|
<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 class="name">
|
||||||
<span
|
<span
|
||||||
|
:style="{ backgroundColor: l.hexColor }"
|
||||||
class="color-bubble"
|
class="color-bubble"
|
||||||
v-if="l.hexColor !== ''"
|
v-if="l.hexColor !== ''">
|
||||||
:style="{ backgroundColor: l.hexColor }">
|
|
||||||
</span>
|
</span>
|
||||||
{{l.title}}
|
{{ l.title }}
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
<label class="hidden-hint" :for="n.id + 'checker'">
|
<label :for="n.id + 'checker'" class="hidden-hint">
|
||||||
Show hidden lists ({{n.lists.length}})...
|
Show hidden lists ({{ n.lists.length }})...
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</aside>
|
</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>
|
||||||
<div
|
<div
|
||||||
class="app-content"
|
|
||||||
:class="[
|
:class="[
|
||||||
{
|
{
|
||||||
'fullpage-overlay': fullpage,
|
'fullpage-overlay': fullpage,
|
||||||
|
@ -227,30 +237,31 @@
|
||||||
},
|
},
|
||||||
$route.name,
|
$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">
|
<transition name="fade">
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</transition>
|
</transition>
|
||||||
<a class="keyboard-shortcuts-button" @click="keyboardShortcutsActive = true">
|
<a @click="keyboardShortcutsActive = true" class="keyboard-shortcuts-button">
|
||||||
<icon icon="keyboard"/>
|
<icon icon="keyboard"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else-if="userAuthenticated && (userInfo && userInfo.type === authTypes.LINK_SHARE)"
|
|
||||||
class="link-share-container"
|
|
||||||
:class="{'has-background': background}"
|
:class="{'has-background': background}"
|
||||||
:style="{'background-image': `url(${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="container has-text-centered link-share-view">
|
||||||
<div class="column is-10 is-offset-1">
|
<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
|
<h1
|
||||||
class="title"
|
:style="{ 'opacity': currentList.title === '' ? '0': '1' }"
|
||||||
:style="{ 'opacity': currentList.title === '' ? '0': '1' }">
|
class="title">
|
||||||
{{ currentList.title === '' ? 'Loading...': currentList.title}}
|
{{ currentList.title === '' ? 'Loading...' : currentList.title }}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="box has-text-left view">
|
<div class="box has-text-left view">
|
||||||
<div class="logout">
|
<div class="logout">
|
||||||
|
@ -268,7 +279,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="noauth-container">
|
<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 is-info" v-if="motd !== ''">
|
||||||
<div class="message-header">
|
<div class="message-header">
|
||||||
<p>Info</p>
|
<p>Info</p>
|
||||||
|
@ -290,25 +301,25 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<keyboard-shortcuts v-if="keyboardShortcutsActive" @close="keyboardShortcutsActive = false"/>
|
<keyboard-shortcuts @close="keyboardShortcutsActive = false" v-if="keyboardShortcutsActive"/>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
import NamespaceService from './services/namespace'
|
import NamespaceService from './services/namespace'
|
||||||
import authTypes from './models/authTypes'
|
import authTypes from './models/authTypes'
|
||||||
import Rights from './models/rights.json'
|
import Rights from './models/rights.json'
|
||||||
|
|
||||||
import swEvents from './ServiceWorker/events'
|
import swEvents from './ServiceWorker/events'
|
||||||
import Notification from './components/misc/notification'
|
import Notification from './components/misc/notification'
|
||||||
import {CURRENT_LIST, IS_FULLPAGE, ONLINE} from './store/mutation-types'
|
import {CURRENT_LIST, IS_FULLPAGE, ONLINE} from './store/mutation-types'
|
||||||
import KeyboardShortcuts from './components/misc/keyboard-shortcuts'
|
import KeyboardShortcuts from './components/misc/keyboard-shortcuts'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'app',
|
name: 'app',
|
||||||
components: {
|
components: {
|
||||||
KeyboardShortcuts,
|
KeyboardShortcuts,
|
||||||
|
@ -332,8 +343,8 @@
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
// Check if the user is offline, show a message then
|
// Check if the user is offline, show a message then
|
||||||
this.$store.commit(ONLINE, navigator.onLine)
|
this.$store.commit(ONLINE, navigator.onLine)
|
||||||
window.addEventListener('online', () => 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('offline', () => this.$store.commit(ONLINE, navigator.onLine))
|
||||||
|
|
||||||
// Password reset
|
// Password reset
|
||||||
if (this.$route.query.userPasswordReset !== undefined) {
|
if (this.$route.query.userPasswordReset !== undefined) {
|
||||||
|
@ -377,10 +388,10 @@
|
||||||
if (navigator && navigator.serviceWorker) {
|
if (navigator && navigator.serviceWorker) {
|
||||||
navigator.serviceWorker.addEventListener(
|
navigator.serviceWorker.addEventListener(
|
||||||
'controllerchange', () => {
|
'controllerchange', () => {
|
||||||
if (this.refreshing) return;
|
if (this.refreshing) return
|
||||||
this.refreshing = true;
|
this.refreshing = true
|
||||||
window.location.reload();
|
window.location.reload()
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,17 +497,17 @@
|
||||||
},
|
},
|
||||||
showRefreshUI(e) {
|
showRefreshUI(e) {
|
||||||
console.log('recieved refresh event', e)
|
console.log('recieved refresh event', e)
|
||||||
this.registration = e.detail;
|
this.registration = e.detail
|
||||||
this.updateAvailable = true;
|
this.updateAvailable = true
|
||||||
},
|
},
|
||||||
refreshApp() {
|
refreshApp() {
|
||||||
this.updateExists = false;
|
this.updateExists = false
|
||||||
if (!this.registration || !this.registration.waiting) {
|
if (!this.registration || !this.registration.waiting) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
// Notify the service worker to actually do the update
|
// Notify the service worker to actually do the update
|
||||||
this.registration.waiting.postMessage('skipWaiting');
|
this.registration.waiting.postMessage('skipWaiting')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,69 +5,69 @@
|
||||||
workbox.routing.registerRoute(
|
workbox.routing.registerRoute(
|
||||||
// This regexp matches all files in precache-manifest
|
// This regexp matches all files in precache-manifest
|
||||||
new RegExp('.+\\.(css|json|js|eot|svg|ttf|woff|woff2|png|html|txt)$'),
|
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
|
// Always send api reqeusts through the network
|
||||||
workbox.routing.registerRoute(
|
workbox.routing.registerRoute(
|
||||||
new RegExp('api\\/v1\\/.*$'),
|
new RegExp('api\\/v1\\/.*$'),
|
||||||
new workbox.strategies.NetworkOnly()
|
new workbox.strategies.NetworkOnly(),
|
||||||
);
|
)
|
||||||
|
|
||||||
// This code listens for the user's confirmation to update the app.
|
// This code listens for the user's confirmation to update the app.
|
||||||
self.addEventListener('message', (e) => {
|
self.addEventListener('message', (e) => {
|
||||||
if (!e.data) {
|
if (!e.data) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (e.data) {
|
switch (e.data) {
|
||||||
case 'skipWaiting':
|
case 'skipWaiting':
|
||||||
self.skipWaiting();
|
self.skipWaiting()
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
// NOOP
|
// NOOP
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const getBearerToken = async () => {
|
const getBearerToken = async () => {
|
||||||
// we can't get a client that sent the current request, therefore we need
|
// we can't get a client that sent the current request, therefore we need
|
||||||
// to ask any controlled page for auth token
|
// to ask any controlled page for auth token
|
||||||
const allClients = await self.clients.matchAll();
|
const allClients = await self.clients.matchAll()
|
||||||
const client = allClients.filter(client => client.type === 'window')[0];
|
const client = allClients.filter(client => client.type === 'window')[0]
|
||||||
|
|
||||||
// if there is no page in scope, we can't get any token
|
// if there is no page in scope, we can't get any token
|
||||||
// and we indicate it with null value
|
// and we indicate it with null value
|
||||||
if(!client) {
|
if (!client) {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// to communicate with a page we will use MessageChannels
|
// to communicate with a page we will use MessageChannels
|
||||||
// they expose pipe-like interface, where a receiver of
|
// they expose pipe-like interface, where a receiver of
|
||||||
// a message uses one end of a port for messaging and
|
// a message uses one end of a port for messaging and
|
||||||
// we use the other end for listening
|
// we use the other end for listening
|
||||||
const channel = new MessageChannel();
|
const channel = new MessageChannel()
|
||||||
|
|
||||||
client.postMessage({
|
client.postMessage({
|
||||||
'action': 'getBearerToken'
|
'action': 'getBearerToken',
|
||||||
}, [channel.port1]);
|
}, [channel.port1])
|
||||||
|
|
||||||
// ports support only onmessage callback which
|
// ports support only onmessage callback which
|
||||||
// is cumbersome to use, so we wrap it with Promise
|
// is cumbersome to use, so we wrap it with Promise
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
channel.port2.onmessage = event => {
|
channel.port2.onmessage = event => {
|
||||||
if (event.data.error) {
|
if (event.data.error) {
|
||||||
console.error('Port error', event.error);
|
console.error('Port error', event.error)
|
||||||
reject(event.data.error);
|
reject(event.data.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(event.data.authToken);
|
resolve(event.data.authToken)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification action
|
// Notification action
|
||||||
self.addEventListener('notificationclick', function(event) {
|
self.addEventListener('notificationclick', function (event) {
|
||||||
const taskId = event.notification.data.taskId
|
const taskId = event.notification.data.taskId
|
||||||
event.notification.close()
|
event.notification.close()
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ self.addEventListener('notificationclick', function(event) {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${token}`,
|
'Authorization': `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({id: taskId, done: true})
|
body: JSON.stringify({id: taskId, done: true}),
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(r => {
|
.then(r => {
|
||||||
|
@ -106,7 +106,7 @@ self.addEventListener('notificationclick', function(event) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
workbox.core.clientsClaim();
|
workbox.core.clientsClaim()
|
||||||
// The precaching code provided by Workbox.
|
// The precaching code provided by Workbox.
|
||||||
self.__precacheManifest = [].concat(self.__precacheManifest || []);
|
self.__precacheManifest = [].concat(self.__precacheManifest || [])
|
||||||
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
|
workbox.precaching.precacheAndRoute(self.__precacheManifest, {})
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="color-picker-container">
|
<div class="color-picker-container">
|
||||||
<verte
|
<verte
|
||||||
v-model="color"
|
|
||||||
:menuPosition="menuPosition"
|
|
||||||
picker="square"
|
|
||||||
model="hex"
|
|
||||||
:enableAlpha="false"
|
:enableAlpha="false"
|
||||||
:rgbSliders="true"/>
|
:menuPosition="menuPosition"
|
||||||
|
:rgbSliders="true"
|
||||||
|
model="hex"
|
||||||
|
picker="square"
|
||||||
|
v-model="color"/>
|
||||||
<a @click="reset" class="reset">
|
<a @click="reset" class="reset">
|
||||||
Reset Color
|
Reset Color
|
||||||
</a>
|
</a>
|
||||||
|
@ -14,10 +14,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import verte from 'verte'
|
import verte from 'verte'
|
||||||
import 'verte/dist/verte.css'
|
import 'verte/dist/verte.css'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'colorPicker',
|
name: 'colorPicker',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
methods: {
|
methods: {
|
||||||
update() {
|
update() {
|
||||||
|
|
||||||
if(this.lastChangeTimeout !== null) {
|
if (this.lastChangeTimeout !== null) {
|
||||||
clearTimeout(this.lastChangeTimeout)
|
clearTimeout(this.lastChangeTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,5 +67,5 @@
|
||||||
this.update()
|
this.update()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<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">
|
<div class="tabs is-right" v-if="hasPreview && isEditEnabled">
|
||||||
<ul>
|
<ul>
|
||||||
<li :class="{'is-active': isPreviewActive}" v-if="isEditActive">
|
<li :class="{'is-active': isPreviewActive}" v-if="isEditActive">
|
||||||
|
@ -11,26 +11,32 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import VueEasymde from 'vue-easymde'
|
import VueEasymde from 'vue-easymde'
|
||||||
import EasyMDE from 'easymde'
|
import EasyMDE from 'easymde'
|
||||||
import marked from 'marked'
|
import marked from 'marked'
|
||||||
import DOMPurify from 'dompurify'
|
import DOMPurify from 'dompurify'
|
||||||
|
|
||||||
import AttachmentModel from '../../models/attachment'
|
import AttachmentModel from '../../models/attachment'
|
||||||
import AttachmentService from '../../services/attachment'
|
import AttachmentService from '../../services/attachment'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'editor',
|
name: 'editor',
|
||||||
components: {
|
components: {
|
||||||
VueEasymde
|
VueEasymde,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
|
@ -199,7 +205,7 @@
|
||||||
title: 'Guide',
|
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>',
|
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)
|
}, timeout)
|
||||||
},
|
},
|
||||||
replaceAt(str, index, replacement) {
|
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) {
|
findNthIndex(str, n) {
|
||||||
|
|
||||||
|
@ -305,7 +311,7 @@
|
||||||
checkboxNum++
|
checkboxNum++
|
||||||
return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checked} class="text-checkbox-${this._uid}"/>`
|
return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checked} class="text-checkbox-${this._uid}"/>`
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
this.preview = DOMPurify.sanitize(marked(this.text))
|
this.preview = DOMPurify.sanitize(marked(this.text))
|
||||||
|
@ -367,33 +373,33 @@
|
||||||
this.renderPreview()
|
this.renderPreview()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../../node_modules/easymde/dist/easymde.min.css';
|
@import '../../../node_modules/easymde/dist/easymde.min.css';
|
||||||
@import '../../styles/theme/variables';
|
@import '../../styles/theme/variables';
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
.tabs ul {
|
.tabs ul {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror {
|
.CodeMirror {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 1px solid $editor-border-color;
|
border: 1px solid $editor-border-color;
|
||||||
|
|
||||||
&-lines pre {
|
&-lines pre {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-scroll {
|
.CodeMirror-scroll {
|
||||||
padding: .5em;
|
padding: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-toolbar {
|
.editor-toolbar {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-top: 1px solid $editor-border-color;
|
border-top: 1px solid $editor-border-color;
|
||||||
border-left: 1px solid $editor-border-color;
|
border-left: 1px solid $editor-border-color;
|
||||||
|
@ -415,14 +421,14 @@
|
||||||
margin-left: -3px;
|
margin-left: -3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pre.CodeMirror-line {
|
pre.CodeMirror-line {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-header {
|
.cm-header {
|
||||||
font-family: $vikunja-font;
|
font-family: $vikunja-font;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="fancycheckbox" :class="{'is-disabled': disabled}">
|
<div :class="{'is-disabled': disabled}" class="fancycheckbox">
|
||||||
<input
|
<input
|
||||||
@change="updateData"
|
|
||||||
type="checkbox"
|
|
||||||
:id="checkBoxId"
|
|
||||||
:checked="checked"
|
:checked="checked"
|
||||||
|
:disabled="disabled"
|
||||||
|
:id="checkBoxId"
|
||||||
|
@change="updateData"
|
||||||
style="display: none;"
|
style="display: none;"
|
||||||
:disabled="disabled"/>
|
type="checkbox"/>
|
||||||
<label :for="checkBoxId" class="check">
|
<label :for="checkBoxId" class="check">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18">
|
<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>
|
<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>
|
<polyline points="1 9 7 14 15 4"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
<span>
|
<span>
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'fancycheckbox',
|
name: 'fancycheckbox',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -56,5 +57,5 @@
|
||||||
this.$emit('change', e.target.checked)
|
this.$emit('change', e.target.checked)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
:class="{ 'is-loading': backgroundService.loading}"
|
||||||
class="card list-background-setting loader-container"
|
class="card list-background-setting loader-container"
|
||||||
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
|
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled">
|
||||||
:class="{ 'is-loading': backgroundService.loading}">
|
|
||||||
<header class="card-header">
|
<header class="card-header">
|
||||||
<p class="card-header-title">
|
<p class="card-header-title">
|
||||||
Set list background
|
Set list background
|
||||||
|
@ -11,47 +11,47 @@
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="content" v-if="uploadBackgroundEnabled">
|
<div class="content" v-if="uploadBackgroundEnabled">
|
||||||
<input
|
<input
|
||||||
type="file"
|
|
||||||
ref="backgroundUploadInput"
|
|
||||||
@change="uploadBackground"
|
@change="uploadBackground"
|
||||||
class="is-hidden"
|
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
|
class="is-hidden"
|
||||||
|
ref="backgroundUploadInput"
|
||||||
|
type="file"
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
class="button is-primary"
|
|
||||||
:class="{'is-loading': backgroundUploadService.loading}"
|
:class="{'is-loading': backgroundUploadService.loading}"
|
||||||
@click="$refs.backgroundUploadInput.click()"
|
@click="$refs.backgroundUploadInput.click()"
|
||||||
|
class="button is-primary"
|
||||||
>
|
>
|
||||||
Choose a background from your pc
|
Choose a background from your pc
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="content" v-if="unsplashBackgroundEnabled">
|
<div class="content" v-if="unsplashBackgroundEnabled">
|
||||||
<input
|
<input
|
||||||
type="text"
|
|
||||||
placeholder="Search for a background..."
|
|
||||||
class="input is-expanded"
|
|
||||||
v-model="backgroundSearchTerm"
|
|
||||||
@keyup="() => newBackgroundSearch()"
|
|
||||||
:class="{'is-loading': backgroundService.loading}"
|
: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>
|
<p class="unsplash-link"><a href="https://unsplash.com" target="_blank">Powered by Unsplash</a></p>
|
||||||
<div class="image-search-result">
|
<div class="image-search-result">
|
||||||
<a
|
<a
|
||||||
|
:key="im.id"
|
||||||
|
:style="{'background-image': `url(${backgroundThumbs[im.id]})`}"
|
||||||
@click="() => setBackground(im.id)"
|
@click="() => setBackground(im.id)"
|
||||||
class="image"
|
class="image"
|
||||||
v-for="im in backgroundSearchResult"
|
v-for="im in backgroundSearchResult">
|
||||||
:style="{'background-image': `url(${backgroundThumbs[im.id]})`}"
|
<a :href="`https://unsplash.com/@${im.info.author}`" class="info">
|
||||||
:key="im.id">
|
|
||||||
<a class="info" :href="`https://unsplash.com/@${im.info.author}`">
|
|
||||||
{{ im.info.authorName }}
|
{{ im.info.authorName }}
|
||||||
</a>
|
</a>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<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"
|
: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">
|
<template v-if="backgroundService.loading">
|
||||||
Loading...
|
Loading...
|
||||||
|
@ -66,11 +66,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BackgroundUnsplashService from '../../../services/backgroundUnsplash'
|
import BackgroundUnsplashService from '../../../services/backgroundUnsplash'
|
||||||
import BackgroundUploadService from '../../../services/backgroundUpload'
|
import BackgroundUploadService from '../../../services/backgroundUpload'
|
||||||
import {CURRENT_LIST} from '../../../store/mutation-types'
|
import {CURRENT_LIST} from '@/store/mutation-types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'background-settings',
|
name: 'background-settings',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -88,7 +88,7 @@
|
||||||
listId: {
|
listId: {
|
||||||
default: 0,
|
default: 0,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
unsplashBackgroundEnabled() {
|
unsplashBackgroundEnabled() {
|
||||||
|
@ -171,5 +171,5 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -13,11 +13,11 @@
|
||||||
<label class="label">Due Date</label>
|
<label class="label">Due Date</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<flat-pickr
|
<flat-pickr
|
||||||
class="input"
|
|
||||||
:config="flatPickerConfig"
|
:config="flatPickerConfig"
|
||||||
|
@on-close="setDueDateFilter"
|
||||||
|
class="input"
|
||||||
placeholder="Due Date Range"
|
placeholder="Due Date Range"
|
||||||
v-model="filters.dueDate"
|
v-model="filters.dueDate"
|
||||||
@on-close="setDueDateFilter"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,11 +26,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Fancycheckbox from '../../input/fancycheckbox'
|
import Fancycheckbox from '../../input/fancycheckbox'
|
||||||
import flatPickr from 'vue-flatpickr-component'
|
import flatPickr from 'vue-flatpickr-component'
|
||||||
import 'flatpickr/dist/flatpickr.css'
|
import 'flatpickr/dist/flatpickr.css'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'filters',
|
name: 'filters',
|
||||||
components: {
|
components: {
|
||||||
Fancycheckbox,
|
Fancycheckbox,
|
||||||
|
@ -66,13 +66,13 @@
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value(newVal) {
|
value(newVal) {
|
||||||
this.$set(this, 'params', newVal)
|
this.$set(this, 'params', newVal)
|
||||||
this.prepareDone()
|
this.prepareDone()
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
change() {
|
change() {
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
},
|
},
|
||||||
prepareDone() {
|
prepareDone() {
|
||||||
// Set filters.done based on params
|
// Set filters.done based on params
|
||||||
if(typeof this.params.filter_by !== 'undefined') {
|
if (typeof this.params.filter_by !== 'undefined') {
|
||||||
let foundDone = false
|
let foundDone = false
|
||||||
this.params.filter_by.forEach((f, i) => {
|
this.params.filter_by.forEach((f, i) => {
|
||||||
if (f === 'done') {
|
if (f === 'done') {
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
|
|
||||||
const parts = this.filters.dueDate.split(' to ')
|
const parts = this.filters.dueDate.split(' to ')
|
||||||
|
|
||||||
if(parts.length < 2) {
|
if (parts.length < 2) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,5 +148,5 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
<template v-if="isMigrating === false && message === '' && lastMigrationDate === null">
|
<template v-if="isMigrating === false && message === '' && lastMigrationDate === null">
|
||||||
<p>To authorize Vikunja to access your {{ name }} Account, click the button below.</p>
|
<p>To authorize Vikunja to access your {{ name }} Account, click the button below.</p>
|
||||||
<a
|
<a
|
||||||
:href="authUrl"
|
|
||||||
class="button is-primary"
|
|
||||||
:class="{'is-loading': migrationService.loading}"
|
:class="{'is-loading': migrationService.loading}"
|
||||||
:disabled="migrationService.loading">
|
:disabled="migrationService.loading"
|
||||||
|
:href="authUrl"
|
||||||
|
class="button is-primary">
|
||||||
Get Started
|
Get Started
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
class="migration-in-progress-container"
|
class="migration-in-progress-container"
|
||||||
v-else-if="isMigrating === true && message === '' && lastMigrationDate === null">
|
v-else-if="isMigrating === true && message === '' && lastMigrationDate === null">
|
||||||
<div class="migration-in-progress">
|
<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">
|
<div class="progress-dots">
|
||||||
<span></span>
|
<span></span>
|
||||||
<span></span>
|
<span></span>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
<span></span>
|
<span></span>
|
||||||
<span></span>
|
<span></span>
|
||||||
</div>
|
</div>
|
||||||
<img src="/images/logo.svg" alt="Vikunja">
|
<img alt="Vikunja" src="/images/logo.svg">
|
||||||
</div>
|
</div>
|
||||||
<p>Importing in progress, hang tight...</p>
|
<p>Importing in progress, hang tight...</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
Are you sure?
|
Are you sure?
|
||||||
</p>
|
</p>
|
||||||
<div class="buttons">
|
<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>
|
<router-link :to="{name: 'home'}" class="button is-danger is-outlined">Cancel</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,9 +54,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AbstractMigrationService from '../../services/migrator/abstractMigrationService'
|
import AbstractMigrationService from '../../services/migrator/abstractMigrationService'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'migration',
|
name: 'migration',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -122,5 +122,5 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'error'
|
name: 'error',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="modal-mask keyboard-shortcuts-modal">
|
<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="modal-content">
|
||||||
<div class="card has-background-white has-no-shadow">
|
<div class="card has-background-white has-no-shadow">
|
||||||
<header class="card-header">
|
<header class="card-header">
|
||||||
|
@ -88,12 +88,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'keyboard-shortcuts',
|
name: 'keyboard-shortcuts',
|
||||||
methods: {
|
methods: {
|
||||||
close() {
|
close() {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="legal-links">
|
<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>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'legal',
|
name: 'legal',
|
||||||
computed: mapState({
|
computed: mapState({
|
||||||
imprintUrl: state => state.config.legal.imprintUrl,
|
imprintUrl: state => state.config.legal.imprintUrl,
|
||||||
privacyPolicyUrl: state => state.config.legal.privacyPolicyUrl,
|
privacyPolicyUrl: state => state.config.legal.privacyPolicyUrl,
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,17 +3,17 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'loading'
|
name: 'loading',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.loader-container {
|
.loader-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 600px;
|
min-width: 600px;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -3,9 +3,9 @@
|
||||||
<template slot="body" slot-scope="props">
|
<template slot="body" slot-scope="props">
|
||||||
<div :class="['vue-notification-template', 'vue-notification', props.item.type]" @click="close(props)">
|
<div :class="['vue-notification-template', 'vue-notification', props.item.type]" @click="close(props)">
|
||||||
<div
|
<div
|
||||||
v-if="props.item.title"
|
|
||||||
class="notification-title"
|
class="notification-title"
|
||||||
v-html="props.item.title"
|
v-html="props.item.title"
|
||||||
|
v-if="props.item.title"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -13,11 +13,13 @@
|
||||||
v-html="props.item.text"
|
v-html="props.item.text"
|
||||||
>
|
>
|
||||||
</div>
|
</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
|
<button
|
||||||
class="button noshadow is-small"
|
:key="'action_'+i"
|
||||||
@click="action.callback"
|
@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 }}
|
{{ action.title }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,23 +29,23 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'notification',
|
name: 'notification',
|
||||||
methods: {
|
methods: {
|
||||||
close(props) {
|
close(props) {
|
||||||
props.close()
|
props.close()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.vue-notification {
|
.vue-notification {
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
margin-top: .5em;
|
margin-top: .5em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,12 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="user" :class="{'is-inline': isInline}">
|
<div :class="{'is-inline': isInline}" class="user">
|
||||||
<img :src="user.getAvatarUrl(avatarSize)" class="avatar" alt="" v-tooltip="user.username" :width="avatarSize" :height="avatarSize"/>
|
<img
|
||||||
<span v-if="showUsername" class="username">{{ user.username }}</span>
|
: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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'user',
|
name: 'user',
|
||||||
props: {
|
props: {
|
||||||
user: {
|
user: {
|
||||||
|
@ -29,11 +35,11 @@
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.user {
|
.user {
|
||||||
margin: .5em;
|
margin: .5em;
|
||||||
|
|
||||||
&.is-inline {
|
&.is-inline {
|
||||||
|
@ -48,5 +54,5 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
<slot name="text"></slot>
|
<slot name="text"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="button is-danger is-inverted noshadow" @click="$emit('close')">Cancel</button>
|
<button @click="$emit('close')" class="button is-danger is-inverted noshadow">Cancel</button>
|
||||||
<button class="button is-success noshadow" @click="$emit('submit')">Do it!</button>
|
<button @click="$emit('submit')" class="button is-success noshadow">Do it!</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'modal',
|
name: 'modal',
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
|
@ -29,6 +29,6 @@
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
<template>
|
<template>
|
||||||
<multiselect
|
<multiselect
|
||||||
v-model="namespace"
|
|
||||||
:options="namespaces"
|
|
||||||
:multiple="false"
|
|
||||||
:searchable="true"
|
|
||||||
:loading="namespaceService.loading"
|
|
||||||
:internal-search="true"
|
:internal-search="true"
|
||||||
|
:loading="namespaceService.loading"
|
||||||
|
:multiple="false"
|
||||||
|
:options="namespaces"
|
||||||
|
:searchable="true"
|
||||||
|
:showNoOptions="false"
|
||||||
@search-change="findNamespaces"
|
@search-change="findNamespaces"
|
||||||
@select="select"
|
@select="select"
|
||||||
placeholder="Search for a namespace..."
|
|
||||||
:showNoOptions="false"
|
|
||||||
label="title"
|
label="title"
|
||||||
track-by="id">
|
placeholder="Search for a namespace..."
|
||||||
|
track-by="id"
|
||||||
|
v-model="namespace">
|
||||||
<template slot="clear" slot-scope="props">
|
<template slot="clear" slot-scope="props">
|
||||||
<div
|
<div
|
||||||
class="multiselect__clear" v-if="namespace.id !== 0"
|
@mousedown.prevent.stop="clearAll(props.search)" class="multiselect__clear"
|
||||||
@mousedown.prevent.stop="clearAll(props.search)"></div>
|
v-if="namespace.id !== 0"></div>
|
||||||
</template>
|
</template>
|
||||||
<span slot="noResult">No namespace found. Consider changing the search query.</span>
|
<span slot="noResult">No namespace found. Consider changing the search query.</span>
|
||||||
</multiselect>
|
</multiselect>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NamespaceService from '../../services/namespace'
|
import NamespaceService from '../../services/namespace'
|
||||||
import NamespaceModel from '../../models/namespace'
|
import NamespaceModel from '../../models/namespace'
|
||||||
import LoadingComponent from '../misc/loading'
|
import LoadingComponent from '../misc/loading'
|
||||||
import ErrorComponent from '../misc/error'
|
import ErrorComponent from '../misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'namespace-search',
|
name: 'namespace-search',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -69,5 +69,5 @@
|
||||||
this.$emit('selected', namespace)
|
this.$emit('selected', namespace)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button type="submit" class="button is-success">
|
<button class="button is-success" type="submit">
|
||||||
Share
|
Share
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,14 +36,14 @@
|
||||||
<th>Delete</th>
|
<th>Delete</th>
|
||||||
</tr>
|
</tr>
|
||||||
<template v-if="linkShares.length > 0">
|
<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>
|
<td>
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control">
|
<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>
|
||||||
<div class="control">
|
<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">
|
<span class="icon">
|
||||||
<icon icon="paste"/>
|
<icon icon="paste"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -75,7 +75,8 @@
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td class="actions">
|
<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">
|
<span class="icon">
|
||||||
<icon icon="trash-alt"/>
|
<icon icon="trash-alt"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -88,9 +89,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<modal
|
<modal
|
||||||
v-if="showDeleteModal"
|
|
||||||
@close="showDeleteModal = false"
|
@close="showDeleteModal = false"
|
||||||
@submit="remove()">
|
@submit="remove()"
|
||||||
|
v-if="showDeleteModal">
|
||||||
<span slot="header">Remove a link share</span>
|
<span slot="header">Remove a link share</span>
|
||||||
<p slot="text">Are you sure you want to remove this link share?<br/>
|
<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/>
|
It will no longer be possible to access this list with this link share.<br/>
|
||||||
|
@ -100,15 +101,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import rights from '../../models/rights'
|
import rights from '../../models/rights'
|
||||||
|
|
||||||
import LinkShareService from '../../services/linkShare'
|
import LinkShareService from '../../services/linkShare'
|
||||||
import LinkShareModel from '../../models/linkShare'
|
import LinkShareModel from '../../models/linkShare'
|
||||||
|
|
||||||
import copy from 'copy-to-clipboard'
|
import copy from 'copy-to-clipboard'
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'linkSharing',
|
name: 'linkSharing',
|
||||||
props: {
|
props: {
|
||||||
listId: {
|
listId: {
|
||||||
|
@ -137,7 +138,7 @@
|
||||||
watch: {
|
watch: {
|
||||||
listId: () => { // watch it
|
listId: () => { // watch it
|
||||||
this.load()
|
this.load()
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: mapState({
|
computed: mapState({
|
||||||
frontendUrl: state => state.config.frontendUrl,
|
frontendUrl: state => state.config.frontendUrl,
|
||||||
|
@ -190,5 +191,5 @@
|
||||||
return this.frontendUrl + 'share/' + hash + '/auth'
|
return this.frontendUrl + 'share/' + hash + '/auth'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="card is-fullwidth">
|
<div class="card is-fullwidth">
|
||||||
<header class="card-header">
|
<header class="card-header">
|
||||||
<p class="card-header-title">
|
<p class="card-header-title">
|
||||||
Shared with these {{shareType}}s
|
Shared with these {{ shareType }}s
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
<div class="card-content content sharables-list">
|
<div class="card-content content sharables-list">
|
||||||
|
@ -10,25 +10,30 @@
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<p class="control is-expanded" v-bind:class="{ 'is-loading': searchService.loading}">
|
<p class="control is-expanded" v-bind:class="{ 'is-loading': searchService.loading}">
|
||||||
<multiselect
|
<multiselect
|
||||||
v-model="sharable"
|
|
||||||
:options="found"
|
|
||||||
:multiple="false"
|
|
||||||
:searchable="true"
|
|
||||||
:loading="searchService.loading"
|
|
||||||
:internal-search="true"
|
:internal-search="true"
|
||||||
|
:label="searchLabel"
|
||||||
|
:loading="searchService.loading"
|
||||||
|
:multiple="false"
|
||||||
|
:options="found"
|
||||||
|
:searchable="true"
|
||||||
|
:showNoOptions="false"
|
||||||
@search-change="find"
|
@search-change="find"
|
||||||
placeholder="Type to search..."
|
placeholder="Type to search..."
|
||||||
:showNoOptions="false"
|
track-by="id"
|
||||||
:label="searchLabel"
|
v-model="sharable">
|
||||||
track-by="id">
|
|
||||||
<template slot="clear" slot-scope="props">
|
<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>
|
</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>
|
</multiselect>
|
||||||
</p>
|
</p>
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<button type="submit" class="button is-success">
|
<button class="button is-success" type="submit">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<icon icon="plus"/>
|
<icon icon="plus"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -39,9 +44,9 @@
|
||||||
</form>
|
</form>
|
||||||
<table class="table is-striped is-hoverable is-fullwidth">
|
<table class="table is-striped is-hoverable is-fullwidth">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="s in sharables" :key="s.id">
|
<tr :key="s.id" v-for="s in sharables">
|
||||||
<template v-if="shareType === 'user'">
|
<template v-if="shareType === 'user'">
|
||||||
<td>{{s.username}}</td>
|
<td>{{ s.username }}</td>
|
||||||
<td>
|
<td>
|
||||||
<template v-if="s.id === userInfo.id">
|
<template v-if="s.id === userInfo.id">
|
||||||
<b class="is-success">You</b>
|
<b class="is-success">You</b>
|
||||||
|
@ -51,7 +56,7 @@
|
||||||
<template v-if="shareType === 'team'">
|
<template v-if="shareType === 'team'">
|
||||||
<td>
|
<td>
|
||||||
<router-link :to="{name: 'teams.edit', params: {id: s.id}}">
|
<router-link :to="{name: 'teams.edit', params: {id: s.id}}">
|
||||||
{{s.name}}
|
{{ s.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</td>
|
</td>
|
||||||
</template>
|
</template>
|
||||||
|
@ -77,13 +82,16 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="actions" v-if="userIsAdmin">
|
<td class="actions" v-if="userIsAdmin">
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select @change="toggleType(s)" v-model="selectedRight[s.id]" class="button buttonright">
|
<select @change="toggleType(s)" class="button buttonright" v-model="selectedRight[s.id]">
|
||||||
<option :value="rights.READ" :selected="s.right === rights.READ">Read only</option>
|
<option :selected="s.right === rights.READ" :value="rights.READ">Read only</option>
|
||||||
<option :value="rights.READ_WRITE" :selected="s.right === rights.READ_WRITE">Read & write</option>
|
<option :selected="s.right === rights.READ_WRITE" :value="rights.READ_WRITE">Read &
|
||||||
<option :value="rights.ADMIN" :selected="s.right === rights.ADMIN">Admin</option>
|
write
|
||||||
|
</option>
|
||||||
|
<option :selected="s.right === rights.ADMIN" :value="rights.ADMIN">Admin</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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">
|
<span class="icon is-small">
|
||||||
<icon icon="trash-alt"/>
|
<icon icon="trash-alt"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -95,38 +103,38 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<modal
|
<modal
|
||||||
v-if="showDeleteModal"
|
|
||||||
@close="showDeleteModal = false"
|
@close="showDeleteModal = false"
|
||||||
@submit="deleteSharable()">
|
@submit="deleteSharable()"
|
||||||
<span slot="header">Remove a {{shareType}} from the {{typeString}}</span>
|
v-if="showDeleteModal">
|
||||||
<p slot="text">Are you sure you want to remove this {{shareType}} from the {{typeString}}?<br/>
|
<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>
|
<b>This CANNOT BE UNDONE!</b></p>
|
||||||
</modal>
|
</modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
import UserNamespaceService from '../../services/userNamespace'
|
import UserNamespaceService from '../../services/userNamespace'
|
||||||
import UserNamespaceModel from '../../models/userNamespace'
|
import UserNamespaceModel from '../../models/userNamespace'
|
||||||
import UserListModel from '../../models/userList'
|
import UserListModel from '../../models/userList'
|
||||||
import UserListService from '../../services/userList'
|
import UserListService from '../../services/userList'
|
||||||
import UserService from '../../services/user'
|
import UserService from '../../services/user'
|
||||||
import UserModel from '../../models/user'
|
import UserModel from '../../models/user'
|
||||||
|
|
||||||
import TeamNamespaceService from '../../services/teamNamespace'
|
import TeamNamespaceService from '../../services/teamNamespace'
|
||||||
import TeamNamespaceModel from '../../models/teamNamespace'
|
import TeamNamespaceModel from '../../models/teamNamespace'
|
||||||
import TeamListModel from '../../models/teamList'
|
import TeamListModel from '../../models/teamList'
|
||||||
import TeamListService from '../../services/teamList'
|
import TeamListService from '../../services/teamList'
|
||||||
import TeamService from '../../services/team'
|
import TeamService from '../../services/team'
|
||||||
import TeamModel from '../../models/team'
|
import TeamModel from '../../models/team'
|
||||||
|
|
||||||
import rights from '../../models/rights'
|
import rights from '../../models/rights'
|
||||||
import LoadingComponent from '../misc/loading'
|
import LoadingComponent from '../misc/loading'
|
||||||
import ErrorComponent from '../misc/error'
|
import ErrorComponent from '../misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'userTeamShare',
|
name: 'userTeamShare',
|
||||||
props: {
|
props: {
|
||||||
type: {
|
type: {
|
||||||
|
@ -172,7 +180,7 @@
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
computed: mapState({
|
computed: mapState({
|
||||||
userInfo: state => state.auth.info
|
userInfo: state => state.auth.info,
|
||||||
}),
|
}),
|
||||||
created() {
|
created() {
|
||||||
|
|
||||||
|
@ -192,8 +200,7 @@
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown type: ' + this.type)
|
throw new Error('Unknown type: ' + this.type)
|
||||||
}
|
}
|
||||||
}
|
} else if (this.shareType === 'team') {
|
||||||
else if (this.shareType === 'team') {
|
|
||||||
this.searchService = new TeamService()
|
this.searchService = new TeamService()
|
||||||
this.sharable = new TeamModel()
|
this.sharable = new TeamModel()
|
||||||
this.searchLabel = 'name'
|
this.searchLabel = 'name'
|
||||||
|
@ -251,7 +258,7 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
add(admin) {
|
add(admin) {
|
||||||
if(admin === null) {
|
if (admin === null) {
|
||||||
admin = false
|
admin = false
|
||||||
}
|
}
|
||||||
this.stuffModel.right = rights.READ
|
this.stuffModel.right = rights.READ
|
||||||
|
@ -307,7 +314,7 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
find(query) {
|
find(query) {
|
||||||
if(query === '') {
|
if (query === '') {
|
||||||
this.$set(this, 'found', [])
|
this.$set(this, 'found', [])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -320,9 +327,9 @@
|
||||||
this.error(e, this)
|
this.error(e, this)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
clearAll () {
|
clearAll() {
|
||||||
this.$set(this, 'found', [])
|
this.$set(this, 'found', [])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,45 +4,45 @@
|
||||||
<label class="label" for="tasktext">Task Text</label>
|
<label class="label" for="tasktext">Task Text</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
v-focus
|
|
||||||
:class="{ 'disabled': taskService.loading}"
|
:class="{ 'disabled': taskService.loading}"
|
||||||
:disabled="taskService.loading"
|
:disabled="taskService.loading"
|
||||||
|
@change="editTaskSubmit()"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
|
||||||
id="tasktext"
|
id="tasktext"
|
||||||
placeholder="The task text is here..."
|
placeholder="The task text is here..."
|
||||||
v-model="taskEditTask.title"
|
type="text"
|
||||||
@change="editTaskSubmit()"/>
|
v-focus
|
||||||
|
v-model="taskEditTask.title"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="taskdescription">Description</label>
|
<label class="label" for="taskdescription">Description</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<editor
|
<editor
|
||||||
placeholder="The tasks description goes here..."
|
|
||||||
id="taskdescription"
|
|
||||||
v-model="taskEditTask.description"
|
|
||||||
:preview-is-default="false"
|
:preview-is-default="false"
|
||||||
|
id="taskdescription"
|
||||||
|
placeholder="The tasks description goes here..."
|
||||||
v-if="editorActive"
|
v-if="editorActive"
|
||||||
|
v-model="taskEditTask.description"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<b>Reminder Dates</b>
|
<b>Reminder Dates</b>
|
||||||
<reminders v-model="taskEditTask.reminderDates" @change="editTaskSubmit()"/>
|
<reminders @change="editTaskSubmit()" v-model="taskEditTask.reminderDates"/>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="taskduedate">Due Date</label>
|
<label class="label" for="taskduedate">Due Date</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<flat-pickr
|
<flat-pickr
|
||||||
:class="{ 'disabled': taskService.loading}"
|
:class="{ 'disabled': taskService.loading}"
|
||||||
class="input"
|
|
||||||
:disabled="taskService.loading"
|
|
||||||
v-model="taskEditTask.dueDate"
|
|
||||||
:config="flatPickerConfig"
|
:config="flatPickerConfig"
|
||||||
|
:disabled="taskService.loading"
|
||||||
@on-close="editTaskSubmit()"
|
@on-close="editTaskSubmit()"
|
||||||
|
class="input"
|
||||||
id="taskduedate"
|
id="taskduedate"
|
||||||
placeholder="The tasks due date is here...">
|
placeholder="The tasks due date is here..."
|
||||||
|
v-model="taskEditTask.dueDate">
|
||||||
</flat-pickr>
|
</flat-pickr>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,25 +53,25 @@
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<flat-pickr
|
<flat-pickr
|
||||||
:class="{ 'disabled': taskService.loading}"
|
:class="{ 'disabled': taskService.loading}"
|
||||||
class="input"
|
|
||||||
:disabled="taskService.loading"
|
|
||||||
v-model="taskEditTask.startDate"
|
|
||||||
:config="flatPickerConfig"
|
:config="flatPickerConfig"
|
||||||
|
:disabled="taskService.loading"
|
||||||
@on-close="editTaskSubmit()"
|
@on-close="editTaskSubmit()"
|
||||||
|
class="input"
|
||||||
id="taskduedate"
|
id="taskduedate"
|
||||||
placeholder="Start date">
|
placeholder="Start date"
|
||||||
|
v-model="taskEditTask.startDate">
|
||||||
</flat-pickr>
|
</flat-pickr>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<flat-pickr
|
<flat-pickr
|
||||||
:class="{ 'disabled': taskService.loading}"
|
:class="{ 'disabled': taskService.loading}"
|
||||||
class="input"
|
|
||||||
:disabled="taskService.loading"
|
|
||||||
v-model="taskEditTask.endDate"
|
|
||||||
:config="flatPickerConfig"
|
:config="flatPickerConfig"
|
||||||
|
:disabled="taskService.loading"
|
||||||
@on-close="editTaskSubmit()"
|
@on-close="editTaskSubmit()"
|
||||||
|
class="input"
|
||||||
id="taskduedate"
|
id="taskduedate"
|
||||||
placeholder="End date">
|
placeholder="End date"
|
||||||
|
v-model="taskEditTask.endDate">
|
||||||
</flat-pickr>
|
</flat-pickr>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -79,20 +79,20 @@
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="">Repeat after</label>
|
<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>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="">Priority</label>
|
<label class="label" for="">Priority</label>
|
||||||
<div class="control priority-select">
|
<div class="control priority-select">
|
||||||
<priority-select v-model="taskEditTask.priority" @change="editTaskSubmit()"/>
|
<priority-select @change="editTaskSubmit()" v-model="taskEditTask.priority"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Percent Done</label>
|
<label class="label">Percent Done</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<percent-done-select v-model="taskEditTask.percentDone" @change="editTaskSubmit()"/>
|
<percent-done-select @change="editTaskSubmit()" v-model="taskEditTask.percentDone"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -106,8 +106,8 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="">Assignees</label>
|
<label class="label" for="">Assignees</label>
|
||||||
<ul class="assingees">
|
<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.username }}
|
||||||
<a @click="deleteAssigneeByIndex(index)">
|
<a @click="deleteAssigneeByIndex(index)">
|
||||||
<icon icon="times"/>
|
<icon icon="times"/>
|
||||||
</a>
|
</a>
|
||||||
|
@ -118,9 +118,9 @@
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<edit-assignees
|
<edit-assignees
|
||||||
:task-id="taskEditTask.id"
|
:initial-assignees="taskEditTask.assignees"
|
||||||
:list-id="taskEditTask.listId"
|
:list-id="taskEditTask.listId"
|
||||||
:initial-assignees="taskEditTask.assignees"/>
|
:task-id="taskEditTask.id"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -132,13 +132,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<related-tasks
|
<related-tasks
|
||||||
class="is-narrow"
|
|
||||||
:task-id="task.id"
|
|
||||||
:list-id="task.listId"
|
|
||||||
:initial-related-tasks="task.relatedTasks"
|
: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
|
Save
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
@ -146,25 +146,25 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import flatPickr from 'vue-flatpickr-component'
|
import flatPickr from 'vue-flatpickr-component'
|
||||||
import 'flatpickr/dist/flatpickr.css'
|
import 'flatpickr/dist/flatpickr.css'
|
||||||
|
|
||||||
import ListService from '../../services/list'
|
import ListService from '../../services/list'
|
||||||
import TaskService from '../../services/task'
|
import TaskService from '../../services/task'
|
||||||
import TaskModel from '../../models/task'
|
import TaskModel from '../../models/task'
|
||||||
import priorities from '../../models/priorities'
|
import priorities from '../../models/priorities'
|
||||||
import PrioritySelect from './partials/prioritySelect'
|
import PrioritySelect from './partials/prioritySelect'
|
||||||
import PercentDoneSelect from './partials/percentDoneSelect'
|
import PercentDoneSelect from './partials/percentDoneSelect'
|
||||||
import EditLabels from './partials/editLabels'
|
import EditLabels from './partials/editLabels'
|
||||||
import EditAssignees from './partials/editAssignees'
|
import EditAssignees from './partials/editAssignees'
|
||||||
import RelatedTasks from './partials/relatedTasks'
|
import RelatedTasks from './partials/relatedTasks'
|
||||||
import RepeatAfter from './partials/repeatAfter'
|
import RepeatAfter from './partials/repeatAfter'
|
||||||
import Reminders from './partials/reminders'
|
import Reminders from './partials/reminders'
|
||||||
import ColorPicker from '../input/colorPicker'
|
import ColorPicker from '../input/colorPicker'
|
||||||
import LoadingComponent from '../misc/loading'
|
import LoadingComponent from '../misc/loading'
|
||||||
import ErrorComponent from '../misc/error'
|
import ErrorComponent from '../misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'edit-task',
|
name: 'edit-task',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -209,13 +209,13 @@
|
||||||
task: {
|
task: {
|
||||||
type: TaskModel,
|
type: TaskModel,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
task() {
|
task() {
|
||||||
this.taskEditTask = this.task
|
this.taskEditTask = this.task
|
||||||
this.initTaskFields()
|
this.initTaskFields()
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.listService = new ListService()
|
this.listService = new ListService()
|
||||||
|
@ -248,11 +248,11 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
form {
|
form {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -2,21 +2,22 @@
|
||||||
<div class="gantt-chart box">
|
<div class="gantt-chart box">
|
||||||
<div class="dates">
|
<div class="dates">
|
||||||
<template v-for="(y, yk) in days">
|
<template v-for="(y, yk) in days">
|
||||||
<div class="months" :key="yk + 'year'">
|
<div :key="yk + 'year'" class="months">
|
||||||
<div class="month" v-for="(m, mk) in days[yk]" :key="mk + 'month'">
|
<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()}}
|
{{ new Date((new Date(yk)).setMonth(mk)).toLocaleString('en-us', {month: 'long'}) }},
|
||||||
|
{{ (new Date(yk)).getFullYear() }}
|
||||||
<div class="days">
|
<div class="days">
|
||||||
<div
|
<div
|
||||||
class="day"
|
:class="{'today': d.toDateString() === now.toDateString()}"
|
||||||
v-for="(d, dk) in days[yk][mk]"
|
|
||||||
:key="dk + 'day'"
|
:key="dk + 'day'"
|
||||||
:style="{'width': dayWidth + 'px'}"
|
: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">
|
<span class="theday" v-if="dayWidth > 25">
|
||||||
{{d.getDate()}}
|
{{ d.getDate() }}
|
||||||
</span>
|
</span>
|
||||||
<span class="weekday" v-if="dayWidth > 25">
|
<span class="weekday" v-if="dayWidth > 25">
|
||||||
{{d.toLocaleString('en-us', { weekday: 'short' })}}
|
{{ d.toLocaleString('en-us', {weekday: 'short'}) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,38 +25,42 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="tasks" :style="{'width': fullWidth + 'px'}">
|
<div :style="{'width': fullWidth + 'px'}" class="tasks">
|
||||||
<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
|
||||||
|
: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
|
<VueDragResize
|
||||||
class="task"
|
|
||||||
:class="{
|
:class="{
|
||||||
'done': t.done,
|
'done': t.done,
|
||||||
'is-current-edit': taskToEdit !== null && taskToEdit.id === t.id,
|
'is-current-edit': taskToEdit !== null && taskToEdit.id === t.id,
|
||||||
'has-light-text': !colorIsDark(t.hexColor),
|
'has-light-text': !colorIsDark(t.hexColor),
|
||||||
'has-dark-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"
|
:gridX="dayWidth"
|
||||||
:sticks="['mr', 'ml']"
|
:h="31"
|
||||||
axis="x"
|
:isActive="canWrite"
|
||||||
|
:minw="dayWidth"
|
||||||
:parentLimitation="true"
|
:parentLimitation="true"
|
||||||
:parentW="fullWidth"
|
:parentW="fullWidth"
|
||||||
@resizestop="resizeTask"
|
:snapToGrid="true"
|
||||||
@dragstop="resizeTask"
|
: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)"
|
@clicked="setTaskDragged(t)"
|
||||||
|
@dragstop="resizeTask"
|
||||||
|
@resizestop="resizeTask"
|
||||||
|
axis="x"
|
||||||
|
class="task"
|
||||||
>
|
>
|
||||||
<span :class="{
|
<span :class="{
|
||||||
'has-high-priority': t.priority >= priorities.HIGH,
|
'has-high-priority': t.priority >= priorities.HIGH,
|
||||||
'has-not-so-high-priority': t.priority === priorities.HIGH,
|
'has-not-so-high-priority': t.priority === priorities.HIGH,
|
||||||
'has-super-high-priority': t.priority === priorities.DO_NOW
|
'has-super-high-priority': t.priority === priorities.DO_NOW
|
||||||
}">{{t.title}}</span>
|
}">{{ t.title }}</span>
|
||||||
<priority-label :priority="t.priority"/>
|
<priority-label :priority="t.priority"/>
|
||||||
<!-- using the key here forces vue to use the updated version model and not the response returned by the api -->
|
<!-- using the key here forces vue to use the updated version model and not the response returned by the api -->
|
||||||
<a @click="editTask(theTasks[k])" class="edit-toggle">
|
<a @click="editTask(theTasks[k])" class="edit-toggle">
|
||||||
|
@ -64,26 +69,30 @@
|
||||||
</VueDragResize>
|
</VueDragResize>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="showTaskswithoutDates">
|
<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
|
<VueDragResize
|
||||||
class="task nodate"
|
|
||||||
:isActive="canWrite"
|
|
||||||
:x="dayOffsetUntilToday * dayWidth - 6"
|
|
||||||
:y="0"
|
|
||||||
:h="31"
|
|
||||||
:minw="dayWidth"
|
|
||||||
:snapToGrid="true"
|
|
||||||
:gridX="dayWidth"
|
:gridX="dayWidth"
|
||||||
:sticks="['mr', 'ml']"
|
:h="31"
|
||||||
axis="x"
|
:isActive="canWrite"
|
||||||
|
:minw="dayWidth"
|
||||||
:parentLimitation="true"
|
:parentLimitation="true"
|
||||||
:parentW="fullWidth"
|
:parentW="fullWidth"
|
||||||
@resizestop="resizeTask"
|
:snapToGrid="true"
|
||||||
@dragstop="resizeTask"
|
:sticks="['mr', 'ml']"
|
||||||
|
:x="dayOffsetUntilToday * dayWidth - 6"
|
||||||
|
:y="0"
|
||||||
@clicked="setTaskDragged(t)"
|
@clicked="setTaskDragged(t)"
|
||||||
|
@dragstop="resizeTask"
|
||||||
|
@resizestop="resizeTask"
|
||||||
|
axis="x"
|
||||||
|
class="task nodate"
|
||||||
v-tooltip="'This task has no dates set.'"
|
v-tooltip="'This task has no dates set.'"
|
||||||
>
|
>
|
||||||
<span>{{t.title}}</span>
|
<span>{{ t.title }}</span>
|
||||||
</VueDragResize>
|
</VueDragResize>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -91,16 +100,16 @@
|
||||||
<form @submit.prevent="addNewTask()" class="add-new-task" v-if="canWrite">
|
<form @submit.prevent="addNewTask()" class="add-new-task" v-if="canWrite">
|
||||||
<transition name="width">
|
<transition name="width">
|
||||||
<input
|
<input
|
||||||
type="text"
|
|
||||||
v-model="newTaskTitle"
|
|
||||||
class="input"
|
|
||||||
v-if="newTaskFieldActive"
|
|
||||||
ref="newTaskTitleField"
|
|
||||||
@keyup.esc="newTaskFieldActive = false"
|
|
||||||
@blur="hideCrateNewTask"
|
@blur="hideCrateNewTask"
|
||||||
|
@keyup.esc="newTaskFieldActive = false"
|
||||||
|
class="input"
|
||||||
|
ref="newTaskTitleField"
|
||||||
|
type="text"
|
||||||
|
v-if="newTaskFieldActive"
|
||||||
|
v-model="newTaskTitle"
|
||||||
/>
|
/>
|
||||||
</transition>
|
</transition>
|
||||||
<button class="button is-primary noshadow" @click="showCreateNewTask">
|
<button @click="showCreateNewTask" class="button is-primary noshadow">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<icon icon="plus"/>
|
<icon icon="plus"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -113,7 +122,7 @@
|
||||||
<p class="card-header-title">
|
<p class="card-header-title">
|
||||||
Edit Task
|
Edit Task
|
||||||
</p>
|
</p>
|
||||||
<a class="card-header-icon" @click="() => {isTaskEdit = false; taskToEdit = null}">
|
<a @click="() => {isTaskEdit = false; taskToEdit = null}" class="card-header-icon">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<icon icon="times"/>
|
<icon icon="times"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -130,18 +139,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import VueDragResize from 'vue-drag-resize'
|
import VueDragResize from 'vue-drag-resize'
|
||||||
import EditTask from './edit-task'
|
import EditTask from './edit-task'
|
||||||
|
|
||||||
import TaskService from '../../services/task'
|
import TaskService from '../../services/task'
|
||||||
import TaskModel from '../../models/task'
|
import TaskModel from '../../models/task'
|
||||||
import priorities from '../../models/priorities'
|
import priorities from '../../models/priorities'
|
||||||
import PriorityLabel from './partials/priorityLabel'
|
import PriorityLabel from './partials/priorityLabel'
|
||||||
import TaskCollectionService from '../../services/taskCollection'
|
import TaskCollectionService from '../../services/taskCollection'
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
import Rights from '../../models/rights.json'
|
import Rights from '../../models/rights.json'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'GanttChart',
|
name: 'GanttChart',
|
||||||
components: {
|
components: {
|
||||||
PriorityLabel,
|
PriorityLabel,
|
||||||
|
@ -158,16 +167,16 @@
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
dateFrom: {
|
dateFrom: {
|
||||||
default: new Date((new Date()).setDate((new Date()).getDate() - 15))
|
default: new Date((new Date()).setDate((new Date()).getDate() - 15)),
|
||||||
},
|
},
|
||||||
dateTo: {
|
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.
|
// The width of a day in pixels, used to calculate all sorts of things.
|
||||||
dayWidth: {
|
dayWidth: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 35,
|
default: 35,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -216,11 +225,11 @@
|
||||||
this.startDate = new Date(this.dateFrom)
|
this.startDate = new Date(this.dateFrom)
|
||||||
this.endDate = new Date(this.dateTo)
|
this.endDate = new Date(this.dateTo)
|
||||||
|
|
||||||
this.dayOffsetUntilToday = Math.floor((this.now - this.startDate) / 1000 / 60 / 60 / 24) +1
|
this.dayOffsetUntilToday = Math.floor((this.now - this.startDate) / 1000 / 60 / 60 / 24) + 1
|
||||||
},
|
},
|
||||||
prepareGanttDays() {
|
prepareGanttDays() {
|
||||||
// Layout: years => [months => [days]]
|
// Layout: years => [months => [days]]
|
||||||
let years = {};
|
let years = {}
|
||||||
for (let d = this.startDate; d <= this.endDate; d.setDate(d.getDate() + 1)) {
|
for (let d = this.startDate; d <= this.endDate; d.setDate(d.getDate() + 1)) {
|
||||||
let date = new Date(d)
|
let date = new Date(d)
|
||||||
if (years[date.getFullYear() + ''] === undefined) {
|
if (years[date.getFullYear() + ''] === undefined) {
|
||||||
|
@ -243,7 +252,7 @@
|
||||||
const getAllTasks = (page = 1) => {
|
const getAllTasks = (page = 1) => {
|
||||||
return this.taskCollectionService.getAll({listId: this.listId}, {}, page)
|
return this.taskCollectionService.getAll({listId: this.listId}, {}, page)
|
||||||
.then(tasks => {
|
.then(tasks => {
|
||||||
if(page < this.taskCollectionService.totalPages) {
|
if (page < this.taskCollectionService.totalPages) {
|
||||||
return getAllTasks(page + 1)
|
return getAllTasks(page + 1)
|
||||||
.then(nextTasks => {
|
.then(nextTasks => {
|
||||||
return tasks.concat(nextTasks)
|
return tasks.concat(nextTasks)
|
||||||
|
@ -261,7 +270,7 @@
|
||||||
.then(tasks => {
|
.then(tasks => {
|
||||||
this.theTasks = tasks
|
this.theTasks = tasks
|
||||||
.filter(t => {
|
.filter(t => {
|
||||||
if(t.startDate === null && !t.done) {
|
if (t.startDate === null && !t.done) {
|
||||||
this.tasksWithoutDates.push(t)
|
this.tasksWithoutDates.push(t)
|
||||||
}
|
}
|
||||||
return t.startDate >= this.startDate && t.endDate <= this.endDate
|
return t.startDate >= this.startDate && t.endDate <= this.endDate
|
||||||
|
@ -269,7 +278,7 @@
|
||||||
.map(t => {
|
.map(t => {
|
||||||
return this.addGantAttributes(t)
|
return this.addGantAttributes(t)
|
||||||
})
|
})
|
||||||
.sort(function(a,b) {
|
.sort(function (a, b) {
|
||||||
if (a.startDate < b.startDate)
|
if (a.startDate < b.startDate)
|
||||||
return -1
|
return -1
|
||||||
if (a.startDate > b.startDate)
|
if (a.startDate > b.startDate)
|
||||||
|
@ -295,7 +304,7 @@
|
||||||
// Timeout to definitly catch if the user clicked on taskedit
|
// Timeout to definitly catch if the user clicked on taskedit
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
||||||
if(this.isTaskEdit) {
|
if (this.isTaskEdit) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +336,7 @@
|
||||||
this.taskService.update(this.taskDragged)
|
this.taskService.update(this.taskDragged)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
// If the task didn't have dates before, we'll update the list
|
// If the task didn't have dates before, we'll update the list
|
||||||
if(didntHaveDates) {
|
if (didntHaveDates) {
|
||||||
for (const t in this.tasksWithoutDates) {
|
for (const t in this.tasksWithoutDates) {
|
||||||
if (this.tasksWithoutDates[t].id === r.id) {
|
if (this.tasksWithoutDates[t].id === r.id) {
|
||||||
this.tasksWithoutDates.splice(t, 1)
|
this.tasksWithoutDates.splice(t, 1)
|
||||||
|
@ -354,7 +363,7 @@
|
||||||
this.isTaskEdit = true
|
this.isTaskEdit = true
|
||||||
},
|
},
|
||||||
showCreateNewTask() {
|
showCreateNewTask() {
|
||||||
if(!this.newTaskFieldActive) {
|
if (!this.newTaskFieldActive) {
|
||||||
// Timeout to not send the form if the field isn't even shown
|
// Timeout to not send the form if the field isn't even shown
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.newTaskFieldActive = true
|
this.newTaskFieldActive = true
|
||||||
|
@ -363,7 +372,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideCrateNewTask() {
|
hideCrateNewTask() {
|
||||||
if(this.newTaskTitle === '') {
|
if (this.newTaskTitle === '') {
|
||||||
this.$nextTick(() => this.newTaskFieldActive = false)
|
this.$nextTick(() => this.newTaskFieldActive = false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -383,5 +392,5 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -28,5 +28,5 @@ export default {
|
||||||
this.error(e, this)
|
this.error(e, this)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
|
@ -137,7 +137,7 @@ export default {
|
||||||
|
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'list.list',
|
name: 'list.list',
|
||||||
query: {search: this.searchTerm}
|
query: {search: this.searchTerm},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
hideSearchBar() {
|
hideSearchBar() {
|
||||||
|
@ -154,12 +154,12 @@ export default {
|
||||||
return {
|
return {
|
||||||
name: 'list.' + type,
|
name: 'list.' + type,
|
||||||
params: {
|
params: {
|
||||||
type: type
|
type: type,
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
page: page,
|
page: page,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
|
@ -6,17 +6,30 @@
|
||||||
</span>
|
</span>
|
||||||
Attachments
|
Attachments
|
||||||
<a
|
<a
|
||||||
v-if="editEnabled"
|
:disabled="attachmentService.loading"
|
||||||
class="button is-primary is-outlined is-small noshadow"
|
|
||||||
@click="$refs.files.click()"
|
@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>
|
<span class="icon is-small"><icon icon="cloud-upload-alt"/></span>
|
||||||
Upload attachment
|
Upload attachment
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<input type="file" id="files" ref="files" multiple @change="uploadNewAttachment()" :disabled="attachmentService.loading" v-if="editEnabled"/>
|
<input
|
||||||
<progress v-if="attachmentService.uploadProgress > 0" class="progress is-primary" :value="attachmentService.uploadProgress" max="100">{{ attachmentService.uploadProgress }}%</progress>
|
: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>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -27,22 +40,30 @@
|
||||||
<th>Created By</th>
|
<th>Created By</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="attachment" v-for="a in attachments" :key="a.id">
|
<tr :key="a.id" class="attachment" v-for="a in attachments">
|
||||||
<td>
|
<td>
|
||||||
{{ a.file.name }}
|
{{ a.file.name }}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ a.file.getHumanSize() }}</td>
|
<td>{{ a.file.getHumanSize() }}</td>
|
||||||
<td>{{ a.file.mime }}</td>
|
<td>{{ a.file.mime }}</td>
|
||||||
<td v-tooltip="formatDate(a.created)">{{ formatDateSince(a.created) }}</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>
|
<td>
|
||||||
<div class="buttons has-addons">
|
<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">
|
<span class="icon">
|
||||||
<icon icon="cloud-download-alt"/>
|
<icon icon="cloud-download-alt"/>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</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">
|
<span class="icon">
|
||||||
<icon icon="trash-alt"/>
|
<icon icon="trash-alt"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -53,7 +74,7 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Dropzone -->
|
<!-- 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="drop-hint">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<icon icon="cloud-upload-alt"/>
|
<icon icon="cloud-upload-alt"/>
|
||||||
|
@ -66,8 +87,8 @@
|
||||||
|
|
||||||
<!-- Delete modal -->
|
<!-- Delete modal -->
|
||||||
<modal
|
<modal
|
||||||
v-if="showDeleteModal"
|
|
||||||
@close="showDeleteModal = false"
|
@close="showDeleteModal = false"
|
||||||
|
v-if="showDeleteModal"
|
||||||
v-on:submit="deleteAttachment()">
|
v-on:submit="deleteAttachment()">
|
||||||
<span slot="header">Delete attachment</span>
|
<span slot="header">Delete attachment</span>
|
||||||
<p slot="text">Are you sure you want to delete the attachment {{ attachmentToDelete.file.name }}?<br/>
|
<p slot="text">Are you sure you want to delete the attachment {{ attachmentToDelete.file.name }}?<br/>
|
||||||
|
@ -77,12 +98,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AttachmentService from '../../../services/attachment'
|
import AttachmentService from '../../../services/attachment'
|
||||||
import AttachmentModel from '../../../models/attachment'
|
import AttachmentModel from '../../../models/attachment'
|
||||||
import User from '../../misc/user'
|
import User from '../../misc/user'
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'attachments',
|
name: 'attachments',
|
||||||
components: {
|
components: {
|
||||||
User,
|
User,
|
||||||
|
@ -112,26 +133,26 @@
|
||||||
this.attachmentService = new AttachmentService()
|
this.attachmentService = new AttachmentService()
|
||||||
},
|
},
|
||||||
computed: mapState({
|
computed: mapState({
|
||||||
attachments: state => state.attachments.attachments
|
attachments: state => state.attachments.attachments,
|
||||||
}),
|
}),
|
||||||
mounted() {
|
mounted() {
|
||||||
document.addEventListener('dragenter', e => {
|
document.addEventListener('dragenter', e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.showDropzone = true
|
this.showDropzone = true
|
||||||
});
|
})
|
||||||
|
|
||||||
window.addEventListener('dragleave', e => {
|
window.addEventListener('dragleave', e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.showDropzone = false
|
this.showDropzone = false
|
||||||
});
|
})
|
||||||
|
|
||||||
document.addEventListener('dragover', e => {
|
document.addEventListener('dragover', e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.showDropzone = true
|
this.showDropzone = true
|
||||||
});
|
})
|
||||||
|
|
||||||
document.addEventListener('drop', e => {
|
document.addEventListener('drop', e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
@ -147,7 +168,7 @@
|
||||||
this.attachmentService.download(attachment)
|
this.attachmentService.download(attachment)
|
||||||
},
|
},
|
||||||
uploadNewAttachment() {
|
uploadNewAttachment() {
|
||||||
if(this.$refs.files.files.length === 0) {
|
if (this.$refs.files.files.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,13 +178,13 @@
|
||||||
const attachmentModel = new AttachmentModel({taskId: this.taskId})
|
const attachmentModel = new AttachmentModel({taskId: this.taskId})
|
||||||
this.attachmentService.create(attachmentModel, files)
|
this.attachmentService.create(attachmentModel, files)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if(r.success !== null) {
|
if (r.success !== null) {
|
||||||
r.success.forEach(a => {
|
r.success.forEach(a => {
|
||||||
this.$store.commit('attachments/add', a)
|
this.$store.commit('attachments/add', a)
|
||||||
this.$store.dispatch('tasks/addTaskAttachment', {taskId: this.taskId, attachment: a})
|
this.$store.dispatch('tasks/addTaskAttachment', {taskId: this.taskId, attachment: a})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if(r.errors !== null) {
|
if (r.errors !== null) {
|
||||||
r.errors.forEach(m => {
|
r.errors.forEach(m => {
|
||||||
this.error(m)
|
this.error(m)
|
||||||
})
|
})
|
||||||
|
@ -187,5 +208,5 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<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">
|
<h1 v-if="canWrite || comments.length > 0">
|
||||||
<span class="icon is-grey">
|
<span class="icon is-grey">
|
||||||
<icon :icon="['far', 'comments']"/>
|
<icon :icon="['far', 'comments']"/>
|
||||||
|
@ -10,24 +10,24 @@
|
||||||
<progress class="progress is-small is-info" max="100" v-if="taskCommentService.loading">Loading
|
<progress class="progress is-small is-info" max="100" v-if="taskCommentService.loading">Loading
|
||||||
comments...
|
comments...
|
||||||
</progress>
|
</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">
|
<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>
|
</figure>
|
||||||
<div class="media-content">
|
<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>
|
<strong>{{ c.author.username }}</strong>
|
||||||
<small v-tooltip="formatDate(c.created)">{{ formatDateSince(c.created) }}</small>
|
<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)"> ·
|
<small v-if="+new Date(c.created) !== +new Date(c.updated)" v-tooltip="formatDate(c.updated)"> ·
|
||||||
edited {{ formatDateSince(c.updated) }}</small>
|
edited {{ formatDateSince(c.updated) }}</small>
|
||||||
</div>
|
</div>
|
||||||
<editor
|
<editor
|
||||||
v-model="c.comment"
|
|
||||||
:has-preview="true"
|
:has-preview="true"
|
||||||
@change="() => {toggleEdit(c);editComment()}"
|
|
||||||
:upload-enabled="true"
|
|
||||||
:upload-callback="attachmentUpload"
|
|
||||||
:is-edit-enabled="canWrite"
|
: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">
|
<div class="comment-actions" v-if="canWrite">
|
||||||
<a @click="toggleDelete(c.id)">Remove</a>
|
<a @click="toggleDelete(c.id)">Remove</a>
|
||||||
|
@ -36,25 +36,25 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="media comment" v-if="canWrite">
|
<div class="media comment" v-if="canWrite">
|
||||||
<figure class="media-left">
|
<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>
|
</figure>
|
||||||
<div class="media-content">
|
<div class="media-content">
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<editor
|
<editor
|
||||||
placeholder="Add your comment..."
|
|
||||||
:class="{'is-loading': taskCommentService.loading && !isCommentEdit}"
|
:class="{'is-loading': taskCommentService.loading && !isCommentEdit}"
|
||||||
v-model="newComment.comment"
|
|
||||||
:has-preview="false"
|
:has-preview="false"
|
||||||
:upload-enabled="true"
|
|
||||||
:upload-callback="attachmentUpload"
|
:upload-callback="attachmentUpload"
|
||||||
|
:upload-enabled="true"
|
||||||
|
placeholder="Add your comment..."
|
||||||
v-if="editorActive"
|
v-if="editorActive"
|
||||||
|
v-model="newComment.comment"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<button class="button is-primary"
|
<button :class="{'is-loading': taskCommentService.loading && !isCommentEdit}"
|
||||||
:class="{'is-loading': taskCommentService.loading && !isCommentEdit}"
|
:disabled="newComment.comment === ''"
|
||||||
@click="addComment()" :disabled="newComment.comment === ''">Comment
|
@click="addComment()" class="button is-primary">Comment
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,9 +62,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<modal
|
<modal
|
||||||
v-if="showDeleteModal"
|
|
||||||
@close="showDeleteModal = false"
|
@close="showDeleteModal = false"
|
||||||
@submit="deleteComment()">
|
@submit="deleteComment()"
|
||||||
|
v-if="showDeleteModal">
|
||||||
<span slot="header">Delete this comment</span>
|
<span slot="header">Delete this comment</span>
|
||||||
<p slot="text">Are you sure you want to delete this comment?
|
<p slot="text">Are you sure you want to delete this comment?
|
||||||
<br/>This <b>CANNOT BE UNDONE!</b></p>
|
<br/>This <b>CANNOT BE UNDONE!</b></p>
|
||||||
|
@ -73,13 +73,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TaskCommentService from '../../../services/taskComment'
|
import TaskCommentService from '../../../services/taskComment'
|
||||||
import TaskCommentModel from '../../../models/taskComment'
|
import TaskCommentModel from '../../../models/taskComment'
|
||||||
import attachmentUpload from '../mixins/attachmentUpload'
|
import attachmentUpload from '../mixins/attachmentUpload'
|
||||||
import LoadingComponent from '../../misc/loading'
|
import LoadingComponent from '../../misc/loading'
|
||||||
import ErrorComponent from '../../misc/error'
|
import ErrorComponent from '../../misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'comments',
|
name: 'comments',
|
||||||
components: {
|
components: {
|
||||||
editor: () => ({
|
editor: () => ({
|
||||||
|
@ -213,5 +213,5 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'date-table-cell',
|
name: 'date-table-cell',
|
||||||
props: {
|
props: {
|
||||||
date: {
|
date: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: 0,
|
default: 0,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
<template>
|
<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>
|
<label class="label">Defer due date</label>
|
||||||
<div class="defer-days">
|
<div class="defer-days">
|
||||||
<button class="button is-outlined is-primary has-no-shadow" @click="() => deferDays(1)">1 day</button>
|
<button @click="() => deferDays(1)" class="button is-outlined is-primary has-no-shadow">1 day</button>
|
||||||
<button class="button is-outlined is-primary has-no-shadow" @click="() => deferDays(3)">3 days</button>
|
<button @click="() => deferDays(3)" class="button is-outlined is-primary has-no-shadow">3 days</button>
|
||||||
<button class="button is-outlined is-primary has-no-shadow" @click="() => deferDays(7)">1 week</button>
|
<button @click="() => deferDays(7)" class="button is-outlined is-primary has-no-shadow">1 week</button>
|
||||||
</div>
|
</div>
|
||||||
<flat-pickr
|
<flat-pickr
|
||||||
:class="{ 'disabled': taskService.loading}"
|
:class="{ 'disabled': taskService.loading}"
|
||||||
class="input"
|
|
||||||
:disabled="taskService.loading"
|
|
||||||
v-model="dueDate"
|
|
||||||
:config="flatPickerConfig"
|
:config="flatPickerConfig"
|
||||||
|
:disabled="taskService.loading"
|
||||||
|
class="input"
|
||||||
|
v-model="dueDate"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TaskService from '../../../services/task'
|
import TaskService from '../../../services/task'
|
||||||
import flatPickr from 'vue-flatpickr-component'
|
import flatPickr from 'vue-flatpickr-component'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'defer-task',
|
name: 'defer-task',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.taskService = new TaskService()
|
this.taskService = new TaskService()
|
||||||
|
@ -108,5 +108,5 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,49 +1,51 @@
|
||||||
<template>
|
<template>
|
||||||
<multiselect
|
<multiselect
|
||||||
:multiple="true"
|
|
||||||
:close-on-select="false"
|
|
||||||
:clear-on-select="true"
|
:clear-on-select="true"
|
||||||
:options-limit="300"
|
:close-on-select="false"
|
||||||
|
:disabled="disabled"
|
||||||
:hide-selected="true"
|
:hide-selected="true"
|
||||||
v-model="assignees"
|
|
||||||
:options="foundUsers"
|
|
||||||
:searchable="true"
|
|
||||||
:loading="listUserService.loading"
|
|
||||||
:internal-search="true"
|
:internal-search="true"
|
||||||
|
:loading="listUserService.loading"
|
||||||
|
:multiple="true"
|
||||||
|
:options="foundUsers"
|
||||||
|
:options-limit="300"
|
||||||
|
:searchable="true"
|
||||||
|
:showNoOptions="false"
|
||||||
@search-change="findUser"
|
@search-change="findUser"
|
||||||
@select="addAssignee"
|
@select="addAssignee"
|
||||||
placeholder="Type to assign a user..."
|
|
||||||
label="username"
|
label="username"
|
||||||
track-by="id"
|
placeholder="Type to assign a user..."
|
||||||
select-label="Assign this user"
|
select-label="Assign this user"
|
||||||
:showNoOptions="false"
|
track-by="id"
|
||||||
:disabled="disabled"
|
v-model="assignees"
|
||||||
>
|
>
|
||||||
<template slot="tag" slot-scope="{ option }">
|
<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">
|
<a @click="removeAssignee(option)" class="remove-assignee" v-if="!disabled">
|
||||||
<icon icon="times"/>
|
<icon icon="times"/>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
<template slot="clear" slot-scope="props">
|
<template slot="clear" slot-scope="props">
|
||||||
<div class="multiselect__clear" v-if="newAssignee !== null && newAssignee.id !== 0"
|
<div
|
||||||
@mousedown.prevent.stop="clearAllFoundUsers(props.search)"></div>
|
@mousedown.prevent.stop="clearAllFoundUsers(props.search)"
|
||||||
|
class="multiselect__clear"
|
||||||
|
v-if="newAssignee !== null && newAssignee.id !== 0"></div>
|
||||||
</template>
|
</template>
|
||||||
<span slot="noResult">No user found. Consider changing the search query.</span>
|
<span slot="noResult">No user found. Consider changing the search query.</span>
|
||||||
</multiselect>
|
</multiselect>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {differenceWith} from 'lodash'
|
import {differenceWith} from 'lodash'
|
||||||
|
|
||||||
import UserModel from '../../../models/user'
|
import UserModel from '../../../models/user'
|
||||||
import ListUserService from '../../../services/listUsers'
|
import ListUserService from '../../../services/listUsers'
|
||||||
import TaskAssigneeService from '../../../services/taskAssignee'
|
import TaskAssigneeService from '../../../services/taskAssignee'
|
||||||
import User from '../../misc/user'
|
import User from '../../misc/user'
|
||||||
import LoadingComponent from '../../misc/loading'
|
import LoadingComponent from '../../misc/loading'
|
||||||
import ErrorComponent from '../../misc/error'
|
import ErrorComponent from '../../misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'editAssignees',
|
name: 'editAssignees',
|
||||||
components: {
|
components: {
|
||||||
User,
|
User,
|
||||||
|
@ -89,7 +91,7 @@
|
||||||
watch: {
|
watch: {
|
||||||
initialAssignees(newVal) {
|
initialAssignees(newVal) {
|
||||||
this.assignees = newVal
|
this.assignees = newVal
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
addAssignee(user) {
|
addAssignee(user) {
|
||||||
|
@ -133,5 +135,5 @@
|
||||||
this.$set(this, 'foundUsers', [])
|
this.$set(this, 'foundUsers', [])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,50 +1,55 @@
|
||||||
<template>
|
<template>
|
||||||
<multiselect
|
<multiselect
|
||||||
:multiple="true"
|
|
||||||
:close-on-select="false"
|
|
||||||
:clear-on-select="true"
|
:clear-on-select="true"
|
||||||
:options-limit="300"
|
:close-on-select="false"
|
||||||
|
:disabled="disabled"
|
||||||
:hide-selected="true"
|
:hide-selected="true"
|
||||||
v-model="labels"
|
|
||||||
:options="foundLabels"
|
|
||||||
:searchable="true"
|
|
||||||
:loading="labelService.loading || labelTaskService.loading"
|
|
||||||
:internal-search="true"
|
:internal-search="true"
|
||||||
|
:loading="labelService.loading || labelTaskService.loading"
|
||||||
|
:multiple="true"
|
||||||
|
:options="foundLabels"
|
||||||
|
:options-limit="300"
|
||||||
|
:searchable="true"
|
||||||
|
:showNoOptions="false"
|
||||||
|
:taggable="true"
|
||||||
@search-change="findLabel"
|
@search-change="findLabel"
|
||||||
@select="addLabel"
|
@select="addLabel"
|
||||||
placeholder="Type to add a new label..."
|
|
||||||
label="title"
|
|
||||||
track-by="id"
|
|
||||||
:taggable="true"
|
|
||||||
:showNoOptions="false"
|
|
||||||
@tag="createAndAddLabel"
|
@tag="createAndAddLabel"
|
||||||
|
label="title"
|
||||||
|
placeholder="Type to add a new label..."
|
||||||
tag-placeholder="Add this as new label"
|
tag-placeholder="Add this as new label"
|
||||||
:disabled="disabled"
|
track-by="id"
|
||||||
|
v-model="labels"
|
||||||
>
|
>
|
||||||
<template slot="tag" slot-scope="{ option }">
|
<template
|
||||||
<span class="tag"
|
slot="tag"
|
||||||
:style="{'background': option.hexColor, 'color': option.textColor}">
|
slot-scope="{ option }">
|
||||||
|
<span
|
||||||
|
:style="{'background': option.hexColor, 'color': option.textColor}"
|
||||||
|
class="tag">
|
||||||
<span>{{ option.title }}</span>
|
<span>{{ option.title }}</span>
|
||||||
<a class="delete is-small" @click="removeLabel(option)"></a>
|
<a @click="removeLabel(option)" class="delete is-small"></a>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template slot="clear" slot-scope="props">
|
<template slot="clear" slot-scope="props">
|
||||||
<div class="multiselect__clear" v-if="labels.length"
|
<div
|
||||||
@mousedown.prevent.stop="clearAllLabels(props.search)"></div>
|
@mousedown.prevent.stop="clearAllLabels(props.search)"
|
||||||
|
class="multiselect__clear"
|
||||||
|
v-if="labels.length"></div>
|
||||||
</template>
|
</template>
|
||||||
</multiselect>
|
</multiselect>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { differenceWith } from 'lodash'
|
import {differenceWith} from 'lodash'
|
||||||
|
|
||||||
import LabelService from '../../../services/label'
|
import LabelService from '../../../services/label'
|
||||||
import LabelModel from '../../../models/label'
|
import LabelModel from '../../../models/label'
|
||||||
import LabelTaskService from '../../../services/labelTask'
|
import LabelTaskService from '../../../services/labelTask'
|
||||||
import LoadingComponent from '../../misc/loading'
|
import LoadingComponent from '../../misc/loading'
|
||||||
import ErrorComponent from '../../misc/error'
|
import ErrorComponent from '../../misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'edit-labels',
|
name: 'edit-labels',
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
|
@ -80,7 +85,7 @@
|
||||||
watch: {
|
watch: {
|
||||||
value(newLabels) {
|
value(newLabels) {
|
||||||
this.labels = newLabels
|
this.labels = newLabels
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.labelService = new LabelService()
|
this.labelService = new LabelService()
|
||||||
|
@ -153,7 +158,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -1,24 +1,28 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="label-wrapper">
|
<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>{{ label.title }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'labels',
|
name: 'labels',
|
||||||
props: {
|
props: {
|
||||||
labels: {
|
labels: {
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.label-wrapper {
|
.label-wrapper {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,22 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<multiselect
|
<multiselect
|
||||||
v-model="list"
|
|
||||||
:options="foundLists"
|
|
||||||
:multiple="false"
|
|
||||||
:searchable="true"
|
|
||||||
:loading="listSerivce.loading"
|
|
||||||
:internal-search="true"
|
:internal-search="true"
|
||||||
|
:loading="listSerivce.loading"
|
||||||
|
:multiple="false"
|
||||||
|
:options="foundLists"
|
||||||
|
:searchable="true"
|
||||||
|
:showNoOptions="false"
|
||||||
@search-change="findLists"
|
@search-change="findLists"
|
||||||
@select="select"
|
@select="select"
|
||||||
placeholder="Type to search for a list..."
|
|
||||||
label="title"
|
|
||||||
track-by="id"
|
|
||||||
:showNoOptions="false"
|
|
||||||
class="control is-expanded"
|
class="control is-expanded"
|
||||||
|
label="title"
|
||||||
|
placeholder="Type to search for a list..."
|
||||||
|
track-by="id"
|
||||||
v-focus
|
v-focus
|
||||||
|
v-model="list"
|
||||||
>
|
>
|
||||||
<template slot="clear" slot-scope="props">
|
<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>
|
||||||
<template slot="option" slot-scope="props">
|
<template slot="option" slot-scope="props">
|
||||||
<span class="list-namespace-title">{{ namespace(props.option.namespaceId) }} ></span>
|
<span class="list-namespace-title">{{ namespace(props.option.namespaceId) }} ></span>
|
||||||
|
@ -27,12 +30,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ListService from '../../../services/list'
|
import ListService from '../../../services/list'
|
||||||
import ListModel from '../../../models/list'
|
import ListModel from '../../../models/list'
|
||||||
import LoadingComponent from '../../misc/loading'
|
import LoadingComponent from '../../misc/loading'
|
||||||
import ErrorComponent from '../../misc/error'
|
import ErrorComponent from '../../misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'listSearch',
|
name: 'listSearch',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -82,5 +85,5 @@
|
||||||
return 'Shared Lists'
|
return 'Shared Lists'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="select">
|
<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">0%</option>
|
||||||
<option value="0.1">10%</option>
|
<option value="0.1">10%</option>
|
||||||
<option value="0.2">20%</option>
|
<option value="0.2">20%</option>
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'percentDoneSelect',
|
name: 'percentDoneSelect',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
updateData() {
|
updateData() {
|
||||||
this.$emit('input', this.percentDone)
|
this.$emit('input', this.percentDone)
|
||||||
this.$emit('change')
|
this.$emit('change')
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<template>
|
<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">
|
<span class="icon" v-if="priority >= priorities.HIGH">
|
||||||
<icon icon="exclamation"/>
|
<icon icon="exclamation"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -16,9 +18,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import priorites from '../../../models/priorities'
|
import priorites from '../../../models/priorities'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'priorityLabel',
|
name: 'priorityLabel',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -34,14 +36,14 @@
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../../styles/theme/variables';
|
@import '../../../styles/theme/variables';
|
||||||
|
|
||||||
span.high-priority{
|
span.high-priority {
|
||||||
color: $red;
|
color: $red;
|
||||||
width: auto !important; // To override the width set in tasks
|
width: auto !important; // To override the width set in tasks
|
||||||
|
|
||||||
|
@ -54,5 +56,5 @@
|
||||||
&.not-so-high {
|
&.not-so-high {
|
||||||
color: $orange;
|
color: $orange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="select">
|
<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.UNSET">Unset</option>
|
||||||
<option :value="priorities.LOW">Low</option>
|
<option :value="priorities.LOW">Low</option>
|
||||||
<option :value="priorities.MEDIUM">Medium</option>
|
<option :value="priorities.MEDIUM">Medium</option>
|
||||||
|
@ -12,9 +12,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import priorites from '../../../models/priorities'
|
import priorites from '../../../models/priorities'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'prioritySelect',
|
name: 'prioritySelect',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
updateData() {
|
updateData() {
|
||||||
this.$emit('input', this.priority)
|
this.$emit('input', this.priority)
|
||||||
this.$emit('change')
|
this.$emit('change')
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,26 +4,26 @@
|
||||||
<label class="label">New Task Relation</label>
|
<label class="label">New Task Relation</label>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<multiselect
|
<multiselect
|
||||||
v-model="newTaskRelationTask"
|
|
||||||
:options="foundTasks"
|
|
||||||
:multiple="false"
|
|
||||||
:searchable="true"
|
|
||||||
:loading="taskService.loading"
|
|
||||||
:internal-search="true"
|
:internal-search="true"
|
||||||
@search-change="findTasks"
|
:loading="taskService.loading"
|
||||||
placeholder="Type search for a new task to add as related..."
|
:multiple="false"
|
||||||
label="title"
|
:options="foundTasks"
|
||||||
track-by="id"
|
:searchable="true"
|
||||||
:taggable="true"
|
|
||||||
:showNoOptions="false"
|
:showNoOptions="false"
|
||||||
|
:taggable="true"
|
||||||
|
@search-change="findTasks"
|
||||||
@tag="createAndRelateTask"
|
@tag="createAndRelateTask"
|
||||||
|
label="title"
|
||||||
|
placeholder="Type search for a new task to add as related..."
|
||||||
tag-placeholder="Add this as new related task"
|
tag-placeholder="Add this as new related task"
|
||||||
|
track-by="id"
|
||||||
|
v-model="newTaskRelationTask"
|
||||||
>
|
>
|
||||||
<template slot="clear" slot-scope="props">
|
<template slot="clear" slot-scope="props">
|
||||||
<div
|
<div
|
||||||
|
@mousedown.prevent.stop="clearAllFoundTasks(props.search)"
|
||||||
class="multiselect__clear"
|
class="multiselect__clear"
|
||||||
v-if="newTaskRelationTask !== null && newTaskRelationTask.id !== 0"
|
v-if="newTaskRelationTask !== null && newTaskRelationTask.id !== 0"></div>
|
||||||
@mousedown.prevent.stop="clearAllFoundTasks(props.search)"></div>
|
|
||||||
</template>
|
</template>
|
||||||
<span slot="noResult">No task found. Consider changing the search query.</span>
|
<span slot="noResult">No task found. Consider changing the search query.</span>
|
||||||
</multiselect>
|
</multiselect>
|
||||||
|
@ -33,53 +33,55 @@
|
||||||
<div class="select is-fullwidth has-defaults">
|
<div class="select is-fullwidth has-defaults">
|
||||||
<select v-model="newTaskRelationKind">
|
<select v-model="newTaskRelationKind">
|
||||||
<option value="unset">Select a relation kind</option>
|
<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] }}
|
{{ label[0] }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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">
|
<template v-if="rts.length > 0">
|
||||||
<span class="title">{{ relationKindTitle(kind, rts.length) }}</span>
|
<span class="title">{{ relationKindTitle(kind, rts.length) }}</span>
|
||||||
<div class="tasks noborder">
|
<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 } }">
|
<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
|
<span
|
||||||
v-if="t.listId !== listId"
|
|
||||||
class="different-list"
|
class="different-list"
|
||||||
|
v-if="t.listId !== listId"
|
||||||
v-tooltip="'This task belongs to a different list.'">
|
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>
|
</span>
|
||||||
{{t.title}}
|
{{ t.title }}
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<a
|
<a
|
||||||
v-if="editEnabled"
|
@click="() => {showDeleteModal = true; relationToDelete = {relationKind: kind, otherTaskId: t.id}}"
|
||||||
class="remove"
|
class="remove"
|
||||||
@click="() => {showDeleteModal = true; relationToDelete = {relationKind: kind, otherTaskId: t.id}}">
|
v-if="editEnabled">
|
||||||
<icon icon="trash-alt"/>
|
<icon icon="trash-alt"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</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.
|
No task relations yet.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Delete modal -->
|
<!-- Delete modal -->
|
||||||
<modal
|
<modal
|
||||||
v-if="showDeleteModal"
|
|
||||||
@close="showDeleteModal = false"
|
@close="showDeleteModal = false"
|
||||||
@submit="removeTaskRelation()">
|
@submit="removeTaskRelation()"
|
||||||
|
v-if="showDeleteModal">
|
||||||
<span slot="header">Delete Task Relation</span>
|
<span slot="header">Delete Task Relation</span>
|
||||||
<p slot="text">Are you sure you want to delete this task relation?<br/>
|
<p slot="text">Are you sure you want to delete this task relation?<br/>
|
||||||
<b>This CANNOT BE UNDONE!</b></p>
|
<b>This CANNOT BE UNDONE!</b></p>
|
||||||
|
@ -88,16 +90,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TaskService from '../../../services/task'
|
import TaskService from '../../../services/task'
|
||||||
import TaskModel from '../../../models/task'
|
import TaskModel from '../../../models/task'
|
||||||
import TaskRelationService from '../../../services/taskRelation'
|
import TaskRelationService from '../../../services/taskRelation'
|
||||||
import relationKinds from '../../../models/relationKinds'
|
import relationKinds from '../../../models/relationKinds'
|
||||||
import TaskRelationModel from '../../../models/taskRelation'
|
import TaskRelationModel from '../../../models/taskRelation'
|
||||||
|
|
||||||
import LoadingComponent from '../../misc/loading'
|
import LoadingComponent from '../../misc/loading'
|
||||||
import ErrorComponent from '../../misc/error'
|
import ErrorComponent from '../../misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'relatedTasks',
|
name: 'relatedTasks',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -233,5 +235,5 @@
|
||||||
return relationKinds[kind][0]
|
return relationKinds[kind][0]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="reminders">
|
<div class="reminders">
|
||||||
<div class="reminder-input"
|
<div
|
||||||
:class="{ 'overdue': (r < nowUnix && index !== (reminders.length - 1))}"
|
: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
|
<flat-pickr
|
||||||
:v-model="reminders"
|
|
||||||
:config="flatPickerConfig"
|
:config="flatPickerConfig"
|
||||||
:id="'taskreminderdate' + index"
|
|
||||||
:value="r"
|
|
||||||
:data-index="index"
|
:data-index="index"
|
||||||
placeholder="Add a new reminder..."
|
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
:id="'taskreminderdate' + index"
|
||||||
|
:v-model="reminders"
|
||||||
|
:value="r"
|
||||||
|
placeholder="Add a new reminder..."
|
||||||
>
|
>
|
||||||
</flat-pickr>
|
</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>
|
<icon icon="times"></icon>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,10 +23,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import flatPickr from 'vue-flatpickr-component'
|
import flatPickr from 'vue-flatpickr-component'
|
||||||
import 'flatpickr/dist/flatpickr.css'
|
import 'flatpickr/dist/flatpickr.css'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'reminders',
|
name: 'reminders',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -96,5 +98,5 @@
|
||||||
this.updateData()
|
this.updateData()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
@change="updateData"
|
||||||
class="input"
|
class="input"
|
||||||
placeholder="Specify an amount..."
|
placeholder="Specify an amount..."
|
||||||
v-model="repeatAfter.amount"
|
v-model="repeatAfter.amount"/>
|
||||||
@change="updateData"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select">
|
<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="hours">Hours</option>
|
||||||
<option value="days">Days</option>
|
<option value="days">Days</option>
|
||||||
<option value="weeks">Weeks</option>
|
<option value="weeks">Weeks</option>
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
</div>
|
</div>
|
||||||
<fancycheckbox
|
<fancycheckbox
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="column"
|
|
||||||
@change="updateData"
|
@change="updateData"
|
||||||
|
class="column"
|
||||||
v-model="task.repeatFromCurrentDate"
|
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.'"
|
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.'"
|
||||||
>
|
>
|
||||||
|
@ -37,9 +37,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Fancycheckbox from '../../input/fancycheckbox'
|
import Fancycheckbox from '../../input/fancycheckbox'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'repeatAfter',
|
name: 'repeatAfter',
|
||||||
components: {Fancycheckbox},
|
components: {Fancycheckbox},
|
||||||
data() {
|
data() {
|
||||||
|
@ -80,26 +80,26 @@
|
||||||
this.task.repeatAfter = this.repeatAfter
|
this.task.repeatAfter = this.repeatAfter
|
||||||
this.$emit('input', this.task)
|
this.$emit('input', this.task)
|
||||||
this.$emit('change')
|
this.$emit('change')
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style lang="scss" scoped>
|
||||||
p {
|
p {
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field.has-addons {
|
.field.has-addons {
|
||||||
|
|
||||||
margin-bottom: .5rem;
|
margin-bottom: .5rem;
|
||||||
|
|
||||||
.control .select select {
|
.control .select select {
|
||||||
height: 2.5em;
|
height: 2.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.columns {
|
.columns {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="task loader-container" :class="{'is-loading': taskService.loading}">
|
<div :class="{'is-loading': taskService.loading}" class="task loader-container">
|
||||||
<fancycheckbox v-model="task.done" @change="markAsDone" :disabled="isArchived || disabled"/>
|
<fancycheckbox :disabled="isArchived || disabled" @change="markAsDone" v-model="task.done"/>
|
||||||
<span class="tasktext" :class="{ 'done': task.done}">
|
<span :class="{ 'done': task.done}" class="tasktext">
|
||||||
<router-link :to="{ name: taskDetailRoute, params: { id: task.id } }">
|
<router-link :to="{ name: taskDetailRoute, params: { id: task.id } }">
|
||||||
<router-link
|
<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 } }"
|
: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 }}
|
{{ $store.getters['lists/getListById'](task.listId).title }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
|
@ -23,37 +23,37 @@
|
||||||
|
|
||||||
<labels :labels="task.labels"/>
|
<labels :labels="task.labels"/>
|
||||||
<user
|
<user
|
||||||
:user="a"
|
|
||||||
:avatar-size="27"
|
:avatar-size="27"
|
||||||
:show-username="false"
|
|
||||||
:is-inline="true"
|
:is-inline="true"
|
||||||
v-for="(a, i) in task.assignees"
|
|
||||||
:key="task.id + 'assignee' + a.id + i"
|
:key="task.id + 'assignee' + a.id + i"
|
||||||
|
:show-username="false"
|
||||||
|
:user="a"
|
||||||
|
v-for="(a, i) in task.assignees"
|
||||||
/>
|
/>
|
||||||
<i
|
<i
|
||||||
v-if="+new Date(task.dueDate) > 0"
|
|
||||||
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
|
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
|
||||||
v-tooltip="formatDate(task.dueDate)"
|
|
||||||
@click.stop="showDefer = !showDefer"
|
@click.stop="showDefer = !showDefer"
|
||||||
|
v-if="+new Date(task.dueDate) > 0"
|
||||||
|
v-tooltip="formatDate(task.dueDate)"
|
||||||
>
|
>
|
||||||
- Due {{formatDateSince(task.dueDate)}}
|
- Due {{ formatDateSince(task.dueDate) }}
|
||||||
</i>
|
</i>
|
||||||
<transition name="fade">
|
<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>
|
</transition>
|
||||||
<priority-label :priority="task.priority"/>
|
<priority-label :priority="task.priority"/>
|
||||||
</span>
|
</span>
|
||||||
<router-link
|
<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 } }"
|
: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 }}
|
{{ $store.getters['lists/getListById'](task.listId).title }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<a
|
<a
|
||||||
class="favorite"
|
|
||||||
:class="{'is-favorite': task.isFavorite}"
|
:class="{'is-favorite': task.isFavorite}"
|
||||||
@click="toggleFavorite">
|
@click="toggleFavorite"
|
||||||
|
class="favorite">
|
||||||
<icon icon="star" v-if="task.isFavorite"/>
|
<icon icon="star" v-if="task.isFavorite"/>
|
||||||
<icon :icon="['far', 'star']" v-else/>
|
<icon :icon="['far', 'star']" v-else/>
|
||||||
</a>
|
</a>
|
||||||
|
@ -62,15 +62,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TaskModel from '../../../models/task'
|
import TaskModel from '../../../models/task'
|
||||||
import PriorityLabel from './priorityLabel'
|
import PriorityLabel from './priorityLabel'
|
||||||
import TaskService from '../../../services/task'
|
import TaskService from '../../../services/task'
|
||||||
import Labels from './labels'
|
import Labels from './labels'
|
||||||
import User from '../../misc/user'
|
import User from '../../misc/user'
|
||||||
import Fancycheckbox from '../../input/fancycheckbox'
|
import Fancycheckbox from '../../input/fancycheckbox'
|
||||||
import DeferTask from './defer-task'
|
import DeferTask from './defer-task'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'singleTaskInList',
|
name: 'singleTaskInList',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
},
|
},
|
||||||
taskDetailRoute: {
|
taskDetailRoute: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'task.list.detail'
|
default: 'task.list.detail',
|
||||||
},
|
},
|
||||||
showList: {
|
showList: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -122,7 +122,10 @@
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
currentList() {
|
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: {
|
methods: {
|
||||||
|
@ -139,10 +142,10 @@
|
||||||
title: 'Undo',
|
title: 'Undo',
|
||||||
callback: () => this.markAsDone({
|
callback: () => this.markAsDone({
|
||||||
target: {
|
target: {
|
||||||
checked: !checked
|
checked: !checked,
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
}]
|
}],
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
|
@ -151,7 +154,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checked) {
|
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 {
|
} else {
|
||||||
updateFunc() // Don't delay it when un-marking it as it doesn't have an animation the other way around
|
updateFunc() // Don't delay it when un-marking it as it doesn't have an animation the other way around
|
||||||
}
|
}
|
||||||
|
@ -169,5 +172,5 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<a @click="click">
|
<a @click="click">
|
||||||
<icon icon="sort-up" v-if="order === 'asc'"/>
|
<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/>
|
<icon icon="sort" v-else/>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'sort',
|
name: 'sort',
|
||||||
props: {
|
props: {
|
||||||
order: {
|
order: {
|
||||||
|
@ -20,5 +20,5 @@
|
||||||
this.$emit('click')
|
this.$emit('click')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -8,57 +8,57 @@
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="control mb-4">
|
<div class="control mb-4">
|
||||||
<label class="radio">
|
<label class="radio">
|
||||||
<input type="radio" name="avatarProvider" v-model="avatarProvider" value="default"/>
|
<input name="avatarProvider" type="radio" v-model="avatarProvider" value="default"/>
|
||||||
Default
|
Default
|
||||||
</label>
|
</label>
|
||||||
<label class="radio">
|
<label class="radio">
|
||||||
<input type="radio" name="avatarProvider" v-model="avatarProvider" value="initials"/>
|
<input name="avatarProvider" type="radio" v-model="avatarProvider" value="initials"/>
|
||||||
Initials
|
Initials
|
||||||
</label>
|
</label>
|
||||||
<label class="radio">
|
<label class="radio">
|
||||||
<input type="radio" name="avatarProvider" v-model="avatarProvider" value="gravatar"/>
|
<input name="avatarProvider" type="radio" v-model="avatarProvider" value="gravatar"/>
|
||||||
Gravatar
|
Gravatar
|
||||||
</label>
|
</label>
|
||||||
<label class="radio">
|
<label class="radio">
|
||||||
<input type="radio" name="avatarProvider" v-model="avatarProvider" value="upload"/>
|
<input name="avatarProvider" type="radio" v-model="avatarProvider" value="upload"/>
|
||||||
Upload
|
Upload
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="avatarProvider === 'upload'">
|
<template v-if="avatarProvider === 'upload'">
|
||||||
<input
|
<input
|
||||||
type="file"
|
|
||||||
ref="avatarUploadInput"
|
|
||||||
@change="cropAvatar"
|
@change="cropAvatar"
|
||||||
class="is-hidden"
|
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
|
class="is-hidden"
|
||||||
|
ref="avatarUploadInput"
|
||||||
|
type="file"
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
v-if="!isCropAvatar"
|
:class="{ 'is-loading': avatarService.loading || loading}"
|
||||||
class="button is-primary"
|
|
||||||
@click="$refs.avatarUploadInput.click()"
|
@click="$refs.avatarUploadInput.click()"
|
||||||
:class="{ 'is-loading': avatarService.loading || loading}">
|
class="button is-primary"
|
||||||
|
v-if="!isCropAvatar">
|
||||||
Upload Avatar
|
Upload Avatar
|
||||||
</a>
|
</a>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<cropper
|
<cropper
|
||||||
:src="avatarToCrop"
|
:src="avatarToCrop"
|
||||||
class="mb-4"
|
|
||||||
@ready="() => loading = false"
|
|
||||||
:stencil-props="{aspectRatio: 1}"
|
:stencil-props="{aspectRatio: 1}"
|
||||||
|
@ready="() => loading = false"
|
||||||
|
class="mb-4"
|
||||||
ref="cropper"/>
|
ref="cropper"/>
|
||||||
<a
|
<a
|
||||||
class="button is-primary"
|
:class="{ 'is-loading': avatarService.loading || loading}"
|
||||||
@click="uploadAvatar"
|
@click="uploadAvatar"
|
||||||
:class="{ 'is-loading': avatarService.loading || loading}">
|
class="button is-primary">
|
||||||
Upload Avatar
|
Upload Avatar
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="bigbuttons" v-if="avatarProvider !== 'upload'">
|
<div class="bigbuttons" v-if="avatarProvider !== 'upload'">
|
||||||
<button @click="updateAvatarStatus()" class="button is-primary is-fullwidth"
|
<button :class="{ 'is-loading': avatarService.loading || loading}" @click="updateAvatarStatus()"
|
||||||
:class="{ 'is-loading': avatarService.loading || loading}">
|
class="button is-primary is-fullwidth">
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -67,12 +67,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {Cropper} from 'vue-advanced-cropper'
|
import {Cropper} from 'vue-advanced-cropper'
|
||||||
|
|
||||||
import AvatarService from '../../services/avatar'
|
import AvatarService from '../../services/avatar'
|
||||||
import AvatarModel from '../../models/avatar'
|
import AvatarModel from '../../models/avatar'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'avatar-settings',
|
name: 'avatar-settings',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -145,5 +145,5 @@
|
||||||
reader.readAsDataURL(avatar[0])
|
reader.readAsDataURL(avatar[0])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
|
||||||
export const applyDrag = (arr, dragResult) => {
|
export const applyDrag = (arr, dragResult) => {
|
||||||
const { removedIndex, addedIndex, payload } = dragResult
|
const {removedIndex, addedIndex, payload} = dragResult
|
||||||
if (removedIndex === null && addedIndex === null) return arr
|
if (removedIndex === null && addedIndex === null) return arr
|
||||||
|
|
||||||
const result = [...arr]
|
const result = [...arr]
|
||||||
|
|
|
@ -19,7 +19,7 @@ export function objectToCamelCase(object) {
|
||||||
|
|
||||||
// Recursive processing
|
// Recursive processing
|
||||||
// Prevent processing for some cases
|
// Prevent processing for some cases
|
||||||
if(object[m] === null) {
|
if (object[m] === null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ export function objectToCamelCase(object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call it again for nested objects
|
// Call it again for nested objects
|
||||||
if(typeof object[m] === 'object') {
|
if (typeof object[m] === 'object') {
|
||||||
parsedObject[camelCase(m)] = objectToCamelCase(object[m])
|
parsedObject[camelCase(m)] = objectToCamelCase(object[m])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ export function objectToSnakeCase(object) {
|
||||||
|
|
||||||
// Recursive processing
|
// Recursive processing
|
||||||
// Prevent processing for some cases
|
// Prevent processing for some cases
|
||||||
if(
|
if (
|
||||||
object[m] === null ||
|
object[m] === null ||
|
||||||
(object[m] instanceof Date)
|
(object[m] instanceof Date)
|
||||||
) {
|
) {
|
||||||
|
@ -71,7 +71,7 @@ export function objectToSnakeCase(object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call it again for nested objects
|
// Call it again for nested objects
|
||||||
if(typeof object[m] === 'object') {
|
if (typeof object[m] === 'object') {
|
||||||
parsedObject[snakeCase(m)] = objectToSnakeCase(object[m])
|
parsedObject[snakeCase(m)] = objectToSnakeCase(object[m])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
export const filterObject = (obj, fn) => {
|
export const filterObject = (obj, fn) => {
|
||||||
let key
|
let key
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
export const saveListView = (listId, routeName) => {
|
export const saveListView = (listId, routeName) => {
|
||||||
const savedListView = localStorage.getItem('listView')
|
const savedListView = localStorage.getItem('listView')
|
||||||
let savedListViewJson = false
|
let savedListViewJson = false
|
||||||
|
@ -7,7 +6,7 @@ export const saveListView = (listId, routeName) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let listView = {}
|
let listView = {}
|
||||||
if(savedListViewJson) {
|
if (savedListViewJson) {
|
||||||
listView = savedListViewJson
|
listView = savedListViewJson
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@ export const saveListView = (listId, routeName) => {
|
||||||
export const getListView = listId => {
|
export const getListView = listId => {
|
||||||
// Remove old stored settings
|
// Remove old stored settings
|
||||||
const savedListView = localStorage.getItem('listView')
|
const savedListView = localStorage.getItem('listView')
|
||||||
if(savedListView !== null && savedListView.startsWith('list.')) {
|
if (savedListView !== null && savedListView.startsWith('list.')) {
|
||||||
localStorage.removeItem('listView')
|
localStorage.removeItem('listView')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ export const getListView = listId => {
|
||||||
|
|
||||||
const savedListViewJson = JSON.parse(savedListView)
|
const savedListViewJson = JSON.parse(savedListView)
|
||||||
|
|
||||||
if(!savedListViewJson[listId]) {
|
if (!savedListViewJson[listId]) {
|
||||||
return 'list.list'
|
return 'list.list'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
export const setTitle = title => {
|
export const setTitle = title => {
|
||||||
if (typeof title === 'undefined' || title === '') {
|
if (typeof title === 'undefined' || title === '') {
|
||||||
document.title = 'Vikunja'
|
document.title = 'Vikunja'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
export const HTTP = axios.create({
|
export const HTTP = axios.create({
|
||||||
baseURL: window.API_URL
|
baseURL: window.API_URL,
|
||||||
})
|
})
|
||||||
|
|
157
src/main.js
157
src/main.js
|
@ -3,77 +3,87 @@ import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
|
||||||
import {VERSION} from './version.json'
|
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}`)
|
console.info(`Vikunja frontend version ${VERSION}`)
|
||||||
|
|
||||||
// Make sure the api url does not contain a / at the end
|
// Make sure the api url does not contain a / at the end
|
||||||
if(window.API_URL.substr(window.API_URL.length - 1, window.API_URL.length) === '/') {
|
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)
|
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)
|
Vue.component('modal', Modal)
|
||||||
|
|
||||||
// Add CSS
|
|
||||||
import './styles/vikunja.scss'
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
// Notifications
|
|
||||||
import Notifications from 'vue-notification'
|
|
||||||
Vue.use(Notifications)
|
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(faSignOutAlt)
|
||||||
library.add(faPlus)
|
library.add(faPlus)
|
||||||
library.add(faListOl)
|
library.add(faListOl)
|
||||||
|
@ -124,15 +134,8 @@ library.add(faStarSolid)
|
||||||
|
|
||||||
Vue.component('icon', FontAwesomeIcon)
|
Vue.component('icon', FontAwesomeIcon)
|
||||||
|
|
||||||
// Tooltip
|
|
||||||
import VTooltip from 'v-tooltip'
|
|
||||||
Vue.use(VTooltip, {defaultHtml: false})
|
Vue.use(VTooltip, {defaultHtml: false})
|
||||||
|
|
||||||
// PWA
|
|
||||||
import './registerServiceWorker'
|
|
||||||
|
|
||||||
// Shortcuts
|
|
||||||
import vueShortkey from 'vue-shortkey'
|
|
||||||
Vue.use(vueShortkey)
|
Vue.use(vueShortkey)
|
||||||
|
|
||||||
// Set focus
|
// Set focus
|
||||||
|
@ -146,14 +149,9 @@ Vue.directive('focus', {
|
||||||
if (window.innerWidth > 769 || (typeof modifiers.always !== 'undefined' && modifiers.always)) {
|
if (window.innerWidth > 769 || (typeof modifiers.always !== 'undefined' && modifiers.always)) {
|
||||||
el.focus()
|
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({
|
Vue.mixin({
|
||||||
methods: {
|
methods: {
|
||||||
formatDateSince: date => {
|
formatDateSince: date => {
|
||||||
|
@ -161,35 +159,32 @@ Vue.mixin({
|
||||||
date = new Date(date)
|
date = new Date(date)
|
||||||
}
|
}
|
||||||
const currentDate = new Date()
|
const currentDate = new Date()
|
||||||
let formatted = '';
|
let formatted = ''
|
||||||
if (date > currentDate) {
|
if (date > currentDate) {
|
||||||
formatted += 'in '
|
formatted += 'in '
|
||||||
}
|
}
|
||||||
formatted += formatDistance(date, currentDate)
|
formatted += formatDistance(date, currentDate)
|
||||||
if(date < currentDate) {
|
if (date < currentDate) {
|
||||||
formatted += ' ago'
|
formatted += ' ago'
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatted;
|
return formatted
|
||||||
},
|
},
|
||||||
formatDate: date => {
|
formatDate: date => {
|
||||||
if (typeof date === 'string') {
|
if (typeof date === 'string') {
|
||||||
date = new Date(date)
|
date = new Date(date)
|
||||||
}
|
}
|
||||||
return date ? format(date, 'PPPPpppp'): ''
|
return date ? format(date, 'PPPPpppp') : ''
|
||||||
},
|
},
|
||||||
error: (e, context, actions = []) => message.error(e, context, actions),
|
error: (e, context, actions = []) => message.error(e, context, actions),
|
||||||
success: (s, context, actions = []) => message.success(s, context, actions),
|
success: (s, context, actions = []) => message.success(s, context, actions),
|
||||||
colorIsDark: colorIsDark,
|
colorIsDark: colorIsDark,
|
||||||
setTitle: setTitle,
|
setTitle: setTitle,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Vuex
|
|
||||||
import {store} from './store'
|
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
store,
|
store,
|
||||||
render: h => h(App)
|
render: h => h(App),
|
||||||
}).$mount('#app')
|
}).$mount('#app')
|
||||||
|
|
|
@ -2,11 +2,11 @@ export default {
|
||||||
setLoading(context) {
|
setLoading(context) {
|
||||||
const timeout = setTimeout(function () {
|
const timeout = setTimeout(function () {
|
||||||
context.loading = true
|
context.loading = true
|
||||||
}, 100);
|
}, 100)
|
||||||
return () => {
|
return () => {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
context.loading = false
|
context.loading = false
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
error(e, context, actions = []) {
|
error(e, context, actions = []) {
|
||||||
// Build the notification text from error response
|
// Build the notification text from error response
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {defaults, omitBy, isNil} from 'lodash'
|
import {defaults, isNil, omitBy} from 'lodash'
|
||||||
import {objectToCamelCase} from '../helpers/case'
|
import {objectToCamelCase} from '@/helpers/case'
|
||||||
|
|
||||||
export default class AbstractModel {
|
export default class AbstractModel {
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,6 @@ export default class FileModel extends AbstractModel {
|
||||||
it++
|
it++
|
||||||
}
|
}
|
||||||
|
|
||||||
return Number(Math.round(size+'e2')+'e-2') + ' ' + sizes[it]
|
return Number(Math.round(size + 'e2') + 'e-2') + ' ' + sizes[it]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import AbstractModel from './abstractModel'
|
import AbstractModel from './abstractModel'
|
||||||
import UserModel from './user'
|
import UserModel from './user'
|
||||||
import {colorIsDark} from '../helpers/colorIsDark'
|
import {colorIsDark} from '@/helpers/colorIsDark'
|
||||||
|
|
||||||
export default class LabelModel extends AbstractModel {
|
export default class LabelModel extends AbstractModel {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import AbstractModel from "./abstractModel";
|
import AbstractModel from './abstractModel'
|
||||||
|
|
||||||
export default class LabelTask extends AbstractModel {
|
export default class LabelTask extends AbstractModel {
|
||||||
defaults() {
|
defaults() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import AbstractModel from "./abstractModel";
|
import AbstractModel from './abstractModel'
|
||||||
|
|
||||||
export default class PasswordResetModel extends AbstractModel {
|
export default class PasswordResetModel extends AbstractModel {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ export default class TeamListModel extends TeamShareBaseModel {
|
||||||
super.defaults(),
|
super.defaults(),
|
||||||
{
|
{
|
||||||
listId: 0,
|
listId: 0,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ export default class TeamMemberModel extends UserModel {
|
||||||
{
|
{
|
||||||
admin: false,
|
admin: false,
|
||||||
teamId: 0,
|
teamId: 0,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ export default class TeamNamespaceModel extends TeamShareBaseModel {
|
||||||
super.defaults(),
|
super.defaults(),
|
||||||
{
|
{
|
||||||
namespaceId: 0,
|
namespaceId: 0,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ export default class TeamShareBaseModel extends AbstractModel {
|
||||||
right: 0,
|
right: 0,
|
||||||
|
|
||||||
created: null,
|
created: null,
|
||||||
updated: null
|
updated: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ export default class UserListModel extends UserShareBaseModel {
|
||||||
super.defaults(),
|
super.defaults(),
|
||||||
{
|
{
|
||||||
listId: 0,
|
listId: 0,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import UserShareBaseModel from "./userShareBase";
|
import UserShareBaseModel from './userShareBase'
|
||||||
import {merge} from 'lodash'
|
import {merge} from 'lodash'
|
||||||
|
|
||||||
// This class extends the user share model with a 'rights' parameter which is used in sharing
|
// 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(),
|
super.defaults(),
|
||||||
{
|
{
|
||||||
namespaceId: 0,
|
namespaceId: 0,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,57 +1,57 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
import { register } from 'register-service-worker'
|
import {register} from 'register-service-worker'
|
||||||
import swEvents from './ServiceWorker/events'
|
import swEvents from './ServiceWorker/events'
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
register(`${process.env.BASE_URL}sw.js`, {
|
register(`${process.env.BASE_URL}sw.js`, {
|
||||||
ready () {
|
ready() {
|
||||||
console.log('App is being served from cache by a service worker.')
|
console.log('App is being served from cache by a service worker.')
|
||||||
},
|
},
|
||||||
registered () {
|
registered() {
|
||||||
console.log('Service worker has been registered.')
|
console.log('Service worker has been registered.')
|
||||||
},
|
},
|
||||||
cached () {
|
cached() {
|
||||||
console.log('Content has been cached for offline use.')
|
console.log('Content has been cached for offline use.')
|
||||||
},
|
},
|
||||||
updatefound () {
|
updatefound() {
|
||||||
console.log('New content is downloading.')
|
console.log('New content is downloading.')
|
||||||
},
|
},
|
||||||
updated (registration) {
|
updated(registration) {
|
||||||
console.log('New content is available; please refresh.')
|
console.log('New content is available; please refresh.')
|
||||||
// Send an event with the updated info
|
// Send an event with the updated info
|
||||||
document.dispatchEvent(
|
document.dispatchEvent(
|
||||||
new CustomEvent(swEvents.SW_UPDATED, { detail:registration })
|
new CustomEvent(swEvents.SW_UPDATED, {detail: registration}),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
offline () {
|
offline() {
|
||||||
console.log('No internet connection found. App is running in offline mode.')
|
console.log('No internet connection found. App is running in offline mode.')
|
||||||
},
|
},
|
||||||
error (error) {
|
error(error) {
|
||||||
console.error('Error during service worker registration:', error)
|
console.error('Error during service worker registration:', error)
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if(navigator && navigator.serviceWorker) {
|
if (navigator && navigator.serviceWorker) {
|
||||||
navigator.serviceWorker.addEventListener('message', event => {
|
navigator.serviceWorker.addEventListener('message', event => {
|
||||||
// for every message we expect an action field
|
// for every message we expect an action field
|
||||||
// determining operation that we should perform
|
// determining operation that we should perform
|
||||||
const { action } = event.data;
|
const {action} = event.data
|
||||||
// we use 2nd port provided by the message channel
|
// we use 2nd port provided by the message channel
|
||||||
const port = event.ports[0];
|
const port = event.ports[0]
|
||||||
|
|
||||||
if(action === 'getBearerToken') {
|
if (action === 'getBearerToken') {
|
||||||
console.debug('Token request from sw');
|
console.debug('Token request from sw')
|
||||||
port.postMessage({
|
port.postMessage({
|
||||||
authToken: localStorage.getItem('token'),
|
authToken: localStorage.getItem('token'),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.error('Unknown event', event);
|
console.error('Unknown event', event)
|
||||||
port.postMessage({
|
port.postMessage({
|
||||||
error: 'Unknown request',
|
error: 'Unknown request',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,25 @@ import ErrorComponent from '../components/misc/error'
|
||||||
// User Handling
|
// User Handling
|
||||||
import LoginComponent from '../views/user/Login'
|
import LoginComponent from '../views/user/Login'
|
||||||
import RegisterComponent from '../views/user/Register'
|
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 = () => ({
|
const PasswordResetComponent = () => ({
|
||||||
component: import(/* webpackPrefetch: true *//* webpackChunkName: "user-settings" */'../views/user/PasswordReset'),
|
component: import(/* webpackPrefetch: true *//* webpackChunkName: "user-settings" */'../views/user/PasswordReset'),
|
||||||
|
@ -40,11 +59,6 @@ const EditListComponent = () => ({
|
||||||
error: ErrorComponent,
|
error: ErrorComponent,
|
||||||
timeout: 60000,
|
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
|
// Namespace Handling
|
||||||
const NewNamespaceComponent = () => ({
|
const NewNamespaceComponent = () => ({
|
||||||
component: import(/* webpackPrefetch: true *//* webpackChunkName: "settings" */'../views/namespaces/NewNamespace'),
|
component: import(/* webpackPrefetch: true *//* webpackChunkName: "settings" */'../views/namespaces/NewNamespace'),
|
||||||
|
@ -58,9 +72,6 @@ const EditNamespaceComponent = () => ({
|
||||||
error: ErrorComponent,
|
error: ErrorComponent,
|
||||||
timeout: 60000,
|
timeout: 60000,
|
||||||
})
|
})
|
||||||
import ListNamespaces from '../views/namespaces/ListNamespaces'
|
|
||||||
// Team Handling
|
|
||||||
import ListTeamsComponent from '../views/teams/ListTeams'
|
|
||||||
|
|
||||||
const EditTeamComponent = () => ({
|
const EditTeamComponent = () => ({
|
||||||
component: import(/* webpackPrefetch: true *//* webpackChunkName: "settings" */'../views/teams/EditTeam'),
|
component: import(/* webpackPrefetch: true *//* webpackChunkName: "settings" */'../views/teams/EditTeam'),
|
||||||
|
@ -74,17 +85,6 @@ const NewTeamComponent = () => ({
|
||||||
error: ErrorComponent,
|
error: ErrorComponent,
|
||||||
timeout: 60000,
|
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)
|
Vue.use(Router)
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ export default new Router({
|
||||||
// Scroll to anchor should still work
|
// Scroll to anchor should still work
|
||||||
if (to.hash) {
|
if (to.hash) {
|
||||||
return {
|
return {
|
||||||
selector: to.hash
|
selector: to.hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ export default new Router({
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: HomeComponent
|
component: HomeComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '*',
|
path: '*',
|
||||||
|
@ -120,22 +120,22 @@ export default new Router({
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'user.login',
|
name: 'user.login',
|
||||||
component: LoginComponent
|
component: LoginComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/get-password-reset',
|
path: '/get-password-reset',
|
||||||
name: 'user.password-reset.request',
|
name: 'user.password-reset.request',
|
||||||
component: GetPasswordResetComponent
|
component: GetPasswordResetComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/password-reset',
|
path: '/password-reset',
|
||||||
name: 'user.password-reset.reset',
|
name: 'user.password-reset.reset',
|
||||||
component: PasswordResetComponent
|
component: PasswordResetComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/register',
|
path: '/register',
|
||||||
name: 'user.register',
|
name: 'user.register',
|
||||||
component: RegisterComponent
|
component: RegisterComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/user/settings',
|
path: '/user/settings',
|
||||||
|
@ -145,7 +145,7 @@ export default new Router({
|
||||||
{
|
{
|
||||||
path: '/share/:share/auth',
|
path: '/share/:share/auth',
|
||||||
name: 'link-share.auth',
|
name: 'link-share.auth',
|
||||||
component: LinkShareAuthComponent
|
component: LinkShareAuthComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/namespaces',
|
path: '/namespaces',
|
||||||
|
@ -155,22 +155,22 @@ export default new Router({
|
||||||
{
|
{
|
||||||
path: '/namespaces/new',
|
path: '/namespaces/new',
|
||||||
name: 'namespace.create',
|
name: 'namespace.create',
|
||||||
component: NewNamespaceComponent
|
component: NewNamespaceComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/namespaces/:id/edit',
|
path: '/namespaces/:id/edit',
|
||||||
name: 'namespace.edit',
|
name: 'namespace.edit',
|
||||||
component: EditNamespaceComponent
|
component: EditNamespaceComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/namespaces/:id/list',
|
path: '/namespaces/:id/list',
|
||||||
name: 'list.create',
|
name: 'list.create',
|
||||||
component: NewListComponent
|
component: NewListComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/lists/:id/edit',
|
path: '/lists/:id/edit',
|
||||||
name: 'list.edit',
|
name: 'list.edit',
|
||||||
component: EditListComponent
|
component: EditListComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/tasks/:id',
|
path: '/tasks/:id',
|
||||||
|
@ -228,27 +228,27 @@ export default new Router({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/teams',
|
path: '/teams',
|
||||||
name: 'teams.index',
|
name: 'teams.index',
|
||||||
component: ListTeamsComponent
|
component: ListTeamsComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/teams/new',
|
path: '/teams/new',
|
||||||
name: 'teams.create',
|
name: 'teams.create',
|
||||||
component: NewTeamComponent
|
component: NewTeamComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/teams/:id/edit',
|
path: '/teams/:id/edit',
|
||||||
name: 'teams.edit',
|
name: 'teams.edit',
|
||||||
component: EditTeamComponent
|
component: EditTeamComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/labels',
|
path: '/labels',
|
||||||
name: 'labels.index',
|
name: 'labels.index',
|
||||||
component: ListLabelsComponent
|
component: ListLabelsComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/migrate',
|
path: '/migrate',
|
||||||
|
@ -260,5 +260,5 @@ export default new Router({
|
||||||
name: 'migrate.service',
|
name: 'migrate.service',
|
||||||
component: MigrateServiceComponent,
|
component: MigrateServiceComponent,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
})
|
})
|
|
@ -1,6 +1,6 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {reduce, replace} from 'lodash'
|
import {reduce, replace} from 'lodash'
|
||||||
import {objectToSnakeCase} from '../helpers/case'
|
import {objectToSnakeCase} from '@/helpers/case'
|
||||||
|
|
||||||
export default class AbstractService {
|
export default class AbstractService {
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ export default class AbstractService {
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const result = this.modelCreateFactory(response.data)
|
const result = this.modelCreateFactory(response.data)
|
||||||
if(typeof model.maxRight !== 'undefined') {
|
if (typeof model.maxRight !== 'undefined') {
|
||||||
result.maxRight = model.maxRight
|
result.maxRight = model.maxRight
|
||||||
}
|
}
|
||||||
return Promise.resolve(result)
|
return Promise.resolve(result)
|
||||||
|
@ -406,7 +406,7 @@ export default class AbstractService {
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const result = this.modelUpdateFactory(response.data)
|
const result = this.modelUpdateFactory(response.data)
|
||||||
if(typeof model.maxRight !== 'undefined') {
|
if (typeof model.maxRight !== 'undefined') {
|
||||||
result.maxRight = model.maxRight
|
result.maxRight = model.maxRight
|
||||||
}
|
}
|
||||||
return Promise.resolve(result)
|
return Promise.resolve(result)
|
||||||
|
|
|
@ -38,17 +38,17 @@ export default class AttachmentService extends AbstractService {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
return window.URL.createObjectURL(new Blob([response.data]));
|
return window.URL.createObjectURL(new Blob([response.data]))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
download(model) {
|
download(model) {
|
||||||
this.getBlobUrl(model).then(url => {
|
this.getBlobUrl(model).then(url => {
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a')
|
||||||
link.href = url;
|
link.href = url
|
||||||
link.setAttribute('download', model.file.name);
|
link.setAttribute('download', model.file.name)
|
||||||
link.click();
|
link.click()
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,12 +62,12 @@ export default class AttachmentService extends AbstractService {
|
||||||
const data = new FormData()
|
const data = new FormData()
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
// TODO: Validation of file size
|
// 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(
|
return this.uploadFormData(
|
||||||
this.getReplacedRoute(this.paths.create, model),
|
this.getReplacedRoute(this.paths.create, model),
|
||||||
data
|
data,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default class BackgroundUploadService extends AbstractService {
|
||||||
return this.uploadFile(
|
return this.uploadFile(
|
||||||
this.getReplacedRoute(this.paths.create, {listId: listId}),
|
this.getReplacedRoute(this.paths.create, {listId: listId}),
|
||||||
file,
|
file,
|
||||||
'background'
|
'background',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import AbstractService from "./abstractService";
|
import AbstractService from './abstractService'
|
||||||
import LabelTask from "../models/labelTask";
|
import LabelTask from '../models/labelTask'
|
||||||
|
|
||||||
export default class LabelTaskService extends AbstractService {
|
export default class LabelTaskService extends AbstractService {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {formatISO} from 'date-fns'
|
||||||
export default class ListUserService extends AbstractService {
|
export default class ListUserService extends AbstractService {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
getAll: '/lists/{listId}/listusers'
|
getAll: '/lists/{listId}/listusers',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,17 +7,17 @@ export default class AbstractMigrationService extends AbstractService {
|
||||||
|
|
||||||
constructor(serviceUrlKey) {
|
constructor(serviceUrlKey) {
|
||||||
super({
|
super({
|
||||||
update: '/migration/'+serviceUrlKey+'/migrate',
|
update: '/migration/' + serviceUrlKey + '/migrate',
|
||||||
})
|
})
|
||||||
this.serviceUrlKey = serviceUrlKey
|
this.serviceUrlKey = serviceUrlKey
|
||||||
}
|
}
|
||||||
|
|
||||||
getAuthUrl() {
|
getAuthUrl() {
|
||||||
return this.getM('/migration/'+this.serviceUrlKey+'/auth')
|
return this.getM('/migration/' + this.serviceUrlKey + '/auth')
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatus() {
|
getStatus() {
|
||||||
return this.getM('/migration/'+this.serviceUrlKey+'/status')
|
return this.getM('/migration/' + this.serviceUrlKey + '/status')
|
||||||
}
|
}
|
||||||
|
|
||||||
migrate(data) {
|
migrate(data) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default class NamespaceService extends AbstractService {
|
||||||
getAll: '/namespaces',
|
getAll: '/namespaces',
|
||||||
update: '/namespaces/{id}',
|
update: '/namespaces/{id}',
|
||||||
delete: '/namespaces/{id}',
|
delete: '/namespaces/{id}',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
processModel(model) {
|
processModel(model) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import AbstractService from './abstractService'
|
||||||
export default class PasswordUpdateService extends AbstractService {
|
export default class PasswordUpdateService extends AbstractService {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
update: '/user/password'
|
update: '/user/password',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ export default class TaskService extends AbstractService {
|
||||||
get: '/tasks/{id}',
|
get: '/tasks/{id}',
|
||||||
update: '/tasks/{id}',
|
update: '/tasks/{id}',
|
||||||
delete: '/tasks/{id}',
|
delete: '/tasks/{id}',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
modelFactory(data) {
|
modelFactory(data) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default class TeamService extends AbstractService {
|
||||||
getAll: '/teams',
|
getAll: '/teams',
|
||||||
update: '/teams/{id}',
|
update: '/teams/{id}',
|
||||||
delete: '/teams/{id}',
|
delete: '/teams/{id}',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
processModel(model) {
|
processModel(model) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import AbstractService from './abstractService'
|
import AbstractService from './abstractService'
|
||||||
import TotpModel from "../models/totp";
|
import TotpModel from '../models/totp'
|
||||||
|
|
||||||
export default class TotpService extends AbstractService {
|
export default class TotpService extends AbstractService {
|
||||||
urlPrefix = '/user/settings/totp'
|
urlPrefix = '/user/settings/totp'
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {formatISO} from 'date-fns'
|
||||||
export default class UserService extends AbstractService {
|
export default class UserService extends AbstractService {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
getAll: '/users'
|
getAll: '/users',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
|
||||||
|
|
||||||
import {CURRENT_LIST, ERROR_MESSAGE, HAS_TASKS, IS_FULLPAGE, LOADING, ONLINE} from './mutation-types'
|
import {CURRENT_LIST, ERROR_MESSAGE, HAS_TASKS, IS_FULLPAGE, LOADING, ONLINE} from './mutation-types'
|
||||||
import config from './modules/config'
|
import config from './modules/config'
|
||||||
import auth from './modules/auth'
|
import auth from './modules/auth'
|
||||||
|
@ -13,7 +10,9 @@ import lists from './modules/lists'
|
||||||
import attachments from './modules/attachments'
|
import attachments from './modules/attachments'
|
||||||
|
|
||||||
import ListService from '../services/list'
|
import ListService from '../services/list'
|
||||||
import {setTitle} from '../helpers/setTitle'
|
import {setTitle} from '@/helpers/setTitle'
|
||||||
|
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
export const store = new Vuex.Store({
|
export const store = new Vuex.Store({
|
||||||
modules: {
|
modules: {
|
||||||
|
@ -97,6 +96,6 @@ export const store = new Vuex.Store({
|
||||||
},
|
},
|
||||||
[HAS_TASKS](state, hasTasks) {
|
[HAS_TASKS](state, hasTasks) {
|
||||||
state.hasTasks = hasTasks
|
state.hasTasks = hasTasks
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
|
@ -1,6 +1,6 @@
|
||||||
import {HTTP} from '../../http-common'
|
import {HTTP} from '@/http-common'
|
||||||
import {ERROR_MESSAGE, LOADING} from "../mutation-types";
|
import {ERROR_MESSAGE, LOADING} from '../mutation-types'
|
||||||
import UserModel from "../../models/user";
|
import UserModel from '../../models/user'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
|
@ -39,10 +39,10 @@ export default {
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
username: credentials.username,
|
username: credentials.username,
|
||||||
password: credentials.password
|
password: credentials.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
if(credentials.totpPasscode) {
|
if (credentials.totpPasscode) {
|
||||||
data.totp_passcode = credentials.totpPasscode
|
data.totp_passcode = credentials.totpPasscode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ export default {
|
||||||
return HTTP.post('register', {
|
return HTTP.post('register', {
|
||||||
username: credentials.username,
|
username: credentials.username,
|
||||||
email: credentials.email,
|
email: credentials.email,
|
||||||
password: credentials.password
|
password: credentials.password,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return ctx.dispatch('login', credentials)
|
return ctx.dispatch('login', credentials)
|
||||||
|
@ -135,7 +135,7 @@ export default {
|
||||||
HTTP.post('user/token', null, {
|
HTTP.post('user/token', null, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + localStorage.getItem('token'),
|
Authorization: 'Bearer ' + localStorage.getItem('token'),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
localStorage.setItem('token', r.data.token)
|
localStorage.setItem('token', r.data.token)
|
||||||
|
@ -149,6 +149,6 @@ export default {
|
||||||
logout(ctx) {
|
logout(ctx) {
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('token')
|
||||||
ctx.dispatch('checkAuth')
|
ctx.dispatch('checkAuth')
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import {CONFIG} from '../mutation-types'
|
import {CONFIG} from '../mutation-types'
|
||||||
import {HTTP} from '../../http-common'
|
import {HTTP} from '@/http-common'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
|
@ -18,7 +18,7 @@ export default {
|
||||||
legal: {
|
legal: {
|
||||||
imprintUrl: '',
|
imprintUrl: '',
|
||||||
privacyPolicyUrl: '',
|
privacyPolicyUrl: '',
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
mutations: {
|
mutations: {
|
||||||
[CONFIG](state, config) {
|
[CONFIG](state, config) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
|
||||||
import BucketService from '../../services/bucket'
|
import BucketService from '../../services/bucket'
|
||||||
import {filterObject} from '../../helpers/filterObject'
|
import {filterObject} from '@/helpers/filterObject'
|
||||||
import {setLoading} from '../helper'
|
import {setLoading} from '../helper'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,7 +90,7 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getTaskById: state => id => {
|
getTaskById: state => id => {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default {
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getListById: state => id => {
|
getListById: state => id => {
|
||||||
if(typeof state[id] !== 'undefined') {
|
if (typeof state[id] !== 'undefined') {
|
||||||
return state[id]
|
return state[id]
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -95,7 +95,7 @@ export default {
|
||||||
},
|
},
|
||||||
loadNamespacesIfFavoritesDontExist(ctx) {
|
loadNamespacesIfFavoritesDontExist(ctx) {
|
||||||
// The first namespace should be the one holding all favorites
|
// The first namespace should be the one holding all favorites
|
||||||
if(ctx.state.namespaces[0].id !== -2) {
|
if (ctx.state.namespaces[0].id !== -2) {
|
||||||
return ctx.dispatch('loadNamespaces')
|
return ctx.dispatch('loadNamespaces')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
.dropzone {
|
.dropzone {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background: rgba(250,250,250,0.8);
|
background: rgba(250, 250, 250, 0.8);
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
&.hidden {
|
&.hidden {
|
||||||
display:none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drop-hint {
|
.drop-hint {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.box,.card{
|
.box, .card {
|
||||||
border: $thickness solid $border;
|
border: $thickness solid $border;
|
||||||
box-shadow: 0.3em 0.3em 0.8em darken($light, 6);
|
box-shadow: 0.3em 0.3em 0.8em darken($light, 6);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,12 @@ fieldset[disabled] .multiselect {
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
box-shadow: 0 0 0 1px transparent;
|
box-shadow: 0 0 0 1px transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
animation: spinning 2.4s cubic-bezier(0.41, 0.26, 0.2, 0.62);
|
animation: spinning 2.4s cubic-bezier(0.41, 0.26, 0.2, 0.62);
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
animation: spinning 2.4s cubic-bezier(0.51, 0.09, 0.21, 0.8);
|
animation: spinning 2.4s cubic-bezier(0.51, 0.09, 0.21, 0.8);
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
|
@ -58,9 +60,11 @@ fieldset[disabled] .multiselect {
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: $text;
|
color: $text;
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
@ -73,12 +77,14 @@ fieldset[disabled] .multiselect {
|
||||||
|
|
||||||
.multiselect--active {
|
.multiselect--active {
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
|
|
||||||
&:not(.multiselect--above) {
|
&:not(.multiselect--above) {
|
||||||
.multiselect__current, .multiselect__input, .multiselect__tags {
|
.multiselect__current, .multiselect__input, .multiselect__tags {
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__select {
|
.multiselect__select {
|
||||||
transform: rotateZ(180deg);
|
transform: rotateZ(180deg);
|
||||||
}
|
}
|
||||||
|
@ -131,6 +137,7 @@ fieldset[disabled] .multiselect {
|
||||||
border-color: $primary;
|
border-color: $primary;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
@ -184,14 +191,17 @@ fieldset[disabled] .multiselect {
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: "×";
|
content: "×";
|
||||||
color: darken($multiselect-highlight, 20);
|
color: darken($multiselect-highlight, 20);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus, &:hover {
|
&:focus, &:hover {
|
||||||
background: lighten($multiselect-highlight, 10);
|
background: lighten($multiselect-highlight, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus:after, &:hover:after {
|
&:focus:after, &:hover:after {
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
|
@ -227,6 +237,7 @@ fieldset[disabled] .multiselect {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: transform 0.2s ease;
|
transition: transform 0.2s ease;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
position: relative;
|
position: relative;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -305,6 +316,7 @@ fieldset[disabled] .multiselect {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -320,6 +332,7 @@ fieldset[disabled] .multiselect {
|
||||||
background: $multiselect-highlight;
|
background: $multiselect-highlight;
|
||||||
outline: none;
|
outline: none;
|
||||||
color: $white;
|
color: $white;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: attr(data-select);
|
content: attr(data-select);
|
||||||
background: $multiselect-highlight;
|
background: $multiselect-highlight;
|
||||||
|
@ -331,13 +344,16 @@ fieldset[disabled] .multiselect {
|
||||||
background: darken($white, 10);
|
background: darken($white, 10);
|
||||||
color: $multiselect-dark;
|
color: $multiselect-dark;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: attr(data-selected);
|
content: attr(data-selected);
|
||||||
color: silver;
|
color: silver;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.multiselect__option--highlight {
|
&.multiselect__option--highlight {
|
||||||
background: $multiselect-highlight-negative;
|
background: $multiselect-highlight-negative;
|
||||||
color: $white;
|
color: $white;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
background: $multiselect-highlight-negative;
|
background: $multiselect-highlight-negative;
|
||||||
content: attr(data-deselect);
|
content: attr(data-deselect);
|
||||||
|
@ -348,6 +364,7 @@ fieldset[disabled] .multiselect {
|
||||||
|
|
||||||
.multiselect--disabled {
|
.multiselect--disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
.multiselect__current, .multiselect__select {
|
.multiselect__current, .multiselect__select {
|
||||||
background: $multiselect-disabled;
|
background: $multiselect-disabled;
|
||||||
color: darken($multiselect-disabled, 40);
|
color: darken($multiselect-disabled, 40);
|
||||||
|
@ -364,9 +381,11 @@ fieldset[disabled] .multiselect {
|
||||||
.multiselect__option--group {
|
.multiselect__option--group {
|
||||||
background: $multiselect-disabled;
|
background: $multiselect-disabled;
|
||||||
color: $multiselect-dark;
|
color: $multiselect-dark;
|
||||||
|
|
||||||
&.multiselect__option--highlight {
|
&.multiselect__option--highlight {
|
||||||
background: $multiselect-dark;
|
background: $multiselect-dark;
|
||||||
color: $white;
|
color: $white;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
background: $multiselect-dark;
|
background: $multiselect-dark;
|
||||||
}
|
}
|
||||||
|
@ -380,6 +399,7 @@ fieldset[disabled] .multiselect {
|
||||||
.multiselect__option--group-selected.multiselect__option--highlight {
|
.multiselect__option--group-selected.multiselect__option--highlight {
|
||||||
background: $multiselect-highlight-negative;
|
background: $multiselect-highlight-negative;
|
||||||
color: $white;
|
color: $white;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
background: $multiselect-highlight-negative;
|
background: $multiselect-highlight-negative;
|
||||||
content: attr(data-deselect);
|
content: attr(data-deselect);
|
||||||
|
@ -406,24 +426,30 @@ fieldset[disabled] .multiselect {
|
||||||
.multiselect {
|
.multiselect {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__select {
|
.multiselect__select {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 1px;
|
left: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__tags {
|
.multiselect__tags {
|
||||||
padding: 8px 8px 0px 40px;
|
padding: 8px 8px 0px 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__content {
|
.multiselect__content {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__option:after {
|
.multiselect__option:after {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__clear {
|
.multiselect__clear {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__spinner {
|
.multiselect__spinner {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 1px;
|
left: 1px;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
// Chrome
|
// Chrome
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: $scrollbar-height;
|
width: $scrollbar-height;
|
||||||
|
@ -25,7 +24,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firefox
|
// Firefox
|
||||||
*{
|
* {
|
||||||
scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color;
|
scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.fullpage{
|
.fullpage {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
padding: 20vw 10vw;
|
padding: 20vw 10vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
input{
|
input {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 3px solid darken($vikunja-blue, 10);
|
border-bottom: 3px solid darken($vikunja-blue, 10);
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|
||||||
&::placeholder{
|
&::placeholder {
|
||||||
color: $vikunja-light-text;
|
color: $vikunja-light-text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3{
|
h3 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,12 +46,12 @@
|
||||||
color: $vikunja-light-text;
|
color: $vikunja-light-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small{
|
.small {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
font-size: 0.5em;
|
font-size: 0.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullpage-overlay{
|
.fullpage-overlay {
|
||||||
z-index: 6 !important;
|
z-index: 6 !important;
|
||||||
}
|
}
|
|
@ -146,7 +146,7 @@ $gantt-vertical-border-color: lighten($grey, 45);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.high-priority{
|
.high-priority {
|
||||||
margin: 0 0 0 .5em;
|
margin: 0 0 0 .5em;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
.labels-list {
|
.labels-list {
|
||||||
a, a:hover{
|
a, a:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag{
|
.tag {
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
background: darken($background, 5);
|
background: darken($background, 5);
|
||||||
|
|
||||||
&.disabled{
|
&.disabled {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
|
||||||
&, a {
|
&, a {
|
||||||
|
|
|
@ -47,18 +47,41 @@
|
||||||
animation: wave 2s ease infinite;
|
animation: wave 2s ease infinite;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
|
|
||||||
&:nth-child(1){ animation-delay: 0; }
|
&:nth-child(1) {
|
||||||
&:nth-child(2){ animation-delay: 100ms; }
|
animation-delay: 0;
|
||||||
&:nth-child(3){ animation-delay: 200ms; }
|
}
|
||||||
&:nth-child(4){ animation-delay: 300ms; }
|
|
||||||
&:nth-child(5){ animation-delay: 400ms; }
|
&:nth-child(2) {
|
||||||
&:nth-child(6){ animation-delay: 500ms; }
|
animation-delay: 100ms;
|
||||||
&:nth-child(7){ animation-delay: 600ms; }
|
}
|
||||||
&:nth-child(8){ animation-delay: 700ms; }
|
|
||||||
|
&: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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes wave{
|
@keyframes wave {
|
||||||
0%, 40%, 100% {
|
0%, 40%, 100% {
|
||||||
transform: translate(0, 0);
|
transform: translate(0, 0);
|
||||||
background-color: $primary;
|
background-color: $primary;
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
ul.teams{
|
ul.teams {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
li{
|
li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-bottom: 1px solid $border;
|
border-bottom: 1px solid $border;
|
||||||
|
|
||||||
a{
|
a {
|
||||||
color: #363636;
|
color: #363636;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
|
|
||||||
&:hover{
|
&:hover {
|
||||||
background: darken(#fff, 2%);
|
background: darken(#fff, 2%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li:last-child{
|
li:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
box-shadow: 0.6em 0.6em 1em lighten($dark, 75);
|
box-shadow: 0.6em 0.6em 1em lighten($dark, 75);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.fullheight{
|
&.fullheight {
|
||||||
padding-right: 7px;
|
padding-right: 7px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
box-shadow: 0.1em 0.1em 0.7em lighten($dark, 75) !important;
|
box-shadow: 0.1em 0.1em 0.7em lighten($dark, 75) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.icon-only{
|
&.icon-only {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.noshadow{
|
&.noshadow {
|
||||||
&,
|
&,
|
||||||
&.is-hovered,
|
&.is-hovered,
|
||||||
&:hover,
|
&:hover,
|
||||||
|
@ -130,7 +130,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bigbuttons{
|
.bigbuttons {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-right{
|
.button-right {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contenteditable form
|
// Contenteditable form
|
||||||
.input.title{
|
.input.title {
|
||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
font-family: $vikunja-font;
|
font-family: $vikunja-font;
|
||||||
font-weight: 400 !important;
|
font-weight: 400 !important;
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3{
|
h1, h2, h3 {
|
||||||
.input.title {
|
.input.title {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
.link-share-list {
|
.link-share-list {
|
||||||
.field.has-addons {
|
.field.has-addons {
|
||||||
.control:first-child{
|
.control:first-child {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
overflow-y: auto
|
overflow-y: auto
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-share-container.has-background .container{
|
.link-share-container.has-background .container {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
@include loader;
|
@include loader;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -15,9 +16,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner{
|
.spinner {
|
||||||
&.is-loading {
|
&.is-loading {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
@include loader;
|
@include loader;
|
||||||
width: 2em;
|
width: 2em;
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notifications{
|
.notifications {
|
||||||
left: 0.5em !important;
|
left: 0.5em !important;
|
||||||
bottom: 1em !important;
|
bottom: 1em !important;
|
||||||
|
|
||||||
.notification-wrapper .notification{
|
.notification-wrapper .notification {
|
||||||
-webkit-border-radius: 0;
|
-webkit-border-radius: 0;
|
||||||
-moz-border-radius: 0;
|
-moz-border-radius: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $tablet){
|
@media screen and (max-width: $tablet) {
|
||||||
& {
|
& {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 1em;
|
left: 1em;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
@-webkit-keyframes bounce {
|
@-webkit-keyframes bounce {
|
||||||
from,
|
from,
|
||||||
20%,
|
20%,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
.fade-enter-active, .fade-leave-active {
|
.fade-enter-active, .fade-leave-active {
|
||||||
transition: opacity $transition-duration;
|
transition: opacity $transition-duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter, .fade-leave-to {
|
.fade-enter, .fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +9,7 @@
|
||||||
.width-enter-active, .width-leave-active {
|
.width-enter-active, .width-leave-active {
|
||||||
transition: width $transition-duration;
|
transition: width $transition-duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
.width-enter, .width-leave-to {
|
.width-enter, .width-leave-to {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"VERSION": "0.12"
|
"VERSION": "dev"
|
||||||
}
|
}
|
|
@ -6,10 +6,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: '404',
|
name: '404',
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setTitle('404')
|
this.setTitle('404')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="content has-text-centered">
|
<div class="content has-text-centered">
|
||||||
<h2>Hi {{userInfo.username}}!</h2>
|
<h2>Hi {{ userInfo.username }}!</h2>
|
||||||
<template v-if="!hasTasks">
|
<template v-if="!hasTasks">
|
||||||
<p>Click on a list or namespace on the left to get started.</p>
|
<p>Click on a list or namespace on the left to get started.</p>
|
||||||
<router-link
|
<router-link
|
||||||
class="button is-primary is-right noshadow is-outlined"
|
|
||||||
:to="{name: 'migrate.start'}"
|
:to="{name: 'migrate.start'}"
|
||||||
|
class="button is-primary is-right noshadow is-outlined"
|
||||||
v-if="migratorsEnabled"
|
v-if="migratorsEnabled"
|
||||||
>
|
>
|
||||||
Import your data into Vikunja
|
Import your data into Vikunja
|
||||||
|
@ -16,10 +16,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
import ShowTasks from './tasks/ShowTasks'
|
import ShowTasks from './tasks/ShowTasks'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
components: {
|
components: {
|
||||||
ShowTasks,
|
ShowTasks,
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
currentDate: new Date(),
|
currentDate: new Date(),
|
||||||
tasks: []
|
tasks: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: mapState({
|
computed: mapState({
|
||||||
|
@ -37,5 +37,5 @@
|
||||||
userInfo: state => state.auth.info,
|
userInfo: state => state.auth.info,
|
||||||
hasTasks: state => state.hasTasks,
|
hasTasks: state => state.hasTasks,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<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>
|
<h1>Manage labels</h1>
|
||||||
<p>
|
<p>
|
||||||
Click on a label to edit it.
|
Click on a label to edit it.
|
||||||
|
@ -9,10 +9,10 @@
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="labels-list column">
|
<div class="labels-list column">
|
||||||
<span
|
<span
|
||||||
v-for="l in labels" :key="l.id"
|
:class="{'disabled': userInfo.id !== l.createdBy.id}" :key="l.id"
|
||||||
class="tag"
|
|
||||||
:class="{'disabled': userInfo.id !== l.createdBy.id}"
|
|
||||||
:style="{'background': l.hexColor, 'color': l.textColor}"
|
:style="{'background': l.hexColor, 'color': l.textColor}"
|
||||||
|
class="tag"
|
||||||
|
v-for="l in labels"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="userInfo.id !== l.createdBy.id"
|
v-if="userInfo.id !== l.createdBy.id"
|
||||||
|
@ -20,12 +20,12 @@
|
||||||
{{ l.title }}
|
{{ l.title }}
|
||||||
</span>
|
</span>
|
||||||
<a
|
<a
|
||||||
@click="editLabel(l)"
|
|
||||||
:style="{'color': l.textColor}"
|
:style="{'color': l.textColor}"
|
||||||
|
@click="editLabel(l)"
|
||||||
v-else>
|
v-else>
|
||||||
{{ l.title }}
|
{{ l.title }}
|
||||||
</a>
|
</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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-4" v-if="isLabelEdit">
|
<div class="column is-4" v-if="isLabelEdit">
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
<span class="card-header-title">
|
<span class="card-header-title">
|
||||||
Edit Label
|
Edit Label
|
||||||
</span>
|
</span>
|
||||||
<a class="card-header-icon" @click="isLabelEdit = false">
|
<a @click="isLabelEdit = false" class="card-header-icon">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<icon icon="times"/>
|
<icon icon="times"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -47,8 +47,8 @@
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
|
||||||
placeholder="Label title"
|
placeholder="Label title"
|
||||||
|
type="text"
|
||||||
v-model="labelEditLabel.title"/>
|
v-model="labelEditLabel.title"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,10 +56,10 @@
|
||||||
<label class="label">Description</label>
|
<label class="label">Description</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<editor
|
<editor
|
||||||
placeholder="Label description"
|
|
||||||
v-model="labelEditLabel.description"
|
|
||||||
:preview-is-default="false"
|
:preview-is-default="false"
|
||||||
|
placeholder="Label description"
|
||||||
v-if="editorActive"
|
v-if="editorActive"
|
||||||
|
v-model="labelEditLabel.description"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,15 +71,15 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<button type="submit" class="button is-fullwidth is-primary"
|
<button :class="{ 'is-loading': labelService.loading}" class="button is-fullwidth is-primary"
|
||||||
:class="{ 'is-loading': labelService.loading}">
|
type="submit">
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a
|
<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">
|
<span class="icon">
|
||||||
<icon icon="trash-alt"/>
|
<icon icon="trash-alt"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -95,15 +95,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
import LabelService from '../../services/label'
|
import LabelService from '../../services/label'
|
||||||
import LabelModel from '../../models/label'
|
import LabelModel from '../../models/label'
|
||||||
import ColorPicker from '../../components/input/colorPicker'
|
import ColorPicker from '../../components/input/colorPicker'
|
||||||
import LoadingComponent from '../../components/misc/loading'
|
import LoadingComponent from '../../components/misc/loading'
|
||||||
import ErrorComponent from '../../components/misc/error'
|
import ErrorComponent from '../../components/misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListLabels',
|
name: 'ListLabels',
|
||||||
components: {
|
components: {
|
||||||
ColorPicker,
|
ColorPicker,
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
this.setTitle('Labels')
|
this.setTitle('Labels')
|
||||||
},
|
},
|
||||||
computed: mapState({
|
computed: mapState({
|
||||||
userInfo: state => state.auth.info
|
userInfo: state => state.auth.info,
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
loadLabels() {
|
loadLabels() {
|
||||||
|
@ -204,7 +204,7 @@
|
||||||
// See https://github.com/NikulinIlya/vue-easymde/issues/3
|
// See https://github.com/NikulinIlya/vue-easymde/issues/3
|
||||||
this.editorActive = false
|
this.editorActive = false
|
||||||
this.$nextTick(() => this.editorActive = true)
|
this.$nextTick(() => this.editorActive = true)
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<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">
|
<div class="notification is-warning" v-if="list.isArchived">
|
||||||
This list is archived.
|
This list is archived.
|
||||||
It is not possible to create new or edit tasks or it.
|
It is not possible to create new or edit tasks or it.
|
||||||
|
@ -17,14 +17,14 @@
|
||||||
<label class="label" for="listtext">List Name</label>
|
<label class="label" for="listtext">List Name</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
v-focus
|
|
||||||
:class="{ 'disabled': listService.loading}"
|
:class="{ 'disabled': listService.loading}"
|
||||||
:disabled="listService.loading"
|
:disabled="listService.loading"
|
||||||
|
@keyup.enter="submit"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
|
||||||
id="listtext"
|
id="listtext"
|
||||||
placeholder="The list title goes here..."
|
placeholder="The list title goes here..."
|
||||||
@keyup.enter="submit"
|
type="text"
|
||||||
|
v-focus
|
||||||
v-model="list.title"/>
|
v-model="list.title"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,14 +37,14 @@
|
||||||
</label>
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
v-focus
|
|
||||||
:class="{ 'disabled': listService.loading}"
|
:class="{ 'disabled': listService.loading}"
|
||||||
:disabled="listService.loading"
|
:disabled="listService.loading"
|
||||||
|
@keyup.enter="submit"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
|
||||||
id="listtext"
|
id="listtext"
|
||||||
placeholder="The list identifier goes here..."
|
placeholder="The list identifier goes here..."
|
||||||
@keyup.enter="submit"
|
type="text"
|
||||||
|
v-focus
|
||||||
v-model="list.identifier"/>
|
v-model="list.identifier"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,10 +54,10 @@
|
||||||
<editor
|
<editor
|
||||||
:class="{ 'disabled': listService.loading}"
|
:class="{ 'disabled': listService.loading}"
|
||||||
:disabled="listService.loading"
|
:disabled="listService.loading"
|
||||||
placeholder="The lists description goes here..."
|
|
||||||
id="listdescription"
|
|
||||||
v-model="list.description"
|
|
||||||
:preview-is-default="false"
|
:preview-is-default="false"
|
||||||
|
id="listdescription"
|
||||||
|
placeholder="The lists description goes here..."
|
||||||
|
v-model="list.description"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -81,14 +81,14 @@
|
||||||
|
|
||||||
<div class="columns bigbuttons">
|
<div class="columns bigbuttons">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<button @click="submit()" class="button is-primary is-fullwidth"
|
<button :class="{ 'is-loading': listService.loading}" @click="submit()"
|
||||||
:class="{ 'is-loading': listService.loading}">
|
class="button is-primary is-fullwidth">
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-1">
|
<div class="column is-1">
|
||||||
<button @click="showDeleteModal = true" class="button is-danger is-fullwidth"
|
<button :class="{ 'is-loading': listService.loading}" @click="showDeleteModal = true"
|
||||||
:class="{ 'is-loading': listService.loading}">
|
class="button is-danger is-fullwidth">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<icon icon="trash-alt"/>
|
<icon icon="trash-alt"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -114,10 +114,10 @@
|
||||||
</p>
|
</p>
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
:class="{'is-loading': listDuplicateService.loading}"
|
||||||
class="button is-success"
|
|
||||||
@click="duplicateList"
|
@click="duplicateList"
|
||||||
:class="{'is-loading': listDuplicateService.loading}">
|
class="button is-success"
|
||||||
|
type="submit">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<icon icon="plus"/>
|
<icon icon="plus"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -132,24 +132,24 @@
|
||||||
<background :list-id="$route.params.id"/>
|
<background :list-id="$route.params.id"/>
|
||||||
|
|
||||||
<component
|
<component
|
||||||
|
:id="list.id"
|
||||||
:is="manageUsersComponent"
|
:is="manageUsersComponent"
|
||||||
:id="list.id"
|
:userIsAdmin="userIsAdmin"
|
||||||
type="list"
|
|
||||||
shareType="user"
|
shareType="user"
|
||||||
:userIsAdmin="userIsAdmin"/>
|
type="list"/>
|
||||||
<component
|
<component
|
||||||
:is="manageTeamsComponent"
|
|
||||||
:id="list.id"
|
:id="list.id"
|
||||||
type="list"
|
:is="manageTeamsComponent"
|
||||||
|
:userIsAdmin="userIsAdmin"
|
||||||
shareType="team"
|
shareType="team"
|
||||||
:userIsAdmin="userIsAdmin"/>
|
type="list"/>
|
||||||
|
|
||||||
<link-sharing :list-id="$route.params.id" v-if="linkSharingEnabled"/>
|
<link-sharing :list-id="$route.params.id" v-if="linkSharingEnabled"/>
|
||||||
|
|
||||||
<modal
|
<modal
|
||||||
v-if="showDeleteModal"
|
|
||||||
@close="showDeleteModal = false"
|
@close="showDeleteModal = false"
|
||||||
@submit="deleteList()">
|
@submit="deleteList()"
|
||||||
|
v-if="showDeleteModal">
|
||||||
<span slot="header">Delete the list</span>
|
<span slot="header">Delete the list</span>
|
||||||
<p slot="text">Are you sure you want to delete this list and all of its contents?
|
<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>
|
<br/>This includes all tasks and <b>CANNOT BE UNDONE!</b></p>
|
||||||
|
@ -158,23 +158,23 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import router from '../../router'
|
import router from '../../router'
|
||||||
import manageSharing from '../../components/sharing/userTeam'
|
import manageSharing from '../../components/sharing/userTeam'
|
||||||
import LinkSharing from '../../components/sharing/linkSharing'
|
import LinkSharing from '../../components/sharing/linkSharing'
|
||||||
|
|
||||||
import ListModel from '../../models/list'
|
import ListModel from '../../models/list'
|
||||||
import ListService from '../../services/list'
|
import ListService from '../../services/list'
|
||||||
import Fancycheckbox from '../../components/input/fancycheckbox'
|
import Fancycheckbox from '../../components/input/fancycheckbox'
|
||||||
import Background from '../../components/list/partials/background-settings'
|
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 ColorPicker from '../../components/input/colorPicker'
|
||||||
import NamespaceSearch from '../../components/namespace/namespace-search'
|
import NamespaceSearch from '../../components/namespace/namespace-search'
|
||||||
import ListDuplicateService from '../../services/listDuplicateService'
|
import ListDuplicateService from '../../services/listDuplicateService'
|
||||||
import ListDuplicateModel from '../../models/listDuplicateModel'
|
import ListDuplicateModel from '../../models/listDuplicateModel'
|
||||||
import LoadingComponent from '../../components/misc/loading'
|
import LoadingComponent from '../../components/misc/loading'
|
||||||
import ErrorComponent from '../../components/misc/error'
|
import ErrorComponent from '../../components/misc/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'EditList',
|
name: 'EditList',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -211,7 +211,7 @@
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
// call again the method if the route changes
|
// call again the method if the route changes
|
||||||
'$route': 'loadList'
|
'$route': 'loadList',
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
linkSharingEnabled() {
|
linkSharingEnabled() {
|
||||||
|
@ -275,6 +275,6 @@
|
||||||
this.error(e, this)
|
this.error(e, this)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="fullpage">
|
<div class="fullpage">
|
||||||
<a class="close" @click="back()">
|
<a @click="back()" class="close">
|
||||||
<icon :icon="['far', 'times-circle']">
|
<icon :icon="['far', 'times-circle']">
|
||||||
</icon>
|
</icon>
|
||||||
</a>
|
</a>
|
||||||
<h3>Create a new list</h3>
|
<h3>Create a new list</h3>
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<p class="control is-expanded" :class="{ 'is-loading': listService.loading}">
|
<p :class="{ 'is-loading': listService.loading}" class="control is-expanded">
|
||||||
<input v-focus
|
<input
|
||||||
class="input"
|
|
||||||
:class="{ 'disabled': listService.loading}"
|
:class="{ 'disabled': listService.loading}"
|
||||||
v-model="list.title"
|
@keyup.enter="newList()"
|
||||||
type="text"
|
|
||||||
placeholder="The list's name goes here..."
|
|
||||||
@keyup.esc="back()"
|
@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>
|
||||||
<p class="control">
|
<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">
|
<span class="icon is-small">
|
||||||
<icon icon="plus"/>
|
<icon icon="plus"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -32,13 +33,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import router from '../../router'
|
import router from '../../router'
|
||||||
import ListService from '../../services/list'
|
import ListService from '../../services/list'
|
||||||
import ListModel from '../../models/list'
|
import ListModel from '../../models/list'
|
||||||
import {IS_FULLPAGE} from '../../store/mutation-types'
|
import {IS_FULLPAGE} from '@/store/mutation-types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "NewList",
|
name: 'NewList',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showError: false,
|
showError: false,
|
||||||
|
@ -77,6 +78,6 @@
|
||||||
back() {
|
back() {
|
||||||
router.go(-1)
|
router.go(-1)
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue