mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
Merge branch 'main' of bitbucket.org:studiotem/jandl-taskmanager-app
This commit is contained in:
commit
bbc476f5cd
17 files changed
+323
-265
No files matched your search
@@ -7,8 +7,9 @@
|
||||
<td>{{ mastertask.name }}</td>
|
||||
<td class="task-action">
|
||||
<div class="d-flex justify-start">
|
||||
<img class="pencil-icon" :src="require('@/plugins/app/_img/icons/pencil.svg')" alt="pencil-icon" @click.stop="openEditMasterTaskModal(mastertask)">
|
||||
<img class="trash-icon" :src="require('@/plugins/app/_img/icons/bin.svg')" alt="trash-icon" @click.stop="openDeleteMasterTaskModal(mastertask)">
|
||||
<img class="pencil-icon" :src="require('@/plugins/app/_img/icons/pencil.svg')" alt="pencil-icon" @click.stop="openEditMasterTaskModal(mastertask, true)">
|
||||
<img class="pencil-icon ml-2" :src="require('@/plugins/app/_img/icons/eye.svg')" alt="eye-icon" @click.stop="openEditMasterTaskModal(mastertask, false)">
|
||||
<img class="trash-icon ml-2" :src="require('@/plugins/app/_img/icons/bin.svg')" alt="trash-icon" @click.stop="openDeleteMasterTaskModal(mastertask)">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -36,8 +37,8 @@ export default {
|
||||
this.$events.emit('show-delete-master-task-modal', { mastertask })
|
||||
},
|
||||
|
||||
openEditMasterTaskModal(mastertask) {
|
||||
this.$events.emit('show-mastertask-modal', { mastertask, editMode: false })
|
||||
openEditMasterTaskModal(mastertask, editMode) {
|
||||
this.$events.emit('show-mastertask-modal', { mastertask, editMode })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
<div class="d-flex justify-start">
|
||||
<img v-if="subtask.is_finished" class="pencil-icon is-finished" :src="require('@/plugins/app/_img/icons/checkmark.svg')" alt="checkmark-icon" @click.stop="clickCompleteTask(subtask)">
|
||||
<img v-if="!subtask.is_finished" class="pencil-icon" :src="require('@/plugins/app/_img/icons/checkmark.svg')" alt="checkmark-icon" @click.stop="clickCompleteTask(subtask)">
|
||||
<img class="pencil-icon ml-2" :src="require('@/plugins/app/_img/icons/eye.svg')" alt="pencil-icon" @click.stop="openTaskModal(subtask)">
|
||||
<img class="trash-icon" :src="require('@/plugins/app/_img/icons/bin.svg')" alt="trash-icon" @click.stop="openDeleteTaskModal(subtask)">
|
||||
<img class="pencil-icon ml-2" :src="require('@/plugins/app/_img/icons/pencil.svg')" alt="pencil-icon" @click.stop="openTaskModal(subtask, true)">
|
||||
<img class="pencil-icon ml-2" :src="require('@/plugins/app/_img/icons/eye.svg')" alt="eye-icon" @click.stop="openTaskModal(subtask, false)">
|
||||
<img class="trash-icon ml-2" :src="require('@/plugins/app/_img/icons/bin.svg')" alt="trash-icon" @click.stop="openDeleteTaskModal(subtask)">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -61,8 +62,8 @@ export default {
|
||||
this.$events.emit('show-delete-task-modal', { subtask })
|
||||
},
|
||||
|
||||
openTaskModal(subtask) {
|
||||
this.$events.emit('show-subtask-modal', { subtask, editMode: false })
|
||||
openTaskModal(subtask, editMode) {
|
||||
this.$events.emit('show-subtask-modal', { subtask, editMode })
|
||||
},
|
||||
|
||||
async clickCompleteTask(subtask) {
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<form>
|
||||
<A-search-result-mastertasks v-if="searchResults?.mastertasks" :mastertasks="searchResults.mastertasks"/>
|
||||
<A-search-result-subtasks v-if="searchResults?.subtasks" :subtasks="searchResults.subtasks" />
|
||||
<A-search-result-team v-if="searchResults?.teams" :teams="searchResults.teams" />
|
||||
</form>
|
||||
<div>
|
||||
<p v-if="!this.searchResults" class="px-3 py-3">{{ this.search?.length ? $t('Searching...') : $t('Type something') }}</p>
|
||||
<p v-else-if="this.searchResults.error" class="px-3">{{ this.searchResults.error }}</p>
|
||||
|
||||
<div v-else>
|
||||
<A-search-result-mastertasks :mastertasks="searchResults.mastertasks"/>
|
||||
<A-search-result-subtasks :subtasks="searchResults.subtasks" />
|
||||
<A-search-result-team :teams="searchResults.teams" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -21,33 +26,59 @@ export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
searchResults: {},
|
||||
searchResults: null,
|
||||
search: ''
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$events.on('onSearch', (search) => {
|
||||
if (!search?.length)
|
||||
return
|
||||
|
||||
this.search = search
|
||||
this.onSearch(search)
|
||||
})
|
||||
},
|
||||
|
||||
watch: {
|
||||
search(newVal) {
|
||||
this.updateSearchResults(newVal)
|
||||
}
|
||||
mounted() {
|
||||
this.$events.on('on-subtask-edited', () => {
|
||||
this.onSearch(this.search, false)
|
||||
})
|
||||
|
||||
this.$events.on('on-subtask-deleted', () => {
|
||||
this.onSearch(this.search, false)
|
||||
})
|
||||
|
||||
this.$events.on('on-mastertask-edited', () => {
|
||||
this.onSearch(this.search, false)
|
||||
})
|
||||
|
||||
this.$events.on('on-mastertask-deleted', () => {
|
||||
this.onSearch(this.search, false)
|
||||
})
|
||||
},
|
||||
|
||||
unmounted() {
|
||||
this.$events.off('on-subtask-edited')
|
||||
this.$events.off('on-subtask-deleted')
|
||||
this.$events.off('on-mastertask-edited')
|
||||
this.$events.off('on-mastertask-deleted')
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateSearchResults: debounce(async function (search) {
|
||||
onSearch: debounce(async function (search, reset = true) {
|
||||
try {
|
||||
if (reset)
|
||||
this.searchResults = null
|
||||
|
||||
const { mastertasks, subtasks, teams} = await this.$axios.get_auth(`v1/search?q=${search}`)
|
||||
this.searchResults = {mastertasks: mastertasks.data, subtasks: subtasks.data, teams: teams.data}
|
||||
console.log(this.searchResults)
|
||||
} catch (error) {
|
||||
this.$toast.error(error)
|
||||
this.searchResults = {error}
|
||||
}
|
||||
}, 500)
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -26,10 +26,11 @@
|
||||
<template v-slot:split-label="{ split }">
|
||||
<div class="w-100 h-100 d-flex flex-column">
|
||||
<span v-if="split.date" class="d-flex align-center">
|
||||
<img v-if="getHeadingDate(split.date) == 'Dnes'" :src="require('@/plugins/app/_img/icons/home.svg')" class="mr-2">
|
||||
<img v-if="split.isHomeoffice" :src="require('@/plugins/app/_img/icons/home.svg')" class="mr-2">
|
||||
<p class="weekday-label">{{ getHeadingDate(split.date) }}</p>
|
||||
</span>
|
||||
<span v-else class="d-flex align-center">
|
||||
<img v-if="split.isHomeoffice" :src="require('@/plugins/app/_img/icons/home.svg')" class="mr-2">
|
||||
<p class="weekday-label">{{ split.name }}</p>
|
||||
</span>
|
||||
|
||||
@@ -64,16 +65,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="event.blocker?.type != 'home_office'" class="custom-event-body w-100 h-100 d-flex flex-column justify-space-between" :class="{'short-event' : getEventLength(event) <= 9000000, 'mini-event' : getEventLength(event) <= 5400000, 'micro-event' : getEventLength(event) <= 3600000}" :style="{backgroundColor: event.color || event.subtask.color}">
|
||||
<div class="event-content" :class="event.type || 'subtask'">
|
||||
<template v-if="event.type == 'blocker'">
|
||||
<h3>{{ event.blocker.name }}</h3>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div v-else-if="['subtask', 'blocker', 'adhoc', 'vacation', 'free_day'].includes(event.type)" class="custom-event-body w-100 h-100 d-flex flex-column justify-space-between" :class="{'short-event' : getEventLength(event) <= 9000000, 'mini-event' : getEventLength(event) <= 5400000, 'micro-event' : getEventLength(event) <= 3600000}" :style="{backgroundColor: event.color || event.subtask.color}">
|
||||
<div class="event-content" :class="{'is-blocker': event.isBlocker}">
|
||||
<template v-if="event.type == 'subtask'">
|
||||
<small v-if="event.subtask.project_code" class="project-code">{{ event.subtask.project_code }}</small>
|
||||
<h3>{{ event.subtask.master_task?.name || event.subtask.master_task }}</h3>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<h3>{{ event.blocker.name }}</h3>
|
||||
</template>
|
||||
|
||||
<div class="d-flex align-center event-details event-initials-wrapper">
|
||||
<div class="event-initials d-flex justify-center align-center">
|
||||
@@ -84,12 +85,12 @@
|
||||
</div>
|
||||
|
||||
<div class="event-footer d-flex">
|
||||
<template v-if="event.type == 'blocker'">
|
||||
<template v-if="event.type == 'adhoc'">
|
||||
<v-btn rounded class="event-footer-button me-2" @click.stop="editBlocker(event)"><img :src="require('@/plugins/app/_img/icons/pencil.svg')" alt=""></v-btn>
|
||||
<v-btn rounded class="event-footer-button me-2" @click.stop="deleteBlocker(event)"><img :src="require('@/plugins/app/_img/icons/bin.svg')" alt=""></v-btn>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<template v-else-if="event.type == 'subtask'">
|
||||
<v-btn rounded class="event-footer-button me-2" :class="{'completed': event.subtask?.is_finished}" @click.stop="completeTask(event)"><img :src="require('@/plugins/app/_img/icons/checkmark.svg')" alt=""></v-btn>
|
||||
<v-btn rounded class="event-footer-button me-2" @click.stop="editTask(event)"><img :src="require('@/plugins/app/_img/icons/eye.svg')" alt=""></v-btn>
|
||||
<v-btn rounded class="event-footer-button me-2" @click.stop="deleteTask(event)"><img :src="require('@/plugins/app/_img/icons/bin.svg')" alt=""></v-btn>
|
||||
@@ -209,11 +210,7 @@ export default {
|
||||
},
|
||||
|
||||
taskClicked(subtask) {
|
||||
if (subtask.type == 'non-working-hours') return
|
||||
// console.log("task", task)
|
||||
if (subtask.end.getTime() - subtask.start.getTime() <= 3600000) {
|
||||
// console.log("ShortTask")
|
||||
}
|
||||
// console.log('task clicked')
|
||||
},
|
||||
|
||||
eventFocused(event) {
|
||||
@@ -385,7 +382,7 @@ export default {
|
||||
padding: 20px 10px
|
||||
|
||||
.event-content
|
||||
&.blocker
|
||||
&.is-blocker
|
||||
*
|
||||
color: #ffffff !important
|
||||
|
||||
|
||||
@@ -3,10 +3,15 @@ import moment from 'moment'
|
||||
|
||||
export default {
|
||||
generateTimelineTasks(subtasks, date, employee) {
|
||||
return [
|
||||
...this.generateTimelineEvents(subtasks, employee, date),
|
||||
...this.generateTimelineNonWorkingHours(date, employee)
|
||||
]
|
||||
const { tasks, blockers, homeoffices } = this.generateTimelineEvents(subtasks, employee, date)
|
||||
const { nonWorkingHours } = this.generateTimelineNonWorkingHours(date, employee)
|
||||
|
||||
return {
|
||||
tasks,
|
||||
blockers,
|
||||
nonWorkingHours,
|
||||
homeoffices
|
||||
}
|
||||
},
|
||||
|
||||
getUnscheduledTime(subtasks) {
|
||||
@@ -21,19 +26,24 @@ export default {
|
||||
},
|
||||
|
||||
generateTimelineEvents(subtasks, employee, date) {
|
||||
let events = []
|
||||
let blockers = []
|
||||
const tasks = []
|
||||
|
||||
subtasks.forEach(subtask => {
|
||||
if (subtask.type == 'blocker') {
|
||||
events = [...events, ...this._generateEventsFromBlocker(subtask, date)]
|
||||
if (['blocker', 'adhoc', 'home_office', 'vacation', 'free_day'].includes(subtask.type)) {
|
||||
blockers = [...blockers, ...this._generateEventsFromBlocker(subtask, date)]
|
||||
} else {
|
||||
subtask.timeblocks?.forEach((timeblock) => {
|
||||
events.push(this._composeTimelineEvent(subtask, employee, date, timeblock))
|
||||
tasks.push(this._composeTimelineEvent(subtask, employee, date, timeblock))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return events
|
||||
return {
|
||||
tasks,
|
||||
blockers: blockers.filter(blocker => blocker.type != 'home_office'),
|
||||
homeoffices: blockers.filter(blocker => blocker.type == 'home_office')
|
||||
}
|
||||
},
|
||||
|
||||
_composeTimelineEvent(subtask, employee, date, timeblock) {
|
||||
@@ -50,7 +60,10 @@ export default {
|
||||
split: this._getSplitFromDate(date, timeblock.start),
|
||||
project_code: subtask.project_code,
|
||||
subtask: subtask,
|
||||
_timeblock_id: timeblock.id
|
||||
type: 'subtask',
|
||||
_timeblock_id: timeblock.id,
|
||||
_real_start: new Date(timeblock.start),
|
||||
_real_end: new Date(timeblock.end)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -58,8 +71,7 @@ export default {
|
||||
const start = moment(blocker.start)
|
||||
const end = moment(blocker.end)
|
||||
|
||||
const dates = start == end ? [moment(start).format('YYYY-MM-DD')] : this._getDatesInRange(start, end)
|
||||
|
||||
const dates = start.isSame(end, 'date') ? [moment(start).format('YYYY-MM-DD')] : this._getDatesInRange(start, end)
|
||||
const blockers = dates.map(d => {
|
||||
const diffInDays = differenceInCalendarDays(date, new Date(d))
|
||||
const dStart = new Date(moment(d).set({hours: start.hours(), minutes: start.minutes()}).format('YYYY-MM-DD HH:mm'))
|
||||
@@ -71,10 +83,13 @@ export default {
|
||||
start: addDays(dStart, diffInDays),
|
||||
end: addDays(dEnd, diffInDays),
|
||||
date: new Date(d),
|
||||
type: 'blocker',
|
||||
type: blocker.type,
|
||||
isBlocker: true,
|
||||
initials: `${blocker.employee?.name[0]}${blocker.employee?.surname[0]}`,
|
||||
color: '#B00020',
|
||||
draggable: false
|
||||
draggable: false,
|
||||
_real_start: dStart,
|
||||
_real_end: dEnd
|
||||
}
|
||||
})
|
||||
|
||||
@@ -117,7 +132,7 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
return events
|
||||
return {nonWorkingHours: events}
|
||||
},
|
||||
|
||||
_getSplitFromDate(date, start) {
|
||||
@@ -125,8 +140,12 @@ export default {
|
||||
},
|
||||
|
||||
_getDifferenceInHours(date1, date2) {
|
||||
let diff =(date1.getTime() - date2.getTime()) / 1000;
|
||||
diff /= (60 * 60)
|
||||
return this._getDifferenceInMinutes(date1, date2) / 60
|
||||
},
|
||||
|
||||
_getDifferenceInMinutes(date1, date2) {
|
||||
let diff = (date1.getTime() - date2.getTime()) / 1000
|
||||
diff /= 60
|
||||
return Math.abs(Math.round(diff))
|
||||
},
|
||||
|
||||
|
||||
@@ -36,30 +36,78 @@ export default {
|
||||
]),
|
||||
|
||||
splitDays() {
|
||||
const days = []
|
||||
const workingHours = {}
|
||||
const days = []
|
||||
const homeoffice = {}
|
||||
const schedulesCapacityBySplit = {}
|
||||
|
||||
const now = moment()
|
||||
const dailyCapacityMinutes = this.user.work_end - this.user.work_start
|
||||
|
||||
this.timelineData.tasks.forEach(task => {
|
||||
if (!workingHours[task.split])
|
||||
workingHours[task.split] = 0
|
||||
|
||||
if (task.type == 'non-working-hours' || task.subtask?.is_finished || task.blocker?.is_finished)
|
||||
return
|
||||
|
||||
workingHours[task.split] += timelineUtils._getDifferenceInHours(task.end, task.start)
|
||||
if (!schedulesCapacityBySplit[task.split])
|
||||
schedulesCapacityBySplit[task.split] = 0
|
||||
|
||||
if (now.isAfter(moment(task._real_end))) {
|
||||
// Ignore elapsed task
|
||||
return
|
||||
}
|
||||
else if (now.isAfter(moment(task._real_start) && now.isBefore(moment(task._real_end)))) {
|
||||
// Task in progress
|
||||
schedulesCapacityBySplit[task.split] += timelineUtils._getDifferenceInMinutes(task._real_end, new Date())
|
||||
}
|
||||
else {
|
||||
// Future task
|
||||
schedulesCapacityBySplit[task.split] += timelineUtils._getDifferenceInMinutes(task._real_end, task._real_start)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
schedulesCapacityBySplit[1] = 0 // Set YESTERDAY value to 0
|
||||
|
||||
this.timelineData.homeoffices.forEach(task => {
|
||||
homeoffice[task.split] = true
|
||||
})
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
if (!schedulesCapacityBySplit[i + 1])
|
||||
schedulesCapacityBySplit[i + 1] = 0
|
||||
|
||||
let unscheduledTime
|
||||
|
||||
if (i == 0) {
|
||||
// Yesterday
|
||||
unscheduledTime = 0
|
||||
} else if (i == 1) {
|
||||
// Today
|
||||
const elapsedMinutes = now.diff(now.clone().startOf('day'), 'minutes')
|
||||
const workStart = this.user.work_start > elapsedMinutes ? this.user.workStart : elapsedMinutes
|
||||
const todayCapacityMinutes = this.user.work_end - workStart
|
||||
unscheduledTime = todayCapacityMinutes - schedulesCapacityBySplit[i + 1] < 0 ? 0 : todayCapacityMinutes - schedulesCapacityBySplit[i + 1]
|
||||
} else {
|
||||
// Future
|
||||
unscheduledTime = dailyCapacityMinutes - schedulesCapacityBySplit[i + 1]
|
||||
}
|
||||
|
||||
console.log(unscheduledTime)
|
||||
|
||||
days.push({
|
||||
id: i + 1,
|
||||
fullname: `${this.filter.employee.name} ${this.filter.employee.surname}`,
|
||||
initials: this.filter.employee.name.charAt(0).toUpperCase() + this.filter.employee.surname.charAt(0).toUpperCase(),
|
||||
unscheduledTime: workingHours[i + 1],
|
||||
unscheduledTime: (unscheduledTime / 60).toFixed(1),
|
||||
date: addDays(this.filter.start, i),
|
||||
isHomeoffice: homeoffice[i],
|
||||
isWeekend: timelineUtils.isWeekend(addDays(this.filter.start, i))
|
||||
})
|
||||
}
|
||||
|
||||
console.log(days)
|
||||
|
||||
return days
|
||||
},
|
||||
|
||||
@@ -74,11 +122,14 @@ export default {
|
||||
if (!this.subtasks || !this.filter)
|
||||
return null
|
||||
|
||||
const { tasks, blockers, nonWorkingHours, homeoffices } = timelineUtils.generateTimelineTasks(this.subtasks, new Date(this.filter.start), this.filter.employee)
|
||||
|
||||
return {
|
||||
name: this.filter.employee.name,
|
||||
surname: this.filter.employee.surname,
|
||||
unscheduledTime: this.unscheduledTime,
|
||||
tasks: timelineUtils.generateTimelineTasks(this.subtasks, new Date(this.filter.start), this.filter.employee)
|
||||
tasks: [...tasks, ...blockers, ...nonWorkingHours],
|
||||
homeoffices
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -121,9 +172,9 @@ export default {
|
||||
|
||||
try {
|
||||
const { subtasks, blockers } = await this.$axios.get_auth(`v1/subtasks?${queryParameters}`)
|
||||
this.config.scrollHour = this.filter?.employee.work_start - 1
|
||||
this.config.scrollHour = (this.filter?.employee.work_start / 60) - 1
|
||||
this.config.date = this.filter.start
|
||||
this.subtasks = [...subtasks, ...blockers.map(blocker => ({...blocker, type: 'blocker'}))]
|
||||
this.subtasks = [...subtasks, ...blockers]
|
||||
} catch (error) {
|
||||
this.$toast.error('Nepodarilo sa nacitat data!')
|
||||
}
|
||||
|
||||
@@ -83,7 +83,8 @@ export default {
|
||||
|
||||
_updateFilters() {
|
||||
this.filter.start = this.date
|
||||
this.filter.end = addDays(this.date, 1)
|
||||
this.filter.end = this.date
|
||||
// this.filter.end = addDays(this.date, 1)
|
||||
this.selectedDate = String(this.filter.start?.toLocaleDateString('sk-SK'))
|
||||
|
||||
this._emitFilterChanged()
|
||||
|
||||
@@ -9,21 +9,35 @@ export default {
|
||||
if (!this.subtasks || !this.blockers)
|
||||
return
|
||||
|
||||
const blockers = this._generateBlockers()
|
||||
|
||||
console.log([...this._generateNonWorking(), ...this._generateTasks(), ...blockers.filter(blocker => blocker.type != 'home_office')])
|
||||
|
||||
return {
|
||||
unscheduledTime: 333,
|
||||
tasks: [...this._generateNonWorking(), ...this._generateTasks(), ...this._generateBlockers()]
|
||||
tasks: [...this._generateNonWorking(), ...this._generateTasks(), ...blockers.filter(blocker => blocker.type != 'home_office')],
|
||||
homeoffices: [...blockers.filter(blocker => blocker.type == 'home_office')]
|
||||
}
|
||||
},
|
||||
|
||||
splitDays() {
|
||||
const days = []
|
||||
const homeoffice = {}
|
||||
|
||||
this.filter.team.employees.forEach((employee, index) => {
|
||||
let workingHours = 0
|
||||
|
||||
this.calendarData.tasks.forEach(task => {
|
||||
if (task?.subtask?.employee_id == employee.id)
|
||||
workingHours += timelineUtils._getDifferenceInHours(task.end, task.start)
|
||||
if ((task.subtask?.employee?.id != employee.id) && (task.blocker?.employee?.id != employee.id))
|
||||
return
|
||||
|
||||
workingHours += timelineUtils._getDifferenceInHours(task.end, task.start)
|
||||
})
|
||||
|
||||
this.calendarData.homeoffices.forEach(task => {
|
||||
if (task.blocker.employee?.id != employee.id) return
|
||||
|
||||
homeoffice[task.split] = true
|
||||
})
|
||||
|
||||
days.push({
|
||||
@@ -31,7 +45,8 @@ export default {
|
||||
name: employee.name,
|
||||
fullname: `${employee.name} ${employee.surname}`,
|
||||
initials: employee.name.charAt(0).toUpperCase() + employee.surname.charAt(0).toUpperCase(),
|
||||
unscheduledTime: workingHours
|
||||
unscheduledTime: workingHours,
|
||||
isHomeoffice: homeoffice[index + 1] ? true : false
|
||||
})
|
||||
})
|
||||
|
||||
@@ -43,14 +58,14 @@ export default {
|
||||
_generateNonWorking() {
|
||||
const nonWorkingHours = []
|
||||
const date = new Date(this.filter.start)
|
||||
let earliestStartHour = null
|
||||
let earliestStartMinutes = null
|
||||
this.calendarConfig.date = date
|
||||
|
||||
const { start: dayStart, end: dayEnd } = this._getDayStartEnd()
|
||||
|
||||
this.filter.team.employees.forEach((employee, index) => {
|
||||
if (!earliestStartHour || employee.work_start < earliestStartHour)
|
||||
earliestStartHour = employee.work_start
|
||||
if (!earliestStartMinutes || (employee.work_start) < earliestStartMinutes)
|
||||
earliestStartMinutes = employee.work_start
|
||||
|
||||
|
||||
nonWorkingHours.push({
|
||||
@@ -72,7 +87,7 @@ export default {
|
||||
})
|
||||
})
|
||||
|
||||
this.calendarConfig.scrollHour = earliestStartHour - 1
|
||||
this.calendarConfig.scrollHour = (earliestStartMinutes) - 1
|
||||
return nonWorkingHours
|
||||
},
|
||||
|
||||
@@ -107,7 +122,7 @@ export default {
|
||||
initials: employee?.name?.charAt(0)?.toUpperCase() + employee?.surname?.charAt(0)?.toUpperCase(),
|
||||
split: split,
|
||||
project_code: subtask.project_code,
|
||||
subtask: subtask,
|
||||
subtask,
|
||||
_timeblock_id: timeblock.id
|
||||
})
|
||||
}
|
||||
@@ -136,7 +151,8 @@ export default {
|
||||
split,
|
||||
start: new Date(blocker.start),
|
||||
end: new Date(blocker.end),
|
||||
type: 'blocker',
|
||||
type: blocker.type,
|
||||
isBlocker: true,
|
||||
initials: `${employee?.name[0]}${employee?.surname[0]}`,
|
||||
color: '#B00020',
|
||||
draggable: false,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<A-team-filter v-if="$store.getters['tm/isInitialized']" :unscheduledTime="calendarData?.unscheduledTime || 0" @filter-changed="onFilterChanged" />
|
||||
|
||||
<Z-timeline v-if="calendarData" :cal-data="calendarData" :cal-config="calendarConfig" :splits="splitDays" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-row no-gutters class="align-center" v-if="employee">
|
||||
<v-row no-gutters class="align-center">
|
||||
<v-col cols="5">
|
||||
<v-sheet class="py-3">
|
||||
<div class="d-flex align-center">
|
||||
@@ -12,9 +12,9 @@
|
||||
<v-col cols="7">
|
||||
<v-sheet class="py-3 d-flex align-center">
|
||||
<p class="employee-selected-avatar d-flex justify-center align-center">
|
||||
{{ employee._initials }}
|
||||
{{ employee ? employee._initials : '?' }}
|
||||
</p>
|
||||
<p class="ml-2">{{ employee._fullName }}</p>
|
||||
<p class="ml-2">{{ employee ? employee._fullName : $t('Unasigned') }}</p>
|
||||
</v-sheet>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -3,34 +3,28 @@ export default {
|
||||
employeesOptions() {
|
||||
const selectedEmployees = [...this.mastertask.employees].map(employee => employee.id)
|
||||
|
||||
const employees = this.enrichedEmployees.filter(
|
||||
return this.enrichedEmployees.filter(
|
||||
(employee) => !selectedEmployees.includes(employee.id)
|
||||
) || []
|
||||
},
|
||||
|
||||
return employees.map((employee) => {
|
||||
return {
|
||||
key: employee.id,
|
||||
value: `${employee.name} ${employee.surname}`,
|
||||
}
|
||||
})
|
||||
projectsOptions() {
|
||||
// Added to "paginate" projects, because v-autocomplete was unable to handle so many projects at once
|
||||
|
||||
const projects = this.projects.filter(project => project.code.includes(this.projectCodeSearch))
|
||||
return projects?.length ? projects.slice(0, 10) : this.projects.slice(0, 10)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onProjectSelected({ key }) {
|
||||
const project = this.projects.find(project => project.id == key)
|
||||
this.mastertask.project_code = project.code
|
||||
this.mastertask.project_id = project.id
|
||||
},
|
||||
// onProjectSelected(id) {
|
||||
// const project = this.projects.find(project => project.id == id)
|
||||
// this.mastertask.project_code = project.code
|
||||
// this.mastertask.project_id = project.id
|
||||
// },
|
||||
|
||||
onMasterTaskSelected({ key }) {
|
||||
const mastertask = this.mastertasks.find(mastertask => mastertask.id == key)
|
||||
this.subtask.master_task = mastertask
|
||||
this.subtask.project_code = mastertask.project_code
|
||||
},
|
||||
|
||||
onEmployeeSelected({ key }) {
|
||||
const employee = this.enrichedEmployees.find(employee => employee.id == key)
|
||||
onEmployeeSelected(id) {
|
||||
const employee = this.enrichedEmployees.find(employee => employee.id == id)
|
||||
this.mastertask.employees.push(employee)
|
||||
this.showAddEmployeeInput = false
|
||||
},
|
||||
@@ -60,6 +54,6 @@ export default {
|
||||
|
||||
this.mastertask.team = this.teams.find(team => team.id == value)
|
||||
this.mastertask.employees = this.mastertask.team.employees
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,17 @@
|
||||
</div>
|
||||
|
||||
<v-card-actions class="d-flex flex-column modal-content">
|
||||
<Z-autocomplete label="Kód projektu" :options="projects.map((project) => ({ key: project.id, value: project.code }))" :validations="v$.mastertask.project_code" :clear-on-submit="false" :value="mastertask.project_code" :class="{ error: v$.mastertask.project_code.$errors.length }" class="modal-input" @option-selected="onProjectSelected" />
|
||||
<v-autocomplete v-model="mastertask.project_id" :label="$t('Project code')" :class="{error: v$.mastertask.project_id.$errors.length}" :items="projectsOptions" item-title="code" item-value="id" variant="solo" class="modal-input" clearable @update:search="projectCodeSearch = $event" />
|
||||
|
||||
<v-text-field v-model="mastertask.name" @focus="onInputFocus" class="modal-input" label="Názov mastertasku" variant="solo" :class="{ error: v$.mastertask.name.$errors.length }" :hint="v$.mastertask.name.$errors ? errorUtils.parseErrors(v$.mastertask.name.$errors) : null"></v-text-field>
|
||||
|
||||
<Datepicker v-model="mastertask.deadline" @focus="onDateFocus" class="modal-input" format="dd.MM.yyyy, HH:mm" locale="sk" :class="{ error: v$.mastertask.deadline.$errors.length }" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Deadline" />
|
||||
<Datepicker v-model="mastertask.deadline" @focus="onDateFocus" :min-date="new Date()" class="modal-input" format="dd.MM.yyyy, HH:mm" locale="sk" :class="{ error: v$.mastertask.deadline.$errors.length }" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Deadline" />
|
||||
|
||||
<v-text-field v-model="mastertask.duration" @focus="onInputFocus" class="modal-input mt-5" variant="solo" type="number" disabled :label="`${mastertask.duration} h`"></v-text-field>
|
||||
|
||||
<v-select v-model="this.mastertask.team_id" @focus="onInputFocus" class="modal-input border-select" :class="{ error: v$.mastertask.team_id.$errors.length }" label="Tím" variant="solo" :items="teams" item-value="id" item-title="name"></v-select>
|
||||
<v-select v-model="mastertask.team_id" @focus="onInputFocus" class="modal-input border-select" :class="{ error: v$.mastertask.team_id.$errors.length }" label="Tím" variant="solo" :items="teams" item-value="id" item-title="name"></v-select>
|
||||
|
||||
<template v-if="!mastertask.id">
|
||||
<template v-if="!mastertask.id && mastertask.team_id">
|
||||
<v-text-field v-for="(employee, index) in mastertask.employees" :key="employee.id" @focus="onInputFocus" :class="{ error: !mastertask.employees[index].duration }" class="modal-input" :label="`${employee.name} ${employee.surname}`" variant="solo" placeholder="h" v-model="mastertask.employees[index].duration" type="number" min="1" @input="onEmployeeDurationChanged">
|
||||
<p class="employee-select-avatar d-flex justify-center align-center">
|
||||
{{ employee.name.charAt(0) + employee.surname.charAt(0) }}
|
||||
@@ -26,7 +26,9 @@
|
||||
</v-text-field>
|
||||
</template>
|
||||
|
||||
<Z-autocomplete label="Vyhľadať zamestnanca" v-if="showAddEmployeeInput" :options="employeesOptions" class="modal-input" @option-selected="onEmployeeSelected" />
|
||||
<v-autocomplete v-if="showAddEmployeeInput" :label="$t('Search for employee')" :class="{error: v$.mastertask.project_id.$errors.length}" :items="employeesOptions" item-title="_fullName" item-value="id" variant="solo" class="modal-input" clearable @update:model-value="onEmployeeSelected" />
|
||||
|
||||
<!-- <Z-autocomplete label="Vyhľadať zamestnanca" :options="employeesOptions" class="modal-input" @option-selected="onEmployeeSelected" /> -->
|
||||
|
||||
<v-btn class="w-100 mb-5 button secondary" :disabled="!mastertask.team?.id" @click="showAddEmployeeInput = true"> <v-icon class="mx-1" icon="mdi-plus" />Pridať zamestnanca </v-btn>
|
||||
|
||||
@@ -79,11 +81,11 @@ import ZFilesUpload from '@/plugins/tm@_components/z-files-upload.vue'
|
||||
import useVuelidate from '@vuelidate/core'
|
||||
import { required } from '@vuelidate/validators'
|
||||
import errorUtils from '@/plugins/lib@auth/_utils/error.utils'
|
||||
import ZAutocomplete from '@/plugins/tm@_components/z-autocomplete.vue'
|
||||
// import ZAutocomplete from '@/plugins/tm@_components/z-autocomplete.vue'
|
||||
import VuetifyFixFocusMixin from '@/plugins/tm@modal/_mixins/vuetify-fix-focus'
|
||||
import moment from 'moment'
|
||||
import { QuillEditor } from '@vueup/vue-quill'
|
||||
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
||||
// import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
||||
|
||||
import '@vueup/vue-quill/dist/vue-quill.snow.css'
|
||||
|
||||
@@ -94,7 +96,7 @@ import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
// MIXINS
|
||||
// computed: employeesOptions
|
||||
// computed: employeesOptions, projectsOptions
|
||||
// methods: onProjectSelected, onEmployeeSelected, onEmployeeDurationChanged
|
||||
// watch: mastertask.team_id
|
||||
|
||||
@@ -106,7 +108,7 @@ export default {
|
||||
ATasksTable,
|
||||
Datepicker,
|
||||
ZFilesUpload,
|
||||
ZAutocomplete,
|
||||
// ZAutocomplete,
|
||||
QuillEditor
|
||||
},
|
||||
|
||||
@@ -119,6 +121,7 @@ export default {
|
||||
mastertask: null,
|
||||
showAddEmployeeInput: false,
|
||||
date: null,
|
||||
projectCodeSearch: null
|
||||
}
|
||||
},
|
||||
|
||||
@@ -127,13 +130,29 @@ export default {
|
||||
'teams',
|
||||
'projects',
|
||||
'enrichedEmployees'
|
||||
])
|
||||
]),
|
||||
|
||||
|
||||
enrichedSubtasks() {
|
||||
if (!this.mastertask?.subtasks)
|
||||
return null
|
||||
|
||||
return this.mastertask.subtasks.map(subtask => {
|
||||
const employee = this.enrichedEmployees.find(employee => employee.id == subtask.employee_id)
|
||||
|
||||
return {
|
||||
...subtask,
|
||||
_initials: employee ? employee.name.charAt(0) + employee.surname.charAt(0) : '?',
|
||||
_fullName: employee ? `${employee.name} ${employee.surname}` : ''
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
validations() {
|
||||
return {
|
||||
mastertask: {
|
||||
project_code: { required },
|
||||
project_id: { required },
|
||||
team_id: { required },
|
||||
name: { required },
|
||||
description: { required },
|
||||
@@ -171,7 +190,8 @@ export default {
|
||||
|
||||
methods: {
|
||||
async showModal({mastertask, editMode = false}) {
|
||||
this.visible = true
|
||||
this.editMode = null
|
||||
this.visible = true
|
||||
this.mastertask = null
|
||||
this.$nextTick(() => this.v$.$reset())
|
||||
|
||||
@@ -179,11 +199,12 @@ export default {
|
||||
if (mastertask?.id) {
|
||||
this.mastertask = await this.$axios.get_auth_data(`v1/mastertasks/${mastertask.id}`)
|
||||
|
||||
const project = this.projects.find((project) => project.code == this.mastertask?.project_code)
|
||||
|
||||
this.mastertask.team_id = this.mastertask.team?.id || null
|
||||
// TODO zmazat ked zacne prichadzat project_id a team_id
|
||||
const project = this.projects.find((project) => project.code == this.mastertask.project_code)
|
||||
this.mastertask.project_id = project.id
|
||||
this.mastertask.project_code = project.code
|
||||
this.mastertask.team_id = this.mastertask.team?.id || null
|
||||
|
||||
// this.mastertask.project_code = project.code
|
||||
} else {
|
||||
this.mastertask = { employees: [], duration: 0 }
|
||||
}
|
||||
@@ -205,15 +226,6 @@ export default {
|
||||
if (this.$refs['description'])
|
||||
this.mastertask.description = this.$refs['description'].getHTML()
|
||||
|
||||
// this.mastertask.subtasks = this.mastertask.subtasks.map(subtask => {
|
||||
// const employee = this.mastertask.employees.find(employee => employee.id == subtask.employee_id)
|
||||
// subtask.duration = employee.duration
|
||||
|
||||
// return subtask
|
||||
// })
|
||||
|
||||
// this.mastertask.project_code = null
|
||||
|
||||
try {
|
||||
this.mastertask.deadline = moment(this.mastertask.deadline).format('YYYY-MM-DD HH:MM:SS')
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
employee: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
props: {
|
||||
employee: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<A-mastertask-name :name="subtask.master_task?.name || task.master_task" :id="subtask.master_task.id" :color="subtask.master_task.color" @close-modal="$emit('close-modal')" />
|
||||
<A-mastertask-name :name="subtask._master_task.name" :id="subtask.master_task_id" :color="subtask._master_task.color" @close-modal="$emit('close-modal')" />
|
||||
<A-mastertask-status :status="subtask.is_finished" />
|
||||
<A-task-timeblocks :timeblocks="subtask.timeblocks" :subtask="subtask" />
|
||||
<Z-created-by :employee-id="subtask.created_by" />
|
||||
<A-task-assigned-employee :employee="subtask.employee" />
|
||||
<A-task-assigned-employee :employee="subtask._employee" />
|
||||
<A-task-planned-time :hours="subtask.duration" />
|
||||
<A-task-remaining-time :hours="subtask.duration - subtask.spent_time" />
|
||||
<A-task-spent-time :hours="subtask.spent_time" :subtask="subtask" />
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
<v-card-actions class="d-flex flex-column modal-content">
|
||||
<v-text-field v-model="subtask.project_code" label="Kód projektu" :value="subtask.project_code" @focus="onInputFocus" class="modal-input" variant="solo" disabled></v-text-field>
|
||||
|
||||
<Z-autocomplete label="Master task" :options="mastertasks.map((mastertask) => ({ key: mastertask.id, value: mastertask.name }))" :validations="v$.subtask.master_task.id" :clear-on-submit="false" :value="subtask?.master_task?.name" :class="{ error: v$.subtask.master_task.id.$errors.length }" class="modal-input" @option-selected="onMasterTaskSelected" />
|
||||
<v-autocomplete v-model="subtask.master_task_id" label="Master task" :class="{error: v$.subtask.master_task_id.$errors.length}" :items="mastertasks" item-title="name" item-value="id" variant="solo" class="modal-input" clearable />
|
||||
|
||||
<v-autocomplete v-model="subtask.employee_id" label="Priradený" :class="{error: v$.subtask.employee_id.$errors.length}" :items="enrichedEmployees" item-title="_fullName" item-value="id" variant="solo" class="modal-input" clearable />
|
||||
|
||||
<Z-autocomplete label="Priradený" :value="subtask?.employee ? `${subtask?.employee?.name} ${subtask?.employee?.surname}` : ''" :options="enrichedEmployees.map(employee => ({key: employee.id, value: employee._fullName}))" :clear-on-submit="false" :class="{ error: v$.subtask.employee.$errors.length }" class="modal-input" @option-selected="onEmployeeSelected" />
|
||||
<v-text-field v-model="subtask.duration" @focus="onInputFocus" class="modal-input" variant="solo" type="number" label="Časový odhad" :class="{ error: v$.subtask.duration.$errors.length }" :hint="v$.subtask.duration.$errors ? errorUtils.parseErrors(v$.subtask.duration.$errors) : null" append-inner-icon="mdi-clock-outline"></v-text-field>
|
||||
|
||||
<div class="w-100 modal-input">
|
||||
@@ -28,8 +29,8 @@
|
||||
<v-card v-else height="100%" class="d-flex flex-column scrolling">
|
||||
<div class="d-flex justify-space-between align-center">
|
||||
<div>
|
||||
<p>{{ subtask.master_task?.project_code }}</p>
|
||||
<h1>{{ subtask.master_task?.name }}</h1>
|
||||
<p>{{ subtask._master_task.project_code }}</p>
|
||||
<h1>{{ subtask._master_task.name }}</h1>
|
||||
</div>
|
||||
|
||||
<v-icon @click.stop="closeModal()" icon="mdi-close" size="large" class="pb-5"></v-icon>
|
||||
@@ -83,7 +84,7 @@ export default {
|
||||
ZTaskReadonlyInfo,
|
||||
ZFilesUpload,
|
||||
QuillEditor,
|
||||
ZAutocomplete
|
||||
// ZAutocomplete
|
||||
},
|
||||
|
||||
data() {
|
||||
@@ -95,7 +96,6 @@ export default {
|
||||
errorUtils,
|
||||
v$: useVuelidate(),
|
||||
preventInputFocus: false,
|
||||
mastertaskIdSelect: null,
|
||||
showDatePicker: false,
|
||||
selectedNewDate: []
|
||||
}
|
||||
@@ -105,24 +105,15 @@ export default {
|
||||
...mapGetters('tm', [
|
||||
'mastertasks',
|
||||
'enrichedEmployees'
|
||||
]),
|
||||
|
||||
filteredEmployees() {
|
||||
if (!this.enrichedEmployees || !this.subtask?.master_task) return []
|
||||
|
||||
const selectedEmployees = this.subtask.master_task.employees?.map((employee) => employee.id) || []
|
||||
return this.enrichedEmployees.filter((employee) => !selectedEmployees.includes(employee.id))
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
validations() {
|
||||
return {
|
||||
subtask: {
|
||||
master_task: {
|
||||
id: { required }
|
||||
},
|
||||
master_task_id: { required },
|
||||
duration: { required },
|
||||
employee: { required }
|
||||
employee_id: { required }
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -153,7 +144,6 @@ export default {
|
||||
this.subtask = this._enrichTask(data)
|
||||
} else {
|
||||
this.subtask = { master_task: mastertask, timeblocks: [{start: tmUtils.formatDateTimeForBackend(moment()), end: tmUtils.formatDateTimeForBackend(moment())}] }
|
||||
this.mastertaskIdSelect = mastertask.id
|
||||
}
|
||||
|
||||
this.editMode = editMode || !this.subtask.id
|
||||
@@ -178,7 +168,7 @@ export default {
|
||||
const { subtask } = await this.$axios.put_auth(`v1/subtasks/${this.subtask.id}`, this.subtask)
|
||||
this.$store.commit('tm/update_subtask', subtask)
|
||||
} else {
|
||||
const { subtask } = await this.$axios.post_auth(`v1/mastertasks/${this.subtask.master_task.id}/subtasks`, this.subtask)
|
||||
const { subtask } = await this.$axios.post_auth(`v1/mastertasks/${this.subtask.master_task_id}/subtasks`, this.subtask)
|
||||
this.subtask.id = subtask.id
|
||||
this.$store.commit('tm/update_subtask', subtask)
|
||||
}
|
||||
@@ -190,17 +180,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async addNewDate() {
|
||||
if (this.selectedNewDate) {
|
||||
this.subtask.timeblocks.push({
|
||||
start: this.selectedNewDate[0],
|
||||
end: this.selectedNewDate[1]
|
||||
})
|
||||
this.showDatePicker = false
|
||||
this.selectedNewDate = []
|
||||
}
|
||||
},
|
||||
|
||||
async downloadFile(id) {
|
||||
try {
|
||||
const file = await this.$axios.get_auth_data(`v1/subtasks/${this.subtask.id}`, { responseType: "arraybuffer" })
|
||||
@@ -210,10 +189,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
removeDate(index) {
|
||||
this.subtask.timeblocks.splice(index, 1)
|
||||
},
|
||||
|
||||
openDeleteTaskModal(subtask) {
|
||||
this.$events.emit("show-delete-task-modal", { subtask })
|
||||
},
|
||||
@@ -226,35 +201,27 @@ export default {
|
||||
this.subtask.files = files
|
||||
},
|
||||
|
||||
onEmployeeSelected({ key }) {
|
||||
this.subtask.employee_id = key
|
||||
},
|
||||
|
||||
_enrichTask(subtask) {
|
||||
subtask.master_task = this.mastertasks.find((mastertask) => mastertask.id == subtask.master_task_id)
|
||||
subtask.employee = this.enrichedEmployees.find((employee) => employee.id == subtask.employee_id)
|
||||
subtask._created_by = this.enrichedEmployees.find((employee) => employee.id == subtask.created_by)
|
||||
this.mastertaskIdSelect = subtask.master_task?.id
|
||||
|
||||
return subtask
|
||||
return {
|
||||
...subtask,
|
||||
_created_by: this.enrichedEmployees.find((employee) => employee.id == subtask.created_by),
|
||||
_employee: this.enrichedEmployees.find((employee) => employee.id == subtask.employee_id),
|
||||
_master_task: this.mastertasks.find((mastertask) => mastertask.id == subtask.master_task_id)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
mastertaskIdSelect(value) {
|
||||
if (!value || !this.subtask) return
|
||||
'subtask.employee_id'(value, oldValue) {
|
||||
if (!value || !oldValue) return
|
||||
|
||||
this.subtask.master_task = this.mastertasks.find((mastertask) => mastertask.id == value)
|
||||
this.subtask.project_code = this.subtask.master_task.project_code
|
||||
this.subtask.timeblocks = []
|
||||
this.subtask._employee = this.enrichedEmployees.find((employee) => employee.id == this.subtask.employee_id)
|
||||
},
|
||||
|
||||
"subtask.employee_id"(value, oldValue) {
|
||||
'subtask.master_task_id'(value) {
|
||||
if (!value) return
|
||||
|
||||
if (value && oldValue)
|
||||
this.subtask.timeblocks = []
|
||||
|
||||
this.subtask.employee = this.enrichedEmployees.find((employee) => employee.id == value)
|
||||
this.subtask._master_task = this.mastertasks.find((mastertask) => mastertask.id == this.subtask.master_task_id)
|
||||
},
|
||||
|
||||
editMode() {
|
||||
|
||||
@@ -1,59 +1,33 @@
|
||||
<template>
|
||||
<v-dialog v-model="visible" max-width="750" max-height="90%" height="700px">
|
||||
<W-loadable :content="team && allEmployees" class="h-100">
|
||||
<W-loadable :content="team" class="h-100">
|
||||
<v-card height="100%" class="d-flex flex-column">
|
||||
<div class="d-flex justify-space-between align-center modal-header">
|
||||
<h1>{{ team.id ? 'Upraviť tím' : 'Vytvoriť tím' }}</h1>
|
||||
<v-icon
|
||||
@click.stop="closeModal()"
|
||||
icon="mdi-close"
|
||||
size="large"
|
||||
></v-icon>
|
||||
<v-icon @click.stop="closeModal()" icon="mdi-close" size="large"></v-icon>
|
||||
</div>
|
||||
|
||||
<v-card-actions class="d-flex flex-column modal-content">
|
||||
<v-text-field
|
||||
class="modal-input"
|
||||
label="Názov tímu"
|
||||
variant="solo"
|
||||
:class="{ error: v$.team.name.$errors.length }"
|
||||
:hint="
|
||||
<v-text-field class="modal-input" label="Názov tímu" variant="solo"
|
||||
:class="{ error: v$.team.name.$errors.length }" :hint="
|
||||
v$.team.name.$errors
|
||||
? errorUtils.parseErrors(v$.team.name.$errors)
|
||||
: null
|
||||
"
|
||||
v-model="team.name"
|
||||
></v-text-field>
|
||||
" v-model="team.name"></v-text-field>
|
||||
|
||||
<div
|
||||
class="relative w-100"
|
||||
v-for="(employee, index) in team.employees"
|
||||
:key="employee.id"
|
||||
>
|
||||
<v-text-field
|
||||
class="modal-input"
|
||||
disabled
|
||||
label="Zamestanec"
|
||||
variant="solo"
|
||||
v-model="team.employees[index]._fullName"
|
||||
>
|
||||
<div class="relative w-100" v-for="(employee, index) in team.employees" :key="employee.id">
|
||||
<v-text-field class="modal-input" disabled label="Zamestanec" variant="solo"
|
||||
v-model="team.employees[index]._fullName">
|
||||
</v-text-field>
|
||||
|
||||
<v-icon
|
||||
class="remove-input"
|
||||
@click.stop="team.employees.splice(index, 1)"
|
||||
icon="mdi-close"
|
||||
size="small"
|
||||
></v-icon>
|
||||
<v-icon class="remove-input" @click.stop="team.employees.splice(index, 1)" icon="mdi-close"
|
||||
size="small"></v-icon>
|
||||
</div>
|
||||
|
||||
<Z-autocomplete
|
||||
label="Vyhľadať zamestnanca"
|
||||
:options="employeesOptions"
|
||||
:validations="v$.team.employees"
|
||||
class="modal-input"
|
||||
@option-selected="employeeSelected"
|
||||
/>
|
||||
<v-autocomplete v-model="employeeSelect" label="Priradený"
|
||||
:class="{ error: v$.team.employees.$errors.length }" :items="employeesOptions"
|
||||
item-title="_fullName" item-value="id" variant="solo" class="modal-input" clearable
|
||||
ref="autocomplete" />
|
||||
</v-card-actions>
|
||||
|
||||
<v-btn class="button primary block" @click="save()">{{ team.id ? 'UPRAVIŤ' : 'VYTVORIŤ' }}</v-btn>
|
||||
@@ -63,31 +37,41 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ZAutocomplete from '@/plugins/tm@_components/z-autocomplete.vue'
|
||||
import useVuelidate from '@vuelidate/core'
|
||||
import { required } from '@vuelidate/validators'
|
||||
import errorUtils from '@/plugins/lib@auth/_utils/error.utils'
|
||||
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
||||
import useVuelidate from '@vuelidate/core'
|
||||
import { required } from '@vuelidate/validators'
|
||||
import errorUtils from '@/plugins/lib@auth/_utils/error.utils'
|
||||
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
const atLeastOne = (value) => value.length
|
||||
|
||||
export default {
|
||||
emits: ['close', 'update'],
|
||||
|
||||
components: {
|
||||
ZAutocomplete,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
allEmployees: null,
|
||||
v$: useVuelidate(),
|
||||
errorUtils,
|
||||
employeeSelect: null,
|
||||
team: null
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters('tm', [
|
||||
'enrichedEmployees'
|
||||
]),
|
||||
|
||||
employeesOptions() {
|
||||
const selectedEmployeesIds = this.team.employees.map(
|
||||
(employee) => employee.id
|
||||
)
|
||||
|
||||
return this.enrichedEmployees.filter(employee => !selectedEmployeesIds.includes(employee.id))
|
||||
}
|
||||
},
|
||||
|
||||
validations() {
|
||||
return {
|
||||
team: {
|
||||
@@ -98,20 +82,17 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
async showModal({ team = null}) {
|
||||
async showModal({ team = null }) {
|
||||
this.visible = true
|
||||
this.team = null
|
||||
this.$nextTick(() => this.v$.$reset())
|
||||
|
||||
try {
|
||||
const allEmployees = await this.$axios.get_auth_data('v1/employees')
|
||||
this.allEmployees = tmUtils.enrichEmployees(allEmployees)
|
||||
|
||||
if (team?.id) {
|
||||
const t = await this.$axios.get_auth_data(`v1/teams/${team.id}`)
|
||||
this.team = this._enrichTeam(t)
|
||||
this.team = this._enrichTeam(t)
|
||||
} else {
|
||||
this.team = {employees: []}
|
||||
this.team = { employees: [] }
|
||||
}
|
||||
} catch (error) {
|
||||
this.$toast.error(error)
|
||||
@@ -141,33 +122,21 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
employeeSelected(selectedOption) {
|
||||
const employee = this.allEmployees.find((empl) => empl.id == selectedOption.key)
|
||||
this.team.employees.push(employee)
|
||||
},
|
||||
|
||||
_enrichTeam(team) {
|
||||
team.employees = tmUtils.enrichEmployees(team.employees)
|
||||
return team
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
employeesOptions() {
|
||||
const selectedEmployeesIds = this.team.employees.map(
|
||||
(employee) => employee.id
|
||||
)
|
||||
watch: {
|
||||
employeeSelect(employeeId) {
|
||||
if (!employeeId)
|
||||
return
|
||||
|
||||
const employees = this.allEmployees.filter(
|
||||
(employee) => !selectedEmployeesIds.includes(employee.id)
|
||||
)
|
||||
|
||||
return employees.map((employee) => {
|
||||
return {
|
||||
key: employee.id,
|
||||
value: `${employee.name} ${employee.surname}`,
|
||||
}
|
||||
})
|
||||
const employee = this.enrichedEmployees.find(employee => employee.id == employeeId)
|
||||
this.team.employees.push(employee)
|
||||
this.employeeSelect = null
|
||||
this.$refs.autocomplete.search = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
editTask(subtask, editMode = false) {
|
||||
this.$events.emit('show-subtask-modal', {subtask, editMode})
|
||||
|
||||
Reference in new issue
Block a user