wip(task-timeline): stage changes

This commit is contained in:
Matej Glasnák committed 2025-12-08 01:56:31 +01:00
1 parent 69ea8be701
commit 00c4bf4d65
7 files changed
+878 -693

No files matched your search

+79 -165
View File
@@ -54,7 +54,7 @@
<div class="splitted-inputs">
<!-- TODO: Bialko validacie -->
<div class="w-100 modal-input">
<div class="left">
<Z-datepicker
ref="datepicker-01"
v-model="subtask.deadline"
@@ -99,20 +99,25 @@
updateEmployeeTimeblocks($event, subtask.employee_id)
"
@clear-employee="handleRemoveEmployee"
@add-timeblock="addTimeblock(subtask.employee_id)"
@add-timeblock="
(afterIndex) => addTimeblock(subtask.employee_id, afterIndex)
"
/>
<!-- Create Mode: Multiple Employees -->
<template v-else>
<A-employee
v-for="(employee, index) in getValidEmployees()"
v-for="(employee, index) in subtask.employees"
:key="`employee-${employee.id || index}`"
:ref="(el) => setEmployeeRef(el, index)"
v-model:employee-data="subtask.employees[index]"
:timeblocks="getEmployeeTimeblocks(employee.id)"
:custom-filter="customFilter"
@update:timeblocks="updateEmployeeTimeblocks($event, employee.id)"
@clear-employee="() => removeEmployeeAtIndex(index)"
@add-timeblock="addTimeblock(employee.id)"
@add-timeblock="
(afterIndex) => addTimeblock(employee.id, afterIndex)
"
/>
</template>
@@ -296,12 +301,12 @@ export default {
isSaving: false,
errorUtils,
v$: useVuelidate(),
employeeValidations: [],
showDatePicker: false,
selectedNewDate: [],
timeblocks: [],
wasFormValidationTriggered: false,
modalName: 'z-task-modal',
employeeRefs: [],
};
},
@@ -490,6 +495,13 @@ export default {
this.editMode = !this.editMode;
},
// Set refs for employee components in create mode
setEmployeeRef(el, index) {
if (el) {
this.employeeRefs[index] = el;
}
},
openDatepicker(ref) {
this.$refs[ref].openMenu();
},
@@ -530,17 +542,6 @@ export default {
this.timeblocks = [...otherTimeblocks, ...timeblocksWithEmployeeId];
},
// Clear employee validation
clearEmployeeValidation(index) {
if (index !== undefined && this.employeeValidations[index]) {
this.employeeValidations[index].employee = false;
this.employeeValidations[index].duration = false;
} else if (index === undefined && this.employeeValidations[0]) {
this.employeeValidations[0].employee = false;
this.employeeValidations[0].duration = false;
}
},
updateTimeblock(val, index, employeeId) {
const employeeTimeblocks = this.getEmployeeTimeblocks(employeeId);
const timeblock = employeeTimeblocks[index];
@@ -555,7 +556,7 @@ export default {
}
},
addTimeblock(employeeId) {
addTimeblock(employeeId, afterIndex = -1) {
const newTimeblock = {
date: new Date(), // Preset to current day
starts_at_time: null,
@@ -563,7 +564,21 @@ export default {
employee_id: employeeId,
};
this.timeblocks.push(newTimeblock);
// Get timeblocks for this employee
const employeeTimeblocks = this.timeblocks.filter(
(tb) => tb.employee_id === employeeId,
);
if (afterIndex === -1 || afterIndex >= employeeTimeblocks.length - 1) {
// Add at the end
this.timeblocks.push(newTimeblock);
} else {
// Find the position in the full timeblocks array
const timeblockToInsertAfter = employeeTimeblocks[afterIndex];
const insertPosition =
this.timeblocks.indexOf(timeblockToInsertAfter) + 1;
this.timeblocks.splice(insertPosition, 0, newTimeblock);
}
},
removeTimeblock(index, employeeId) {
@@ -628,6 +643,7 @@ export default {
this.visible = true;
this.timeblocks = [];
this.employeeRefs = [];
this.$nextTick(() => {
this.v$.$reset();
@@ -922,55 +938,42 @@ export default {
}
},
_validateEmployees() {
return new Promise((resolve) => {
this.employeeValidations = [];
async _validateEmployees() {
if (this.subtask.id) {
// For existing subtasks, at least one employee is required
// Component-level validation will handle the conditional validation
const hasEmployee = !!(
this.subtask.employee_id && this.subtask.duration
);
if (!hasEmployee) {
this.$toast.error('Je potrebné vybrať zamestnanca a trvanie');
return false;
}
} else {
// For new subtasks, require at least one complete employee (id + duration)
const validEmployees = this.subtask.employees.filter(
(employee) => employee.id && employee.duration,
);
if (this.subtask.id) {
// For existing subtasks, require both employee and duration
if (!this.subtask.employee_id || !this.subtask.duration) {
this.employeeValidations[0] = {
employee: !this.subtask.employee_id,
duration: !this.subtask.duration,
};
}
} else {
// For new subtasks, require at least one employee with duration
let hasValidEmployee = false;
this.subtask.employees.forEach((employee, index) => {
// Skip validation if both id and duration are empty
if (!employee.id && !employee.duration) {
// If this is the first employee and no valid employee exists yet, show validation
if (index === 0 && !hasValidEmployee) {
this.employeeValidations[index] = {
employee: true,
duration: true,
};
}
return;
}
// Validate if either id or duration is filled
if (employee.id || employee.duration) {
this.employeeValidations[index] = {
employee: !employee.id,
duration: !employee.duration,
};
// If both id and duration are present, mark as valid
if (employee.id && employee.duration) {
hasValidEmployee = true;
}
}
});
if (validEmployees.length === 0) {
this.$toast.error(
'Je potrebné pridať aspoň jedného zamestnanca s trvaním',
);
return false;
}
const isError = this.employeeValidations.some(
(employee) => employee.duration || employee.employee,
);
resolve(!isError);
});
// Validate all employee component refs (includes employee select and timeblocks validation)
for (const employeeRef of this.employeeRefs) {
if (employeeRef && typeof employeeRef.validate === 'function') {
const isValid = await employeeRef.validate();
if (!isValid) {
return false;
}
}
}
}
return true;
},
async _validateTimeblocks() {
@@ -1046,6 +1049,18 @@ export default {
.border-bottom
border-bottom: 1px solid rgba(0,0,0,.1)
.splitted-inputs
display: flex
justify-content: space-between
gap: 16px
.left
flex: 1
.right ::v-deep
.v-field__field
height: 56px !important
.done-btn
background: #7f39fb !important
border-radius: 4px !important
@@ -1053,111 +1068,10 @@ export default {
height: 50px
width: 203px
.remove-employee
position: relative
width: 100%
svg
position: absolute
right: 5px
top: 29px
z-index: 5
opacity: .5
&.hide-arrow ::v-deep
.mdi-menu-down,
.mdi-menu-up
display: none
.employee-wrapper
position: relative
width: 100%
svg
position: absolute
right: 4px
top: 28px
z-index: 5
opacity: .5
&.hide-arrow ::v-deep
.mdi-menu-down,
.mdi-menu-up
display: none
@media screen and (max-width: 522px)
.files
margin-top: 104px !important
.employees-timeblocks
display: flex
flex-direction: column
width: 100%
margin-top: 20px
.employee-timeblocks-section
width: 100%
margin-bottom: 30px
border: 1px solid #e0e0e0
border-radius: 8px
padding: 20px
background: #fafafa
.employee-header
margin-bottom: 15px
padding-bottom: 10px
border-bottom: 2px solid #7f39fb
h3
margin: 0
color: #7f39fb
font-size: 18px
font-weight: 600
.end-button
width: 100%
display: flex
justify-content: flex-end
align-items: center
margin-top: 20px
padding-top: 20px
border-top: 1px solid #e0e0e0
button
background: #7f39fb !important
border-radius: 4px !important
color: #fff !important
height: 50px
width: 203px
border: none
cursor: pointer
transition: all 0.2s
font-size: 14px
font-weight: 500
&:hover
background: #6b2dd9 !important
&:active
background: #5a24b8 !important
.timeblock
position: relative
border-top: 1px solid #e0e0e0
padding: 20px 0 10px 0
.splitted-time-inputs
display: flex !important
flex-direction: row !important
gap: 15px
margin-bottom: -11px
.datepicker-wrapper
width: 70%
.datepicker-input
width: 100% !important
pointer-events: none
.quill-editor-wrapper
:deep(.ql-container)
min-height: 200px