diff --git a/app/src/plugins/tm@modal/task/_components/employees/a-employee-timeblock-timeline.vue b/app/src/plugins/tm@modal/task/_components/employees/a-employee-timeblock-timeline.vue
index 43aa3fb..423c6d7 100644
--- a/app/src/plugins/tm@modal/task/_components/employees/a-employee-timeblock-timeline.vue
+++ b/app/src/plugins/tm@modal/task/_components/employees/a-employee-timeblock-timeline.vue
@@ -47,6 +47,9 @@
}"
>
+
+
+
(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
diff --git a/app/src/plugins/tm@modal/task/_components/employees/a-employee-timeblock.vue b/app/src/plugins/tm@modal/task/_components/employees/a-employee-timeblock.vue
index 182b5d9..7022124 100644
--- a/app/src/plugins/tm@modal/task/_components/employees/a-employee-timeblock.vue
+++ b/app/src/plugins/tm@modal/task/_components/employees/a-employee-timeblock.vue
@@ -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
diff --git a/app/src/plugins/tm@modal/task/z-task-modal.vue b/app/src/plugins/tm@modal/task/z-task-modal.vue
index f22498e..b3afea8 100644
--- a/app/src/plugins/tm@modal/task/z-task-modal.vue
+++ b/app/src/plugins/tm@modal/task/z-task-modal.vue
@@ -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