refactor(employee-timeline): improve code readability and consistency by adding semicolons and restructuring computed properties

This commit is contained in:
Matej Glasnák committed 2025-12-08 03:29:49 +01:00
1 parent c55c2ce3e9
commit d3addd1a39
3 files changed
+110 -98

No files matched your search

+107 -88
View File
@@ -1,89 +1,104 @@
import { useQuery } from '@tanstack/vue-query' import { useQuery } from '@tanstack/vue-query';
import { computed, Ref, unref } from 'vue' import { computed, Ref, unref } from 'vue';
import axios from '@/plugins/w@custom/axios/models/axios' import axios from '@/plugins/w@custom/axios/models/axios';
import moment from 'moment' import moment from 'moment';
import timelineUtils from '@/plugins/tm@dashboard/tasks/_utils/timeline.utils' import timelineUtils from '@/plugins/tm@dashboard/tasks/_utils/timeline.utils';
import { import {
detectCollision, detectCollision,
calculateTimeblockDuration, calculateTimeblockDuration,
getCollisionSegments, getCollisionSegments,
type Timeblock, type Timeblock,
type CollisionInfo, type CollisionInfo,
} from './timeblock.utils' } from './timeblock.utils';
interface Employee { interface Employee {
id: number id: number;
name: string name: string;
surname: string surname: string;
work_start: number work_start: number;
work_end: number work_end: number;
} }
export function useEmployeeTimeline( export function useEmployeeTimeline(
employee: Ref<Employee | null>, employee: Ref<Employee | null>,
date: Ref<Date | string | null>, date: Ref<Date | string | null>,
currentTimeblock: Ref<Timeblock | null> currentTimeblock: Ref<Timeblock | null>,
) { ) {
// Fetch employee's tasks for the selected date // Fetch employee's tasks for the selected date
const { data: tasksData, isLoading, error, refetch } = useQuery({ const {
data: tasksData,
isLoading,
error,
refetch,
} = useQuery({
queryKey: computed(() => [ queryKey: computed(() => [
'employee-timeline', '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(),
}).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 emp = unref(employee) const emp = unref(employee);
const d = unref(date) const d = unref(date);
if (!data || !emp || !d) { if (!data || !emp || !d) {
return [] return [];
} }
const subtasks = [...(data.subtasks || []), ...(data.blockers || [])] const subtasks = [...(data.subtasks || []), ...(data.blockers || [])];
const dateObj = new Date(d) const selectedDateStr = moment(d).format('YYYY-MM-DD');
const selectedDateStr = moment(d).format('YYYY-MM-DD')
try { try {
// Process timeblocks directly instead of using timeline utils // Process timeblocks directly instead of using timeline utils
// which is designed for multi-day views // which is designed for multi-day views
const tasks: any[] = [] const tasks: any[] = [];
subtasks.forEach((subtask) => { subtasks.forEach((subtask) => {
if (!['blocker', 'adhoc', 'home_office', 'vacation', 'free_day', 'office365_blocker'].includes(subtask.type)) { if (
![
'blocker',
'adhoc',
'home_office',
'vacation',
'free_day',
'office365_blocker',
].includes(subtask.type)
) {
subtask.timeblocks?.forEach((timeblock: any) => { subtask.timeblocks?.forEach((timeblock: any) => {
const timeblockDateStr = moment(timeblock.start).format('YYYY-MM-DD') const timeblockDateStr = moment(timeblock.start).format(
'YYYY-MM-DD',
);
// Only include timeblocks for the selected date // 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';
tasks.push({ tasks.push({
id: subtask.id, id: subtask.id,
@@ -98,28 +113,28 @@ export function useEmployeeTimeline(
_timeblock_is_finished: timeblock.is_finished, _timeblock_is_finished: timeblock.is_finished,
_real_start: new Date(timeblock.start), _real_start: new Date(timeblock.start),
_real_end: new Date(timeblock.end), _real_end: new Date(timeblock.end),
}) });
} }
}) });
} }
}) });
return tasks return tasks;
} catch (error) { } catch (error) {
console.error('Error generating timeline tasks:', error) console.error('Error generating timeline tasks:', error);
return [] return [];
} }
}) });
// Calculate scheduled time for the day (in hours) // Calculate scheduled time for the day (in hours)
const scheduledTime = computed(() => { const scheduledTime = computed(() => {
const tasks = unref(timelineTasks) const tasks = unref(timelineTasks);
const d = unref(date) const d = unref(date);
const now = moment() const now = moment();
if (!tasks || !d) return 0 if (!tasks || !d) return 0;
let totalMinutes = 0 let totalMinutes = 0;
tasks.forEach((task) => { tasks.forEach((task) => {
if ( if (
@@ -127,64 +142,69 @@ export function useEmployeeTimeline(
task.subtask?.is_finished || task.subtask?.is_finished ||
task.blocker?.is_finished task.blocker?.is_finished
) { ) {
return return;
} }
if (now.isAfter(moment(task._real_end))) { if (now.isAfter(moment(task._real_end))) {
// Ignore elapsed tasks // Ignore elapsed tasks
return return;
} else if ( } else if (
now.isAfter(moment(task._real_start)) && now.isAfter(moment(task._real_start)) &&
now.isBefore(moment(task._real_end)) now.isBefore(moment(task._real_end))
) { ) {
// Task in progress // Task in progress
totalMinutes += timelineUtils._getDifferenceInMinutes(task._real_end, new Date()) totalMinutes += timelineUtils._getDifferenceInMinutes(
task._real_end,
new Date(),
);
} else { } else {
// Future task // Future task
totalMinutes += timelineUtils._getDifferenceInMinutes( totalMinutes += timelineUtils._getDifferenceInMinutes(
task._real_end, task._real_end,
task._real_start task._real_start,
) );
} }
}) });
return (totalMinutes / 60).toFixed(1) return (totalMinutes / 60).toFixed(1);
}) });
// Calculate free/unscheduled time for the day (in hours) // Calculate free/unscheduled time for the day (in hours)
const freeTime = computed(() => { const freeTime = computed(() => {
const emp = unref(employee) const emp = unref(employee);
const d = unref(date) const d = unref(date);
const scheduled = parseFloat(String(unref(scheduledTime))) const scheduled = parseFloat(String(unref(scheduledTime)));
if (!emp || !d) return '0' if (!emp || !d) return '0';
const now = moment() const now = moment();
const dateObj = moment(d) const dateObj = moment(d);
const dailyCapacityHours = emp.work_end - emp.work_start const dailyCapacityHours = emp.work_end - emp.work_start;
if (dateObj.isBefore(now, 'days')) { if (dateObj.isBefore(now, 'days')) {
// Past date // Past date
return '0' return '0';
} else if (dateObj.isSame(now, 'days')) { } else if (dateObj.isSame(now, 'days')) {
// Today - calculate remaining capacity // Today - calculate remaining capacity
const elapsedMinutes = now.diff(now.clone().startOf('day'), 'minutes') const elapsedMinutes = now.diff(now.clone().startOf('day'), 'minutes');
const workStart = const workStart =
emp.work_start * 60 > elapsedMinutes ? emp.work_start : elapsedMinutes / 60 emp.work_start * 60 > elapsedMinutes
const todayCapacityHours = emp.work_end - workStart ? emp.work_start
const free = todayCapacityHours - scheduled : elapsedMinutes / 60;
return free < 0 ? '0' : free.toFixed(1) const todayCapacityHours = emp.work_end - workStart;
const free = todayCapacityHours - scheduled;
return free < 0 ? '0' : free.toFixed(1);
} else { } else {
// Future date // Future date
const free = dailyCapacityHours - scheduled const free = dailyCapacityHours - scheduled;
return free < 0 ? '0' : free.toFixed(1) return free < 0 ? '0' : free.toFixed(1);
} }
}) });
// Detect collisions with current timeblock // Detect collisions with current timeblock
const collision = computed<CollisionInfo>(() => { const collision = computed<CollisionInfo>(() => {
const timeblock = unref(currentTimeblock) const timeblock = unref(currentTimeblock);
const tasks = unref(timelineTasks) const tasks = unref(timelineTasks);
if (!timeblock || !tasks) { if (!timeblock || !tasks) {
return { return {
@@ -192,30 +212,30 @@ export function useEmployeeTimeline(
startCollides: false, startCollides: false,
endCollides: false, endCollides: false,
collidingTasks: [], collidingTasks: [],
} };
} }
return detectCollision(timeblock, tasks) return detectCollision(timeblock, tasks);
}) });
// Get collision segments for timeline visualization // Get collision segments for timeline visualization
const collisionSegments = computed(() => { const collisionSegments = computed(() => {
const timeblock = unref(currentTimeblock) const timeblock = unref(currentTimeblock);
const tasks = unref(timelineTasks) const tasks = unref(timelineTasks);
if (!timeblock || !tasks) { if (!timeblock || !tasks) {
return [] return [];
} }
return getCollisionSegments(timeblock, tasks) return getCollisionSegments(timeblock, tasks);
}) });
// Calculate current timeblock duration // Calculate current timeblock duration
const timeblockDuration = computed(() => { const timeblockDuration = computed(() => {
const timeblock = unref(currentTimeblock) const timeblock = unref(currentTimeblock);
if (!timeblock) return 0 if (!timeblock) return 0;
return calculateTimeblockDuration(timeblock) return calculateTimeblockDuration(timeblock);
}) });
return { return {
// Query states // Query states
@@ -232,6 +252,5 @@ export function useEmployeeTimeline(
collision, collision,
collisionSegments, collisionSegments,
timeblockDuration, timeblockDuration,
} };
} }
@@ -3,8 +3,8 @@
<div v-if="isLoading" class="timeline-loading"> <div v-if="isLoading" class="timeline-loading">
<span>Načítavam...</span> <span>Načítavam...</span>
</div> </div>
<div v-else class="timeline__content" ref="timelineContainer"> <div v-else ref="timelineContainer" class="timeline__content">
<div class="timeline-scroll" ref="timelineScroll"> <div ref="timelineScroll" class="timeline-scroll">
<a-employee-timeblock-timeline-header <a-employee-timeblock-timeline-header
:start-hour="startHour" :start-hour="startHour"
:end-hour="endHour" :end-hour="endHour"
@@ -29,14 +29,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { import { computed, ref, watch, nextTick, onMounted } from 'vue';
computed,
ref,
watch,
nextTick,
onMounted,
getCurrentInstance,
} from 'vue';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import AEmployeeSelect from './a-employee-select.vue'; import AEmployeeSelect from './a-employee-select.vue';
import AEmployeeTimeblock from './a-employee-timeblock.vue'; import AEmployeeTimeblock from './a-employee-timeblock.vue';