mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 06:20:21 +00:00
[bugy & CR]
This commit is contained in:
1 parent
4f7847bc1b
commit
88792943e6
20 files changed
+201
-107
No files matched your search
@@ -15,7 +15,7 @@
|
|||||||
:class="{ active: isActive(page.pageName) }"
|
:class="{ active: isActive(page.pageName) }"
|
||||||
@click="$router.push({ name: page.pageName }); closeMenu()"> {{ page.title }}
|
@click="$router.push({ name: page.pageName }); closeMenu()"> {{ page.title }}
|
||||||
<span
|
<span
|
||||||
v-if="page.pageName == 'Overview - Unscheduled tasks' && unscheduledSubtasks"
|
v-if="page.pageName == 'Overview - Unscheduled tasks' && unscheduledSubtasks?.length >= 1"
|
||||||
class="mb-4 text-center rounded-circle badge">{{ unscheduledSubtasks?.length }}</span>
|
class="mb-4 text-center rounded-circle badge">{{ unscheduledSubtasks?.length }}</span>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ export default {
|
|||||||
const cleanedItem = normalizedItem.replace(pattern, '');
|
const cleanedItem = normalizedItem.replace(pattern, '');
|
||||||
const cleanedInput = normalizedInput.replace(pattern, '');
|
const cleanedInput = normalizedInput.replace(pattern, '');
|
||||||
|
|
||||||
// Check if the cleaned item starts with the cleaned input
|
// Check if the cleaned item starts with the cleaned input and return empty value for dropdown if present
|
||||||
return cleanedItem.toLowerCase().includes(cleanedInput.toLowerCase());
|
return cleanedItem.toLowerCase().includes(cleanedInput.toLowerCase()) || item === '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,9 @@ export default {
|
|||||||
return moment(date).format('YYYY-MM-DD HH:mm:ss')
|
return moment(date).format('YYYY-MM-DD HH:mm:ss')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
formatDate(date) {
|
||||||
|
return moment(date).format('DD.MM.yyyy')
|
||||||
|
},
|
||||||
|
|
||||||
isBefore(date, compareTo = null) {
|
isBefore(date, compareTo = null) {
|
||||||
return moment(date).isBefore(moment(compareTo))
|
return moment(date).isBefore(moment(compareTo))
|
||||||
|
|||||||
@@ -82,6 +82,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<small class="event-initials-date">{{ event.date?.toLocaleDateString('sk-SK') }}</small>
|
<small class="event-initials-date">{{ event.date?.toLocaleDateString('sk-SK') }}</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="event.subtask?.deadline" class="d-flex align-center">
|
||||||
|
<v-icon icon="mdi-skull-outline" size="small" />
|
||||||
|
<small class="ml-1" :class="`${tmUtils.isAfter(event.subtask?.deadline) ? 'text-black' : 'text-red-darken-4'}`">{{ tmUtils.formatDate(event.subtask?.deadline) }}</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="event-footer d-flex">
|
<div class="event-footer d-flex">
|
||||||
@@ -144,7 +149,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
focusedEventId: null,
|
focusedEventId: null,
|
||||||
timeCellHeight: 100
|
timeCellHeight: 100,
|
||||||
|
tmUtils
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -437,6 +443,7 @@ export default {
|
|||||||
|
|
||||||
.custom-event-body
|
.custom-event-body
|
||||||
padding: 20px 10px
|
padding: 20px 10px
|
||||||
|
width: 100%
|
||||||
|
|
||||||
.event-content
|
.event-content
|
||||||
&.is-blocker
|
&.is-blocker
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default {
|
|||||||
margin-top: 5px
|
margin-top: 5px
|
||||||
display: -webkit-box
|
display: -webkit-box
|
||||||
-webkit-line-clamp: 3
|
-webkit-line-clamp: 3
|
||||||
-webkit-box-orient: vertical
|
-webkit-box-orient: vertical
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
&:after
|
&:after
|
||||||
background: unset
|
background: unset
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-row class="px-10 py-8 subheader">
|
<v-row class="px-10 py-8 subheader">
|
||||||
<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-autocomplete :custom-filter="customFilter" :match-any="true" v-model="filter.projectId" class="subheader-input" no-data-text="reset" :items="[{id: null, code: ''}, ...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.teamId" class="subheader-input" :items="[...teams]" item-title="name" item-value="id" label="Tím" variant="outlined" />
|
<v-autocomplete :custom-filter="customFilter" :match-any="true" v-model="filter.teamId" class="subheader-input" :items="[{id: null, name: ''}, ...teams]" item-title="name" item-value="id" label="Tím" variant="outlined" />
|
||||||
|
|
||||||
<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-autocomplete :custom-filter="customFilter" :match-any="true" v-model="filter.employeeId" class="subheader-input" :items="[{id: null, _fullName: ''}, ...enrichedEmployees]" item-title="_fullName" item-value="id" label="Zamestnanec" variant="outlined" />
|
||||||
|
|
||||||
<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-autocomplete :custom-filter="customFilter" :match-any="true" v-model="filter.createdById" class="subheader-input" :items="[{id: null, _fullName: ''}, ...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" />
|
||||||
|
|
||||||
@@ -106,6 +106,7 @@ export default {
|
|||||||
top: 0
|
top: 0
|
||||||
z-index: 9999
|
z-index: 9999
|
||||||
background: white
|
background: white
|
||||||
|
flex: none !important
|
||||||
|
|
||||||
.subheader-input
|
.subheader-input
|
||||||
max-width: 250px !important
|
max-width: 250px !important
|
||||||
|
|||||||
@@ -33,7 +33,32 @@ export default {
|
|||||||
if (!this.mastertasks)
|
if (!this.mastertasks)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
return this.mastertasks.map(event => {
|
// return this.mastertasks.map(event => {
|
||||||
|
|
||||||
|
// const startOfDay = new Date(event.starts_at);
|
||||||
|
// startOfDay.setHours(0, 0, 0, 0); // Set to 00:00:00.000
|
||||||
|
|
||||||
|
// const endOfDay = new Date(event.deadline);
|
||||||
|
// endOfDay.setHours(23, 59, 59, 999); // Set to 23:59:59.999
|
||||||
|
|
||||||
|
// console.log({
|
||||||
|
// start: startOfDay,
|
||||||
|
// end: endOfDay,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// id: event.id,
|
||||||
|
// title: `${event.project_code} - ${event.name}`,
|
||||||
|
// title_html: `${event.project_code} - <b>${event.name}</b>`,
|
||||||
|
// start: startOfDay,
|
||||||
|
// end: endOfDay,
|
||||||
|
// is_finished: event.is_finished,
|
||||||
|
// project_code: event.project_code,
|
||||||
|
// backgroundColor: `${event.color}`,
|
||||||
|
// borderColor: event.color
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
return this.mastertasks.map(event => {
|
||||||
return {
|
return {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
title: `${event.project_code} - ${event.name}`,
|
title: `${event.project_code} - ${event.name}`,
|
||||||
@@ -94,5 +119,5 @@ export default {
|
|||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
.non-projects
|
.non-projects
|
||||||
height: 27rem !important
|
height: 100vw !important
|
||||||
</style>
|
</style>
|
||||||
@@ -34,9 +34,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters('auth', [
|
...mapGetters('auth', ['user']),
|
||||||
'user'
|
|
||||||
]),
|
|
||||||
|
|
||||||
splitDays() {
|
splitDays() {
|
||||||
const days = []
|
const days = []
|
||||||
@@ -87,7 +85,7 @@ export default {
|
|||||||
} else if (date.isSame(moment(), 'days')) {
|
} else if (date.isSame(moment(), 'days')) {
|
||||||
// Today
|
// Today
|
||||||
const elapsedMinutes = now.diff(now.clone().startOf('day'), 'minutes')
|
const elapsedMinutes = now.diff(now.clone().startOf('day'), 'minutes')
|
||||||
const workStart = this.user.work_start * 60 > elapsedMinutes ? this.user.work_start : elapsedMinutes
|
const workStart = this.user.work_start * 60 > elapsedMinutes ? this.user.work_start : elapsedMinutes / 60
|
||||||
const todayCapacityMinutes = (this.user.work_end - workStart) * 60
|
const todayCapacityMinutes = (this.user.work_end - workStart) * 60
|
||||||
unscheduledTime = todayCapacityMinutes - schedulesCapacityBySplit[i + 1] / 60 < 0 ? 0 : todayCapacityMinutes - schedulesCapacityBySplit[i + 1]
|
unscheduledTime = todayCapacityMinutes - schedulesCapacityBySplit[i + 1] / 60 < 0 ? 0 : todayCapacityMinutes - schedulesCapacityBySplit[i + 1]
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export default {
|
|||||||
this.$events.off('on-blocker-deleted')
|
this.$events.off('on-blocker-deleted')
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async onFilterChanged(filter) {
|
async onFilterChanged(filter) {
|
||||||
this.filter = filter
|
this.filter = filter
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
<W-loadable :content="files" emptyMessage="Žiadne prílohy neboli nahraté">
|
<W-loadable :content="files" emptyMessage="Žiadne prílohy neboli nahraté">
|
||||||
<div v-for="(file, index) in files" :key="file.id" class="d-flex align-center flex-wrap" :class="[index === 0 ? 'ml-0' : 'ml-2']">
|
<div v-for="(file, index) in files" :key="file.id" class="d-flex align-center flex-wrap" :class="[index === 0 ? 'ml-0' : 'ml-2']">
|
||||||
<v-chip class="mt-2">
|
<v-chip class="mt-2">
|
||||||
<p @click="handleDownload(index)" class="cursor-pointer">{{ file.name }}</p>
|
<p @click="downloadFile(file)" class="cursor-pointer">{{ file.name }}</p>
|
||||||
<v-icon class="mx-1" icon="mdi-paperclip"></v-icon>
|
<v-icon class="mx-1" icon="mdi-paperclip"></v-icon>
|
||||||
<p>{{ (file.file_size / (1024 * 1024)).toFixed(2) }} MB</p>
|
<p>{{ (file.file_size / (1024 * 1024)).toFixed(2) }} MB</p>
|
||||||
</v-chip>
|
</v-chip>
|
||||||
<div v-if="showDownload" class="d-flex align-center">
|
<div v-if="file.isLoading" class="d-flex align-center">
|
||||||
<img class="gif-loader" :src="require('@/plugins/app/_img/spinner.gif')" alt="Loading...">
|
<img class="gif-loader" :src="require('@/plugins/app/_img/spinner.gif')" alt="Loading...">
|
||||||
<p class="ml-2">Súbor sa sťahuje, počkajte prosím</p>
|
<p class="ml-2">Súbor sa sťahuje, počkajte prosím</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,28 +18,36 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import * as FileSaver from "file-saver"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
files: {
|
files: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
}
|
||||||
|
|
||||||
downloadFile: {
|
|
||||||
type: Function,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
|
|
||||||
showDownload: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
data() {
|
||||||
handleDownload(index) {
|
return {
|
||||||
this.downloadFile(index)
|
isLoading: false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async downloadFile(file) {
|
||||||
|
file.isLoading = true // Show loader
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(file.path)
|
||||||
|
const blob = await response.blob()
|
||||||
|
FileSaver.saveAs(blob, file.name)
|
||||||
|
} catch (error) {
|
||||||
|
this.$toast.error(error)
|
||||||
|
} finally {
|
||||||
|
file.isLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,12 +7,10 @@
|
|||||||
<Z-remaining-time :hours="mastertask.duration - mastertask.time_worked" />
|
<Z-remaining-time :hours="mastertask.duration - mastertask.time_worked" />
|
||||||
<Z-spent-time :hours="mastertask.time_worked" :is-exceeded="mastertask.time_worked > mastertask.duration" />
|
<Z-spent-time :hours="mastertask.time_worked" :is-exceeded="mastertask.time_worked > mastertask.duration" />
|
||||||
<Z-description :description="mastertask.description" />
|
<Z-description :description="mastertask.description" />
|
||||||
<Z-files :files="mastertask.files" :download-file="downloadFile" :showDownload="showDownload" />
|
<Z-files :files="mastertask.files" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as FileSaver from "file-saver"
|
|
||||||
|
|
||||||
import ZCreatedBy from '../../_components/read-only/z-created-by.vue'
|
import ZCreatedBy from '../../_components/read-only/z-created-by.vue'
|
||||||
import ZDeadline from '../../_components/read-only/z-deadline.vue'
|
import ZDeadline from '../../_components/read-only/z-deadline.vue'
|
||||||
import ZDescription from '../../_components/read-only/z-description.vue'
|
import ZDescription from '../../_components/read-only/z-description.vue'
|
||||||
@@ -41,31 +39,6 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showDownload: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async downloadFile(id) {
|
|
||||||
this.showDownload = true // Show loader
|
|
||||||
|
|
||||||
const file = this.mastertask.files[id]
|
|
||||||
|
|
||||||
// Iterate over each file in the task
|
|
||||||
const fileName = file.name
|
|
||||||
|
|
||||||
fetch(file.path)
|
|
||||||
.then(response => response.blob())
|
|
||||||
.then(blob => {
|
|
||||||
FileSaver.saveAs(blob, fileName)
|
|
||||||
this.showDownload = false
|
|
||||||
})
|
|
||||||
.catch(error => this.$toast.error(error))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<A-mastertask-description :mastertask="mastertask" />
|
<A-mastertask-description :mastertask="mastertask" />
|
||||||
|
|
||||||
<div class="d-flex justify-start align-center mt-8">
|
<div class="d-flex justify-start align-center mt-8">
|
||||||
<v-btn class="button primary" @click="toggleIsFinished()">{{ mastertask.is_finished === 1 ? 'Otvoriť mastertask' : 'Ukončiť mastertask' }}</v-btn>
|
<v-btn class="button primary" @click="toggleIsFinished()">{{ mastertask.is_finished ? 'Otvoriť mastertask' : 'Ukončiť mastertask' }}</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<A-tasks-table :subtasks="mastertask.subtasks" />
|
<A-tasks-table :subtasks="mastertask.subtasks" />
|
||||||
@@ -264,9 +264,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleIsFinished() {
|
async toggleIsFinished() {
|
||||||
this.mastertask.is_finished = this.mastertask.is_finished === 0 ? 1 : 0
|
this.mastertask.is_finished = this.mastertask.is_finished == 0 ? 1 : 0
|
||||||
this.save()
|
this.save()
|
||||||
|
// try {
|
||||||
|
// const toggleMastertask = { ...this.mastertask, is_finished: this.mastertask.is_finished ? 0 : 1, duration: this.mastertask.duration }
|
||||||
|
// const { mastertask } = await this.$axios.put_auth(`v1/mastertasks/${toggleMastertask.id}`, toggleMastertask)
|
||||||
|
|
||||||
|
// this.$emit('on-mastertask-edited', mastertask)
|
||||||
|
// } catch (error) {
|
||||||
|
// this.$toast.error(error)
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
|||||||
@@ -15,11 +15,6 @@
|
|||||||
{{ employee.name.charAt(0) + employee.surname.charAt(0) }}
|
{{ employee.name.charAt(0) + employee.surname.charAt(0) }}
|
||||||
</p>
|
</p>
|
||||||
<p class="ml-2" v-html="`${employee.name} ${employee.surname}`" />
|
<p class="ml-2" v-html="`${employee.name} ${employee.surname}`" />
|
||||||
<!-- <v-tooltip text="Vymazať priradeného zamestnanca" location="top">
|
|
||||||
<template v-slot:activator="{ props }">
|
|
||||||
<v-icon v-bind="props" class="ml-2 cursor-pointer" icon="mdi-close" />
|
|
||||||
</template>
|
|
||||||
</v-tooltip> -->
|
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -32,6 +27,19 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// methods: {
|
||||||
|
// async deleteEmployee() {
|
||||||
|
// try {
|
||||||
|
// const tempSubtask = {...this.subtask, employee_id: null, deadline: moment(this.subtask.deadline).format('YYYY-MM-DD HH:mm:ss') }
|
||||||
|
// const { subtask } = await this.$axios.put_auth(`v1/subtasks/${tempSubtask.id}`, tempSubtask)
|
||||||
|
// // employee_id
|
||||||
|
// this.$emit('on-subtask-edited', subtask)
|
||||||
|
// } catch (error) {
|
||||||
|
// this.$toast.error(error)
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<v-row no-gutters class="align-center">
|
||||||
|
<v-col cols="5">
|
||||||
|
<v-sheet class="py-3">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon icon="mdi-calendar-week" size="large"></v-icon>
|
||||||
|
<p class="mx-3">Deadline</p>
|
||||||
|
</div>
|
||||||
|
</v-sheet>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="7">
|
||||||
|
<v-sheet class="py-3">
|
||||||
|
<p v-if="deadline" :class="`${tmUtils.isAfter(deadline) ? 'text-black' : 'text-red-darken-4'}`">{{ tmUtils.formatDate(deadline) }}</p>
|
||||||
|
<p v-else>Tento task nema určený deadline</p>
|
||||||
|
</v-sheet>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmUtils from '@/plugins/tm@_utils/tm.utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
deadline: {
|
||||||
|
type: [String, Object, null],
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tmUtils
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -181,7 +181,6 @@ export default {
|
|||||||
this.closeModal()
|
this.closeModal()
|
||||||
this.$events.emit('on-subtask-edited', subtask)
|
this.$events.emit('on-subtask-edited', subtask)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
|
||||||
this.$toast.error(error.message || error)
|
this.$toast.error(error.message || error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<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-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-mastertask-status :isFinished="subtask.is_finished" />
|
||||||
|
<A-task-deadline :deadline="subtask.deadline" />
|
||||||
<A-task-timeblocks :timeblocks="subtask.timeblocks" :subtask="subtask" @update-subtask="$emit('update-subtask', $event)" />
|
<A-task-timeblocks :timeblocks="subtask.timeblocks" :subtask="subtask" @update-subtask="$emit('update-subtask', $event)" />
|
||||||
<Z-created-by :employee-id="subtask.created_by" />
|
<Z-created-by :employee-id="subtask.created_by" />
|
||||||
<A-task-assigned-employee :employee="subtask._employee" />
|
<A-task-assigned-employee :employee="subtask._employee" />
|
||||||
@@ -8,12 +9,10 @@
|
|||||||
<A-task-remaining-time :hours="subtask.duration - subtask.spent_time" />
|
<A-task-remaining-time :hours="subtask.duration - subtask.spent_time" />
|
||||||
<A-task-spent-time :hours="subtask.spent_time" :subtask="subtask" />
|
<A-task-spent-time :hours="subtask.spent_time" :subtask="subtask" />
|
||||||
<A-task-description :description="subtask.description" :mastertask-description="subtask.master_task_description" />
|
<A-task-description :description="subtask.description" :mastertask-description="subtask.master_task_description" />
|
||||||
<Z-files :files="subtask.files" :download-file="downloadFile" :showDownload="showDownload" />
|
<Z-files :files="subtask.files" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as FileSaver from "file-saver"
|
|
||||||
|
|
||||||
import AMastertaskName from './_components/a-mastertask-name.vue'
|
import AMastertaskName from './_components/a-mastertask-name.vue'
|
||||||
import AMastertaskStatus from './_components/a-mastertask-status.vue'
|
import AMastertaskStatus from './_components/a-mastertask-status.vue'
|
||||||
import ATaskTimeblocks from './_components/a-task-timeblocks.vue'
|
import ATaskTimeblocks from './_components/a-task-timeblocks.vue'
|
||||||
@@ -22,6 +21,7 @@ import ATaskAssignedEmployee from './_components/a-task-assigned-employee.vue
|
|||||||
import ATaskPlannedTime from './_components/a-task-planned-time.vue'
|
import ATaskPlannedTime from './_components/a-task-planned-time.vue'
|
||||||
import ATaskSpentTime from './_components/a-task-spent-time.vue'
|
import ATaskSpentTime from './_components/a-task-spent-time.vue'
|
||||||
import ATaskRemainingTime from './_components/a-task-remaining-time.vue'
|
import ATaskRemainingTime from './_components/a-task-remaining-time.vue'
|
||||||
|
import ATaskDeadline from './_components/a-task-deadline.vue'
|
||||||
import ATaskDescription from './_components/a-task-description.vue'
|
import ATaskDescription from './_components/a-task-description.vue'
|
||||||
import ZFiles from '@/plugins/tm@modal/_components/read-only/z-files.vue'
|
import ZFiles from '@/plugins/tm@modal/_components/read-only/z-files.vue'
|
||||||
|
|
||||||
@@ -38,7 +38,8 @@ export default {
|
|||||||
ATaskPlannedTime,
|
ATaskPlannedTime,
|
||||||
ATaskSpentTime,
|
ATaskSpentTime,
|
||||||
ATaskRemainingTime,
|
ATaskRemainingTime,
|
||||||
ATaskDescription
|
ATaskDescription,
|
||||||
|
ATaskDeadline
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@@ -46,30 +47,6 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showDownload: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async downloadFile(id) {
|
|
||||||
this.showDownload = true // Show loader
|
|
||||||
|
|
||||||
// Iterate over each file in the task
|
|
||||||
const file = this.subtask.files[id]
|
|
||||||
const fileName = file.name
|
|
||||||
|
|
||||||
fetch(file.path)
|
|
||||||
.then(response => response.blob())
|
|
||||||
.then(blob => {
|
|
||||||
FileSaver.saveAs(blob, fileName)
|
|
||||||
this.showDownload = false
|
|
||||||
})
|
|
||||||
.catch(error => this.$toast.error(error))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -268,7 +268,6 @@ export default {
|
|||||||
|
|
||||||
this.closeModal()
|
this.closeModal()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
|
||||||
this.$toast.error(error)
|
this.$toast.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
this.isSaving = true
|
this.isSaving = true
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
<v-table>
|
<v-table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-left">Kód projektu</th>
|
<th @click="getSelectedTimeblockDeadline" class="text-left">Kód projektu</th>
|
||||||
<th class="text-left">Názov</th>
|
<th class="text-left">Názov</th>
|
||||||
|
<th class="text-left">Deadline</th>
|
||||||
|
<th class="text-left">Najbližší neuzavretý</th>
|
||||||
<th class="text-left">Naplánované</th>
|
<th class="text-left">Naplánované</th>
|
||||||
<th class="text-left">Zostávajúce</th>
|
<th class="text-left">Zostávajúce</th>
|
||||||
<th class="text-left">Akcia</th>
|
<th class="text-left">Akcia</th>
|
||||||
@@ -14,6 +16,13 @@
|
|||||||
<tr class="cursor-pointer" v-for="subtask in subtasks" :key="subtask.id" @click.stop="editTask(subtask, false)">
|
<tr class="cursor-pointer" v-for="subtask in subtasks" :key="subtask.id" @click.stop="editTask(subtask, false)">
|
||||||
<td>{{ subtask.project_code }}</td>
|
<td>{{ subtask.project_code }}</td>
|
||||||
<td>{{ subtask.title ?? subtask.master_task?.name ?? subtask.master_task }}</td>
|
<td>{{ subtask.title ?? subtask.master_task?.name ?? subtask.master_task }}</td>
|
||||||
|
<td v-if="subtask.deadline">{{ getDeadline(subtask) }}</td>
|
||||||
|
<v-tooltip text="Deadline z Mastertasku" location="end">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<td v-if="!subtask.deadline" v-bind="props">{{ getDeadline(subtask) }}*</td>
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
|
<td>{{ displayTimeblock(subtask) }}</td>
|
||||||
<td>{{ subtask.duration }} h</td>
|
<td>{{ subtask.duration }} h</td>
|
||||||
<td>{{ (subtask.duration - subtask.spent_time).toFixed(2) }} h</td>
|
<td>{{ (subtask.duration - subtask.spent_time).toFixed(2) }} h</td>
|
||||||
<td class="task-action">
|
<td class="task-action">
|
||||||
@@ -31,6 +40,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
subtasks: {
|
subtasks: {
|
||||||
@@ -38,6 +50,10 @@ export default {
|
|||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapGetters('tm', ['mastertasks'])
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
editTask(subtask, editMode = false) {
|
editTask(subtask, editMode = false) {
|
||||||
@@ -46,6 +62,34 @@ export default {
|
|||||||
|
|
||||||
deleteTask(subtask) {
|
deleteTask(subtask) {
|
||||||
this.$events.emit('show-delete-task-modal', {subtask})
|
this.$events.emit('show-delete-task-modal', {subtask})
|
||||||
|
},
|
||||||
|
|
||||||
|
getDeadline(subtask) {
|
||||||
|
if(subtask.deadline) return moment(subtask.deadline).format('DD.MM.yyyy')
|
||||||
|
else if(!this.mastertasks) return ''
|
||||||
|
const masterTask = this.mastertasks.find((mt) => mt.id === subtask.master_task_id)
|
||||||
|
return moment(masterTask).format('DD.MM.yyyy') ? moment(masterTask.deadline).format('DD.MM.yyyy') : ''
|
||||||
|
},
|
||||||
|
|
||||||
|
getSelectedTimeblockDeadline(timeblocks) {
|
||||||
|
const currentDate = moment()
|
||||||
|
|
||||||
|
const unfinishedTimeblocks = timeblocks.filter(tb => tb.is_finished === 0)
|
||||||
|
if (unfinishedTimeblocks.length > 0) return moment(unfinishedTimeblocks[0].end).format('DD.MM.yyyy, HH:mm')
|
||||||
|
|
||||||
|
const upcomingTimeblocks = timeblocks
|
||||||
|
.filter(tb => moment(tb.end).isAfter(currentDate))
|
||||||
|
.sort((a, b) => moment(a.end).diff(moment(b.end)))
|
||||||
|
|
||||||
|
if (upcomingTimeblocks.length > 0 && upcomingTimeblocks.is_finished === 0) return moment(upcomingTimeblocks[0].end).format('DD.MM.yyyy, HH:mm')
|
||||||
|
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
|
||||||
|
displayTimeblock(subtask) {
|
||||||
|
const timeblockDeadline = this.getSelectedTimeblockDeadline(subtask.timeblocks)
|
||||||
|
|
||||||
|
return timeblockDeadline !== '' ? timeblockDeadline : 'Žiadny termín'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,12 @@
|
|||||||
|
|
||||||
<td>{{ subtask.project_code }}</td>
|
<td>{{ subtask.project_code }}</td>
|
||||||
<td>{{ subtask.title ?? subtask.master_task?.name ?? subtask.master_task }}</td>
|
<td>{{ subtask.title ?? subtask.master_task?.name ?? subtask.master_task }}</td>
|
||||||
<td>{{ getDeadline(subtask) }}</td>
|
<td v-if="subtask.deadline">{{ getDeadline(subtask) }}</td>
|
||||||
|
<v-tooltip text="Deadline z Mastertasku" location="end">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<td v-if="!subtask.deadline" v-bind="props">{{ getDeadline(subtask) }}*</td>
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
<td>{{ subtask.spent_time }} h</td>
|
<td>{{ subtask.spent_time }} h</td>
|
||||||
<td class="task-action">
|
<td class="task-action">
|
||||||
<div class="d-flex justify-start">
|
<div class="d-flex justify-start">
|
||||||
|
|||||||
@@ -81,11 +81,13 @@ export class I18n {
|
|||||||
(response) => {
|
(response) => {
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
|
// fixnut aby logger nezaznamenaval chybu ak padne requet kvoli loggeru
|
||||||
(error) => {
|
(error) => {
|
||||||
axios.post('https://logger.wezeo.dev/cms/api/data/new', {
|
if(error.response) {
|
||||||
data: JSON.stringify(error)
|
axios.post('https://logger.wezeo.dev/cms/api/data/new', {
|
||||||
})
|
data: JSON.stringify(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in new issue
Block a user