feat(z-task-modal): enhance timeblock conversion logic and improve draft handling for employee-specific timeblocks

This commit is contained in:
Matej Glasnák committed 2026-01-08 02:28:03 +01:00
1 parent 81aaa293dd
commit 7a5f354912
4 files changed
+91 -91

No files matched your search

@@ -652,7 +652,7 @@ export default {
this._update(subtask); this._update(subtask);
}, },
async toggleTimeblockIsFinished(originalTimeblock, index) { async toggleTimeblockIsFinished(originalTimeblock, _index) {
// Only works for published subtasks with saved timeblocks // Only works for published subtasks with saved timeblocks
if (!originalTimeblock.id) { if (!originalTimeblock.id) {
return; return;
@@ -1,5 +1,8 @@
<template> <template>
<div class="timeblock"> <div
class="timeblock"
:class="{ 'timeblock--finished': modelValue.is_finished }"
>
<a-employee-timeblock-header <a-employee-timeblock-header
:model-value="modelValue" :model-value="modelValue"
:min-date="minDate" :min-date="minDate"
@@ -25,8 +28,12 @@
/> />
<!-- Timeline Visualization --> <!-- Timeline Visualization -->
<a-employee-timeblock-timeline <div
v-if="modelValue.date && employee" v-if="modelValue.date && employee"
:class="{ 'timeline-wrapper--finished': modelValue.is_finished }"
class="timeline-wrapper"
>
<a-employee-timeblock-timeline
ref="timelineRef" ref="timelineRef"
:is-loading="isLoading" :is-loading="isLoading"
:employee="employee" :employee="employee"
@@ -42,6 +49,7 @@
@timeblock-updated="handleTimeblockUpdated" @timeblock-updated="handleTimeblockUpdated"
/> />
</div> </div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -487,4 +495,9 @@ watch(
border-radius: 8px border-radius: 8px
margin-bottom: 8px margin-bottom: 8px
overflow: hidden overflow: hidden
.timeblock--finished
opacity: 0.4
pointer-events: none
cursor: not-allowed
</style> </style>
@@ -11,6 +11,7 @@ export interface TimeblockData {
starts_at_time: string | null; starts_at_time: string | null;
ends_at_time: string | null; ends_at_time: string | null;
employee_id?: number | null; employee_id?: number | null;
is_finished?: number;
} }
export interface EmployeeFullData { export interface EmployeeFullData {
+74 -88
View File
@@ -480,35 +480,32 @@ export default {
data._deadline_time = splittedDeadline[1]; data._deadline_time = splittedDeadline[1];
// Convert BE format timeblocks to internal format // Convert BE format timeblocks to internal format
const convertedTimeblocks = []; if (data.is_draft && Array.isArray(data.employees)) {
if (data.timeblocks && data.timeblocks.length > 0) { // Handle draft subtasks with employees array
data.timeblocks.forEach((timeblock) => { const allDraftTimeblocks = [];
// If already in internal format, keep as is
if (timeblock.date) {
convertedTimeblocks.push({
...timeblock,
employee_id: data.employee_id, // Add employee_id
});
return;
}
// Convert from BE format to internal format data.employees = data.employees.map((emp) => {
if (timeblock.start && timeblock.end) { const convertedTimeblocks = (emp.timeblocks || [])
const startMoment = moment(timeblock.start, 'YYYY-MM-DD HH:mm:ss'); .map((tb) => this._convertTimeblockToInternal(tb, emp.id))
const endMoment = moment(timeblock.end, 'YYYY-MM-DD HH:mm:ss'); .filter(Boolean);
convertedTimeblocks.push({
id: timeblock.id || null, // Add to flat array for edit mode
date: startMoment.toDate(), allDraftTimeblocks.push(...convertedTimeblocks);
starts_at_time: startMoment.format('HH:mm'),
ends_at_time: endMoment.format('HH:mm'), return {
is_finished: timeblock.is_finished || 0, ...emp,
employee_id: data.employee_id, // Add employee_id timeblocks: convertedTimeblocks,
};
}); });
// Update flat timeblocks array for edit mode
this.timeblocks = allDraftTimeblocks;
} else {
// Handle published subtasks with root-level timeblocks
this.timeblocks = (data.timeblocks || [])
.map((tb) => this._convertTimeblockToInternal(tb, data.employee_id))
.filter(Boolean);
} }
});
}
// Store in component's timeblocks array
this.timeblocks = convertedTimeblocks;
this.subtask = this._enrichTask(data); this.subtask = this._enrichTask(data);
@@ -522,6 +519,38 @@ export default {
}, },
methods: { methods: {
/**
* Convert a timeblock from backend format to internal format
* @param {Object} timeblock - Timeblock in BE format (start/end) or internal format (date/times)
* @param {Number} employeeId - Employee ID to associate with the timeblock
* @returns {Object} Timeblock in internal format
*/
_convertTimeblockToInternal(timeblock, employeeId) {
// If already in internal format, return as is
if (timeblock.date) {
return {
...timeblock,
employee_id: employeeId,
};
}
// Convert from BE format to internal format
if (timeblock.start && timeblock.end) {
const startMoment = moment(timeblock.start, 'YYYY-MM-DD HH:mm:ss');
const endMoment = moment(timeblock.end, 'YYYY-MM-DD HH:mm:ss');
return {
id: timeblock.id || null,
date: startMoment.toDate(),
starts_at_time: startMoment.format('HH:mm'),
ends_at_time: endMoment.format('HH:mm'),
is_finished: timeblock.is_finished || 0,
employee_id: employeeId,
};
}
return null;
},
customFilterWithEmployeeFilter(item, input, itemObject) { customFilterWithEmployeeFilter(item, input, itemObject) {
const shouldShowItem = this.customFilter(item, input); const shouldShowItem = this.customFilter(item, input);
@@ -749,84 +778,41 @@ export default {
); );
// Convert BE format timeblocks to internal format // Convert BE format timeblocks to internal format
const convertedTimeblocks = [];
if (data.timeblocks && data.timeblocks.length > 0) {
data.timeblocks.forEach((timeblock) => {
// If already in internal format, keep as is
if (timeblock.date) {
convertedTimeblocks.push({
...timeblock,
employee_id: data.employee_id, // Add employee_id for edit mode
});
return;
}
// Convert from BE format to internal format
if (timeblock.start && timeblock.end) {
const startMoment = moment(
timeblock.start,
'YYYY-MM-DD HH:mm:ss',
);
const endMoment = moment(timeblock.end, 'YYYY-MM-DD HH:mm:ss');
convertedTimeblocks.push({
id: timeblock.id || null,
date: startMoment.toDate(),
starts_at_time: startMoment.format('HH:mm'),
ends_at_time: endMoment.format('HH:mm'),
is_finished: timeblock.is_finished || 0,
employee_id: data.employee_id, // Add employee_id for edit mode
});
}
});
}
// Store timeblocks in the component's timeblocks array
this.timeblocks = convertedTimeblocks;
// If draft with employees data, parse timeblocks for each employee
if ( if (
data.is_draft && data.is_draft &&
data.employees && data.employees &&
Array.isArray(data.employees) Array.isArray(data.employees)
) { ) {
// Handle draft subtasks with employees array
const draftTimeblocks = []; const draftTimeblocks = [];
data.employees.forEach((employee) => { data.employees = data.employees.map((employee) => {
if (employee.timeblocks && Array.isArray(employee.timeblocks)) { const convertedTimeblocks = (employee.timeblocks || [])
employee.timeblocks.forEach((timeblock) => { .map((tb) => this._convertTimeblockToInternal(tb, employee.id))
// Convert BE format to internal format .filter(Boolean);
if (timeblock.start && timeblock.end) {
const startMoment = moment( draftTimeblocks.push(...convertedTimeblocks);
timeblock.start,
'YYYY-MM-DD HH:mm:ss', return {
); ...employee,
const endMoment = moment( timeblocks: convertedTimeblocks,
timeblock.end, };
'YYYY-MM-DD HH:mm:ss',
);
draftTimeblocks.push({
id: timeblock.id || null,
date: startMoment.toDate(),
starts_at_time: startMoment.format('HH:mm'),
ends_at_time: endMoment.format('HH:mm'),
is_finished: timeblock.is_finished || 0,
employee_id: employee.id, // Associate with employee
});
}
});
}
}); });
// Use draft timeblocks if they exist
if (draftTimeblocks.length > 0) {
this.timeblocks = draftTimeblocks; this.timeblocks = draftTimeblocks;
}
// Ensure employees array has proper structure for editing // Ensure employees array has proper structure for editing
// Check if last employee is not empty, then add empty slot
const lastEmployee = data.employees[data.employees.length - 1]; const lastEmployee = data.employees[data.employees.length - 1];
if (lastEmployee && (lastEmployee.id || lastEmployee.duration)) { if (lastEmployee && (lastEmployee.id || lastEmployee.duration)) {
data.employees.push({}); data.employees.push({});
} }
} else {
// Handle published subtasks with root-level timeblocks
this.timeblocks = (data.timeblocks || [])
.map((tb) =>
this._convertTimeblockToInternal(tb, data.employee_id),
)
.filter(Boolean);
} }
// Ensure at least one timeblock exists for edit mode (non-draft single employee) // Ensure at least one timeblock exists for edit mode (non-draft single employee)