mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
feat(ongoing tasks): added option to close task along with all it's unfinished timeblocks
This commit is contained in:
1 parent
2b47abadfa
commit
ff8130901d
4 files changed
+217
-3
No files matched your search
@@ -22,6 +22,7 @@
|
|||||||
<Z-delete-team-member-modal ref="z-delete-team-member-modal" />
|
<Z-delete-team-member-modal ref="z-delete-team-member-modal" />
|
||||||
<Z-show-menu-modal ref="z-show-menu-modal" />
|
<Z-show-menu-modal ref="z-show-menu-modal" />
|
||||||
<Z-unable-to-edit-office-task-modal ref="z-unable-to-edit-office-task-modal" />
|
<Z-unable-to-edit-office-task-modal ref="z-unable-to-edit-office-task-modal" />
|
||||||
|
<Z-unable-to-complete-task-modal ref="z-unable-to-complete-task-modal" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -40,6 +41,7 @@ import ZMasterTaskModal from '@/plugins/tm@modal/master-task/z-master-task-mod
|
|||||||
import ZDeleteTeamMemberModal from '@/plugins/tm@modal/master-task/z-delete-team-member-modal.vue'
|
import ZDeleteTeamMemberModal from '@/plugins/tm@modal/master-task/z-delete-team-member-modal.vue'
|
||||||
import ZShowMenuModal from '@/plugins/tm@modal/task-cell/z-show-menu-modal.vue'
|
import ZShowMenuModal from '@/plugins/tm@modal/task-cell/z-show-menu-modal.vue'
|
||||||
import ZUnableToEditOfficeTaskModal from '@/plugins/tm@modal/task/z-unable-to-edit-office-task-modal.vue'
|
import ZUnableToEditOfficeTaskModal from '@/plugins/tm@modal/task/z-unable-to-edit-office-task-modal.vue'
|
||||||
|
import ZUnableToCompleteTaskModal from '@/plugins/tm@modal/task/z-unable-to-complete-task-modal.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -57,7 +59,8 @@ export default {
|
|||||||
ZShowMenuModal,
|
ZShowMenuModal,
|
||||||
ZCompleteMasterTaskModal,
|
ZCompleteMasterTaskModal,
|
||||||
ZDeleteTimeBlockModal,
|
ZDeleteTimeBlockModal,
|
||||||
ZUnableToEditOfficeTaskModal
|
ZUnableToEditOfficeTaskModal,
|
||||||
|
ZUnableToCompleteTaskModal
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -141,6 +144,10 @@ export default {
|
|||||||
this.$refs['z-unable-to-edit-office-task-modal'].showModal({ mode })
|
this.$refs['z-unable-to-edit-office-task-modal'].showModal({ mode })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.$events.on('show-unable-to-complete-task-modal', ({ subtask }) => {
|
||||||
|
this.$refs['z-unable-to-complete-task-modal'].showModal({ subtask })
|
||||||
|
})
|
||||||
|
|
||||||
this._showModalsAfterInicialization()
|
this._showModalsAfterInicialization()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ export default {
|
|||||||
state.ongoingSubtasks = subtasks
|
state.ongoingSubtasks = subtasks
|
||||||
},
|
},
|
||||||
|
|
||||||
|
remove_ongoing_subtask(state: any, subtaskId: any) {
|
||||||
|
state.ongoingSubtasks = state.ongoingSubtasks.filter(st => st.id != subtaskId)
|
||||||
|
},
|
||||||
|
|
||||||
update_team(state: any, team: any) {
|
update_team(state: any, team: any) {
|
||||||
state.teams = state.teams.map(t => {
|
state.teams = state.teams.map(t => {
|
||||||
if (t.id == team.id)
|
if (t.id == team.id)
|
||||||
@@ -199,6 +203,10 @@ export default {
|
|||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
removeOngoingSubtask({commit}: {commit: any}, subtaskId: any) {
|
||||||
|
commit('remove_ongoing_subtask', subtaskId)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog v-model="visible" max-width="750" persistent>
|
||||||
|
<v-card>
|
||||||
|
<div class="d-flex justify-space-between align-center">
|
||||||
|
<h1>Sub Task má nedokončené úlohy</h1>
|
||||||
|
|
||||||
|
<v-icon
|
||||||
|
@click.stop="closeModal()"
|
||||||
|
icon="mdi-close"
|
||||||
|
size="large"
|
||||||
|
></v-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-content">
|
||||||
|
<div v-for="(timeblock) in subtask?.timeblocks" :key="timeblock.id" class="d-flex align-center mb-3">
|
||||||
|
<p class="task-badge rounded-pill" :class="{'strike-through': timeblock.is_finished}">{{ tmUtils.formatDateTime(timeblock.start) }} - {{ tmUtils.formatDateTime(timeblock.end) }}</p>
|
||||||
|
<v-icon @click.stop="handleFinishTimeblock(timeblock)" class="finish-icon circle-border cursor-pointer ml-2" :class="{'disabled': loadingTimeblocks[timeblock.id] || loadingCloseAll, 'finished': timeblock.is_finished}" icon="mdi-check" size="medium" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
<v-btn @click="closeModal()" class="button secondary">Zrušiť</v-btn>
|
||||||
|
<v-btn @click="handleFinishAllTimeblocks()" :loading="loadingCloseAll" class="button primary">Uzatvoriť všetky</v-btn>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import modalMixin from '@/plugins/tm@modal/_mixins/modal.mixin'
|
||||||
|
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [ modalMixin ],
|
||||||
|
emits: ['close', 'on-subtask-edited'],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'edit' // edit or delete
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tmUtils,
|
||||||
|
visible: false,
|
||||||
|
subtask: null,
|
||||||
|
loadingTimeblocks: {},
|
||||||
|
loadingCloseAll: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async showModal({ subtask }) {
|
||||||
|
this.subtask = subtask
|
||||||
|
this.visible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
async complete() {
|
||||||
|
console.log('complete')
|
||||||
|
},
|
||||||
|
|
||||||
|
closeModal() {
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleFinishTimeblock(timeblock) {
|
||||||
|
const subtask = JSON.parse(JSON.stringify(this.subtask))
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loadingTimeblocks[timeblock.id] = true
|
||||||
|
await this.$axios.put_auth(`v1/timeblocks/${timeblock.id}`, {...timeblock, is_finished: 1})
|
||||||
|
timeblock.is_finished = 1
|
||||||
|
|
||||||
|
const areAllTimeblocksFinished = subtask.timeblocks.every(timeblock => timeblock.is_finished)
|
||||||
|
|
||||||
|
if (areAllTimeblocksFinished) {
|
||||||
|
await this._finishSubtask()
|
||||||
|
this.$store.dispatch('tm/removeOngoingSubtask', this.subtask.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$events.emit('on-subtask-edited', subtask)
|
||||||
|
this.closeModal()
|
||||||
|
} catch (error) {
|
||||||
|
this.$toast.error(error.message || error)
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'development')
|
||||||
|
console.log('[a-task-timeblocks.vue] Error: ', error)
|
||||||
|
} finally {
|
||||||
|
this.loadingTimeblocks[timeblock.id] = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleFinishAllTimeblocks() {
|
||||||
|
this.loadingCloseAll = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const promises = this.subtask.timeblocks.map(timeblock => {
|
||||||
|
return this.$axios.put_auth(`v1/timeblocks/${timeblock.id}`, {...timeblock, is_finished: 1})
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
|
||||||
|
this.subtask.timeblocks = this.subtask.timeblocks.map(timeblock => {
|
||||||
|
timeblock.is_finished = 1
|
||||||
|
return timeblock
|
||||||
|
})
|
||||||
|
|
||||||
|
await this._finishSubtask()
|
||||||
|
|
||||||
|
this.$store.dispatch('tm/removeOngoingSubtask', this.subtask.id)
|
||||||
|
this.$events.emit('on-subtask-edited', this.subtask)
|
||||||
|
this.closeModal()
|
||||||
|
} catch (error) {
|
||||||
|
this.$toast.error(error.message || error)
|
||||||
|
} finally {
|
||||||
|
this.loadingCloseAll = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async _finishSubtask() {
|
||||||
|
const tempSubtask = {...this.subtask, is_finished: 1, timesheets: this.subtask.timesheets.map((timesheet) => {
|
||||||
|
delete timesheet.employee_name
|
||||||
|
return timesheet
|
||||||
|
})}
|
||||||
|
|
||||||
|
return this.$axios.put_auth(`v1/subtasks/${tempSubtask.id}`, tempSubtask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.card-content {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-actions {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finish-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.finished {
|
||||||
|
background: #06dece;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
<td class="task-action">
|
<td class="task-action">
|
||||||
<div class="d-flex justify-start align-center">
|
<div class="d-flex justify-start align-center">
|
||||||
|
<img class="pencil-icon mr-3" :class="{'disabled': loadingSubtask[subtask.id]}" :src="checkmarkDouble" @click.stop="handleCompleteTask(subtask)">
|
||||||
|
|
||||||
<template v-if="subtask.type == 'office365'">
|
<template v-if="subtask.type == 'office365'">
|
||||||
<img class="pencil-icon" :src="eye" @click.stop="editTask(subtask, false)">
|
<img class="pencil-icon" :src="eye" @click.stop="editTask(subtask, false)">
|
||||||
<img class="office-icon" :src="office365" />
|
<img class="office-icon" :src="office365" />
|
||||||
@@ -47,7 +49,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { bin, eye, office365 } from '@/plugins/app/_img'
|
import { bin, eye, office365, checkmarkDouble } from '@/plugins/app/_img'
|
||||||
|
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
@@ -64,7 +66,9 @@ export default {
|
|||||||
return {
|
return {
|
||||||
eye,
|
eye,
|
||||||
bin,
|
bin,
|
||||||
office365
|
office365,
|
||||||
|
checkmarkDouble,
|
||||||
|
loadingSubtask: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -77,6 +81,16 @@ export default {
|
|||||||
this.$events.emit('show-subtask-modal', {subtask, editMode})
|
this.$events.emit('show-subtask-modal', {subtask, editMode})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async handleCompleteTask(subtask) {
|
||||||
|
const areAllTimeBlocksFinished = subtask.timeblocks.every(tb => tb.is_finished)
|
||||||
|
|
||||||
|
if (!areAllTimeBlocksFinished) {
|
||||||
|
this.$events.emit('show-unable-to-complete-task-modal', { subtask })
|
||||||
|
} else {
|
||||||
|
await this._finishSubtask(subtask)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
deleteTask(subtask) {
|
deleteTask(subtask) {
|
||||||
if (subtask.type == 'office365')
|
if (subtask.type == 'office365')
|
||||||
return this.$events.emit('show-unable-to-edit-office-task-modal', { mode: 'delete' })
|
return this.$events.emit('show-unable-to-edit-office-task-modal', { mode: 'delete' })
|
||||||
@@ -110,6 +124,28 @@ export default {
|
|||||||
const timeblockDeadline = this.getSelectedTimeblockDeadline(subtask.timeblocks)
|
const timeblockDeadline = this.getSelectedTimeblockDeadline(subtask.timeblocks)
|
||||||
|
|
||||||
return timeblockDeadline !== '' ? timeblockDeadline : 'Žiadny termín'
|
return timeblockDeadline !== '' ? timeblockDeadline : 'Žiadny termín'
|
||||||
|
},
|
||||||
|
|
||||||
|
async _finishSubtask(subtask) {
|
||||||
|
const tempSubtask = {...subtask, is_finished: 1, timesheets: subtask?.timesheets.map((timesheet) => {
|
||||||
|
delete timesheet.employee_name
|
||||||
|
return timesheet
|
||||||
|
}) || []}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loadingSubtask[subtask.id] = true
|
||||||
|
await this.$axios.put_auth(`v1/subtasks/${tempSubtask.id}`, tempSubtask)
|
||||||
|
|
||||||
|
this.$store.dispatch('tm/removeOngoingSubtask', subtask.id)
|
||||||
|
this.$events.emit('on-subtask-edited', subtask)
|
||||||
|
} catch (error) {
|
||||||
|
this.$toast.error(error.message || error)
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'development')
|
||||||
|
console.log('[a-ongoing-table.vue] Error: ', error)
|
||||||
|
} finally {
|
||||||
|
this.loadingSubtask[subtask.id] = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,6 +170,10 @@ v-table, th, td
|
|||||||
margin: 6px
|
margin: 6px
|
||||||
padding: 6px
|
padding: 6px
|
||||||
|
|
||||||
|
.disabled
|
||||||
|
opacity: 0.5
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
tbody
|
tbody
|
||||||
tr:hover
|
tr:hover
|
||||||
background: rgba(0,0,0,.1)
|
background: rgba(0,0,0,.1)
|
||||||
|
|||||||
Reference in new issue
Block a user