@outbuild-company/schedule-core 1.6.3 → 1.8.0
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/cdn/schedule-core.global.js +1 -1
- package/dist/cdn/schedule-core.global.js.map +1 -1
- package/dist/index.cjs +2429 -839
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -37
- package/dist/index.d.ts +100 -37
- package/dist/index.js +2429 -839
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -132,6 +132,9 @@ var REJECTION_REASON = {
|
|
|
132
132
|
ACTIVITY_NOT_FOUND: "activity_not_found",
|
|
133
133
|
PARENT_NOT_FOUND: "parent_not_found",
|
|
134
134
|
ACTIVITY_IDS_EMPTY: "activity_ids_empty",
|
|
135
|
+
NO_ACTIVITY_SELECTION: "no_activity_selection",
|
|
136
|
+
CLIPBOARD_EMPTY: "clipboard_empty",
|
|
137
|
+
ACTIVITY_PASTE_INVARIANT_FAILED: "activity_paste_invariant_failed",
|
|
135
138
|
ANCHOR_SIBLING_CONFLICT: "anchor_sibling_conflict",
|
|
136
139
|
ANCHOR_SIBLING_NOT_FOUND: "anchor_sibling_not_found",
|
|
137
140
|
ANCHOR_SIBLING_WRONG_PARENT: "anchor_sibling_wrong_parent",
|
|
@@ -139,6 +142,8 @@ var REJECTION_REASON = {
|
|
|
139
142
|
CANNOT_MOVE_INTO_OWN_DESCENDANT: "cannot_move_into_own_descendant",
|
|
140
143
|
INDENT_NO_ELIGIBLE_SIBLING: "indent_no_eligible_sibling",
|
|
141
144
|
OUTDENT_ROOT_LEVEL_NOT_EDITABLE: "outdent_root_level_not_editable",
|
|
145
|
+
HIERARCHY_CYCLE: "hierarchy_cycle",
|
|
146
|
+
SANDBOX_PERSISTENCE_FORBIDDEN: "sandbox_persistence_forbidden",
|
|
142
147
|
INVALID_PROGRESS_VALUE: "invalid_progress_value_must_be_0_or_100"
|
|
143
148
|
};
|
|
144
149
|
|
|
@@ -267,8 +272,17 @@ function toDays(hours, hoursPerDay) {
|
|
|
267
272
|
return hours / hoursPerDay;
|
|
268
273
|
}
|
|
269
274
|
function calendarDaySpan(startDate, endDate) {
|
|
270
|
-
const
|
|
271
|
-
|
|
275
|
+
const startDay = Date.UTC(
|
|
276
|
+
startDate.getUTCFullYear(),
|
|
277
|
+
startDate.getUTCMonth(),
|
|
278
|
+
startDate.getUTCDate()
|
|
279
|
+
);
|
|
280
|
+
const endDay = Date.UTC(
|
|
281
|
+
endDate.getUTCFullYear(),
|
|
282
|
+
endDate.getUTCMonth(),
|
|
283
|
+
endDate.getUTCDate()
|
|
284
|
+
);
|
|
285
|
+
return Math.floor((endDay - startDay) / MILLISECONDS_PER_DAY) + 1;
|
|
272
286
|
}
|
|
273
287
|
var FIELD_REGISTRY = {
|
|
274
288
|
name: { valueKind: "string", extract: (activity) => activity.name },
|
|
@@ -283,7 +297,10 @@ var FIELD_REGISTRY = {
|
|
|
283
297
|
},
|
|
284
298
|
uniqueCorrelativeId: {
|
|
285
299
|
valueKind: "number",
|
|
286
|
-
extract: (activity) =>
|
|
300
|
+
extract: (activity) => {
|
|
301
|
+
const value = Number(activity.uniqueCorrelativeId);
|
|
302
|
+
return Number.isFinite(value) ? value : null;
|
|
303
|
+
}
|
|
287
304
|
},
|
|
288
305
|
progress: { valueKind: "number", extract: (activity) => activity.progress },
|
|
289
306
|
durationDays: {
|
|
@@ -292,7 +309,7 @@ var FIELD_REGISTRY = {
|
|
|
292
309
|
},
|
|
293
310
|
calendarDuration: {
|
|
294
311
|
valueKind: "number",
|
|
295
|
-
extract: (activity) => calendarDaySpan(activity.startDate, activity.endDate)
|
|
312
|
+
extract: (activity) => activity.type === "milestone" ? 0 : calendarDaySpan(activity.startDate, activity.endDate)
|
|
296
313
|
},
|
|
297
314
|
cost: { valueKind: "number", extract: (activity) => activity.cost },
|
|
298
315
|
usedCost: { valueKind: "number", extract: (activity) => activity.usedCost },
|
|
@@ -658,34 +675,102 @@ var OPERATORS_BY_KIND = {
|
|
|
658
675
|
boolean: /* @__PURE__ */ new Set(["someOf", "notSomeOf"]),
|
|
659
676
|
reference: /* @__PURE__ */ new Set(["someOf", "notSomeOf"])
|
|
660
677
|
};
|
|
661
|
-
function
|
|
678
|
+
function normalizeDateRange(value) {
|
|
679
|
+
if (value === void 0) return { ok: true, dateRange: void 0 };
|
|
680
|
+
if (typeof value !== "object" || value === null) {
|
|
681
|
+
return { ok: false, reason: "invalid_value" };
|
|
682
|
+
}
|
|
683
|
+
const { start, end } = value;
|
|
684
|
+
if (!(start instanceof Date) || !(end instanceof Date)) {
|
|
685
|
+
return { ok: false, reason: "invalid_value" };
|
|
686
|
+
}
|
|
687
|
+
const startTime = start.getTime();
|
|
688
|
+
const endTime = end.getTime();
|
|
689
|
+
if (!Number.isFinite(startTime) || !Number.isFinite(endTime) || startTime > endTime) {
|
|
690
|
+
return { ok: false, reason: "invalid_value" };
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
ok: true,
|
|
694
|
+
dateRange: { start: new Date(startTime), end: new Date(endTime) }
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function normalizeCriterion(criterion) {
|
|
662
698
|
const descriptor = getFieldDescriptor(criterion.field);
|
|
663
|
-
if (descriptor === null) return "unknown_field";
|
|
699
|
+
if (descriptor === null) return { ok: false, reason: "unknown_field" };
|
|
664
700
|
if (!OPERATORS_BY_KIND[descriptor.valueKind].has(criterion.operator)) {
|
|
665
|
-
return "unknown_operator";
|
|
701
|
+
return { ok: false, reason: "unknown_operator" };
|
|
702
|
+
}
|
|
703
|
+
if (descriptor.valueKind === "number") {
|
|
704
|
+
const value = normalizeNumberValue(criterion.value);
|
|
705
|
+
return value === null ? { ok: false, reason: "invalid_value" } : {
|
|
706
|
+
ok: true,
|
|
707
|
+
criterion: { ...criterion, value }
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
if (descriptor.valueKind === "date") {
|
|
711
|
+
const value = normalizeDateValue(criterion.value);
|
|
712
|
+
return value === null ? { ok: false, reason: "invalid_value" } : {
|
|
713
|
+
ok: true,
|
|
714
|
+
criterion: { ...criterion, value }
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
const rejection = validateValueForKind(criterion, descriptor.valueKind);
|
|
718
|
+
if (rejection !== null) return { ok: false, reason: rejection };
|
|
719
|
+
if (descriptor.valueKind === "boolean") {
|
|
720
|
+
return {
|
|
721
|
+
ok: true,
|
|
722
|
+
criterion: {
|
|
723
|
+
...criterion,
|
|
724
|
+
value: [...new Set(criterion.value)]
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
if (descriptor.valueKind === "id-array" || descriptor.valueKind === "enum" || descriptor.valueKind === "reference") {
|
|
729
|
+
return {
|
|
730
|
+
ok: true,
|
|
731
|
+
criterion: {
|
|
732
|
+
...criterion,
|
|
733
|
+
value: [
|
|
734
|
+
...new Set(
|
|
735
|
+
criterion.value.map(String)
|
|
736
|
+
)
|
|
737
|
+
]
|
|
738
|
+
}
|
|
739
|
+
};
|
|
666
740
|
}
|
|
667
|
-
return
|
|
741
|
+
return { ok: true, criterion };
|
|
668
742
|
}
|
|
669
743
|
function validateValueForKind(criterion, valueKind) {
|
|
670
744
|
const { value } = criterion;
|
|
671
|
-
if (valueKind === "
|
|
672
|
-
return Array.isArray(value) ? null : "invalid_value";
|
|
673
|
-
}
|
|
674
|
-
if (Array.isArray(value)) return "invalid_value";
|
|
675
|
-
if (valueKind === "number") {
|
|
676
|
-
return Number.isFinite(Number(value)) ? null : "invalid_value";
|
|
745
|
+
if (valueKind === "boolean") {
|
|
746
|
+
return Array.isArray(value) && value.every((item) => typeof item === "boolean") ? null : "invalid_value";
|
|
677
747
|
}
|
|
678
|
-
if (valueKind === "
|
|
679
|
-
return
|
|
748
|
+
if (valueKind === "id-array" || valueKind === "enum" || valueKind === "reference") {
|
|
749
|
+
return Array.isArray(value) && value.every(
|
|
750
|
+
(item) => typeof item === "string" || typeof item === "number"
|
|
751
|
+
) ? null : "invalid_value";
|
|
680
752
|
}
|
|
753
|
+
if (Array.isArray(value)) return "invalid_value";
|
|
681
754
|
return null;
|
|
682
755
|
}
|
|
683
|
-
function
|
|
684
|
-
if (value
|
|
685
|
-
if (typeof value
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
756
|
+
function normalizeNumberValue(value) {
|
|
757
|
+
if (typeof value === "string" && value.trim() === "") return null;
|
|
758
|
+
if (typeof value !== "string" && typeof value !== "number") return null;
|
|
759
|
+
const parsed2 = typeof value === "number" ? value : Number(value);
|
|
760
|
+
return Number.isFinite(parsed2) ? parsed2 : null;
|
|
761
|
+
}
|
|
762
|
+
function normalizeDateValue(value) {
|
|
763
|
+
let date2;
|
|
764
|
+
if (value instanceof Date) date2 = new Date(value.getTime());
|
|
765
|
+
else if (typeof value === "string") date2 = new Date(value);
|
|
766
|
+
else if (typeof value === "number") date2 = new Date(value);
|
|
767
|
+
else return null;
|
|
768
|
+
return Number.isFinite(date2.getTime()) ? date2 : null;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// src/shared/clone-domain-value.ts
|
|
772
|
+
function cloneDomainValue(value) {
|
|
773
|
+
return structuredClone(value);
|
|
689
774
|
}
|
|
690
775
|
|
|
691
776
|
// src/dispatch/filter.ts
|
|
@@ -715,15 +800,21 @@ function resolveFilter(adapter, filter, hoursPerDay) {
|
|
|
715
800
|
}
|
|
716
801
|
function dispatchFilterSet(action, deps) {
|
|
717
802
|
const { adapter, hoursPerDay } = deps;
|
|
803
|
+
const criteria = [];
|
|
718
804
|
for (const criterion of action.criteria) {
|
|
719
|
-
const
|
|
720
|
-
if (
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
805
|
+
const result = normalizeCriterion(criterion);
|
|
806
|
+
if (!result.ok) return { ok: false, reason: result.reason };
|
|
807
|
+
criteria.push(result.criterion);
|
|
808
|
+
}
|
|
809
|
+
const rangeResult = normalizeDateRange(action.dateRange);
|
|
810
|
+
if (!rangeResult.ok) return { ok: false, reason: rangeResult.reason };
|
|
811
|
+
const filter = cloneDomainValue(
|
|
812
|
+
rangeResult.dateRange === void 0 ? { criteria, logic: action.logic } : {
|
|
813
|
+
criteria,
|
|
814
|
+
logic: action.logic,
|
|
815
|
+
dateRange: rangeResult.dateRange
|
|
816
|
+
}
|
|
817
|
+
);
|
|
727
818
|
adapter.setActiveFilter(isEmptyFilter(filter) ? null : filter);
|
|
728
819
|
const { visibleIds, projection } = resolveFilter(
|
|
729
820
|
adapter,
|
|
@@ -868,7 +959,7 @@ function dispatchSortSet(action, deps) {
|
|
|
868
959
|
const rejection = validateRule(rule);
|
|
869
960
|
if (rejection !== null) return { ok: false, reason: rejection };
|
|
870
961
|
}
|
|
871
|
-
const order = action.rules.length === 0 ? null : { rules: action.rules };
|
|
962
|
+
const order = action.rules.length === 0 ? null : cloneDomainValue({ rules: action.rules });
|
|
872
963
|
adapter.setActiveOrder(order);
|
|
873
964
|
const changes = {
|
|
874
965
|
source: action,
|
|
@@ -1436,6 +1527,11 @@ function isDescendantOf(childId, ancestorId, adapter) {
|
|
|
1436
1527
|
}
|
|
1437
1528
|
return false;
|
|
1438
1529
|
}
|
|
1530
|
+
function isSummaryHierarchyLink(source, target, adapter) {
|
|
1531
|
+
const sourceIsSummary = isSummary2(source, adapter);
|
|
1532
|
+
const targetIsSummary = isSummary2(target, adapter);
|
|
1533
|
+
return targetIsSummary && isDescendantOf(source, target, adapter) || sourceIsSummary && isDescendantOf(target, source, adapter);
|
|
1534
|
+
}
|
|
1439
1535
|
function getLeafDescendants(parentId, adapter) {
|
|
1440
1536
|
const leaves = [];
|
|
1441
1537
|
const stack = [parentId];
|
|
@@ -1694,6 +1790,8 @@ function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag,
|
|
|
1694
1790
|
target: targetId,
|
|
1695
1791
|
type: originalLink.type,
|
|
1696
1792
|
lag: originalLink.lag,
|
|
1793
|
+
freeSlackHours: null,
|
|
1794
|
+
isCritical: false,
|
|
1697
1795
|
_sourceLag: sourceLag,
|
|
1698
1796
|
_targetLag: targetLag,
|
|
1699
1797
|
_trueLag: trueLag,
|
|
@@ -2373,6 +2471,18 @@ var noopReporter = {
|
|
|
2373
2471
|
}
|
|
2374
2472
|
};
|
|
2375
2473
|
|
|
2474
|
+
// src/internal/state/calendar-duration.ts
|
|
2475
|
+
var CALENDAR_DAY_MS = 864e5;
|
|
2476
|
+
function computeCalendarDuration(activity) {
|
|
2477
|
+
if (activity.type === "milestone") return 0;
|
|
2478
|
+
const start = activity.startDate?.getTime();
|
|
2479
|
+
const end = activity.endDate?.getTime();
|
|
2480
|
+
if (start == null || end == null || Number.isNaN(start) || Number.isNaN(end)) {
|
|
2481
|
+
return null;
|
|
2482
|
+
}
|
|
2483
|
+
return Math.floor((end - start) / CALENDAR_DAY_MS) + 1;
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2376
2486
|
// src/autoscheduler/integration/apply-results.ts
|
|
2377
2487
|
function applyResults(adapter, result) {
|
|
2378
2488
|
if (result.cancelled || result.updatedIds.length === 0) return;
|
|
@@ -2384,6 +2494,7 @@ function applyResults(adapter, result) {
|
|
|
2384
2494
|
if (!liveActivity) continue;
|
|
2385
2495
|
liveActivity.startDate = plan.startDate;
|
|
2386
2496
|
liveActivity.endDate = plan.endDate;
|
|
2497
|
+
liveActivity.calendarDuration = computeCalendarDuration(liveActivity);
|
|
2387
2498
|
adapter.updateActivity(id);
|
|
2388
2499
|
}
|
|
2389
2500
|
});
|
|
@@ -2450,13 +2561,253 @@ function hasPath(adjacency, from, to) {
|
|
|
2450
2561
|
return false;
|
|
2451
2562
|
}
|
|
2452
2563
|
|
|
2564
|
+
// src/internal/dependency-graph/strongly-connected-components.ts
|
|
2565
|
+
function circularComponents(edges) {
|
|
2566
|
+
const walk = buildWalkState(edges);
|
|
2567
|
+
for (const node of walk.adjacency.keys()) {
|
|
2568
|
+
if (!walk.records.has(node)) collectFromRoot(node, walk);
|
|
2569
|
+
}
|
|
2570
|
+
return walk.components;
|
|
2571
|
+
}
|
|
2572
|
+
function buildWalkState(edges) {
|
|
2573
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
2574
|
+
const selfLooped = /* @__PURE__ */ new Set();
|
|
2575
|
+
for (const edge of edges) {
|
|
2576
|
+
const source = String(edge.source);
|
|
2577
|
+
const target = String(edge.target);
|
|
2578
|
+
const sourceNeighbors = neighborsOf(adjacency, source);
|
|
2579
|
+
neighborsOf(adjacency, target);
|
|
2580
|
+
if (source === target) {
|
|
2581
|
+
selfLooped.add(source);
|
|
2582
|
+
continue;
|
|
2583
|
+
}
|
|
2584
|
+
sourceNeighbors.push(target);
|
|
2585
|
+
}
|
|
2586
|
+
return {
|
|
2587
|
+
adjacency,
|
|
2588
|
+
selfLooped,
|
|
2589
|
+
records: /* @__PURE__ */ new Map(),
|
|
2590
|
+
componentStack: [],
|
|
2591
|
+
nextIndex: 0,
|
|
2592
|
+
components: []
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
function neighborsOf(adjacency, node) {
|
|
2596
|
+
const existing = adjacency.get(node);
|
|
2597
|
+
if (existing) return existing;
|
|
2598
|
+
const created = [];
|
|
2599
|
+
adjacency.set(node, created);
|
|
2600
|
+
return created;
|
|
2601
|
+
}
|
|
2602
|
+
function collectFromRoot(root, walk) {
|
|
2603
|
+
const frames = [];
|
|
2604
|
+
frames.push({ node: root, record: enterNode(root, walk), neighborCursor: 0 });
|
|
2605
|
+
while (frames.length > 0) {
|
|
2606
|
+
const frame = frames[frames.length - 1];
|
|
2607
|
+
if (frame === void 0) break;
|
|
2608
|
+
const neighbors = walk.adjacency.get(frame.node) ?? [];
|
|
2609
|
+
if (frame.neighborCursor < neighbors.length) {
|
|
2610
|
+
const neighbor = neighbors[frame.neighborCursor];
|
|
2611
|
+
frame.neighborCursor += 1;
|
|
2612
|
+
if (neighbor === void 0) continue;
|
|
2613
|
+
visitNeighbor(frame, neighbor, frames, walk);
|
|
2614
|
+
continue;
|
|
2615
|
+
}
|
|
2616
|
+
frames.pop();
|
|
2617
|
+
const parentFrame = frames[frames.length - 1];
|
|
2618
|
+
if (parentFrame && frame.record.lowLink < parentFrame.record.lowLink) {
|
|
2619
|
+
parentFrame.record.lowLink = frame.record.lowLink;
|
|
2620
|
+
}
|
|
2621
|
+
const isComponentRoot = frame.record.lowLink === frame.record.visitIndex;
|
|
2622
|
+
if (isComponentRoot) emitComponent(frame.node, walk);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
function visitNeighbor(frame, neighbor, frames, walk) {
|
|
2626
|
+
const neighborRecord = walk.records.get(neighbor);
|
|
2627
|
+
if (!neighborRecord) {
|
|
2628
|
+
frames.push({
|
|
2629
|
+
node: neighbor,
|
|
2630
|
+
record: enterNode(neighbor, walk),
|
|
2631
|
+
neighborCursor: 0
|
|
2632
|
+
});
|
|
2633
|
+
return;
|
|
2634
|
+
}
|
|
2635
|
+
const lowersCurrent = neighborRecord.onStack && neighborRecord.visitIndex < frame.record.lowLink;
|
|
2636
|
+
if (lowersCurrent) frame.record.lowLink = neighborRecord.visitIndex;
|
|
2637
|
+
}
|
|
2638
|
+
function enterNode(node, walk) {
|
|
2639
|
+
const record = {
|
|
2640
|
+
visitIndex: walk.nextIndex,
|
|
2641
|
+
lowLink: walk.nextIndex,
|
|
2642
|
+
onStack: true
|
|
2643
|
+
};
|
|
2644
|
+
walk.nextIndex += 1;
|
|
2645
|
+
walk.records.set(node, record);
|
|
2646
|
+
walk.componentStack.push({ node, record });
|
|
2647
|
+
return record;
|
|
2648
|
+
}
|
|
2649
|
+
function emitComponent(rootNode, walk) {
|
|
2650
|
+
const component = [];
|
|
2651
|
+
for (; ; ) {
|
|
2652
|
+
const entry = walk.componentStack.pop();
|
|
2653
|
+
if (entry === void 0) break;
|
|
2654
|
+
entry.record.onStack = false;
|
|
2655
|
+
component.push(entry.node);
|
|
2656
|
+
if (entry.node === rootNode) break;
|
|
2657
|
+
}
|
|
2658
|
+
const firstMember = component[0];
|
|
2659
|
+
const isSingleSelfLoop = component.length === 1 && firstMember !== void 0 && walk.selfLooped.has(firstMember);
|
|
2660
|
+
const isCircular = component.length > 1 || isSingleSelfLoop;
|
|
2661
|
+
if (isCircular) walk.components.push(component);
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
// src/internal/dependency-graph/effective-graph.ts
|
|
2665
|
+
function projectEffectiveEdges(port, links, proposal) {
|
|
2666
|
+
const projectionPort = proposal ? new ReparentedPort(port, proposal) : port;
|
|
2667
|
+
return expandParentLinks([...links], projectionPort);
|
|
2668
|
+
}
|
|
2669
|
+
var ReparentedPort = class {
|
|
2670
|
+
constructor(base, proposal) {
|
|
2671
|
+
this.base = base;
|
|
2672
|
+
for (const [childId, proposedParent] of proposal.reparented) {
|
|
2673
|
+
const childKey = String(childId);
|
|
2674
|
+
const newParent = proposedParent === null || isRootParent(proposedParent) ? null : proposedParent;
|
|
2675
|
+
const oldParent = base.getActivity(childId)?.parentId ?? null;
|
|
2676
|
+
const oldKey = oldParent === null || isRootParent(oldParent) ? null : String(oldParent);
|
|
2677
|
+
const newKey = newParent === null ? null : String(newParent);
|
|
2678
|
+
if (oldKey === newKey) continue;
|
|
2679
|
+
this.reparented.set(childKey, newParent);
|
|
2680
|
+
if (oldKey !== null) {
|
|
2681
|
+
const removed = this.movedOutByParent.get(oldKey) ?? /* @__PURE__ */ new Set();
|
|
2682
|
+
removed.add(childKey);
|
|
2683
|
+
this.movedOutByParent.set(oldKey, removed);
|
|
2684
|
+
}
|
|
2685
|
+
if (newKey !== null) {
|
|
2686
|
+
const gained = this.movedInByParent.get(newKey) ?? [];
|
|
2687
|
+
gained.push(childId);
|
|
2688
|
+
this.movedInByParent.set(newKey, gained);
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
base;
|
|
2693
|
+
reparented = /* @__PURE__ */ new Map();
|
|
2694
|
+
movedOutByParent = /* @__PURE__ */ new Map();
|
|
2695
|
+
movedInByParent = /* @__PURE__ */ new Map();
|
|
2696
|
+
withOverride(activity) {
|
|
2697
|
+
if (!activity) return null;
|
|
2698
|
+
const override = this.reparented.get(String(activity.id));
|
|
2699
|
+
if (override === void 0) return activity;
|
|
2700
|
+
return { ...activity, parentId: override };
|
|
2701
|
+
}
|
|
2702
|
+
getActivity(activityId) {
|
|
2703
|
+
return this.withOverride(this.base.getActivity(activityId));
|
|
2704
|
+
}
|
|
2705
|
+
getLiveActivity(activityId) {
|
|
2706
|
+
return this.withOverride(this.base.getLiveActivity(activityId));
|
|
2707
|
+
}
|
|
2708
|
+
getAllActivities() {
|
|
2709
|
+
return this.base.getAllActivities().map((activity) => this.withOverride(activity) ?? activity);
|
|
2710
|
+
}
|
|
2711
|
+
getChildren(parentId) {
|
|
2712
|
+
const parentKey = String(parentId);
|
|
2713
|
+
const removed = this.movedOutByParent.get(parentKey);
|
|
2714
|
+
const baseChildren = this.base.getChildren(parentId);
|
|
2715
|
+
const kept = removed ? baseChildren.filter((childId) => !removed.has(String(childId))) : [...baseChildren];
|
|
2716
|
+
const gained = this.movedInByParent.get(parentKey);
|
|
2717
|
+
if (gained) kept.push(...gained);
|
|
2718
|
+
return kept;
|
|
2719
|
+
}
|
|
2720
|
+
getParent(activityId) {
|
|
2721
|
+
const override = this.reparented.get(String(activityId));
|
|
2722
|
+
if (override === void 0) return this.base.getParent(activityId);
|
|
2723
|
+
if (override === null) return null;
|
|
2724
|
+
return this.getActivity(override);
|
|
2725
|
+
}
|
|
2726
|
+
isChildOf(childId, parentId) {
|
|
2727
|
+
const targetKey = String(parentId);
|
|
2728
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2729
|
+
let current = this.getActivity(childId);
|
|
2730
|
+
while (current) {
|
|
2731
|
+
const parent = current.parentId;
|
|
2732
|
+
if (parent === null || isRootParent(parent)) return false;
|
|
2733
|
+
const parentKey = String(parent);
|
|
2734
|
+
if (parentKey === targetKey) return true;
|
|
2735
|
+
if (visited.has(parentKey)) return false;
|
|
2736
|
+
visited.add(parentKey);
|
|
2737
|
+
current = this.getActivity(parent);
|
|
2738
|
+
}
|
|
2739
|
+
return false;
|
|
2740
|
+
}
|
|
2741
|
+
getAllLinks() {
|
|
2742
|
+
return this.base.getAllLinks();
|
|
2743
|
+
}
|
|
2744
|
+
getAllIds() {
|
|
2745
|
+
return this.base.getAllIds();
|
|
2746
|
+
}
|
|
2747
|
+
getLink(linkId) {
|
|
2748
|
+
return this.base.getLink(linkId);
|
|
2749
|
+
}
|
|
2750
|
+
activityExists(activityId) {
|
|
2751
|
+
return this.base.activityExists(activityId);
|
|
2752
|
+
}
|
|
2753
|
+
getOutgoingLinkIds(activityId) {
|
|
2754
|
+
return this.base.getOutgoingLinkIds(activityId);
|
|
2755
|
+
}
|
|
2756
|
+
getIncomingLinkIds(activityId) {
|
|
2757
|
+
return this.base.getIncomingLinkIds(activityId);
|
|
2758
|
+
}
|
|
2759
|
+
getClosestWorkTime(params) {
|
|
2760
|
+
return this.base.getClosestWorkTime(params);
|
|
2761
|
+
}
|
|
2762
|
+
calculateEndDate(params) {
|
|
2763
|
+
return this.base.calculateEndDate(params);
|
|
2764
|
+
}
|
|
2765
|
+
calculateDuration(params) {
|
|
2766
|
+
return this.base.calculateDuration(params);
|
|
2767
|
+
}
|
|
2768
|
+
batchUpdate(runBatch) {
|
|
2769
|
+
this.base.batchUpdate(runBatch);
|
|
2770
|
+
}
|
|
2771
|
+
updateActivity(activityId) {
|
|
2772
|
+
this.base.updateActivity(activityId);
|
|
2773
|
+
}
|
|
2774
|
+
getFlags() {
|
|
2775
|
+
return this.base.getFlags();
|
|
2776
|
+
}
|
|
2777
|
+
getProjectEnd() {
|
|
2778
|
+
return this.base.getProjectEnd();
|
|
2779
|
+
}
|
|
2780
|
+
};
|
|
2781
|
+
|
|
2782
|
+
// src/internal/dependency-graph/effective-graph-validation.ts
|
|
2783
|
+
function validateTopologyCommit(input) {
|
|
2784
|
+
const { port, currentLinks, proposedLinks, proposal } = input;
|
|
2785
|
+
const proposedEdges = projectEffectiveEdges(port, proposedLinks, proposal);
|
|
2786
|
+
if (!hasDirectedCycle(proposedEdges)) return { ok: true };
|
|
2787
|
+
const currentEdges = projectEffectiveEdges(port, currentLinks);
|
|
2788
|
+
const corruptBefore = corruptActivityIds(currentEdges);
|
|
2789
|
+
const corruptAfter = corruptActivityIds(proposedEdges);
|
|
2790
|
+
const newlyCyclicActivityIds = [...corruptAfter].filter(
|
|
2791
|
+
(activityId) => !corruptBefore.has(activityId)
|
|
2792
|
+
);
|
|
2793
|
+
if (newlyCyclicActivityIds.length === 0) return { ok: true };
|
|
2794
|
+
return { ok: false, newlyCyclicActivityIds };
|
|
2795
|
+
}
|
|
2796
|
+
function corruptActivityIds(edges) {
|
|
2797
|
+
const corrupt = /* @__PURE__ */ new Set();
|
|
2798
|
+
for (const component of circularComponents(edges)) {
|
|
2799
|
+
for (const activityId of component) corrupt.add(activityId);
|
|
2800
|
+
}
|
|
2801
|
+
return corrupt;
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2453
2804
|
// src/autoscheduler/links/apply-link-operation.ts
|
|
2454
2805
|
function applyLinkOperation(op, deps) {
|
|
2455
2806
|
switch (op.kind) {
|
|
2456
2807
|
case "create":
|
|
2457
2808
|
return applyCreate(op, deps);
|
|
2458
2809
|
case "update":
|
|
2459
|
-
return
|
|
2810
|
+
return applyLinkUpdate(op, deps.port);
|
|
2460
2811
|
case "delete":
|
|
2461
2812
|
return applyDelete(op, deps);
|
|
2462
2813
|
}
|
|
@@ -2482,6 +2833,10 @@ function applyLinkCreates(links, deps) {
|
|
|
2482
2833
|
rejected = { applied: false, rejected: "activity-missing" };
|
|
2483
2834
|
break;
|
|
2484
2835
|
}
|
|
2836
|
+
if (isSummaryHierarchyLink(operation.source, operation.target, deps.port)) {
|
|
2837
|
+
rejected = { applied: false, rejected: "hierarchy-link" };
|
|
2838
|
+
break;
|
|
2839
|
+
}
|
|
2485
2840
|
const key = linkKey(operation);
|
|
2486
2841
|
const duplicateId = duplicateKeys.get(key);
|
|
2487
2842
|
if (duplicateId !== void 0) {
|
|
@@ -2507,6 +2862,26 @@ function applyLinkCreates(links, deps) {
|
|
|
2507
2862
|
return { applied: false, rejected: "cycle" };
|
|
2508
2863
|
}
|
|
2509
2864
|
if (rejected) return rejected;
|
|
2865
|
+
const proposedLinks = [
|
|
2866
|
+
...existingLinks,
|
|
2867
|
+
...links.map(({ operation, linkId }) => ({
|
|
2868
|
+
id: linkId,
|
|
2869
|
+
source: operation.source,
|
|
2870
|
+
target: operation.target,
|
|
2871
|
+
type: operation.type,
|
|
2872
|
+
lag: operation.lag,
|
|
2873
|
+
freeSlackHours: null,
|
|
2874
|
+
isCritical: false
|
|
2875
|
+
}))
|
|
2876
|
+
];
|
|
2877
|
+
const effectiveVerdict = validateTopologyCommit({
|
|
2878
|
+
port: deps.port,
|
|
2879
|
+
currentLinks: existingLinks,
|
|
2880
|
+
proposedLinks
|
|
2881
|
+
});
|
|
2882
|
+
if (!effectiveVerdict.ok) {
|
|
2883
|
+
return { applied: false, rejected: "cycle" };
|
|
2884
|
+
}
|
|
2510
2885
|
for (const { operation, linkId } of links) {
|
|
2511
2886
|
const preserved = deps.preserved?.get(linkId);
|
|
2512
2887
|
deps.port.addLink({
|
|
@@ -2515,6 +2890,8 @@ function applyLinkCreates(links, deps) {
|
|
|
2515
2890
|
target: operation.target,
|
|
2516
2891
|
type: operation.type,
|
|
2517
2892
|
lag: operation.lag,
|
|
2893
|
+
freeSlackHours: null,
|
|
2894
|
+
isCritical: false,
|
|
2518
2895
|
...preserved?.ganttId === void 0 ? {} : { ganttId: preserved.ganttId },
|
|
2519
2896
|
...preserved?.sectorId === void 0 ? {} : { sectorId: preserved.sectorId },
|
|
2520
2897
|
...preserved?.proplannerId === void 0 ? {} : { proplannerId: preserved.proplannerId }
|
|
@@ -2535,6 +2912,9 @@ function applyCreate(op, deps) {
|
|
|
2535
2912
|
if (!src || !tgt) {
|
|
2536
2913
|
return { applied: false, rejected: "activity-missing" };
|
|
2537
2914
|
}
|
|
2915
|
+
if (isSummaryHierarchyLink(op.source, op.target, port)) {
|
|
2916
|
+
return { applied: false, rejected: "hierarchy-link" };
|
|
2917
|
+
}
|
|
2538
2918
|
const existingLinks = port.getAllLinks();
|
|
2539
2919
|
for (const l of existingLinks) {
|
|
2540
2920
|
if (String(l.source) === String(op.source) && String(l.target) === String(op.target) && l.type === op.type) {
|
|
@@ -2554,13 +2934,22 @@ function applyCreate(op, deps) {
|
|
|
2554
2934
|
source: op.source,
|
|
2555
2935
|
target: op.target,
|
|
2556
2936
|
type: op.type,
|
|
2557
|
-
lag: op.lag
|
|
2937
|
+
lag: op.lag,
|
|
2938
|
+
freeSlackHours: null,
|
|
2939
|
+
isCritical: false
|
|
2558
2940
|
};
|
|
2941
|
+
const effectiveVerdict = validateTopologyCommit({
|
|
2942
|
+
port,
|
|
2943
|
+
currentLinks: existingLinks,
|
|
2944
|
+
proposedLinks: [...existingLinks, link]
|
|
2945
|
+
});
|
|
2946
|
+
if (!effectiveVerdict.ok) {
|
|
2947
|
+
return { applied: false, rejected: "cycle" };
|
|
2948
|
+
}
|
|
2559
2949
|
port.addLink(link);
|
|
2560
2950
|
return { applied: true, linkId: id };
|
|
2561
2951
|
}
|
|
2562
|
-
function
|
|
2563
|
-
const { port } = deps;
|
|
2952
|
+
function applyLinkUpdate(op, port) {
|
|
2564
2953
|
const link = port.getLink(op.linkId);
|
|
2565
2954
|
if (!link) return { applied: false, rejected: "link-missing" };
|
|
2566
2955
|
if (op.lag !== void 0) port.setLinkField(op.linkId, "lag", op.lag);
|
|
@@ -2731,6 +3120,10 @@ function diffOutgoingLinks(input) {
|
|
|
2731
3120
|
}
|
|
2732
3121
|
|
|
2733
3122
|
// src/autoscheduler/links/lag-units.ts
|
|
3123
|
+
function roundLagDays(lagDays) {
|
|
3124
|
+
const rounded = Math.sign(lagDays) * Math.round(Math.abs(lagDays));
|
|
3125
|
+
return Object.is(rounded, -0) ? 0 : rounded;
|
|
3126
|
+
}
|
|
2734
3127
|
function lagDaysToHours(lagDays, hoursPerDay) {
|
|
2735
3128
|
return Math.sign(lagDays) * Math.round(Math.abs(lagDays) * hoursPerDay);
|
|
2736
3129
|
}
|
|
@@ -2993,10 +3386,7 @@ function adjustLinkLagOnTaskMove(targetId, adapter, preEditTarget) {
|
|
|
2993
3386
|
calendarApi
|
|
2994
3387
|
);
|
|
2995
3388
|
if (newLag === link.lag) continue;
|
|
2996
|
-
|
|
2997
|
-
{ kind: "update", linkId: link.id, lag: newLag },
|
|
2998
|
-
{ port: adapter }
|
|
2999
|
-
);
|
|
3389
|
+
applyLinkUpdate({ linkId: link.id, lag: newLag }, adapter);
|
|
3000
3390
|
}
|
|
3001
3391
|
}
|
|
3002
3392
|
|
|
@@ -3060,19 +3450,44 @@ async function recomputeParentsCooperatively(parents, adapter, progressMode, opt
|
|
|
3060
3450
|
}
|
|
3061
3451
|
function findAncestorsOfDirty(adapter, dirtyIds) {
|
|
3062
3452
|
const ancestors = /* @__PURE__ */ new Set();
|
|
3063
|
-
|
|
3453
|
+
const parentById = /* @__PURE__ */ new Map();
|
|
3454
|
+
for (const id of dirtyIds) {
|
|
3455
|
+
walkAncestors(id, adapter, ancestors, parentById);
|
|
3456
|
+
}
|
|
3064
3457
|
if (ancestors.size === 0) return [];
|
|
3065
3458
|
const ids = Array.from(ancestors);
|
|
3066
|
-
const depths =
|
|
3459
|
+
const depths = computeKnownDepths(ids, parentById);
|
|
3067
3460
|
return sortDeepestFirst(ids, depths);
|
|
3068
3461
|
}
|
|
3069
|
-
function walkAncestors(id, adapter, collector) {
|
|
3462
|
+
function walkAncestors(id, adapter, collector, parentById) {
|
|
3070
3463
|
let current = adapter.getParentId(id);
|
|
3071
3464
|
while (current && !isRootParent(current)) {
|
|
3072
3465
|
if (collector.has(current)) return;
|
|
3073
3466
|
collector.add(current);
|
|
3074
|
-
|
|
3467
|
+
const parent = adapter.getParentId(current);
|
|
3468
|
+
parentById.set(current, parent);
|
|
3469
|
+
current = parent;
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
function computeKnownDepths(ids, parentById) {
|
|
3473
|
+
const depths = /* @__PURE__ */ new Map();
|
|
3474
|
+
for (const id of ids) {
|
|
3475
|
+
if (depths.has(id)) continue;
|
|
3476
|
+
const path = [];
|
|
3477
|
+
const inPath = /* @__PURE__ */ new Set();
|
|
3478
|
+
let current = id;
|
|
3479
|
+
while (current && !isRootParent(current) && !depths.has(current) && !inPath.has(current)) {
|
|
3480
|
+
path.push(current);
|
|
3481
|
+
inPath.add(current);
|
|
3482
|
+
current = parentById.get(current) ?? null;
|
|
3483
|
+
}
|
|
3484
|
+
let depth = current ? depths.get(current) ?? 0 : 0;
|
|
3485
|
+
for (let index = path.length - 1; index >= 0; index -= 1) {
|
|
3486
|
+
depth += 1;
|
|
3487
|
+
depths.set(path[index], depth);
|
|
3488
|
+
}
|
|
3075
3489
|
}
|
|
3490
|
+
return depths;
|
|
3076
3491
|
}
|
|
3077
3492
|
function aggregateChildren(childrenIds, adapter, includeRealWork) {
|
|
3078
3493
|
let minStart = null;
|
|
@@ -3303,7 +3718,7 @@ var setEndDate = (value) => ({
|
|
|
3303
3718
|
|
|
3304
3719
|
// src/columns/shared/date-normalize.ts
|
|
3305
3720
|
function truncateToUtcDay(date2, tz) {
|
|
3306
|
-
|
|
3721
|
+
{
|
|
3307
3722
|
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
3308
3723
|
timeZone: tz,
|
|
3309
3724
|
year: "numeric",
|
|
@@ -3313,9 +3728,6 @@ function truncateToUtcDay(date2, tz) {
|
|
|
3313
3728
|
const get = (kind) => Number(parts.find((p) => p.type === kind)?.value);
|
|
3314
3729
|
return new Date(Date.UTC(get("year"), get("month") - 1, get("day")));
|
|
3315
3730
|
}
|
|
3316
|
-
return new Date(
|
|
3317
|
-
Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate())
|
|
3318
|
-
);
|
|
3319
3731
|
}
|
|
3320
3732
|
function endOfLocalDay(date2, tz) {
|
|
3321
3733
|
{
|
|
@@ -3325,11 +3737,11 @@ function endOfLocalDay(date2, tz) {
|
|
|
3325
3737
|
}
|
|
3326
3738
|
}
|
|
3327
3739
|
function nextWorkingDay(calendar, domainDay) {
|
|
3328
|
-
let cursor = truncateToUtcDay(domainDay);
|
|
3740
|
+
let cursor = truncateToUtcDay(domainDay, "UTC");
|
|
3329
3741
|
while (!calendar.isWorkTime(cursor, CALENDAR_UNIT.DAY)) {
|
|
3330
3742
|
cursor = new Date(cursor);
|
|
3331
3743
|
cursor.setUTCDate(cursor.getUTCDate() + 1);
|
|
3332
|
-
cursor = truncateToUtcDay(cursor);
|
|
3744
|
+
cursor = truncateToUtcDay(cursor, "UTC");
|
|
3333
3745
|
}
|
|
3334
3746
|
return cursor;
|
|
3335
3747
|
}
|
|
@@ -3367,11 +3779,12 @@ var DATELESS_CONSTRAINTS = /* @__PURE__ */ new Set([
|
|
|
3367
3779
|
]);
|
|
3368
3780
|
var IMPLIED_CONSTRAINT_ON_DATE_EDIT = "snet";
|
|
3369
3781
|
function normalizeConstraintDate(typedDay, calendar, constraintType) {
|
|
3782
|
+
const constraintDay = truncateToUtcDay(typedDay, "UTC");
|
|
3370
3783
|
if (!START_BASED_CONSTRAINTS2.has(constraintType) && !END_BASED_CONSTRAINTS.has(constraintType)) {
|
|
3371
|
-
return
|
|
3784
|
+
return constraintDay;
|
|
3372
3785
|
}
|
|
3373
|
-
const reference = START_BASED_CONSTRAINTS2.has(constraintType) ? shiftStart(calendar,
|
|
3374
|
-
const result = new Date(
|
|
3786
|
+
const reference = START_BASED_CONSTRAINTS2.has(constraintType) ? shiftStart(calendar, constraintDay) : shiftEnd(calendar, constraintDay);
|
|
3787
|
+
const result = new Date(constraintDay);
|
|
3375
3788
|
result.setUTCHours(
|
|
3376
3789
|
reference.getUTCHours(),
|
|
3377
3790
|
reference.getUTCMinutes(),
|
|
@@ -3427,10 +3840,8 @@ var durationPipeline = {
|
|
|
3427
3840
|
},
|
|
3428
3841
|
parseInput(rawValue, _activity, ctx) {
|
|
3429
3842
|
const numericValue = parseFloat(String(rawValue));
|
|
3430
|
-
if (Number.
|
|
3431
|
-
return parsed(
|
|
3432
|
-
ctx.inputUnit === "hours" ? numericValue : durationToHours(numericValue, ctx.hoursPerDay)
|
|
3433
|
-
);
|
|
3843
|
+
if (!Number.isFinite(numericValue)) return parseError("not_a_number");
|
|
3844
|
+
return parsed(durationToHours(numericValue, ctx.hoursPerDay));
|
|
3434
3845
|
},
|
|
3435
3846
|
validate(_activity, oldValue, newValue) {
|
|
3436
3847
|
if (isNegativeDuration(newValue)) {
|
|
@@ -3879,17 +4290,7 @@ var startDatePipeline = {
|
|
|
3879
4290
|
transform(activity, newValue, ctx) {
|
|
3880
4291
|
const calendar = ctx.calendars.getCalendar(activity.calendarId);
|
|
3881
4292
|
if (!calendar) {
|
|
3882
|
-
|
|
3883
|
-
{
|
|
3884
|
-
startDate: newValue,
|
|
3885
|
-
...setConstraintTypeImplied(),
|
|
3886
|
-
...setConstraintDate(newValue)
|
|
3887
|
-
},
|
|
3888
|
-
{
|
|
3889
|
-
autoSchedule: true,
|
|
3890
|
-
postProcessors: ["adjustLinkLagOnTaskMove"]
|
|
3891
|
-
}
|
|
3892
|
-
);
|
|
4293
|
+
throw new Error("startDatePipeline: no calendar resolved (integrity)");
|
|
3893
4294
|
}
|
|
3894
4295
|
const finalStart = shiftStart(calendar, newValue);
|
|
3895
4296
|
const milestoneActivity = activity.type === "milestone";
|
|
@@ -3919,7 +4320,7 @@ function checkParentStartConstraint(activity, newDate, ctx) {
|
|
|
3919
4320
|
activity.id
|
|
3920
4321
|
);
|
|
3921
4322
|
if (!parentStartDate) return null;
|
|
3922
|
-
const parentDay = truncateToUtcDay(parentStartDate);
|
|
4323
|
+
const parentDay = truncateToUtcDay(parentStartDate, "UTC");
|
|
3923
4324
|
if (newDate.getTime() < parentDay.getTime()) {
|
|
3924
4325
|
return invalid("before_parent_start");
|
|
3925
4326
|
}
|
|
@@ -3967,7 +4368,6 @@ var endDatePipeline = {
|
|
|
3967
4368
|
const calendar = ctx.calendars.getCalendar(activity.calendarId);
|
|
3968
4369
|
if (!calendar) return parsed(day);
|
|
3969
4370
|
if (!calendar.isWorkTime(day, CALENDAR_UNIT.DAY)) {
|
|
3970
|
-
if (ctx.dateFormat.split(" ").length > 1) return parsed(day);
|
|
3971
4371
|
return parsed(
|
|
3972
4372
|
calendar.getClosestWorkTime({
|
|
3973
4373
|
direction: "past",
|
|
@@ -4150,7 +4550,12 @@ var constraintDatePipeline = {
|
|
|
4150
4550
|
transform(activity, newValue, ctx) {
|
|
4151
4551
|
const finalType = isUnconstrained(activity) ? IMPLIED_CONSTRAINT_ON_DATE_EDIT : effectiveConstraintType(activity);
|
|
4152
4552
|
const calendar = ctx.calendars.getCalendar(activity.calendarId);
|
|
4153
|
-
|
|
4553
|
+
if (!calendar) {
|
|
4554
|
+
throw new Error(
|
|
4555
|
+
"constraintDatePipeline: no calendar resolved (integrity)"
|
|
4556
|
+
);
|
|
4557
|
+
}
|
|
4558
|
+
const storedDate = normalizeConstraintDate(newValue, calendar, finalType);
|
|
4154
4559
|
const patch = buildConstraintDateExtraFields(activity, storedDate);
|
|
4155
4560
|
return fieldChanges(patch, {
|
|
4156
4561
|
autoSchedule: true
|
|
@@ -4450,6 +4855,31 @@ var COLUMN_WRITABLE_FIELDS = new Set(
|
|
|
4450
4855
|
COLUMN_WRITABLE_FIELD_LIST
|
|
4451
4856
|
);
|
|
4452
4857
|
|
|
4858
|
+
// src/constants/activity-properties.ts
|
|
4859
|
+
var ACTIVITY_PROPERTY = {
|
|
4860
|
+
PARENT: "parentId",
|
|
4861
|
+
TYPE: "type",
|
|
4862
|
+
START_DATE: "startDate",
|
|
4863
|
+
END_DATE: "endDate",
|
|
4864
|
+
CONSTRAINT_TYPE: "constraintType",
|
|
4865
|
+
CONSTRAINT_DATE: "constraintDate",
|
|
4866
|
+
PROGRESS: "progress",
|
|
4867
|
+
COST: "cost",
|
|
4868
|
+
USED_COST: "usedCost",
|
|
4869
|
+
REAL_COST: "realCost",
|
|
4870
|
+
WORK_HOURS: "workHours",
|
|
4871
|
+
REAL_WORK_HOURS: "realWorkHours",
|
|
4872
|
+
CORRELATIVE_ID: "correlativeId",
|
|
4873
|
+
CUSTOM_ID: "customId",
|
|
4874
|
+
NEW_ACTIVITIES_ARRAY: "newActivityIds",
|
|
4875
|
+
HAS_NEW_ACTIVITIES: "hasNewActivities",
|
|
4876
|
+
CALENDAR_DURATION: "calendarDuration"};
|
|
4877
|
+
var LINK_PROPERTY = {
|
|
4878
|
+
TYPE: "type",
|
|
4879
|
+
LAG: "lag",
|
|
4880
|
+
FREE_SLACK_HOURS: "freeSlackHours",
|
|
4881
|
+
IS_CRITICAL: "isCritical"};
|
|
4882
|
+
|
|
4453
4883
|
// src/internal/state/dynamic-writes.ts
|
|
4454
4884
|
function setActivityFieldDynamic(state, activityId, field, value) {
|
|
4455
4885
|
state.setActivityFieldDynamic(activityId, field, value);
|
|
@@ -4501,11 +4931,6 @@ function cloneCriticalPath(value) {
|
|
|
4501
4931
|
};
|
|
4502
4932
|
}
|
|
4503
4933
|
|
|
4504
|
-
// src/shared/clone-domain-value.ts
|
|
4505
|
-
function cloneDomainValue(value) {
|
|
4506
|
-
return structuredClone(value);
|
|
4507
|
-
}
|
|
4508
|
-
|
|
4509
4934
|
// src/dispatch/shared/snapshots.ts
|
|
4510
4935
|
var YIELD_EVERY_N_ENTRIES = 200;
|
|
4511
4936
|
function collectTouchedIds(primary, changes) {
|
|
@@ -4617,6 +5042,7 @@ function diffActivity(before, after) {
|
|
|
4617
5042
|
const beforeRec = before ?? {};
|
|
4618
5043
|
const afterRec = after;
|
|
4619
5044
|
const keys = /* @__PURE__ */ new Set([...Object.keys(beforeRec), ...Object.keys(afterRec)]);
|
|
5045
|
+
keys.delete(ACTIVITY_PROPERTY.CALENDAR_DURATION);
|
|
4620
5046
|
for (const k of keys) {
|
|
4621
5047
|
if (Object.hasOwn(beforeRec, k) !== Object.hasOwn(afterRec, k) || !fieldValueEqual(beforeRec[k], afterRec[k])) {
|
|
4622
5048
|
fields[k] = {
|
|
@@ -5308,10 +5734,11 @@ function mutateTheDateToFutureOrPastInBaseRestriction(dateBaseToCalculate, restr
|
|
|
5308
5734
|
|
|
5309
5735
|
// src/critical-path/legacy/base/parents-calculations/forward.js
|
|
5310
5736
|
var CalculateForwardParentsWithLinks = class {
|
|
5311
|
-
constructor(linkedActivitiesData, activity, gantt) {
|
|
5737
|
+
constructor(linkedActivitiesData, activity, gantt, linkCandidates) {
|
|
5312
5738
|
this.linkedActivitiesData = linkedActivitiesData;
|
|
5313
5739
|
this.activity = activity;
|
|
5314
5740
|
this.gantt = gantt;
|
|
5741
|
+
this.linkCandidates = linkCandidates;
|
|
5315
5742
|
this.activityCalendar = this.gantt.getCalendar(this.activity.calendar_id);
|
|
5316
5743
|
if (!this.activityCalendar) {
|
|
5317
5744
|
throw new Error(
|
|
@@ -5356,6 +5783,10 @@ var CalculateForwardParentsWithLinks = class {
|
|
|
5356
5783
|
continue;
|
|
5357
5784
|
}
|
|
5358
5785
|
if (calculation) {
|
|
5786
|
+
this.linkCandidates?.set(linkedActivity.linkId, {
|
|
5787
|
+
targetId: String(this.activity.id),
|
|
5788
|
+
...calculation
|
|
5789
|
+
});
|
|
5359
5790
|
calculations.push(calculation);
|
|
5360
5791
|
}
|
|
5361
5792
|
}
|
|
@@ -5803,7 +6234,8 @@ function parentWithLinks(params) {
|
|
|
5803
6234
|
linkDirection,
|
|
5804
6235
|
calculations,
|
|
5805
6236
|
gantt,
|
|
5806
|
-
direction
|
|
6237
|
+
direction,
|
|
6238
|
+
linkCandidates
|
|
5807
6239
|
} = params;
|
|
5808
6240
|
const linksArray = parent[linkProperty];
|
|
5809
6241
|
const parentData = extractParentData(parent);
|
|
@@ -5818,7 +6250,8 @@ function parentWithLinks(params) {
|
|
|
5818
6250
|
calculation = new forward_default(
|
|
5819
6251
|
linksWithActivityData,
|
|
5820
6252
|
parent,
|
|
5821
|
-
gantt
|
|
6253
|
+
gantt,
|
|
6254
|
+
linkCandidates
|
|
5822
6255
|
);
|
|
5823
6256
|
calculation = calculation.calculate();
|
|
5824
6257
|
} else {
|
|
@@ -5863,6 +6296,7 @@ function getLinksWithActivityDataForward(linksArray, linkDirection, calculations
|
|
|
5863
6296
|
return null;
|
|
5864
6297
|
}
|
|
5865
6298
|
return {
|
|
6299
|
+
linkId: String(linkData.id),
|
|
5866
6300
|
es: calculationsOfLinkedActivity.es,
|
|
5867
6301
|
ef: calculationsOfLinkedActivity.ef,
|
|
5868
6302
|
linkLag: linkData.lag,
|
|
@@ -5963,6 +6397,7 @@ var CalculationOfParent = class {
|
|
|
5963
6397
|
this.calculationOfParentsFromLinks = params.calculationOfParentsFromLinks;
|
|
5964
6398
|
this.singleParents = params.singleParents;
|
|
5965
6399
|
this.parentCalculationFromChildren = params.parentCalculationFromChildren;
|
|
6400
|
+
this.linkCandidates = params.linkCandidates;
|
|
5966
6401
|
this.gantt = params.gantt;
|
|
5967
6402
|
}
|
|
5968
6403
|
handleParentWithLinks() {
|
|
@@ -6010,7 +6445,8 @@ var CalculationOfParent = class {
|
|
|
6010
6445
|
linkDirection: this.linkDirection,
|
|
6011
6446
|
gantt: this.gantt,
|
|
6012
6447
|
calculations: this.calculations,
|
|
6013
|
-
direction: this.direction
|
|
6448
|
+
direction: this.direction,
|
|
6449
|
+
linkCandidates: this.linkCandidates
|
|
6014
6450
|
});
|
|
6015
6451
|
this.calculationsOfParentThatImpactChildrens.set(activity.id, calculation);
|
|
6016
6452
|
this.calculationOfParentsFromLinks.add(activity.id);
|
|
@@ -6659,6 +7095,7 @@ var CriticalPathHelpers = class {
|
|
|
6659
7095
|
calculationOfParentsFromLinks: this.calculationOfParentsFromLinks,
|
|
6660
7096
|
singleParents: this.singleParents,
|
|
6661
7097
|
parentCalculationFromChildren: this.parentCalculationFromChildren,
|
|
7098
|
+
linkCandidates: this.linkCandidates,
|
|
6662
7099
|
gantt: this.gantt
|
|
6663
7100
|
});
|
|
6664
7101
|
if (this.pendingParentsWithLinks.has(activity.id)) {
|
|
@@ -6726,6 +7163,7 @@ var LinksRulesCalculationsMethods = class {
|
|
|
6726
7163
|
this.parentsWithLinks = calculationObject.parentsWithLinks;
|
|
6727
7164
|
this.parentsCalculations = calculationObject.parentsCalculations;
|
|
6728
7165
|
this.gantt = calculationObject.ganttInstance;
|
|
7166
|
+
this.linkCandidates = calculationObject.linkCandidates;
|
|
6729
7167
|
}
|
|
6730
7168
|
getEsAndEfFromPreviousCalculatedActivities(activity) {
|
|
6731
7169
|
const isAlap = this.alapMap.has(activity);
|
|
@@ -6897,7 +7335,12 @@ var LinksConstraintCalculator = class extends LinksRulesCalculationsMethods_defa
|
|
|
6897
7335
|
if (!linkData) {
|
|
6898
7336
|
throw new Error("Link data not found", { cause: { linkId: link } });
|
|
6899
7337
|
}
|
|
6900
|
-
|
|
7338
|
+
const calculation = this.calculateLink(linkData);
|
|
7339
|
+
this.linkCandidates?.set(String(linkData.id), {
|
|
7340
|
+
targetId: String(this.referenceActivity.id),
|
|
7341
|
+
...calculation
|
|
7342
|
+
});
|
|
7343
|
+
return calculation;
|
|
6901
7344
|
});
|
|
6902
7345
|
const parent = Number(this.referenceActivity.parent);
|
|
6903
7346
|
if (this.parentsWithLinks.has(parent)) {
|
|
@@ -7163,6 +7606,7 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7163
7606
|
this.totalFloatSnltFnlt = /* @__PURE__ */ new Map();
|
|
7164
7607
|
this.totalFloatForMsoMfo = /* @__PURE__ */ new Map();
|
|
7165
7608
|
this.totalFloatForAsap = /* @__PURE__ */ new Map();
|
|
7609
|
+
this.linkCandidates = /* @__PURE__ */ new Map();
|
|
7166
7610
|
this.calculationOfParentsFromLinks = /* @__PURE__ */ new Set();
|
|
7167
7611
|
this.parentCalculationFromChildren = /* @__PURE__ */ new Set();
|
|
7168
7612
|
this.forbiddenActivitiesLinkedToParentsWithLinks = /* @__PURE__ */ new Set();
|
|
@@ -7261,7 +7705,8 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7261
7705
|
constraint: constraintType,
|
|
7262
7706
|
parentsWithLinks: this.pendingParentsWithLinks,
|
|
7263
7707
|
parentsCalculations: this.calculationsOfParentThatImpactChildrens,
|
|
7264
|
-
ganttInstance: this.gantt
|
|
7708
|
+
ganttInstance: this.gantt,
|
|
7709
|
+
linkCandidates: this.linkCandidates
|
|
7265
7710
|
}).calculate();
|
|
7266
7711
|
if (constraintType === CONSTRAINT_TYPES.ASAP) {
|
|
7267
7712
|
this.totalFloatForAsap.set(activity, {
|
|
@@ -7279,7 +7724,8 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7279
7724
|
constraint: constraintType,
|
|
7280
7725
|
parentsWithLinks: this.pendingParentsWithLinks,
|
|
7281
7726
|
parentsCalculations: this.calculationsOfParentThatImpactChildrens,
|
|
7282
|
-
ganttInstance: this.gantt
|
|
7727
|
+
ganttInstance: this.gantt,
|
|
7728
|
+
linkCandidates: this.linkCandidates
|
|
7283
7729
|
}).calculate());
|
|
7284
7730
|
}
|
|
7285
7731
|
if (constraintType === CONSTRAINT_TYPES.ALAP) {
|
|
@@ -8316,7 +8762,6 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8316
8762
|
}
|
|
8317
8763
|
}
|
|
8318
8764
|
if ([CONSTRAINT_TYPES.SNLT].includes(activity.constraint_type)) {
|
|
8319
|
-
const constraintDate = activity.constraint_date;
|
|
8320
8765
|
const parentLinkCalcualation2 = this.calculationsOfParentThatImpactChildrens.get(parent);
|
|
8321
8766
|
const lsToComparison = addDurationToDate(
|
|
8322
8767
|
this.gantt.getCalendar(activity.calendar_id),
|
|
@@ -8324,6 +8769,7 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8324
8769
|
-activity.duration,
|
|
8325
8770
|
activity
|
|
8326
8771
|
);
|
|
8772
|
+
const constraintDate = activity.constraint_date ?? lsToComparison;
|
|
8327
8773
|
ls = new Date(
|
|
8328
8774
|
Math.min(constraintDate.getTime(), lsToComparison.getTime())
|
|
8329
8775
|
);
|
|
@@ -8335,8 +8781,8 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8335
8781
|
);
|
|
8336
8782
|
}
|
|
8337
8783
|
if ([CONSTRAINT_TYPES.FNLT].includes(activity.constraint_type)) {
|
|
8338
|
-
const constraintDate = activity.constraint_date;
|
|
8339
8784
|
const parentLinkCalcualation2 = this.calculationsOfParentThatImpactChildrens.get(parent);
|
|
8785
|
+
const constraintDate = activity.constraint_date ?? parentLinkCalcualation2.lf;
|
|
8340
8786
|
ls = new Date(
|
|
8341
8787
|
Math.min(constraintDate.getTime(), parentLinkCalcualation2.lf.getTime())
|
|
8342
8788
|
);
|
|
@@ -8720,18 +9166,20 @@ var FreeFloat = class {
|
|
|
8720
9166
|
);
|
|
8721
9167
|
}
|
|
8722
9168
|
calculateFreeFloatForEndOfProject(constraint) {
|
|
9169
|
+
const calculation = constraint === CONSTRAINT_TYPES.ALAP ? this.backwardMap.get(this.activity.id) : this.forwardMap.get(this.activity.id);
|
|
8723
9170
|
return this.parseHoursToDays(
|
|
8724
9171
|
this.calculationWithCalendar(
|
|
8725
9172
|
this.gantt.getSubtaskDates().end_date,
|
|
8726
|
-
constraint === CONSTRAINT_TYPES.ALAP ?
|
|
9173
|
+
constraint === CONSTRAINT_TYPES.ALAP ? calculation?.lf : calculation?.ef
|
|
8727
9174
|
)
|
|
8728
9175
|
);
|
|
8729
9176
|
}
|
|
8730
9177
|
calculateFreeFloatOfRestriction(constraint) {
|
|
9178
|
+
const calculation = this.forwardMap.get(this.activity.id);
|
|
8731
9179
|
return this.parseHoursToDays(
|
|
8732
9180
|
this.calculationWithCalendar(
|
|
8733
9181
|
this.activity.constraint_date,
|
|
8734
|
-
constraint === CONSTRAINT_TYPES.FNLT ?
|
|
9182
|
+
constraint === CONSTRAINT_TYPES.FNLT ? calculation?.ef : calculation?.es
|
|
8735
9183
|
)
|
|
8736
9184
|
);
|
|
8737
9185
|
}
|
|
@@ -8749,111 +9197,33 @@ var FreeFloat = class {
|
|
|
8749
9197
|
sucessorId,
|
|
8750
9198
|
predecessorId
|
|
8751
9199
|
}) {
|
|
8752
|
-
const
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
9200
|
+
const successor = (isSuscessorAlap ? this.backwardMap : this.forwardMap).get(sucessorId);
|
|
9201
|
+
const predecessor = (constraint === CONSTRAINT_TYPES.ALAP ? this.backwardMap : this.forwardMap).get(predecessorId);
|
|
9202
|
+
const predecessorStart = constraint === CONSTRAINT_TYPES.ALAP ? predecessor?.ls : predecessor?.es;
|
|
9203
|
+
const predecessorFinish = constraint === CONSTRAINT_TYPES.ALAP ? predecessor?.lf : predecessor?.ef;
|
|
9204
|
+
return {
|
|
9205
|
+
fs: {
|
|
9206
|
+
sucessor: successor?.ls ?? successor?.es,
|
|
9207
|
+
predecessor: predecessorFinish
|
|
9208
|
+
},
|
|
9209
|
+
ss: {
|
|
9210
|
+
sucessor: successor?.ls ?? successor?.es,
|
|
9211
|
+
predecessor: predecessorStart
|
|
9212
|
+
},
|
|
9213
|
+
ff: {
|
|
9214
|
+
sucessor: successor?.lf ?? successor?.ef,
|
|
9215
|
+
predecessor: predecessorFinish
|
|
9216
|
+
},
|
|
9217
|
+
sf: {
|
|
9218
|
+
sucessor: successor?.lf ?? successor?.ef,
|
|
9219
|
+
predecessor: predecessorStart
|
|
8758
9220
|
}
|
|
8759
|
-
return this.forwardMap.get(predecessorId)[direction];
|
|
8760
9221
|
};
|
|
8761
|
-
if (constraint === CONSTRAINT_TYPES.ASAP || constraint === CONSTRAINT_TYPES.SNET || constraint === CONSTRAINT_TYPES.FNET || constraint === CONSTRAINT_TYPES.MSO || constraint === CONSTRAINT_TYPES.MFO) {
|
|
8762
|
-
return {
|
|
8763
|
-
fs: {
|
|
8764
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
8765
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
8766
|
-
},
|
|
8767
|
-
ss: {
|
|
8768
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
8769
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
8770
|
-
},
|
|
8771
|
-
ff: {
|
|
8772
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
8773
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
8774
|
-
},
|
|
8775
|
-
sf: {
|
|
8776
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
8777
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
8778
|
-
}
|
|
8779
|
-
};
|
|
8780
|
-
}
|
|
8781
|
-
if (constraint === CONSTRAINT_TYPES.ALAP) {
|
|
8782
|
-
return {
|
|
8783
|
-
fs: {
|
|
8784
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
8785
|
-
predecessor: getPredecessorDate({
|
|
8786
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
8787
|
-
direction: "lf"
|
|
8788
|
-
})
|
|
8789
|
-
},
|
|
8790
|
-
ss: {
|
|
8791
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
8792
|
-
predecessor: getPredecessorDate({
|
|
8793
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
8794
|
-
direction: "ls"
|
|
8795
|
-
})
|
|
8796
|
-
},
|
|
8797
|
-
ff: {
|
|
8798
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
8799
|
-
predecessor: getPredecessorDate({
|
|
8800
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
8801
|
-
direction: "lf"
|
|
8802
|
-
})
|
|
8803
|
-
},
|
|
8804
|
-
sf: {
|
|
8805
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
8806
|
-
predecessor: getPredecessorDate({
|
|
8807
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
8808
|
-
direction: "ls"
|
|
8809
|
-
})
|
|
8810
|
-
}
|
|
8811
|
-
};
|
|
8812
|
-
}
|
|
8813
|
-
if (constraint === CONSTRAINT_TYPES.SNLT) {
|
|
8814
|
-
return {
|
|
8815
|
-
fs: {
|
|
8816
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
8817
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
8818
|
-
},
|
|
8819
|
-
ss: {
|
|
8820
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
8821
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
8822
|
-
},
|
|
8823
|
-
ff: {
|
|
8824
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
8825
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
8826
|
-
},
|
|
8827
|
-
sf: {
|
|
8828
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
8829
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
8830
|
-
}
|
|
8831
|
-
};
|
|
8832
|
-
}
|
|
8833
|
-
if (constraint === CONSTRAINT_TYPES.FNLT) {
|
|
8834
|
-
return {
|
|
8835
|
-
fs: {
|
|
8836
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
8837
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
8838
|
-
},
|
|
8839
|
-
ss: {
|
|
8840
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
8841
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
8842
|
-
},
|
|
8843
|
-
ff: {
|
|
8844
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
8845
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
8846
|
-
},
|
|
8847
|
-
sf: {
|
|
8848
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
8849
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
8850
|
-
}
|
|
8851
|
-
};
|
|
8852
|
-
}
|
|
8853
9222
|
}
|
|
8854
9223
|
calculateMsoMfo(constraint) {
|
|
8855
9224
|
const CONSTRAINT_DATE2 = this.activity.constraint_date || /* @__PURE__ */ new Date();
|
|
8856
|
-
const
|
|
9225
|
+
const calculation = this.forwardMap.get(this.activity.id);
|
|
9226
|
+
const DATE_TO_CALCULATE = constraint === CONSTRAINT_TYPES.MSO ? calculation?.es : calculation?.ef;
|
|
8857
9227
|
const freeSlack = this.calculationWithCalendar(
|
|
8858
9228
|
CONSTRAINT_DATE2,
|
|
8859
9229
|
DATE_TO_CALCULATE
|
|
@@ -8867,6 +9237,9 @@ var FreeFloat = class {
|
|
|
8867
9237
|
return filterValues.length ? Math.min(...filterValues) : 0;
|
|
8868
9238
|
}
|
|
8869
9239
|
calculationWithCalendar(dateOne, dateTwo) {
|
|
9240
|
+
if (!(dateOne instanceof Date) || !(dateTwo instanceof Date) || !Number.isFinite(dateOne.getTime()) || !Number.isFinite(dateTwo.getTime())) {
|
|
9241
|
+
return Number.NaN;
|
|
9242
|
+
}
|
|
8870
9243
|
const key = `${dateTwo.getTime()}_${dateOne.getTime()}`;
|
|
8871
9244
|
if (this.durationCache.has(key)) {
|
|
8872
9245
|
return this.durationCache.get(key);
|
|
@@ -8880,7 +9253,7 @@ var FreeFloat = class {
|
|
|
8880
9253
|
return result;
|
|
8881
9254
|
}
|
|
8882
9255
|
parseHoursToDays(date2) {
|
|
8883
|
-
if (
|
|
9256
|
+
if (date2 === void 0 || date2 === null) {
|
|
8884
9257
|
return 0;
|
|
8885
9258
|
}
|
|
8886
9259
|
return date2 / this.gantt.config.hoursPerDay;
|
|
@@ -8946,6 +9319,7 @@ var CriticalPath = class {
|
|
|
8946
9319
|
this.backwardPathCalculations = null;
|
|
8947
9320
|
this.totalFloat = /* @__PURE__ */ new Map();
|
|
8948
9321
|
this.freeFloat = /* @__PURE__ */ new Map();
|
|
9322
|
+
this.linkResidualHours = /* @__PURE__ */ new Map();
|
|
8949
9323
|
this.currentCalculationId = 0;
|
|
8950
9324
|
}
|
|
8951
9325
|
getCurrentCalculationId = () => this.currentCalculationId;
|
|
@@ -8976,6 +9350,13 @@ var CriticalPath = class {
|
|
|
8976
9350
|
if (!isCurrent()) return false;
|
|
8977
9351
|
this.forwardPathCalculations = forwardPathCalculations;
|
|
8978
9352
|
this.backwardPathCalculations = backwardPath.calculations;
|
|
9353
|
+
const linkResidualHours = await this.calculateLinkResidualHours(
|
|
9354
|
+
forwardPath.linkCandidates,
|
|
9355
|
+
forwardPathCalculations,
|
|
9356
|
+
isCurrent
|
|
9357
|
+
);
|
|
9358
|
+
if (!linkResidualHours || !isCurrent()) return false;
|
|
9359
|
+
this.linkResidualHours = linkResidualHours;
|
|
8979
9360
|
const { totalFloatMap, freeFloatMap } = await this.calculateFloatsAsync(
|
|
8980
9361
|
forwardPath,
|
|
8981
9362
|
backwardPath,
|
|
@@ -8995,6 +9376,38 @@ var CriticalPath = class {
|
|
|
8995
9376
|
throw error;
|
|
8996
9377
|
}
|
|
8997
9378
|
}
|
|
9379
|
+
async calculateLinkResidualHours(linkCandidates, forwardCalculations, isCurrent = () => true) {
|
|
9380
|
+
const residuals = /* @__PURE__ */ new Map();
|
|
9381
|
+
let sliceStartTime = performance.now();
|
|
9382
|
+
for (const [linkId, candidate] of linkCandidates) {
|
|
9383
|
+
sliceStartTime = await yieldIfNeeded2(sliceStartTime);
|
|
9384
|
+
if (!isCurrent()) return null;
|
|
9385
|
+
const targetId = Number(candidate.targetId);
|
|
9386
|
+
const target = this.gantt.getTask(targetId);
|
|
9387
|
+
const settled = forwardCalculations.get(targetId);
|
|
9388
|
+
const candidateEf = candidate.ef;
|
|
9389
|
+
const settledEf = settled?.ef;
|
|
9390
|
+
const calendar = target && this.gantt.getCalendar(target.calendar_id);
|
|
9391
|
+
if (!calendar || !(candidateEf instanceof Date) || !(settledEf instanceof Date) || !Number.isFinite(candidateEf.getTime()) || !Number.isFinite(settledEf.getTime())) {
|
|
9392
|
+
residuals.set(linkId, null);
|
|
9393
|
+
continue;
|
|
9394
|
+
}
|
|
9395
|
+
const candidateTime = candidateEf.getTime();
|
|
9396
|
+
const settledTime = settledEf.getTime();
|
|
9397
|
+
const duration = settledTime >= candidateTime ? calendar.calculateDuration({
|
|
9398
|
+
start_date: candidateEf,
|
|
9399
|
+
end_date: settledEf
|
|
9400
|
+
}) : -calendar.calculateDuration({
|
|
9401
|
+
start_date: settledEf,
|
|
9402
|
+
end_date: candidateEf
|
|
9403
|
+
});
|
|
9404
|
+
residuals.set(
|
|
9405
|
+
linkId,
|
|
9406
|
+
Number.isFinite(duration) ? Object.is(duration, -0) ? 0 : duration : null
|
|
9407
|
+
);
|
|
9408
|
+
}
|
|
9409
|
+
return residuals;
|
|
9410
|
+
}
|
|
8998
9411
|
async calculateFloatsAsync(forward, backward, isCurrent) {
|
|
8999
9412
|
try {
|
|
9000
9413
|
const projectDates = this.gantt.getSubtaskDates();
|
|
@@ -9098,14 +9511,52 @@ function computeLevels(state) {
|
|
|
9098
9511
|
for (const id of activitiesById.keys()) resolveLevel(id, /* @__PURE__ */ new Set());
|
|
9099
9512
|
return levelById;
|
|
9100
9513
|
}
|
|
9101
|
-
function
|
|
9514
|
+
function createLegacyIds(state) {
|
|
9515
|
+
const canonicalIds = /* @__PURE__ */ new Set();
|
|
9516
|
+
state.forEachActivity((activity, id) => {
|
|
9517
|
+
canonicalIds.add(String(id));
|
|
9518
|
+
if (activity.parentId !== null) canonicalIds.add(String(activity.parentId));
|
|
9519
|
+
});
|
|
9520
|
+
const numericCounts = /* @__PURE__ */ new Map();
|
|
9521
|
+
for (const id of canonicalIds) {
|
|
9522
|
+
const numeric = Number(id);
|
|
9523
|
+
if (Number.isSafeInteger(numeric) && numeric !== 0) {
|
|
9524
|
+
numericCounts.set(numeric, (numericCounts.get(numeric) ?? 0) + 1);
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
const canonicalToLegacy = /* @__PURE__ */ new Map();
|
|
9528
|
+
const legacyToCanonical = /* @__PURE__ */ new Map();
|
|
9529
|
+
for (const id of canonicalIds) {
|
|
9530
|
+
const numeric = Number(id);
|
|
9531
|
+
if (Number.isSafeInteger(numeric) && numeric !== 0 && numericCounts.get(numeric) === 1) {
|
|
9532
|
+
canonicalToLegacy.set(id, numeric);
|
|
9533
|
+
legacyToCanonical.set(numeric, id);
|
|
9534
|
+
}
|
|
9535
|
+
}
|
|
9536
|
+
let next = 1;
|
|
9537
|
+
const toLegacy = (rawId) => {
|
|
9538
|
+
const id = String(rawId);
|
|
9539
|
+
const existing = canonicalToLegacy.get(id);
|
|
9540
|
+
if (existing !== void 0) return existing;
|
|
9541
|
+
while (legacyToCanonical.has(next)) next += 1;
|
|
9542
|
+
canonicalToLegacy.set(id, next);
|
|
9543
|
+
legacyToCanonical.set(next, id);
|
|
9544
|
+
return next++;
|
|
9545
|
+
};
|
|
9546
|
+
for (const id of canonicalIds) toLegacy(id);
|
|
9547
|
+
return {
|
|
9548
|
+
toLegacy,
|
|
9549
|
+
toCanonical: (id) => typeof id === "number" ? legacyToCanonical.get(id) ?? null : canonicalToLegacy.has(String(id)) ? String(id) : null
|
|
9550
|
+
};
|
|
9551
|
+
}
|
|
9552
|
+
function toLegacyTasks(state, ids) {
|
|
9102
9553
|
const levelById = computeLevels(state);
|
|
9103
9554
|
const leveled = /* @__PURE__ */ new Map();
|
|
9104
9555
|
state.forEachActivity((activity, id) => {
|
|
9105
9556
|
const key = String(id);
|
|
9106
9557
|
leveled.set(key, {
|
|
9107
|
-
id:
|
|
9108
|
-
parent: activity.parentId === null ? 0 :
|
|
9558
|
+
id: ids.toLegacy(key),
|
|
9559
|
+
parent: activity.parentId === null ? 0 : ids.toLegacy(activity.parentId),
|
|
9109
9560
|
$level: levelById.get(key) ?? 0,
|
|
9110
9561
|
$source: state.getOutgoingLinkIds(key),
|
|
9111
9562
|
$target: state.getIncomingLinkIds(key),
|
|
@@ -9179,7 +9630,8 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9179
9630
|
};
|
|
9180
9631
|
const bounds = projectBounds(state);
|
|
9181
9632
|
await yieldToBrowser();
|
|
9182
|
-
const
|
|
9633
|
+
const ids = createLegacyIds(state);
|
|
9634
|
+
const leveled = toLegacyTasks(state, ids);
|
|
9183
9635
|
await yieldToBrowser();
|
|
9184
9636
|
const byTime = [...leveled.values()].sort(
|
|
9185
9637
|
(a, b) => (a.start_date?.getTime() ?? 0) - (b.start_date?.getTime() ?? 0)
|
|
@@ -9189,24 +9641,31 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9189
9641
|
return {
|
|
9190
9642
|
fullyParsed: true,
|
|
9191
9643
|
config: { hoursPerDay },
|
|
9192
|
-
getTask: (id) =>
|
|
9644
|
+
getTask: (id) => {
|
|
9645
|
+
const canonicalId = ids.toCanonical(id);
|
|
9646
|
+
return canonicalId === null ? null : leveled.get(canonicalId) ?? null;
|
|
9647
|
+
},
|
|
9193
9648
|
getLink: (id) => {
|
|
9194
9649
|
const link = state.getLink(id);
|
|
9195
9650
|
if (!link) return null;
|
|
9196
9651
|
return {
|
|
9197
9652
|
...link,
|
|
9198
|
-
source:
|
|
9199
|
-
target:
|
|
9653
|
+
source: ids.toLegacy(link.source),
|
|
9654
|
+
target: ids.toLegacy(link.target),
|
|
9200
9655
|
lag: link.lag
|
|
9201
9656
|
};
|
|
9202
9657
|
},
|
|
9203
9658
|
getCalendar: resolveCalendar,
|
|
9204
|
-
getChildren: (parentId) =>
|
|
9659
|
+
getChildren: (parentId) => {
|
|
9660
|
+
const canonicalId = ids.toCanonical(parentId);
|
|
9661
|
+
return canonicalId === null ? [] : state.getChildren(canonicalId).map(ids.toLegacy);
|
|
9662
|
+
},
|
|
9205
9663
|
eachTask: (cb) => {
|
|
9206
9664
|
for (const task of treeOrdered) cb(task);
|
|
9207
9665
|
},
|
|
9208
9666
|
getTaskByTime: () => byTime,
|
|
9209
9667
|
getSubtaskDates: () => bounds,
|
|
9668
|
+
getCanonicalActivityId: ids.toCanonical,
|
|
9210
9669
|
refreshData: () => {
|
|
9211
9670
|
}
|
|
9212
9671
|
};
|
|
@@ -9214,21 +9673,92 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9214
9673
|
|
|
9215
9674
|
// src/critical-path/run-critical-path.ts
|
|
9216
9675
|
var CP_WRITE_YIELD_EVERY = 1e3;
|
|
9217
|
-
function
|
|
9218
|
-
const
|
|
9219
|
-
const forward = maps.forward?.get(activityId);
|
|
9220
|
-
const backward = maps.backward?.get(activityId);
|
|
9221
|
-
const totalSlack = maps.totalFloat.get(activityId) ?? null;
|
|
9676
|
+
function fieldsForLink(rawResidual, sourceIsCritical, targetIsCritical, targetProgress) {
|
|
9677
|
+
const freeSlackHours = typeof rawResidual === "number" && Number.isFinite(rawResidual) ? Object.is(rawResidual, -0) ? 0 : rawResidual : null;
|
|
9222
9678
|
return {
|
|
9223
|
-
|
|
9224
|
-
|
|
9679
|
+
freeSlackHours,
|
|
9680
|
+
isCritical: freeSlackHours !== null && freeSlackHours <= 0 && sourceIsCritical && targetIsCritical && targetProgress === 0
|
|
9681
|
+
};
|
|
9682
|
+
}
|
|
9683
|
+
async function collectActivityFields(state, maps, hoursPerDay, toCanonicalActivityId, isCurrent) {
|
|
9684
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9685
|
+
let sinceYield = 0;
|
|
9686
|
+
for (const calculationId of maps.forward?.keys() ?? []) {
|
|
9687
|
+
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9688
|
+
sinceYield = 0;
|
|
9689
|
+
await yieldToBrowser();
|
|
9690
|
+
if (!isCurrent()) return null;
|
|
9691
|
+
}
|
|
9692
|
+
const activityId = toCanonicalActivityId(calculationId);
|
|
9693
|
+
if (activityId === null) continue;
|
|
9694
|
+
const activity = state.getActivity(activityId);
|
|
9695
|
+
if (!activity) continue;
|
|
9696
|
+
fields.set(
|
|
9697
|
+
String(activityId),
|
|
9698
|
+
fieldsForActivity(
|
|
9699
|
+
calculationId,
|
|
9700
|
+
Number(activity.progress ?? 0),
|
|
9701
|
+
maps,
|
|
9702
|
+
hoursPerDay
|
|
9703
|
+
)
|
|
9704
|
+
);
|
|
9705
|
+
}
|
|
9706
|
+
return isCurrent() ? fields : null;
|
|
9707
|
+
}
|
|
9708
|
+
async function collectLinkFields(state, linkResidualHours, activityFields, isCurrent) {
|
|
9709
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9710
|
+
let sinceYield = 0;
|
|
9711
|
+
for (const link of state.getAllLinks()) {
|
|
9712
|
+
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9713
|
+
sinceYield = 0;
|
|
9714
|
+
await yieldToBrowser();
|
|
9715
|
+
if (!isCurrent()) return null;
|
|
9716
|
+
}
|
|
9717
|
+
fields.set(
|
|
9718
|
+
String(link.id),
|
|
9719
|
+
fieldsForLink(
|
|
9720
|
+
linkResidualHours.get(String(link.id)),
|
|
9721
|
+
activityFields.get(link.source)?.isCritical === true,
|
|
9722
|
+
activityFields.get(link.target)?.isCritical === true,
|
|
9723
|
+
state.getActivity(link.target)?.progress
|
|
9724
|
+
)
|
|
9725
|
+
);
|
|
9726
|
+
}
|
|
9727
|
+
return isCurrent() ? fields : null;
|
|
9728
|
+
}
|
|
9729
|
+
var slackDaysToHours = (valueDays, hoursPerDay) => {
|
|
9730
|
+
if (typeof valueDays !== "number" || !Number.isFinite(valueDays) || !Number.isFinite(hoursPerDay) || hoursPerDay <= 0) {
|
|
9731
|
+
return null;
|
|
9732
|
+
}
|
|
9733
|
+
const hours = valueDays * hoursPerDay;
|
|
9734
|
+
if (!Number.isFinite(hours)) return null;
|
|
9735
|
+
return Object.is(hours, -0) ? 0 : hours;
|
|
9736
|
+
};
|
|
9737
|
+
function fieldsForActivity(activityId, progress, maps, hoursPerDay) {
|
|
9738
|
+
const toDate = (value) => {
|
|
9739
|
+
if (value == null) return null;
|
|
9740
|
+
const date2 = new Date(value);
|
|
9741
|
+
return Number.isFinite(date2.getTime()) ? date2 : null;
|
|
9742
|
+
};
|
|
9743
|
+
const forward = maps.forward?.get(activityId);
|
|
9744
|
+
const backward = maps.backward?.get(activityId);
|
|
9745
|
+
const totalSlackHours = slackDaysToHours(
|
|
9746
|
+
maps.totalFloat.get(activityId),
|
|
9747
|
+
hoursPerDay
|
|
9748
|
+
);
|
|
9749
|
+
return {
|
|
9750
|
+
criticalPath: {
|
|
9751
|
+
earlyStart: toDate(forward?.es),
|
|
9225
9752
|
earlyFinish: toDate(forward?.ef),
|
|
9226
9753
|
lateStart: toDate(backward?.ls),
|
|
9227
9754
|
lateFinish: toDate(backward?.lf),
|
|
9228
|
-
totalSlackHours
|
|
9755
|
+
totalSlackHours
|
|
9229
9756
|
},
|
|
9230
|
-
isCritical: progress < 100 &&
|
|
9231
|
-
freeSlackHours:
|
|
9757
|
+
isCritical: progress < 100 && totalSlackHours !== null && totalSlackHours <= 0,
|
|
9758
|
+
freeSlackHours: slackDaysToHours(
|
|
9759
|
+
maps.freeFloat.get(activityId)?.activityFreeFloat,
|
|
9760
|
+
hoursPerDay
|
|
9761
|
+
)
|
|
9232
9762
|
};
|
|
9233
9763
|
}
|
|
9234
9764
|
var datesEqual = (left, right) => left === right || left !== null && right !== null && left.getTime() === right.getTime();
|
|
@@ -9241,6 +9771,14 @@ function setCriticalPathFieldsIfChanged(state, activityId, fields) {
|
|
|
9241
9771
|
}
|
|
9242
9772
|
state.setActivityFields(activityId, fields);
|
|
9243
9773
|
}
|
|
9774
|
+
function setCriticalPathLinkFieldsIfChanged(state, linkId, fields) {
|
|
9775
|
+
const link = state.getLink(linkId);
|
|
9776
|
+
if (!link || Object.is(link.freeSlackHours, fields.freeSlackHours) && Object.is(link.isCritical, fields.isCritical)) {
|
|
9777
|
+
return;
|
|
9778
|
+
}
|
|
9779
|
+
state.setLinkField(linkId, "freeSlackHours", fields.freeSlackHours);
|
|
9780
|
+
state.setLinkField(linkId, "isCritical", fields.isCritical);
|
|
9781
|
+
}
|
|
9244
9782
|
async function computeCriticalPathFields(state, hoursPerDay, isCurrent = () => true) {
|
|
9245
9783
|
if (!isCurrent()) return null;
|
|
9246
9784
|
const gantt = await createGanttAdapter(state, hoursPerDay);
|
|
@@ -9258,61 +9796,36 @@ async function computeCriticalPathFields(state, hoursPerDay, isCurrent = () => t
|
|
|
9258
9796
|
totalFloat: criticalPath.totalFloat,
|
|
9259
9797
|
freeFloat: criticalPath.freeFloat
|
|
9260
9798
|
};
|
|
9261
|
-
const
|
|
9262
|
-
const fieldsByActivity = /* @__PURE__ */ new Map();
|
|
9263
|
-
let sinceYield = 0;
|
|
9264
|
-
for (const activityId of activityIds) {
|
|
9265
|
-
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9266
|
-
sinceYield = 0;
|
|
9267
|
-
await yieldToBrowser();
|
|
9268
|
-
if (!isCurrent()) return null;
|
|
9269
|
-
}
|
|
9270
|
-
const activity = state.getActivity(activityId);
|
|
9271
|
-
if (!activity) continue;
|
|
9272
|
-
const progress = Number(activity.progress ?? 0);
|
|
9273
|
-
fieldsByActivity.set(
|
|
9274
|
-
activityId,
|
|
9275
|
-
fieldsForActivity(activityId, progress, maps)
|
|
9276
|
-
);
|
|
9277
|
-
}
|
|
9278
|
-
return isCurrent() ? fieldsByActivity : null;
|
|
9279
|
-
}
|
|
9280
|
-
async function applyCriticalPath(state, hoursPerDay, isCurrent = () => true) {
|
|
9281
|
-
const fieldsByActivity = await computeCriticalPathFields(
|
|
9799
|
+
const activities = await collectActivityFields(
|
|
9282
9800
|
state,
|
|
9801
|
+
maps,
|
|
9283
9802
|
hoursPerDay,
|
|
9803
|
+
gantt.getCanonicalActivityId,
|
|
9804
|
+
isCurrent
|
|
9805
|
+
);
|
|
9806
|
+
if (!activities) return null;
|
|
9807
|
+
const links = await collectLinkFields(
|
|
9808
|
+
state,
|
|
9809
|
+
criticalPath.linkResidualHours,
|
|
9810
|
+
activities,
|
|
9284
9811
|
isCurrent
|
|
9285
9812
|
);
|
|
9286
|
-
|
|
9287
|
-
|
|
9813
|
+
return links ? { activities, links } : null;
|
|
9814
|
+
}
|
|
9815
|
+
async function applyCriticalPath(state, hoursPerDay, isCurrent = () => true) {
|
|
9816
|
+
const fields = await computeCriticalPathFields(state, hoursPerDay, isCurrent);
|
|
9817
|
+
if (!fields || !isCurrent()) return false;
|
|
9818
|
+
for (const [activityId, activityFields] of fields.activities) {
|
|
9288
9819
|
if (!isCurrent()) return false;
|
|
9289
|
-
setCriticalPathFieldsIfChanged(state, activityId,
|
|
9820
|
+
setCriticalPathFieldsIfChanged(state, activityId, activityFields);
|
|
9821
|
+
}
|
|
9822
|
+
for (const [linkId, linkFields] of fields.links) {
|
|
9823
|
+
if (!isCurrent()) return false;
|
|
9824
|
+
setCriticalPathLinkFieldsIfChanged(state, linkId, linkFields);
|
|
9290
9825
|
}
|
|
9291
9826
|
return true;
|
|
9292
9827
|
}
|
|
9293
9828
|
|
|
9294
|
-
// src/constants/activity-properties.ts
|
|
9295
|
-
var ACTIVITY_PROPERTY = {
|
|
9296
|
-
PARENT: "parentId",
|
|
9297
|
-
TYPE: "type",
|
|
9298
|
-
START_DATE: "startDate",
|
|
9299
|
-
END_DATE: "endDate",
|
|
9300
|
-
CONSTRAINT_TYPE: "constraintType",
|
|
9301
|
-
CONSTRAINT_DATE: "constraintDate",
|
|
9302
|
-
PROGRESS: "progress",
|
|
9303
|
-
COST: "cost",
|
|
9304
|
-
USED_COST: "usedCost",
|
|
9305
|
-
REAL_COST: "realCost",
|
|
9306
|
-
WORK_HOURS: "workHours",
|
|
9307
|
-
REAL_WORK_HOURS: "realWorkHours",
|
|
9308
|
-
CORRELATIVE_ID: "correlativeId",
|
|
9309
|
-
CUSTOM_ID: "customId",
|
|
9310
|
-
NEW_ACTIVITIES_ARRAY: "newActivityIds",
|
|
9311
|
-
HAS_NEW_ACTIVITIES: "hasNewActivities"};
|
|
9312
|
-
var LINK_PROPERTY = {
|
|
9313
|
-
TYPE: "type",
|
|
9314
|
-
LAG: "lag"};
|
|
9315
|
-
|
|
9316
9829
|
// src/propagations/upward/real-cost.ts
|
|
9317
9830
|
function emitRealCost(state) {
|
|
9318
9831
|
const rootIds = [];
|
|
@@ -9365,6 +9878,8 @@ var NON_SCHEDULING_INLINE_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
9365
9878
|
COLUMN.TAGS
|
|
9366
9879
|
]);
|
|
9367
9880
|
var NON_SCHEDULING_KINDS = /* @__PURE__ */ new Set([
|
|
9881
|
+
"activity-clipboard-clear",
|
|
9882
|
+
"activity-copy",
|
|
9368
9883
|
"selection-toggle",
|
|
9369
9884
|
"selection-replace",
|
|
9370
9885
|
"visibility-set",
|
|
@@ -9378,6 +9893,8 @@ var NON_SCHEDULING_KINDS = /* @__PURE__ */ new Set([
|
|
|
9378
9893
|
"status-criteria-set"
|
|
9379
9894
|
]);
|
|
9380
9895
|
var PURE_VIEW_STATE_KINDS = /* @__PURE__ */ new Set([
|
|
9896
|
+
"activity-clipboard-clear",
|
|
9897
|
+
"activity-copy",
|
|
9381
9898
|
"selection-toggle",
|
|
9382
9899
|
"selection-replace",
|
|
9383
9900
|
"visibility-set",
|
|
@@ -9406,9 +9923,15 @@ function isSchedulingDispatch(action, structuralStaleKinds) {
|
|
|
9406
9923
|
return true;
|
|
9407
9924
|
}
|
|
9408
9925
|
function dispatchChangesSchedulingState(action) {
|
|
9926
|
+
if (action.kind === "activity-lookahead-sync") {
|
|
9927
|
+
return action.persistedProgress !== void 0;
|
|
9928
|
+
}
|
|
9409
9929
|
return isSchedulingDispatch(action, NO_STRUCTURAL_EXEMPTIONS);
|
|
9410
9930
|
}
|
|
9411
9931
|
function resolveRunCriticalPath(action) {
|
|
9932
|
+
if (action.kind === "activity-lookahead-sync") {
|
|
9933
|
+
return action.persistedProgress !== void 0;
|
|
9934
|
+
}
|
|
9412
9935
|
return isSchedulingDispatch(
|
|
9413
9936
|
action,
|
|
9414
9937
|
STRUCTURAL_KINDS_LEAVING_CRITICAL_PATH_STALE
|
|
@@ -9694,6 +10217,23 @@ function detectSirAutoReject(beforeSnap, adapter, touchedIds) {
|
|
|
9694
10217
|
return effects;
|
|
9695
10218
|
}
|
|
9696
10219
|
|
|
10220
|
+
// src/dispatch/conversion.ts
|
|
10221
|
+
function snapshotToLink(l) {
|
|
10222
|
+
const view = {
|
|
10223
|
+
id: String(l.id),
|
|
10224
|
+
source: String(l.source),
|
|
10225
|
+
target: String(l.target),
|
|
10226
|
+
type: l.type,
|
|
10227
|
+
lag: Number(l.lag),
|
|
10228
|
+
freeSlackHours: typeof l.freeSlackHours === "number" && Number.isFinite(l.freeSlackHours) ? l.freeSlackHours : null,
|
|
10229
|
+
isCritical: l.isCritical === true
|
|
10230
|
+
};
|
|
10231
|
+
if (l.proplannerId != null) view.proplannerId = Number(l.proplannerId);
|
|
10232
|
+
if (l.ganttId != null) view.ganttId = Number(l.ganttId);
|
|
10233
|
+
if (l.sectorId != null) view.sectorId = Number(l.sectorId);
|
|
10234
|
+
return view;
|
|
10235
|
+
}
|
|
10236
|
+
|
|
9697
10237
|
// src/dispatch/shared/change-set.ts
|
|
9698
10238
|
async function assembleChangeSet(adapter, args) {
|
|
9699
10239
|
const allTouched = collectChangeSetIds(adapter, args);
|
|
@@ -9703,7 +10243,8 @@ async function assembleChangeSet(adapter, args) {
|
|
|
9703
10243
|
const insertedIds = collectInsertedIds(captured);
|
|
9704
10244
|
mergeDirtyBeforeImages(adapter, captured, insertedIds, before, allTouched);
|
|
9705
10245
|
}
|
|
9706
|
-
const
|
|
10246
|
+
const sirDetection = args.sirDetection;
|
|
10247
|
+
const beforeDiffEffects = sirDetection === "before-diff" ? detectSirAutoReject(before, adapter, allTouched) : [];
|
|
9707
10248
|
const correlativeBefore = buildCorrelativeBeforeMap(
|
|
9708
10249
|
args.correlativeShifts,
|
|
9709
10250
|
allTouched
|
|
@@ -9714,18 +10255,91 @@ async function assembleChangeSet(adapter, args) {
|
|
|
9714
10255
|
allTouched,
|
|
9715
10256
|
correlativeBefore
|
|
9716
10257
|
);
|
|
9717
|
-
const effects =
|
|
10258
|
+
const effects = sirDetection === "after-diff" ? detectSirAutoReject(before, adapter, allTouched) : beforeDiffEffects;
|
|
9718
10259
|
const warnings = args.warnings ?? [];
|
|
10260
|
+
const linkChanges = mergeLinkChanges(
|
|
10261
|
+
args.links ?? [],
|
|
10262
|
+
captured ? buildCapturedCriticalPathLinkChanges(adapter, captured) : []
|
|
10263
|
+
);
|
|
9719
10264
|
return {
|
|
9720
10265
|
source: args.source,
|
|
9721
10266
|
activities: activityChanges,
|
|
9722
|
-
links:
|
|
10267
|
+
links: linkChanges,
|
|
9723
10268
|
calendars: [],
|
|
9724
10269
|
trackingEvents: args.trackingEvents ?? [],
|
|
9725
10270
|
...effects.length > 0 ? { effects } : {},
|
|
9726
10271
|
...warnings.length > 0 ? { warnings } : {}
|
|
9727
10272
|
};
|
|
9728
10273
|
}
|
|
10274
|
+
function buildCapturedCriticalPathLinkChanges(adapter, captured) {
|
|
10275
|
+
const changes = [];
|
|
10276
|
+
for (const operation of captured.ops) {
|
|
10277
|
+
if (operation.kind !== "link-field") continue;
|
|
10278
|
+
const after = adapter.getLink(operation.linkId);
|
|
10279
|
+
if (!after) continue;
|
|
10280
|
+
const fields = {};
|
|
10281
|
+
for (const field of ["freeSlackHours", "isCritical"]) {
|
|
10282
|
+
if (!Object.is(operation.before[field], after[field])) {
|
|
10283
|
+
fields[field] = {
|
|
10284
|
+
before: operation.before[field],
|
|
10285
|
+
after: after[field]
|
|
10286
|
+
};
|
|
10287
|
+
}
|
|
10288
|
+
}
|
|
10289
|
+
if (Object.keys(fields).length === 0) continue;
|
|
10290
|
+
changes.push({
|
|
10291
|
+
id: operation.linkId,
|
|
10292
|
+
kind: "updated",
|
|
10293
|
+
fields,
|
|
10294
|
+
after: snapshotToLink(after)
|
|
10295
|
+
});
|
|
10296
|
+
}
|
|
10297
|
+
return changes;
|
|
10298
|
+
}
|
|
10299
|
+
function mergeLinkChanges(explicit, captured) {
|
|
10300
|
+
const merged = [];
|
|
10301
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
10302
|
+
for (const change of explicit) {
|
|
10303
|
+
const index = indexById.get(change.id);
|
|
10304
|
+
if (index === void 0) {
|
|
10305
|
+
indexById.set(change.id, merged.length);
|
|
10306
|
+
merged.push(change);
|
|
10307
|
+
continue;
|
|
10308
|
+
}
|
|
10309
|
+
const current = merged[index];
|
|
10310
|
+
if ((current.kind === "updated" || current.kind === "created") && change.kind === "updated") {
|
|
10311
|
+
merged[index] = {
|
|
10312
|
+
...current,
|
|
10313
|
+
fields: { ...current.fields, ...change.fields },
|
|
10314
|
+
after: change.after
|
|
10315
|
+
};
|
|
10316
|
+
continue;
|
|
10317
|
+
}
|
|
10318
|
+
merged[index] = change;
|
|
10319
|
+
}
|
|
10320
|
+
for (const change of captured) {
|
|
10321
|
+
const index = indexById.get(change.id);
|
|
10322
|
+
if (index === void 0) {
|
|
10323
|
+
indexById.set(change.id, merged.length);
|
|
10324
|
+
merged.push(change);
|
|
10325
|
+
continue;
|
|
10326
|
+
}
|
|
10327
|
+
const current = merged[index];
|
|
10328
|
+
if (current.kind === "deleted") continue;
|
|
10329
|
+
const capturedFields = current.kind === "created" ? Object.fromEntries(
|
|
10330
|
+
Object.entries(change.fields ?? {}).map(([field, value]) => [
|
|
10331
|
+
field,
|
|
10332
|
+
{ before: void 0, after: value.after }
|
|
10333
|
+
])
|
|
10334
|
+
) : change.fields;
|
|
10335
|
+
merged[index] = {
|
|
10336
|
+
...current,
|
|
10337
|
+
fields: { ...current.fields, ...capturedFields },
|
|
10338
|
+
after: change.after
|
|
10339
|
+
};
|
|
10340
|
+
}
|
|
10341
|
+
return merged;
|
|
10342
|
+
}
|
|
9729
10343
|
function buildCorrelativeBeforeMap(shifts, touched) {
|
|
9730
10344
|
if (!shifts || shifts.length === 0) return void 0;
|
|
9731
10345
|
const correlativeBefore = /* @__PURE__ */ new Map();
|
|
@@ -9761,21 +10375,6 @@ function collectInsertedIds(captured) {
|
|
|
9761
10375
|
);
|
|
9762
10376
|
}
|
|
9763
10377
|
|
|
9764
|
-
// src/dispatch/conversion.ts
|
|
9765
|
-
function snapshotToLink(l) {
|
|
9766
|
-
const view = {
|
|
9767
|
-
id: String(l.id),
|
|
9768
|
-
source: String(l.source),
|
|
9769
|
-
target: String(l.target),
|
|
9770
|
-
type: l.type,
|
|
9771
|
-
lag: Number(l.lag)
|
|
9772
|
-
};
|
|
9773
|
-
if (l.proplannerId != null) view.proplannerId = Number(l.proplannerId);
|
|
9774
|
-
if (l.ganttId != null) view.ganttId = Number(l.ganttId);
|
|
9775
|
-
if (l.sectorId != null) view.sectorId = Number(l.sectorId);
|
|
9776
|
-
return view;
|
|
9777
|
-
}
|
|
9778
|
-
|
|
9779
10378
|
// src/dispatch/shared/helpers.ts
|
|
9780
10379
|
function buildLinkChangesForBatch(applied, beforeLinks, adapter) {
|
|
9781
10380
|
const out = [];
|
|
@@ -10120,6 +10719,26 @@ function collectDeterministicIds(operations) {
|
|
|
10120
10719
|
});
|
|
10121
10720
|
return ids.size > 0 ? ids : void 0;
|
|
10122
10721
|
}
|
|
10722
|
+
function resolveDeterministicIds(deterministicIds, deps) {
|
|
10723
|
+
if (deterministicIds === void 0) return void 0;
|
|
10724
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
10725
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
10726
|
+
const nextAvailableId = () => {
|
|
10727
|
+
let candidate = deps.linkIdGen.next();
|
|
10728
|
+
while (deps.adapter.getLink(candidate) !== null || deps.isLinkIdReserved(String(candidate)) || assigned.has(String(candidate))) {
|
|
10729
|
+
candidate = deps.linkIdGen.next();
|
|
10730
|
+
}
|
|
10731
|
+
return candidate;
|
|
10732
|
+
};
|
|
10733
|
+
for (const [operationIndex, explicitId] of deterministicIds) {
|
|
10734
|
+
const isReserved = deps.adapter.getLink(explicitId) !== null || deps.isLinkIdReserved(String(explicitId)) || assigned.has(String(explicitId));
|
|
10735
|
+
const linkId = isReserved ? nextAvailableId() : explicitId;
|
|
10736
|
+
if (!isReserved) deps.linkIdGen.bump(explicitId);
|
|
10737
|
+
assigned.add(String(linkId));
|
|
10738
|
+
resolved.set(operationIndex, linkId);
|
|
10739
|
+
}
|
|
10740
|
+
return resolved;
|
|
10741
|
+
}
|
|
10123
10742
|
function batchOpToLinkOperation(op, hoursPerDay) {
|
|
10124
10743
|
if (op.kind === "create") {
|
|
10125
10744
|
return {
|
|
@@ -10152,7 +10771,7 @@ async function dispatchLinkBatch(operations, source, options, deps, deterministi
|
|
|
10152
10771
|
operations,
|
|
10153
10772
|
adapter,
|
|
10154
10773
|
linkIdGen,
|
|
10155
|
-
deterministicIds
|
|
10774
|
+
resolveDeterministicIds(deterministicIds, deps)
|
|
10156
10775
|
);
|
|
10157
10776
|
if (operations.length === 1 && applied[0] && applied[0].rejected !== null && source.kind !== "inline-edit") {
|
|
10158
10777
|
return { ok: false, reason: applied[0].rejected };
|
|
@@ -10249,7 +10868,7 @@ async function dispatchInlineEditLinks(action, options, deps) {
|
|
|
10249
10868
|
const { specs: specsInDays } = parsePredecessorString(action.newValue);
|
|
10250
10869
|
const specs = specsInDays.map((spec) => ({
|
|
10251
10870
|
...spec,
|
|
10252
|
-
lag: lagDaysToHours(spec.lag, sector.hoursPerDay)
|
|
10871
|
+
lag: lagDaysToHours(roundLagDays(spec.lag), sector.hoursPerDay)
|
|
10253
10872
|
}));
|
|
10254
10873
|
const byCorrelative = /* @__PURE__ */ new Map();
|
|
10255
10874
|
adapter.forEachActivity((a) => {
|
|
@@ -10334,26 +10953,19 @@ function recomputeHhCascadeForParent(startParentId, adapter) {
|
|
|
10334
10953
|
if (!startParentId || startParentId === "0") return;
|
|
10335
10954
|
let currentParentId = String(startParentId);
|
|
10336
10955
|
while (currentParentId !== null) {
|
|
10337
|
-
const
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
const child = adapter.getActivity(childId);
|
|
10342
|
-
if (!child) continue;
|
|
10343
|
-
const hh = readNumber(child, ACTIVITY_PROPERTY.WORK_HOURS);
|
|
10344
|
-
const rw = readChildRealWork(childId, child, hh, adapter);
|
|
10345
|
-
hhSum += hh;
|
|
10346
|
-
rwSum += rw;
|
|
10347
|
-
}
|
|
10956
|
+
const { workHours, realWorkHours } = computeParentHhRollup(
|
|
10957
|
+
currentParentId,
|
|
10958
|
+
adapter
|
|
10959
|
+
);
|
|
10348
10960
|
adapter.setActivityField(
|
|
10349
10961
|
currentParentId,
|
|
10350
10962
|
ACTIVITY_PROPERTY.WORK_HOURS,
|
|
10351
|
-
|
|
10963
|
+
workHours
|
|
10352
10964
|
);
|
|
10353
10965
|
adapter.setActivityField(
|
|
10354
10966
|
currentParentId,
|
|
10355
10967
|
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
10356
|
-
|
|
10968
|
+
realWorkHours
|
|
10357
10969
|
);
|
|
10358
10970
|
const parentSnap = adapter.getActivity(currentParentId);
|
|
10359
10971
|
if (!parentSnap) break;
|
|
@@ -10369,6 +10981,40 @@ function recomputeHhCascadeForActivity(activityId, adapter) {
|
|
|
10369
10981
|
if (!parentId || isRootParent(parentId)) return;
|
|
10370
10982
|
recomputeHhCascadeForParent(parentId, adapter);
|
|
10371
10983
|
}
|
|
10984
|
+
function recomputeRealWorkFromProgress(progressIds, parentIdsDeepestFirst, adapter) {
|
|
10985
|
+
for (const activityId of progressIds) {
|
|
10986
|
+
if (adapter.hasChildren(activityId)) continue;
|
|
10987
|
+
const activity = adapter.getActivity(activityId);
|
|
10988
|
+
if (!activity) continue;
|
|
10989
|
+
adapter.setActivityField(
|
|
10990
|
+
activityId,
|
|
10991
|
+
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
10992
|
+
computeLeafRealWork(
|
|
10993
|
+
readNumber(activity, ACTIVITY_PROPERTY.WORK_HOURS),
|
|
10994
|
+
readNumber(activity, ACTIVITY_PROPERTY.PROGRESS)
|
|
10995
|
+
)
|
|
10996
|
+
);
|
|
10997
|
+
}
|
|
10998
|
+
for (const parentId of parentIdsDeepestFirst) {
|
|
10999
|
+
adapter.setActivityField(
|
|
11000
|
+
parentId,
|
|
11001
|
+
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
11002
|
+
computeParentHhRollup(parentId, adapter).realWorkHours
|
|
11003
|
+
);
|
|
11004
|
+
}
|
|
11005
|
+
}
|
|
11006
|
+
function computeParentHhRollup(parentId, adapter) {
|
|
11007
|
+
let workHours = 0;
|
|
11008
|
+
let realWorkHours = 0;
|
|
11009
|
+
for (const childId of adapter.getChildren(parentId)) {
|
|
11010
|
+
const child = adapter.getActivity(childId);
|
|
11011
|
+
if (!child) continue;
|
|
11012
|
+
const childWorkHours = readNumber(child, ACTIVITY_PROPERTY.WORK_HOURS);
|
|
11013
|
+
workHours += childWorkHours;
|
|
11014
|
+
realWorkHours += readChildRealWork(childId, child, childWorkHours, adapter);
|
|
11015
|
+
}
|
|
11016
|
+
return { workHours, realWorkHours };
|
|
11017
|
+
}
|
|
10372
11018
|
function readNumber(activity, key) {
|
|
10373
11019
|
const v = activity[key];
|
|
10374
11020
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
@@ -10586,6 +11232,11 @@ function buildNewActivity(input, adapter) {
|
|
|
10586
11232
|
...base,
|
|
10587
11233
|
startDate,
|
|
10588
11234
|
endDate,
|
|
11235
|
+
calendarDuration: computeCalendarDuration({
|
|
11236
|
+
type: base.type,
|
|
11237
|
+
startDate,
|
|
11238
|
+
endDate
|
|
11239
|
+
}),
|
|
10589
11240
|
newActivityIds: [...base.newActivityIds],
|
|
10590
11241
|
pendingRequestIds: [...base.pendingRequestIds],
|
|
10591
11242
|
responsableIds: [...base.responsableIds],
|
|
@@ -10736,6 +11387,36 @@ function computeFractionalCidAtIndex(adapter, parentKey, targetIndex, excludeId)
|
|
|
10736
11387
|
return (before + after) / 2;
|
|
10737
11388
|
}
|
|
10738
11389
|
|
|
11390
|
+
// src/dispatch/creation-overrides.ts
|
|
11391
|
+
var PUBLIC_CREATION_OVERRIDE_FIELDS = /* @__PURE__ */ new Set([
|
|
11392
|
+
...[...CREATION_OVERRIDE_FIELDS].filter((field) => field !== "durationHours"),
|
|
11393
|
+
"durationDays"
|
|
11394
|
+
]);
|
|
11395
|
+
function normalizeActivityCreationOverrides(raw, hoursPerDay) {
|
|
11396
|
+
if (raw === void 0) return { ok: true, value: {} };
|
|
11397
|
+
const prototype = Object.getPrototypeOf(raw);
|
|
11398
|
+
if (prototype !== null && prototype !== Object.prototype) {
|
|
11399
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11400
|
+
}
|
|
11401
|
+
for (const key of Reflect.ownKeys(raw)) {
|
|
11402
|
+
if (typeof key === "symbol" || !PUBLIC_CREATION_OVERRIDE_FIELDS.has(key)) {
|
|
11403
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11404
|
+
}
|
|
11405
|
+
}
|
|
11406
|
+
const { durationDays, ...rest } = raw;
|
|
11407
|
+
if (durationDays !== void 0 && (!Number.isFinite(durationDays) || durationDays < 0)) {
|
|
11408
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11409
|
+
}
|
|
11410
|
+
return {
|
|
11411
|
+
ok: true,
|
|
11412
|
+
value: {
|
|
11413
|
+
...rest,
|
|
11414
|
+
...durationDays === 0 && (rest.type === void 0 || rest.type === "task") ? { type: "milestone" } : {},
|
|
11415
|
+
...durationDays === void 0 ? {} : { durationHours: durationToHours(durationDays, hoursPerDay) }
|
|
11416
|
+
}
|
|
11417
|
+
};
|
|
11418
|
+
}
|
|
11419
|
+
|
|
10739
11420
|
// src/dispatch/handlers/activity-create.ts
|
|
10740
11421
|
async function createActivityCore(action, deps, opts = {}) {
|
|
10741
11422
|
const { adapter, scheduler, sector, calendars, activityIdGen, uidGen } = deps;
|
|
@@ -10743,7 +11424,12 @@ async function createActivityCore(action, deps, opts = {}) {
|
|
|
10743
11424
|
const target = validateCreateTarget(action, adapter);
|
|
10744
11425
|
if (!target.ok) return target;
|
|
10745
11426
|
const { parent, anchorSiblingId, isRoot } = target;
|
|
10746
|
-
const newId = resolveNewActivityId(
|
|
11427
|
+
const newId = resolveNewActivityId(
|
|
11428
|
+
action.activityId,
|
|
11429
|
+
adapter,
|
|
11430
|
+
activityIdGen,
|
|
11431
|
+
deps.isActivityIdReserved
|
|
11432
|
+
);
|
|
10747
11433
|
const newUid = uidGen.next();
|
|
10748
11434
|
const activityInput = {
|
|
10749
11435
|
id: newId,
|
|
@@ -10836,9 +11522,18 @@ function validateAnchorSibling(action, adapter, isRoot, anchorSiblingId) {
|
|
|
10836
11522
|
}
|
|
10837
11523
|
return null;
|
|
10838
11524
|
}
|
|
10839
|
-
function resolveNewActivityId(explicitId, adapter, activityIdGen) {
|
|
10840
|
-
|
|
10841
|
-
|
|
11525
|
+
function resolveNewActivityId(explicitId, adapter, activityIdGen, isActivityIdReserved) {
|
|
11526
|
+
const nextAvailableId = () => {
|
|
11527
|
+
let candidate = activityIdGen.next();
|
|
11528
|
+
while (adapter.activityExists(candidate) || isActivityIdReserved(String(candidate))) {
|
|
11529
|
+
candidate = activityIdGen.next();
|
|
11530
|
+
}
|
|
11531
|
+
return candidate;
|
|
11532
|
+
};
|
|
11533
|
+
if (explicitId === void 0) return nextAvailableId();
|
|
11534
|
+
if (adapter.activityExists(explicitId) || isActivityIdReserved(String(explicitId))) {
|
|
11535
|
+
return nextAvailableId();
|
|
11536
|
+
}
|
|
10842
11537
|
activityIdGen.bump(explicitId);
|
|
10843
11538
|
return explicitId;
|
|
10844
11539
|
}
|
|
@@ -10917,7 +11612,15 @@ async function buildCreateChangeSet(adapter, action, coreResult, scheduledIds, h
|
|
|
10917
11612
|
}
|
|
10918
11613
|
async function dispatchActivityCreate(action, options, deps) {
|
|
10919
11614
|
const { adapter, scheduler, sector } = deps;
|
|
10920
|
-
const
|
|
11615
|
+
const normalized2 = normalizeActivityCreationOverrides(
|
|
11616
|
+
action.overrides,
|
|
11617
|
+
sector.hoursPerDay
|
|
11618
|
+
);
|
|
11619
|
+
if (!normalized2.ok) return normalized2;
|
|
11620
|
+
const core = await createActivityCore(
|
|
11621
|
+
{ ...action, overrides: normalized2.value },
|
|
11622
|
+
deps
|
|
11623
|
+
);
|
|
10921
11624
|
if (!core.ok) return core;
|
|
10922
11625
|
const { newId } = core;
|
|
10923
11626
|
const createdAutoScheduling = core.newActivity.autoScheduling;
|
|
@@ -11129,7 +11832,7 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11129
11832
|
for (const linkId of incidentLinkIds) {
|
|
11130
11833
|
adapter.removeLink(linkId);
|
|
11131
11834
|
}
|
|
11132
|
-
const
|
|
11835
|
+
const viewStateChanges2 = captureDeletedViewState(adapter, toDelete);
|
|
11133
11836
|
let removed = 0;
|
|
11134
11837
|
for (const id of toDelete) {
|
|
11135
11838
|
removed += 1;
|
|
@@ -11218,7 +11921,7 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11218
11921
|
});
|
|
11219
11922
|
return {
|
|
11220
11923
|
ok: true,
|
|
11221
|
-
changes:
|
|
11924
|
+
changes: viewStateChanges2.length > 0 ? { ...changes, viewState: viewStateChanges2 } : changes,
|
|
11222
11925
|
__beforeSnap: deletedRowsSnap,
|
|
11223
11926
|
__beforeLinks: beforeLinksByStringId
|
|
11224
11927
|
};
|
|
@@ -11326,7 +12029,7 @@ async function dispatchActivityBatch(action, options, deps) {
|
|
|
11326
12029
|
source: link.source,
|
|
11327
12030
|
target: link.target,
|
|
11328
12031
|
type: link.type,
|
|
11329
|
-
lag:
|
|
12032
|
+
lag: lagDaysToHours(link.lag, deps.sector.hoursPerDay)
|
|
11330
12033
|
},
|
|
11331
12034
|
linkId: link.linkId
|
|
11332
12035
|
}));
|
|
@@ -11398,7 +12101,7 @@ function validateBatch(action, deps) {
|
|
|
11398
12101
|
}
|
|
11399
12102
|
if (item.activityId !== void 0) {
|
|
11400
12103
|
const activityId = String(item.activityId);
|
|
11401
|
-
if (activityId.length === 0 || activityId === "0" || explicitActivityIds.has(activityId)
|
|
12104
|
+
if (activityId.length === 0 || activityId === "0" || explicitActivityIds.has(activityId)) {
|
|
11402
12105
|
return REJECTION_REASON.INVALID;
|
|
11403
12106
|
}
|
|
11404
12107
|
explicitActivityIds.add(activityId);
|
|
@@ -11420,7 +12123,7 @@ function validateBatch(action, deps) {
|
|
|
11420
12123
|
}
|
|
11421
12124
|
if (link.linkId !== void 0) {
|
|
11422
12125
|
const linkId = String(link.linkId);
|
|
11423
|
-
if (linkId.length === 0 || explicitLinkIds.has(linkId)
|
|
12126
|
+
if (linkId.length === 0 || explicitLinkIds.has(linkId)) {
|
|
11424
12127
|
return REJECTION_REASON.INVALID;
|
|
11425
12128
|
}
|
|
11426
12129
|
explicitLinkIds.add(linkId);
|
|
@@ -11456,24 +12159,38 @@ function hasParentCycle(items) {
|
|
|
11456
12159
|
return false;
|
|
11457
12160
|
}
|
|
11458
12161
|
function buildBatchPlan(action, deps) {
|
|
12162
|
+
const normalizedOverrides = /* @__PURE__ */ new Map();
|
|
11459
12163
|
for (const item of action.items) {
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
|
|
12164
|
+
const normalized2 = normalizeActivityCreationOverrides(
|
|
12165
|
+
item.overrides,
|
|
12166
|
+
deps.sector.hoursPerDay
|
|
12167
|
+
);
|
|
12168
|
+
if (!normalized2.ok) return normalized2;
|
|
12169
|
+
normalizedOverrides.set(item.batchId, normalized2.value);
|
|
11463
12170
|
}
|
|
11464
12171
|
const activityIdsByBatchId = /* @__PURE__ */ new Map();
|
|
12172
|
+
const plannedActivityIds = /* @__PURE__ */ new Set();
|
|
11465
12173
|
for (const item of action.items) {
|
|
11466
|
-
|
|
11467
|
-
item.
|
|
11468
|
-
|
|
12174
|
+
const activityId = resolvePlannedActivityId(
|
|
12175
|
+
item.activityId,
|
|
12176
|
+
action.mode,
|
|
12177
|
+
plannedActivityIds,
|
|
12178
|
+
deps
|
|
11469
12179
|
);
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
const linkIds = action.links.map(
|
|
11475
|
-
|
|
11476
|
-
|
|
12180
|
+
plannedActivityIds.add(String(activityId));
|
|
12181
|
+
activityIdsByBatchId.set(item.batchId, activityId);
|
|
12182
|
+
}
|
|
12183
|
+
const plannedLinkIds = /* @__PURE__ */ new Set();
|
|
12184
|
+
const linkIds = action.links.map((link) => {
|
|
12185
|
+
const linkId = resolvePlannedLinkId(
|
|
12186
|
+
link.linkId,
|
|
12187
|
+
action.mode,
|
|
12188
|
+
plannedLinkIds,
|
|
12189
|
+
deps
|
|
12190
|
+
);
|
|
12191
|
+
plannedLinkIds.add(String(linkId));
|
|
12192
|
+
return linkId;
|
|
12193
|
+
});
|
|
11477
12194
|
const insertionCorrelativeIds = planInsertionCorrelatives(
|
|
11478
12195
|
action,
|
|
11479
12196
|
activityIdsByBatchId,
|
|
@@ -11496,7 +12213,8 @@ function buildBatchPlan(action, deps) {
|
|
|
11496
12213
|
activityId,
|
|
11497
12214
|
parentId,
|
|
11498
12215
|
parentBatchId,
|
|
11499
|
-
insertionCorrelativeId
|
|
12216
|
+
insertionCorrelativeId,
|
|
12217
|
+
overrides: normalizedOverrides.get(item.batchId) ?? {}
|
|
11500
12218
|
});
|
|
11501
12219
|
}
|
|
11502
12220
|
const links = [];
|
|
@@ -11517,6 +12235,30 @@ function buildBatchPlan(action, deps) {
|
|
|
11517
12235
|
}
|
|
11518
12236
|
return { ok: true, plan: { items, links } };
|
|
11519
12237
|
}
|
|
12238
|
+
function resolvePlannedActivityId(explicitId, mode, plannedIds, deps) {
|
|
12239
|
+
const explicitIsAvailable = explicitId !== void 0 && (mode === "replace" || deps.adapter.getActivity(explicitId) === null && !deps.isActivityIdReserved(String(explicitId))) && !plannedIds.has(String(explicitId));
|
|
12240
|
+
if (explicitIsAvailable) {
|
|
12241
|
+
deps.activityIdGen.bump(explicitId);
|
|
12242
|
+
return explicitId;
|
|
12243
|
+
}
|
|
12244
|
+
let candidate = deps.activityIdGen.next();
|
|
12245
|
+
while (plannedIds.has(String(candidate)) || mode === "append" && (deps.adapter.getActivity(candidate) !== null || deps.isActivityIdReserved(String(candidate)))) {
|
|
12246
|
+
candidate = deps.activityIdGen.next();
|
|
12247
|
+
}
|
|
12248
|
+
return candidate;
|
|
12249
|
+
}
|
|
12250
|
+
function resolvePlannedLinkId(explicitId, mode, plannedIds, deps) {
|
|
12251
|
+
const explicitIsAvailable = explicitId !== void 0 && (mode === "replace" || deps.adapter.getLink(explicitId) === null && !deps.isLinkIdReserved(String(explicitId))) && !plannedIds.has(String(explicitId));
|
|
12252
|
+
if (explicitIsAvailable) {
|
|
12253
|
+
deps.linkIdGen.bump(explicitId);
|
|
12254
|
+
return explicitId;
|
|
12255
|
+
}
|
|
12256
|
+
let candidate = deps.linkIdGen.next();
|
|
12257
|
+
while (plannedIds.has(String(candidate)) || mode === "append" && (deps.adapter.getLink(candidate) !== null || deps.isLinkIdReserved(String(candidate)))) {
|
|
12258
|
+
candidate = deps.linkIdGen.next();
|
|
12259
|
+
}
|
|
12260
|
+
return candidate;
|
|
12261
|
+
}
|
|
11520
12262
|
function planInsertionCorrelatives(action, activityIdsByBatchId, deps) {
|
|
11521
12263
|
if (isSimpleRootAppend(action)) {
|
|
11522
12264
|
const firstCorrelativeId = deps.adapter.activityCount();
|
|
@@ -11626,7 +12368,7 @@ async function createPlannedActivities(action, plannedItems, deps) {
|
|
|
11626
12368
|
{
|
|
11627
12369
|
parentId: item.parentId,
|
|
11628
12370
|
activityId: item.activityId,
|
|
11629
|
-
|
|
12371
|
+
overrides: item.overrides,
|
|
11630
12372
|
...action.eventSource === void 0 ? {} : { eventSource: action.eventSource }
|
|
11631
12373
|
},
|
|
11632
12374
|
deps,
|
|
@@ -11668,180 +12410,299 @@ function stringKeyedLinks(links) {
|
|
|
11668
12410
|
return new Map(Array.from(links, ([linkId, link]) => [String(linkId), link]));
|
|
11669
12411
|
}
|
|
11670
12412
|
|
|
11671
|
-
// src/dispatch/handlers/
|
|
11672
|
-
function
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
}
|
|
11678
|
-
const parentId2 = sibling.parentId == null ? ROOT_PARENT_ID : String(sibling.parentId);
|
|
11679
|
-
return { ok: true, parentId: parentId2, firstAfter: destination.afterSiblingId };
|
|
12413
|
+
// src/dispatch/handlers/sir-sync.ts
|
|
12414
|
+
function dispatchSirSync(action, deps) {
|
|
12415
|
+
const { adapter } = deps;
|
|
12416
|
+
const id = action.activityId;
|
|
12417
|
+
const snapshot = adapter.getActivity(id);
|
|
12418
|
+
if (!snapshot) {
|
|
12419
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
11680
12420
|
}
|
|
11681
|
-
const
|
|
11682
|
-
const
|
|
11683
|
-
|
|
11684
|
-
);
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
}
|
|
11688
|
-
|
|
11689
|
-
|
|
12421
|
+
const before = snapshot.pendingRequestIds ?? [];
|
|
12422
|
+
const after = action.pendingRequests.map((r) => r.id);
|
|
12423
|
+
adapter.setActivityField(action.activityId, "pendingRequestIds", after);
|
|
12424
|
+
const updated = adapter.getActivity(id);
|
|
12425
|
+
if (!updated) {
|
|
12426
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
12427
|
+
}
|
|
12428
|
+
const change = {
|
|
12429
|
+
id,
|
|
12430
|
+
kind: "updated",
|
|
12431
|
+
fields: { pendingRequestIds: { before, after } },
|
|
12432
|
+
// ISSUE-025: `after` must be the COMPLETE post-change entity; a partial
|
|
12433
|
+
// object crashes any consumer that projects the full snapshot.
|
|
12434
|
+
after: structuredCloneActivity(updated)
|
|
12435
|
+
};
|
|
11690
12436
|
return {
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
12437
|
+
ok: true,
|
|
12438
|
+
changes: {
|
|
12439
|
+
source: action,
|
|
12440
|
+
activities: [change],
|
|
12441
|
+
links: [],
|
|
12442
|
+
calendars: [],
|
|
12443
|
+
trackingEvents: []
|
|
12444
|
+
}
|
|
11697
12445
|
};
|
|
11698
12446
|
}
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
12447
|
+
|
|
12448
|
+
// src/propagations/upward/progress-rollup.ts
|
|
12449
|
+
function recomputeAllProgressRollup(adapter) {
|
|
12450
|
+
for (const parentId of parentsDeepestFirst(adapter)) {
|
|
12451
|
+
rollupParent(parentId, adapter);
|
|
12452
|
+
}
|
|
12453
|
+
}
|
|
12454
|
+
function recomputeProgressRollupForParents(parentIdsDeepestFirst, adapter) {
|
|
12455
|
+
for (const parentId of parentIdsDeepestFirst) {
|
|
12456
|
+
rollupParent(parentId, adapter);
|
|
12457
|
+
}
|
|
12458
|
+
}
|
|
12459
|
+
function parentsDeepestFirst(adapter) {
|
|
12460
|
+
const parents = /* @__PURE__ */ new Set();
|
|
12461
|
+
adapter.forEachActivity((_activity, id) => {
|
|
12462
|
+
if (adapter.getChildren(id).length > 0) parents.add(id);
|
|
12463
|
+
});
|
|
12464
|
+
const depthById = /* @__PURE__ */ new Map();
|
|
12465
|
+
for (const id of parents) depthById.set(id, depthOf2(id, adapter));
|
|
12466
|
+
return [...parents].sort(
|
|
12467
|
+
(a, b) => (depthById.get(b) ?? 0) - (depthById.get(a) ?? 0)
|
|
12468
|
+
);
|
|
12469
|
+
}
|
|
12470
|
+
function depthOf2(id, adapter) {
|
|
12471
|
+
let depth = 0;
|
|
12472
|
+
let current = id;
|
|
12473
|
+
while (current && current !== "0") {
|
|
12474
|
+
if (!adapter.getActivity(current)) break;
|
|
12475
|
+
depth += 1;
|
|
12476
|
+
current = adapter.getParentId(current);
|
|
12477
|
+
}
|
|
12478
|
+
return depth;
|
|
12479
|
+
}
|
|
12480
|
+
function rollupParent(parentId, adapter) {
|
|
12481
|
+
const childIds = adapter.getChildren(parentId);
|
|
12482
|
+
if (childIds.length === 0) return;
|
|
12483
|
+
const children = [];
|
|
12484
|
+
for (const childId of childIds) {
|
|
12485
|
+
const child = adapter.getActivity(childId);
|
|
12486
|
+
if (!child) continue;
|
|
12487
|
+
children.push({
|
|
12488
|
+
progress: Number(child.progress ?? 0),
|
|
12489
|
+
ponderator: Number(child.ponderator ?? 0)
|
|
12490
|
+
});
|
|
12491
|
+
}
|
|
12492
|
+
const rollup = computeWeightedProgressRollup(children);
|
|
12493
|
+
if (rollup !== null) {
|
|
12494
|
+
adapter.setActivityField(
|
|
12495
|
+
parentId,
|
|
12496
|
+
COLUMN.PROGRESS,
|
|
12497
|
+
roundProgressPerLevel(rollup)
|
|
12498
|
+
);
|
|
12499
|
+
}
|
|
12500
|
+
}
|
|
12501
|
+
|
|
12502
|
+
// src/dispatch/handlers/persistence-acknowledge.ts
|
|
12503
|
+
function persistedOwnersOf(entities) {
|
|
12504
|
+
const owners = /* @__PURE__ */ new Map();
|
|
12505
|
+
for (const entity of entities) {
|
|
12506
|
+
const proplannerId = entity.proplannerId;
|
|
12507
|
+
if (typeof proplannerId === "number" && Number.isSafeInteger(proplannerId) && proplannerId > 0) {
|
|
12508
|
+
owners.set(proplannerId, String(entity.id));
|
|
11723
12509
|
}
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
12510
|
+
}
|
|
12511
|
+
return owners;
|
|
12512
|
+
}
|
|
12513
|
+
function assertAllNewEntitiesAcknowledged(entities, assignments, entityName) {
|
|
12514
|
+
const acknowledgedIds = new Set(assignments.map(({ id }) => String(id)));
|
|
12515
|
+
for (const entity of entities) {
|
|
12516
|
+
if ((entity.proplannerId == null || entity.proplannerId === 0) && !acknowledgedIds.has(String(entity.id))) {
|
|
12517
|
+
throw new Error(
|
|
12518
|
+
`persistence-acknowledge: missing ${entityName} proplannerId for "${String(entity.id)}"`
|
|
12519
|
+
);
|
|
12520
|
+
}
|
|
12521
|
+
}
|
|
12522
|
+
}
|
|
12523
|
+
function assertValidAssignment(assignment, entityName, readCurrent, ownerByBackendId, seenIds, seenBackendIds) {
|
|
12524
|
+
const id = String(assignment.id);
|
|
12525
|
+
if (id.length === 0 || seenIds.has(id)) {
|
|
12526
|
+
throw new Error(
|
|
12527
|
+
`persistence-acknowledge: duplicate or empty ${entityName} id "${id}"`
|
|
11739
12528
|
);
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
|
|
12529
|
+
}
|
|
12530
|
+
if (!Number.isSafeInteger(assignment.proplannerId) || assignment.proplannerId <= 0) {
|
|
12531
|
+
throw new Error(
|
|
12532
|
+
`persistence-acknowledge: invalid ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
12533
|
+
);
|
|
12534
|
+
}
|
|
12535
|
+
if (seenBackendIds.has(assignment.proplannerId)) {
|
|
12536
|
+
throw new Error(
|
|
12537
|
+
`persistence-acknowledge: duplicate ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
12538
|
+
);
|
|
12539
|
+
}
|
|
12540
|
+
const state = readCurrent(id);
|
|
12541
|
+
if (!state.exists) {
|
|
12542
|
+
throw new Error(
|
|
12543
|
+
`persistence-acknowledge: unknown ${entityName} id "${id}"`
|
|
12544
|
+
);
|
|
12545
|
+
}
|
|
12546
|
+
if (state.current !== null && state.current !== 0 && state.current !== assignment.proplannerId) {
|
|
12547
|
+
throw new Error(
|
|
12548
|
+
`persistence-acknowledge: ${entityName} "${id}" already has proplannerId ${String(state.current)}`
|
|
12549
|
+
);
|
|
12550
|
+
}
|
|
12551
|
+
const existingOwner = ownerByBackendId.get(assignment.proplannerId);
|
|
12552
|
+
if (existingOwner !== void 0 && existingOwner !== id) {
|
|
12553
|
+
throw new Error(
|
|
12554
|
+
`persistence-acknowledge: ${entityName} proplannerId ${String(assignment.proplannerId)} already belongs to "${existingOwner}"`
|
|
12555
|
+
);
|
|
12556
|
+
}
|
|
12557
|
+
seenIds.add(id);
|
|
12558
|
+
seenBackendIds.add(assignment.proplannerId);
|
|
12559
|
+
}
|
|
12560
|
+
function assertValidAssignments(assignments, entityName, readCurrent, ownerByBackendId) {
|
|
12561
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
12562
|
+
const seenBackendIds = /* @__PURE__ */ new Set();
|
|
12563
|
+
for (const assignment of assignments) {
|
|
12564
|
+
assertValidAssignment(
|
|
12565
|
+
assignment,
|
|
12566
|
+
entityName,
|
|
12567
|
+
readCurrent,
|
|
12568
|
+
ownerByBackendId,
|
|
12569
|
+
seenIds,
|
|
12570
|
+
seenBackendIds
|
|
12571
|
+
);
|
|
12572
|
+
}
|
|
12573
|
+
}
|
|
12574
|
+
function assertValidReconciledProgress(entries, deps) {
|
|
12575
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12576
|
+
for (const entry of entries) {
|
|
12577
|
+
const id = String(entry.activityId);
|
|
12578
|
+
if (id.length === 0 || seen.has(id)) {
|
|
12579
|
+
throw new Error(
|
|
12580
|
+
`persistence-acknowledge: duplicate or empty reconciled activity id "${id}"`
|
|
12581
|
+
);
|
|
12582
|
+
}
|
|
12583
|
+
if (!Number.isFinite(entry.progress) || entry.progress < 0 || entry.progress > 100) {
|
|
12584
|
+
throw new Error(
|
|
12585
|
+
`persistence-acknowledge: invalid reconciled progress ${String(entry.progress)} for "${id}"`
|
|
11751
12586
|
);
|
|
11752
12587
|
}
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
12588
|
+
if (!deps.adapter.getActivity(id)) {
|
|
12589
|
+
throw new Error(
|
|
12590
|
+
`persistence-acknowledge: unknown reconciled activity id "${id}"`
|
|
12591
|
+
);
|
|
12592
|
+
}
|
|
12593
|
+
seen.add(id);
|
|
11756
12594
|
}
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
adapter
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
12595
|
+
}
|
|
12596
|
+
async function applyReconciledProgress(entries, deps) {
|
|
12597
|
+
const progressToApply = entries.filter((entry) => {
|
|
12598
|
+
const current = deps.adapter.getActivity(String(entry.activityId));
|
|
12599
|
+
return current !== null && current.progress !== entry.progress;
|
|
12600
|
+
});
|
|
12601
|
+
const progressIds = new Set(
|
|
12602
|
+
progressToApply.map((entry) => String(entry.activityId))
|
|
11765
12603
|
);
|
|
11766
|
-
|
|
11767
|
-
const
|
|
11768
|
-
for (const
|
|
11769
|
-
|
|
11770
|
-
const newTarget = originalToNew.get(link.target);
|
|
11771
|
-
if (newSource === void 0 || newTarget === void 0) continue;
|
|
11772
|
-
const op = {
|
|
11773
|
-
kind: "create",
|
|
11774
|
-
source: newSource,
|
|
11775
|
-
target: newTarget,
|
|
11776
|
-
type: link.type,
|
|
11777
|
-
lag: lagDaysToHours(link.lag, sector.hoursPerDay)
|
|
11778
|
-
};
|
|
11779
|
-
const res = applyLinkOperation(op, {
|
|
11780
|
-
port: adapter,
|
|
11781
|
-
newLinkId: link.linkId ? () => link.linkId : deps.linkIdGen.next
|
|
11782
|
-
});
|
|
11783
|
-
appliedLinks.push({
|
|
11784
|
-
op,
|
|
11785
|
-
finalLinkId: res.applied ? res.linkId : null,
|
|
11786
|
-
rejected: res.applied ? null : res.rejected ?? "link_op_rejected"
|
|
11787
|
-
});
|
|
12604
|
+
const ancestors = findAncestorsOfDirty(deps.adapter, progressIds);
|
|
12605
|
+
const touched = new Set(progressIds);
|
|
12606
|
+
for (const parentId of ancestors) {
|
|
12607
|
+
touched.add(String(parentId));
|
|
11788
12608
|
}
|
|
11789
|
-
const
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
action,
|
|
11798
|
-
autoscheduleFrom: createdIds[0] ?? null,
|
|
11799
|
-
recomputeParentsFrom: createdIds,
|
|
11800
|
-
now: deps.now,
|
|
11801
|
-
options
|
|
12609
|
+
const before = await snapshotActivities(deps.adapter, touched);
|
|
12610
|
+
deps.adapter.batchUpdate(() => {
|
|
12611
|
+
for (const entry of progressToApply) {
|
|
12612
|
+
deps.adapter.setActivityField(
|
|
12613
|
+
String(entry.activityId),
|
|
12614
|
+
"progress",
|
|
12615
|
+
entry.progress
|
|
12616
|
+
);
|
|
11802
12617
|
}
|
|
12618
|
+
recomputeProgressRollupForParents(ancestors, deps.adapter);
|
|
12619
|
+
recomputeRealWorkFromProgress(progressIds, ancestors, deps.adapter);
|
|
12620
|
+
});
|
|
12621
|
+
return [...await buildActivityChanges(deps.adapter, before, touched)];
|
|
12622
|
+
}
|
|
12623
|
+
async function dispatchPersistenceAcknowledge(action, deps) {
|
|
12624
|
+
const activities = action.activities ?? [];
|
|
12625
|
+
const links = action.links ?? [];
|
|
12626
|
+
const reconciledProgress = action.reconciledProgress ?? [];
|
|
12627
|
+
assertValidReconciledProgress(reconciledProgress, deps);
|
|
12628
|
+
const currentActivities = deps.adapter.getAllActivities();
|
|
12629
|
+
const currentLinks = deps.adapter.getAllLinks();
|
|
12630
|
+
const activityOwnerByBackendId = persistedOwnersOf(currentActivities);
|
|
12631
|
+
const linkOwnerByBackendId = persistedOwnersOf(currentLinks);
|
|
12632
|
+
assertValidAssignments(
|
|
12633
|
+
activities,
|
|
12634
|
+
"activity",
|
|
12635
|
+
(id) => {
|
|
12636
|
+
const activity = deps.adapter.getActivity(id);
|
|
12637
|
+
return {
|
|
12638
|
+
exists: activity !== null,
|
|
12639
|
+
current: activity?.proplannerId ?? null
|
|
12640
|
+
};
|
|
12641
|
+
},
|
|
12642
|
+
activityOwnerByBackendId
|
|
11803
12643
|
);
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
|
|
12644
|
+
assertValidAssignments(
|
|
12645
|
+
links,
|
|
12646
|
+
"link",
|
|
12647
|
+
(id) => {
|
|
12648
|
+
const link = deps.adapter.getLink(id);
|
|
12649
|
+
return { exists: link !== null, current: link?.proplannerId ?? null };
|
|
12650
|
+
},
|
|
12651
|
+
linkOwnerByBackendId
|
|
11808
12652
|
);
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
12653
|
+
assertAllNewEntitiesAcknowledged(currentActivities, activities, "activity");
|
|
12654
|
+
assertAllNewEntitiesAcknowledged(currentLinks, links, "link");
|
|
12655
|
+
const activityChanges = await applyReconciledProgress(
|
|
12656
|
+
reconciledProgress,
|
|
12657
|
+
deps
|
|
12658
|
+
);
|
|
12659
|
+
const linkChanges = [];
|
|
12660
|
+
deps.adapter.batchUpdate(() => {
|
|
12661
|
+
for (const assignment of activities) {
|
|
12662
|
+
const before = deps.adapter.getActivity(assignment.id);
|
|
12663
|
+
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
12664
|
+
const previousProplannerId = before.proplannerId;
|
|
12665
|
+
deps.adapter.setActivityField(
|
|
12666
|
+
assignment.id,
|
|
12667
|
+
"proplannerId",
|
|
12668
|
+
assignment.proplannerId
|
|
12669
|
+
);
|
|
12670
|
+
activityChanges.push({
|
|
12671
|
+
id: assignment.id,
|
|
12672
|
+
kind: "updated",
|
|
12673
|
+
fields: {
|
|
12674
|
+
proplannerId: {
|
|
12675
|
+
before: previousProplannerId,
|
|
12676
|
+
after: assignment.proplannerId
|
|
12677
|
+
}
|
|
12678
|
+
},
|
|
12679
|
+
after: { ...before, proplannerId: assignment.proplannerId }
|
|
12680
|
+
});
|
|
12681
|
+
}
|
|
12682
|
+
for (const assignment of links) {
|
|
12683
|
+
const before = deps.adapter.getLink(assignment.id);
|
|
12684
|
+
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
12685
|
+
const previousProplannerId = before.proplannerId;
|
|
12686
|
+
deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
|
|
12687
|
+
linkChanges.push({
|
|
12688
|
+
id: assignment.id,
|
|
12689
|
+
kind: "updated",
|
|
12690
|
+
fields: {
|
|
12691
|
+
proplannerId: {
|
|
12692
|
+
before: previousProplannerId,
|
|
12693
|
+
after: assignment.proplannerId
|
|
12694
|
+
}
|
|
12695
|
+
},
|
|
12696
|
+
after: { ...before, proplannerId: assignment.proplannerId }
|
|
12697
|
+
});
|
|
12698
|
+
}
|
|
12699
|
+
});
|
|
11839
12700
|
return {
|
|
11840
12701
|
ok: true,
|
|
11841
12702
|
changes: {
|
|
11842
12703
|
source: action,
|
|
11843
|
-
activities:
|
|
11844
|
-
links:
|
|
12704
|
+
activities: activityChanges,
|
|
12705
|
+
links: linkChanges,
|
|
11845
12706
|
calendars: [],
|
|
11846
12707
|
trackingEvents: []
|
|
11847
12708
|
}
|
|
@@ -11849,8 +12710,15 @@ function dispatchSirSync(action, deps) {
|
|
|
11849
12710
|
}
|
|
11850
12711
|
|
|
11851
12712
|
// src/dispatch/handlers/activity-lookahead-sync.ts
|
|
11852
|
-
function dispatchActivityLookaheadSync(action, deps) {
|
|
12713
|
+
async function dispatchActivityLookaheadSync(action, options, deps) {
|
|
12714
|
+
if (hasInvalidPersistedProgress(action)) {
|
|
12715
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
12716
|
+
}
|
|
11853
12717
|
const ids = [...new Set(action.activityIds.map(String))];
|
|
12718
|
+
const acknowledgesUnknownPersistedActivity = action.acknowledgePersistedLookahead === true && ids.some((activityId) => !deps.isActivityIdReserved(activityId));
|
|
12719
|
+
if (acknowledgesUnknownPersistedActivity) {
|
|
12720
|
+
return { ok: false, reason: REJECTION_REASON.INVALID };
|
|
12721
|
+
}
|
|
11854
12722
|
const snapshots = ids.map((id) => deps.adapter.getActivity(id));
|
|
11855
12723
|
if (snapshots.some((snapshot) => !snapshot)) {
|
|
11856
12724
|
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
@@ -11890,6 +12758,39 @@ function dispatchActivityLookaheadSync(action, deps) {
|
|
|
11890
12758
|
}
|
|
11891
12759
|
});
|
|
11892
12760
|
}
|
|
12761
|
+
if (action.persistedProgress !== void 0) {
|
|
12762
|
+
const progressChanges = await applyReconciledProgress(
|
|
12763
|
+
[{ activityId: ids[0], progress: action.persistedProgress }],
|
|
12764
|
+
deps
|
|
12765
|
+
);
|
|
12766
|
+
if (progressChanges.length > 0) {
|
|
12767
|
+
await runPostMutation(
|
|
12768
|
+
{
|
|
12769
|
+
adapter: deps.adapter,
|
|
12770
|
+
scheduler: deps.scheduler,
|
|
12771
|
+
sector: deps.sector,
|
|
12772
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
12773
|
+
},
|
|
12774
|
+
{
|
|
12775
|
+
action,
|
|
12776
|
+
autoscheduleFrom: null,
|
|
12777
|
+
recomputeParentsFrom: [],
|
|
12778
|
+
now: deps.now,
|
|
12779
|
+
options
|
|
12780
|
+
}
|
|
12781
|
+
);
|
|
12782
|
+
return {
|
|
12783
|
+
ok: true,
|
|
12784
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
12785
|
+
source: action,
|
|
12786
|
+
beforeSnap: /* @__PURE__ */ new Map(),
|
|
12787
|
+
touchedIds: [],
|
|
12788
|
+
scheduledIds: [],
|
|
12789
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
12790
|
+
})
|
|
12791
|
+
};
|
|
12792
|
+
}
|
|
12793
|
+
}
|
|
11893
12794
|
return {
|
|
11894
12795
|
ok: true,
|
|
11895
12796
|
changes: {
|
|
@@ -11901,6 +12802,10 @@ function dispatchActivityLookaheadSync(action, deps) {
|
|
|
11901
12802
|
}
|
|
11902
12803
|
};
|
|
11903
12804
|
}
|
|
12805
|
+
function hasInvalidPersistedProgress(action) {
|
|
12806
|
+
const progress = action.persistedProgress;
|
|
12807
|
+
return progress !== void 0 && (action.activityIds.length !== 1 || !Number.isFinite(progress) || progress < 0 || progress > 100);
|
|
12808
|
+
}
|
|
11904
12809
|
|
|
11905
12810
|
// src/internal/hierarchy/invalid-links.ts
|
|
11906
12811
|
function collectInvalidLinkIdsForCycle(movedActivityIds, adapter) {
|
|
@@ -11934,6 +12839,37 @@ function isAncestor(descendantId, candidateAncestorId, adapter) {
|
|
|
11934
12839
|
return false;
|
|
11935
12840
|
}
|
|
11936
12841
|
|
|
12842
|
+
// src/dispatch/shared/hierarchy-cycle-guard.ts
|
|
12843
|
+
function reparentClosesEffectiveCycle(adapter, reparented) {
|
|
12844
|
+
const links = adapter.getAllLinks();
|
|
12845
|
+
const verdict = validateTopologyCommit({
|
|
12846
|
+
port: adapter,
|
|
12847
|
+
currentLinks: links,
|
|
12848
|
+
proposedLinks: links,
|
|
12849
|
+
proposal: { reparented }
|
|
12850
|
+
});
|
|
12851
|
+
return !verdict.ok;
|
|
12852
|
+
}
|
|
12853
|
+
function collectCyclicReparents(planned, adapter) {
|
|
12854
|
+
const cyclic = /* @__PURE__ */ new Set();
|
|
12855
|
+
if (planned.length === 0) return cyclic;
|
|
12856
|
+
const combined = new Map(
|
|
12857
|
+
planned.map((reparent) => [reparent.activityId, reparent.newParentId])
|
|
12858
|
+
);
|
|
12859
|
+
if (!reparentClosesEffectiveCycle(adapter, combined)) return cyclic;
|
|
12860
|
+
const accepted = /* @__PURE__ */ new Map();
|
|
12861
|
+
for (const reparent of planned) {
|
|
12862
|
+
const trial = new Map(accepted);
|
|
12863
|
+
trial.set(reparent.activityId, reparent.newParentId);
|
|
12864
|
+
if (reparentClosesEffectiveCycle(adapter, trial)) {
|
|
12865
|
+
cyclic.add(reparent.activityId);
|
|
12866
|
+
continue;
|
|
12867
|
+
}
|
|
12868
|
+
accepted.set(reparent.activityId, reparent.newParentId);
|
|
12869
|
+
}
|
|
12870
|
+
return cyclic;
|
|
12871
|
+
}
|
|
12872
|
+
|
|
11937
12873
|
// src/dispatch/handlers/activity-move.ts
|
|
11938
12874
|
async function dispatchActivityMove(action, options, deps) {
|
|
11939
12875
|
const { adapter, scheduler, sector } = deps;
|
|
@@ -11975,6 +12911,17 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
11975
12911
|
const oldParentKey = normalizeParentKey(oldParent);
|
|
11976
12912
|
const newParentKey = String(action.parentId);
|
|
11977
12913
|
const parentChanged = oldParentKey !== newParentKey;
|
|
12914
|
+
if (parentChanged) {
|
|
12915
|
+
const reparented = /* @__PURE__ */ new Map([
|
|
12916
|
+
[
|
|
12917
|
+
action.activityId,
|
|
12918
|
+
newParentIsRoot ? null : action.parentId
|
|
12919
|
+
]
|
|
12920
|
+
]);
|
|
12921
|
+
if (reparentClosesEffectiveCycle(adapter, reparented)) {
|
|
12922
|
+
return { ok: false, reason: REJECTION_REASON.HIERARCHY_CYCLE };
|
|
12923
|
+
}
|
|
12924
|
+
}
|
|
11978
12925
|
const touched = /* @__PURE__ */ new Set([action.activityId]);
|
|
11979
12926
|
if (parentChanged) {
|
|
11980
12927
|
if (oldParentKey !== ROOT_PARENT_ID) touched.add(oldParentKey);
|
|
@@ -12132,21 +13079,14 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
12132
13079
|
};
|
|
12133
13080
|
}
|
|
12134
13081
|
|
|
12135
|
-
// src/dispatch/
|
|
12136
|
-
|
|
12137
|
-
const
|
|
12138
|
-
scheduler.invalidateAllCaches();
|
|
12139
|
-
if (action.activityIds.length === 0) {
|
|
12140
|
-
return { ok: false, reason: REJECTION_REASON.ACTIVITY_IDS_EMPTY };
|
|
12141
|
-
}
|
|
12142
|
-
const selectedSet = new Set(action.activityIds);
|
|
12143
|
-
const touched = /* @__PURE__ */ new Set();
|
|
13082
|
+
// src/dispatch/shared/indent-planning.ts
|
|
13083
|
+
function planIndentMoves(activityIds, adapter) {
|
|
13084
|
+
const selectedSet = new Set(activityIds);
|
|
12144
13085
|
const failed = [];
|
|
12145
|
-
const succeededIds = [];
|
|
12146
13086
|
const moves = [];
|
|
12147
13087
|
const plannedIds = /* @__PURE__ */ new Set();
|
|
12148
13088
|
const blockingSet = new Set(selectedSet);
|
|
12149
|
-
const sortedIds = [...
|
|
13089
|
+
const sortedIds = [...activityIds].sort((a, b) => {
|
|
12150
13090
|
const aSnap = adapter.getActivity(a);
|
|
12151
13091
|
const bSnap = adapter.getActivity(b);
|
|
12152
13092
|
const ac = aSnap ? readCorrelativeId(aSnap) : NOT_SET_SORT_VALUE2;
|
|
@@ -12181,12 +13121,43 @@ async function dispatchActivityIndent(action, options, deps) {
|
|
|
12181
13121
|
const oldParentId = isRootParent(oldParentRaw) ? ROOT_PARENT_ID : String(oldParentRaw);
|
|
12182
13122
|
moves.push({ activityId: id, newParentId: targetParentId, oldParentId });
|
|
12183
13123
|
plannedIds.add(id);
|
|
12184
|
-
touched.add(id);
|
|
12185
|
-
touched.add(targetParentId);
|
|
12186
|
-
if (oldParentId !== ROOT_PARENT_ID) touched.add(oldParentId);
|
|
12187
13124
|
}
|
|
13125
|
+
const cyclicMoveIds = collectCyclicReparents(moves, adapter);
|
|
13126
|
+
for (const cyclicId of cyclicMoveIds) {
|
|
13127
|
+
failed.push({
|
|
13128
|
+
activityId: cyclicId,
|
|
13129
|
+
reason: REJECTION_REASON.HIERARCHY_CYCLE
|
|
13130
|
+
});
|
|
13131
|
+
}
|
|
13132
|
+
const allowedMoves = moves.filter(
|
|
13133
|
+
(move) => !cyclicMoveIds.has(move.activityId)
|
|
13134
|
+
);
|
|
13135
|
+
return { moves, cyclicMoveIds, allowedMoves, failed };
|
|
13136
|
+
}
|
|
13137
|
+
|
|
13138
|
+
// src/dispatch/handlers/activity-indent.ts
|
|
13139
|
+
async function dispatchActivityIndent(action, options, deps) {
|
|
13140
|
+
const { adapter, scheduler, sector } = deps;
|
|
13141
|
+
scheduler.invalidateAllCaches();
|
|
13142
|
+
if (action.activityIds.length === 0) {
|
|
13143
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_IDS_EMPTY };
|
|
13144
|
+
}
|
|
13145
|
+
const touched = /* @__PURE__ */ new Set();
|
|
13146
|
+
const succeededIds = [];
|
|
13147
|
+
const plan = planIndentMoves(action.activityIds, adapter);
|
|
13148
|
+
const failed = [
|
|
13149
|
+
...plan.failed
|
|
13150
|
+
];
|
|
13151
|
+
for (const move of plan.moves) {
|
|
13152
|
+
touched.add(String(move.activityId));
|
|
13153
|
+
touched.add(String(move.newParentId));
|
|
13154
|
+
if (move.oldParentId !== ROOT_PARENT_ID) {
|
|
13155
|
+
touched.add(String(move.oldParentId));
|
|
13156
|
+
}
|
|
13157
|
+
}
|
|
13158
|
+
const allowedMoves = plan.allowedMoves;
|
|
12188
13159
|
const beforeSnap = await snapshotActivities(adapter, touched);
|
|
12189
|
-
for (const { activityId, newParentId } of
|
|
13160
|
+
for (const { activityId, newParentId } of allowedMoves) {
|
|
12190
13161
|
adapter.setActivityField(activityId, ACTIVITY_PROPERTY.PARENT, newParentId);
|
|
12191
13162
|
adapter.setActivityField(
|
|
12192
13163
|
activityId,
|
|
@@ -12196,7 +13167,7 @@ async function dispatchActivityIndent(action, options, deps) {
|
|
|
12196
13167
|
succeededIds.push(activityId);
|
|
12197
13168
|
}
|
|
12198
13169
|
const promotedParents = /* @__PURE__ */ new Set();
|
|
12199
|
-
for (const { activityId, newParentId } of
|
|
13170
|
+
for (const { activityId, newParentId } of allowedMoves) {
|
|
12200
13171
|
if (promotedParents.has(newParentId)) continue;
|
|
12201
13172
|
promotedParents.add(newParentId);
|
|
12202
13173
|
const newParent = adapter.getActivity(newParentId);
|
|
@@ -12248,7 +13219,7 @@ async function dispatchActivityIndent(action, options, deps) {
|
|
|
12248
13219
|
releaseClearedCustomId(oldNewParentCustomId, deps.customIdTracker);
|
|
12249
13220
|
}
|
|
12250
13221
|
const indentAffectedParents = /* @__PURE__ */ new Set();
|
|
12251
|
-
for (const m of
|
|
13222
|
+
for (const m of allowedMoves) {
|
|
12252
13223
|
if (m.oldParentId !== ROOT_PARENT_ID)
|
|
12253
13224
|
indentAffectedParents.add(m.oldParentId);
|
|
12254
13225
|
indentAffectedParents.add(m.newParentId);
|
|
@@ -12409,8 +13380,24 @@ async function dispatchActivityOutdent(action, options, deps) {
|
|
|
12409
13380
|
if (grandparentKey !== ROOT_PARENT_ID) touched.add(String(grandparentKey));
|
|
12410
13381
|
oldParents.add(oldParentKey);
|
|
12411
13382
|
}
|
|
13383
|
+
const cyclicPlanIds = collectCyclicReparents(
|
|
13384
|
+
planned.map((plan) => ({
|
|
13385
|
+
activityId: plan.activityId,
|
|
13386
|
+
newParentId: plan.grandparentKey
|
|
13387
|
+
})),
|
|
13388
|
+
adapter
|
|
13389
|
+
);
|
|
13390
|
+
for (const cyclicId of cyclicPlanIds) {
|
|
13391
|
+
failed.push({
|
|
13392
|
+
activityId: cyclicId,
|
|
13393
|
+
reason: REJECTION_REASON.HIERARCHY_CYCLE
|
|
13394
|
+
});
|
|
13395
|
+
}
|
|
13396
|
+
const allowedPlans = planned.filter(
|
|
13397
|
+
(plan) => !cyclicPlanIds.has(plan.activityId)
|
|
13398
|
+
);
|
|
12412
13399
|
const beforeSnap = await snapshotActivities(adapter, touched);
|
|
12413
|
-
for (const plan of
|
|
13400
|
+
for (const plan of allowedPlans) {
|
|
12414
13401
|
adapter.setActivityField(
|
|
12415
13402
|
plan.activityId,
|
|
12416
13403
|
ACTIVITY_PROPERTY.PARENT,
|
|
@@ -12444,7 +13431,7 @@ async function dispatchActivityOutdent(action, options, deps) {
|
|
|
12444
13431
|
task: parentActivity
|
|
12445
13432
|
}),
|
|
12446
13433
|
idsRemoved: new Set(
|
|
12447
|
-
|
|
13434
|
+
allowedPlans.filter((plan) => plan.oldParentKey === oldParentKey).map((plan) => plan.activityId)
|
|
12448
13435
|
)
|
|
12449
13436
|
});
|
|
12450
13437
|
if (!demotion) {
|
|
@@ -12470,7 +13457,7 @@ async function dispatchActivityOutdent(action, options, deps) {
|
|
|
12470
13457
|
for (const oldParentKey of oldParents) {
|
|
12471
13458
|
outdentAffectedParents.add(String(oldParentKey));
|
|
12472
13459
|
}
|
|
12473
|
-
for (const plan of
|
|
13460
|
+
for (const plan of allowedPlans) {
|
|
12474
13461
|
if (plan.grandparentKey !== ROOT_PARENT_ID) {
|
|
12475
13462
|
outdentAffectedParents.add(String(plan.grandparentKey));
|
|
12476
13463
|
}
|
|
@@ -12558,6 +13545,17 @@ async function dispatchActivitySetProgress(action, options, deps) {
|
|
|
12558
13545
|
"[ScheduleCore] progressPipeline not registered \u2014 cannot dispatch activity-set-progress"
|
|
12559
13546
|
);
|
|
12560
13547
|
}
|
|
13548
|
+
const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy);
|
|
13549
|
+
if (!editGate.allowed) {
|
|
13550
|
+
const declaredButtonOverride = action.origin === "action-button" && (editGate.reason === "has_lookahead_tasks" || editGate.reason === "summary");
|
|
13551
|
+
if (!declaredButtonOverride) {
|
|
13552
|
+
return {
|
|
13553
|
+
ok: false,
|
|
13554
|
+
reason: editGate.reason,
|
|
13555
|
+
...editGate.alertKey ? { alertKey: editGate.alertKey } : {}
|
|
13556
|
+
};
|
|
13557
|
+
}
|
|
13558
|
+
}
|
|
12561
13559
|
const changes = pipeline.transform(
|
|
12562
13560
|
activitySnap,
|
|
12563
13561
|
action.newValue,
|
|
@@ -12974,171 +13972,29 @@ function syncCustomIdTracker(edit, oldCustomId, deps) {
|
|
|
12974
13972
|
const rawValue = liveActivity ? liveActivity.customId : null;
|
|
12975
13973
|
const newCustomId = typeof rawValue === "string" ? rawValue : null;
|
|
12976
13974
|
if (oldCustomId !== newCustomId) {
|
|
12977
|
-
deps.customIdTracker.trackCustomIdChange(oldCustomId, newCustomId);
|
|
12978
|
-
}
|
|
12979
|
-
}
|
|
12980
|
-
function recordConstraintPrior(priors, edit, beforeSnap) {
|
|
12981
|
-
if (!isConstraintEditColumn(edit.column)) return;
|
|
12982
|
-
const activityKey = String(edit.activityId);
|
|
12983
|
-
if (priors.has(activityKey)) return;
|
|
12984
|
-
const preBatchSnapshot = beforeSnap.get(activityKey);
|
|
12985
|
-
if (!preBatchSnapshot) return;
|
|
12986
|
-
priors.set(activityKey, preBatchSnapshot.constraintDate ?? null);
|
|
12987
|
-
}
|
|
12988
|
-
function mergeBeforeSnapshots2(beforeSnap, ids, deps) {
|
|
12989
|
-
const missing = /* @__PURE__ */ new Set();
|
|
12990
|
-
for (const candidateId of ids) {
|
|
12991
|
-
if (!beforeSnap.has(candidateId)) missing.add(candidateId);
|
|
12992
|
-
}
|
|
12993
|
-
if (missing.size === 0) return;
|
|
12994
|
-
for (const missingId of missing) {
|
|
12995
|
-
const liveActivity = deps.adapter.getActivity(missingId);
|
|
12996
|
-
if (liveActivity) {
|
|
12997
|
-
beforeSnap.set(missingId, structuredCloneActivity(liveActivity));
|
|
12998
|
-
}
|
|
12999
|
-
}
|
|
13000
|
-
}
|
|
13001
|
-
|
|
13002
|
-
// src/dispatch/handlers/persistence-acknowledge.ts
|
|
13003
|
-
function assertValidAssignment(assignment, entityName, readCurrent, ownerByBackendId, seenIds, seenBackendIds) {
|
|
13004
|
-
const id = String(assignment.id);
|
|
13005
|
-
if (id.length === 0 || seenIds.has(id)) {
|
|
13006
|
-
throw new Error(
|
|
13007
|
-
`persistence-acknowledge: duplicate or empty ${entityName} id "${id}"`
|
|
13008
|
-
);
|
|
13009
|
-
}
|
|
13010
|
-
if (!Number.isSafeInteger(assignment.proplannerId) || assignment.proplannerId <= 0) {
|
|
13011
|
-
throw new Error(
|
|
13012
|
-
`persistence-acknowledge: invalid ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
13013
|
-
);
|
|
13014
|
-
}
|
|
13015
|
-
if (seenBackendIds.has(assignment.proplannerId)) {
|
|
13016
|
-
throw new Error(
|
|
13017
|
-
`persistence-acknowledge: duplicate ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
13018
|
-
);
|
|
13019
|
-
}
|
|
13020
|
-
const state = readCurrent(id);
|
|
13021
|
-
if (!state.exists) {
|
|
13022
|
-
throw new Error(
|
|
13023
|
-
`persistence-acknowledge: unknown ${entityName} id "${id}"`
|
|
13024
|
-
);
|
|
13025
|
-
}
|
|
13026
|
-
if (state.current !== null && state.current !== assignment.proplannerId) {
|
|
13027
|
-
throw new Error(
|
|
13028
|
-
`persistence-acknowledge: ${entityName} "${id}" already has proplannerId ${String(state.current)}`
|
|
13029
|
-
);
|
|
13030
|
-
}
|
|
13031
|
-
const existingOwner = ownerByBackendId.get(assignment.proplannerId);
|
|
13032
|
-
if (existingOwner !== void 0 && existingOwner !== id) {
|
|
13033
|
-
throw new Error(
|
|
13034
|
-
`persistence-acknowledge: ${entityName} proplannerId ${String(assignment.proplannerId)} already belongs to "${existingOwner}"`
|
|
13035
|
-
);
|
|
13975
|
+
deps.customIdTracker.trackCustomIdChange(oldCustomId, newCustomId);
|
|
13036
13976
|
}
|
|
13037
|
-
seenIds.add(id);
|
|
13038
|
-
seenBackendIds.add(assignment.proplannerId);
|
|
13039
13977
|
}
|
|
13040
|
-
function
|
|
13041
|
-
|
|
13042
|
-
const
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
readCurrent,
|
|
13048
|
-
ownerByBackendId,
|
|
13049
|
-
seenIds,
|
|
13050
|
-
seenBackendIds
|
|
13051
|
-
);
|
|
13052
|
-
}
|
|
13978
|
+
function recordConstraintPrior(priors, edit, beforeSnap) {
|
|
13979
|
+
if (!isConstraintEditColumn(edit.column)) return;
|
|
13980
|
+
const activityKey = String(edit.activityId);
|
|
13981
|
+
if (priors.has(activityKey)) return;
|
|
13982
|
+
const preBatchSnapshot = beforeSnap.get(activityKey);
|
|
13983
|
+
if (!preBatchSnapshot) return;
|
|
13984
|
+
priors.set(activityKey, preBatchSnapshot.constraintDate ?? null);
|
|
13053
13985
|
}
|
|
13054
|
-
function
|
|
13055
|
-
const
|
|
13056
|
-
const
|
|
13057
|
-
|
|
13058
|
-
for (const activity of deps.adapter.getAllActivities()) {
|
|
13059
|
-
if (activity.proplannerId != null) {
|
|
13060
|
-
activityOwnerByBackendId.set(activity.proplannerId, String(activity.id));
|
|
13061
|
-
}
|
|
13986
|
+
function mergeBeforeSnapshots2(beforeSnap, ids, deps) {
|
|
13987
|
+
const missing = /* @__PURE__ */ new Set();
|
|
13988
|
+
for (const candidateId of ids) {
|
|
13989
|
+
if (!beforeSnap.has(candidateId)) missing.add(candidateId);
|
|
13062
13990
|
}
|
|
13063
|
-
|
|
13064
|
-
for (const
|
|
13065
|
-
|
|
13066
|
-
|
|
13991
|
+
if (missing.size === 0) return;
|
|
13992
|
+
for (const missingId of missing) {
|
|
13993
|
+
const liveActivity = deps.adapter.getActivity(missingId);
|
|
13994
|
+
if (liveActivity) {
|
|
13995
|
+
beforeSnap.set(missingId, structuredCloneActivity(liveActivity));
|
|
13067
13996
|
}
|
|
13068
13997
|
}
|
|
13069
|
-
assertValidAssignments(
|
|
13070
|
-
activities,
|
|
13071
|
-
"activity",
|
|
13072
|
-
(id) => {
|
|
13073
|
-
const activity = deps.adapter.getActivity(id);
|
|
13074
|
-
return {
|
|
13075
|
-
exists: activity !== null,
|
|
13076
|
-
current: activity?.proplannerId ?? null
|
|
13077
|
-
};
|
|
13078
|
-
},
|
|
13079
|
-
activityOwnerByBackendId
|
|
13080
|
-
);
|
|
13081
|
-
assertValidAssignments(
|
|
13082
|
-
links,
|
|
13083
|
-
"link",
|
|
13084
|
-
(id) => {
|
|
13085
|
-
const link = deps.adapter.getLink(id);
|
|
13086
|
-
return { exists: link !== null, current: link?.proplannerId ?? null };
|
|
13087
|
-
},
|
|
13088
|
-
linkOwnerByBackendId
|
|
13089
|
-
);
|
|
13090
|
-
const activityChanges = [];
|
|
13091
|
-
const linkChanges = [];
|
|
13092
|
-
deps.adapter.batchUpdate(() => {
|
|
13093
|
-
for (const assignment of activities) {
|
|
13094
|
-
const before = deps.adapter.getActivity(assignment.id);
|
|
13095
|
-
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
13096
|
-
const previousProplannerId = before.proplannerId;
|
|
13097
|
-
deps.adapter.setActivityField(
|
|
13098
|
-
assignment.id,
|
|
13099
|
-
"proplannerId",
|
|
13100
|
-
assignment.proplannerId
|
|
13101
|
-
);
|
|
13102
|
-
activityChanges.push({
|
|
13103
|
-
id: assignment.id,
|
|
13104
|
-
kind: "updated",
|
|
13105
|
-
fields: {
|
|
13106
|
-
proplannerId: {
|
|
13107
|
-
before: previousProplannerId,
|
|
13108
|
-
after: assignment.proplannerId
|
|
13109
|
-
}
|
|
13110
|
-
},
|
|
13111
|
-
after: { ...before, proplannerId: assignment.proplannerId }
|
|
13112
|
-
});
|
|
13113
|
-
}
|
|
13114
|
-
for (const assignment of links) {
|
|
13115
|
-
const before = deps.adapter.getLink(assignment.id);
|
|
13116
|
-
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
13117
|
-
const previousProplannerId = before.proplannerId;
|
|
13118
|
-
deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
|
|
13119
|
-
linkChanges.push({
|
|
13120
|
-
id: assignment.id,
|
|
13121
|
-
kind: "updated",
|
|
13122
|
-
fields: {
|
|
13123
|
-
proplannerId: {
|
|
13124
|
-
before: previousProplannerId,
|
|
13125
|
-
after: assignment.proplannerId
|
|
13126
|
-
}
|
|
13127
|
-
},
|
|
13128
|
-
after: { ...before, proplannerId: assignment.proplannerId }
|
|
13129
|
-
});
|
|
13130
|
-
}
|
|
13131
|
-
});
|
|
13132
|
-
return {
|
|
13133
|
-
ok: true,
|
|
13134
|
-
changes: {
|
|
13135
|
-
source: action,
|
|
13136
|
-
activities: activityChanges,
|
|
13137
|
-
links: linkChanges,
|
|
13138
|
-
calendars: [],
|
|
13139
|
-
trackingEvents: []
|
|
13140
|
-
}
|
|
13141
|
-
};
|
|
13142
13998
|
}
|
|
13143
13999
|
|
|
13144
14000
|
// src/baselines/normalize-baseline-point.ts
|
|
@@ -13336,55 +14192,6 @@ function recomputeAllPonderators(criterion, adapter) {
|
|
|
13336
14192
|
});
|
|
13337
14193
|
}
|
|
13338
14194
|
|
|
13339
|
-
// src/propagations/upward/progress-rollup.ts
|
|
13340
|
-
function recomputeAllProgressRollup(adapter) {
|
|
13341
|
-
for (const parentId of parentsDeepestFirst(adapter)) {
|
|
13342
|
-
rollupParent(parentId, adapter);
|
|
13343
|
-
}
|
|
13344
|
-
}
|
|
13345
|
-
function parentsDeepestFirst(adapter) {
|
|
13346
|
-
const parents = /* @__PURE__ */ new Set();
|
|
13347
|
-
adapter.forEachActivity((_activity, id) => {
|
|
13348
|
-
if (adapter.getChildren(id).length > 0) parents.add(id);
|
|
13349
|
-
});
|
|
13350
|
-
const depthById = /* @__PURE__ */ new Map();
|
|
13351
|
-
for (const id of parents) depthById.set(id, depthOf2(id, adapter));
|
|
13352
|
-
return [...parents].sort(
|
|
13353
|
-
(a, b) => (depthById.get(b) ?? 0) - (depthById.get(a) ?? 0)
|
|
13354
|
-
);
|
|
13355
|
-
}
|
|
13356
|
-
function depthOf2(id, adapter) {
|
|
13357
|
-
let depth = 0;
|
|
13358
|
-
let current = id;
|
|
13359
|
-
while (current && current !== "0") {
|
|
13360
|
-
if (!adapter.getActivity(current)) break;
|
|
13361
|
-
depth += 1;
|
|
13362
|
-
current = adapter.getParentId(current);
|
|
13363
|
-
}
|
|
13364
|
-
return depth;
|
|
13365
|
-
}
|
|
13366
|
-
function rollupParent(parentId, adapter) {
|
|
13367
|
-
const childIds = adapter.getChildren(parentId);
|
|
13368
|
-
if (childIds.length === 0) return;
|
|
13369
|
-
const children = [];
|
|
13370
|
-
for (const childId of childIds) {
|
|
13371
|
-
const child = adapter.getActivity(childId);
|
|
13372
|
-
if (!child) continue;
|
|
13373
|
-
children.push({
|
|
13374
|
-
progress: Number(child.progress ?? 0),
|
|
13375
|
-
ponderator: Number(child.ponderator ?? 0)
|
|
13376
|
-
});
|
|
13377
|
-
}
|
|
13378
|
-
const rollup = computeWeightedProgressRollup(children);
|
|
13379
|
-
if (rollup !== null) {
|
|
13380
|
-
adapter.setActivityField(
|
|
13381
|
-
parentId,
|
|
13382
|
-
COLUMN.PROGRESS,
|
|
13383
|
-
roundProgressPerLevel(rollup)
|
|
13384
|
-
);
|
|
13385
|
-
}
|
|
13386
|
-
}
|
|
13387
|
-
|
|
13388
14195
|
// src/dispatch/shared/baseline-weighted-fields.ts
|
|
13389
14196
|
function recomputeBaselineWeightedFields(deps, criterion) {
|
|
13390
14197
|
recomputeAllPonderators(criterion, deps.adapter);
|
|
@@ -13488,9 +14295,207 @@ async function dispatchStatusCriteriaSet(action, deps) {
|
|
|
13488
14295
|
ok: true,
|
|
13489
14296
|
changes: await assembleChangeSet(deps.adapter, {
|
|
13490
14297
|
source: action,
|
|
13491
|
-
beforeSnap,
|
|
13492
|
-
touchedIds: changedIds,
|
|
13493
|
-
scheduledIds: [],
|
|
14298
|
+
beforeSnap,
|
|
14299
|
+
touchedIds: changedIds,
|
|
14300
|
+
scheduledIds: [],
|
|
14301
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
14302
|
+
})
|
|
14303
|
+
};
|
|
14304
|
+
}
|
|
14305
|
+
|
|
14306
|
+
// src/activity-copy-paste/policy.ts
|
|
14307
|
+
var ACTIVITY_COPY_FIELDS = [
|
|
14308
|
+
"name",
|
|
14309
|
+
"description",
|
|
14310
|
+
"type",
|
|
14311
|
+
"startDate",
|
|
14312
|
+
"durationHours",
|
|
14313
|
+
"constraintType",
|
|
14314
|
+
"constraintDate",
|
|
14315
|
+
"calendarId",
|
|
14316
|
+
"cost",
|
|
14317
|
+
"hasCustomPonderator",
|
|
14318
|
+
"subcontractId",
|
|
14319
|
+
"responsableIds",
|
|
14320
|
+
"tagIds"
|
|
14321
|
+
];
|
|
14322
|
+
|
|
14323
|
+
// src/activity-copy-paste/copy.ts
|
|
14324
|
+
function copyOverrides(activity) {
|
|
14325
|
+
const overrides = {};
|
|
14326
|
+
for (const field of ACTIVITY_COPY_FIELDS) {
|
|
14327
|
+
Object.assign(overrides, { [field]: cloneDomainValue(activity[field]) });
|
|
14328
|
+
}
|
|
14329
|
+
return overrides;
|
|
14330
|
+
}
|
|
14331
|
+
function selectedInTreeOrder(adapter, selected) {
|
|
14332
|
+
const ordered = [];
|
|
14333
|
+
const visit = (id) => {
|
|
14334
|
+
if (selected.has(String(id))) ordered.push(id);
|
|
14335
|
+
for (const childId of adapter.getChildren(id)) visit(childId);
|
|
14336
|
+
};
|
|
14337
|
+
for (const rootId of adapter.getRootIds()) visit(rootId);
|
|
14338
|
+
return ordered;
|
|
14339
|
+
}
|
|
14340
|
+
function dispatchActivityCopy(action, deps) {
|
|
14341
|
+
const selected = new Set(deps.adapter.checkedIds().map(String));
|
|
14342
|
+
if (selected.size === 0) {
|
|
14343
|
+
return { ok: false, reason: REJECTION_REASON.NO_ACTIVITY_SELECTION };
|
|
14344
|
+
}
|
|
14345
|
+
const orderedIds = selectedInTreeOrder(deps.adapter, selected);
|
|
14346
|
+
deps.clipboard.value = {
|
|
14347
|
+
activities: orderedIds.map((id) => {
|
|
14348
|
+
const activity = deps.adapter.getActivity(id);
|
|
14349
|
+
if (activity === null)
|
|
14350
|
+
throw new Error(`Selected activity ${id} is missing`);
|
|
14351
|
+
return {
|
|
14352
|
+
originalId: id,
|
|
14353
|
+
originalParentId: activity.parentId ?? "0",
|
|
14354
|
+
overrides: copyOverrides(activity)
|
|
14355
|
+
};
|
|
14356
|
+
}),
|
|
14357
|
+
links: deps.adapter.getAllLinks().filter(
|
|
14358
|
+
(link) => selected.has(String(link.source)) && selected.has(String(link.target))
|
|
14359
|
+
).map((link) => cloneDomainValue(link))
|
|
14360
|
+
};
|
|
14361
|
+
const selection = dispatchSelectionReplace(
|
|
14362
|
+
{ kind: "selection-replace", activityIds: [] },
|
|
14363
|
+
{ adapter: deps.adapter }
|
|
14364
|
+
);
|
|
14365
|
+
if (!selection.ok) return selection;
|
|
14366
|
+
return { ...selection, changes: { ...selection.changes, source: action } };
|
|
14367
|
+
}
|
|
14368
|
+
function dispatchActivityClipboardClear(action, clipboard) {
|
|
14369
|
+
clipboard.value = null;
|
|
14370
|
+
return {
|
|
14371
|
+
ok: true,
|
|
14372
|
+
changes: {
|
|
14373
|
+
source: action,
|
|
14374
|
+
activities: [],
|
|
14375
|
+
links: [],
|
|
14376
|
+
calendars: [],
|
|
14377
|
+
trackingEvents: []
|
|
14378
|
+
}
|
|
14379
|
+
};
|
|
14380
|
+
}
|
|
14381
|
+
|
|
14382
|
+
// src/activity-copy-paste/paste.ts
|
|
14383
|
+
function resolveDestination(afterSiblingId, adapter) {
|
|
14384
|
+
const sibling = adapter.getActivity(afterSiblingId);
|
|
14385
|
+
if (!sibling) {
|
|
14386
|
+
return { ok: false, reason: REJECTION_REASON.ANCHOR_SIBLING_NOT_FOUND };
|
|
14387
|
+
}
|
|
14388
|
+
return {
|
|
14389
|
+
ok: true,
|
|
14390
|
+
parentId: sibling.parentId == null ? ROOT_PARENT_ID : String(sibling.parentId)
|
|
14391
|
+
};
|
|
14392
|
+
}
|
|
14393
|
+
var invariantFailure = () => ({
|
|
14394
|
+
ok: false,
|
|
14395
|
+
reason: REJECTION_REASON.ACTIVITY_PASTE_INVARIANT_FAILED
|
|
14396
|
+
});
|
|
14397
|
+
async function dispatchActivityPasteCopied(action, options, deps) {
|
|
14398
|
+
const clipboard = deps.activityClipboard.value;
|
|
14399
|
+
if (clipboard === null) {
|
|
14400
|
+
return { ok: false, reason: REJECTION_REASON.CLIPBOARD_EMPTY };
|
|
14401
|
+
}
|
|
14402
|
+
const destination = resolveDestination(
|
|
14403
|
+
action.destination.afterSiblingId,
|
|
14404
|
+
deps.adapter
|
|
14405
|
+
);
|
|
14406
|
+
if (!destination.ok) return destination;
|
|
14407
|
+
const beforeSnapshot = await snapshotActivities(
|
|
14408
|
+
deps.adapter,
|
|
14409
|
+
/* @__PURE__ */ new Set()
|
|
14410
|
+
);
|
|
14411
|
+
const touched = /* @__PURE__ */ new Set();
|
|
14412
|
+
const originalToNew = /* @__PURE__ */ new Map();
|
|
14413
|
+
const createdIds = [];
|
|
14414
|
+
let previousRootId;
|
|
14415
|
+
for (const copied of clipboard.activities) {
|
|
14416
|
+
const mappedParent = originalToNew.get(copied.originalParentId);
|
|
14417
|
+
const created = await createActivityCore(
|
|
14418
|
+
{
|
|
14419
|
+
parentId: mappedParent ?? destination.parentId,
|
|
14420
|
+
...mappedParent === void 0 ? {
|
|
14421
|
+
afterSiblingId: previousRootId ?? action.destination.afterSiblingId
|
|
14422
|
+
} : {},
|
|
14423
|
+
overrides: cloneDomainValue(copied.overrides),
|
|
14424
|
+
eventSource: action.eventSource
|
|
14425
|
+
},
|
|
14426
|
+
deps,
|
|
14427
|
+
{
|
|
14428
|
+
skipCorrelativeRecompute: true,
|
|
14429
|
+
skipBeforeSnap: true,
|
|
14430
|
+
customIdReferenceId: action.destination.afterSiblingId
|
|
14431
|
+
}
|
|
14432
|
+
);
|
|
14433
|
+
if (!created.ok) return created;
|
|
14434
|
+
originalToNew.set(copied.originalId, created.newId);
|
|
14435
|
+
createdIds.push(created.newId);
|
|
14436
|
+
if (mappedParent === void 0) previousRootId = created.newId;
|
|
14437
|
+
}
|
|
14438
|
+
const correlativeShifts = recomputeCorrelativeIds(deps.adapter);
|
|
14439
|
+
const createdIdSet = new Set(createdIds.map(String));
|
|
14440
|
+
foldCorrelativeShifts(
|
|
14441
|
+
deps.adapter,
|
|
14442
|
+
correlativeShifts,
|
|
14443
|
+
createdIdSet,
|
|
14444
|
+
beforeSnapshot,
|
|
14445
|
+
touched
|
|
14446
|
+
);
|
|
14447
|
+
for (const createdId of createdIds) touched.add(String(createdId));
|
|
14448
|
+
const appliedLinks = [];
|
|
14449
|
+
for (const copiedLink of clipboard.links) {
|
|
14450
|
+
const source = originalToNew.get(copiedLink.source);
|
|
14451
|
+
const target = originalToNew.get(copiedLink.target);
|
|
14452
|
+
if (source === void 0 || target === void 0) return invariantFailure();
|
|
14453
|
+
const operation = {
|
|
14454
|
+
kind: "create",
|
|
14455
|
+
source,
|
|
14456
|
+
target,
|
|
14457
|
+
type: copiedLink.type,
|
|
14458
|
+
lag: copiedLink.lag
|
|
14459
|
+
};
|
|
14460
|
+
const result = applyLinkOperation(operation, {
|
|
14461
|
+
port: deps.adapter,
|
|
14462
|
+
newLinkId: deps.linkIdGen.next
|
|
14463
|
+
});
|
|
14464
|
+
if (!result.applied) return invariantFailure();
|
|
14465
|
+
appliedLinks.push({
|
|
14466
|
+
op: operation,
|
|
14467
|
+
finalLinkId: result.linkId,
|
|
14468
|
+
rejected: null
|
|
14469
|
+
});
|
|
14470
|
+
}
|
|
14471
|
+
const { scheduledIds } = await runPostMutation(
|
|
14472
|
+
{
|
|
14473
|
+
adapter: deps.adapter,
|
|
14474
|
+
scheduler: deps.scheduler,
|
|
14475
|
+
sector: deps.sector,
|
|
14476
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
14477
|
+
},
|
|
14478
|
+
{
|
|
14479
|
+
action,
|
|
14480
|
+
autoscheduleFrom: createdIds[0] ?? null,
|
|
14481
|
+
recomputeParentsFrom: createdIds,
|
|
14482
|
+
now: deps.now,
|
|
14483
|
+
options
|
|
14484
|
+
}
|
|
14485
|
+
);
|
|
14486
|
+
const linkChanges = buildLinkChangesForBatch(
|
|
14487
|
+
appliedLinks,
|
|
14488
|
+
/* @__PURE__ */ new Map(),
|
|
14489
|
+
deps.adapter
|
|
14490
|
+
);
|
|
14491
|
+
return {
|
|
14492
|
+
ok: true,
|
|
14493
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
14494
|
+
source: action,
|
|
14495
|
+
beforeSnap: beforeSnapshot,
|
|
14496
|
+
touchedIds: touched,
|
|
14497
|
+
scheduledIds,
|
|
14498
|
+
links: linkChanges,
|
|
13494
14499
|
hoursPerDay: deps.sector.hoursPerDay
|
|
13495
14500
|
})
|
|
13496
14501
|
};
|
|
@@ -13498,6 +14503,18 @@ async function dispatchStatusCriteriaSet(action, deps) {
|
|
|
13498
14503
|
|
|
13499
14504
|
// src/dispatch/dispatch.ts
|
|
13500
14505
|
async function performDispatch(action, options, deps) {
|
|
14506
|
+
if (action.kind === "activity-copy") {
|
|
14507
|
+
return dispatchActivityCopy(action, {
|
|
14508
|
+
adapter: deps.adapter,
|
|
14509
|
+
clipboard: deps.activityClipboard
|
|
14510
|
+
});
|
|
14511
|
+
}
|
|
14512
|
+
if (action.kind === "activity-clipboard-clear") {
|
|
14513
|
+
return dispatchActivityClipboardClear(action, deps.activityClipboard);
|
|
14514
|
+
}
|
|
14515
|
+
if (action.kind === "activity-paste-copied") {
|
|
14516
|
+
return dispatchActivityPasteCopied(action, options, deps);
|
|
14517
|
+
}
|
|
13501
14518
|
if (action.kind === "activity-batch") {
|
|
13502
14519
|
return dispatchActivityBatch(action, options, deps);
|
|
13503
14520
|
}
|
|
@@ -13522,9 +14539,6 @@ async function performDispatch(action, options, deps) {
|
|
|
13522
14539
|
if (action.kind === "activity-create") {
|
|
13523
14540
|
return dispatchActivityCreate(action, options, deps);
|
|
13524
14541
|
}
|
|
13525
|
-
if (action.kind === "activity-paste") {
|
|
13526
|
-
return dispatchActivityPaste(action, options, deps);
|
|
13527
|
-
}
|
|
13528
14542
|
if (action.kind === "activity-delete") {
|
|
13529
14543
|
return dispatchActivityDelete(action, options, deps);
|
|
13530
14544
|
}
|
|
@@ -13568,7 +14582,7 @@ async function performDispatch(action, options, deps) {
|
|
|
13568
14582
|
return dispatchSirSync(action, deps);
|
|
13569
14583
|
}
|
|
13570
14584
|
if (action.kind === "activity-lookahead-sync") {
|
|
13571
|
-
return dispatchActivityLookaheadSync(action, deps);
|
|
14585
|
+
return dispatchActivityLookaheadSync(action, options, deps);
|
|
13572
14586
|
}
|
|
13573
14587
|
if (action.kind === "inline-edit" && (action.column === COLUMN.CUSTOM_PREDECESSORS || action.column === COLUMN.CUSTOM_SUCESSORS)) {
|
|
13574
14588
|
return dispatchInlineEditLinks(action, options, deps);
|
|
@@ -16718,7 +17732,7 @@ var ViewStateStore = class {
|
|
|
16718
17732
|
};
|
|
16719
17733
|
|
|
16720
17734
|
// src/internal/state/schedule-state.ts
|
|
16721
|
-
var ScheduleState = class {
|
|
17735
|
+
var ScheduleState = class _ScheduleState {
|
|
16722
17736
|
_activities = /* @__PURE__ */ new Map();
|
|
16723
17737
|
_lastStartDate = /* @__PURE__ */ new Map();
|
|
16724
17738
|
_promotionSnapshot = /* @__PURE__ */ new Map();
|
|
@@ -16740,22 +17754,52 @@ var ScheduleState = class {
|
|
|
16740
17754
|
_promotionSnapshotBefore = null;
|
|
16741
17755
|
_calendar;
|
|
16742
17756
|
_flags;
|
|
17757
|
+
_calendarSnapshot;
|
|
16743
17758
|
constructor(snapshot, buildOrderComparator = () => null) {
|
|
16744
17759
|
this._buildOrderComparator = buildOrderComparator;
|
|
17760
|
+
this._calendarSnapshot = cloneDomainValue(snapshot.calendars);
|
|
16745
17761
|
for (const activity of snapshot.activities) {
|
|
16746
17762
|
this._activities.set(activity.id, activity);
|
|
16747
17763
|
}
|
|
16748
17764
|
for (const link of snapshot.links) {
|
|
16749
|
-
|
|
16750
|
-
|
|
17765
|
+
const normalized2 = {
|
|
17766
|
+
...link,
|
|
17767
|
+
freeSlackHours: link.freeSlackHours ?? null,
|
|
17768
|
+
isCritical: link.isCritical === true
|
|
17769
|
+
};
|
|
17770
|
+
this._links.set(String(link.id), normalized2);
|
|
17771
|
+
this._indexLink(normalized2);
|
|
16751
17772
|
}
|
|
16752
17773
|
this._calendar = new CalendarCalculator(
|
|
16753
|
-
createCalendarSetup(
|
|
17774
|
+
createCalendarSetup(this._calendarSnapshot)
|
|
16754
17775
|
);
|
|
16755
17776
|
this._flags = buildFlags(snapshot);
|
|
16756
17777
|
this._hierarchy = new HierarchyIndex(this._activities);
|
|
16757
17778
|
this._hierarchy.rebuild();
|
|
16758
17779
|
}
|
|
17780
|
+
fork() {
|
|
17781
|
+
const fork = new _ScheduleState(
|
|
17782
|
+
{
|
|
17783
|
+
activities: this.getAllActivities().map(cloneCoreActivity),
|
|
17784
|
+
links: this.getAllLinks().map((link) => ({ ...link })),
|
|
17785
|
+
calendars: cloneDomainValue(this._calendarSnapshot)
|
|
17786
|
+
},
|
|
17787
|
+
this._buildOrderComparator
|
|
17788
|
+
);
|
|
17789
|
+
fork._viewState.restore(cloneDomainValue(this._viewState.snapshot()));
|
|
17790
|
+
fork._lastStartDate = new Map(
|
|
17791
|
+
Array.from(this._lastStartDate, ([id, date2]) => [id, new Date(date2)])
|
|
17792
|
+
);
|
|
17793
|
+
fork._promotionSnapshot = new Map(
|
|
17794
|
+
Array.from(this._promotionSnapshot, ([id, snapshot]) => [
|
|
17795
|
+
id,
|
|
17796
|
+
cloneDomainValue(snapshot)
|
|
17797
|
+
])
|
|
17798
|
+
);
|
|
17799
|
+
fork._flags = cloneDomainValue(this._flags);
|
|
17800
|
+
fork._filterProjection = cloneDomainValue(this._filterProjection);
|
|
17801
|
+
return fork;
|
|
17802
|
+
}
|
|
16759
17803
|
getAllActivities() {
|
|
16760
17804
|
return Array.from(this._activities.values());
|
|
16761
17805
|
}
|
|
@@ -16979,6 +18023,7 @@ var ScheduleState = class {
|
|
|
16979
18023
|
if (!activity) return;
|
|
16980
18024
|
this._writeCapture.note(String(activityId), activity);
|
|
16981
18025
|
activity[field] = value;
|
|
18026
|
+
refreshCalendarDurationIfDateBearing(activity, field);
|
|
16982
18027
|
if (field === ACTIVITY_PROPERTY.PARENT) this._hierarchy.markDirty();
|
|
16983
18028
|
if (fieldAffectsVisualOrder(field)) this._cache.visualOrderIds = null;
|
|
16984
18029
|
}
|
|
@@ -16992,6 +18037,7 @@ var ScheduleState = class {
|
|
|
16992
18037
|
}
|
|
16993
18038
|
this._writeCapture.note(String(activityId), activity);
|
|
16994
18039
|
Reflect.set(activity, field, coerceActivityFieldValue(field, value));
|
|
18040
|
+
refreshCalendarDurationIfDateBearing(activity, field);
|
|
16995
18041
|
if (field === ACTIVITY_PROPERTY.PARENT) this._hierarchy.markDirty();
|
|
16996
18042
|
if (fieldAffectsVisualOrder(field)) this._cache.visualOrderIds = null;
|
|
16997
18043
|
}
|
|
@@ -17002,6 +18048,7 @@ var ScheduleState = class {
|
|
|
17002
18048
|
let reparented = false;
|
|
17003
18049
|
for (const field of Object.keys(fields)) {
|
|
17004
18050
|
Reflect.set(activity, field, Reflect.get(fields, field));
|
|
18051
|
+
refreshCalendarDurationIfDateBearing(activity, field);
|
|
17005
18052
|
if (field === ACTIVITY_PROPERTY.PARENT) reparented = true;
|
|
17006
18053
|
if (fieldAffectsVisualOrder(field)) this._cache.visualOrderIds = null;
|
|
17007
18054
|
}
|
|
@@ -17011,8 +18058,13 @@ var ScheduleState = class {
|
|
|
17011
18058
|
this._writeCapture.noteLinkAdd(String(link.id));
|
|
17012
18059
|
const superseded = this._links.get(String(link.id));
|
|
17013
18060
|
if (superseded) this._deindexLink(superseded);
|
|
17014
|
-
|
|
17015
|
-
|
|
18061
|
+
const normalized2 = {
|
|
18062
|
+
...link,
|
|
18063
|
+
freeSlackHours: link.freeSlackHours ?? null,
|
|
18064
|
+
isCritical: link.isCritical === true
|
|
18065
|
+
};
|
|
18066
|
+
this._links.set(String(link.id), normalized2);
|
|
18067
|
+
this._indexLink(normalized2);
|
|
17016
18068
|
}
|
|
17017
18069
|
_indexLink(link) {
|
|
17018
18070
|
pushLinkRef(this._outgoing, String(link.source), link.id);
|
|
@@ -17165,11 +18217,17 @@ var ScheduleState = class {
|
|
|
17165
18217
|
};
|
|
17166
18218
|
var EMPTY_LINK_IDS = [];
|
|
17167
18219
|
function isMutableLinkField(field) {
|
|
17168
|
-
return field === LINK_PROPERTY.LAG || field === LINK_PROPERTY.TYPE;
|
|
18220
|
+
return field === LINK_PROPERTY.LAG || field === LINK_PROPERTY.TYPE || field === LINK_PROPERTY.FREE_SLACK_HOURS || field === LINK_PROPERTY.IS_CRITICAL;
|
|
17169
18221
|
}
|
|
17170
18222
|
function fieldAffectsVisualOrder(field) {
|
|
17171
18223
|
return field === ACTIVITY_PROPERTY.PARENT || field === ACTIVITY_PROPERTY.CORRELATIVE_ID;
|
|
17172
18224
|
}
|
|
18225
|
+
function refreshCalendarDurationIfDateBearing(activity, field) {
|
|
18226
|
+
if (field !== ACTIVITY_PROPERTY.START_DATE && field !== ACTIVITY_PROPERTY.END_DATE && field !== ACTIVITY_PROPERTY.TYPE) {
|
|
18227
|
+
return;
|
|
18228
|
+
}
|
|
18229
|
+
activity.calendarDuration = computeCalendarDuration(activity);
|
|
18230
|
+
}
|
|
17173
18231
|
function detachLinkRef(refs, linkId) {
|
|
17174
18232
|
return refs.filter((ref) => String(ref) !== String(linkId));
|
|
17175
18233
|
}
|
|
@@ -17217,15 +18275,7 @@ function readChildren(state, parentId) {
|
|
|
17217
18275
|
return out;
|
|
17218
18276
|
}
|
|
17219
18277
|
function readChildrenIds(state, parentId) {
|
|
17220
|
-
|
|
17221
|
-
if (key === ROOT_PARENT_ID) {
|
|
17222
|
-
const roots = [];
|
|
17223
|
-
state.forEachActivity((activity, id) => {
|
|
17224
|
-
if (isRootParent(activity.parentId)) roots.push(id);
|
|
17225
|
-
});
|
|
17226
|
-
return roots;
|
|
17227
|
-
}
|
|
17228
|
-
return getChildrenInVisualOrder(key, state);
|
|
18278
|
+
return getChildrenInVisualOrder(parentId, state);
|
|
17229
18279
|
}
|
|
17230
18280
|
function readSelectedActivityIds(state) {
|
|
17231
18281
|
return state.checkedIds().map(String);
|
|
@@ -17288,8 +18338,7 @@ function buildPipelineContext(adapter, options) {
|
|
|
17288
18338
|
activityReader: createStateBackedActivityReader(adapter),
|
|
17289
18339
|
dateFormat: options.dateFormat ?? "YYYY-MM-DD",
|
|
17290
18340
|
customHours: options.customHours ?? {},
|
|
17291
|
-
hoursPerDay: options.hoursPerDay
|
|
17292
|
-
inputUnit: options.inputUnit ?? "days"
|
|
18341
|
+
hoursPerDay: options.hoursPerDay
|
|
17293
18342
|
};
|
|
17294
18343
|
}
|
|
17295
18344
|
|
|
@@ -17299,15 +18348,26 @@ function willRunCriticalPath(action) {
|
|
|
17299
18348
|
}
|
|
17300
18349
|
|
|
17301
18350
|
// src/dispatch/undo/undo-entry.ts
|
|
17302
|
-
|
|
17303
|
-
|
|
18351
|
+
var DERIVED_LINK_FIELDS = /* @__PURE__ */ new Set(["freeSlackHours", "isCritical"]);
|
|
18352
|
+
function withoutDerivedFields(entry, derivedFields) {
|
|
18353
|
+
const fields = Object.fromEntries(
|
|
18354
|
+
Object.entries(entry.fields ?? {}).filter(
|
|
18355
|
+
([field]) => !derivedFields.has(field)
|
|
18356
|
+
)
|
|
18357
|
+
);
|
|
18358
|
+
if (entry.kind === "updated" && Object.keys(fields).length === 0) return null;
|
|
18359
|
+
return {
|
|
17304
18360
|
...entry,
|
|
18361
|
+
...entry.fields === void 0 ? {} : { fields },
|
|
17305
18362
|
after: null
|
|
17306
|
-
}
|
|
17307
|
-
|
|
18363
|
+
};
|
|
18364
|
+
}
|
|
18365
|
+
function buildHistoryChangeSet(changes) {
|
|
18366
|
+
const activitiesWithoutAfter = changes.activities.map((entry) => ({
|
|
17308
18367
|
...entry,
|
|
17309
18368
|
after: null
|
|
17310
18369
|
}));
|
|
18370
|
+
const linksWithoutAfter = changes.links.map((entry) => withoutDerivedFields(entry, DERIVED_LINK_FIELDS)).filter((entry) => entry !== null);
|
|
17311
18371
|
const source = changes.source.kind === "activity-batch" ? { ...changes.source, items: [], links: [] } : changes.source;
|
|
17312
18372
|
return cloneDomainValue({
|
|
17313
18373
|
...changes,
|
|
@@ -17317,7 +18377,22 @@ function buildHistoryChangeSet(changes) {
|
|
|
17317
18377
|
});
|
|
17318
18378
|
}
|
|
17319
18379
|
function buildUndoEntry(changeSet, beforeSnap, beforeLinks, afterSnap, afterLinks) {
|
|
17320
|
-
return {
|
|
18380
|
+
return {
|
|
18381
|
+
changeSet,
|
|
18382
|
+
beforeSnap,
|
|
18383
|
+
beforeLinks: resetDerivedLinkSnapshots(beforeLinks),
|
|
18384
|
+
afterSnap,
|
|
18385
|
+
afterLinks: resetDerivedLinkSnapshots(afterLinks)
|
|
18386
|
+
};
|
|
18387
|
+
}
|
|
18388
|
+
function resetDerivedLinkSnapshots(snapshots) {
|
|
18389
|
+
if (!snapshots) return void 0;
|
|
18390
|
+
return new Map(
|
|
18391
|
+
Array.from(snapshots, ([id, link]) => [
|
|
18392
|
+
id,
|
|
18393
|
+
{ ...link, freeSlackHours: null, isCritical: false }
|
|
18394
|
+
])
|
|
18395
|
+
);
|
|
17321
18396
|
}
|
|
17322
18397
|
function captureCreatedSnapshots(state, changeSet) {
|
|
17323
18398
|
const afterSnap = /* @__PURE__ */ new Map();
|
|
@@ -17371,7 +18446,7 @@ function mergeCoalesced(top, next) {
|
|
|
17371
18446
|
}
|
|
17372
18447
|
function getDispatchHistoryPolicy(action) {
|
|
17373
18448
|
if (action.kind === "persistence-acknowledge") return "clear-on-success";
|
|
17374
|
-
if (action.kind === "sir-sync" || action.kind === "activity-lookahead-sync" || action.kind === "baseline-apply" || action.kind === "ponderator-criterion-set" || action.kind === "status-criteria-set" || action.kind === "selection-toggle" || action.kind === "selection-replace" || action.kind === "visibility-set" || action.kind === "filter-set" || action.kind === "sort-set") {
|
|
18449
|
+
if (action.kind === "sir-sync" || action.kind === "activity-lookahead-sync" || action.kind === "baseline-apply" || action.kind === "ponderator-criterion-set" || action.kind === "status-criteria-set" || action.kind === "activity-clipboard-clear" || action.kind === "activity-copy" || action.kind === "selection-toggle" || action.kind === "selection-replace" || action.kind === "visibility-set" || action.kind === "filter-set" || action.kind === "sort-set") {
|
|
17375
18450
|
return "skip";
|
|
17376
18451
|
}
|
|
17377
18452
|
return "record";
|
|
@@ -17615,7 +18690,7 @@ function applyChangeSetSide(state, entry, side) {
|
|
|
17615
18690
|
// src/dispatch/undo/undo-recorder.ts
|
|
17616
18691
|
var DEFAULT_MAX_DEPTH = 100;
|
|
17617
18692
|
var COALESCE_WINDOW_MS = 600;
|
|
17618
|
-
var UndoRecorder = class {
|
|
18693
|
+
var UndoRecorder = class _UndoRecorder {
|
|
17619
18694
|
constructor(maxDepth = DEFAULT_MAX_DEPTH) {
|
|
17620
18695
|
this.maxDepth = maxDepth;
|
|
17621
18696
|
}
|
|
@@ -17624,6 +18699,14 @@ var UndoRecorder = class {
|
|
|
17624
18699
|
redoStack = [];
|
|
17625
18700
|
_lastKey = null;
|
|
17626
18701
|
_lastTime = 0;
|
|
18702
|
+
fork() {
|
|
18703
|
+
const fork = new _UndoRecorder(this.maxDepth);
|
|
18704
|
+
fork.undoStack.push(...cloneDomainValue(this.undoStack));
|
|
18705
|
+
fork.redoStack.push(...cloneDomainValue(this.redoStack));
|
|
18706
|
+
fork._lastKey = this._lastKey;
|
|
18707
|
+
fork._lastTime = this._lastTime;
|
|
18708
|
+
return fork;
|
|
18709
|
+
}
|
|
17627
18710
|
record(entry, coalesceKey = null, now = 0) {
|
|
17628
18711
|
const top = this.undoStack[this.undoStack.length - 1];
|
|
17629
18712
|
if (coalesceKey != null && coalesceKey === this._lastKey && now - this._lastTime <= COALESCE_WINDOW_MS && top !== void 0 && canCoalesce(top, entry)) {
|
|
@@ -17747,40 +18830,56 @@ function buildStateSnapshot(activities, links, calendars, baseCalendars = []) {
|
|
|
17747
18830
|
}
|
|
17748
18831
|
|
|
17749
18832
|
// src/boundary/save/activity-changes.ts
|
|
17750
|
-
var
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
|
|
17760
|
-
|
|
17761
|
-
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
"
|
|
17780
|
-
"
|
|
17781
|
-
"
|
|
17782
|
-
"
|
|
17783
|
-
|
|
18833
|
+
var ACTIVITY_SAVE_FIELD_POLICY = {
|
|
18834
|
+
id: "ignored",
|
|
18835
|
+
proplannerId: "ignored",
|
|
18836
|
+
parentId: "tracked",
|
|
18837
|
+
name: "tracked",
|
|
18838
|
+
description: "tracked",
|
|
18839
|
+
type: "tracked",
|
|
18840
|
+
startDate: "tracked",
|
|
18841
|
+
endDate: "tracked",
|
|
18842
|
+
durationHours: "tracked",
|
|
18843
|
+
constraintType: "tracked",
|
|
18844
|
+
constraintDate: "tracked",
|
|
18845
|
+
autoScheduling: "tracked",
|
|
18846
|
+
calendarId: "tracked",
|
|
18847
|
+
assignedDefaultCalendar: "ignored",
|
|
18848
|
+
dateOrigin: "tracked",
|
|
18849
|
+
progress: "tracked",
|
|
18850
|
+
cost: "tracked",
|
|
18851
|
+
usedCost: "tracked",
|
|
18852
|
+
realCost: "tracked",
|
|
18853
|
+
workHours: "tracked",
|
|
18854
|
+
realWorkHours: "tracked",
|
|
18855
|
+
ponderator: "tracked",
|
|
18856
|
+
hasCustomPonderator: "tracked",
|
|
18857
|
+
correlativeId: "tracked",
|
|
18858
|
+
uniqueCorrelativeId: "tracked",
|
|
18859
|
+
customId: "tracked",
|
|
18860
|
+
subcontractId: "tracked",
|
|
18861
|
+
isLookahead: "tracked",
|
|
18862
|
+
hasLookaheadTasks: "ignored",
|
|
18863
|
+
newActivityIds: "tracked",
|
|
18864
|
+
hasNewActivities: "tracked",
|
|
18865
|
+
isNewActivity: "ignored",
|
|
18866
|
+
pendingRequestIds: "ignored",
|
|
18867
|
+
responsableIds: "tracked",
|
|
18868
|
+
tagIds: "tracked",
|
|
18869
|
+
baselinePoints: "ignored",
|
|
18870
|
+
baselineSnapshot: "ignored",
|
|
18871
|
+
recursiveDurationHours: "ignored",
|
|
18872
|
+
isCritical: "ignored",
|
|
18873
|
+
freeSlackHours: "ignored",
|
|
18874
|
+
calendarDuration: "ignored",
|
|
18875
|
+
expectedProgress: "ignored",
|
|
18876
|
+
expectedProgressBaseline: "ignored",
|
|
18877
|
+
status: "ignored",
|
|
18878
|
+
criticalPath: "ignored"
|
|
18879
|
+
};
|
|
18880
|
+
var PERSISTABLE_ACTIVITY_FIELDS = Object.keys(ACTIVITY_SAVE_FIELD_POLICY).filter((field) => {
|
|
18881
|
+
return ACTIVITY_SAVE_FIELD_POLICY[field] === "tracked";
|
|
18882
|
+
});
|
|
17784
18883
|
var DATE_FIELDS2 = /* @__PURE__ */ new Set([
|
|
17785
18884
|
"startDate",
|
|
17786
18885
|
"endDate",
|
|
@@ -17808,7 +18907,6 @@ function snapshotFieldValue(field, value) {
|
|
|
17808
18907
|
return value;
|
|
17809
18908
|
}
|
|
17810
18909
|
function activityChangedSince(current, saved) {
|
|
17811
|
-
if (current.assignedDefaultCalendar) return true;
|
|
17812
18910
|
for (const field of PERSISTABLE_ACTIVITY_FIELDS) {
|
|
17813
18911
|
if (!fieldEquals(field, current[field], saved[field])) return true;
|
|
17814
18912
|
}
|
|
@@ -17826,15 +18924,35 @@ function dateEquals(current, saved) {
|
|
|
17826
18924
|
return currentTime === savedTime;
|
|
17827
18925
|
}
|
|
17828
18926
|
function idArrayEquals(current, saved) {
|
|
17829
|
-
const currentIds = Array.isArray(current) ?
|
|
17830
|
-
const savedIds = Array.isArray(saved) ?
|
|
17831
|
-
if (currentIds.
|
|
17832
|
-
|
|
18927
|
+
const currentIds = new Set(Array.isArray(current) ? current.map(String) : []);
|
|
18928
|
+
const savedIds = new Set(Array.isArray(saved) ? saved.map(String) : []);
|
|
18929
|
+
if (currentIds.size !== savedIds.size) return false;
|
|
18930
|
+
for (const currentId of currentIds) {
|
|
18931
|
+
if (!savedIds.has(currentId)) return false;
|
|
18932
|
+
}
|
|
18933
|
+
return true;
|
|
17833
18934
|
}
|
|
17834
18935
|
|
|
17835
18936
|
// src/boundary/save/link-changes.ts
|
|
18937
|
+
var LINK_SAVE_FIELD_POLICY = {
|
|
18938
|
+
id: "ignored",
|
|
18939
|
+
source: "tracked",
|
|
18940
|
+
target: "tracked",
|
|
18941
|
+
type: "tracked",
|
|
18942
|
+
lag: "tracked",
|
|
18943
|
+
freeSlackHours: "ignored",
|
|
18944
|
+
isCritical: "ignored",
|
|
18945
|
+
ganttId: "ignored",
|
|
18946
|
+
sectorId: "ignored",
|
|
18947
|
+
proplannerId: "ignored"
|
|
18948
|
+
};
|
|
18949
|
+
var TRACKED_LINK_FIELDS = Object.keys(LINK_SAVE_FIELD_POLICY).filter((field) => {
|
|
18950
|
+
return LINK_SAVE_FIELD_POLICY[field] === "tracked";
|
|
18951
|
+
});
|
|
17836
18952
|
function linkChangedSince(baseline, current) {
|
|
17837
|
-
return
|
|
18953
|
+
return TRACKED_LINK_FIELDS.some(
|
|
18954
|
+
(field) => baseline[field] !== current[field]
|
|
18955
|
+
);
|
|
17838
18956
|
}
|
|
17839
18957
|
function checkNoUpdatedLinks(baseline, current) {
|
|
17840
18958
|
const baselineById = new Map(
|
|
@@ -17848,23 +18966,73 @@ function checkNoUpdatedLinks(baseline, current) {
|
|
|
17848
18966
|
}
|
|
17849
18967
|
|
|
17850
18968
|
// src/boundary/save/save-tracker.ts
|
|
17851
|
-
|
|
18969
|
+
function hasPersistedIdentity(proplannerId) {
|
|
18970
|
+
const isNewIdentity = proplannerId == null || proplannerId === 0;
|
|
18971
|
+
if (isNewIdentity) return false;
|
|
18972
|
+
const isValidPersistedIdentity = typeof proplannerId === "number" && Number.isSafeInteger(proplannerId) && proplannerId > 0;
|
|
18973
|
+
if (!isValidPersistedIdentity) {
|
|
18974
|
+
throw new Error(`invalid proplannerId ${String(proplannerId)}`);
|
|
18975
|
+
}
|
|
18976
|
+
return true;
|
|
18977
|
+
}
|
|
18978
|
+
function assertValidPersistedIdentities(activities, links) {
|
|
18979
|
+
for (const activity of activities) {
|
|
18980
|
+
hasPersistedIdentity(activity.proplannerId);
|
|
18981
|
+
}
|
|
18982
|
+
for (const link of links) {
|
|
18983
|
+
hasPersistedIdentity(link.proplannerId);
|
|
18984
|
+
}
|
|
18985
|
+
}
|
|
18986
|
+
var SaveTracker = class _SaveTracker {
|
|
18987
|
+
trackingEnabled;
|
|
17852
18988
|
initialized = false;
|
|
17853
18989
|
dirtyActivityIds = /* @__PURE__ */ new Set();
|
|
17854
18990
|
dirtyLinkIds = /* @__PURE__ */ new Set();
|
|
18991
|
+
initialCalendarRepairIds = /* @__PURE__ */ new Set();
|
|
17855
18992
|
linksAtLastSave = /* @__PURE__ */ new Map();
|
|
17856
18993
|
activitiesAtLastSave = /* @__PURE__ */ new Map();
|
|
18994
|
+
constructor(trackingEnabled = true) {
|
|
18995
|
+
this.trackingEnabled = trackingEnabled;
|
|
18996
|
+
}
|
|
18997
|
+
fork() {
|
|
18998
|
+
const fork = new _SaveTracker(false);
|
|
18999
|
+
fork.initialized = this.initialized;
|
|
19000
|
+
for (const [linkId, link] of this.linksAtLastSave) {
|
|
19001
|
+
fork.linksAtLastSave.set(linkId, cloneDomainValue(link));
|
|
19002
|
+
}
|
|
19003
|
+
for (const [activityId, activity] of this.activitiesAtLastSave) {
|
|
19004
|
+
fork.activitiesAtLastSave.set(activityId, cloneDomainValue(activity));
|
|
19005
|
+
}
|
|
19006
|
+
return fork;
|
|
19007
|
+
}
|
|
17857
19008
|
init(activities, links) {
|
|
19009
|
+
if (!this.trackingEnabled) return;
|
|
19010
|
+
assertValidPersistedIdentities(activities, links);
|
|
17858
19011
|
this.snapshotActivities(activities);
|
|
17859
19012
|
this.snapshotLinks(links);
|
|
19013
|
+
for (const activity of activities) {
|
|
19014
|
+
if (activity.assignedDefaultCalendar) {
|
|
19015
|
+
const activityId = String(activity.id);
|
|
19016
|
+
this.initialCalendarRepairIds.add(activityId);
|
|
19017
|
+
this.dirtyActivityIds.add(activityId);
|
|
19018
|
+
}
|
|
19019
|
+
}
|
|
17860
19020
|
this.initialized = true;
|
|
17861
19021
|
}
|
|
19022
|
+
acknowledge(activities, links) {
|
|
19023
|
+
if (!this.trackingEnabled) return;
|
|
19024
|
+
assertValidPersistedIdentities(activities, links);
|
|
19025
|
+
this.snapshotActivities(activities);
|
|
19026
|
+
this.snapshotLinks(links);
|
|
19027
|
+
}
|
|
17862
19028
|
snapshotActivities(activities) {
|
|
19029
|
+
if (!this.trackingEnabled) return;
|
|
17863
19030
|
this.activitiesAtLastSave.clear();
|
|
17864
19031
|
this.dirtyActivityIds.clear();
|
|
19032
|
+
this.initialCalendarRepairIds.clear();
|
|
17865
19033
|
for (const activity of activities) {
|
|
17866
19034
|
const activityId = String(activity.id);
|
|
17867
|
-
if (activity.proplannerId
|
|
19035
|
+
if (!hasPersistedIdentity(activity.proplannerId)) {
|
|
17868
19036
|
this.dirtyActivityIds.add(activityId);
|
|
17869
19037
|
continue;
|
|
17870
19038
|
}
|
|
@@ -17884,11 +19052,12 @@ var SaveTracker = class {
|
|
|
17884
19052
|
}
|
|
17885
19053
|
}
|
|
17886
19054
|
snapshotLinks(links) {
|
|
19055
|
+
if (!this.trackingEnabled) return;
|
|
17887
19056
|
this.linksAtLastSave.clear();
|
|
17888
19057
|
this.dirtyLinkIds.clear();
|
|
17889
19058
|
for (const link of links) {
|
|
17890
19059
|
const linkId = String(link.id);
|
|
17891
|
-
if (link.proplannerId
|
|
19060
|
+
if (!hasPersistedIdentity(link.proplannerId)) {
|
|
17892
19061
|
this.dirtyLinkIds.add(linkId);
|
|
17893
19062
|
continue;
|
|
17894
19063
|
}
|
|
@@ -17903,12 +19072,11 @@ var SaveTracker = class {
|
|
|
17903
19072
|
}
|
|
17904
19073
|
}
|
|
17905
19074
|
recordChanges(activities, links, getActivity, getLink) {
|
|
17906
|
-
if (!this.initialized) return;
|
|
19075
|
+
if (!this.trackingEnabled || !this.initialized) return;
|
|
17907
19076
|
for (const change of activities) {
|
|
17908
19077
|
const activityId = String(change.id);
|
|
17909
19078
|
const current = getActivity(activityId);
|
|
17910
|
-
|
|
17911
|
-
if (current === null ? saved !== void 0 : current.proplannerId == null || saved === void 0 || activityChangedSince(current, saved.persistable)) {
|
|
19079
|
+
if (this.activityIsDirty(activityId, current)) {
|
|
17912
19080
|
this.dirtyActivityIds.add(activityId);
|
|
17913
19081
|
} else {
|
|
17914
19082
|
this.dirtyActivityIds.delete(activityId);
|
|
@@ -17917,81 +19085,147 @@ var SaveTracker = class {
|
|
|
17917
19085
|
for (const change of links) {
|
|
17918
19086
|
const linkId = String(change.id);
|
|
17919
19087
|
const current = getLink(linkId);
|
|
17920
|
-
|
|
17921
|
-
if (current === null ? saved !== void 0 : current.proplannerId == null || saved === void 0 || linkChangedSince(saved, current)) {
|
|
19088
|
+
if (this.linkIsDirty(linkId, current)) {
|
|
17922
19089
|
this.dirtyLinkIds.add(linkId);
|
|
17923
19090
|
} else {
|
|
17924
19091
|
this.dirtyLinkIds.delete(linkId);
|
|
17925
19092
|
}
|
|
17926
19093
|
}
|
|
17927
19094
|
}
|
|
17928
|
-
|
|
17929
|
-
|
|
17930
|
-
|
|
19095
|
+
activityIsDirty(activityId, current) {
|
|
19096
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19097
|
+
if (current === null) return saved !== void 0;
|
|
19098
|
+
return this.initialCalendarRepairIds.has(activityId) || !hasPersistedIdentity(current.proplannerId) || saved === void 0 || activityChangedSince(current, saved.persistable);
|
|
19099
|
+
}
|
|
19100
|
+
linkIsDirty(linkId, current) {
|
|
19101
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19102
|
+
if (current === null) return saved !== void 0;
|
|
19103
|
+
return !hasPersistedIdentity(current.proplannerId) || saved === void 0 || linkChangedSince(saved, current);
|
|
19104
|
+
}
|
|
19105
|
+
acknowledgePersistedLookahead(changes, getActivity) {
|
|
19106
|
+
if (!this.trackingEnabled || !this.initialized) return;
|
|
19107
|
+
const acknowledgements = changes.map((change) => {
|
|
19108
|
+
const activityId = typeof change === "string" ? change : String(change.id);
|
|
17931
19109
|
const current = getActivity(activityId);
|
|
17932
19110
|
const saved = this.activitiesAtLastSave.get(activityId);
|
|
17933
|
-
|
|
19111
|
+
const isUnknownPersistedActivity = current === null || saved === void 0;
|
|
19112
|
+
if (isUnknownPersistedActivity) {
|
|
19113
|
+
throw new Error(`unknown persisted activity "${activityId}"`);
|
|
19114
|
+
}
|
|
19115
|
+
return {
|
|
19116
|
+
activityId,
|
|
19117
|
+
acknowledgesLookahead: typeof change === "string" || Boolean(change.fields?.isLookahead),
|
|
19118
|
+
acknowledgesProgress: typeof change !== "string" && Boolean(change.fields?.progress),
|
|
19119
|
+
current,
|
|
19120
|
+
saved
|
|
19121
|
+
};
|
|
19122
|
+
});
|
|
19123
|
+
for (const {
|
|
19124
|
+
activityId,
|
|
19125
|
+
acknowledgesLookahead,
|
|
19126
|
+
acknowledgesProgress,
|
|
19127
|
+
current,
|
|
19128
|
+
saved
|
|
19129
|
+
} of acknowledgements) {
|
|
17934
19130
|
const persistable = {
|
|
17935
19131
|
...saved.persistable,
|
|
17936
|
-
isLookahead: current.isLookahead
|
|
19132
|
+
...acknowledgesLookahead ? { isLookahead: current.isLookahead } : {},
|
|
19133
|
+
...acknowledgesProgress ? { progress: current.progress } : {}
|
|
17937
19134
|
};
|
|
17938
19135
|
this.activitiesAtLastSave.set(activityId, {
|
|
17939
19136
|
...saved,
|
|
17940
19137
|
persistable
|
|
17941
19138
|
});
|
|
17942
|
-
if (current.proplannerId
|
|
19139
|
+
if (this.initialCalendarRepairIds.has(activityId) || !hasPersistedIdentity(current.proplannerId) || activityChangedSince(current, persistable)) {
|
|
17943
19140
|
this.dirtyActivityIds.add(activityId);
|
|
17944
19141
|
} else {
|
|
17945
19142
|
this.dirtyActivityIds.delete(activityId);
|
|
17946
19143
|
}
|
|
17947
19144
|
}
|
|
17948
19145
|
}
|
|
19146
|
+
hasCheckpointActivityId(activityId) {
|
|
19147
|
+
return this.activitiesAtLastSave.has(activityId);
|
|
19148
|
+
}
|
|
19149
|
+
hasCheckpointLinkId(linkId) {
|
|
19150
|
+
return this.linksAtLastSave.has(linkId);
|
|
19151
|
+
}
|
|
17949
19152
|
hasUnsavedChanges() {
|
|
17950
|
-
return this.initialized && (this.dirtyActivityIds.size > 0 || this.dirtyLinkIds.size > 0);
|
|
19153
|
+
return this.trackingEnabled && this.initialized && (this.dirtyActivityIds.size > 0 || this.dirtyLinkIds.size > 0);
|
|
19154
|
+
}
|
|
19155
|
+
newActivities(getActivity) {
|
|
19156
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19157
|
+
const activities = [];
|
|
19158
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19159
|
+
const activity = getActivity(activityId);
|
|
19160
|
+
const isNewActivity = activity !== null && !hasPersistedIdentity(activity.proplannerId);
|
|
19161
|
+
if (isNewActivity) activities.push(activity);
|
|
19162
|
+
}
|
|
19163
|
+
return activities;
|
|
19164
|
+
}
|
|
19165
|
+
newLinks(getLink) {
|
|
19166
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19167
|
+
const links = [];
|
|
19168
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19169
|
+
const link = getLink(linkId);
|
|
19170
|
+
const isNewLink = link !== null && !hasPersistedIdentity(link.proplannerId);
|
|
19171
|
+
if (isNewLink) links.push(link);
|
|
19172
|
+
}
|
|
19173
|
+
return links;
|
|
19174
|
+
}
|
|
19175
|
+
deletedActivities(getActivity) {
|
|
19176
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19177
|
+
const deleted = [];
|
|
19178
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19179
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19180
|
+
if (saved && getActivity(activityId) === null) {
|
|
19181
|
+
deleted.push({ ...saved.deletedSnapshot });
|
|
19182
|
+
}
|
|
19183
|
+
}
|
|
19184
|
+
return deleted;
|
|
17951
19185
|
}
|
|
17952
|
-
|
|
17953
|
-
if (!this.initialized) return [];
|
|
17954
|
-
const currentIds = new Set(
|
|
17955
|
-
activities.map((activity) => String(activity.id))
|
|
17956
|
-
);
|
|
19186
|
+
deletedLinks(getLink) {
|
|
19187
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
17957
19188
|
const deleted = [];
|
|
17958
|
-
for (const
|
|
17959
|
-
|
|
17960
|
-
deleted.push({ ...saved
|
|
19189
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19190
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19191
|
+
if (saved && getLink(linkId) === null) deleted.push({ ...saved });
|
|
17961
19192
|
}
|
|
17962
19193
|
return deleted;
|
|
17963
19194
|
}
|
|
17964
|
-
|
|
17965
|
-
if (!this.initialized) return [];
|
|
17966
|
-
const
|
|
17967
|
-
|
|
17968
|
-
|
|
17969
|
-
(
|
|
17970
|
-
|
|
17971
|
-
|
|
17972
|
-
|
|
17973
|
-
|
|
17974
|
-
|
|
17975
|
-
|
|
17976
|
-
|
|
17977
|
-
|
|
17978
|
-
if (!saved) return true;
|
|
17979
|
-
return activityChangedSince(activity, saved.persistable);
|
|
17980
|
-
});
|
|
19195
|
+
modifiedActivities(getActivity) {
|
|
19196
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19197
|
+
const modified = [];
|
|
19198
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19199
|
+
const activity = getActivity(activityId);
|
|
19200
|
+
if (activity === null || !hasPersistedIdentity(activity.proplannerId)) {
|
|
19201
|
+
continue;
|
|
19202
|
+
}
|
|
19203
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19204
|
+
if (saved === void 0 || this.initialCalendarRepairIds.has(activityId) || activityChangedSince(activity, saved.persistable)) {
|
|
19205
|
+
modified.push(activity);
|
|
19206
|
+
}
|
|
19207
|
+
}
|
|
19208
|
+
return modified;
|
|
17981
19209
|
}
|
|
17982
|
-
modifiedLinks(
|
|
17983
|
-
if (!this.initialized) return [];
|
|
17984
|
-
|
|
17985
|
-
|
|
17986
|
-
const
|
|
17987
|
-
if (
|
|
17988
|
-
|
|
17989
|
-
|
|
19210
|
+
modifiedLinks(getLink) {
|
|
19211
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19212
|
+
const modified = [];
|
|
19213
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19214
|
+
const link = getLink(linkId);
|
|
19215
|
+
if (link === null) continue;
|
|
19216
|
+
if (!hasPersistedIdentity(link.proplannerId)) continue;
|
|
19217
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19218
|
+
if (saved === void 0 || linkChangedSince(saved, link)) {
|
|
19219
|
+
modified.push(link);
|
|
19220
|
+
}
|
|
19221
|
+
}
|
|
19222
|
+
return modified;
|
|
17990
19223
|
}
|
|
17991
19224
|
clear() {
|
|
17992
19225
|
this.initialized = false;
|
|
17993
19226
|
this.dirtyActivityIds.clear();
|
|
17994
19227
|
this.dirtyLinkIds.clear();
|
|
19228
|
+
this.initialCalendarRepairIds.clear();
|
|
17995
19229
|
this.linksAtLastSave.clear();
|
|
17996
19230
|
this.activitiesAtLastSave.clear();
|
|
17997
19231
|
}
|
|
@@ -18045,14 +19279,14 @@ function isAutoSchedulable(progress) {
|
|
|
18045
19279
|
}
|
|
18046
19280
|
function parseConstraintLabel(label, reporter) {
|
|
18047
19281
|
if (!label) return null;
|
|
18048
|
-
const
|
|
18049
|
-
if (
|
|
19282
|
+
const normalized2 = CONSTRAINT_LABEL_MAP[label.toUpperCase()];
|
|
19283
|
+
if (normalized2 === void 0) {
|
|
18050
19284
|
reporter.warn(
|
|
18051
19285
|
`[parseFromBackend] unknown constraint label "${label}" \u2014 defaulting to "asap"`
|
|
18052
19286
|
);
|
|
18053
19287
|
return DEFAULT_CONSTRAINT_TYPE;
|
|
18054
19288
|
}
|
|
18055
|
-
return
|
|
19289
|
+
return normalized2;
|
|
18056
19290
|
}
|
|
18057
19291
|
function resolveConstraint(raw, reporter) {
|
|
18058
19292
|
const constraintType = parseConstraintLabel(raw.constraint, reporter);
|
|
@@ -18093,6 +19327,24 @@ function parseIdList(raw) {
|
|
|
18093
19327
|
function parseParentId(rawParentId) {
|
|
18094
19328
|
return rawParentId === ROOT_PARENT_ID ? null : String(rawParentId);
|
|
18095
19329
|
}
|
|
19330
|
+
function normalizePersistedActivityId(rawId) {
|
|
19331
|
+
if (rawId == null) return null;
|
|
19332
|
+
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
19333
|
+
if (!isValidIdentity) {
|
|
19334
|
+
throw new Error(`invalid activity proplannerId ${String(rawId)}`);
|
|
19335
|
+
}
|
|
19336
|
+
return rawId;
|
|
19337
|
+
}
|
|
19338
|
+
function parseCriticality(raw, reporter) {
|
|
19339
|
+
if (typeof raw.is_critical === "boolean") return raw.is_critical;
|
|
19340
|
+
reporter.error(
|
|
19341
|
+
"[parseFromBackend] invalid activity is_critical \u2014 defaulting to false",
|
|
19342
|
+
new TypeError(
|
|
19343
|
+
`activity "${raw.unique_id}" received ${typeof raw.is_critical}`
|
|
19344
|
+
)
|
|
19345
|
+
);
|
|
19346
|
+
return false;
|
|
19347
|
+
}
|
|
18096
19348
|
function parseBaselinePoints(raw, reporter) {
|
|
18097
19349
|
if (!Array.isArray(raw)) return [];
|
|
18098
19350
|
return raw.map((point) => normalizeBaselinePoint(point, reporter)).filter((point) => point !== null);
|
|
@@ -18113,9 +19365,10 @@ function parseActivity(raw, context) {
|
|
|
18113
19365
|
const startDate = parseBackendDate(raw.start_date);
|
|
18114
19366
|
const endDate = isMilestone ? startDate : parseBackendDate(raw.end_date);
|
|
18115
19367
|
const durationDays = isMilestone ? 0 : Number(raw.duration);
|
|
19368
|
+
const isCritical = parseCriticality(raw, context.reporter);
|
|
18116
19369
|
return {
|
|
18117
19370
|
id: String(raw.unique_id),
|
|
18118
|
-
proplannerId:
|
|
19371
|
+
proplannerId: normalizePersistedActivityId(raw.id),
|
|
18119
19372
|
parentId: parseParentId(raw.parent_id),
|
|
18120
19373
|
name: raw.name,
|
|
18121
19374
|
description: raw.description ?? "",
|
|
@@ -18155,7 +19408,12 @@ function parseActivity(raw, context) {
|
|
|
18155
19408
|
Number(raw.sumOfDurationRecursively ?? 0),
|
|
18156
19409
|
context.hoursPerDay
|
|
18157
19410
|
),
|
|
18158
|
-
|
|
19411
|
+
calendarDuration: computeCalendarDuration({
|
|
19412
|
+
type: raw.type,
|
|
19413
|
+
startDate,
|
|
19414
|
+
endDate
|
|
19415
|
+
}),
|
|
19416
|
+
isCritical,
|
|
18159
19417
|
freeSlackHours: null,
|
|
18160
19418
|
expectedProgress: null,
|
|
18161
19419
|
expectedProgressBaseline: null,
|
|
@@ -18412,6 +19670,14 @@ function resolveLinkType(rawType) {
|
|
|
18412
19670
|
if (isLinkTypeCode(rawType)) return rawType;
|
|
18413
19671
|
return null;
|
|
18414
19672
|
}
|
|
19673
|
+
function normalizePersistedLinkId(rawId) {
|
|
19674
|
+
if (rawId == null) return void 0;
|
|
19675
|
+
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
19676
|
+
if (!isValidIdentity) {
|
|
19677
|
+
throw new Error(`invalid link proplannerId ${String(rawId)}`);
|
|
19678
|
+
}
|
|
19679
|
+
return rawId;
|
|
19680
|
+
}
|
|
18415
19681
|
function normalizeLinks(rawLinks, context) {
|
|
18416
19682
|
const parsedLinks = [];
|
|
18417
19683
|
for (const rawLink of rawLinks) {
|
|
@@ -18450,16 +19716,18 @@ function parseLink(link, activityIds, hoursPerDay) {
|
|
|
18450
19716
|
}
|
|
18451
19717
|
const ganttIdRaw = link.ganttId;
|
|
18452
19718
|
const sectorIdRaw = link.sectorId;
|
|
18453
|
-
const
|
|
19719
|
+
const proplannerId = normalizePersistedLinkId(link.id);
|
|
18454
19720
|
return {
|
|
18455
19721
|
id,
|
|
18456
19722
|
source,
|
|
18457
19723
|
target,
|
|
18458
19724
|
type,
|
|
18459
19725
|
lag: lagDaysToHours(Number(link.lag ?? 0), hoursPerDay),
|
|
19726
|
+
freeSlackHours: null,
|
|
19727
|
+
isCritical: false,
|
|
18460
19728
|
...typeof ganttIdRaw === "number" ? { ganttId: ganttIdRaw } : {},
|
|
18461
19729
|
...typeof sectorIdRaw === "number" ? { sectorId: sectorIdRaw } : {},
|
|
18462
|
-
...
|
|
19730
|
+
...proplannerId === void 0 ? {} : { proplannerId }
|
|
18463
19731
|
};
|
|
18464
19732
|
}
|
|
18465
19733
|
function dedupeLinksBySignature(links) {
|
|
@@ -18519,7 +19787,8 @@ function parseSector(sector) {
|
|
|
18519
19787
|
...customIdIncrement !== void 0 ? { customIdIncrement } : {},
|
|
18520
19788
|
...isPrimaveraEndDate ? { updateDurationForPrimaveraEndDate: true } : {},
|
|
18521
19789
|
activityCreter: parseActivityCreter(sector.activity_creter),
|
|
18522
|
-
statusCriteria: DEFAULT_STATUS_CRITERIA
|
|
19790
|
+
statusCriteria: DEFAULT_STATUS_CRITERIA,
|
|
19791
|
+
ganttId: null
|
|
18523
19792
|
};
|
|
18524
19793
|
}
|
|
18525
19794
|
|
|
@@ -18585,6 +19854,9 @@ function createActivityIdGenerator(options = {}) {
|
|
|
18585
19854
|
}
|
|
18586
19855
|
}
|
|
18587
19856
|
return {
|
|
19857
|
+
fork() {
|
|
19858
|
+
return createActivityIdGenerator({ seed: counter });
|
|
19859
|
+
},
|
|
18588
19860
|
next() {
|
|
18589
19861
|
counter += 1;
|
|
18590
19862
|
return String(counter);
|
|
@@ -18616,6 +19888,9 @@ function createLinkIdGenerator(options = {}) {
|
|
|
18616
19888
|
}
|
|
18617
19889
|
}
|
|
18618
19890
|
return {
|
|
19891
|
+
fork() {
|
|
19892
|
+
return createLinkIdGenerator({ seed: counter });
|
|
19893
|
+
},
|
|
18619
19894
|
next() {
|
|
18620
19895
|
counter += 1;
|
|
18621
19896
|
return String(counter);
|
|
@@ -18651,6 +19926,9 @@ function createUniqueCorrelativeIdGenerator(options = {}) {
|
|
|
18651
19926
|
}
|
|
18652
19927
|
}
|
|
18653
19928
|
return {
|
|
19929
|
+
fork() {
|
|
19930
|
+
return createUniqueCorrelativeIdGenerator({ seed: counter });
|
|
19931
|
+
},
|
|
18654
19932
|
next() {
|
|
18655
19933
|
counter += 1;
|
|
18656
19934
|
return String(counter);
|
|
@@ -18778,7 +20056,7 @@ function selectGenerationStrategy(customId, useActivityCustomIdAsReference) {
|
|
|
18778
20056
|
function cloneStats(stats) {
|
|
18779
20057
|
return { ...stats, usedSuffixes: new Set(stats.usedSuffixes) };
|
|
18780
20058
|
}
|
|
18781
|
-
var CustomIdTracker = class {
|
|
20059
|
+
var CustomIdTracker = class _CustomIdTracker {
|
|
18782
20060
|
prefixMap = /* @__PURE__ */ new Map();
|
|
18783
20061
|
processedIds = /* @__PURE__ */ new Set();
|
|
18784
20062
|
_journalPrefixBefore = null;
|
|
@@ -18839,6 +20117,19 @@ var CustomIdTracker = class {
|
|
|
18839
20117
|
this.defaultPrefix = options.defaultPrefix ?? DEFAULT_PREFIX;
|
|
18840
20118
|
this.suffixIncrement = options.suffixIncrement ?? DEFAULT_SUFFIX_INCREMENT;
|
|
18841
20119
|
}
|
|
20120
|
+
fork() {
|
|
20121
|
+
const fork = new _CustomIdTracker({
|
|
20122
|
+
defaultPrefix: this.defaultPrefix,
|
|
20123
|
+
suffixIncrement: this.suffixIncrement
|
|
20124
|
+
});
|
|
20125
|
+
for (const [prefix, stats] of this.prefixMap) {
|
|
20126
|
+
fork.prefixMap.set(prefix, cloneStats(stats));
|
|
20127
|
+
}
|
|
20128
|
+
for (const customId of this.processedIds) {
|
|
20129
|
+
fork.processedIds.add(customId);
|
|
20130
|
+
}
|
|
20131
|
+
return fork;
|
|
20132
|
+
}
|
|
18842
20133
|
extractPrefix(customId) {
|
|
18843
20134
|
return extractPrefix(customId);
|
|
18844
20135
|
}
|
|
@@ -19311,6 +20602,7 @@ function recomputeSkippedLeafEnds(state) {
|
|
|
19311
20602
|
durationHours: activity.durationHours,
|
|
19312
20603
|
task: activity
|
|
19313
20604
|
});
|
|
20605
|
+
activity.calendarDuration = computeCalendarDuration(activity);
|
|
19314
20606
|
});
|
|
19315
20607
|
}
|
|
19316
20608
|
function recomputeDurationsFromDates(state) {
|
|
@@ -19382,6 +20674,7 @@ function initializeCore(input) {
|
|
|
19382
20674
|
);
|
|
19383
20675
|
const sector = parsed2.sector;
|
|
19384
20676
|
sector.statusCriteria = resolveStatusCriteria(input.statusCriteria);
|
|
20677
|
+
sector.ganttId = input.ganttId ?? null;
|
|
19385
20678
|
const calendars = parsed2.calendars;
|
|
19386
20679
|
const baseCalendars = parsed2.baseCalendars ?? [];
|
|
19387
20680
|
const snapshot = buildStateSnapshot(
|
|
@@ -19443,6 +20736,133 @@ function initializeCore(input) {
|
|
|
19443
20736
|
};
|
|
19444
20737
|
}
|
|
19445
20738
|
|
|
20739
|
+
// src/previsualization/canonical-digest.ts
|
|
20740
|
+
function normalized(value) {
|
|
20741
|
+
if (value === void 0) return ["undefined"];
|
|
20742
|
+
if (value === null) return ["null"];
|
|
20743
|
+
if (typeof value === "string") return ["string", value];
|
|
20744
|
+
if (typeof value === "boolean") return ["boolean", value];
|
|
20745
|
+
if (typeof value === "number") {
|
|
20746
|
+
return ["number", Object.is(value, -0) ? "-0" : String(value)];
|
|
20747
|
+
}
|
|
20748
|
+
if (typeof value === "bigint") return ["bigint", String(value)];
|
|
20749
|
+
if (value instanceof Date) return ["date", String(value.getTime())];
|
|
20750
|
+
if (Array.isArray(value)) return ["array", value.map(normalized)];
|
|
20751
|
+
if (value instanceof Map) {
|
|
20752
|
+
return [
|
|
20753
|
+
"map",
|
|
20754
|
+
Array.from(value, ([key, entry]) => [
|
|
20755
|
+
normalized(key),
|
|
20756
|
+
normalized(entry)
|
|
20757
|
+
]).sort(
|
|
20758
|
+
(left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))
|
|
20759
|
+
)
|
|
20760
|
+
];
|
|
20761
|
+
}
|
|
20762
|
+
if (value instanceof Set) {
|
|
20763
|
+
return [
|
|
20764
|
+
"set",
|
|
20765
|
+
Array.from(value, normalized).sort(
|
|
20766
|
+
(left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))
|
|
20767
|
+
)
|
|
20768
|
+
];
|
|
20769
|
+
}
|
|
20770
|
+
if (typeof value === "object") {
|
|
20771
|
+
return [
|
|
20772
|
+
"object",
|
|
20773
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalized(entry)])
|
|
20774
|
+
];
|
|
20775
|
+
}
|
|
20776
|
+
throw new TypeError(`Unsupported canonical value: ${typeof value}`);
|
|
20777
|
+
}
|
|
20778
|
+
function serializeCanonicalSchedule(value) {
|
|
20779
|
+
return JSON.stringify(normalized(value));
|
|
20780
|
+
}
|
|
20781
|
+
|
|
20782
|
+
// src/previsualization/reconciliation.ts
|
|
20783
|
+
function entityChanges(target, source) {
|
|
20784
|
+
const targetById = new Map(
|
|
20785
|
+
target.map((entity) => [String(entity.id), entity])
|
|
20786
|
+
);
|
|
20787
|
+
const sourceById = new Map(
|
|
20788
|
+
source.map((entity) => [String(entity.id), entity])
|
|
20789
|
+
);
|
|
20790
|
+
const ids = [.../* @__PURE__ */ new Set([...targetById.keys(), ...sourceById.keys()])].sort();
|
|
20791
|
+
const changes = [];
|
|
20792
|
+
for (const id of ids) {
|
|
20793
|
+
const before = sourceById.get(id);
|
|
20794
|
+
const after = targetById.get(id);
|
|
20795
|
+
if (after === void 0) {
|
|
20796
|
+
changes.push({ id, kind: "deleted", after: null });
|
|
20797
|
+
continue;
|
|
20798
|
+
}
|
|
20799
|
+
if (before === void 0) {
|
|
20800
|
+
changes.push({ id, kind: "created", after: cloneDomainValue(after) });
|
|
20801
|
+
continue;
|
|
20802
|
+
}
|
|
20803
|
+
const fields = changedFields(before, after);
|
|
20804
|
+
if (Object.keys(fields).length > 0) {
|
|
20805
|
+
changes.push({
|
|
20806
|
+
id,
|
|
20807
|
+
kind: "updated",
|
|
20808
|
+
fields,
|
|
20809
|
+
after: cloneDomainValue(after)
|
|
20810
|
+
});
|
|
20811
|
+
}
|
|
20812
|
+
}
|
|
20813
|
+
return changes;
|
|
20814
|
+
}
|
|
20815
|
+
function changedFields(before, after) {
|
|
20816
|
+
const fields = {};
|
|
20817
|
+
const keys = [
|
|
20818
|
+
.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])
|
|
20819
|
+
].sort();
|
|
20820
|
+
for (const key of keys) {
|
|
20821
|
+
if (serializeCanonicalSchedule(before[key]) !== serializeCanonicalSchedule(after[key])) {
|
|
20822
|
+
fields[key] = {
|
|
20823
|
+
before: cloneDomainValue(before[key]),
|
|
20824
|
+
after: cloneDomainValue(after[key])
|
|
20825
|
+
};
|
|
20826
|
+
}
|
|
20827
|
+
}
|
|
20828
|
+
return fields;
|
|
20829
|
+
}
|
|
20830
|
+
function viewStateChanges(target, source) {
|
|
20831
|
+
const selectedBefore = new Set(source.selectedIds.map(String));
|
|
20832
|
+
const selectedAfter = new Set(target.selectedIds.map(String));
|
|
20833
|
+
const hiddenBefore = new Set(source.hiddenIds.map(String));
|
|
20834
|
+
const hiddenAfter = new Set(target.hiddenIds.map(String));
|
|
20835
|
+
const targetIds = target.activities.map(({ id }) => String(id)).sort();
|
|
20836
|
+
const changes = [];
|
|
20837
|
+
for (const activityId of targetIds) {
|
|
20838
|
+
const checkedBefore = selectedBefore.has(activityId);
|
|
20839
|
+
const checkedAfter = selectedAfter.has(activityId);
|
|
20840
|
+
const visibleBefore = !hiddenBefore.has(activityId);
|
|
20841
|
+
const visibleAfter = !hiddenAfter.has(activityId);
|
|
20842
|
+
if (checkedBefore === checkedAfter && visibleBefore === visibleAfter)
|
|
20843
|
+
continue;
|
|
20844
|
+
changes.push({
|
|
20845
|
+
activityId,
|
|
20846
|
+
...checkedBefore === checkedAfter ? {} : { checked: { before: checkedBefore, after: checkedAfter } },
|
|
20847
|
+
...visibleBefore === visibleAfter ? {} : { visible: { before: visibleBefore, after: visibleAfter } }
|
|
20848
|
+
});
|
|
20849
|
+
}
|
|
20850
|
+
return changes;
|
|
20851
|
+
}
|
|
20852
|
+
function createProjectionChangeSet(target, source) {
|
|
20853
|
+
const orderChanged = serializeCanonicalSchedule(target.order) !== serializeCanonicalSchedule(source.order);
|
|
20854
|
+
return {
|
|
20855
|
+
source: { kind: "init" },
|
|
20856
|
+
activities: entityChanges(target.activities, source.activities),
|
|
20857
|
+
links: entityChanges(target.links, source.links),
|
|
20858
|
+
calendars: entityChanges(target.calendars, source.calendars),
|
|
20859
|
+
trackingEvents: [],
|
|
20860
|
+
effects: [],
|
|
20861
|
+
viewState: viewStateChanges(target, source),
|
|
20862
|
+
...orderChanged ? { order: cloneDomainValue(target.order) } : {}
|
|
20863
|
+
};
|
|
20864
|
+
}
|
|
20865
|
+
|
|
19446
20866
|
// src/propagations/upward/date-gesture-preview.ts
|
|
19447
20867
|
var emptyFormula = () => ({
|
|
19448
20868
|
constantStart: null,
|
|
@@ -19588,6 +21008,10 @@ function evaluateEnd(formula, proposedEdgeTimeMs) {
|
|
|
19588
21008
|
}
|
|
19589
21009
|
|
|
19590
21010
|
// src/init/schedule-core.ts
|
|
21011
|
+
var FORK_SOURCE = /* @__PURE__ */ Symbol("ScheduleCore.fork.source");
|
|
21012
|
+
function acknowledgesPersistence(action) {
|
|
21013
|
+
return action.kind === "persistence-acknowledge" || action.kind === "activity-lookahead-sync" && action.acknowledgePersistedLookahead === true;
|
|
21014
|
+
}
|
|
19591
21015
|
function changesRequireFilterProjectionRebuild(changes) {
|
|
19592
21016
|
for (const change of changes.activities) {
|
|
19593
21017
|
if (change.kind === "deleted") return true;
|
|
@@ -19611,9 +21035,11 @@ function isCurrentFilterProjection(projection, filter, context) {
|
|
|
19611
21035
|
const hasCurrentLocale = projection.context.locale === context.locale;
|
|
19612
21036
|
return hasCurrentHours && hasCurrentLocale;
|
|
19613
21037
|
}
|
|
19614
|
-
var ScheduleCore = class {
|
|
21038
|
+
var ScheduleCore = class _ScheduleCore {
|
|
21039
|
+
_activityClipboard = { value: null };
|
|
19615
21040
|
_status = SCHEDULE_CORE_STATUS.READY;
|
|
19616
21041
|
coreRuntime;
|
|
21042
|
+
_isFork;
|
|
19617
21043
|
_undo = new UndoRecorder();
|
|
19618
21044
|
_opQueue = Promise.resolve();
|
|
19619
21045
|
_scheduleRevision = 0;
|
|
@@ -19644,6 +21070,42 @@ var ScheduleCore = class {
|
|
|
19644
21070
|
_criticalPathRevision = -1;
|
|
19645
21071
|
_activeCriticalPath = null;
|
|
19646
21072
|
constructor(input) {
|
|
21073
|
+
if (FORK_SOURCE in input) {
|
|
21074
|
+
this._isFork = true;
|
|
21075
|
+
const source = input[FORK_SOURCE];
|
|
21076
|
+
const state = source.coreRuntime.state.fork();
|
|
21077
|
+
this.coreRuntime = {
|
|
21078
|
+
state,
|
|
21079
|
+
scheduler: new AutoScheduler(state, source.coreRuntime.reporter),
|
|
21080
|
+
sector: cloneDomainValue(source.coreRuntime.sector),
|
|
21081
|
+
activityIdGenerator: source.coreRuntime.activityIdGenerator.fork(),
|
|
21082
|
+
linkIdGenerator: source.coreRuntime.linkIdGenerator.fork(),
|
|
21083
|
+
uniqueCorrelativeIdGenerator: source.coreRuntime.uniqueCorrelativeIdGenerator.fork(),
|
|
21084
|
+
customIdTracker: source.coreRuntime.customIdTracker.fork(),
|
|
21085
|
+
calendars: new Map(
|
|
21086
|
+
Array.from(source.coreRuntime.calendars, ([id, calendar]) => [
|
|
21087
|
+
id,
|
|
21088
|
+
cloneDomainValue(calendar)
|
|
21089
|
+
])
|
|
21090
|
+
),
|
|
21091
|
+
baseCalendars: cloneDomainValue(source.coreRuntime.baseCalendars),
|
|
21092
|
+
clock: source.coreRuntime.clock,
|
|
21093
|
+
reporter: source.coreRuntime.reporter
|
|
21094
|
+
};
|
|
21095
|
+
this._activityClipboard.value = cloneDomainValue(
|
|
21096
|
+
source._activityClipboard.value
|
|
21097
|
+
);
|
|
21098
|
+
this._undo = source._undo.fork();
|
|
21099
|
+
this._saveTracker = source._saveTracker.fork();
|
|
21100
|
+
this._scheduleRevision = source._scheduleRevision;
|
|
21101
|
+
this._pendingScheduleRevision = source._scheduleRevision;
|
|
21102
|
+
this._criticalPathRevision = source._criticalPathRevision;
|
|
21103
|
+
this._initPromise = Promise.resolve();
|
|
21104
|
+
this._criticalPathReady = Promise.resolve(null);
|
|
21105
|
+
this._ready = this._initPromise;
|
|
21106
|
+
return;
|
|
21107
|
+
}
|
|
21108
|
+
this._isFork = false;
|
|
19647
21109
|
const initialized = initializeCore(input);
|
|
19648
21110
|
this.coreRuntime = {
|
|
19649
21111
|
state: initialized.state,
|
|
@@ -19680,6 +21142,15 @@ var ScheduleCore = class {
|
|
|
19680
21142
|
get ready() {
|
|
19681
21143
|
return this._ready;
|
|
19682
21144
|
}
|
|
21145
|
+
fork() {
|
|
21146
|
+
return this._enqueue(async () => {
|
|
21147
|
+
await this._ready;
|
|
21148
|
+
this.assertReady();
|
|
21149
|
+
return new _ScheduleCore({
|
|
21150
|
+
[FORK_SOURCE]: this
|
|
21151
|
+
});
|
|
21152
|
+
});
|
|
21153
|
+
}
|
|
19683
21154
|
get criticalPathReady() {
|
|
19684
21155
|
return this._criticalPathReady;
|
|
19685
21156
|
}
|
|
@@ -19707,6 +21178,23 @@ var ScheduleCore = class {
|
|
|
19707
21178
|
this.assertReady();
|
|
19708
21179
|
return readAllActivities(this.coreRuntime.state);
|
|
19709
21180
|
}
|
|
21181
|
+
/**
|
|
21182
|
+
* Read del plan de indent (ISSUE-080): los padres destino DISTINTOS bajo los
|
|
21183
|
+
* que quedarían las actividades si se indentaran ahora. Es exactamente la
|
|
21184
|
+
* planificación que ejecuta el handler (`planIndentMoves`, autoridad única),
|
|
21185
|
+
* expuesta para que la frontera pueda correr el protocolo de conversión a
|
|
21186
|
+
* madre (chequeo backend + modal) ANTES de despachar. Solo lecturas.
|
|
21187
|
+
*/
|
|
21188
|
+
getIndentTargetParentIds(activityIds) {
|
|
21189
|
+
this.assertReady();
|
|
21190
|
+
const plan = planIndentMoves(
|
|
21191
|
+
activityIds.map((id) => String(id)),
|
|
21192
|
+
this.coreRuntime.state
|
|
21193
|
+
);
|
|
21194
|
+
return [
|
|
21195
|
+
...new Set(plan.allowedMoves.map((move) => String(move.newParentId)))
|
|
21196
|
+
];
|
|
21197
|
+
}
|
|
19710
21198
|
getChildrenView(parentId) {
|
|
19711
21199
|
this.assertReady();
|
|
19712
21200
|
return readChildren(this.coreRuntime.state, parentId);
|
|
@@ -19715,10 +21203,45 @@ var ScheduleCore = class {
|
|
|
19715
21203
|
this.assertReady();
|
|
19716
21204
|
return readLink(this.coreRuntime.state, id);
|
|
19717
21205
|
}
|
|
21206
|
+
getLinksForActivityView(activityId) {
|
|
21207
|
+
this.assertReady();
|
|
21208
|
+
const state = this.coreRuntime.state;
|
|
21209
|
+
const links = [];
|
|
21210
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21211
|
+
const append = (linkId) => {
|
|
21212
|
+
if (seen.has(linkId)) return;
|
|
21213
|
+
seen.add(linkId);
|
|
21214
|
+
const link = readLink(state, linkId);
|
|
21215
|
+
if (link) links.push(link);
|
|
21216
|
+
};
|
|
21217
|
+
for (const linkId of state.getOutgoingLinkIds(activityId)) append(linkId);
|
|
21218
|
+
for (const linkId of state.getIncomingLinkIds(activityId)) append(linkId);
|
|
21219
|
+
return links;
|
|
21220
|
+
}
|
|
19718
21221
|
getAllLinksView() {
|
|
19719
21222
|
this.assertReady();
|
|
19720
21223
|
return readAllLinks(this.coreRuntime.state);
|
|
19721
21224
|
}
|
|
21225
|
+
auditEffectiveGraph() {
|
|
21226
|
+
this.assertReady();
|
|
21227
|
+
const state = this.coreRuntime.state;
|
|
21228
|
+
const logicalLinks = state.getAllLinks();
|
|
21229
|
+
const effectiveEdges = projectEffectiveEdges(state, logicalLinks);
|
|
21230
|
+
const components = circularComponents(effectiveEdges);
|
|
21231
|
+
let largestComponentSize = 0;
|
|
21232
|
+
for (const component of components) {
|
|
21233
|
+
if (component.length > largestComponentSize) {
|
|
21234
|
+
largestComponentSize = component.length;
|
|
21235
|
+
}
|
|
21236
|
+
}
|
|
21237
|
+
return {
|
|
21238
|
+
activityCount: state.getAllIds().length,
|
|
21239
|
+
logicalLinkCount: logicalLinks.length,
|
|
21240
|
+
effectiveEdgeCount: effectiveEdges.length,
|
|
21241
|
+
circularComponents: components,
|
|
21242
|
+
largestComponentSize
|
|
21243
|
+
};
|
|
21244
|
+
}
|
|
19722
21245
|
getProperty(activityId, property) {
|
|
19723
21246
|
this.assertReady();
|
|
19724
21247
|
const snapshot = this.coreRuntime.state.getActivity(activityId);
|
|
@@ -19747,11 +21270,11 @@ var ScheduleCore = class {
|
|
|
19747
21270
|
}
|
|
19748
21271
|
getActiveFilter() {
|
|
19749
21272
|
this.assertReady();
|
|
19750
|
-
return this.coreRuntime.state.getActiveFilter();
|
|
21273
|
+
return cloneDomainValue(this.coreRuntime.state.getActiveFilter());
|
|
19751
21274
|
}
|
|
19752
21275
|
getActiveOrder() {
|
|
19753
21276
|
this.assertReady();
|
|
19754
|
-
return this.coreRuntime.state.getActiveOrder();
|
|
21277
|
+
return cloneDomainValue(this.coreRuntime.state.getActiveOrder());
|
|
19755
21278
|
}
|
|
19756
21279
|
getVisualOrderIds() {
|
|
19757
21280
|
this.assertReady();
|
|
@@ -19792,23 +21315,65 @@ var ScheduleCore = class {
|
|
|
19792
21315
|
}
|
|
19793
21316
|
getModifiedLinks() {
|
|
19794
21317
|
this.assertReady();
|
|
19795
|
-
return this._saveTracker.modifiedLinks(this.
|
|
21318
|
+
return this._saveTracker.modifiedLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
21319
|
+
}
|
|
21320
|
+
getNewLinks() {
|
|
21321
|
+
this.assertReady();
|
|
21322
|
+
return this._saveTracker.newLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
19796
21323
|
}
|
|
19797
21324
|
getModifiedActivities() {
|
|
19798
21325
|
this.assertReady();
|
|
19799
|
-
return this._saveTracker.modifiedActivities(
|
|
21326
|
+
return this._saveTracker.modifiedActivities(
|
|
21327
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21328
|
+
).map(cloneDomainValue);
|
|
21329
|
+
}
|
|
21330
|
+
getNewActivities() {
|
|
21331
|
+
this.assertReady();
|
|
21332
|
+
return this._saveTracker.newActivities(
|
|
21333
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21334
|
+
).map(cloneDomainValue);
|
|
19800
21335
|
}
|
|
19801
21336
|
hasUnsavedChanges() {
|
|
19802
21337
|
this.assertReady();
|
|
19803
21338
|
return this._saveTracker.hasUnsavedChanges();
|
|
19804
21339
|
}
|
|
21340
|
+
/** Read-only revision; moves once per substantive mutation. */
|
|
21341
|
+
getScheduleRevision() {
|
|
21342
|
+
return this._scheduleRevision;
|
|
21343
|
+
}
|
|
21344
|
+
getCanonicalDigest() {
|
|
21345
|
+
this.assertReady();
|
|
21346
|
+
const byId = (left, right) => String(left.id).localeCompare(String(right.id));
|
|
21347
|
+
return serializeCanonicalSchedule({
|
|
21348
|
+
sector: this.getSector(),
|
|
21349
|
+
activities: [...this.getAllActivitiesView()].sort(byId),
|
|
21350
|
+
links: [...this.getAllLinksView()].sort(byId),
|
|
21351
|
+
calendars: [...this.getAllCalendars()].sort(byId),
|
|
21352
|
+
baseCalendars: [...this.getBaseCalendars()].sort(byId)
|
|
21353
|
+
});
|
|
21354
|
+
}
|
|
21355
|
+
createProjectionChangeSetFrom(source) {
|
|
21356
|
+
this.assertReady();
|
|
21357
|
+
source.assertReady();
|
|
21358
|
+
const projectionOf = (core) => ({
|
|
21359
|
+
activities: core.getAllActivitiesView(),
|
|
21360
|
+
links: core.getAllLinksView(),
|
|
21361
|
+
calendars: core.getAllCalendars(),
|
|
21362
|
+
selectedIds: core.getSelectedActivityIds(),
|
|
21363
|
+
hiddenIds: core.getHiddenActivityIds(),
|
|
21364
|
+
order: collectBranchOrder(core.coreRuntime.state)
|
|
21365
|
+
});
|
|
21366
|
+
return createProjectionChangeSet(projectionOf(this), projectionOf(source));
|
|
21367
|
+
}
|
|
19805
21368
|
getDeletedActivitiesSinceLastSave() {
|
|
19806
21369
|
this.assertReady();
|
|
19807
|
-
return this._saveTracker.deletedActivities(
|
|
21370
|
+
return this._saveTracker.deletedActivities(
|
|
21371
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21372
|
+
).map(cloneDomainValue);
|
|
19808
21373
|
}
|
|
19809
21374
|
getDeletedLinksSinceLastSave() {
|
|
19810
21375
|
this.assertReady();
|
|
19811
|
-
return this._saveTracker.deletedLinks(this.
|
|
21376
|
+
return this._saveTracker.deletedLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
19812
21377
|
}
|
|
19813
21378
|
_enqueue(work) {
|
|
19814
21379
|
const workPromise = this._opQueue.then(work, work);
|
|
@@ -19903,10 +21468,18 @@ var ScheduleCore = class {
|
|
|
19903
21468
|
}
|
|
19904
21469
|
async _runDispatch(action, options) {
|
|
19905
21470
|
this.assertReady();
|
|
21471
|
+
if (this._isFork && acknowledgesPersistence(action)) {
|
|
21472
|
+
return {
|
|
21473
|
+
result: {
|
|
21474
|
+
ok: false,
|
|
21475
|
+
reason: REJECTION_REASON.SANDBOX_PERSISTENCE_FORBIDDEN
|
|
21476
|
+
},
|
|
21477
|
+
changedState: false
|
|
21478
|
+
};
|
|
21479
|
+
}
|
|
19906
21480
|
const pipelineContext = buildPipelineContext(this.coreRuntime.state, {
|
|
19907
21481
|
hoursPerDay: this.coreRuntime.sector.hoursPerDay,
|
|
19908
|
-
dateFormat: this.coreRuntime.sector.dateFormat
|
|
19909
|
-
inputUnit: options.inputUnit ?? "days"
|
|
21482
|
+
dateFormat: this.coreRuntime.sector.dateFormat
|
|
19910
21483
|
});
|
|
19911
21484
|
const historyPolicy = getDispatchHistoryPolicy(action);
|
|
19912
21485
|
const generatorSnapshot = [
|
|
@@ -19926,9 +21499,12 @@ var ScheduleCore = class {
|
|
|
19926
21499
|
calendars: this.coreRuntime.calendars,
|
|
19927
21500
|
activityIdGen: this.coreRuntime.activityIdGenerator,
|
|
19928
21501
|
linkIdGen: this.coreRuntime.linkIdGenerator,
|
|
21502
|
+
isActivityIdReserved: (activityId) => this._saveTracker.hasCheckpointActivityId(activityId),
|
|
21503
|
+
isLinkIdReserved: (linkId) => this._saveTracker.hasCheckpointLinkId(linkId),
|
|
19929
21504
|
uidGen: this.coreRuntime.uniqueCorrelativeIdGenerator,
|
|
19930
21505
|
customIdTracker: this.coreRuntime.customIdTracker,
|
|
19931
21506
|
reporter: this.coreRuntime.reporter,
|
|
21507
|
+
activityClipboard: this._activityClipboard,
|
|
19932
21508
|
defaultBaseCalendarId: this.coreRuntime.baseCalendars.find(
|
|
19933
21509
|
(calendar) => calendar.baseDefault
|
|
19934
21510
|
)?.id ?? null,
|
|
@@ -19961,16 +21537,19 @@ var ScheduleCore = class {
|
|
|
19961
21537
|
}
|
|
19962
21538
|
if (result.ok) {
|
|
19963
21539
|
if (historyPolicy === "clear-on-success") {
|
|
19964
|
-
this._saveTracker.
|
|
19965
|
-
this.coreRuntime.state.getAllActivities()
|
|
21540
|
+
this._saveTracker.acknowledge(
|
|
21541
|
+
this.coreRuntime.state.getAllActivities(),
|
|
21542
|
+
this.coreRuntime.state.getAllLinks()
|
|
19966
21543
|
);
|
|
19967
|
-
this._saveTracker.snapshotLinks(this.getAllLinksView());
|
|
19968
21544
|
this._undo.clear();
|
|
19969
21545
|
} else if (action.kind === "activity-lookahead-sync" && action.acknowledgePersistedLookahead === true) {
|
|
19970
21546
|
this._saveTracker.acknowledgePersistedLookahead(
|
|
19971
|
-
|
|
21547
|
+
result.changes.activities,
|
|
19972
21548
|
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
19973
21549
|
);
|
|
21550
|
+
if (action.persistedProgress !== void 0) {
|
|
21551
|
+
this._undo.clear();
|
|
21552
|
+
}
|
|
19974
21553
|
} else {
|
|
19975
21554
|
this._saveTracker.recordChanges(
|
|
19976
21555
|
result.changes.activities,
|
|
@@ -20234,13 +21813,20 @@ var ScheduleCore = class {
|
|
|
20234
21813
|
this.coreRuntime.customIdTracker.beginCustomIdTransaction();
|
|
20235
21814
|
let changes;
|
|
20236
21815
|
try {
|
|
20237
|
-
for (const [activityId, fields] of calculatedFields) {
|
|
21816
|
+
for (const [activityId, fields] of calculatedFields.activities) {
|
|
20238
21817
|
setCriticalPathFieldsIfChanged(
|
|
20239
21818
|
this.coreRuntime.state,
|
|
20240
21819
|
activityId,
|
|
20241
21820
|
fields
|
|
20242
21821
|
);
|
|
20243
21822
|
}
|
|
21823
|
+
for (const [linkId, fields] of calculatedFields.links) {
|
|
21824
|
+
setCriticalPathLinkFieldsIfChanged(
|
|
21825
|
+
this.coreRuntime.state,
|
|
21826
|
+
linkId,
|
|
21827
|
+
fields
|
|
21828
|
+
);
|
|
21829
|
+
}
|
|
20244
21830
|
changes = await assembleChangeSet(this.coreRuntime.state, {
|
|
20245
21831
|
source: { kind: "init" },
|
|
20246
21832
|
beforeSnap: /* @__PURE__ */ new Map(),
|
|
@@ -20496,6 +22082,9 @@ var DEFAULT_HOURS_PER_DAY = 8;
|
|
|
20496
22082
|
|
|
20497
22083
|
// src/dispatch/action-kinds.ts
|
|
20498
22084
|
var DISPATCH_ACTION_KIND = {
|
|
22085
|
+
ACTIVITY_CLIPBOARD_CLEAR: "activity-clipboard-clear",
|
|
22086
|
+
ACTIVITY_COPY: "activity-copy",
|
|
22087
|
+
ACTIVITY_PASTE_COPIED: "activity-paste-copied",
|
|
20499
22088
|
PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge",
|
|
20500
22089
|
BASELINE_APPLY: "baseline-apply",
|
|
20501
22090
|
PONDERATOR_CRITERION_SET: "ponderator-criterion-set",
|
|
@@ -20513,7 +22102,6 @@ var DISPATCH_ACTION_KIND = {
|
|
|
20513
22102
|
ACTIVITY_INDENT: "activity-indent",
|
|
20514
22103
|
ACTIVITY_OUTDENT: "activity-outdent",
|
|
20515
22104
|
ACTIVITY_SET_PROGRESS: "activity-set-progress",
|
|
20516
|
-
ACTIVITY_PASTE: "activity-paste",
|
|
20517
22105
|
SELECTION_TOGGLE: "selection-toggle",
|
|
20518
22106
|
SELECTION_REPLACE: "selection-replace",
|
|
20519
22107
|
VISIBILITY_SET: "visibility-set",
|
|
@@ -20523,6 +22111,9 @@ var DISPATCH_ACTION_KIND = {
|
|
|
20523
22111
|
ACTIVITY_LOOKAHEAD_SYNC: "activity-lookahead-sync"
|
|
20524
22112
|
};
|
|
20525
22113
|
var KIND_CATALOG_COVERS_UNION = {
|
|
22114
|
+
[DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR]: true,
|
|
22115
|
+
[DISPATCH_ACTION_KIND.ACTIVITY_COPY]: true,
|
|
22116
|
+
[DISPATCH_ACTION_KIND.ACTIVITY_PASTE_COPIED]: true,
|
|
20526
22117
|
[DISPATCH_ACTION_KIND.PERSISTENCE_ACKNOWLEDGE]: true,
|
|
20527
22118
|
[DISPATCH_ACTION_KIND.BASELINE_APPLY]: true,
|
|
20528
22119
|
[DISPATCH_ACTION_KIND.PONDERATOR_CRITERION_SET]: true,
|
|
@@ -20540,7 +22131,6 @@ var KIND_CATALOG_COVERS_UNION = {
|
|
|
20540
22131
|
[DISPATCH_ACTION_KIND.ACTIVITY_INDENT]: true,
|
|
20541
22132
|
[DISPATCH_ACTION_KIND.ACTIVITY_OUTDENT]: true,
|
|
20542
22133
|
[DISPATCH_ACTION_KIND.ACTIVITY_SET_PROGRESS]: true,
|
|
20543
|
-
[DISPATCH_ACTION_KIND.ACTIVITY_PASTE]: true,
|
|
20544
22134
|
[DISPATCH_ACTION_KIND.SELECTION_TOGGLE]: true,
|
|
20545
22135
|
[DISPATCH_ACTION_KIND.SELECTION_REPLACE]: true,
|
|
20546
22136
|
[DISPATCH_ACTION_KIND.VISIBILITY_SET]: true,
|