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:
@ -55,7 +55,7 @@ export default {
|
||||
computed: {
|
||||
showIconOnly() {
|
||||
return this.icon !== '' && typeof this.$slots.default === 'undefined'
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
click(e) {
|
||||
|
@ -137,18 +137,18 @@ export default {
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string'
|
||||
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
|
||||
},
|
||||
chooseDateLabel: {
|
||||
type: String,
|
||||
default() {
|
||||
return this.$t('input.datepicker.chooseDate')
|
||||
}
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setDateValue(this.value)
|
||||
|
@ -366,7 +366,7 @@ export default {
|
||||
link: (href, title, text) => {
|
||||
const isLocal = href.startsWith(`${location.protocol}//${location.hostname}`)
|
||||
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) {
|
||||
|
@ -108,21 +108,21 @@ export default {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false
|
||||
}
|
||||
},
|
||||
},
|
||||
// The placeholder of the search input
|
||||
placeholder: {
|
||||
type: String,
|
||||
default() {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
},
|
||||
// The search results where the @search listener needs to put the results into
|
||||
searchResults: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
},
|
||||
// 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.
|
||||
@ -130,13 +130,13 @@ export default {
|
||||
type: String,
|
||||
default() {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
},
|
||||
// The object with the value, updated every time an entry is selected.
|
||||
value: {
|
||||
default() {
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
// If true, will provide an "add this as a new value" entry which fires an @create event when clicking on it.
|
||||
creatable: {
|
||||
@ -150,14 +150,14 @@ export default {
|
||||
type: String,
|
||||
default() {
|
||||
return this.$t('input.multiselect.createPlaceholder')
|
||||
}
|
||||
},
|
||||
},
|
||||
// The text shown next to an option.
|
||||
selectPlaceholder: {
|
||||
type: String,
|
||||
default() {
|
||||
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.
|
||||
multiple: {
|
||||
|
@ -22,7 +22,7 @@ export default {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -6,6 +6,6 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'nothing'
|
||||
name: 'nothing',
|
||||
}
|
||||
</script>
|
||||
|
@ -14,7 +14,7 @@ export default {
|
||||
keys: {
|
||||
type: Array,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -57,7 +57,7 @@ export default {
|
||||
if (this.disabled) {
|
||||
return this.$t('task.subscription.subscribedThroughParent', {
|
||||
entity: this.entity,
|
||||
parent: this.subscription.entity
|
||||
parent: this.subscription.entity,
|
||||
})
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ export default {
|
||||
.catch(e => {
|
||||
this.error(e)
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -481,7 +481,7 @@ export default {
|
||||
reset() {
|
||||
this.query = ''
|
||||
this.selectedCmd = null
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -235,11 +235,11 @@ export default {
|
||||
this.searchLabel = 'username'
|
||||
|
||||
if (this.type === 'list') {
|
||||
this.typeString = `list`
|
||||
this.typeString = 'list'
|
||||
this.stuffService = new UserListService()
|
||||
this.stuffModel = new UserListModel({listId: this.id})
|
||||
} else if (this.type === 'namespace') {
|
||||
this.typeString = `namespace`
|
||||
this.typeString = 'namespace'
|
||||
this.stuffService = new UserNamespaceService()
|
||||
this.stuffModel = new UserNamespaceModel({
|
||||
namespaceId: this.id,
|
||||
@ -253,11 +253,11 @@ export default {
|
||||
this.searchLabel = 'name'
|
||||
|
||||
if (this.type === 'list') {
|
||||
this.typeString = `list`
|
||||
this.typeString = 'list'
|
||||
this.stuffService = new TeamListService()
|
||||
this.stuffModel = new TeamListModel({listId: this.id})
|
||||
} else if (this.type === 'namespace') {
|
||||
this.typeString = `namespace`
|
||||
this.typeString = 'namespace'
|
||||
this.stuffService = new TeamNamespaceService()
|
||||
this.stuffModel = new TeamNamespaceModel({
|
||||
namespaceId: this.id,
|
||||
@ -278,7 +278,7 @@ export default {
|
||||
.then((r) => {
|
||||
this.$set(this, 'sharables', r)
|
||||
r.forEach((s) =>
|
||||
this.$set(this.selectedRight, s.id, s.right)
|
||||
this.$set(this.selectedRight, s.id, s.right),
|
||||
)
|
||||
})
|
||||
.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)
|
||||
startDate.setDate(
|
||||
startDate.getDate() + newRect.left / this.dayWidth
|
||||
startDate.getDate() + newRect.left / this.dayWidth,
|
||||
)
|
||||
startDate.setUTCHours(0)
|
||||
startDate.setUTCMinutes(0)
|
||||
@ -397,7 +397,7 @@ export default {
|
||||
this.taskDragged.startDate = startDate
|
||||
let endDate = new Date(startDate)
|
||||
endDate.setDate(
|
||||
startDate.getDate() + newRect.width / this.dayWidth
|
||||
startDate.getDate() + newRect.width / this.dayWidth,
|
||||
)
|
||||
this.taskDragged.startDate = startDate
|
||||
this.taskDragged.endDate = endDate
|
||||
@ -440,7 +440,7 @@ export default {
|
||||
this.$set(
|
||||
this.theTasks,
|
||||
tt,
|
||||
this.addGantAttributes(r)
|
||||
this.addGantAttributes(r),
|
||||
)
|
||||
break
|
||||
}
|
||||
|
@ -26,6 +26,11 @@ export default {
|
||||
const parsedTask = parseTaskText(newTaskTitle)
|
||||
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
|
||||
if (parsedTask.list !== null) {
|
||||
const list = this.$store.getters['lists/findListByExactname'](parsedTask.list)
|
||||
@ -34,7 +39,11 @@ export default {
|
||||
if (listId === null) {
|
||||
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
|
||||
// task create request. Because _that_ happens in a promise, we'll need something to call when it resolves.
|
||||
const createTask = () => {
|
||||
@ -83,7 +92,7 @@ export default {
|
||||
.then(res => {
|
||||
return addLabelToTask(res)
|
||||
})
|
||||
.catch(e => Promise.reject(e))
|
||||
.catch(e => Promise.reject(e)),
|
||||
)
|
||||
}
|
||||
})
|
||||
@ -110,7 +119,7 @@ export default {
|
||||
assignees.push(user)
|
||||
}
|
||||
return Promise.resolve(users)
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -229,7 +229,7 @@ export default {
|
||||
.then((r) => {
|
||||
this.$store.commit(
|
||||
'attachments/removeById',
|
||||
this.attachmentToDelete.id
|
||||
this.attachmentToDelete.id,
|
||||
)
|
||||
this.success(r)
|
||||
})
|
||||
|
@ -91,7 +91,7 @@ export default {
|
||||
.finally(() => {
|
||||
this.saving = false
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -95,7 +95,7 @@ export default {
|
||||
.finally(() => {
|
||||
this.saving = false
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -32,6 +32,11 @@ export default {
|
||||
foundLists: [],
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Multiselect,
|
||||
},
|
||||
@ -39,6 +44,14 @@ export default {
|
||||
this.listSerivce = new ListService()
|
||||
this.list = new ListModel()
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.list = newVal
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.list = this.value
|
||||
},
|
||||
methods: {
|
||||
findLists(query) {
|
||||
if (query === '') {
|
||||
@ -58,7 +71,9 @@ export default {
|
||||
this.$set(this, 'foundLists', [])
|
||||
},
|
||||
select(list) {
|
||||
this.list = list
|
||||
this.$emit('selected', list)
|
||||
this.$emit('input', list)
|
||||
},
|
||||
namespace(namespaceId) {
|
||||
const namespace = this.$store.getters['namespaces/getNamespaceById'](namespaceId)
|
||||
|
@ -135,7 +135,7 @@ export default {
|
||||
showListColor: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
theTask(newVal) {
|
||||
@ -178,13 +178,13 @@ export default {
|
||||
this.success({
|
||||
message: this.task.done ?
|
||||
this.$t('task.doneSuccess') :
|
||||
this.$t('task.undoneSuccess')
|
||||
this.$t('task.undoneSuccess'),
|
||||
}, [{
|
||||
title: 'Undo',
|
||||
callback: () => {
|
||||
this.task.done = !this.task.done
|
||||
this.markAsDone(!checked)
|
||||
}
|
||||
},
|
||||
}])
|
||||
})
|
||||
.catch(e => {
|
||||
|
Reference in New Issue
Block a user