Skip to content

Editing, Validation, Diagnostics

These APIs are presentation-free. They make decisions and shape data, while your application owns forms, popovers, panels, toasts, and persistence.

import {
GANTT_BEFORE_ASSIGNMENT_CHANGE_EVENT,
GANTT_BEFORE_DEPENDENCY_CHANGE_EVENT,
GANTT_BEFORE_TASK_CHANGE_EVENT,
TASK_EDITOR_FIELD_SCHEMA,
buildDependencyValidationSummary,
buildResourceOverallocationSummary,
createApprovalGateValidator,
createForbiddenDateRangeValidator,
createGanttBeforeTaskChangeValidationHandler,
createGanttRoleEditabilityHelpers,
createLockedPhaseValidator,
createRoleGuardValidator,
createTaskDetailPopoverModel,
createTaskEditorFormValues,
normalizeTaskEditorSubmit,
} from '@revolist/revogrid-enterprise';

Before-change events are cancelable. Use role helpers to decide whether a task, dependency, or assignment mutation should proceed. Call event.preventDefault() to stop the packaged mutation before the Gantt store changes; timeline previews are cleared and the next render keeps the previous task, dependency, or assignment data.

const policy = {
roles: {
viewer: [],
planner: ['task:*', 'dependency:create', 'dependency:delete'],
staffing: ['assignment:edit'],
admin: ['*'],
},
fallback: 'deny',
} as const;
const editability = createGanttRoleEditabilityHelpers(policy);
const getRoleContext = () => ({ roles: currentUser.roles });
grid.addEventListener(GANTT_BEFORE_TASK_CHANGE_EVENT, (event) => {
if (!editability.canEditTask(event.detail, getRoleContext())) {
event.preventDefault();
}
});
grid.addEventListener(GANTT_BEFORE_DEPENDENCY_CHANGE_EVENT, (event) => {
if (!editability.canEditDependency(event.detail, getRoleContext())) {
event.preventDefault();
}
});
grid.addEventListener(GANTT_BEFORE_ASSIGNMENT_CHANGE_EVENT, (event) => {
if (!editability.canEditAssignment(event.detail, getRoleContext())) {
event.preventDefault();
}
});

Task before-change actions cover move, resize, create, edit, progress, split, indent, outdent, and delete. Dependency before-change actions cover create, delete, edit, and replace; replace is emitted by predecessor/successor inline field edits and includes previousDependencies plus proposed dependencies because one cell edit can add, update, and remove multiple links. Assignment edits emit proposed assignments and previousAssignments.

Use the field schema and initial value builder to render a framework form, then normalize submitted values into a TaskUpdate patch.

For a complete standalone UI example, see Task Editing.

const fields = TASK_EDITOR_FIELD_SCHEMA.filter((field) =>
['name', 'startDate', 'endDate', 'progressPercent', 'resourceLabels'].includes(field.id),
);
const values = createTaskEditorFormValues(task, resources, assignments);
renderTaskEditor({
fields,
values,
onSubmit(nextValues) {
const result = normalizeTaskEditorSubmit(task, nextValues);
if (!result.ok) {
renderErrors(result.errors);
return;
}
updateTask(task.id, result.patch);
},
});

createTaskDetailPopoverModel() returns sections, fields, warnings, and resource labels. Your app handles hover/focus, positioning, and rendering.

const model = createTaskDetailPopoverModel({
row,
task,
resources,
assignments,
});
renderPopover({
title: model.title,
subtitle: model.subtitle,
sections: model.sections,
resources: model.resourceAssignments.map((assignment) => assignment.displayValue),
});

Use dependency summaries to power badges, side panels, or task focus commands.

const summary = buildDependencyValidationSummary({
conflicts: schedulerResult.conflicts,
issues: schedulerResult.issues,
rows: projectedRows,
});
renderDiagnosticsPanel({
count: summary.totalCount,
groups: summary.groups,
});

Use resource summaries when staffing dashboards need to show who is overloaded and when.

const overloads = buildResourceOverallocationSummary({
rows: resourcePlanningRows,
loadSummaries: resourceLoadSummaries,
conflicts: schedulerResult.conflicts,
});
renderResourceHealth({
count: overloads.totalCount,
peakOverloadUnits: overloads.peakOverloadUnits,
groups: overloads.groups,
});

Compose validators and attach the generated handler to gantt-before-task-change.

const validators = [
createRoleGuardValidator({ policy }),
createForbiddenDateRangeValidator({
ranges: [{ startDate: '2026-05-20', endDate: '2026-05-27', label: 'release freeze' }],
}),
createLockedPhaseValidator({
lockedPhaseIds: ['closed'],
getPhaseId: (detail) => detail.taskId ? phaseByTaskId[detail.taskId] : null,
}),
createApprovalGateValidator({
actions: ['move', 'resize', 'progress'],
requiresApproval: (detail) => detail.changes.progressPercent === 100,
}),
];
grid.addEventListener(
GANTT_BEFORE_TASK_CHANGE_EVENT,
createGanttBeforeTaskChangeValidationHandler({
context: () => validationContext,
validators,
onReject: ({ decision }) => showValidationToast(decision.message ?? 'Change rejected'),
}),
);