@outbuild-company/schedule-core 1.7.0 → 1.9.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 +2131 -1182
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -57
- package/dist/index.d.ts +21 -57
- package/dist/index.js +2131 -1182
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -139,12 +139,11 @@ var REJECTION_REASON = {
|
|
|
139
139
|
ANCHOR_SIBLING_NOT_FOUND: "anchor_sibling_not_found",
|
|
140
140
|
ANCHOR_SIBLING_WRONG_PARENT: "anchor_sibling_wrong_parent",
|
|
141
141
|
PARENT_FROZEN_BY_SIR: "parent_frozen_by_sir",
|
|
142
|
-
SIR_REQUEST_NOT_PENDING: "sir_request_not_pending",
|
|
143
|
-
PREVIEW_UNSUPPORTED_ACTION: "preview_unsupported_action",
|
|
144
142
|
CANNOT_MOVE_INTO_OWN_DESCENDANT: "cannot_move_into_own_descendant",
|
|
145
143
|
INDENT_NO_ELIGIBLE_SIBLING: "indent_no_eligible_sibling",
|
|
146
144
|
OUTDENT_ROOT_LEVEL_NOT_EDITABLE: "outdent_root_level_not_editable",
|
|
147
145
|
HIERARCHY_CYCLE: "hierarchy_cycle",
|
|
146
|
+
SANDBOX_PERSISTENCE_FORBIDDEN: "sandbox_persistence_forbidden",
|
|
148
147
|
INVALID_PROGRESS_VALUE: "invalid_progress_value_must_be_0_or_100"
|
|
149
148
|
};
|
|
150
149
|
|
|
@@ -419,6 +418,107 @@ function getFieldDescriptor(field) {
|
|
|
419
418
|
return FIELD_REGISTRY[field] ?? null;
|
|
420
419
|
}
|
|
421
420
|
|
|
421
|
+
// src/internal/state/view-state.ts
|
|
422
|
+
function isRelativePlacementPinValid(activityId, pin, parentOf) {
|
|
423
|
+
const branchParentId = String(pin.branchParentId);
|
|
424
|
+
const currentParentId = String(parentOf(activityId) ?? "0");
|
|
425
|
+
if (currentParentId !== branchParentId) return false;
|
|
426
|
+
const referenceId = String(pin.referenceId);
|
|
427
|
+
return referenceId === branchParentId || String(parentOf(referenceId) ?? "0") === branchParentId;
|
|
428
|
+
}
|
|
429
|
+
var ViewStateStore = class {
|
|
430
|
+
checkedSet = /* @__PURE__ */ new Set();
|
|
431
|
+
hiddenSet = /* @__PURE__ */ new Set();
|
|
432
|
+
activeFilter = null;
|
|
433
|
+
activeOrder = null;
|
|
434
|
+
relativePlacementPins = /* @__PURE__ */ new Map();
|
|
435
|
+
isChecked(activityId) {
|
|
436
|
+
return this.checkedSet.has(activityId);
|
|
437
|
+
}
|
|
438
|
+
isVisible(activityId) {
|
|
439
|
+
return !this.hiddenSet.has(activityId);
|
|
440
|
+
}
|
|
441
|
+
hiddenIds() {
|
|
442
|
+
return [...this.hiddenSet];
|
|
443
|
+
}
|
|
444
|
+
getActiveFilter() {
|
|
445
|
+
return this.activeFilter;
|
|
446
|
+
}
|
|
447
|
+
setActiveFilter(nextFilter) {
|
|
448
|
+
this.activeFilter = nextFilter;
|
|
449
|
+
}
|
|
450
|
+
getActiveOrder() {
|
|
451
|
+
return this.activeOrder;
|
|
452
|
+
}
|
|
453
|
+
setActiveOrder(nextOrder) {
|
|
454
|
+
this.activeOrder = nextOrder;
|
|
455
|
+
}
|
|
456
|
+
setRelativePlacementPin(activityId, pin) {
|
|
457
|
+
this.relativePlacementPins.set(String(activityId), {
|
|
458
|
+
referenceId: String(pin.referenceId),
|
|
459
|
+
branchParentId: String(pin.branchParentId),
|
|
460
|
+
side: pin.side
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
getRelativePlacementPins() {
|
|
464
|
+
return this.relativePlacementPins;
|
|
465
|
+
}
|
|
466
|
+
restoreRelativePlacementPins(pins) {
|
|
467
|
+
this.relativePlacementPins = new Map(pins);
|
|
468
|
+
}
|
|
469
|
+
checkedIds() {
|
|
470
|
+
return [...this.checkedSet];
|
|
471
|
+
}
|
|
472
|
+
checkedCount() {
|
|
473
|
+
return this.checkedSet.size;
|
|
474
|
+
}
|
|
475
|
+
setChecked(activityId, nextChecked) {
|
|
476
|
+
if (nextChecked === this.checkedSet.has(activityId)) return false;
|
|
477
|
+
if (nextChecked) this.checkedSet.add(activityId);
|
|
478
|
+
else this.checkedSet.delete(activityId);
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
setVisible(activityId, nextVisible) {
|
|
482
|
+
if (nextVisible === !this.hiddenSet.has(activityId)) return false;
|
|
483
|
+
if (nextVisible) this.hiddenSet.delete(activityId);
|
|
484
|
+
else this.hiddenSet.add(activityId);
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
forget(activityId) {
|
|
488
|
+
this.checkedSet.delete(activityId);
|
|
489
|
+
this.hiddenSet.delete(activityId);
|
|
490
|
+
this.relativePlacementPins.delete(activityId);
|
|
491
|
+
for (const [pinnedId, pin] of this.relativePlacementPins) {
|
|
492
|
+
if (pin.referenceId === activityId) {
|
|
493
|
+
this.relativePlacementPins.delete(pinnedId);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
hydrate(seed2) {
|
|
498
|
+
this.checkedSet = new Set(seed2.checkedIds ?? []);
|
|
499
|
+
this.hiddenSet = new Set(seed2.hiddenIds ?? []);
|
|
500
|
+
this.activeFilter = null;
|
|
501
|
+
this.activeOrder = null;
|
|
502
|
+
this.relativePlacementPins = /* @__PURE__ */ new Map();
|
|
503
|
+
}
|
|
504
|
+
snapshot() {
|
|
505
|
+
return {
|
|
506
|
+
checked: new Set(this.checkedSet),
|
|
507
|
+
hidden: new Set(this.hiddenSet),
|
|
508
|
+
activeFilter: this.activeFilter,
|
|
509
|
+
activeOrder: this.activeOrder,
|
|
510
|
+
relativePlacementPins: new Map(this.relativePlacementPins)
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
restore(snapshot) {
|
|
514
|
+
this.checkedSet = new Set(snapshot.checked);
|
|
515
|
+
this.hiddenSet = new Set(snapshot.hidden);
|
|
516
|
+
this.activeFilter = snapshot.activeFilter;
|
|
517
|
+
this.activeOrder = snapshot.activeOrder;
|
|
518
|
+
this.relativePlacementPins = new Map(snapshot.relativePlacementPins);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
|
|
422
522
|
// src/internal/filter/evaluate-filter.ts
|
|
423
523
|
function toTargetDate(value) {
|
|
424
524
|
if (value instanceof Date) return value;
|
|
@@ -561,13 +661,8 @@ function seedFilterProjection(input) {
|
|
|
561
661
|
);
|
|
562
662
|
}
|
|
563
663
|
const isMatch = matchesFilter(activity, filter, context);
|
|
564
|
-
|
|
565
|
-
visibleReferenceCounts.set(
|
|
566
|
-
activityId,
|
|
567
|
-
(isMatch ? 1 : 0) + (isPinned ? 1 : 0)
|
|
568
|
-
);
|
|
664
|
+
visibleReferenceCounts.set(activityId, isMatch ? 1 : 0);
|
|
569
665
|
if (isMatch) matchedIds.add(activityId);
|
|
570
|
-
if (isPinned) pinnedVisibleIds.add(activityId);
|
|
571
666
|
}
|
|
572
667
|
return {
|
|
573
668
|
matchedIds,
|
|
@@ -577,6 +672,29 @@ function seedFilterProjection(input) {
|
|
|
577
672
|
remainingChildren
|
|
578
673
|
};
|
|
579
674
|
}
|
|
675
|
+
function applyRelativePlacementPins(seed2, pins) {
|
|
676
|
+
if (pins === void 0) return;
|
|
677
|
+
for (const [activityId, pin] of pins) {
|
|
678
|
+
const pinnedId = String(activityId);
|
|
679
|
+
const referenceId = String(pin.referenceId);
|
|
680
|
+
if (!seed2.parentIds.has(pinnedId) || !seed2.parentIds.has(referenceId) || !isRelativePlacementPinValid(
|
|
681
|
+
pinnedId,
|
|
682
|
+
pin,
|
|
683
|
+
(activityId2) => seed2.parentIds.get(activityId2) ?? null
|
|
684
|
+
) || (seed2.visibleReferenceCounts.get(referenceId) ?? 0) === 0) {
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
seed2.pinnedVisibleIds.add(pinnedId);
|
|
688
|
+
let currentId = pinnedId;
|
|
689
|
+
while (currentId !== null) {
|
|
690
|
+
seed2.visibleReferenceCounts.set(
|
|
691
|
+
currentId,
|
|
692
|
+
(seed2.visibleReferenceCounts.get(currentId) ?? 0) + 1
|
|
693
|
+
);
|
|
694
|
+
currentId = seed2.parentIds.get(currentId) ?? null;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
580
698
|
function propagateVisibleReferenceCounts(seed2) {
|
|
581
699
|
const { visibleReferenceCounts, parentIds, remainingChildren } = seed2;
|
|
582
700
|
const pendingIds = [];
|
|
@@ -608,6 +726,7 @@ function collectVisibleIds(visibleReferenceCounts) {
|
|
|
608
726
|
function buildFilterProjection(input) {
|
|
609
727
|
const seed2 = seedFilterProjection(input);
|
|
610
728
|
propagateVisibleReferenceCounts(seed2);
|
|
729
|
+
applyRelativePlacementPins(seed2, input.relativePlacementPins);
|
|
611
730
|
const visibleIds = collectVisibleIds(seed2.visibleReferenceCounts);
|
|
612
731
|
return {
|
|
613
732
|
filter: input.filter,
|
|
@@ -795,7 +914,8 @@ function resolveFilter(adapter, filter, hoursPerDay) {
|
|
|
795
914
|
activities,
|
|
796
915
|
parentOf: (activityId) => adapter.getParentId(activityId),
|
|
797
916
|
filter,
|
|
798
|
-
context: buildFilterContext(hoursPerDay)
|
|
917
|
+
context: buildFilterContext(hoursPerDay),
|
|
918
|
+
relativePlacementPins: adapter.getRelativePlacementPins()
|
|
799
919
|
});
|
|
800
920
|
return { visibleIds: projection.visibleIds, projection };
|
|
801
921
|
}
|
|
@@ -846,25 +966,118 @@ function normalizeParentKey(parent) {
|
|
|
846
966
|
|
|
847
967
|
// src/internal/hierarchy/visual-order.ts
|
|
848
968
|
var NOT_SET_SORT_VALUE = Number.MAX_SAFE_INTEGER;
|
|
849
|
-
|
|
969
|
+
var EMPTY_RELATIVE_PLACEMENT_PINS = /* @__PURE__ */ new Map();
|
|
970
|
+
function buildRelativePlacementPinIndex(adapter) {
|
|
971
|
+
const index = /* @__PURE__ */ new Map();
|
|
972
|
+
for (const [activityId, pin] of adapter.getRelativePlacementPins?.() ?? []) {
|
|
973
|
+
const pinnedId = String(activityId);
|
|
974
|
+
const referenceId = String(pin.referenceId);
|
|
975
|
+
if (adapter.getActivity(pinnedId) === null || adapter.getActivity(referenceId) === null || !isRelativePlacementPinValid(
|
|
976
|
+
pinnedId,
|
|
977
|
+
pin,
|
|
978
|
+
(activityId2) => adapter.getParentId(activityId2)
|
|
979
|
+
)) {
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
const targetParentId = String(pin.branchParentId);
|
|
983
|
+
const branchPins = index.get(targetParentId) ?? /* @__PURE__ */ new Map();
|
|
984
|
+
branchPins.set(pinnedId, pin);
|
|
985
|
+
index.set(targetParentId, branchPins);
|
|
986
|
+
}
|
|
987
|
+
return index;
|
|
988
|
+
}
|
|
989
|
+
function getChildrenInVisualOrder(parentId, adapter, pinIndex = buildRelativePlacementPinIndex(adapter)) {
|
|
850
990
|
const children = collectChildrenSnapshots(parentId, adapter);
|
|
851
991
|
const userOrder = adapter.getOrderComparator?.() ?? null;
|
|
852
992
|
children.sort(
|
|
853
993
|
userOrder === null ? compareActivitiesInVisualOrder : (a, b) => userOrder(a, b) || compareActivitiesInVisualOrder(a, b)
|
|
854
994
|
);
|
|
855
|
-
return
|
|
995
|
+
return applyRelativePlacementPins2(
|
|
996
|
+
children.map((activity) => String(activity.id)),
|
|
997
|
+
parentId,
|
|
998
|
+
pinIndex.get(String(parentId)) ?? EMPTY_RELATIVE_PLACEMENT_PINS
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
function applyRelativePlacementPins2(orderedIds, parentId, pins) {
|
|
1002
|
+
const order = buildLinkedActivityOrder(orderedIds);
|
|
1003
|
+
for (const [activityId, pin] of pins) {
|
|
1004
|
+
const pinnedId = String(activityId);
|
|
1005
|
+
const referenceId = String(pin.referenceId);
|
|
1006
|
+
const isFirstChild = referenceId === String(parentId);
|
|
1007
|
+
if (!order.previous.has(pinnedId) || !isFirstChild && (!order.previous.has(referenceId) || pinnedId === referenceId)) {
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
detachActivity(order, pinnedId);
|
|
1011
|
+
if (isFirstChild) insertActivityFirst(order, pinnedId);
|
|
1012
|
+
else insertActivityBeside(order, pinnedId, referenceId, pin.side);
|
|
1013
|
+
}
|
|
1014
|
+
return materializeActivityOrder(order);
|
|
1015
|
+
}
|
|
1016
|
+
function buildLinkedActivityOrder(orderedIds) {
|
|
1017
|
+
const previous = /* @__PURE__ */ new Map();
|
|
1018
|
+
const next = /* @__PURE__ */ new Map();
|
|
1019
|
+
for (let index = 0; index < orderedIds.length; index++) {
|
|
1020
|
+
const activityId = orderedIds[index];
|
|
1021
|
+
if (activityId === void 0) continue;
|
|
1022
|
+
previous.set(activityId, orderedIds[index - 1] ?? null);
|
|
1023
|
+
next.set(activityId, orderedIds[index + 1] ?? null);
|
|
1024
|
+
}
|
|
1025
|
+
return { previous, next, head: orderedIds[0] ?? null };
|
|
1026
|
+
}
|
|
1027
|
+
function detachActivity(order, activityId) {
|
|
1028
|
+
const before = order.previous.get(activityId) ?? null;
|
|
1029
|
+
const after = order.next.get(activityId) ?? null;
|
|
1030
|
+
if (before === null) order.head = after;
|
|
1031
|
+
else order.next.set(before, after);
|
|
1032
|
+
if (after !== null) order.previous.set(after, before);
|
|
1033
|
+
}
|
|
1034
|
+
function insertActivityFirst(order, activityId) {
|
|
1035
|
+
order.previous.set(activityId, null);
|
|
1036
|
+
order.next.set(activityId, order.head);
|
|
1037
|
+
if (order.head !== null) order.previous.set(order.head, activityId);
|
|
1038
|
+
order.head = activityId;
|
|
1039
|
+
}
|
|
1040
|
+
function insertActivityBeside(order, activityId, referenceId, side) {
|
|
1041
|
+
if (side === "before") {
|
|
1042
|
+
const before = order.previous.get(referenceId) ?? null;
|
|
1043
|
+
order.previous.set(activityId, before);
|
|
1044
|
+
order.next.set(activityId, referenceId);
|
|
1045
|
+
order.previous.set(referenceId, activityId);
|
|
1046
|
+
if (before === null) order.head = activityId;
|
|
1047
|
+
else order.next.set(before, activityId);
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
const after = order.next.get(referenceId) ?? null;
|
|
1051
|
+
order.previous.set(activityId, referenceId);
|
|
1052
|
+
order.next.set(activityId, after);
|
|
1053
|
+
order.next.set(referenceId, activityId);
|
|
1054
|
+
if (after !== null) order.previous.set(after, activityId);
|
|
1055
|
+
}
|
|
1056
|
+
function materializeActivityOrder(order) {
|
|
1057
|
+
const result = [];
|
|
1058
|
+
let activityId = order.head;
|
|
1059
|
+
while (activityId !== null) {
|
|
1060
|
+
result.push(activityId);
|
|
1061
|
+
activityId = order.next.get(activityId) ?? null;
|
|
1062
|
+
}
|
|
1063
|
+
return result;
|
|
856
1064
|
}
|
|
857
1065
|
function iterateInVisualOrder(adapter) {
|
|
858
1066
|
const result = [];
|
|
1067
|
+
const pinIndex = buildRelativePlacementPinIndex(adapter);
|
|
859
1068
|
const visit = (activity) => {
|
|
860
1069
|
result.push(activity);
|
|
861
|
-
const childIds = getChildrenInVisualOrder(
|
|
1070
|
+
const childIds = getChildrenInVisualOrder(
|
|
1071
|
+
String(activity.id),
|
|
1072
|
+
adapter,
|
|
1073
|
+
pinIndex
|
|
1074
|
+
);
|
|
862
1075
|
for (const childId of childIds) {
|
|
863
1076
|
const child = adapter.getActivity(childId);
|
|
864
1077
|
if (child) visit(child);
|
|
865
1078
|
}
|
|
866
1079
|
};
|
|
867
|
-
const rootIds = getChildrenInVisualOrder(ROOT_PARENT_ID, adapter);
|
|
1080
|
+
const rootIds = getChildrenInVisualOrder(ROOT_PARENT_ID, adapter, pinIndex);
|
|
868
1081
|
for (const rootId of rootIds) {
|
|
869
1082
|
const root = adapter.getActivity(rootId);
|
|
870
1083
|
if (root) visit(root);
|
|
@@ -925,8 +1138,9 @@ function setHas(set, id) {
|
|
|
925
1138
|
// src/dispatch/shared/collect-branch-order.ts
|
|
926
1139
|
function collectBranchOrder(adapter) {
|
|
927
1140
|
const branches = [];
|
|
1141
|
+
const pinIndex = buildRelativePlacementPinIndex(adapter);
|
|
928
1142
|
const visit = (parentId) => {
|
|
929
|
-
const childIds = getChildrenInVisualOrder(parentId, adapter);
|
|
1143
|
+
const childIds = getChildrenInVisualOrder(parentId, adapter, pinIndex);
|
|
930
1144
|
if (childIds.length > 1) {
|
|
931
1145
|
branches.push({ parentId, childIds });
|
|
932
1146
|
}
|
|
@@ -939,8 +1153,9 @@ function collectBranchOrder(adapter) {
|
|
|
939
1153
|
}
|
|
940
1154
|
function collectBranchOrderForParents(adapter, parentIds) {
|
|
941
1155
|
const branches = [];
|
|
1156
|
+
const pinIndex = buildRelativePlacementPinIndex(adapter);
|
|
942
1157
|
for (const parentId of parentIds) {
|
|
943
|
-
const childIds = getChildrenInVisualOrder(parentId, adapter);
|
|
1158
|
+
const childIds = getChildrenInVisualOrder(parentId, adapter, pinIndex);
|
|
944
1159
|
if (childIds.length > 1) branches.push({ parentId, childIds });
|
|
945
1160
|
}
|
|
946
1161
|
return branches;
|
|
@@ -1791,6 +2006,8 @@ function buildVirtualLink(originalLink, sourceId, targetId, counter, sourceLag,
|
|
|
1791
2006
|
target: targetId,
|
|
1792
2007
|
type: originalLink.type,
|
|
1793
2008
|
lag: originalLink.lag,
|
|
2009
|
+
freeSlackHours: null,
|
|
2010
|
+
isCritical: false,
|
|
1794
2011
|
_sourceLag: sourceLag,
|
|
1795
2012
|
_targetLag: targetLag,
|
|
1796
2013
|
_trueLag: trueLag,
|
|
@@ -2868,7 +3085,9 @@ function applyLinkCreates(links, deps) {
|
|
|
2868
3085
|
source: operation.source,
|
|
2869
3086
|
target: operation.target,
|
|
2870
3087
|
type: operation.type,
|
|
2871
|
-
lag: operation.lag
|
|
3088
|
+
lag: operation.lag,
|
|
3089
|
+
freeSlackHours: null,
|
|
3090
|
+
isCritical: false
|
|
2872
3091
|
}))
|
|
2873
3092
|
];
|
|
2874
3093
|
const effectiveVerdict = validateTopologyCommit({
|
|
@@ -2887,6 +3106,8 @@ function applyLinkCreates(links, deps) {
|
|
|
2887
3106
|
target: operation.target,
|
|
2888
3107
|
type: operation.type,
|
|
2889
3108
|
lag: operation.lag,
|
|
3109
|
+
freeSlackHours: null,
|
|
3110
|
+
isCritical: false,
|
|
2890
3111
|
...preserved?.ganttId === void 0 ? {} : { ganttId: preserved.ganttId },
|
|
2891
3112
|
...preserved?.sectorId === void 0 ? {} : { sectorId: preserved.sectorId },
|
|
2892
3113
|
...preserved?.proplannerId === void 0 ? {} : { proplannerId: preserved.proplannerId }
|
|
@@ -2929,7 +3150,9 @@ function applyCreate(op, deps) {
|
|
|
2929
3150
|
source: op.source,
|
|
2930
3151
|
target: op.target,
|
|
2931
3152
|
type: op.type,
|
|
2932
|
-
lag: op.lag
|
|
3153
|
+
lag: op.lag,
|
|
3154
|
+
freeSlackHours: null,
|
|
3155
|
+
isCritical: false
|
|
2933
3156
|
};
|
|
2934
3157
|
const effectiveVerdict = validateTopologyCommit({
|
|
2935
3158
|
port,
|
|
@@ -3443,19 +3666,44 @@ async function recomputeParentsCooperatively(parents, adapter, progressMode, opt
|
|
|
3443
3666
|
}
|
|
3444
3667
|
function findAncestorsOfDirty(adapter, dirtyIds) {
|
|
3445
3668
|
const ancestors = /* @__PURE__ */ new Set();
|
|
3446
|
-
|
|
3669
|
+
const parentById = /* @__PURE__ */ new Map();
|
|
3670
|
+
for (const id of dirtyIds) {
|
|
3671
|
+
walkAncestors(id, adapter, ancestors, parentById);
|
|
3672
|
+
}
|
|
3447
3673
|
if (ancestors.size === 0) return [];
|
|
3448
3674
|
const ids = Array.from(ancestors);
|
|
3449
|
-
const depths =
|
|
3675
|
+
const depths = computeKnownDepths(ids, parentById);
|
|
3450
3676
|
return sortDeepestFirst(ids, depths);
|
|
3451
3677
|
}
|
|
3452
|
-
function walkAncestors(id, adapter, collector) {
|
|
3678
|
+
function walkAncestors(id, adapter, collector, parentById) {
|
|
3453
3679
|
let current = adapter.getParentId(id);
|
|
3454
3680
|
while (current && !isRootParent(current)) {
|
|
3455
3681
|
if (collector.has(current)) return;
|
|
3456
3682
|
collector.add(current);
|
|
3457
|
-
|
|
3683
|
+
const parent = adapter.getParentId(current);
|
|
3684
|
+
parentById.set(current, parent);
|
|
3685
|
+
current = parent;
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
function computeKnownDepths(ids, parentById) {
|
|
3689
|
+
const depths = /* @__PURE__ */ new Map();
|
|
3690
|
+
for (const id of ids) {
|
|
3691
|
+
if (depths.has(id)) continue;
|
|
3692
|
+
const path = [];
|
|
3693
|
+
const inPath = /* @__PURE__ */ new Set();
|
|
3694
|
+
let current = id;
|
|
3695
|
+
while (current && !isRootParent(current) && !depths.has(current) && !inPath.has(current)) {
|
|
3696
|
+
path.push(current);
|
|
3697
|
+
inPath.add(current);
|
|
3698
|
+
current = parentById.get(current) ?? null;
|
|
3699
|
+
}
|
|
3700
|
+
let depth = current ? depths.get(current) ?? 0 : 0;
|
|
3701
|
+
for (let index = path.length - 1; index >= 0; index -= 1) {
|
|
3702
|
+
depth += 1;
|
|
3703
|
+
depths.set(path[index], depth);
|
|
3704
|
+
}
|
|
3458
3705
|
}
|
|
3706
|
+
return depths;
|
|
3459
3707
|
}
|
|
3460
3708
|
function aggregateChildren(childrenIds, adapter, includeRealWork) {
|
|
3461
3709
|
let minStart = null;
|
|
@@ -3679,19 +3927,6 @@ function durationToHours(durationDays, hoursPerDay) {
|
|
|
3679
3927
|
return Math.round(Math.ceil(durationDays * hoursPerDay * 60) / 60);
|
|
3680
3928
|
}
|
|
3681
3929
|
|
|
3682
|
-
// src/columns/shared/error-codes.ts
|
|
3683
|
-
var PARSE_ERROR = {
|
|
3684
|
-
NOT_A_NUMBER: "not_a_number",
|
|
3685
|
-
// ISSUE-057 (CP6): la unidad de una duración se declara, no se adivina.
|
|
3686
|
-
MISSING_INPUT_UNIT: "missing_input_unit",
|
|
3687
|
-
// D-OWNER-5: una duración en días es entera; una fracción se rechaza sin
|
|
3688
|
-
// redondear (el redondeo silencioso era la puerta del bug de las 320 horas).
|
|
3689
|
-
FRACTIONAL_DAYS: "fractional_days"
|
|
3690
|
-
};
|
|
3691
|
-
var VALIDATION_REASON = {
|
|
3692
|
-
UNCHANGED: "unchanged",
|
|
3693
|
-
NEGATIVE_DURATION: "negative_duration"};
|
|
3694
|
-
|
|
3695
3930
|
// src/columns/end-date/setter.ts
|
|
3696
3931
|
var setEndDate = (value) => ({
|
|
3697
3932
|
endDate: value
|
|
@@ -3793,6 +4028,10 @@ var POST_PROCESSOR = {
|
|
|
3793
4028
|
RECORD_LAST_START_DATE: "recordLastStartDate"
|
|
3794
4029
|
};
|
|
3795
4030
|
|
|
4031
|
+
// src/columns/shared/error-codes.ts
|
|
4032
|
+
var VALIDATION_REASON = {
|
|
4033
|
+
NEGATIVE_DURATION: "negative_duration"};
|
|
4034
|
+
|
|
3796
4035
|
// src/columns/shared/constraint-validation.ts
|
|
3797
4036
|
function checkConstraintViolation2(constraintType, constraintDate, projectedDate) {
|
|
3798
4037
|
return checkConstraintViolation(constraintType, constraintDate, projectedDate);
|
|
@@ -3817,18 +4056,7 @@ var durationPipeline = {
|
|
|
3817
4056
|
},
|
|
3818
4057
|
parseInput(rawValue, _activity, ctx) {
|
|
3819
4058
|
const numericValue = parseFloat(String(rawValue));
|
|
3820
|
-
if (!Number.isFinite(numericValue))
|
|
3821
|
-
return parseError(PARSE_ERROR.NOT_A_NUMBER);
|
|
3822
|
-
}
|
|
3823
|
-
if (ctx.inputUnit === void 0) {
|
|
3824
|
-
return parseError(PARSE_ERROR.MISSING_INPUT_UNIT);
|
|
3825
|
-
}
|
|
3826
|
-
if (ctx.inputUnit === "hours") {
|
|
3827
|
-
return parsed(numericValue);
|
|
3828
|
-
}
|
|
3829
|
-
if (!Number.isInteger(numericValue)) {
|
|
3830
|
-
return parseError(PARSE_ERROR.FRACTIONAL_DAYS);
|
|
3831
|
-
}
|
|
4059
|
+
if (!Number.isFinite(numericValue)) return parseError("not_a_number");
|
|
3832
4060
|
return parsed(durationToHours(numericValue, ctx.hoursPerDay));
|
|
3833
4061
|
},
|
|
3834
4062
|
validate(_activity, oldValue, newValue) {
|
|
@@ -4072,8 +4300,8 @@ var costPipeline = {
|
|
|
4072
4300
|
}
|
|
4073
4301
|
return canEdit();
|
|
4074
4302
|
},
|
|
4075
|
-
parseInput(rawValue
|
|
4076
|
-
if (String(rawValue).trim() === "") return parsed(
|
|
4303
|
+
parseInput(rawValue) {
|
|
4304
|
+
if (String(rawValue).trim() === "") return parsed(0);
|
|
4077
4305
|
const numericValue = parseFloat(String(rawValue));
|
|
4078
4306
|
if (Number.isNaN(numericValue)) return parseError("not_a_number");
|
|
4079
4307
|
return parsed(numericValue);
|
|
@@ -4122,8 +4350,8 @@ var usedCostPipeline = {
|
|
|
4122
4350
|
}
|
|
4123
4351
|
return canEdit();
|
|
4124
4352
|
},
|
|
4125
|
-
parseInput(rawValue
|
|
4126
|
-
if (String(rawValue).trim() === "") return parsed(
|
|
4353
|
+
parseInput(rawValue) {
|
|
4354
|
+
if (String(rawValue).trim() === "") return parsed(0);
|
|
4127
4355
|
const numericValue = parseFloat(String(rawValue));
|
|
4128
4356
|
if (Number.isNaN(numericValue)) return parseError("not_a_number");
|
|
4129
4357
|
return parsed(numericValue);
|
|
@@ -4864,7 +5092,9 @@ var ACTIVITY_PROPERTY = {
|
|
|
4864
5092
|
CALENDAR_DURATION: "calendarDuration"};
|
|
4865
5093
|
var LINK_PROPERTY = {
|
|
4866
5094
|
TYPE: "type",
|
|
4867
|
-
LAG: "lag"
|
|
5095
|
+
LAG: "lag",
|
|
5096
|
+
FREE_SLACK_HOURS: "freeSlackHours",
|
|
5097
|
+
IS_CRITICAL: "isCritical"};
|
|
4868
5098
|
|
|
4869
5099
|
// src/internal/state/dynamic-writes.ts
|
|
4870
5100
|
function setActivityFieldDynamic(state, activityId, field, value) {
|
|
@@ -5720,10 +5950,11 @@ function mutateTheDateToFutureOrPastInBaseRestriction(dateBaseToCalculate, restr
|
|
|
5720
5950
|
|
|
5721
5951
|
// src/critical-path/legacy/base/parents-calculations/forward.js
|
|
5722
5952
|
var CalculateForwardParentsWithLinks = class {
|
|
5723
|
-
constructor(linkedActivitiesData, activity, gantt) {
|
|
5953
|
+
constructor(linkedActivitiesData, activity, gantt, linkCandidates) {
|
|
5724
5954
|
this.linkedActivitiesData = linkedActivitiesData;
|
|
5725
5955
|
this.activity = activity;
|
|
5726
5956
|
this.gantt = gantt;
|
|
5957
|
+
this.linkCandidates = linkCandidates;
|
|
5727
5958
|
this.activityCalendar = this.gantt.getCalendar(this.activity.calendar_id);
|
|
5728
5959
|
if (!this.activityCalendar) {
|
|
5729
5960
|
throw new Error(
|
|
@@ -5768,6 +5999,10 @@ var CalculateForwardParentsWithLinks = class {
|
|
|
5768
5999
|
continue;
|
|
5769
6000
|
}
|
|
5770
6001
|
if (calculation) {
|
|
6002
|
+
this.linkCandidates?.set(linkedActivity.linkId, {
|
|
6003
|
+
targetId: String(this.activity.id),
|
|
6004
|
+
...calculation
|
|
6005
|
+
});
|
|
5771
6006
|
calculations.push(calculation);
|
|
5772
6007
|
}
|
|
5773
6008
|
}
|
|
@@ -6215,7 +6450,8 @@ function parentWithLinks(params) {
|
|
|
6215
6450
|
linkDirection,
|
|
6216
6451
|
calculations,
|
|
6217
6452
|
gantt,
|
|
6218
|
-
direction
|
|
6453
|
+
direction,
|
|
6454
|
+
linkCandidates
|
|
6219
6455
|
} = params;
|
|
6220
6456
|
const linksArray = parent[linkProperty];
|
|
6221
6457
|
const parentData = extractParentData(parent);
|
|
@@ -6230,7 +6466,8 @@ function parentWithLinks(params) {
|
|
|
6230
6466
|
calculation = new forward_default(
|
|
6231
6467
|
linksWithActivityData,
|
|
6232
6468
|
parent,
|
|
6233
|
-
gantt
|
|
6469
|
+
gantt,
|
|
6470
|
+
linkCandidates
|
|
6234
6471
|
);
|
|
6235
6472
|
calculation = calculation.calculate();
|
|
6236
6473
|
} else {
|
|
@@ -6275,6 +6512,7 @@ function getLinksWithActivityDataForward(linksArray, linkDirection, calculations
|
|
|
6275
6512
|
return null;
|
|
6276
6513
|
}
|
|
6277
6514
|
return {
|
|
6515
|
+
linkId: String(linkData.id),
|
|
6278
6516
|
es: calculationsOfLinkedActivity.es,
|
|
6279
6517
|
ef: calculationsOfLinkedActivity.ef,
|
|
6280
6518
|
linkLag: linkData.lag,
|
|
@@ -6375,6 +6613,7 @@ var CalculationOfParent = class {
|
|
|
6375
6613
|
this.calculationOfParentsFromLinks = params.calculationOfParentsFromLinks;
|
|
6376
6614
|
this.singleParents = params.singleParents;
|
|
6377
6615
|
this.parentCalculationFromChildren = params.parentCalculationFromChildren;
|
|
6616
|
+
this.linkCandidates = params.linkCandidates;
|
|
6378
6617
|
this.gantt = params.gantt;
|
|
6379
6618
|
}
|
|
6380
6619
|
handleParentWithLinks() {
|
|
@@ -6422,7 +6661,8 @@ var CalculationOfParent = class {
|
|
|
6422
6661
|
linkDirection: this.linkDirection,
|
|
6423
6662
|
gantt: this.gantt,
|
|
6424
6663
|
calculations: this.calculations,
|
|
6425
|
-
direction: this.direction
|
|
6664
|
+
direction: this.direction,
|
|
6665
|
+
linkCandidates: this.linkCandidates
|
|
6426
6666
|
});
|
|
6427
6667
|
this.calculationsOfParentThatImpactChildrens.set(activity.id, calculation);
|
|
6428
6668
|
this.calculationOfParentsFromLinks.add(activity.id);
|
|
@@ -7071,6 +7311,7 @@ var CriticalPathHelpers = class {
|
|
|
7071
7311
|
calculationOfParentsFromLinks: this.calculationOfParentsFromLinks,
|
|
7072
7312
|
singleParents: this.singleParents,
|
|
7073
7313
|
parentCalculationFromChildren: this.parentCalculationFromChildren,
|
|
7314
|
+
linkCandidates: this.linkCandidates,
|
|
7074
7315
|
gantt: this.gantt
|
|
7075
7316
|
});
|
|
7076
7317
|
if (this.pendingParentsWithLinks.has(activity.id)) {
|
|
@@ -7138,6 +7379,7 @@ var LinksRulesCalculationsMethods = class {
|
|
|
7138
7379
|
this.parentsWithLinks = calculationObject.parentsWithLinks;
|
|
7139
7380
|
this.parentsCalculations = calculationObject.parentsCalculations;
|
|
7140
7381
|
this.gantt = calculationObject.ganttInstance;
|
|
7382
|
+
this.linkCandidates = calculationObject.linkCandidates;
|
|
7141
7383
|
}
|
|
7142
7384
|
getEsAndEfFromPreviousCalculatedActivities(activity) {
|
|
7143
7385
|
const isAlap = this.alapMap.has(activity);
|
|
@@ -7309,7 +7551,12 @@ var LinksConstraintCalculator = class extends LinksRulesCalculationsMethods_defa
|
|
|
7309
7551
|
if (!linkData) {
|
|
7310
7552
|
throw new Error("Link data not found", { cause: { linkId: link } });
|
|
7311
7553
|
}
|
|
7312
|
-
|
|
7554
|
+
const calculation = this.calculateLink(linkData);
|
|
7555
|
+
this.linkCandidates?.set(String(linkData.id), {
|
|
7556
|
+
targetId: String(this.referenceActivity.id),
|
|
7557
|
+
...calculation
|
|
7558
|
+
});
|
|
7559
|
+
return calculation;
|
|
7313
7560
|
});
|
|
7314
7561
|
const parent = Number(this.referenceActivity.parent);
|
|
7315
7562
|
if (this.parentsWithLinks.has(parent)) {
|
|
@@ -7575,6 +7822,7 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7575
7822
|
this.totalFloatSnltFnlt = /* @__PURE__ */ new Map();
|
|
7576
7823
|
this.totalFloatForMsoMfo = /* @__PURE__ */ new Map();
|
|
7577
7824
|
this.totalFloatForAsap = /* @__PURE__ */ new Map();
|
|
7825
|
+
this.linkCandidates = /* @__PURE__ */ new Map();
|
|
7578
7826
|
this.calculationOfParentsFromLinks = /* @__PURE__ */ new Set();
|
|
7579
7827
|
this.parentCalculationFromChildren = /* @__PURE__ */ new Set();
|
|
7580
7828
|
this.forbiddenActivitiesLinkedToParentsWithLinks = /* @__PURE__ */ new Set();
|
|
@@ -7673,7 +7921,8 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7673
7921
|
constraint: constraintType,
|
|
7674
7922
|
parentsWithLinks: this.pendingParentsWithLinks,
|
|
7675
7923
|
parentsCalculations: this.calculationsOfParentThatImpactChildrens,
|
|
7676
|
-
ganttInstance: this.gantt
|
|
7924
|
+
ganttInstance: this.gantt,
|
|
7925
|
+
linkCandidates: this.linkCandidates
|
|
7677
7926
|
}).calculate();
|
|
7678
7927
|
if (constraintType === CONSTRAINT_TYPES.ASAP) {
|
|
7679
7928
|
this.totalFloatForAsap.set(activity, {
|
|
@@ -7691,7 +7940,8 @@ var ForwardPath = class extends generic_calculations_default {
|
|
|
7691
7940
|
constraint: constraintType,
|
|
7692
7941
|
parentsWithLinks: this.pendingParentsWithLinks,
|
|
7693
7942
|
parentsCalculations: this.calculationsOfParentThatImpactChildrens,
|
|
7694
|
-
ganttInstance: this.gantt
|
|
7943
|
+
ganttInstance: this.gantt,
|
|
7944
|
+
linkCandidates: this.linkCandidates
|
|
7695
7945
|
}).calculate());
|
|
7696
7946
|
}
|
|
7697
7947
|
if (constraintType === CONSTRAINT_TYPES.ALAP) {
|
|
@@ -8728,7 +8978,6 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8728
8978
|
}
|
|
8729
8979
|
}
|
|
8730
8980
|
if ([CONSTRAINT_TYPES.SNLT].includes(activity.constraint_type)) {
|
|
8731
|
-
const constraintDate = activity.constraint_date;
|
|
8732
8981
|
const parentLinkCalcualation2 = this.calculationsOfParentThatImpactChildrens.get(parent);
|
|
8733
8982
|
const lsToComparison = addDurationToDate(
|
|
8734
8983
|
this.gantt.getCalendar(activity.calendar_id),
|
|
@@ -8736,6 +8985,7 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8736
8985
|
-activity.duration,
|
|
8737
8986
|
activity
|
|
8738
8987
|
);
|
|
8988
|
+
const constraintDate = activity.constraint_date ?? lsToComparison;
|
|
8739
8989
|
ls = new Date(
|
|
8740
8990
|
Math.min(constraintDate.getTime(), lsToComparison.getTime())
|
|
8741
8991
|
);
|
|
@@ -8747,8 +8997,8 @@ var BackwardPath = class extends generic_calculations_default {
|
|
|
8747
8997
|
);
|
|
8748
8998
|
}
|
|
8749
8999
|
if ([CONSTRAINT_TYPES.FNLT].includes(activity.constraint_type)) {
|
|
8750
|
-
const constraintDate = activity.constraint_date;
|
|
8751
9000
|
const parentLinkCalcualation2 = this.calculationsOfParentThatImpactChildrens.get(parent);
|
|
9001
|
+
const constraintDate = activity.constraint_date ?? parentLinkCalcualation2.lf;
|
|
8752
9002
|
ls = new Date(
|
|
8753
9003
|
Math.min(constraintDate.getTime(), parentLinkCalcualation2.lf.getTime())
|
|
8754
9004
|
);
|
|
@@ -9132,18 +9382,20 @@ var FreeFloat = class {
|
|
|
9132
9382
|
);
|
|
9133
9383
|
}
|
|
9134
9384
|
calculateFreeFloatForEndOfProject(constraint) {
|
|
9385
|
+
const calculation = constraint === CONSTRAINT_TYPES.ALAP ? this.backwardMap.get(this.activity.id) : this.forwardMap.get(this.activity.id);
|
|
9135
9386
|
return this.parseHoursToDays(
|
|
9136
9387
|
this.calculationWithCalendar(
|
|
9137
9388
|
this.gantt.getSubtaskDates().end_date,
|
|
9138
|
-
constraint === CONSTRAINT_TYPES.ALAP ?
|
|
9389
|
+
constraint === CONSTRAINT_TYPES.ALAP ? calculation?.lf : calculation?.ef
|
|
9139
9390
|
)
|
|
9140
9391
|
);
|
|
9141
9392
|
}
|
|
9142
9393
|
calculateFreeFloatOfRestriction(constraint) {
|
|
9394
|
+
const calculation = this.forwardMap.get(this.activity.id);
|
|
9143
9395
|
return this.parseHoursToDays(
|
|
9144
9396
|
this.calculationWithCalendar(
|
|
9145
9397
|
this.activity.constraint_date,
|
|
9146
|
-
constraint === CONSTRAINT_TYPES.FNLT ?
|
|
9398
|
+
constraint === CONSTRAINT_TYPES.FNLT ? calculation?.ef : calculation?.es
|
|
9147
9399
|
)
|
|
9148
9400
|
);
|
|
9149
9401
|
}
|
|
@@ -9161,111 +9413,33 @@ var FreeFloat = class {
|
|
|
9161
9413
|
sucessorId,
|
|
9162
9414
|
predecessorId
|
|
9163
9415
|
}) {
|
|
9164
|
-
const
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9416
|
+
const successor = (isSuscessorAlap ? this.backwardMap : this.forwardMap).get(sucessorId);
|
|
9417
|
+
const predecessor = (constraint === CONSTRAINT_TYPES.ALAP ? this.backwardMap : this.forwardMap).get(predecessorId);
|
|
9418
|
+
const predecessorStart = constraint === CONSTRAINT_TYPES.ALAP ? predecessor?.ls : predecessor?.es;
|
|
9419
|
+
const predecessorFinish = constraint === CONSTRAINT_TYPES.ALAP ? predecessor?.lf : predecessor?.ef;
|
|
9420
|
+
return {
|
|
9421
|
+
fs: {
|
|
9422
|
+
sucessor: successor?.ls ?? successor?.es,
|
|
9423
|
+
predecessor: predecessorFinish
|
|
9424
|
+
},
|
|
9425
|
+
ss: {
|
|
9426
|
+
sucessor: successor?.ls ?? successor?.es,
|
|
9427
|
+
predecessor: predecessorStart
|
|
9428
|
+
},
|
|
9429
|
+
ff: {
|
|
9430
|
+
sucessor: successor?.lf ?? successor?.ef,
|
|
9431
|
+
predecessor: predecessorFinish
|
|
9432
|
+
},
|
|
9433
|
+
sf: {
|
|
9434
|
+
sucessor: successor?.lf ?? successor?.ef,
|
|
9435
|
+
predecessor: predecessorStart
|
|
9170
9436
|
}
|
|
9171
|
-
return this.forwardMap.get(predecessorId)[direction];
|
|
9172
9437
|
};
|
|
9173
|
-
if (constraint === CONSTRAINT_TYPES.ASAP || constraint === CONSTRAINT_TYPES.SNET || constraint === CONSTRAINT_TYPES.FNET || constraint === CONSTRAINT_TYPES.MSO || constraint === CONSTRAINT_TYPES.MFO) {
|
|
9174
|
-
return {
|
|
9175
|
-
fs: {
|
|
9176
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9177
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9178
|
-
},
|
|
9179
|
-
ss: {
|
|
9180
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9181
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9182
|
-
},
|
|
9183
|
-
ff: {
|
|
9184
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9185
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9186
|
-
},
|
|
9187
|
-
sf: {
|
|
9188
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9189
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9190
|
-
}
|
|
9191
|
-
};
|
|
9192
|
-
}
|
|
9193
|
-
if (constraint === CONSTRAINT_TYPES.ALAP) {
|
|
9194
|
-
return {
|
|
9195
|
-
fs: {
|
|
9196
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9197
|
-
predecessor: getPredecessorDate({
|
|
9198
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
9199
|
-
direction: "lf"
|
|
9200
|
-
})
|
|
9201
|
-
},
|
|
9202
|
-
ss: {
|
|
9203
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9204
|
-
predecessor: getPredecessorDate({
|
|
9205
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
9206
|
-
direction: "ls"
|
|
9207
|
-
})
|
|
9208
|
-
},
|
|
9209
|
-
ff: {
|
|
9210
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9211
|
-
predecessor: getPredecessorDate({
|
|
9212
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
9213
|
-
direction: "lf"
|
|
9214
|
-
})
|
|
9215
|
-
},
|
|
9216
|
-
sf: {
|
|
9217
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9218
|
-
predecessor: getPredecessorDate({
|
|
9219
|
-
constraint: CONSTRAINT_TYPES.ALAP,
|
|
9220
|
-
direction: "ls"
|
|
9221
|
-
})
|
|
9222
|
-
}
|
|
9223
|
-
};
|
|
9224
|
-
}
|
|
9225
|
-
if (constraint === CONSTRAINT_TYPES.SNLT) {
|
|
9226
|
-
return {
|
|
9227
|
-
fs: {
|
|
9228
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9229
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9230
|
-
},
|
|
9231
|
-
ss: {
|
|
9232
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9233
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9234
|
-
},
|
|
9235
|
-
ff: {
|
|
9236
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9237
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9238
|
-
},
|
|
9239
|
-
sf: {
|
|
9240
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9241
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9242
|
-
}
|
|
9243
|
-
};
|
|
9244
|
-
}
|
|
9245
|
-
if (constraint === CONSTRAINT_TYPES.FNLT) {
|
|
9246
|
-
return {
|
|
9247
|
-
fs: {
|
|
9248
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9249
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9250
|
-
},
|
|
9251
|
-
ss: {
|
|
9252
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).ls : this.forwardMap.get(sucessorId).es,
|
|
9253
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9254
|
-
},
|
|
9255
|
-
ff: {
|
|
9256
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9257
|
-
predecessor: getPredecessorDate({ direction: "ef" })
|
|
9258
|
-
},
|
|
9259
|
-
sf: {
|
|
9260
|
-
sucessor: isSuscessorAlap ? this.backwardMap.get(sucessorId).lf : this.forwardMap.get(sucessorId).ef,
|
|
9261
|
-
predecessor: getPredecessorDate({ direction: "es" })
|
|
9262
|
-
}
|
|
9263
|
-
};
|
|
9264
|
-
}
|
|
9265
9438
|
}
|
|
9266
9439
|
calculateMsoMfo(constraint) {
|
|
9267
9440
|
const CONSTRAINT_DATE2 = this.activity.constraint_date || /* @__PURE__ */ new Date();
|
|
9268
|
-
const
|
|
9441
|
+
const calculation = this.forwardMap.get(this.activity.id);
|
|
9442
|
+
const DATE_TO_CALCULATE = constraint === CONSTRAINT_TYPES.MSO ? calculation?.es : calculation?.ef;
|
|
9269
9443
|
const freeSlack = this.calculationWithCalendar(
|
|
9270
9444
|
CONSTRAINT_DATE2,
|
|
9271
9445
|
DATE_TO_CALCULATE
|
|
@@ -9279,6 +9453,9 @@ var FreeFloat = class {
|
|
|
9279
9453
|
return filterValues.length ? Math.min(...filterValues) : 0;
|
|
9280
9454
|
}
|
|
9281
9455
|
calculationWithCalendar(dateOne, dateTwo) {
|
|
9456
|
+
if (!(dateOne instanceof Date) || !(dateTwo instanceof Date) || !Number.isFinite(dateOne.getTime()) || !Number.isFinite(dateTwo.getTime())) {
|
|
9457
|
+
return Number.NaN;
|
|
9458
|
+
}
|
|
9282
9459
|
const key = `${dateTwo.getTime()}_${dateOne.getTime()}`;
|
|
9283
9460
|
if (this.durationCache.has(key)) {
|
|
9284
9461
|
return this.durationCache.get(key);
|
|
@@ -9292,7 +9469,7 @@ var FreeFloat = class {
|
|
|
9292
9469
|
return result;
|
|
9293
9470
|
}
|
|
9294
9471
|
parseHoursToDays(date2) {
|
|
9295
|
-
if (
|
|
9472
|
+
if (date2 === void 0 || date2 === null) {
|
|
9296
9473
|
return 0;
|
|
9297
9474
|
}
|
|
9298
9475
|
return date2 / this.gantt.config.hoursPerDay;
|
|
@@ -9358,6 +9535,7 @@ var CriticalPath = class {
|
|
|
9358
9535
|
this.backwardPathCalculations = null;
|
|
9359
9536
|
this.totalFloat = /* @__PURE__ */ new Map();
|
|
9360
9537
|
this.freeFloat = /* @__PURE__ */ new Map();
|
|
9538
|
+
this.linkResidualHours = /* @__PURE__ */ new Map();
|
|
9361
9539
|
this.currentCalculationId = 0;
|
|
9362
9540
|
}
|
|
9363
9541
|
getCurrentCalculationId = () => this.currentCalculationId;
|
|
@@ -9388,6 +9566,13 @@ var CriticalPath = class {
|
|
|
9388
9566
|
if (!isCurrent()) return false;
|
|
9389
9567
|
this.forwardPathCalculations = forwardPathCalculations;
|
|
9390
9568
|
this.backwardPathCalculations = backwardPath.calculations;
|
|
9569
|
+
const linkResidualHours = await this.calculateLinkResidualHours(
|
|
9570
|
+
forwardPath.linkCandidates,
|
|
9571
|
+
forwardPathCalculations,
|
|
9572
|
+
isCurrent
|
|
9573
|
+
);
|
|
9574
|
+
if (!linkResidualHours || !isCurrent()) return false;
|
|
9575
|
+
this.linkResidualHours = linkResidualHours;
|
|
9391
9576
|
const { totalFloatMap, freeFloatMap } = await this.calculateFloatsAsync(
|
|
9392
9577
|
forwardPath,
|
|
9393
9578
|
backwardPath,
|
|
@@ -9407,6 +9592,38 @@ var CriticalPath = class {
|
|
|
9407
9592
|
throw error;
|
|
9408
9593
|
}
|
|
9409
9594
|
}
|
|
9595
|
+
async calculateLinkResidualHours(linkCandidates, forwardCalculations, isCurrent = () => true) {
|
|
9596
|
+
const residuals = /* @__PURE__ */ new Map();
|
|
9597
|
+
let sliceStartTime = performance.now();
|
|
9598
|
+
for (const [linkId, candidate] of linkCandidates) {
|
|
9599
|
+
sliceStartTime = await yieldIfNeeded2(sliceStartTime);
|
|
9600
|
+
if (!isCurrent()) return null;
|
|
9601
|
+
const targetId = Number(candidate.targetId);
|
|
9602
|
+
const target = this.gantt.getTask(targetId);
|
|
9603
|
+
const settled = forwardCalculations.get(targetId);
|
|
9604
|
+
const candidateEf = candidate.ef;
|
|
9605
|
+
const settledEf = settled?.ef;
|
|
9606
|
+
const calendar = target && this.gantt.getCalendar(target.calendar_id);
|
|
9607
|
+
if (!calendar || !(candidateEf instanceof Date) || !(settledEf instanceof Date) || !Number.isFinite(candidateEf.getTime()) || !Number.isFinite(settledEf.getTime())) {
|
|
9608
|
+
residuals.set(linkId, null);
|
|
9609
|
+
continue;
|
|
9610
|
+
}
|
|
9611
|
+
const candidateTime = candidateEf.getTime();
|
|
9612
|
+
const settledTime = settledEf.getTime();
|
|
9613
|
+
const duration = settledTime >= candidateTime ? calendar.calculateDuration({
|
|
9614
|
+
start_date: candidateEf,
|
|
9615
|
+
end_date: settledEf
|
|
9616
|
+
}) : -calendar.calculateDuration({
|
|
9617
|
+
start_date: settledEf,
|
|
9618
|
+
end_date: candidateEf
|
|
9619
|
+
});
|
|
9620
|
+
residuals.set(
|
|
9621
|
+
linkId,
|
|
9622
|
+
Number.isFinite(duration) ? Object.is(duration, -0) ? 0 : duration : null
|
|
9623
|
+
);
|
|
9624
|
+
}
|
|
9625
|
+
return residuals;
|
|
9626
|
+
}
|
|
9410
9627
|
async calculateFloatsAsync(forward, backward, isCurrent) {
|
|
9411
9628
|
try {
|
|
9412
9629
|
const projectDates = this.gantt.getSubtaskDates();
|
|
@@ -9510,14 +9727,52 @@ function computeLevels(state) {
|
|
|
9510
9727
|
for (const id of activitiesById.keys()) resolveLevel(id, /* @__PURE__ */ new Set());
|
|
9511
9728
|
return levelById;
|
|
9512
9729
|
}
|
|
9513
|
-
function
|
|
9730
|
+
function createLegacyIds(state) {
|
|
9731
|
+
const canonicalIds = /* @__PURE__ */ new Set();
|
|
9732
|
+
state.forEachActivity((activity, id) => {
|
|
9733
|
+
canonicalIds.add(String(id));
|
|
9734
|
+
if (activity.parentId !== null) canonicalIds.add(String(activity.parentId));
|
|
9735
|
+
});
|
|
9736
|
+
const numericCounts = /* @__PURE__ */ new Map();
|
|
9737
|
+
for (const id of canonicalIds) {
|
|
9738
|
+
const numeric = Number(id);
|
|
9739
|
+
if (Number.isSafeInteger(numeric) && numeric !== 0) {
|
|
9740
|
+
numericCounts.set(numeric, (numericCounts.get(numeric) ?? 0) + 1);
|
|
9741
|
+
}
|
|
9742
|
+
}
|
|
9743
|
+
const canonicalToLegacy = /* @__PURE__ */ new Map();
|
|
9744
|
+
const legacyToCanonical = /* @__PURE__ */ new Map();
|
|
9745
|
+
for (const id of canonicalIds) {
|
|
9746
|
+
const numeric = Number(id);
|
|
9747
|
+
if (Number.isSafeInteger(numeric) && numeric !== 0 && numericCounts.get(numeric) === 1) {
|
|
9748
|
+
canonicalToLegacy.set(id, numeric);
|
|
9749
|
+
legacyToCanonical.set(numeric, id);
|
|
9750
|
+
}
|
|
9751
|
+
}
|
|
9752
|
+
let next = 1;
|
|
9753
|
+
const toLegacy = (rawId) => {
|
|
9754
|
+
const id = String(rawId);
|
|
9755
|
+
const existing = canonicalToLegacy.get(id);
|
|
9756
|
+
if (existing !== void 0) return existing;
|
|
9757
|
+
while (legacyToCanonical.has(next)) next += 1;
|
|
9758
|
+
canonicalToLegacy.set(id, next);
|
|
9759
|
+
legacyToCanonical.set(next, id);
|
|
9760
|
+
return next++;
|
|
9761
|
+
};
|
|
9762
|
+
for (const id of canonicalIds) toLegacy(id);
|
|
9763
|
+
return {
|
|
9764
|
+
toLegacy,
|
|
9765
|
+
toCanonical: (id) => typeof id === "number" ? legacyToCanonical.get(id) ?? null : canonicalToLegacy.has(String(id)) ? String(id) : null
|
|
9766
|
+
};
|
|
9767
|
+
}
|
|
9768
|
+
function toLegacyTasks(state, ids) {
|
|
9514
9769
|
const levelById = computeLevels(state);
|
|
9515
9770
|
const leveled = /* @__PURE__ */ new Map();
|
|
9516
9771
|
state.forEachActivity((activity, id) => {
|
|
9517
9772
|
const key = String(id);
|
|
9518
9773
|
leveled.set(key, {
|
|
9519
|
-
id:
|
|
9520
|
-
parent: activity.parentId === null ? 0 :
|
|
9774
|
+
id: ids.toLegacy(key),
|
|
9775
|
+
parent: activity.parentId === null ? 0 : ids.toLegacy(activity.parentId),
|
|
9521
9776
|
$level: levelById.get(key) ?? 0,
|
|
9522
9777
|
$source: state.getOutgoingLinkIds(key),
|
|
9523
9778
|
$target: state.getIncomingLinkIds(key),
|
|
@@ -9591,7 +9846,8 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9591
9846
|
};
|
|
9592
9847
|
const bounds = projectBounds(state);
|
|
9593
9848
|
await yieldToBrowser();
|
|
9594
|
-
const
|
|
9849
|
+
const ids = createLegacyIds(state);
|
|
9850
|
+
const leveled = toLegacyTasks(state, ids);
|
|
9595
9851
|
await yieldToBrowser();
|
|
9596
9852
|
const byTime = [...leveled.values()].sort(
|
|
9597
9853
|
(a, b) => (a.start_date?.getTime() ?? 0) - (b.start_date?.getTime() ?? 0)
|
|
@@ -9601,24 +9857,31 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9601
9857
|
return {
|
|
9602
9858
|
fullyParsed: true,
|
|
9603
9859
|
config: { hoursPerDay },
|
|
9604
|
-
getTask: (id) =>
|
|
9860
|
+
getTask: (id) => {
|
|
9861
|
+
const canonicalId = ids.toCanonical(id);
|
|
9862
|
+
return canonicalId === null ? null : leveled.get(canonicalId) ?? null;
|
|
9863
|
+
},
|
|
9605
9864
|
getLink: (id) => {
|
|
9606
9865
|
const link = state.getLink(id);
|
|
9607
9866
|
if (!link) return null;
|
|
9608
9867
|
return {
|
|
9609
9868
|
...link,
|
|
9610
|
-
source:
|
|
9611
|
-
target:
|
|
9869
|
+
source: ids.toLegacy(link.source),
|
|
9870
|
+
target: ids.toLegacy(link.target),
|
|
9612
9871
|
lag: link.lag
|
|
9613
9872
|
};
|
|
9614
9873
|
},
|
|
9615
9874
|
getCalendar: resolveCalendar,
|
|
9616
|
-
getChildren: (parentId) =>
|
|
9875
|
+
getChildren: (parentId) => {
|
|
9876
|
+
const canonicalId = ids.toCanonical(parentId);
|
|
9877
|
+
return canonicalId === null ? [] : state.getChildren(canonicalId).map(ids.toLegacy);
|
|
9878
|
+
},
|
|
9617
9879
|
eachTask: (cb) => {
|
|
9618
9880
|
for (const task of treeOrdered) cb(task);
|
|
9619
9881
|
},
|
|
9620
9882
|
getTaskByTime: () => byTime,
|
|
9621
9883
|
getSubtaskDates: () => bounds,
|
|
9884
|
+
getCanonicalActivityId: ids.toCanonical,
|
|
9622
9885
|
refreshData: () => {
|
|
9623
9886
|
}
|
|
9624
9887
|
};
|
|
@@ -9626,21 +9889,92 @@ async function createGanttAdapter(state, hoursPerDay) {
|
|
|
9626
9889
|
|
|
9627
9890
|
// src/critical-path/run-critical-path.ts
|
|
9628
9891
|
var CP_WRITE_YIELD_EVERY = 1e3;
|
|
9629
|
-
function
|
|
9630
|
-
const
|
|
9892
|
+
function fieldsForLink(rawResidual, sourceIsCritical, targetIsCritical, targetProgress) {
|
|
9893
|
+
const freeSlackHours = typeof rawResidual === "number" && Number.isFinite(rawResidual) ? Object.is(rawResidual, -0) ? 0 : rawResidual : null;
|
|
9894
|
+
return {
|
|
9895
|
+
freeSlackHours,
|
|
9896
|
+
isCritical: freeSlackHours !== null && freeSlackHours <= 0 && sourceIsCritical && targetIsCritical && targetProgress === 0
|
|
9897
|
+
};
|
|
9898
|
+
}
|
|
9899
|
+
async function collectActivityFields(state, maps, hoursPerDay, toCanonicalActivityId, isCurrent) {
|
|
9900
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9901
|
+
let sinceYield = 0;
|
|
9902
|
+
for (const calculationId of maps.forward?.keys() ?? []) {
|
|
9903
|
+
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9904
|
+
sinceYield = 0;
|
|
9905
|
+
await yieldToBrowser();
|
|
9906
|
+
if (!isCurrent()) return null;
|
|
9907
|
+
}
|
|
9908
|
+
const activityId = toCanonicalActivityId(calculationId);
|
|
9909
|
+
if (activityId === null) continue;
|
|
9910
|
+
const activity = state.getActivity(activityId);
|
|
9911
|
+
if (!activity) continue;
|
|
9912
|
+
fields.set(
|
|
9913
|
+
String(activityId),
|
|
9914
|
+
fieldsForActivity(
|
|
9915
|
+
calculationId,
|
|
9916
|
+
Number(activity.progress ?? 0),
|
|
9917
|
+
maps,
|
|
9918
|
+
hoursPerDay
|
|
9919
|
+
)
|
|
9920
|
+
);
|
|
9921
|
+
}
|
|
9922
|
+
return isCurrent() ? fields : null;
|
|
9923
|
+
}
|
|
9924
|
+
async function collectLinkFields(state, linkResidualHours, activityFields, isCurrent) {
|
|
9925
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9926
|
+
let sinceYield = 0;
|
|
9927
|
+
for (const link of state.getAllLinks()) {
|
|
9928
|
+
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9929
|
+
sinceYield = 0;
|
|
9930
|
+
await yieldToBrowser();
|
|
9931
|
+
if (!isCurrent()) return null;
|
|
9932
|
+
}
|
|
9933
|
+
fields.set(
|
|
9934
|
+
String(link.id),
|
|
9935
|
+
fieldsForLink(
|
|
9936
|
+
linkResidualHours.get(String(link.id)),
|
|
9937
|
+
activityFields.get(link.source)?.isCritical === true,
|
|
9938
|
+
activityFields.get(link.target)?.isCritical === true,
|
|
9939
|
+
state.getActivity(link.target)?.progress
|
|
9940
|
+
)
|
|
9941
|
+
);
|
|
9942
|
+
}
|
|
9943
|
+
return isCurrent() ? fields : null;
|
|
9944
|
+
}
|
|
9945
|
+
var slackDaysToHours = (valueDays, hoursPerDay) => {
|
|
9946
|
+
if (typeof valueDays !== "number" || !Number.isFinite(valueDays) || !Number.isFinite(hoursPerDay) || hoursPerDay <= 0) {
|
|
9947
|
+
return null;
|
|
9948
|
+
}
|
|
9949
|
+
const hours = valueDays * hoursPerDay;
|
|
9950
|
+
if (!Number.isFinite(hours)) return null;
|
|
9951
|
+
return Object.is(hours, -0) ? 0 : hours;
|
|
9952
|
+
};
|
|
9953
|
+
function fieldsForActivity(activityId, progress, maps, hoursPerDay) {
|
|
9954
|
+
const toDate = (value) => {
|
|
9955
|
+
if (value == null) return null;
|
|
9956
|
+
const date2 = new Date(value);
|
|
9957
|
+
return Number.isFinite(date2.getTime()) ? date2 : null;
|
|
9958
|
+
};
|
|
9631
9959
|
const forward = maps.forward?.get(activityId);
|
|
9632
9960
|
const backward = maps.backward?.get(activityId);
|
|
9633
|
-
const
|
|
9961
|
+
const totalSlackHours = slackDaysToHours(
|
|
9962
|
+
maps.totalFloat.get(activityId),
|
|
9963
|
+
hoursPerDay
|
|
9964
|
+
);
|
|
9634
9965
|
return {
|
|
9635
9966
|
criticalPath: {
|
|
9636
9967
|
earlyStart: toDate(forward?.es),
|
|
9637
9968
|
earlyFinish: toDate(forward?.ef),
|
|
9638
9969
|
lateStart: toDate(backward?.ls),
|
|
9639
9970
|
lateFinish: toDate(backward?.lf),
|
|
9640
|
-
totalSlackHours
|
|
9971
|
+
totalSlackHours
|
|
9641
9972
|
},
|
|
9642
|
-
isCritical: progress < 100 &&
|
|
9643
|
-
freeSlackHours:
|
|
9973
|
+
isCritical: progress < 100 && totalSlackHours !== null && totalSlackHours <= 0,
|
|
9974
|
+
freeSlackHours: slackDaysToHours(
|
|
9975
|
+
maps.freeFloat.get(activityId)?.activityFreeFloat,
|
|
9976
|
+
hoursPerDay
|
|
9977
|
+
)
|
|
9644
9978
|
};
|
|
9645
9979
|
}
|
|
9646
9980
|
var datesEqual = (left, right) => left === right || left !== null && right !== null && left.getTime() === right.getTime();
|
|
@@ -9653,6 +9987,14 @@ function setCriticalPathFieldsIfChanged(state, activityId, fields) {
|
|
|
9653
9987
|
}
|
|
9654
9988
|
state.setActivityFields(activityId, fields);
|
|
9655
9989
|
}
|
|
9990
|
+
function setCriticalPathLinkFieldsIfChanged(state, linkId, fields) {
|
|
9991
|
+
const link = state.getLink(linkId);
|
|
9992
|
+
if (!link || Object.is(link.freeSlackHours, fields.freeSlackHours) && Object.is(link.isCritical, fields.isCritical)) {
|
|
9993
|
+
return;
|
|
9994
|
+
}
|
|
9995
|
+
state.setLinkField(linkId, "freeSlackHours", fields.freeSlackHours);
|
|
9996
|
+
state.setLinkField(linkId, "isCritical", fields.isCritical);
|
|
9997
|
+
}
|
|
9656
9998
|
async function computeCriticalPathFields(state, hoursPerDay, isCurrent = () => true) {
|
|
9657
9999
|
if (!isCurrent()) return null;
|
|
9658
10000
|
const gantt = await createGanttAdapter(state, hoursPerDay);
|
|
@@ -9670,37 +10012,34 @@ async function computeCriticalPathFields(state, hoursPerDay, isCurrent = () => t
|
|
|
9670
10012
|
totalFloat: criticalPath.totalFloat,
|
|
9671
10013
|
freeFloat: criticalPath.freeFloat
|
|
9672
10014
|
};
|
|
9673
|
-
const
|
|
9674
|
-
const fieldsByActivity = /* @__PURE__ */ new Map();
|
|
9675
|
-
let sinceYield = 0;
|
|
9676
|
-
for (const activityId of activityIds) {
|
|
9677
|
-
if (++sinceYield >= CP_WRITE_YIELD_EVERY) {
|
|
9678
|
-
sinceYield = 0;
|
|
9679
|
-
await yieldToBrowser();
|
|
9680
|
-
if (!isCurrent()) return null;
|
|
9681
|
-
}
|
|
9682
|
-
const activity = state.getActivity(activityId);
|
|
9683
|
-
if (!activity) continue;
|
|
9684
|
-
const progress = Number(activity.progress ?? 0);
|
|
9685
|
-
fieldsByActivity.set(
|
|
9686
|
-
activityId,
|
|
9687
|
-
fieldsForActivity(activityId, progress, maps)
|
|
9688
|
-
);
|
|
9689
|
-
}
|
|
9690
|
-
return isCurrent() ? fieldsByActivity : null;
|
|
9691
|
-
}
|
|
9692
|
-
async function applyCriticalPath(state, hoursPerDay, isCurrent = () => true) {
|
|
9693
|
-
const fieldsByActivity = await computeCriticalPathFields(
|
|
10015
|
+
const activities = await collectActivityFields(
|
|
9694
10016
|
state,
|
|
10017
|
+
maps,
|
|
9695
10018
|
hoursPerDay,
|
|
10019
|
+
gantt.getCanonicalActivityId,
|
|
10020
|
+
isCurrent
|
|
10021
|
+
);
|
|
10022
|
+
if (!activities) return null;
|
|
10023
|
+
const links = await collectLinkFields(
|
|
10024
|
+
state,
|
|
10025
|
+
criticalPath.linkResidualHours,
|
|
10026
|
+
activities,
|
|
9696
10027
|
isCurrent
|
|
9697
10028
|
);
|
|
9698
|
-
|
|
9699
|
-
|
|
10029
|
+
return links ? { activities, links } : null;
|
|
10030
|
+
}
|
|
10031
|
+
async function applyCriticalPath(state, hoursPerDay, isCurrent = () => true) {
|
|
10032
|
+
const fields = await computeCriticalPathFields(state, hoursPerDay, isCurrent);
|
|
10033
|
+
if (!fields || !isCurrent()) return false;
|
|
10034
|
+
for (const [activityId, activityFields] of fields.activities) {
|
|
9700
10035
|
if (!isCurrent()) return false;
|
|
9701
|
-
setCriticalPathFieldsIfChanged(state, activityId,
|
|
10036
|
+
setCriticalPathFieldsIfChanged(state, activityId, activityFields);
|
|
9702
10037
|
}
|
|
9703
|
-
|
|
10038
|
+
for (const [linkId, linkFields] of fields.links) {
|
|
10039
|
+
if (!isCurrent()) return false;
|
|
10040
|
+
setCriticalPathLinkFieldsIfChanged(state, linkId, linkFields);
|
|
10041
|
+
}
|
|
10042
|
+
return true;
|
|
9704
10043
|
}
|
|
9705
10044
|
|
|
9706
10045
|
// src/propagations/upward/real-cost.ts
|
|
@@ -9800,9 +10139,15 @@ function isSchedulingDispatch(action, structuralStaleKinds) {
|
|
|
9800
10139
|
return true;
|
|
9801
10140
|
}
|
|
9802
10141
|
function dispatchChangesSchedulingState(action) {
|
|
10142
|
+
if (action.kind === "activity-lookahead-sync") {
|
|
10143
|
+
return action.persistedProgress !== void 0;
|
|
10144
|
+
}
|
|
9803
10145
|
return isSchedulingDispatch(action, NO_STRUCTURAL_EXEMPTIONS);
|
|
9804
10146
|
}
|
|
9805
10147
|
function resolveRunCriticalPath(action) {
|
|
10148
|
+
if (action.kind === "activity-lookahead-sync") {
|
|
10149
|
+
return action.persistedProgress !== void 0;
|
|
10150
|
+
}
|
|
9806
10151
|
return isSchedulingDispatch(
|
|
9807
10152
|
action,
|
|
9808
10153
|
STRUCTURAL_KINDS_LEAVING_CRITICAL_PATH_STALE
|
|
@@ -10088,6 +10433,23 @@ function detectSirAutoReject(beforeSnap, adapter, touchedIds) {
|
|
|
10088
10433
|
return effects;
|
|
10089
10434
|
}
|
|
10090
10435
|
|
|
10436
|
+
// src/dispatch/conversion.ts
|
|
10437
|
+
function snapshotToLink(l) {
|
|
10438
|
+
const view = {
|
|
10439
|
+
id: String(l.id),
|
|
10440
|
+
source: String(l.source),
|
|
10441
|
+
target: String(l.target),
|
|
10442
|
+
type: l.type,
|
|
10443
|
+
lag: Number(l.lag),
|
|
10444
|
+
freeSlackHours: typeof l.freeSlackHours === "number" && Number.isFinite(l.freeSlackHours) ? l.freeSlackHours : null,
|
|
10445
|
+
isCritical: l.isCritical === true
|
|
10446
|
+
};
|
|
10447
|
+
if (l.proplannerId != null) view.proplannerId = Number(l.proplannerId);
|
|
10448
|
+
if (l.ganttId != null) view.ganttId = Number(l.ganttId);
|
|
10449
|
+
if (l.sectorId != null) view.sectorId = Number(l.sectorId);
|
|
10450
|
+
return view;
|
|
10451
|
+
}
|
|
10452
|
+
|
|
10091
10453
|
// src/dispatch/shared/change-set.ts
|
|
10092
10454
|
async function assembleChangeSet(adapter, args) {
|
|
10093
10455
|
const allTouched = collectChangeSetIds(adapter, args);
|
|
@@ -10097,7 +10459,7 @@ async function assembleChangeSet(adapter, args) {
|
|
|
10097
10459
|
const insertedIds = collectInsertedIds(captured);
|
|
10098
10460
|
mergeDirtyBeforeImages(adapter, captured, insertedIds, before, allTouched);
|
|
10099
10461
|
}
|
|
10100
|
-
const sirDetection = args.
|
|
10462
|
+
const sirDetection = args.sirDetection;
|
|
10101
10463
|
const beforeDiffEffects = sirDetection === "before-diff" ? detectSirAutoReject(before, adapter, allTouched) : [];
|
|
10102
10464
|
const correlativeBefore = buildCorrelativeBeforeMap(
|
|
10103
10465
|
args.correlativeShifts,
|
|
@@ -10111,16 +10473,89 @@ async function assembleChangeSet(adapter, args) {
|
|
|
10111
10473
|
);
|
|
10112
10474
|
const effects = sirDetection === "after-diff" ? detectSirAutoReject(before, adapter, allTouched) : beforeDiffEffects;
|
|
10113
10475
|
const warnings = args.warnings ?? [];
|
|
10476
|
+
const linkChanges = mergeLinkChanges(
|
|
10477
|
+
args.links ?? [],
|
|
10478
|
+
captured ? buildCapturedCriticalPathLinkChanges(adapter, captured) : []
|
|
10479
|
+
);
|
|
10114
10480
|
return {
|
|
10115
10481
|
source: args.source,
|
|
10116
10482
|
activities: activityChanges,
|
|
10117
|
-
links:
|
|
10483
|
+
links: linkChanges,
|
|
10118
10484
|
calendars: [],
|
|
10119
10485
|
trackingEvents: args.trackingEvents ?? [],
|
|
10120
10486
|
...effects.length > 0 ? { effects } : {},
|
|
10121
10487
|
...warnings.length > 0 ? { warnings } : {}
|
|
10122
10488
|
};
|
|
10123
10489
|
}
|
|
10490
|
+
function buildCapturedCriticalPathLinkChanges(adapter, captured) {
|
|
10491
|
+
const changes = [];
|
|
10492
|
+
for (const operation of captured.ops) {
|
|
10493
|
+
if (operation.kind !== "link-field") continue;
|
|
10494
|
+
const after = adapter.getLink(operation.linkId);
|
|
10495
|
+
if (!after) continue;
|
|
10496
|
+
const fields = {};
|
|
10497
|
+
for (const field of ["freeSlackHours", "isCritical"]) {
|
|
10498
|
+
if (!Object.is(operation.before[field], after[field])) {
|
|
10499
|
+
fields[field] = {
|
|
10500
|
+
before: operation.before[field],
|
|
10501
|
+
after: after[field]
|
|
10502
|
+
};
|
|
10503
|
+
}
|
|
10504
|
+
}
|
|
10505
|
+
if (Object.keys(fields).length === 0) continue;
|
|
10506
|
+
changes.push({
|
|
10507
|
+
id: operation.linkId,
|
|
10508
|
+
kind: "updated",
|
|
10509
|
+
fields,
|
|
10510
|
+
after: snapshotToLink(after)
|
|
10511
|
+
});
|
|
10512
|
+
}
|
|
10513
|
+
return changes;
|
|
10514
|
+
}
|
|
10515
|
+
function mergeLinkChanges(explicit, captured) {
|
|
10516
|
+
const merged = [];
|
|
10517
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
10518
|
+
for (const change of explicit) {
|
|
10519
|
+
const index = indexById.get(change.id);
|
|
10520
|
+
if (index === void 0) {
|
|
10521
|
+
indexById.set(change.id, merged.length);
|
|
10522
|
+
merged.push(change);
|
|
10523
|
+
continue;
|
|
10524
|
+
}
|
|
10525
|
+
const current = merged[index];
|
|
10526
|
+
if ((current.kind === "updated" || current.kind === "created") && change.kind === "updated") {
|
|
10527
|
+
merged[index] = {
|
|
10528
|
+
...current,
|
|
10529
|
+
fields: { ...current.fields, ...change.fields },
|
|
10530
|
+
after: change.after
|
|
10531
|
+
};
|
|
10532
|
+
continue;
|
|
10533
|
+
}
|
|
10534
|
+
merged[index] = change;
|
|
10535
|
+
}
|
|
10536
|
+
for (const change of captured) {
|
|
10537
|
+
const index = indexById.get(change.id);
|
|
10538
|
+
if (index === void 0) {
|
|
10539
|
+
indexById.set(change.id, merged.length);
|
|
10540
|
+
merged.push(change);
|
|
10541
|
+
continue;
|
|
10542
|
+
}
|
|
10543
|
+
const current = merged[index];
|
|
10544
|
+
if (current.kind === "deleted") continue;
|
|
10545
|
+
const capturedFields = current.kind === "created" ? Object.fromEntries(
|
|
10546
|
+
Object.entries(change.fields ?? {}).map(([field, value]) => [
|
|
10547
|
+
field,
|
|
10548
|
+
{ before: void 0, after: value.after }
|
|
10549
|
+
])
|
|
10550
|
+
) : change.fields;
|
|
10551
|
+
merged[index] = {
|
|
10552
|
+
...current,
|
|
10553
|
+
fields: { ...current.fields, ...capturedFields },
|
|
10554
|
+
after: change.after
|
|
10555
|
+
};
|
|
10556
|
+
}
|
|
10557
|
+
return merged;
|
|
10558
|
+
}
|
|
10124
10559
|
function buildCorrelativeBeforeMap(shifts, touched) {
|
|
10125
10560
|
if (!shifts || shifts.length === 0) return void 0;
|
|
10126
10561
|
const correlativeBefore = /* @__PURE__ */ new Map();
|
|
@@ -10156,21 +10591,6 @@ function collectInsertedIds(captured) {
|
|
|
10156
10591
|
);
|
|
10157
10592
|
}
|
|
10158
10593
|
|
|
10159
|
-
// src/dispatch/conversion.ts
|
|
10160
|
-
function snapshotToLink(l) {
|
|
10161
|
-
const view = {
|
|
10162
|
-
id: String(l.id),
|
|
10163
|
-
source: String(l.source),
|
|
10164
|
-
target: String(l.target),
|
|
10165
|
-
type: l.type,
|
|
10166
|
-
lag: Number(l.lag)
|
|
10167
|
-
};
|
|
10168
|
-
if (l.proplannerId != null) view.proplannerId = Number(l.proplannerId);
|
|
10169
|
-
if (l.ganttId != null) view.ganttId = Number(l.ganttId);
|
|
10170
|
-
if (l.sectorId != null) view.sectorId = Number(l.sectorId);
|
|
10171
|
-
return view;
|
|
10172
|
-
}
|
|
10173
|
-
|
|
10174
10594
|
// src/dispatch/shared/helpers.ts
|
|
10175
10595
|
function buildLinkChangesForBatch(applied, beforeLinks, adapter) {
|
|
10176
10596
|
const out = [];
|
|
@@ -10438,7 +10858,6 @@ async function dispatchInlineEdit(action, options, deps) {
|
|
|
10438
10858
|
touchedIds,
|
|
10439
10859
|
scheduledIds,
|
|
10440
10860
|
sirDetection: "before-diff",
|
|
10441
|
-
preview: options.preview,
|
|
10442
10861
|
links: linkChanges,
|
|
10443
10862
|
trackingEvents,
|
|
10444
10863
|
...constraintWarning ? { warnings: [constraintWarning] } : {},
|
|
@@ -10516,6 +10935,26 @@ function collectDeterministicIds(operations) {
|
|
|
10516
10935
|
});
|
|
10517
10936
|
return ids.size > 0 ? ids : void 0;
|
|
10518
10937
|
}
|
|
10938
|
+
function resolveDeterministicIds(deterministicIds, deps) {
|
|
10939
|
+
if (deterministicIds === void 0) return void 0;
|
|
10940
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
10941
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
10942
|
+
const nextAvailableId = () => {
|
|
10943
|
+
let candidate = deps.linkIdGen.next();
|
|
10944
|
+
while (deps.adapter.getLink(candidate) !== null || deps.isLinkIdReserved(String(candidate)) || assigned.has(String(candidate))) {
|
|
10945
|
+
candidate = deps.linkIdGen.next();
|
|
10946
|
+
}
|
|
10947
|
+
return candidate;
|
|
10948
|
+
};
|
|
10949
|
+
for (const [operationIndex, explicitId] of deterministicIds) {
|
|
10950
|
+
const isReserved = deps.adapter.getLink(explicitId) !== null || deps.isLinkIdReserved(String(explicitId)) || assigned.has(String(explicitId));
|
|
10951
|
+
const linkId = isReserved ? nextAvailableId() : explicitId;
|
|
10952
|
+
if (!isReserved) deps.linkIdGen.bump(explicitId);
|
|
10953
|
+
assigned.add(String(linkId));
|
|
10954
|
+
resolved.set(operationIndex, linkId);
|
|
10955
|
+
}
|
|
10956
|
+
return resolved;
|
|
10957
|
+
}
|
|
10519
10958
|
function batchOpToLinkOperation(op, hoursPerDay) {
|
|
10520
10959
|
if (op.kind === "create") {
|
|
10521
10960
|
return {
|
|
@@ -10548,7 +10987,7 @@ async function dispatchLinkBatch(operations, source, options, deps, deterministi
|
|
|
10548
10987
|
operations,
|
|
10549
10988
|
adapter,
|
|
10550
10989
|
linkIdGen,
|
|
10551
|
-
deterministicIds
|
|
10990
|
+
resolveDeterministicIds(deterministicIds, deps)
|
|
10552
10991
|
);
|
|
10553
10992
|
if (operations.length === 1 && applied[0] && applied[0].rejected !== null && source.kind !== "inline-edit") {
|
|
10554
10993
|
return { ok: false, reason: applied[0].rejected };
|
|
@@ -10730,26 +11169,19 @@ function recomputeHhCascadeForParent(startParentId, adapter) {
|
|
|
10730
11169
|
if (!startParentId || startParentId === "0") return;
|
|
10731
11170
|
let currentParentId = String(startParentId);
|
|
10732
11171
|
while (currentParentId !== null) {
|
|
10733
|
-
const
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
const child = adapter.getActivity(childId);
|
|
10738
|
-
if (!child) continue;
|
|
10739
|
-
const hh = readNumber(child, ACTIVITY_PROPERTY.WORK_HOURS);
|
|
10740
|
-
const rw = readChildRealWork(childId, child, hh, adapter);
|
|
10741
|
-
hhSum += hh;
|
|
10742
|
-
rwSum += rw;
|
|
10743
|
-
}
|
|
11172
|
+
const { workHours, realWorkHours } = computeParentHhRollup(
|
|
11173
|
+
currentParentId,
|
|
11174
|
+
adapter
|
|
11175
|
+
);
|
|
10744
11176
|
adapter.setActivityField(
|
|
10745
11177
|
currentParentId,
|
|
10746
11178
|
ACTIVITY_PROPERTY.WORK_HOURS,
|
|
10747
|
-
|
|
11179
|
+
workHours
|
|
10748
11180
|
);
|
|
10749
11181
|
adapter.setActivityField(
|
|
10750
11182
|
currentParentId,
|
|
10751
11183
|
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
10752
|
-
|
|
11184
|
+
realWorkHours
|
|
10753
11185
|
);
|
|
10754
11186
|
const parentSnap = adapter.getActivity(currentParentId);
|
|
10755
11187
|
if (!parentSnap) break;
|
|
@@ -10765,6 +11197,40 @@ function recomputeHhCascadeForActivity(activityId, adapter) {
|
|
|
10765
11197
|
if (!parentId || isRootParent(parentId)) return;
|
|
10766
11198
|
recomputeHhCascadeForParent(parentId, adapter);
|
|
10767
11199
|
}
|
|
11200
|
+
function recomputeRealWorkFromProgress(progressIds, parentIdsDeepestFirst, adapter) {
|
|
11201
|
+
for (const activityId of progressIds) {
|
|
11202
|
+
if (adapter.hasChildren(activityId)) continue;
|
|
11203
|
+
const activity = adapter.getActivity(activityId);
|
|
11204
|
+
if (!activity) continue;
|
|
11205
|
+
adapter.setActivityField(
|
|
11206
|
+
activityId,
|
|
11207
|
+
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
11208
|
+
computeLeafRealWork(
|
|
11209
|
+
readNumber(activity, ACTIVITY_PROPERTY.WORK_HOURS),
|
|
11210
|
+
readNumber(activity, ACTIVITY_PROPERTY.PROGRESS)
|
|
11211
|
+
)
|
|
11212
|
+
);
|
|
11213
|
+
}
|
|
11214
|
+
for (const parentId of parentIdsDeepestFirst) {
|
|
11215
|
+
adapter.setActivityField(
|
|
11216
|
+
parentId,
|
|
11217
|
+
ACTIVITY_PROPERTY.REAL_WORK_HOURS,
|
|
11218
|
+
computeParentHhRollup(parentId, adapter).realWorkHours
|
|
11219
|
+
);
|
|
11220
|
+
}
|
|
11221
|
+
}
|
|
11222
|
+
function computeParentHhRollup(parentId, adapter) {
|
|
11223
|
+
let workHours = 0;
|
|
11224
|
+
let realWorkHours = 0;
|
|
11225
|
+
for (const childId of adapter.getChildren(parentId)) {
|
|
11226
|
+
const child = adapter.getActivity(childId);
|
|
11227
|
+
if (!child) continue;
|
|
11228
|
+
const childWorkHours = readNumber(child, ACTIVITY_PROPERTY.WORK_HOURS);
|
|
11229
|
+
workHours += childWorkHours;
|
|
11230
|
+
realWorkHours += readChildRealWork(childId, child, childWorkHours, adapter);
|
|
11231
|
+
}
|
|
11232
|
+
return { workHours, realWorkHours };
|
|
11233
|
+
}
|
|
10768
11234
|
function readNumber(activity, key) {
|
|
10769
11235
|
const v = activity[key];
|
|
10770
11236
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
@@ -11137,6 +11603,36 @@ function computeFractionalCidAtIndex(adapter, parentKey, targetIndex, excludeId)
|
|
|
11137
11603
|
return (before + after) / 2;
|
|
11138
11604
|
}
|
|
11139
11605
|
|
|
11606
|
+
// src/dispatch/creation-overrides.ts
|
|
11607
|
+
var PUBLIC_CREATION_OVERRIDE_FIELDS = /* @__PURE__ */ new Set([
|
|
11608
|
+
...[...CREATION_OVERRIDE_FIELDS].filter((field) => field !== "durationHours"),
|
|
11609
|
+
"durationDays"
|
|
11610
|
+
]);
|
|
11611
|
+
function normalizeActivityCreationOverrides(raw, hoursPerDay) {
|
|
11612
|
+
if (raw === void 0) return { ok: true, value: {} };
|
|
11613
|
+
const prototype = Object.getPrototypeOf(raw);
|
|
11614
|
+
if (prototype !== null && prototype !== Object.prototype) {
|
|
11615
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11616
|
+
}
|
|
11617
|
+
for (const key of Reflect.ownKeys(raw)) {
|
|
11618
|
+
if (typeof key === "symbol" || !PUBLIC_CREATION_OVERRIDE_FIELDS.has(key)) {
|
|
11619
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11620
|
+
}
|
|
11621
|
+
}
|
|
11622
|
+
const { durationDays, ...rest } = raw;
|
|
11623
|
+
if (durationDays !== void 0 && (!Number.isFinite(durationDays) || durationDays < 0)) {
|
|
11624
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
11625
|
+
}
|
|
11626
|
+
return {
|
|
11627
|
+
ok: true,
|
|
11628
|
+
value: {
|
|
11629
|
+
...rest,
|
|
11630
|
+
...durationDays === 0 && (rest.type === void 0 || rest.type === "task") ? { type: "milestone" } : {},
|
|
11631
|
+
...durationDays === void 0 ? {} : { durationHours: durationToHours(durationDays, hoursPerDay) }
|
|
11632
|
+
}
|
|
11633
|
+
};
|
|
11634
|
+
}
|
|
11635
|
+
|
|
11140
11636
|
// src/dispatch/handlers/activity-create.ts
|
|
11141
11637
|
async function createActivityCore(action, deps, opts = {}) {
|
|
11142
11638
|
const { adapter, scheduler, sector, calendars, activityIdGen, uidGen } = deps;
|
|
@@ -11144,7 +11640,12 @@ async function createActivityCore(action, deps, opts = {}) {
|
|
|
11144
11640
|
const target = validateCreateTarget(action, adapter);
|
|
11145
11641
|
if (!target.ok) return target;
|
|
11146
11642
|
const { parent, anchorSiblingId, isRoot } = target;
|
|
11147
|
-
const newId = resolveNewActivityId(
|
|
11643
|
+
const newId = resolveNewActivityId(
|
|
11644
|
+
action.activityId,
|
|
11645
|
+
adapter,
|
|
11646
|
+
activityIdGen,
|
|
11647
|
+
deps.isActivityIdReserved
|
|
11648
|
+
);
|
|
11148
11649
|
const newUid = uidGen.next();
|
|
11149
11650
|
const activityInput = {
|
|
11150
11651
|
id: newId,
|
|
@@ -11237,9 +11738,18 @@ function validateAnchorSibling(action, adapter, isRoot, anchorSiblingId) {
|
|
|
11237
11738
|
}
|
|
11238
11739
|
return null;
|
|
11239
11740
|
}
|
|
11240
|
-
function resolveNewActivityId(explicitId, adapter, activityIdGen) {
|
|
11241
|
-
|
|
11242
|
-
|
|
11741
|
+
function resolveNewActivityId(explicitId, adapter, activityIdGen, isActivityIdReserved) {
|
|
11742
|
+
const nextAvailableId = () => {
|
|
11743
|
+
let candidate = activityIdGen.next();
|
|
11744
|
+
while (adapter.activityExists(candidate) || isActivityIdReserved(String(candidate))) {
|
|
11745
|
+
candidate = activityIdGen.next();
|
|
11746
|
+
}
|
|
11747
|
+
return candidate;
|
|
11748
|
+
};
|
|
11749
|
+
if (explicitId === void 0) return nextAvailableId();
|
|
11750
|
+
if (adapter.activityExists(explicitId) || isActivityIdReserved(String(explicitId))) {
|
|
11751
|
+
return nextAvailableId();
|
|
11752
|
+
}
|
|
11243
11753
|
activityIdGen.bump(explicitId);
|
|
11244
11754
|
return explicitId;
|
|
11245
11755
|
}
|
|
@@ -11318,7 +11828,15 @@ async function buildCreateChangeSet(adapter, action, coreResult, scheduledIds, h
|
|
|
11318
11828
|
}
|
|
11319
11829
|
async function dispatchActivityCreate(action, options, deps) {
|
|
11320
11830
|
const { adapter, scheduler, sector } = deps;
|
|
11321
|
-
const
|
|
11831
|
+
const normalized2 = normalizeActivityCreationOverrides(
|
|
11832
|
+
action.overrides,
|
|
11833
|
+
sector.hoursPerDay
|
|
11834
|
+
);
|
|
11835
|
+
if (!normalized2.ok) return normalized2;
|
|
11836
|
+
const core = await createActivityCore(
|
|
11837
|
+
{ ...action, overrides: normalized2.value },
|
|
11838
|
+
deps
|
|
11839
|
+
);
|
|
11322
11840
|
if (!core.ok) return core;
|
|
11323
11841
|
const { newId } = core;
|
|
11324
11842
|
const createdAutoScheduling = core.newActivity.autoScheduling;
|
|
@@ -11530,7 +12048,7 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11530
12048
|
for (const linkId of incidentLinkIds) {
|
|
11531
12049
|
adapter.removeLink(linkId);
|
|
11532
12050
|
}
|
|
11533
|
-
const
|
|
12051
|
+
const viewStateChanges2 = captureDeletedViewState(adapter, toDelete);
|
|
11534
12052
|
let removed = 0;
|
|
11535
12053
|
for (const id of toDelete) {
|
|
11536
12054
|
removed += 1;
|
|
@@ -11612,7 +12130,6 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11612
12130
|
touchedIds,
|
|
11613
12131
|
scheduledIds,
|
|
11614
12132
|
sirDetection: "after-diff",
|
|
11615
|
-
preview: options.preview,
|
|
11616
12133
|
links: buildLinkDeletions(beforeLinks),
|
|
11617
12134
|
trackingEvents: [trackingEvent],
|
|
11618
12135
|
hoursPerDay: sector.hoursPerDay,
|
|
@@ -11620,7 +12137,7 @@ async function dispatchActivityDelete(action, options, deps) {
|
|
|
11620
12137
|
});
|
|
11621
12138
|
return {
|
|
11622
12139
|
ok: true,
|
|
11623
|
-
changes:
|
|
12140
|
+
changes: viewStateChanges2.length > 0 ? { ...changes, viewState: viewStateChanges2 } : changes,
|
|
11624
12141
|
__beforeSnap: deletedRowsSnap,
|
|
11625
12142
|
__beforeLinks: beforeLinksByStringId
|
|
11626
12143
|
};
|
|
@@ -11728,7 +12245,7 @@ async function dispatchActivityBatch(action, options, deps) {
|
|
|
11728
12245
|
source: link.source,
|
|
11729
12246
|
target: link.target,
|
|
11730
12247
|
type: link.type,
|
|
11731
|
-
lag:
|
|
12248
|
+
lag: lagDaysToHours(link.lag, deps.sector.hoursPerDay)
|
|
11732
12249
|
},
|
|
11733
12250
|
linkId: link.linkId
|
|
11734
12251
|
}));
|
|
@@ -11773,7 +12290,6 @@ async function dispatchActivityBatch(action, options, deps) {
|
|
|
11773
12290
|
links: buildLinkChangesForBatch(appliedLinks, beforeLinks, deps.adapter),
|
|
11774
12291
|
hoursPerDay: deps.sector.hoursPerDay,
|
|
11775
12292
|
correlativeShifts: existingCorrelativeShifts,
|
|
11776
|
-
preview: options.preview,
|
|
11777
12293
|
...action.mode === "replace" ? { sirDetection: "after-diff" } : {}
|
|
11778
12294
|
});
|
|
11779
12295
|
return {
|
|
@@ -11801,7 +12317,7 @@ function validateBatch(action, deps) {
|
|
|
11801
12317
|
}
|
|
11802
12318
|
if (item.activityId !== void 0) {
|
|
11803
12319
|
const activityId = String(item.activityId);
|
|
11804
|
-
if (activityId.length === 0 || activityId === "0" || explicitActivityIds.has(activityId)
|
|
12320
|
+
if (activityId.length === 0 || activityId === "0" || explicitActivityIds.has(activityId)) {
|
|
11805
12321
|
return REJECTION_REASON.INVALID;
|
|
11806
12322
|
}
|
|
11807
12323
|
explicitActivityIds.add(activityId);
|
|
@@ -11823,7 +12339,7 @@ function validateBatch(action, deps) {
|
|
|
11823
12339
|
}
|
|
11824
12340
|
if (link.linkId !== void 0) {
|
|
11825
12341
|
const linkId = String(link.linkId);
|
|
11826
|
-
if (linkId.length === 0 || explicitLinkIds.has(linkId)
|
|
12342
|
+
if (linkId.length === 0 || explicitLinkIds.has(linkId)) {
|
|
11827
12343
|
return REJECTION_REASON.INVALID;
|
|
11828
12344
|
}
|
|
11829
12345
|
explicitLinkIds.add(linkId);
|
|
@@ -11859,24 +12375,38 @@ function hasParentCycle(items) {
|
|
|
11859
12375
|
return false;
|
|
11860
12376
|
}
|
|
11861
12377
|
function buildBatchPlan(action, deps) {
|
|
12378
|
+
const normalizedOverrides = /* @__PURE__ */ new Map();
|
|
11862
12379
|
for (const item of action.items) {
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
12380
|
+
const normalized2 = normalizeActivityCreationOverrides(
|
|
12381
|
+
item.overrides,
|
|
12382
|
+
deps.sector.hoursPerDay
|
|
12383
|
+
);
|
|
12384
|
+
if (!normalized2.ok) return normalized2;
|
|
12385
|
+
normalizedOverrides.set(item.batchId, normalized2.value);
|
|
11866
12386
|
}
|
|
11867
12387
|
const activityIdsByBatchId = /* @__PURE__ */ new Map();
|
|
12388
|
+
const plannedActivityIds = /* @__PURE__ */ new Set();
|
|
11868
12389
|
for (const item of action.items) {
|
|
11869
|
-
|
|
11870
|
-
item.
|
|
11871
|
-
|
|
12390
|
+
const activityId = resolvePlannedActivityId(
|
|
12391
|
+
item.activityId,
|
|
12392
|
+
action.mode,
|
|
12393
|
+
plannedActivityIds,
|
|
12394
|
+
deps
|
|
11872
12395
|
);
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
const linkIds = action.links.map(
|
|
11878
|
-
|
|
11879
|
-
|
|
12396
|
+
plannedActivityIds.add(String(activityId));
|
|
12397
|
+
activityIdsByBatchId.set(item.batchId, activityId);
|
|
12398
|
+
}
|
|
12399
|
+
const plannedLinkIds = /* @__PURE__ */ new Set();
|
|
12400
|
+
const linkIds = action.links.map((link) => {
|
|
12401
|
+
const linkId = resolvePlannedLinkId(
|
|
12402
|
+
link.linkId,
|
|
12403
|
+
action.mode,
|
|
12404
|
+
plannedLinkIds,
|
|
12405
|
+
deps
|
|
12406
|
+
);
|
|
12407
|
+
plannedLinkIds.add(String(linkId));
|
|
12408
|
+
return linkId;
|
|
12409
|
+
});
|
|
11880
12410
|
const insertionCorrelativeIds = planInsertionCorrelatives(
|
|
11881
12411
|
action,
|
|
11882
12412
|
activityIdsByBatchId,
|
|
@@ -11899,7 +12429,8 @@ function buildBatchPlan(action, deps) {
|
|
|
11899
12429
|
activityId,
|
|
11900
12430
|
parentId,
|
|
11901
12431
|
parentBatchId,
|
|
11902
|
-
insertionCorrelativeId
|
|
12432
|
+
insertionCorrelativeId,
|
|
12433
|
+
overrides: normalizedOverrides.get(item.batchId) ?? {}
|
|
11903
12434
|
});
|
|
11904
12435
|
}
|
|
11905
12436
|
const links = [];
|
|
@@ -11920,6 +12451,30 @@ function buildBatchPlan(action, deps) {
|
|
|
11920
12451
|
}
|
|
11921
12452
|
return { ok: true, plan: { items, links } };
|
|
11922
12453
|
}
|
|
12454
|
+
function resolvePlannedActivityId(explicitId, mode, plannedIds, deps) {
|
|
12455
|
+
const explicitIsAvailable = explicitId !== void 0 && (mode === "replace" || deps.adapter.getActivity(explicitId) === null && !deps.isActivityIdReserved(String(explicitId))) && !plannedIds.has(String(explicitId));
|
|
12456
|
+
if (explicitIsAvailable) {
|
|
12457
|
+
deps.activityIdGen.bump(explicitId);
|
|
12458
|
+
return explicitId;
|
|
12459
|
+
}
|
|
12460
|
+
let candidate = deps.activityIdGen.next();
|
|
12461
|
+
while (plannedIds.has(String(candidate)) || mode === "append" && (deps.adapter.getActivity(candidate) !== null || deps.isActivityIdReserved(String(candidate)))) {
|
|
12462
|
+
candidate = deps.activityIdGen.next();
|
|
12463
|
+
}
|
|
12464
|
+
return candidate;
|
|
12465
|
+
}
|
|
12466
|
+
function resolvePlannedLinkId(explicitId, mode, plannedIds, deps) {
|
|
12467
|
+
const explicitIsAvailable = explicitId !== void 0 && (mode === "replace" || deps.adapter.getLink(explicitId) === null && !deps.isLinkIdReserved(String(explicitId))) && !plannedIds.has(String(explicitId));
|
|
12468
|
+
if (explicitIsAvailable) {
|
|
12469
|
+
deps.linkIdGen.bump(explicitId);
|
|
12470
|
+
return explicitId;
|
|
12471
|
+
}
|
|
12472
|
+
let candidate = deps.linkIdGen.next();
|
|
12473
|
+
while (plannedIds.has(String(candidate)) || mode === "append" && (deps.adapter.getLink(candidate) !== null || deps.isLinkIdReserved(String(candidate)))) {
|
|
12474
|
+
candidate = deps.linkIdGen.next();
|
|
12475
|
+
}
|
|
12476
|
+
return candidate;
|
|
12477
|
+
}
|
|
11923
12478
|
function planInsertionCorrelatives(action, activityIdsByBatchId, deps) {
|
|
11924
12479
|
if (isSimpleRootAppend(action)) {
|
|
11925
12480
|
const firstCorrelativeId = deps.adapter.activityCount();
|
|
@@ -12029,7 +12584,7 @@ async function createPlannedActivities(action, plannedItems, deps) {
|
|
|
12029
12584
|
{
|
|
12030
12585
|
parentId: item.parentId,
|
|
12031
12586
|
activityId: item.activityId,
|
|
12032
|
-
|
|
12587
|
+
overrides: item.overrides,
|
|
12033
12588
|
...action.eventSource === void 0 ? {} : { eventSource: action.eventSource }
|
|
12034
12589
|
},
|
|
12035
12590
|
deps,
|
|
@@ -12106,301 +12661,280 @@ function dispatchSirSync(action, deps) {
|
|
|
12106
12661
|
};
|
|
12107
12662
|
}
|
|
12108
12663
|
|
|
12109
|
-
// src/
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12664
|
+
// src/propagations/upward/progress-rollup.ts
|
|
12665
|
+
function recomputeAllProgressRollup(adapter) {
|
|
12666
|
+
for (const parentId of parentsDeepestFirst(adapter)) {
|
|
12667
|
+
rollupParent(parentId, adapter);
|
|
12668
|
+
}
|
|
12669
|
+
}
|
|
12670
|
+
function recomputeProgressRollupForParents(parentIdsDeepestFirst, adapter) {
|
|
12671
|
+
for (const parentId of parentIdsDeepestFirst) {
|
|
12672
|
+
rollupParent(parentId, adapter);
|
|
12673
|
+
}
|
|
12674
|
+
}
|
|
12675
|
+
function parentsDeepestFirst(adapter) {
|
|
12676
|
+
const parents = /* @__PURE__ */ new Set();
|
|
12677
|
+
adapter.forEachActivity((_activity, id) => {
|
|
12678
|
+
if (adapter.getChildren(id).length > 0) parents.add(id);
|
|
12679
|
+
});
|
|
12680
|
+
const depthById = /* @__PURE__ */ new Map();
|
|
12681
|
+
for (const id of parents) depthById.set(id, depthOf2(id, adapter));
|
|
12682
|
+
return [...parents].sort(
|
|
12683
|
+
(a, b) => (depthById.get(b) ?? 0) - (depthById.get(a) ?? 0)
|
|
12115
12684
|
);
|
|
12116
|
-
|
|
12117
|
-
|
|
12685
|
+
}
|
|
12686
|
+
function depthOf2(id, adapter) {
|
|
12687
|
+
let depth = 0;
|
|
12688
|
+
let current = id;
|
|
12689
|
+
while (current && current !== "0") {
|
|
12690
|
+
if (!adapter.getActivity(current)) break;
|
|
12691
|
+
depth += 1;
|
|
12692
|
+
current = adapter.getParentId(current);
|
|
12118
12693
|
}
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
const
|
|
12123
|
-
|
|
12124
|
-
const
|
|
12125
|
-
const
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12694
|
+
return depth;
|
|
12695
|
+
}
|
|
12696
|
+
function rollupParent(parentId, adapter) {
|
|
12697
|
+
const childIds = adapter.getChildren(parentId);
|
|
12698
|
+
if (childIds.length === 0) return;
|
|
12699
|
+
const children = [];
|
|
12700
|
+
for (const childId of childIds) {
|
|
12701
|
+
const child = adapter.getActivity(childId);
|
|
12702
|
+
if (!child) continue;
|
|
12703
|
+
children.push({
|
|
12704
|
+
progress: Number(child.progress ?? 0),
|
|
12705
|
+
ponderator: Number(child.ponderator ?? 0)
|
|
12706
|
+
});
|
|
12707
|
+
}
|
|
12708
|
+
const rollup = computeWeightedProgressRollup(children);
|
|
12709
|
+
if (rollup !== null) {
|
|
12710
|
+
adapter.setActivityField(
|
|
12711
|
+
parentId,
|
|
12712
|
+
COLUMN.PROGRESS,
|
|
12713
|
+
roundProgressPerLevel(rollup)
|
|
12135
12714
|
);
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12715
|
+
}
|
|
12716
|
+
}
|
|
12717
|
+
|
|
12718
|
+
// src/dispatch/handlers/persistence-acknowledge.ts
|
|
12719
|
+
function persistedOwnersOf(entities) {
|
|
12720
|
+
const owners = /* @__PURE__ */ new Map();
|
|
12721
|
+
for (const entity of entities) {
|
|
12722
|
+
const proplannerId = entity.proplannerId;
|
|
12723
|
+
if (typeof proplannerId === "number" && Number.isSafeInteger(proplannerId) && proplannerId > 0) {
|
|
12724
|
+
owners.set(proplannerId, String(entity.id));
|
|
12143
12725
|
}
|
|
12144
|
-
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12726
|
+
}
|
|
12727
|
+
return owners;
|
|
12728
|
+
}
|
|
12729
|
+
function assertAllNewEntitiesAcknowledged(entities, assignments, entityName) {
|
|
12730
|
+
const acknowledgedIds = new Set(assignments.map(({ id }) => String(id)));
|
|
12731
|
+
for (const entity of entities) {
|
|
12732
|
+
if ((entity.proplannerId == null || entity.proplannerId === 0) && !acknowledgedIds.has(String(entity.id))) {
|
|
12733
|
+
throw new Error(
|
|
12734
|
+
`persistence-acknowledge: missing ${entityName} proplannerId for "${String(entity.id)}"`
|
|
12735
|
+
);
|
|
12736
|
+
}
|
|
12737
|
+
}
|
|
12738
|
+
}
|
|
12739
|
+
function assertValidAssignment(assignment, entityName, readCurrent, ownerByBackendId, seenIds, seenBackendIds) {
|
|
12740
|
+
const id = String(assignment.id);
|
|
12741
|
+
if (id.length === 0 || seenIds.has(id)) {
|
|
12742
|
+
throw new Error(
|
|
12743
|
+
`persistence-acknowledge: duplicate or empty ${entityName} id "${id}"`
|
|
12148
12744
|
);
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
edit.activityId,
|
|
12154
|
-
outcome.changes.postProcessors ?? [],
|
|
12155
|
-
adapter,
|
|
12156
|
-
preEditSnapshot
|
|
12745
|
+
}
|
|
12746
|
+
if (!Number.isSafeInteger(assignment.proplannerId) || assignment.proplannerId <= 0) {
|
|
12747
|
+
throw new Error(
|
|
12748
|
+
`persistence-acknowledge: invalid ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
12157
12749
|
);
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12750
|
+
}
|
|
12751
|
+
if (seenBackendIds.has(assignment.proplannerId)) {
|
|
12752
|
+
throw new Error(
|
|
12753
|
+
`persistence-acknowledge: duplicate ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
12161
12754
|
);
|
|
12162
|
-
needsAutoSchedule ||= Boolean(outcome.changes.needsAutoSchedule);
|
|
12163
|
-
for (const trackingEvent of outcome.changes.trackingEvents ?? []) {
|
|
12164
|
-
trackingEvents.push({
|
|
12165
|
-
name: trackingEvent.name,
|
|
12166
|
-
properties: trackingEvent.properties ?? {}
|
|
12167
|
-
});
|
|
12168
|
-
}
|
|
12169
12755
|
}
|
|
12170
|
-
const
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
options
|
|
12176
|
-
};
|
|
12177
|
-
const batchHasConstraintEdits = constraintPriorsByActivity.size > 0;
|
|
12178
|
-
const { scheduledIds } = await (batchHasConstraintEdits ? settleConstraintEdits(
|
|
12179
|
-
{
|
|
12180
|
-
adapter,
|
|
12181
|
-
scheduler,
|
|
12182
|
-
sector,
|
|
12183
|
-
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
12184
|
-
},
|
|
12185
|
-
{
|
|
12186
|
-
postMutationArgs,
|
|
12187
|
-
revertTargets: [...constraintPriorsByActivity].filter(
|
|
12188
|
-
([activityId]) => !explicitDateEditActivities.has(activityId)
|
|
12189
|
-
).map(([activityId, priorConstraintDate]) => ({
|
|
12190
|
-
activityId,
|
|
12191
|
-
priorConstraintDate
|
|
12192
|
-
})),
|
|
12193
|
-
revert: (activityId, priorConstraintDate) => revertNoOpConstraintEdit(
|
|
12194
|
-
activityId,
|
|
12195
|
-
priorConstraintDate,
|
|
12196
|
-
adapter,
|
|
12197
|
-
deps.ctx.calendars
|
|
12198
|
-
)
|
|
12199
|
-
}
|
|
12200
|
-
) : runPostMutation(
|
|
12201
|
-
{
|
|
12202
|
-
adapter,
|
|
12203
|
-
scheduler,
|
|
12204
|
-
sector,
|
|
12205
|
-
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
12206
|
-
},
|
|
12207
|
-
postMutationArgs
|
|
12208
|
-
));
|
|
12209
|
-
return {
|
|
12210
|
-
ok: true,
|
|
12211
|
-
verdicts,
|
|
12212
|
-
changes: await assembleChangeSet(adapter, {
|
|
12213
|
-
source: action,
|
|
12214
|
-
beforeSnap,
|
|
12215
|
-
touchedIds,
|
|
12216
|
-
scheduledIds,
|
|
12217
|
-
sirDetection: "before-diff",
|
|
12218
|
-
preview: options.preview,
|
|
12219
|
-
links: linkChanges,
|
|
12220
|
-
trackingEvents,
|
|
12221
|
-
warnings,
|
|
12222
|
-
hoursPerDay: sector.hoursPerDay
|
|
12223
|
-
})
|
|
12224
|
-
};
|
|
12225
|
-
}
|
|
12226
|
-
function applyOneEdit(edit, deps) {
|
|
12227
|
-
const { ctx } = deps;
|
|
12228
|
-
const activityId = edit.activityId;
|
|
12229
|
-
const pipeline = findPipelineForColumn(edit.column);
|
|
12230
|
-
if (!pipeline) {
|
|
12231
|
-
return {
|
|
12232
|
-
verdict: {
|
|
12233
|
-
activityId,
|
|
12234
|
-
ok: false,
|
|
12235
|
-
reason: REJECTION_REASON.NO_PIPELINE_FOR_COLUMN
|
|
12236
|
-
},
|
|
12237
|
-
changes: null
|
|
12238
|
-
};
|
|
12239
|
-
}
|
|
12240
|
-
const activitySnap = ctx.activityReader.getActivity(edit.activityId);
|
|
12241
|
-
if (!activitySnap) {
|
|
12242
|
-
return {
|
|
12243
|
-
verdict: {
|
|
12244
|
-
activityId,
|
|
12245
|
-
ok: false,
|
|
12246
|
-
reason: REJECTION_REASON.ACTIVITY_NOT_FOUND
|
|
12247
|
-
},
|
|
12248
|
-
changes: null
|
|
12249
|
-
};
|
|
12250
|
-
}
|
|
12251
|
-
const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy);
|
|
12252
|
-
if (!editGate.allowed) {
|
|
12253
|
-
return {
|
|
12254
|
-
verdict: {
|
|
12255
|
-
activityId,
|
|
12256
|
-
ok: false,
|
|
12257
|
-
reason: editGate.reason ?? REJECTION_REASON.CANNOT_EDIT,
|
|
12258
|
-
...editGate.alertKey ? { alertKey: editGate.alertKey } : {}
|
|
12259
|
-
},
|
|
12260
|
-
changes: null
|
|
12261
|
-
};
|
|
12262
|
-
}
|
|
12263
|
-
const parsed2 = pipeline.parseInput(edit.newValue, activitySnap, ctx);
|
|
12264
|
-
if (!parsed2.ok) {
|
|
12265
|
-
return {
|
|
12266
|
-
verdict: {
|
|
12267
|
-
activityId,
|
|
12268
|
-
ok: false,
|
|
12269
|
-
reason: parsed2.reason ?? REJECTION_REASON.PARSE_ERROR
|
|
12270
|
-
},
|
|
12271
|
-
changes: null
|
|
12272
|
-
};
|
|
12756
|
+
const state = readCurrent(id);
|
|
12757
|
+
if (!state.exists) {
|
|
12758
|
+
throw new Error(
|
|
12759
|
+
`persistence-acknowledge: unknown ${entityName} id "${id}"`
|
|
12760
|
+
);
|
|
12273
12761
|
}
|
|
12274
|
-
|
|
12275
|
-
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
parsed2.value,
|
|
12279
|
-
ctx
|
|
12280
|
-
);
|
|
12281
|
-
if (!validation.valid) {
|
|
12282
|
-
return {
|
|
12283
|
-
verdict: {
|
|
12284
|
-
activityId,
|
|
12285
|
-
ok: false,
|
|
12286
|
-
reason: validation.reason ?? REJECTION_REASON.INVALID,
|
|
12287
|
-
...validation.notification?.messageKey ? { alertKey: validation.notification.messageKey } : {}
|
|
12288
|
-
},
|
|
12289
|
-
changes: null
|
|
12290
|
-
};
|
|
12762
|
+
if (state.current !== null && state.current !== 0 && state.current !== assignment.proplannerId) {
|
|
12763
|
+
throw new Error(
|
|
12764
|
+
`persistence-acknowledge: ${entityName} "${id}" already has proplannerId ${String(state.current)}`
|
|
12765
|
+
);
|
|
12291
12766
|
}
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
|
|
12297
|
-
return {
|
|
12298
|
-
verdict: {
|
|
12299
|
-
activityId,
|
|
12300
|
-
ok: false,
|
|
12301
|
-
reason: REJECTION_REASON.CUSTOM_ID_DUPLICATE
|
|
12302
|
-
},
|
|
12303
|
-
changes: null
|
|
12304
|
-
};
|
|
12305
|
-
}
|
|
12767
|
+
const existingOwner = ownerByBackendId.get(assignment.proplannerId);
|
|
12768
|
+
if (existingOwner !== void 0 && existingOwner !== id) {
|
|
12769
|
+
throw new Error(
|
|
12770
|
+
`persistence-acknowledge: ${entityName} proplannerId ${String(assignment.proplannerId)} already belongs to "${existingOwner}"`
|
|
12771
|
+
);
|
|
12306
12772
|
}
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
parsed2.value,
|
|
12310
|
-
ctx,
|
|
12311
|
-
parsed2.raw
|
|
12312
|
-
);
|
|
12313
|
-
return { verdict: { activityId, ok: true }, changes };
|
|
12314
|
-
}
|
|
12315
|
-
function readLiveCustomId(edit, deps) {
|
|
12316
|
-
if (edit.column !== CUSTOM_ID_COLUMN) return null;
|
|
12317
|
-
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
12318
|
-
const rawValue = liveActivity ? liveActivity.customId : null;
|
|
12319
|
-
return typeof rawValue === "string" ? rawValue : null;
|
|
12773
|
+
seenIds.add(id);
|
|
12774
|
+
seenBackendIds.add(assignment.proplannerId);
|
|
12320
12775
|
}
|
|
12321
|
-
function
|
|
12322
|
-
|
|
12323
|
-
const
|
|
12324
|
-
const
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12776
|
+
function assertValidAssignments(assignments, entityName, readCurrent, ownerByBackendId) {
|
|
12777
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
12778
|
+
const seenBackendIds = /* @__PURE__ */ new Set();
|
|
12779
|
+
for (const assignment of assignments) {
|
|
12780
|
+
assertValidAssignment(
|
|
12781
|
+
assignment,
|
|
12782
|
+
entityName,
|
|
12783
|
+
readCurrent,
|
|
12784
|
+
ownerByBackendId,
|
|
12785
|
+
seenIds,
|
|
12786
|
+
seenBackendIds
|
|
12787
|
+
);
|
|
12328
12788
|
}
|
|
12329
12789
|
}
|
|
12330
|
-
function
|
|
12331
|
-
|
|
12332
|
-
const
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
12790
|
+
function assertValidReconciledProgress(entries, deps) {
|
|
12791
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12792
|
+
for (const entry of entries) {
|
|
12793
|
+
const id = String(entry.activityId);
|
|
12794
|
+
if (id.length === 0 || seen.has(id)) {
|
|
12795
|
+
throw new Error(
|
|
12796
|
+
`persistence-acknowledge: duplicate or empty reconciled activity id "${id}"`
|
|
12797
|
+
);
|
|
12798
|
+
}
|
|
12799
|
+
if (!Number.isFinite(entry.progress) || entry.progress < 0 || entry.progress > 100) {
|
|
12800
|
+
throw new Error(
|
|
12801
|
+
`persistence-acknowledge: invalid reconciled progress ${String(entry.progress)} for "${id}"`
|
|
12802
|
+
);
|
|
12803
|
+
}
|
|
12804
|
+
if (!deps.adapter.getActivity(id)) {
|
|
12805
|
+
throw new Error(
|
|
12806
|
+
`persistence-acknowledge: unknown reconciled activity id "${id}"`
|
|
12807
|
+
);
|
|
12348
12808
|
}
|
|
12809
|
+
seen.add(id);
|
|
12349
12810
|
}
|
|
12350
12811
|
}
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
const
|
|
12360
|
-
|
|
12361
|
-
|
|
12812
|
+
async function applyReconciledProgress(entries, deps) {
|
|
12813
|
+
const progressToApply = entries.filter((entry) => {
|
|
12814
|
+
const current = deps.adapter.getActivity(String(entry.activityId));
|
|
12815
|
+
return current !== null && current.progress !== entry.progress;
|
|
12816
|
+
});
|
|
12817
|
+
const progressIds = new Set(
|
|
12818
|
+
progressToApply.map((entry) => String(entry.activityId))
|
|
12819
|
+
);
|
|
12820
|
+
const ancestors = findAncestorsOfDirty(deps.adapter, progressIds);
|
|
12821
|
+
const touched = new Set(progressIds);
|
|
12822
|
+
for (const parentId of ancestors) {
|
|
12823
|
+
touched.add(String(parentId));
|
|
12362
12824
|
}
|
|
12363
|
-
const
|
|
12364
|
-
adapter.
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12825
|
+
const before = await snapshotActivities(deps.adapter, touched);
|
|
12826
|
+
deps.adapter.batchUpdate(() => {
|
|
12827
|
+
for (const entry of progressToApply) {
|
|
12828
|
+
deps.adapter.setActivityField(
|
|
12829
|
+
String(entry.activityId),
|
|
12830
|
+
"progress",
|
|
12831
|
+
entry.progress
|
|
12832
|
+
);
|
|
12833
|
+
}
|
|
12834
|
+
recomputeProgressRollupForParents(ancestors, deps.adapter);
|
|
12835
|
+
recomputeRealWorkFromProgress(progressIds, ancestors, deps.adapter);
|
|
12836
|
+
});
|
|
12837
|
+
return [...await buildActivityChanges(deps.adapter, before, touched)];
|
|
12838
|
+
}
|
|
12839
|
+
async function dispatchPersistenceAcknowledge(action, deps) {
|
|
12840
|
+
const activities = action.activities ?? [];
|
|
12841
|
+
const links = action.links ?? [];
|
|
12842
|
+
const reconciledProgress = action.reconciledProgress ?? [];
|
|
12843
|
+
assertValidReconciledProgress(reconciledProgress, deps);
|
|
12844
|
+
const currentActivities = deps.adapter.getAllActivities();
|
|
12845
|
+
const currentLinks = deps.adapter.getAllLinks();
|
|
12846
|
+
const activityOwnerByBackendId = persistedOwnersOf(currentActivities);
|
|
12847
|
+
const linkOwnerByBackendId = persistedOwnersOf(currentLinks);
|
|
12848
|
+
assertValidAssignments(
|
|
12849
|
+
activities,
|
|
12850
|
+
"activity",
|
|
12851
|
+
(id) => {
|
|
12852
|
+
const activity = deps.adapter.getActivity(id);
|
|
12853
|
+
return {
|
|
12854
|
+
exists: activity !== null,
|
|
12855
|
+
current: activity?.proplannerId ?? null
|
|
12856
|
+
};
|
|
12387
12857
|
},
|
|
12388
|
-
|
|
12858
|
+
activityOwnerByBackendId
|
|
12859
|
+
);
|
|
12860
|
+
assertValidAssignments(
|
|
12861
|
+
links,
|
|
12862
|
+
"link",
|
|
12863
|
+
(id) => {
|
|
12864
|
+
const link = deps.adapter.getLink(id);
|
|
12865
|
+
return { exists: link !== null, current: link?.proplannerId ?? null };
|
|
12866
|
+
},
|
|
12867
|
+
linkOwnerByBackendId
|
|
12868
|
+
);
|
|
12869
|
+
assertAllNewEntitiesAcknowledged(currentActivities, activities, "activity");
|
|
12870
|
+
assertAllNewEntitiesAcknowledged(currentLinks, links, "link");
|
|
12871
|
+
const activityChanges = await applyReconciledProgress(
|
|
12872
|
+
reconciledProgress,
|
|
12389
12873
|
deps
|
|
12390
12874
|
);
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
(
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12875
|
+
const linkChanges = [];
|
|
12876
|
+
deps.adapter.batchUpdate(() => {
|
|
12877
|
+
for (const assignment of activities) {
|
|
12878
|
+
const before = deps.adapter.getActivity(assignment.id);
|
|
12879
|
+
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
12880
|
+
const previousProplannerId = before.proplannerId;
|
|
12881
|
+
deps.adapter.setActivityField(
|
|
12882
|
+
assignment.id,
|
|
12883
|
+
"proplannerId",
|
|
12884
|
+
assignment.proplannerId
|
|
12885
|
+
);
|
|
12886
|
+
activityChanges.push({
|
|
12887
|
+
id: assignment.id,
|
|
12888
|
+
kind: "updated",
|
|
12889
|
+
fields: {
|
|
12890
|
+
proplannerId: {
|
|
12891
|
+
before: previousProplannerId,
|
|
12892
|
+
after: assignment.proplannerId
|
|
12893
|
+
}
|
|
12894
|
+
},
|
|
12895
|
+
after: { ...before, proplannerId: assignment.proplannerId }
|
|
12896
|
+
});
|
|
12897
|
+
}
|
|
12898
|
+
for (const assignment of links) {
|
|
12899
|
+
const before = deps.adapter.getLink(assignment.id);
|
|
12900
|
+
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
12901
|
+
const previousProplannerId = before.proplannerId;
|
|
12902
|
+
deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
|
|
12903
|
+
linkChanges.push({
|
|
12904
|
+
id: assignment.id,
|
|
12905
|
+
kind: "updated",
|
|
12906
|
+
fields: {
|
|
12907
|
+
proplannerId: {
|
|
12908
|
+
before: previousProplannerId,
|
|
12909
|
+
after: assignment.proplannerId
|
|
12910
|
+
}
|
|
12911
|
+
},
|
|
12912
|
+
after: { ...before, proplannerId: assignment.proplannerId }
|
|
12913
|
+
});
|
|
12914
|
+
}
|
|
12915
|
+
});
|
|
12916
|
+
return {
|
|
12917
|
+
ok: true,
|
|
12918
|
+
changes: {
|
|
12919
|
+
source: action,
|
|
12920
|
+
activities: activityChanges,
|
|
12921
|
+
links: linkChanges,
|
|
12922
|
+
calendars: [],
|
|
12923
|
+
trackingEvents: []
|
|
12924
|
+
}
|
|
12925
|
+
};
|
|
12399
12926
|
}
|
|
12400
12927
|
|
|
12401
12928
|
// src/dispatch/handlers/activity-lookahead-sync.ts
|
|
12402
|
-
function dispatchActivityLookaheadSync(action, deps) {
|
|
12929
|
+
async function dispatchActivityLookaheadSync(action, options, deps) {
|
|
12930
|
+
if (hasInvalidPersistedProgress(action)) {
|
|
12931
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
12932
|
+
}
|
|
12403
12933
|
const ids = [...new Set(action.activityIds.map(String))];
|
|
12934
|
+
const acknowledgesUnknownPersistedActivity = action.acknowledgePersistedLookahead === true && ids.some((activityId) => !deps.isActivityIdReserved(activityId));
|
|
12935
|
+
if (acknowledgesUnknownPersistedActivity) {
|
|
12936
|
+
return { ok: false, reason: REJECTION_REASON.INVALID };
|
|
12937
|
+
}
|
|
12404
12938
|
const snapshots = ids.map((id) => deps.adapter.getActivity(id));
|
|
12405
12939
|
if (snapshots.some((snapshot) => !snapshot)) {
|
|
12406
12940
|
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
@@ -12440,6 +12974,39 @@ function dispatchActivityLookaheadSync(action, deps) {
|
|
|
12440
12974
|
}
|
|
12441
12975
|
});
|
|
12442
12976
|
}
|
|
12977
|
+
if (action.persistedProgress !== void 0) {
|
|
12978
|
+
const progressChanges = await applyReconciledProgress(
|
|
12979
|
+
[{ activityId: ids[0], progress: action.persistedProgress }],
|
|
12980
|
+
deps
|
|
12981
|
+
);
|
|
12982
|
+
if (progressChanges.length > 0) {
|
|
12983
|
+
await runPostMutation(
|
|
12984
|
+
{
|
|
12985
|
+
adapter: deps.adapter,
|
|
12986
|
+
scheduler: deps.scheduler,
|
|
12987
|
+
sector: deps.sector,
|
|
12988
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
12989
|
+
},
|
|
12990
|
+
{
|
|
12991
|
+
action,
|
|
12992
|
+
autoscheduleFrom: null,
|
|
12993
|
+
recomputeParentsFrom: [],
|
|
12994
|
+
now: deps.now,
|
|
12995
|
+
options
|
|
12996
|
+
}
|
|
12997
|
+
);
|
|
12998
|
+
return {
|
|
12999
|
+
ok: true,
|
|
13000
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
13001
|
+
source: action,
|
|
13002
|
+
beforeSnap: /* @__PURE__ */ new Map(),
|
|
13003
|
+
touchedIds: [],
|
|
13004
|
+
scheduledIds: [],
|
|
13005
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
13006
|
+
})
|
|
13007
|
+
};
|
|
13008
|
+
}
|
|
13009
|
+
}
|
|
12443
13010
|
return {
|
|
12444
13011
|
ok: true,
|
|
12445
13012
|
changes: {
|
|
@@ -12451,6 +13018,10 @@ function dispatchActivityLookaheadSync(action, deps) {
|
|
|
12451
13018
|
}
|
|
12452
13019
|
};
|
|
12453
13020
|
}
|
|
13021
|
+
function hasInvalidPersistedProgress(action) {
|
|
13022
|
+
const progress = action.persistedProgress;
|
|
13023
|
+
return progress !== void 0 && (action.activityIds.length !== 1 || !Number.isFinite(progress) || progress < 0 || progress > 100);
|
|
13024
|
+
}
|
|
12454
13025
|
|
|
12455
13026
|
// src/internal/hierarchy/invalid-links.ts
|
|
12456
13027
|
function collectInvalidLinkIdsForCycle(movedActivityIds, adapter) {
|
|
@@ -12718,7 +13289,6 @@ async function dispatchActivityMove(action, options, deps) {
|
|
|
12718
13289
|
touchedIds: touched,
|
|
12719
13290
|
scheduledIds,
|
|
12720
13291
|
sirDetection: "before-diff",
|
|
12721
|
-
preview: options.preview,
|
|
12722
13292
|
trackingEvents: [trackingEvent],
|
|
12723
13293
|
hoursPerDay: sector.hoursPerDay
|
|
12724
13294
|
})
|
|
@@ -13216,44 +13786,205 @@ async function dispatchActivitySetProgress(action, options, deps) {
|
|
|
13216
13786
|
changes.postProcessors ?? [],
|
|
13217
13787
|
adapter
|
|
13218
13788
|
);
|
|
13219
|
-
const { scheduledIds } = await runPostMutation(
|
|
13220
|
-
{
|
|
13221
|
-
adapter,
|
|
13222
|
-
scheduler,
|
|
13223
|
-
sector: deps.sector,
|
|
13224
|
-
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
13225
|
-
},
|
|
13226
|
-
{
|
|
13227
|
-
action,
|
|
13228
|
-
autoscheduleFrom: changes.needsAutoSchedule ? action.activityId : null,
|
|
13229
|
-
recomputeParentsFrom: [],
|
|
13230
|
-
collectDirty: (sids) => collectDirtyForInlineEdit(action.activityId, touchedIds, sids),
|
|
13231
|
-
now: deps.now,
|
|
13232
|
-
options
|
|
13789
|
+
const { scheduledIds } = await runPostMutation(
|
|
13790
|
+
{
|
|
13791
|
+
adapter,
|
|
13792
|
+
scheduler,
|
|
13793
|
+
sector: deps.sector,
|
|
13794
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
13795
|
+
},
|
|
13796
|
+
{
|
|
13797
|
+
action,
|
|
13798
|
+
autoscheduleFrom: changes.needsAutoSchedule ? action.activityId : null,
|
|
13799
|
+
recomputeParentsFrom: [],
|
|
13800
|
+
collectDirty: (sids) => collectDirtyForInlineEdit(action.activityId, touchedIds, sids),
|
|
13801
|
+
now: deps.now,
|
|
13802
|
+
options
|
|
13803
|
+
}
|
|
13804
|
+
);
|
|
13805
|
+
const trackingEvents = (changes.trackingEvents ?? []).map((e) => ({
|
|
13806
|
+
name: e.name,
|
|
13807
|
+
properties: e.properties ?? {}
|
|
13808
|
+
}));
|
|
13809
|
+
return {
|
|
13810
|
+
ok: true,
|
|
13811
|
+
changes: await assembleChangeSet(adapter, {
|
|
13812
|
+
source: action,
|
|
13813
|
+
beforeSnap,
|
|
13814
|
+
touchedIds,
|
|
13815
|
+
scheduledIds,
|
|
13816
|
+
trackingEvents,
|
|
13817
|
+
hoursPerDay: sector.hoursPerDay
|
|
13818
|
+
})
|
|
13819
|
+
};
|
|
13820
|
+
}
|
|
13821
|
+
|
|
13822
|
+
// src/dispatch/handlers/dates-batch.ts
|
|
13823
|
+
async function dispatchDatesBatch(action, options, deps) {
|
|
13824
|
+
const { adapter, scheduler, sector } = deps;
|
|
13825
|
+
const verdicts = [];
|
|
13826
|
+
const beforeSnap = /* @__PURE__ */ new Map();
|
|
13827
|
+
const touchedIds = /* @__PURE__ */ new Set();
|
|
13828
|
+
const trackingEvents = [];
|
|
13829
|
+
const linkChanges = [];
|
|
13830
|
+
const warnings = [];
|
|
13831
|
+
let needsAutoSchedule = false;
|
|
13832
|
+
for (const edit of action.edits) {
|
|
13833
|
+
const outcome = applyOneEdit(edit, deps);
|
|
13834
|
+
verdicts.push(outcome.verdict);
|
|
13835
|
+
if (!outcome.verdict.ok || !outcome.changes) continue;
|
|
13836
|
+
const editTouched = collectTouchedIds(edit.activityId, outcome.changes);
|
|
13837
|
+
mergeBeforeSnapshots(beforeSnap, editTouched, deps);
|
|
13838
|
+
for (const id of editTouched) touchedIds.add(id);
|
|
13839
|
+
const beforeLinkLags = snapshotIncomingLinkLags(adapter, edit.activityId);
|
|
13840
|
+
const preEditSnapshot = snapshotSingleActivity(
|
|
13841
|
+
adapter,
|
|
13842
|
+
String(edit.activityId)
|
|
13843
|
+
);
|
|
13844
|
+
applyFieldChanges(adapter, edit.activityId, outcome.changes);
|
|
13845
|
+
runPostProcessorsOnAdapter(
|
|
13846
|
+
edit.activityId,
|
|
13847
|
+
outcome.changes.postProcessors ?? [],
|
|
13848
|
+
adapter,
|
|
13849
|
+
preEditSnapshot
|
|
13850
|
+
);
|
|
13851
|
+
linkChanges.push(
|
|
13852
|
+
...diffIncomingLinkLagChanges(beforeLinkLags, adapter, edit.activityId)
|
|
13853
|
+
);
|
|
13854
|
+
needsAutoSchedule ||= Boolean(outcome.changes.needsAutoSchedule);
|
|
13855
|
+
for (const e of outcome.changes.trackingEvents ?? []) {
|
|
13856
|
+
trackingEvents.push({
|
|
13857
|
+
name: e.name,
|
|
13858
|
+
properties: e.properties ?? {}
|
|
13859
|
+
});
|
|
13860
|
+
}
|
|
13861
|
+
const constraintWarning = toConstraintWarning(
|
|
13862
|
+
edit.activityId,
|
|
13863
|
+
outcome.changes.constraintWarning
|
|
13864
|
+
);
|
|
13865
|
+
if (constraintWarning) warnings.push(constraintWarning);
|
|
13866
|
+
}
|
|
13867
|
+
const { scheduledIds } = await runPostMutation(
|
|
13868
|
+
{
|
|
13869
|
+
adapter,
|
|
13870
|
+
scheduler,
|
|
13871
|
+
sector: deps.sector,
|
|
13872
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
13873
|
+
},
|
|
13874
|
+
{
|
|
13875
|
+
action,
|
|
13876
|
+
autoscheduleFrom: needsAutoSchedule ? "roots" : null,
|
|
13877
|
+
recomputeParentsFrom: touchedIds,
|
|
13878
|
+
now: deps.now,
|
|
13879
|
+
options
|
|
13880
|
+
}
|
|
13881
|
+
);
|
|
13882
|
+
return {
|
|
13883
|
+
ok: true,
|
|
13884
|
+
verdicts,
|
|
13885
|
+
changes: await assembleChangeSet(adapter, {
|
|
13886
|
+
source: action,
|
|
13887
|
+
beforeSnap,
|
|
13888
|
+
touchedIds,
|
|
13889
|
+
scheduledIds,
|
|
13890
|
+
sirDetection: "before-diff",
|
|
13891
|
+
links: linkChanges,
|
|
13892
|
+
trackingEvents,
|
|
13893
|
+
warnings,
|
|
13894
|
+
hoursPerDay: sector.hoursPerDay
|
|
13895
|
+
})
|
|
13896
|
+
};
|
|
13897
|
+
}
|
|
13898
|
+
function applyOneEdit(edit, deps) {
|
|
13899
|
+
const { ctx } = deps;
|
|
13900
|
+
const activityId = edit.activityId;
|
|
13901
|
+
const pipeline = findPipelineForColumn(edit.column);
|
|
13902
|
+
if (!pipeline) {
|
|
13903
|
+
throw new Error(
|
|
13904
|
+
`[ScheduleCore] no pipeline registered for column "${edit.column}"`
|
|
13905
|
+
);
|
|
13906
|
+
}
|
|
13907
|
+
const activitySnap = ctx.activityReader.getActivity(edit.activityId);
|
|
13908
|
+
if (!activitySnap) {
|
|
13909
|
+
throw new Error(`[ScheduleCore] activity "${edit.activityId}" not found`);
|
|
13910
|
+
}
|
|
13911
|
+
const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy);
|
|
13912
|
+
if (!editGate.allowed) {
|
|
13913
|
+
return {
|
|
13914
|
+
verdict: {
|
|
13915
|
+
activityId,
|
|
13916
|
+
ok: false,
|
|
13917
|
+
reason: editGate.reason ?? REJECTION_REASON.CANNOT_EDIT,
|
|
13918
|
+
...editGate.alertKey ? { alertKey: editGate.alertKey } : {}
|
|
13919
|
+
},
|
|
13920
|
+
changes: null
|
|
13921
|
+
};
|
|
13922
|
+
}
|
|
13923
|
+
const parsed2 = pipeline.parseInput(edit.newValue, activitySnap, ctx);
|
|
13924
|
+
if (!parsed2.ok) {
|
|
13925
|
+
return {
|
|
13926
|
+
verdict: {
|
|
13927
|
+
activityId,
|
|
13928
|
+
ok: false,
|
|
13929
|
+
reason: parsed2.reason ?? REJECTION_REASON.PARSE_ERROR
|
|
13930
|
+
},
|
|
13931
|
+
changes: null
|
|
13932
|
+
};
|
|
13933
|
+
}
|
|
13934
|
+
const oldValue = Reflect.get(activitySnap, pipeline.targetField);
|
|
13935
|
+
const validation = pipeline.validate(
|
|
13936
|
+
activitySnap,
|
|
13937
|
+
oldValue,
|
|
13938
|
+
parsed2.value,
|
|
13939
|
+
ctx
|
|
13940
|
+
);
|
|
13941
|
+
if (!validation.valid) {
|
|
13942
|
+
return {
|
|
13943
|
+
verdict: {
|
|
13944
|
+
activityId,
|
|
13945
|
+
ok: false,
|
|
13946
|
+
reason: validation.reason ?? REJECTION_REASON.INVALID,
|
|
13947
|
+
...validation.notification?.messageKey ? { alertKey: validation.notification.messageKey } : {}
|
|
13948
|
+
},
|
|
13949
|
+
changes: null
|
|
13950
|
+
};
|
|
13951
|
+
}
|
|
13952
|
+
const changes = pipeline.transform(
|
|
13953
|
+
activitySnap,
|
|
13954
|
+
parsed2.value,
|
|
13955
|
+
ctx,
|
|
13956
|
+
parsed2.raw
|
|
13957
|
+
);
|
|
13958
|
+
return { verdict: { activityId, ok: true }, changes };
|
|
13959
|
+
}
|
|
13960
|
+
function mergeBeforeSnapshots(beforeSnap, ids, deps) {
|
|
13961
|
+
const missing = /* @__PURE__ */ new Set();
|
|
13962
|
+
for (const id of ids) {
|
|
13963
|
+
if (!beforeSnap.has(id)) missing.add(id);
|
|
13964
|
+
}
|
|
13965
|
+
if (missing.size === 0) return;
|
|
13966
|
+
for (const missingId of missing) {
|
|
13967
|
+
const liveActivity = deps.adapter.getActivity(missingId);
|
|
13968
|
+
if (liveActivity) {
|
|
13969
|
+
beforeSnap.set(missingId, structuredCloneActivity(liveActivity));
|
|
13233
13970
|
}
|
|
13234
|
-
|
|
13235
|
-
const trackingEvents = (changes.trackingEvents ?? []).map((e) => ({
|
|
13236
|
-
name: e.name,
|
|
13237
|
-
properties: e.properties ?? {}
|
|
13238
|
-
}));
|
|
13239
|
-
return {
|
|
13240
|
-
ok: true,
|
|
13241
|
-
changes: await assembleChangeSet(adapter, {
|
|
13242
|
-
source: action,
|
|
13243
|
-
beforeSnap,
|
|
13244
|
-
touchedIds,
|
|
13245
|
-
scheduledIds,
|
|
13246
|
-
trackingEvents,
|
|
13247
|
-
hoursPerDay: sector.hoursPerDay
|
|
13248
|
-
})
|
|
13249
|
-
};
|
|
13971
|
+
}
|
|
13250
13972
|
}
|
|
13251
13973
|
|
|
13252
|
-
// src/dispatch/handlers/
|
|
13253
|
-
|
|
13974
|
+
// src/dispatch/handlers/bulk-edit.ts
|
|
13975
|
+
var CUSTOM_ID_COLUMN = COLUMN.CUSTOM_ID;
|
|
13976
|
+
async function dispatchBulkEdit(action, options, deps) {
|
|
13254
13977
|
const { adapter, scheduler, sector } = deps;
|
|
13978
|
+
const editsIncludeDuration = action.edits.some(
|
|
13979
|
+
(edit) => edit.column === COLUMN.DURATION
|
|
13980
|
+
);
|
|
13981
|
+
if (editsIncludeDuration) {
|
|
13982
|
+
scheduler.invalidateExpandedLinksCache();
|
|
13983
|
+
}
|
|
13255
13984
|
const verdicts = [];
|
|
13256
13985
|
const beforeSnap = /* @__PURE__ */ new Map();
|
|
13986
|
+
const constraintPriorsByActivity = /* @__PURE__ */ new Map();
|
|
13987
|
+
const explicitDateEditActivities = /* @__PURE__ */ new Set();
|
|
13257
13988
|
const touchedIds = /* @__PURE__ */ new Set();
|
|
13258
13989
|
const trackingEvents = [];
|
|
13259
13990
|
const linkChanges = [];
|
|
@@ -13263,52 +13994,83 @@ async function dispatchDatesBatch(action, options, deps) {
|
|
|
13263
13994
|
const outcome = applyOneEdit2(edit, deps);
|
|
13264
13995
|
verdicts.push(outcome.verdict);
|
|
13265
13996
|
if (!outcome.verdict.ok || !outcome.changes) continue;
|
|
13997
|
+
const constraintWarning = toConstraintWarning(
|
|
13998
|
+
edit.activityId,
|
|
13999
|
+
outcome.changes.constraintWarning
|
|
14000
|
+
);
|
|
14001
|
+
if (constraintWarning) warnings.push(constraintWarning);
|
|
13266
14002
|
const editTouched = collectTouchedIds(edit.activityId, outcome.changes);
|
|
13267
14003
|
mergeBeforeSnapshots2(beforeSnap, editTouched, deps);
|
|
13268
|
-
for (const
|
|
14004
|
+
for (const touchedId of editTouched) touchedIds.add(touchedId);
|
|
14005
|
+
recordConstraintPrior(constraintPriorsByActivity, edit, beforeSnap);
|
|
14006
|
+
if (isExplicitConstraintDateEdit(edit.column)) {
|
|
14007
|
+
explicitDateEditActivities.add(String(edit.activityId));
|
|
14008
|
+
}
|
|
13269
14009
|
const beforeLinkLags = snapshotIncomingLinkLags(adapter, edit.activityId);
|
|
13270
14010
|
const preEditSnapshot = snapshotSingleActivity(
|
|
13271
14011
|
adapter,
|
|
13272
14012
|
String(edit.activityId)
|
|
13273
14013
|
);
|
|
13274
|
-
|
|
14014
|
+
const customIdBeforeApply = readLiveCustomId(edit, deps);
|
|
14015
|
+
const changes = outcome.changes;
|
|
14016
|
+
applyFieldChanges(adapter, edit.activityId, changes);
|
|
13275
14017
|
runPostProcessorsOnAdapter(
|
|
13276
14018
|
edit.activityId,
|
|
13277
14019
|
outcome.changes.postProcessors ?? [],
|
|
13278
14020
|
adapter,
|
|
13279
14021
|
preEditSnapshot
|
|
13280
14022
|
);
|
|
14023
|
+
syncCustomIdTracker(edit, customIdBeforeApply, deps);
|
|
13281
14024
|
linkChanges.push(
|
|
13282
14025
|
...diffIncomingLinkLagChanges(beforeLinkLags, adapter, edit.activityId)
|
|
13283
14026
|
);
|
|
13284
14027
|
needsAutoSchedule ||= Boolean(outcome.changes.needsAutoSchedule);
|
|
13285
|
-
for (const
|
|
14028
|
+
for (const trackingEvent of outcome.changes.trackingEvents ?? []) {
|
|
13286
14029
|
trackingEvents.push({
|
|
13287
|
-
name:
|
|
13288
|
-
properties:
|
|
14030
|
+
name: trackingEvent.name,
|
|
14031
|
+
properties: trackingEvent.properties ?? {}
|
|
13289
14032
|
});
|
|
13290
14033
|
}
|
|
13291
|
-
const constraintWarning = toConstraintWarning(
|
|
13292
|
-
edit.activityId,
|
|
13293
|
-
outcome.changes.constraintWarning
|
|
13294
|
-
);
|
|
13295
|
-
if (constraintWarning) warnings.push(constraintWarning);
|
|
13296
14034
|
}
|
|
13297
|
-
const
|
|
14035
|
+
const postMutationArgs = {
|
|
14036
|
+
action,
|
|
14037
|
+
autoscheduleFrom: needsAutoSchedule ? "roots" : null,
|
|
14038
|
+
recomputeParentsFrom: touchedIds,
|
|
14039
|
+
now: deps.now,
|
|
14040
|
+
options
|
|
14041
|
+
};
|
|
14042
|
+
const batchHasConstraintEdits = constraintPriorsByActivity.size > 0;
|
|
14043
|
+
const { scheduledIds } = await (batchHasConstraintEdits ? settleConstraintEdits(
|
|
13298
14044
|
{
|
|
13299
14045
|
adapter,
|
|
13300
14046
|
scheduler,
|
|
13301
|
-
sector
|
|
14047
|
+
sector,
|
|
13302
14048
|
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
13303
14049
|
},
|
|
13304
14050
|
{
|
|
13305
|
-
|
|
13306
|
-
|
|
13307
|
-
|
|
13308
|
-
|
|
13309
|
-
|
|
14051
|
+
postMutationArgs,
|
|
14052
|
+
revertTargets: [...constraintPriorsByActivity].filter(
|
|
14053
|
+
([activityId]) => !explicitDateEditActivities.has(activityId)
|
|
14054
|
+
).map(([activityId, priorConstraintDate]) => ({
|
|
14055
|
+
activityId,
|
|
14056
|
+
priorConstraintDate
|
|
14057
|
+
})),
|
|
14058
|
+
revert: (activityId, priorConstraintDate) => revertNoOpConstraintEdit(
|
|
14059
|
+
activityId,
|
|
14060
|
+
priorConstraintDate,
|
|
14061
|
+
adapter,
|
|
14062
|
+
deps.ctx.calendars
|
|
14063
|
+
)
|
|
13310
14064
|
}
|
|
13311
|
-
)
|
|
14065
|
+
) : runPostMutation(
|
|
14066
|
+
{
|
|
14067
|
+
adapter,
|
|
14068
|
+
scheduler,
|
|
14069
|
+
sector,
|
|
14070
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
14071
|
+
},
|
|
14072
|
+
postMutationArgs
|
|
14073
|
+
));
|
|
13312
14074
|
return {
|
|
13313
14075
|
ok: true,
|
|
13314
14076
|
verdicts,
|
|
@@ -13318,7 +14080,6 @@ async function dispatchDatesBatch(action, options, deps) {
|
|
|
13318
14080
|
touchedIds,
|
|
13319
14081
|
scheduledIds,
|
|
13320
14082
|
sirDetection: "before-diff",
|
|
13321
|
-
preview: options.preview,
|
|
13322
14083
|
links: linkChanges,
|
|
13323
14084
|
trackingEvents,
|
|
13324
14085
|
warnings,
|
|
@@ -13331,13 +14092,25 @@ function applyOneEdit2(edit, deps) {
|
|
|
13331
14092
|
const activityId = edit.activityId;
|
|
13332
14093
|
const pipeline = findPipelineForColumn(edit.column);
|
|
13333
14094
|
if (!pipeline) {
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
14095
|
+
return {
|
|
14096
|
+
verdict: {
|
|
14097
|
+
activityId,
|
|
14098
|
+
ok: false,
|
|
14099
|
+
reason: REJECTION_REASON.NO_PIPELINE_FOR_COLUMN
|
|
14100
|
+
},
|
|
14101
|
+
changes: null
|
|
14102
|
+
};
|
|
13337
14103
|
}
|
|
13338
14104
|
const activitySnap = ctx.activityReader.getActivity(edit.activityId);
|
|
13339
14105
|
if (!activitySnap) {
|
|
13340
|
-
|
|
14106
|
+
return {
|
|
14107
|
+
verdict: {
|
|
14108
|
+
activityId,
|
|
14109
|
+
ok: false,
|
|
14110
|
+
reason: REJECTION_REASON.ACTIVITY_NOT_FOUND
|
|
14111
|
+
},
|
|
14112
|
+
changes: null
|
|
14113
|
+
};
|
|
13341
14114
|
}
|
|
13342
14115
|
const editGate = pipeline.canEdit(activitySnap, ctx.hierarchy);
|
|
13343
14116
|
if (!editGate.allowed) {
|
|
@@ -13380,6 +14153,21 @@ function applyOneEdit2(edit, deps) {
|
|
|
13380
14153
|
changes: null
|
|
13381
14154
|
};
|
|
13382
14155
|
}
|
|
14156
|
+
if (edit.column === CUSTOM_ID_COLUMN && parsed2.value !== null) {
|
|
14157
|
+
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
14158
|
+
const oldCustomIdRaw = liveActivity ? liveActivity.customId : null;
|
|
14159
|
+
const oldCustomId = typeof oldCustomIdRaw === "string" ? oldCustomIdRaw : null;
|
|
14160
|
+
if (deps.customIdTracker.isCustomIdInUse(String(parsed2.value), oldCustomId)) {
|
|
14161
|
+
return {
|
|
14162
|
+
verdict: {
|
|
14163
|
+
activityId,
|
|
14164
|
+
ok: false,
|
|
14165
|
+
reason: REJECTION_REASON.CUSTOM_ID_DUPLICATE
|
|
14166
|
+
},
|
|
14167
|
+
changes: null
|
|
14168
|
+
};
|
|
14169
|
+
}
|
|
14170
|
+
}
|
|
13383
14171
|
const changes = pipeline.transform(
|
|
13384
14172
|
activitySnap,
|
|
13385
14173
|
parsed2.value,
|
|
@@ -13388,226 +14176,41 @@ function applyOneEdit2(edit, deps) {
|
|
|
13388
14176
|
);
|
|
13389
14177
|
return { verdict: { activityId, ok: true }, changes };
|
|
13390
14178
|
}
|
|
13391
|
-
function
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
if (missing.size === 0) return;
|
|
13397
|
-
for (const missingId of missing) {
|
|
13398
|
-
const liveActivity = deps.adapter.getActivity(missingId);
|
|
13399
|
-
if (liveActivity) {
|
|
13400
|
-
beforeSnap.set(missingId, structuredCloneActivity(liveActivity));
|
|
13401
|
-
}
|
|
13402
|
-
}
|
|
13403
|
-
}
|
|
13404
|
-
|
|
13405
|
-
// src/dispatch/handlers/persistence-acknowledge.ts
|
|
13406
|
-
function assertValidAssignment(assignment, entityName, readCurrent, ownerByBackendId, seenIds, seenBackendIds) {
|
|
13407
|
-
const id = String(assignment.id);
|
|
13408
|
-
if (id.length === 0 || seenIds.has(id)) {
|
|
13409
|
-
throw new Error(
|
|
13410
|
-
`persistence-acknowledge: duplicate or empty ${entityName} id "${id}"`
|
|
13411
|
-
);
|
|
13412
|
-
}
|
|
13413
|
-
if (!Number.isSafeInteger(assignment.proplannerId) || assignment.proplannerId <= 0) {
|
|
13414
|
-
throw new Error(
|
|
13415
|
-
`persistence-acknowledge: invalid ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
13416
|
-
);
|
|
13417
|
-
}
|
|
13418
|
-
if (seenBackendIds.has(assignment.proplannerId)) {
|
|
13419
|
-
throw new Error(
|
|
13420
|
-
`persistence-acknowledge: duplicate ${entityName} proplannerId ${String(assignment.proplannerId)}`
|
|
13421
|
-
);
|
|
13422
|
-
}
|
|
13423
|
-
const state = readCurrent(id);
|
|
13424
|
-
if (!state.exists) {
|
|
13425
|
-
throw new Error(
|
|
13426
|
-
`persistence-acknowledge: unknown ${entityName} id "${id}"`
|
|
13427
|
-
);
|
|
13428
|
-
}
|
|
13429
|
-
if (state.current !== null && state.current !== assignment.proplannerId) {
|
|
13430
|
-
throw new Error(
|
|
13431
|
-
`persistence-acknowledge: ${entityName} "${id}" already has proplannerId ${String(state.current)}`
|
|
13432
|
-
);
|
|
13433
|
-
}
|
|
13434
|
-
const existingOwner = ownerByBackendId.get(assignment.proplannerId);
|
|
13435
|
-
if (existingOwner !== void 0 && existingOwner !== id) {
|
|
13436
|
-
throw new Error(
|
|
13437
|
-
`persistence-acknowledge: ${entityName} proplannerId ${String(assignment.proplannerId)} already belongs to "${existingOwner}"`
|
|
13438
|
-
);
|
|
13439
|
-
}
|
|
13440
|
-
seenIds.add(id);
|
|
13441
|
-
seenBackendIds.add(assignment.proplannerId);
|
|
13442
|
-
}
|
|
13443
|
-
function assertValidAssignments(assignments, entityName, readCurrent, ownerByBackendId) {
|
|
13444
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
13445
|
-
const seenBackendIds = /* @__PURE__ */ new Set();
|
|
13446
|
-
for (const assignment of assignments) {
|
|
13447
|
-
assertValidAssignment(
|
|
13448
|
-
assignment,
|
|
13449
|
-
entityName,
|
|
13450
|
-
readCurrent,
|
|
13451
|
-
ownerByBackendId,
|
|
13452
|
-
seenIds,
|
|
13453
|
-
seenBackendIds
|
|
13454
|
-
);
|
|
13455
|
-
}
|
|
14179
|
+
function readLiveCustomId(edit, deps) {
|
|
14180
|
+
if (edit.column !== CUSTOM_ID_COLUMN) return null;
|
|
14181
|
+
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
14182
|
+
const rawValue = liveActivity ? liveActivity.customId : null;
|
|
14183
|
+
return typeof rawValue === "string" ? rawValue : null;
|
|
13456
14184
|
}
|
|
13457
|
-
function
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
);
|
|
13465
|
-
}
|
|
13466
|
-
if (!Number.isFinite(entry.progress) || entry.progress < 0 || entry.progress > 100) {
|
|
13467
|
-
throw new Error(
|
|
13468
|
-
`persistence-acknowledge: invalid reconciled progress ${String(entry.progress)} for "${id}"`
|
|
13469
|
-
);
|
|
13470
|
-
}
|
|
13471
|
-
if (!deps.adapter.getActivity(id)) {
|
|
13472
|
-
throw new Error(
|
|
13473
|
-
`persistence-acknowledge: unknown reconciled activity id "${id}"`
|
|
13474
|
-
);
|
|
13475
|
-
}
|
|
13476
|
-
seen.add(id);
|
|
14185
|
+
function syncCustomIdTracker(edit, oldCustomId, deps) {
|
|
14186
|
+
if (edit.column !== CUSTOM_ID_COLUMN) return;
|
|
14187
|
+
const liveActivity = deps.adapter.getActivity(edit.activityId);
|
|
14188
|
+
const rawValue = liveActivity ? liveActivity.customId : null;
|
|
14189
|
+
const newCustomId = typeof rawValue === "string" ? rawValue : null;
|
|
14190
|
+
if (oldCustomId !== newCustomId) {
|
|
14191
|
+
deps.customIdTracker.trackCustomIdChange(oldCustomId, newCustomId);
|
|
13477
14192
|
}
|
|
13478
14193
|
}
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
const
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
for (const link of deps.adapter.getAllLinks()) {
|
|
13492
|
-
if (link.proplannerId != null) {
|
|
13493
|
-
linkOwnerByBackendId.set(link.proplannerId, String(link.id));
|
|
13494
|
-
}
|
|
14194
|
+
function recordConstraintPrior(priors, edit, beforeSnap) {
|
|
14195
|
+
if (!isConstraintEditColumn(edit.column)) return;
|
|
14196
|
+
const activityKey = String(edit.activityId);
|
|
14197
|
+
if (priors.has(activityKey)) return;
|
|
14198
|
+
const preBatchSnapshot = beforeSnap.get(activityKey);
|
|
14199
|
+
if (!preBatchSnapshot) return;
|
|
14200
|
+
priors.set(activityKey, preBatchSnapshot.constraintDate ?? null);
|
|
14201
|
+
}
|
|
14202
|
+
function mergeBeforeSnapshots2(beforeSnap, ids, deps) {
|
|
14203
|
+
const missing = /* @__PURE__ */ new Set();
|
|
14204
|
+
for (const candidateId of ids) {
|
|
14205
|
+
if (!beforeSnap.has(candidateId)) missing.add(candidateId);
|
|
13495
14206
|
}
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
(
|
|
13500
|
-
|
|
13501
|
-
return {
|
|
13502
|
-
exists: activity !== null,
|
|
13503
|
-
current: activity?.proplannerId ?? null
|
|
13504
|
-
};
|
|
13505
|
-
},
|
|
13506
|
-
activityOwnerByBackendId
|
|
13507
|
-
);
|
|
13508
|
-
assertValidAssignments(
|
|
13509
|
-
links,
|
|
13510
|
-
"link",
|
|
13511
|
-
(id) => {
|
|
13512
|
-
const link = deps.adapter.getLink(id);
|
|
13513
|
-
return { exists: link !== null, current: link?.proplannerId ?? null };
|
|
13514
|
-
},
|
|
13515
|
-
linkOwnerByBackendId
|
|
13516
|
-
);
|
|
13517
|
-
const activityChanges = [];
|
|
13518
|
-
const linkChanges = [];
|
|
13519
|
-
const progressToApply = reconciledProgress.filter((entry) => {
|
|
13520
|
-
const current = deps.adapter.getActivity(String(entry.activityId));
|
|
13521
|
-
return current !== null && current.progress !== entry.progress;
|
|
13522
|
-
});
|
|
13523
|
-
const touchedForProgress = new Set(
|
|
13524
|
-
progressToApply.map((entry) => String(entry.activityId))
|
|
13525
|
-
);
|
|
13526
|
-
const parentsToRollup = /* @__PURE__ */ new Set();
|
|
13527
|
-
for (const entry of progressToApply) {
|
|
13528
|
-
const activity = deps.adapter.getActivity(String(entry.activityId));
|
|
13529
|
-
if (activity && !isRootParent(activity.parentId)) {
|
|
13530
|
-
parentsToRollup.add(String(activity.parentId));
|
|
13531
|
-
touchedForProgress.add(String(activity.parentId));
|
|
14207
|
+
if (missing.size === 0) return;
|
|
14208
|
+
for (const missingId of missing) {
|
|
14209
|
+
const liveActivity = deps.adapter.getActivity(missingId);
|
|
14210
|
+
if (liveActivity) {
|
|
14211
|
+
beforeSnap.set(missingId, structuredCloneActivity(liveActivity));
|
|
13532
14212
|
}
|
|
13533
14213
|
}
|
|
13534
|
-
const beforeProgressSnap = await snapshotActivities(
|
|
13535
|
-
deps.adapter,
|
|
13536
|
-
touchedForProgress
|
|
13537
|
-
);
|
|
13538
|
-
deps.adapter.batchUpdate(() => {
|
|
13539
|
-
for (const entry of progressToApply) {
|
|
13540
|
-
deps.adapter.setActivityField(
|
|
13541
|
-
String(entry.activityId),
|
|
13542
|
-
"progress",
|
|
13543
|
-
entry.progress
|
|
13544
|
-
);
|
|
13545
|
-
}
|
|
13546
|
-
for (const parentId of parentsToRollup) {
|
|
13547
|
-
recomputeRollupCascadesForParent(parentId, deps.adapter);
|
|
13548
|
-
}
|
|
13549
|
-
for (const id of touchedForProgress) {
|
|
13550
|
-
const before = beforeProgressSnap.get(id);
|
|
13551
|
-
const after = deps.adapter.getActivity(id);
|
|
13552
|
-
if (!before || !after || before.progress === after.progress) continue;
|
|
13553
|
-
activityChanges.push({
|
|
13554
|
-
id,
|
|
13555
|
-
kind: "updated",
|
|
13556
|
-
fields: {
|
|
13557
|
-
progress: { before: before.progress, after: after.progress }
|
|
13558
|
-
},
|
|
13559
|
-
after: { ...after }
|
|
13560
|
-
});
|
|
13561
|
-
}
|
|
13562
|
-
for (const assignment of activities) {
|
|
13563
|
-
const before = deps.adapter.getActivity(assignment.id);
|
|
13564
|
-
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
13565
|
-
const previousProplannerId = before.proplannerId;
|
|
13566
|
-
deps.adapter.setActivityField(
|
|
13567
|
-
assignment.id,
|
|
13568
|
-
"proplannerId",
|
|
13569
|
-
assignment.proplannerId
|
|
13570
|
-
);
|
|
13571
|
-
activityChanges.push({
|
|
13572
|
-
id: assignment.id,
|
|
13573
|
-
kind: "updated",
|
|
13574
|
-
fields: {
|
|
13575
|
-
proplannerId: {
|
|
13576
|
-
before: previousProplannerId,
|
|
13577
|
-
after: assignment.proplannerId
|
|
13578
|
-
}
|
|
13579
|
-
},
|
|
13580
|
-
after: { ...before, proplannerId: assignment.proplannerId }
|
|
13581
|
-
});
|
|
13582
|
-
}
|
|
13583
|
-
for (const assignment of links) {
|
|
13584
|
-
const before = deps.adapter.getLink(assignment.id);
|
|
13585
|
-
if (!before || before.proplannerId === assignment.proplannerId) continue;
|
|
13586
|
-
const previousProplannerId = before.proplannerId;
|
|
13587
|
-
deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
|
|
13588
|
-
linkChanges.push({
|
|
13589
|
-
id: assignment.id,
|
|
13590
|
-
kind: "updated",
|
|
13591
|
-
fields: {
|
|
13592
|
-
proplannerId: {
|
|
13593
|
-
before: previousProplannerId,
|
|
13594
|
-
after: assignment.proplannerId
|
|
13595
|
-
}
|
|
13596
|
-
},
|
|
13597
|
-
after: { ...before, proplannerId: assignment.proplannerId }
|
|
13598
|
-
});
|
|
13599
|
-
}
|
|
13600
|
-
});
|
|
13601
|
-
return {
|
|
13602
|
-
ok: true,
|
|
13603
|
-
changes: {
|
|
13604
|
-
source: action,
|
|
13605
|
-
activities: activityChanges,
|
|
13606
|
-
links: linkChanges,
|
|
13607
|
-
calendars: [],
|
|
13608
|
-
trackingEvents: []
|
|
13609
|
-
}
|
|
13610
|
-
};
|
|
13611
14214
|
}
|
|
13612
14215
|
|
|
13613
14216
|
// src/baselines/normalize-baseline-point.ts
|
|
@@ -13805,55 +14408,6 @@ function recomputeAllPonderators(criterion, adapter) {
|
|
|
13805
14408
|
});
|
|
13806
14409
|
}
|
|
13807
14410
|
|
|
13808
|
-
// src/propagations/upward/progress-rollup.ts
|
|
13809
|
-
function recomputeAllProgressRollup(adapter) {
|
|
13810
|
-
for (const parentId of parentsDeepestFirst(adapter)) {
|
|
13811
|
-
rollupParent(parentId, adapter);
|
|
13812
|
-
}
|
|
13813
|
-
}
|
|
13814
|
-
function parentsDeepestFirst(adapter) {
|
|
13815
|
-
const parents = /* @__PURE__ */ new Set();
|
|
13816
|
-
adapter.forEachActivity((_activity, id) => {
|
|
13817
|
-
if (adapter.getChildren(id).length > 0) parents.add(id);
|
|
13818
|
-
});
|
|
13819
|
-
const depthById = /* @__PURE__ */ new Map();
|
|
13820
|
-
for (const id of parents) depthById.set(id, depthOf2(id, adapter));
|
|
13821
|
-
return [...parents].sort(
|
|
13822
|
-
(a, b) => (depthById.get(b) ?? 0) - (depthById.get(a) ?? 0)
|
|
13823
|
-
);
|
|
13824
|
-
}
|
|
13825
|
-
function depthOf2(id, adapter) {
|
|
13826
|
-
let depth = 0;
|
|
13827
|
-
let current = id;
|
|
13828
|
-
while (current && current !== "0") {
|
|
13829
|
-
if (!adapter.getActivity(current)) break;
|
|
13830
|
-
depth += 1;
|
|
13831
|
-
current = adapter.getParentId(current);
|
|
13832
|
-
}
|
|
13833
|
-
return depth;
|
|
13834
|
-
}
|
|
13835
|
-
function rollupParent(parentId, adapter) {
|
|
13836
|
-
const childIds = adapter.getChildren(parentId);
|
|
13837
|
-
if (childIds.length === 0) return;
|
|
13838
|
-
const children = [];
|
|
13839
|
-
for (const childId of childIds) {
|
|
13840
|
-
const child = adapter.getActivity(childId);
|
|
13841
|
-
if (!child) continue;
|
|
13842
|
-
children.push({
|
|
13843
|
-
progress: Number(child.progress ?? 0),
|
|
13844
|
-
ponderator: Number(child.ponderator ?? 0)
|
|
13845
|
-
});
|
|
13846
|
-
}
|
|
13847
|
-
const rollup = computeWeightedProgressRollup(children);
|
|
13848
|
-
if (rollup !== null) {
|
|
13849
|
-
adapter.setActivityField(
|
|
13850
|
-
parentId,
|
|
13851
|
-
COLUMN.PROGRESS,
|
|
13852
|
-
roundProgressPerLevel(rollup)
|
|
13853
|
-
);
|
|
13854
|
-
}
|
|
13855
|
-
}
|
|
13856
|
-
|
|
13857
14411
|
// src/dispatch/shared/baseline-weighted-fields.ts
|
|
13858
14412
|
function recomputeBaselineWeightedFields(deps, criterion) {
|
|
13859
14413
|
recomputeAllPonderators(criterion, deps.adapter);
|
|
@@ -14243,11 +14797,8 @@ async function performDispatch(action, options, deps) {
|
|
|
14243
14797
|
if (action.kind === "sir-sync") {
|
|
14244
14798
|
return dispatchSirSync(action, deps);
|
|
14245
14799
|
}
|
|
14246
|
-
if (action.kind === "sir-approve") {
|
|
14247
|
-
return dispatchSirApprove(action, options, deps);
|
|
14248
|
-
}
|
|
14249
14800
|
if (action.kind === "activity-lookahead-sync") {
|
|
14250
|
-
return dispatchActivityLookaheadSync(action, deps);
|
|
14801
|
+
return dispatchActivityLookaheadSync(action, options, deps);
|
|
14251
14802
|
}
|
|
14252
14803
|
if (action.kind === "inline-edit" && (action.column === COLUMN.CUSTOM_PREDECESSORS || action.column === COLUMN.CUSTOM_SUCESSORS)) {
|
|
14253
14804
|
return dispatchInlineEditLinks(action, options, deps);
|
|
@@ -17325,79 +17876,8 @@ var HierarchyIndex = class {
|
|
|
17325
17876
|
}
|
|
17326
17877
|
};
|
|
17327
17878
|
|
|
17328
|
-
// src/internal/state/view-state.ts
|
|
17329
|
-
var ViewStateStore = class {
|
|
17330
|
-
checkedSet = /* @__PURE__ */ new Set();
|
|
17331
|
-
hiddenSet = /* @__PURE__ */ new Set();
|
|
17332
|
-
activeFilter = null;
|
|
17333
|
-
activeOrder = null;
|
|
17334
|
-
isChecked(activityId) {
|
|
17335
|
-
return this.checkedSet.has(activityId);
|
|
17336
|
-
}
|
|
17337
|
-
isVisible(activityId) {
|
|
17338
|
-
return !this.hiddenSet.has(activityId);
|
|
17339
|
-
}
|
|
17340
|
-
hiddenIds() {
|
|
17341
|
-
return [...this.hiddenSet];
|
|
17342
|
-
}
|
|
17343
|
-
getActiveFilter() {
|
|
17344
|
-
return this.activeFilter;
|
|
17345
|
-
}
|
|
17346
|
-
setActiveFilter(nextFilter) {
|
|
17347
|
-
this.activeFilter = nextFilter;
|
|
17348
|
-
}
|
|
17349
|
-
getActiveOrder() {
|
|
17350
|
-
return this.activeOrder;
|
|
17351
|
-
}
|
|
17352
|
-
setActiveOrder(nextOrder) {
|
|
17353
|
-
this.activeOrder = nextOrder;
|
|
17354
|
-
}
|
|
17355
|
-
checkedIds() {
|
|
17356
|
-
return [...this.checkedSet];
|
|
17357
|
-
}
|
|
17358
|
-
checkedCount() {
|
|
17359
|
-
return this.checkedSet.size;
|
|
17360
|
-
}
|
|
17361
|
-
setChecked(activityId, nextChecked) {
|
|
17362
|
-
if (nextChecked === this.checkedSet.has(activityId)) return false;
|
|
17363
|
-
if (nextChecked) this.checkedSet.add(activityId);
|
|
17364
|
-
else this.checkedSet.delete(activityId);
|
|
17365
|
-
return true;
|
|
17366
|
-
}
|
|
17367
|
-
setVisible(activityId, nextVisible) {
|
|
17368
|
-
if (nextVisible === !this.hiddenSet.has(activityId)) return false;
|
|
17369
|
-
if (nextVisible) this.hiddenSet.delete(activityId);
|
|
17370
|
-
else this.hiddenSet.add(activityId);
|
|
17371
|
-
return true;
|
|
17372
|
-
}
|
|
17373
|
-
forget(activityId) {
|
|
17374
|
-
this.checkedSet.delete(activityId);
|
|
17375
|
-
this.hiddenSet.delete(activityId);
|
|
17376
|
-
}
|
|
17377
|
-
hydrate(seed2) {
|
|
17378
|
-
this.checkedSet = new Set(seed2.checkedIds ?? []);
|
|
17379
|
-
this.hiddenSet = new Set(seed2.hiddenIds ?? []);
|
|
17380
|
-
this.activeFilter = null;
|
|
17381
|
-
this.activeOrder = null;
|
|
17382
|
-
}
|
|
17383
|
-
snapshot() {
|
|
17384
|
-
return {
|
|
17385
|
-
checked: new Set(this.checkedSet),
|
|
17386
|
-
hidden: new Set(this.hiddenSet),
|
|
17387
|
-
activeFilter: this.activeFilter,
|
|
17388
|
-
activeOrder: this.activeOrder
|
|
17389
|
-
};
|
|
17390
|
-
}
|
|
17391
|
-
restore(snapshot) {
|
|
17392
|
-
this.checkedSet = new Set(snapshot.checked);
|
|
17393
|
-
this.hiddenSet = new Set(snapshot.hidden);
|
|
17394
|
-
this.activeFilter = snapshot.activeFilter;
|
|
17395
|
-
this.activeOrder = snapshot.activeOrder;
|
|
17396
|
-
}
|
|
17397
|
-
};
|
|
17398
|
-
|
|
17399
17879
|
// src/internal/state/schedule-state.ts
|
|
17400
|
-
var ScheduleState = class {
|
|
17880
|
+
var ScheduleState = class _ScheduleState {
|
|
17401
17881
|
_activities = /* @__PURE__ */ new Map();
|
|
17402
17882
|
_lastStartDate = /* @__PURE__ */ new Map();
|
|
17403
17883
|
_promotionSnapshot = /* @__PURE__ */ new Map();
|
|
@@ -17419,22 +17899,52 @@ var ScheduleState = class {
|
|
|
17419
17899
|
_promotionSnapshotBefore = null;
|
|
17420
17900
|
_calendar;
|
|
17421
17901
|
_flags;
|
|
17902
|
+
_calendarSnapshot;
|
|
17422
17903
|
constructor(snapshot, buildOrderComparator = () => null) {
|
|
17423
17904
|
this._buildOrderComparator = buildOrderComparator;
|
|
17905
|
+
this._calendarSnapshot = cloneDomainValue(snapshot.calendars);
|
|
17424
17906
|
for (const activity of snapshot.activities) {
|
|
17425
17907
|
this._activities.set(activity.id, activity);
|
|
17426
17908
|
}
|
|
17427
17909
|
for (const link of snapshot.links) {
|
|
17428
|
-
|
|
17429
|
-
|
|
17910
|
+
const normalized2 = {
|
|
17911
|
+
...link,
|
|
17912
|
+
freeSlackHours: link.freeSlackHours ?? null,
|
|
17913
|
+
isCritical: link.isCritical === true
|
|
17914
|
+
};
|
|
17915
|
+
this._links.set(String(link.id), normalized2);
|
|
17916
|
+
this._indexLink(normalized2);
|
|
17430
17917
|
}
|
|
17431
17918
|
this._calendar = new CalendarCalculator(
|
|
17432
|
-
createCalendarSetup(
|
|
17919
|
+
createCalendarSetup(this._calendarSnapshot)
|
|
17433
17920
|
);
|
|
17434
17921
|
this._flags = buildFlags(snapshot);
|
|
17435
17922
|
this._hierarchy = new HierarchyIndex(this._activities);
|
|
17436
17923
|
this._hierarchy.rebuild();
|
|
17437
17924
|
}
|
|
17925
|
+
fork() {
|
|
17926
|
+
const fork = new _ScheduleState(
|
|
17927
|
+
{
|
|
17928
|
+
activities: this.getAllActivities().map(cloneCoreActivity),
|
|
17929
|
+
links: this.getAllLinks().map((link) => ({ ...link })),
|
|
17930
|
+
calendars: cloneDomainValue(this._calendarSnapshot)
|
|
17931
|
+
},
|
|
17932
|
+
this._buildOrderComparator
|
|
17933
|
+
);
|
|
17934
|
+
fork._viewState.restore(cloneDomainValue(this._viewState.snapshot()));
|
|
17935
|
+
fork._lastStartDate = new Map(
|
|
17936
|
+
Array.from(this._lastStartDate, ([id, date2]) => [id, new Date(date2)])
|
|
17937
|
+
);
|
|
17938
|
+
fork._promotionSnapshot = new Map(
|
|
17939
|
+
Array.from(this._promotionSnapshot, ([id, snapshot]) => [
|
|
17940
|
+
id,
|
|
17941
|
+
cloneDomainValue(snapshot)
|
|
17942
|
+
])
|
|
17943
|
+
);
|
|
17944
|
+
fork._flags = cloneDomainValue(this._flags);
|
|
17945
|
+
fork._filterProjection = cloneDomainValue(this._filterProjection);
|
|
17946
|
+
return fork;
|
|
17947
|
+
}
|
|
17438
17948
|
getAllActivities() {
|
|
17439
17949
|
return Array.from(this._activities.values());
|
|
17440
17950
|
}
|
|
@@ -17544,6 +18054,18 @@ var ScheduleState = class {
|
|
|
17544
18054
|
this._viewState.setActiveOrder(nextOrder);
|
|
17545
18055
|
this.invalidateDerivedOrder();
|
|
17546
18056
|
}
|
|
18057
|
+
setRelativePlacementPin(activityId, pin) {
|
|
18058
|
+
this._viewState.setRelativePlacementPin(String(activityId), pin);
|
|
18059
|
+
this.invalidateVisualOrderIds();
|
|
18060
|
+
}
|
|
18061
|
+
getRelativePlacementPins() {
|
|
18062
|
+
return this._viewState.getRelativePlacementPins();
|
|
18063
|
+
}
|
|
18064
|
+
restoreRelativePlacementPins(pins) {
|
|
18065
|
+
this._viewState.restoreRelativePlacementPins(pins);
|
|
18066
|
+
this._filterProjection = null;
|
|
18067
|
+
this.invalidateVisualOrderIds();
|
|
18068
|
+
}
|
|
17547
18069
|
/**
|
|
17548
18070
|
* Rebuild-on-demand, memoized: at most one build per invalidation, never one
|
|
17549
18071
|
* per comparison. getChildrenInVisualOrder asks for this once and then sorts a
|
|
@@ -17692,9 +18214,14 @@ var ScheduleState = class {
|
|
|
17692
18214
|
addLink(link) {
|
|
17693
18215
|
this._writeCapture.noteLinkAdd(String(link.id));
|
|
17694
18216
|
const superseded = this._links.get(String(link.id));
|
|
17695
|
-
if (superseded) this._deindexLink(superseded);
|
|
17696
|
-
|
|
17697
|
-
|
|
18217
|
+
if (superseded) this._deindexLink(superseded);
|
|
18218
|
+
const normalized2 = {
|
|
18219
|
+
...link,
|
|
18220
|
+
freeSlackHours: link.freeSlackHours ?? null,
|
|
18221
|
+
isCritical: link.isCritical === true
|
|
18222
|
+
};
|
|
18223
|
+
this._links.set(String(link.id), normalized2);
|
|
18224
|
+
this._indexLink(normalized2);
|
|
17698
18225
|
}
|
|
17699
18226
|
_indexLink(link) {
|
|
17700
18227
|
pushLinkRef(this._outgoing, String(link.source), link.id);
|
|
@@ -17745,10 +18272,14 @@ var ScheduleState = class {
|
|
|
17745
18272
|
const activity = this.getActivity(activityId);
|
|
17746
18273
|
if (activity) this._writeCapture.noteRemove(String(activityId), activity);
|
|
17747
18274
|
const key = String(activityId);
|
|
17748
|
-
|
|
18275
|
+
const relativePlacementPins = this._viewState.getRelativePlacementPins();
|
|
18276
|
+
const hasDependentPin = [...relativePlacementPins.values()].some(
|
|
18277
|
+
({ referenceId }) => referenceId === key
|
|
18278
|
+
);
|
|
18279
|
+
if (this._viewState.isChecked(key) || !this._viewState.isVisible(key) || relativePlacementPins.has(key) || hasDependentPin) {
|
|
17749
18280
|
this._captureViewStateOnce();
|
|
17750
|
-
this._viewState.forget(key);
|
|
17751
18281
|
}
|
|
18282
|
+
this._viewState.forget(key);
|
|
17752
18283
|
this._activities.delete(activityId);
|
|
17753
18284
|
if (this._lastStartDate.has(String(activityId))) {
|
|
17754
18285
|
this._captureLastStartDateOnce();
|
|
@@ -17847,7 +18378,7 @@ var ScheduleState = class {
|
|
|
17847
18378
|
};
|
|
17848
18379
|
var EMPTY_LINK_IDS = [];
|
|
17849
18380
|
function isMutableLinkField(field) {
|
|
17850
|
-
return field === LINK_PROPERTY.LAG || field === LINK_PROPERTY.TYPE;
|
|
18381
|
+
return field === LINK_PROPERTY.LAG || field === LINK_PROPERTY.TYPE || field === LINK_PROPERTY.FREE_SLACK_HOURS || field === LINK_PROPERTY.IS_CRITICAL;
|
|
17851
18382
|
}
|
|
17852
18383
|
function fieldAffectsVisualOrder(field) {
|
|
17853
18384
|
return field === ACTIVITY_PROPERTY.PARENT || field === ACTIVITY_PROPERTY.CORRELATIVE_ID;
|
|
@@ -17968,12 +18499,7 @@ function buildPipelineContext(adapter, options) {
|
|
|
17968
18499
|
activityReader: createStateBackedActivityReader(adapter),
|
|
17969
18500
|
dateFormat: options.dateFormat ?? "YYYY-MM-DD",
|
|
17970
18501
|
customHours: options.customHours ?? {},
|
|
17971
|
-
hoursPerDay: options.hoursPerDay
|
|
17972
|
-
// ISSUE-057 (CP6): sin default. Un comando de duración sin inputUnit se
|
|
17973
|
-
// rechaza en el pipeline; el resto de columnas no lo leen. La clave se
|
|
17974
|
-
// OMITE cuando no se declaró (exactOptionalPropertyTypes), no se pone a
|
|
17975
|
-
// undefined.
|
|
17976
|
-
...options.inputUnit !== void 0 ? { inputUnit: options.inputUnit } : {}
|
|
18502
|
+
hoursPerDay: options.hoursPerDay
|
|
17977
18503
|
};
|
|
17978
18504
|
}
|
|
17979
18505
|
|
|
@@ -17983,15 +18509,26 @@ function willRunCriticalPath(action) {
|
|
|
17983
18509
|
}
|
|
17984
18510
|
|
|
17985
18511
|
// src/dispatch/undo/undo-entry.ts
|
|
17986
|
-
|
|
17987
|
-
|
|
18512
|
+
var DERIVED_LINK_FIELDS = /* @__PURE__ */ new Set(["freeSlackHours", "isCritical"]);
|
|
18513
|
+
function withoutDerivedFields(entry, derivedFields) {
|
|
18514
|
+
const fields = Object.fromEntries(
|
|
18515
|
+
Object.entries(entry.fields ?? {}).filter(
|
|
18516
|
+
([field]) => !derivedFields.has(field)
|
|
18517
|
+
)
|
|
18518
|
+
);
|
|
18519
|
+
if (entry.kind === "updated" && Object.keys(fields).length === 0) return null;
|
|
18520
|
+
return {
|
|
17988
18521
|
...entry,
|
|
18522
|
+
...entry.fields === void 0 ? {} : { fields },
|
|
17989
18523
|
after: null
|
|
17990
|
-
}
|
|
17991
|
-
|
|
18524
|
+
};
|
|
18525
|
+
}
|
|
18526
|
+
function buildHistoryChangeSet(changes) {
|
|
18527
|
+
const activitiesWithoutAfter = changes.activities.map((entry) => ({
|
|
17992
18528
|
...entry,
|
|
17993
18529
|
after: null
|
|
17994
18530
|
}));
|
|
18531
|
+
const linksWithoutAfter = changes.links.map((entry) => withoutDerivedFields(entry, DERIVED_LINK_FIELDS)).filter((entry) => entry !== null);
|
|
17995
18532
|
const source = changes.source.kind === "activity-batch" ? { ...changes.source, items: [], links: [] } : changes.source;
|
|
17996
18533
|
return cloneDomainValue({
|
|
17997
18534
|
...changes,
|
|
@@ -18000,8 +18537,25 @@ function buildHistoryChangeSet(changes) {
|
|
|
18000
18537
|
links: linksWithoutAfter
|
|
18001
18538
|
});
|
|
18002
18539
|
}
|
|
18003
|
-
function buildUndoEntry(changeSet, beforeSnap, beforeLinks, afterSnap, afterLinks) {
|
|
18004
|
-
return {
|
|
18540
|
+
function buildUndoEntry(changeSet, beforeSnap, beforeLinks, afterSnap, afterLinks, relativePlacementPinsBefore, relativePlacementPinsAfter) {
|
|
18541
|
+
return {
|
|
18542
|
+
changeSet,
|
|
18543
|
+
beforeSnap,
|
|
18544
|
+
beforeLinks: resetDerivedLinkSnapshots(beforeLinks),
|
|
18545
|
+
afterSnap,
|
|
18546
|
+
afterLinks: resetDerivedLinkSnapshots(afterLinks),
|
|
18547
|
+
relativePlacementPinsBefore,
|
|
18548
|
+
relativePlacementPinsAfter
|
|
18549
|
+
};
|
|
18550
|
+
}
|
|
18551
|
+
function resetDerivedLinkSnapshots(snapshots) {
|
|
18552
|
+
if (!snapshots) return void 0;
|
|
18553
|
+
return new Map(
|
|
18554
|
+
Array.from(snapshots, ([id, link]) => [
|
|
18555
|
+
id,
|
|
18556
|
+
{ ...link, freeSlackHours: null, isCritical: false }
|
|
18557
|
+
])
|
|
18558
|
+
);
|
|
18005
18559
|
}
|
|
18006
18560
|
function captureCreatedSnapshots(state, changeSet) {
|
|
18007
18561
|
const afterSnap = /* @__PURE__ */ new Map();
|
|
@@ -18053,8 +18607,7 @@ function mergeCoalesced(top, next) {
|
|
|
18053
18607
|
};
|
|
18054
18608
|
return buildUndoEntry(changeSet);
|
|
18055
18609
|
}
|
|
18056
|
-
function getDispatchHistoryPolicy(action
|
|
18057
|
-
if (options?.preview === true) return "skip";
|
|
18610
|
+
function getDispatchHistoryPolicy(action) {
|
|
18058
18611
|
if (action.kind === "persistence-acknowledge") return "clear-on-success";
|
|
18059
18612
|
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") {
|
|
18060
18613
|
return "skip";
|
|
@@ -18295,12 +18848,14 @@ function applyChangeSetSide(state, entry, side) {
|
|
|
18295
18848
|
);
|
|
18296
18849
|
}
|
|
18297
18850
|
}
|
|
18851
|
+
const pins = side === "before" ? entry.relativePlacementPinsBefore : entry.relativePlacementPinsAfter;
|
|
18852
|
+
if (pins !== void 0) state.restoreRelativePlacementPins(pins);
|
|
18298
18853
|
}
|
|
18299
18854
|
|
|
18300
18855
|
// src/dispatch/undo/undo-recorder.ts
|
|
18301
18856
|
var DEFAULT_MAX_DEPTH = 100;
|
|
18302
18857
|
var COALESCE_WINDOW_MS = 600;
|
|
18303
|
-
var UndoRecorder = class {
|
|
18858
|
+
var UndoRecorder = class _UndoRecorder {
|
|
18304
18859
|
constructor(maxDepth = DEFAULT_MAX_DEPTH) {
|
|
18305
18860
|
this.maxDepth = maxDepth;
|
|
18306
18861
|
}
|
|
@@ -18309,6 +18864,14 @@ var UndoRecorder = class {
|
|
|
18309
18864
|
redoStack = [];
|
|
18310
18865
|
_lastKey = null;
|
|
18311
18866
|
_lastTime = 0;
|
|
18867
|
+
fork() {
|
|
18868
|
+
const fork = new _UndoRecorder(this.maxDepth);
|
|
18869
|
+
fork.undoStack.push(...cloneDomainValue(this.undoStack));
|
|
18870
|
+
fork.redoStack.push(...cloneDomainValue(this.redoStack));
|
|
18871
|
+
fork._lastKey = this._lastKey;
|
|
18872
|
+
fork._lastTime = this._lastTime;
|
|
18873
|
+
return fork;
|
|
18874
|
+
}
|
|
18312
18875
|
record(entry, coalesceKey = null, now = 0) {
|
|
18313
18876
|
const top = this.undoStack[this.undoStack.length - 1];
|
|
18314
18877
|
if (coalesceKey != null && coalesceKey === this._lastKey && now - this._lastTime <= COALESCE_WINDOW_MS && top !== void 0 && canCoalesce(top, entry)) {
|
|
@@ -18432,40 +18995,56 @@ function buildStateSnapshot(activities, links, calendars, baseCalendars = []) {
|
|
|
18432
18995
|
}
|
|
18433
18996
|
|
|
18434
18997
|
// src/boundary/save/activity-changes.ts
|
|
18435
|
-
var
|
|
18436
|
-
|
|
18437
|
-
|
|
18438
|
-
|
|
18439
|
-
|
|
18440
|
-
|
|
18441
|
-
|
|
18442
|
-
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18446
|
-
|
|
18447
|
-
|
|
18448
|
-
|
|
18449
|
-
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
"
|
|
18465
|
-
"
|
|
18466
|
-
"
|
|
18467
|
-
"
|
|
18468
|
-
|
|
18998
|
+
var ACTIVITY_SAVE_FIELD_POLICY = {
|
|
18999
|
+
id: "ignored",
|
|
19000
|
+
proplannerId: "ignored",
|
|
19001
|
+
parentId: "tracked",
|
|
19002
|
+
name: "tracked",
|
|
19003
|
+
description: "tracked",
|
|
19004
|
+
type: "tracked",
|
|
19005
|
+
startDate: "tracked",
|
|
19006
|
+
endDate: "tracked",
|
|
19007
|
+
durationHours: "tracked",
|
|
19008
|
+
constraintType: "tracked",
|
|
19009
|
+
constraintDate: "tracked",
|
|
19010
|
+
autoScheduling: "tracked",
|
|
19011
|
+
calendarId: "tracked",
|
|
19012
|
+
assignedDefaultCalendar: "ignored",
|
|
19013
|
+
dateOrigin: "tracked",
|
|
19014
|
+
progress: "tracked",
|
|
19015
|
+
cost: "tracked",
|
|
19016
|
+
usedCost: "tracked",
|
|
19017
|
+
realCost: "tracked",
|
|
19018
|
+
workHours: "tracked",
|
|
19019
|
+
realWorkHours: "tracked",
|
|
19020
|
+
ponderator: "tracked",
|
|
19021
|
+
hasCustomPonderator: "tracked",
|
|
19022
|
+
correlativeId: "tracked",
|
|
19023
|
+
uniqueCorrelativeId: "tracked",
|
|
19024
|
+
customId: "tracked",
|
|
19025
|
+
subcontractId: "tracked",
|
|
19026
|
+
isLookahead: "tracked",
|
|
19027
|
+
hasLookaheadTasks: "ignored",
|
|
19028
|
+
newActivityIds: "tracked",
|
|
19029
|
+
hasNewActivities: "tracked",
|
|
19030
|
+
isNewActivity: "ignored",
|
|
19031
|
+
pendingRequestIds: "ignored",
|
|
19032
|
+
responsableIds: "tracked",
|
|
19033
|
+
tagIds: "tracked",
|
|
19034
|
+
baselinePoints: "ignored",
|
|
19035
|
+
baselineSnapshot: "ignored",
|
|
19036
|
+
recursiveDurationHours: "ignored",
|
|
19037
|
+
isCritical: "ignored",
|
|
19038
|
+
freeSlackHours: "ignored",
|
|
19039
|
+
calendarDuration: "ignored",
|
|
19040
|
+
expectedProgress: "ignored",
|
|
19041
|
+
expectedProgressBaseline: "ignored",
|
|
19042
|
+
status: "ignored",
|
|
19043
|
+
criticalPath: "ignored"
|
|
19044
|
+
};
|
|
19045
|
+
var PERSISTABLE_ACTIVITY_FIELDS = Object.keys(ACTIVITY_SAVE_FIELD_POLICY).filter((field) => {
|
|
19046
|
+
return ACTIVITY_SAVE_FIELD_POLICY[field] === "tracked";
|
|
19047
|
+
});
|
|
18469
19048
|
var DATE_FIELDS2 = /* @__PURE__ */ new Set([
|
|
18470
19049
|
"startDate",
|
|
18471
19050
|
"endDate",
|
|
@@ -18493,7 +19072,6 @@ function snapshotFieldValue(field, value) {
|
|
|
18493
19072
|
return value;
|
|
18494
19073
|
}
|
|
18495
19074
|
function activityChangedSince(current, saved) {
|
|
18496
|
-
if (current.assignedDefaultCalendar) return true;
|
|
18497
19075
|
for (const field of PERSISTABLE_ACTIVITY_FIELDS) {
|
|
18498
19076
|
if (!fieldEquals(field, current[field], saved[field])) return true;
|
|
18499
19077
|
}
|
|
@@ -18511,15 +19089,35 @@ function dateEquals(current, saved) {
|
|
|
18511
19089
|
return currentTime === savedTime;
|
|
18512
19090
|
}
|
|
18513
19091
|
function idArrayEquals(current, saved) {
|
|
18514
|
-
const currentIds = Array.isArray(current) ?
|
|
18515
|
-
const savedIds = Array.isArray(saved) ?
|
|
18516
|
-
if (currentIds.
|
|
18517
|
-
|
|
19092
|
+
const currentIds = new Set(Array.isArray(current) ? current.map(String) : []);
|
|
19093
|
+
const savedIds = new Set(Array.isArray(saved) ? saved.map(String) : []);
|
|
19094
|
+
if (currentIds.size !== savedIds.size) return false;
|
|
19095
|
+
for (const currentId of currentIds) {
|
|
19096
|
+
if (!savedIds.has(currentId)) return false;
|
|
19097
|
+
}
|
|
19098
|
+
return true;
|
|
18518
19099
|
}
|
|
18519
19100
|
|
|
18520
19101
|
// src/boundary/save/link-changes.ts
|
|
19102
|
+
var LINK_SAVE_FIELD_POLICY = {
|
|
19103
|
+
id: "ignored",
|
|
19104
|
+
source: "tracked",
|
|
19105
|
+
target: "tracked",
|
|
19106
|
+
type: "tracked",
|
|
19107
|
+
lag: "tracked",
|
|
19108
|
+
freeSlackHours: "ignored",
|
|
19109
|
+
isCritical: "ignored",
|
|
19110
|
+
ganttId: "ignored",
|
|
19111
|
+
sectorId: "ignored",
|
|
19112
|
+
proplannerId: "ignored"
|
|
19113
|
+
};
|
|
19114
|
+
var TRACKED_LINK_FIELDS = Object.keys(LINK_SAVE_FIELD_POLICY).filter((field) => {
|
|
19115
|
+
return LINK_SAVE_FIELD_POLICY[field] === "tracked";
|
|
19116
|
+
});
|
|
18521
19117
|
function linkChangedSince(baseline, current) {
|
|
18522
|
-
return
|
|
19118
|
+
return TRACKED_LINK_FIELDS.some(
|
|
19119
|
+
(field) => baseline[field] !== current[field]
|
|
19120
|
+
);
|
|
18523
19121
|
}
|
|
18524
19122
|
function checkNoUpdatedLinks(baseline, current) {
|
|
18525
19123
|
const baselineById = new Map(
|
|
@@ -18533,23 +19131,73 @@ function checkNoUpdatedLinks(baseline, current) {
|
|
|
18533
19131
|
}
|
|
18534
19132
|
|
|
18535
19133
|
// src/boundary/save/save-tracker.ts
|
|
18536
|
-
|
|
19134
|
+
function hasPersistedIdentity(proplannerId) {
|
|
19135
|
+
const isNewIdentity = proplannerId == null || proplannerId === 0;
|
|
19136
|
+
if (isNewIdentity) return false;
|
|
19137
|
+
const isValidPersistedIdentity = typeof proplannerId === "number" && Number.isSafeInteger(proplannerId) && proplannerId > 0;
|
|
19138
|
+
if (!isValidPersistedIdentity) {
|
|
19139
|
+
throw new Error(`invalid proplannerId ${String(proplannerId)}`);
|
|
19140
|
+
}
|
|
19141
|
+
return true;
|
|
19142
|
+
}
|
|
19143
|
+
function assertValidPersistedIdentities(activities, links) {
|
|
19144
|
+
for (const activity of activities) {
|
|
19145
|
+
hasPersistedIdentity(activity.proplannerId);
|
|
19146
|
+
}
|
|
19147
|
+
for (const link of links) {
|
|
19148
|
+
hasPersistedIdentity(link.proplannerId);
|
|
19149
|
+
}
|
|
19150
|
+
}
|
|
19151
|
+
var SaveTracker = class _SaveTracker {
|
|
19152
|
+
trackingEnabled;
|
|
18537
19153
|
initialized = false;
|
|
18538
19154
|
dirtyActivityIds = /* @__PURE__ */ new Set();
|
|
18539
19155
|
dirtyLinkIds = /* @__PURE__ */ new Set();
|
|
19156
|
+
initialCalendarRepairIds = /* @__PURE__ */ new Set();
|
|
18540
19157
|
linksAtLastSave = /* @__PURE__ */ new Map();
|
|
18541
19158
|
activitiesAtLastSave = /* @__PURE__ */ new Map();
|
|
19159
|
+
constructor(trackingEnabled = true) {
|
|
19160
|
+
this.trackingEnabled = trackingEnabled;
|
|
19161
|
+
}
|
|
19162
|
+
fork() {
|
|
19163
|
+
const fork = new _SaveTracker(false);
|
|
19164
|
+
fork.initialized = this.initialized;
|
|
19165
|
+
for (const [linkId, link] of this.linksAtLastSave) {
|
|
19166
|
+
fork.linksAtLastSave.set(linkId, cloneDomainValue(link));
|
|
19167
|
+
}
|
|
19168
|
+
for (const [activityId, activity] of this.activitiesAtLastSave) {
|
|
19169
|
+
fork.activitiesAtLastSave.set(activityId, cloneDomainValue(activity));
|
|
19170
|
+
}
|
|
19171
|
+
return fork;
|
|
19172
|
+
}
|
|
18542
19173
|
init(activities, links) {
|
|
19174
|
+
if (!this.trackingEnabled) return;
|
|
19175
|
+
assertValidPersistedIdentities(activities, links);
|
|
18543
19176
|
this.snapshotActivities(activities);
|
|
18544
19177
|
this.snapshotLinks(links);
|
|
19178
|
+
for (const activity of activities) {
|
|
19179
|
+
if (activity.assignedDefaultCalendar) {
|
|
19180
|
+
const activityId = String(activity.id);
|
|
19181
|
+
this.initialCalendarRepairIds.add(activityId);
|
|
19182
|
+
this.dirtyActivityIds.add(activityId);
|
|
19183
|
+
}
|
|
19184
|
+
}
|
|
18545
19185
|
this.initialized = true;
|
|
18546
19186
|
}
|
|
19187
|
+
acknowledge(activities, links) {
|
|
19188
|
+
if (!this.trackingEnabled) return;
|
|
19189
|
+
assertValidPersistedIdentities(activities, links);
|
|
19190
|
+
this.snapshotActivities(activities);
|
|
19191
|
+
this.snapshotLinks(links);
|
|
19192
|
+
}
|
|
18547
19193
|
snapshotActivities(activities) {
|
|
19194
|
+
if (!this.trackingEnabled) return;
|
|
18548
19195
|
this.activitiesAtLastSave.clear();
|
|
18549
19196
|
this.dirtyActivityIds.clear();
|
|
19197
|
+
this.initialCalendarRepairIds.clear();
|
|
18550
19198
|
for (const activity of activities) {
|
|
18551
19199
|
const activityId = String(activity.id);
|
|
18552
|
-
if (activity.proplannerId
|
|
19200
|
+
if (!hasPersistedIdentity(activity.proplannerId)) {
|
|
18553
19201
|
this.dirtyActivityIds.add(activityId);
|
|
18554
19202
|
continue;
|
|
18555
19203
|
}
|
|
@@ -18569,11 +19217,12 @@ var SaveTracker = class {
|
|
|
18569
19217
|
}
|
|
18570
19218
|
}
|
|
18571
19219
|
snapshotLinks(links) {
|
|
19220
|
+
if (!this.trackingEnabled) return;
|
|
18572
19221
|
this.linksAtLastSave.clear();
|
|
18573
19222
|
this.dirtyLinkIds.clear();
|
|
18574
19223
|
for (const link of links) {
|
|
18575
19224
|
const linkId = String(link.id);
|
|
18576
|
-
if (link.proplannerId
|
|
19225
|
+
if (!hasPersistedIdentity(link.proplannerId)) {
|
|
18577
19226
|
this.dirtyLinkIds.add(linkId);
|
|
18578
19227
|
continue;
|
|
18579
19228
|
}
|
|
@@ -18588,12 +19237,11 @@ var SaveTracker = class {
|
|
|
18588
19237
|
}
|
|
18589
19238
|
}
|
|
18590
19239
|
recordChanges(activities, links, getActivity, getLink) {
|
|
18591
|
-
if (!this.initialized) return;
|
|
19240
|
+
if (!this.trackingEnabled || !this.initialized) return;
|
|
18592
19241
|
for (const change of activities) {
|
|
18593
19242
|
const activityId = String(change.id);
|
|
18594
19243
|
const current = getActivity(activityId);
|
|
18595
|
-
|
|
18596
|
-
if (current === null ? saved !== void 0 : current.proplannerId == null || saved === void 0 || activityChangedSince(current, saved.persistable)) {
|
|
19244
|
+
if (this.activityIsDirty(activityId, current)) {
|
|
18597
19245
|
this.dirtyActivityIds.add(activityId);
|
|
18598
19246
|
} else {
|
|
18599
19247
|
this.dirtyActivityIds.delete(activityId);
|
|
@@ -18602,81 +19250,147 @@ var SaveTracker = class {
|
|
|
18602
19250
|
for (const change of links) {
|
|
18603
19251
|
const linkId = String(change.id);
|
|
18604
19252
|
const current = getLink(linkId);
|
|
18605
|
-
|
|
18606
|
-
if (current === null ? saved !== void 0 : current.proplannerId == null || saved === void 0 || linkChangedSince(saved, current)) {
|
|
19253
|
+
if (this.linkIsDirty(linkId, current)) {
|
|
18607
19254
|
this.dirtyLinkIds.add(linkId);
|
|
18608
19255
|
} else {
|
|
18609
19256
|
this.dirtyLinkIds.delete(linkId);
|
|
18610
19257
|
}
|
|
18611
19258
|
}
|
|
18612
19259
|
}
|
|
18613
|
-
|
|
18614
|
-
|
|
18615
|
-
|
|
19260
|
+
activityIsDirty(activityId, current) {
|
|
19261
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19262
|
+
if (current === null) return saved !== void 0;
|
|
19263
|
+
return this.initialCalendarRepairIds.has(activityId) || !hasPersistedIdentity(current.proplannerId) || saved === void 0 || activityChangedSince(current, saved.persistable);
|
|
19264
|
+
}
|
|
19265
|
+
linkIsDirty(linkId, current) {
|
|
19266
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19267
|
+
if (current === null) return saved !== void 0;
|
|
19268
|
+
return !hasPersistedIdentity(current.proplannerId) || saved === void 0 || linkChangedSince(saved, current);
|
|
19269
|
+
}
|
|
19270
|
+
acknowledgePersistedLookahead(changes, getActivity) {
|
|
19271
|
+
if (!this.trackingEnabled || !this.initialized) return;
|
|
19272
|
+
const acknowledgements = changes.map((change) => {
|
|
19273
|
+
const activityId = typeof change === "string" ? change : String(change.id);
|
|
18616
19274
|
const current = getActivity(activityId);
|
|
18617
19275
|
const saved = this.activitiesAtLastSave.get(activityId);
|
|
18618
|
-
|
|
19276
|
+
const isUnknownPersistedActivity = current === null || saved === void 0;
|
|
19277
|
+
if (isUnknownPersistedActivity) {
|
|
19278
|
+
throw new Error(`unknown persisted activity "${activityId}"`);
|
|
19279
|
+
}
|
|
19280
|
+
return {
|
|
19281
|
+
activityId,
|
|
19282
|
+
acknowledgesLookahead: typeof change === "string" || Boolean(change.fields?.isLookahead),
|
|
19283
|
+
acknowledgesProgress: typeof change !== "string" && Boolean(change.fields?.progress),
|
|
19284
|
+
current,
|
|
19285
|
+
saved
|
|
19286
|
+
};
|
|
19287
|
+
});
|
|
19288
|
+
for (const {
|
|
19289
|
+
activityId,
|
|
19290
|
+
acknowledgesLookahead,
|
|
19291
|
+
acknowledgesProgress,
|
|
19292
|
+
current,
|
|
19293
|
+
saved
|
|
19294
|
+
} of acknowledgements) {
|
|
18619
19295
|
const persistable = {
|
|
18620
19296
|
...saved.persistable,
|
|
18621
|
-
isLookahead: current.isLookahead
|
|
19297
|
+
...acknowledgesLookahead ? { isLookahead: current.isLookahead } : {},
|
|
19298
|
+
...acknowledgesProgress ? { progress: current.progress } : {}
|
|
18622
19299
|
};
|
|
18623
19300
|
this.activitiesAtLastSave.set(activityId, {
|
|
18624
19301
|
...saved,
|
|
18625
19302
|
persistable
|
|
18626
19303
|
});
|
|
18627
|
-
if (current.proplannerId
|
|
19304
|
+
if (this.initialCalendarRepairIds.has(activityId) || !hasPersistedIdentity(current.proplannerId) || activityChangedSince(current, persistable)) {
|
|
18628
19305
|
this.dirtyActivityIds.add(activityId);
|
|
18629
19306
|
} else {
|
|
18630
19307
|
this.dirtyActivityIds.delete(activityId);
|
|
18631
19308
|
}
|
|
18632
19309
|
}
|
|
18633
19310
|
}
|
|
19311
|
+
hasCheckpointActivityId(activityId) {
|
|
19312
|
+
return this.activitiesAtLastSave.has(activityId);
|
|
19313
|
+
}
|
|
19314
|
+
hasCheckpointLinkId(linkId) {
|
|
19315
|
+
return this.linksAtLastSave.has(linkId);
|
|
19316
|
+
}
|
|
18634
19317
|
hasUnsavedChanges() {
|
|
18635
|
-
return this.initialized && (this.dirtyActivityIds.size > 0 || this.dirtyLinkIds.size > 0);
|
|
19318
|
+
return this.trackingEnabled && this.initialized && (this.dirtyActivityIds.size > 0 || this.dirtyLinkIds.size > 0);
|
|
19319
|
+
}
|
|
19320
|
+
newActivities(getActivity) {
|
|
19321
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19322
|
+
const activities = [];
|
|
19323
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19324
|
+
const activity = getActivity(activityId);
|
|
19325
|
+
const isNewActivity = activity !== null && !hasPersistedIdentity(activity.proplannerId);
|
|
19326
|
+
if (isNewActivity) activities.push(activity);
|
|
19327
|
+
}
|
|
19328
|
+
return activities;
|
|
19329
|
+
}
|
|
19330
|
+
newLinks(getLink) {
|
|
19331
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19332
|
+
const links = [];
|
|
19333
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19334
|
+
const link = getLink(linkId);
|
|
19335
|
+
const isNewLink = link !== null && !hasPersistedIdentity(link.proplannerId);
|
|
19336
|
+
if (isNewLink) links.push(link);
|
|
19337
|
+
}
|
|
19338
|
+
return links;
|
|
19339
|
+
}
|
|
19340
|
+
deletedActivities(getActivity) {
|
|
19341
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19342
|
+
const deleted = [];
|
|
19343
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19344
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19345
|
+
if (saved && getActivity(activityId) === null) {
|
|
19346
|
+
deleted.push({ ...saved.deletedSnapshot });
|
|
19347
|
+
}
|
|
19348
|
+
}
|
|
19349
|
+
return deleted;
|
|
18636
19350
|
}
|
|
18637
|
-
|
|
18638
|
-
if (!this.initialized) return [];
|
|
18639
|
-
const currentIds = new Set(
|
|
18640
|
-
activities.map((activity) => String(activity.id))
|
|
18641
|
-
);
|
|
19351
|
+
deletedLinks(getLink) {
|
|
19352
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
18642
19353
|
const deleted = [];
|
|
18643
|
-
for (const
|
|
18644
|
-
|
|
18645
|
-
deleted.push({ ...saved
|
|
19354
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19355
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19356
|
+
if (saved && getLink(linkId) === null) deleted.push({ ...saved });
|
|
18646
19357
|
}
|
|
18647
19358
|
return deleted;
|
|
18648
19359
|
}
|
|
18649
|
-
|
|
18650
|
-
if (!this.initialized) return [];
|
|
18651
|
-
const
|
|
18652
|
-
|
|
18653
|
-
|
|
18654
|
-
(
|
|
18655
|
-
|
|
18656
|
-
|
|
18657
|
-
|
|
18658
|
-
|
|
18659
|
-
|
|
18660
|
-
|
|
18661
|
-
|
|
18662
|
-
|
|
18663
|
-
if (!saved) return true;
|
|
18664
|
-
return activityChangedSince(activity, saved.persistable);
|
|
18665
|
-
});
|
|
19360
|
+
modifiedActivities(getActivity) {
|
|
19361
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19362
|
+
const modified = [];
|
|
19363
|
+
for (const activityId of this.dirtyActivityIds) {
|
|
19364
|
+
const activity = getActivity(activityId);
|
|
19365
|
+
if (activity === null || !hasPersistedIdentity(activity.proplannerId)) {
|
|
19366
|
+
continue;
|
|
19367
|
+
}
|
|
19368
|
+
const saved = this.activitiesAtLastSave.get(activityId);
|
|
19369
|
+
if (saved === void 0 || this.initialCalendarRepairIds.has(activityId) || activityChangedSince(activity, saved.persistable)) {
|
|
19370
|
+
modified.push(activity);
|
|
19371
|
+
}
|
|
19372
|
+
}
|
|
19373
|
+
return modified;
|
|
18666
19374
|
}
|
|
18667
|
-
modifiedLinks(
|
|
18668
|
-
if (!this.initialized) return [];
|
|
18669
|
-
|
|
18670
|
-
|
|
18671
|
-
const
|
|
18672
|
-
if (
|
|
18673
|
-
|
|
18674
|
-
|
|
19375
|
+
modifiedLinks(getLink) {
|
|
19376
|
+
if (!this.trackingEnabled || !this.initialized) return [];
|
|
19377
|
+
const modified = [];
|
|
19378
|
+
for (const linkId of this.dirtyLinkIds) {
|
|
19379
|
+
const link = getLink(linkId);
|
|
19380
|
+
if (link === null) continue;
|
|
19381
|
+
if (!hasPersistedIdentity(link.proplannerId)) continue;
|
|
19382
|
+
const saved = this.linksAtLastSave.get(linkId);
|
|
19383
|
+
if (saved === void 0 || linkChangedSince(saved, link)) {
|
|
19384
|
+
modified.push(link);
|
|
19385
|
+
}
|
|
19386
|
+
}
|
|
19387
|
+
return modified;
|
|
18675
19388
|
}
|
|
18676
19389
|
clear() {
|
|
18677
19390
|
this.initialized = false;
|
|
18678
19391
|
this.dirtyActivityIds.clear();
|
|
18679
19392
|
this.dirtyLinkIds.clear();
|
|
19393
|
+
this.initialCalendarRepairIds.clear();
|
|
18680
19394
|
this.linksAtLastSave.clear();
|
|
18681
19395
|
this.activitiesAtLastSave.clear();
|
|
18682
19396
|
}
|
|
@@ -18730,14 +19444,14 @@ function isAutoSchedulable(progress) {
|
|
|
18730
19444
|
}
|
|
18731
19445
|
function parseConstraintLabel(label, reporter) {
|
|
18732
19446
|
if (!label) return null;
|
|
18733
|
-
const
|
|
18734
|
-
if (
|
|
19447
|
+
const normalized2 = CONSTRAINT_LABEL_MAP[label.toUpperCase()];
|
|
19448
|
+
if (normalized2 === void 0) {
|
|
18735
19449
|
reporter.warn(
|
|
18736
19450
|
`[parseFromBackend] unknown constraint label "${label}" \u2014 defaulting to "asap"`
|
|
18737
19451
|
);
|
|
18738
19452
|
return DEFAULT_CONSTRAINT_TYPE;
|
|
18739
19453
|
}
|
|
18740
|
-
return
|
|
19454
|
+
return normalized2;
|
|
18741
19455
|
}
|
|
18742
19456
|
function resolveConstraint(raw, reporter) {
|
|
18743
19457
|
const constraintType = parseConstraintLabel(raw.constraint, reporter);
|
|
@@ -18778,6 +19492,24 @@ function parseIdList(raw) {
|
|
|
18778
19492
|
function parseParentId(rawParentId) {
|
|
18779
19493
|
return rawParentId === ROOT_PARENT_ID ? null : String(rawParentId);
|
|
18780
19494
|
}
|
|
19495
|
+
function normalizePersistedActivityId(rawId) {
|
|
19496
|
+
if (rawId == null) return null;
|
|
19497
|
+
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
19498
|
+
if (!isValidIdentity) {
|
|
19499
|
+
throw new Error(`invalid activity proplannerId ${String(rawId)}`);
|
|
19500
|
+
}
|
|
19501
|
+
return rawId;
|
|
19502
|
+
}
|
|
19503
|
+
function parseCriticality(raw, reporter) {
|
|
19504
|
+
if (typeof raw.is_critical === "boolean") return raw.is_critical;
|
|
19505
|
+
reporter.error(
|
|
19506
|
+
"[parseFromBackend] invalid activity is_critical \u2014 defaulting to false",
|
|
19507
|
+
new TypeError(
|
|
19508
|
+
`activity "${raw.unique_id}" received ${typeof raw.is_critical}`
|
|
19509
|
+
)
|
|
19510
|
+
);
|
|
19511
|
+
return false;
|
|
19512
|
+
}
|
|
18781
19513
|
function parseBaselinePoints(raw, reporter) {
|
|
18782
19514
|
if (!Array.isArray(raw)) return [];
|
|
18783
19515
|
return raw.map((point) => normalizeBaselinePoint(point, reporter)).filter((point) => point !== null);
|
|
@@ -18798,9 +19530,10 @@ function parseActivity(raw, context) {
|
|
|
18798
19530
|
const startDate = parseBackendDate(raw.start_date);
|
|
18799
19531
|
const endDate = isMilestone ? startDate : parseBackendDate(raw.end_date);
|
|
18800
19532
|
const durationDays = isMilestone ? 0 : Number(raw.duration);
|
|
19533
|
+
const isCritical = parseCriticality(raw, context.reporter);
|
|
18801
19534
|
return {
|
|
18802
19535
|
id: String(raw.unique_id),
|
|
18803
|
-
proplannerId:
|
|
19536
|
+
proplannerId: normalizePersistedActivityId(raw.id),
|
|
18804
19537
|
parentId: parseParentId(raw.parent_id),
|
|
18805
19538
|
name: raw.name,
|
|
18806
19539
|
description: raw.description ?? "",
|
|
@@ -18845,7 +19578,7 @@ function parseActivity(raw, context) {
|
|
|
18845
19578
|
startDate,
|
|
18846
19579
|
endDate
|
|
18847
19580
|
}),
|
|
18848
|
-
isCritical
|
|
19581
|
+
isCritical,
|
|
18849
19582
|
freeSlackHours: null,
|
|
18850
19583
|
expectedProgress: null,
|
|
18851
19584
|
expectedProgressBaseline: null,
|
|
@@ -19102,6 +19835,14 @@ function resolveLinkType(rawType) {
|
|
|
19102
19835
|
if (isLinkTypeCode(rawType)) return rawType;
|
|
19103
19836
|
return null;
|
|
19104
19837
|
}
|
|
19838
|
+
function normalizePersistedLinkId(rawId) {
|
|
19839
|
+
if (rawId == null) return void 0;
|
|
19840
|
+
const isValidIdentity = typeof rawId === "number" && (rawId === 0 || Number.isSafeInteger(rawId) && rawId > 0);
|
|
19841
|
+
if (!isValidIdentity) {
|
|
19842
|
+
throw new Error(`invalid link proplannerId ${String(rawId)}`);
|
|
19843
|
+
}
|
|
19844
|
+
return rawId;
|
|
19845
|
+
}
|
|
19105
19846
|
function normalizeLinks(rawLinks, context) {
|
|
19106
19847
|
const parsedLinks = [];
|
|
19107
19848
|
for (const rawLink of rawLinks) {
|
|
@@ -19140,16 +19881,18 @@ function parseLink(link, activityIds, hoursPerDay) {
|
|
|
19140
19881
|
}
|
|
19141
19882
|
const ganttIdRaw = link.ganttId;
|
|
19142
19883
|
const sectorIdRaw = link.sectorId;
|
|
19143
|
-
const
|
|
19884
|
+
const proplannerId = normalizePersistedLinkId(link.id);
|
|
19144
19885
|
return {
|
|
19145
19886
|
id,
|
|
19146
19887
|
source,
|
|
19147
19888
|
target,
|
|
19148
19889
|
type,
|
|
19149
19890
|
lag: lagDaysToHours(Number(link.lag ?? 0), hoursPerDay),
|
|
19891
|
+
freeSlackHours: null,
|
|
19892
|
+
isCritical: false,
|
|
19150
19893
|
...typeof ganttIdRaw === "number" ? { ganttId: ganttIdRaw } : {},
|
|
19151
19894
|
...typeof sectorIdRaw === "number" ? { sectorId: sectorIdRaw } : {},
|
|
19152
|
-
...
|
|
19895
|
+
...proplannerId === void 0 ? {} : { proplannerId }
|
|
19153
19896
|
};
|
|
19154
19897
|
}
|
|
19155
19898
|
function dedupeLinksBySignature(links) {
|
|
@@ -19276,6 +20019,9 @@ function createActivityIdGenerator(options = {}) {
|
|
|
19276
20019
|
}
|
|
19277
20020
|
}
|
|
19278
20021
|
return {
|
|
20022
|
+
fork() {
|
|
20023
|
+
return createActivityIdGenerator({ seed: counter });
|
|
20024
|
+
},
|
|
19279
20025
|
next() {
|
|
19280
20026
|
counter += 1;
|
|
19281
20027
|
return String(counter);
|
|
@@ -19307,6 +20053,9 @@ function createLinkIdGenerator(options = {}) {
|
|
|
19307
20053
|
}
|
|
19308
20054
|
}
|
|
19309
20055
|
return {
|
|
20056
|
+
fork() {
|
|
20057
|
+
return createLinkIdGenerator({ seed: counter });
|
|
20058
|
+
},
|
|
19310
20059
|
next() {
|
|
19311
20060
|
counter += 1;
|
|
19312
20061
|
return String(counter);
|
|
@@ -19342,6 +20091,9 @@ function createUniqueCorrelativeIdGenerator(options = {}) {
|
|
|
19342
20091
|
}
|
|
19343
20092
|
}
|
|
19344
20093
|
return {
|
|
20094
|
+
fork() {
|
|
20095
|
+
return createUniqueCorrelativeIdGenerator({ seed: counter });
|
|
20096
|
+
},
|
|
19345
20097
|
next() {
|
|
19346
20098
|
counter += 1;
|
|
19347
20099
|
return String(counter);
|
|
@@ -19469,7 +20221,7 @@ function selectGenerationStrategy(customId, useActivityCustomIdAsReference) {
|
|
|
19469
20221
|
function cloneStats(stats) {
|
|
19470
20222
|
return { ...stats, usedSuffixes: new Set(stats.usedSuffixes) };
|
|
19471
20223
|
}
|
|
19472
|
-
var CustomIdTracker = class {
|
|
20224
|
+
var CustomIdTracker = class _CustomIdTracker {
|
|
19473
20225
|
prefixMap = /* @__PURE__ */ new Map();
|
|
19474
20226
|
processedIds = /* @__PURE__ */ new Set();
|
|
19475
20227
|
_journalPrefixBefore = null;
|
|
@@ -19530,6 +20282,19 @@ var CustomIdTracker = class {
|
|
|
19530
20282
|
this.defaultPrefix = options.defaultPrefix ?? DEFAULT_PREFIX;
|
|
19531
20283
|
this.suffixIncrement = options.suffixIncrement ?? DEFAULT_SUFFIX_INCREMENT;
|
|
19532
20284
|
}
|
|
20285
|
+
fork() {
|
|
20286
|
+
const fork = new _CustomIdTracker({
|
|
20287
|
+
defaultPrefix: this.defaultPrefix,
|
|
20288
|
+
suffixIncrement: this.suffixIncrement
|
|
20289
|
+
});
|
|
20290
|
+
for (const [prefix, stats] of this.prefixMap) {
|
|
20291
|
+
fork.prefixMap.set(prefix, cloneStats(stats));
|
|
20292
|
+
}
|
|
20293
|
+
for (const customId of this.processedIds) {
|
|
20294
|
+
fork.processedIds.add(customId);
|
|
20295
|
+
}
|
|
20296
|
+
return fork;
|
|
20297
|
+
}
|
|
19533
20298
|
extractPrefix(customId) {
|
|
19534
20299
|
return extractPrefix(customId);
|
|
19535
20300
|
}
|
|
@@ -20136,6 +20901,133 @@ function initializeCore(input) {
|
|
|
20136
20901
|
};
|
|
20137
20902
|
}
|
|
20138
20903
|
|
|
20904
|
+
// src/previsualization/canonical-digest.ts
|
|
20905
|
+
function normalized(value) {
|
|
20906
|
+
if (value === void 0) return ["undefined"];
|
|
20907
|
+
if (value === null) return ["null"];
|
|
20908
|
+
if (typeof value === "string") return ["string", value];
|
|
20909
|
+
if (typeof value === "boolean") return ["boolean", value];
|
|
20910
|
+
if (typeof value === "number") {
|
|
20911
|
+
return ["number", Object.is(value, -0) ? "-0" : String(value)];
|
|
20912
|
+
}
|
|
20913
|
+
if (typeof value === "bigint") return ["bigint", String(value)];
|
|
20914
|
+
if (value instanceof Date) return ["date", String(value.getTime())];
|
|
20915
|
+
if (Array.isArray(value)) return ["array", value.map(normalized)];
|
|
20916
|
+
if (value instanceof Map) {
|
|
20917
|
+
return [
|
|
20918
|
+
"map",
|
|
20919
|
+
Array.from(value, ([key, entry]) => [
|
|
20920
|
+
normalized(key),
|
|
20921
|
+
normalized(entry)
|
|
20922
|
+
]).sort(
|
|
20923
|
+
(left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))
|
|
20924
|
+
)
|
|
20925
|
+
];
|
|
20926
|
+
}
|
|
20927
|
+
if (value instanceof Set) {
|
|
20928
|
+
return [
|
|
20929
|
+
"set",
|
|
20930
|
+
Array.from(value, normalized).sort(
|
|
20931
|
+
(left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))
|
|
20932
|
+
)
|
|
20933
|
+
];
|
|
20934
|
+
}
|
|
20935
|
+
if (typeof value === "object") {
|
|
20936
|
+
return [
|
|
20937
|
+
"object",
|
|
20938
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalized(entry)])
|
|
20939
|
+
];
|
|
20940
|
+
}
|
|
20941
|
+
throw new TypeError(`Unsupported canonical value: ${typeof value}`);
|
|
20942
|
+
}
|
|
20943
|
+
function serializeCanonicalSchedule(value) {
|
|
20944
|
+
return JSON.stringify(normalized(value));
|
|
20945
|
+
}
|
|
20946
|
+
|
|
20947
|
+
// src/previsualization/reconciliation.ts
|
|
20948
|
+
function entityChanges(target, source) {
|
|
20949
|
+
const targetById = new Map(
|
|
20950
|
+
target.map((entity) => [String(entity.id), entity])
|
|
20951
|
+
);
|
|
20952
|
+
const sourceById = new Map(
|
|
20953
|
+
source.map((entity) => [String(entity.id), entity])
|
|
20954
|
+
);
|
|
20955
|
+
const ids = [.../* @__PURE__ */ new Set([...targetById.keys(), ...sourceById.keys()])].sort();
|
|
20956
|
+
const changes = [];
|
|
20957
|
+
for (const id of ids) {
|
|
20958
|
+
const before = sourceById.get(id);
|
|
20959
|
+
const after = targetById.get(id);
|
|
20960
|
+
if (after === void 0) {
|
|
20961
|
+
changes.push({ id, kind: "deleted", after: null });
|
|
20962
|
+
continue;
|
|
20963
|
+
}
|
|
20964
|
+
if (before === void 0) {
|
|
20965
|
+
changes.push({ id, kind: "created", after: cloneDomainValue(after) });
|
|
20966
|
+
continue;
|
|
20967
|
+
}
|
|
20968
|
+
const fields = changedFields(before, after);
|
|
20969
|
+
if (Object.keys(fields).length > 0) {
|
|
20970
|
+
changes.push({
|
|
20971
|
+
id,
|
|
20972
|
+
kind: "updated",
|
|
20973
|
+
fields,
|
|
20974
|
+
after: cloneDomainValue(after)
|
|
20975
|
+
});
|
|
20976
|
+
}
|
|
20977
|
+
}
|
|
20978
|
+
return changes;
|
|
20979
|
+
}
|
|
20980
|
+
function changedFields(before, after) {
|
|
20981
|
+
const fields = {};
|
|
20982
|
+
const keys = [
|
|
20983
|
+
.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])
|
|
20984
|
+
].sort();
|
|
20985
|
+
for (const key of keys) {
|
|
20986
|
+
if (serializeCanonicalSchedule(before[key]) !== serializeCanonicalSchedule(after[key])) {
|
|
20987
|
+
fields[key] = {
|
|
20988
|
+
before: cloneDomainValue(before[key]),
|
|
20989
|
+
after: cloneDomainValue(after[key])
|
|
20990
|
+
};
|
|
20991
|
+
}
|
|
20992
|
+
}
|
|
20993
|
+
return fields;
|
|
20994
|
+
}
|
|
20995
|
+
function viewStateChanges(target, source) {
|
|
20996
|
+
const selectedBefore = new Set(source.selectedIds.map(String));
|
|
20997
|
+
const selectedAfter = new Set(target.selectedIds.map(String));
|
|
20998
|
+
const hiddenBefore = new Set(source.hiddenIds.map(String));
|
|
20999
|
+
const hiddenAfter = new Set(target.hiddenIds.map(String));
|
|
21000
|
+
const targetIds = target.activities.map(({ id }) => String(id)).sort();
|
|
21001
|
+
const changes = [];
|
|
21002
|
+
for (const activityId of targetIds) {
|
|
21003
|
+
const checkedBefore = selectedBefore.has(activityId);
|
|
21004
|
+
const checkedAfter = selectedAfter.has(activityId);
|
|
21005
|
+
const visibleBefore = !hiddenBefore.has(activityId);
|
|
21006
|
+
const visibleAfter = !hiddenAfter.has(activityId);
|
|
21007
|
+
if (checkedBefore === checkedAfter && visibleBefore === visibleAfter)
|
|
21008
|
+
continue;
|
|
21009
|
+
changes.push({
|
|
21010
|
+
activityId,
|
|
21011
|
+
...checkedBefore === checkedAfter ? {} : { checked: { before: checkedBefore, after: checkedAfter } },
|
|
21012
|
+
...visibleBefore === visibleAfter ? {} : { visible: { before: visibleBefore, after: visibleAfter } }
|
|
21013
|
+
});
|
|
21014
|
+
}
|
|
21015
|
+
return changes;
|
|
21016
|
+
}
|
|
21017
|
+
function createProjectionChangeSet(target, source) {
|
|
21018
|
+
const orderChanged = serializeCanonicalSchedule(target.order) !== serializeCanonicalSchedule(source.order);
|
|
21019
|
+
return {
|
|
21020
|
+
source: { kind: "init" },
|
|
21021
|
+
activities: entityChanges(target.activities, source.activities),
|
|
21022
|
+
links: entityChanges(target.links, source.links),
|
|
21023
|
+
calendars: entityChanges(target.calendars, source.calendars),
|
|
21024
|
+
trackingEvents: [],
|
|
21025
|
+
effects: [],
|
|
21026
|
+
viewState: viewStateChanges(target, source),
|
|
21027
|
+
...orderChanged ? { order: cloneDomainValue(target.order) } : {}
|
|
21028
|
+
};
|
|
21029
|
+
}
|
|
21030
|
+
|
|
20139
21031
|
// src/propagations/upward/date-gesture-preview.ts
|
|
20140
21032
|
var emptyFormula = () => ({
|
|
20141
21033
|
constantStart: null,
|
|
@@ -20281,7 +21173,10 @@ function evaluateEnd(formula, proposedEdgeTimeMs) {
|
|
|
20281
21173
|
}
|
|
20282
21174
|
|
|
20283
21175
|
// src/init/schedule-core.ts
|
|
20284
|
-
var
|
|
21176
|
+
var FORK_SOURCE = /* @__PURE__ */ Symbol("ScheduleCore.fork.source");
|
|
21177
|
+
function acknowledgesPersistence(action) {
|
|
21178
|
+
return action.kind === "persistence-acknowledge" || action.kind === "activity-lookahead-sync" && action.acknowledgePersistedLookahead === true;
|
|
21179
|
+
}
|
|
20285
21180
|
function changesRequireFilterProjectionRebuild(changes) {
|
|
20286
21181
|
for (const change of changes.activities) {
|
|
20287
21182
|
if (change.kind === "deleted") return true;
|
|
@@ -20299,16 +21194,46 @@ function collectChangedActivities(changes, adapter) {
|
|
|
20299
21194
|
}
|
|
20300
21195
|
return activities;
|
|
20301
21196
|
}
|
|
21197
|
+
function changesAffectRelativePinProjection(changes, changedActivities, adapter, pins) {
|
|
21198
|
+
if (pins.size === 0) return false;
|
|
21199
|
+
if (changes.activities.some(
|
|
21200
|
+
(change) => change.kind === "created" && pins.has(String(change.id))
|
|
21201
|
+
)) {
|
|
21202
|
+
return true;
|
|
21203
|
+
}
|
|
21204
|
+
const referenceIds = new Set(
|
|
21205
|
+
[...pins.values()].map(({ referenceId }) => String(referenceId))
|
|
21206
|
+
);
|
|
21207
|
+
for (const activity of changedActivities) {
|
|
21208
|
+
let activityId = String(activity.id);
|
|
21209
|
+
while (activityId !== null) {
|
|
21210
|
+
if (referenceIds.has(activityId)) return true;
|
|
21211
|
+
activityId = adapter.getParentId(activityId);
|
|
21212
|
+
}
|
|
21213
|
+
}
|
|
21214
|
+
return false;
|
|
21215
|
+
}
|
|
20302
21216
|
function isCurrentFilterProjection(projection, filter, context) {
|
|
20303
21217
|
if (projection === null || projection.filter !== filter) return false;
|
|
20304
21218
|
const hasCurrentHours = projection.context.hoursPerDay === context.hoursPerDay;
|
|
20305
21219
|
const hasCurrentLocale = projection.context.locale === context.locale;
|
|
20306
21220
|
return hasCurrentHours && hasCurrentLocale;
|
|
20307
21221
|
}
|
|
20308
|
-
|
|
21222
|
+
function relativePlacementPinsEqual(left, right) {
|
|
21223
|
+
if (left.size !== right.size) return false;
|
|
21224
|
+
for (const [activityId, pin] of left) {
|
|
21225
|
+
const other = right.get(activityId);
|
|
21226
|
+
if (other === void 0 || other.referenceId !== pin.referenceId || other.branchParentId !== pin.branchParentId || other.side !== pin.side) {
|
|
21227
|
+
return false;
|
|
21228
|
+
}
|
|
21229
|
+
}
|
|
21230
|
+
return true;
|
|
21231
|
+
}
|
|
21232
|
+
var ScheduleCore = class _ScheduleCore {
|
|
20309
21233
|
_activityClipboard = { value: null };
|
|
20310
21234
|
_status = SCHEDULE_CORE_STATUS.READY;
|
|
20311
21235
|
coreRuntime;
|
|
21236
|
+
_isFork;
|
|
20312
21237
|
_undo = new UndoRecorder();
|
|
20313
21238
|
_opQueue = Promise.resolve();
|
|
20314
21239
|
_scheduleRevision = 0;
|
|
@@ -20339,6 +21264,42 @@ var ScheduleCore = class {
|
|
|
20339
21264
|
_criticalPathRevision = -1;
|
|
20340
21265
|
_activeCriticalPath = null;
|
|
20341
21266
|
constructor(input) {
|
|
21267
|
+
if (FORK_SOURCE in input) {
|
|
21268
|
+
this._isFork = true;
|
|
21269
|
+
const source = input[FORK_SOURCE];
|
|
21270
|
+
const state = source.coreRuntime.state.fork();
|
|
21271
|
+
this.coreRuntime = {
|
|
21272
|
+
state,
|
|
21273
|
+
scheduler: new AutoScheduler(state, source.coreRuntime.reporter),
|
|
21274
|
+
sector: cloneDomainValue(source.coreRuntime.sector),
|
|
21275
|
+
activityIdGenerator: source.coreRuntime.activityIdGenerator.fork(),
|
|
21276
|
+
linkIdGenerator: source.coreRuntime.linkIdGenerator.fork(),
|
|
21277
|
+
uniqueCorrelativeIdGenerator: source.coreRuntime.uniqueCorrelativeIdGenerator.fork(),
|
|
21278
|
+
customIdTracker: source.coreRuntime.customIdTracker.fork(),
|
|
21279
|
+
calendars: new Map(
|
|
21280
|
+
Array.from(source.coreRuntime.calendars, ([id, calendar]) => [
|
|
21281
|
+
id,
|
|
21282
|
+
cloneDomainValue(calendar)
|
|
21283
|
+
])
|
|
21284
|
+
),
|
|
21285
|
+
baseCalendars: cloneDomainValue(source.coreRuntime.baseCalendars),
|
|
21286
|
+
clock: source.coreRuntime.clock,
|
|
21287
|
+
reporter: source.coreRuntime.reporter
|
|
21288
|
+
};
|
|
21289
|
+
this._activityClipboard.value = cloneDomainValue(
|
|
21290
|
+
source._activityClipboard.value
|
|
21291
|
+
);
|
|
21292
|
+
this._undo = source._undo.fork();
|
|
21293
|
+
this._saveTracker = source._saveTracker.fork();
|
|
21294
|
+
this._scheduleRevision = source._scheduleRevision;
|
|
21295
|
+
this._pendingScheduleRevision = source._scheduleRevision;
|
|
21296
|
+
this._criticalPathRevision = source._criticalPathRevision;
|
|
21297
|
+
this._initPromise = Promise.resolve();
|
|
21298
|
+
this._criticalPathReady = Promise.resolve(null);
|
|
21299
|
+
this._ready = this._initPromise;
|
|
21300
|
+
return;
|
|
21301
|
+
}
|
|
21302
|
+
this._isFork = false;
|
|
20342
21303
|
const initialized = initializeCore(input);
|
|
20343
21304
|
this.coreRuntime = {
|
|
20344
21305
|
state: initialized.state,
|
|
@@ -20375,6 +21336,15 @@ var ScheduleCore = class {
|
|
|
20375
21336
|
get ready() {
|
|
20376
21337
|
return this._ready;
|
|
20377
21338
|
}
|
|
21339
|
+
fork() {
|
|
21340
|
+
return this._enqueue(async () => {
|
|
21341
|
+
await this._ready;
|
|
21342
|
+
this.assertReady();
|
|
21343
|
+
return new _ScheduleCore({
|
|
21344
|
+
[FORK_SOURCE]: this
|
|
21345
|
+
});
|
|
21346
|
+
});
|
|
21347
|
+
}
|
|
20378
21348
|
get criticalPathReady() {
|
|
20379
21349
|
return this._criticalPathReady;
|
|
20380
21350
|
}
|
|
@@ -20427,6 +21397,21 @@ var ScheduleCore = class {
|
|
|
20427
21397
|
this.assertReady();
|
|
20428
21398
|
return readLink(this.coreRuntime.state, id);
|
|
20429
21399
|
}
|
|
21400
|
+
getLinksForActivityView(activityId) {
|
|
21401
|
+
this.assertReady();
|
|
21402
|
+
const state = this.coreRuntime.state;
|
|
21403
|
+
const links = [];
|
|
21404
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21405
|
+
const append = (linkId) => {
|
|
21406
|
+
if (seen.has(linkId)) return;
|
|
21407
|
+
seen.add(linkId);
|
|
21408
|
+
const link = readLink(state, linkId);
|
|
21409
|
+
if (link) links.push(link);
|
|
21410
|
+
};
|
|
21411
|
+
for (const linkId of state.getOutgoingLinkIds(activityId)) append(linkId);
|
|
21412
|
+
for (const linkId of state.getIncomingLinkIds(activityId)) append(linkId);
|
|
21413
|
+
return links;
|
|
21414
|
+
}
|
|
20430
21415
|
getAllLinksView() {
|
|
20431
21416
|
this.assertReady();
|
|
20432
21417
|
return readAllLinks(this.coreRuntime.state);
|
|
@@ -20524,91 +21509,65 @@ var ScheduleCore = class {
|
|
|
20524
21509
|
}
|
|
20525
21510
|
getModifiedLinks() {
|
|
20526
21511
|
this.assertReady();
|
|
20527
|
-
return this._saveTracker.modifiedLinks(this.
|
|
21512
|
+
return this._saveTracker.modifiedLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
21513
|
+
}
|
|
21514
|
+
getNewLinks() {
|
|
21515
|
+
this.assertReady();
|
|
21516
|
+
return this._saveTracker.newLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
20528
21517
|
}
|
|
20529
21518
|
getModifiedActivities() {
|
|
20530
21519
|
this.assertReady();
|
|
20531
|
-
return this._saveTracker.modifiedActivities(
|
|
21520
|
+
return this._saveTracker.modifiedActivities(
|
|
21521
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21522
|
+
).map(cloneDomainValue);
|
|
21523
|
+
}
|
|
21524
|
+
getNewActivities() {
|
|
21525
|
+
this.assertReady();
|
|
21526
|
+
return this._saveTracker.newActivities(
|
|
21527
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21528
|
+
).map(cloneDomainValue);
|
|
20532
21529
|
}
|
|
20533
21530
|
hasUnsavedChanges() {
|
|
20534
21531
|
this.assertReady();
|
|
20535
21532
|
return this._saveTracker.hasUnsavedChanges();
|
|
20536
21533
|
}
|
|
20537
|
-
/**
|
|
20538
|
-
* Read-only view of the internal schedule revision. It moves once per
|
|
20539
|
-
* substantive CANONICAL mutation (dispatch, undo, redo). It does NOT move
|
|
20540
|
-
* for a preview dispatch nor for `applyPreviewInverse`: comparing the value
|
|
20541
|
-
* captured in `PreviewDispatchInfo.revision` against the current one
|
|
20542
|
-
* answers "did the schedule change while the preview was open".
|
|
20543
|
-
*/
|
|
21534
|
+
/** Read-only revision; moves once per substantive mutation. */
|
|
20544
21535
|
getScheduleRevision() {
|
|
20545
21536
|
return this._scheduleRevision;
|
|
20546
21537
|
}
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
20554
|
-
|
|
20555
|
-
|
|
20556
|
-
|
|
20557
|
-
|
|
20558
|
-
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20568
|
-
);
|
|
20569
|
-
}
|
|
20570
|
-
}
|
|
20571
|
-
const afterLinks = /* @__PURE__ */ new Map();
|
|
20572
|
-
for (const change of inverse.links) {
|
|
20573
|
-
if (change.kind !== "deleted" && change.after) {
|
|
20574
|
-
afterLinks.set(String(change.id), { ...change.after });
|
|
20575
|
-
}
|
|
20576
|
-
}
|
|
20577
|
-
const entry = buildUndoEntry(
|
|
20578
|
-
inverse,
|
|
20579
|
-
void 0,
|
|
20580
|
-
void 0,
|
|
20581
|
-
afterSnap,
|
|
20582
|
-
afterLinks
|
|
20583
|
-
);
|
|
20584
|
-
applyRedo(state, entry);
|
|
20585
|
-
if (needsResync(entry)) this._resyncCustomIdTrackerFromModel();
|
|
20586
|
-
const restored = {
|
|
20587
|
-
...inverse,
|
|
20588
|
-
activities: inverse.activities.map((change) => {
|
|
20589
|
-
if (change.kind === "deleted") return { ...change, after: null };
|
|
20590
|
-
const live = state.getActivity(String(change.id));
|
|
20591
|
-
return {
|
|
20592
|
-
...change,
|
|
20593
|
-
after: live ? structuredCloneActivity(live) : null
|
|
20594
|
-
};
|
|
20595
|
-
}),
|
|
20596
|
-
links: inverse.links.map((change) => {
|
|
20597
|
-
if (change.kind === "deleted") return { ...change, after: null };
|
|
20598
|
-
const live = state.getLink(String(change.id));
|
|
20599
|
-
return { ...change, after: live ? snapshotToLink(live) : null };
|
|
20600
|
-
})
|
|
20601
|
-
};
|
|
20602
|
-
return this._reapplyViewState(restored) ?? restored;
|
|
21538
|
+
getCanonicalDigest() {
|
|
21539
|
+
this.assertReady();
|
|
21540
|
+
const byId = (left, right) => String(left.id).localeCompare(String(right.id));
|
|
21541
|
+
return serializeCanonicalSchedule({
|
|
21542
|
+
sector: this.getSector(),
|
|
21543
|
+
activities: [...this.getAllActivitiesView()].sort(byId),
|
|
21544
|
+
links: [...this.getAllLinksView()].sort(byId),
|
|
21545
|
+
calendars: [...this.getAllCalendars()].sort(byId),
|
|
21546
|
+
baseCalendars: [...this.getBaseCalendars()].sort(byId)
|
|
21547
|
+
});
|
|
21548
|
+
}
|
|
21549
|
+
createProjectionChangeSetFrom(source) {
|
|
21550
|
+
this.assertReady();
|
|
21551
|
+
source.assertReady();
|
|
21552
|
+
const projectionOf = (core) => ({
|
|
21553
|
+
activities: core.getAllActivitiesView(),
|
|
21554
|
+
links: core.getAllLinksView(),
|
|
21555
|
+
calendars: core.getAllCalendars(),
|
|
21556
|
+
selectedIds: core.getSelectedActivityIds(),
|
|
21557
|
+
hiddenIds: core.getHiddenActivityIds(),
|
|
21558
|
+
order: collectBranchOrder(core.coreRuntime.state)
|
|
20603
21559
|
});
|
|
21560
|
+
return createProjectionChangeSet(projectionOf(this), projectionOf(source));
|
|
20604
21561
|
}
|
|
20605
21562
|
getDeletedActivitiesSinceLastSave() {
|
|
20606
21563
|
this.assertReady();
|
|
20607
|
-
return this._saveTracker.deletedActivities(
|
|
21564
|
+
return this._saveTracker.deletedActivities(
|
|
21565
|
+
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
21566
|
+
).map(cloneDomainValue);
|
|
20608
21567
|
}
|
|
20609
21568
|
getDeletedLinksSinceLastSave() {
|
|
20610
21569
|
this.assertReady();
|
|
20611
|
-
return this._saveTracker.deletedLinks(this.
|
|
21570
|
+
return this._saveTracker.deletedLinks((linkId) => this.coreRuntime.state.getLink(linkId)).map(cloneDomainValue);
|
|
20612
21571
|
}
|
|
20613
21572
|
_enqueue(work) {
|
|
20614
21573
|
const workPromise = this._opQueue.then(work, work);
|
|
@@ -20684,11 +21643,11 @@ var ScheduleCore = class {
|
|
|
20684
21643
|
});
|
|
20685
21644
|
}
|
|
20686
21645
|
async dispatch(action, options = {}) {
|
|
20687
|
-
const admitted =
|
|
21646
|
+
const admitted = this._beginScheduleMutation(action);
|
|
20688
21647
|
return this._enqueue(() => this._dispatchInner(action, options, admitted));
|
|
20689
21648
|
}
|
|
20690
21649
|
async applyActivityBatch(action, options = {}) {
|
|
20691
|
-
const admitted =
|
|
21650
|
+
const admitted = this._beginScheduleMutation(action);
|
|
20692
21651
|
return this._enqueue(() => this._dispatchInner(action, options, admitted));
|
|
20693
21652
|
}
|
|
20694
21653
|
async _dispatchInner(action, options = {}, admitted = false) {
|
|
@@ -20703,23 +21662,22 @@ var ScheduleCore = class {
|
|
|
20703
21662
|
}
|
|
20704
21663
|
async _runDispatch(action, options) {
|
|
20705
21664
|
this.assertReady();
|
|
20706
|
-
if (
|
|
21665
|
+
if (this._isFork && acknowledgesPersistence(action)) {
|
|
20707
21666
|
return {
|
|
20708
21667
|
result: {
|
|
20709
21668
|
ok: false,
|
|
20710
|
-
reason: REJECTION_REASON.
|
|
21669
|
+
reason: REJECTION_REASON.SANDBOX_PERSISTENCE_FORBIDDEN
|
|
20711
21670
|
},
|
|
20712
21671
|
changedState: false
|
|
20713
21672
|
};
|
|
20714
21673
|
}
|
|
20715
21674
|
const pipelineContext = buildPipelineContext(this.coreRuntime.state, {
|
|
20716
21675
|
hoursPerDay: this.coreRuntime.sector.hoursPerDay,
|
|
20717
|
-
dateFormat: this.coreRuntime.sector.dateFormat
|
|
20718
|
-
// ISSUE-057 (CP6): sin default silencioso 'days'; la clave se omite si
|
|
20719
|
-
// no se declaró (exactOptionalPropertyTypes).
|
|
20720
|
-
...options.inputUnit !== void 0 ? { inputUnit: options.inputUnit } : {}
|
|
21676
|
+
dateFormat: this.coreRuntime.sector.dateFormat
|
|
20721
21677
|
});
|
|
20722
|
-
const historyPolicy = getDispatchHistoryPolicy(action
|
|
21678
|
+
const historyPolicy = getDispatchHistoryPolicy(action);
|
|
21679
|
+
const capturesRelativePlacementPins = action.kind === "activity-create" || action.kind === "activity-delete" || action.kind === "activity-batch" && action.mode === "replace";
|
|
21680
|
+
const relativePlacementPinsBefore = capturesRelativePlacementPins ? new Map(this.coreRuntime.state.getRelativePlacementPins()) : void 0;
|
|
20723
21681
|
const generatorSnapshot = [
|
|
20724
21682
|
this.coreRuntime.activityIdGenerator.snapshot(),
|
|
20725
21683
|
this.coreRuntime.linkIdGenerator.snapshot(),
|
|
@@ -20737,6 +21695,8 @@ var ScheduleCore = class {
|
|
|
20737
21695
|
calendars: this.coreRuntime.calendars,
|
|
20738
21696
|
activityIdGen: this.coreRuntime.activityIdGenerator,
|
|
20739
21697
|
linkIdGen: this.coreRuntime.linkIdGenerator,
|
|
21698
|
+
isActivityIdReserved: (activityId) => this._saveTracker.hasCheckpointActivityId(activityId),
|
|
21699
|
+
isLinkIdReserved: (linkId) => this._saveTracker.hasCheckpointLinkId(linkId),
|
|
20740
21700
|
uidGen: this.coreRuntime.uniqueCorrelativeIdGenerator,
|
|
20741
21701
|
customIdTracker: this.coreRuntime.customIdTracker,
|
|
20742
21702
|
reporter: this.coreRuntime.reporter,
|
|
@@ -20754,18 +21714,26 @@ var ScheduleCore = class {
|
|
|
20754
21714
|
this.coreRuntime.state.endWriteCapture();
|
|
20755
21715
|
this.coreRuntime.customIdTracker.commitCustomIdTransaction();
|
|
20756
21716
|
}
|
|
21717
|
+
if (result.ok) this._recordRelativePlacementPin(action, result.changes);
|
|
20757
21718
|
if (result.ok && historyPolicy === "record" && changeSetIsSubstantive(result.changes)) {
|
|
20758
21719
|
const created = captureCreatedSnapshots(
|
|
20759
21720
|
this.coreRuntime.state,
|
|
20760
21721
|
result.changes
|
|
20761
21722
|
);
|
|
21723
|
+
const relativePlacementPinsAfter = capturesRelativePlacementPins ? new Map(this.coreRuntime.state.getRelativePlacementPins()) : void 0;
|
|
21724
|
+
const relativePlacementPinsChanged = relativePlacementPinsBefore !== void 0 && relativePlacementPinsAfter !== void 0 && !relativePlacementPinsEqual(
|
|
21725
|
+
relativePlacementPinsBefore,
|
|
21726
|
+
relativePlacementPinsAfter
|
|
21727
|
+
);
|
|
20762
21728
|
this._undo.record(
|
|
20763
21729
|
buildUndoEntry(
|
|
20764
21730
|
buildHistoryChangeSet(result.changes),
|
|
20765
21731
|
result.__beforeSnap,
|
|
20766
21732
|
result.__beforeLinks,
|
|
20767
21733
|
created.afterSnap,
|
|
20768
|
-
created.afterLinks
|
|
21734
|
+
created.afterLinks,
|
|
21735
|
+
relativePlacementPinsChanged ? relativePlacementPinsBefore : void 0,
|
|
21736
|
+
relativePlacementPinsChanged ? relativePlacementPinsAfter : void 0
|
|
20769
21737
|
),
|
|
20770
21738
|
computeCoalesceKey(action),
|
|
20771
21739
|
Date.now()
|
|
@@ -20773,17 +21741,20 @@ var ScheduleCore = class {
|
|
|
20773
21741
|
}
|
|
20774
21742
|
if (result.ok) {
|
|
20775
21743
|
if (historyPolicy === "clear-on-success") {
|
|
20776
|
-
this._saveTracker.
|
|
20777
|
-
this.coreRuntime.state.getAllActivities()
|
|
21744
|
+
this._saveTracker.acknowledge(
|
|
21745
|
+
this.coreRuntime.state.getAllActivities(),
|
|
21746
|
+
this.coreRuntime.state.getAllLinks()
|
|
20778
21747
|
);
|
|
20779
|
-
this._saveTracker.snapshotLinks(this.getAllLinksView());
|
|
20780
21748
|
this._undo.clear();
|
|
20781
21749
|
} else if (action.kind === "activity-lookahead-sync" && action.acknowledgePersistedLookahead === true) {
|
|
20782
21750
|
this._saveTracker.acknowledgePersistedLookahead(
|
|
20783
|
-
|
|
21751
|
+
result.changes.activities,
|
|
20784
21752
|
(activityId) => this.coreRuntime.state.getActivity(activityId)
|
|
20785
21753
|
);
|
|
20786
|
-
|
|
21754
|
+
if (action.persistedProgress !== void 0) {
|
|
21755
|
+
this._undo.clear();
|
|
21756
|
+
}
|
|
21757
|
+
} else {
|
|
20787
21758
|
this._saveTracker.recordChanges(
|
|
20788
21759
|
result.changes.activities,
|
|
20789
21760
|
result.changes.links,
|
|
@@ -20794,20 +21765,6 @@ var ScheduleCore = class {
|
|
|
20794
21765
|
}
|
|
20795
21766
|
result = this._withReappliedViewState(action, result);
|
|
20796
21767
|
if (!result.ok) return { result, changedState: false };
|
|
20797
|
-
if (options.preview === true) {
|
|
20798
|
-
const inverse = buildInverseChangeSet(
|
|
20799
|
-
this.coreRuntime.state,
|
|
20800
|
-
buildUndoEntry(buildHistoryChangeSet(result.changes)),
|
|
20801
|
-
"before"
|
|
20802
|
-
);
|
|
20803
|
-
return {
|
|
20804
|
-
result: {
|
|
20805
|
-
...toPublicDispatchResult(result),
|
|
20806
|
-
preview: { inverse, revision: this._scheduleRevision }
|
|
20807
|
-
},
|
|
20808
|
-
changedState: false
|
|
20809
|
-
};
|
|
20810
|
-
}
|
|
20811
21768
|
if (dispatchChangesSchedulingState(action) && changeSetIsSubstantive(result.changes)) {
|
|
20812
21769
|
this._recordScheduleMutation(
|
|
20813
21770
|
options.skipAutoSchedule !== true && options.skipCriticalPath !== true && resolveRunCriticalPath(action)
|
|
@@ -20844,6 +21801,19 @@ var ScheduleCore = class {
|
|
|
20844
21801
|
const merged = this._reapplyViewState(result.changes);
|
|
20845
21802
|
return merged === null ? result : { ...result, changes: merged };
|
|
20846
21803
|
}
|
|
21804
|
+
_recordRelativePlacementPin(action, changes) {
|
|
21805
|
+
const state = this.coreRuntime.state;
|
|
21806
|
+
if (action.kind !== "activity-create") return;
|
|
21807
|
+
const referenceId = action.afterSiblingId ?? action.beforeSiblingId ?? (action.eventSource === "Creation button (+)" && action.parentId !== ROOT_PARENT_ID ? action.parentId : void 0);
|
|
21808
|
+
if (referenceId === void 0) return;
|
|
21809
|
+
const created = changes.activities.find(({ kind }) => kind === "created");
|
|
21810
|
+
if (created === void 0) return;
|
|
21811
|
+
state.setRelativePlacementPin(created.id, {
|
|
21812
|
+
referenceId,
|
|
21813
|
+
branchParentId: action.parentId,
|
|
21814
|
+
side: action.beforeSiblingId === void 0 ? "after" : "before"
|
|
21815
|
+
});
|
|
21816
|
+
}
|
|
20847
21817
|
/**
|
|
20848
21818
|
* The single place the two view-state passes are chained. Dispatch, undo and
|
|
20849
21819
|
* redo all route through here: when this existed only inside the dispatch
|
|
@@ -20866,39 +21836,7 @@ var ScheduleCore = class {
|
|
|
20866
21836
|
sequenced.changes ?? withFilter ?? changes,
|
|
20867
21837
|
sequenced.touchedParents
|
|
20868
21838
|
);
|
|
20869
|
-
|
|
20870
|
-
const finalChanges = reapplied ?? changes;
|
|
20871
|
-
const createdIds = finalChanges.activities.filter((change) => change.kind === "created").map((change) => String(change.id));
|
|
20872
|
-
if (createdIds.length === 0) return reapplied;
|
|
20873
|
-
const createdAndAncestors = new Set(createdIds);
|
|
20874
|
-
const filterProjection = this.coreRuntime.state.getFilterProjection();
|
|
20875
|
-
for (const createdId of createdIds) {
|
|
20876
|
-
if (filterProjection !== null) {
|
|
20877
|
-
filterProjection.pinnedVisibleIds.add(createdId);
|
|
20878
|
-
}
|
|
20879
|
-
let activityId = createdId;
|
|
20880
|
-
while (activityId !== null && activityId !== ROOT_PARENT_ID) {
|
|
20881
|
-
createdAndAncestors.add(activityId);
|
|
20882
|
-
if (filterProjection !== null) {
|
|
20883
|
-
filterProjection.visibleIds.add(activityId);
|
|
20884
|
-
filterProjection.visibleReferenceCounts.set(
|
|
20885
|
-
activityId,
|
|
20886
|
-
(filterProjection.visibleReferenceCounts.get(activityId) ?? 0) + 1
|
|
20887
|
-
);
|
|
20888
|
-
}
|
|
20889
|
-
activityId = this.coreRuntime.state.getParentId(activityId);
|
|
20890
|
-
}
|
|
20891
|
-
}
|
|
20892
|
-
const createdVisibility = applyVisibleSet(
|
|
20893
|
-
this.coreRuntime.state,
|
|
20894
|
-
createdAndAncestors,
|
|
20895
|
-
createdAndAncestors
|
|
20896
|
-
);
|
|
20897
|
-
if (createdVisibility.length === 0) return reapplied;
|
|
20898
|
-
return {
|
|
20899
|
-
...finalChanges,
|
|
20900
|
-
viewState: [...finalChanges.viewState ?? [], ...createdVisibility]
|
|
20901
|
-
};
|
|
21839
|
+
return withOrder ?? sequenced.changes ?? withFilter;
|
|
20902
21840
|
}
|
|
20903
21841
|
/**
|
|
20904
21842
|
* Emits `order` for the branches this mutation resequenced, when no user order
|
|
@@ -20977,16 +21915,22 @@ var ScheduleCore = class {
|
|
|
20977
21915
|
context
|
|
20978
21916
|
);
|
|
20979
21917
|
const changedActivities = changesRequireFilterProjectionRebuild(changes) ? null : collectChangedActivities(changes, adapter);
|
|
21918
|
+
const relativePlacementPins = adapter.getRelativePlacementPins();
|
|
20980
21919
|
let affectedIds;
|
|
20981
21920
|
let visibleIds;
|
|
20982
|
-
const needsProjectionRebuild = changedActivities === null || !projectionIsCurrent
|
|
21921
|
+
const needsProjectionRebuild = changedActivities === null || !projectionIsCurrent || changesAffectRelativePinProjection(
|
|
21922
|
+
changes,
|
|
21923
|
+
changedActivities,
|
|
21924
|
+
adapter,
|
|
21925
|
+
relativePlacementPins
|
|
21926
|
+
);
|
|
20983
21927
|
if (needsProjectionRebuild) {
|
|
20984
21928
|
const rebuiltProjection = buildFilterProjection({
|
|
20985
21929
|
activities: adapter.getAllActivities(),
|
|
20986
21930
|
parentOf: (activityId) => adapter.getParentId(activityId),
|
|
20987
21931
|
filter,
|
|
20988
21932
|
context,
|
|
20989
|
-
|
|
21933
|
+
relativePlacementPins
|
|
20990
21934
|
});
|
|
20991
21935
|
adapter.setFilterProjection(rebuiltProjection);
|
|
20992
21936
|
visibleIds = rebuiltProjection.visibleIds;
|
|
@@ -21060,13 +22004,20 @@ var ScheduleCore = class {
|
|
|
21060
22004
|
this.coreRuntime.customIdTracker.beginCustomIdTransaction();
|
|
21061
22005
|
let changes;
|
|
21062
22006
|
try {
|
|
21063
|
-
for (const [activityId, fields] of calculatedFields) {
|
|
22007
|
+
for (const [activityId, fields] of calculatedFields.activities) {
|
|
21064
22008
|
setCriticalPathFieldsIfChanged(
|
|
21065
22009
|
this.coreRuntime.state,
|
|
21066
22010
|
activityId,
|
|
21067
22011
|
fields
|
|
21068
22012
|
);
|
|
21069
22013
|
}
|
|
22014
|
+
for (const [linkId, fields] of calculatedFields.links) {
|
|
22015
|
+
setCriticalPathLinkFieldsIfChanged(
|
|
22016
|
+
this.coreRuntime.state,
|
|
22017
|
+
linkId,
|
|
22018
|
+
fields
|
|
22019
|
+
);
|
|
22020
|
+
}
|
|
21070
22021
|
changes = await assembleChangeSet(this.coreRuntime.state, {
|
|
21071
22022
|
source: { kind: "init" },
|
|
21072
22023
|
beforeSnap: /* @__PURE__ */ new Map(),
|
|
@@ -21348,7 +22299,6 @@ var DISPATCH_ACTION_KIND = {
|
|
|
21348
22299
|
FILTER_SET: "filter-set",
|
|
21349
22300
|
SORT_SET: "sort-set",
|
|
21350
22301
|
SIR_SYNC: "sir-sync",
|
|
21351
|
-
SIR_APPROVE: "sir-approve",
|
|
21352
22302
|
ACTIVITY_LOOKAHEAD_SYNC: "activity-lookahead-sync"
|
|
21353
22303
|
};
|
|
21354
22304
|
var KIND_CATALOG_COVERS_UNION = {
|
|
@@ -21378,7 +22328,6 @@ var KIND_CATALOG_COVERS_UNION = {
|
|
|
21378
22328
|
[DISPATCH_ACTION_KIND.FILTER_SET]: true,
|
|
21379
22329
|
[DISPATCH_ACTION_KIND.SORT_SET]: true,
|
|
21380
22330
|
[DISPATCH_ACTION_KIND.SIR_SYNC]: true,
|
|
21381
|
-
[DISPATCH_ACTION_KIND.SIR_APPROVE]: true,
|
|
21382
22331
|
[DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC]: true
|
|
21383
22332
|
};
|
|
21384
22333
|
var DISPATCH_ACTION_KINDS = Object.keys(KIND_CATALOG_COVERS_UNION);
|