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