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