mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 06:20:21 +00:00
[timeblocks]
This commit is contained in:
1 parent
cb44878dcb
commit
6a89b7bc47
5 files changed
+218
-17
No files matched your search
@@ -7,6 +7,12 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
setFocusEvent() {
|
||||
document.querySelectorAll('.v-autocomplete input').forEach(input => {
|
||||
input.addEventListener('focus', this.onInputFocus)
|
||||
})
|
||||
},
|
||||
|
||||
onDateFocus($event) {
|
||||
if ($event == undefined) {
|
||||
this.preventInputFocus = true
|
||||
|
||||
@@ -98,7 +98,10 @@ export default {
|
||||
this.starts_at_time = null
|
||||
this.ends_at_time = null
|
||||
|
||||
this.$nextTick(() => this.v$.$reset())
|
||||
this.$nextTick(() => {
|
||||
this.setFocusEvent()
|
||||
this.v$.$reset()
|
||||
})
|
||||
|
||||
try {
|
||||
if (blocker?.id) {
|
||||
|
||||
@@ -260,6 +260,7 @@ export default {
|
||||
this.visible = true
|
||||
this.mastertask = null
|
||||
this.$nextTick(() => {
|
||||
this.setFocusEvent()
|
||||
this.v$.$reset()
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div class="timeblock w-100">
|
||||
<div v-if="deletable && editable" class="x-button">
|
||||
<button @click="$emit('removeTimeblock')" class="x-button">x</button>
|
||||
</div>
|
||||
<Datepicker :model-value="timeblock.date" @focus="$emit('onDateFocus')" @update:model-value="updateDate" format="dd.MM.yyyy" locale="sk" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Dátum" :auto-apply="true" :enable-time-picker="false" class="mb-5 modal-input" />
|
||||
<div class="splitted-time-inputs">
|
||||
<v-text-field :value="timeblock.starts_at_time" @input="updateStartTime" @focus="onInputFocus" :class="{ error: v$?.timeblock.starts_at_time.$errors.length }" type="time" variant="solo" bg-color="white" label="Začiatok" />
|
||||
<v-text-field :value="timeblock.ends_at_time" @input="updateEndTime" @focus="onInputFocus" :class="{ error: v$?.timeblock.ends_at_time.$errors.length }" type="time" variant="solo" bg-color="white" label="Koniec" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import useVuelidate from '@vuelidate/core'
|
||||
import { requiredIf } from '@vuelidate/validators'
|
||||
import Datepicker from '@vuepic/vue-datepicker'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Datepicker
|
||||
},
|
||||
|
||||
|
||||
props: {
|
||||
timeblock: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
|
||||
deletable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
v$: useVuelidate(),
|
||||
}
|
||||
},
|
||||
|
||||
validations() {
|
||||
return {
|
||||
timeblock: {
|
||||
starts_at_time: { required: requiredIf(this.timeblock.date) },
|
||||
ends_at_time: { required: requiredIf(this.timeblock.date) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onFocus() {
|
||||
console.log('focus')
|
||||
},
|
||||
updateDate(date) {
|
||||
const timeblock = { ...this.timeblock }
|
||||
if (date) timeblock.date = date
|
||||
this.$emit('updateTimeblock', timeblock)
|
||||
},
|
||||
updateStartTime(event) {
|
||||
const timeblock = { ...this.timeblock }
|
||||
timeblock.starts_at_time = event.target.value
|
||||
this.$emit('updateTimeblock', timeblock)
|
||||
},
|
||||
updateEndTime(event) {
|
||||
const timeblock = { ...this.timeblock }
|
||||
timeblock.ends_at_time = event.target.value
|
||||
this.$emit('updateTimeblock', timeblock)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.timeblock
|
||||
position: relative
|
||||
border-top: 1px solid #e0e0e0
|
||||
padding: 20px 0 5px 0
|
||||
.splitted-time-inputs
|
||||
display: flex !important
|
||||
flex-direction: row !important
|
||||
gap: 15px
|
||||
margin-bottom: -11px
|
||||
|
||||
.x-button
|
||||
display: flex
|
||||
justify-content: flex-end
|
||||
align-items: center
|
||||
position: relative
|
||||
bottom: 5px
|
||||
|
||||
button
|
||||
margin-right: 5px
|
||||
|
||||
.error input
|
||||
border: 1px solid red !important
|
||||
</style>e
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<div class="splitted-inputs">
|
||||
<!-- TODO: Bialko validacie -->
|
||||
<Datepicker v-model="subtask.deadline" :auto-apply="true" @focus="onDateFocus" :min-date="new Date()" class="modal-input left" format="dd.MM.yyyy" locale="sk" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Deadline" :class="{ error: v$.subtask.deadline.$errors?.length }" :validations="v$.subtask.deadline" />
|
||||
<Datepicker v-model="subtask.deadline" @focus="onDateFocus" :auto-apply="true" :min-date="new Date()" class="modal-input left" format="dd.MM.yyyy" locale="sk" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Deadline" :class="{ error: v$.subtask.deadline.$errors?.length }" :validations="v$.subtask.deadline" />
|
||||
|
||||
<v-text-field class="modal-input right" variant="solo" v-model="subtask._deadline_time" label="Čas" append-inner-icon="mdi-clock-outline" type="time" @focus="onInputFocus" :class="{ error: v$.subtask._deadline_time.$errors.length }" :hint="v$.subtask._deadline_time.$errors ? errorUtils.parseErrors(v$.subtask._deadline_time.$errors) : null" />
|
||||
</div>
|
||||
@@ -62,11 +62,28 @@
|
||||
<!-- <div class="w-100">
|
||||
<Datepicker v-model="subtask.date" :class="{ error: v$.subtask.date.$errors.length }" format="dd.MM.yyyy" locale="sk" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Dátum" :auto-apply="true" :enable-time-picker="false" @focus="onDateFocus" class="mb-5 modal-input" />
|
||||
<div class="splitted-time-inputs">
|
||||
<v-text-field v-model="subtask.starts_at_time" :class="{ error: v$.subtask.starts_at_time.$errors.length }" type="time" variant="solo" bg-color="white" label="Začiatok" />
|
||||
<v-text-field v-model="subtask.ends_at_time" :class="{ error: v$.subtask.ends_at_time.$errors.length }" type="time" variant="solo" bg-color="white" label="Koniec" />
|
||||
<v-text-field :class="{ error: v$.subtask.starts_at_time.$errors.length }" type="time" variant="solo" bg-color="white" label="Začiatok" />
|
||||
<v-text-field :class="{ error: v$.subtask.ends_at_time.$errors.length }" type="time" variant="solo" bg-color="white" label="Koniec" />
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<template v-if="!subtask.id">
|
||||
<div class="timeblocks w-100">
|
||||
<A-timeblock v-for="(timeblock, index) in timeblocks" :key="index" @updateTimeblock="val => updateTimeblock(val, index)" @removeTimeblock="removeTimeblock(index)" @onDateFocus="onDateFocus" :timeblock="timeblock" :deletable="index > 0" editable :ref="`timetable-${index}`" />
|
||||
<div class="end-button w-100">
|
||||
<button @click="timeblocks.push({date: null, starts_at_time: null, ends_at_time: null})">Pridať časový blok</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-else v-for="(timeblock, index) in subtask.timeblocks" :key="index" class="timeblock w-100">
|
||||
<Datepicker :model-value="timeblock.start.substring(0, 10)" format="dd.MM.yyyy" locale="sk" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Dátum" :auto-apply="true" :enable-time-picker="false" readonly class="mb-5 modal-input" />
|
||||
<div class="splitted-time-inputs w-100">
|
||||
<v-text-field :value="timeblock.start.substring(11, 16)" type="time" variant="solo" bg-color="white" label="Začiatok" />
|
||||
<v-text-field :value="timeblock.end.substring(11, 16)" type="time" variant="solo" bg-color="white" label="Koniec" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-100 modal-input">
|
||||
<QuillEditor ref="description" v-model="subtask.description" placeholder="Popis" theme="snow" class="rich-text-editor" @input="subtask.description = $event.srcElement.innerHTML" />
|
||||
</div>
|
||||
@@ -123,7 +140,7 @@ import Datepicker from '@vuepic/vue-datepicker'
|
||||
import ZFilesUpload from '@/plugins/tm@_components/z-files-upload.vue'
|
||||
import ATaskTimeblocks from '@/plugins/tm@modal/task/_components/_components/a-task-timeblocks.vue'
|
||||
import useVuelidate from '@vuelidate/core'
|
||||
import { required, requiredIf } from '@vuelidate/validators'
|
||||
import { required, requiredIf, optional } from '@vuelidate/validators'
|
||||
import errorUtils from '@/plugins/lib@auth/_utils/error.utils'
|
||||
import { QuillEditor } from '@vueup/vue-quill'
|
||||
import VuetifyFixFocusMixin from '@/plugins/tm@modal/_mixins/vuetify-fix-focus'
|
||||
@@ -135,15 +152,19 @@ import '@vueup/vue-quill/dist/vue-quill.snow.css'
|
||||
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
|
||||
|
||||
import ZTaskReadonlyInfo from './_components/z-task-readonly-info.vue'
|
||||
import ATimeblock from './_components/a-timeblock.vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import modalMixin from '@/plugins/tm@modal/_mixins/modal.mixin'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
emits: ['close', 'refresh'],
|
||||
mixins: [VuetifyFixFocusMixin, SelectsMixin, AutocompleteMixin, modalMixin],
|
||||
|
||||
components: {
|
||||
ZTaskReadonlyInfo,
|
||||
ATimeblock,
|
||||
ZFilesUpload,
|
||||
Datepicker,
|
||||
// ATaskTimeblocks,
|
||||
@@ -160,9 +181,13 @@ export default {
|
||||
errorUtils,
|
||||
v$: useVuelidate(),
|
||||
employeeValidations: [],
|
||||
preventInputFocus: false,
|
||||
showDatePicker: false,
|
||||
selectedNewDate: []
|
||||
selectedNewDate: [],
|
||||
timeblocks: [{
|
||||
date: null,
|
||||
starts_at_time: null,
|
||||
ends_at_time: null
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -176,9 +201,6 @@ export default {
|
||||
master_task_id: { required },
|
||||
deadline: { required },
|
||||
_deadline_time: { required },
|
||||
// starts_at_time: { requiredIf: requiredIf(this.subtask.date) },
|
||||
// ends_at_time: { requiredIf: requiredIf(this.subtask.starts_at_time) }
|
||||
// description: { required }
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -210,11 +232,28 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateTimeblock(val, index) {
|
||||
this.timeblocks[index] = val
|
||||
},
|
||||
|
||||
removeTimeblock(index) {
|
||||
this.timeblocks.splice(index, 1)
|
||||
},
|
||||
|
||||
async showModal({ subtask, editMode = false, mastertask = {} }) {
|
||||
console.log(subtask)
|
||||
this.isSaving = false
|
||||
this.subtask = null
|
||||
this.visible = true
|
||||
this.$nextTick(() => this.v$.$reset())
|
||||
this.timeblocks = [{
|
||||
date: null,
|
||||
starts_at_time: null,
|
||||
ends_at_time: null
|
||||
}]
|
||||
this.$nextTick(() => {
|
||||
this.v$.$reset()
|
||||
this.setFocusEvent()
|
||||
})
|
||||
|
||||
try {
|
||||
if (subtask?.id) {
|
||||
@@ -263,9 +302,10 @@ export default {
|
||||
|
||||
async save() {
|
||||
const employeesValid = await this._validateEmployees()
|
||||
const timeblocksValid = this.subtask?.id ? true : await this._validateTimeblocks()
|
||||
|
||||
if (!(await this.v$.$validate())) return
|
||||
if (!employeesValid) return
|
||||
if (!employeesValid || !timeblocksValid) return
|
||||
|
||||
this.subtask.description = this.$refs['description'].getHTML()
|
||||
this._mergeDeadlineTimeDate()
|
||||
@@ -286,7 +326,13 @@ export default {
|
||||
this.$store.commit('tm/update_subtask', subtask)
|
||||
this.$events.emit('on-subtask-edited', this.subtask)
|
||||
} else {
|
||||
const { subtasks } = await this.$axios.post_auth(`v1/mastertasks/${this.subtask.master_task_id}/subtasks`, this.subtask)
|
||||
this.subtask.employees.forEach(employee => {
|
||||
if (employee.id)
|
||||
employee.timeblocks = this.timeblocks.filter(timeblock => timeblock.date).map(timeblock => { return { start: `${moment(timeblock.date).format('YYYY-MM-DD')} ${timeblock.starts_at_time}:00`, end: `${moment(timeblock.date).format('YYYY-MM-DD')} ${timeblock.ends_at_time}:00`, id: null } })
|
||||
})
|
||||
const subtaskCopy = JSON.parse(JSON.stringify(this.subtask))
|
||||
delete subtaskCopy._master_task
|
||||
const { subtasks } = await this.$axios.post_auth(`v1/mastertasks/${this.subtask.master_task_id}/subtasks`, subtaskCopy)
|
||||
this.subtask.id = subtasks[0].id
|
||||
|
||||
subtasks.forEach((subtask) => {
|
||||
@@ -364,6 +410,15 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
async _validateTimeblocks() {
|
||||
let invalid = false
|
||||
for(let i = 0; i < this.timeblocks.length; i++) {
|
||||
const ref = this.$refs[`timetable-${i}`]
|
||||
invalid = await ref[0].v$.$validate() || invalid
|
||||
}
|
||||
return invalid
|
||||
},
|
||||
|
||||
_enrichTask(subtask) {
|
||||
return {
|
||||
...subtask,
|
||||
@@ -470,8 +525,41 @@ export default {
|
||||
.files
|
||||
margin-top: 104px !important
|
||||
|
||||
// .splitted-time-inputs
|
||||
// display: flex !important
|
||||
// flex-direction: row !important
|
||||
// gap: 15px
|
||||
.timeblocks
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
border-bottom: 1px solid #e0e0e0
|
||||
|
||||
.end-button
|
||||
width: 100%
|
||||
display: flex
|
||||
justify-content: flex-end
|
||||
align-items: center
|
||||
border-top: 1px solid #e0e0e0
|
||||
height: 70px
|
||||
|
||||
button
|
||||
background: #7f39fb !important
|
||||
border-radius: 4px !important
|
||||
color: #fff !important
|
||||
height: 50px
|
||||
width: 203px
|
||||
|
||||
.splitted-time-inputs
|
||||
display: flex !important
|
||||
flex-direction: row !important
|
||||
gap: 15px
|
||||
margin-bottom: -11px
|
||||
|
||||
.timeblock
|
||||
position: relative
|
||||
border-top: 1px solid #e0e0e0
|
||||
padding: 20px 0 10px 0
|
||||
|
||||
.splitted-time-inputs
|
||||
display: flex !important
|
||||
flex-direction: row !important
|
||||
gap: 15px
|
||||
margin-bottom: -11px
|
||||
</style>
|
||||
Reference in new issue
Block a user