Add translations (#562)
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/562 Co-authored-by: konrad <konrad@kola-entertainments.de> Co-committed-by: konrad <konrad@kola-entertainments.de>
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="content has-text-centered">
|
||||
<h1>Not found</h1>
|
||||
<p>The page you requested does not exist.</p>
|
||||
<h1>{{ $t('404.title') }}</h1>
|
||||
<p>{{ $t('404.text') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -1,26 +1,26 @@
|
||||
<template>
|
||||
<div class="content has-text-centered">
|
||||
<h2>
|
||||
Hi {{ userInfo.name !== '' ? userInfo.name : userInfo.username }}!
|
||||
{{ $t('home.welcome', {username: userInfo.name !== '' ? userInfo.name : userInfo.username}) }}!
|
||||
</h2>
|
||||
<template v-if="!hasTasks">
|
||||
<p>You can create a new list for your new tasks:</p>
|
||||
<p>{{ $t('home.list.newText') }}</p>
|
||||
<x-button
|
||||
:to="{name: 'list.create', params: { id: defaultNamespaceId }}"
|
||||
:shadow="false"
|
||||
class="ml-2"
|
||||
v-if="defaultNamespaceId > 0"
|
||||
>
|
||||
Create a new list
|
||||
{{ $t('home.list.new') }}
|
||||
</x-button>
|
||||
<p class="mt-4" v-if="migratorsEnabled">
|
||||
Or import your lists and tasks from other services into Vikunja:
|
||||
{{ $t('home.list.importText') }}
|
||||
</p>
|
||||
<x-button
|
||||
v-if="migratorsEnabled"
|
||||
:to="{ name: 'migrate.start' }"
|
||||
:shadow="false">
|
||||
Import your data into Vikunja
|
||||
{{ $t('home.list.import') }}
|
||||
</x-button>
|
||||
</template>
|
||||
<ShowTasks :show-all="true" v-if="hasLists"/>
|
||||
@ -28,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import {mapState} from 'vuex'
|
||||
import ShowTasks from './tasks/ShowTasks'
|
||||
|
||||
export default {
|
||||
|
@ -2,13 +2,12 @@
|
||||
<div class="modal-mask keyboard-shortcuts-modal">
|
||||
<div @click.self="$router.back()" class="modal-container">
|
||||
<div class="modal-content">
|
||||
<card class="has-background-white has-no-shadow" title="Create A Saved Filter">
|
||||
<card class="has-background-white has-no-shadow" :title="$t('filters.create.title')">
|
||||
<p>
|
||||
A saved filter is a virtual list which is computed from a set of filters each time it is
|
||||
accessed. Once created, it will appear in a special namespace.
|
||||
{{ $t('filters.create.description') }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<label class="label" for="title">Title</label>
|
||||
<label class="label" for="title">{{ $t('filters.attributes.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
v-model="savedFilter.title"
|
||||
@ -16,14 +15,14 @@
|
||||
:disabled="savedFilterService.loading"
|
||||
class="input"
|
||||
id="Title"
|
||||
placeholder="The saved filter title goes here..."
|
||||
:placeholder="$t('filters.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="description">Description</label>
|
||||
<label class="label" for="description">{{ $t('filters.attributes.description') }}</label>
|
||||
<div class="control">
|
||||
<editor
|
||||
v-model="savedFilter.description"
|
||||
@ -31,13 +30,13 @@
|
||||
:disabled="savedFilterService.loading"
|
||||
:preview-is-default="false"
|
||||
id="description"
|
||||
placeholder="The description goes here..."
|
||||
:placeholder="$t('filters.attributes.descriptionPlaceholder')"
|
||||
v-if="editorActive"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="filters">Filters</label>
|
||||
<label class="label" for="filters">{{ $t('filters.title') }}</label>
|
||||
<div class="control">
|
||||
<filters
|
||||
:class="{ 'disabled': savedFilterService.loading}"
|
||||
@ -53,7 +52,7 @@
|
||||
@click="create()"
|
||||
class="is-fullwidth"
|
||||
>
|
||||
Create new saved filter
|
||||
{{ $t('filters.create.action') }}
|
||||
</x-button>
|
||||
</card>
|
||||
</div>
|
||||
|
@ -3,9 +3,9 @@
|
||||
@close="$router.back()"
|
||||
@submit="deleteSavedFilter()"
|
||||
>
|
||||
<span slot="header">Delete this saved filter</span>
|
||||
<span slot="header">{{ $t('filters.delete.header') }}</span>
|
||||
<p slot="text">
|
||||
Are you sure you want to delete this saved filter?
|
||||
{{ $t('filters.delete.text') }}
|
||||
</p>
|
||||
</modal>
|
||||
</template>
|
||||
@ -34,7 +34,7 @@ export default {
|
||||
this.filterService.delete(filter)
|
||||
.then(() => {
|
||||
this.$store.dispatch('namespaces/loadNamespaces')
|
||||
this.success({message: 'The filter was deleted successfully.'})
|
||||
this.success({message: this.$t('filters.delete.success')})
|
||||
this.$router.push({name: 'namespaces.index'})
|
||||
})
|
||||
.catch(e => this.error(e))
|
||||
|
@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Edit This Saved Filter"
|
||||
:title="$t('filters.edit.title')"
|
||||
primary-icon=""
|
||||
primary-label="Save"
|
||||
:primary-label="$t('misc.save')"
|
||||
@primary="save"
|
||||
tertary="Delete"
|
||||
:tertary="$t('misc.delete')"
|
||||
@tertary="$router.push({ name: 'filter.list.settings.delete', params: { id: $route.params.listId } })"
|
||||
>
|
||||
<form @submit.prevent="save()">
|
||||
<div class="field">
|
||||
<label class="label" for="title">Filter Title</label>
|
||||
<label class="label" for="title">{{ $t('filters.attributes.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
:class="{ 'disabled': filterService.loading}"
|
||||
@ -17,27 +17,27 @@
|
||||
@keyup.enter="save"
|
||||
class="input"
|
||||
id="title"
|
||||
placeholder="The title goes here..."
|
||||
:placeholder="$t('filters.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="filter.title"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="description">Description</label>
|
||||
<label class="label" for="description">{{ $t('filters.attributes.description') }}</label>
|
||||
<div class="control">
|
||||
<editor
|
||||
:class="{ 'disabled': filterService.loading}"
|
||||
:disabled="filterService.loading"
|
||||
:preview-is-default="false"
|
||||
id="description"
|
||||
placeholder="The description goes here..."
|
||||
:placeholder="$t('filters.attributes.descriptionPlaceholder')"
|
||||
v-model="filter.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="filters">Filters</label>
|
||||
<label class="label" for="filters">{{ $t('filters.title') }}</label>
|
||||
<div class="control">
|
||||
<filters
|
||||
:class="{ 'disabled': filterService.loading}"
|
||||
@ -117,7 +117,7 @@ export default {
|
||||
this.filterService.update(this.filter)
|
||||
.then(r => {
|
||||
this.$store.dispatch('namespaces/loadNamespaces')
|
||||
this.success({message: 'The filter was saved successfully.'})
|
||||
this.success({message: this.$t('filters.attributes.edit.success')})
|
||||
this.filter = r
|
||||
this.filters = objectToSnakeCase(this.filter.filters)
|
||||
this.$router.back()
|
||||
|
@ -5,19 +5,17 @@
|
||||
class="is-pulled-right"
|
||||
icon="plus"
|
||||
>
|
||||
New label
|
||||
{{ $t('label.create.header') }}
|
||||
</x-button>
|
||||
|
||||
<div class="content">
|
||||
<h1>Manage labels</h1>
|
||||
<p v-if="labels.length > 0">
|
||||
Click on a label to edit it.
|
||||
You can edit all labels you created, you can use all labels which are associated with a task to whose
|
||||
list you have access.
|
||||
<h1>{{ $t('label.manage') }}</h1>
|
||||
<p v-if="Object.entries(labels).length > 0">
|
||||
{{ $t('label.description') }}
|
||||
</p>
|
||||
<p v-else class="has-text-centered has-text-grey is-italic">
|
||||
You currently do not have any labels.
|
||||
<router-link :to="{name:'labels.create'}">Create a new label.</router-link>
|
||||
{{ $t('label.newCTA') }}
|
||||
<router-link :to="{name:'labels.create'}">{{ $t('label.create.title') }}.</router-link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -31,7 +29,7 @@
|
||||
>
|
||||
<span
|
||||
v-if="userInfo.id !== l.createdBy.id"
|
||||
v-tooltip.bottom="'You are not allowed to edit this label because you dont own it.'">
|
||||
v-tooltip.bottom="$t('label.edit.forbidden')">
|
||||
{{ l.title }}
|
||||
</span>
|
||||
<a
|
||||
@ -44,31 +42,31 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="column is-4" v-if="isLabelEdit">
|
||||
<card title="Edit Label" :has-close="true" @close="() => isLabelEdit = false">
|
||||
<card :title="$t('label.edit.header')" :has-close="true" @close="() => isLabelEdit = false">
|
||||
<form @submit.prevent="editLabelSubmit()">
|
||||
<div class="field">
|
||||
<label class="label">Title</label>
|
||||
<label class="label">{{ $t('label.attributes.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
placeholder="Label title"
|
||||
:placeholder="$t('label.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
v-model="labelEditLabel.title"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Description</label>
|
||||
<label class="label">{{ $t('label.attributes.description') }}</label>
|
||||
<div class="control">
|
||||
<editor
|
||||
:preview-is-default="false"
|
||||
placeholder="Label description"
|
||||
:placeholder="$t('label.attributes.description')"
|
||||
v-if="editorActive"
|
||||
v-model="labelEditLabel.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<label class="label">{{ $t('label.attributes.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="labelEditLabel.hexColor"/>
|
||||
</div>
|
||||
@ -80,7 +78,7 @@
|
||||
class="is-fullwidth"
|
||||
@click="editLabelSubmit()"
|
||||
>
|
||||
Save
|
||||
{{ $t('misc.save') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<div class="control">
|
||||
@ -130,7 +128,7 @@ export default {
|
||||
this.loadLabels()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Labels')
|
||||
this.setTitle(this.$t('label.title'))
|
||||
},
|
||||
computed: mapState({
|
||||
userInfo: state => state.auth.info,
|
||||
@ -147,7 +145,7 @@ export default {
|
||||
deleteLabel(label) {
|
||||
this.$store.dispatch('labels/deleteLabel', label)
|
||||
.then(() => {
|
||||
this.success({message: 'The label was successfully deleted.'})
|
||||
this.success({message: this.$t('label.deleteSuccess')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -156,7 +154,7 @@ export default {
|
||||
editLabelSubmit() {
|
||||
this.$store.dispatch('labels/updateLabel', this.labelEditLabel)
|
||||
.then(() => {
|
||||
this.success({message: 'The label was successfully updated.'})
|
||||
this.success({message: this.$t('label.edit.success')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Create a new label"
|
||||
:title="$t('label.create.title')"
|
||||
@create="newLabel()"
|
||||
:create-disabled="label.title === ''"
|
||||
>
|
||||
<div class="field">
|
||||
<label class="label" for="labelTitle">Label Title</label>
|
||||
<label class="label" for="labelTitle">{{ $t('label.attributes.title') }}</label>
|
||||
<div
|
||||
class="control is-expanded"
|
||||
:class="{ 'is-loading': loading }"
|
||||
@ -13,7 +13,7 @@
|
||||
<input
|
||||
:class="{ disabled: loading }"
|
||||
class="input"
|
||||
placeholder="The label title goes here..."
|
||||
:placeholder="$t('label.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
id="labelTitle"
|
||||
v-focus
|
||||
@ -23,10 +23,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="showError && label.title === ''">
|
||||
Please specify a title.
|
||||
{{ $t('label.create.titleRequired') }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<label class="label">{{ $t('label.attributes.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="label.hexColor"/>
|
||||
</div>
|
||||
@ -58,7 +58,7 @@ export default {
|
||||
this.label = new LabelModel()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Create a new label')
|
||||
this.setTitle(this.$t('label.create.title'))
|
||||
},
|
||||
computed: mapState({
|
||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels',
|
||||
@ -77,7 +77,7 @@ export default {
|
||||
name: 'labels.index',
|
||||
params: {id: r.id},
|
||||
})
|
||||
this.success({message: 'The label was successfully created.'})
|
||||
this.success({message: this.$t('label.create.success')})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.error(e)
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<create-edit title="Create a new list" @create="newList()" :create-disabled="list.title === ''">
|
||||
<create-edit :title="$t('list.create.header')" @create="newList()" :create-disabled="list.title === ''">
|
||||
<div class="field">
|
||||
<label class="label" for="listTitle">List Title</label>
|
||||
<label class="label" for="listTitle">{{ $t('list.title') }}</label>
|
||||
<div
|
||||
:class="{ 'is-loading': listService.loading }"
|
||||
class="control"
|
||||
@ -11,7 +11,7 @@
|
||||
@keyup.enter="newList()"
|
||||
@keyup.esc="$router.back()"
|
||||
class="input"
|
||||
placeholder="The list's title goes here..."
|
||||
:placeholder="$t('list.create.titlePlaceholder')"
|
||||
type="text"
|
||||
name="listTitle"
|
||||
v-focus
|
||||
@ -20,10 +20,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="showError && list.title === ''">
|
||||
Please specify a title.
|
||||
{{ $t('list.create.addTitleRequired') }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<label class="label">{{ $t('list.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="list.hexColor" />
|
||||
</div>
|
||||
@ -55,7 +55,7 @@ export default {
|
||||
this.listService = new ListService()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Create a new list')
|
||||
this.setTitle(this.$t('list.create.header'))
|
||||
},
|
||||
methods: {
|
||||
newList() {
|
||||
@ -69,7 +69,7 @@ export default {
|
||||
this.$store
|
||||
.dispatch('lists/createList', this.list)
|
||||
.then((r) => {
|
||||
this.success({message: 'The list was successfully created.'})
|
||||
this.success({message: this.$t('list.create.createdSuccess') })
|
||||
this.$router.push({
|
||||
name: 'list.index',
|
||||
params: { listId: r.id },
|
||||
|
@ -8,29 +8,28 @@
|
||||
<router-link
|
||||
:class="{'is-active': $route.name.includes('list.list')}"
|
||||
:to="{ name: 'list.list', params: { listId: listId } }">
|
||||
List
|
||||
{{ $t('list.list.title') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
:class="{'is-active': $route.name.includes('list.gantt')}"
|
||||
:to="{ name: 'list.gantt', params: { listId: listId } }">
|
||||
Gantt
|
||||
{{ $t('list.gantt.title') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
:class="{'is-active': $route.name.includes('list.table')}"
|
||||
:to="{ name: 'list.table', params: { listId: listId } }">
|
||||
Table
|
||||
{{ $t('list.table.title') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
:class="{'is-active': $route.name.includes('list.kanban')}"
|
||||
:to="{ name: 'list.kanban', params: { listId: listId } }">
|
||||
Kanban
|
||||
{{ $t('list.kanban.title') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div class="notification is-warning" v-if="currentList.isArchived">
|
||||
This list is archived.
|
||||
It is not possible to create new or edit tasks or it.
|
||||
{{ $t('list.archived') }}
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
|
@ -3,12 +3,12 @@
|
||||
@close="$router.back()"
|
||||
@submit="archiveList()"
|
||||
>
|
||||
<span slot="header">{{ list.isArchived ? 'Un-' : '' }}Archive this list</span>
|
||||
<span slot="header">{{ list.isArchived ? $t('list.archive.unarchive') : $t('list.archive.archive') }}</span>
|
||||
<p slot="text" v-if="list.isArchived">
|
||||
You will be able to create new tasks or edit it.
|
||||
{{ $t('list.archive.unarchiveText') }}
|
||||
</p>
|
||||
<p slot="text" v-else>
|
||||
You won't be able to edit this list or create new tasks until you un-archive it.
|
||||
{{ $t('list.archive.archiveText') }}
|
||||
</p>
|
||||
</modal>
|
||||
</template>
|
||||
@ -27,7 +27,7 @@ export default {
|
||||
created() {
|
||||
this.listService = new ListService()
|
||||
this.list = this.$store.getters['lists/getListById'](this.$route.params.listId)
|
||||
this.setTitle(`Archive "${this.list.title}"`)
|
||||
this.setTitle(this.$t('list.archive.title', {list: this.list.title}))
|
||||
},
|
||||
methods: {
|
||||
archiveList() {
|
||||
@ -38,7 +38,7 @@ export default {
|
||||
.then(r => {
|
||||
this.$store.commit('currentList', r)
|
||||
this.$store.commit('namespaces/setListInNamespaceById', r)
|
||||
this.success({message: 'The list was successfully archived.'})
|
||||
this.success({message: this.$t('list.archive.success')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
|
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Set list background"
|
||||
:title="$t('list.background.title')"
|
||||
primary-label=""
|
||||
:loading="backgroundService.loading"
|
||||
class="list-background-setting"
|
||||
:wide="true"
|
||||
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
|
||||
:tertary="hasBackground ? 'Remove Background' : ''"
|
||||
:tertary="hasBackground ? $t('list.background.remove') : ''"
|
||||
@tertary="removeBackground()"
|
||||
>
|
||||
<div class="mb-4" v-if="uploadBackgroundEnabled">
|
||||
@ -22,7 +22,7 @@
|
||||
@click="$refs.backgroundUploadInput.click()"
|
||||
type="primary"
|
||||
>
|
||||
Choose a background from your pc
|
||||
{{ $t('list.background.upload') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<template v-if="unsplashBackgroundEnabled">
|
||||
@ -30,11 +30,13 @@
|
||||
:class="{'is-loading': backgroundService.loading}"
|
||||
@keyup="() => newBackgroundSearch()"
|
||||
class="input is-expanded"
|
||||
placeholder="Search for a background..."
|
||||
:placeholder="$t('list.background.searchPlaceholder')"
|
||||
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">{{ $t('list.background.poweredByUnsplash') }}</a>
|
||||
</p>
|
||||
<div class="image-search-result">
|
||||
<a
|
||||
:key="im.id"
|
||||
@ -55,7 +57,7 @@
|
||||
type="secondary"
|
||||
v-if="backgroundSearchResult.length > 0"
|
||||
>
|
||||
{{ backgroundService.loading ? 'Loading...' : 'Load more photos' }}
|
||||
{{ backgroundService.loading ? $t('misc.loading') : $t('list.background.loadMore') }}
|
||||
</x-button>
|
||||
</template>
|
||||
</create-edit>
|
||||
@ -95,7 +97,7 @@ export default {
|
||||
this.backgroundService = new BackgroundUnsplashService()
|
||||
this.backgroundUploadService = new BackgroundUploadService()
|
||||
this.listService = new ListService()
|
||||
this.setTitle('Set a list background')
|
||||
this.setTitle(this.$t('list.background.title'))
|
||||
// Show the default collection of backgrounds
|
||||
this.newBackgroundSearch()
|
||||
},
|
||||
@ -144,7 +146,7 @@ export default {
|
||||
.then(l => {
|
||||
this.$store.commit(CURRENT_LIST, l)
|
||||
this.$store.commit('namespaces/setListInNamespaceById', l)
|
||||
this.success({message: 'The background has been set successfully!'})
|
||||
this.success({message: this.$t('list.background.success')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -159,7 +161,7 @@ export default {
|
||||
.then(l => {
|
||||
this.$store.commit(CURRENT_LIST, l)
|
||||
this.$store.commit('namespaces/setListInNamespaceById', l)
|
||||
this.success({message: 'The background has been set successfully!'})
|
||||
this.success({message: this.$t('list.background.success')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -170,7 +172,7 @@ export default {
|
||||
.then(l => {
|
||||
this.$store.commit(CURRENT_LIST, l)
|
||||
this.$store.commit('namespaces/setListInNamespaceById', l)
|
||||
this.success({message: 'The background has been removed successfully!'})
|
||||
this.success({message: this.$t('list.background.removeSuccess')})
|
||||
this.$router.back()
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -3,9 +3,11 @@
|
||||
@close="$router.back()"
|
||||
@submit="deleteList()"
|
||||
>
|
||||
<span slot="header">Delete this list</span>
|
||||
<p slot="text">Are you sure you want to delete this list and all of its contents?
|
||||
<br/>This includes all tasks and <b>CANNOT BE UNDONE!</b></p>
|
||||
<span slot="header">{{ $t('list.delete.header') }}</span>
|
||||
<p slot="text">
|
||||
{{ $t('list.delete.text1') }}<br/>
|
||||
{{ $t('list.delete.text2') }}
|
||||
</p>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
@ -22,7 +24,7 @@ export default {
|
||||
created() {
|
||||
this.listService = new ListService()
|
||||
const list = this.$store.getters['lists/getListById'](this.$route.params.listId)
|
||||
this.setTitle(`Delete "${list.title}"`)
|
||||
this.setTitle(this.$t('list.delete.title', {list: list.title}))
|
||||
},
|
||||
methods: {
|
||||
deleteList() {
|
||||
@ -31,7 +33,7 @@ export default {
|
||||
this.listService.delete(list)
|
||||
.then(() => {
|
||||
this.$store.commit('namespaces/removeListFromNamespaceById', list)
|
||||
this.success({message: 'The list was successfully deleted.'})
|
||||
this.success({message: this.$t('list.delete.success')})
|
||||
this.$router.push({name: 'home'})
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Duplicate this list"
|
||||
:title="$t('list.duplicate.title')"
|
||||
primary-icon="paste"
|
||||
primary-label="Duplicate"
|
||||
:primary-label="$t('list.duplicate.label')"
|
||||
@primary="duplicateList"
|
||||
:loading="listDuplicateService.loading"
|
||||
>
|
||||
<p>Select a namespace which should hold the duplicated list:</p>
|
||||
<p>
|
||||
{{ $t('list.duplicate.text') }}
|
||||
</p>
|
||||
<namespace-search @selected="selectNamespace"/>
|
||||
</create-edit>
|
||||
</template>
|
||||
@ -31,7 +33,7 @@ export default {
|
||||
},
|
||||
created() {
|
||||
this.listDuplicateService = new ListDuplicateService()
|
||||
this.setTitle('Duplicate List')
|
||||
this.setTitle(this.$t('list.duplicate.title'))
|
||||
},
|
||||
methods: {
|
||||
selectNamespace(namespace) {
|
||||
@ -46,7 +48,7 @@ export default {
|
||||
.then(r => {
|
||||
this.$store.commit('namespaces/addListToNamespace', r.list)
|
||||
this.$store.commit('lists/setList', r.list)
|
||||
this.success({message: 'The list was successfully duplicated.'})
|
||||
this.success({message: this.$t('list.duplicate.success')})
|
||||
this.$router.push({name: 'list.index', params: {listId: r.list.id}})
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Edit This List"
|
||||
:title="$t('list.edit.header')"
|
||||
primary-icon=""
|
||||
primary-label="Save"
|
||||
:primary-label="$t('misc.save')"
|
||||
@primary="save"
|
||||
tertary="Delete"
|
||||
:tertary="$t('misc.delete')"
|
||||
@tertary="$router.push({ name: 'list.list.settings.delete', params: { id: $route.params.listId } })"
|
||||
>
|
||||
<div class="field">
|
||||
<label class="label" for="listtext">List Name</label>
|
||||
<label class="label" for="listtext">{{ $t('list.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
:class="{ 'disabled': listService.loading}"
|
||||
@ -16,7 +16,7 @@
|
||||
@keyup.enter="save"
|
||||
class="input"
|
||||
id="listtext"
|
||||
placeholder="The list title goes here..."
|
||||
:placeholder="$t('list.edit.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="list.title"/>
|
||||
@ -26,8 +26,8 @@
|
||||
<label
|
||||
class="label"
|
||||
for="listtext"
|
||||
v-tooltip="'The list identifier can be used to uniquely identify a task across lists. You can set it to empty to disable it.'">
|
||||
List Identifier
|
||||
v-tooltip="$t('list.edit.identifierTooltip')">
|
||||
{{ $t('list.edit.identifier') }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@ -36,27 +36,27 @@
|
||||
@keyup.enter="save"
|
||||
class="input"
|
||||
id="listtext"
|
||||
placeholder="The list identifier goes here..."
|
||||
:placeholder="$t('list.edit.identifierPlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="list.identifier"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="listdescription">Description</label>
|
||||
<label class="label" for="listdescription">{{ $t('list.edit.description') }}</label>
|
||||
<div class="control">
|
||||
<editor
|
||||
:class="{ 'disabled': listService.loading}"
|
||||
:disabled="listService.loading"
|
||||
:preview-is-default="false"
|
||||
id="listdescription"
|
||||
placeholder="The lists description goes here..."
|
||||
:placeholder="$t('list.edit.descriptionPlaceholder')"
|
||||
v-model="list.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<label class="label">{{ $t('list.edit.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="list.hexColor"/>
|
||||
</div>
|
||||
@ -106,7 +106,7 @@ export default {
|
||||
.then(r => {
|
||||
this.$set(this, 'list', r)
|
||||
this.$store.commit(CURRENT_LIST, r)
|
||||
this.setTitle(`Edit "${this.list.title}"`)
|
||||
this.setTitle(this.$t('list.edit.title', {list: this.list.title}))
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -115,7 +115,7 @@ export default {
|
||||
save() {
|
||||
this.$store.dispatch('lists/updateList', this.list)
|
||||
.then(() => {
|
||||
this.success({message: 'The list was successfully updated.'})
|
||||
this.success({message: this.$t('list.edit.success')})
|
||||
this.$router.back()
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Share this list"
|
||||
:title="$t('list.share.header')"
|
||||
primary-label=""
|
||||
>
|
||||
<component
|
||||
@ -67,7 +67,7 @@ export default {
|
||||
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
|
||||
this.manageTeamsComponent = 'userTeam'
|
||||
this.manageUsersComponent = 'userTeam'
|
||||
this.setTitle(`Share "${this.list.title}"`)
|
||||
this.setTitle(this.$t('list.share.title', {list: this.list.title}))
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
|
@ -3,41 +3,41 @@
|
||||
<card :padding="false" class="has-overflow">
|
||||
<div class="gantt-options p-4">
|
||||
<fancycheckbox class="is-block" v-model="showTaskswithoutDates">
|
||||
Show tasks which don't have dates set
|
||||
{{ $t('list.gantt.showTasksWithoutDates') }}
|
||||
</fancycheckbox>
|
||||
<div class="range-picker">
|
||||
<div class="field">
|
||||
<label class="label" for="dayWidth">Size</label>
|
||||
<label class="label" for="dayWidth">{{ $t('list.gantt.size') }}</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select id="dayWidth" v-model.number="dayWidth">
|
||||
<option value="35">Default</option>
|
||||
<option value="10">Month</option>
|
||||
<option value="80">Day</option>
|
||||
<option value="35">{{ $t('list.gantt.default') }}</option>
|
||||
<option value="10">{{ $t('list.gantt.month') }}</option>
|
||||
<option value="80">{{ $t('list.gantt.day') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="fromDate">From</label>
|
||||
<label class="label" for="fromDate">{{ $t('list.gantt.from') }}</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
:config="flatPickerConfig"
|
||||
class="input"
|
||||
id="fromDate"
|
||||
placeholder="From"
|
||||
:placeholder="$t('list.gantt.from')"
|
||||
v-model="dateFrom"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="toDate">To</label>
|
||||
<label class="label" for="toDate">{{ $t('list.gantt.to') }}</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
:config="flatPickerConfig"
|
||||
class="input"
|
||||
id="toDate"
|
||||
placeholder="To"
|
||||
:placeholder="$t('list.gantt.to')"
|
||||
v-model="dateTo"
|
||||
/>
|
||||
</div>
|
||||
@ -66,7 +66,6 @@ import GanttChart from '../../../components/tasks/gantt-component'
|
||||
import flatPickr from 'vue-flatpickr-component'
|
||||
import Fancycheckbox from '../../../components/input/fancycheckbox'
|
||||
import {saveListView} from '@/helpers/saveListView'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Gantt',
|
||||
@ -88,17 +87,19 @@ export default {
|
||||
dateTo: null,
|
||||
}
|
||||
},
|
||||
computed: mapState({
|
||||
flatPickerConfig: state => ({
|
||||
altFormat: 'j M Y',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d',
|
||||
enableTime: false,
|
||||
locale: {
|
||||
firstDayOfWeek: state.auth.settings.weekStart,
|
||||
},
|
||||
})
|
||||
}),
|
||||
computed: {
|
||||
flatPickerConfig() {
|
||||
return {
|
||||
altFormat: this.$t('date.altFormatShort'),
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d',
|
||||
enableTime: false,
|
||||
locale: {
|
||||
firstDayOfWeek: this.$store.state.auth.settings.weekStart,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
this.dateFrom = new Date((new Date()).setDate((new Date()).getDate() - 15))
|
||||
this.dateTo = new Date((new Date()).setDate((new Date()).getDate() + 30))
|
||||
|
@ -7,7 +7,7 @@
|
||||
icon="filter"
|
||||
type="secondary"
|
||||
>
|
||||
Filters
|
||||
{{ $t('filters.title') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<filter-popup
|
||||
@ -22,7 +22,7 @@
|
||||
<span
|
||||
v-if="bucket.isDoneBucket"
|
||||
class="icon is-small has-text-success mr-2"
|
||||
v-tooltip="'All tasks moved into this bucket will automatically marked as done.'"
|
||||
v-tooltip="$t('list.kanban.doneBucketHint')"
|
||||
>
|
||||
<icon icon="check-double"/>
|
||||
</span>
|
||||
@ -71,26 +71,26 @@
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
Limit: {{ bucket.limit > 0 ? bucket.limit : 'Not set' }}
|
||||
{{ $t('list.kanban.limit', {limit: bucket.limit > 0 ? bucket.limit : $t('list.kanban.noLimit') }) }}
|
||||
</template>
|
||||
</a>
|
||||
<a
|
||||
@click="toggleDoneBucket(bucket)"
|
||||
class="dropdown-item"
|
||||
v-tooltip="'All tasks moved into the done bucket will be marked as done automatically. All tasks marked as done from elsewhere will be moved as well.'"
|
||||
v-tooltip="$t('list.kanban.doneBucketHintExtended')"
|
||||
>
|
||||
<span class="icon is-small" :class="{'has-text-success': bucket.isDoneBucket}"><icon
|
||||
icon="check-double"/></span>
|
||||
Done bucket
|
||||
{{ $t('list.kanban.doneBucket') }}
|
||||
</a>
|
||||
<a
|
||||
:class="{'is-disabled': buckets.length <= 1}"
|
||||
@click="() => deleteBucketModal(bucket.id)"
|
||||
class="dropdown-item has-text-danger"
|
||||
v-tooltip="buckets.length <= 1 ? 'You cannot remove the last bucket.' : ''"
|
||||
v-tooltip="buckets.length <= 1 ? $t('list.kanban.deleteLast') : ''"
|
||||
>
|
||||
<span class="icon is-small"><icon icon="trash-alt"/></span>
|
||||
Delete
|
||||
{{ $t('misc.delete') }}
|
||||
</a>
|
||||
</dropdown>
|
||||
</div>
|
||||
@ -192,14 +192,14 @@
|
||||
@focusout="toggleShowNewTaskInput(bucket.id)"
|
||||
@keyup.enter="addTaskToBucket(bucket.id)"
|
||||
@keyup.esc="toggleShowNewTaskInput(bucket.id)"
|
||||
placeholder="Enter the new task text..."
|
||||
:placeholder="$t('list.kanban.addTaskPlaceholder')"
|
||||
type="text"
|
||||
v-focus.always
|
||||
v-model="newTaskText"
|
||||
/>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="newTaskError[bucket.id] && newTaskText === ''">
|
||||
Please specify a title.
|
||||
{{ $t('list.list.addTitleRequired') }}
|
||||
</p>
|
||||
</div>
|
||||
<x-button
|
||||
@ -210,12 +210,7 @@
|
||||
icon="plus"
|
||||
type="secondary"
|
||||
>
|
||||
<template v-if="bucket.tasks.length === 0">
|
||||
Add a task
|
||||
</template>
|
||||
<template v-else>
|
||||
Add another task
|
||||
</template>
|
||||
{{ bucket.tasks.length === 0 ? $t('list.kanban.addTask') : $t('list.kanban.addAnotherTask') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -228,7 +223,7 @@
|
||||
@keyup.enter="createNewBucket"
|
||||
@keyup.esc="() => showNewBucketInput = false"
|
||||
class="input"
|
||||
placeholder="Enter the new bucket title..."
|
||||
:placeholder="$t('list.kanban.addBucketPlaceholder')"
|
||||
type="text"
|
||||
v-focus.always
|
||||
v-if="showNewBucketInput"
|
||||
@ -242,7 +237,7 @@
|
||||
type="secondary"
|
||||
icon="plus"
|
||||
>
|
||||
Create a new bucket
|
||||
{{ $t('list.kanban.addBucket') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -257,10 +252,10 @@
|
||||
@close="showBucketDeleteModal = false"
|
||||
@submit="deleteBucket()"
|
||||
v-if="showBucketDeleteModal">
|
||||
<span slot="header">Delete the bucket</span>
|
||||
<span slot="header">{{ $t('list.kanban.deleteHeaderBucket') }}</span>
|
||||
<p slot="text">
|
||||
Are you sure you want to delete this bucket?<br/>
|
||||
This will not delete any tasks but move them into the default bucket.
|
||||
{{ $t('list.kanban.deleteBucketText1') }}<br/>
|
||||
{{ $t('list.kanban.deleteBucketText2') }}
|
||||
</p>
|
||||
</modal>
|
||||
</transition>
|
||||
@ -560,7 +555,7 @@ export default {
|
||||
|
||||
this.$store.dispatch('kanban/deleteBucket', {bucket: bucket, params: this.params})
|
||||
.then(() => {
|
||||
this.success({message: 'The bucket has been deleted successfully.'})
|
||||
this.success({message: this.$t('list.kanban.deleteBucketSuccess')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -590,7 +585,7 @@ export default {
|
||||
.then(r => {
|
||||
realBucket.title = r.title
|
||||
bucketTitleElement.blur()
|
||||
this.success({message: 'The bucket title has been saved successfully.'})
|
||||
this.success({message: this.$t('list.kanban.bucketTitleSavedSuccess')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -600,7 +595,7 @@ export default {
|
||||
bucket.limit = parseInt(bucket.limit)
|
||||
this.$store.dispatch('kanban/updateBucket', bucket)
|
||||
.then(() => {
|
||||
this.success({message: 'The bucket limit been saved successfully.'})
|
||||
this.success({message: this.$t('list.kanban.bucketLimitSavedSuccess')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -622,7 +617,7 @@ export default {
|
||||
bucket.isDoneBucket = !bucket.isDoneBucket
|
||||
this.$store.dispatch('kanban/updateBucket', bucket)
|
||||
.then(() => {
|
||||
this.success({message: 'The done bucket has been saved successfully.'})
|
||||
this.success({message: this.$t('list.kanban.doneBucketSavedSuccess')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
|
@ -11,7 +11,7 @@
|
||||
@blur="hideSearchBar()"
|
||||
@keyup.enter="searchTasks"
|
||||
class="input"
|
||||
placeholder="Search"
|
||||
:placeholder="$t('misc.search')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="searchTerm"/>
|
||||
@ -25,7 +25,7 @@
|
||||
@click="searchTasks"
|
||||
:shadow="false"
|
||||
>
|
||||
Search
|
||||
{{ $t('misc.search') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -41,7 +41,7 @@
|
||||
type="secondary"
|
||||
icon="filter"
|
||||
>
|
||||
Filters
|
||||
{{ $t('filters.title') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<filter-popup
|
||||
@ -59,7 +59,7 @@
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
@keyup.enter="addTask()"
|
||||
class="input"
|
||||
placeholder="Add a new task..."
|
||||
:placeholder="$t('list.list.addPlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="newTaskText"
|
||||
@ -75,18 +75,20 @@
|
||||
@click="addTask()"
|
||||
icon="plus"
|
||||
>
|
||||
Add
|
||||
{{ $t('list.list.add') }}
|
||||
</x-button>
|
||||
</p>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="showError && newTaskText === ''">
|
||||
Please specify a list title.
|
||||
{{ $t('list.list.addTitleRequired') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<nothing v-if="ctaVisible && tasks.length === 0 && !taskCollectionService.loading">
|
||||
This list is currently empty.
|
||||
<a @click="$refs.newTaskInput.focus()">Create a new task.</a>
|
||||
{{ $t('list.list.empty') }}
|
||||
<a @click="$refs.newTaskInput.focus()">
|
||||
{{ $t('list.list.newTaskCta') }}
|
||||
</a>
|
||||
</nothing>
|
||||
|
||||
<div class="tasks-container">
|
||||
@ -107,7 +109,7 @@
|
||||
</div>
|
||||
<card
|
||||
v-if="isTaskEdit"
|
||||
class="taskedit mt-0" title="Edit Task" :has-close="true" @close="() => isTaskEdit = false"
|
||||
class="taskedit mt-0" :title="$t('list.list.editTask')" :has-close="true" @close="() => isTaskEdit = false"
|
||||
:shadow="false">
|
||||
<edit-task :task="taskEditTask"/>
|
||||
</card>
|
||||
@ -123,14 +125,14 @@
|
||||
:to="getRouteForPagination(currentPage - 1)"
|
||||
class="pagination-previous"
|
||||
tag="button">
|
||||
Previous
|
||||
{{ $t('misc.previous') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
:disabled="currentPage === taskCollectionService.totalPages"
|
||||
:to="getRouteForPagination(currentPage + 1)"
|
||||
class="pagination-next"
|
||||
tag="button">
|
||||
Next page
|
||||
{{ $t('misc.next') }}
|
||||
</router-link>
|
||||
<ul class="pagination-list">
|
||||
<template v-for="(p, i) in pages">
|
||||
|
@ -7,31 +7,31 @@
|
||||
icon="th"
|
||||
type="secondary"
|
||||
>
|
||||
Columns
|
||||
{{ $t('list.table.columns') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click.prevent.stop="() => {showTaskFilter = !showTaskFilter; showActiveColumnsFilter = false}"
|
||||
icon="filter"
|
||||
type="secondary"
|
||||
>
|
||||
Filters
|
||||
{{ $t('filters.title') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<card v-if="showActiveColumnsFilter">
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.id">#</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.done">Done</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.title">Title</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.priority">Priority</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.labels">Labels</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.assignees">Assignees</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.dueDate">Due Date</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.startDate">Start Date</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.endDate">End Date</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.percentDone">% Done</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.created">Created</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.updated">Updated</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.createdBy">Created By</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.done">{{ $t('task.attributes.done') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.title">{{ $t('task.attributes.title') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.priority">{{ $t('task.attributes.priority') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.labels">{{ $t('task.attributes.labels') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.assignees">{{ $t('task.attributes.assignees') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.dueDate">{{ $t('task.attributes.dueDate') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.startDate">{{ $t('task.attributes.startDate') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.endDate">{{ $t('task.attributes.endDate') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.percentDone">{{ $t('task.attributes.percentDone') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.created">{{ $t('task.attributes.created') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.updated">{{ $t('task.attributes.updated') }}</fancycheckbox>
|
||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.createdBy">{{ $t('task.attributes.createdBy') }}</fancycheckbox>
|
||||
</card>
|
||||
</transition>
|
||||
<filter-popup
|
||||
@ -50,49 +50,49 @@
|
||||
<sort :order="sortBy.id" @click="sort('id')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.done">
|
||||
Done
|
||||
{{ $t('task.attributes.done') }}
|
||||
<sort :order="sortBy.done" @click="sort('done')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.title">
|
||||
Name
|
||||
{{ $t('task.attributes.title') }}
|
||||
<sort :order="sortBy.title" @click="sort('title')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.priority">
|
||||
Priority
|
||||
{{ $t('task.attributes.priority') }}
|
||||
<sort :order="sortBy.priority" @click="sort('priority')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.labels">
|
||||
Labels
|
||||
{{ $t('task.attributes.labels') }}
|
||||
</th>
|
||||
<th v-if="activeColumns.assignees">
|
||||
Assignees
|
||||
{{ $t('task.attributes.assignees') }}
|
||||
</th>
|
||||
<th v-if="activeColumns.dueDate">
|
||||
Due Date
|
||||
{{ $t('task.attributes.dueDate') }}
|
||||
<sort :order="sortBy.due_date" @click="sort('due_date')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.startDate">
|
||||
Start Date
|
||||
{{ $t('task.attributes.startDate') }}
|
||||
<sort :order="sortBy.start_date" @click="sort('start_date')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.endDate">
|
||||
End Date
|
||||
{{ $t('task.attributes.endDate') }}
|
||||
<sort :order="sortBy.end_date" @click="sort('end_date')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.percentDone">
|
||||
% Done
|
||||
{{ $t('task.attributes.percentDone') }}
|
||||
<sort :order="sortBy.percent_done" @click="sort('percent_done')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.created">
|
||||
Created
|
||||
{{ $t('task.attributes.created') }}
|
||||
<sort :order="sortBy.created" @click="sort('created')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.updated">
|
||||
Updated
|
||||
{{ $t('task.attributes.updated') }}
|
||||
<sort :order="sortBy.updated" @click="sort('updated')"/>
|
||||
</th>
|
||||
<th v-if="activeColumns.createdBy">
|
||||
Created By
|
||||
{{ $t('task.attributes.createdBy') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -156,14 +156,14 @@
|
||||
:to="getRouteForPagination(currentPage - 1, 'table')"
|
||||
class="pagination-previous"
|
||||
tag="button">
|
||||
Previous
|
||||
{{ $t('misc.previous') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
:disabled="currentPage === taskCollectionService.totalPages"
|
||||
:to="getRouteForPagination(currentPage + 1, 'table')"
|
||||
class="pagination-next"
|
||||
tag="button">
|
||||
Next page
|
||||
{{ $t('mist.next') }}
|
||||
</router-link>
|
||||
<ul class="pagination-list">
|
||||
<template v-for="(p, i) in pages">
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<h1>Import your data from other services to Vikunja</h1>
|
||||
<p>Click on the logo of one of the third-party services below to get started.</p>
|
||||
<h1>{{ $t('migrate.title') }}</h1>
|
||||
<p>{{ $t('migrate.description') }}</p>
|
||||
<div class="migration-services-overview">
|
||||
<router-link :key="m" :to="{name: 'migrate.service', params: {service: m}}" v-for="m in availableMigrators">
|
||||
<img :alt="m" :src="`/images/migration/${m}.png`"/>
|
||||
@ -15,7 +15,7 @@
|
||||
export default {
|
||||
name: 'migrate.service',
|
||||
mounted() {
|
||||
this.setTitle('Import your data to Vikunja')
|
||||
this.setTitle(this.$t('migrate.title'))
|
||||
},
|
||||
computed: {
|
||||
availableMigrators() {
|
||||
|
@ -21,7 +21,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle(`Import your data from ${this.name} into Vikunja`)
|
||||
this.setTitle(this.$t('migrate.titleService', {name: this.name}))
|
||||
},
|
||||
created() {
|
||||
switch (this.$route.params.service) {
|
||||
|
@ -1,20 +1,20 @@
|
||||
<template>
|
||||
<div class="content namespaces-list loader-container" :class="{'is-loading': loading}">
|
||||
<x-button :to="{name: 'namespace.create'}" class="new-namespace" icon="plus">
|
||||
Create namespace
|
||||
{{ $t('namespace.create.title') }}
|
||||
</x-button>
|
||||
<x-button :to="{name: 'filters.create'}" class="new-namespace" icon="filter">
|
||||
Create saved filter
|
||||
{{ $t('filters.create.title') }}
|
||||
</x-button>
|
||||
|
||||
<fancycheckbox class="show-archived-check" v-model="showArchived" @change="saveShowArchivedState">
|
||||
Show Archived
|
||||
{{ $t('namespace.showArchived') }}
|
||||
</fancycheckbox>
|
||||
|
||||
<p class="has-text-centered has-text-grey mt-4 is-italic" v-if="namespaces.length === 0">
|
||||
You don't have any namespaces right now.
|
||||
{{ $t('namespace.noneAvailable') }}
|
||||
<router-link :to="{name: 'namespace.create'}">
|
||||
Create a namespace.
|
||||
{{ $t('namespace.create.title') }}.
|
||||
</router-link>
|
||||
</p>
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
v-if="n.id > 0 && n.lists.length > 0"
|
||||
icon="plus"
|
||||
>
|
||||
Create list
|
||||
{{ $t('list.create.header') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
:to="{name: 'namespace.settings.archive', params: {id: n.id}}"
|
||||
@ -35,20 +35,20 @@
|
||||
v-if="n.isArchived"
|
||||
icon="archive"
|
||||
>
|
||||
Un-Archive
|
||||
{{ $t('namespace.unarchive') }}
|
||||
</x-button>
|
||||
|
||||
<h1>
|
||||
<span>{{ n.title }}</span>
|
||||
<span class="is-archived" v-if="n.isArchived">
|
||||
Archived
|
||||
{{ $t('namespace.archived') }}
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<p class="has-text-centered has-text-grey mt-4 is-italic" v-if="n.lists.length === 0">
|
||||
This namespace does not contain any lists.
|
||||
{{ $t('namespaces.noLists') }}
|
||||
<router-link :to="{name: 'list.create', params: {id: n.id}}">
|
||||
Create a new list in this namespace.
|
||||
{{ $t('namespace.createList') }}
|
||||
</router-link>
|
||||
</p>
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
>
|
||||
<div class="is-archived-container">
|
||||
<span class="is-archived" v-if="l.isArchived">
|
||||
Archived
|
||||
{{ $t('namespace.archived') }}
|
||||
</span>
|
||||
<span
|
||||
:class="{'is-favorite': l.isFavorite, 'is-archived': l.isArchived}"
|
||||
@ -112,7 +112,7 @@ export default {
|
||||
this.loadBackgroundsForLists()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Namespaces & Lists')
|
||||
this.setTitle(this.$t('namespace.title'))
|
||||
},
|
||||
computed: mapState({
|
||||
namespaces(state) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Create a new namespace"
|
||||
:title="$t('namespace.create.title')"
|
||||
@create="newNamespace()"
|
||||
:create-disabled="namespace.title === ''"
|
||||
>
|
||||
<div class="field">
|
||||
<label class="label" for="namespaceTitle">Namespace Title</label>
|
||||
<label class="label" for="namespaceTitle">{{ $t('namespace.attributes.title') }}</label>
|
||||
<div
|
||||
class="control is-expanded"
|
||||
:class="{ 'is-loading': namespaceService.loading }"
|
||||
@ -14,7 +14,7 @@
|
||||
@keyup.enter="newNamespace()"
|
||||
@keyup.esc="back()"
|
||||
class="input"
|
||||
placeholder="The namespace's name goes here..."
|
||||
:placeholder="$t('namespace.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
:class="{ disabled: namespaceService.loading }"
|
||||
v-focus
|
||||
@ -23,21 +23,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="showError && namespace.title === ''">
|
||||
Please specify a title.
|
||||
{{ $t('namespace.create.titleRequired') }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<label class="label">{{ $t('namespace.attributes.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="namespace.hexColor" />
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
class="is-small has-text-centered"
|
||||
v-tooltip.bottom="
|
||||
'A namespace is a collection of lists you can share and use to organize your lists with. In fact, every list belongs to a namepace.'
|
||||
"
|
||||
v-tooltip.bottom="$t('namespace.create.explanation')"
|
||||
>
|
||||
What's a namespace?
|
||||
{{ $t('namespace.create.tooltip') }}
|
||||
</p>
|
||||
</create-edit>
|
||||
</template>
|
||||
@ -66,7 +64,7 @@ export default {
|
||||
this.namespaceService = new NamespaceService()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Create a new namespace')
|
||||
this.setTitle(this.$t('namespace.create.title'))
|
||||
},
|
||||
methods: {
|
||||
newNamespace() {
|
||||
@ -80,7 +78,7 @@ export default {
|
||||
.create(this.namespace)
|
||||
.then((r) => {
|
||||
this.$store.commit('namespaces/addNamespace', r)
|
||||
this.success({message: 'The namespace was successfully created.'})
|
||||
this.success({message: this.$t('namespace.create.success') })
|
||||
this.$router.back()
|
||||
})
|
||||
.catch((e) => {
|
||||
|
@ -3,13 +3,12 @@
|
||||
@close="$router.back()"
|
||||
@submit="archiveNamespace()"
|
||||
>
|
||||
<span slot="header">{{ namespace.isArchived ? 'Un-' : '' }}Archive this namespace</span>
|
||||
<span slot="header">{{ title }}</span>
|
||||
<p slot="text" v-if="namespace.isArchived">
|
||||
You will be able to create new lists or edit it.
|
||||
{{ $t('namespace.archive.unarchiveText') }}
|
||||
</p>
|
||||
<p slot="text" v-else>
|
||||
You won't be able to edit this namespace or create new list until you un-archive it.<br/>
|
||||
This will also archive all lists in this namespace.
|
||||
{{ $t('namespace.archive.archiveText') }}
|
||||
</p>
|
||||
</modal>
|
||||
</template>
|
||||
@ -23,12 +22,16 @@ export default {
|
||||
return {
|
||||
namespaceService: NamespaceService,
|
||||
namespace: null,
|
||||
title: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.namespaceService = new NamespaceService()
|
||||
this.namespace = this.$store.getters['namespaces/getNamespaceById'](this.$route.params.id)
|
||||
this.setTitle(`Archive "${this.namespace.title}"`)
|
||||
this.title = this.namespace.isArchived ?
|
||||
this.$t('namespace.archive.titleUnarchive', { namespace: this.namespace.title }) :
|
||||
this.$t('namespace.archive.titleArchive', { namespace: this.namespace.title })
|
||||
this.setTitle(this.title)
|
||||
},
|
||||
methods: {
|
||||
archiveNamespace() {
|
||||
@ -38,7 +41,7 @@ export default {
|
||||
this.namespaceService.update(this.namespace)
|
||||
.then(r => {
|
||||
this.$store.commit('namespaces/setNamespaceById', r)
|
||||
this.success({message: 'The namespace was successfully archived.'})
|
||||
this.success({message: this.$t('namespace.archive.success')})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
|
@ -3,9 +3,11 @@
|
||||
@close="$router.back()"
|
||||
@submit="deleteNamespace()"
|
||||
>
|
||||
<span slot="header">Delete this namespace</span>
|
||||
<p slot="text">Are you sure you want to delete this namespace and all of its contents?
|
||||
<br/>This includes all tasks and <b>CANNOT BE UNDONE!</b></p>
|
||||
<span slot="header">{{ title }}</span>
|
||||
<p slot="text">
|
||||
{{ $t('namespace.delete.text1') }}<br/>
|
||||
{{ $t('namespace.delete.text2') }}
|
||||
</p>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
@ -17,13 +19,15 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
namespaceService: NamespaceService,
|
||||
title: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.namespaceService = new NamespaceService()
|
||||
|
||||
const namespace = this.$store.getters['namespaces/getNamespaceById'](this.$route.params.id)
|
||||
this.setTitle(`Delete "${namespace.title}"`)
|
||||
this.title = this.$t('namespace.delete.title', {namespace: namespace.title})
|
||||
this.setTitle(this.title)
|
||||
},
|
||||
methods: {
|
||||
deleteNamespace() {
|
||||
@ -31,7 +35,7 @@ export default {
|
||||
|
||||
this.$store.dispatch('namespaces/deleteNamespace', namespace)
|
||||
.then(() => {
|
||||
this.success({message: 'The namespace was successfully deleted.'})
|
||||
this.success({message: this.$t('namespace.delete.success')})
|
||||
this.$router.push({name: 'home'})
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -1,53 +1,53 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Edit This Namespace"
|
||||
:title="title"
|
||||
primary-icon=""
|
||||
primary-label="Save"
|
||||
:primary-label="$t('misc.save')"
|
||||
@primary="save"
|
||||
tertary="Delete"
|
||||
:tertary="$t('misc.delete')"
|
||||
@tertary="$router.push({ name: 'namespace.settings.delete', params: { id: $route.params.id } })"
|
||||
>
|
||||
<form @submit.prevent="save()">
|
||||
<div class="field">
|
||||
<label class="label" for="namespacetext">Namespace Name</label>
|
||||
<label class="label" for="namespacetext">{{ $t('namespace.attributes.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
:class="{ 'disabled': namespaceService.loading}"
|
||||
:disabled="namespaceService.loading"
|
||||
class="input"
|
||||
id="namespacetext"
|
||||
placeholder="The namespace text is here..."
|
||||
:placeholder="$t('namespace.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="namespace.title"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="namespacedescription">Description</label>
|
||||
<label class="label" for="namespacedescription">{{ $t('namespace.attributes.description') }}</label>
|
||||
<div class="control">
|
||||
<editor
|
||||
:class="{ 'disabled': namespaceService.loading}"
|
||||
:disabled="namespaceService.loading"
|
||||
:preview-is-default="false"
|
||||
id="namespacedescription"
|
||||
placeholder="The namespaces description goes here..."
|
||||
:placeholder="$t('namespace.attributes.descriptionPlaceholder')"
|
||||
v-if="editorActive"
|
||||
v-model="namespace.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="isArchivedCheck">Is Archived</label>
|
||||
<label class="label" for="isArchivedCheck">{{ $t('namespace.attributes.archived') }}</label>
|
||||
<div class="control">
|
||||
<fancycheckbox
|
||||
v-model="namespace.isArchived"
|
||||
v-tooltip="'If a namespace is archived, you cannot create new lists or edit it.'">
|
||||
This namespace is archived
|
||||
v-tooltip="$t('namespace.archive.description')">
|
||||
{{ $t('namespace.attributes.isArchived') }}
|
||||
</fancycheckbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<label class="label">{{ $t('namespace.attributes.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="namespace.hexColor"/>
|
||||
</div>
|
||||
@ -70,9 +70,9 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
namespaceService: NamespaceService,
|
||||
|
||||
namespace: NamespaceModel,
|
||||
editorActive: false,
|
||||
title: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@ -115,7 +115,8 @@ export default {
|
||||
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
|
||||
this.manageTeamsComponent = 'manageSharing'
|
||||
this.manageUsersComponent = 'manageSharing'
|
||||
this.setTitle(`Edit "${r.title}"`)
|
||||
this.title = this.$t('namespace.edit.title', {namespace: r.title})
|
||||
this.setTitle(this.title)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -126,7 +127,7 @@ export default {
|
||||
.then(r => {
|
||||
// Update the namespace in the parent
|
||||
this.$store.commit('namespaces/setNamespaceById', r)
|
||||
this.success({message: 'The namespace was successfully updated.'})
|
||||
this.success({message: this.$t('namespace.edit.success')})
|
||||
this.$router.back()
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Share this Namespace"
|
||||
:title="title"
|
||||
primary-label=""
|
||||
>
|
||||
<component
|
||||
@ -30,10 +30,10 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
namespaceService: NamespaceService,
|
||||
namespace: NamespaceModel,
|
||||
manageUsersComponent: '',
|
||||
manageTeamsComponent: '',
|
||||
|
||||
namespace: NamespaceModel,
|
||||
title: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@ -66,7 +66,8 @@ export default {
|
||||
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
|
||||
this.manageTeamsComponent = 'manageSharing'
|
||||
this.manageUsersComponent = 'manageSharing'
|
||||
this.setTitle(`Share "${this.namespace.title}"`)
|
||||
this.title = this.$t('namespace.share.title', { namespace: this.namespace.title })
|
||||
this.setTitle(this.title)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="notification is-info is-light has-text-centered" v-if="loading">
|
||||
Authenticating...
|
||||
{{ $t('sharing.authenticating') }}
|
||||
</div>
|
||||
<div v-if="authenticateWithPassword" class="box">
|
||||
<p class="pb-2">
|
||||
This shared list requires a password. Please enter it below:
|
||||
{{ $t('sharing.passwordRequired') }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
@ -13,7 +13,7 @@
|
||||
id="linkSharePassword"
|
||||
type="password"
|
||||
class="input"
|
||||
placeholder="e.g. ••••••••••••"
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
v-model="password"
|
||||
v-focus
|
||||
@keyup.enter.prevent="auth"
|
||||
@ -22,7 +22,7 @@
|
||||
</div>
|
||||
|
||||
<x-button @click="auth" :loading="loading">
|
||||
Login
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
|
||||
<div class="notification is-danger mt-4" v-if="error !== ''">
|
||||
@ -52,7 +52,7 @@ export default {
|
||||
this.auth()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Authenticating...')
|
||||
this.setTitle(this.$t('sharing.authenticating'))
|
||||
},
|
||||
computed: mapState({
|
||||
authLinkShare: state => state.auth.authenticated && (state.auth.info && state.auth.info.type === authTypes.LINK_SHARE),
|
||||
@ -77,12 +77,13 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
let error = 'An error occured.'
|
||||
// TODO: Put this logic in a global error handler method which checks all auth codes
|
||||
let error = this.$t('sharing.error')
|
||||
if (e.response && e.response.data && e.response.data.message) {
|
||||
error = e.response.data.message
|
||||
}
|
||||
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13002) {
|
||||
error = 'The password is invalid.'
|
||||
error = this.$t('sharing.invalidPassword')
|
||||
}
|
||||
this.error = error
|
||||
})
|
||||
|
@ -6,11 +6,13 @@
|
||||
v-if="!showAll"
|
||||
v-model="showNulls"
|
||||
>
|
||||
Show tasks without dates
|
||||
{{ $t('task.show.noDates') }}
|
||||
</fancycheckbox>
|
||||
<h3 v-if="showAll && tasks.length > 0">Current tasks</h3>
|
||||
<h3 v-if="showAll && tasks.length > 0">
|
||||
{{ $t('task.show.current') }}
|
||||
</h3>
|
||||
<h3 v-else-if="!showAll" class="mb-2">
|
||||
Tasks from
|
||||
{{ $t('task.show.from') }}
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
:config="flatPickerConfig"
|
||||
@ -19,7 +21,7 @@
|
||||
class="input"
|
||||
v-model="cStartDate"
|
||||
/>
|
||||
until
|
||||
{{ $t('task.show.until') }}
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': taskService.loading}"
|
||||
:config="flatPickerConfig"
|
||||
@ -30,12 +32,12 @@
|
||||
/>
|
||||
</h3>
|
||||
<div v-if="!showAll" class="mb-4">
|
||||
<x-button type="secondary" @click="showTodaysTasks()" class="mr-2">Today</x-button>
|
||||
<x-button type="secondary" @click="setDatesToNextWeek()" class="mr-2">Next Week</x-button>
|
||||
<x-button type="secondary" @click="setDatesToNextMonth()">Next Month</x-button>
|
||||
<x-button type="secondary" @click="showTodaysTasks()" class="mr-2">{{ $t('task.show.today') }}</x-button>
|
||||
<x-button type="secondary" @click="setDatesToNextWeek()" class="mr-2">{{ $t('task.show.nextWeek') }}</x-button>
|
||||
<x-button type="secondary" @click="setDatesToNextMonth()">{{ $t('task.show.nextMonth') }}</x-button>
|
||||
</div>
|
||||
<template v-if="!taskService.loading && (!tasks || tasks.length === 0) && showNothingToDo">
|
||||
<h3 class="nothing">Nothing to do - Have a nice day!</h3>
|
||||
<h3 class="nothing">{{ $t('task.show.noTasks') }}</h3>
|
||||
<img alt="" src="/images/cool.svg"/>
|
||||
</template>
|
||||
<div :class="{ 'is-loading': taskService.loading}" class="spinner"></div>
|
||||
@ -106,19 +108,23 @@ export default {
|
||||
this.cEndDate = newVal
|
||||
},
|
||||
},
|
||||
computed: mapState({
|
||||
userAuthenticated: state => state.auth.authenticated,
|
||||
flatPickerConfig: state => ({
|
||||
altFormat: 'j M Y H:i',
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
locale: {
|
||||
firstDayOfWeek: state.auth.settings.weekStart,
|
||||
},
|
||||
})
|
||||
}),
|
||||
computed: {
|
||||
flatPickerConfig() {
|
||||
return {
|
||||
altFormat: this.$t('date.altFormatLong'),
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
locale: {
|
||||
firstDayOfWeek: this.$store.state.auth.settings.weekStart,
|
||||
},
|
||||
}
|
||||
},
|
||||
...mapState({
|
||||
userAuthenticated: state => state.auth.authenticated,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
setDate() {
|
||||
this.$router.push({
|
||||
@ -151,9 +157,9 @@ export default {
|
||||
this.showNulls = this.$route.query.showNulls
|
||||
|
||||
if (this.showAll) {
|
||||
this.setTitle('Current Tasks')
|
||||
this.setTitle(this.$t('task.show.titleCurrent'))
|
||||
} else {
|
||||
this.setTitle(`Tasks from ${this.cStartDate.toLocaleDateString()} until ${this.cEndDate.toLocaleDateString()}`)
|
||||
this.setTitle(this.$t('task.show.titleDates', { from: this.cStartDate.toLocaleDateString(), to: this.cEndDate.toLocaleDateString()}))
|
||||
}
|
||||
|
||||
const params = {
|
||||
|
@ -18,7 +18,7 @@
|
||||
<!-- Assignees -->
|
||||
<div class="detail-title">
|
||||
<icon icon="users"/>
|
||||
Assignees
|
||||
{{ $t('task.attributes.assignees') }}
|
||||
</div>
|
||||
<edit-assignees
|
||||
:disabled="!canWrite"
|
||||
@ -33,7 +33,7 @@
|
||||
<!-- Priority -->
|
||||
<div class="detail-title">
|
||||
<icon :icon="['far', 'star']"/>
|
||||
Priority
|
||||
{{ $t('task.attributes.priority') }}
|
||||
</div>
|
||||
<priority-select
|
||||
:disabled="!canWrite"
|
||||
@ -47,13 +47,13 @@
|
||||
<!-- Due Date -->
|
||||
<div class="detail-title">
|
||||
<icon icon="calendar"/>
|
||||
Due Date
|
||||
{{ $t('task.attributes.dueDate') }}
|
||||
</div>
|
||||
<div class="date-input">
|
||||
<datepicker
|
||||
v-model="task.dueDate"
|
||||
@close-on-change="() => saveTask()"
|
||||
choose-date-label="Click here to set a due date"
|
||||
:choose-date-label="$t('task.detail.chooseDueDate')"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
ref="dueDate"
|
||||
/>
|
||||
@ -73,7 +73,7 @@
|
||||
<!-- Percent Done -->
|
||||
<div class="detail-title">
|
||||
<icon icon="percent"/>
|
||||
Percent Done
|
||||
{{ $t('task.attributes.percentDone') }}
|
||||
</div>
|
||||
<percent-done-select
|
||||
:disabled="!canWrite"
|
||||
@ -87,13 +87,13 @@
|
||||
<!-- Start Date -->
|
||||
<div class="detail-title">
|
||||
<icon icon="calendar-week"/>
|
||||
Start Date
|
||||
{{ $t('task.attributes.startDate') }}
|
||||
</div>
|
||||
<div class="date-input">
|
||||
<datepicker
|
||||
v-model="task.startDate"
|
||||
@close-on-change="() => saveTask()"
|
||||
choose-date-label="Click here to set a start date"
|
||||
:choose-date-label="$t('task.detail.chooseStartDate')"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
ref="startDate"
|
||||
/>
|
||||
@ -114,13 +114,13 @@
|
||||
<!-- End Date -->
|
||||
<div class="detail-title">
|
||||
<icon icon="calendar-week"/>
|
||||
End Date
|
||||
{{ $t('task.attributes.endDate') }}
|
||||
</div>
|
||||
<div class="date-input">
|
||||
<datepicker
|
||||
v-model="task.endDate"
|
||||
@close-on-change="() => saveTask()"
|
||||
choose-date-label="Click here to set an end date"
|
||||
:choose-date-label="$t('task.detail.chooseEndDate')"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
ref="endDate"
|
||||
/>
|
||||
@ -140,7 +140,7 @@
|
||||
<!-- Reminders -->
|
||||
<div class="detail-title">
|
||||
<icon icon="history"/>
|
||||
Reminders
|
||||
{{ $t('task.attributes.reminders') }}
|
||||
</div>
|
||||
<reminders
|
||||
:disabled="!canWrite"
|
||||
@ -154,7 +154,7 @@
|
||||
<!-- Repeat after -->
|
||||
<div class="detail-title">
|
||||
<icon :icon="['far', 'clock']"/>
|
||||
Repeat
|
||||
{{ $t('task.attributes.repeat') }}
|
||||
</div>
|
||||
<repeat-after
|
||||
:disabled="!canWrite"
|
||||
@ -168,7 +168,7 @@
|
||||
<!-- Color -->
|
||||
<div class="detail-title">
|
||||
<icon icon="fill-drip"/>
|
||||
Color
|
||||
{{ $t('task.attributes.color') }}
|
||||
</div>
|
||||
<color-picker
|
||||
@change="saveTask"
|
||||
@ -185,7 +185,7 @@
|
||||
<span class="icon is-grey">
|
||||
<icon icon="tags"/>
|
||||
</span>
|
||||
Labels
|
||||
{{ $t('task.attributes.labels') }}
|
||||
</div>
|
||||
<edit-labels :disabled="!canWrite" :task-id="taskId" ref="labels" v-model="task.labels"/>
|
||||
</div>
|
||||
@ -214,7 +214,7 @@
|
||||
<span class="icon is-grey">
|
||||
<icon icon="tasks"/>
|
||||
</span>
|
||||
Related Tasks
|
||||
{{ $t('task.attributes.relatedTasks') }}
|
||||
</h3>
|
||||
<related-tasks
|
||||
:edit-enabled="canWrite"
|
||||
@ -232,7 +232,7 @@
|
||||
<span class="icon is-grey">
|
||||
<icon icon="list"/>
|
||||
</span>
|
||||
Move task to a different list
|
||||
{{ $t('task.detail.move') }}
|
||||
</h3>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
@ -253,7 +253,7 @@
|
||||
icon="check-double"
|
||||
type="secondary"
|
||||
>
|
||||
{{ task.done ? 'Mark as undone' : 'Done!' }}
|
||||
{{ task.done ? $t('task.detail.undone') : $t('task.detail.done') }}
|
||||
</x-button>
|
||||
<task-subscription
|
||||
entity="task"
|
||||
@ -267,7 +267,7 @@
|
||||
type="secondary"
|
||||
v-shortkey="['a']">
|
||||
<span class="icon is-small"><icon icon="users"/></span>
|
||||
Assign this task to a user
|
||||
{{ $t('task.detail.actions.assign') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('labels')"
|
||||
@ -276,14 +276,14 @@
|
||||
v-shortkey="['l']"
|
||||
icon="tags"
|
||||
>
|
||||
Add labels
|
||||
{{ $t('task.detail.actions.label') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('priority')"
|
||||
type="secondary"
|
||||
:icon="['far', 'star']"
|
||||
>
|
||||
Set Priority
|
||||
{{ $t('task.detail.actions.priority') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('dueDate')"
|
||||
@ -292,42 +292,42 @@
|
||||
v-shortkey="['d']"
|
||||
icon="calendar"
|
||||
>
|
||||
Set Due Date
|
||||
{{ $t('task.detail.actions.dueDate') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('startDate')"
|
||||
type="secondary"
|
||||
icon="calendar-week"
|
||||
>
|
||||
Set a Start Date
|
||||
{{ $t('task.detail.actions.startDate') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('endDate')"
|
||||
type="secondary"
|
||||
icon="calendar-week"
|
||||
>
|
||||
Set an End Date
|
||||
{{ $t('task.detail.actions.endDate') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('reminders')"
|
||||
type="secondary"
|
||||
icon="history"
|
||||
>
|
||||
Set Reminders
|
||||
{{ $t('task.detail.actions.reminders') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('repeatAfter')"
|
||||
type="secondary"
|
||||
:icon="['far', 'clock']"
|
||||
>
|
||||
Set a repeating interval
|
||||
{{ $t('task.detail.actions.repeatAfter') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('percentDone')"
|
||||
type="secondary"
|
||||
icon="percent"
|
||||
>
|
||||
Set Percent Done
|
||||
{{ $t('task.detail.actions.percentDone') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('attachments')"
|
||||
@ -336,7 +336,7 @@
|
||||
v-shortkey="['f']"
|
||||
icon="paperclip"
|
||||
>
|
||||
Add attachments
|
||||
{{ $t('task.detail.actions.attachments') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('relatedTasks')"
|
||||
@ -345,21 +345,21 @@
|
||||
v-shortkey="['r']"
|
||||
icon="tasks"
|
||||
>
|
||||
Add task relations
|
||||
{{ $t('task.detail.actions.relatedTasks') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('moveList')"
|
||||
type="secondary"
|
||||
icon="list"
|
||||
>
|
||||
Move task
|
||||
{{ $t('task.detail.actions.moveList') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="setFieldActive('color')"
|
||||
type="secondary"
|
||||
icon="fill-drip"
|
||||
>
|
||||
Set task color
|
||||
{{ $t('task.detail.actions.color') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
@click="showDeleteModal = true"
|
||||
@ -367,21 +367,27 @@
|
||||
:shadow="false"
|
||||
class="is-danger is-outlined has-no-border"
|
||||
>
|
||||
Delete task
|
||||
{{ $t('task.detail.actions.delete') }}
|
||||
</x-button>
|
||||
|
||||
<!-- Created / Updated [by] -->
|
||||
<p class="created">
|
||||
Created <span v-tooltip="formatDate(task.created)">{{ formatDateSince(task.created) }}</span>
|
||||
by {{ task.createdBy.getDisplayName() }}
|
||||
<i18n path="task.detail.created">
|
||||
<span v-tooltip="formatDate(task.created)">{{ formatDateSince(task.created) }}</span>
|
||||
{{ task.createdBy.getDisplayName() }}
|
||||
</i18n>
|
||||
<template v-if="+new Date(task.created) !== +new Date(task.updated)">
|
||||
<br/>
|
||||
<!-- Computed properties to show the actual date every time it gets updated -->
|
||||
Updated <span v-tooltip="updatedFormatted">{{ updatedSince }}</span>
|
||||
<i18n path="task.detail.updated">
|
||||
<span v-tooltip="updatedFormatted">{{ updatedSince }}</span>
|
||||
</i18n>
|
||||
</template>
|
||||
<template v-if="task.done">
|
||||
<br/>
|
||||
Done <span v-tooltip="doneFormatted">{{ doneSince }}</span>
|
||||
<i18n path="task.detail.doneAt">
|
||||
<span v-tooltip="doneFormatted">{{ doneSince }}</span>
|
||||
</i18n>
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
@ -393,11 +399,10 @@
|
||||
@close="showDeleteModal = false"
|
||||
@submit="deleteTask()"
|
||||
v-if="showDeleteModal">
|
||||
<span slot="header">Delete this task</span>
|
||||
<span slot="header">{{ $t('task.detail.delete.header') }}</span>
|
||||
<p slot="text">
|
||||
Are you sure you want to remove this task? <br/>
|
||||
This will also remove all attachments, reminders and relations associated with this task and
|
||||
<b>cannot be undone!</b>
|
||||
{{ $t('task.detail.delete.text1') }}<br/>
|
||||
{{ $t('task.detail.delete.text2') }}
|
||||
</p>
|
||||
</modal>
|
||||
</transition>
|
||||
@ -620,7 +625,7 @@ export default {
|
||||
callback: undoCallback,
|
||||
}]
|
||||
}
|
||||
this.success({message: 'The task was saved successfully.'}, actions)
|
||||
this.success({message: this.$t('task.detail.updateSuccess')}, actions)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
@ -648,7 +653,7 @@ export default {
|
||||
deleteTask() {
|
||||
this.$store.dispatch('tasks/delete', this.task)
|
||||
.then(() => {
|
||||
this.success({message: 'The task has been deleted successfully.'})
|
||||
this.success({message: this.$t('task.detail.deleteSuccess')})
|
||||
this.$router.push({name: 'list.index', params: {listId: this.task.listId}})
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -3,21 +3,17 @@
|
||||
class="loader-container is-max-width-desktop"
|
||||
:class="{ 'is-loading': teamService.loading }"
|
||||
>
|
||||
<card class="is-fullwidth" v-if="userIsAdmin" title="Edit Team">
|
||||
<card class="is-fullwidth" v-if="userIsAdmin" :title="title">
|
||||
<form @submit.prevent="save()">
|
||||
<div class="field">
|
||||
<label class="label" for="teamtext"
|
||||
>Team Name</label
|
||||
>
|
||||
<label class="label" for="teamtext">{{ $t('team.attributes.name') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
:class="{
|
||||
disabled: teamMemberService.loading,
|
||||
}"
|
||||
:class="{ disabled: teamMemberService.loading }"
|
||||
:disabled="teamMemberService.loading"
|
||||
class="input"
|
||||
id="teamtext"
|
||||
placeholder="The team text is here..."
|
||||
:placeholder="$t('team.attributes.namePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="team.name"
|
||||
@ -28,19 +24,17 @@
|
||||
class="help is-danger"
|
||||
v-if="showError && team.name === ''"
|
||||
>
|
||||
Please specify a name.
|
||||
{{ $t('team.attributes.nameRequired') }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<label class="label" for="teamdescription"
|
||||
>Description</label
|
||||
>
|
||||
<label class="label" for="teamdescription">{{ $t('team.attributes.description') }}</label>
|
||||
<div class="control">
|
||||
<editor
|
||||
:class="{ disabled: teamService.loading }"
|
||||
:disabled="teamService.loading"
|
||||
:preview-is-default="false"
|
||||
id="teamdescription"
|
||||
placeholder="The teams description goes here..."
|
||||
:placeholder="$t('team.attributes.descriptionPlaceholder')"
|
||||
v-model="team.description"
|
||||
/>
|
||||
</div>
|
||||
@ -54,7 +48,7 @@
|
||||
:loading="teamService.loading"
|
||||
class="is-fullwidth"
|
||||
>
|
||||
Save
|
||||
{{ $t('misc.save') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<div class="control">
|
||||
@ -68,13 +62,13 @@
|
||||
</div>
|
||||
</card>
|
||||
|
||||
<card class="is-fullwidth has-overflow" title="Team Members" :padding="false">
|
||||
<card class="is-fullwidth has-overflow" :title="$t('team.edit.members')" :padding="false">
|
||||
<div class="p-4" v-if="userIsAdmin">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<multiselect
|
||||
:loading="userService.loading"
|
||||
placeholder="Type to search..."
|
||||
:placeholder="$t('team.edit.search')"
|
||||
@search="findUser"
|
||||
:search-results="foundUsers"
|
||||
label="username"
|
||||
@ -83,7 +77,7 @@
|
||||
</div>
|
||||
<div class="control">
|
||||
<x-button @click="addUser" icon="plus">
|
||||
Add To Team
|
||||
{{ $t('team.edit.addUser') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -102,13 +96,13 @@
|
||||
<span class="icon is-small">
|
||||
<icon icon="lock"/>
|
||||
</span>
|
||||
Admin
|
||||
{{ $t('team.attributes.admin') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="icon is-small">
|
||||
<icon icon="user"/>
|
||||
</span>
|
||||
Member
|
||||
{{ $t('team.attributes.member') }}
|
||||
</template>
|
||||
</td>
|
||||
<td class="actions" v-if="userIsAdmin">
|
||||
@ -118,7 +112,7 @@
|
||||
class="mr-2"
|
||||
v-if="m.id !== userInfo.id"
|
||||
>
|
||||
Make {{ m.admin ? 'Member' : 'Admin' }}
|
||||
{{ m.admin ? $t('team.edit.makeMember') : $t('team.edit.makeAdmin') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
:loading="teamMemberService.loading"
|
||||
@ -140,13 +134,10 @@
|
||||
@submit="deleteTeam()"
|
||||
v-if="showDeleteModal"
|
||||
>
|
||||
<span slot="header">Delete the team</span>
|
||||
<span slot="header">{{ $t('team.edit.delete.header') }}</span>
|
||||
<p slot="text">
|
||||
Are you sure you want to delete this team and all of its
|
||||
members?<br/>
|
||||
All team members will loose access to lists and namespaces
|
||||
shared with this team.<br/>
|
||||
<b>This CANNOT BE UNDONE!</b>
|
||||
{{ $t('team.edit.delete.text1') }}<br/>
|
||||
{{ $t('team.edit.delete.text2') }}
|
||||
</p>
|
||||
</modal>
|
||||
</transition>
|
||||
@ -157,12 +148,10 @@
|
||||
@submit="deleteUser()"
|
||||
v-if="showUserDeleteModal"
|
||||
>
|
||||
<span slot="header">Remove a user from the team</span>
|
||||
<span slot="header">{{ $t('team.edit.deleteUser.header') }}</span>
|
||||
<p slot="text">
|
||||
Are you sure you want to remove this user from the team?<br/>
|
||||
They will loose access to all lists and namespaces this team has
|
||||
access to.<br/>
|
||||
<b>This CANNOT BE UNDONE!</b>
|
||||
{{ $t('team.edit.deleteUser.text1') }}
|
||||
{{ $t('team.edit.deleteUser.text2') }}
|
||||
</p>
|
||||
</modal>
|
||||
</transition>
|
||||
@ -170,7 +159,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import router from '../../router'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
import TeamService from '../../services/team'
|
||||
@ -204,14 +192,13 @@ export default {
|
||||
userService: UserService,
|
||||
|
||||
showError: false,
|
||||
title: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Multiselect,
|
||||
editor: () => ({
|
||||
component: import(
|
||||
/* webpackChunkName: "editor" */ '../../components/input/editor'
|
||||
),
|
||||
component: import(/* webpackChunkName: "editor" */ '../../components/input/editor'),
|
||||
loading: LoadingComponent,
|
||||
error: ErrorComponent,
|
||||
timeout: 60000,
|
||||
@ -246,7 +233,8 @@ export default {
|
||||
.get(this.team)
|
||||
.then((response) => {
|
||||
this.$set(this, 'team', response)
|
||||
this.setTitle(`Edit Team ${this.team.name}`)
|
||||
this.title = this.$t('team.edit.title', {team: this.team.name})
|
||||
this.setTitle(this.title)
|
||||
})
|
||||
.catch((e) => {
|
||||
this.error(e)
|
||||
@ -263,7 +251,7 @@ export default {
|
||||
.update(this.team)
|
||||
.then((response) => {
|
||||
this.team = response
|
||||
this.success({message: 'The team was successfully updated.'})
|
||||
this.success({message: this.$t('team.edit.success')})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.error(e)
|
||||
@ -273,8 +261,8 @@ export default {
|
||||
this.teamService
|
||||
.delete(this.team)
|
||||
.then(() => {
|
||||
this.success({message: 'The team was successfully deleted.'})
|
||||
router.push({name: 'teams.index'})
|
||||
this.success({message: this.$t('team.edit.delete.success')})
|
||||
this.$router.push({name: 'teams.index'})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.error(e)
|
||||
@ -284,10 +272,7 @@ export default {
|
||||
this.teamMemberService
|
||||
.delete(this.member)
|
||||
.then(() => {
|
||||
this.success({
|
||||
message:
|
||||
'The user was successfully deleted from the team.',
|
||||
})
|
||||
this.success({message: this.$t('team.edit.deleteUser.success')})
|
||||
this.loadTeam()
|
||||
})
|
||||
.catch((e) => {
|
||||
@ -306,7 +291,7 @@ export default {
|
||||
.create(newMember)
|
||||
.then(() => {
|
||||
this.loadTeam()
|
||||
this.success({message: 'The team member was successfully added.'})
|
||||
this.success({message: this.$t('team.edit.userAddedSuccess')})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.error(e)
|
||||
@ -325,10 +310,9 @@ export default {
|
||||
}
|
||||
}
|
||||
this.success({
|
||||
message:
|
||||
'The team member was successfully made ' +
|
||||
(member.admin ? 'admin' : 'member') +
|
||||
'.',
|
||||
message: member.admin ?
|
||||
this.$t('team.edit.madeAdmin') :
|
||||
this.$t('team.edit.madeMember')
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
|
@ -5,10 +5,10 @@
|
||||
class="is-pulled-right"
|
||||
icon="plus"
|
||||
>
|
||||
New Team
|
||||
{{ $t('team.create.title') }}
|
||||
</x-button>
|
||||
|
||||
<h1>Teams</h1>
|
||||
<h1>{{ $t('team.title') }}</h1>
|
||||
<ul class="teams box" v-if="teams.length > 0">
|
||||
<li :key="t.id" v-for="t in teams">
|
||||
<router-link :to="{name: 'teams.edit', params: {id: t.id}}">
|
||||
@ -17,9 +17,9 @@
|
||||
</li>
|
||||
</ul>
|
||||
<p v-else-if="!teamService.loading" class="has-text-centered has-text-grey is-italic">
|
||||
You are currently not part of any teams.
|
||||
{{ $t('team.noTeams') }}
|
||||
<router-link :to="{name: 'teams.create'}">
|
||||
Create a new team.
|
||||
{{ $t('team.create.title') }}.
|
||||
</router-link>
|
||||
</p>
|
||||
</div>
|
||||
@ -41,7 +41,7 @@ export default {
|
||||
this.loadTeams()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Teams')
|
||||
this.setTitle(this.$t('team.title'))
|
||||
},
|
||||
methods: {
|
||||
loadTeams() {
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<create-edit
|
||||
title="Create a new team"
|
||||
:title="$t('team.create.title')"
|
||||
@create="newTeam()"
|
||||
:create-disabled="team.name === ''"
|
||||
>
|
||||
<div class="field">
|
||||
<label class="label" for="teamName">Team Name</label>
|
||||
<label class="label" for="teamName">{{ $t('team.attributes.name') }}</label>
|
||||
<div
|
||||
class="control is-expanded"
|
||||
:class="{ 'is-loading': teamService.loading }"
|
||||
@ -14,7 +14,7 @@
|
||||
:class="{ 'disabled': teamService.loading }"
|
||||
class="input"
|
||||
id="teamName"
|
||||
placeholder="The team's name goes here..."
|
||||
:placeholder="$t('team.attributes.namePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="team.name"
|
||||
@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="showError && team.name === ''">
|
||||
Please specify a name.
|
||||
{{ $t('team.attributes.nameRequired') }}
|
||||
</p>
|
||||
</create-edit>
|
||||
</template>
|
||||
@ -50,7 +50,7 @@ export default {
|
||||
this.team = new TeamModel()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Create a new Team')
|
||||
this.setTitle(this.$t('team.create.title'))
|
||||
},
|
||||
methods: {
|
||||
newTeam() {
|
||||
@ -67,7 +67,7 @@ export default {
|
||||
name: 'teams.edit',
|
||||
params: { id: response.id },
|
||||
})
|
||||
this.success({message: 'The team was successfully created.'})
|
||||
this.success({message: this.$t('team.create.success') })
|
||||
})
|
||||
.catch((e) => {
|
||||
this.error(e)
|
||||
|
@ -3,17 +3,17 @@
|
||||
<h2 class="title has-text-centered">Login</h2>
|
||||
<div class="box">
|
||||
<div class="notification is-success has-text-centered" v-if="confirmedEmailSuccess">
|
||||
You successfully confirmed your email! You can log in now.
|
||||
{{ $t('user.auth.confirmEmailSuccess') }}
|
||||
</div>
|
||||
<api-config @foundApi="hasApiUrl = true"/>
|
||||
<form @submit.prevent="submit" id="loginform" v-if="hasApiUrl && localAuthEnabled">
|
||||
<div class="field">
|
||||
<label class="label" for="username">Username Or Email Address</label>
|
||||
<label class="label" for="username">{{ $t('user.auth.usernameEmail') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input" id="username"
|
||||
name="username"
|
||||
placeholder="e.g. frederick"
|
||||
:placeholder="$t('user.auth.usernamePlaceholder')"
|
||||
ref="username"
|
||||
required
|
||||
type="text"
|
||||
@ -24,13 +24,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="password">Password</label>
|
||||
<label class="label" for="password">{{ $t('user.auth.password') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="e.g. ••••••••••••"
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
ref="password"
|
||||
required
|
||||
type="password"
|
||||
@ -40,12 +40,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field" v-if="needsTotpPasscode">
|
||||
<label class="label" for="totpPasscode">Two Factor Authentication Code</label>
|
||||
<label class="label" for="totpPasscode">{{ $t('user.auth.totpTitle') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="totpPasscode"
|
||||
placeholder="e.g. 123456"
|
||||
:placeholder="$t('user.auth.totpPlaceholder')"
|
||||
ref="totpPasscode"
|
||||
required
|
||||
type="text"
|
||||
@ -61,19 +61,19 @@
|
||||
@click="submit"
|
||||
:loading="loading"
|
||||
>
|
||||
Login
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
:to="{ name: 'user.register' }"
|
||||
v-if="registrationEnabled"
|
||||
type="secondary"
|
||||
>
|
||||
Register
|
||||
{{ $t('user.auth.register') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<router-link :to="{ name: 'user.password-reset.request' }" class="reset-password-link">
|
||||
Reset your password
|
||||
{{ $t('user.auth.resetPassword') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
@ -92,7 +92,7 @@
|
||||
type="secondary"
|
||||
class="is-fullwidth mt-2"
|
||||
>
|
||||
Log in with {{ p.name }}
|
||||
{{ $t('user.auth.loginWith', {provider: p.name}) }}
|
||||
</x-button>
|
||||
</div>
|
||||
|
||||
@ -109,6 +109,7 @@ import {HTTPFactory} from '@/http-common'
|
||||
import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types'
|
||||
import legal from '../../components/misc/legal'
|
||||
import ApiConfig from '@/components/misc/api-config'
|
||||
import {getErrorText} from '@/message'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -147,7 +148,7 @@ export default {
|
||||
},
|
||||
created() {
|
||||
this.hasApiUrl = window.API_URL !== ''
|
||||
this.setTitle('Login')
|
||||
this.setTitle(this.$t('user.auth.login'))
|
||||
},
|
||||
computed: mapState({
|
||||
registrationEnabled: state => state.config.registrationEnabled,
|
||||
@ -183,7 +184,14 @@ export default {
|
||||
}
|
||||
|
||||
this.$store.dispatch('auth/login', credentials)
|
||||
.catch(() => {
|
||||
.catch(e => {
|
||||
const err = getErrorText(e, p => this.$t(p))
|
||||
if (typeof err[1] !== 'undefined') {
|
||||
this.$store.commit(ERROR_MESSAGE, err[1])
|
||||
return
|
||||
}
|
||||
|
||||
this.$store.commit(ERROR_MESSAGE, err[0])
|
||||
})
|
||||
},
|
||||
redirectToProvider(provider) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
<div class="notification is-info" v-if="loading">
|
||||
Authenticating...
|
||||
{{ $t('user.auth.authenticating') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -13,6 +13,7 @@
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types'
|
||||
import {getErrorText} from '@/message'
|
||||
|
||||
export default {
|
||||
name: 'Auth',
|
||||
@ -41,7 +42,7 @@ export default {
|
||||
const state = localStorage.getItem('state')
|
||||
if(typeof this.$route.query.state === 'undefined' || this.$route.query.state !== state) {
|
||||
localStorage.removeItem('authenticating')
|
||||
this.$store.commit(ERROR_MESSAGE, 'State does not match, refusing to continue!')
|
||||
this.$store.commit(ERROR_MESSAGE, this.$t('user.auth.openIdStateError'))
|
||||
return
|
||||
}
|
||||
|
||||
@ -54,8 +55,14 @@ export default {
|
||||
.then(() => {
|
||||
this.$router.push({name: 'home'})
|
||||
})
|
||||
.catch(() => {
|
||||
// Handled through global state
|
||||
.catch(e => {
|
||||
const err = getErrorText(e, p => this.$t(p))
|
||||
if (typeof err[1] !== 'undefined') {
|
||||
this.$store.commit(ERROR_MESSAGE, err[1])
|
||||
return
|
||||
}
|
||||
|
||||
this.$store.commit(ERROR_MESSAGE, err[0])
|
||||
})
|
||||
.finally(() => {
|
||||
localStorage.removeItem('authenticating')
|
||||
|
@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="title has-text-centered">Reset your password</h2>
|
||||
<h2 class="title has-text-centered">{{ $t('user.auth.resetPassword') }}</h2>
|
||||
<div class="box">
|
||||
<form @submit.prevent="submit" id="form" v-if="!successMessage">
|
||||
<div class="field">
|
||||
<label class="label" for="password1">Password</label>
|
||||
<label class="label" for="password1">{{ $t('user.auth.password') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="password1"
|
||||
name="password1"
|
||||
placeholder="e.g. ••••••••••••"
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
required
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
@ -19,13 +19,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="password2">Retype your password</label>
|
||||
<label class="label" for="password2">{{ $t('user.auth.passwordRepeat') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="password2"
|
||||
name="password2"
|
||||
placeholder="e.g. ••••••••••••"
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
required
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
@ -39,12 +39,12 @@
|
||||
:loading="this.passwordResetService.loading"
|
||||
@click="submit"
|
||||
>
|
||||
Reset your password
|
||||
{{ $t('user.auth.resetPassoword') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification is-info" v-if="this.passwordResetService.loading">
|
||||
Loading...
|
||||
{{ $t('misc.loading') }}
|
||||
</div>
|
||||
<div class="notification is-danger" v-if="errorMsg">
|
||||
{{ errorMsg }}
|
||||
@ -55,7 +55,7 @@
|
||||
{{ successMessage }}
|
||||
</div>
|
||||
<x-button :to="{ name: 'user.login' }">
|
||||
Login
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<legal/>
|
||||
@ -87,14 +87,14 @@ export default {
|
||||
this.passwordResetService = new PasswordResetService()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Reset your password')
|
||||
this.setTitle(this.$t('user.auth.resetPassword'))
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.errorMsg = ''
|
||||
|
||||
if (this.credentials.password2 !== this.credentials.password) {
|
||||
this.errorMsg = 'Passwords don\'t match'
|
||||
this.errorMsg = this.$t('user.auth.passwordsDontMatch')
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="title has-text-centered">Register</h2>
|
||||
<h2 class="title has-text-centered">{{ $t('user.auth.register') }}</h2>
|
||||
<div class="box">
|
||||
<form @submit.prevent="submit" id="registerform">
|
||||
<div class="field">
|
||||
<label class="label" for="username">Username</label>
|
||||
<label class="label" for="username">{{ $t('user.auth.username') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="username"
|
||||
name="username"
|
||||
placeholder="e.g. frederick"
|
||||
:placeholder="$t('user.auth.usernamePlaceholder')"
|
||||
required
|
||||
type="text"
|
||||
autocomplete="username"
|
||||
@ -21,13 +21,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="email">E-mail address</label>
|
||||
<label class="label" for="email">{{ $t('user.auth.email') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="e.g. frederic@vikunja.io"
|
||||
:placeholder="$t('user.auth.emailPlaceholder')"
|
||||
required
|
||||
type="email"
|
||||
v-model="credentials.email"
|
||||
@ -36,13 +36,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="password1">Password</label>
|
||||
<label class="label" for="password1">{{ $t('user.auth.password') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="password1"
|
||||
name="password1"
|
||||
placeholder="e.g. ••••••••••••"
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
required
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
@ -52,13 +52,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="password2">Retype your password</label>
|
||||
<label class="label" for="password2">{{ $t('user.auth.passwordRepeat') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="password2"
|
||||
name="password2"
|
||||
placeholder="e.g. ••••••••••••"
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
required
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
@ -76,13 +76,15 @@
|
||||
@click="submit"
|
||||
class="mr-2"
|
||||
>
|
||||
Register
|
||||
{{ $t('user.auth.register') }}
|
||||
</x-button>
|
||||
<x-button :to="{ name: 'user.login' }" type="secondary">
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
<x-button :to="{ name: 'user.login' }" type="secondary">Login</x-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification is-info" v-if="loading">
|
||||
Loading...
|
||||
{{ $t('misc.loading') }}
|
||||
</div>
|
||||
<div class="notification is-danger" v-if="errorMessage !== ''">
|
||||
{{ errorMessage }}
|
||||
@ -120,7 +122,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Register')
|
||||
this.setTitle(this.$t('user.auth.register'))
|
||||
},
|
||||
computed: mapState({
|
||||
authenticated: state => state.auth.authenticated,
|
||||
@ -133,7 +135,7 @@ export default {
|
||||
this.$store.commit(ERROR_MESSAGE, '')
|
||||
|
||||
if (this.credentials.password2 !== this.credentials.password) {
|
||||
this.$store.commit(ERROR_MESSAGE, 'Passwords don\'t match.')
|
||||
this.$store.commit(ERROR_MESSAGE, this.$t('user.auth.passwordsDontMatch'))
|
||||
this.$store.commit(LOADING, false)
|
||||
return
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="title has-text-centered">Reset your password</h2>
|
||||
<h2 class="title has-text-centered">{{ $t('user.auth.resetPassword') }}</h2>
|
||||
<div class="box">
|
||||
<form @submit.prevent="submit" v-if="!isSuccess">
|
||||
<div class="field">
|
||||
<label class="label" for="email">E-mail address</label>
|
||||
<label class="label" for="email">{{ $t('user.auth.email') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="e.g. frederic@vikunja.io"
|
||||
:placeholder="$t('user.auth.emailPlaceholder')"
|
||||
required
|
||||
type="email"
|
||||
v-focus
|
||||
@ -24,9 +24,11 @@
|
||||
@click="submit"
|
||||
:loading="passwordResetService.loading"
|
||||
>
|
||||
Send me a password reset link
|
||||
{{ $t('user.auth.resetPasswordAction') }}
|
||||
</x-button>
|
||||
<x-button :to="{ name: 'user.login' }" type="secondary">
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
<x-button :to="{ name: 'user.login' }" type="secondary">Login</x-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification is-danger" v-if="errorMsg">
|
||||
@ -35,9 +37,11 @@
|
||||
</form>
|
||||
<div class="has-text-centered" v-if="isSuccess">
|
||||
<div class="notification is-success">
|
||||
Check your inbox! You should have a mail with instructions on how to reset your password.
|
||||
{{ $t('user.auth.resetPasswordSuccess') }}
|
||||
</div>
|
||||
<x-button :to="{ name: 'user.login' }">Login</x-button>
|
||||
<x-button :to="{ name: 'user.login' }">
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<legal/>
|
||||
</div>
|
||||
@ -66,7 +70,7 @@ export default {
|
||||
this.passwordReset = new PasswordResetModel()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Reset your password')
|
||||
this.setTitle(this.$t('user.auth.resetPassword'))
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
|
@ -3,15 +3,15 @@
|
||||
:class="{ 'is-loading': passwordUpdateService.loading || emailUpdateService.loading || totpService.loading }"
|
||||
class="loader-container is-max-width-desktop">
|
||||
<!-- General -->
|
||||
<card title="General Settings" class="general-settings">
|
||||
<card :title="$t('user.settings.general.title')" class="general-settings">
|
||||
<div class="field">
|
||||
<label class="label" for="newName">Name</label>
|
||||
<label class="label" for="newName">{{ $t('user.settings.general.name') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@keyup.enter="updateSettings"
|
||||
class="input"
|
||||
id="newName"
|
||||
placeholder="The new name"
|
||||
:placeholder="$t('user.settings.general.newName')"
|
||||
type="text"
|
||||
v-model="settings.name"/>
|
||||
</div>
|
||||
@ -19,42 +19,54 @@
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="settings.emailRemindersEnabled"/>
|
||||
Send me reminders for tasks via Email
|
||||
{{ $t('user.settings.general.emailReminders') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="settings.overdueTasksRemindersEnabled"/>
|
||||
Send me reminders for overdue undone tasks via email each morning
|
||||
{{ $t('user.settings.general.overdueReminders') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="settings.discoverableByName"/>
|
||||
Let other users find me when they search for my name
|
||||
{{ $t('user.settings.general.discoverableByName') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="settings.discoverableByEmail"/>
|
||||
Let other users find me when they search for my full email
|
||||
{{ $t('user.settings.general.discoverableByEmail') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="playSoundWhenDone"/>
|
||||
Play a sound when marking tasks as done
|
||||
{{ $t('user.settings.general.playSoundWhenDone') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="is-flex is-align-items-center">
|
||||
<span>
|
||||
Week starts on
|
||||
{{ $t('user.settings.general.weekStart') }}
|
||||
</span>
|
||||
<div class="select ml-2">
|
||||
<select v-model.number="settings.weekStart">
|
||||
<option value="0">Sunday</option>
|
||||
<option value="1">Monday</option>
|
||||
<option value="0">{{ $t('user.settings.general.weekStartSunday') }}</option>
|
||||
<option value="1">{{ $t('user.settings.general.weekStartMonday') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="is-flex is-align-items-center">
|
||||
<span>
|
||||
{{ $t('user.settings.general.language') }}
|
||||
</span>
|
||||
<div class="select ml-2">
|
||||
<select v-model.number="language">
|
||||
<option :value="l" v-for="(lang, l) in availableLanguages" :key="l">{{ lang }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
@ -65,7 +77,7 @@
|
||||
@click="updateSettings()"
|
||||
class="is-fullwidth mt-4"
|
||||
>
|
||||
Save
|
||||
{{ $t('misc.save') }}
|
||||
</x-button>
|
||||
</card>
|
||||
|
||||
@ -73,40 +85,40 @@
|
||||
<avatar-settings/>
|
||||
|
||||
<!-- Password update -->
|
||||
<card title="Update Your Password">
|
||||
<card :title="$t('user.settings.newPasswordTitle')">
|
||||
<form @submit.prevent="updatePassword()">
|
||||
<div class="field">
|
||||
<label class="label" for="newPassword">New Password</label>
|
||||
<label class="label" for="newPassword">{{ $t('user.settings.newPassword') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@keyup.enter="updatePassword"
|
||||
class="input"
|
||||
id="newPassword"
|
||||
placeholder="The new password..."
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
type="password"
|
||||
v-model="passwordUpdate.newPassword"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="newPasswordConfirm">New Password Confirmation</label>
|
||||
<label class="label" for="newPasswordConfirm">{{ $t('user.settings.newPasswordConfirm') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@keyup.enter="updatePassword"
|
||||
class="input"
|
||||
id="newPasswordConfirm"
|
||||
placeholder="Confirm your new password..."
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
type="password"
|
||||
v-model="passwordConfirm"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="currentPassword">Current Password</label>
|
||||
<label class="label" for="currentPassword">{{ $t('user.settings.currentPassword') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@keyup.enter="updatePassword"
|
||||
class="input"
|
||||
id="currentPassword"
|
||||
placeholder="Your current password"
|
||||
:placeholder="$t('user.settings.currentPasswordPlaceholder')"
|
||||
type="password"
|
||||
v-model="passwordUpdate.oldPassword"/>
|
||||
</div>
|
||||
@ -117,33 +129,33 @@
|
||||
:loading="passwordUpdateService.loading"
|
||||
@click="updatePassword()"
|
||||
class="is-fullwidth mt-4">
|
||||
Save
|
||||
{{ $t('misc.save') }}
|
||||
</x-button>
|
||||
</card>
|
||||
|
||||
<!-- Update E-Mail -->
|
||||
<card title="Update Your E-Mail Address">
|
||||
<card :title="$t('user.settings.updateEmailTitle')">
|
||||
<form @submit.prevent="updateEmail()">
|
||||
<div class="field">
|
||||
<label class="label" for="newEmail">New Email Address</label>
|
||||
<label class="label" for="newEmail">{{ $t('user.settings.updateEmailNew') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@keyup.enter="updateEmail"
|
||||
class="input"
|
||||
id="newEmail"
|
||||
placeholder="The new email address..."
|
||||
:placeholder="$t('user.auth.emailPlaceholder')"
|
||||
type="email"
|
||||
v-model="emailUpdate.newEmail"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="currentPasswordEmail">Current Password</label>
|
||||
<label class="label" for="currentPasswordEmail">{{ $t('user.settings.currentPassword') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@keyup.enter="updateEmail"
|
||||
class="input"
|
||||
id="currentPasswordEmail"
|
||||
placeholder="Your current password"
|
||||
:placeholder="$t('user.settings.currentPasswordPlaceholder')"
|
||||
type="password"
|
||||
v-model="emailUpdate.password"/>
|
||||
</div>
|
||||
@ -154,82 +166,83 @@
|
||||
:loading="emailUpdateService.loading"
|
||||
@click="updateEmail()"
|
||||
class="is-fullwidth mt-4">
|
||||
Save
|
||||
{{ $t('misc.save') }}
|
||||
</x-button>
|
||||
</card>
|
||||
|
||||
<!-- TOTP -->
|
||||
<card title="Two Factor Authentication" v-if="totpEnabled">
|
||||
<card :title="$t('user.settings.totp.title')" v-if="totpEnabled">
|
||||
<x-button
|
||||
:loading="totpService.loading"
|
||||
@click="totpEnroll()"
|
||||
v-if="!totpEnrolled && totp.secret === ''">
|
||||
Enroll
|
||||
{{ $t('user.settings.totp.enroll') }}
|
||||
</x-button>
|
||||
<template v-else-if="totp.secret !== '' && !totp.enabled">
|
||||
<p>
|
||||
To finish your setup, use this secret in your totp app (Google Authenticator or similar):
|
||||
{{ $t('user.settings.totp.finishSetupPart1') }}
|
||||
<strong>{{ totp.secret }}</strong><br/>
|
||||
After that, enter a code from your app below.
|
||||
{{ $t('user.settings.totp.finishSetupPart2') }}
|
||||
</p>
|
||||
<p>
|
||||
Alternatively you can scan this QR code:<br/>
|
||||
{{ $t('user.settings.totp.scanQR') }}<br/>
|
||||
<img :src="totpQR" alt=""/>
|
||||
</p>
|
||||
<div class="field">
|
||||
<label class="label" for="totpConfirmPasscode">Passcode</label>
|
||||
<label class="label" for="totpConfirmPasscode">{{ $t('user.settings.totp.passcode') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@keyup.enter="totpConfirm()"
|
||||
class="input"
|
||||
id="totpConfirmPasscode"
|
||||
placeholder="A code generated by your totp application"
|
||||
:placeholder="$t('user.settings.totp.passcodePlaceholder')"
|
||||
type="text"
|
||||
v-model="totpConfirmPasscode"/>
|
||||
</div>
|
||||
</div>
|
||||
<x-button @click="totpConfirm()">Confirm</x-button>
|
||||
<x-button @click="totpConfirm()">{{ $t('misc.confirm') }}</x-button>
|
||||
</template>
|
||||
<template v-else-if="totp.secret !== '' && totp.enabled">
|
||||
<p>
|
||||
You've sucessfully set up two factor authentication!
|
||||
{{ $t('user.settings.totp.setupSuccess') }}
|
||||
</p>
|
||||
<p v-if="!totpDisableForm">
|
||||
<x-button @click="totpDisableForm = true" class="is-danger">Disable</x-button>
|
||||
<x-button @click="totpDisableForm = true" class="is-danger">{{ $t('misc.disable') }}</x-button>
|
||||
</p>
|
||||
<div v-if="totpDisableForm">
|
||||
<div class="field">
|
||||
<label class="label" for="currentPassword">Please Enter Your Password</label>
|
||||
<label class="label" for="currentPassword">{{ $t('user.settings.totp.enterPassword') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
@keyup.enter="totpDisable"
|
||||
class="input"
|
||||
id="currentPassword"
|
||||
placeholder="Your current password"
|
||||
:placeholder="$t('user.settings.currentPasswordPlaceholder')"
|
||||
type="password"
|
||||
v-focus
|
||||
v-model="totpDisablePassword"/>
|
||||
</div>
|
||||
</div>
|
||||
<x-button @click="totpDisable()" class="is-danger">Disable two factor authentication</x-button>
|
||||
<x-button @click="totpDisable()" class="is-danger">
|
||||
{{ $t('user.settings.totp.disable') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</template>
|
||||
</card>
|
||||
|
||||
<!-- Migration -->
|
||||
<card title="Migrate from other services to Vikunja" v-if="migratorsEnabled">
|
||||
<card :title="$t('migrate.title')" v-if="migratorsEnabled">
|
||||
<x-button
|
||||
:to="{name: 'migrate.start'}"
|
||||
>
|
||||
Import your data into Vikunja
|
||||
{{ $t('migrate.import') }}
|
||||
</x-button>
|
||||
</card>
|
||||
|
||||
<!-- Caldav -->
|
||||
<card v-if="caldavEnabled" title="Caldav">
|
||||
<card v-if="caldavEnabled" :title="$t('user.settings.caldav.title')">
|
||||
<p>
|
||||
You can connect Vikunja to caldav clients to view and manage all tasks from different clients.
|
||||
Enter this url into your client:
|
||||
{{ $t('user.settings.caldav.howTo') }}
|
||||
</p>
|
||||
<div class="field has-addons no-input-mobile">
|
||||
<div class="control is-expanded">
|
||||
@ -239,14 +252,14 @@
|
||||
<x-button
|
||||
@click="copy(caldavUrl)"
|
||||
:shadow="false"
|
||||
v-tooltip="'Copy to clipboard'"
|
||||
v-tooltip="$t('misc.copy')"
|
||||
icon="paste"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<a href="https://vikunja.io/docs/caldav/" target="_blank">
|
||||
More information about caldav in Vikunja
|
||||
{{ $t('user.settings.caldav.more') }}
|
||||
</a>
|
||||
</p>
|
||||
</card>
|
||||
@ -263,6 +276,7 @@ import TotpService from '../../services/totp'
|
||||
import UserSettingsService from '../../services/userSettings'
|
||||
import UserSettingsModel from '../../models/userSettings'
|
||||
import {playSoundWhenDoneKey} from '@/helpers/playPop'
|
||||
import {availableLanguages, saveLanguage, getCurrentLanguage} from '@/i18n/setup'
|
||||
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
@ -288,6 +302,7 @@ export default {
|
||||
totpDisableForm: false,
|
||||
totpDisablePassword: '',
|
||||
playSoundWhenDone: false,
|
||||
language: getCurrentLanguage(),
|
||||
|
||||
settings: UserSettingsModel,
|
||||
userSettingsService: UserSettingsService,
|
||||
@ -314,7 +329,7 @@ export default {
|
||||
this.totpStatus()
|
||||
},
|
||||
mounted() {
|
||||
this.setTitle('Settings')
|
||||
this.setTitle(this.$t('user.settings.title'))
|
||||
},
|
||||
computed: {
|
||||
caldavUrl() {
|
||||
@ -328,6 +343,9 @@ export default {
|
||||
|
||||
return `${apiBase}/dav/principals/${this.userInfo.username}/`
|
||||
},
|
||||
availableLanguages() {
|
||||
return availableLanguages
|
||||
},
|
||||
...mapState({
|
||||
totpEnabled: state => state.config.totpEnabled,
|
||||
migratorsEnabled: state => state.config.availableMigrators !== null && state.config.availableMigrators.length > 0,
|
||||
@ -338,20 +356,20 @@ export default {
|
||||
methods: {
|
||||
updatePassword() {
|
||||
if (this.passwordConfirm !== this.passwordUpdate.newPassword) {
|
||||
this.error({message: 'The new password and its confirmation don\'t match.'})
|
||||
this.error({message: this.$t('user.settings.passwordsDontMatch')})
|
||||
return
|
||||
}
|
||||
|
||||
this.passwordUpdateService.update(this.passwordUpdate)
|
||||
.then(() => {
|
||||
this.success({message: 'The password was successfully updated.'})
|
||||
this.success({message: this.$t('user.settings.passwordUpdateSuccess')})
|
||||
})
|
||||
.catch(e => this.error(e))
|
||||
},
|
||||
updateEmail() {
|
||||
this.emailUpdateService.update(this.emailUpdate)
|
||||
.then(() => {
|
||||
this.success({message: 'Your email address was successfully updated. We\'ve sent you a link to confirm it.'})
|
||||
this.success({message: this.$t('user.settings.updateEmailSuccess')})
|
||||
})
|
||||
.catch(e => this.error(e))
|
||||
},
|
||||
@ -394,7 +412,7 @@ export default {
|
||||
this.totpService.enable({passcode: this.totpConfirmPasscode})
|
||||
.then(() => {
|
||||
this.$set(this.totp, 'enabled', true)
|
||||
this.success({message: 'You\'ve successfully confirmed your totp setup and can use it from now on!'})
|
||||
this.success({message: this.$t('user.settings.totp.confirmSuccess')})
|
||||
})
|
||||
.catch(e => this.error(e))
|
||||
},
|
||||
@ -403,17 +421,18 @@ export default {
|
||||
.then(() => {
|
||||
this.totpEnrolled = false
|
||||
this.$set(this, 'totp', new TotpModel())
|
||||
this.success({message: 'Two factor authentication was sucessfully disabled.'})
|
||||
this.success({message: this.$t('user.settings.totp.disableSuccess')})
|
||||
})
|
||||
.catch(e => this.error(e))
|
||||
},
|
||||
updateSettings() {
|
||||
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone)
|
||||
saveLanguage(this.language)
|
||||
|
||||
this.userSettingsService.update(this.settings)
|
||||
.then(() => {
|
||||
this.$store.commit('auth/setUserSettings', this.settings)
|
||||
this.success({message: 'The name was successfully changed.'})
|
||||
this.success({message: this.$t('user.settings.general.savedSuccess')})
|
||||
})
|
||||
.catch(e => this.error(e))
|
||||
},
|
||||
|
Reference in New Issue
Block a user