Merge branch 'master' of ssh://git.kolaente.de:9022/vikunja/frontend

This commit is contained in:
konrad 2018-11-28 21:31:38 +01:00
commit 29fb6d4190
No known key found for this signature in database
GPG key ID: F40E70337AB24C9B
20 changed files with 420 additions and 117 deletions

View file

@ -42,12 +42,12 @@
<p class="menu-label" v-if="loading">Loading...</p> <p class="menu-label" v-if="loading">Loading...</p>
<template v-for="n in namespaces"> <template v-for="n in namespaces">
<div :key="n.id"> <div :key="n.id">
<router-link :to="{name: 'editNamespace', params: {id: n.id} }" class="nsettings"> <router-link :to="{name: 'editNamespace', params: {id: n.id} }" class="nsettings" v-if="n.id > 0">
<span class="icon"> <span class="icon">
<icon icon="cog"/> <icon icon="cog"/>
</span> </span>
</router-link> </router-link>
<router-link :to="{ name: 'newList', params: { id: n.id} }" class="is-success nsettings" :key="n.id + 'newList'"> <router-link :to="{ name: 'newList', params: { id: n.id} }" class="is-success nsettings" :key="n.id + 'newList'" v-if="n.id > 0">
<span class="icon"> <span class="icon">
<icon icon="plus"/> <icon icon="plus"/>
</span> </span>

View file

@ -7,9 +7,19 @@
<div class="box tasks" v-if="tasks && tasks.length > 0"> <div class="box tasks" v-if="tasks && tasks.length > 0">
<div @click="gotoList(l.listID)" class="task" v-for="l in tasks" v-bind:key="l.id" v-if="!l.done"> <div @click="gotoList(l.listID)" class="task" v-for="l in tasks" v-bind:key="l.id" v-if="!l.done">
<label v-bind:for="l.id"> <label v-bind:for="l.id">
<input type="checkbox" v-bind:id="l.id" disabled> <div class="fancycheckbox">
{{l.text}} <input @change="markAsDone" type="checkbox" v-bind:id="l.id" v-bind:checked="l.done" style="display: none;" disabled>
<i v-if="l.dueDate > 0"> - Due on {{formatUnixDate(l.dueDate)}}</i> <label v-bind:for="l.id" class="check">
<svg width="18px" height="18px" viewBox="0 0 18 18">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
<polyline points="1 9 7 14 15 4"></polyline>
</svg>
</label>
</div>
<span class="tasktext">
{{l.text}}
<i v-if="l.dueDate > 0"> - Due on {{formatUnixDate(l.dueDate)}}</i>
</span>
</label> </label>
</div> </div>
</div> </div>
@ -38,21 +48,26 @@
} }
}, },
created() { created() {
this.loadPendingTasks() if (auth.user.authenticated) {
this.loadPendingTasks()
}
}, },
methods: { methods: {
logout() { logout() {
auth.logout() auth.logout()
}, },
loadPendingTasks() { loadPendingTasks() {
this.loading = true const cancel = message.setLoading(this)
HTTP.get(`tasks`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.get(`tasks`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
this.tasks = response.data this.tasks = response.data
this.tasks.sort(this.sortyByDeadline) this.tasks.sort(this.sortyByDeadline)
cancel()
this.loading = false this.loading = false
}) })
.catch(e => { .catch(e => {
cancel()
this.loading = false
this.handleError(e) this.handleError(e)
}) })
}, },
@ -66,7 +81,6 @@
router.push({name: 'showList', params: {id: lid}}) router.push({name: 'showList', params: {id: lid}})
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
} }
}, },

View file

@ -97,7 +97,7 @@
}, },
methods: { methods: {
loadList() { loadList() {
this.loading = true const cancel = message.setLoading(this)
HTTP.get(`lists/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.get(`lists/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
@ -105,14 +105,14 @@
if (response.data.owner.id === this.user.infos.id) { if (response.data.owner.id === this.user.infos.id) {
this.userIsAdmin = true this.userIsAdmin = true
} }
this.loading = false cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) this.handleError(e)
}) })
}, },
submit() { submit() {
this.loading = true const cancel = message.setLoading(this)
HTTP.post(`lists/` + this.$route.params.id, this.list, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.post(`lists/` + this.$route.params.id, this.list, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
@ -126,27 +126,30 @@
} }
} }
this.handleSuccess({message: 'The list was successfully updated.'}) this.handleSuccess({message: 'The list was successfully updated.'})
cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
deleteList() { deleteList() {
const cancel = message.setLoading(this)
HTTP.delete(`lists/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.delete(`lists/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => { .then(() => {
this.handleSuccess({message: 'The list was successfully deleted.'}) this.handleSuccess({message: 'The list was successfully deleted.'})
cancel()
router.push({name: 'home'}) router.push({name: 'home'})
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
} }

View file

@ -45,23 +45,23 @@
}, },
methods: { methods: {
newList() { newList() {
this.loading = true const cancel = message.setLoading(this)
HTTP.put(`namespaces/` + this.$route.params.id + `/lists`, this.list, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.put(`namespaces/` + this.$route.params.id + `/lists`, this.list, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => { .then(() => {
this.$parent.loadNamespaces() this.$parent.loadNamespaces()
this.handleSuccess({message: 'The list was successfully created.'}) this.handleSuccess({message: 'The list was successfully created.'})
cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
} }

View file

@ -30,8 +30,18 @@
<div class="box tasks" v-if="this.list.tasks && this.list.tasks.length > 0"> <div class="box tasks" v-if="this.list.tasks && this.list.tasks.length > 0">
<div class="task" v-for="l in list.tasks" v-bind:key="l.id"> <div class="task" v-for="l in list.tasks" v-bind:key="l.id">
<label v-bind:for="l.id"> <label v-bind:for="l.id">
<input @change="markAsDone" type="checkbox" v-bind:id="l.id" v-bind:checked="l.done"> <div class="fancycheckbox">
{{l.text}} <input @change="markAsDone" type="checkbox" v-bind:id="l.id" v-bind:checked="l.done" style="display: none;">
<label v-bind:for="l.id" class="check">
<svg width="18px" height="18px" viewBox="0 0 18 18">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
<polyline points="1 9 7 14 15 4"></polyline>
</svg>
</label>
</div>
<span class="tasktext">
{{l.text}}
</span>
</label> </label>
<div @click="editTask(l.id)" class="icon settings"> <div @click="editTask(l.id)" class="icon settings">
<icon icon="cog"/> <icon icon="cog"/>
@ -67,36 +77,51 @@
</div> </div>
</div> </div>
<div class="columns"> <b>Reminder Dates</b>
<div class="column"> <div class="reminder-input" :class="{ 'overdue': (r < nowUnix && index !== (taskEditTask.reminderDates.length - 1))}" v-for="(r, index) in taskEditTask.reminderDates" v-bind:key="index">
<div class="field"> <flat-pickr
<label class="label" for="taskduedate">Due Date</label> :class="{ 'disabled': loading}"
<div class="control"> :disabled="loading"
<flat-pickr :v-model="taskEditTask.reminderDates"
:class="{ 'disabled': loading}" :config="flatPickerConfig"
class="input" :id="'taskreminderdate' + index"
:disabled="loading" :value="r"
v-model="taskEditTask.dueDate" :data-index="index"
:config="flatPickerConfig" placeholder="Add a new reminder...">
id="taskduedate" </flat-pickr>
placeholder="The tasks due date is here..."> <a v-if="index !== (taskEditTask.reminderDates.length - 1)" @click="removeReminderByIndex(index)"><icon icon="times"></icon></a>
</flat-pickr> </div>
</div>
</div> <div class="field">
<label class="label" for="taskduedate">Due Date</label>
<div class="control">
<flat-pickr
:class="{ 'disabled': loading}"
class="input"
:disabled="loading"
v-model="taskEditTask.dueDate"
:config="flatPickerConfig"
id="taskduedate"
placeholder="The tasks due date is here...">
</flat-pickr>
</div> </div>
<div class="column"> </div>
<div class="field">
<label class="label" for="taskreminderdate">Reminder Date</label> <div class="field">
<div class="control"> <label class="label" for="">Repeat after</label>
<flat-pickr <div class="control repeat-after-input columns">
:class="{ 'disabled': loading}" <div class="column">
class="input" <input class="input" placeholder="Specify an amount..." v-model="repeatAfter.amount"/>
:disabled="loading" </div>
v-model="taskEditTask.reminderDate" <div class="column">
:config="flatPickerConfig" <div class="select">
id="taskreminderdate" <select v-model="repeatAfter.type">
placeholder="The tasks reminder date is here..."> <option value="hours">Hours</option>
</flat-pickr> <option value="days">Days</option>
<option value="weeks">Weeks</option>
<option value="months">Months</option>
<option value="years">Years</option>
</select>
</div> </div>
</div> </div>
</div> </div>
@ -133,12 +158,16 @@
loading: false, loading: false,
isTaskEdit: false, isTaskEdit: false,
taskEditTask: {}, taskEditTask: {},
lastReminder: 0,
nowUnix: new Date(),
repeatAfter: {type: 'days', amount: null},
flatPickerConfig:{ flatPickerConfig:{
altFormat: 'j M Y H:i', altFormat: 'j M Y H:i',
altInput: true, altInput: true,
dateFormat: 'Y-m-d H:i', dateFormat: 'Y-m-d H:i',
enableTime: true, enableTime: true,
defaultDate: new Date(), onOpen: this.updateLastReminderDate,
onClose: this.addReminderDate,
}, },
} }
}, },
@ -161,18 +190,22 @@
methods: { methods: {
loadList() { loadList() {
this.isTaskEdit = false this.isTaskEdit = false
this.loading = true const cancel = message.setLoading(this)
HTTP.get(`lists/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.get(`lists/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
this.loading = false
// Make date objects from timestamps // Make date objects from timestamps
for (const t in response.data.tasks) { for (const t in response.data.tasks) {
let dueDate = new Date(response.data.tasks[t].dueDate * 1000) let dueDate = new Date(response.data.tasks[t].dueDate * 1000)
let reminderDate = new Date(response.data.tasks[t].reminderDate * 1000) if (dueDate === 0) {
response.data.tasks[t].dueDate = dueDate response.data.tasks[t].dueDate = null
response.data.tasks[t].reminderDate = reminderDate } else {
response.data.tasks[t].dueDate = dueDate
}
for (const rd in response.data.tasks[t].reminderDates) {
response.data.tasks[t].reminderDates[rd] = new Date(response.data.tasks[t].reminderDates[rd] * 1000)
}
} }
// This adds a new elemednt "list" to our object which contains all lists // This adds a new elemednt "list" to our object which contains all lists
@ -180,40 +213,45 @@
if (this.list.tasks === null) { if (this.list.tasks === null) {
this.list.tasks = [] this.list.tasks = []
} }
cancel() // cancel the timer
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
addTask() { addTask() {
this.loading = true const cancel = message.setLoading(this)
HTTP.put(`lists/` + this.$route.params.id, {text: this.newTask}, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.put(`lists/` + this.$route.params.id, {text: this.newTask}, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
this.list.tasks.push(response.data) this.list.tasks.push(response.data)
this.handleSuccess({message: 'The task was successfully created.'}) this.handleSuccess({message: 'The task was successfully created.'})
cancel() // cancel the timer
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
this.newTask = '' this.newTask = ''
}, },
markAsDone(e) { markAsDone(e) {
const cancel = message.setLoading(this)
this.loading = true
HTTP.post(`tasks/` + e.target.id, {done: e.target.checked}, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.post(`tasks/` + e.target.id, {done: e.target.checked}, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
this.updateTaskByID(parseInt(e.target.id), response.data) this.updateTaskByID(parseInt(e.target.id), response.data)
this.handleSuccess({message: 'The task was successfully ' + (e.target.checked ? 'un-' :'') + 'marked as done.'}) this.handleSuccess({message: 'The task was successfully ' + (e.target.checked ? 'un-' :'') + 'marked as done.'})
cancel() // To not set the spinner to loading when the request is made in less than 100ms, would lead to loading infinitly.
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
editTask(id) { editTask(id) {
// Find the slected task and set it to the current object // Find the selected task and set it to the current object
for (const t in this.list.tasks) { for (const t in this.list.tasks) {
if (this.list.tasks[t].id === id) { if (this.list.tasks[t].id === id) {
this.taskEditTask = this.list.tasks[t] this.taskEditTask = this.list.tasks[t]
@ -221,45 +259,150 @@
} }
} }
if (this.taskEditTask.reminderDates === null) {
this.taskEditTask.reminderDates = []
}
this.taskEditTask.reminderDates = this.removeNullsFromArray(this.taskEditTask.reminderDates)
this.taskEditTask.reminderDates.push(null)
// Re-convert the the amount from seconds to be used with our form
let repeatAfterHours = (this.taskEditTask.repeatAfter / 60) / 60
// if its dividable by 24, its something with days
if (repeatAfterHours % 24 === 0) {
let repeatAfterDays = repeatAfterHours / 24
if (repeatAfterDays % 7 === 0) {
this.repeatAfter.type = 'weeks'
this.repeatAfter.amount = repeatAfterDays / 7
} else if (repeatAfterDays % 30 === 0) {
this.repeatAfter.type = 'months'
this.repeatAfter.amount = repeatAfterDays / 30
} else if (repeatAfterDays % 365 === 0) {
this.repeatAfter.type = 'years'
this.repeatAfter.amount = repeatAfterDays / 365
} else {
this.repeatAfter.type = 'days'
this.repeatAfter.amount = repeatAfterDays
}
} else {
// otherwise hours
this.repeatAfter.type = 'hours'
this.repeatAfter.amount = repeatAfterHours
}
this.isTaskEdit = true this.isTaskEdit = true
}, },
editTaskSubmit() { editTaskSubmit() {
this.loading = true const cancel = message.setLoading(this)
// Convert the date in a unix timestamp // Convert the date in a unix timestamp
let duedate = (+ new Date(this.taskEditTask.dueDate)) / 1000 let duedate = (+ new Date(this.taskEditTask.dueDate)) / 1000
let reminderdate = (+ new Date(this.taskEditTask.reminderDate)) / 1000
this.taskEditTask.dueDate = duedate this.taskEditTask.dueDate = duedate
this.taskEditTask.reminderDate = reminderdate
// remove all nulls
this.taskEditTask.reminderDates = this.removeNullsFromArray(this.taskEditTask.reminderDates)
// Make normal timestamps from js timestamps
for (const t in this.taskEditTask.reminderDates) {
this.taskEditTask.reminderDates[t] = Math.round(this.taskEditTask.reminderDates[t] / 1000)
}
// Make the repeating amount to seconds
let repeatAfterSeconds = 0
if (this.repeatAfter.amount !== null || this.repeatAfter.amount !== 0) {
switch (this.repeatAfter.type) {
case 'hours':
repeatAfterSeconds = this.repeatAfter.amount * 60 * 60
break;
case 'days':
repeatAfterSeconds = this.repeatAfter.amount * 60 * 60 * 24
break;
case 'weeks':
repeatAfterSeconds = this.repeatAfter.amount * 60 * 60 * 24 * 7
break;
case 'months':
repeatAfterSeconds = this.repeatAfter.amount * 60 * 60 * 24 * 30
break;
case 'years':
repeatAfterSeconds = this.repeatAfter.amount * 60 * 60 * 24 * 365
break;
}
}
this.taskEditTask.repeatAfter = repeatAfterSeconds
HTTP.post(`tasks/` + this.taskEditTask.id, this.taskEditTask, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.post(`tasks/` + this.taskEditTask.id, this.taskEditTask, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
response.data.dueDate = new Date(response.data.dueDate * 1000) response.data.dueDate = new Date(response.data.dueDate * 1000)
response.data.reminderDate = new Date(response.data.reminderDate * 1000) response.data.reminderDates = this.makeJSReminderDatesAfterUpdate(response.data.reminderDates)
// Update the task in the list // Update the task in the list
this.updateTaskByID(this.taskEditTask.id, response.data) this.updateTaskByID(this.taskEditTask.id, response.data)
// Also update the current taskedit object so the ui changes
this.$set(this, 'taskEditTask', response.data) // Also update the current taskedit object so the ui changes
this.$set(this, 'taskEditTask', response.data)
this.handleSuccess({message: 'The task was successfully updated.'}) this.handleSuccess({message: 'The task was successfully updated.'})
cancel() // cancel the timers
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
updateTaskByID(id, updatedTask) { updateTaskByID(id, updatedTask) {
for (const t in this.list.tasks) { for (const t in this.list.tasks) {
if (this.list.tasks[t].id === id) { if (this.list.tasks[t].id === id) {
//updatedTask.reminderDates = this.makeJSReminderDatesAfterUpdate(updatedTask.reminderDates)
this.$set(this.list.tasks, t, updatedTask) this.$set(this.list.tasks, t, updatedTask)
break break
} }
} }
}, },
updateLastReminderDate(selectedDates) {
this.lastReminder = +new Date(selectedDates[0])
},
addReminderDate(selectedDates, dateStr, instance) {
let newDate = +new Date(selectedDates[0])
// Don't update if nothing changed
if (newDate === this.lastReminder) {
return
}
let index = parseInt(instance.input.dataset.index)
this.taskEditTask.reminderDates[index] = newDate
let lastIndex = this.taskEditTask.reminderDates.length - 1
// put a new null at the end if we changed something
if (lastIndex === index && !isNaN(newDate)) {
this.taskEditTask.reminderDates.push(null)
}
},
removeReminderByIndex(index) {
this.taskEditTask.reminderDates.splice(index, 1)
// Reset the last to 0 to have the "add reminder" button
this.taskEditTask.reminderDates[this.taskEditTask.reminderDates.length - 1] = null
},
removeNullsFromArray(array) {
for (const index in array) {
if (array[index] === null) {
array.splice(index, 1)
}
}
return array
},
makeJSReminderDatesAfterUpdate(dates) {
// Make js timestamps from normal timestamps
for (const rd in dates) {
dates[rd] = +new Date(dates[rd] * 1000)
}
if (dates == null) {
dates = []
}
dates.push(null)
return dates
},
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
} }

View file

@ -97,7 +97,7 @@
}, },
methods: { methods: {
loadNamespace() { loadNamespace() {
this.loading = true const cancel = message.setLoading(this)
HTTP.get(`namespaces/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.get(`namespaces/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
@ -105,14 +105,15 @@
if (response.data.owner.id === this.user.infos.id) { if (response.data.owner.id === this.user.infos.id) {
this.userIsAdmin = true this.userIsAdmin = true
} }
this.loading = false cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
submit() { submit() {
this.loading = true const cancel = message.setLoading(this)
HTTP.post(`namespaces/` + this.$route.params.id, this.namespace, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.post(`namespaces/` + this.$route.params.id, this.namespace, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
@ -124,27 +125,30 @@
} }
} }
this.handleSuccess({message: 'The namespace was successfully updated.'}) this.handleSuccess({message: 'The namespace was successfully updated.'})
cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
deleteNamespace() { deleteNamespace() {
const cancel = message.setLoading(this)
HTTP.delete(`namespaces/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.delete(`namespaces/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => { .then(() => {
this.handleSuccess({message: 'The namespace was successfully deleted.'}) this.handleSuccess({message: 'The namespace was successfully deleted.'})
router.push({name: 'home'}) cancel()
router.push({name: 'home'})
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
} }

View file

@ -45,23 +45,23 @@
}, },
methods: { methods: {
newNamespace() { newNamespace() {
this.loading = true const cancel = message.setLoading(this)
HTTP.put(`namespaces`, this.namespace, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.put(`namespaces`, this.namespace, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => { .then(() => {
this.$parent.loadNamespaces() this.$parent.loadNamespaces()
this.handleSuccess({message: 'The namespace was successfully created.'}) this.handleSuccess({message: 'The namespace was successfully created.'})
cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
} }

View file

@ -121,27 +121,33 @@
}, },
methods: { methods: {
loadTeams() { loadTeams() {
const cancel = message.setLoading(this)
HTTP.get(this.typeString + `s/` + this.id + `/teams`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.get(this.typeString + `s/` + this.id + `/teams`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
this.$set(this, 'listTeams', response.data) this.$set(this, 'listTeams', response.data)
this.loading = false cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
deleteTeam() { deleteTeam() {
const cancel = message.setLoading(this)
HTTP.delete(this.typeString + `s/` + this.id + `/teams/` + this.teamToDelete, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.delete(this.typeString + `s/` + this.id + `/teams/` + this.teamToDelete, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => { .then(() => {
this.showTeamDeleteModal = false; this.showTeamDeleteModal = false;
this.handleSuccess({message: 'The team was successfully deleted from the ' + this.typeString + '.'}) this.handleSuccess({message: 'The team was successfully deleted from the ' + this.typeString + '.'})
this.loadTeams() this.loadTeams()
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
addTeam(admin) { addTeam(admin) {
const cancel = message.setLoading(this)
if(admin === null) { if(admin === null) {
admin = false admin = false
} }
@ -154,12 +160,15 @@
.then(() => { .then(() => {
this.loadTeams() this.loadTeams()
this.handleSuccess({message: 'The team was successfully added.'}) this.handleSuccess({message: 'The team was successfully added.'})
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
toggleTeamType(teamid, current) { toggleTeamType(teamid, current) {
const cancel = message.setLoading(this)
let right = 0 let right = 0
if (!current) { if (!current) {
right = 2 right = 2
@ -169,17 +178,17 @@
.then(() => { .then(() => {
this.loadTeams() this.loadTeams()
this.handleSuccess({message: 'The team right was successfully updated.'}) this.handleSuccess({message: 'The team right was successfully updated.'})
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
}, },

View file

@ -142,28 +142,34 @@
}, },
methods: { methods: {
loadUsers() { loadUsers() {
const cancel = message.setLoading(this)
HTTP.get(this.typeString + `s/` + this.id + `/users`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.get(this.typeString + `s/` + this.id + `/users`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
//response.data.push(this.list.owner) //response.data.push(this.list.owner)
this.$set(this, 'users', response.data) this.$set(this, 'users', response.data)
this.loading = false cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
deleteUser() { deleteUser() {
const cancel = message.setLoading(this)
HTTP.delete(this.typeString + `s/` + this.id + `/users/` + this.userToDelete, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.delete(this.typeString + `s/` + this.id + `/users/` + this.userToDelete, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => { .then(() => {
this.showUserDeleteModal = false; this.showUserDeleteModal = false;
this.handleSuccess({message: 'The user was successfully deleted from the ' + this.typeString + '.'}) this.handleSuccess({message: 'The user was successfully deleted from the ' + this.typeString + '.'})
this.loadUsers() this.loadUsers()
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
addUser(admin) { addUser(admin) {
const cancel = message.setLoading(this)
if(admin === null) { if(admin === null) {
admin = false admin = false
} }
@ -179,12 +185,15 @@
this.loadUsers() this.loadUsers()
this.newUser = {} this.newUser = {}
this.handleSuccess({message: 'The user was successfully added.'}) this.handleSuccess({message: 'The user was successfully added.'})
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
toggleUserType(userid, current) { toggleUserType(userid, current) {
const cancel = message.setLoading(this)
let right = 0 let right = 0
if (!current) { if (!current) {
right = 2 right = 2
@ -194,16 +203,18 @@
.then(() => { .then(() => {
this.loadUsers() this.loadUsers()
this.handleSuccess({message: 'The user right was successfully updated.'}) this.handleSuccess({message: 'The user right was successfully updated.'})
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
findUsers(query) { findUsers(query) {
this.loading = true; const cancel = message.setLoading(this)
if(query === '') { if(query === '') {
this.$set(this, 'foundUsers', []) this.$set(this, 'foundUsers', [])
this.loading = false cancel()
return return
} }
@ -220,9 +231,10 @@
}) })
} }
this.loading = false cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
@ -233,11 +245,9 @@
return `and ${count} others` return `and ${count} others`
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
}, },

View file

@ -170,7 +170,7 @@
}, },
methods: { methods: {
loadTeam() { loadTeam() {
this.loading = true const cancel = message.setLoading(this)
HTTP.get(`teams/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.get(`teams/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
@ -181,14 +181,14 @@
this.userIsAdmin = true this.userIsAdmin = true
} }
} }
this.loading = false cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) this.handleError(e)
}) })
}, },
submit() { submit() {
this.loading = true const cancel = message.setLoading(this)
HTTP.post(`teams/` + this.$route.params.id, this.team, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.post(`teams/` + this.$route.params.id, this.team, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
@ -200,33 +200,42 @@
} }
} }
this.handleSuccess({message: 'The team was successfully updated.'}) this.handleSuccess({message: 'The team was successfully updated.'})
cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) cancel()
this.handleError(e)
}) })
}, },
deleteTeam() { deleteTeam() {
const cancel = message.setLoading(this)
HTTP.delete(`teams/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.delete(`teams/` + this.$route.params.id, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => { .then(() => {
this.handleSuccess({message: 'The team was successfully deleted.'}) this.handleSuccess({message: 'The team was successfully deleted.'})
cancel()
router.push({name: 'home'}) router.push({name: 'home'})
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
deleteUser() { deleteUser() {
const cancel = message.setLoading(this)
HTTP.delete(`teams/` + this.$route.params.id + `/members/` + this.userToDelete, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.delete(`teams/` + this.$route.params.id + `/members/` + this.userToDelete, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => { .then(() => {
this.showUserDeleteModal = false; this.showUserDeleteModal = false;
this.handleSuccess({message: 'The user was successfully deleted from the team.'}) this.handleSuccess({message: 'The user was successfully deleted from the team.'})
this.loadTeam() this.loadTeam()
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
addUser(admin) { addUser(admin) {
const cancel = message.setLoading(this)
if(admin === null) { if(admin === null) {
admin = false admin = false
} }
@ -234,8 +243,10 @@
.then(() => { .then(() => {
this.loadTeam() this.loadTeam()
this.handleSuccess({message: 'The team member was successfully added.'}) this.handleSuccess({message: 'The team member was successfully added.'})
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
@ -246,11 +257,9 @@
this.addUser(!current) this.addUser(!current)
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
} }

View file

@ -40,19 +40,18 @@
}, },
methods: { methods: {
loadTeams() { loadTeams() {
this.loading = true const cancel = message.setLoading(this)
HTTP.get(`teams`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.get(`teams`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
this.$set(this, 'teams', response.data) this.$set(this, 'teams', response.data)
this.loading = false cancel()
}) })
.catch(e => { .catch(e => {
this.handleError(e) this.handleError(e)
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
} }

View file

@ -45,23 +45,23 @@
}, },
methods: { methods: {
newTeam() { newTeam() {
this.loading = true const cancel = message.setLoading(this)
HTTP.put(`teams`, this.team, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}}) HTTP.put(`teams`, this.team, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => { .then(response => {
router.push({name:'editTeam', params:{id: response.data.id}}) router.push({name:'editTeam', params:{id: response.data.id}})
this.handleSuccess({message: 'The team was successfully created.'}) this.handleSuccess({message: 'The team was successfully created.'})
cancel()
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
message.error(e, this) message.error(e, this)
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
message.success(e, this) message.success(e, this)
} }
} }

View file

@ -36,6 +36,7 @@
import auth from '../../auth' import auth from '../../auth'
import router from '../../router' import router from '../../router'
import {HTTP} from '../../http-common' import {HTTP} from '../../http-common'
import message from '../../message'
export default { export default {
data() { data() {
@ -53,15 +54,15 @@
// Try to verify the email // Try to verify the email
let emailVerifyToken = localStorage.getItem('emailConfirmToken') let emailVerifyToken = localStorage.getItem('emailConfirmToken')
if (emailVerifyToken) { if (emailVerifyToken) {
this.loading = true const cancel = message.setLoading(this)
HTTP.post(`user/confirm`, {token: emailVerifyToken}) HTTP.post(`user/confirm`, {token: emailVerifyToken})
.then(() => { .then(() => {
localStorage.removeItem('emailConfirmToken') localStorage.removeItem('emailConfirmToken')
this.loading = false
this.confirmedEmailSuccess = true this.confirmedEmailSuccess = true
cancel()
}) })
.catch(e => { .catch(e => {
this.loading = false cancel()
this.error = e.response.data.message this.error = e.response.data.message
}) })
} }

View file

@ -38,6 +38,7 @@
<script> <script>
import {HTTP} from '../../http-common' import {HTTP} from '../../http-common'
import message from '../../message'
export default { export default {
data() { data() {
@ -53,11 +54,11 @@
}, },
methods: { methods: {
submit() { submit() {
this.loading = true const cancel = message.setLoading(this)
this.error = '' this.error = ''
if (this.credentials.password2 !== this.credentials.password) { if (this.credentials.password2 !== this.credentials.password) {
this.loading = false cancel()
this.error = 'Passwords don\'t match' this.error = 'Passwords don\'t match'
return return
} }
@ -71,17 +72,17 @@
.then(response => { .then(response => {
this.handleSuccess(response) this.handleSuccess(response)
localStorage.removeItem('passwordResetToken') localStorage.removeItem('passwordResetToken')
cancel()
}) })
.catch(e => { .catch(e => {
this.error = e.response.data.message this.error = e.response.data.message
cancel()
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
this.error = e.response.data.message this.error = e.response.data.message
}, },
handleSuccess(e) { handleSuccess(e) {
this.loading = false
this.successMessage = e.data.message this.successMessage = e.data.message
} }
} }

View file

@ -31,6 +31,7 @@
<script> <script>
import {HTTP} from '../../http-common' import {HTTP} from '../../http-common'
import message from '../../message'
export default { export default {
data() { data() {
@ -43,7 +44,7 @@
}, },
methods: { methods: {
submit() { submit() {
this.loading = true const cancel = message.setLoading(this)
this.error = '' this.error = ''
let credentials = { let credentials = {
user_name: this.username, user_name: this.username,
@ -51,15 +52,15 @@
HTTP.post(`user/password/token`, credentials) HTTP.post(`user/password/token`, credentials)
.then(() => { .then(() => {
this.loading = false cancel()
this.isSuccess = true this.isSuccess = true
}) })
.catch(e => { .catch(e => {
cancel()
this.handleError(e) this.handleError(e)
}) })
}, },
handleError(e) { handleError(e) {
this.loading = false
this.error = e.response.data.message this.error = e.response.data.message
}, },
} }

View file

@ -29,6 +29,7 @@ import { faUsers } from '@fortawesome/free-solid-svg-icons'
import { faUser } from '@fortawesome/free-solid-svg-icons' import { faUser } from '@fortawesome/free-solid-svg-icons'
import { faLock } from '@fortawesome/free-solid-svg-icons' import { faLock } from '@fortawesome/free-solid-svg-icons'
import { faPen } from '@fortawesome/free-solid-svg-icons' import { faPen } from '@fortawesome/free-solid-svg-icons'
import { faTimes } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
library.add(faSignOutAlt) library.add(faSignOutAlt)
@ -43,6 +44,7 @@ library.add(faUsers)
library.add(faUser) library.add(faUser)
library.add(faLock) library.add(faLock)
library.add(faPen) library.add(faPen)
library.add(faTimes)
Vue.component('icon', FontAwesomeIcon) Vue.component('icon', FontAwesomeIcon)

View file

@ -1,4 +1,12 @@
export default { export default {
setLoading(context) {
const timeout = setTimeout(function () {
context.loading = true
}, 100);
return () => {
clearTimeout(timeout);
};
},
error(e, context) { error(e, context) {
// Build the notification text from error response // Build the notification text from error response
let err = e.message let err = e.message
@ -12,6 +20,8 @@ export default {
title: 'Error', title: 'Error',
text: err text: err
}) })
context.loading = false
}, },
success(e, context) { success(e, context) {
// Build the notification text from error response // Build the notification text from error response
@ -26,6 +36,8 @@ export default {
title: 'Success', title: 'Success',
text: err text: err
}) })
context.loading = false
}, },
} }

View file

@ -98,6 +98,10 @@ h1,h2,h3,h4,h5,h6{
width: 96%; width: 96%;
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
.tasktext {
vertical-align: top;
}
} }
input[type="checkbox"] { input[type="checkbox"] {
@ -119,6 +123,33 @@ h1,h2,h3,h4,h5,h6{
.taskedit{ .taskedit{
min-height: calc(100% - 1rem); min-height: calc(100% - 1rem);
margin-top: 1rem; margin-top: 1rem;
.reminder-input{
margin: 0;
&.overdue input{
color: $red;
}
&:last-child {
margin-bottom: 0.75rem;
}
a {
color: $red;
vertical-align: sub;
}
input {
width: 90%;
border: none;
&:focus {
border: none;
box-shadow: none;
}
}
}
} }
.settings{ .settings{
@ -153,4 +184,62 @@ h1,h2,h3,h4,h5,h6{
border-width: 0.25em; border-width: 0.25em;
} }
} }
}
// Fancy Checkboxes
.fancycheckbox {
display: inline-block;
padding-right: 5px;
padding-top: 3px;
.check {
cursor: pointer;
position: relative;
margin: auto;
width: 18px;
height: 18px;
-webkit-tap-highlight-color: transparent;
transform: translate3d(0, 0, 0);
&:hover svg {
stroke: $primary;
}
svg {
position: relative;
z-index: 1;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #c8ccd4;
stroke-width: 1.5;
transform: translate3d(0, 0, 0);
transition: all 0.2s ease;
path {
stroke-dasharray: 60;
stroke-dashoffset: 0;
}
polyline {
stroke-dasharray: 22;
stroke-dashoffset: 66;
}
}
}
input[type=checkbox]:checked + .check svg {
stroke: $primary;
}
input[type=checkbox]:checked + .check svg path {
stroke-dashoffset: 60;
transition: all 0.3s linear;
}
input[type=checkbox]:checked + .check svg polyline {
stroke-dashoffset: 42;
transition: all 0.2s linear;
transition-delay: 0.15s;
}
} }

View file

@ -62,6 +62,7 @@
* [ ] Links an den Freigewordenen Platz Menüpunkte machen à la "Heute"/Morgen/Diese Woche etc. Da kommt dann alles rein was dann due ist. * [ ] Links an den Freigewordenen Platz Menüpunkte machen à la "Heute"/Morgen/Diese Woche etc. Da kommt dann alles rein was dann due ist.
* [ ] Andere Icons? --> Freepikkram * [ ] Andere Icons? --> Freepikkram
* [ ] Fertige Tasks schöner visualisieren * [ ] Fertige Tasks schöner visualisieren
* [ ] Alles abgehakte ausblenden, mit btn zum wieder einblenden
* [ ] Search everything * [ ] Search everything
* [ ] Lists * [ ] Lists

5
vue.config.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
configureWebpack: {
devtool: 'source-map'
}
}