Lattice Grid Buy a licence

demo D276

A project plan, balanced across people in React

Put people on tasks and see who is booked past what they can do: the clashing bars are outlined and the avatar ringed, then one press shifts the lower-priority work later until the plan is clear

gantt.overAllocations · gantt.level()

This puts people on tasks and shows who is booked past what they can do, outlining the clashing bars and ringing the avatar, then levels the plan in one press by shifting lower-priority work later. It turns overallocation from something you hunt for into something the plan shows and can resolve.

Building…
Loading a live grid…

This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.

The configuration

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">

<div id="app"></div>

<script type="module">
  import React from 'https://esm.sh/react@18';
  import { createRoot } from 'https://esm.sh/react-dom@18/client';
  import { createGantt } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/gantt.esm.min.js';

  // mountSplit is a second render path the plain <LatticeGantt> component does
  // not cover (see gantt-split-view), so this stays an imperative build
  // against a ref'd container, the same as a non-React library would use.
  function App() {
    const planRef = React.useRef(null);
    const ganttRef = React.useRef(null);
    const [status, setStatus] = React.useState('');

    const render = React.useCallback(() => {
      const gantt = ganttRef.current;
      gantt.mountSplit(planRef.current, {
        gridWidth: 300, rowHeight: 34, height: 440, zoom: 'day',
        nonWorking: 'weekends', showArrows: true, showProgress: true,
      });
      // overAllocations reports where a resource is booked beyond capacity
      // across concurrent tasks; the split view rings the over-booked avatars.
      const over = gantt.overAllocations;
      setStatus(over.length ? 'Over-booked: ' + [...new Set(over.map((o) => o.resource))].join(', ') : 'Everyone is within capacity.');
    }, []);

    React.useEffect(() => {
      // Each task carries an assignee; each resource a capacity. Two tasks
      // fall to one person at the same time, so that resource is booked past
      // capacity.
      const tasks = [/* the task list, each with an assignee */];
      const dependencies = [/* the dependency links */];
      const gantt = createGantt({
        tasks, dependencies,
        resources: [
          { id: 'Ana Ruiz', capacity: 1 },
          { id: 'Bo Ito', capacity: 1 },
          { id: 'Cy Okafor', capacity: 1 },
        ],
        projectStart: '2027-01-04', calendar: 'weekends', autoSchedule: true,
      });
      ganttRef.current = gantt;
      render();
      return () => gantt.destroy();
    }, [render]);

    return (
      <>
        <div style={{ marginBottom: '10px' }}>
          {/* level() shifts the lower-priority tasks later to clear the
              over-allocation, honouring the dependency order and the
              working-day calendar. */}
          <button onClick={() => { ganttRef.current.level(); render(); }}>Level the plan</button> <span>{status}</span>
        </div>
        <div ref={planRef} style={{ height: '440px', overflow: 'auto' }} />
      </>
    );
  }

  createRoot(document.getElementById('app')).render(<App />);
</script>