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
0453c39fc5
18 files changed
+253
-102
No files matched your search
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
<Z-master-task-modal ref="z-master-task-modal" @master-task-edited="onMasterTaskEdited" />
|
<Z-master-task-modal ref="z-master-task-modal" @master-task-edited="onMasterTaskEdited" />
|
||||||
<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" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -32,6 +33,7 @@ import ZBlockerModal from '@/plugins/tm@modal/blocker/z-blocker-modal.vue'
|
|||||||
import ZDeleteMasterTaskModal from '@/plugins/tm@modal/master-task/z-delete-master-task-modal.vue'
|
import ZDeleteMasterTaskModal from '@/plugins/tm@modal/master-task/z-delete-master-task-modal.vue'
|
||||||
import ZMasterTaskModal from '@/plugins/tm@modal/master-task/z-master-task-modal.vue'
|
import ZMasterTaskModal from '@/plugins/tm@modal/master-task/z-master-task-modal.vue'
|
||||||
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'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -45,10 +47,15 @@ export default {
|
|||||||
ZTaskAddTimeModal,
|
ZTaskAddTimeModal,
|
||||||
ZBlockerModal,
|
ZBlockerModal,
|
||||||
ZDeleteMasterTaskModal,
|
ZDeleteMasterTaskModal,
|
||||||
ZDeleteTeamMemberModal
|
ZDeleteTeamMemberModal,
|
||||||
|
ZShowMenuModal
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.$events.on('show-menu-modal', (data) => {
|
||||||
|
this.$refs['z-show-menu-modal'].showModal(data)
|
||||||
|
})
|
||||||
|
|
||||||
this.$events.on('show-delete-team-modal', ({team}) => {
|
this.$events.on('show-delete-team-modal', ({team}) => {
|
||||||
this.$refs['z-delete-team-modal'].showModal({ team })
|
this.$refs['z-delete-team-modal'].showModal({ team })
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect width="16" height="16" fill="white"/>
|
|
||||||
<path d="M2 7.82843L4.82843 10.6569L10.4853 5" stroke="black" stroke-opacity="0.87" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M2 7.82843L4.82843 10.6569L10.4853 5" stroke="black" stroke-opacity="0.87" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<rect x="7.03906" y="7.61621" width="1.09208" height="1.7726" transform="rotate(45 7.03906 7.61621)" fill="white"/>
|
<rect x="7.03906" y="7.61621" width="1.09208" height="1.7726" transform="rotate(45 7.03906 7.61621)" fill="white"/>
|
||||||
<path d="M6 7.82843L8.82843 10.6569L14.4853 5" stroke="black" stroke-opacity="0.87" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M6 7.82843L8.82843 10.6569L14.4853 5" stroke="black" stroke-opacity="0.87" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 529 B After Width: | Height: | Size: 485 B |
@@ -64,6 +64,7 @@ transition-group.vuecal__cell(
|
|||||||
<script>
|
<script>
|
||||||
import Event from './event.vue'
|
import Event from './event.vue'
|
||||||
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['vuecal', 'utils', 'modules', 'view', 'domEvents'],
|
inject: ['vuecal', 'utils', 'modules', 'view', 'domEvents'],
|
||||||
@@ -259,7 +260,15 @@ export default {
|
|||||||
dateFrom.setHours(Math.floor(DOMEvent.offsetY / 100), 0, 0, 0)
|
dateFrom.setHours(Math.floor(DOMEvent.offsetY / 100), 0, 0, 0)
|
||||||
const dateTo = new Date(dateFrom)
|
const dateTo = new Date(dateFrom)
|
||||||
dateTo.setHours(dateFrom.getHours() + 1)
|
dateTo.setHours(dateFrom.getHours() + 1)
|
||||||
this.$events.emit('show-subtask-modal', {})
|
|
||||||
|
const time = moment(dateFrom).format('HH:mm')
|
||||||
|
const date = moment(dateFrom).format('DD.MM.yyyy')
|
||||||
|
|
||||||
|
this.$events.emit('show-menu-modal', {
|
||||||
|
time: time,
|
||||||
|
date: date
|
||||||
|
})
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$events.emit('add-timeblock', {
|
this.$events.emit('add-timeblock', {
|
||||||
id: null,
|
id: null,
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onDragStart(e) {
|
onDragStart(e) {
|
||||||
if (this.event.type == 'blocker' || this.event.type == 'non-working-hours') return
|
if (this.event.type == 'blocker' || this.event.type == 'non-working-hours') return //|| this.event._timeblock_is_finished || this.event.subtask?.is_finished
|
||||||
this.event.draggingStatic = false
|
this.event.draggingStatic = false
|
||||||
this.dnd && this.dnd.eventDragStart(e, this.event);
|
this.dnd && this.dnd.eventDragStart(e, this.event);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
customFilter(item, input) {
|
||||||
|
// Replace accented characters with non-accented equivalents
|
||||||
|
const normalizedItem = item.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||||
|
const normalizedInput = input.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||||
|
|
||||||
|
// Remove any character that is not a letter, number, or space from both the item and the input
|
||||||
|
const pattern = /[^a-zA-Z0-9 ]/g;
|
||||||
|
const cleanedItem = normalizedItem.replace(pattern, '');
|
||||||
|
const cleanedInput = normalizedInput.replace(pattern, '');
|
||||||
|
|
||||||
|
// Check if the cleaned item starts with the cleaned input
|
||||||
|
return cleanedItem.toLowerCase().includes(cleanedInput.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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._timeblock_is_finished ? '#dedfe0' : event.color || event.subtask.color}">
|
<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._timeblock_is_finished || event.subtask.is_finished ? '#dedfe0' : event.color || event.subtask.color}">
|
||||||
<div class="event-content" :class="{'is-blocker': event.isBlocker}">
|
<div class="event-content" :class="{'is-blocker': event.isBlocker}">
|
||||||
<template v-if="event.type == 'subtask'">
|
<template v-if="event.type == 'subtask'">
|
||||||
<small v-if="event.subtask.project_code" class="project-code">{{ event.subtask.project_code }}</small>
|
<small v-if="event.subtask.project_code" class="project-code">{{ event.subtask.project_code }}</small>
|
||||||
@@ -163,6 +163,7 @@ export default {
|
|||||||
...event.subtask,
|
...event.subtask,
|
||||||
timeblocks: [...event.subtask.timeblocks, {
|
timeblocks: [...event.subtask.timeblocks, {
|
||||||
id: event._timeblock_id,
|
id: event._timeblock_id,
|
||||||
|
is_finished: 0,
|
||||||
start: tmUtils.formatDateTimeForBackend(start),
|
start: tmUtils.formatDateTimeForBackend(start),
|
||||||
end: tmUtils.formatDateTimeForBackend(end)
|
end: tmUtils.formatDateTimeForBackend(end)
|
||||||
}]
|
}]
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-row class="px-10 py-8 subheader">
|
<v-row class="px-10 py-8 subheader">
|
||||||
<v-autocomplete v-model="filter.projectId" class="subheader-input" :items="[null, ...projectsOptions]" item-title="code" item-value="id" label="Projekt" variant="outlined" @update:search="projectCodeSearch = $event" />
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" v-model="filter.projectId" class="subheader-input" :items="[...projectsOptions]" item-title="code" item-value="id" label="Projekt" variant="outlined" @update:search="projectCodeSearch = $event" />
|
||||||
|
|
||||||
<v-select v-model="filter.teamId" class="subheader-input" :items="[null, ...teams]" label="Tím" variant="outlined" item-title="name" item-value="id" />
|
<v-select v-model="filter.teamId" class="subheader-input" :items="[null, ...teams]" label="Tím" variant="outlined" item-title="name" item-value="id" />
|
||||||
|
|
||||||
<v-select v-model="filter.employeeId" class="subheader-input" :items="[null, ...enrichedEmployees]" label="Zamestnanec" variant="outlined" item-title="_fullName" item-value="id" />
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" v-model="filter.employeeId" class="subheader-input" :items="[...enrichedEmployees]" item-title="_fullName" item-value="id" label="Zamestnanec" variant="outlined" />
|
||||||
|
|
||||||
<v-select v-model="filter.createdById" class="subheader-input" :items="[null, ...enrichedEmployees]" label="Vytvoril" variant="outlined" item-title="_fullName" item-value="id" />
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" v-model="filter.createdById" class="subheader-input" :items="[...enrichedEmployees]" label="Vytvoril" variant="outlined" item-title="_fullName" item-value="id" />
|
||||||
|
|
||||||
<v-select v-model="filter.month" class="subheader-input" :items="months" label="Mesiac" variant="outlined" />
|
<v-select v-model="filter.month" class="subheader-input" :items="months" label="Mesiac" variant="outlined" />
|
||||||
|
|
||||||
@@ -18,9 +18,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [AutocompleteMixin],
|
||||||
emits: ['filterChanged'],
|
emits: ['filterChanged'],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@@ -56,6 +58,7 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
const monthIndex = new Date().getMonth()
|
const monthIndex = new Date().getMonth()
|
||||||
this.filter.month = monthIndex < 9 ? `0${monthIndex + 1}` : `${monthIndex + 1}`
|
this.filter.month = monthIndex < 9 ? `0${monthIndex + 1}` : `${monthIndex + 1}`
|
||||||
|
console.log(this.enrichedEmployees)
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -1,43 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-row class="px-7 py-5 subheader">
|
<v-row class="px-7 py-5 subheader">
|
||||||
<v-autocomplete
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" v-model="selectedEmployee" class="subheader-input"
|
||||||
v-model="selectedEmployee"
|
:items="enrichedEmployees" :menu-props="{ maxHeight: 500 }" item-title="_fullName" item-value="id"
|
||||||
class="subheader-input"
|
label="Zamestnanec" variant="outlined"></v-autocomplete>
|
||||||
:items="enrichedEmployees"
|
|
||||||
:menu-props="{maxHeight: 500}"
|
|
||||||
item-title="_fullName"
|
|
||||||
item-value="id"
|
|
||||||
label="Zamestnanec"
|
|
||||||
variant="outlined"
|
|
||||||
></v-autocomplete>
|
|
||||||
<div class="d-flex date-wrapper">
|
<div class="d-flex date-wrapper">
|
||||||
<v-btn class="subheader-icon-button prev" variant="outlined" @click="prevWeek()"><img :src="require('@/plugins/app/_img/icons/icon-prev.svg')" alt=""></v-btn>
|
<v-btn class="subheader-icon-button prev" variant="outlined" @click="prevWeek()"><img
|
||||||
<v-text-field
|
:src="require('@/plugins/app/_img/icons/icon-prev.svg')" alt=""></v-btn>
|
||||||
class="subheader-input disabled"
|
<v-text-field class="subheader-input disabled" label="Dátum" v-model="selectedDate" variant="outlined"
|
||||||
label="Dátum"
|
append-inner-icon="mdi-calendar-blank-outline"></v-text-field>
|
||||||
v-model="selectedDate"
|
<v-btn class="subheader-icon-button next" variant="outlined" @click="nextWeek()"><img
|
||||||
variant="outlined"
|
:src="require('@/plugins/app/_img/icons/icon-next.svg')" alt=""></v-btn>
|
||||||
append-inner-icon="mdi-calendar-blank-outline"
|
|
||||||
></v-text-field>
|
|
||||||
<v-btn class="subheader-icon-button next" variant="outlined" @click="nextWeek()"><img :src="require('@/plugins/app/_img/icons/icon-next.svg')" alt=""></v-btn>
|
|
||||||
</div>
|
</div>
|
||||||
<v-text-field
|
<v-text-field class="subheader-input disabled" label="Nenaplánované hodiny" variant="outlined" :disabled="true"
|
||||||
class="subheader-input disabled"
|
v-model="time"></v-text-field>
|
||||||
label="Nenaplánované hodiny"
|
|
||||||
variant="outlined"
|
|
||||||
:disabled="true"
|
|
||||||
v-model="time"
|
|
||||||
></v-text-field>
|
|
||||||
</v-row>
|
</v-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { addDays, subDays } from 'date-fns'
|
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
import { addDays, subDays } from 'date-fns'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [AutocompleteMixin],
|
||||||
emits: ['filter-changed'],
|
emits: ['filter-changed'],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
splitDays: {
|
splitDays: {
|
||||||
type: [Array, null],
|
type: [Array, null],
|
||||||
@@ -58,7 +46,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters('auth', ['user']),
|
...mapGetters('auth', ['user']),
|
||||||
...mapGetters('tm', ['enrichedEmployees']),
|
...mapGetters('tm', ['enrichedEmployees']),
|
||||||
@@ -82,9 +70,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateFilters() {
|
_updateFilters() {
|
||||||
this.filter.start = subDays(this.date, 1)
|
this.filter.start = subDays(this.date, 1)
|
||||||
this.filter.end = addDays(this.date, 5)
|
this.filter.end = addDays(this.date, 5)
|
||||||
this.selectedDate = String(this.filter.start?.toLocaleDateString('sk-SK') + ' - ' + this.filter.end?.toLocaleDateString('sk-SK'))
|
this.selectedDate = String(this.filter.start?.toLocaleDateString('sk-SK') + ' - ' + this.filter.end?.toLocaleDateString('sk-SK'))
|
||||||
|
|
||||||
this._emitFilterChanged()
|
this._emitFilterChanged()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,10 +8,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="7">
|
<v-col cols="7">
|
||||||
<v-sheet class="py-3 d-flex flex-wrap flex-column">
|
<v-sheet class="py-3 d-flex flex-wrap flex-column">
|
||||||
<p class="task-badge px-4 py-2 rounded-pill flex-shrink-0">{{ team?.name || 'Nepriradený' }}</p>
|
<template v-if="team">
|
||||||
<div class="d-flex align-center flex-shrink-0 mt-2 task-badge px-4 py-2 rounded-pill" v-for="employee in enrichedEmployees" :key="employee.id">
|
<p class="task-badge px-4 py-2 rounded-pill flex-shrink-0">{{ team.name }}</p>
|
||||||
|
<div class="d-flex align-center flex-shrink-0 mt-2 task-badge px-4 py-2 rounded-pill" v-for="employee in enrichedEmployeesInTeam" :key="employee.id">
|
||||||
|
<p class="employee-selected-avatar d-flex justify-center align-center">
|
||||||
|
{{ employee._initials }}
|
||||||
|
</p>
|
||||||
|
<p class="ml-2">{{ employee._fullName }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<p class="task-badge px-4 py-2 rounded-pill flex-shrink-0">Nepriradený</p>
|
||||||
|
<div class="d-flex align-center flex-shrink-0 mt-2 task-badge px-4 py-2 rounded-pill" v-for="employee in enrichedEmployeesOutsideTeam" :key="employee.id">
|
||||||
<p class="employee-selected-avatar d-flex justify-center align-center">
|
<p class="employee-selected-avatar d-flex justify-center align-center">
|
||||||
{{ employee._initials }}
|
{{ employee._initials }}
|
||||||
</p>
|
</p>
|
||||||
@@ -40,8 +52,19 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
enrichedEmployees() {
|
enrichedEmployeesInTeam() {
|
||||||
return tmUtils.enrichEmployees(this.employees)
|
if (!this.team?.employees)
|
||||||
|
return []
|
||||||
|
|
||||||
|
return tmUtils.enrichEmployees(this.team.employees)
|
||||||
|
},
|
||||||
|
|
||||||
|
enrichedEmployeesOutsideTeam() {
|
||||||
|
if (!this.team)
|
||||||
|
return tmUtils.enrichEmployees(this.employees)
|
||||||
|
|
||||||
|
const employeesOutsideOfTeam = this.employees.filter(employee => !this.team.employees.find(em => em.id == employee.id))
|
||||||
|
return tmUtils.enrichEmployees(employeesOutsideOfTeam)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-card-actions class="d-flex flex-column modal-content">
|
<v-card-actions class="d-flex flex-column modal-content">
|
||||||
<v-text-field @focus="onInputFocus" class="modal-input" label="Názov blockeru" :class="{ error: v$.blocker.name.$errors.length }" :hint="v$.blocker.name.$errors ? errorUtils.parseErrors(v$.blocker.name.$errors) : null" v-model="blocker.name" variant="solo"></v-text-field>
|
<v-text-field @focus="onInputFocus" class="modal-input" label="Názov blockeru"
|
||||||
|
:class="{ error: v$.blocker.name.$errors.length }"
|
||||||
<v-autocomplete
|
:hint="v$.blocker.name.$errors ? errorUtils.parseErrors(v$.blocker.name.$errors) : null"
|
||||||
clearable
|
v-model="blocker.name" variant="solo"></v-text-field>
|
||||||
label="Vyhľadať zamestnanca"
|
|
||||||
:items="[...enrichedEmployees]"
|
|
||||||
item-title="_fullName"
|
|
||||||
item-value="id"
|
|
||||||
variant="solo"
|
|
||||||
class="modal-input"
|
|
||||||
|
|
||||||
v-model="blocker.employee_id"
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" clearable label="Vyhľadať zamestnanca"
|
||||||
/>
|
:items="[...enrichedEmployees]" item-title="_fullName" item-value="id" variant="solo"
|
||||||
|
class="modal-input" v-model="blocker.employee_id" />
|
||||||
<!-- <Z-autocomplete label="Vyhľadať zamestnanca" :value="blocker?.employee ? `${blocker?.employee?.name} ${blocker?.employee?.surname}` : ''" :options="enrichedEmployees.map(employee => ({key: employee.id, value: employee._fullName}))" :clear-on-submit="false" :class="{ error: v$.blocker.employee.$errors.length }" class="modal-input" @option-selected="onEmployeeSelected" /> -->
|
<!-- <Z-autocomplete label="Vyhľadať zamestnanca" :value="blocker?.employee ? `${blocker?.employee?.name} ${blocker?.employee?.surname}` : ''" :options="enrichedEmployees.map(employee => ({key: employee.id, value: employee._fullName}))" :clear-on-submit="false" :class="{ error: v$.blocker.employee.$errors.length }" class="modal-input" @option-selected="onEmployeeSelected" /> -->
|
||||||
<Datepicker class="modal-input mb-5" v-model="datepicker" format="dd.MM.yyyy" locale="sk" :class="{ error: v$.datepicker.$errors?.length }" :validations="v$.datepicker" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Dátum" :enable-time-picker="false" ref="datepicker" />
|
<Datepicker class="modal-input mb-5" v-model="datepicker" format="dd.MM.yyyy" locale="sk"
|
||||||
|
:class="{ error: v$.datepicker.$errors?.length }" :validations="v$.datepicker" select-text="Vybrať"
|
||||||
|
cancel-text="Zrušiť" placeholder="Dátum" :enable-time-picker="false" ref="datepicker" />
|
||||||
|
|
||||||
<v-text-field v-model="starts_at_time" type="time" variant="solo" bg-color="white" :hint="v$.starts_at_time.$errors ? errorUtils.parseErrors(v$.starts_at_time.$errors) : null" :class="{ error: v$.starts_at_time.$errors.length }" class="mb-1 modal-input" label="Začiatok" />
|
<v-text-field v-model="starts_at_time" type="time" variant="solo" bg-color="white"
|
||||||
<v-text-field v-model="ends_at_time" type="time" variant="solo" bg-color="white" :hint="v$.ends_at_time.$errors ? errorUtils.parseErrors(v$.ends_at_time.$errors) : null" :class="{ error: v$.ends_at_time.$errors.length }" class="modal-input" label="Koniec" />
|
:hint="v$.starts_at_time.$errors ? errorUtils.parseErrors(v$.starts_at_time.$errors) : null"
|
||||||
|
:class="{ error: v$.starts_at_time.$errors.length }" class="mb-1 modal-input" label="Začiatok" />
|
||||||
|
<v-text-field v-model="ends_at_time" type="time" variant="solo" bg-color="white"
|
||||||
|
:hint="v$.ends_at_time.$errors ? errorUtils.parseErrors(v$.ends_at_time.$errors) : null"
|
||||||
|
:class="{ error: v$.ends_at_time.$errors.length }" class="modal-input" label="Koniec" />
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
|
||||||
<v-btn class="button primary" @click="save()">{{ blocker.id ? "UPRAVIŤ" : "VYTVORIŤ" }}</v-btn>
|
<v-btn class="button primary" @click="save()">{{ blocker.id ? "UPRAVIŤ" : "VYTVORIŤ" }}</v-btn>
|
||||||
@@ -35,17 +36,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Datepicker from "@vuepic/vue-datepicker"
|
import Datepicker from "@vuepic/vue-datepicker"
|
||||||
import useVuelidate from "@vuelidate/core"
|
import useVuelidate from "@vuelidate/core"
|
||||||
import { required } from "@vuelidate/validators"
|
import { required } from "@vuelidate/validators"
|
||||||
import errorUtils from "@/plugins/lib@auth/_utils/error.utils"
|
import errorUtils from "@/plugins/lib@auth/_utils/error.utils"
|
||||||
import VuetifyFixFocusMixin from "@/plugins/tm@modal/_mixins/vuetify-fix-focus"
|
import VuetifyFixFocusMixin from "@/plugins/tm@modal/_mixins/vuetify-fix-focus"
|
||||||
import { mapGetters } from "vuex"
|
import { mapGetters } from "vuex"
|
||||||
import moment from "moment"
|
import moment from "moment"
|
||||||
// import ZAutocomplete from '@/plugins/tm@_components/z-autocomplete.vue'
|
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [VuetifyFixFocusMixin],
|
mixins: [VuetifyFixFocusMixin, AutocompleteMixin],
|
||||||
|
|
||||||
emits: ["close"],
|
emits: ["close"],
|
||||||
|
|
||||||
@@ -108,7 +109,12 @@ export default {
|
|||||||
//fixnut validacie employee_id
|
//fixnut validacie employee_id
|
||||||
this.blocker = blocker
|
this.blocker = blocker
|
||||||
} else {
|
} else {
|
||||||
this.blocker = { start: null }
|
this.blocker = {start: null }
|
||||||
|
|
||||||
|
if (blocker.starts_at_time && blocker.datepicker) {
|
||||||
|
this.datepicker = blocker.datepicker
|
||||||
|
this.starts_at_time = blocker.starts_at_time
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$toast.error(error)
|
this.$toast.error(error)
|
||||||
|
|||||||
@@ -43,8 +43,26 @@ export default {
|
|||||||
|
|
||||||
this.mastertask.employees = this.mastertask.employees.map(employee => {
|
this.mastertask.employees = this.mastertask.employees.map(employee => {
|
||||||
if (typeof employee.duration == 'string') {
|
if (typeof employee.duration == 'string') {
|
||||||
totalDuration += parseInt(employee.duration)
|
totalDuration += parseFloat(employee.duration)
|
||||||
employee.duration = parseInt(employee.duration)
|
employee.duration = parseFloat(employee.duration)
|
||||||
|
}
|
||||||
|
else if (typeof employee.duration == 'number') {
|
||||||
|
totalDuration += employee.duration
|
||||||
|
}
|
||||||
|
|
||||||
|
return employee
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!this.mastertask.team)
|
||||||
|
return this.mastertask.duration = totalDuration
|
||||||
|
|
||||||
|
this.mastertask.team.employees = this.mastertask.team.employees.map(employee => {
|
||||||
|
if (isNaN(employee.duration) || isNaN(parseFloat(employee.duration)))
|
||||||
|
return employee
|
||||||
|
|
||||||
|
if (typeof employee.duration == 'string') {
|
||||||
|
totalDuration += parseFloat(employee.duration)
|
||||||
|
employee.duration = parseFloat(employee.duration)
|
||||||
}
|
}
|
||||||
else if (typeof employee.duration == 'number') {
|
else if (typeof employee.duration == 'number') {
|
||||||
totalDuration += employee.duration
|
totalDuration += employee.duration
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-card-actions class="d-flex flex-column modal-content">
|
<v-card-actions class="d-flex flex-column modal-content">
|
||||||
<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-autocomplete :custom-filter="customFilter" :match-any="true" 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>
|
<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>
|
||||||
|
|
||||||
@@ -21,28 +21,21 @@
|
|||||||
:class="{ error: v$.mastertask._deadline_time.$errors.length }" :hint="v$.mastertask._deadline_time.$errors ? errorUtils.parseErrors(v$.mastertask._deadline_time.$errors) : null" />
|
:class="{ error: v$.mastertask._deadline_time.$errors.length }" :hint="v$.mastertask._deadline_time.$errors ? errorUtils.parseErrors(v$.mastertask._deadline_time.$errors) : null" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-text-field v-model="mastertask.duration" @focus="onInputFocus" class="modal-input" variant="solo" type="number" :label="`${mastertask.duration} h`"></v-text-field>
|
<v-text-field v-model="mastertask.duration" @focus="onInputFocus" class="modal-input" variant="solo" type="number" :label="`${mastertask.duration} h`" :disabled="true"></v-text-field>
|
||||||
|
|
||||||
<v-select v-model="mastertask.team_id" @focus="onInputFocus" class="modal-input border-select" :class="{ error: v$.mastertask.employees.$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" label="Tím" variant="solo" :items="teams" item-value="id" item-title="name"></v-select>
|
||||||
|
|
||||||
<template v-if="!mastertask.id && mastertask.team_id">
|
<template v-if="mastertask.employees">
|
||||||
<v-text-field v-for="(employee, index) in mastertask.employees" :key="employee.id" @focus="onInputFocus" class="modal-input" :label="`${employee.name} ${employee.surname}`" variant="solo" placeholder="h" v-model="mastertask.employees[index].duration" type="number" min="1" @input="onEmployeeDurationChanged">
|
<v-text-field v-for="(employee, index) in mastertask.employees" :key="employee.id" @focus="onInputFocus" 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">
|
<p class="employee-select-avatar d-flex justify-center align-center">
|
||||||
{{ employee?.name.charAt(0) + employee?.surname.charAt(0) }}
|
{{ employee?.name.charAt(0) + employee?.surname.charAt(0) }}
|
||||||
</p>
|
</p>
|
||||||
<v-icon @click.stop="removeMemberAlert(employee)" class="remove-input" icon="mdi-close" size="small"></v-icon>
|
|
||||||
</v-text-field>
|
<v-icon @click.stop="removeEmployee(employee)" class="remove-input" icon="mdi-close" size="small"></v-icon>
|
||||||
</template>
|
|
||||||
<template v-if="!mastertask.id && !mastertask.team_id && mastertask.employees">
|
|
||||||
<v-text-field v-for="(employee, index) in mastertask.employees" :key="employee.id" @focus="onInputFocus" 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) }}
|
|
||||||
</p>
|
|
||||||
<!-- <v-icon @click.stop="removeMemberAlert(employee)" class="remove-input" icon="mdi-close" size="small"></v-icon> -->
|
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<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" />
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" 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" /> -->
|
<!-- <Z-autocomplete label="Vyhľadať zamestnanca" :options="employeesOptions" class="modal-input" @option-selected="onEmployeeSelected" /> -->
|
||||||
|
|
||||||
@@ -109,6 +102,7 @@ import SelectsMixin from './_mixins/selects.mixin'
|
|||||||
import ATasksTable from './_components/a-tasks-table.vue'
|
import ATasksTable from './_components/a-tasks-table.vue'
|
||||||
import AMastertaskDescription from './_components/a-mastertask-description.vue'
|
import AMastertaskDescription from './_components/a-mastertask-description.vue'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// MIXINS
|
// MIXINS
|
||||||
@@ -117,7 +111,7 @@ export default {
|
|||||||
// watch: mastertask.team_id
|
// watch: mastertask.team_id
|
||||||
|
|
||||||
emits: ['master-task-edited', 'close'],
|
emits: ['master-task-edited', 'close'],
|
||||||
mixins: [VuetifyFixFocusMixin, SelectsMixin],
|
mixins: [VuetifyFixFocusMixin, SelectsMixin, AutocompleteMixin],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
AMastertaskDescription,
|
AMastertaskDescription,
|
||||||
@@ -170,7 +164,6 @@ export default {
|
|||||||
return {
|
return {
|
||||||
mastertask: {
|
mastertask: {
|
||||||
project_id: { required },
|
project_id: { required },
|
||||||
employees: { required },
|
|
||||||
name: { required },
|
name: { required },
|
||||||
description: { required },
|
description: { required },
|
||||||
deadline: { required },
|
deadline: { required },
|
||||||
@@ -215,6 +208,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
removeEmployee({ id }) {
|
||||||
|
const isEmployeeInTeam = this.mastertask.team.employees.find(employee => employee.id == id)
|
||||||
|
|
||||||
|
if (isEmployeeInTeam) {
|
||||||
|
this.mastertask.team_id = null
|
||||||
|
this.mastertask.team = null
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mastertask.employees = this.mastertask.employees.filter(employee => employee.id != id)
|
||||||
|
},
|
||||||
|
|
||||||
removeMemberAlert(member) {
|
removeMemberAlert(member) {
|
||||||
const team = this.mastertask.team
|
const team = this.mastertask.team
|
||||||
this.$events.emit('show-delete-team-member-modal', { member, team })
|
this.$events.emit('show-delete-team-member-modal', { member, team })
|
||||||
@@ -244,7 +248,7 @@ export default {
|
|||||||
|
|
||||||
this.editMode = editMode || !this.mastertask.id
|
this.editMode = editMode || !this.mastertask.id
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$toast.error(error)
|
this.$toast.error(error.message || error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -254,6 +258,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
console.log(this.mastertask)
|
||||||
if (!(await this.v$.$validate())) return
|
if (!(await this.v$.$validate())) return
|
||||||
|
|
||||||
if (this.$refs['description'])
|
if (this.$refs['description'])
|
||||||
@@ -305,7 +310,7 @@ export default {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs['description']?.setHTML(this.mastertask.description || '')
|
this.$refs['description']?.setHTML(this.mastertask.description || '')
|
||||||
|
|
||||||
if (!this.mastertask) return
|
if (!this.mastertask?.id) return
|
||||||
|
|
||||||
const date = new Date(this.mastertask.deadline)
|
const date = new Date(this.mastertask.deadline)
|
||||||
const hours = date.getHours()
|
const hours = date.getHours()
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog v-model="visible" max-width="750">
|
||||||
|
<v-card>
|
||||||
|
<div class="d-flex justify-space-between align-center">
|
||||||
|
<h1>Vytvoriť</h1>
|
||||||
|
<v-icon @click.stop="closeModal()" icon="mdi-close" size="large"></v-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-card-actions class="mt-10 d-flex justify-start align-center">
|
||||||
|
<v-btn class="button primary half outline" @click.stop="createTask()"
|
||||||
|
>TASK</v-btn
|
||||||
|
>
|
||||||
|
<v-btn class="button primary half outline" @click.stop="createBlocker()"
|
||||||
|
>BLOCKER</v-btn
|
||||||
|
>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
emits: ['update', 'data'],
|
||||||
|
props: ['data'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
localData: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async showModal(data) {
|
||||||
|
this.localData = data
|
||||||
|
this.visible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
closeModal() {
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
createTask() {
|
||||||
|
this.$events.emit('show-subtask-modal', {
|
||||||
|
subtask: {
|
||||||
|
deadline: this.localData.date,
|
||||||
|
_deadline_time: this.localData.time,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
createBlocker() {
|
||||||
|
this.$events.emit('show-blocker-modal', {
|
||||||
|
blocker: {
|
||||||
|
starts_at_time: this.localData.time,
|
||||||
|
datepicker: this.localData.date,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -81,7 +81,7 @@ export default {
|
|||||||
validations() {
|
validations() {
|
||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
hours: {required, numeric, minValue: minValue(1)}
|
hours: {required, numeric, minValue: minValue(0.1)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -96,7 +96,7 @@ export default {
|
|||||||
subtask.timesheets.push({
|
subtask.timesheets.push({
|
||||||
id: null,
|
id: null,
|
||||||
employee_id: subtask.employee_id,
|
employee_id: subtask.employee_id,
|
||||||
time: typeof this.form.hours == 'string' ? parseInt(this.form.hours) : this.form.hours
|
time: typeof this.form.hours == 'string' ? parseFloat(this.form.hours) : this.form.hours
|
||||||
})
|
})
|
||||||
|
|
||||||
this._update(subtask)
|
this._update(subtask)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default {
|
|||||||
validations() {
|
validations() {
|
||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
hours: {required, numeric, minValue: minValue(1)}
|
hours: {required, numeric, minValue: minValue(0.1)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -57,7 +57,7 @@ export default {
|
|||||||
this.subtask.timesheets.push({
|
this.subtask.timesheets.push({
|
||||||
id: null,
|
id: null,
|
||||||
employee_id: this.subtask.employee_id,
|
employee_id: this.subtask.employee_id,
|
||||||
time: typeof this.form.hours == 'string' ? parseInt(this.form.hours) : this.form.hours
|
time: typeof this.form.hours == 'string' ? parseFloat(this.form.hours) : this.form.hours
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<v-card-actions class="d-flex flex-column modal-content">
|
<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>
|
<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>
|
||||||
|
|
||||||
<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 :custom-filter="customFilter" :match-any="true" 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-text-field v-model="subtask.title" @focus="onInputFocus" class="modal-input" label="Názov subtasku" variant="solo"></v-text-field>
|
<v-text-field v-model="subtask.title" @focus="onInputFocus" class="modal-input" label="Názov subtasku" variant="solo"></v-text-field>
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<template v-if="subtask.id">
|
<template v-if="subtask.id">
|
||||||
<v-autocomplete v-model="subtask.employee_id" label="Priradený" :items="enrichedEmployees" item-title="_fullName" item-value="id" variant="solo" class="modal-input" :class="{ error: employeeValidations[0]?.employee }"
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" v-model="subtask.employee_id" label="Priradený" :items="enrichedEmployees" item-title="_fullName" item-value="id" variant="solo" class="modal-input" :class="{ error: employeeValidations[0]?.employee }"
|
||||||
@update:model-value="employeeValidations[0] ? employeeValidations[0].employee = false : null" />
|
@update:model-value="employeeValidations[0] ? employeeValidations[0].employee = false : null" />
|
||||||
<v-text-field v-model="subtask.duration" class="modal-input" variant="solo" type="number" label="Časový odhad" :class="{ error: employeeValidations[0]?.duration }" append-inner-icon="mdi-clock-outline" @focus="onInputFocus"
|
<v-text-field v-model="subtask.duration" class="modal-input" variant="solo" type="number" label="Časový odhad" :class="{ error: employeeValidations[0]?.duration }" append-inner-icon="mdi-clock-outline" @focus="onInputFocus"
|
||||||
@update:model-value="employeeValidations[0] ? employeeValidations[0].duration = false : null" />
|
@update:model-value="employeeValidations[0] ? employeeValidations[0].duration = false : null" />
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div v-for="(employee, index) in subtask.employees" :key="`employee-${index}`" class="splitted-inputs">
|
<div v-for="(employee, index) in subtask.employees" :key="`employee-${index}`" class="splitted-inputs">
|
||||||
<v-autocomplete v-model="employee.id" label="Priradený" :items="enrichedEmployees" item-title="_fullName" item-value="id" variant="solo" class="modal-input left" :class="{ error: employeeValidations[index]?.employee }"
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" v-model="employee.id" label="Priradený" :items="enrichedEmployees" item-title="_fullName" item-value="id" variant="solo" class="modal-input left" :class="{ error: employeeValidations[index]?.employee }"
|
||||||
@update:model-value="employeeValidations[index] ? employeeValidations[index].employee = false : null" />
|
@update:model-value="employeeValidations[index] ? employeeValidations[index].employee = false : null" />
|
||||||
<v-text-field v-model="employee.duration" class="modal-input right" variant="solo" type="number" label="Časový odhad" append-inner-icon="mdi-clock-outline" :class="{ error: employeeValidations[index]?.duration }" @focus="onInputFocus"
|
<v-text-field v-model="employee.duration" class="modal-input right" variant="solo" type="number" label="Časový odhad" append-inner-icon="mdi-clock-outline" :class="{ error: employeeValidations[index]?.duration }" @focus="onInputFocus"
|
||||||
@update:model-value="employeeValidations[index] ? employeeValidations[index].duration = false : null" />
|
@update:model-value="employeeValidations[index] ? employeeValidations[index].duration = false : null" />
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
<div class="d-flex justify-start align-center mt-8">
|
<div class="d-flex justify-start align-center mt-8">
|
||||||
<v-btn class="done-btn" v-if="!subtask.is_finished" @click.stop="showCompleteTaskModal()">Dokončiť</v-btn>
|
<v-btn class="done-btn" v-if="!subtask.is_finished" @click.stop="showCompleteTaskModal()">Dokončiť</v-btn>
|
||||||
<v-btn class="button secondary" v-else @click.stop="showCompleteTaskModal()">Znovu otvoriť</v-btn>
|
<v-btn class="button secondary" v-else @click.stop="showCompleteTaskModal()">Znovu otvoriť</v-btn>
|
||||||
<v-btn class="button secondary" @click="save()">Uložiť zmeny</v-btn>
|
<v-btn class="button secondary" @click="closeModal()">Uložiť zmeny</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
@@ -113,14 +113,16 @@ import VuetifyFixFocusMixin from "@/plugins/tm@modal/_mixins/vuetify-fix-focus"
|
|||||||
import SelectsMixin from "../master-task/_mixins/selects.mixin"
|
import SelectsMixin from "../master-task/_mixins/selects.mixin"
|
||||||
import * as FileSaver from "file-saver"
|
import * as FileSaver from "file-saver"
|
||||||
import tmUtils from "@/plugins/tm@_utils/tm.utils"
|
import tmUtils from "@/plugins/tm@_utils/tm.utils"
|
||||||
|
import moment from 'moment'
|
||||||
import "@vueup/vue-quill/dist/vue-quill.snow.css"
|
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 ZTaskReadonlyInfo from "./_components/z-task-readonly-info.vue"
|
||||||
import { mapGetters } from "vuex"
|
import { mapGetters } from "vuex"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits: ["close", "refresh"],
|
emits: ["close", "refresh"],
|
||||||
mixins: [VuetifyFixFocusMixin, SelectsMixin],
|
mixins: [VuetifyFixFocusMixin, SelectsMixin, AutocompleteMixin],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ZTaskReadonlyInfo,
|
ZTaskReadonlyInfo,
|
||||||
@@ -196,6 +198,11 @@ export default {
|
|||||||
this.subtask = this._enrichTask(data)
|
this.subtask = this._enrichTask(data)
|
||||||
} else {
|
} else {
|
||||||
this.subtask = { master_task: mastertask, employees: [{}], _deadline_time: '17:00' }
|
this.subtask = { master_task: mastertask, employees: [{}], _deadline_time: '17:00' }
|
||||||
|
|
||||||
|
// if (subtask.deadline && subtask._deadline_time) {
|
||||||
|
// this.subtask.deadline = subtask.deadline
|
||||||
|
// this.subtask._deadline_time = subtask._deadline_time
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
this.editMode = editMode || !this.subtask.id
|
this.editMode = editMode || !this.subtask.id
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -222,6 +229,8 @@ export default {
|
|||||||
this._mergeDeadlineTimeDate()
|
this._mergeDeadlineTimeDate()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.subtask.deadline = moment(this.subtask.deadline).format('YYYY-MM-DD HH:MM:SS')
|
||||||
|
|
||||||
if (this.subtask?.id) {
|
if (this.subtask?.id) {
|
||||||
const { subtask } = await this.$axios.put_auth(`v1/subtasks/${this.subtask.id}`, this.subtask)
|
const { subtask } = await this.$axios.put_auth(`v1/subtasks/${this.subtask.id}`, this.subtask)
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
size="small"></v-icon>
|
size="small"></v-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-autocomplete v-model="employeeSelect" label="Priradený"
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" v-model="employeeSelect" label="Priradený"
|
||||||
:class="{ error: v$.team.employees.$errors.length }" :items="employeesOptions"
|
:class="{ error: v$.team.employees.$errors.length }" :items="employeesOptions"
|
||||||
item-title="_fullName" item-value="id" variant="solo" class="modal-input" clearable
|
item-title="_fullName" item-value="id" variant="solo" class="modal-input" clearable
|
||||||
ref="autocomplete" />
|
ref="autocomplete" />
|
||||||
@@ -42,10 +42,12 @@ import { required } from '@vuelidate/validators'
|
|||||||
import errorUtils from '@/plugins/lib@auth/_utils/error.utils'
|
import errorUtils from '@/plugins/lib@auth/_utils/error.utils'
|
||||||
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
|
||||||
|
|
||||||
const atLeastOne = (value) => value.length
|
const atLeastOne = (value) => value.length
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [AutocompleteMixin],
|
||||||
emits: ['close', 'update'],
|
emits: ['close', 'update'],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-row class="px-10 py-8 subheader">
|
<v-row class="px-10 py-8 subheader">
|
||||||
<v-select
|
<v-autocomplete :custom-filter="customFilter" :match-any="true"
|
||||||
v-model="selectedEmployeeId"
|
v-model="selectedEmployeeId"
|
||||||
class="subheader-input"
|
class="subheader-input"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
label="Zamestanec"
|
label="Zamestanec"
|
||||||
:items="[null, ...enrichedEmployees]"
|
:items="enrichedEmployees"
|
||||||
item-title="_fullName"
|
item-title="_fullName"
|
||||||
item-value="id"
|
item-value="id"
|
||||||
></v-select>
|
/>
|
||||||
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
@@ -24,8 +24,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [AutocompleteMixin],
|
||||||
emits: ['search', 'employee-selected'],
|
emits: ['search', 'employee-selected'],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
Reference in new issue
Block a user