ct-gantt-core 1.0.7 → 1.0.9

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 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: true,
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,82 @@ 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, 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.progress = row.capacity ? Math.round(row.workload / row.capacity * 100) : 0;
905
+ }
906
+ function aggregateDepartment(department, people, dates) {
907
+ 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) }));
908
+ const workload = days.reduce((sum, day) => sum + day.hours, 0);
909
+ const capacity = days.reduce((sum, day) => sum + day.capacity, 0);
910
+ return { department, people, days, workload, capacity, progress: capacity ? Math.round(workload / capacity * 100) : 0 };
911
+ }
912
+
801
913
  // src/engine/GanttEngine.ts
802
914
  function mergeTaskPatch(task, patch) {
915
+ const type = patch.type ?? task.type;
916
+ const calendarId = patch.calendarId ?? task.calendarId ?? "standard";
917
+ const planStart = patch.planStart ?? task.plan.start;
918
+ const planEnd = type === "milestone" ? planStart : patch.planEnd ?? task.plan.end;
919
+ const duration = type === "milestone" ? 1 : plannedDurationBetween(planStart, planEnd, calendarId);
803
920
  return {
804
921
  ...task,
805
922
  ...patch,
923
+ duration,
806
924
  plan: {
807
925
  ...task.plan,
808
- start: patch.planStart ?? task.plan.start,
809
- end: patch.planEnd ?? task.plan.end,
926
+ start: planStart,
927
+ end: planEnd,
810
928
  progress: patch.progress ?? task.plan.progress
811
929
  },
812
930
  actual: {
@@ -1176,12 +1294,15 @@ function createGanttEngine(options = {}) {
1176
1294
  RESOURCE_COLOR_PALETTE,
1177
1295
  addDays,
1178
1296
  addHours,
1297
+ addPlannedDuration,
1298
+ alignPlannedDate,
1179
1299
  checkCyclicDependency,
1180
1300
  computeHourScale,
1181
1301
  computeImpact,
1182
1302
  computeLayout,
1183
1303
  computeResourceLayout,
1184
1304
  computeTimeScale,
1305
+ computeWorkload,
1185
1306
  createGanttEngine,
1186
1307
  defaultConfig,
1187
1308
  diffDays,
@@ -1194,6 +1315,7 @@ function createGanttEngine(options = {}) {
1194
1315
  isValidDate,
1195
1316
  mergeTaskPatch,
1196
1317
  normalizeLinks,
1318
+ plannedDurationBetween,
1197
1319
  resolveResourceColor,
1198
1320
  resolveUsageColor,
1199
1321
  resolveUsageTimes,
@@ -1201,6 +1323,7 @@ function createGanttEngine(options = {}) {
1201
1323
  scheduleByDependencies,
1202
1324
  shiftTask,
1203
1325
  startOfDay,
1326
+ subtractPlannedDuration,
1204
1327
  toDate,
1205
1328
  toDateTime
1206
1329
  });