@outbuild-company/schedule-core 1.4.0 → 1.6.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/index.d.cts CHANGED
@@ -149,6 +149,20 @@ interface Link {
149
149
  sectorId?: number;
150
150
  proplannerId?: number;
151
151
  }
152
+ interface DeletedActivitySnapshot {
153
+ readonly id: ActivityId;
154
+ readonly proplannerId: number;
155
+ readonly parentId: ActivityId | null;
156
+ readonly hadBaseline: boolean;
157
+ }
158
+ interface DeletedLinkSnapshot {
159
+ readonly id: LinkId;
160
+ readonly proplannerId: number;
161
+ readonly source: ActivityId;
162
+ readonly target: ActivityId;
163
+ readonly type: LinkType;
164
+ readonly lag: number;
165
+ }
152
166
  interface CalendarWorktime {
153
167
  days: Array<false | string[]>;
154
168
  hours: string[];
@@ -572,7 +586,16 @@ interface BackendCalendarInput {
572
586
  baseexceptiondays?: BackendCalendarExceptionInput[];
573
587
  }
574
588
  interface BackendActivityInput {
575
- id: number;
589
+ /**
590
+ * Persisted backend identity.
591
+ *
592
+ * Synthetic activities that have not been persisted yet use `null`.
593
+ * `unique_id` remains their stable local identity.
594
+ *
595
+ * A numeric zero is intentionally preserved as a numeric backend identity
596
+ * for compatibility with the certified proplanner-id/002 scenario.
597
+ */
598
+ id: number | null;
576
599
  unique_id: string;
577
600
  parent_id: string;
578
601
  unique_correlative_id: string;
@@ -707,6 +730,9 @@ declare class ScheduleCore {
707
730
  getProjectWorkHours(): ProjectWorkHours | null;
708
731
  getModifiedLinks(): ReadonlyArray<Readonly<Link>>;
709
732
  getModifiedActivities(): ReadonlyArray<Readonly<CoreActivity>>;
733
+ hasUnsavedChanges(): boolean;
734
+ getDeletedActivitiesSinceLastSave(): ReadonlyArray<Readonly<DeletedActivitySnapshot>>;
735
+ getDeletedLinksSinceLastSave(): ReadonlyArray<Readonly<DeletedLinkSnapshot>>;
710
736
  private _enqueue;
711
737
  dispatch(action: DispatchAction, options?: DispatchOptions): Promise<DispatchResult>;
712
738
  private _dispatchInner;
@@ -766,8 +792,12 @@ declare class ScheduleCore {
766
792
  private _startCriticalPathForCurrentRevision;
767
793
  private _runCriticalPathAndCapture;
768
794
  private _recomputeAfterHistoryRestore;
769
- undo(): Promise<ChangeSet | null>;
770
- redo(): Promise<ChangeSet | null>;
795
+ undo(options?: {
796
+ skipCriticalPath?: boolean;
797
+ }): Promise<ChangeSet | null>;
798
+ redo(options?: {
799
+ skipCriticalPath?: boolean;
800
+ }): Promise<ChangeSet | null>;
771
801
  canUndo(): boolean;
772
802
  canRedo(): boolean;
773
803
  clearHistory(): void;
@@ -925,4 +955,4 @@ declare const DISPATCH_TRACK_EVENT: {
925
955
  };
926
956
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
927
957
 
928
- export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
958
+ export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
package/dist/index.d.ts CHANGED
@@ -149,6 +149,20 @@ interface Link {
149
149
  sectorId?: number;
150
150
  proplannerId?: number;
151
151
  }
152
+ interface DeletedActivitySnapshot {
153
+ readonly id: ActivityId;
154
+ readonly proplannerId: number;
155
+ readonly parentId: ActivityId | null;
156
+ readonly hadBaseline: boolean;
157
+ }
158
+ interface DeletedLinkSnapshot {
159
+ readonly id: LinkId;
160
+ readonly proplannerId: number;
161
+ readonly source: ActivityId;
162
+ readonly target: ActivityId;
163
+ readonly type: LinkType;
164
+ readonly lag: number;
165
+ }
152
166
  interface CalendarWorktime {
153
167
  days: Array<false | string[]>;
154
168
  hours: string[];
@@ -572,7 +586,16 @@ interface BackendCalendarInput {
572
586
  baseexceptiondays?: BackendCalendarExceptionInput[];
573
587
  }
574
588
  interface BackendActivityInput {
575
- id: number;
589
+ /**
590
+ * Persisted backend identity.
591
+ *
592
+ * Synthetic activities that have not been persisted yet use `null`.
593
+ * `unique_id` remains their stable local identity.
594
+ *
595
+ * A numeric zero is intentionally preserved as a numeric backend identity
596
+ * for compatibility with the certified proplanner-id/002 scenario.
597
+ */
598
+ id: number | null;
576
599
  unique_id: string;
577
600
  parent_id: string;
578
601
  unique_correlative_id: string;
@@ -707,6 +730,9 @@ declare class ScheduleCore {
707
730
  getProjectWorkHours(): ProjectWorkHours | null;
708
731
  getModifiedLinks(): ReadonlyArray<Readonly<Link>>;
709
732
  getModifiedActivities(): ReadonlyArray<Readonly<CoreActivity>>;
733
+ hasUnsavedChanges(): boolean;
734
+ getDeletedActivitiesSinceLastSave(): ReadonlyArray<Readonly<DeletedActivitySnapshot>>;
735
+ getDeletedLinksSinceLastSave(): ReadonlyArray<Readonly<DeletedLinkSnapshot>>;
710
736
  private _enqueue;
711
737
  dispatch(action: DispatchAction, options?: DispatchOptions): Promise<DispatchResult>;
712
738
  private _dispatchInner;
@@ -766,8 +792,12 @@ declare class ScheduleCore {
766
792
  private _startCriticalPathForCurrentRevision;
767
793
  private _runCriticalPathAndCapture;
768
794
  private _recomputeAfterHistoryRestore;
769
- undo(): Promise<ChangeSet | null>;
770
- redo(): Promise<ChangeSet | null>;
795
+ undo(options?: {
796
+ skipCriticalPath?: boolean;
797
+ }): Promise<ChangeSet | null>;
798
+ redo(options?: {
799
+ skipCriticalPath?: boolean;
800
+ }): Promise<ChangeSet | null>;
771
801
  canUndo(): boolean;
772
802
  canRedo(): boolean;
773
803
  clearHistory(): void;
@@ -925,4 +955,4 @@ declare const DISPATCH_TRACK_EVENT: {
925
955
  };
926
956
  type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
927
957
 
928
- export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
958
+ export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, type BranchOrderChange, CALENDAR_UNIT, COLUMN, CONSTRAINT_TYPE, CONSTRAINT_TYPE_TO_LABEL, CREATION_KIND, type Calendar, type CalendarId, type CalendarUnit, type CalendarWorktime, type ChangeSet, type ColumnName, type ConstraintType, type ConstraintWarning, type CoreActivity, type CreationKind, DEFAULT_HOURS_PER_DAY, DISPATCH_ACTION_KIND, DISPATCH_ACTION_KINDS, DISPATCH_TRACK_EVENT, type DeletedActivitySnapshot, type DeletedLinkSnapshot, type DispatchAction, type DispatchActionKind, type DispatchOptions, type DispatchRejectReason, type DispatchResult, type DispatchTrackEvent, type EntityChange, type FilterCriterion, type FilterDateRange, type FilterField, type FilterState, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, type OrderRule, type OrderState, type ParsedInput, type PastedActivityInput, type PastedLinkInput, type PendingRequest, type PersistedEntityIdentity, REJECTION_REASON, ROOT_PARENT_ID, ScheduleCore, type ScheduleCoreInput, type ScheduleCoreReporter, type ScheduleCoreStatus, type ScheduleEffect, type SectorMetadata, type StatusCriteria, type TrackingEvent, WORK_TIME_DIRECTION, type WorkTimeDirection, checkNoUpdatedLinks, computeExpectedProgress, expectedProgressFromBaseline, getActiveBaseline, getUnsavedActivities, isRootParent, normalizeParentKey, parseFromBackend, willRunCriticalPath, yieldToBrowser };
package/dist/index.js CHANGED
@@ -4022,7 +4022,10 @@ var MAX_CUSTOM_ID_LENGTH = 20;
4022
4022
  // src/columns/custom-id/custom-id-pipeline.ts
4023
4023
  var customIdPipeline = {
4024
4024
  targetField: "customId",
4025
- canEdit() {
4025
+ canEdit(activity, hierarchy) {
4026
+ if (isSummaryActivity2(activity, hierarchy)) {
4027
+ return cannotEdit("summary", "summary_column_value");
4028
+ }
4026
4029
  return canEdit();
4027
4030
  },
4028
4031
  parseInput(rawValue) {
@@ -10335,7 +10338,6 @@ function buildParentMutations(input) {
10335
10338
  fields.constraintType = CONSTRAINT_TYPE2.ASAP;
10336
10339
  fields.constraintDate = null;
10337
10340
  fields.progress = 0;
10338
- fields.expectedProgressBaseline = 0;
10339
10341
  }
10340
10342
  }
10341
10343
  return {
@@ -10401,6 +10403,18 @@ function findFirstSelectedSibling(taskId, selected, adapter) {
10401
10403
  }
10402
10404
  return firstSelected;
10403
10405
  }
10406
+ function hasAncestorInSet(activityId, candidates, adapter) {
10407
+ const visited = /* @__PURE__ */ new Set([activityId]);
10408
+ let parentId = normalizeParentKey(
10409
+ adapter.getActivity(activityId)?.parentId
10410
+ );
10411
+ while (parentId !== ROOT_PARENT_ID && !visited.has(parentId)) {
10412
+ if (candidates.has(parentId)) return true;
10413
+ visited.add(parentId);
10414
+ parentId = normalizeParentKey(adapter.getActivity(parentId)?.parentId);
10415
+ }
10416
+ return false;
10417
+ }
10404
10418
  function findPrevSiblingHelper(taskId, adapter) {
10405
10419
  const a = adapter.getActivity(taskId);
10406
10420
  if (!a) return null;
@@ -10469,14 +10483,7 @@ async function createActivityCore(action, deps, opts = {}) {
10469
10483
  const parentWasLeaf = parent !== null && adapter.getChildren(parent.id).length === 0;
10470
10484
  const beforeSnap = opts.skipBeforeSnap ? /* @__PURE__ */ new Map() : await snapshotActivities(adapter, initialTouched);
10471
10485
  adapter.addActivity(newActivity);
10472
- applyParentMutations(
10473
- action,
10474
- newId,
10475
- parent,
10476
- adapter,
10477
- opts.preserveParentCustomId ?? false,
10478
- parentWasLeaf
10479
- );
10486
+ applyParentMutations(action, newId, parent, adapter, parentWasLeaf);
10480
10487
  recomputeHhCascadeForActivity(newId, adapter);
10481
10488
  recomputeUsedCostCascadeForActivity(newId, adapter);
10482
10489
  recomputeCostCascadeForActivity(newId, adapter);
@@ -10546,7 +10553,7 @@ function assignCustomId(newActivity, action, deps, anchorSiblingId, parent, opts
10546
10553
  return;
10547
10554
  }
10548
10555
  }
10549
- const customIdAnchorId = anchorSiblingId ?? opts.customIdReferenceId;
10556
+ const customIdAnchorId = opts.customIdReferenceId ?? anchorSiblingId;
10550
10557
  const derivesFromAnchor = customIdAnchorId !== void 0;
10551
10558
  const referenceActivity = derivesFromAnchor ? adapter.getActivity(customIdAnchorId) : parent;
10552
10559
  newActivity.customId = customIdTracker.generateCustomIdForNewChild({
@@ -10554,7 +10561,7 @@ function assignCustomId(newActivity, action, deps, anchorSiblingId, parent, opts
10554
10561
  useActivityCustomIdAsReference: derivesFromAnchor
10555
10562
  });
10556
10563
  }
10557
- function applyParentMutations(action, newId, parent, adapter, preserveParentCustomId, parentWasLeaf) {
10564
+ function applyParentMutations(action, newId, parent, adapter, parentWasLeaf) {
10558
10565
  if (!parent) return;
10559
10566
  const parentMutations = buildParentMutations({
10560
10567
  parentId: action.parentId,
@@ -10564,8 +10571,6 @@ function applyParentMutations(action, newId, parent, adapter, preserveParentCust
10564
10571
  });
10565
10572
  if (!parentMutations) return;
10566
10573
  for (const [key, value] of Object.entries(parentMutations.fields)) {
10567
- const skipCustomIdMutation = key === ACTIVITY_PROPERTY.CUSTOM_ID && preserveParentCustomId;
10568
- if (skipCustomIdMutation) continue;
10569
10574
  setActivityFieldDynamic(adapter, parentMutations.parentId, key, value);
10570
10575
  }
10571
10576
  if (parentMutations.promotionSnapshot) {
@@ -10719,10 +10724,7 @@ async function dispatchActivityPaste(action, options, deps) {
10719
10724
  {
10720
10725
  skipCorrelativeRecompute: true,
10721
10726
  skipBeforeSnap: true,
10722
- ...mappedParent !== void 0 ? {
10723
- customIdReferenceId: action.referenceActivityId,
10724
- preserveParentCustomId: true
10725
- } : {}
10727
+ customIdReferenceId: action.referenceActivityId
10726
10728
  }
10727
10729
  );
10728
10730
  if (!core.ok) return core;
@@ -11433,6 +11435,7 @@ async function dispatchActivityIndent(action, options, deps) {
11433
11435
  const failed = [];
11434
11436
  const succeededIds = [];
11435
11437
  const moves = [];
11438
+ const plannedIds = /* @__PURE__ */ new Set();
11436
11439
  const blockingSet = new Set(selectedSet);
11437
11440
  const sortedIds = [...action.activityIds].sort((a, b) => {
11438
11441
  const aSnap = adapter.getActivity(a);
@@ -11451,6 +11454,7 @@ async function dispatchActivityIndent(action, options, deps) {
11451
11454
  blockingSet.delete(id);
11452
11455
  continue;
11453
11456
  }
11457
+ if (hasAncestorInSet(id, plannedIds, adapter)) continue;
11454
11458
  const targetParentId = findPreviousNonSelectedSibling(
11455
11459
  id,
11456
11460
  blockingSet,
@@ -11467,6 +11471,7 @@ async function dispatchActivityIndent(action, options, deps) {
11467
11471
  const oldParentRaw = adapter.getActivity(id)?.parentId;
11468
11472
  const oldParentId = isRootParent(oldParentRaw) ? ROOT_PARENT_ID : String(oldParentRaw);
11469
11473
  moves.push({ activityId: id, newParentId: targetParentId, oldParentId });
11474
+ plannedIds.add(id);
11470
11475
  touched.add(id);
11471
11476
  touched.add(targetParentId);
11472
11477
  if (oldParentId !== ROOT_PARENT_ID) touched.add(oldParentId);
@@ -11629,6 +11634,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11629
11634
  const succeededIds = [];
11630
11635
  const oldParents = /* @__PURE__ */ new Set();
11631
11636
  const planned = [];
11637
+ const plannedIds = /* @__PURE__ */ new Set();
11632
11638
  const sortedIds = [...action.activityIds].sort((a, b) => {
11633
11639
  const aSnap = adapter.getActivity(a);
11634
11640
  const bSnap = adapter.getActivity(b);
@@ -11646,6 +11652,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11646
11652
  });
11647
11653
  continue;
11648
11654
  }
11655
+ if (hasAncestorInSet(key, plannedIds, adapter)) continue;
11649
11656
  const oldParent = activity.parentId;
11650
11657
  if (isRootParent(oldParent)) {
11651
11658
  failed.push({
@@ -11687,6 +11694,7 @@ async function dispatchActivityOutdent(action, options, deps) {
11687
11694
  oldParentKey,
11688
11695
  targetIndexInGrandparent: targetIdx
11689
11696
  });
11697
+ plannedIds.add(key);
11690
11698
  touched.add(String(key));
11691
11699
  touched.add(String(oldParentKey));
11692
11700
  if (grandparentKey !== ROOT_PARENT_ID) touched.add(String(grandparentKey));
@@ -12376,6 +12384,7 @@ function dispatchPersistenceAcknowledge(action, deps) {
12376
12384
  for (const assignment of activities) {
12377
12385
  const before = deps.adapter.getActivity(assignment.id);
12378
12386
  if (!before || before.proplannerId === assignment.proplannerId) continue;
12387
+ const previousProplannerId = before.proplannerId;
12379
12388
  deps.adapter.setActivityField(
12380
12389
  assignment.id,
12381
12390
  "proplannerId",
@@ -12386,7 +12395,7 @@ function dispatchPersistenceAcknowledge(action, deps) {
12386
12395
  kind: "updated",
12387
12396
  fields: {
12388
12397
  proplannerId: {
12389
- before: before.proplannerId,
12398
+ before: previousProplannerId,
12390
12399
  after: assignment.proplannerId
12391
12400
  }
12392
12401
  },
@@ -12396,13 +12405,14 @@ function dispatchPersistenceAcknowledge(action, deps) {
12396
12405
  for (const assignment of links) {
12397
12406
  const before = deps.adapter.getLink(assignment.id);
12398
12407
  if (!before || before.proplannerId === assignment.proplannerId) continue;
12408
+ const previousProplannerId = before.proplannerId;
12399
12409
  deps.adapter.setLinkProplannerId(assignment.id, assignment.proplannerId);
12400
12410
  linkChanges.push({
12401
12411
  id: assignment.id,
12402
12412
  kind: "updated",
12403
12413
  fields: {
12404
12414
  proplannerId: {
12405
- before: before.proplannerId,
12415
+ before: previousProplannerId,
12406
12416
  after: assignment.proplannerId
12407
12417
  }
12408
12418
  },
@@ -17033,7 +17043,6 @@ var PERSISTABLE_ACTIVITY_FIELDS = [
17033
17043
  "isLookahead",
17034
17044
  "newActivityIds",
17035
17045
  "hasNewActivities",
17036
- "pendingRequestIds",
17037
17046
  "responsableIds",
17038
17047
  "tagIds"
17039
17048
  ];
@@ -17045,7 +17054,6 @@ var DATE_FIELDS2 = /* @__PURE__ */ new Set([
17045
17054
  ]);
17046
17055
  var ID_ARRAY_FIELDS = /* @__PURE__ */ new Set([
17047
17056
  "newActivityIds",
17048
- "pendingRequestIds",
17049
17057
  "responsableIds",
17050
17058
  "tagIds"
17051
17059
  ]);
@@ -17103,10 +17111,15 @@ var SaveTracker = class {
17103
17111
  this.activitiesAtLastSave.clear();
17104
17112
  for (const activity of activities) {
17105
17113
  if (activity.proplannerId == null) continue;
17106
- this.activitiesAtLastSave.set(
17107
- String(activity.id),
17108
- projectPersistableActivity(activity)
17109
- );
17114
+ this.activitiesAtLastSave.set(String(activity.id), {
17115
+ persistable: projectPersistableActivity(activity),
17116
+ deletedSnapshot: {
17117
+ id: activity.id,
17118
+ proplannerId: activity.proplannerId,
17119
+ parentId: activity.parentId,
17120
+ hadBaseline: activity.baselineSnapshot !== null || activity.baselinePoints.length > 0
17121
+ }
17122
+ });
17110
17123
  }
17111
17124
  }
17112
17125
  snapshotLinks(links) {
@@ -17114,11 +17127,51 @@ var SaveTracker = class {
17114
17127
  for (const link of links) {
17115
17128
  if (link.proplannerId == null) continue;
17116
17129
  this.linksAtLastSave.set(String(link.id), {
17130
+ id: link.id,
17131
+ proplannerId: link.proplannerId,
17132
+ source: link.source,
17133
+ target: link.target,
17117
17134
  lag: link.lag,
17118
17135
  type: link.type
17119
17136
  });
17120
17137
  }
17121
17138
  }
17139
+ hasUnsavedChanges(activities, links) {
17140
+ if (!this.initialized) return false;
17141
+ if (this.deletedActivities(activities).length > 0) return true;
17142
+ if (this.deletedLinks(links).length > 0) return true;
17143
+ const activityChanged = activities.some((activity) => {
17144
+ if (activity.proplannerId == null) return true;
17145
+ const saved = this.activitiesAtLastSave.get(String(activity.id));
17146
+ return !saved || activityChangedSince(activity, saved.persistable);
17147
+ });
17148
+ if (activityChanged) return true;
17149
+ return links.some((link) => {
17150
+ if (link.proplannerId == null) return true;
17151
+ const saved = this.linksAtLastSave.get(String(link.id));
17152
+ return !saved || link.lag !== saved.lag || link.type !== saved.type;
17153
+ });
17154
+ }
17155
+ deletedActivities(activities) {
17156
+ if (!this.initialized) return [];
17157
+ const currentIds = new Set(
17158
+ activities.map((activity) => String(activity.id))
17159
+ );
17160
+ const deleted = [];
17161
+ for (const [activityId, saved] of this.activitiesAtLastSave) {
17162
+ if (currentIds.has(activityId)) continue;
17163
+ deleted.push({ ...saved.deletedSnapshot });
17164
+ }
17165
+ return deleted;
17166
+ }
17167
+ deletedLinks(links) {
17168
+ if (!this.initialized) return [];
17169
+ const currentIds = new Set(links.map((link) => String(link.id)));
17170
+ return Array.from(
17171
+ this.linksAtLastSave.entries(),
17172
+ ([linkId, saved]) => currentIds.has(linkId) ? null : { ...saved }
17173
+ ).filter((link) => link !== null);
17174
+ }
17122
17175
  modifiedActivities(activities) {
17123
17176
  if (!this.initialized) return [];
17124
17177
  return activities.filter((activity) => {
@@ -17126,7 +17179,7 @@ var SaveTracker = class {
17126
17179
  if (activity.assignedDefaultCalendar) return true;
17127
17180
  const saved = this.activitiesAtLastSave.get(String(activity.id));
17128
17181
  if (!saved) return true;
17129
- return activityChangedSince(activity, saved);
17182
+ return activityChangedSince(activity, saved.persistable);
17130
17183
  });
17131
17184
  }
17132
17185
  modifiedLinks(links) {
@@ -17286,7 +17339,7 @@ function parseActivity(raw, context) {
17286
17339
  hasCustomPonderator: raw.hasCustomPonderator,
17287
17340
  correlativeId: raw.correlative_id,
17288
17341
  uniqueCorrelativeId: raw.unique_correlative_id,
17289
- customId: raw.custom_id,
17342
+ customId: normalizeCustomId(raw),
17290
17343
  subcontractId: raw.subcontractId,
17291
17344
  isLookahead: raw.isOnLookahead,
17292
17345
  hasLookaheadTasks: Array.isArray(raw.tasks) && raw.tasks.length > 0,
@@ -17310,6 +17363,9 @@ function parseActivity(raw, context) {
17310
17363
  criticalPath: null
17311
17364
  };
17312
17365
  }
17366
+ function normalizeCustomId(raw) {
17367
+ return raw.has_childs ? null : raw.custom_id;
17368
+ }
17313
17369
  function forceRootProjectToRoot(activities) {
17314
17370
  const rootProject = activities.find(
17315
17371
  (activity) => activity.correlativeId === 0 && activity.type === "project"
@@ -18721,6 +18777,21 @@ var ScheduleCore = class {
18721
18777
  this.assertReady();
18722
18778
  return this._saveTracker.modifiedActivities(this.coreRuntime.state.getAllActivities()).map(cloneDomainValue);
18723
18779
  }
18780
+ hasUnsavedChanges() {
18781
+ this.assertReady();
18782
+ return this._saveTracker.hasUnsavedChanges(
18783
+ this.coreRuntime.state.getAllActivities(),
18784
+ this.getAllLinksView()
18785
+ );
18786
+ }
18787
+ getDeletedActivitiesSinceLastSave() {
18788
+ this.assertReady();
18789
+ return this._saveTracker.deletedActivities(this.coreRuntime.state.getAllActivities()).map(cloneDomainValue);
18790
+ }
18791
+ getDeletedLinksSinceLastSave() {
18792
+ this.assertReady();
18793
+ return this._saveTracker.deletedLinks(this.getAllLinksView()).map(cloneDomainValue);
18794
+ }
18724
18795
  _enqueue(work) {
18725
18796
  const workPromise = this._opQueue.then(work, work);
18726
18797
  this._opQueue = workPromise.then(
@@ -19041,7 +19112,7 @@ var ScheduleCore = class {
19041
19112
  applyStatusPass(state, this.coreRuntime.sector.statusCriteria);
19042
19113
  }
19043
19114
  }
19044
- undo() {
19115
+ undo(options = {}) {
19045
19116
  const operation = this._enqueue(async () => {
19046
19117
  this.assertReady();
19047
19118
  const entry = this._undo.takeUndo();
@@ -19061,7 +19132,7 @@ var ScheduleCore = class {
19061
19132
  });
19062
19133
  void operation.then(
19063
19134
  (changes) => {
19064
- if (changes) {
19135
+ if (changes && options.skipCriticalPath !== true) {
19065
19136
  void this.recomputeCriticalPath().catch((error) => {
19066
19137
  this.coreRuntime.reporter.error(
19067
19138
  "[ScheduleCore] Critical Path recompute after undo failed",
@@ -19074,7 +19145,7 @@ var ScheduleCore = class {
19074
19145
  );
19075
19146
  return operation;
19076
19147
  }
19077
- redo() {
19148
+ redo(options = {}) {
19078
19149
  const operation = this._enqueue(async () => {
19079
19150
  this.assertReady();
19080
19151
  const entry = this._undo.takeRedo();
@@ -19094,7 +19165,7 @@ var ScheduleCore = class {
19094
19165
  });
19095
19166
  void operation.then(
19096
19167
  (changes) => {
19097
- if (changes) {
19168
+ if (changes && options.skipCriticalPath !== true) {
19098
19169
  void this.recomputeCriticalPath().catch((error) => {
19099
19170
  this.coreRuntime.reporter.error(
19100
19171
  "[ScheduleCore] Critical Path recompute after redo failed",