suivi-dhx-trial-gantt
Version:
An open source JavaScript Gantt chart that helps you illustrate a project schedule in a nice-looking chart.
685 lines (586 loc) • 19.1 kB
HTML
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Integration with DHTMLX Suite</title>
<script src="../../codebase/dhtmlxgantt.js?v=9.0.7"></script>
<link rel="stylesheet" href="../../codebase/dhtmlxgantt.css?v=9.0.7">
<script type="text/javascript" src="https://cdn.dhtmlx.com/suite/8.3/suite.js?v=9.0.7"></script>
<script type="text/javascript" src="./common/suite_task_editor.js?v=9.0.7"></script>
<script type="text/javascript" src="./common/suite_link_editor.js?v=9.0.7"></script>
<script type="text/javascript" src="./common/suite_calendar_editor.js?v=9.0.7"></script>
<script type="text/javascript" src="./common/suite_resource_editor.js?v=9.0.7"></script>
<link rel="stylesheet" href="https://cdn.dhtmlx.com/suite/8.3/suite.css?v=9.0.7">
<link href="https://cdn.materialdesignicons.com/3.6.95/css/materialdesignicons.min.css?v=9.0.7" media="all" rel="stylesheet" type="text/css">
<style>
html, body {
height: 100%;
width:100%;
padding: 0px;
margin: 0px;
/*overflow: hidden;*/
}
/*⇊⇊⇊resource section⇊⇊⇊*/
.gantt_grid_scale .gantt_grid_head_cell,
.gantt_task .gantt_task_scale .gantt_scale_cell {
font-weight: bold;
font-size: 14px;
color: rgba(0, 0, 0, 0.7);
}
.resource_marker{
text-align: center;
}
.resource_marker div{
width: 28px;
height: 28px;
border-radius: 15px;
color: #FFF;
margin: 3px;
display: inline-flex;
justify-content: center;
align-items: center;
}
.resource_marker.workday_ok div {
background: var(--dhx-gantt-base-colors-success);
}
.resource_marker.workday_over div{
background: var(--dhx-gantt-base-colors-error);
}
.owner-label {
width: 20px;
height: 20px;
font-size: 12px;
display: inline-flex;
justify-content: center;
align-items: center;
border: 1px solid #cccccc;
border-radius: 25px;
background: #e6e6e6;
color: #6f6f6f;
margin: 0 3px;
font-weight: bold;
}
#calendarSelector, #calendarDatesEditor, #resourceEdit{
display: flex;
}
#calendarSelector select, #resourceEdit select{
width: 100%;
}
#slider_container{
display: flex;
}
#slider{
width: 150px;
}
#zoom_level{
width: 70px;
}
.new_calendar_name{
z-index: 999999999999;
}
.weekend{
background: var(--dhx-gantt-base-colors-background-alt);
}
</style>
</head>
<body>
<div id="slider_container" >
<label id=zoom_level>Week </label>
<div id="slider" ></div>
</div>
<div id="gantt_here" style='width:100%; height:90%'></div>
<script>
// Gantt configuration
var taskData = {
"data": [
{ "id": "10", "text": "Project #1", "start_date": "10-04-2025", "end_date": "14-04-2025", "order": 10, "progress": 0.4, "open": true, "tags": ['1', '3'] },
{ "id": "1", "text": "Task #1", "owner": [{ "resource_id": "6", "value": "5" }, { "resource_id": "7", "value": "2" }], "start_date": "10-04-2025", "end_date": "13-04-2025", "order": 10, "progress": 0.6, "parent": "10", "tags": ['1'] },
{ "id": "2", "text": "Task #2", "owner": [{ "resource_id": "8", "value": "3" }], "calendar_id": "fulltime", "start_date": "11-04-2025", "end_date": "14-04-2025", "order": 20, "progress": 0.6, "parent": "10", "description": "This task has its own calendars, so it will override resource calendar settings. Change the task calendar to global to apply resource calendars.", "tags": ['5'] },
{ "id": "20", "text": "Project #2", "start_date": "10-04-2025", "duration": 24, "order": 10, "progress": 0.4, "type": "project", "open": true, "tags": ['2'] },
{ "id": "3", "text": "Task #3", "owner": [{ "resource_id": "9", "value": "7" }], "calendar_id": "custom", "start_date": "12-04-2025", "end_date": "14-04-2025", "order": 10, "progress": 0.6, "parent": "20", "description": "This task has its own calendars, so it will override resource calendar settings. Change the task calendar to global to apply resource calendars." },
{ "id": "4", "text": "Task #4", "owner": [{ "resource_id": "10", "value": "12" }, { "resource_id": "9", "value": "9" }], "start_date": "14-04-2025", "end_date": "16-04-2025", "order": 20, "progress": 0.6, "parent": "20", "tags": ['3', '4'] }
],
"links": [
{ "id": "1", "source": '1', "target": '2', "type": "0" },
{ "id": "2", "source": '2', "target": '3', "type": "0" },
{ "id": "3", "source": '3', "target": '4', "type": "0" },
{ "id": "4", "source": '2', "target": '5', "type": "0" }
]
}
gantt.plugins({
auto_scheduling: true
});
gantt.config.auto_scheduling = true;
gantt.config.duration_unit = "hour"
var zoomConfig = {
levels: [
{
name: "hour",
scale_height: 50,
min_column_width: 15,
scales: [
{ unit: "day", format: "%d" },
{ unit: "hour", format: "%H" },
]
},
{
name: "day",
scale_height: 50,
min_column_width: 80,
scales: [
{ unit: "day", step: 1, format: "%d %M" }
]
},
{
name: "week",
scale_height: 50,
min_column_width: 50,
scales: [
{
unit: "week", step: 1, format: function (date) {
var dateToStr = gantt.date.date_to_str("%d %M");
var endDate = gantt.date.add(date, 7 - date.getDay(), "day");
var weekNum = gantt.date.date_to_str("%W")(date);
return "#" + weekNum + ", " + dateToStr(date) + " - " + dateToStr(endDate);
}
},
{ unit: "day", step: 1, format: "%j %D" }
]
},
{
name: "month",
scale_height: 50,
min_column_width: 120,
scales: [
{ unit: "month", format: "%F, %Y" },
{ unit: "week", format: "Week #%W" }
]
},
{
name: "quarter",
height: 50,
min_column_width: 90,
scales: [
{
unit: "quarter", step: 1, format: function (date) {
var dateToStr = gantt.date.date_to_str("%M");
var endDate = gantt.date.add(date, 2 - date.getMonth() % 3, "month");
return dateToStr(date) + " - " + dateToStr(endDate);
}
},
{ unit: "month", step: 1, format: "%M" },
]
},
{
name: "year",
scale_height: 50,
min_column_width: 30,
scales: [
{ unit: "year", step: 1, format: "%Y" }
]
}
],
useKey: "ctrlKey",
trigger: "wheel",
element: function () {
return gantt.$root.querySelector(".gantt_task");
}
};
gantt.ext.zoom.init(zoomConfig);
gantt.ext.zoom.setLevel("week");
gantt.config.bar_height = 20;
gantt.config.work_time = true;
// gantt.config.correct_work_time = true;
gantt.templates.timeline_cell_class = function (task, date) {
if (!gantt.isWorkTime({ date: date, task: task, unit: gantt.getScale().unit })) return "weekend";
};
gantt.config.dynamic_resource_calendars = true;
gantt.addCalendar({
id: "custom"
});
gantt.getCalendar("custom").setWorkTime({ date: gantt.date.str_to_date("%d-%m-%Y")("10-04-2025"), hours: ["8:00-12:00"] })
gantt.getCalendar("custom").setWorkTime({ date: gantt.date.str_to_date("%d-%m-%Y")("12-04-2025"), hours: ["9:00-18:00"] })
gantt.getCalendar("custom").setWorkTime({ date: gantt.date.str_to_date("%d-%m-%Y")("14-04-2025"), hours: false })
/*
var autoFormatter = gantt.ext.formatters.durationFormatter({
enter: "day",
store: "hour",
format: "auto"
});
*/
gantt.config.columns = [
{ name: "text", tree: true, width: 200, resize: true },
{ name: "start_date", align: "center", width: 100, resize: true },
{
name: "owner", align: "center", width: 85, label: "Owner", template: function (task) {
if (task.type == gantt.config.types.project) {
return "";
}
var store = gantt.getDatastore("resource");
var assignments = task[gantt.config.resource_property];
if (!assignments || !assignments.length) {
return "Unassigned";
}
if (assignments.length == 1) {
var resourceItem = store.getItem(assignments[0].resource_id)
if (!resourceItem) {
return "Unassigned"
}
return store.getItem(assignments[0].resource_id).text;
}
var result = "";
assignments.forEach(function (assignment) {
var owner = store.getItem(assignment.resource_id);
if (!owner) {
return;
}
result += "<div class='owner-label' title='" + owner.text + "'>" + owner.text.substr(0, 1) + "</div>";
});
return result;
}, resize: true
},
{
name: "duration", label: "Duration", resize: true, align: "center", /*template: function(task) {
return autoFormatter.format(task.duration);
},*/width: 100
},
{ name: "add", width: 44 }
];
function getResourceAssignments(resourceId) {
var assignments;
var store = gantt.getDatastore(gantt.config.resource_store);
var resource = store.getItem(resourceId);
if (resource.$level === 0) {
assignments = [];
store.getChildren(resourceId).forEach(function (childId) {
assignments = assignments.concat(gantt.getResourceAssignments(childId));
});
}
else if (resource.$level === 1) {
assignments = gantt.getResourceAssignments(resourceId);
}
else {
assignments = gantt.getResourceAssignments(resource.$resource_id, resource.$task_id);
}
return assignments;
}
var resourceConfig = {
columns: [
{
name: "name", label: "Name", tree: true, template: function (resource) {
return resource.text;
}
},
{
name: "workload", label: "Workload", template: function (resource) {
var totalDuration = 0;
if (resource.$role === "task") {
gantt.getResourceAssignments(resource.$resource_id, resource.$task_id).forEach(function (a) {
totalDuration += a.value * a.duration;
});
} else {
getResourceAssignments(resource.id).forEach(function (assignment) {
totalDuration += Number(assignment.value) * assignment.duration;
});
}
return (totalDuration || 0) + "h";
/* var totalDuration = 0;
if (resource.$level == 2) {
var assignment = gantt.getResourceAssignments(resource.$resource_id, resource.$task_id)[0];
totalDuration = resource.duration * assignment.value;
} else {
var assignments = getResourceAssignments(resource.id);
assignments.forEach(function (assignment) {
var task = gantt.getTask(assignment.task_id);
totalDuration += Number(assignment.value) * task.duration;
});
}
return (totalDuration || 0) + "h";*/
}
}
]
};
gantt.templates.resource_cell_class = function (start_date, end_date, resource, tasks) {
var css = [];
css.push("resource_marker");
if (tasks.length <= 1) {
css.push("workday_ok");
} else {
css.push("workday_over");
}
return css.join(" ");
};
gantt.templates.resource_cell_value = function (start_date, end_date, resource, tasks) {
var result = 0;
tasks.forEach(function (item) {
var assignments = gantt.getResourceAssignments(resource.id, item.id);
assignments.forEach(function (assignment) {
var task = gantt.getTask(assignment.task_id);
if (+assignment.start_date <= +start_date && +end_date <= +assignment.end_date) {
result += assignment.value * 1;
}
});
});
if (result % 1) {
result = Math.round(result * 10) / 10;
}
return "<div>" + result + "</div>";
};
gantt.config.resource_store = "resource";
gantt.config.resource_property = "owner";
gantt.config.order_branch = true;
gantt.config.open_tree_initially = true;
gantt.config.layout = {
css: "gantt_container",
rows: [
{
cols: [
{ view: "grid", group: "grids", scrollY: "scrollVer" },
{ resizer: true, width: 1 },
{ view: "timeline", scrollX: "scrollHor", scrollY: "scrollVer" },
{ view: "scrollbar", id: "scrollVer", group: "vertical" }
],
gravity: 2
},
{ resizer: true, width: 1 },
{
config: resourceConfig,
cols: [
{ view: "resourceGrid", group: "grids", width: 435, scrollY: "resourceVScroll" },
{ resizer: true, width: 1 },
{ view: "resourceTimeline", scrollX: "scrollHor", scrollY: "resourceVScroll" },
{ view: "scrollbar", id: "resourceVScroll", group: "vertical" }
],
gravity: 1
},
{ view: "scrollbar", id: "scrollHor" }
]
};
gantt.$resourceStore = gantt.createDatastore({
name: gantt.config.resource_store,
type: "treeDatastore",
initItem: function (item) {
item.parent = item.parent || gantt.config.root_id;
item[gantt.config.resource_property] = item.id;
item.open = true;
return item;
}
});
gantt.$resourceStore.attachEvent("onFilterItem", function (id, item) {
if (item.hide) {
return false;
}
return true;
});
gantt.$resourceStore.attachEvent("onParse", function () {
var people = [];
gantt.$resourceStore.eachItem(function (res) {
if (!gantt.$resourceStore.hasChild(res.id)) {
var copy = gantt.copy(res);
copy.key = res.id;
copy.label = res.text;
people.push(copy);
}
});
gantt.updateCollection("people", people);
});
gantt.$resourceStore.parse([
{ id: '1', text: "QA", parent: null },
{ id: '2', text: "Development", parent: null },
{ id: '3', text: "Sales", parent: null },
{ id: '4', text: "Other", parent: null },
{ id: '5', text: "Unassigned", parent: '4' },
{ id: '6', text: "John", parent: '1', unit: "hours/day" },
{ id: '7', text: "Mike", parent: '2', unit: "hours/day" },
{ id: '8', text: "Anna", parent: '2', unit: "hours/day" },
{ id: '9', text: "Bill", parent: '3', unit: "hours/day" },
{ id: '10', text: "Floe", parent: '3', unit: "hours/day" }
]);
gantt.attachEvent("onTaskCreated", function (task) {
// Suite expects String type for the IDs
task.id += "";
// because the work time is enabled
// and the duration is calculated in hours
task.duration = 8;
return true;
});
gantt.attachEvent("onBeforeLinkAdd", function (id, link) {
// Suite expects String type for the IDs
link.id += "";
return true;
});
gantt.init("gantt_here");
gantt.parse(taskData);
// DHTMLX Components configuration
var slider = new dhx.Slider("slider", {
min: 0,
max: 4,
value: 2,
tooltip: false,
step: 1
});
slider.events.on("Change", function (newValue, oldValue, isRange) {
gantt.ext.zoom.setLevel(newValue);
document.querySelector("#zoom_level").innerHTML = zoomConfig.levels[newValue].name.replace(/(^\w|\s\w)/g, function (m) { return m.toUpperCase() })
});
//Custom lightbox configuration
var dhxWindow = new dhx.Window({
title: "DHX Window",
modal: false,
resizable: true,
movable: true,
closable: true,
header: true,
footer: true,
viewportOverflow: true,
height: 500,
width: 700,
minWidth: 400,
minHeight: 300
});
dhxWindow.footer.data.parse([
{ type: "button", view: "flat", size: "medium", color: "primary", value: "Save", id: "save", },
{ type: "button", view: "link", size: "medium", color: "primary", value: "Cancel", id: "cancel", },
{ type: "button", view: "link", size: "medium", color: "danger", value: "Delete", id: "delete", }
])
dhxWindow.footer.events.on("click", function (id) {
if (id === "save") {
saveTask()
dhxWindow.hide()
}
if (id === "cancel") {
dhxWindow.hide()
}
if (id === "delete") {
deleteTask()
dhxWindow.hide()
}
});
var formatDate = gantt.date.str_to_date("%Y-%m-%d");
gantt.showLightbox = function (id) {
dhxWindow.show();
setTimeout(function () {
gantt._lightbox_id = id;
var task = gantt.getTask(id);
gantt._lightbox_task = gantt.copy(task);
gantt._lightbox_links = "load";
gantt._removed_links = [];
var title = document.querySelector(".dhx_navbar-title")
title.innerHTML = task.text || "New task"
addTabBar()
}, 100)
}
dhxWindow.handLightboxClick = function (e) {
var tab = gantt._tabbar.getActive();
var functionName = e.target.dataset.onclick;
var functionArgument = e.target.dataset.onclick_argument;
if (functionName) {
gantt.$lightboxControl[tab][functionName](functionArgument);
}
}
dhxWindow.events.on("AfterShow", function (position) {
gantt.event(dhxWindow._popup, "click", dhxWindow.handLightboxClick);
});
dhxWindow.events.on("BeforeHide", function (position, events) {
gantt.hideLightbox();
gantt.eventRemove(dhxWindow._popup, "click", dhxWindow.handLightboxClick);
});
gantt.hideLightbox = function () {
if (gantt._lightbox_task.$new) {
deleteTask()
}
gantt._lightbox_task = null;
gantt._lightbox_id = null;
}
function saveTask() {
var id = gantt.getState().lightbox;
var task = gantt.getTask(gantt._lightbox_id);
// update task properties
gantt.mixin(task, gantt._lightbox_task, true)
//update resources
var assignmentStore = gantt.getDatastore(gantt.config.resource_assignment_store);
var assignments = gantt._lightbox_task[gantt.config.resource_property] || [];
for (var i = 0; i < assignments.length; i++) {
var updatedAssignment = assignments[i]
var existingAssignmentId = updatedAssignment.$id;
var existingAssignment = assignmentStore.getItem(existingAssignmentId)
if (existingAssignment) {
for (var property in updatedAssignment) {
existingAssignment[property] = updatedAssignment[property]
}
}
else {
//assignmentStore.addItem(resource)
}
}
// update the task
if (task.$new) {
delete gantt._lightbox_task.$new;
delete task.$new;
gantt.addTask(task)
}
else {
gantt.updateTask(id)
}
for (var i = 0; i < gantt._removed_links.length; i++) {
var linkId = gantt._removed_links[i];
if (gantt.isLinkExists(linkId)) gantt.deleteLink(linkId)
}
var links = gantt._lightbox_links;
if (links != "load") {
for (var i = 0; i < links.length; i++) {
var link = links[i];
if (gantt.isLinkExists(link.id)) {
var oldLink = gantt.getLink(link.id);
oldLink.source = link.source;
oldLink.target = link.target;
oldLink.type = link.type;
oldLink.lag = link.lag;
gantt.updateLink(link.id);
}
else if (gantt.isTaskExists(link.source) && gantt.isTaskExists(link.target)) {
gantt.addLink(link);
}
}
}
}
function deleteTask() {
var id = gantt.getState().lightbox;
gantt.deleteTask(id)
}
function addTabBar() {
if (gantt._tabbar) gantt._tabbar.destructor();
gantt._tabbar = new dhx.Tabbar(null, {
views: [
{ id: "task", tab: "Task Data", css: "panel flex" },
{ id: "links", tab: "Links", css: "panel flex" },
{ id: "calendars", tab: "Calendars", css: "panel flex" },
{ id: "resources", tab: "Resources", css: "panel flex" }
]
});
dhxWindow.attach(gantt._tabbar)
dhx.awaitRedraw().then(function () {
gantt.$lightboxControl.fillTabContent()
})
gantt._tabbar.events.on("Change", function (activeId, prevId) {
gantt.$lightboxControl.fillTabContent(activeId)
});
}
gantt.$lightboxControl = {
task: {},
links: {},
calendars: {},
resources: {}
};
gantt.$lightboxControl.fillTabContent = function (id) {
id = id || "task"
gantt.$lightboxControl[id].addForm();
}
initTaskEditForm();
initResourceEditForm();
initLinkEditForm();
initCalendarEditForm();
setTimeout(function () {
gantt.showLightbox(4)
}, 200)
</script>
</body>