refactor(timeline): updated modal for adding timeblock to correctly display other (draft) timeblocks when editing subtask draft

This commit is contained in:
Matej Glasnák committed 2026-01-26 13:12:26 +01:00
1 parent 9f2db65481
commit c5672c94f2
5 files changed
+110 -50

No files matched your search

@@ -25,7 +25,7 @@ export function useEmployeeTimeline(
currentTimeblock: Ref<Timeblock | null>, currentTimeblock: Ref<Timeblock | null>,
otherLocalTimeblocks?: Ref<Timeblock[]>, otherLocalTimeblocks?: Ref<Timeblock[]>,
editingBlockerId?: Ref<number | null>, editingBlockerId?: Ref<number | null>,
showDraftTasks?: Ref<boolean>, draftTimeblocks?: Ref<Timeblock[]>,
) { ) {
// Fetch employee's tasks for the selected date // Fetch employee's tasks for the selected date
const { const {
@@ -64,61 +64,60 @@ export function useEmployeeTimeline(
}), }),
}); });
const { // const {
data: conceptTasksData, // data: conceptTasksData,
isLoading: conceptTasksLoading, // isLoading: conceptTasksLoading,
error: conceptTasksError, // error: conceptTasksError,
refetch: conceptTasksRefetch, // refetch: conceptTasksRefetch,
} = useQuery({ // } = useQuery({
queryKey: computed(() => [ // queryKey: computed(() => [
'concept-employee-timeline', // 'concept-employee-timeline',
unref(employee)?.id, // unref(employee)?.id,
unref(date) ? moment(unref(date)).format('YYYY-MM-DD') : null, // unref(date) ? moment(unref(date)).format('YYYY-MM-DD') : null,
]), // ]),
queryFn: async () => { // queryFn: async () => {
const emp = unref(employee); // const emp = unref(employee);
const d = unref(date); // const d = unref(date);
if (!emp?.id || !d) { // if (!emp?.id || !d) {
return null; // return null;
} // }
const dateStr = moment(d).format('YYYY-MM-DD'); // const dateStr = moment(d).format('YYYY-MM-DD');
const queryParams = new URLSearchParams({ // const queryParams = new URLSearchParams({
start: dateStr, // start: dateStr,
end: dateStr, // end: dateStr,
employee_id: emp.id.toString(), // employee_id: emp.id.toString(),
type: 'draft', // type: 'draft',
}).toString(); // }).toString();
const response = await axios.get_auth(`v1/subtasks?${queryParams}`); // const response = await axios.get_auth(`v1/subtasks?${queryParams}`);
return response; // return response;
}, // },
enabled: computed(() => { // enabled: computed(() => {
const emp = unref(employee); // const emp = unref(employee);
const d = unref(date); // const d = unref(date);
return !!(emp?.id && d); // return !!(emp?.id && d);
}), // }),
}); // });
// Generate timeline tasks from fetched data // Generate timeline tasks from fetched data
const timelineTasks = computed(() => { const timelineTasks = computed(() => {
const data = unref(tasksData); const data = unref(tasksData);
const conceptData = unref(conceptTasksData); // const conceptData = unref(conceptTasksData);
const emp = unref(employee); const emp = unref(employee);
const d = unref(date); const d = unref(date);
const excludeBlockerId = editingBlockerId ? unref(editingBlockerId) : null; const excludeBlockerId = editingBlockerId ? unref(editingBlockerId) : null;
const includeDrafts = showDraftTasks ? unref(showDraftTasks) : false; const draftTbs = draftTimeblocks ? unref(draftTimeblocks) : [];
if (!data || !conceptData || !emp || !d) { if (!data || !emp || !d) {
return []; return [];
} }
const subtasks = [ const subtasks = [
...(data.subtasks || []), ...(data.subtasks || []),
...(data.blockers || []), ...(data.blockers || []),
// Only include concept tasks if showDraftTasks is true // ...(conceptData.subtasks || []),
...(includeDrafts ? (conceptData.subtasks || []) : []),
]; ];
const selectedDateStr = moment(d).format('YYYY-MM-DD'); const selectedDateStr = moment(d).format('YYYY-MM-DD');
@@ -221,6 +220,39 @@ export function useEmployeeTimeline(
} }
}); });
// Process draft timeblocks from other employees in the same draft subtask
draftTbs.forEach((timeblock: any) => {
// Skip if timeblock doesn't have required fields
if (!timeblock.start || !timeblock.end) {
return;
}
const timeblockDateStr = moment(timeblock.start).format('YYYY-MM-DD');
// Only include timeblocks for the selected date
if (timeblockDateStr === selectedDateStr) {
const task = {
id: null, // Draft timeblock doesn't have a subtask ID
start: new Date(timeblock.start),
end: new Date(timeblock.end),
master_task: null,
date: new Date(timeblock.start),
project_code: null,
subtask: null,
is_draft: true,
blocker: undefined,
type: 'draft_timeblock',
_timeblock_id: timeblock.id || null,
_timeblock_is_finished: timeblock.is_finished || false,
_real_start: new Date(timeblock.start),
_real_end: new Date(timeblock.end),
_employee_id: timeblock.employee_id,
};
tasks.push(task);
}
});
return tasks; return tasks;
} catch (error) { } catch (error) {
console.error('Error generating timeline tasks:', error); console.error('Error generating timeline tasks:', error);
@@ -328,9 +360,9 @@ export function useEmployeeTimeline(
error, error,
refetch, refetch,
conceptTasksLoading, // conceptTasksLoading,
conceptTasksError, // conceptTasksError,
conceptTasksRefetch, // conceptTasksRefetch,
// Timeline data // Timeline data
timelineTasks, timelineTasks,
@@ -117,7 +117,7 @@
:employee="employee" :employee="employee"
:deletable="false" :deletable="false"
:index="0" :index="0"
:show-draft-tasks="!!subtask.is_draft" :draft-timeblocks="draftTimeblocks"
/> />
</v-card-text> </v-card-text>
@@ -215,6 +215,11 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
isDraft: {
type: Boolean,
default: false,
},
}, },
emits: ['update-subtask'], emits: ['update-subtask'],
@@ -261,6 +266,26 @@ export default {
); );
}, },
// Get draft timeblocks for the current employee in backend format for timeline
draftTimeblocks() {
if (!this.isDraft) {
return [];
}
// Convert timeblocks from internal format to backend format for timeline
return this.timeblocks
.filter((tb) => tb.date && tb.starts_at_time && tb.ends_at_time)
.map((tb) => {
const dateStr = moment(tb.date).format('YYYY-MM-DD');
return {
...tb,
start: `${dateStr} ${tb.starts_at_time}:00`,
end: `${dateStr} ${tb.ends_at_time}:00`,
employee_id: this.subtask.employee_id,
};
});
},
// Hour width based on screen size // Hour width based on screen size
hourWidth() { hourWidth() {
return window.innerWidth <= 768 return window.innerWidth <= 768
@@ -77,7 +77,7 @@ interface Props {
currentIndex?: number; currentIndex?: number;
deletable?: boolean; deletable?: boolean;
index?: number; index?: number;
showDraftTasks?: boolean; // Whether to show draft/concept tasks on timeline draftTimeblocks?: TimeblockData[]; // Draft timeblocks from all employees in draft subtask
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@@ -88,7 +88,7 @@ const props = withDefaults(defineProps<Props>(), {
currentIndex: 0, currentIndex: 0,
deletable: false, deletable: false,
index: 0, index: 0,
showDraftTasks: false, draftTimeblocks: () => [],
}); });
const emit = defineEmits<{ const emit = defineEmits<{
@@ -240,7 +240,7 @@ const otherTimeblocksForSameDate = computed(() => {
// Use timeline composable // Use timeline composable
const editingBlockerIdRef = computed(() => props.editingBlockerId); const editingBlockerIdRef = computed(() => props.editingBlockerId);
const showDraftTasksRef = computed(() => props.showDraftTasks); const draftTimeblocksRef = computed(() => props.draftTimeblocks);
const { const {
isLoading, isLoading,
@@ -249,14 +249,13 @@ const {
freeTime: _freeTime, freeTime: _freeTime,
timeblockDuration, timeblockDuration,
refetch, refetch,
conceptTasksRefetch,
} = useEmployeeTimeline( } = useEmployeeTimeline(
employeeRef, employeeRef,
dateRef, dateRef,
timeblockRef, timeblockRef,
otherTimeblocksForSameDate, otherTimeblocksForSameDate,
editingBlockerIdRef, editingBlockerIdRef,
showDraftTasksRef, draftTimeblocksRef,
); );
// Force refetch when date changes // Force refetch when date changes
@@ -265,14 +264,14 @@ watch(
() => { () => {
if (props.modelValue.date && props.employee?.id) { if (props.modelValue.date && props.employee?.id) {
refetch(); refetch();
conceptTasksRefetch(); // conceptTasksRefetch();
} }
}, },
); );
onMounted(() => { onMounted(() => {
refetch(); refetch();
conceptTasksRefetch(); // conceptTasksRefetch();
}); });
// All tasks from timelineTasks are already filtered for the selected date // All tasks from timelineTasks are already filtered for the selected date
@@ -19,6 +19,7 @@
:all-employee-timeblocks="timeblocks" :all-employee-timeblocks="timeblocks"
:all-timeblocks="allTimeblocksForCurrentEmployee" :all-timeblocks="allTimeblocksForCurrentEmployee"
:deleted-timeblock-ids="deletedTimeblockIds" :deleted-timeblock-ids="deletedTimeblockIds"
:draft-timeblocks="draftTimeblocks"
:current-index="index" :current-index="index"
:deletable="timeblocks.length > 1" :deletable="timeblocks.length > 1"
:index="index" :index="index"
@@ -46,6 +47,7 @@ interface Props {
timeblocks?: TimeblockData[]; timeblocks?: TimeblockData[];
allTimeblocks?: TimeblockData[]; // All timeblocks from all employees allTimeblocks?: TimeblockData[]; // All timeblocks from all employees
deletedTimeblockIds?: number[]; // IDs of saved timeblocks that have been deleted deletedTimeblockIds?: number[]; // IDs of saved timeblocks that have been deleted
draftTimeblocks?: TimeblockData[]; // Draft timeblocks from all employees in draft subtask
customFilter?: CustomFilterFunction; customFilter?: CustomFilterFunction;
hasBackground?: boolean; hasBackground?: boolean;
required?: boolean; required?: boolean;
@@ -55,6 +57,7 @@ const props = withDefaults(defineProps<Props>(), {
timeblocks: () => [], timeblocks: () => [],
allTimeblocks: () => [], allTimeblocks: () => [],
deletedTimeblockIds: () => [], deletedTimeblockIds: () => [],
draftTimeblocks: () => [],
customFilter: undefined, customFilter: undefined,
hasBackground: true, hasBackground: true,
required: false, required: false,
@@ -24,6 +24,7 @@
<A-task-planned-time :hours="employeeData.duration" /> <A-task-planned-time :hours="employeeData.duration" />
<A-task-timeblocks <A-task-timeblocks
:timeblocks="employeeData.timeblocks" :timeblocks="employeeData.timeblocks"
:is-draft="!!subtask.is_draft"
:subtask="{ :subtask="{
...subtask, ...subtask,
employee_id: employeeData.id, employee_id: employeeData.id,