mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
refactor(employee-timeline): improve code readability and consistency by adding semicolons and restructuring computed properties
This commit is contained in:
1 parent
c55c2ce3e9
commit
d3addd1a39
3 files changed
+110
-98
No files matched your search
@@ -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,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
+2
-2
@@ -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';
|
||||||
|
|||||||
Reference in new issue
Block a user