Skip to content

Instantly share code, notes, and snippets.

@ibuilder
Created March 29, 2025 00:11
Show Gist options
  • Save ibuilder/a66a3d29eae0c74459bdc1dfceb232a6 to your computer and use it in GitHub Desktop.
Save ibuilder/a66a3d29eae0c74459bdc1dfceb232a6 to your computer and use it in GitHub Desktop.
This Gantt chart for a house building project includes: Task Information: Each task has a name, start date, end date, and calculated duration Tasks are displayed chronologically from site preparation to final inspection Key Milestones as requested: Foundation Complete (May 10, 2025) Building Top Out (June 25, 2025) Building Closeout (October 5, …
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>House Building Project Gantt Chart</title>
<!-- Bootstrap CSS -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<style>
.gantt-container {
overflow-x: auto;
margin-top: 20px;
}
.gantt-row {
height: 40px;
border-bottom: 1px solid #e0e0e0;
display: flex;
align-items: center;
}
.gantt-header {
background-color: #f8f9fa;
font-weight: bold;
border-bottom: 2px solid #dee2e6;
}
.gantt-labels {
width: 300px;
background-color: white;
position: sticky;
left: 0;
z-index: 10;
padding-right: 10px;
border-right: 2px solid #dee2e6;
}
.gantt-chart {
position: relative;
display: flex;
flex-grow: 1;
}
.day-column {
min-width: 30px;
text-align: center;
border-right: 1px solid #f0f0f0;
}
.weekend {
background-color: #f8f9fa;
}
.bar {
position: absolute;
height: 24px;
border-radius: 4px;
background-color: #007bff;
z-index: 5;
}
.milestone {
position: absolute;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 20px solid #dc3545;
z-index: 6;
}
.milestone-label {
position: absolute;
font-size: 12px;
font-weight: bold;
color: #dc3545;
white-space: nowrap;
top: -20px;
}
.task-dates {
font-size: 12px;
color: #666;
}
.header-sticky {
position: sticky;
top: 0;
z-index: 20;
background-color: #f8f9fa;
}
</style>
</head>
<body>
<div class="container-fluid mt-3">
<h1 class="text-center mb-4">House Building Project Schedule</h1>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">Gantt Chart Timeline</h4>
</div>
<div class="card-body">
<div class="gantt-container" id="gantt-container">
<!-- Gantt chart will be rendered here by JavaScript -->
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">Project Information</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h5>Project Details</h5>
<p><strong>Project Name:</strong> Custom House Build</p>
<p><strong>Start Date:</strong> <span id="project-start-date"></span></p>
<p><strong>End Date:</strong> <span id="project-end-date"></span></p>
<p><strong>Total Duration:</strong> <span id="project-duration"></span> days</p>
</div>
<div class="col-md-6">
<h5>Legend</h5>
<div class="d-flex align-items-center mb-2">
<div style="width: 50px; height: 20px; background-color: #007bff; border-radius: 4px;"></div>
<span class="ms-2">Task</span>
</div>
<div class="d-flex align-items-center">
<div style="width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; border-bottom: 20px solid #dc3545;"></div>
<span class="ms-2">Milestone</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap and JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Project data
const tasks = [
{
id: 1,
name: "Site Preparation",
start: "2025-04-01",
end: "2025-04-10",
milestone: false
},
{
id: 2,
name: "Excavation",
start: "2025-04-11",
end: "2025-04-18",
milestone: false
},
{
id: 3,
name: "Foundation",
start: "2025-04-19",
end: "2025-05-10",
milestone: false
},
{
id: 4,
name: "Foundation Complete",
start: "2025-05-10",
end: "2025-05-10",
milestone: true
},
{
id: 5,
name: "Framing",
start: "2025-05-11",
end: "2025-06-05",
milestone: false
},
{
id: 6,
name: "Roofing",
start: "2025-06-06",
end: "2025-06-20",
milestone: false
},
{
id: 7,
name: "Window & Door Installation",
start: "2025-06-15",
end: "2025-06-25",
milestone: false
},
{
id: 8,
name: "Building Top Out",
start: "2025-06-25",
end: "2025-06-25",
milestone: true
},
{
id: 9,
name: "Plumbing Rough-In",
start: "2025-06-26",
end: "2025-07-10",
milestone: false
},
{
id: 10,
name: "Electrical Rough-In",
start: "2025-07-01",
end: "2025-07-15",
milestone: false
},
{
id: 11,
name: "HVAC Installation",
start: "2025-07-05",
end: "2025-07-20",
milestone: false
},
{
id: 12,
name: "Insulation",
start: "2025-07-21",
end: "2025-07-28",
milestone: false
},
{
id: 13,
name: "Drywall",
start: "2025-07-29",
end: "2025-08-15",
milestone: false
},
{
id: 14,
name: "Interior Painting",
start: "2025-08-16",
end: "2025-08-30",
milestone: false
},
{
id: 15,
name: "Flooring Installation",
start: "2025-08-25",
end: "2025-09-10",
milestone: false
},
{
id: 16,
name: "Cabinet Installation",
start: "2025-09-11",
end: "2025-09-20",
milestone: false
},
{
id: 17,
name: "Appliance Installation",
start: "2025-09-21",
end: "2025-09-25",
milestone: false
},
{
id: 18,
name: "Fixture Installation",
start: "2025-09-26",
end: "2025-10-05",
milestone: false
},
{
id: 19,
name: "Building Closeout",
start: "2025-10-05",
end: "2025-10-05",
milestone: true
},
{
id: 20,
name: "Final Inspection",
start: "2025-10-06",
end: "2025-10-10",
milestone: false
},
{
id: 21,
name: "Final Punchlist",
start: "2025-10-10",
end: "2025-10-10",
milestone: true
}
];
// Calculate project duration
const startDate = new Date(tasks.reduce((min, task) => {
return new Date(task.start) < new Date(min) ? task.start : min;
}, tasks[0].start));
const endDate = new Date(tasks.reduce((max, task) => {
return new Date(task.end) > new Date(max) ? task.end : max;
}, tasks[0].end));
// Display project info
document.getElementById('project-start-date').textContent = formatDate(startDate);
document.getElementById('project-end-date').textContent = formatDate(endDate);
const projectDuration = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
document.getElementById('project-duration').textContent = projectDuration;
// Generate dates for the chart
const dates = [];
const currentDate = new Date(startDate);
while (currentDate <= endDate) {
dates.push(new Date(currentDate));
currentDate.setDate(currentDate.getDate() + 1);
}
// Build the gantt chart
buildGanttChart(tasks, dates, startDate);
});
function buildGanttChart(tasks, dates, startDate) {
const container = document.getElementById('gantt-container');
// Create header
const headerRow = document.createElement('div');
headerRow.className = 'gantt-row gantt-header header-sticky';
const headerLabels = document.createElement('div');
headerLabels.className = 'gantt-labels';
headerLabels.innerHTML = '<div class="ps-3">Task Name</div>';
headerRow.appendChild(headerLabels);
const headerChart = document.createElement('div');
headerChart.className = 'gantt-chart';
// Add date headers
dates.forEach((date, index) => {
const dayCol = document.createElement('div');
dayCol.className = 'day-column';
// Check if it's a weekend
const day = date.getDay();
if (day === 0 || day === 6) {
dayCol.classList.add('weekend');
}
// Display date format depending on the day
if (date.getDate() === 1 || index === 0) {
// First day of month or first day in chart - show month/day
dayCol.innerHTML = `${date.getMonth() + 1}/${date.getDate()}`;
} else {
// Just the day
dayCol.innerHTML = date.getDate();
}
headerChart.appendChild(dayCol);
});
headerRow.appendChild(headerChart);
container.appendChild(headerRow);
// Create task rows
tasks.forEach(task => {
const row = document.createElement('div');
row.className = 'gantt-row';
const taskLabels = document.createElement('div');
taskLabels.className = 'gantt-labels';
// Task name and dates
const taskName = document.createElement('div');
taskName.className = 'd-flex flex-column ps-3';
taskName.innerHTML = `
<div>${task.name}</div>
<div class="task-dates">
${task.milestone ? 'Milestone' : formatDate(new Date(task.start)) + ' - ' + formatDate(new Date(task.end))}
${task.milestone ? '' : ' (' + calculateDuration(task.start, task.end) + ' days)'}
</div>
`;
taskLabels.appendChild(taskName);
row.appendChild(taskLabels);
const taskChart = document.createElement('div');
taskChart.className = 'gantt-chart';
// Add day columns for reference
dates.forEach(date => {
const dayCol = document.createElement('div');
dayCol.className = 'day-column';
// Check if it's a weekend
const day = date.getDay();
if (day === 0 || day === 6) {
dayCol.classList.add('weekend');
}
taskChart.appendChild(dayCol);
});
// Add the task bar or milestone
if (task.milestone) {
// Milestone diamond
const milestone = document.createElement('div');
milestone.className = 'milestone';
const taskStartDate = new Date(task.start);
const daysDiff = Math.ceil((taskStartDate - startDate) / (1000 * 60 * 60 * 24));
milestone.style.left = `${daysDiff * 30 + 15 - 10}px`; // 30px per day, centered
milestone.style.top = '10px';
// Add milestone label
const milestoneLabel = document.createElement('div');
milestoneLabel.className = 'milestone-label';
milestoneLabel.textContent = task.name;
milestoneLabel.style.left = `${daysDiff * 30 + 15}px`; // 30px per day, centered
taskChart.appendChild(milestone);
taskChart.appendChild(milestoneLabel);
} else {
// Task bar
const bar = document.createElement('div');
bar.className = 'bar';
const taskStartDate = new Date(task.start);
const taskEndDate = new Date(task.end);
const daysDiff = Math.ceil((taskStartDate - startDate) / (1000 * 60 * 60 * 24));
const duration = calculateDuration(task.start, task.end);
bar.style.left = `${daysDiff * 30}px`; // 30px per day
bar.style.width = `${duration * 30}px`;
bar.style.top = '8px';
taskChart.appendChild(bar);
}
row.appendChild(taskChart);
container.appendChild(row);
});
}
function formatDate(date) {
return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
}
function calculateDuration(start, end) {
const startDate = new Date(start);
const endDate = new Date(end);
return Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment