UNPKG

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
<!DOCTYPE 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 &nbsp;</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>