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;
|
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(
|
export function useEmployeeTimeline(
|
||||||
employee: Ref<Employee | null>,
|
employee: Ref<Employee | null>,
|
||||||
date: Ref<Date | string | null>,
|
date: Ref<Date | string | null>,
|
||||||
@@ -129,7 +176,7 @@ export function useEmployeeTimeline(
|
|||||||
const tasks: any[] = [];
|
const tasks: any[] = [];
|
||||||
|
|
||||||
subtasks.forEach((subtask) => {
|
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 (
|
if (
|
||||||
!subtask.timeblocks &&
|
!subtask.timeblocks &&
|
||||||
subtask.start &&
|
subtask.start &&
|
||||||
@@ -143,22 +190,42 @@ export function useEmployeeTimeline(
|
|||||||
'office365_blocker',
|
'office365_blocker',
|
||||||
].includes(subtask.type)
|
].includes(subtask.type)
|
||||||
) {
|
) {
|
||||||
// Skip the blocker being edited
|
// Pri editácii blokera ho na timeline nezobrazujeme
|
||||||
if (excludeBlockerId && subtask.id === excludeBlockerId) {
|
if (excludeBlockerId && subtask.id === excludeBlockerId) {
|
||||||
return;
|
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 type = subtask.type;
|
||||||
|
|
||||||
const task = {
|
const task = {
|
||||||
id: subtask.id,
|
id: subtask.id,
|
||||||
start: new Date(subtask.start),
|
start: realStart,
|
||||||
end: new Date(subtask.end),
|
end: realEnd,
|
||||||
master_task: null,
|
master_task: null,
|
||||||
date: new Date(subtask.start),
|
date: realStart,
|
||||||
project_code: null,
|
project_code: null,
|
||||||
subtask: subtask,
|
subtask: subtask,
|
||||||
is_draft: !!subtask.is_draft,
|
is_draft: !!subtask.is_draft,
|
||||||
@@ -166,17 +233,17 @@ export function useEmployeeTimeline(
|
|||||||
type,
|
type,
|
||||||
_timeblock_id: null,
|
_timeblock_id: null,
|
||||||
_timeblock_is_finished: subtask.is_finished || false,
|
_timeblock_is_finished: subtask.is_finished || false,
|
||||||
_real_start: new Date(subtask.start),
|
_real_start: realStart,
|
||||||
_real_end: new Date(subtask.end),
|
_real_end: realEnd,
|
||||||
};
|
};
|
||||||
|
|
||||||
tasks.push(task);
|
tasks.push(task);
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
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)) {
|
if (subtask.is_draft && Array.isArray(subtask.employees)) {
|
||||||
const employeeData = subtask.employees.find(
|
const employeeData = subtask.employees.find(
|
||||||
(empData: any) => Number(empData.id) === Number(emp.id),
|
(empData: any) => Number(empData.id) === Number(emp.id),
|
||||||
@@ -187,13 +254,11 @@ export function useEmployeeTimeline(
|
|||||||
timeblocks = subtask.timeblocks;
|
timeblocks = subtask.timeblocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process timeblocks (same logic for both regular and concept tasks)
|
|
||||||
timeblocks?.forEach((timeblock: any) => {
|
timeblocks?.forEach((timeblock: any) => {
|
||||||
const timeblockDateStr = moment(timeblock.start).format(
|
const timeblockDateStr = moment(timeblock.start).format(
|
||||||
'YYYY-MM-DD',
|
'YYYY-MM-DD',
|
||||||
);
|
);
|
||||||
|
// Len bloky na vybraný deň
|
||||||
// Only include timeblocks for the selected date
|
|
||||||
if (timeblockDateStr === selectedDateStr) {
|
if (timeblockDateStr === selectedDateStr) {
|
||||||
let type = 'subtask';
|
let type = 'subtask';
|
||||||
if (subtask.type === 'office365') type = 'office365';
|
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) => {
|
draftTbs.forEach((timeblock: any) => {
|
||||||
// Skip if timeblock doesn't have required fields
|
|
||||||
if (!timeblock.start || !timeblock.end) {
|
if (!timeblock.start || !timeblock.end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeblockDateStr = moment(timeblock.start).format('YYYY-MM-DD');
|
const timeblockDateStr = moment(timeblock.start).format('YYYY-MM-DD');
|
||||||
|
// Len bloky na vybraný deň
|
||||||
// Only include timeblocks for the selected date
|
|
||||||
if (timeblockDateStr === selectedDateStr) {
|
if (timeblockDateStr === selectedDateStr) {
|
||||||
const task = {
|
const task = {
|
||||||
id: null, // Draft timeblock doesn't have a subtask ID
|
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)
|
// Calculate total planned time including local edits (for display purposes)
|
||||||
const totalPlannedTime = computed(() => {
|
const totalPlannedTime = computed(() => {
|
||||||
const scheduled = parseFloat(String(unref(scheduledTime)));
|
const scheduled = parseFloat(String(unref(scheduledTime)));
|
||||||
|
|
||||||
// Calculate duration of other local unsaved timeblocks for the same date
|
// Calculate duration of other local unsaved timeblocks for the same date
|
||||||
let localTimeblocksHours = 0;
|
let localTimeblocksHours = 0;
|
||||||
if (otherLocalTimeblocks) {
|
if (otherLocalTimeblocks) {
|
||||||
@@ -332,7 +394,6 @@ export function useEmployeeTimeline(
|
|||||||
if (!emp || !d) return '0';
|
if (!emp || !d) return '0';
|
||||||
|
|
||||||
const dailyCapacityHours = emp.work_end - emp.work_start;
|
const dailyCapacityHours = emp.work_end - emp.work_start;
|
||||||
|
|
||||||
return (dailyCapacityHours - totalPlanned).toFixed(1);
|
return (dailyCapacityHours - totalPlanned).toFixed(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user