@outbuild-company/schedule-core 1.17.0 → 1.17.2

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.cjs CHANGED
@@ -1290,6 +1290,24 @@ function getFieldDescriptor(field) {
1290
1290
  return FIELD_REGISTRY[field] ?? null;
1291
1291
  }
1292
1292
 
1293
+ // src/internal/view-attributes/types.ts
1294
+ var VISUAL_ROW_KIND = {
1295
+ GROUP: "group",
1296
+ ACTIVITY: "activity"
1297
+ };
1298
+ var UNASSIGNED_VALUE_ID = "unassigned";
1299
+ var GROUP_ID_PREFIX = "g";
1300
+ var GROUP_ID_SEPARATOR = ":";
1301
+ var UNASSIGNED_GROUP_LABEL = "Unassigned";
1302
+ var MINIMUM_GROUP_DEPTH = 1;
1303
+ var MINIMUM_GROUP_LEVEL = 1;
1304
+ function buildGroupRowId(attributeKey, valueId) {
1305
+ return `${GROUP_ID_PREFIX}${GROUP_ID_SEPARATOR}${attributeKey}${GROUP_ID_SEPARATOR}${valueId}`;
1306
+ }
1307
+ function appendGroupRowId(parentRowId, attributeKey, valueId) {
1308
+ return `${parentRowId}${GROUP_ID_SEPARATOR}${attributeKey}${GROUP_ID_SEPARATOR}${valueId}`;
1309
+ }
1310
+
1293
1311
  // src/internal/view-attributes/index-catalog.ts
1294
1312
  var ROOT_PARENT_SENTINEL = null;
1295
1313
  function childListKey(parentId) {
@@ -1363,10 +1381,13 @@ function expandSelectedValueIds(catalog, selectedIds) {
1363
1381
  }
1364
1382
  return expanded;
1365
1383
  }
1366
- function truncatedPath(catalog, valueId, depth) {
1384
+ function levelWindowPath(catalog, valueId, startLevel, depth) {
1385
+ const from = Math.max(startLevel, MINIMUM_GROUP_LEVEL);
1367
1386
  const path = catalog.pathByValueId.get(valueId);
1368
- if (path === void 0 || path.length === 0) return [valueId];
1369
- return path.slice(0, Math.max(depth, 1));
1387
+ if (path === void 0 || path.length === 0) {
1388
+ return from > MINIMUM_GROUP_LEVEL ? [] : [valueId];
1389
+ }
1390
+ return path.slice(from - 1, Math.max(depth, from));
1370
1391
  }
1371
1392
 
1372
1393
  // src/internal/view-attributes/match.ts
@@ -1988,21 +2009,32 @@ function normalizeParentKey(parent) {
1988
2009
  var NOT_SET_SORT_VALUE = Number.MAX_SAFE_INTEGER;
1989
2010
  function getChildrenInVisualOrder(parentId, adapter) {
1990
2011
  const children = collectChildrenSnapshots(parentId, adapter);
1991
- const userOrder = adapter.getOrderComparator?.() ?? null;
1992
- const mode = adapter.getOrderMode?.() ?? ORDER_MODE.STANDARD;
1993
- if (userOrder !== null && mode === ORDER_MODE.KEEP_WBS_IN_PLACE) {
1994
- return sortKeepWbsInPlace(children, userOrder, adapter);
2012
+ const ordered = sortSiblingsInOrderMode(
2013
+ children,
2014
+ adapter.getOrderComparator?.() ?? null,
2015
+ adapter.getOrderMode?.() ?? ORDER_MODE.STANDARD,
2016
+ (activityId) => adapter.hasChildren(activityId)
2017
+ );
2018
+ return ordered.map((activity) => String(activity.id));
2019
+ }
2020
+ function sortSiblingsInOrderMode(siblings, userOrder, mode, hasChildren) {
2021
+ if (userOrder === null) {
2022
+ siblings.sort(compareActivitiesInVisualOrder);
2023
+ return siblings;
2024
+ }
2025
+ if (mode === ORDER_MODE.KEEP_WBS_IN_PLACE) {
2026
+ return sortKeepWbsInPlace(siblings, userOrder, hasChildren);
1995
2027
  }
1996
- children.sort(
1997
- userOrder === null ? compareActivitiesInVisualOrder : (a, b) => userOrder(a, b) || compareActivitiesInVisualOrder(a, b)
2028
+ siblings.sort(
2029
+ (a, b) => userOrder(a, b) || compareActivitiesInVisualOrder(a, b)
1998
2030
  );
1999
- return children.map((activity) => String(activity.id));
2031
+ return siblings;
2000
2032
  }
2001
- function sortKeepWbsInPlace(children, userOrder, adapter) {
2033
+ function sortKeepWbsInPlace(siblings, userOrder, hasChildren) {
2002
2034
  const leafActivities = [];
2003
2035
  const wbsItems = [];
2004
- for (const child of children) {
2005
- if (adapter.hasChildren(child.id)) {
2036
+ for (const child of siblings) {
2037
+ if (hasChildren(child.id)) {
2006
2038
  wbsItems.push(child);
2007
2039
  } else {
2008
2040
  leafActivities.push(child);
@@ -2012,10 +2044,9 @@ function sortKeepWbsInPlace(children, userOrder, adapter) {
2012
2044
  (a, b) => userOrder(a, b) || compareActivitiesInVisualOrder(a, b)
2013
2045
  );
2014
2046
  wbsItems.sort(compareActivitiesInVisualOrder);
2015
- return [
2016
- ...leafActivities.map((a) => String(a.id)),
2017
- ...wbsItems.map((a) => String(a.id))
2018
- ];
2047
+ siblings.length = 0;
2048
+ siblings.push(...leafActivities, ...wbsItems);
2049
+ return siblings;
2019
2050
  }
2020
2051
  function iterateInVisualOrder(adapter) {
2021
2052
  const result = [];
@@ -2148,44 +2179,66 @@ function dispatchSortSet(action, deps) {
2148
2179
  return { ok: true, changes };
2149
2180
  }
2150
2181
 
2151
- // src/internal/view-attributes/types.ts
2152
- var VISUAL_ROW_KIND = {
2153
- GROUP: "group",
2154
- ACTIVITY: "activity"
2155
- };
2156
- var UNASSIGNED_VALUE_ID = "unassigned";
2157
- var GROUP_ID_PREFIX = "g";
2158
- var GROUP_ID_SEPARATOR = ":";
2159
- var UNASSIGNED_GROUP_LABEL = "Unassigned";
2160
- var MINIMUM_GROUP_DEPTH = 1;
2161
- function buildGroupRowId(attributeKey, valueId) {
2162
- return `${GROUP_ID_PREFIX}${GROUP_ID_SEPARATOR}${attributeKey}${GROUP_ID_SEPARATOR}${valueId}`;
2163
- }
2164
-
2165
2182
  // src/dispatch/view-attributes.ts
2166
2183
  function isFiniteInteger(value) {
2167
2184
  return Number.isInteger(value) && Number.isFinite(value);
2168
2185
  }
2169
- function toGroupState(input) {
2170
- if (typeof input.attributeKey !== "string" || input.attributeKey.length === 0) {
2186
+ function isValidDirection(value) {
2187
+ return value === ORDER_DIRECTION.ASCENDING || value === ORDER_DIRECTION.DESCENDING;
2188
+ }
2189
+ function toLevelWindow(input) {
2190
+ const startLevel = input.startLevel ?? MINIMUM_GROUP_LEVEL;
2191
+ if (!isFiniteInteger(startLevel) || startLevel < MINIMUM_GROUP_LEVEL) {
2171
2192
  return null;
2172
2193
  }
2173
2194
  if (!isFiniteInteger(input.depth) || input.depth < MINIMUM_GROUP_DEPTH) {
2174
2195
  return null;
2175
2196
  }
2176
- if (input.direction !== ORDER_DIRECTION.ASCENDING && input.direction !== ORDER_DIRECTION.DESCENDING) {
2197
+ if (input.depth < startLevel) return null;
2198
+ return { startLevel, depth: input.depth };
2199
+ }
2200
+ var INVALID_CRITERION = /* @__PURE__ */ Symbol("invalid-group-criterion");
2201
+ function toSecondaryCriterion(input) {
2202
+ const secondary = input.secondary;
2203
+ if (secondary === void 0 || secondary === null) return null;
2204
+ if (typeof secondary !== "object") return INVALID_CRITERION;
2205
+ if (typeof secondary.attributeKey !== "string" || secondary.attributeKey.length === 0) {
2206
+ return INVALID_CRITERION;
2207
+ }
2208
+ if (secondary.attributeKey === input.attributeKey) return INVALID_CRITERION;
2209
+ const window2 = toLevelWindow(secondary);
2210
+ if (window2 === null) return INVALID_CRITERION;
2211
+ if (!isValidDirection(secondary.direction)) return INVALID_CRITERION;
2212
+ return {
2213
+ attributeKey: secondary.attributeKey,
2214
+ startLevel: window2.startLevel,
2215
+ depth: window2.depth,
2216
+ direction: secondary.direction
2217
+ };
2218
+ }
2219
+ function toGroupState(input) {
2220
+ if (typeof input.attributeKey !== "string" || input.attributeKey.length === 0) {
2221
+ return null;
2222
+ }
2223
+ const window2 = toLevelWindow(input);
2224
+ if (window2 === null) return null;
2225
+ if (!isValidDirection(input.direction)) {
2177
2226
  return null;
2178
2227
  }
2179
2228
  if (typeof input.includeWbs !== "boolean") return null;
2180
2229
  if (!Array.isArray(input.collapsedIds) || input.collapsedIds.some((id) => typeof id !== "string")) {
2181
2230
  return null;
2182
2231
  }
2232
+ const secondary = toSecondaryCriterion(input);
2233
+ if (secondary === INVALID_CRITERION) return null;
2183
2234
  return {
2184
2235
  attributeKey: input.attributeKey,
2185
- depth: input.depth,
2236
+ startLevel: window2.startLevel,
2237
+ depth: window2.depth,
2186
2238
  direction: input.direction,
2187
2239
  includeWbs: input.includeWbs,
2188
- collapsedIds: new Set(input.collapsedIds)
2240
+ collapsedIds: new Set(input.collapsedIds),
2241
+ secondary
2189
2242
  };
2190
2243
  }
2191
2244
  function emptyViewChanges(action) {
@@ -4973,6 +5026,7 @@ var POST_PROCESSOR = {
4973
5026
 
4974
5027
  // src/columns/shared/error-codes.ts
4975
5028
  var VALIDATION_REASON = {
5029
+ UNCHANGED: "unchanged",
4976
5030
  NEGATIVE_DURATION: "negative_duration"};
4977
5031
  var EDIT_DENIAL_REASON = {
4978
5032
  COMPLETED: "completed",
@@ -5446,14 +5500,6 @@ var startDatePipeline = {
5446
5500
  if (isCompletedActivity(activity)) {
5447
5501
  return cannotEdit("completed", "cant_edit_completed_activity");
5448
5502
  }
5449
- if (!isValidDate(edit.newValue)) return canEdit();
5450
- if (isSameDay(activity.startDate, edit.newValue)) return canEdit();
5451
- if (edit.newValue.getTime() > activity.startDate.getTime()) {
5452
- return cannotEdit(
5453
- "sir_start_after_progress",
5454
- "sir_cant_move_start_after_progress"
5455
- );
5456
- }
5457
5503
  return canEdit();
5458
5504
  },
5459
5505
  parseInput(rawValue, _activity, _ctx) {
@@ -15939,6 +15985,7 @@ function mergeBeforeSnapshots(beforeSnap, ids, deps) {
15939
15985
 
15940
15986
  // src/dispatch/handlers/bulk-edit.ts
15941
15987
  var CUSTOM_ID_COLUMN = COLUMN.CUSTOM_ID;
15988
+ var SIR_APPLY_EVENT_SOURCE2 = "sir-apply";
15942
15989
  async function dispatchBulkEdit(action, options, deps) {
15943
15990
  const { adapter, scheduler, sector } = deps;
15944
15991
  const editsIncludeDuration = action.edits.some(
@@ -15962,6 +16009,13 @@ async function dispatchBulkEdit(action, options, deps) {
15962
16009
  for (const edit of action.edits) {
15963
16010
  const outcome = applyOneEdit2(edit, deps, action.eventSource);
15964
16011
  verdicts.push(outcome.verdict);
16012
+ if (action.eventSource === SIR_APPLY_EVENT_SOURCE2 && !outcome.verdict.ok && outcome.verdict.reason !== VALIDATION_REASON.UNCHANGED) {
16013
+ return {
16014
+ ok: false,
16015
+ reason: outcome.verdict.reason ?? REJECTION_REASON.INVALID,
16016
+ ...outcome.verdict.alertKey ? { alertKey: outcome.verdict.alertKey } : {}
16017
+ };
16018
+ }
15965
16019
  if (!outcome.verdict.ok || !outcome.changes) continue;
15966
16020
  const constraintWarning = toConstraintWarning(
15967
16021
  edit.activityId,
@@ -20092,10 +20146,10 @@ var ViewStateStore = class {
20092
20146
  };
20093
20147
 
20094
20148
  // src/internal/view-attributes/project-rows.ts
20095
- function createNode(attributeKey, valueId, label, color, level, sequence) {
20149
+ function createNode(rowId, valueId, label, color, level, sequence) {
20096
20150
  return {
20097
20151
  valueId,
20098
- id: buildGroupRowId(attributeKey, valueId),
20152
+ id: rowId,
20099
20153
  label,
20100
20154
  color,
20101
20155
  level,
@@ -20104,6 +20158,35 @@ function createNode(attributeKey, valueId, label, color, level, sequence) {
20104
20158
  activities: []
20105
20159
  };
20106
20160
  }
20161
+ function buildGroupTree(activities, criterion, catalog, root, nestIds) {
20162
+ const { attributeKey, startLevel, depth } = criterion;
20163
+ for (const activity of activities) {
20164
+ const assignedValueId = catalog.assignmentByActivityId.get(
20165
+ String(activity.id)
20166
+ );
20167
+ const window2 = assignedValueId === void 0 ? [] : levelWindowPath(catalog, assignedValueId, startLevel, depth);
20168
+ const path = window2.length === 0 ? [UNASSIGNED_VALUE_ID] : window2;
20169
+ let cursor = root;
20170
+ path.forEach((valueId, index) => {
20171
+ let child = cursor.children.get(valueId);
20172
+ if (child === void 0) {
20173
+ const value = catalog.valueById.get(valueId);
20174
+ child = createNode(
20175
+ nestIds ? appendGroupRowId(cursor.id, attributeKey, valueId) : buildGroupRowId(attributeKey, valueId),
20176
+ valueId,
20177
+ value?.label ?? (valueId === UNASSIGNED_VALUE_ID ? UNASSIGNED_GROUP_LABEL : valueId),
20178
+ value?.color ?? null,
20179
+ root.level + 1 + index,
20180
+ value?.sequence ?? Number.MAX_SAFE_INTEGER
20181
+ );
20182
+ cursor.children.set(valueId, child);
20183
+ }
20184
+ cursor = child;
20185
+ });
20186
+ cursor.activities.push(activity);
20187
+ }
20188
+ return root;
20189
+ }
20107
20190
  function countActivities(node) {
20108
20191
  let total = node.activities.length;
20109
20192
  for (const child of node.children.values()) total += countActivities(child);
@@ -20140,7 +20223,31 @@ function emitWbsOrderedActivities(memberActivities, activitiesInVisualOrder, par
20140
20223
  });
20141
20224
  }
20142
20225
  }
20143
- function emitNode(node, group, activitiesInVisualOrder, parentOf, rows) {
20226
+ function emitActivities(node, context) {
20227
+ const { rows } = context;
20228
+ if (context.group.includeWbs) {
20229
+ emitWbsOrderedActivities(
20230
+ node.activities,
20231
+ context.activitiesInVisualOrder,
20232
+ context.parentOf,
20233
+ rows
20234
+ );
20235
+ return;
20236
+ }
20237
+ for (const activity of sortSiblingsInOrderMode(
20238
+ [...node.activities],
20239
+ context.comparator,
20240
+ context.orderMode,
20241
+ context.hasChildren
20242
+ )) {
20243
+ rows.push({
20244
+ kind: VISUAL_ROW_KIND.ACTIVITY,
20245
+ activityId: String(activity.id)
20246
+ });
20247
+ }
20248
+ }
20249
+ function emitNode(node, childDirection, allowSecondary, context) {
20250
+ const { group, rows } = context;
20144
20251
  rows.push({
20145
20252
  kind: VISUAL_ROW_KIND.GROUP,
20146
20253
  id: node.id,
@@ -20150,25 +20257,25 @@ function emitNode(node, group, activitiesInVisualOrder, parentOf, rows) {
20150
20257
  activityCount: countActivities(node)
20151
20258
  });
20152
20259
  if (group.collapsedIds.has(node.id)) return;
20153
- for (const child of sortedChildNodes(node, group.direction)) {
20154
- emitNode(child, group, activitiesInVisualOrder, parentOf, rows);
20260
+ for (const child of sortedChildNodes(node, childDirection)) {
20261
+ emitNode(child, childDirection, allowSecondary, context);
20155
20262
  }
20156
20263
  if (node.activities.length === 0) return;
20157
- if (group.includeWbs) {
20158
- emitWbsOrderedActivities(
20264
+ const secondary = group.secondary;
20265
+ if (allowSecondary && secondary != null && context.secondaryCatalog != null) {
20266
+ const secondaryRoot = buildGroupTree(
20159
20267
  node.activities,
20160
- activitiesInVisualOrder,
20161
- parentOf,
20162
- rows
20268
+ secondary,
20269
+ context.secondaryCatalog,
20270
+ createNode(node.id, "", "", null, node.level, 0),
20271
+ true
20163
20272
  );
20273
+ for (const child of sortedChildNodes(secondaryRoot, secondary.direction)) {
20274
+ emitNode(child, secondary.direction, false, context);
20275
+ }
20164
20276
  return;
20165
20277
  }
20166
- for (const activity of node.activities) {
20167
- rows.push({
20168
- kind: VISUAL_ROW_KIND.ACTIVITY,
20169
- activityId: String(activity.id)
20170
- });
20171
- }
20278
+ emitActivities(node, context);
20172
20279
  }
20173
20280
  function projectVisualRows(input) {
20174
20281
  const visibleActivities = input.activitiesInVisualOrder.filter(
@@ -20181,34 +20288,26 @@ function projectVisualRows(input) {
20181
20288
  }));
20182
20289
  }
20183
20290
  const { group, catalog } = input;
20184
- const root = createNode(group.attributeKey, "", "", null, -1, 0);
20185
- for (const activity of visibleActivities) {
20186
- const assignedValueId = catalog.assignmentByActivityId.get(
20187
- String(activity.id)
20188
- );
20189
- const path = assignedValueId === void 0 ? [UNASSIGNED_VALUE_ID] : truncatedPath(catalog, assignedValueId, group.depth);
20190
- let cursor = root;
20191
- path.forEach((valueId, index) => {
20192
- let child = cursor.children.get(valueId);
20193
- if (child === void 0) {
20194
- const value = catalog.valueById.get(valueId);
20195
- child = createNode(
20196
- group.attributeKey,
20197
- valueId,
20198
- value?.label ?? (valueId === UNASSIGNED_VALUE_ID ? UNASSIGNED_GROUP_LABEL : valueId),
20199
- value?.color ?? null,
20200
- index,
20201
- value?.sequence ?? Number.MAX_SAFE_INTEGER
20202
- );
20203
- cursor.children.set(valueId, child);
20204
- }
20205
- cursor = child;
20206
- });
20207
- cursor.activities.push(activity);
20208
- }
20291
+ const root = buildGroupTree(
20292
+ visibleActivities,
20293
+ group,
20294
+ catalog,
20295
+ createNode("", "", "", null, -1, 0),
20296
+ false
20297
+ );
20209
20298
  const rows = [];
20299
+ const context = {
20300
+ group,
20301
+ activitiesInVisualOrder: input.activitiesInVisualOrder,
20302
+ parentOf: input.parentOf,
20303
+ hasChildren: input.hasChildren,
20304
+ comparator: input.comparator,
20305
+ orderMode: input.orderMode,
20306
+ secondaryCatalog: input.secondaryCatalog,
20307
+ rows
20308
+ };
20210
20309
  for (const child of sortedChildNodes(root, group.direction)) {
20211
- emitNode(child, group, input.activitiesInVisualOrder, input.parentOf, rows);
20310
+ emitNode(child, group.direction, true, context);
20212
20311
  }
20213
20312
  return rows;
20214
20313
  }
@@ -20484,6 +20583,7 @@ var ScheduleState = class _ScheduleState {
20484
20583
  if (this._cache.visualRows) return this._cache.visualRows;
20485
20584
  const group = this._viewAttributes.getGroup();
20486
20585
  const catalog = group === null ? null : this._viewAttributes.getCatalog(group.attributeKey);
20586
+ const secondaryCatalog = group?.secondary == null ? null : this._viewAttributes.getCatalog(group.secondary.attributeKey);
20487
20587
  const activitiesInVisualOrder = [];
20488
20588
  for (const activityId of this.getVisualOrderIds()) {
20489
20589
  const activity = this.getActivity(activityId);
@@ -20493,8 +20593,12 @@ var ScheduleState = class _ScheduleState {
20493
20593
  activitiesInVisualOrder,
20494
20594
  isVisible: (activityId) => this.isVisible(activityId),
20495
20595
  parentOf: (activityId) => this.getParentId(activityId),
20596
+ hasChildren: (activityId) => this.hasChildren(activityId),
20496
20597
  group,
20497
- catalog
20598
+ catalog,
20599
+ secondaryCatalog,
20600
+ comparator: this.getOrderComparator(),
20601
+ orderMode: this.getOrderMode()
20498
20602
  });
20499
20603
  return this._cache.visualRows;
20500
20604
  }
@@ -25325,6 +25429,7 @@ exports.LINK_RELATIONSHIP = LINK_RELATIONSHIP;
25325
25429
  exports.LINK_TYPE = LINK_TYPE;
25326
25430
  exports.LINK_TYPE_CODE = LINK_TYPE_CODE;
25327
25431
  exports.MINIMUM_GROUP_DEPTH = MINIMUM_GROUP_DEPTH;
25432
+ exports.MINIMUM_GROUP_LEVEL = MINIMUM_GROUP_LEVEL;
25328
25433
  exports.NEW_ACTIVITY_DEFAULTS = NEW_ACTIVITY_DEFAULTS;
25329
25434
  exports.NOT_STARTED_PROGRESS = NOT_STARTED_PROGRESS;
25330
25435
  exports.ORDER_DIRECTION = ORDER_DIRECTION;
@@ -25342,6 +25447,7 @@ exports.UNASSIGNED_VALUE_ID = UNASSIGNED_VALUE_ID;
25342
25447
  exports.VIEW_ATTRIBUTE_KIND = VIEW_ATTRIBUTE_KIND;
25343
25448
  exports.VISUAL_ROW_KIND = VISUAL_ROW_KIND;
25344
25449
  exports.WORK_TIME_DIRECTION = WORK_TIME_DIRECTION;
25450
+ exports.appendGroupRowId = appendGroupRowId;
25345
25451
  exports.buildGroupRowId = buildGroupRowId;
25346
25452
  exports.checkNoUpdatedLinks = checkNoUpdatedLinks;
25347
25453
  exports.computeExpectedProgress = computeExpectedProgress;