@outbuild-company/schedule-core 1.2.0 → 1.4.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 +1105 -1388
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -822
- package/dist/index.d.ts +119 -822
- package/dist/index.js +1105 -1388
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,95 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types for the custom AutoScheduler and its satellite packages
|
|
3
|
-
* (recording, replay, shadow, discovery).
|
|
4
|
-
*
|
|
5
|
-
* This file is the single seam between all components. All scheduler code
|
|
6
|
-
* depends ONLY on these types — never on the DHTMLX gantt object directly.
|
|
7
|
-
*/
|
|
8
|
-
type ActivityId$1 = string;
|
|
9
|
-
type LinkId$1 = string;
|
|
10
|
-
/** Value constants for link types. Matches `gantt.config.links`. */
|
|
11
1
|
declare const LINK_TYPE: {
|
|
12
2
|
readonly FINISH_TO_START: "0";
|
|
13
3
|
readonly START_TO_START: "1";
|
|
14
4
|
readonly FINISH_TO_FINISH: "2";
|
|
15
5
|
readonly START_TO_FINISH: "3";
|
|
16
6
|
};
|
|
17
|
-
interface LinkSnapshot {
|
|
18
|
-
id: LinkId$1;
|
|
19
|
-
source: ActivityId$1;
|
|
20
|
-
target: ActivityId$1;
|
|
21
|
-
type: LinkType;
|
|
22
|
-
lag: number;
|
|
23
|
-
/**
|
|
24
|
-
* Precomputed lag components for a VIRTUAL summary-expanded link. When
|
|
25
|
-
* present, the link calculator uses them verbatim instead of deriving the
|
|
26
|
-
* components from leaf durations (the normal `normalize` switch).
|
|
27
|
-
*
|
|
28
|
-
* These exist so an FF/SF link INTO a summary can carry the SUMMARY's
|
|
29
|
-
* duration (`_targetLag = -summary.duration`) and the per-leaf offset from
|
|
30
|
-
* the summary's start (`_trueLag = userLag + off_tgt(leaf)`), matching legacy
|
|
31
|
-
* `_getImplicitLinks`/`_convertToFinishToStartLink` under
|
|
32
|
-
* `auto_scheduling_move_projects`. `_targetLag`/`_trueLag` are computed once
|
|
33
|
-
* at expansion time, matching legacy relations frozen per run, and the
|
|
34
|
-
* per-leaf offset in `_trueLag` applies only to leaves with no incoming
|
|
35
|
-
* links (legacy `!c.$target.length` — a leaf with its own driver keeps
|
|
36
|
-
* offset 0, otherwise the frozen offset compounds on every settle).
|
|
37
|
-
* `_sourceLag` is the expansion-time fallback: when `_summarySourceId` is
|
|
38
|
-
* present (SS/SF out of a summary), the ASAP calculator re-derives it at
|
|
39
|
-
* evaluation time from the summary's CURRENT derived bounds so the virtual
|
|
40
|
-
* constraint stays anchored at the summary start even after the anchor leaf
|
|
41
|
-
* moves within the same run.
|
|
42
|
-
*
|
|
43
|
-
* `_summaryTargetId` marks a virtual link whose `_trueLag` bakes a NON-ZERO
|
|
44
|
-
* leaf offset measured from the target summary's expansion-time bounds: the
|
|
45
|
-
* ASAP pass refuses to pull the leaf back through such a link unless the
|
|
46
|
-
* predecessor itself moved in the run (a stale frozen offset must never
|
|
47
|
-
* drag its own leaf earlier — that is the summary-link drift bug).
|
|
48
|
-
*
|
|
49
|
-
* The three lag components must be set together (or none): a half-populated
|
|
50
|
-
* link would mix precomputed and derived components. Every non-virtual link
|
|
51
|
-
* leaves them undefined and hits the fast path.
|
|
52
|
-
*/
|
|
53
|
-
_sourceLag?: number;
|
|
54
|
-
_targetLag?: number;
|
|
55
|
-
_trueLag?: number;
|
|
56
|
-
_summarySourceId?: ActivityId$1;
|
|
57
|
-
_summaryTargetId?: ActivityId$1;
|
|
58
|
-
ganttId?: number;
|
|
59
|
-
sectorId?: number;
|
|
60
|
-
/** BE row id (distinct from the schedule-level `id`). */
|
|
61
|
-
proplannerId?: number;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Constraint Rules — single source of truth.
|
|
66
|
-
*
|
|
67
|
-
* This module owns the definition of constraint types and the rules that
|
|
68
|
-
* govern them. Both the autoscheduler and the manual scheduling pipelines
|
|
69
|
-
* (column edits, drag, bulk) consume the same primitives from here.
|
|
70
|
-
*
|
|
71
|
-
* Two surfaces:
|
|
72
|
-
*
|
|
73
|
-
* (a) Manual scheduling — pipelines call `checkConstraintViolation()` after
|
|
74
|
-
* a user types a new value, to decide whether to emit a warning.
|
|
75
|
-
* Surface: scalar Date inputs, no adapter needed.
|
|
76
|
-
*
|
|
77
|
-
* (b) Autoscheduler — `computeConstraintBounds()` derives the four date
|
|
78
|
-
* boundaries (earliest/latest × start/end) an activity's constraint
|
|
79
|
-
* imposes. `isStartWithinBounds()` and `isEndWithinBounds()` are used
|
|
80
|
-
* by `limitPlanDates()` to decide whether a candidate date (from a
|
|
81
|
-
* link) respects the constraint.
|
|
82
|
-
*
|
|
83
|
-
* Domain invariant the autoscheduler must respect:
|
|
84
|
-
* The autoscheduler never moves an activity to a date that violates that
|
|
85
|
-
* activity's own constraint. If a cascade via a link would push the
|
|
86
|
-
* activity outside its constraint bounds, the activity stays put.
|
|
87
|
-
*
|
|
88
|
-
* Manual scheduling does NOT enforce this — a user is allowed to type a
|
|
89
|
-
* value that violates their own constraint. The pipeline applies the edit
|
|
90
|
-
* and emits a warning; the UI decides what to do with the warning (typically
|
|
91
|
-
* shows a "constraint violation detected" modal and offers to revert).
|
|
92
|
-
*/
|
|
93
7
|
|
|
94
8
|
type ConstraintType = 'asap' | 'alap' | 'snet' | 'snlt' | 'fnet' | 'fnlt' | 'mso' | 'mfo';
|
|
95
9
|
declare const CONSTRAINT_TYPE: {
|
|
@@ -103,104 +17,44 @@ declare const CONSTRAINT_TYPE: {
|
|
|
103
17
|
readonly MFO: "mfo";
|
|
104
18
|
};
|
|
105
19
|
|
|
106
|
-
/**
|
|
107
|
-
* Public internal model types — the normalized form the core works with
|
|
108
|
-
* after parsing the backend payload.
|
|
109
|
-
*
|
|
110
|
-
* Field naming follows snake_case for entity fields (matches backend +
|
|
111
|
-
* DHTMLX convention) and camelCase for everything else, per Q14 in
|
|
112
|
-
* SCHEDULE_CORE_API.md.
|
|
113
|
-
*/
|
|
114
20
|
type ActivityId = string;
|
|
115
21
|
type LinkId = string;
|
|
116
22
|
type CalendarId = string;
|
|
117
23
|
type ActivityType = 'project' | 'task' | 'milestone';
|
|
118
24
|
|
|
119
|
-
/** DHTMLX link kinds: 0 = FS, 1 = SS, 2 = FF, 3 = SF. */
|
|
120
25
|
type LinkType = '0' | '1' | '2' | '3';
|
|
121
|
-
/** Direction for closest-work-time snapping. */
|
|
122
26
|
type WorkTimeDirection = 'future' | 'past';
|
|
123
27
|
declare const WORK_TIME_DIRECTION: {
|
|
124
28
|
readonly FUTURE: "future";
|
|
125
29
|
readonly PAST: "past";
|
|
126
30
|
};
|
|
127
|
-
/** Calendar query granularity (the engine boundary adds 'minute'/'any'). */
|
|
128
31
|
type CalendarUnit = 'hour' | 'day';
|
|
129
32
|
declare const CALENDAR_UNIT: {
|
|
130
33
|
readonly HOUR: "hour";
|
|
131
34
|
readonly DAY: "day";
|
|
132
35
|
};
|
|
133
|
-
/**
|
|
134
|
-
* Project-level criterion used to redistribute `ponderator` (weight) among
|
|
135
|
-
* sibling activities. Backend stores it lowercase on `projects.activity_creter`
|
|
136
|
-
* (`'duration' | 'cost' | 'hh'`, default `'duration'`); the core normalizes
|
|
137
|
-
* to uppercase. See `getActiveBaseline` / `recomputePonderatorsForParent`.
|
|
138
|
-
*/
|
|
139
36
|
type ActivityCreter = 'DURATION' | 'COST' | 'HH';
|
|
140
|
-
/** Criterion for the `status` column: compare progress against baseline or live expected. */
|
|
141
37
|
type StatusCriteria = 'Baseline' | 'Actual';
|
|
142
|
-
/**
|
|
143
|
-
* A versioned container of baseline points for a sector
|
|
144
|
-
* (backend `sectorbaselineversion` model). Exactly one version per sector
|
|
145
|
-
* has `active: true` — that is the one the ponderator computation and the
|
|
146
|
-
* baseline columns read.
|
|
147
|
-
*/
|
|
148
38
|
interface BaselineVersion {
|
|
149
39
|
id?: number;
|
|
150
40
|
name?: string | null;
|
|
151
|
-
/** Marks the live baseline. Exactly one active version per sector. */
|
|
152
41
|
active?: boolean;
|
|
153
42
|
visible?: boolean;
|
|
154
|
-
/** Backend extras passed through opaquely. */
|
|
155
43
|
[key: string]: unknown;
|
|
156
44
|
}
|
|
157
|
-
/**
|
|
158
|
-
* A single baseline data point for an activity, as stored by the backend
|
|
159
|
-
* (`sectorbaselinepoint` model) and carried verbatim through the core.
|
|
160
|
-
* Snapshots the activity's planned start/end/duration/cost/work at the
|
|
161
|
-
* moment a baseline version was saved. The schedule reads the ACTIVE point
|
|
162
|
-
* (see `getActiveBaseline`) to derive `ponderator` (baseline-only, no
|
|
163
|
-
* fallback — see memory `project_ponderator_baseline_fix`).
|
|
164
|
-
*
|
|
165
|
-
* Casing note: the frontend baseline columns (`startBaseCol` …
|
|
166
|
-
* `durationBaseCol`) read these fields directly off the activity, so the
|
|
167
|
-
* core preserves the backend snake_case exactly (passthrough, not parsed).
|
|
168
|
-
*
|
|
169
|
-
* Unit trap: `duration` is in DAYS (backend unit), unlike the activity's
|
|
170
|
-
* `duration` which the core models in working hours — convert with the
|
|
171
|
-
* sector's hours-per-day before arithmetic (legacy `transformDaysToHours`).
|
|
172
|
-
*/
|
|
173
45
|
interface BaselinePoint {
|
|
174
|
-
/** Planned start, backend date string `"YYYY/MM/DD H:MM"` or ISO. */
|
|
175
46
|
start_date: string;
|
|
176
|
-
/** Planned end (inclusive). */
|
|
177
47
|
end_date: string;
|
|
178
|
-
/** Planned duration in DAYS (backend unit). */
|
|
179
48
|
duration: number;
|
|
180
|
-
/** Planned cost. May arrive as a numeric string. */
|
|
181
49
|
cost: number | string;
|
|
182
|
-
/** Planned work hours (HH). May arrive as a numeric string. */
|
|
183
50
|
hh_work: number | string;
|
|
184
|
-
/** The version this point belongs to; `.active` selects the live one. */
|
|
185
51
|
sectorbaselineversion?: BaselineVersion;
|
|
186
52
|
sectorbaselineversionId?: number;
|
|
187
53
|
baseCalendarId?: number;
|
|
188
54
|
hoursPerDay?: number;
|
|
189
55
|
hoursPerWeek?: number;
|
|
190
|
-
/** Backend extras passed through opaquely. */
|
|
191
56
|
[key: string]: unknown;
|
|
192
57
|
}
|
|
193
|
-
/**
|
|
194
|
-
* Pure-domain activity model produced by `parseFromBackend`. Snake_case
|
|
195
|
-
* matches the backend convention. No index signature — every field is
|
|
196
|
-
* explicit. New backend fields must be added here and preserved by
|
|
197
|
-
* `normalize-activity.ts`.
|
|
198
|
-
*/
|
|
199
|
-
/**
|
|
200
|
-
* A pending SIR freezing an activity, reduced to the only field the core
|
|
201
|
-
* needs: its id (equal to the backend SIR id and the front's Redux item id).
|
|
202
|
-
* The schedule load already filters to pending, so presence ⇒ pending.
|
|
203
|
-
*/
|
|
204
58
|
interface PendingRequest {
|
|
205
59
|
id: string;
|
|
206
60
|
}
|
|
@@ -234,7 +88,6 @@ interface CoreBaselinePoint {
|
|
|
234
88
|
workHours: number;
|
|
235
89
|
versionId: number | null;
|
|
236
90
|
isActiveVersion: boolean;
|
|
237
|
-
/** Whether this version is the one selected for visual comparison. */
|
|
238
91
|
isVisibleVersion: boolean;
|
|
239
92
|
baseCalendarId: CalendarId | null;
|
|
240
93
|
hoursPerDay: number | null;
|
|
@@ -294,79 +147,25 @@ interface Link {
|
|
|
294
147
|
lag: number;
|
|
295
148
|
ganttId?: number;
|
|
296
149
|
sectorId?: number;
|
|
297
|
-
/** BE row id (distinct from the schedule-level `id`/`unique_id`). */
|
|
298
150
|
proplannerId?: number;
|
|
299
151
|
}
|
|
300
152
|
interface CalendarWorktime {
|
|
301
|
-
/**
|
|
302
|
-
* Per-weekday working hours, length 7 — index 0 = Sunday.
|
|
303
|
-
*
|
|
304
|
-
* - `false` → non-working day
|
|
305
|
-
* - `string[]` → working day with these `"HH:MM-HH:MM"` windows
|
|
306
|
-
* (supports per-day variation, e.g. a short Friday,
|
|
307
|
-
* and multi-shift split days like `["8-12","13-17"]`)
|
|
308
|
-
*
|
|
309
|
-
* Single source of truth: this is exactly what `parseFromBackend` emits. A
|
|
310
|
-
* usable working day carries its resolved windows; `[]` is reserved for a
|
|
311
|
-
* catalog calendar that declares the day as working but provides no usable
|
|
312
|
-
* shift anywhere. Those catalog-only calendars remain selectable/persistable
|
|
313
|
-
* but are deliberately not registered as arithmetic calendars. The vendored
|
|
314
|
-
* work-calendar engine natively also accepts a boolean `true` per day (DHTMLX
|
|
315
|
-
* contract), but that form never appears in the domain `Calendar`; it is
|
|
316
|
-
* reserved for the engine-facing internals (`SnapshotWorktime`,
|
|
317
|
-
* synthetic replay fixtures).
|
|
318
|
-
*
|
|
319
|
-
* The work-calendar engine consumes each weekday independently, so
|
|
320
|
-
* different days can carry different windows.
|
|
321
|
-
*/
|
|
322
153
|
days: Array<false | string[]>;
|
|
323
|
-
/**
|
|
324
|
-
* Default working-hour windows (`"HH:MM-HH:MM"`). Used as the engine's
|
|
325
|
-
* global fallback. `parseFromBackend` sets it to the first usable working
|
|
326
|
-
* day's windows, or `[]` when the calendar has no usable shift.
|
|
327
|
-
*/
|
|
328
154
|
hours: string[];
|
|
329
|
-
/**
|
|
330
|
-
* Per-date exceptions: holidays (non-working) and date-level shift
|
|
331
|
-
* overrides. Keys are `Date.UTC(year, month, day)` (UTC midnight epoch
|
|
332
|
-
* ms) stringified — this matches the lookup the work-calendar engine
|
|
333
|
-
* performs internally at `calendar.dates[dateValue]`.
|
|
334
|
-
*
|
|
335
|
-
* Value semantics:
|
|
336
|
-
* - `false` → the day is non-working
|
|
337
|
-
* - `string[]` → use these `"HH:MM-HH:MM"` windows instead
|
|
338
|
-
* of the default `hours`
|
|
339
|
-
*
|
|
340
|
-
* Source: `BackendCalendarInput.exceptiondays[]` after the adapter expands
|
|
341
|
-
* `from_date`/`to_date` ranges into individual UTC days.
|
|
342
|
-
*/
|
|
343
155
|
dates?: Record<string, false | string[]>;
|
|
344
|
-
/** Optional per-week overrides for advanced calendars. */
|
|
345
156
|
customWeeks?: Record<string, unknown>;
|
|
346
157
|
}
|
|
347
158
|
interface Calendar {
|
|
348
159
|
id: CalendarId;
|
|
349
|
-
/** Display name. Used by the DHTMLX calendar-selector column. */
|
|
350
160
|
name: string;
|
|
351
161
|
worktime: CalendarWorktime;
|
|
352
162
|
is_default: boolean;
|
|
353
|
-
/**
|
|
354
|
-
* True for "base" calendars (baseline comparison). The backend ships
|
|
355
|
-
* these with an `id` suffixed `-base` and `baseDefault` instead of
|
|
356
|
-
* `is_default`. The bridge uses this to set `gantt.defaultBaseCalendar`
|
|
357
|
-
* and to keep base calendars out of the selector dropdown.
|
|
358
|
-
*/
|
|
359
163
|
baseDefault?: boolean;
|
|
360
|
-
/**
|
|
361
|
-
* Date-keyed map of exceptions (holidays, working overrides).
|
|
362
|
-
* Keys are `YYYY-MM-DD` UTC.
|
|
363
|
-
*/
|
|
364
164
|
exceptions?: Record<string, {
|
|
365
165
|
working: boolean;
|
|
366
166
|
hours?: string[];
|
|
367
167
|
}>;
|
|
368
168
|
}
|
|
369
|
-
/** Sector metadata the core respects from the backend. */
|
|
370
169
|
interface SectorMetadata {
|
|
371
170
|
id: string;
|
|
372
171
|
name: string;
|
|
@@ -375,48 +174,12 @@ interface SectorMetadata {
|
|
|
375
174
|
dateFormat: string;
|
|
376
175
|
projectId: number;
|
|
377
176
|
companyId: number;
|
|
378
|
-
/**
|
|
379
|
-
* Project's `custom_id_prefix` (Outbuild project setting). Used by the
|
|
380
|
-
* CustomIdTracker to generate ids for new activities. Falls back to
|
|
381
|
-
* `'A'` if not provided.
|
|
382
|
-
*/
|
|
383
177
|
customIdPrefix?: string;
|
|
384
|
-
/**
|
|
385
|
-
* Project's `custom_id_increment` (Outbuild project setting), as a
|
|
386
|
-
* string so leading zeros are preserved. Used by the CustomIdTracker
|
|
387
|
-
* to choose the next suffix step. Falls back to `'10'` if not provided.
|
|
388
|
-
*/
|
|
389
178
|
customIdIncrement?: string;
|
|
390
|
-
/**
|
|
391
|
-
* Legacy Primavera-import flag. When true, the core recomputes each
|
|
392
|
-
* activity's `duration` from its start/end against its calendar at load
|
|
393
|
-
* (Primavera stores end-driven durations that may not match the
|
|
394
|
-
* calendar). Mirrors the legacy `fixPrimaveraDurations`. See CALENDARS.md.
|
|
395
|
-
*/
|
|
396
179
|
updateDurationForPrimaveraEndDate?: boolean;
|
|
397
|
-
/**
|
|
398
|
-
* Project's `activity_creter` — criterion for ponderator redistribution
|
|
399
|
-
* (`'DURATION' | 'COST' | 'HH'`). Lives at the project level; arrives via
|
|
400
|
-
* the sector payload's pass-through bucket. `parseSector` always sets it,
|
|
401
|
-
* defaulting to `'DURATION'` (the backend default) when absent/unknown.
|
|
402
|
-
*/
|
|
403
180
|
activityCreter?: ActivityCreter;
|
|
404
|
-
/**
|
|
405
|
-
* Project's status criterion for the `status` column — `'Baseline'` compares
|
|
406
|
-
* `progress` against `expected_progress_base`, `'Actual'` against
|
|
407
|
-
* `expected_progress`. Mutable at runtime via `ScheduleCore.setStatusCriteria`
|
|
408
|
-
* (the Baseline↔Actual toggle). Not a backend field (client-only session
|
|
409
|
-
* setting); a complete sector always carries it — `parseSector` sets the
|
|
410
|
-
* default and `resolveStatusCriteria` normalizes the client override at init.
|
|
411
|
-
* See `docs/domain/columns/COLUMN_status.md`.
|
|
412
|
-
*/
|
|
413
181
|
statusCriteria: StatusCriteria;
|
|
414
182
|
}
|
|
415
|
-
/**
|
|
416
|
-
* A single entity mutation record emitted by a dispatch ChangeSet.
|
|
417
|
-
* Layer-neutral: contains no dispatch-specific dependencies and is used
|
|
418
|
-
* both by `dispatch/` and by `internal/tracking/`.
|
|
419
|
-
*/
|
|
420
183
|
interface EntityChange<T> {
|
|
421
184
|
id: string;
|
|
422
185
|
kind: 'created' | 'updated' | 'deleted';
|
|
@@ -470,45 +233,61 @@ declare const COLUMN: {
|
|
|
470
233
|
};
|
|
471
234
|
type ColumnName = (typeof COLUMN)[keyof typeof COLUMN];
|
|
472
235
|
|
|
473
|
-
/**
|
|
474
|
-
* Task creation pipeline — types.
|
|
475
|
-
*
|
|
476
|
-
* Ports the pure parts of `src/assets/js/custom_actions/createTask/` into a
|
|
477
|
-
* functional pipeline that replay tests (and eventually prod) can invoke
|
|
478
|
-
* against an adapter instead of the live gantt.
|
|
479
|
-
*/
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Closed, canonical contract for create/paste overrides: camelCase, working
|
|
483
|
-
* hours, limited to the fields a caller may seed on a new activity. Identity
|
|
484
|
-
* and structure (`id`/`parentId`/`correlativeId`/`uniqueCorrelativeId`),
|
|
485
|
-
* derived/CP/baseline outputs and view-state are NOT overridable. PP2 adapts
|
|
486
|
-
* its own vocabulary to this contract in step 9.
|
|
487
|
-
*/
|
|
488
236
|
declare const CREATION_OVERRIDE_FIELD_LIST: readonly ["name", "description", "type", "startDate", "durationHours", "constraintType", "constraintDate", "calendarId", "assignedDefaultCalendar", "autoScheduling", "progress", "cost", "usedCost", "realCost", "workHours", "realWorkHours", "ponderator", "hasCustomPonderator", "customId", "subcontractId", "responsableIds", "tagIds", "isLookahead"];
|
|
489
237
|
type CreationOverrideField = (typeof CREATION_OVERRIDE_FIELD_LIST)[number];
|
|
490
238
|
type CreationOverrides = Partial<Pick<CoreActivity, CreationOverrideField>>;
|
|
491
239
|
|
|
240
|
+
type StringOperator = 'includes' | 'notIncludes' | 'is' | 'isNot';
|
|
241
|
+
type NumberOperator = 'equals' | 'notEquals' | 'greaterThan' | 'lessThan' | 'greaterOrEqual' | 'lessOrEqual';
|
|
242
|
+
type DateOperator = 'after' | 'before';
|
|
243
|
+
type MembershipOperator = 'someOf' | 'notSomeOf';
|
|
244
|
+
type FilterOperator = StringOperator | NumberOperator | DateOperator | MembershipOperator;
|
|
245
|
+
type FilterField = 'name' | 'description' | 'customId' | 'correlativeId' | 'uniqueCorrelativeId' | 'progress' | 'durationDays' | 'calendarDuration' | 'cost' | 'usedCost' | 'realCost' | 'workHours' | 'realWorkHours' | 'ponderator' | 'freeSlackDays' | 'totalSlackDays' | 'expectedProgressBaseline' | 'baselineDurationDays' | 'baselineCost' | 'baselineWorkHours' | 'startDate' | 'endDate' | 'constraintDate' | 'baselineStartDate' | 'baselineEndDate' | 'earlyStart' | 'earlyFinish' | 'lateStart' | 'lateFinish' | 'responsableIds' | 'tagIds' | 'status' | 'constraintType' | 'calendarId' | 'subcontractId' | 'isCritical';
|
|
246
|
+
type FilterScalarValue = string | number | Date;
|
|
247
|
+
type FilterCriterionValue = FilterScalarValue | ReadonlyArray<string | number>;
|
|
248
|
+
interface FilterCriterion {
|
|
249
|
+
readonly field: FilterField;
|
|
250
|
+
readonly operator: FilterOperator;
|
|
251
|
+
readonly value: FilterCriterionValue;
|
|
252
|
+
}
|
|
253
|
+
interface FilterDateRange {
|
|
254
|
+
readonly start: Date;
|
|
255
|
+
readonly end: Date;
|
|
256
|
+
}
|
|
257
|
+
interface FilterState {
|
|
258
|
+
readonly criteria: ReadonlyArray<FilterCriterion>;
|
|
259
|
+
readonly logic: 'and' | 'or';
|
|
260
|
+
readonly dateRange?: FilterDateRange;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
interface OrderRule {
|
|
264
|
+
/**
|
|
265
|
+
* Deliberately FilterField, not an alias of it: the columns a user can sort by
|
|
266
|
+
* are the columns a user can filter by, and the filter's registry already
|
|
267
|
+
* resolves units, baseline snapshots and critical-path values for all of them.
|
|
268
|
+
* A second field vocabulary would drift from this one on the first new column.
|
|
269
|
+
*/
|
|
270
|
+
readonly field: FilterField;
|
|
271
|
+
readonly direction: 'asc' | 'desc';
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* The active ordering, as an ordered list of keys: rule 0 decides, rule 1 breaks
|
|
275
|
+
* its ties, and so on. An empty list means "no user ordering", which is stored
|
|
276
|
+
* as null rather than an empty state (mirroring isEmptyFilter).
|
|
277
|
+
*/
|
|
278
|
+
interface OrderState {
|
|
279
|
+
readonly rules: ReadonlyArray<OrderRule>;
|
|
280
|
+
}
|
|
492
281
|
/**
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
496
|
-
* - `inline-edit` — edit a single column on an activity.
|
|
497
|
-
* - `link-create` — add a link between two activities.
|
|
498
|
-
* - `link-update` — change the lag and/or type of an existing link.
|
|
499
|
-
* - `link-delete` — remove a link by id.
|
|
500
|
-
* - `activity-create` — create a new activity (root, child, or sibling).
|
|
501
|
-
* - `activity-delete` — cascade-delete activities + incident links.
|
|
502
|
-
* - `activity-move` — reorder among siblings or move to a different parent.
|
|
503
|
-
* - `activity-indent` — multi-select indent (children of previous non-selected sibling).
|
|
504
|
-
* - `activity-outdent` — multi-select outdent (move to grandparent level).
|
|
505
|
-
* - `activity-set-progress` — complete/uncomplete (0/100) with cascade.
|
|
506
|
-
* - `selection-toggle` / `selection-replace` — checkbox selection (prop-only).
|
|
507
|
-
* - `activity-paste` — bulk paste (N activities + M links, one atomic ChangeSet).
|
|
508
|
-
* - `dates-batch` — K date edits as one unit of work (multi-drag).
|
|
509
|
-
* - `bulk-edit` — K edits on any pipeline column as one unit of work (bulk editors).
|
|
510
|
-
* - `links-batch` — K link create/update/delete as one atomic unit (mass link/unlink buttons).
|
|
282
|
+
* One resequenced sibling group. Ordering is hierarchical: an activity only ever
|
|
283
|
+
* competes with its own siblings, so a change is expressed per parent rather
|
|
284
|
+
* than as a flat sequence over the whole schedule.
|
|
511
285
|
*/
|
|
286
|
+
interface BranchOrderChange {
|
|
287
|
+
readonly parentId: string;
|
|
288
|
+
readonly childIds: ReadonlyArray<string>;
|
|
289
|
+
}
|
|
290
|
+
|
|
512
291
|
type LinksBatchOperation = {
|
|
513
292
|
kind: 'create';
|
|
514
293
|
source: ActivityId;
|
|
@@ -549,13 +328,6 @@ type DispatchAction = {
|
|
|
549
328
|
column: string;
|
|
550
329
|
newValue: unknown;
|
|
551
330
|
} | {
|
|
552
|
-
/**
|
|
553
|
-
* K ediciones de fecha como UNA unidad de trabajo (multi-drag): cada
|
|
554
|
-
* edit corre su pipeline individual (verdict por barra), pero el
|
|
555
|
-
* post-mutation (autoscheduler → bounds → CP) corre UNA sola vez y
|
|
556
|
-
* sale UN ChangeSet. Patrón hermano de `activity-paste` /
|
|
557
|
-
* `dispatchInlineEditLinks`. Track onAfterTaskDrag F2.2.
|
|
558
|
-
*/
|
|
559
331
|
kind: 'dates-batch';
|
|
560
332
|
edits: ReadonlyArray<{
|
|
561
333
|
activityId: ActivityId;
|
|
@@ -563,21 +335,6 @@ type DispatchAction = {
|
|
|
563
335
|
newValue: unknown;
|
|
564
336
|
}>;
|
|
565
337
|
} | {
|
|
566
|
-
/**
|
|
567
|
-
* K edits on ANY pipeline column as ONE unit of work — the
|
|
568
|
-
* generalization of `dates-batch` to the 16 registry columns
|
|
569
|
-
* (schedule bulk editors / modals; PLAN_convergencia_wiring §4.1).
|
|
570
|
-
* Each edit resolves its pipeline via `findPipelineForColumn` and
|
|
571
|
-
* runs phases 1-4 with an individual verdict (a rejected edit does
|
|
572
|
-
* NOT abort the others — paste semantics). A column without a
|
|
573
|
-
* pipeline (e.g. the link columns `custom_predecessors` /
|
|
574
|
-
* `custom_sucessors`, which mutate the link graph and dispatch as
|
|
575
|
-
* link intents) rejects ONLY that edit. The post-mutation pass
|
|
576
|
-
* (autoscheduler → parent bounds → CP) runs ONCE at the end and a
|
|
577
|
-
* single ChangeSet comes out = ONE undo step (never coalesces).
|
|
578
|
-
* Supports both shapes: K activities × same column, and 1 activity
|
|
579
|
-
* × several columns (atomic multi-field edit).
|
|
580
|
-
*/
|
|
581
338
|
kind: 'bulk-edit';
|
|
582
339
|
edits: ReadonlyArray<{
|
|
583
340
|
activityId: ActivityId;
|
|
@@ -590,16 +347,12 @@ type DispatchAction = {
|
|
|
590
347
|
source: ActivityId;
|
|
591
348
|
target: ActivityId;
|
|
592
349
|
type: LinkType;
|
|
593
|
-
/** Lag in DAYS (public boundary unit; converted to working hours inside). */
|
|
594
350
|
lag: number;
|
|
595
|
-
/** Optional explicit id (tests pass deterministic ids). */
|
|
596
351
|
linkId?: LinkId;
|
|
597
352
|
} | {
|
|
598
353
|
kind: 'link-update';
|
|
599
354
|
linkId: LinkId;
|
|
600
|
-
/** New type. Omit to preserve. */
|
|
601
355
|
type?: LinkType;
|
|
602
|
-
/** New lag in DAYS (public boundary unit). Omit to preserve. */
|
|
603
356
|
lag?: number;
|
|
604
357
|
} | {
|
|
605
358
|
kind: 'link-delete';
|
|
@@ -609,254 +362,88 @@ type DispatchAction = {
|
|
|
609
362
|
operations: ReadonlyArray<LinksBatchOperation>;
|
|
610
363
|
} | {
|
|
611
364
|
kind: 'activity-create';
|
|
612
|
-
/** Where to put the new activity. Use `'0'` for root. */
|
|
613
365
|
parentId: ActivityId | '0';
|
|
614
|
-
/**
|
|
615
|
-
* Optional — insert the new activity immediately AFTER this sibling.
|
|
616
|
-
* If omitted (and `beforeSiblingId` also omitted), the new activity
|
|
617
|
-
* is appended as the last child of `parentId`. The sibling must
|
|
618
|
-
* currently be a child of `parentId`, otherwise the dispatch is
|
|
619
|
-
* rejected. Mutually exclusive with `beforeSiblingId`.
|
|
620
|
-
*/
|
|
621
366
|
afterSiblingId?: ActivityId | undefined;
|
|
622
|
-
/**
|
|
623
|
-
* Optional — insert the new activity immediately BEFORE this sibling.
|
|
624
|
-
* Use case: "insert as first child" (`beforeSiblingId = currentFirstChild`)
|
|
625
|
-
* or "insert at position N" (`beforeSiblingId = children[N]`).
|
|
626
|
-
* The sibling must currently be a child of `parentId`, otherwise the
|
|
627
|
-
* dispatch is rejected. Mutually exclusive with `afterSiblingId`.
|
|
628
|
-
*/
|
|
629
367
|
beforeSiblingId?: ActivityId | undefined;
|
|
630
|
-
/**
|
|
631
|
-
* Canonical field overrides applied on top of the creation defaults —
|
|
632
|
-
* camelCase, working hours, limited to creation-writable fields
|
|
633
|
-
* (`CreationOverrides`). Identity/structure and derived outputs are not
|
|
634
|
-
* overridable.
|
|
635
|
-
*/
|
|
636
368
|
overrides?: CreationOverrides;
|
|
637
|
-
/**
|
|
638
|
-
* Deterministic activity id — used by tests and by replay flows
|
|
639
|
-
* (paste, undo) that need to assign a specific id. If omitted, the
|
|
640
|
-
* core's internal id generator allocates one.
|
|
641
|
-
*/
|
|
642
369
|
activityId?: ActivityId;
|
|
643
|
-
/**
|
|
644
|
-
* Where this create originated from — passed through to the
|
|
645
|
-
* `schedule_activity_creation` Amplitude event. Free-form string
|
|
646
|
-
* (matches legacy `INSERT_EVENT_SOURCES`).
|
|
647
|
-
*/
|
|
648
370
|
eventSource?: string | undefined;
|
|
649
371
|
} | {
|
|
650
372
|
kind: 'activity-delete';
|
|
651
|
-
/**
|
|
652
|
-
* IDs to delete. Each id triggers a cascade delete of its entire
|
|
653
|
-
* subtree. Links incident to any deleted activity are removed too.
|
|
654
|
-
* Operation is atomic — if any id is missing, the whole dispatch is
|
|
655
|
-
* rejected before mutating anything.
|
|
656
|
-
*/
|
|
657
373
|
activityIds: ReadonlyArray<ActivityId>;
|
|
658
374
|
eventSource?: string;
|
|
659
375
|
} | {
|
|
660
376
|
kind: 'activity-move';
|
|
661
|
-
/**
|
|
662
|
-
* The activity to move. Single-id only — for batch reorder use
|
|
663
|
-
* multiple dispatches (each one observes the post-previous state).
|
|
664
|
-
*/
|
|
665
377
|
activityId: ActivityId;
|
|
666
|
-
/** Destination parent. Same as current parent = reorder within. */
|
|
667
378
|
parentId: ActivityId | '0';
|
|
668
|
-
/**
|
|
669
|
-
* Optional — position the moved activity immediately below this
|
|
670
|
-
* sibling. If omitted, appended as the last child of `parentId`.
|
|
671
|
-
* The sibling must currently be a child of `parentId`; otherwise
|
|
672
|
-
* the dispatch is rejected. Mutually exclusive with `beforeSiblingId`.
|
|
673
|
-
*/
|
|
674
379
|
afterSiblingId?: ActivityId;
|
|
675
380
|
beforeSiblingId?: ActivityId;
|
|
676
381
|
eventSource?: string;
|
|
677
382
|
} | {
|
|
678
383
|
kind: 'activity-indent';
|
|
679
|
-
/**
|
|
680
|
-
* IDs to indent. Multi-select honors chain semantics: each id is
|
|
681
|
-
* moved under its closest previous sibling that is NOT in the
|
|
682
|
-
* selected set, so consecutive selected siblings all land under the
|
|
683
|
-
* same anchor (matches legacy `findPreviousNonSelectedSibling`).
|
|
684
|
-
*/
|
|
685
384
|
activityIds: ReadonlyArray<ActivityId>;
|
|
686
385
|
eventSource?: string;
|
|
687
386
|
} | {
|
|
688
387
|
kind: 'activity-outdent';
|
|
689
|
-
/**
|
|
690
|
-
* IDs to outdent. Multi-select preserves relative order among the
|
|
691
|
-
* selected siblings via initial-index capture (matches legacy
|
|
692
|
-
* `initialFirstSiblings` + `initialTaskIndexes` pre-loop setup).
|
|
693
|
-
*/
|
|
694
388
|
activityIds: ReadonlyArray<ActivityId>;
|
|
695
389
|
eventSource?: string;
|
|
696
390
|
} | {
|
|
697
|
-
/**
|
|
698
|
-
* Set progress to 0 or 100 with recursive cascade semantics.
|
|
699
|
-
*
|
|
700
|
-
* Triggered by the schedule's "complete" / "uncomplete" buttons.
|
|
701
|
-
* **Distinct intent from `inline-edit`** — bypasses the
|
|
702
|
-
* `isSummaryActivity` gate of `progressPipeline.canEdit` because
|
|
703
|
-
* the user is explicitly invoking the recursive operation
|
|
704
|
-
* ("complete this parent and all its descendants"), not editing
|
|
705
|
-
* a single cell.
|
|
706
|
-
*
|
|
707
|
-
* Inline edits on the progress column still use `inline-edit` with
|
|
708
|
-
* full `canEdit`/`validate` gating (which blocks summary activities
|
|
709
|
-
* by design). See [[complete activity business rules]] R8 and the
|
|
710
|
-
* bifurcation rationale.
|
|
711
|
-
*
|
|
712
|
-
* Internally reuses the `progressPipeline.transform` so cascade,
|
|
713
|
-
* rollup, tracking and visualization are identical to inline-edit
|
|
714
|
-
* with newValue=0 or newValue=100.
|
|
715
|
-
*/
|
|
716
391
|
kind: 'activity-set-progress';
|
|
717
392
|
activityId: ActivityId;
|
|
718
|
-
/** Strictly 0 (uncomplete) or 100 (complete). */
|
|
719
393
|
newValue: 0 | 100;
|
|
720
394
|
eventSource?: string;
|
|
721
395
|
} | {
|
|
722
|
-
/**
|
|
723
|
-
* Checkbox selection toggle (2026-06-06). The core is the
|
|
724
|
-
* authority of the check state (`checked` / `visibleChecked` /
|
|
725
|
-
* `mustApplyVisibleChecked` written as a single bit): it runs the
|
|
726
|
-
* full propagation (descendants cascade, frozen partition with
|
|
727
|
-
* parent blocking, unified upward pass — see
|
|
728
|
-
* `internal/selection/compute-selection-update.ts`) and emits a
|
|
729
|
-
* prop-only ChangeSet the bridge applies in bulk to DHTMLX
|
|
730
|
-
* (silent store write + viewport repaint). No autoscheduler, no
|
|
731
|
-
* critical path.
|
|
732
|
-
*/
|
|
733
396
|
kind: 'selection-toggle';
|
|
734
397
|
activityId: ActivityId;
|
|
735
|
-
/** Checkbox state AFTER the user click. */
|
|
736
398
|
isChecked: boolean;
|
|
737
|
-
/**
|
|
738
|
-
* Ids frozen by pending SIRs, computed by the caller from the
|
|
739
|
-
* runtime (DHTMLX task props — the core's own `pendingRequests`
|
|
740
|
-
* is load-time-stale until TODO #10). When omitted, the core
|
|
741
|
-
* falls back to its own snapshots.
|
|
742
|
-
*/
|
|
743
399
|
frozenIds?: ReadonlyArray<ActivityId>;
|
|
744
|
-
/**
|
|
745
|
-
* Enable the upward auto-check of ancestors when all their
|
|
746
|
-
* checkable children become selected. Mirrors the (inverted)
|
|
747
|
-
* CHECKBOX_NOT_AUTOMATIC_PARENT_SELECTION flag. Default: false.
|
|
748
|
-
*/
|
|
749
400
|
autoCheckParents?: boolean;
|
|
750
401
|
eventSource?: string;
|
|
751
402
|
} | {
|
|
752
|
-
/**
|
|
753
|
-
* Replace the whole selection with an exact id set — no
|
|
754
|
-
* propagation. Used by clear / block-selection / external sync.
|
|
755
|
-
* Unknown ids are ignored. Same prop-only ChangeSet contract as
|
|
756
|
-
* `selection-toggle`.
|
|
757
|
-
*/
|
|
758
403
|
kind: 'selection-replace';
|
|
759
404
|
activityIds: ReadonlyArray<ActivityId>;
|
|
760
405
|
eventSource?: string;
|
|
761
406
|
} | {
|
|
762
|
-
/**
|
|
763
|
-
* View-model visibility set (Fase 1, 2026-07-04). ABSOLUTE set: the
|
|
764
|
-
* UI sends the COMPLETE list of ids visible after its filters run;
|
|
765
|
-
* the core diffs against the current `visible` bits (an id changes
|
|
766
|
-
* when `(visible !== false) !== willBeVisible`) and emits a
|
|
767
|
-
* prop-only ChangeSet carrying ONLY the `visible` field for the
|
|
768
|
-
* activities that changed. Unknown ids are ignored. Same prop-only
|
|
769
|
-
* contract as `selection-replace`: no autoscheduler, no critical
|
|
770
|
-
* path, no undo step. Idempotent.
|
|
771
|
-
*/
|
|
772
407
|
kind: 'visibility-set';
|
|
773
408
|
visibleIds: ReadonlyArray<ActivityId>;
|
|
774
409
|
eventSource?: string;
|
|
775
410
|
} | {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
411
|
+
kind: 'filter-set';
|
|
412
|
+
criteria: ReadonlyArray<FilterCriterion>;
|
|
413
|
+
logic: 'and' | 'or';
|
|
414
|
+
dateRange?: FilterDateRange;
|
|
415
|
+
eventSource?: string;
|
|
416
|
+
} | {
|
|
417
|
+
kind: 'sort-set';
|
|
418
|
+
rules: ReadonlyArray<OrderRule>;
|
|
419
|
+
eventSource?: string;
|
|
420
|
+
} | {
|
|
784
421
|
kind: 'sir-sync';
|
|
785
422
|
activityId: ActivityId;
|
|
786
423
|
pendingRequests: ReadonlyArray<PendingRequest>;
|
|
787
424
|
eventSource?: string;
|
|
788
425
|
} | {
|
|
789
|
-
/**
|
|
790
|
-
* Synchronize the schedule-side flags owned by Lookahead task
|
|
791
|
-
* creation/deletion. The Lookahead records remain at the application
|
|
792
|
-
* boundary; the core owns these flags because they affect schedule
|
|
793
|
-
* editing rules and the public activity view.
|
|
794
|
-
*/
|
|
795
426
|
kind: 'activity-lookahead-sync';
|
|
796
427
|
activityIds: ReadonlyArray<ActivityId>;
|
|
797
428
|
isLookahead: boolean;
|
|
798
429
|
hasLookaheadTasks: boolean;
|
|
799
430
|
eventSource?: string;
|
|
800
431
|
} | {
|
|
801
|
-
/**
|
|
802
|
-
* Bulk paste (2026-06-08). Creates N activities (preserving the
|
|
803
|
-
* copied subtree hierarchy) plus M internal links in a SINGLE
|
|
804
|
-
* atomic ChangeSet — the future unit of undo. Replaces the
|
|
805
|
-
* DHTMLX-side paste that bypassed the core via `gantt.isPasting`.
|
|
806
|
-
*
|
|
807
|
-
* The caller (bridge) supplies, per activity, the field
|
|
808
|
-
* `overrides` already shaped by the paste prepare step (resets +
|
|
809
|
-
* validated catalogs). The core allocates ids/uids, re-hangs the
|
|
810
|
-
* hierarchy via `originalId → newId`, promotes parents, runs the
|
|
811
|
-
* hh/cost cascade + correlative sweep + autoscheduler ONCE at the
|
|
812
|
-
* end of the batch (not per activity).
|
|
813
|
-
*
|
|
814
|
-
* See [[COPY_PASTE_CORE_MIGRATION_DESIGN]].
|
|
815
|
-
*/
|
|
816
432
|
kind: 'activity-paste';
|
|
817
|
-
/**
|
|
818
|
-
* Where the pasted roots land. `{ parentId, index }` pastes INSIDE
|
|
819
|
-
* the reference (index 0 = first child). `{ afterSiblingId }`
|
|
820
|
-
* pastes as a sibling immediately below the reference; the parent
|
|
821
|
-
* is derived from the sibling.
|
|
822
|
-
*/
|
|
823
433
|
destination: {
|
|
824
434
|
parentId: ActivityId | '0';
|
|
825
435
|
index: number;
|
|
826
436
|
} | {
|
|
827
437
|
afterSiblingId: ActivityId;
|
|
828
438
|
};
|
|
829
|
-
/**
|
|
830
|
-
* The activity the paste is anchored to (the single selected row).
|
|
831
|
-
* Used as the custom_id reference for pasted children. Replaces
|
|
832
|
-
* the legacy `gantt.pasteReferenceActivity` instance flag.
|
|
833
|
-
*/
|
|
834
439
|
referenceActivityId: ActivityId;
|
|
835
|
-
/**
|
|
836
|
-
* Activities to create, in tree order (parents before children).
|
|
837
|
-
* `originalId` / `originalParentId` are the ids from the copied
|
|
838
|
-
* payload; the core remaps them to freshly allocated ids and uses
|
|
839
|
-
* the remap to re-hang the hierarchy and the links. A pasted activity
|
|
840
|
-
* may carry its visible baseline snapshot, but it is not enrolled in an
|
|
841
|
-
* already-created historical baseline.
|
|
842
|
-
*/
|
|
843
440
|
activities: ReadonlyArray<PastedActivityInput>;
|
|
844
|
-
/**
|
|
845
|
-
* Internal links to recreate. `source`/`target` reference the
|
|
846
|
-
* ORIGINAL (copied) ids; links with an endpoint outside the
|
|
847
|
-
* pasted set are dropped (dangling).
|
|
848
|
-
*/
|
|
849
441
|
links: ReadonlyArray<PastedLinkInput>;
|
|
850
442
|
eventSource?: string;
|
|
851
443
|
};
|
|
852
444
|
interface PastedActivityInput {
|
|
853
445
|
readonly originalId: ActivityId;
|
|
854
446
|
readonly originalParentId: ActivityId | '0';
|
|
855
|
-
/**
|
|
856
|
-
* Optional explicit destination id. Production normally omits it and lets
|
|
857
|
-
* the adapter allocate; recorder replay supplies MAIN's observed id so the
|
|
858
|
-
* whole-tree oracle can compare identity without an invented id remap.
|
|
859
|
-
*/
|
|
860
447
|
readonly activityId?: ActivityId;
|
|
861
448
|
readonly overrides: CreationOverrides;
|
|
862
449
|
readonly baselineSnapshot?: ActivityBaselineSnapshot | null;
|
|
@@ -865,51 +452,20 @@ interface PastedLinkInput {
|
|
|
865
452
|
readonly source: ActivityId;
|
|
866
453
|
readonly target: ActivityId;
|
|
867
454
|
readonly type: LinkType;
|
|
868
|
-
/** Lag in DAYS (public boundary unit; converted to working hours inside). */
|
|
869
455
|
readonly lag: number;
|
|
870
456
|
readonly linkId?: LinkId;
|
|
871
457
|
}
|
|
872
458
|
interface DispatchOptions {
|
|
873
|
-
/** Skip the autoscheduler pass after the mutation. Default: false. */
|
|
874
459
|
skipAutoSchedule?: boolean;
|
|
875
|
-
/** Skip the CP recompute after the mutation. Default: false. */
|
|
876
460
|
skipCriticalPath?: boolean;
|
|
877
|
-
/**
|
|
878
|
-
* Unit of the INPUT `newValue` for unit-bearing inline-edit columns (today:
|
|
879
|
-
* duration). Default 'days' (public contract; card + tests send days). The
|
|
880
|
-
* DHTMLX grid editor produces the store unit (HOURS via formatter.parse), so
|
|
881
|
-
* the bridge passes 'hours' for those edits → the pipeline skips the days→hours
|
|
882
|
-
* conversion instead of double-converting.
|
|
883
|
-
*/
|
|
884
461
|
inputUnit?: 'days' | 'hours';
|
|
885
462
|
}
|
|
886
463
|
type DispatchResult = {
|
|
887
464
|
ok: true;
|
|
888
465
|
changes: ChangeSet;
|
|
889
|
-
/**
|
|
890
|
-
* Solo los intents batch (`dates-batch` / `bulk-edit`): resultado
|
|
891
|
-
* POR EDIT, en el orden de `edits`. Un edit rechazado no aborta a
|
|
892
|
-
* los demás (semántica paste); el bridge re-proyecta solo los
|
|
893
|
-
* rechazados.
|
|
894
|
-
*/
|
|
895
466
|
verdicts?: ReadonlyArray<DatesBatchVerdict>;
|
|
896
467
|
activityVerdicts?: ReadonlyArray<DatesBatchVerdict>;
|
|
897
468
|
linkVerdicts?: ReadonlyArray<LinkBatchVerdict>;
|
|
898
|
-
/**
|
|
899
|
-
* INTERNAL (undo / B4). Full pre-mutation snapshot of every
|
|
900
|
-
* deleted/structurally-removed activity row, keyed by STRING id. The
|
|
901
|
-
* ChangeSet emits deleted rows as `{ kind:'deleted', after:null }` with no
|
|
902
|
-
* `before`, so resurrection on undo needs this. Populated by structural
|
|
903
|
-
* handlers (delete/move/paste); stripped before the result reaches the
|
|
904
|
-
* bridge in the Wave-3 integration.
|
|
905
|
-
*/
|
|
906
|
-
__beforeSnap?: ReadonlyMap<string, CoreActivity>;
|
|
907
|
-
/**
|
|
908
|
-
* INTERNAL (undo / B4). Incident links of deleted activities, keyed by
|
|
909
|
-
* STRING id (EntityChange link ids are String()-normalized). Carries the
|
|
910
|
-
* engine `LinkSnapshot` (lag in working hours) for unit-correct re-add.
|
|
911
|
-
*/
|
|
912
|
-
__beforeLinks?: ReadonlyMap<string, LinkSnapshot>;
|
|
913
469
|
} | {
|
|
914
470
|
ok: false;
|
|
915
471
|
reason: string;
|
|
@@ -940,34 +496,17 @@ interface TrackingEvent {
|
|
|
940
496
|
name: string;
|
|
941
497
|
properties: Record<string, unknown>;
|
|
942
498
|
}
|
|
943
|
-
/**
|
|
944
|
-
* Side effects a dispatch emits that are NOT entity-field changes and NOT
|
|
945
|
-
* Amplitude tracking — commands the bridge must act on. Today: SIR
|
|
946
|
-
* auto-reject (a frozen activity's dates changed or it was deleted, so its
|
|
947
|
-
* pending SIR must be rejected). The core owns the rule; the bridge consumes
|
|
948
|
-
* the effect (Redux REJECTED; durable persistence rides the activity save).
|
|
949
|
-
* See `SIR_MAP.md` §10.
|
|
950
|
-
*/
|
|
951
499
|
type ScheduleEffect = {
|
|
952
500
|
kind: 'sir-auto-reject';
|
|
953
501
|
activityId: ActivityId;
|
|
954
|
-
/** Backend SIR id == the front Redux item id (see parseFromBackend A1). */
|
|
955
502
|
sirId: string;
|
|
956
503
|
reason: 'date_changed' | 'activity_deleted';
|
|
957
504
|
};
|
|
958
|
-
/**
|
|
959
|
-
* A direct user edit violated the activity's own (pre-edit) constraint. Per
|
|
960
|
-
* the canonical model (PRD constraint violations, R2/R3): the edit APPLIES
|
|
961
|
-
* anyway — the warning is data for the UI (constraint validation modal),
|
|
962
|
-
* never a rejection, and it is only emitted from direct edits (the
|
|
963
|
-
* autoscheduler never violates, R1). Decision D-A 2026-07-04.
|
|
964
|
-
*/
|
|
965
505
|
interface ConstraintWarning {
|
|
966
506
|
kind: 'constraint_violation';
|
|
967
507
|
activityId: ActivityId;
|
|
968
508
|
constraintType: ConstraintType;
|
|
969
509
|
constraintDate: Date;
|
|
970
|
-
/** The projected date that violated (start or end, per constraint type). */
|
|
971
510
|
projectedDate: Date;
|
|
972
511
|
messageKey: string;
|
|
973
512
|
}
|
|
@@ -994,22 +533,20 @@ interface ChangeSet {
|
|
|
994
533
|
trackingEvents: ReadonlyArray<TrackingEvent>;
|
|
995
534
|
viewState?: ReadonlyArray<ViewStateChange>;
|
|
996
535
|
/**
|
|
997
|
-
*
|
|
998
|
-
*
|
|
999
|
-
*
|
|
536
|
+
* Sibling groups whose display sequence changed, each already in final order.
|
|
537
|
+
* Carried per branch rather than as a flat sequence because ordering never
|
|
538
|
+
* reparents: the renderer assigns each array and repaints once.
|
|
539
|
+
*
|
|
540
|
+
* Absent when nothing resequenced. Never a partial answer: a branch listed
|
|
541
|
+
* here holds all of its children.
|
|
1000
542
|
*/
|
|
543
|
+
order?: ReadonlyArray<BranchOrderChange>;
|
|
1001
544
|
effects?: ReadonlyArray<ScheduleEffect>;
|
|
1002
|
-
/**
|
|
1003
|
-
* Optional — present only when a direct edit violated a constraint (see
|
|
1004
|
-
* `ConstraintWarning`). Consumers read `changeSet.warnings ?? []`.
|
|
1005
|
-
*/
|
|
1006
545
|
warnings?: ReadonlyArray<ConstraintWarning>;
|
|
1007
546
|
}
|
|
1008
547
|
|
|
1009
548
|
interface ProjectWorkHours {
|
|
1010
|
-
/** Earliest start of the working day across the week, `"HH:MM"`. */
|
|
1011
549
|
startHour: string;
|
|
1012
|
-
/** Latest end of the working day across the week, `"HH:MM"`. */
|
|
1013
550
|
endHour: string;
|
|
1014
551
|
}
|
|
1015
552
|
|
|
@@ -1118,102 +655,18 @@ interface BackendScheduleInput {
|
|
|
1118
655
|
baseCalendars?: BackendCalendarInput[] | undefined;
|
|
1119
656
|
}
|
|
1120
657
|
|
|
1121
|
-
/**
|
|
1122
|
-
* Injected reporting port. The core is UI-agnostic: it cannot `console.*`,
|
|
1123
|
-
* show UI, or know about Sentry. When it hits a recoverable data-quality
|
|
1124
|
-
* issue (`warn`) or a failure it could not complete (`error`), it calls this
|
|
1125
|
-
* port; the consumer (react_client) implements it and decides the outcome
|
|
1126
|
-
* (Sentry, toast, nothing). Same injected-port pattern as `AutoSchedulerPort`.
|
|
1127
|
-
*/
|
|
1128
658
|
interface ScheduleCoreReporter {
|
|
1129
|
-
/** Recoverable data-quality issue; the core continues with its fallback. */
|
|
1130
659
|
warn(message: string, context?: unknown): void;
|
|
1131
|
-
/** A real failure the core could not complete. */
|
|
1132
660
|
error(message: string, cause?: unknown): void;
|
|
1133
661
|
}
|
|
1134
662
|
|
|
1135
|
-
/**
|
|
1136
|
-
* initialize-core — the construction + load-pass orchestration extracted from
|
|
1137
|
-
* the `ScheduleCore` constructor. `initializeCore(input)` performs the entire
|
|
1138
|
-
* load sequence (parse → snapshot → state → demote → id generators →
|
|
1139
|
-
* custom-id tracker → optional baseline block → initial passes) and returns
|
|
1140
|
-
* the assembled internals (`CoreInternals`) for the facade to assign. The
|
|
1141
|
-
* facade constructor is assignment-only.
|
|
1142
|
-
*
|
|
1143
|
-
* The load-pass ORDER here is a CONTRACT — see `CURSOR.md`
|
|
1144
|
-
* §"Flujo de core.dispatch paso a paso" and the inline comments below. Do not
|
|
1145
|
-
* reorder, merge, or drop any step.
|
|
1146
|
-
*
|
|
1147
|
-
* The public types (`ScheduleCoreStatus`, `ScheduleCoreInput`) live here so
|
|
1148
|
-
* `initialize-core.ts` never imports from `scheduleCore.ts` (which imports
|
|
1149
|
-
* `initializeCore`), avoiding an import cycle. `scheduleCore.ts` re-exports the
|
|
1150
|
-
* two public types
|
|
1151
|
-
* so `src/index.ts`'s existing `export type { ... } from './init/schedule-core.js'`
|
|
1152
|
-
* keeps resolving unchanged.
|
|
1153
|
-
*/
|
|
1154
|
-
|
|
1155
663
|
type ScheduleCoreStatus = 'ready' | 'destroyed' | 'poisoned';
|
|
1156
664
|
interface ScheduleCoreInput extends BackendScheduleInput {
|
|
1157
|
-
/**
|
|
1158
|
-
* Skip the full autoscheduler pass that normally runs from the
|
|
1159
|
-
* constructor (`runInitialPasses`).
|
|
1160
|
-
*
|
|
1161
|
-
* Backend data already ships with `start_date` / `end_date` / `duration`
|
|
1162
|
-
* scheduled — running the full ASAP+ALAP graph at load is redundant and
|
|
1163
|
-
* extremely slow on large schedules (thousands of activities + links).
|
|
1164
|
-
*
|
|
1165
|
-
* When `true`, the constructor still runs the lightweight bottom-up
|
|
1166
|
-
* post-processors (`updateParentBoundsFromChildren`) so parent bounds
|
|
1167
|
-
* and progress rollup are consistent. The autoscheduler runs lazily on
|
|
1168
|
-
* the first `dispatch(...)` call.
|
|
1169
|
-
*
|
|
1170
|
-
* Default: `false` (preserve existing behaviour). The bridge in
|
|
1171
|
-
* `react_client/` sets this to `true` for production loads.
|
|
1172
|
-
*/
|
|
1173
665
|
skipInitialAutoSchedule?: boolean;
|
|
1174
|
-
/**
|
|
1175
|
-
* Baseline points (backend `sectorbaselinepoint[]`, the `activityponts`
|
|
1176
|
-
* endpoint payload) to overlay at load. Each point maps to an activity by
|
|
1177
|
-
* `point.activityId` → `activity.proplannerId`. When present, the constructor
|
|
1178
|
-
* overlays them so the baseline columns + `expected_progress_base` can read the
|
|
1179
|
-
* ACTIVE point. NB: ponderators are NOT recomputed at load (backend ships the
|
|
1180
|
-
* value prod renders verbatim — recomputing diverges); `goCalculatePonderators`
|
|
1181
|
-
* is a save-time op. Omit (or pass `[]`) for projects with no baseline.
|
|
1182
|
-
*/
|
|
1183
666
|
baselinePoints?: readonly BaselinePoint[];
|
|
1184
|
-
/**
|
|
1185
|
-
* Clock port for time-dependent columns (`expected_progress*`). A FUNCTION
|
|
1186
|
-
* (not a frozen instant): the core calls it FRESH each time it needs "today"
|
|
1187
|
-
* (load + every dispatch that recomputes expected_progress), so the value
|
|
1188
|
-
* tracks the real day even on a long-lived session — never congelado.
|
|
1189
|
-
*
|
|
1190
|
-
* Opt-in (no default): omit it → the core does NOT compute `expected_progress`
|
|
1191
|
-
* (preserves the legacy path; keeps the test suite deterministic). Production
|
|
1192
|
-
* injects `() => new Date()`; tests inject `() => fixedDate` for reproducibility.
|
|
1193
|
-
* The core never calls `new Date()` itself — this port is the only time source.
|
|
1194
|
-
*/
|
|
1195
667
|
clock?: () => Date;
|
|
1196
|
-
/**
|
|
1197
|
-
* Critical-path mode at LOAD. The CP is ALWAYS computed; this flag only
|
|
1198
|
-
* decides WHEN `core.ready` is considered settled:
|
|
1199
|
-
* - `false` (default — front): `core.ready` resolves WITHOUT the CP (fast
|
|
1200
|
-
* load); the CP runs async and is delivered via `core.criticalPathReady`.
|
|
1201
|
-
* - `true` (backend, no front): `core.ready` WAITS for the CP — it is part of
|
|
1202
|
-
* the load (blocking). The caller does a single `await core.ready` and has
|
|
1203
|
-
* the dates + CP fields ready.
|
|
1204
|
-
*/
|
|
1205
668
|
criticalPathOnLoad?: boolean;
|
|
1206
|
-
/**
|
|
1207
|
-
* Initial status criterion (`'Baseline' | 'Actual'`) for the `status` column.
|
|
1208
|
-
* Not a backend field (client-only view pref); the client passes the current
|
|
1209
|
-
* value, defaulting to `'Baseline'`. Overrides the sector default when present.
|
|
1210
|
-
* Changed at runtime via `ScheduleCore.setStatusCriteria`.
|
|
1211
|
-
*/
|
|
1212
669
|
statusCriteria?: StatusCriteria;
|
|
1213
|
-
/**
|
|
1214
|
-
* Optional injected port for data-quality warnings and scheduling errors.
|
|
1215
|
-
* Defaults to a no-op when absent (the core never touches console/Sentry).
|
|
1216
|
-
*/
|
|
1217
670
|
reporter?: ScheduleCoreReporter;
|
|
1218
671
|
}
|
|
1219
672
|
|
|
@@ -1242,13 +695,9 @@ declare class ScheduleCore {
|
|
|
1242
695
|
forEachActivityId(visit: (id: string) => void): void;
|
|
1243
696
|
getChildrenIds(parentId: ActivityId | '0'): ReadonlyArray<ActivityId>;
|
|
1244
697
|
getSelectedActivityIds(): string[];
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
* `visible` is the consumer's job. Memoized in the state and invalidated
|
|
1249
|
-
* on any structural mutation (add/remove/reparent/renumber), so repeated
|
|
1250
|
-
* reads between mutations are O(1).
|
|
1251
|
-
*/
|
|
698
|
+
getHiddenActivityIds(): string[];
|
|
699
|
+
getActiveFilter(): Readonly<FilterState> | null;
|
|
700
|
+
getActiveOrder(): Readonly<OrderState> | null;
|
|
1252
701
|
getVisualOrderIds(): ReadonlyArray<ActivityId>;
|
|
1253
702
|
hasChild(parentId: ActivityId | '0'): boolean;
|
|
1254
703
|
getCalendar(id: CalendarId): Readonly<Calendar> | null;
|
|
@@ -1261,36 +710,66 @@ declare class ScheduleCore {
|
|
|
1261
710
|
private _enqueue;
|
|
1262
711
|
dispatch(action: DispatchAction, options?: DispatchOptions): Promise<DispatchResult>;
|
|
1263
712
|
private _dispatchInner;
|
|
1264
|
-
/**
|
|
1265
|
-
* Starts or joins the Critical Path calculation for the current schedule
|
|
1266
|
-
* revision. Snapshot capture and the final commit are serialized with user
|
|
1267
|
-
* mutations, but the expensive calculation runs outside `_opQueue` against
|
|
1268
|
-
* an isolated state. A newer revision aborts this job and makes its result
|
|
1269
|
-
* ineligible to commit.
|
|
1270
|
-
*/
|
|
1271
713
|
recomputeCriticalPath(): Promise<{
|
|
1272
714
|
changes: ChangeSet;
|
|
1273
715
|
} | null>;
|
|
1274
716
|
isCriticalPathSettled(): boolean;
|
|
1275
717
|
whenCriticalPathSettled(): Promise<void>;
|
|
718
|
+
private _withReappliedViewState;
|
|
719
|
+
/**
|
|
720
|
+
* The single place the two view-state passes are chained. Dispatch, undo and
|
|
721
|
+
* redo all route through here: when this existed only inside the dispatch
|
|
722
|
+
* path, undo and redo reapplied the filter and forgot the order, so an edit
|
|
723
|
+
* that moved a row and was then undone left the row in its new position.
|
|
724
|
+
*
|
|
725
|
+
* The sequence between the passes is indifferent. Filter and order are
|
|
726
|
+
* orthogonal projections over the same tree — the filter decides which rows
|
|
727
|
+
* exist on screen and never reads the order; the order sequences every
|
|
728
|
+
* sibling group from the hierarchy index and never reads visibility. Either
|
|
729
|
+
* sequence produces the same ChangeSet.
|
|
730
|
+
*
|
|
731
|
+
* Both passes answer null while their state is inactive, so an unfiltered,
|
|
732
|
+
* unsorted schedule pays two property reads.
|
|
733
|
+
*/
|
|
734
|
+
private _reapplyViewState;
|
|
735
|
+
/**
|
|
736
|
+
* Emits `order` for the branches this mutation resequenced, when no user order
|
|
737
|
+
* is active.
|
|
738
|
+
*
|
|
739
|
+
* The contract is that `order` reports a CHANGED SEQUENCE, not the presence of
|
|
740
|
+
* a sort. Tying emission to the cause instead of the effect is what produced
|
|
741
|
+
* the undo bug and, later, the reparent ones: without an active order a move,
|
|
742
|
+
* an indent, an outdent or the undo of any of them rearranged rows and said
|
|
743
|
+
* nothing, so an incremental consumer kept the old sequence. The undo of a
|
|
744
|
+
* reparent was the worst of them — it carried neither `order` nor a single
|
|
745
|
+
* correlativeId, so the position was not recoverable by any consumer.
|
|
746
|
+
*
|
|
747
|
+
* The touched branches are derived from the ChangeSet rather than accumulated
|
|
748
|
+
* in the state: an entity whose parentId or correlativeId moved, plus the
|
|
749
|
+
* parents of created and deleted rows, is exactly the set of branches whose
|
|
750
|
+
* sequence can differ. That keeps this linear in the blast radius, adds
|
|
751
|
+
* nothing to the write path, and cannot leak across dispatches.
|
|
752
|
+
*/
|
|
753
|
+
private _emitTouchedBranchOrder;
|
|
754
|
+
/**
|
|
755
|
+
* Re-sequences the grid after any mutation that could have changed a value the
|
|
756
|
+
* active order sorts by. Ordering is a view over the data, so an edit that
|
|
757
|
+
* moves a row past its sibling must move the row, exactly as the filter makes
|
|
758
|
+
* a no-longer-matching row disappear.
|
|
759
|
+
*
|
|
760
|
+
* Production only re-sorts after a bar drag; diverging from that is a
|
|
761
|
+
* deliberate product decision, not an oversight.
|
|
762
|
+
*/
|
|
763
|
+
private _reapplyActiveOrder;
|
|
764
|
+
private _reapplyActiveFilter;
|
|
1276
765
|
private _recordScheduleMutation;
|
|
1277
766
|
private _startCriticalPathForCurrentRevision;
|
|
1278
767
|
private _runCriticalPathAndCapture;
|
|
1279
|
-
/**
|
|
1280
|
-
* Undo/Redo restores the user's historical mutation while retaining current
|
|
1281
|
-
* non-historical truth (for example a refreshed baseline). Re-derive every
|
|
1282
|
-
* value that depends on both so the restored model is immediately coherent.
|
|
1283
|
-
*/
|
|
1284
768
|
private _recomputeAfterHistoryRestore;
|
|
1285
769
|
undo(): Promise<ChangeSet | null>;
|
|
1286
770
|
redo(): Promise<ChangeSet | null>;
|
|
1287
771
|
canUndo(): boolean;
|
|
1288
772
|
canRedo(): boolean;
|
|
1289
|
-
/**
|
|
1290
|
-
* Establishes a new persistence boundary without mutating schedule state.
|
|
1291
|
-
* Completed saves call this synchronously so neither prior undo entries nor
|
|
1292
|
-
* their redo branch can cross the persisted boundary.
|
|
1293
|
-
*/
|
|
1294
773
|
clearHistory(): void;
|
|
1295
774
|
undoDepth(): number;
|
|
1296
775
|
private _resyncCustomIdTrackerFromModel;
|
|
@@ -1300,13 +779,6 @@ declare class ScheduleCore {
|
|
|
1300
779
|
private readonly _ready;
|
|
1301
780
|
private readonly _saveTracker;
|
|
1302
781
|
private assertReady;
|
|
1303
|
-
/**
|
|
1304
|
-
* Revert the current dispatch's mutations from the write-capture journal. If
|
|
1305
|
-
* the restore ITSELF throws, the state may be a third, partially-inverted
|
|
1306
|
-
* state — worse than either endpoint — so poison the core: refuse all further
|
|
1307
|
-
* dispatches and surface the fault so the host reloads from backend. A failed
|
|
1308
|
-
* rollback is never swallowed.
|
|
1309
|
-
*/
|
|
1310
782
|
private _rollback;
|
|
1311
783
|
}
|
|
1312
784
|
|
|
@@ -1323,25 +795,6 @@ interface ParsedInput {
|
|
|
1323
795
|
}
|
|
1324
796
|
declare function parseFromBackend(input: BackendScheduleInput, reporter?: ScheduleCoreReporter): ParsedInput;
|
|
1325
797
|
|
|
1326
|
-
/**
|
|
1327
|
-
* Returns the ACTIVE baseline point of an activity, or null.
|
|
1328
|
-
*
|
|
1329
|
-
* Ported verbatim from the legacy selector used across the schedule
|
|
1330
|
-
* (`react_client/src/views/ganttContainer/gantt/gantt.helper.js`
|
|
1331
|
-
* calculatePonderators / getDurationRecursively, and the baseline columns
|
|
1332
|
-
* `startBaseCol`…`durationBaseCol`):
|
|
1333
|
-
*
|
|
1334
|
-
* activity.baseline_points.find(b => b.sectorbaselineversion?.active)
|
|
1335
|
-
*
|
|
1336
|
-
* Exactly one version per sector is active. Returns null when the activity
|
|
1337
|
-
* has no `baseline_points`, an empty array, or no point whose version is
|
|
1338
|
-
* active — matching the legacy behavior where such activities get
|
|
1339
|
-
* `ponderator = 0` (baseline-only, no fallback to real values; see memory
|
|
1340
|
-
* `project_ponderator_baseline_fix`).
|
|
1341
|
-
*
|
|
1342
|
-
* The `active` flag is treated as truthy (not strictly `=== true`) to match
|
|
1343
|
-
* the legacy `if (base.sectorbaselineversion.active)` check.
|
|
1344
|
-
*/
|
|
1345
798
|
declare function getActiveBaseline(activity: {
|
|
1346
799
|
baselinePoints?: readonly CoreBaselinePoint[];
|
|
1347
800
|
} | null | undefined): CoreBaselinePoint | null;
|
|
@@ -1349,135 +802,48 @@ declare function getActiveBaseline(activity: {
|
|
|
1349
802
|
interface CalendarLike {
|
|
1350
803
|
calculateDuration(start: Date, end: Date): number;
|
|
1351
804
|
}
|
|
1352
|
-
/**
|
|
1353
|
-
* Expected % a baseline-planned activity should have completed by `now`,
|
|
1354
|
-
* measured in working time against its baseline calendar. Faithful port of
|
|
1355
|
-
* legacy `calculateExpected` (lookahead-common.js): edge rules first, else the
|
|
1356
|
-
* working-time ratio. Returns 0..100. `now` should already be the comparison
|
|
1357
|
-
* instant the caller wants (e.g. end-of-day).
|
|
1358
|
-
*
|
|
1359
|
-
* Shared by the `expected_progress` (live) and `expected_progress_base` columns
|
|
1360
|
-
* (column-owns-everything: the formula is the one piece both columns share).
|
|
1361
|
-
*/
|
|
1362
805
|
declare function expectedProgressFromBaseline(start: Date, end: Date, now: Date, calendar: CalendarLike): number;
|
|
1363
806
|
|
|
1364
807
|
interface ExpectedProgressAdapter {
|
|
1365
808
|
getChildrenIds(parentId: string | 0): readonly string[];
|
|
1366
809
|
getActivity(id: string): Record<string, unknown> | null;
|
|
1367
810
|
setActivityField(id: string, field: string, value: unknown): void;
|
|
1368
|
-
/** Resolve a baseline calendar by its engine id (`${baseCalendarId}-base`). */
|
|
1369
811
|
getBaseCalendar(engineId: string): CalendarLike | null;
|
|
1370
812
|
}
|
|
1371
|
-
/**
|
|
1372
|
-
* Walks the tree from `rootIds`, writing `expected_progress_base` on every
|
|
1373
|
-
* activity: leaves from their baseline calendar, parents as
|
|
1374
|
-
* `Σ(childBase × child.ponderator) / 100` (faithful port of engine.ts
|
|
1375
|
-
* aggregateHierarchy). Returns the ids whose value changed.
|
|
1376
|
-
*/
|
|
1377
813
|
declare function computeExpectedProgress(rootIds: readonly string[], now: Date, adapter: ExpectedProgressAdapter, defaultBaseCalendarId?: string | null): string[];
|
|
1378
814
|
|
|
1379
815
|
type StaticActivityDefaults = Omit<CoreActivity, 'id' | 'parentId' | 'name' | 'startDate' | 'endDate' | 'durationHours' | 'calendarId' | 'uniqueCorrelativeId'>;
|
|
1380
816
|
declare const NEW_ACTIVITY_DEFAULTS: Readonly<StaticActivityDefaults>;
|
|
1381
817
|
|
|
1382
|
-
/**
|
|
1383
|
-
* Schedule-core equivalent of the legacy frontend `checkNoUpdatedLinks`.
|
|
1384
|
-
*
|
|
1385
|
-
* Returns the CURRENT links that were MODIFIED relative to a `baseline` (the
|
|
1386
|
-
* last loaded/saved link state). A link is "modified" iff its `lag` or `type`
|
|
1387
|
-
* differs from its baseline counterpart (matched by `id`).
|
|
1388
|
-
*
|
|
1389
|
-
* Why only lag/type: empirically those are the only mutable fields of a link —
|
|
1390
|
-
* `source`/`target` are identity, and the rest are backend/derived. The core
|
|
1391
|
-
* `link-update` intent (`dispatch/types.ts`) changes exactly "lag and/or type",
|
|
1392
|
-
* so this comparison is complete.
|
|
1393
|
-
*
|
|
1394
|
-
* New links (absent from baseline) and deleted links (present only in baseline)
|
|
1395
|
-
* are NOT "modified" — those belong to the unsaved / deleted buckets. The
|
|
1396
|
-
* baseline is supplied by the caller (the save flow's reference snapshot); per
|
|
1397
|
-
* interface discipline this is a pure helper, not a method on the core/port.
|
|
1398
|
-
*/
|
|
1399
818
|
declare function checkNoUpdatedLinks(baseline: readonly Link[], current: readonly Link[]): Link[];
|
|
1400
819
|
|
|
1401
|
-
/**
|
|
1402
|
-
* Schedule-core equivalent of the legacy frontend `checkNoSavedActivities` /
|
|
1403
|
-
* `checkNoSavedLinks`: an entity is "unsaved" when it has no backend id
|
|
1404
|
-
* (`proplannerId`) — i.e. it was created in-session and not yet persisted.
|
|
1405
|
-
*
|
|
1406
|
-
* Pure helper (same `!proplannerId` criterion as legacy, so 0/null/undefined
|
|
1407
|
-
* all count as unsaved). Works on `core.getAllActivitiesView()` / `getAllLinks()`
|
|
1408
|
-
* or on `gantt.serialize().data` — both carry `proplannerId`. Per interface
|
|
1409
|
-
* discipline this is a helper, not a method on the core/port.
|
|
1410
|
-
*/
|
|
1411
820
|
interface WithProplannerId {
|
|
1412
821
|
proplannerId?: number | null;
|
|
1413
822
|
[key: string]: unknown;
|
|
1414
823
|
}
|
|
1415
824
|
declare const getUnsavedActivities: <T extends WithProplannerId>(activities?: readonly T[]) => T[];
|
|
1416
825
|
|
|
1417
|
-
/**
|
|
1418
|
-
* Returns a microtask-priority yield. Resolves on the next event-loop
|
|
1419
|
-
* tick — the browser is free to paint, handle input, or process other
|
|
1420
|
-
* tasks in between.
|
|
1421
|
-
*/
|
|
1422
826
|
declare function yieldToBrowser(): Promise<void>;
|
|
1423
827
|
|
|
1424
|
-
/**
|
|
1425
|
-
* Does this dispatch recompute the critical path? The single authority for the
|
|
1426
|
-
* question, shared by the dispatch gate (`resolveDerivedPasses`) and the
|
|
1427
|
-
* bridge, which reads it to defer the CP out of the paint. Mirrors legacy: the
|
|
1428
|
-
* CP re-derives on every scheduling change. Prop-only/administrative intents
|
|
1429
|
-
* and edits that touch only non-scheduling columns leave the current CP
|
|
1430
|
-
* revision intact; structural reparents are never exempt.
|
|
1431
|
-
*/
|
|
1432
828
|
declare function willRunCriticalPath(action: DispatchAction): boolean;
|
|
1433
829
|
|
|
1434
|
-
/** Activity types */
|
|
1435
830
|
declare const ACTIVITY_TYPE: {
|
|
1436
831
|
readonly TASK: "task";
|
|
1437
832
|
readonly PROJECT: "project";
|
|
1438
833
|
readonly MILESTONE: "milestone";
|
|
1439
834
|
};
|
|
1440
835
|
|
|
1441
|
-
/**
|
|
1442
|
-
* Root-parent sentinel — single source of truth.
|
|
1443
|
-
*
|
|
1444
|
-
* Root-level activities have meant "root" under several shapes across the
|
|
1445
|
-
* codebase and the recorded data: string `'0'` (post-parse domain model),
|
|
1446
|
-
* numeric `0` (DHTMLX / engine seam), and nullish. `isRootParent` accepts
|
|
1447
|
-
* all of them; `normalizeParentKey` collapses them to the canonical `'0'`.
|
|
1448
|
-
*
|
|
1449
|
-
* NOTE: `'0'` is overloaded elsewhere in the domain (FS link-type code,
|
|
1450
|
-
* correlative ids). These constants are ONLY for the parent axis.
|
|
1451
|
-
*/
|
|
1452
|
-
/** Canonical root-parent id in the post-parse domain model. */
|
|
1453
836
|
declare const ROOT_PARENT_ID: "0";
|
|
1454
|
-
/** True when `parent` denotes the root level, under any historical shape. */
|
|
1455
837
|
declare function isRootParent(parent: unknown): boolean;
|
|
1456
|
-
/** Collapse any root shape to the canonical `'0'`; stringify the rest. */
|
|
1457
838
|
declare function normalizeParentKey(parent: unknown): string;
|
|
1458
839
|
|
|
1459
|
-
/**
|
|
1460
|
-
* Canonical bidirectional map between DHTMLX link kinds ('0'-'3') and the
|
|
1461
|
-
* backend/display two-letter codes (fs/ss/ff/sf), plus the predecessor
|
|
1462
|
-
* display-string vocabulary.
|
|
1463
|
-
*
|
|
1464
|
-
* Single source of truth — replaces the hand-maintained copies that lived in
|
|
1465
|
-
* parse-predecessor-string (code→number), apply-link-operation
|
|
1466
|
-
* (number→code), boundary/backend parseFromBackend (code→number) and
|
|
1467
|
-
* inverses (number→code). The mutual `satisfies Record<…>` constraints make
|
|
1468
|
-
* omitting or typo'ing a key on either side a compile error.
|
|
1469
|
-
*/
|
|
1470
|
-
|
|
1471
|
-
/** Backend/display two-letter link codes. */
|
|
1472
840
|
type LinkTypeCode = 'fs' | 'ss' | 'ff' | 'sf';
|
|
1473
|
-
/** DHTMLX numeric kind → backend/display code. */
|
|
1474
841
|
declare const LINK_TYPE_CODE: {
|
|
1475
842
|
readonly "0": "fs";
|
|
1476
843
|
readonly "1": "ss";
|
|
1477
844
|
readonly "2": "ff";
|
|
1478
845
|
readonly "3": "sf";
|
|
1479
846
|
};
|
|
1480
|
-
/** Backend/display code → DHTMLX numeric kind (inverse of LINK_TYPE_CODE). */
|
|
1481
847
|
declare const LINK_CODE_TO_TYPE: {
|
|
1482
848
|
readonly fs: "0";
|
|
1483
849
|
readonly ss: "1";
|
|
@@ -1485,49 +851,10 @@ declare const LINK_CODE_TO_TYPE: {
|
|
|
1485
851
|
readonly sf: "3";
|
|
1486
852
|
};
|
|
1487
853
|
|
|
1488
|
-
/**
|
|
1489
|
-
* Inverse boundary maps (domain → backend vocabulary).
|
|
1490
|
-
*
|
|
1491
|
-
* The forward translations live in `constants.ts` (`CONSTRAINT_LABEL_MAP`)
|
|
1492
|
-
* and `link-display.ts` (`LINK_CODE_TO_NUMBER`). The *serialization*
|
|
1493
|
-
* core → backend is intentionally NOT owned by this package today — it
|
|
1494
|
-
* lives in the consumer bridge (`react_client/gantt.helper.js`,
|
|
1495
|
-
* `from_number_to_code` / `GanttConstraint` reverse lookup) because the
|
|
1496
|
-
* bridge is the source of truth for what the server expects on save.
|
|
1497
|
-
*
|
|
1498
|
-
* These inverses exist so the mapping is documented and testable next to
|
|
1499
|
-
* its forward counterpart, and so a future `core → backend` layer doesn't
|
|
1500
|
-
* re-derive them by hand. **Internal only — not exported in `src/index.ts`.**
|
|
1501
|
-
* See Obsidian `BOUNDARY_TRANSLATIONS.md` §"Mapeos inversos".
|
|
1502
|
-
*/
|
|
1503
|
-
|
|
1504
|
-
/** Inverse of `CONSTRAINT_LABEL_MAP` — internal code → backend label. */
|
|
1505
854
|
declare const CONSTRAINT_TYPE_TO_LABEL: Record<ConstraintType, string>;
|
|
1506
855
|
|
|
1507
|
-
/**
|
|
1508
|
-
* Working-day vocabulary — genuine cross-component primitive (creation
|
|
1509
|
-
* defaults, internal/state context fallbacks, link lag display). The value
|
|
1510
|
-
* is the FALLBACK only; a sector- or calendar-provided hoursPerDay always
|
|
1511
|
-
* takes precedence at every consumer.
|
|
1512
|
-
*/
|
|
1513
856
|
declare const DEFAULT_HOURS_PER_DAY = 8;
|
|
1514
857
|
|
|
1515
|
-
/**
|
|
1516
|
-
* Canonical catalog of `DispatchAction` kinds.
|
|
1517
|
-
*
|
|
1518
|
-
* The discriminated union in `types.ts` remains the source of truth; this
|
|
1519
|
-
* module exposes it as RUNTIME vocabulary so `.js` consumers (bridge
|
|
1520
|
-
* entry-points, guard tests) stop retyping the wire strings. Exhaustiveness
|
|
1521
|
-
* is compile-checked HERE (not in the guard test — `tsconfig.json` excludes
|
|
1522
|
-
* `*.test.ts` from `tsc --noEmit` and vitest does not typecheck):
|
|
1523
|
-
* - `satisfies Record<string, DispatchActionKind>` rejects any entry whose
|
|
1524
|
-
* value is not a union member (catalog ⊆ union);
|
|
1525
|
-
* - `KIND_CATALOG_COVERS_UNION: Record<DispatchActionKind, true>` requires
|
|
1526
|
-
* one entry per union member (union ⊆ catalog) — adding a kind to
|
|
1527
|
-
* `DispatchAction` without updating this module breaks the build.
|
|
1528
|
-
* The string VALUES are a frozen contract (see `constants.guard.test.ts`).
|
|
1529
|
-
*/
|
|
1530
|
-
|
|
1531
858
|
type DispatchActionKind = DispatchAction['kind'];
|
|
1532
859
|
declare const DISPATCH_ACTION_KIND: {
|
|
1533
860
|
readonly PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge";
|
|
@@ -1551,28 +878,13 @@ declare const DISPATCH_ACTION_KIND: {
|
|
|
1551
878
|
readonly SELECTION_TOGGLE: "selection-toggle";
|
|
1552
879
|
readonly SELECTION_REPLACE: "selection-replace";
|
|
1553
880
|
readonly VISIBILITY_SET: "visibility-set";
|
|
881
|
+
readonly FILTER_SET: "filter-set";
|
|
882
|
+
readonly SORT_SET: "sort-set";
|
|
1554
883
|
readonly SIR_SYNC: "sir-sync";
|
|
1555
884
|
readonly ACTIVITY_LOOKAHEAD_SYNC: "activity-lookahead-sync";
|
|
1556
885
|
};
|
|
1557
886
|
declare const DISPATCH_ACTION_KINDS: ReadonlyArray<DispatchActionKind>;
|
|
1558
887
|
|
|
1559
|
-
/**
|
|
1560
|
-
* Canonical vocabulary of activity-creation triggers.
|
|
1561
|
-
*
|
|
1562
|
-
* The engine's `activity-create` intent does NOT carry this field — these
|
|
1563
|
-
* kinds name the USER trigger the consumer's creation hub routes on before
|
|
1564
|
-
* dispatching (`requestActivityCreation` / `CreateActivityIntent` in
|
|
1565
|
-
* `react_client` core path). The engine owns the vocabulary so the client
|
|
1566
|
-
* and the bridge import ONE canonical source instead of retyping the
|
|
1567
|
-
* literals (CONSTANTES_DECISIONES paso 1). The string VALUES are a frozen
|
|
1568
|
-
* contract — verified verbatim against the client union
|
|
1569
|
-
* (`core/features/createActivity/intents.ts`) on 2026-07-02.
|
|
1570
|
-
*
|
|
1571
|
-
* - `CHILD` — per-row "+" button (runs the lookahead guard).
|
|
1572
|
-
* - `CHILD_CONFIRMED` — lookahead-cleanup modal confirm (guard already passed).
|
|
1573
|
-
* - `LINE` — green bar "add-task-by-line".
|
|
1574
|
-
* - `ROOT` — generic "+" (header / create), no guard.
|
|
1575
|
-
*/
|
|
1576
888
|
declare const CREATION_KIND: {
|
|
1577
889
|
readonly CHILD: "child";
|
|
1578
890
|
readonly CHILD_CONFIRMED: "child-confirmed";
|
|
@@ -1581,16 +893,6 @@ declare const CREATION_KIND: {
|
|
|
1581
893
|
};
|
|
1582
894
|
type CreationKind = (typeof CREATION_KIND)[keyof typeof CREATION_KIND];
|
|
1583
895
|
|
|
1584
|
-
/**
|
|
1585
|
-
* Canonical catalog of dispatch rejection reasons.
|
|
1586
|
-
*
|
|
1587
|
-
* `DispatchResult.reason` stays `string` on purpose: it is the cross-domain
|
|
1588
|
-
* bag that also carries pipeline gate codes (`ParseErrorCode`,
|
|
1589
|
-
* `ValidationReason`, `EditDenialReason`) and the link-engine rejected
|
|
1590
|
-
* verdicts. This module covers the reasons DISPATCH ITSELF produces, so
|
|
1591
|
-
* producers and tests share one compile-checked source. The string VALUES
|
|
1592
|
-
* are a frozen contract — the bridge matches on them.
|
|
1593
|
-
*/
|
|
1594
896
|
declare const REJECTION_REASON: {
|
|
1595
897
|
readonly CANNOT_EDIT: "cannot_edit";
|
|
1596
898
|
readonly PARSE_ERROR: "parse_error";
|
|
@@ -1614,11 +916,6 @@ declare const REJECTION_REASON: {
|
|
|
1614
916
|
};
|
|
1615
917
|
type DispatchRejectReason = (typeof REJECTION_REASON)[keyof typeof REJECTION_REASON];
|
|
1616
918
|
|
|
1617
|
-
/**
|
|
1618
|
-
* Amplitude event names emitted by the dispatch layer (structure mutations).
|
|
1619
|
-
* Analytics CONTRACT — values must stay byte-identical to production.
|
|
1620
|
-
* Column-level entry events live in `columns/shared/trackingEvents.ts`.
|
|
1621
|
-
*/
|
|
1622
919
|
declare const DISPATCH_TRACK_EVENT: {
|
|
1623
920
|
readonly ACTIVITY_CREATION: "schedule_activity_creation";
|
|
1624
921
|
readonly ACTIVITY_DELETION: "schedule_activity_deletion";
|
|
@@ -1628,4 +925,4 @@ declare const DISPATCH_TRACK_EVENT: {
|
|
|
1628
925
|
};
|
|
1629
926
|
type DispatchTrackEvent = (typeof DISPATCH_TRACK_EVENT)[keyof typeof DISPATCH_TRACK_EVENT];
|
|
1630
927
|
|
|
1631
|
-
export { ACTIVITY_TYPE, type ActivityCreter, type ActivityId, type ActivityType, type BackendActivityInput, type BackendCalendarInput, type BackendLinkInput, type BackendScheduleInput, type BackendSectorInput, type BaselinePoint, type BaselineVersion, 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, LINK_CODE_TO_TYPE, LINK_TYPE, LINK_TYPE_CODE, type Link, type LinkId, type LinkType, type LinkTypeCode, type LinksBatchOperation, NEW_ACTIVITY_DEFAULTS, 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 };
|
|
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 };
|