1
0

Cleanup code & make sure it has a common code style

This commit is contained in:
kolaente
2020-09-05 22:35:52 +02:00
parent 4a8b15e7be
commit a8a7f70a3c
132 changed files with 6821 additions and 6595 deletions

View File

@ -1,10 +1,10 @@
<template>
<div class="is-max-width-desktop show-tasks">
<fancycheckbox
class="is-pulled-right"
v-if="!showAll"
v-model="showNulls"
@change="loadPendingTasks"
@change="loadPendingTasks"
class="is-pulled-right"
v-if="!showAll"
v-model="showNulls"
>
Show tasks without dates
</fancycheckbox>
@ -12,184 +12,184 @@
<h3 v-else>
Tasks from
<flat-pickr
:class="{ 'disabled': taskService.loading}"
class="input"
:disabled="taskService.loading"
v-model="cStartDate"
:config="flatPickerConfig"
@on-close="loadPendingTasks"
:class="{ 'disabled': taskService.loading}"
:config="flatPickerConfig"
:disabled="taskService.loading"
@on-close="loadPendingTasks"
class="input"
v-model="cStartDate"
/>
until
<flat-pickr
:class="{ 'disabled': taskService.loading}"
class="input"
:disabled="taskService.loading"
v-model="cEndDate"
:config="flatPickerConfig"
@on-close="loadPendingTasks"
:class="{ 'disabled': taskService.loading}"
:config="flatPickerConfig"
:disabled="taskService.loading"
@on-close="loadPendingTasks"
class="input"
v-model="cEndDate"
/>
</h3>
<template v-if="!taskService.loading && (!hasUndoneTasks || !tasks || tasks.length === 0)">
<h3 class="nothing">Nothing to do - Have a nice day!</h3>
<img src="/images/cool.svg" alt=""/>
<img alt="" src="/images/cool.svg"/>
</template>
<div class="spinner" :class="{ 'is-loading': taskService.loading}"></div>
<div :class="{ 'is-loading': taskService.loading}" class="spinner"></div>
<div class="tasks" v-if="tasks && tasks.length > 0">
<div class="task" v-for="t in tasks" :key="t.id">
<single-task-in-list :the-task="t" @taskUpdated="updateTasks" :show-list="true"/>
<div :key="t.id" class="task" v-for="t in tasks">
<single-task-in-list :show-list="true" :the-task="t" @taskUpdated="updateTasks"/>
</div>
</div>
</div>
</template>
<script>
import TaskService from '../../services/task'
import SingleTaskInList from '../../components/tasks/partials/singleTaskInList'
import {HAS_TASKS} from '../../store/mutation-types'
import {mapState} from 'vuex'
import TaskService from '../../services/task'
import SingleTaskInList from '../../components/tasks/partials/singleTaskInList'
import {HAS_TASKS} from '@/store/mutation-types'
import {mapState} from 'vuex'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import Fancycheckbox from '../../components/input/fancycheckbox'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import Fancycheckbox from '../../components/input/fancycheckbox'
export default {
name: 'ShowTasks',
components: {
Fancycheckbox,
SingleTaskInList,
flatPickr,
export default {
name: 'ShowTasks',
components: {
Fancycheckbox,
SingleTaskInList,
flatPickr,
},
data() {
return {
tasks: [],
hasUndoneTasks: false,
taskService: TaskService,
showNulls: true,
cStartDate: null,
cEndDate: null,
flatPickerConfig: {
altFormat: 'j M Y H:i',
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
},
}
},
props: {
startDate: Date,
endDate: Date,
showAll: Boolean,
},
created() {
this.taskService = new TaskService()
this.cStartDate = this.startDate
this.cEndDate = this.endDate
this.loadPendingTasks()
},
watch: {
'$route': 'loadPendingTasks',
startDate(newVal) {
this.cStartDate = newVal
},
data() {
return {
tasks: [],
hasUndoneTasks: false,
taskService: TaskService,
showNulls: true,
cStartDate: null,
cEndDate: null,
flatPickerConfig: {
altFormat: 'j M Y H:i',
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
},
endDate(newVal) {
this.cEndDate = newVal
},
},
computed: mapState({
userAuthenticated: state => state.auth.authenticated,
}),
methods: {
loadPendingTasks() {
// Since this route is authentication only, users would get an error message if they access the page unauthenticated.
// Since this component is mounted as the home page before unauthenticated users get redirected
// to the login page, they will almost always see the error message.
if (!this.userAuthenticated) {
return
}
},
props: {
startDate: Date,
endDate: Date,
showAll: Boolean,
},
created() {
this.taskService = new TaskService()
this.cStartDate = this.startDate
this.cEndDate = this.endDate
this.loadPendingTasks()
},
watch: {
'$route': 'loadPendingTasks',
startDate(newVal) {
this.cStartDate = newVal
},
endDate(newVal) {
this.cEndDate = newVal
},
},
computed: mapState({
userAuthenticated: state => state.auth.authenticated,
}),
methods: {
loadPendingTasks() {
// Since this route is authentication only, users would get an error message if they access the page unauthenticated.
// Since this component is mounted as the home page before unauthenticated users get redirected
// to the login page, they will almost always see the error message.
if (!this.userAuthenticated) {
return
}
// Make sure all dates are date objects
this.cStartDate = new Date(this.cStartDate)
this.cEndDate = new Date(this.cEndDate)
// Make sure all dates are date objects
this.cStartDate = new Date(this.cStartDate)
this.cEndDate = new Date(this.cEndDate)
if (this.showAll) {
this.setTitle('Current Tasks')
} else {
this.setTitle(`Tasks from ${this.cStartDate.toLocaleDateString()} until ${this.cEndDate.toLocaleDateString()}`)
}
if (this.showAll) {
this.setTitle('Current Tasks')
} else {
this.setTitle(`Tasks from ${this.cStartDate.toLocaleDateString()} until ${this.cEndDate.toLocaleDateString()}`)
}
const params = {
sort_by: ['due_date', 'id'],
order_by: ['desc', 'desc'],
filter_by: ['done'],
filter_value: [false],
filter_comparator: ['equals'],
filter_concat: 'and',
filter_include_nulls: this.showNulls,
}
if (!this.showAll) {
if(this.showNulls) {
params.filter_by.push('start_date')
params.filter_value.push(this.cStartDate)
params.filter_comparator.push('greater')
params.filter_by.push('end_date')
params.filter_value.push(this.cEndDate)
params.filter_comparator.push('less')
}
params.filter_by.push('due_date')
params.filter_value.push(this.cEndDate)
params.filter_comparator.push('less')
params.filter_by.push('due_date')
const params = {
sort_by: ['due_date', 'id'],
order_by: ['desc', 'desc'],
filter_by: ['done'],
filter_value: [false],
filter_comparator: ['equals'],
filter_concat: 'and',
filter_include_nulls: this.showNulls,
}
if (!this.showAll) {
if (this.showNulls) {
params.filter_by.push('start_date')
params.filter_value.push(this.cStartDate)
params.filter_comparator.push('greater')
params.filter_by.push('end_date')
params.filter_value.push(this.cEndDate)
params.filter_comparator.push('less')
}
this.taskService.getAll({}, params)
.then(r => {
if (r.length > 0) {
for (const index in r) {
if (r[index].done !== true) {
this.hasUndoneTasks = true
}
params.filter_by.push('due_date')
params.filter_value.push(this.cEndDate)
params.filter_comparator.push('less')
params.filter_by.push('due_date')
params.filter_value.push(this.cStartDate)
params.filter_comparator.push('greater')
}
this.taskService.getAll({}, params)
.then(r => {
if (r.length > 0) {
for (const index in r) {
if (r[index].done !== true) {
this.hasUndoneTasks = true
}
}
this.$set(this, 'tasks', r.filter(t => !t.done))
this.$store.commit(HAS_TASKS, r.length > 0)
})
.catch(e => {
this.error(e, this)
})
},
sortTasks() {
if (this.tasks === null || this.tasks === []) {
return
}
return this.tasks.sort(function (a, b) {
if (a.done < b.done)
return -1
if (a.done > b.done)
return 1
if (a.id > b.id)
return -1
if (a.id < b.id)
return 1
return 0
})
},
updateTasks(updatedTask) {
for (const t in this.tasks) {
if (this.tasks[t].id === updatedTask.id) {
this.$set(this.tasks, t, updatedTask)
break
}
}
this.sortTasks()
},
this.$set(this, 'tasks', r.filter(t => !t.done))
this.$store.commit(HAS_TASKS, r.length > 0)
})
.catch(e => {
this.error(e, this)
})
},
}
sortTasks() {
if (this.tasks === null || this.tasks === []) {
return
}
return this.tasks.sort(function (a, b) {
if (a.done < b.done)
return -1
if (a.done > b.done)
return 1
if (a.id > b.id)
return -1
if (a.id < b.id)
return 1
return 0
})
},
updateTasks(updatedTask) {
for (const t in this.tasks) {
if (this.tasks[t].id === updatedTask.id) {
this.$set(this.tasks, t, updatedTask)
break
}
}
this.sortTasks()
},
},
}
</script>

View File

@ -1,46 +1,46 @@
<template>
<div class="content has-text-centered">
<ShowTasks
:start-date="startDate"
:end-date="endDate"
:start-date="startDate"
/>
</div>
</template>
<script>
import ShowTasks from './ShowTasks'
import ShowTasks from './ShowTasks'
export default {
name: 'ShowTasksInRange',
components: {
ShowTasks,
},
data() {
return {
startDate: new Date(this.$route.params.startDateUnix),
endDate: new Date(this.$route.params.endDateUnix),
}
},
watch: {
// call again the method if the route changes
'$route': 'setDates'
},
created() {
this.setDates();
},
methods: {
setDates() {
switch (this.$route.params.type) {
case 'week':
this.startDate = new Date();
this.endDate = new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000);
break;
case 'month':
this.startDate = new Date();
this.endDate = new Date((new Date()).setMonth((new Date()).getMonth() + 1));
break;
}
}
export default {
name: 'ShowTasksInRange',
components: {
ShowTasks,
},
data() {
return {
startDate: new Date(this.$route.params.startDateUnix),
endDate: new Date(this.$route.params.endDateUnix),
}
}
},
watch: {
// call again the method if the route changes
'$route': 'setDates',
},
created() {
this.setDates()
},
methods: {
setDates() {
switch (this.$route.params.type) {
case 'week':
this.startDate = new Date()
this.endDate = new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
break
case 'month':
this.startDate = new Date()
this.endDate = new Date((new Date()).setMonth((new Date()).getMonth() + 1))
break
}
},
},
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="loader-container task-view-container" :class="{ 'is-loading': taskService.loading}">
<div :class="{ 'is-loading': taskService.loading}" class="loader-container task-view-container">
<div class="task-view">
<div class="heading">
<h1 class="title task-id" v-if="task.identifier === ''">
@ -9,8 +9,8 @@
{{ task.identifier }}
</h1>
<div class="is-done" v-if="task.done">Done</div>
<h1 class="title input" contenteditable="true" @focusout="saveTaskOnChange()" ref="taskTitle"
@keyup.ctrl.enter="saveTaskOnChange()">{{ task.title }}</h1>
<h1 @focusout="saveTaskOnChange()" @keyup.ctrl.enter="saveTaskOnChange()" class="title input" contenteditable="true"
ref="taskTitle">{{ task.title }}</h1>
</div>
<h6 class="subtitle" v-if="parent && parent.namespace && parent.list">
{{ parent.namespace.title }} >
@ -22,7 +22,7 @@
<!-- Content and buttons -->
<div class="columns">
<!-- Content -->
<div class="column" :class="{'is-two-thirds': canWrite}">
<div :class="{'is-two-thirds': canWrite}" class="column">
<div class="columns details">
<div class="column assignees" v-if="activeFields.assignees">
<!-- Assignees -->
@ -31,11 +31,11 @@
Assignees
</div>
<edit-assignees
:task-id="task.id"
:list-id="task.listId"
:initial-assignees="task.assignees"
ref="assignees"
:disabled="!canWrite"
:disabled="!canWrite"
:initial-assignees="task.assignees"
:list-id="task.listId"
:task-id="task.id"
ref="assignees"
/>
</div>
<div class="column" v-if="activeFields.priority">
@ -45,10 +45,10 @@
Priority
</div>
<priority-select
v-model="task.priority"
@change="saveTask"
ref="priority"
:disabled="!canWrite"/>
:disabled="!canWrite"
@change="saveTask"
ref="priority"
v-model="task.priority"/>
</div>
<div class="column" v-if="activeFields.dueDate">
<!-- Due Date -->
@ -58,17 +58,17 @@
</div>
<div class="date-input">
<flat-pickr
:class="{ 'disabled': taskService.loading}"
class="input"
:disabled="taskService.loading || !canWrite"
v-model="dueDate"
:config="flatPickerConfig"
@on-close="saveTask"
placeholder="Click here to set a due date"
ref="dueDate"
:class="{ 'disabled': taskService.loading}"
:config="flatPickerConfig"
:disabled="taskService.loading || !canWrite"
@on-close="saveTask"
class="input"
placeholder="Click here to set a due date"
ref="dueDate"
v-model="dueDate"
>
</flat-pickr>
<a v-if="dueDate && canWrite" @click="() => {dueDate = task.dueDate = null;saveTask()}">
<a @click="() => {dueDate = task.dueDate = null;saveTask()}" v-if="dueDate && canWrite">
<span class="icon is-small">
<icon icon="times"></icon>
</span>
@ -82,10 +82,10 @@
Percent Done
</div>
<percent-done-select
v-model="task.percentDone"
@change="saveTask"
ref="percentDone"
:disabled="!canWrite"/>
:disabled="!canWrite"
@change="saveTask"
ref="percentDone"
v-model="task.percentDone"/>
</div>
<div class="column" v-if="activeFields.startDate">
<!-- Start Date -->
@ -95,17 +95,17 @@
</div>
<div class="date-input">
<flat-pickr
:class="{ 'disabled': taskService.loading}"
class="input"
:disabled="taskService.loading || !canWrite"
v-model="task.startDate"
:config="flatPickerConfig"
@on-close="saveTask"
placeholder="Click here to set a start date"
ref="startDate"
:class="{ 'disabled': taskService.loading}"
:config="flatPickerConfig"
:disabled="taskService.loading || !canWrite"
@on-close="saveTask"
class="input"
placeholder="Click here to set a start date"
ref="startDate"
v-model="task.startDate"
>
</flat-pickr>
<a v-if="task.startDate && canWrite" @click="() => {task.startDate = null;saveTask()}">
<a @click="() => {task.startDate = null;saveTask()}" v-if="task.startDate && canWrite">
<span class="icon is-small">
<icon icon="times"></icon>
</span>
@ -120,17 +120,17 @@
</div>
<div class="date-input">
<flat-pickr
:class="{ 'disabled': taskService.loading}"
class="input"
:disabled="taskService.loading || !canWrite"
v-model="task.endDate"
:config="flatPickerConfig"
@on-close="saveTask"
placeholder="Click here to set an end date"
ref="endDate"
:class="{ 'disabled': taskService.loading}"
:config="flatPickerConfig"
:disabled="taskService.loading || !canWrite"
@on-close="saveTask"
class="input"
placeholder="Click here to set an end date"
ref="endDate"
v-model="task.endDate"
>
</flat-pickr>
<a v-if="task.endDate && canWrite" @click="() => {task.endDate = null;saveTask()}">
<a @click="() => {task.endDate = null;saveTask()}" v-if="task.endDate && canWrite">
<span class="icon is-small">
<icon icon="times"></icon>
</span>
@ -144,10 +144,10 @@
Reminders
</div>
<reminders
v-model="task.reminderDates"
@change="saveTask"
ref="reminders"
:disabled="!canWrite"/>
:disabled="!canWrite"
@change="saveTask"
ref="reminders"
v-model="task.reminderDates"/>
</div>
<div class="column" v-if="activeFields.repeatAfter">
<!-- Repeat after -->
@ -156,10 +156,10 @@
Repeat
</div>
<repeat-after
v-model="task"
@change="saveTask"
:disabled="!canWrite"
ref="repeatAfter"/>
:disabled="!canWrite"
@change="saveTask"
ref="repeatAfter"
v-model="task"/>
</div>
<div class="column" v-if="activeFields.color">
<!-- Color -->
@ -168,10 +168,10 @@
Color
</div>
<color-picker
v-model="taskColor"
menu-position="bottom"
@change="saveTask"
ref="color"/>
@change="saveTask"
menu-position="bottom"
ref="color"
v-model="taskColor"/>
</div>
</div>
@ -183,11 +183,11 @@
</span>
Labels
</div>
<edit-labels :task-id="taskId" v-model="task.labels" ref="labels" :disabled="!canWrite"/>
<edit-labels :disabled="!canWrite" :task-id="taskId" ref="labels" v-model="task.labels"/>
</div>
<!-- Description -->
<div class="details content description" :class="{ 'has-top-border': activeFields.labels }">
<div :class="{ 'has-top-border': activeFields.labels }" class="details content description">
<h3>
<span class="icon is-grey">
<icon icon="align-left"/>
@ -195,20 +195,20 @@
Description
</h3>
<editor
v-model="task.description"
@change="saveTask"
:upload-enabled="true"
:upload-callback="attachmentUpload"
:is-edit-enabled="canWrite"
placeholder="Click here to enter a description..."/>
:is-edit-enabled="canWrite"
:upload-callback="attachmentUpload"
:upload-enabled="true"
@change="saveTask"
placeholder="Click here to enter a description..."
v-model="task.description"/>
</div>
<!-- Attachments -->
<div class="content attachments has-top-border" v-if="activeFields.attachments">
<attachments
:task-id="taskId"
ref="attachments"
:edit-enabled="canWrite"
:edit-enabled="canWrite"
:task-id="taskId"
ref="attachments"
/>
</div>
@ -221,12 +221,12 @@
Related Tasks
</h3>
<related-tasks
:task-id="taskId"
:list-id="task.listId"
:initial-related-tasks="task.relatedTasks"
:show-no-relations-notice="true"
ref="relatedTasks"
:edit-enabled="canWrite"
:edit-enabled="canWrite"
:initial-related-tasks="task.relatedTasks"
:list-id="task.listId"
:show-no-relations-notice="true"
:task-id="taskId"
ref="relatedTasks"
/>
</div>
@ -246,13 +246,13 @@
</div>
<!-- Comments -->
<comments :task-id="taskId" :can-write="canWrite"/>
<comments :can-write="canWrite" :task-id="taskId"/>
</div>
<div class="column is-one-third action-buttons" v-if="canWrite">
<a
class="button is-outlined noshadow has-no-border"
:class="{'is-success': !task.done}"
@click="toggleTaskDone()">
:class="{'is-success': !task.done}"
@click="toggleTaskDone()"
class="button is-outlined noshadow has-no-border">
<span class="icon is-small"><icon icon="check-double"/></span>
<template v-if="task.done">
Mark as undone
@ -262,78 +262,78 @@
</template>
</a>
<a
class="button"
@click="setFieldActive('assignees')"
v-shortkey="['ctrl', 'shift', 'a']"
@shortkey="setFieldActive('assignees')">
@click="setFieldActive('assignees')"
@shortkey="setFieldActive('assignees')"
class="button"
v-shortkey="['ctrl', 'shift', 'a']">
<span class="icon is-small"><icon icon="users"/></span>
Assign this task to a user
</a>
<a
class="button"
@click="setFieldActive('labels')"
v-shortkey="['ctrl', 'shift', 'l']"
@shortkey="setFieldActive('labels')">
@click="setFieldActive('labels')"
@shortkey="setFieldActive('labels')"
class="button"
v-shortkey="['ctrl', 'shift', 'l']">
<span class="icon is-small"><icon icon="tags"/></span>
Add labels
</a>
<a class="button" @click="setFieldActive('reminders')">
<a @click="setFieldActive('reminders')" class="button">
<span class="icon is-small"><icon icon="history"/></span>
Set Reminders
</a>
<a
class="button"
@click="setFieldActive('dueDate')"
v-shortkey="['ctrl', 'shift', 'd']"
@shortkey="setFieldActive('dueDate')">
@click="setFieldActive('dueDate')"
@shortkey="setFieldActive('dueDate')"
class="button"
v-shortkey="['ctrl', 'shift', 'd']">
<span class="icon is-small"><icon icon="calendar"/></span>
Set Due Date
</a>
<a class="button" @click="setFieldActive('startDate')">
<a @click="setFieldActive('startDate')" class="button">
<span class="icon is-small"><icon icon="calendar-week"/></span>
Set a Start Date
</a>
<a class="button" @click="setFieldActive('endDate')">
<a @click="setFieldActive('endDate')" class="button">
<span class="icon is-small"><icon icon="calendar-week"/></span>
Set an End Date
</a>
<a class="button" @click="setFieldActive('repeatAfter')">
<a @click="setFieldActive('repeatAfter')" class="button">
<span class="icon is-small"><icon :icon="['far', 'clock']"/></span>
Set a repeating interval
</a>
<a class="button" @click="setFieldActive('priority')">
<a @click="setFieldActive('priority')" class="button">
<span class="icon is-small"><icon :icon="['far', 'star']"/></span>
Set Priority
</a>
<a class="button" @click="setFieldActive('percentDone')">
<a @click="setFieldActive('percentDone')" class="button">
<span class="icon is-small"><icon icon="percent"/></span>
Set Percent Done
</a>
<a
class="button"
@click="setFieldActive('attachments')"
v-shortkey="['ctrl', 'shift', 'f']"
@shortkey="setFieldActive('attachments')">
@click="setFieldActive('attachments')"
@shortkey="setFieldActive('attachments')"
class="button"
v-shortkey="['ctrl', 'shift', 'f']">
<span class="icon is-small"><icon icon="paperclip"/></span>
Add attachments
</a>
<a
class="button"
@click="setFieldActive('relatedTasks')"
v-shortkey="['ctrl', 'shift', 'r']"
@shortkey="setFieldActive('relatedTasks')">
@click="setFieldActive('relatedTasks')"
@shortkey="setFieldActive('relatedTasks')"
class="button"
v-shortkey="['ctrl', 'shift', 'r']">
<span class="icon is-small"><icon icon="tasks"/></span>
Add task relations
</a>
<a class="button" @click="setFieldActive('moveList')">
<a @click="setFieldActive('moveList')" class="button">
<span class="icon is-small"><icon icon="list"/></span>
Move task
</a>
<a class="button" @click="setFieldActive('color')">
<a @click="setFieldActive('color')" class="button">
<span class="icon is-small"><icon icon="fill-drip"/></span>
Set task color
</a>
<a class="button is-danger is-outlined noshadow has-no-border" @click="showDeleteModal = true">
<a @click="showDeleteModal = true" class="button is-danger is-outlined noshadow has-no-border">
<span class="icon is-small"><icon icon="trash-alt"/></span>
Delete task
</a>
@ -344,9 +344,9 @@
</div>
<modal
v-if="showDeleteModal"
@close="showDeleteModal = false"
@submit="deleteTask()">
@close="showDeleteModal = false"
@submit="deleteTask()"
v-if="showDeleteModal">
<span slot="header">Delete this task</span>
<p slot="text">
Are you sure you want to remove this task? <br/>
@ -358,269 +358,269 @@
</template>
<script>
import TaskService from '../../services/task'
import TaskModel from '../../models/task'
import relationKinds from '../../models/relationKinds.json'
import TaskService from '../../services/task'
import TaskModel from '../../models/task'
import relationKinds from '../../models/relationKinds.json'
import priorites from '../../models/priorities.json'
import rights from '../../models/rights.json'
import priorites from '../../models/priorities.json'
import rights from '../../models/rights.json'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import PrioritySelect from '../../components/tasks/partials/prioritySelect'
import PercentDoneSelect from '../../components/tasks/partials/percentDoneSelect'
import EditLabels from '../../components/tasks/partials/editLabels'
import EditAssignees from '../../components/tasks/partials/editAssignees'
import Attachments from '../../components/tasks/partials/attachments'
import RelatedTasks from '../../components/tasks/partials/relatedTasks'
import RepeatAfter from '../../components/tasks/partials/repeatAfter'
import Reminders from '../../components/tasks/partials/reminders'
import Comments from '../../components/tasks/partials/comments'
import router from '../../router'
import ListSearch from '../../components/tasks/partials/listSearch'
import ColorPicker from '../../components/input/colorPicker'
import attachmentUpload from '../../components/tasks/mixins/attachmentUpload'
import LoadingComponent from '../../components/misc/loading'
import ErrorComponent from '../../components/misc/error'
import PrioritySelect from '../../components/tasks/partials/prioritySelect'
import PercentDoneSelect from '../../components/tasks/partials/percentDoneSelect'
import EditLabels from '../../components/tasks/partials/editLabels'
import EditAssignees from '../../components/tasks/partials/editAssignees'
import Attachments from '../../components/tasks/partials/attachments'
import RelatedTasks from '../../components/tasks/partials/relatedTasks'
import RepeatAfter from '../../components/tasks/partials/repeatAfter'
import Reminders from '../../components/tasks/partials/reminders'
import Comments from '../../components/tasks/partials/comments'
import router from '../../router'
import ListSearch from '../../components/tasks/partials/listSearch'
import ColorPicker from '../../components/input/colorPicker'
import attachmentUpload from '../../components/tasks/mixins/attachmentUpload'
import LoadingComponent from '../../components/misc/loading'
import ErrorComponent from '../../components/misc/error'
export default {
name: 'TaskDetailView',
components: {
ColorPicker,
ListSearch,
Reminders,
RepeatAfter,
RelatedTasks,
Attachments,
EditAssignees,
EditLabels,
PercentDoneSelect,
PrioritySelect,
Comments,
flatPickr,
editor: () => ({
component: import(/* webpackPrefetch: true *//* webpackChunkName: "editor" */ '../../components/input/editor'),
loading: LoadingComponent,
error: ErrorComponent,
timeout: 60000,
}),
},
mixins: [
attachmentUpload,
],
data() {
return {
taskId: Number(this.$route.params.id),
taskService: TaskService,
task: TaskModel,
relationKinds: relationKinds,
// The due date is a seperate property in the task to prevent flatpickr from modifying the task model
// in store right after updating it from the api resulting in the wrong due date format being saved in the task.
dueDate: null,
// We doubled the task color property here because verte does not have a real change property, leading
// to the color property change being triggered when the # is removed from it, leading to an update,
// which leads in turn to a change... This creates an infinite loop in which the task is updated, changed,
// updated, changed, updated and so on.
// To prevent this, we put the task color property in a seperate value which is set to the task color
// when it is saved and loaded.
taskColor: '',
export default {
name: 'TaskDetailView',
components: {
ColorPicker,
ListSearch,
Reminders,
RepeatAfter,
RelatedTasks,
Attachments,
EditAssignees,
EditLabels,
PercentDoneSelect,
PrioritySelect,
Comments,
flatPickr,
editor: () => ({
component: import(/* webpackPrefetch: true *//* webpackChunkName: "editor" */ '../../components/input/editor'),
loading: LoadingComponent,
error: ErrorComponent,
timeout: 60000,
}),
},
mixins: [
attachmentUpload,
],
data() {
return {
taskId: Number(this.$route.params.id),
taskService: TaskService,
task: TaskModel,
relationKinds: relationKinds,
// The due date is a seperate property in the task to prevent flatpickr from modifying the task model
// in store right after updating it from the api resulting in the wrong due date format being saved in the task.
dueDate: null,
// We doubled the task color property here because verte does not have a real change property, leading
// to the color property change being triggered when the # is removed from it, leading to an update,
// which leads in turn to a change... This creates an infinite loop in which the task is updated, changed,
// updated, changed, updated and so on.
// To prevent this, we put the task color property in a seperate value which is set to the task color
// when it is saved and loaded.
taskColor: '',
showDeleteModal: false,
taskTitle: '',
descriptionChanged: false,
listViewName: 'list.list',
showDeleteModal: false,
taskTitle: '',
descriptionChanged: false,
listViewName: 'list.list',
priorities: priorites,
flatPickerConfig: {
altFormat: 'j M Y H:i',
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
},
activeFields: {
assignees: false,
priority: false,
dueDate: false,
percentDone: false,
startDate: false,
endDate: false,
reminders: false,
repeatAfter: false,
labels: false,
attachments: false,
relatedTasks: false,
moveList: false,
color: false,
},
}
},
watch: {
'$route': 'loadTask',
},
created() {
this.taskService = new TaskService()
this.task = new TaskModel()
},
mounted() {
priorities: priorites,
flatPickerConfig: {
altFormat: 'j M Y H:i',
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
},
activeFields: {
assignees: false,
priority: false,
dueDate: false,
percentDone: false,
startDate: false,
endDate: false,
reminders: false,
repeatAfter: false,
labels: false,
attachments: false,
relatedTasks: false,
moveList: false,
color: false,
},
}
},
watch: {
'$route': 'loadTask',
},
created() {
this.taskService = new TaskService()
this.task = new TaskModel()
},
mounted() {
// Build the list path from the task detail name to send the user to the view they came from.
const parts = this.$route.name.split('.')
if (parts.length > 2 && parts[2] === 'detail') {
this.listViewName = `list.${parts[1]}`
// Build the list path from the task detail name to send the user to the view they came from.
const parts = this.$route.name.split('.')
if (parts.length > 2 && parts[2] === 'detail') {
this.listViewName = `list.${parts[1]}`
}
this.loadTask()
},
computed: {
parent() {
if (!this.task.listId) {
return {
namespace: null,
list: null,
}
}
this.loadTask()
if (!this.$store.getters['namespaces/getListAndNamespaceById']) {
return null
}
return this.$store.getters['namespaces/getListAndNamespaceById'](this.task.listId)
},
computed: {
parent() {
if (!this.task.listId) {
return {
namespace: null,
list: null,
}
}
if (!this.$store.getters['namespaces/getListAndNamespaceById']) {
return null
}
return this.$store.getters['namespaces/getListAndNamespaceById'](this.task.listId)
},
canWrite() {
return this.task && this.task.maxRight && this.task.maxRight > rights.READ
},
canWrite() {
return this.task && this.task.maxRight && this.task.maxRight > rights.READ
},
methods: {
loadTask() {
this.taskId = Number(this.$route.params.id)
this.taskService.get({id: this.taskId})
.then(r => {
this.$set(this, 'task', r)
this.$store.commit('attachments/set', r.attachments)
this.taskTitle = this.task.title
this.taskColor = this.task.hexColor
this.setActiveFields()
this.setTitle(this.task.title)
})
.catch(e => {
this.error(e, this)
})
},
setActiveFields() {
this.dueDate = this.task.dueDate ? this.task.dueDate : null
this.task.startDate = this.task.startDate ? this.task.startDate : null
this.task.endDate = this.task.endDate ? this.task.endDate : null
// Set all active fields based on values in the model
this.activeFields.assignees = this.task.assignees.length > 0
this.activeFields.priority = this.task.priority !== priorites.UNSET
this.activeFields.dueDate = this.task.dueDate !== null
this.activeFields.percentDone = this.task.percentDone > 0
this.activeFields.startDate = this.task.startDate !== null
this.activeFields.endDate = this.task.endDate !== null
// On chrome, reminderDates.length holds the actual number of reminders that are not null.
// Unlike on desktop where it holds all reminders, including the ones which are null.
// This causes the reminders to dissapear entierly when only one is set and the user is on mobile.
this.activeFields.reminders = this.task.reminderDates.length > 1 || (window.innerWidth < 769 && this.task.reminderDates.length > 0)
this.activeFields.repeatAfter = this.task.repeatAfter.amount > 0
this.activeFields.labels = this.task.labels.length > 0
this.activeFields.attachments = this.task.attachments.length > 0
this.activeFields.relatedTasks = Object.keys(this.task.relatedTasks).length > 0
},
saveTaskOnChange() {
this.$refs.taskTitle.spellcheck = false
// Pull the task title from the contenteditable
let taskTitle = this.$refs.taskTitle.textContent
this.task.title = taskTitle
// We only want to save if the title was actually change.
// Because the contenteditable does not have a change event,
// we're building it ourselves and only calling saveTask()
// if the task title changed.
if (this.task.title !== this.taskTitle) {
this.saveTask()
this.taskTitle = taskTitle
}
},
saveTask(undoCallback = null) {
if (!this.canWrite) {
return
}
this.task.dueDate = this.dueDate
this.task.hexColor = this.taskColor
// If no end date is being set, but a start date and due date,
// use the due date as the end date
if (this.task.endDate === null && this.task.startDate !== null && this.task.dueDate !== null) {
this.task.endDate = this.task.dueDate
}
this.$store.dispatch('tasks/update', this.task)
.then(r => {
this.$set(this, 'task', r)
let actions = []
if (undoCallback !== null) {
actions = [{
title: 'Undo',
callback: undoCallback,
}]
this.success({message: 'The task was saved successfully.'}, this, actions)
}
this.dueDate = this.task.dueDate
this.setActiveFields()
})
.catch(e => {
this.error(e, this)
})
},
setFieldActive(fieldName) {
this.activeFields[fieldName] = true
this.$nextTick(() => {
if (this.$refs[fieldName]) {
this.$refs[fieldName].$el.focus()
}
},
methods: {
loadTask() {
this.taskId = Number(this.$route.params.id)
this.taskService.get({id: this.taskId})
.then(r => {
this.$set(this, 'task', r)
this.$store.commit('attachments/set', r.attachments)
this.taskTitle = this.task.title
this.taskColor = this.task.hexColor
this.setActiveFields()
this.setTitle(this.task.title)
})
.catch(e => {
this.error(e, this)
})
},
deleteTask() {
this.$store.dispatch('tasks/delete', this.task)
.then(() => {
this.success({message: 'The task been deleted successfully.'}, this)
router.back()
})
.catch(e => {
this.error(e, this)
})
},
toggleTaskDone() {
this.task.done = !this.task.done
this.saveTask(() => this.toggleTaskDone())
},
setDescriptionChanged(e) {
if (e.key === 'Enter' || e.key === 'Control') {
return
}
this.descriptionChanged = true
},
saveTaskIfDescriptionChanged() {
// We want to only save the description if it was changed.
// Since we can either trigger this with ctrl+enter or @change, it would be possible to save a task first
// with ctrl+enter and then with @change although nothing changed since the last save when @change gets fired.
// To only save one time we added this method.
if (this.descriptionChanged) {
this.descriptionChanged = false
this.saveTask()
}
},
changeList(list) {
this.task.listId = list.id
this.saveTask()
},
},
}
setActiveFields() {
this.dueDate = this.task.dueDate ? this.task.dueDate : null
this.task.startDate = this.task.startDate ? this.task.startDate : null
this.task.endDate = this.task.endDate ? this.task.endDate : null
// Set all active fields based on values in the model
this.activeFields.assignees = this.task.assignees.length > 0
this.activeFields.priority = this.task.priority !== priorites.UNSET
this.activeFields.dueDate = this.task.dueDate !== null
this.activeFields.percentDone = this.task.percentDone > 0
this.activeFields.startDate = this.task.startDate !== null
this.activeFields.endDate = this.task.endDate !== null
// On chrome, reminderDates.length holds the actual number of reminders that are not null.
// Unlike on desktop where it holds all reminders, including the ones which are null.
// This causes the reminders to dissapear entierly when only one is set and the user is on mobile.
this.activeFields.reminders = this.task.reminderDates.length > 1 || (window.innerWidth < 769 && this.task.reminderDates.length > 0)
this.activeFields.repeatAfter = this.task.repeatAfter.amount > 0
this.activeFields.labels = this.task.labels.length > 0
this.activeFields.attachments = this.task.attachments.length > 0
this.activeFields.relatedTasks = Object.keys(this.task.relatedTasks).length > 0
},
saveTaskOnChange() {
this.$refs.taskTitle.spellcheck = false
// Pull the task title from the contenteditable
let taskTitle = this.$refs.taskTitle.textContent
this.task.title = taskTitle
// We only want to save if the title was actually change.
// Because the contenteditable does not have a change event,
// we're building it ourselves and only calling saveTask()
// if the task title changed.
if (this.task.title !== this.taskTitle) {
this.saveTask()
this.taskTitle = taskTitle
}
},
saveTask(undoCallback = null) {
if (!this.canWrite) {
return
}
this.task.dueDate = this.dueDate
this.task.hexColor = this.taskColor
// If no end date is being set, but a start date and due date,
// use the due date as the end date
if (this.task.endDate === null && this.task.startDate !== null && this.task.dueDate !== null) {
this.task.endDate = this.task.dueDate
}
this.$store.dispatch('tasks/update', this.task)
.then(r => {
this.$set(this, 'task', r)
let actions = []
if (undoCallback !== null) {
actions = [{
title: 'Undo',
callback: undoCallback,
}]
this.success({message: 'The task was saved successfully.'}, this, actions)
}
this.dueDate = this.task.dueDate
this.setActiveFields()
})
.catch(e => {
this.error(e, this)
})
},
setFieldActive(fieldName) {
this.activeFields[fieldName] = true
this.$nextTick(() => {
if (this.$refs[fieldName]) {
this.$refs[fieldName].$el.focus()
}
})
},
deleteTask() {
this.$store.dispatch('tasks/delete', this.task)
.then(() => {
this.success({message: 'The task been deleted successfully.'}, this)
router.back()
})
.catch(e => {
this.error(e, this)
})
},
toggleTaskDone() {
this.task.done = !this.task.done
this.saveTask(() => this.toggleTaskDone())
},
setDescriptionChanged(e) {
if (e.key === 'Enter' || e.key === 'Control') {
return
}
this.descriptionChanged = true
},
saveTaskIfDescriptionChanged() {
// We want to only save the description if it was changed.
// Since we can either trigger this with ctrl+enter or @change, it would be possible to save a task first
// with ctrl+enter and then with @change although nothing changed since the last save when @change gets fired.
// To only save one time we added this method.
if (this.descriptionChanged) {
this.descriptionChanged = false
this.saveTask()
}
},
changeList(list) {
this.task.listId = list.id
this.saveTask()
},
},
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<div class="modal-mask">
<div class="modal-container" @click.self="close()">
<div @click.self="close()" class="modal-container">
<div class="scrolling-content">
<a @click="close()" class="close">
<icon icon="times"/>
@ -12,18 +12,18 @@
</template>
<script>
import TaskDetailView from './TaskDetailView'
import router from '../../router'
import TaskDetailView from './TaskDetailView'
import router from '../../router'
export default {
name: 'TaskDetailViewModal',
components: {
TaskDetailView,
export default {
name: 'TaskDetailViewModal',
components: {
TaskDetailView,
},
methods: {
close() {
router.back()
},
methods: {
close() {
router.back()
},
},
}
},
}
</script>