Merge branch 'main' of bitbucket.org:studiotem/jandl-taskmanager-app

This commit is contained in:
StudioTem committed 2023-09-27 14:18:53 +02:00
commit 5ed4d64875
23 files changed
+279 -59

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+1
View File
@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<link rel="icon" href="<%= BASE_URL %>favicon/favicon.ico" />
<title>ToDo by JANDL</title>
</head>
<body>
@@ -182,29 +182,53 @@ export default {
const date = addDays(this.calConfig.date, newSplit - 1)
const start = date.setHours(Math.round(event.startTimeMinutes / 60), Math.round(event.startTimeMinutes % 60), 0)
const end = date.setHours(Math.round(event.endTimeMinutes / 60), Math.round(event.endTimeMinutes % 60), 0)
const split = this.splits.find(split => split.id == newSplit )
const editedSubtask = {
...event.subtask,
timeblocks: [...event.subtask.timeblocks, {
id: event._timeblock_id,
is_finished: 0,
start: tmUtils.formatDateTimeForBackend(start),
end: tmUtils.formatDateTimeForBackend(end)
}],
timesheets: event.subtask.timesheets.map((timesheet) => {
delete timesheet.employee_name
return timesheet
})
}
if(this.$route.name == 'Dashboard - Tasks') {
const editedSubtask = {
...event.subtask,
timeblocks: [...event.subtask.timeblocks, {
id: event._timeblock_id,
is_finished: 0,
start: tmUtils.formatDateTimeForBackend(start),
end: tmUtils.formatDateTimeForBackend(end)
}],
timesheets: event.subtask.timesheets.map((timesheet) => {
delete timesheet.employee_name
return timesheet
})
}
try {
const { subtask } = await this.$axios.put_auth(`v1/subtasks/${editedSubtask.id}`, editedSubtask)
this.$events.emit('on-subtask-edited', subtask)
try {
const { subtask } = await this.$axios.put_auth(`v1/subtasks/${editedSubtask.id}`, editedSubtask)
this.$events.emit('on-subtask-edited', subtask)
if (subtask.employee_id == this.$store.getters['auth/user'].id)
this.$events.emit('show-task-add-time-modal', { subtask })
} catch (error) {
this.$toast.error(error.message || error)
if (subtask.employee_id == this.$store.getters['auth/user'].id)
this.$events.emit('show-task-add-time-modal', { subtask })
} catch (error) {
this.$toast.error(error.message || error)
}
} else if(this.$route.name == 'Dashboard - Teams') {
const editedSubtask = {
...event.subtask,
employee_id: split.employee_id,
timeblocks: [...event.subtask.timeblocks, {
id: event._timeblock_id,
is_finished: 0,
start: tmUtils.formatDateTimeForBackend(event.start),
end: tmUtils.formatDateTimeForBackend(event.end)
}],
timesheets: event.subtask.timesheets.map((timesheet) => {
delete timesheet.employee_name
return timesheet
})
}
try {
const { subtask } = await this.$axios.put_auth(`v1/subtasks/${editedSubtask.id}`, editedSubtask)
this.$events.emit('on-subtask-edited', subtask)
} catch (error) {
this.$toast.error(error.message || error)
}
}
},
@@ -2,7 +2,6 @@ import { setHours, setMinutes } from 'date-fns'
import moment from 'moment'
import timelineUtils from '@/plugins/tm@dashboard/tasks/_utils/timeline.utils'
export default {
computed: {
calendarData() {
@@ -57,6 +56,8 @@ export default {
days.push({
id: index + 1,
name: employee.name,
employee_id: employee.id,
// date: this.filter.start,
fullname: `${employee.name} ${employee.surname}`,
initials: employee.name.charAt(0).toUpperCase() + employee.surname.charAt(0).toUpperCase(),
unscheduledTime: unscheduledTime > 0 ? (unscheduledTime / 60).toFixed(1) : 0,
+5 -1
View File
@@ -1,7 +1,7 @@
<template>
<div ref="page">
<A-team-filter v-if="$store.getters['tm/isInitialized']" :split-days="calendarData ? splitDays : null" @filter-changed="onFilterChanged" />
<Z-timeline v-if="calendarData" :cal-data="calendarData" :cal-config="calendarConfig" :splits="splitDays" />
<Z-timeline v-if="calendarData && !hideCal" :cal-data="calendarData" :cal-config="calendarConfig" :splits="splitDays" />
</div>
</template>
@@ -23,6 +23,8 @@ export default {
data() {
return {
hideCal: false,
filter: null,
subtasks: null,
blockers: null,
@@ -78,6 +80,8 @@ export default {
const { subtasks, blockers } = await this.$axios.get_auth(`v1/subtasks?${queryParameters}`)
this.subtasks = subtasks
this.blockers = blockers
this.hideCal = true
setTimeout(() => { this.hideCal = false }, 0);
} catch (error) {
this.$toast.error('Nepodarilo sa nacitat data!')
}
@@ -2,7 +2,7 @@
<Z-status :status="mastertask.is_finished" />
<Z-deadline :deadline="mastertask.deadline" />
<Z-created-by :employee-id="mastertask.created_by" />
<Z-team :team="mastertask.team" :employees="mastertask.employees" />
<Z-team :team="mastertask.team" :employees="mastertask._allEmployees || mastertask.employees" />
<Z-planned-time :hours="mastertask.duration" />
<Z-remaining-time :hours="mastertask.duration - mastertask.time_worked" />
<Z-spent-time :hours="mastertask.time_worked" :is-exceeded="mastertask.time_worked > mastertask.duration" />
@@ -3,12 +3,12 @@
<tbody class="px-5 py-5">
<tr v-for="subtask in enrichedtasks" :key="subtask.id">
<td v-if="subtask?.title" class="w-25">{{ subtask.title }}</td>
<td v-else>{{ subtask?.master_task }}</td>
<td class="w-50" v-else>{{ subtask?.master_task }}</td>
<td class="d-flex align-center">
<p class="employee-selected-avatar d-flex justify-center align-center">
{{ subtask._employeeInitials }}
</p>
<p class="ml-2">{{ subtask._employeeFullName }}</p>
<p class="ml-2 line-clamp">{{ subtask._employeeFullName }}</p>
</td>
<td>
<p class="text-light-green" v-if="subtask.is_finished">Ukončený</p>
@@ -78,4 +78,11 @@ v-table, tbody
td:not(:last-child)
border-right: thin solid rgba(var(--v-border-color), var(--v-border-opacity))
.line-clamp
display: -webkit-box
-webkit-line-clamp: 2
-webkit-box-orient: vertical
overflow: hidden
text-overflow: ellipsis
</style>
@@ -22,7 +22,7 @@ import modalMixin from '@/plugins/tm@modal/_mixins/modal.mixin'
export default {
mixins: [ modalMixin ],
emits: ['close', 'on-master-task-edited'],
emits: ['close', 'master-task-edited'],
data() {
return {
@@ -43,7 +43,8 @@ export default {
const { mastertask } = await this.$axios.put_auth(`v1/mastertasks/${tempMastertask.id}`, tempMastertask)
this.visible = false
this.$emit('on-master-task-edited', mastertask)
this.$emit('master-task-edited', mastertask)
this.$events.emit('close-both-modals')
} catch (error) {
this.$toast.error(error)
}
@@ -23,9 +23,9 @@
<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" :class="{error: v$.mastertask.employees.$errors.length}" label="Tím" variant="solo" :items="teams" item-value="id" item-title="name" :disabled="mastertask.id"></v-select>
<template v-if="mastertask.employees">
<template v-if="!mastertask.id">
<v-text-field v-for="(employee, index) in mastertask.employees" :key="employee.id" @focus="onInputFocus" class="modal-input" :class="{error: employee.invalid}" :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) }}
@@ -35,6 +35,17 @@
</v-text-field>
</template>
<!-- mastertask._allEmployees is used in edit to combine team employees with extra employees and subtask durations -->
<template v-else>
<v-text-field v-for="employee in mastertask._allEmployees" :key="employee.id" class="modal-input" :label="`${employee.name} ${employee.surname}`" variant="solo" placeholder="h" v-model="employee.duration" type="number" :disabled="true">
<p class="employee-select-avatar d-flex justify-center align-center">
{{ employee?.name.charAt(0) + employee?.surname.charAt(0) }}
</p>
<v-icon @click.stop="removeEmployee(employee)" class="remove-input" icon="mdi-close" size="small"></v-icon>
</v-text-field>
</template>
<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" /> -->
@@ -60,7 +71,7 @@
<div class="d-flex justify-space-between align-center modal-header border-bottom pb-4">
<h1>{{ mastertask.name }}</h1>
<div class="d-flex justify-start">
<img class="icon" :src="require('@/plugins/app/_img/icons/pencil.svg')" alt="pencil-icon" @click.stop="editMode = !editMode" />
<img v-if="!mastertask.is_finished" class="icon" :src="require('@/plugins/app/_img/icons/pencil.svg')" alt="pencil-icon" @click.stop="editMode = !editMode" />
<img class="icon trash" :src="require('@/plugins/app/_img/icons/bin.svg')" alt="trash-icon" @click.stop="openDeleteMasterTaskModal(mastertask)" />
</div>
</div>
@@ -184,6 +195,8 @@ export default {
},
mounted() {
this.$events.on('close-both-modals', this.closeBothModals)
this.$events.on('master-task:team-member-deleted', ({ teamId, memberId }) => {
if (this.mastertask.team_id != teamId)
return
@@ -216,9 +229,15 @@ export default {
this.$events.off('on-subtask-deleted')
this.$events.off('on-subtask-edited')
this.$events.off('master-task:team-member-deleted')
this.$events.off('close-both-modals', this.closeBothModals)
},
methods: {
closeBothModals() {
this.$emit('close')
this.visible = false
},
removeEmployee({ id }) {
// const isEmployeeInTeam = this.mastertask.team?.employees.find(employee => employee.id == id)
@@ -253,6 +272,15 @@ export default {
this.mastertask.project_id = project?.id || null
this.mastertask.team_id = this.mastertask.team?.id || null
this.mastertask._allEmployees = this.mastertask.subtasks.map(({ employee_id, duration}) => {
const employee = this.enrichedEmployees.find(({ id }) => id == employee_id)
employee.duration = duration
return employee
})
console.log('NEW', this.mastertask)
// this.mastertask.project_code = project.code
} else {
this.mastertask = { employees: [], duration: 0, _deadline_time: '17:00' }
@@ -264,11 +292,20 @@ export default {
}
},
async toggleIsFinished(mastertask) {
this.$events.emit('show-complete-master-task-modal', { mastertask })
toggleIsFinished() {
this.$events.emit('show-complete-master-task-modal', { mastertask: this.mastertask })
},
async save() {
this.isSaving = true
if (this.mastertask.id) {
this.mastertask.employees = this.mastertask.employees.map(employee => {
const { duration } = this.mastertask.subtasks.find(({ employee_id }) => employee_id == employee.id)
employee.duration = duration
return employee
})
}
const vuelidateValid = await this.v$.$validate()
const employeesValid = await this._validateEmployees()
if (!vuelidateValid || !employeesValid) return
@@ -279,7 +316,6 @@ export default {
this._mergeDeadlineTimeDate()
try {
this.isSaving = true
this.mastertask.deadline = moment(this.mastertask.deadline).format('YYYY-MM-DD HH:MM:SS')
if (this.mastertask.id) await this.$axios.put_auth_data(`v1/mastertasks/${this.mastertask.id}`, this.mastertask)
@@ -290,7 +326,7 @@ export default {
} catch (error) {
this.$toast.error(error)
} finally {
this.isSaving = true
this.isSaving = false
}
},
@@ -307,7 +343,12 @@ export default {
},
addTask() {
this.$events.emit('show-subtask-modal', { subtask: null, editMode: false, mastertask: this.mastertask })
this.$events.emit('show-subtask-modal', {
subtask: {
master_task_id: this.mastertask.id,
project_code: this.mastertask.project_code
}
})
},
onQuillEditorInput() {
@@ -43,7 +43,7 @@ export default {
delete timesheet.employee_name
return timesheet
})}
const { subtask } = await this.$axios.put_auth(`v1/subtasks/${tempSubtask.id}`, tempSubtask)
const { subtask } = await this.$axios.put_auth(`v1/subtasks/${tempSubtask.id}`, tempSubtask)
this.visible = false
this.$emit('on-subtask-edited', subtask)
+29 -2
View File
@@ -49,8 +49,13 @@
<template v-else>
<!-- R: Subtask nemoze mat viacero employees ci? Vyhodit -->
<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 remove-employee" :class="{'hide-arrow': employee.id}">
<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" />
<svg v-if="employee.id" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="dp__icon dp__clear_icon dp__input_icons" data-test="clear-icon"
@click.prevent="employee.id = null">
<path d="M23.057 7.057l-16 16c-0.52 0.52-0.52 1.365 0 1.885s1.365 0.52 1.885 0l16-16c0.52-0.52 0.52-1.365 0-1.885s-1.365-0.52-1.885 0z"></path>
<path d="M7.057 8.943l16 16c0.52 0.52 1.365 0.52 1.885 0s0.52-1.365 0-1.885l-16-16c-0.52-0.52-1.365-0.52-1.885 0s-0.52 1.365 0 1.885z"></path>
</svg>
<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" />
</div>
</template>
@@ -220,6 +225,11 @@ export default {
this.subtask.deadline = subtask.deadline
this.subtask._deadline_time = subtask._deadline_time
}
if(subtask?.master_task_id && subtask?.project_code) {
this.subtask.master_task_id = subtask.master_task_id
this.subtask.project_code = subtask.project_code
}
}
this.editMode = editMode || !this.subtask.id
@@ -269,6 +279,7 @@ export default {
this.$events.emit('on-subtask-edited', this.subtask)
})
this.$events.emit('task-created')
this.$events.emit('close-both-modals')
}
this.closeModal()
@@ -408,6 +419,22 @@ export default {
height: 50px
width: 203px
.remove-employee
position: relative
width: 100%
svg
position: absolute
right: 205px
top: 28px
z-index: 5
opacity: .5
&.hide-arrow ::v-deep
.mdi-menu-down,
.mdi-menu-up
display: none
.employee-wrapper
position: relative
width: 100%
@@ -417,7 +444,7 @@ export default {
right: 4px
top: 28px
z-index: 5
opacity: 0.5
opacity: .5
&.hide-arrow ::v-deep
.mdi-menu-down,
@@ -9,7 +9,6 @@
item-title="_fullName"
item-value="id"
/>
<v-text-field
v-model="searchQuery"
class="subheader-input"
@@ -19,21 +18,38 @@
@input="onInput"
type="text"
></v-text-field>
<v-select
v-model="sortOrder"
class="subheader-input"
:items="sortOptions"
variant="outlined"
label="Zoradiť podľa"
item-title="text"
item-value="value"
></v-select>
</v-row>
</template>
<script>
import { mapGetters } from 'vuex'
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
import { mapGetters } from 'vuex'
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin'
export default {
mixins: [AutocompleteMixin],
emits: ['search', 'employee-selected'],
emits: ['search', 'employee-selected', 'update-sort-order'],
data() {
return {
selectedEmployeeId: null,
searchQuery: ''
searchQuery: '',
sortOrder: null,
sortOptions: [
{ text: 'Zrušiť filter', value: null },
{ text: 'Názov (A-Z)', value: 'nameAsc' },
{ text: 'Názov (Z-A)', value: 'nameDesc' },
{ text: 'Dátum (Najnovší - Najstarší)', value: 'deadlineDesc' },
{ text: 'Dátum (Najstarší - Najnovší)', value: 'deadlineAsc' }
]
}
},
@@ -42,18 +58,22 @@ export default {
},
computed: {
...mapGetters('tm', ['enrichedEmployees']),
...mapGetters('tm', ['enrichedEmployees'])
},
methods: {
onInput() {
this.$emit('search', this.searchQuery)
}
},
},
watch: {
selectedEmployeeId() {
this.$emit('employee-selected', this.selectedEmployeeId)
},
sortOrder(value) {
this.$emit('update-sort-order', value)
}
}
}
@@ -92,4 +112,11 @@ export default {
max-width: 100% !important
min-width: 100%
margin-bottom: 15px
:deep(.v-select__selection-text)
display: -webkit-box
-webkit-line-clamp: 1
-webkit-box-orient: vertical
overflow: hidden
text-overflow: ellipsis
</style>
@@ -15,7 +15,7 @@
<tbody>
<tr class="cursor-pointer" v-for="subtask in subtasks" :key="subtask.id" @click.stop="editTask(subtask, false)">
<td>{{ subtask.project_code }}</td>
<td>{{ subtask.title ?? subtask.master_task?.name ?? subtask.master_task }}</td>
<td>{{ subtask.title ?? 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 }">
@@ -1,7 +1,7 @@
<template>
<div>
<Z-tasks-search @search="onSearch" @employee-selected="onEmployeeSelected" />
<Z-tasks-search @search="onSearch" @employee-selected="onEmployeeSelected" @update-sort-order="onUpdateSortOrder" />
<W-loadable :content="filteredOngoingTasks" :emptyMessage="searchQuery.length ? 'Neboli nájdené žiadne úlohy' : 'Nenašli sa žiadne záznamy'">
<A-ongoing-table :subtasks="filteredOngoingTasks" />
@@ -23,7 +23,8 @@ export default {
data() {
return {
searchQuery: ''
searchQuery: '',
sortOrder: null
}
},
@@ -32,15 +33,48 @@ export default {
},
computed: {
...mapGetters('tm', ['ongoingSubtasks']),
...mapGetters('tm', ['ongoingSubtasks', 'mastertasks']),
filteredOngoingTasks() {
if (!this.ongoingSubtasks)
return null
return this.ongoingSubtasks.filter(subtask => {
return this.filterParams(this.searchQuery, subtask.master_task?.name || subtask.master_task) || this.filterParams(this.searchQuery, subtask.project_code)
let filteredSubtasks = this.ongoingSubtasks.filter(subtask => {
return this.filterParams(this.searchQuery, subtask.master_task?.name || subtask.title) || this.filterParams(this.searchQuery, subtask.project_code)
})
//Sorting by name A-Z
if (this.sortOrder === 'nameAsc') {
filteredSubtasks.sort((a, b) => {
const nameA = (a.title || a.master_task || '').toLowerCase()
const nameB = (b.title || b.master_task || '').toLowerCase()
return nameA.localeCompare(nameB)
})
//Sorting by name Z-A
} else if (this.sortOrder === 'nameDesc') {
filteredSubtasks.sort((a, b) => {
const nameA = (a.title || a.master_task || '').toLowerCase()
const nameB = (b.title || b.master_task || '').toLowerCase()
return nameB.localeCompare(nameA)
})
//Sorting by date Newest-Oldest
} else if (this.sortOrder === 'deadlineDesc') {
filteredSubtasks.sort((a, b) => {
const deadlineA = a.deadline ? new Date(a.deadline) : new Date(this.getMasterTaskDeadline(a))
const deadlineB = b.deadline ? new Date(b.deadline) : new Date(this.getMasterTaskDeadline(b))
return deadlineB - deadlineA
})
//Sorting by date Oldest-Newest
} else if (this.sortOrder === 'deadlineAsc') {
filteredSubtasks.sort((a, b) => {
const deadlineA = a.deadline ? new Date(a.deadline) : new Date(this.getMasterTaskDeadline(a))
const deadlineB = b.deadline ? new Date(b.deadline) : new Date(this.getMasterTaskDeadline(b))
return deadlineA - deadlineB
})
}
return filteredSubtasks
}
},
@@ -53,9 +87,19 @@ export default {
this.$store.dispatch('tm/loadOngoingSubtasks', id)
},
onUpdateSortOrder(newSortOrder) {
this.sortOrder = newSortOrder
},
filterParams(searchQuery, row) {
if(!row) return false
return row.toLowerCase().includes(searchQuery.toLowerCase())
},
getMasterTaskDeadline(subtask) {
if(!this.mastertasks) return ''
const masterTask = this.mastertasks.find((mt) => mt.id === subtask.master_task_id)
return masterTask ? masterTask.deadline : ''
}
}
}
@@ -14,7 +14,7 @@
<tr class="cursor-pointer" v-for="subtask in subtasks" :key="subtask.id" @click.stop="editTask(subtask, false)">
<td>{{ subtask.project_code }}</td>
<td>{{ subtask.title ?? subtask.master_task?.name ?? subtask.master_task }}</td>
<td>{{ subtask.title ?? 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 }">
@@ -1,7 +1,7 @@
<template>
<div>
<Z-tasks-search @search="onSearch" @employee-selected="onEmployeeSelected" />
<Z-tasks-search @search="onSearch" @employee-selected="onEmployeeSelected" @update-sort-order="onUpdateSortOrder" />
<W-loadable :content="filteredUnscheduledTasks" :emptyMessage="searchQuery.length ? 'Neboli nájdené žiadne úlohy' : 'Nenašli sa žiadne záznamy'">
<A-unscheduled-table :subtasks="filteredUnscheduledTasks" />
@@ -23,7 +23,8 @@ export default {
data() {
return {
searchQuery: ''
searchQuery: '',
sortOrder: null
}
},
@@ -32,15 +33,47 @@ export default {
},
computed: {
...mapGetters('tm', ['unscheduledSubtasks']),
...mapGetters('tm', ['unscheduledSubtasks', 'mastertasks']),
filteredUnscheduledTasks() {
if (!this.unscheduledSubtasks)
return null
return this.unscheduledSubtasks.filter(subtask => {
return this.filterParams(this.searchQuery, subtask.master_task?.name || subtask.master_task) || this.filterParams(this.searchQuery, subtask.project_code)
let filteredSubtasks = this.unscheduledSubtasks.filter(subtask => {
return this.filterParams(this.searchQuery, subtask.master_task?.name || subtask.title) || this.filterParams(this.searchQuery, subtask.project_code)
})
if (this.sortOrder === 'nameAsc') {
filteredSubtasks.sort((a, b) => {
const nameA = (a.title || a.master_task || '').toLowerCase()
const nameB = (b.title || b.master_task || '').toLowerCase()
return nameA.localeCompare(nameB)
})
//Sorting by name Z-A
} else if (this.sortOrder === 'nameDesc') {
filteredSubtasks.sort((a, b) => {
// const { nameA, nameB } = this._getTaskTitleOrMastertaskName(a, b)
const nameA = (a.title || a.master_task || '').toLowerCase()
const nameB = (b.title || b.master_task || '').toLowerCase()
return nameB.localeCompare(nameA)
})
//Sorting by date Newest-Oldest
} else if (this.sortOrder === 'deadlineDesc') {
filteredSubtasks.sort((a, b) => {
const deadlineA = a.deadline ? new Date(a.deadline) : new Date(this.getMasterTaskDeadline(a))
const deadlineB = b.deadline ? new Date(b.deadline) : new Date(this.getMasterTaskDeadline(b))
return deadlineB - deadlineA
})
//Sorting by date Oldest-Newest
} else if (this.sortOrder === 'deadlineAsc') {
filteredSubtasks.sort((a, b) => {
const deadlineA = a.deadline ? new Date(a.deadline) : new Date(this.getMasterTaskDeadline(a))
const deadlineB = b.deadline ? new Date(b.deadline) : new Date(this.getMasterTaskDeadline(b))
return deadlineA - deadlineB
})
}
return filteredSubtasks
}
},
@@ -53,9 +86,19 @@ export default {
this.$store.dispatch('tm/loadUnscheduledSubtasks', id)
},
onUpdateSortOrder(newSortOrder) {
this.sortOrder = newSortOrder
},
filterParams(searchQuery, row) {
if(!row) return false
return row.toLowerCase().includes(searchQuery.toLowerCase())
},
getMasterTaskDeadline(subtask) {
if(!this.mastertasks) return ''
const masterTask = this.mastertasks.find((mt) => mt.id === subtask.master_task_id)
return masterTask ? masterTask.deadline : ''
}
}
}