Add default list setting & creating tasks from home (#520)
Co-authored-by: sytone <github@sytone.com> Co-authored-by: Sytone <github@sytone.com> Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/520 Reviewed-by: konrad <konrad@kola-entertainments.de> Co-authored-by: sytone <kolaente@sytone.com> Co-committed-by: sytone <kolaente@sytone.com>
This commit is contained in:
parent
bad5e3d0ec
commit
306a926c66
37 changed files with 342 additions and 163 deletions
|
@ -95,7 +95,7 @@ steps:
|
||||||
CYPRESS_TEST_SECRET: averyLongSecretToSe33dtheDB
|
CYPRESS_TEST_SECRET: averyLongSecretToSe33dtheDB
|
||||||
YARN_CACHE_FOLDER: .cache/yarn/
|
YARN_CACHE_FOLDER: .cache/yarn/
|
||||||
CYPRESS_CACHE_FOLDER: .cache/cypress/
|
CYPRESS_CACHE_FOLDER: .cache/cypress/
|
||||||
CYPRESS_DEFAULT_COMMAND_TIMEOUT: 20000
|
CYPRESS_DEFAULT_COMMAND_TIMEOUT: 60000
|
||||||
commands:
|
commands:
|
||||||
- sed -i 's/localhost/api/g' public/index.html
|
- sed -i 's/localhost/api/g' public/index.html
|
||||||
- yarn serve & npx wait-on http://localhost:8080
|
- yarn serve & npx wait-on http://localhost:8080
|
||||||
|
|
22
.editorconfig
Normal file
22
.editorconfig
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = tab
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
insert_final_newline = false
|
||||||
|
|
||||||
|
[*.vue]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.{yaml,yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
12
README.md
12
README.md
|
@ -20,21 +20,25 @@ If you find any security-related issues you don't want to disclose publicly, ple
|
||||||
There is a [docker image available](https://hub.docker.com/r/vikunja/api) with support for http/2 and aggressive caching enabled.
|
There is a [docker image available](https://hub.docker.com/r/vikunja/api) with support for http/2 and aggressive caching enabled.
|
||||||
|
|
||||||
## Project setup
|
## Project setup
|
||||||
```
|
|
||||||
|
```shell
|
||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
### Compiles and hot-reloads for development
|
||||||
```
|
|
||||||
|
```shell
|
||||||
yarn run serve
|
yarn run serve
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compiles and minifies for production
|
### Compiles and minifies for production
|
||||||
```
|
|
||||||
|
```shell
|
||||||
yarn run build
|
yarn run build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Lints and fixes files
|
### Lints and fixes files
|
||||||
```
|
|
||||||
|
```shell
|
||||||
yarn run lint
|
yarn run lint
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: [
|
||||||
'@vue/app'
|
'@vue/app',
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ describe('User Settings', () => {
|
||||||
.contains('Save')
|
.contains('Save')
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
cy.wait(3000) // Wait for the request to finish
|
|
||||||
cy.get('.global-notification')
|
cy.get('.global-notification')
|
||||||
.should('contain', 'Success')
|
.should('contain', 'Success')
|
||||||
cy.get('.navbar .user .username')
|
cy.get('.navbar .user .username')
|
||||||
|
|
19
package.json
19
package.json
|
@ -69,7 +69,24 @@
|
||||||
"plugin:vue/essential",
|
"plugin:vue/essential",
|
||||||
"eslint:recommended"
|
"eslint:recommended"
|
||||||
],
|
],
|
||||||
"rules": {},
|
"rules": {
|
||||||
|
"vue/html-quotes": [
|
||||||
|
"error",
|
||||||
|
"double"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"comma-dangle": [
|
||||||
|
"error",
|
||||||
|
"always-multiline"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
]
|
||||||
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"parser": "babel-eslint"
|
"parser": "babel-eslint"
|
||||||
},
|
},
|
||||||
|
|
|
@ -55,7 +55,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
showIconOnly() {
|
showIconOnly() {
|
||||||
return this.icon !== '' && typeof this.$slots.default === 'undefined'
|
return this.icon !== '' && typeof this.$slots.default === 'undefined'
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
click(e) {
|
click(e) {
|
||||||
|
|
|
@ -137,18 +137,18 @@ export default {
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string'
|
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
|
||||||
},
|
},
|
||||||
chooseDateLabel: {
|
chooseDateLabel: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
return this.$t('input.datepicker.chooseDate')
|
return this.$t('input.datepicker.chooseDate')
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setDateValue(this.value)
|
this.setDateValue(this.value)
|
||||||
|
|
|
@ -366,7 +366,7 @@ export default {
|
||||||
link: (href, title, text) => {
|
link: (href, title, text) => {
|
||||||
const isLocal = href.startsWith(`${location.protocol}//${location.hostname}`)
|
const isLocal = href.startsWith(`${location.protocol}//${location.hostname}`)
|
||||||
const html = linkRenderer.call(renderer, href, title, text)
|
const html = linkRenderer.call(renderer, href, title, text)
|
||||||
return isLocal ? html : html.replace(/^<a /, `<a target="_blank" rel="noreferrer noopener nofollow" `)
|
return isLocal ? html : html.replace(/^<a /, '<a target="_blank" rel="noreferrer noopener nofollow" ')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
highlight: function (code, language) {
|
highlight: function (code, language) {
|
||||||
|
|
|
@ -108,21 +108,21 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default() {
|
default() {
|
||||||
return false
|
return false
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// The placeholder of the search input
|
// The placeholder of the search input
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
return ''
|
return ''
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// The search results where the @search listener needs to put the results into
|
// The search results where the @search listener needs to put the results into
|
||||||
searchResults: {
|
searchResults: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default() {
|
default() {
|
||||||
return []
|
return []
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// The name of the property of the searched object to show the user.
|
// The name of the property of the searched object to show the user.
|
||||||
// If empty the component will show all raw data of an entry.
|
// If empty the component will show all raw data of an entry.
|
||||||
|
@ -130,13 +130,13 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
return ''
|
return ''
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// The object with the value, updated every time an entry is selected.
|
// The object with the value, updated every time an entry is selected.
|
||||||
value: {
|
value: {
|
||||||
default() {
|
default() {
|
||||||
return null
|
return null
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// If true, will provide an "add this as a new value" entry which fires an @create event when clicking on it.
|
// If true, will provide an "add this as a new value" entry which fires an @create event when clicking on it.
|
||||||
creatable: {
|
creatable: {
|
||||||
|
@ -150,14 +150,14 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
return this.$t('input.multiselect.createPlaceholder')
|
return this.$t('input.multiselect.createPlaceholder')
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// The text shown next to an option.
|
// The text shown next to an option.
|
||||||
selectPlaceholder: {
|
selectPlaceholder: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
return this.$t('input.multiselect.selectPlaceholder')
|
return this.$t('input.multiselect.selectPlaceholder')
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// If true, allows for selecting multiple items. v-model will be an array with all selected values in that case.
|
// If true, allows for selecting multiple items. v-model will be an array with all selected values in that case.
|
||||||
multiple: {
|
multiple: {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: '',
|
default: '',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,6 +6,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'nothing'
|
name: 'nothing',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default {
|
||||||
keys: {
|
keys: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -57,7 +57,7 @@ export default {
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
return this.$t('task.subscription.subscribedThroughParent', {
|
return this.$t('task.subscription.subscribedThroughParent', {
|
||||||
entity: this.entity,
|
entity: this.entity,
|
||||||
parent: this.subscription.entity
|
parent: this.subscription.entity,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ export default {
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
this.error(e)
|
this.error(e)
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -481,7 +481,7 @@ export default {
|
||||||
reset() {
|
reset() {
|
||||||
this.query = ''
|
this.query = ''
|
||||||
this.selectedCmd = null
|
this.selectedCmd = null
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -235,11 +235,11 @@ export default {
|
||||||
this.searchLabel = 'username'
|
this.searchLabel = 'username'
|
||||||
|
|
||||||
if (this.type === 'list') {
|
if (this.type === 'list') {
|
||||||
this.typeString = `list`
|
this.typeString = 'list'
|
||||||
this.stuffService = new UserListService()
|
this.stuffService = new UserListService()
|
||||||
this.stuffModel = new UserListModel({listId: this.id})
|
this.stuffModel = new UserListModel({listId: this.id})
|
||||||
} else if (this.type === 'namespace') {
|
} else if (this.type === 'namespace') {
|
||||||
this.typeString = `namespace`
|
this.typeString = 'namespace'
|
||||||
this.stuffService = new UserNamespaceService()
|
this.stuffService = new UserNamespaceService()
|
||||||
this.stuffModel = new UserNamespaceModel({
|
this.stuffModel = new UserNamespaceModel({
|
||||||
namespaceId: this.id,
|
namespaceId: this.id,
|
||||||
|
@ -253,11 +253,11 @@ export default {
|
||||||
this.searchLabel = 'name'
|
this.searchLabel = 'name'
|
||||||
|
|
||||||
if (this.type === 'list') {
|
if (this.type === 'list') {
|
||||||
this.typeString = `list`
|
this.typeString = 'list'
|
||||||
this.stuffService = new TeamListService()
|
this.stuffService = new TeamListService()
|
||||||
this.stuffModel = new TeamListModel({listId: this.id})
|
this.stuffModel = new TeamListModel({listId: this.id})
|
||||||
} else if (this.type === 'namespace') {
|
} else if (this.type === 'namespace') {
|
||||||
this.typeString = `namespace`
|
this.typeString = 'namespace'
|
||||||
this.stuffService = new TeamNamespaceService()
|
this.stuffService = new TeamNamespaceService()
|
||||||
this.stuffModel = new TeamNamespaceModel({
|
this.stuffModel = new TeamNamespaceModel({
|
||||||
namespaceId: this.id,
|
namespaceId: this.id,
|
||||||
|
@ -278,7 +278,7 @@ export default {
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
this.$set(this, 'sharables', r)
|
this.$set(this, 'sharables', r)
|
||||||
r.forEach((s) =>
|
r.forEach((s) =>
|
||||||
this.$set(this.selectedRight, s.id, s.right)
|
this.$set(this.selectedRight, s.id, s.right),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
|
102
src/components/tasks/add-task.vue
Normal file
102
src/components/tasks/add-task.vue
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<template>
|
||||||
|
<div class="task-add">
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<p :class="{ 'is-loading': taskService.loading}" class="control has-icons-left is-expanded">
|
||||||
|
<input
|
||||||
|
:class="{ 'disabled': taskService.loading}"
|
||||||
|
@keyup.enter="addTask()"
|
||||||
|
class="input"
|
||||||
|
:placeholder="$t('list.list.addPlaceholder')"
|
||||||
|
type="text"
|
||||||
|
v-focus
|
||||||
|
v-model="newTaskTitle"
|
||||||
|
ref="newTaskInput"
|
||||||
|
@keyup="errorMessage = ''"
|
||||||
|
/>
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<icon icon="tasks"/>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<x-button
|
||||||
|
:disabled="newTaskTitle.length === 0"
|
||||||
|
@click="addTask()"
|
||||||
|
icon="plus"
|
||||||
|
>
|
||||||
|
{{ $t('list.list.add') }}
|
||||||
|
</x-button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p class="help is-danger" v-if="errorMessage !== ''">
|
||||||
|
{{ errorMessage }}
|
||||||
|
</p>
|
||||||
|
<quick-add-magic v-if="errorMessage === ''"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ListService from '../../services/list'
|
||||||
|
import TaskService from '../../services/task'
|
||||||
|
import LabelService from '../../services/label'
|
||||||
|
import LabelTaskService from '../../services/labelTask'
|
||||||
|
import createTask from '@/components/tasks/mixins/createTask'
|
||||||
|
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'add-task',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
newTaskTitle: '',
|
||||||
|
listService: ListService,
|
||||||
|
taskService: TaskService,
|
||||||
|
labelService: LabelService,
|
||||||
|
labelTaskService: LabelTaskService,
|
||||||
|
errorMessage: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mixins: [
|
||||||
|
createTask,
|
||||||
|
],
|
||||||
|
components: {
|
||||||
|
QuickAddMagic,
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.listService = new ListService()
|
||||||
|
this.taskService = new TaskService()
|
||||||
|
this.labelService = new LabelService()
|
||||||
|
this.labelTaskService = new LabelTaskService()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addTask() {
|
||||||
|
if (this.newTaskTitle === '') {
|
||||||
|
this.errorMessage = this.$t('list.create.addTitleRequired')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.errorMessage = ''
|
||||||
|
|
||||||
|
this.createNewTask(this.newTaskTitle, 0, this.$store.state.auth.settings.defaultListId)
|
||||||
|
.then(task => {
|
||||||
|
this.newTaskTitle = ''
|
||||||
|
this.$emit('taskAdded', task)
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
if (e === 'NO_LIST') {
|
||||||
|
this.errorMessage = this.$t('list.create.addListRequired')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.error(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.task-add {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
height: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -388,7 +388,7 @@ export default {
|
||||||
|
|
||||||
let startDate = new Date(this.startDate)
|
let startDate = new Date(this.startDate)
|
||||||
startDate.setDate(
|
startDate.setDate(
|
||||||
startDate.getDate() + newRect.left / this.dayWidth
|
startDate.getDate() + newRect.left / this.dayWidth,
|
||||||
)
|
)
|
||||||
startDate.setUTCHours(0)
|
startDate.setUTCHours(0)
|
||||||
startDate.setUTCMinutes(0)
|
startDate.setUTCMinutes(0)
|
||||||
|
@ -397,7 +397,7 @@ export default {
|
||||||
this.taskDragged.startDate = startDate
|
this.taskDragged.startDate = startDate
|
||||||
let endDate = new Date(startDate)
|
let endDate = new Date(startDate)
|
||||||
endDate.setDate(
|
endDate.setDate(
|
||||||
startDate.getDate() + newRect.width / this.dayWidth
|
startDate.getDate() + newRect.width / this.dayWidth,
|
||||||
)
|
)
|
||||||
this.taskDragged.startDate = startDate
|
this.taskDragged.startDate = startDate
|
||||||
this.taskDragged.endDate = endDate
|
this.taskDragged.endDate = endDate
|
||||||
|
@ -440,7 +440,7 @@ export default {
|
||||||
this.$set(
|
this.$set(
|
||||||
this.theTasks,
|
this.theTasks,
|
||||||
tt,
|
tt,
|
||||||
this.addGantAttributes(r)
|
this.addGantAttributes(r),
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,11 @@ export default {
|
||||||
const parsedTask = parseTaskText(newTaskTitle)
|
const parsedTask = parseTaskText(newTaskTitle)
|
||||||
const assignees = []
|
const assignees = []
|
||||||
|
|
||||||
|
// Uses the following ways to get the list id of the new task:
|
||||||
|
// 1. If specified in quick add magic, look in store if it exists and use it if it does
|
||||||
|
// 2. Else check if a list was passed as parameter
|
||||||
|
// 3. Otherwise use the id from the route parameter
|
||||||
|
// 4. If none of the above worked, reject the promise with an error.
|
||||||
let listId = null
|
let listId = null
|
||||||
if (parsedTask.list !== null) {
|
if (parsedTask.list !== null) {
|
||||||
const list = this.$store.getters['lists/findListByExactname'](parsedTask.list)
|
const list = this.$store.getters['lists/findListByExactname'](parsedTask.list)
|
||||||
|
@ -35,6 +40,10 @@ export default {
|
||||||
listId = lId !== 0 ? lId : this.$route.params.listId
|
listId = lId !== 0 ? lId : this.$route.params.listId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof listId === 'undefined' || listId === 0) {
|
||||||
|
return Promise.reject('NO_LIST')
|
||||||
|
}
|
||||||
|
|
||||||
// Separate closure because we need to wait for the results of the user search if users were entered in the
|
// Separate closure because we need to wait for the results of the user search if users were entered in the
|
||||||
// task create request. Because _that_ happens in a promise, we'll need something to call when it resolves.
|
// task create request. Because _that_ happens in a promise, we'll need something to call when it resolves.
|
||||||
const createTask = () => {
|
const createTask = () => {
|
||||||
|
@ -83,7 +92,7 @@ export default {
|
||||||
.then(res => {
|
.then(res => {
|
||||||
return addLabelToTask(res)
|
return addLabelToTask(res)
|
||||||
})
|
})
|
||||||
.catch(e => Promise.reject(e))
|
.catch(e => Promise.reject(e)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -110,7 +119,7 @@ export default {
|
||||||
assignees.push(user)
|
assignees.push(user)
|
||||||
}
|
}
|
||||||
return Promise.resolve(users)
|
return Promise.resolve(users)
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,7 @@ export default {
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
this.$store.commit(
|
this.$store.commit(
|
||||||
'attachments/removeById',
|
'attachments/removeById',
|
||||||
this.attachmentToDelete.id
|
this.attachmentToDelete.id,
|
||||||
)
|
)
|
||||||
this.success(r)
|
this.success(r)
|
||||||
})
|
})
|
||||||
|
|
|
@ -91,7 +91,7 @@ export default {
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.saving = false
|
this.saving = false
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -95,7 +95,7 @@ export default {
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.saving = false
|
this.saving = false
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -32,6 +32,11 @@ export default {
|
||||||
foundLists: [],
|
foundLists: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
Multiselect,
|
Multiselect,
|
||||||
},
|
},
|
||||||
|
@ -39,6 +44,14 @@ export default {
|
||||||
this.listSerivce = new ListService()
|
this.listSerivce = new ListService()
|
||||||
this.list = new ListModel()
|
this.list = new ListModel()
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
value(newVal) {
|
||||||
|
this.list = newVal
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.list = this.value
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
findLists(query) {
|
findLists(query) {
|
||||||
if (query === '') {
|
if (query === '') {
|
||||||
|
@ -58,7 +71,9 @@ export default {
|
||||||
this.$set(this, 'foundLists', [])
|
this.$set(this, 'foundLists', [])
|
||||||
},
|
},
|
||||||
select(list) {
|
select(list) {
|
||||||
|
this.list = list
|
||||||
this.$emit('selected', list)
|
this.$emit('selected', list)
|
||||||
|
this.$emit('input', list)
|
||||||
},
|
},
|
||||||
namespace(namespaceId) {
|
namespace(namespaceId) {
|
||||||
const namespace = this.$store.getters['namespaces/getNamespaceById'](namespaceId)
|
const namespace = this.$store.getters['namespaces/getNamespaceById'](namespaceId)
|
||||||
|
|
|
@ -135,7 +135,7 @@ export default {
|
||||||
showListColor: {
|
showListColor: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
theTask(newVal) {
|
theTask(newVal) {
|
||||||
|
@ -178,13 +178,13 @@ export default {
|
||||||
this.success({
|
this.success({
|
||||||
message: this.task.done ?
|
message: this.task.done ?
|
||||||
this.$t('task.doneSuccess') :
|
this.$t('task.doneSuccess') :
|
||||||
this.$t('task.undoneSuccess')
|
this.$t('task.undoneSuccess'),
|
||||||
}, [{
|
}, [{
|
||||||
title: 'Undo',
|
title: 'Undo',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.task.done = !this.task.done
|
this.task.done = !this.task.done
|
||||||
this.markAsDone(!checked)
|
this.markAsDone(!checked)
|
||||||
}
|
},
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
|
|
|
@ -12,7 +12,7 @@ export const createDateFromString = dateString => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dateString.includes('-')) {
|
if (dateString.includes('-')) {
|
||||||
dateString = dateString.replace(/-/g, "/")
|
dateString = dateString.replace(/-/g, '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Date(dateString)
|
return new Date(dateString)
|
||||||
|
|
|
@ -65,7 +65,8 @@
|
||||||
"weekStart": "Week starts on",
|
"weekStart": "Week starts on",
|
||||||
"weekStartSunday": "Sunday",
|
"weekStartSunday": "Sunday",
|
||||||
"weekStartMonday": "Monday",
|
"weekStartMonday": "Monday",
|
||||||
"language": "Language"
|
"language": "Language",
|
||||||
|
"defaultList": "Default List"
|
||||||
},
|
},
|
||||||
"totp": {
|
"totp": {
|
||||||
"title": "Two Factor Authentication",
|
"title": "Two Factor Authentication",
|
||||||
|
@ -109,7 +110,8 @@
|
||||||
"header": "Create a new list",
|
"header": "Create a new list",
|
||||||
"titlePlaceholder": "The list's title goes here…",
|
"titlePlaceholder": "The list's title goes here…",
|
||||||
"addTitleRequired": "Please specify a title.",
|
"addTitleRequired": "Please specify a title.",
|
||||||
"createdSuccess": "The list was successfully created."
|
"createdSuccess": "The list was successfully created.",
|
||||||
|
"addListRequired": "Please specify a list or set a default list in the settings."
|
||||||
},
|
},
|
||||||
"archive": {
|
"archive": {
|
||||||
"title": "Archive \"{list}\"",
|
"title": "Archive \"{list}\"",
|
||||||
|
@ -204,7 +206,6 @@
|
||||||
"title": "List",
|
"title": "List",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"addPlaceholder": "Add a new task…",
|
"addPlaceholder": "Add a new task…",
|
||||||
"addTitleRequired": "Please specify a title.",
|
|
||||||
"empty": "This list is currently empty.",
|
"empty": "This list is currently empty.",
|
||||||
"newTaskCta": "Create a new task.",
|
"newTaskCta": "Create a new task.",
|
||||||
"editTask": "Edit Task"
|
"editTask": "Edit Task"
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default class UserSettingsModel extends AbstractModel {
|
||||||
discoverableByName: false,
|
discoverableByName: false,
|
||||||
discoverableByEmail: false,
|
discoverableByEmail: false,
|
||||||
overdueTasksRemindersEnabled: true,
|
overdueTasksRemindersEnabled: true,
|
||||||
|
defaultListId: undefined,
|
||||||
weekStart: 0,
|
weekStart: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ export default new Router({
|
||||||
name: 'list.create',
|
name: 'list.create',
|
||||||
components: {
|
components: {
|
||||||
popup: NewListComponent,
|
popup: NewListComponent,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/namespaces/:id/settings/edit',
|
path: '/namespaces/:id/settings/edit',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {HTTPFactory} from '@/http-common'
|
import { HTTPFactory } from '@/http-common'
|
||||||
import {ERROR_MESSAGE, LOADING} from '../mutation-types'
|
import { ERROR_MESSAGE, LOADING } from '../mutation-types'
|
||||||
import UserModel from '../../models/user'
|
import UserModel from '../../models/user'
|
||||||
import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth'
|
import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth'
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ export default {
|
||||||
// Logs a user in with a set of credentials.
|
// Logs a user in with a set of credentials.
|
||||||
login(ctx, credentials) {
|
login(ctx, credentials) {
|
||||||
const HTTP = HTTPFactory()
|
const HTTP = HTTPFactory()
|
||||||
ctx.commit(LOADING, true, {root: true})
|
ctx.commit(LOADING, true, { root: true })
|
||||||
|
|
||||||
// Delete an eventually preexisting old token
|
// Delete an eventually preexisting old token
|
||||||
removeToken()
|
removeToken()
|
||||||
|
@ -93,7 +93,7 @@ export default {
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
ctx.commit(LOADING, false, {root: true})
|
ctx.commit(LOADING, false, { root: true })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// Registers a new user and logs them in.
|
// Registers a new user and logs them in.
|
||||||
|
@ -110,18 +110,18 @@ export default {
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
if (e.response && e.response.data && e.response.data.message) {
|
if (e.response && e.response.data && e.response.data.message) {
|
||||||
ctx.commit(ERROR_MESSAGE, e.response.data.message, {root: true})
|
ctx.commit(ERROR_MESSAGE, e.response.data.message, { root: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
ctx.commit(LOADING, false, {root: true})
|
ctx.commit(LOADING, false, { root: true })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openIdAuth(ctx, {provider, code}) {
|
openIdAuth(ctx, { provider, code }) {
|
||||||
const HTTP = HTTPFactory()
|
const HTTP = HTTPFactory()
|
||||||
ctx.commit(LOADING, true, {root: true})
|
ctx.commit(LOADING, true, { root: true })
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
code: code,
|
code: code,
|
||||||
|
@ -143,10 +143,10 @@ export default {
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
ctx.commit(LOADING, false, {root: true})
|
ctx.commit(LOADING, false, { root: true })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
linkShareAuth(ctx, {hash, password}) {
|
linkShareAuth(ctx, { hash, password }) {
|
||||||
const HTTP = HTTPFactory()
|
const HTTP = HTTPFactory()
|
||||||
return HTTP.post('/shares/' + hash + '/auth', {
|
return HTTP.post('/shares/' + hash + '/auth', {
|
||||||
password: password,
|
password: password,
|
||||||
|
|
|
@ -10,13 +10,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-add {
|
.list-view .task-add {
|
||||||
padding: 1rem 1rem 0;
|
padding: 1rem 1rem 0;
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
.button {
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-title {
|
.list-title {
|
||||||
|
|
|
@ -3,10 +3,15 @@
|
||||||
<h2>
|
<h2>
|
||||||
{{ $t(`home.welcome${welcome}`, {username: userInfo.name !== '' ? userInfo.name : userInfo.username}) }}!
|
{{ $t(`home.welcome${welcome}`, {username: userInfo.name !== '' ? userInfo.name : userInfo.username}) }}!
|
||||||
</h2>
|
</h2>
|
||||||
|
<add-task
|
||||||
|
:listId="defaultListId"
|
||||||
|
@taskAdded="updateTaskList"
|
||||||
|
class="is-max-width-desktop"
|
||||||
|
/>
|
||||||
<template v-if="!hasTasks">
|
<template v-if="!hasTasks">
|
||||||
<p>{{ $t('home.list.newText') }}</p>
|
<p>{{ $t('home.list.newText') }}</p>
|
||||||
<x-button
|
<x-button
|
||||||
:to="{name: 'list.create', params: { id: defaultNamespaceId }}"
|
:to="{ name: 'list.create', params: { id: defaultNamespaceId } }"
|
||||||
:shadow="false"
|
:shadow="false"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
v-if="defaultNamespaceId > 0"
|
v-if="defaultNamespaceId > 0"
|
||||||
|
@ -34,7 +39,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ShowTasks :show-all="true" v-if="hasLists"/>
|
<ShowTasks :show-all="true" v-if="hasLists" :key="showTasksKey"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -43,18 +48,21 @@ import {mapState} from 'vuex'
|
||||||
import ShowTasks from './tasks/ShowTasks'
|
import ShowTasks from './tasks/ShowTasks'
|
||||||
import {getHistory} from '@/modules/listHistory'
|
import {getHistory} from '@/modules/listHistory'
|
||||||
import ListCard from '@/components/list/partials/list-card'
|
import ListCard from '@/components/list/partials/list-card'
|
||||||
|
import AddTask from '../components/tasks/add-task'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
components: {
|
components: {
|
||||||
ListCard,
|
ListCard,
|
||||||
ShowTasks,
|
ShowTasks,
|
||||||
|
AddTask,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
currentDate: new Date(),
|
currentDate: new Date(),
|
||||||
tasks: [],
|
tasks: [],
|
||||||
|
showTasksKey: 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -86,10 +94,13 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
...mapState({
|
...mapState({
|
||||||
migratorsEnabled: state => state.config.availableMigrators !== null && state.config.availableMigrators.length > 0,
|
migratorsEnabled: state =>
|
||||||
|
state.config.availableMigrators !== null &&
|
||||||
|
state.config.availableMigrators.length > 0,
|
||||||
authenticated: state => state.auth.authenticated,
|
authenticated: state => state.auth.authenticated,
|
||||||
userInfo: state => state.auth.info,
|
userInfo: state => state.auth.info,
|
||||||
hasTasks: state => state.hasTasks,
|
hasTasks: state => state.hasTasks,
|
||||||
|
defaultListId: state => state.auth.defaultListId,
|
||||||
defaultNamespaceId: state => {
|
defaultNamespaceId: state => {
|
||||||
if (state.namespaces.namespaces.length === 0) {
|
if (state.namespaces.namespaces.length === 0) {
|
||||||
return 0
|
return 0
|
||||||
|
@ -105,6 +116,13 @@ export default {
|
||||||
return state.namespaces.namespaces[0].lists.length > 0
|
return state.namespaces.namespaces[0].lists.length > 0
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}
|
},
|
||||||
|
methods: {
|
||||||
|
// This is to reload the tasks list after adding a new task through the global task add.
|
||||||
|
// FIXME: Should use vuex (somehow?)
|
||||||
|
updateTaskList() {
|
||||||
|
this.showTasksKey++
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="{ 'is-loading': taskCollectionService.loading}"
|
:class="{ 'is-loading': taskCollectionService.loading }"
|
||||||
class="loader-container is-max-width-desktop list-view">
|
class="loader-container is-max-width-desktop list-view"
|
||||||
<div class="filter-container" v-if="list.isSavedFilter && !list.isSavedFilter()">
|
>
|
||||||
|
<div
|
||||||
|
class="filter-container"
|
||||||
|
v-if="list.isSavedFilter && !list.isSavedFilter()"
|
||||||
|
>
|
||||||
<div class="items">
|
<div class="items">
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<div :class="{ 'hidden': !showTaskSearch }" class="field has-addons">
|
<div :class="{ hidden: !showTaskSearch }" class="field has-addons">
|
||||||
<div class="control has-icons-left has-icons-right">
|
<div class="control has-icons-left has-icons-right">
|
||||||
<input
|
<input
|
||||||
@blur="hideSearchBar()"
|
@blur="hideSearchBar()"
|
||||||
|
@ -14,9 +18,10 @@
|
||||||
:placeholder="$t('misc.search')"
|
:placeholder="$t('misc.search')"
|
||||||
type="text"
|
type="text"
|
||||||
v-focus
|
v-focus
|
||||||
v-model="searchTerm"/>
|
v-model="searchTerm"
|
||||||
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<icon icon="search"/>
|
<icon icon="search" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
@ -52,48 +57,30 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<card :padding="false" :has-content="false" class="has-overflow">
|
<card :padding="false" :has-content="false" class="has-overflow">
|
||||||
<div class="field task-add" v-if="!list.isArchived && canWrite && list.id > 0">
|
<template
|
||||||
<div class="field is-grouped">
|
v-if="!list.isArchived && canWrite && list.id > 0"
|
||||||
<p :class="{ 'is-loading': taskService.loading}" class="control has-icons-left is-expanded">
|
>
|
||||||
<input
|
<add-task
|
||||||
:class="{ 'disabled': taskService.loading}"
|
@taskAdded="updateTaskList"
|
||||||
@keyup.enter="addTask()"
|
ref="newTaskInput"
|
||||||
class="input"
|
/>
|
||||||
:placeholder="$t('list.list.addPlaceholder')"
|
</template>
|
||||||
type="text"
|
|
||||||
v-focus
|
|
||||||
v-model="newTaskText"
|
|
||||||
ref="newTaskInput"
|
|
||||||
/>
|
|
||||||
<span class="icon is-small is-left">
|
|
||||||
<icon icon="tasks"/>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p class="control">
|
|
||||||
<x-button
|
|
||||||
:disabled="newTaskText.length === 0"
|
|
||||||
@click="addTask()"
|
|
||||||
icon="plus"
|
|
||||||
>
|
|
||||||
{{ $t('list.list.add') }}
|
|
||||||
</x-button>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p class="help is-danger" v-if="showError && newTaskText === ''">
|
|
||||||
{{ $t('list.list.addTitleRequired') }}
|
|
||||||
</p>
|
|
||||||
<quick-add-magic v-if="!showError"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nothing v-if="ctaVisible && tasks.length === 0 && !taskCollectionService.loading">
|
<nothing v-if="ctaVisible && tasks.length === 0 && !taskCollectionService.loading">
|
||||||
|
|
||||||
{{ $t('list.list.empty') }}
|
{{ $t('list.list.empty') }}
|
||||||
<a @click="$refs.newTaskInput.focus()">
|
<a @click="focusNewTaskInput()">
|
||||||
{{ $t('list.list.newTaskCta') }}
|
{{ $t('list.list.newTaskCta') }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</nothing>
|
</nothing>
|
||||||
|
|
||||||
<div class="tasks-container">
|
<div class="tasks-container">
|
||||||
<div :class="{'short': isTaskEdit}" class="tasks mt-0" v-if="tasks && tasks.length > 0">
|
<div
|
||||||
|
:class="{ short: isTaskEdit }"
|
||||||
|
class="tasks mt-0"
|
||||||
|
v-if="tasks && tasks.length > 0"
|
||||||
|
>
|
||||||
<single-task-in-list
|
<single-task-in-list
|
||||||
:show-list-color="false"
|
:show-list-color="false"
|
||||||
:disabled="!canWrite"
|
:disabled="!canWrite"
|
||||||
|
@ -103,8 +90,12 @@
|
||||||
task-detail-route="task.detail"
|
task-detail-route="task.detail"
|
||||||
v-for="t in tasks"
|
v-for="t in tasks"
|
||||||
>
|
>
|
||||||
<div @click="editTask(t.id)" class="icon settings" v-if="!list.isArchived && canWrite">
|
<div
|
||||||
<icon icon="pencil-alt"/>
|
@click="editTask(t.id)"
|
||||||
|
class="icon settings"
|
||||||
|
v-if="!list.isArchived && canWrite"
|
||||||
|
>
|
||||||
|
<icon icon="pencil-alt" />
|
||||||
</div>
|
</div>
|
||||||
</single-task-in-list>
|
</single-task-in-list>
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,7 +112,8 @@
|
||||||
aria-label="pagination"
|
aria-label="pagination"
|
||||||
class="pagination is-centered p-4"
|
class="pagination is-centered p-4"
|
||||||
role="navigation"
|
role="navigation"
|
||||||
v-if="taskCollectionService.totalPages > 1">
|
v-if="taskCollectionService.totalPages > 1"
|
||||||
|
>
|
||||||
<router-link
|
<router-link
|
||||||
:disabled="currentPage === 1"
|
:disabled="currentPage === 1"
|
||||||
:to="getRouteForPagination(currentPage - 1)"
|
:to="getRouteForPagination(currentPage - 1)"
|
||||||
|
@ -138,13 +130,16 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
<ul class="pagination-list">
|
<ul class="pagination-list">
|
||||||
<template v-for="(p, i) in pages">
|
<template v-for="(p, i) in pages">
|
||||||
<li :key="'page'+i" v-if="p.isEllipsis"><span class="pagination-ellipsis">…</span></li>
|
<li :key="'page' + i" v-if="p.isEllipsis">
|
||||||
<li :key="'page'+i" v-else>
|
<span class="pagination-ellipsis">…</span>
|
||||||
|
</li>
|
||||||
|
<li :key="'page' + i" v-else>
|
||||||
<router-link
|
<router-link
|
||||||
:aria-label="'Goto page ' + p.number"
|
:aria-label="'Goto page ' + p.number"
|
||||||
:class="{'is-current': p.number === currentPage}"
|
:class="{ 'is-current': p.number === currentPage }"
|
||||||
:to="getRouteForPagination(p.number)"
|
:to="getRouteForPagination(p.number)"
|
||||||
class="pagination-link">
|
class="pagination-link"
|
||||||
|
>
|
||||||
{{ p.number }}
|
{{ p.number }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
@ -155,10 +150,8 @@
|
||||||
|
|
||||||
<!-- This router view is used to show the task popup while keeping the kanban board itself -->
|
<!-- This router view is used to show the task popup while keeping the kanban board itself -->
|
||||||
<transition name="modal">
|
<transition name="modal">
|
||||||
<router-view/>
|
<router-view />
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -167,15 +160,16 @@ import TaskService from '../../../services/task'
|
||||||
import TaskModel from '../../../models/task'
|
import TaskModel from '../../../models/task'
|
||||||
|
|
||||||
import EditTask from '../../../components/tasks/edit-task'
|
import EditTask from '../../../components/tasks/edit-task'
|
||||||
|
import AddTask from '../../../components/tasks/add-task'
|
||||||
import SingleTaskInList from '../../../components/tasks/partials/singleTaskInList'
|
import SingleTaskInList from '../../../components/tasks/partials/singleTaskInList'
|
||||||
import taskList from '../../../components/tasks/mixins/taskList'
|
import taskList from '../../../components/tasks/mixins/taskList'
|
||||||
import {saveListView} from '@/helpers/saveListView'
|
import { saveListView } from '@/helpers/saveListView'
|
||||||
import Rights from '../../../models/rights.json'
|
import Rights from '../../../models/rights.json'
|
||||||
import {mapState} from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import FilterPopup from '@/components/list/partials/filter-popup'
|
import FilterPopup from '@/components/list/partials/filter-popup'
|
||||||
|
import { HAS_TASKS } from '@/store/mutation-types'
|
||||||
import Nothing from '@/components/misc/nothing'
|
import Nothing from '@/components/misc/nothing'
|
||||||
import createTask from '@/components/tasks/mixins/createTask'
|
import createTask from '@/components/tasks/mixins/createTask'
|
||||||
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'List',
|
name: 'List',
|
||||||
|
@ -184,8 +178,6 @@ export default {
|
||||||
taskService: TaskService,
|
taskService: TaskService,
|
||||||
isTaskEdit: false,
|
isTaskEdit: false,
|
||||||
taskEditTask: TaskModel,
|
taskEditTask: TaskModel,
|
||||||
newTaskText: '',
|
|
||||||
showError: false,
|
|
||||||
ctaVisible: false,
|
ctaVisible: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -194,11 +186,11 @@ export default {
|
||||||
createTask,
|
createTask,
|
||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
QuickAddMagic,
|
|
||||||
Nothing,
|
Nothing,
|
||||||
FilterPopup,
|
FilterPopup,
|
||||||
SingleTaskInList,
|
SingleTaskInList,
|
||||||
EditTask,
|
EditTask,
|
||||||
|
AddTask,
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.taskService = new TaskService()
|
this.taskService = new TaskService()
|
||||||
|
@ -212,7 +204,7 @@ export default {
|
||||||
list: state => state.currentList,
|
list: state => state.currentList,
|
||||||
}),
|
}),
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$nextTick(() => this.ctaVisible = true)
|
this.$nextTick(() => (this.ctaVisible = true))
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// This function initializes the tasks page and loads the first page of tasks
|
// This function initializes the tasks page and loads the first page of tasks
|
||||||
|
@ -221,22 +213,13 @@ export default {
|
||||||
this.isTaskEdit = false
|
this.isTaskEdit = false
|
||||||
this.loadTasks(page, search)
|
this.loadTasks(page, search)
|
||||||
},
|
},
|
||||||
addTask() {
|
focusNewTaskInput() {
|
||||||
if (this.newTaskText === '') {
|
this.$refs.newTaskInput.$refs.newTaskInput.focus()
|
||||||
this.showError = true
|
},
|
||||||
return
|
updateTaskList(task) {
|
||||||
}
|
this.tasks.push(task)
|
||||||
this.showError = false
|
this.sortTasks()
|
||||||
|
this.$store.commit(HAS_TASKS, true)
|
||||||
this.createNewTask(this.newTaskText)
|
|
||||||
.then(task => {
|
|
||||||
this.tasks.push(task)
|
|
||||||
this.sortTasks()
|
|
||||||
this.newTaskText = ''
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
this.error(e)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
editTask(id) {
|
editTask(id) {
|
||||||
// Find the selected task and set it to the current object
|
// Find the selected task and set it to the current object
|
||||||
|
|
|
@ -645,7 +645,7 @@ export default {
|
||||||
this.$refs[fieldName].$el.scrollIntoView({
|
this.$refs[fieldName].$el.scrollIntoView({
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
block: 'center',
|
block: 'center',
|
||||||
inline: 'nearest'
|
inline: 'nearest',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -312,7 +312,7 @@ export default {
|
||||||
this.success({
|
this.success({
|
||||||
message: member.admin ?
|
message: member.admin ?
|
||||||
this.$t('team.edit.madeAdmin') :
|
this.$t('team.edit.madeAdmin') :
|
||||||
this.$t('team.edit.madeMember')
|
this.$t('team.edit.madeMember'),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
|
|
@ -129,7 +129,7 @@ export default {
|
||||||
let emailVerifyToken = localStorage.getItem('emailConfirmToken')
|
let emailVerifyToken = localStorage.getItem('emailConfirmToken')
|
||||||
if (emailVerifyToken) {
|
if (emailVerifyToken) {
|
||||||
const cancel = this.setLoading()
|
const cancel = this.setLoading()
|
||||||
HTTP.post(`user/confirm`, {token: emailVerifyToken})
|
HTTP.post('user/confirm', {token: emailVerifyToken})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
localStorage.removeItem('emailConfirmToken')
|
localStorage.removeItem('emailConfirmToken')
|
||||||
this.confirmedEmailSuccess = true
|
this.confirmedEmailSuccess = true
|
||||||
|
|
|
@ -16,6 +16,12 @@
|
||||||
v-model="settings.name"/>
|
v-model="settings.name"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">
|
||||||
|
{{ $t('user.settings.general.defaultList') }}
|
||||||
|
</label>
|
||||||
|
<list-search v-model="defaultList"/>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input type="checkbox" v-model="settings.emailRemindersEnabled"/>
|
<input type="checkbox" v-model="settings.emailRemindersEnabled"/>
|
||||||
|
@ -282,6 +288,7 @@ import {mapState} from 'vuex'
|
||||||
|
|
||||||
import AvatarSettings from '../../components/user/avatar-settings'
|
import AvatarSettings from '../../components/user/avatar-settings'
|
||||||
import copy from 'copy-to-clipboard'
|
import copy from 'copy-to-clipboard'
|
||||||
|
import ListSearch from '@/components/tasks/partials/listSearch'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
|
@ -306,9 +313,12 @@ export default {
|
||||||
|
|
||||||
settings: UserSettingsModel,
|
settings: UserSettingsModel,
|
||||||
userSettingsService: UserSettingsService,
|
userSettingsService: UserSettingsService,
|
||||||
|
|
||||||
|
defaultList: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
ListSearch,
|
||||||
AvatarSettings,
|
AvatarSettings,
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
@ -326,6 +336,8 @@ export default {
|
||||||
|
|
||||||
this.playSoundWhenDone = localStorage.getItem(playSoundWhenDoneKey) === 'true' || localStorage.getItem(playSoundWhenDoneKey) === null
|
this.playSoundWhenDone = localStorage.getItem(playSoundWhenDoneKey) === 'true' || localStorage.getItem(playSoundWhenDoneKey) === null
|
||||||
|
|
||||||
|
this.defaultList = this.$store.getters['lists/getListById'](this.settings.defaultListId)
|
||||||
|
|
||||||
this.totpStatus()
|
this.totpStatus()
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -351,7 +363,7 @@ export default {
|
||||||
migratorsEnabled: state => state.config.availableMigrators !== null && state.config.availableMigrators.length > 0,
|
migratorsEnabled: state => state.config.availableMigrators !== null && state.config.availableMigrators.length > 0,
|
||||||
caldavEnabled: state => state.config.caldavEnabled,
|
caldavEnabled: state => state.config.caldavEnabled,
|
||||||
userInfo: state => state.auth.info,
|
userInfo: state => state.auth.info,
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updatePassword() {
|
updatePassword() {
|
||||||
|
@ -428,6 +440,7 @@ export default {
|
||||||
updateSettings() {
|
updateSettings() {
|
||||||
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone)
|
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone)
|
||||||
saveLanguage(this.language)
|
saveLanguage(this.language)
|
||||||
|
this.settings.defaultListId = this.defaultList ? this.defaultList.id : 0
|
||||||
|
|
||||||
this.userSettingsService.update(this.settings)
|
this.userSettingsService.update(this.settings)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
@ -20,22 +20,22 @@ module.exports = {
|
||||||
msTileImage: 'images/icons/msapplication-icon-144x144.png',
|
msTileImage: 'images/icons/msapplication-icon-144x144.png',
|
||||||
},
|
},
|
||||||
manifestOptions: {
|
manifestOptions: {
|
||||||
"icons": [
|
'icons': [
|
||||||
{
|
{
|
||||||
"src": "./images/icons/android-chrome-192x192.png",
|
'src': './images/icons/android-chrome-192x192.png',
|
||||||
"sizes": "192x192",
|
'sizes': '192x192',
|
||||||
"type": "image/png"
|
'type': 'image/png',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "./images/icons/android-chrome-512x512.png",
|
'src': './images/icons/android-chrome-512x512.png',
|
||||||
"sizes": "512x512",
|
'sizes': '512x512',
|
||||||
"type": "image/png"
|
'type': 'image/png',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "./images/icons/icon-maskable.png",
|
'src': './images/icons/icon-maskable.png',
|
||||||
"sizes": "1024x1024",
|
'sizes': '1024x1024',
|
||||||
"type": "image/png",
|
'type': 'image/png',
|
||||||
"purpose": "maskable"
|
'purpose': 'maskable',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
shortcuts: [
|
shortcuts: [
|
||||||
|
@ -62,8 +62,8 @@ module.exports = {
|
||||||
name: 'Teams Overview',
|
name: 'Teams Overview',
|
||||||
short_name: 'Teams',
|
short_name: 'Teams',
|
||||||
url: '/teams',
|
url: '/teams',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
Loading…
Reference in a new issue