Skip to content

Manual Scheduling

Source code
Astro astro
---
import GanttScheduling from '@revolist/revogrid-examples/components/gantt/GanttScheduling.vue';
---

<GanttScheduling client:only="vue" />
TypeScript ts
// src/components/gantt/GanttScheduling.ts
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();

import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';
import type { TaskEntity, DependencyEntity, CalendarEntity } from '@revolist/revogrid-enterprise';
import { currentTheme } from '../composables/useRandomData';

const { isDark } = currentTheme();

const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';

// Calendar-aware scheduling: durations are in working days (Mon–Fri, excluding holidays)
const ganttConfig = {
  id: PROJECT_ID,
  name: 'Website Redesign',
  version: '1',
  currency: 'USD',
  timeZone: 'America/New_York',
  primaryCalendarId: CALENDAR_ID,
  updatedAt: '2026-04-06T00:00:00Z',
  zoomPreset: 'week' as const,
  scheduling: {
    excludeHolidaysFromDuration: true, // durations skip weekends and holidays
  },
  visuals: {
    shadeNonWorkingTime: true, // shade weekend columns on the timeline
    projectLineDate: '2026-04-06', // vertical status-date line
  },
};

// US calendar with public holidays
const calendars: CalendarEntity[] = [
  {
    id: CALENDAR_ID,
    name: 'US Standard',
    timeZone: 'America/New_York',
    workingDays: [1, 2, 3, 4, 5], // Mon–Fri
    holidays: [
      '2026-05-25', // Memorial Day
      '2026-07-04', // Independence Day
    ],
    hoursPerDay: 8,
  },
];

const tasks: TaskEntity[] = [
  {
    id: 't1', projectId: PROJECT_ID, parentId: null,
    wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress',
    startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15,
    progressPercent: 60, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't2', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done',
    startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5,
    progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't3', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done',
    startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0,
    progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
  },
  {
    id: 't4', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress',
    startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10,
    progressPercent: 40, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't5', projectId: PROJECT_ID, parentId: null,
    wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
  },
  {
    id: 't6', projectId: PROJECT_ID, parentId: 't5',
    wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started',
    // constraint: cannot start before May 4 (waiting on external API)
    startDate: '2026-05-04', endDate: '2026-05-20', durationDays: 13,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    constraintType: 'start-no-earlier-than',
    constraintDate: '2026-05-04',
  },
  {
    id: 't7', projectId: PROJECT_ID, parentId: 't5',
    wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    deadlineDate: '2026-05-22', // deadline marker — project expects early delivery
  },
  {
    id: 't8', projectId: PROJECT_ID, parentId: null,
    wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started',
    startDate: '2026-05-28', endDate: '2026-05-28', durationDays: 0,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
  },
];

const dependencies: DependencyEntity[] = [
  { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
  { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
  { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
  { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
  { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
  { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
];

export function load(parentSelector: string) {
  const grid = document.createElement('revo-grid');

  grid.theme = isDark() ? 'darkCompact' : 'compact';
  grid.hideAttribution = true;
  grid.plugins = [GanttPlugin];
  grid.gantt = ganttConfig;
  grid.ganttCalendars = calendars;
  grid.ganttDependencies = dependencies;
  grid.source = tasks;
  grid.columns = [
    createDefaultTaskTableColumn('wbs'),
    createDefaultTaskTableColumn('name'),
  ];

  document.querySelector(parentSelector)?.appendChild(grid);
}
Vue vue
<template>
  <RevoGrid
    hide-attribution
    style="height: 500px"
    :theme="isDark ? 'darkCompact' : 'compact'"
    :plugins="plugins"
    :source="tasks"
    :columns="columns"
    :gantt.prop="ganttConfig"
    :gantt-dependencies.prop="dependencies"
    :gantt-calendars.prop="calendars"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';
import type { TaskEntity, DependencyEntity, CalendarEntity } from '@revolist/revogrid-enterprise';
import { currentThemeVue } from '../composables/useRandomData';

const { isDark } = currentThemeVue();

const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';

const plugins = ref([GanttPlugin]);

const ganttConfig = ref({
  id: PROJECT_ID,
  name: 'Website Redesign',
  version: '1',
  currency: 'USD',
  timeZone: 'America/New_York',
  primaryCalendarId: CALENDAR_ID,
  updatedAt: '2026-04-06T00:00:00Z',
  zoomPreset: 'week' as const,
  scheduling: {
    excludeHolidaysFromDuration: true,
  },
  visuals: {
    shadeNonWorkingTime: true,
    projectLineDate: '2026-04-06',
  },
});

const calendars = ref<CalendarEntity[]>([
  {
    id: CALENDAR_ID,
    name: 'US Standard',
    timeZone: 'America/New_York',
    workingDays: [1, 2, 3, 4, 5],
    holidays: ['2026-05-25', '2026-07-04'],
    hoursPerDay: 8,
  },
]);

const tasks = ref<TaskEntity[]>([
  {
    id: 't1', projectId: PROJECT_ID, parentId: null,
    wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress',
    startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15,
    progressPercent: 60, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't2', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done',
    startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5,
    progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't3', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done',
    startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0,
    progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
  },
  {
    id: 't4', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress',
    startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10,
    progressPercent: 40, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't5', projectId: PROJECT_ID, parentId: null,
    wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
  },
  {
    id: 't6', projectId: PROJECT_ID, parentId: 't5',
    wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started',
    startDate: '2026-05-04', endDate: '2026-05-20', durationDays: 13,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    constraintType: 'start-no-earlier-than',
    constraintDate: '2026-05-04',
  },
  {
    id: 't7', projectId: PROJECT_ID, parentId: 't5',
    wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    deadlineDate: '2026-05-22',
  },
  {
    id: 't8', projectId: PROJECT_ID, parentId: null,
    wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started',
    startDate: '2026-05-28', endDate: '2026-05-28', durationDays: 0,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
  },
]);

const dependencies = ref<DependencyEntity[]>([
  { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
  { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
  { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
  { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
  { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
  { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
]);

const columns = ref([
  createDefaultTaskTableColumn('wbs'),
  createDefaultTaskTableColumn('name'),
]);
</script>
React tsx
import React, { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';
import type { TaskEntity, DependencyEntity, CalendarEntity } from '@revolist/revogrid-enterprise';
import { currentTheme } from '../composables/useRandomData';

const { isDark } = currentTheme();

const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';

const tasks: TaskEntity[] = [
  {
    id: 't1', projectId: PROJECT_ID, parentId: null,
    wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress',
    startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15,
    progressPercent: 60, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't2', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done',
    startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5,
    progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't3', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done',
    startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0,
    progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
  },
  {
    id: 't4', projectId: PROJECT_ID, parentId: 't1',
    wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress',
    startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10,
    progressPercent: 40, calendarId: CALENDAR_ID, isCritical: true, tags: [],
  },
  {
    id: 't5', projectId: PROJECT_ID, parentId: null,
    wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
  },
  {
    id: 't6', projectId: PROJECT_ID, parentId: 't5',
    wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started',
    startDate: '2026-05-04', endDate: '2026-05-20', durationDays: 13,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    constraintType: 'start-no-earlier-than',
    constraintDate: '2026-05-04',
  },
  {
    id: 't7', projectId: PROJECT_ID, parentId: 't5',
    wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    deadlineDate: '2026-05-22',
  },
  {
    id: 't8', projectId: PROJECT_ID, parentId: null,
    wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started',
    startDate: '2026-05-28', endDate: '2026-05-28', durationDays: 0,
    progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
  },
];

const dependencies: DependencyEntity[] = [
  { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
  { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
  { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
  { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
  { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
  { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
];

const calendars: CalendarEntity[] = [
  {
    id: CALENDAR_ID,
    name: 'US Standard',
    timeZone: 'America/New_York',
    workingDays: [1, 2, 3, 4, 5],
    holidays: ['2026-05-25', '2026-07-04'],
    hoursPerDay: 8,
  },
];

function GanttScheduling() {
  const ganttConfig = useMemo(() => ({
    id: PROJECT_ID,
    name: 'Website Redesign',
    version: '1',
    currency: 'USD',
    timeZone: 'America/New_York',
    primaryCalendarId: CALENDAR_ID,
    updatedAt: '2026-04-06T00:00:00Z',
    zoomPreset: 'week' as const,
    scheduling: {
      excludeHolidaysFromDuration: true,
    },
    visuals: {
      shadeNonWorkingTime: true,
      projectLineDate: '2026-04-06',
    },
  }), []);

  const columns = useMemo(() => [
    createDefaultTaskTableColumn('wbs'),
    createDefaultTaskTableColumn('name'),
  ], []);

  return (
    <RevoGrid
      style={{ height: '500px' }}
      theme={isDark() ? 'darkCompact' : 'compact'}
      hideAttribution
      plugins={[GanttPlugin]}
      source={tasks}
      columns={columns}
      gantt={ganttConfig}
      ganttDependencies={dependencies}
      ganttCalendars={calendars}
    />
  );
}

export default GanttScheduling;
Angular ts
import { Component, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';
import type { TaskEntity, DependencyEntity, CalendarEntity } from '@revolist/revogrid-enterprise';
import { currentTheme } from '../composables/useRandomData';

const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';

@Component({
  selector: 'gantt-scheduling-grid',
  standalone: true,
  imports: [RevoGrid],
  // Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
  schemas: [NO_ERRORS_SCHEMA],
  template: `
    <revo-grid
      style="min-height: 500px"
      [theme]="theme"
      [hideAttribution]="true"
      [plugins]="plugins"
      [source]="tasks"
      [columns]="columns"
      [gantt]="ganttConfig"
      [ganttDependencies]="dependencies"
      [ganttCalendars]="calendars"
    ></revo-grid>
  `,
  encapsulation: ViewEncapsulation.None,
})
export class GanttSchedulingGridComponent {
  theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
  plugins = [GanttPlugin];

  ganttConfig = {
    id: PROJECT_ID,
    name: 'Website Redesign',
    version: '1',
    currency: 'USD',
    timeZone: 'America/New_York',
    primaryCalendarId: CALENDAR_ID,
    updatedAt: '2026-04-06T00:00:00Z',
    zoomPreset: 'week' as const,
    scheduling: {
      excludeHolidaysFromDuration: true,
    },
    visuals: {
      shadeNonWorkingTime: true,
      projectLineDate: '2026-04-06',
    },
  };

  calendars: CalendarEntity[] = [
    {
      id: CALENDAR_ID,
      name: 'US Standard',
      timeZone: 'America/New_York',
      workingDays: [1, 2, 3, 4, 5],
      holidays: ['2026-05-25', '2026-07-04'],
      hoursPerDay: 8,
    },
  ];

  tasks: TaskEntity[] = [
    {
      id: 't1', projectId: PROJECT_ID, parentId: null,
      wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress',
      startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15,
      progressPercent: 60, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    },
    {
      id: 't2', projectId: PROJECT_ID, parentId: 't1',
      wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done',
      startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5,
      progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    },
    {
      id: 't3', projectId: PROJECT_ID, parentId: 't1',
      wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done',
      startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0,
      progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
    },
    {
      id: 't4', projectId: PROJECT_ID, parentId: 't1',
      wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress',
      startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10,
      progressPercent: 40, calendarId: CALENDAR_ID, isCritical: true, tags: [],
    },
    {
      id: 't5', projectId: PROJECT_ID, parentId: null,
      wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started',
      startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24,
      progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [],
    },
    {
      id: 't6', projectId: PROJECT_ID, parentId: 't5',
      wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started',
      startDate: '2026-05-04', endDate: '2026-05-20', durationDays: 13,
      progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
      constraintType: 'start-no-earlier-than',
      constraintDate: '2026-05-04',
    },
    {
      id: 't7', projectId: PROJECT_ID, parentId: 't5',
      wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started',
      startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24,
      progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [],
      deadlineDate: '2026-05-22',
    },
    {
      id: 't8', projectId: PROJECT_ID, parentId: null,
      wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started',
      startDate: '2026-05-28', endDate: '2026-05-28', durationDays: 0,
      progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'],
    },
  ];

  dependencies: DependencyEntity[] = [
    { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
    { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
    { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
    { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
    { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
    { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
  ];

  columns = [
    createDefaultTaskTableColumn('wbs'),
    createDefaultTaskTableColumn('name'),
  ];
}

Set manuallyScheduled on each task, or use taskModeDefault for tasks that omit it:

{
id: 'kickoff',
name: 'Fixed Kickoff Event',
manuallyScheduled: true,
startDate: '2026-04-20',
endDate: '2026-04-20',
durationDays: 0,
// other task fields
}

Automatic tasks are dependency-safe. Dragging, resizing from the start edge, or editing an automatic successor before its predecessor allows will clamp the effective schedule to the dependency-valid date.

Manual tasks keep authored dates. If the manual start violates an active dependency, the scheduler reports manual-dependency-violation and the row/bar renders a scheduling warning indicator.

When a manual task is placed at a date that violates an incoming dependency (e.g., starts before a FS predecessor finishes), the scheduler does not move the task. Instead, it:

  1. Records a manual-dependency-violation conflict.
  2. Displays a warning icon in the “Scheduling Warning” column.
  3. Highlights the task bar with warning styling.

This allows planners to maintain “best guess” or “negotiated” dates while still seeing where the logical project structure is broken.