mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
feat(z-task-modal): implement draft handling for timeblocks and improve employee-specific timeblock management
This commit is contained in:
1 parent
1bca6cbfae
commit
81aaa293dd
3 files changed
+188
-25
No files matched your search
@@ -33,9 +33,9 @@
|
||||
icon="mdi-minus"
|
||||
size="medium"
|
||||
@click.stop="
|
||||
subtask.id
|
||||
timeblock.id
|
||||
? deleteTimeblock(timeblock)
|
||||
: deleteTimeblockWithoutId(index)
|
||||
: deleteTimeblockByIndex(index)
|
||||
"
|
||||
/>
|
||||
<p
|
||||
@@ -48,11 +48,17 @@
|
||||
<v-icon
|
||||
v-if="!isOffice365Task"
|
||||
v-show="subtask.id"
|
||||
class="circle-border cursor-pointer ml-2"
|
||||
:class="{ completed: timeblock.is_finished }"
|
||||
class="circle-border ml-2"
|
||||
:class="{
|
||||
completed: timeblock.is_finished,
|
||||
'cursor-pointer': !subtask.is_draft,
|
||||
'disabled-check': subtask.is_draft,
|
||||
}"
|
||||
icon="mdi-check"
|
||||
size="medium"
|
||||
@click.stop="toggleTimeblockIsFinished(timeblock)"
|
||||
@click.stop="
|
||||
!subtask.is_draft && toggleTimeblockIsFinished(timeblock, index)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -551,14 +557,29 @@ export default {
|
||||
subtask.is_finished = 0;
|
||||
}
|
||||
|
||||
// Add timeblock in internal format from newTimeblock
|
||||
subtask.timeblocks.push({
|
||||
const newTimeblockData = {
|
||||
id: null,
|
||||
date: this.newTimeblock.date,
|
||||
starts_at_time: this.newTimeblock.starts_at_time,
|
||||
ends_at_time: this.newTimeblock.ends_at_time,
|
||||
is_finished: 0,
|
||||
});
|
||||
};
|
||||
|
||||
// For drafts with employees array, add to the correct employee's timeblocks
|
||||
if (subtask.is_draft && Array.isArray(subtask.employees)) {
|
||||
const employeeIndex = subtask.employees.findIndex(
|
||||
(emp) => emp.id == this.subtask.employee_id,
|
||||
);
|
||||
if (employeeIndex !== -1) {
|
||||
if (!subtask.employees[employeeIndex].timeblocks) {
|
||||
subtask.employees[employeeIndex].timeblocks = [];
|
||||
}
|
||||
subtask.employees[employeeIndex].timeblocks.push(newTimeblockData);
|
||||
}
|
||||
} else {
|
||||
// For published subtasks, add to root timeblocks
|
||||
subtask.timeblocks.push(newTimeblockData);
|
||||
}
|
||||
|
||||
if (subtask.deadline)
|
||||
subtask.deadline = tmUtils.formatDateTimeForBackend(
|
||||
@@ -592,14 +613,29 @@ export default {
|
||||
subtask.is_finished = 0;
|
||||
}
|
||||
|
||||
// Add timeblock in internal format from newTimeblock
|
||||
subtask.timeblocks.push({
|
||||
const newTimeblockData = {
|
||||
id: null,
|
||||
date: this.newTimeblock.date,
|
||||
starts_at_time: this.newTimeblock.starts_at_time,
|
||||
ends_at_time: this.newTimeblock.ends_at_time,
|
||||
is_finished: 0,
|
||||
});
|
||||
};
|
||||
|
||||
// For drafts with employees array, add to the correct employee's timeblocks
|
||||
if (subtask.is_draft && Array.isArray(subtask.employees)) {
|
||||
const employeeIndex = subtask.employees.findIndex(
|
||||
(emp) => emp.id == this.subtask.employee_id,
|
||||
);
|
||||
if (employeeIndex !== -1) {
|
||||
if (!subtask.employees[employeeIndex].timeblocks) {
|
||||
subtask.employees[employeeIndex].timeblocks = [];
|
||||
}
|
||||
subtask.employees[employeeIndex].timeblocks.push(newTimeblockData);
|
||||
}
|
||||
} else {
|
||||
// For published subtasks, add to root timeblocks
|
||||
subtask.timeblocks.push(newTimeblockData);
|
||||
}
|
||||
|
||||
if (subtask.deadline)
|
||||
subtask.deadline = tmUtils.formatDateTimeForBackend(
|
||||
@@ -616,8 +652,12 @@ export default {
|
||||
this._update(subtask);
|
||||
},
|
||||
|
||||
async toggleTimeblockIsFinished(originalTimeblock) {
|
||||
// TODO: Pridat odpracovany cas modal?
|
||||
async toggleTimeblockIsFinished(originalTimeblock, index) {
|
||||
// Only works for published subtasks with saved timeblocks
|
||||
if (!originalTimeblock.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const subtask = JSON.parse(JSON.stringify(this.subtask));
|
||||
const timeblock = JSON.parse(JSON.stringify(originalTimeblock));
|
||||
|
||||
@@ -657,12 +697,25 @@ export default {
|
||||
is_finished: newTimeblock.is_finished,
|
||||
};
|
||||
|
||||
subtask.timeblocks = subtask.timeblocks.map((originalTimeblock) => {
|
||||
if (originalTimeblock.id == convertedTimeblock.id)
|
||||
return convertedTimeblock;
|
||||
|
||||
return originalTimeblock;
|
||||
// Update timeblock in correct location (employee's array for drafts, root for published)
|
||||
if (subtask.is_draft && Array.isArray(subtask.employees)) {
|
||||
const employeeIndex = subtask.employees.findIndex(
|
||||
(emp) => emp.id == this.subtask.employee_id,
|
||||
);
|
||||
if (employeeIndex !== -1) {
|
||||
subtask.employees[employeeIndex].timeblocks = subtask.employees[
|
||||
employeeIndex
|
||||
].timeblocks.map((tb) => {
|
||||
if (tb.id == convertedTimeblock.id) return convertedTimeblock;
|
||||
return tb;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
subtask.timeblocks = subtask.timeblocks.map((tb) => {
|
||||
if (tb.id == convertedTimeblock.id) return convertedTimeblock;
|
||||
return tb;
|
||||
});
|
||||
}
|
||||
|
||||
this.$events.emit('on-subtask-edited', subtask);
|
||||
this.$emit('update-subtask', subtask);
|
||||
@@ -677,9 +730,34 @@ export default {
|
||||
async deleteTimeblock({ id }) {
|
||||
const subtask = JSON.parse(JSON.stringify(this.subtask));
|
||||
|
||||
// Remove timeblock from correct location (employee's array for drafts, root for published)
|
||||
if (subtask.is_draft && Array.isArray(subtask.employees)) {
|
||||
// Find which employee this timeblock belongs to by searching through all employees
|
||||
let found = false;
|
||||
subtask.employees = subtask.employees.map((emp) => {
|
||||
if (!emp.timeblocks) return emp;
|
||||
|
||||
// Check if this employee has the timeblock
|
||||
const hasTimeblock = emp.timeblocks.some((tb) => tb.id == id);
|
||||
if (hasTimeblock) {
|
||||
found = true;
|
||||
return {
|
||||
...emp,
|
||||
timeblocks: emp.timeblocks.filter((tb) => tb.id != id),
|
||||
};
|
||||
}
|
||||
return emp;
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
console.warn('Timeblock not found in any employee', id);
|
||||
}
|
||||
} else {
|
||||
subtask.timeblocks = subtask.timeblocks.filter(
|
||||
(timeblock) => timeblock.id != id,
|
||||
);
|
||||
}
|
||||
|
||||
if (subtask.deadline)
|
||||
subtask.deadline = tmUtils.formatDateTimeForBackend(
|
||||
this._mergeDeadlineTimeDate(),
|
||||
@@ -688,8 +766,34 @@ export default {
|
||||
this._update(subtask);
|
||||
},
|
||||
|
||||
deleteTimeblockWithoutId(_index) {
|
||||
this.$emit('delete-timeblock');
|
||||
async deleteTimeblockByIndex(index) {
|
||||
const subtask = JSON.parse(JSON.stringify(this.subtask));
|
||||
|
||||
// Remove timeblock by index from correct location
|
||||
if (subtask.is_draft && Array.isArray(subtask.employees)) {
|
||||
const employeeIndex = subtask.employees.findIndex(
|
||||
(emp) => emp.id == this.subtask.employee_id,
|
||||
);
|
||||
if (
|
||||
employeeIndex !== -1 &&
|
||||
subtask.employees[employeeIndex].timeblocks
|
||||
) {
|
||||
subtask.employees[employeeIndex].timeblocks.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
subtask.timeblocks.splice(index, 1);
|
||||
}
|
||||
|
||||
if (subtask.deadline)
|
||||
subtask.deadline = tmUtils.formatDateTimeForBackend(
|
||||
this._mergeDeadlineTimeDate(),
|
||||
);
|
||||
|
||||
if (!subtask.id) {
|
||||
this.$emit('update-unfinished-subtask', subtask);
|
||||
} else {
|
||||
this._update(subtask);
|
||||
}
|
||||
},
|
||||
|
||||
async _update(data) {
|
||||
@@ -699,7 +803,42 @@ export default {
|
||||
return timesheet;
|
||||
});
|
||||
|
||||
// Convert timeblocks from internal format to BE format
|
||||
const payload = { ...data, timesheets };
|
||||
|
||||
// For drafts with employees array, properly structure the employees with their timeblocks
|
||||
if (data.is_draft && Array.isArray(data.employees)) {
|
||||
const validEmployees = data.employees
|
||||
.filter((emp) => emp.id)
|
||||
.map((emp) => ({
|
||||
id: emp.id,
|
||||
duration: emp.duration,
|
||||
// Convert employee timeblocks to BE format
|
||||
timeblocks: (emp.timeblocks || [])
|
||||
.filter((tb) => tb.date || tb.start)
|
||||
.map((tb) => {
|
||||
if (tb.start && !tb.date) {
|
||||
return {
|
||||
id: tb.id || null,
|
||||
start: tb.start,
|
||||
end: tb.end,
|
||||
is_finished: tb.is_finished || 0,
|
||||
};
|
||||
}
|
||||
const dateStr = moment(tb.date).format('YYYY-MM-DD');
|
||||
return {
|
||||
id: tb.id || null,
|
||||
start: `${dateStr} ${tb.starts_at_time}:00`,
|
||||
end: `${dateStr} ${tb.ends_at_time}:00`,
|
||||
is_finished: tb.is_finished || 0,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
payload.employees = validEmployees;
|
||||
// For drafts, don't send root-level timeblocks
|
||||
delete payload.timeblocks;
|
||||
} else {
|
||||
// For published subtasks, convert and send root-level timeblocks
|
||||
const timeblocks = (data.timeblocks || [])
|
||||
.filter((timeblock) => {
|
||||
// Filter timeblocks that have either old format (date) or new format (start)
|
||||
@@ -726,9 +865,18 @@ export default {
|
||||
};
|
||||
});
|
||||
|
||||
payload.timeblocks = timeblocks;
|
||||
}
|
||||
|
||||
// Remove UI-only enriched fields
|
||||
delete payload._created_by;
|
||||
delete payload._employee;
|
||||
delete payload._master_task;
|
||||
delete payload._deadline_time;
|
||||
|
||||
const { subtask } = await this.$axios.put_auth(
|
||||
`v1/subtasks/${data.id}`,
|
||||
{ ...data, timesheets, timeblocks },
|
||||
payload,
|
||||
);
|
||||
|
||||
this.closeModal();
|
||||
@@ -792,6 +940,10 @@ export default {
|
||||
.completed
|
||||
background: #06dece
|
||||
|
||||
.disabled-check
|
||||
opacity: 0.5
|
||||
cursor: not-allowed
|
||||
|
||||
.existing-timeblock-timeline
|
||||
margin-top: 8px
|
||||
padding-left: 16px
|
||||
|
||||
@@ -87,8 +87,8 @@ export default {
|
||||
...mapGetters('tm', ['enrichedEmployees']),
|
||||
|
||||
normalizedEmployees() {
|
||||
// Draft with multiple employees
|
||||
if (this.subtask.is_draft && Array.isArray(this.subtask.employees)) {
|
||||
// Drafts always have employees array
|
||||
if (this.subtask.is_draft) {
|
||||
return this.subtask.employees
|
||||
.filter((emp) => emp.id) // Filter out empty employee slots
|
||||
.map((emp) => ({
|
||||
@@ -99,7 +99,7 @@ export default {
|
||||
}));
|
||||
}
|
||||
|
||||
// Single employee (published subtask)
|
||||
// Published subtask - single employee format
|
||||
return [
|
||||
{
|
||||
id: this.subtask.employee_id,
|
||||
@@ -111,6 +111,11 @@ export default {
|
||||
},
|
||||
|
||||
hasMultipleEmployees() {
|
||||
// For drafts, always use multi-employee styling (even with 1 employee)
|
||||
if (this.subtask.is_draft) {
|
||||
return true;
|
||||
}
|
||||
// For published subtasks, check actual count
|
||||
return this.normalizedEmployees.length > 1;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -86,6 +86,12 @@ export default {
|
||||
|
||||
methods: {
|
||||
async showModal({ subtask }) {
|
||||
// Prevent using this modal for drafts - they should be edited instead
|
||||
if (subtask.is_draft) {
|
||||
this.$toast.info('Pre koncepty použite režim úprav');
|
||||
return;
|
||||
}
|
||||
|
||||
this.subtask = subtask;
|
||||
this.visible = true;
|
||||
this.form.hours = null;
|
||||
|
||||
Reference in new issue
Block a user