import { useState, useMemo, useCallback, useEffect, useRef } from 'react'; import { getData } from '../data'; import { Gantt, Editor, defaultEditorItems, ContextMenu } from '../../src'; import './GanttSummariesProgress.css'; function GanttSummariesProgress({ skinSettings }) { const data = useMemo(() => getData(), []); const tasks = data.tasks; const [api, setApi] = useState(); const [items, setItems] = useState(defaultEditorItems); const dayDiff = (next, prev) => { const d = (next - prev) / 1000 / 60 / 60 / 24; return Math.ceil(Math.abs(d)); }; /** * The formula of calculation is ∑d*p / ∑d , where "d" is task duration in days, * "p" is the task progress and "∑" stands for a sum of all loaded tasks */ function getSummaryProgress(id) { const [totalProgress, totalDuration] = collectProgressFromKids(id); const res = totalProgress / totalDuration; return isNaN(res) ? 0 : Math.round(res); } function collectProgressFromKids(id) { let totalProgress = 0, totalDuration = 0; const kids = api.getTask(id).data; kids?.forEach((kid) => { let duration = 0; if (kid.type !== 'milestone' && kid.type !== 'summary') { duration = kid.duration || dayDiff(kid.end, kid.start); totalDuration += duration; totalProgress += duration * kid.progress; } const [p, d] = collectProgressFromKids(kid.id); totalProgress += p; totalDuration += d; }); return [totalProgress, totalDuration]; } function recalcSummaryProgress(id, self) { const { tasks } = api.getState(); const task = api.getTask(id); if (task.type != 'milestone') { const summary = self && task.type === 'summary' ? id : tasks.getSummaryId(id); if (summary) { const progress = getSummaryProgress(summary); api.exec('update-task', { id: summary, task: { progress }, }); } } } const recalcRef = useRef(null); recalcRef.current = recalcSummaryProgress; const init = useCallback((api) => { setApi(api); // auto progress calculations api.on('add-task', ({ id }) => { recalcRef.current(id); }); api.on('update-task', ({ id }) => { recalcRef.current(id); }); api.on('delete-task', ({ source }) => { recalcRef.current(source, true); }); api.on('copy-task', ({ id }) => { recalcRef.current(id); }); api.on('move-task', ({ id, source, inProgress }) => { if (inProgress) return; if (api.getTask(id).parent != source) recalcRef.current(source, true); recalcRef.current(id); }); // disabling progress slider for summary tasks api.on('show-editor', ({ id }) => { if (id) { const type = api.getTask(id).type; setItems( defaultEditorItems.map((ed) => ({ ...ed, ...(ed.key == 'progress' && { config: { disabled: type === 'summary' }, }), })), ); } }); }, []); useEffect(() => { if (!api) return; api.getState().tasks.forEach((task) => { recalcSummaryProgress(task.id, true); }); }, [api]); return (