# Task Editing
URL: https://pro.rv-grid.com/guides/gantt/interaction/task-editing/
Source: src/content/docs/guides/gantt/interaction/task-editing.mdx
Description: Inline editing, the default task editor dialog, and scheduler-backed mutation flow.
Task edits route through mutation services and scheduler recomputation. Before applying task changes, Gantt emits cancelable `gantt-before-task-change`; dependency field edits emit `gantt-before-dependency-change`; assignee edits emit `gantt-before-assignment-change`.

Command/event surface is defined in:

- `packages/enterprise/plugins/gantt/grid/gantt-events.ts`
- `packages/enterprise/plugins/gantt/grid/gantt-plugin.ts`

For audited changes, pair with Gantt history tooling (`features/history`).

## Inline Editing

Inline Gantt edits update the task through the Gantt mutation services, recompute the project, and emit history snapshots for user changes. Initial project hydration is not recorded as a history step.

Call `event.preventDefault()` in a before-change listener to reject a field edit before the task, dependency, or assignment store is mutated. Rejected predecessor/successor edits leave the old dependency rows in place; rejected assignee edits leave the old assignment rows in place.

## Task Editor Dialog

`GanttPlugin` installs the packaged task editor dialog by default, so every Gantt grid gets the row context-menu editor without registering an extra plugin. The same editor contract is also available as presentation-free helpers: render controls from `TASK_EDITOR_FIELD_SCHEMA`, seed them with `createTaskEditorFormValues()`, and turn submitted values into a `TaskUpdate` patch with `normalizeTaskEditorSubmit()`.

The packaged dialog is responsive for mobile use. On phone-width viewports it becomes a full-viewport dialog, switches task fields and dependency rows to a single column, keeps the form body scrollable, and uses larger controls for touch input. The same validation and submit flow runs on desktop, tablet, and phone layouts.

## How It Works

The task editor demo opens the editor from the Gantt row context menu and includes the packaged Gantt toolbar:

- The grid renders normal Gantt tasks, dependencies, calendars, resources, and assignments.
- Right-click a task row and choose **Edit...**.
- Use row context-menu **Add** to insert a `New task N` row without opening the editor, or toolbar **Task** to create a task and open the same editor for the new row.
- The auto-installed task editor dialog adds the row-menu edit item and owns the native HTML `<dialog>`.
- New tasks open in the editor by default after the Gantt plugin emits its post-create lifecycle event. Set `ganttTaskEditorDialog.openOnCreate` to `false` when task creation should stay silent.
- The dialog renders a tabbed task information UI from `TASK_EDITOR_FIELD_SCHEMA`.
- **Details** contains core schedule fields, constraints, notes, and manual scheduling.
- **Advanced** contains task type, effort-driven scheduling, work, remaining duration, actual dates, inactive state, leveling priority, can-level, and leveling delay.
- **Predecessors** and **Successors** edit dependency target, dependency type, and lead/lag days.
- **Assignments** renders the resource assignment picker when `ganttResources` and `ganttAssignments` are available.
- The patch preview calls `normalizeTaskEditorSubmit(task, values)` on every edit.
- The submit button applies the returned patch through the Gantt runtime/provider edit path, updates `grid.ganttAssignments` and `grid.ganttDependencies`, and refreshes the project.

```ts
grid.plugins = [GanttPlugin];
grid.ganttTaskEditorDialog = {
  title: 'Task details',
  description: 'Update task fields and save the changes to the project schedule.',
  applyLabel: 'Save changes',
  localeText: {
    tabs: {
      assignments: 'Team',
    },
    fields: {
      effortMode: 'Task type',
    },
    resourcesSearch: {
      placeholder: 'Find a resource',
    },
  },
};

const values = createTaskEditorFormValues(task, resources, assignments);
const result = normalizeTaskEditorSubmit(task, values);

if (result.ok) {
  updateTask(task.id, result.patch);
}
```

## Fields

The schema includes editable task fields such as `name`, `startDate`, `endDate`, `durationDays`, `progressPercent`, constraints, deadlines, actual dates, work, effort mode, effort-driven scheduling, inactive state, leveling controls, manual scheduling, and `notes`. It also includes display fields such as `resourceLabels`.

Date fields open as date-only controls by default. Use the field-level **Time** toggle in the packaged dialog when you need minute-level scheduling; the dialog stores the submitted value as a UTC ISO datetime.

Set `gantt.dateFormats.editor` when the packaged dialog should show custom date text instead of native date inputs. In that mode, the dialog uses a text input, calls your parser as the user types, and still submits ISO dates to `normalizeTaskEditorSubmit()` and the Gantt mutation services:

```ts
grid.gantt = {
  ...project,
  dateFormats: {
    locale: 'en-GB',
    timeZone: 'UTC',
    table: { day: '2-digit', month: '2-digit', year: 'numeric' },
    tooltip: { day: '2-digit', month: 'short', year: 'numeric' },
    editor: {
      options: { day: '2-digit', month: '2-digit', year: 'numeric' },
      parser(value) {
        const match = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(value.trim());
        return match ? `${match[3]}-${match[2]}-${match[1]}` : null;
      },
    },
  },
};
```

Custom date formats are presentation and input helpers only. Keep persisted tasks as `YYYY-MM-DD` dates or UTC datetimes ending in `Z`.

`resourceLabels` is read-only in the schema helper because `TaskUpdate` does not own assignment rows. The packaged dialog upgrades that field into a resource assignment picker and writes the selected resources to `grid.ganttAssignments`. Set `ganttTaskEditorDialog.resources` to `false` when you want the field to stay read-only.

Entity panels include search controls: resource assignment searches by resource name and role, while predecessor and successor panels search task names. Resource lists, task dropdowns, dependency type dropdowns, and schema-backed select options are sorted A-Z by their displayed labels.

Use `ganttTaskEditorDialog.localeText` to translate or customize packaged dialog text. It covers tabs, field labels, field option labels, dependency type labels, empty states, search placeholders, switch labels, validation headings, save errors, and context-menu text. Existing top-level `title`, `description`, `applyLabel`, `resetLabel`, `closeLabel`, and `menuItemName` still work as direct overrides for those common labels.

Use `ganttTaskEditorDialog.openOnCreate = false` to keep toolbar or programmatic task creation from opening the dialog. The Gantt row context menu always creates silently through the default **Add** action when task creation is allowed, and the editor plugin prepends its **Edit...** item while preserving custom `rowContextMenu` or `contextMenu` items and `resolve` behavior. Set `gantt.contextMenu = false` to opt out of generated Gantt menu items, or set `ganttTaskEditorDialog.contextMenu` when only the packaged editor menu item should change.

## Validation

`normalizeTaskEditorSubmit()` validates values before returning a patch:

- `name` cannot be empty.
- Date values must be ISO dates like `2026-05-12` or UTC datetimes ending in `Z`.
- `endDate` cannot be earlier than `startDate`.
- Numeric fields must be non-negative.
- `progressPercent` is clamped to `0..100`.
- Unsupported constraint values return field errors.

If both `startDate` and `endDate` are present, the helper recalculates `durationDays` from the date range. If values are unchanged, the default result omits them from the patch.

## Production Wiring

In production, keep the same flow but replace the demo's local source update with your persistence and project-state update path:

```ts
async function submitTaskEditor(task, values) {
  const result = normalizeTaskEditorSubmit(task, values);

  if (!result.ok) {
    renderFieldErrors(result.errors);
    return;
  }

  await api.updateTask(task.id, result.patch);
  grid.source = grid.source.map((row) =>
    row.id === task.id ? { ...row, ...result.patch } : row,
  );
}
```

The Gantt plugin still owns timeline projection and scheduling when the refreshed task source is applied.

The packaged dialog defaults to applying task field patches through the Gantt runtime/provider edit path and replacing the edited task's assignment rows in `grid.ganttAssignments`. This keeps task editor saves visible to `HistoryPlugin` instead of bypassing grid edit tracking with direct `grid.source` replacement. To persist edits first, provide `ganttTaskEditorDialog.onSubmit`; return `false` from that callback when your application has already applied the task and assignment updates and the plugin should skip the default patch.

Dependency tabs also patch `grid.ganttDependencies` by replacing links that involve the edited task. Existing dependency ids are preserved, new rows get deterministic ids, and lag supports negative lead or positive lag days.

Set `preview: true` only for diagnostics or documentation when you want to show the generated `TaskUpdate` JSON.
