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>,
otherLocalTimeblocks?: Ref<Timeblock[]>,
editingBlockerId?: Ref<number | null>,
showDraftTasks?: Ref<boolean>,
draftTimeblocks?: Ref<Timeblock[]>,
) {
// Fetch employee's tasks for the selected date
const {
@@ -64,61 +64,60 @@ export function useEmployeeTimeline(
}),
});
const {
data: conceptTasksData,
isLoading: conceptTasksLoading,
error: conceptTasksError,
refetch: conceptTasksRefetch,
} = useQuery({
queryKey: computed(() => [
'concept-employee-timeline',
unref(employee)?.id,
unref(date) ? moment(unref(date)).format('YYYY-MM-DD') : null,
]),
queryFn: async () => {
const emp = unref(employee);
const d = unref(date);
// const {
// data: conceptTasksData,
// isLoading: conceptTasksLoading,
// error: conceptTasksError,
// refetch: conceptTasksRefetch,
// } = useQuery({
// queryKey: computed(() => [
// 'concept-employee-timeline',
// unref(employee)?.id,
// unref(date) ? moment(unref(date)).format('YYYY-MM-DD') : null,
// ]),
// queryFn: async () => {
// const emp = unref(employee);
// const d = unref(date);
if (!emp?.id || !d) {
return null;
}
// if (!emp?.id || !d) {
// return null;
// }
const dateStr = moment(d).format('YYYY-MM-DD');
const queryParams = new URLSearchParams({
start: dateStr,
end: dateStr,
employee_id: emp.id.toString(),
type: 'draft',
}).toString();
// const dateStr = moment(d).format('YYYY-MM-DD');
// const queryParams = new URLSearchParams({
// start: dateStr,
// end: dateStr,
// employee_id: emp.id.toString(),
// type: 'draft',
// }).toString();
const response = await axios.get_auth(`v1/subtasks?${queryParams}`);
return response;
},
enabled: computed(() => {
const emp = unref(employee);
const d = unref(date);
return !!(emp?.id && d);
}),
});
// const response = await axios.get_auth(`v1/subtasks?${queryParams}`);
// return response;
// },
// enabled: computed(() => {
// const emp = unref(employee);
// const d = unref(date);
// return !!(emp?.id && d);
// }),
// });
// Generate timeline tasks from fetched data
const timelineTasks = computed(() => {
const data = unref(tasksData);
const conceptData = unref(conceptTasksData);
// const conceptData = unref(conceptTasksData);
const emp = unref(employee);
const d = unref(date);
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 [];
}
const subtasks = [
...(data.subtasks || []),
...(data.blockers || []),
// Only include concept tasks if showDraftTasks is true
...(includeDrafts ? (conceptData.subtasks || []) : []),
// ...(conceptData.subtasks || []),
];
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;
} catch (error) {
console.error('Error generating timeline tasks:', error);
@@ -328,9 +360,9 @@ export function useEmployeeTimeline(
error,
refetch,
conceptTasksLoading,
conceptTasksError,
conceptTasksRefetch,
// conceptTasksLoading,
// conceptTasksError,
// conceptTasksRefetch,
// Timeline data
timelineTasks,
@@ -117,7 +117,7 @@
:employee="employee"
:deletable="false"
:index="0"
:show-draft-tasks="!!subtask.is_draft"
:draft-timeblocks="draftTimeblocks"
/>
</v-card-text>
@@ -215,6 +215,11 @@ export default {
type: Object,
required: true,
},
isDraft: {
type: Boolean,
default: false,
},
},
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
hourWidth() {
return window.innerWidth <= 768
@@ -77,7 +77,7 @@ interface Props {
currentIndex?: number;
deletable?: boolean;
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>(), {
@@ -88,7 +88,7 @@ const props = withDefaults(defineProps<Props>(), {
currentIndex: 0,
deletable: false,
index: 0,
showDraftTasks: false,
draftTimeblocks: () => [],
});
const emit = defineEmits<{
@@ -240,7 +240,7 @@ const otherTimeblocksForSameDate = computed(() => {
// Use timeline composable
const editingBlockerIdRef = computed(() => props.editingBlockerId);
const showDraftTasksRef = computed(() => props.showDraftTasks);
const draftTimeblocksRef = computed(() => props.draftTimeblocks);
const {
isLoading,
@@ -249,14 +249,13 @@ const {
freeTime: _freeTime,
timeblockDuration,
refetch,
conceptTasksRefetch,
} = useEmployeeTimeline(
employeeRef,
dateRef,
timeblockRef,
otherTimeblocksForSameDate,
editingBlockerIdRef,
showDraftTasksRef,
draftTimeblocksRef,
);
// Force refetch when date changes
@@ -265,14 +264,14 @@ watch(
() => {
if (props.modelValue.date && props.employee?.id) {
refetch();
conceptTasksRefetch();
// conceptTasksRefetch();
}
},
);
onMounted(() => {
refetch();
conceptTasksRefetch();
// conceptTasksRefetch();
});
// All tasks from timelineTasks are already filtered for the selected date
@@ -19,6 +19,7 @@
:all-employee-timeblocks="timeblocks"
:all-timeblocks="allTimeblocksForCurrentEmployee"
:deleted-timeblock-ids="deletedTimeblockIds"
:draft-timeblocks="draftTimeblocks"
:current-index="index"
:deletable="timeblocks.length > 1"
:index="index"
@@ -46,6 +47,7 @@ interface Props {
timeblocks?: TimeblockData[];
allTimeblocks?: TimeblockData[]; // All timeblocks from all employees
deletedTimeblockIds?: number[]; // IDs of saved timeblocks that have been deleted
draftTimeblocks?: TimeblockData[]; // Draft timeblocks from all employees in draft subtask
customFilter?: CustomFilterFunction;
hasBackground?: boolean;
required?: boolean;
@@ -55,6 +57,7 @@ const props = withDefaults(defineProps<Props>(), {
timeblocks: () => [],
allTimeblocks: () => [],
deletedTimeblockIds: () => [],
draftTimeblocks: () => [],
customFilter: undefined,
hasBackground: true,
required: false,
@@ -24,6 +24,7 @@
<A-task-planned-time :hours="employeeData.duration" />
<A-task-timeblocks
:timeblocks="employeeData.timeblocks"
:is-draft="!!subtask.is_draft"
:subtask="{
...subtask,
employee_id: employeeData.id,