mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 06:20:21 +00:00
feat(timeline): add function to clip multi-day blockers to work hours and update timeline rendering
This commit is contained in:
1 parent
4f8332e08e
commit
b6c4badd67
1 file changed
+82
-21
@@ -19,6 +19,53 @@ interface Employee {
|
||||
work_end: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orezá multi-dňový blocker na segment v rámci jedného dňa a pracovného času.
|
||||
* Blocker začína najskôr na začiatku pracovného času a končí najneskôr na konci pracovného času.
|
||||
*
|
||||
* Príklad: blocker pondelok 12:00 – streda 12:00, pracovný čas 8:00–16:00
|
||||
* - Pondelok: 12:00–16:00
|
||||
* - Utorok: 8:00–16:00
|
||||
* - Streda: 8:00–12:00
|
||||
*
|
||||
* @returns { start: Date, end: Date } ak má segment na daný deň prienik s pracovným časom, inak null
|
||||
*/
|
||||
function clipBlockerToWorkHoursOnDay(
|
||||
blockerStart: moment.Moment,
|
||||
blockerEnd: moment.Moment,
|
||||
selectedDateStr: string,
|
||||
workStartHours: number,
|
||||
workEndHours: number,
|
||||
): { start: Date; end: Date } | null {
|
||||
const dayWorkStart = moment(selectedDateStr)
|
||||
.set({
|
||||
hour: Math.floor(workStartHours),
|
||||
minute: Math.round((workStartHours % 1) * 60),
|
||||
second: 0,
|
||||
millisecond: 0,
|
||||
})
|
||||
.startOf('minute');
|
||||
const dayWorkEnd = moment(selectedDateStr)
|
||||
.set({
|
||||
hour: Math.floor(workEndHours),
|
||||
minute: Math.round((workEndHours % 1) * 60),
|
||||
second: 0,
|
||||
millisecond: 0,
|
||||
})
|
||||
.startOf('minute');
|
||||
|
||||
const clipStart = blockerStart.isBefore(dayWorkStart)
|
||||
? dayWorkStart
|
||||
: blockerStart;
|
||||
const clipEnd = blockerEnd.isAfter(dayWorkEnd) ? dayWorkEnd : blockerEnd;
|
||||
|
||||
if (!clipEnd.isAfter(clipStart)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { start: clipStart.toDate(), end: clipEnd.toDate() };
|
||||
}
|
||||
|
||||
export function useEmployeeTimeline(
|
||||
employee: Ref<Employee | null>,
|
||||
date: Ref<Date | string | null>,
|
||||
@@ -129,7 +176,7 @@ export function useEmployeeTimeline(
|
||||
const tasks: any[] = [];
|
||||
|
||||
subtasks.forEach((subtask) => {
|
||||
// Check if this is a blocker/adhoc/etc with direct start/end fields (no timeblocks array)
|
||||
// Blocker/adhoc/vacation/… – majú priamo start/end (nie pole timeblocks)
|
||||
if (
|
||||
!subtask.timeblocks &&
|
||||
subtask.start &&
|
||||
@@ -143,22 +190,42 @@ export function useEmployeeTimeline(
|
||||
'office365_blocker',
|
||||
].includes(subtask.type)
|
||||
) {
|
||||
// Skip the blocker being edited
|
||||
// Pri editácii blokera ho na timeline nezobrazujeme
|
||||
if (excludeBlockerId && subtask.id === excludeBlockerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const blockerDateStr = moment(subtask.start).format('YYYY-MM-DD');
|
||||
const blockerStart = moment(subtask.start);
|
||||
const blockerEnd = moment(subtask.end);
|
||||
const selectedDay = moment(selectedDateStr);
|
||||
// Blocker zobrazíme len ak je vybraný deň v jeho rozsahu (FE kontrola aj pri multi-day z BE)
|
||||
const selectedDayInRange =
|
||||
selectedDay.isSameOrAfter(blockerStart, 'day') &&
|
||||
selectedDay.isSameOrBefore(blockerEnd, 'day');
|
||||
|
||||
if (blockerDateStr === selectedDateStr) {
|
||||
if (selectedDayInRange) {
|
||||
const workStart = emp.work_start ?? 8;
|
||||
const workEnd = emp.work_end ?? 16;
|
||||
const segment = clipBlockerToWorkHoursOnDay(
|
||||
blockerStart,
|
||||
blockerEnd,
|
||||
selectedDateStr,
|
||||
workStart,
|
||||
workEnd,
|
||||
);
|
||||
if (!segment) {
|
||||
return; // na tento deň nemá blocker prienik s pracovným časom
|
||||
}
|
||||
|
||||
const { start: realStart, end: realEnd } = segment;
|
||||
const type = subtask.type;
|
||||
|
||||
const task = {
|
||||
id: subtask.id,
|
||||
start: new Date(subtask.start),
|
||||
end: new Date(subtask.end),
|
||||
start: realStart,
|
||||
end: realEnd,
|
||||
master_task: null,
|
||||
date: new Date(subtask.start),
|
||||
date: realStart,
|
||||
project_code: null,
|
||||
subtask: subtask,
|
||||
is_draft: !!subtask.is_draft,
|
||||
@@ -166,17 +233,17 @@ export function useEmployeeTimeline(
|
||||
type,
|
||||
_timeblock_id: null,
|
||||
_timeblock_is_finished: subtask.is_finished || false,
|
||||
_real_start: new Date(subtask.start),
|
||||
_real_end: new Date(subtask.end),
|
||||
_real_start: realStart,
|
||||
_real_end: realEnd,
|
||||
};
|
||||
|
||||
tasks.push(task);
|
||||
}
|
||||
} else {
|
||||
// Determine the correct timeblocks array based on task type
|
||||
// Bežné úlohy – časové bloky z timeblocks (alebo z employees pri draft)
|
||||
let timeblocks: any[] | undefined;
|
||||
|
||||
// For draft tasks with employees array, get timeblocks from the matching employee
|
||||
// Draft úlohy: timeblocks hľadáme u zodpovedajúceho zamestnanca
|
||||
if (subtask.is_draft && Array.isArray(subtask.employees)) {
|
||||
const employeeData = subtask.employees.find(
|
||||
(empData: any) => Number(empData.id) === Number(emp.id),
|
||||
@@ -187,13 +254,11 @@ export function useEmployeeTimeline(
|
||||
timeblocks = subtask.timeblocks;
|
||||
}
|
||||
|
||||
// Process timeblocks (same logic for both regular and concept tasks)
|
||||
timeblocks?.forEach((timeblock: any) => {
|
||||
const timeblockDateStr = moment(timeblock.start).format(
|
||||
'YYYY-MM-DD',
|
||||
);
|
||||
|
||||
// Only include timeblocks for the selected date
|
||||
// Len bloky na vybraný deň
|
||||
if (timeblockDateStr === selectedDateStr) {
|
||||
let type = 'subtask';
|
||||
if (subtask.type === 'office365') type = 'office365';
|
||||
@@ -221,16 +286,13 @@ export function useEmployeeTimeline(
|
||||
}
|
||||
});
|
||||
|
||||
// Process draft timeblocks from other employees in the same draft subtask
|
||||
// Draft timeblocky od iných zamestnancov v rámci toho istého draft subtasku
|
||||
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
|
||||
// Len bloky na vybraný deň
|
||||
if (timeblockDateStr === selectedDateStr) {
|
||||
const task = {
|
||||
id: null, // Draft timeblock doesn't have a subtask ID
|
||||
@@ -301,7 +363,7 @@ export function useEmployeeTimeline(
|
||||
// Calculate total planned time including local edits (for display purposes)
|
||||
const totalPlannedTime = computed(() => {
|
||||
const scheduled = parseFloat(String(unref(scheduledTime)));
|
||||
|
||||
|
||||
// Calculate duration of other local unsaved timeblocks for the same date
|
||||
let localTimeblocksHours = 0;
|
||||
if (otherLocalTimeblocks) {
|
||||
@@ -332,7 +394,6 @@ export function useEmployeeTimeline(
|
||||
if (!emp || !d) return '0';
|
||||
|
||||
const dailyCapacityHours = emp.work_end - emp.work_start;
|
||||
|
||||
return (dailyCapacityHours - totalPlanned).toFixed(1);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user