Gantt is a modern, interactive Gantt chart component with drag-and-drop task rescheduling, progress editing, dependency arrows and multiple view modes (Quarter Day, Half Day, Day, Week, Month, Year). Based on frappe-gantt by Frappe Technologies.
A project plan with 8 tasks, dependencies and progress. Drag a bar to reschedule it, drag its ends to resize it, drag the progress handle to change completion, click a task or a date cell, or switch the view mode. Every interaction fires an ajax event shown below as "Last action".
<style>
.gantt-demo { border: 1px solid var(--p-content-border-color); border-radius: 10px; overflow: hidden; }
.gantt-demo-header { display: flex; align-items: center; justify-content: space-between; gap: .75rem;
padding: .8rem 1rem; background: light-dark(var(--p-surface-50), var(--p-surface-950)); border-bottom: 1px solid var(--p-content-border-color); }
.gantt-demo-title { font-weight: 600; font-size: 1.05rem; margin: 0; }
.gantt-demo-subtitle { font-size: .85rem; opacity: .7; margin: .15rem 0 0; }
.gantt-demo-body { padding: 0; }
.gantt-demo-status { display: flex; flex-wrap: wrap; gap: 1.5rem; padding: .7rem 1rem;
border-top: 1px solid var(--p-content-border-color); background: light-dark(var(--p-surface-50), var(--p-surface-950)); font-size: .9rem; }
.gantt-demo-status strong { margin-right: .35rem; }
</style>
<div class="card">
<h5>Basic Usage</h5>
<p>A project plan with 8 tasks, dependencies and progress. Drag a bar to reschedule it, drag its ends to resize it, drag the progress handle to change completion, click a task or a date cell, or switch the view mode. Every interaction fires an ajax event shown below as "Last action".</p>
<h:form id="formBasicUsage">
<p:messages id="messages" showSummary="true" showDetail="true"/>
<div class="gantt-demo">
<div class="gantt-demo-header">
<div>
<p class="gantt-demo-title">Website relaunch plan</p>
<p class="gantt-demo-subtitle">Drag bars to reschedule, stretch ends to resize, move the handle to update progress.</p>
</div>
<p:chip label="#{ganttController.viewMode} view" icon="pi pi-calendar" />
</div>
<div class="gantt-demo-body">
<pe:gantt id="gantt"
widgetVar="ganttViewWidget"
tasks="#{ganttController.tasks}"
viewMode="#{ganttController.viewMode}"
todayButton="true"
viewModeSelect="true"
showProgress="true"
columnWidth="45"
barHeight="30"
dateFormat="YYYY-MM-DD"
lang="en"
style="height:400px">
<p:ajax event="click" listener="#{ganttController.onTaskClick}" update="lastAction selectedTask messages" />
<p:ajax event="dateChange" listener="#{ganttController.onDateChange}" update="lastAction selectedTask messages" />
<p:ajax event="progressChange" listener="#{ganttController.onProgressChange}" update="lastAction selectedTask messages" />
<p:ajax event="viewChange" listener="#{ganttController.onViewChange}" update="lastAction selectedTask messages" />
<p:ajax event="dateClick" listener="#{ganttController.onDateClick}" update="lastAction selectedTask messages" />
</pe:gantt>
</div>
<div class="gantt-demo-status">
<span><strong>Selected:</strong><h:outputText id="selectedTask" value="#{empty ganttController.selectedTaskId ? 'none' : ganttController.selectedTaskId}" /></span>
<span><strong>Last action:</strong><h:outputText id="lastAction" value="#{empty ganttController.lastAction ? 'interact with the chart' : ganttController.lastAction}" /></span>
</div>
</div>
</h:form>
</div>
package org.primefaces.extensions.showcase.controller.gantt;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import org.primefaces.extensions.component.gantt.GanttTask;
import org.primefaces.extensions.event.GanttDateChangeEvent;
import org.primefaces.extensions.event.GanttDateClickEvent;
import org.primefaces.extensions.event.GanttProgressChangeEvent;
import org.primefaces.extensions.event.GanttSelectEvent;
import org.primefaces.extensions.event.GanttViewChangeEvent;
/**
* GanttController
*/
@Named
@ViewScoped
public class GanttController implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private List<GanttTask> tasks;
private String viewMode = "Week";
private String selectedTaskId;
private String lastAction;
public GanttController() {
super();
init();
}
public void init() {
final LocalDate today = LocalDate.now();
tasks = new ArrayList<>();
tasks.add(new GanttTask("1", "Project Kickoff", date(today, -21), date(today, -19), 100, "", "#ff5e57"));
tasks.add(new GanttTask("2", "Requirements Gathering", date(today, -18), date(today, -12), 80, "1", "#ff9f43"));
tasks.add(new GanttTask("3", "Design Phase", date(today, -11), date(today, -5), 60, "2", "#54a0ff"));
tasks.add(new GanttTask("4", "Development Sprint 1", date(today, -4), date(today, 6), 40, "3", "#5f27cd"));
tasks.add(new GanttTask("5", "Development Sprint 2", date(today, 7), date(today, 17), 20, "4", "#5f27cd"));
tasks.add(new GanttTask("6", "Testing & QA", date(today, 18), date(today, 27), 0, "5", "#00d2d3"));
tasks.add(new GanttTask("7", "Deployment", date(today, 28), date(today, 31), 0, "6", "#10ac84"));
tasks.add(new GanttTask("8", "Post-Launch Review", date(today, 32), date(today, 35), 0, "7", "#ee5253"));
}
private String date(final LocalDate base, final int offset) {
return base.plusDays(offset).format(DateTimeFormatter.ISO_LOCAL_DATE);
}
public void onTaskClick(final GanttSelectEvent event) {
selectedTaskId = event.getTaskId();
lastAction = "Task " + selectedTaskId + " selected";
addMessage("Task selected", lastAction);
}
public void onDateChange(final GanttDateChangeEvent event) {
final GanttTask task = findTask(event.getTaskId());
if (task != null && event.getStart() != null && event.getEnd() != null) {
task.setStart(formatDate(event.getStart()));
task.setEnd(formatDate(event.getEnd()));
}
lastAction = "Task " + event.getTaskId() + " moved to " + formatDate(event.getStart()) + " - " + formatDate(event.getEnd());
addMessage("Date changed", lastAction);
}
public void onProgressChange(final GanttProgressChangeEvent event) {
final GanttTask task = findTask(event.getTaskId());
if (task != null) {
task.setProgress(event.getProgress());
}
lastAction = "Task " + event.getTaskId() + " progress set to " + event.getProgress() + "%";
addMessage("Progress changed", lastAction);
}
public void onViewChange(final GanttViewChangeEvent event) {
viewMode = event.getViewMode();
lastAction = "View changed to " + viewMode;
}
public void onDateClick(final GanttDateClickEvent event) {
lastAction = "Date " + event.getDate() + " selected";
addMessage("Date selected", lastAction);
}
private GanttTask findTask(final String taskId) {
if (taskId == null) {
return null;
}
for (final GanttTask task : tasks) {
if (taskId.equals(task.getId())) {
return task;
}
}
return null;
}
private String formatDate(final java.util.Date date) {
if (date == null) {
return "";
}
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE);
}
private void addMessage(final String summary, final String detail) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, summary, detail));
}
public List<GanttTask> getTasks() {
return tasks;
}
public void setTasks(List<GanttTask> tasks) {
this.tasks = tasks;
}
public String getViewMode() {
return viewMode;
}
public void setViewMode(String viewMode) {
this.viewMode = viewMode;
}
public String getSelectedTaskId() {
return selectedTaskId;
}
public void setSelectedTaskId(String selectedTaskId) {
this.selectedTaskId = selectedTaskId;
}
public String getLastAction() {
return lastAction;
}
public void setLastAction(String lastAction) {
this.lastAction = lastAction;
}
}