Files
jandl-taskmanager-app/app/src/plugins/tm@_utils/useEmployeeTimeline.ts
T

238 lines
5.8 KiB
TypeScript

import { useQuery } from '@tanstack/vue-query';
import { computed, Ref, unref, watch } from 'vue';
import axios from '@/plugins/w@custom/axios/models/axios';
import moment from 'moment';
import timelineUtils from '@/plugins/tm@dashboard/tasks/_utils/timeline.utils';
import {
detectCollision,
calculateTimeblockDuration,
getCollisionSegments,
type Timeblock,
type CollisionInfo,
} from './timeblock.utils';
interface Employee {
id: number;
name: string;
surname: string;
work_start: number;
work_end: number;
}
export function useEmployeeTimeline(
employee: Ref<Employee | null>,
date: Ref<Date | string | null>,
currentTimeblock: Ref<Timeblock | null>,
) {
// Fetch employee's tasks for the selected date
const {
data: tasksData,
isLoading,
error,
refetch,
} = useQuery({
queryKey: computed(() => [
'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;
}
const dateStr = moment(d).format('YYYY-MM-DD');
const queryParams = new URLSearchParams({
start: dateStr,
end: dateStr,
employee_id: emp.id.toString(),
}).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);
}),
});
// Generate timeline tasks from fetched data
const timelineTasks = computed(() => {
const data = unref(tasksData);
const emp = unref(employee);
const d = unref(date);
if (!data || !emp || !d) {
return [];
}
const subtasks = [...(data.subtasks || []), ...(data.blockers || [])];
const selectedDateStr = moment(d).format('YYYY-MM-DD');
try {
// Process timeblocks directly instead of using timeline utils
// which is designed for multi-day views
const tasks: any[] = [];
subtasks.forEach((subtask) => {
if (
![
'blocker',
'adhoc',
'home_office',
'vacation',
'free_day',
'office365_blocker',
].includes(subtask.type)
) {
subtask.timeblocks?.forEach((timeblock: any) => {
const timeblockDateStr = moment(timeblock.start).format(
'YYYY-MM-DD',
);
// Only include timeblocks for the selected date
if (timeblockDateStr === selectedDateStr) {
let type = 'subtask';
if (subtask.type === 'office365') type = 'office365';
tasks.push({
id: subtask.id,
start: new Date(timeblock.start),
end: new Date(timeblock.end),
master_task: subtask.master_task,
date: new Date(timeblock.start),
project_code: subtask.project_code,
subtask: subtask,
type,
_timeblock_id: timeblock.id,
_timeblock_is_finished: timeblock.is_finished,
_real_start: new Date(timeblock.start),
_real_end: new Date(timeblock.end),
});
}
});
}
});
return tasks;
} catch (error) {
console.error('Error generating timeline tasks:', error);
return [];
}
});
// Calculate scheduled time for the day (in hours)
const scheduledTime = computed(() => {
const tasks = unref(timelineTasks);
const d = unref(date);
if (!tasks || !d) return 0;
let totalMinutes = 0;
tasks.forEach((task) => {
if (
task.type === 'non-working-hours' ||
task.subtask?.is_finished ||
task.blocker?.is_finished
) {
return;
}
totalMinutes += timelineUtils._getDifferenceInMinutes(
task._real_end,
task._real_start,
);
});
return (totalMinutes / 60).toFixed(1);
});
// Calculate free/unscheduled time for the day (in hours)
const freeTime = computed(() => {
const emp = unref(employee);
const d = unref(date);
const scheduled = parseFloat(String(unref(scheduledTime)));
if (!emp || !d) return '0';
const dailyCapacityHours = emp.work_end - emp.work_start;
return (dailyCapacityHours - scheduled).toFixed(1);
});
// Detect collisions with current timeblock
const collision = computed<CollisionInfo>(() => {
const timeblock = unref(currentTimeblock);
const tasks = unref(timelineTasks);
if (!timeblock || !tasks) {
return {
hasCollision: false,
startCollides: false,
endCollides: false,
collidingTasks: [],
};
}
return detectCollision(timeblock, tasks);
});
// Get collision segments for timeline visualization
const collisionSegments = computed(() => {
const timeblock = unref(currentTimeblock);
const tasks = unref(timelineTasks);
if (!timeblock || !tasks) {
return [];
}
return getCollisionSegments(timeblock, tasks);
});
// Calculate current timeblock duration
const timeblockDuration = computed(() => {
const timeblock = unref(currentTimeblock);
if (!timeblock) return 0;
return calculateTimeblockDuration(timeblock);
});
watch(
scheduledTime,
(newVal) => {
console.log('scheduledTime', newVal);
},
{ immediate: true },
);
watch(
freeTime,
(newVal) => {
console.log('freeTime', newVal);
},
{ immediate: true },
);
return {
// Query states
isLoading,
error,
refetch,
// Timeline data
timelineTasks,
scheduledTime,
freeTime,
// Collision detection
collision,
collisionSegments,
timeblockDuration,
};
}