feat(z-task-modal): improve employee selection logic and add weekend overlay in timeblock components

This commit is contained in:
Matej Glasnák committed 2026-01-08 01:13:06 +01:00
1 parent 1b666a63a3
commit 198f50dfc1
3 files changed
+81 -20

No files matched your search

@@ -47,6 +47,9 @@
}"
></div>
<!-- Weekend background overlay -->
<div v-if="isWeekend" class="weekend-overlay"></div>
<!-- Existing scheduled blocks -->
<div
v-for="(task, index) in scheduledBlocks"
@@ -257,6 +260,15 @@ const dragState = ref({
});
const resizedCurrentBlock = ref<{ start: Date; end: Date } | null>(null);
// Check if the current date is a weekend (Saturday or Sunday)
const isWeekend = computed(() => {
const date = props.currentTimeblockRange.start;
if (!date) return false;
const dayOfWeek = moment(date).day(); // 0 = Sunday, 6 = Saturday
return dayOfWeek === 0 || dayOfWeek === 6;
});
// Convert other timeblocks to renderable format
const otherTimeblocksRendered = computed(() => {
return props.otherTimeblocks
@@ -728,6 +740,17 @@ defineExpose({
z-index: 1
pointer-events: none
.weekend-overlay
position: absolute
top: 0
left: 0
width: 100%
height: 100%
background: rgba(255, 124, 124, 0.3) !important
z-index: 1
pointer-events: none
border-radius: 4px
.scheduled-block
position: absolute
top: 5px
@@ -281,15 +281,39 @@ const scheduledBlocks = computed(() => {
);
});
// Recalculate collision using filtered scheduled blocks (not raw timeline tasks)
// This prevents false collisions with timeblocks being edited
const collision = computed(() => {
return detectCollision(props.modelValue, scheduledBlocks.value);
// Convert other unsaved timeblocks to task format for collision detection
const otherTimeblocksAsTasks = computed(() => {
return otherTimeblocksForSameDate.value
.map((tb) => {
const { start, end } = timeblockToDateRange(tb);
if (!start || !end) return null;
return {
_real_start: start,
_real_end: end,
_timeblock_id: tb.id || null,
};
})
.filter((task) => task !== null);
});
// Recalculate collision segments using filtered scheduled blocks
// Combine scheduled blocks with other unsaved timeblocks for collision detection
const allBlocksForCollisionCheck = computed(() => {
return [...scheduledBlocks.value, ...otherTimeblocksAsTasks.value];
});
// Recalculate collision using filtered scheduled blocks AND other unsaved timeblocks
// This prevents false collisions with timeblocks being edited, but catches collisions with other unsaved timeblocks
const collision = computed(() => {
return detectCollision(props.modelValue, allBlocksForCollisionCheck.value);
});
// Recalculate collision segments using filtered scheduled blocks AND other unsaved timeblocks
const collisionSegments = computed(() => {
return getCollisionSegmentsUtil(props.modelValue, scheduledBlocks.value);
return getCollisionSegmentsUtil(
props.modelValue,
allBlocksForCollisionCheck.value,
);
});
// Get current timeblock range
+28 -14
View File
@@ -531,19 +531,20 @@ export default {
const employeeId = itemObject.value;
if (!this.subtask.id) {
// New subtask with multiple employees support or edit of draft with multiple employees
return (
shouldShowItem &&
!this.timeblocks.some((tb) => tb.employee_id === employeeId)
// New subtask with multiple employees support
// Hide employees that are already selected in any slot
// The v-autocomplete will still display the currently selected employee
const isAlreadySelected = this.subtask.employees.some(
(emp) => emp.id && Number(emp.id) === employeeId,
);
return shouldShowItem && !isAlreadySelected;
} else if (this.subtask.is_draft) {
// Edit draft subtask with multiple employees support
return (
shouldShowItem &&
!this.subtask.employees.some(
(employee) => Number(employee.id) === employeeId,
)
// Hide employees that are already selected in any slot
const isAlreadySelected = this.subtask.employees.some(
(emp) => emp.id && Number(emp.id) === employeeId,
);
return shouldShowItem && !isAlreadySelected;
} else {
// Saved standard subtask, allow "re-select" for better UX
return shouldShowItem;
@@ -1046,8 +1047,9 @@ export default {
// For drafts with multiple employees, include employees array
if (this.subtask.is_draft && this.isDraftWithMultipleEmployees) {
const validEmployees = this.subtask.employees.filter(
(employee) => employee.id && employee.duration,
const validEmployees = this._filterValidEmployees(
this.subtask.employees,
concept,
);
// Assign timeblocks to each employee
@@ -1090,9 +1092,9 @@ export default {
this.$store.commit('tm/update_subtask', subtask);
this.$events.emit('on-subtask-edited', this.subtask);
} else {
// Filter out employees that don't have both id and duration
const validEmployees = this.subtask.employees.filter(
(employee) => employee.id && employee.duration,
const validEmployees = this._filterValidEmployees(
this.subtask.employees,
concept,
);
// Assign timeblocks to each employee based on employee_id
@@ -1198,6 +1200,18 @@ export default {
}
},
_filterValidEmployees(employees, isConcept) {
// Filter employees based on whether it's a concept/draft or published task
// For concepts: only require id (duration can be null/0)
// For published tasks: require both id and duration
return employees.filter((employee) => {
if (isConcept) {
return employee.id; // Only require ID for concepts/drafts
}
return employee.id && employee.duration; // Require both for published tasks
});
},
async _validateEmployees() {
if (this.subtask.id && !this.subtask.is_draft) {
// For existing subtasks, at least one employee is required