ct-gantt-core 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +128 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -1
- package/dist/index.d.ts +67 -1
- package/dist/index.js +123 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,12 +30,15 @@ __export(index_exports, {
|
|
|
30
30
|
RESOURCE_COLOR_PALETTE: () => RESOURCE_COLOR_PALETTE,
|
|
31
31
|
addDays: () => addDays,
|
|
32
32
|
addHours: () => addHours,
|
|
33
|
+
addPlannedDuration: () => addPlannedDuration,
|
|
34
|
+
alignPlannedDate: () => alignPlannedDate,
|
|
33
35
|
checkCyclicDependency: () => checkCyclicDependency,
|
|
34
36
|
computeHourScale: () => computeHourScale,
|
|
35
37
|
computeImpact: () => computeImpact,
|
|
36
38
|
computeLayout: () => computeLayout,
|
|
37
39
|
computeResourceLayout: () => computeResourceLayout,
|
|
38
40
|
computeTimeScale: () => computeTimeScale,
|
|
41
|
+
computeWorkload: () => computeWorkload,
|
|
39
42
|
createGanttEngine: () => createGanttEngine,
|
|
40
43
|
defaultConfig: () => defaultConfig,
|
|
41
44
|
diffDays: () => diffDays,
|
|
@@ -48,6 +51,7 @@ __export(index_exports, {
|
|
|
48
51
|
isValidDate: () => isValidDate,
|
|
49
52
|
mergeTaskPatch: () => mergeTaskPatch,
|
|
50
53
|
normalizeLinks: () => normalizeLinks,
|
|
54
|
+
plannedDurationBetween: () => plannedDurationBetween,
|
|
51
55
|
resolveResourceColor: () => resolveResourceColor,
|
|
52
56
|
resolveUsageColor: () => resolveUsageColor,
|
|
53
57
|
resolveUsageTimes: () => resolveUsageTimes,
|
|
@@ -55,6 +59,7 @@ __export(index_exports, {
|
|
|
55
59
|
scheduleByDependencies: () => scheduleByDependencies,
|
|
56
60
|
shiftTask: () => shiftTask,
|
|
57
61
|
startOfDay: () => startOfDay,
|
|
62
|
+
subtractPlannedDuration: () => subtractPlannedDuration,
|
|
58
63
|
toDate: () => toDate,
|
|
59
64
|
toDateTime: () => toDateTime
|
|
60
65
|
});
|
|
@@ -107,7 +112,7 @@ var defaultConfig = {
|
|
|
107
112
|
showPlanBar: true,
|
|
108
113
|
showActualBar: true,
|
|
109
114
|
showTimelineWhenEmpty: false,
|
|
110
|
-
builtInTaskEditor:
|
|
115
|
+
builtInTaskEditor: false,
|
|
111
116
|
builtInMarkerEditor: true,
|
|
112
117
|
editablePlan: false,
|
|
113
118
|
editableActual: true,
|
|
@@ -303,6 +308,52 @@ function diffHours(start, end) {
|
|
|
303
308
|
function inclusiveDays(start, end) {
|
|
304
309
|
return Math.max(0, diffDays(start, end) + 1);
|
|
305
310
|
}
|
|
311
|
+
function addPlannedDuration(start, duration, calendarId = "standard") {
|
|
312
|
+
const days = Math.max(0, Math.trunc(Number.isFinite(duration) ? duration : 0));
|
|
313
|
+
if (days === 0) return toDate(start);
|
|
314
|
+
if (calendarId === "delivery") return addDays(start, days - 1);
|
|
315
|
+
let current = toDate(start);
|
|
316
|
+
let remaining = days;
|
|
317
|
+
while (remaining > 0) {
|
|
318
|
+
const day = current.getDay();
|
|
319
|
+
if (day !== 0 && day !== 6) remaining -= 1;
|
|
320
|
+
if (remaining > 0) current = addDays(current, 1);
|
|
321
|
+
}
|
|
322
|
+
return current;
|
|
323
|
+
}
|
|
324
|
+
function subtractPlannedDuration(end, duration, calendarId = "standard") {
|
|
325
|
+
const days = Math.max(0, Math.trunc(Number.isFinite(duration) ? duration : 0));
|
|
326
|
+
if (days === 0) return toDate(end);
|
|
327
|
+
if (calendarId === "delivery") return addDays(end, -(days - 1));
|
|
328
|
+
let current = toDate(end);
|
|
329
|
+
let remaining = days;
|
|
330
|
+
while (remaining > 0) {
|
|
331
|
+
const day = current.getDay();
|
|
332
|
+
if (day !== 0 && day !== 6) remaining -= 1;
|
|
333
|
+
if (remaining > 0) current = addDays(current, -1);
|
|
334
|
+
}
|
|
335
|
+
return current;
|
|
336
|
+
}
|
|
337
|
+
function alignPlannedDate(date, calendarId = "standard", direction = 1) {
|
|
338
|
+
let current = toDate(date);
|
|
339
|
+
if (calendarId === "delivery") return current;
|
|
340
|
+
while (current.getDay() === 0 || current.getDay() === 6) {
|
|
341
|
+
current = addDays(current, direction);
|
|
342
|
+
}
|
|
343
|
+
return current;
|
|
344
|
+
}
|
|
345
|
+
function plannedDurationBetween(start, end, calendarId = "standard") {
|
|
346
|
+
const first = toDate(start);
|
|
347
|
+
const last = toDate(end);
|
|
348
|
+
if (last.getTime() < first.getTime()) return 0;
|
|
349
|
+
if (calendarId === "delivery") return inclusiveDays(first, last);
|
|
350
|
+
let duration = 0;
|
|
351
|
+
for (let current = first; current <= last; current = addDays(current, 1)) {
|
|
352
|
+
const day = current.getDay();
|
|
353
|
+
if (day !== 0 && day !== 6) duration += 1;
|
|
354
|
+
}
|
|
355
|
+
return duration;
|
|
356
|
+
}
|
|
306
357
|
function isValidDate(value) {
|
|
307
358
|
const date = value instanceof Date ? value : new Date(value);
|
|
308
359
|
return !Number.isNaN(date.getTime());
|
|
@@ -798,15 +849,84 @@ function minDate2(dates) {
|
|
|
798
849
|
return new Date(Math.min(...dates.map((date) => date.getTime())));
|
|
799
850
|
}
|
|
800
851
|
|
|
852
|
+
// src/engines/computeWorkload.ts
|
|
853
|
+
function computeWorkload(tasks, people, departments = [], range) {
|
|
854
|
+
const dated = range ?? inferRange(tasks);
|
|
855
|
+
if (!dated) return { dates: [], departments: [], ungrouped: [] };
|
|
856
|
+
const dates = datesBetween(dated.start, dated.end);
|
|
857
|
+
const dateSet = new Set(dates);
|
|
858
|
+
const rows = new Map(people.map((person) => [person.id, createPerson(person, dates)]));
|
|
859
|
+
for (const task of tasks) {
|
|
860
|
+
if (task.type === "summary" || task.type === "milestone") continue;
|
|
861
|
+
const ids = task.resources ?? [];
|
|
862
|
+
if (!ids.length) continue;
|
|
863
|
+
const values = task.dailyWorkloads ?? averageTaskWorkload(task);
|
|
864
|
+
for (const personId of ids) {
|
|
865
|
+
const row = rows.get(personId);
|
|
866
|
+
if (!row) continue;
|
|
867
|
+
for (const [date, hours] of Object.entries(values)) if (dateSet.has(date)) row.days.find((day) => day.date === date).hours += hours;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
for (const row of rows.values()) finalize(row);
|
|
871
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
872
|
+
const ungrouped = [];
|
|
873
|
+
for (const row of rows.values()) (row.person.departmentId ? grouped.get(row.person.departmentId) ?? (grouped.set(row.person.departmentId, []), grouped.get(row.person.departmentId)) : ungrouped).push(row);
|
|
874
|
+
const departmentMap = new Map(departments.map((item) => [item.id, item]));
|
|
875
|
+
const result = [...grouped].map(([id, members]) => aggregateDepartment(departmentMap.get(id) ?? { id, name: id }, members, dates));
|
|
876
|
+
return { dates, departments: result, ungrouped };
|
|
877
|
+
}
|
|
878
|
+
function inferRange(tasks) {
|
|
879
|
+
if (!tasks.length) return void 0;
|
|
880
|
+
const dates = tasks.flatMap((t) => [toDate(t.plan.start), toDate(t.plan.end)]);
|
|
881
|
+
return { start: new Date(Math.min(...dates.map((d) => d.getTime()))), end: new Date(Math.max(...dates.map((d) => d.getTime()))) };
|
|
882
|
+
}
|
|
883
|
+
function datesBetween(start, end) {
|
|
884
|
+
const values = [];
|
|
885
|
+
for (let date = toDate(start); date <= toDate(end); date = addDays(date, 1)) values.push(formatDate(date));
|
|
886
|
+
return values;
|
|
887
|
+
}
|
|
888
|
+
function averageTaskWorkload(task) {
|
|
889
|
+
const total = task.workload ?? (task.duration ?? inclusiveDays(task.plan.start, task.plan.end)) * 8;
|
|
890
|
+
const allDates = datesBetween(task.plan.start, task.plan.end);
|
|
891
|
+
const dates = task.calendarId === "delivery" ? allDates : allDates.filter((date) => {
|
|
892
|
+
const day = toDate(date).getDay();
|
|
893
|
+
return day !== 0 && day !== 6;
|
|
894
|
+
});
|
|
895
|
+
const hours = dates.length ? total / dates.length : 0;
|
|
896
|
+
return Object.fromEntries(dates.map((date) => [date, hours]));
|
|
897
|
+
}
|
|
898
|
+
function createPerson(person, dates) {
|
|
899
|
+
return { person, days: dates.map((date) => ({ date, hours: 0, capacity: person.capacityPerDay ?? 8 })), workload: 0, capacity: 0, utilization: 0, progress: 0 };
|
|
900
|
+
}
|
|
901
|
+
function finalize(row) {
|
|
902
|
+
row.workload = row.days.reduce((sum, day) => sum + day.hours, 0);
|
|
903
|
+
row.capacity = row.days.reduce((sum, day) => sum + day.capacity, 0);
|
|
904
|
+
row.utilization = row.capacity ? Math.round(row.workload / row.capacity * 100) : 0;
|
|
905
|
+
row.progress = row.utilization;
|
|
906
|
+
}
|
|
907
|
+
function aggregateDepartment(department, people, dates) {
|
|
908
|
+
const days = dates.map((date, index) => ({ date, hours: people.reduce((sum, person) => sum + person.days[index].hours, 0), capacity: people.reduce((sum, person) => sum + person.days[index].capacity, 0) }));
|
|
909
|
+
const workload = days.reduce((sum, day) => sum + day.hours, 0);
|
|
910
|
+
const capacity = days.reduce((sum, day) => sum + day.capacity, 0);
|
|
911
|
+
const utilization = capacity ? Math.round(workload / capacity * 100) : 0;
|
|
912
|
+
return { department, people, days, workload, capacity, utilization, progress: utilization };
|
|
913
|
+
}
|
|
914
|
+
|
|
801
915
|
// src/engine/GanttEngine.ts
|
|
802
916
|
function mergeTaskPatch(task, patch) {
|
|
917
|
+
const type = patch.type ?? task.type;
|
|
918
|
+
const calendarId = patch.calendarId ?? task.calendarId ?? "standard";
|
|
919
|
+
const planStart = patch.planStart ?? task.plan.start;
|
|
920
|
+
const planEnd = type === "milestone" ? planStart : patch.planEnd ?? task.plan.end;
|
|
921
|
+
const duration = type === "milestone" ? 1 : plannedDurationBetween(planStart, planEnd, calendarId);
|
|
803
922
|
return {
|
|
804
923
|
...task,
|
|
805
924
|
...patch,
|
|
925
|
+
duration,
|
|
806
926
|
plan: {
|
|
807
927
|
...task.plan,
|
|
808
|
-
start:
|
|
809
|
-
end:
|
|
928
|
+
start: planStart,
|
|
929
|
+
end: planEnd,
|
|
810
930
|
progress: patch.progress ?? task.plan.progress
|
|
811
931
|
},
|
|
812
932
|
actual: {
|
|
@@ -1176,12 +1296,15 @@ function createGanttEngine(options = {}) {
|
|
|
1176
1296
|
RESOURCE_COLOR_PALETTE,
|
|
1177
1297
|
addDays,
|
|
1178
1298
|
addHours,
|
|
1299
|
+
addPlannedDuration,
|
|
1300
|
+
alignPlannedDate,
|
|
1179
1301
|
checkCyclicDependency,
|
|
1180
1302
|
computeHourScale,
|
|
1181
1303
|
computeImpact,
|
|
1182
1304
|
computeLayout,
|
|
1183
1305
|
computeResourceLayout,
|
|
1184
1306
|
computeTimeScale,
|
|
1307
|
+
computeWorkload,
|
|
1185
1308
|
createGanttEngine,
|
|
1186
1309
|
defaultConfig,
|
|
1187
1310
|
diffDays,
|
|
@@ -1194,6 +1317,7 @@ function createGanttEngine(options = {}) {
|
|
|
1194
1317
|
isValidDate,
|
|
1195
1318
|
mergeTaskPatch,
|
|
1196
1319
|
normalizeLinks,
|
|
1320
|
+
plannedDurationBetween,
|
|
1197
1321
|
resolveResourceColor,
|
|
1198
1322
|
resolveUsageColor,
|
|
1199
1323
|
resolveUsageTimes,
|
|
@@ -1201,6 +1325,7 @@ function createGanttEngine(options = {}) {
|
|
|
1201
1325
|
scheduleByDependencies,
|
|
1202
1326
|
shiftTask,
|
|
1203
1327
|
startOfDay,
|
|
1328
|
+
subtractPlannedDuration,
|
|
1204
1329
|
toDate,
|
|
1205
1330
|
toDateTime
|
|
1206
1331
|
});
|