UNPKG

jetsum_dhtmlx_gantt

Version:

An open source JavaScript Gantt chart that helps you illustrate a project schedule in a nice-looking chart.

493 lines (425 loc) 12.7 kB
<!DOCTYPE html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>Group by multiple resources</title> <script src="../../codebase/dhtmlxgantt.js?v=7.1.9"></script> <script src="./common/jquery_multiselect.js?v=7.1.9"></script> <link rel="stylesheet" href="./common/jquery_multiselect.css?v=7.1.9"> <link rel="stylesheet" href="../common/controls_styles.css?v=7.1.9"> <script src="https://code.jquery.com/jquery-3.3.1.min.js?v=7.1.9" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.jquery.js?v=7.1.9"></script> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.css?v=7.1.9"> <link rel="stylesheet" href="../../codebase/dhtmlxgantt.css?v=7.1.9"> <script src="../common/resource_project_construction.js?v=7.1.9"></script> <style> html, body { padding: 0px; margin: 0px; height: 100%; } #gantt_here { width:100%; height: 800px; height:calc(100vh - 52px); } .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; line-height: 29px; display: inline-block; color: #FFF; margin: 3px; } .resource_marker.workday_ok div { border-radius: 15px; background: #51c185; } .resource_marker.workday_over div{ border-radius: 3px; background: #ff8686; } .folder_row { font-weight: bold; } .highlighted_resource, .highlighted_resource.odd { background-color: rgba(255, 251, 224, 0.6); } .resource-controls .gantt_layout_content{ padding: 7px; overflow: hidden; } .resource-controls label{ margin: 0 10px; vertical-align: bottom; display: inline-block; color: #3e3e3e; padding: 2px; transition: box-shadow 0.2s; } .resource-controls label:hover{ box-shadow: 0 2px rgba(84, 147, 255, 0.42); } .resource-controls label.active, .resource-controls label.active:hover { box-shadow: 0 2px #5493ffae; color: #1f1f1f; } .resource-controls input{ vertical-align: top; } .gantt_task_cell.week_end { background-color: #e8e8e87d; } .gantt_task_row.gantt_selected .gantt_task_cell.week_end { background-color: #e8e8e87d !important; } .group_row, .group_row.odd, .gantt_task_row.group_row{ background-color: rgba(232, 232, 232, 0.6); } .owner-label{ width: 20px; height: 20px; line-height: 20px; font-size: 12px; display: inline-block; border: 1px solid #cccccc; border-radius: 25px; background: #e6e6e6; color: #6f6f6f; margin: 0 3px; font-weight: bold; } </style> </head> <body> <div class="gantt_control" > <input type='button' id='default' onclick="toggleGroups(this)" value="Show Resource view"> </div> <div id="gantt_here"></div> <script> gantt.plugins({ grouping: true, auto_scheduling: true }); gantt.config.date_format = "%Y-%m-%d %H:%i:%s"; function shouldHighlightTask(task){ var store = gantt.$resourcesStore; var taskResource = task[gantt.config.resource_property], selectedResource = store.getSelectedId(); if(taskResource == selectedResource || store.isChildOf(taskResource, selectedResource)){ return true; } } gantt.templates.grid_row_class = function(start, end, task){ var css = []; if(gantt.hasChild(task.id)){ css.push("folder_row"); } if(task.$virtual){ css.push("group_row") } if(shouldHighlightTask(task)){ css.push("highlighted_resource"); } return css.join(" "); }; gantt.templates.task_row_class = function(start, end, task){ if(shouldHighlightTask(task)){ return "highlighted_resource"; } return ""; }; gantt.templates.timeline_cell_class = function (task, date) { if (!gantt.isWorkTime({date: date, task: task})) return "week_end"; return ""; }; 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 html = "<div>" if(resourceMode == "hours"){ html += tasks.length * 8; }else{ html += tasks.length; } html += "</div>"; return html; }; function shouldHighlightResource(resource){ var selectedTaskId = gantt.getState().selected_task; if(gantt.isTaskExists(selectedTaskId)){ var selectedTask = gantt.getTask(selectedTaskId), selectedResource = selectedTask[gantt.config.resource_property]; if(resource.id == selectedResource){ return true; }else if(gantt.$resourcesStore.isChildOf(selectedResource, resource.id)){ return true; } } return false; } var resourceTemplates = { grid_row_class: function(start, end, resource){ var css = []; if(gantt.$resourcesStore.hasChild(resource.id)){ css.push("folder_row"); css.push("group_row"); } if(shouldHighlightResource(resource)){ css.push("highlighted_resource"); } return css.join(" "); }, task_row_class: function(start, end, resource){ var css = []; if(shouldHighlightResource(resource)){ css.push("highlighted_resource"); } if(gantt.$resourcesStore.hasChild(resource.id)){ css.push("group_row"); } return css.join(" "); } }; gantt.locale.labels.section_owner = "Owner"; gantt.config.lightbox.sections = [ {name: "description", height: 38, map_to: "text", type: "textarea", focus: true}, {name: "owner",height: 60, type:"multiselect", options: gantt.serverList("people"), map_to:"owner_id", unassigned_value:5 }, {name: "time", type: "duration", map_to: "auto"} ]; function getResourceTasks(resourceId) { var store = gantt.getDatastore(gantt.config.resource_store), field = gantt.config.resource_property, tasks; if (store.hasChild(resourceId)) { tasks = gantt.getTaskBy(field, store.getChildren(resourceId)); }else{ tasks = gantt.getTaskBy(field, resourceId); } return tasks; } var resourceConfig = { scale_height: 30, scales: [ {unit: "day", step: 1, date: "%d %M"} ], columns: [ { name: "name", label: "Name", tree:true, width:250, template: function (resource) { return resource.text; }, resize: true }, { name: "specialty", label: "Speciality", width:150, align:"center",template: function (resource) { return resource.specialty || ""; }, resize: true }, { name: "workload", label: "Workload", align:"center", template: function (resource) { var tasks = getResourceTasks(resource.id); var totalDuration = 0; tasks.forEach(function(task){ totalDuration += task.duration; }); return (totalDuration || 0) * 8 + "h"; }, resize: true }, { name: "capacity", label: "Capacity", align:"center",template: function (resource) { var store = gantt.getDatastore(gantt.config.resource_store); var n = store.hasChild(resource.id) ? store.getChildren(resource.id).length : 1 var state = gantt.getState(); return gantt.calculateDuration(state.min_date, state.max_date) * n * 8 + "h"; } } ] }; gantt.config.scales = [ {unit: "month", step: 1, format: "%F, %Y"}, {unit: "day", step: 1, format: "%d %M"} ]; gantt.config.auto_scheduling = true; gantt.config.auto_scheduling_strict = true; gantt.config.work_time = true; gantt.config.columns = [ {name: "text", tree: true, width: 320, resize: true}, {name: "start_date", align: "center", width: 80, resize: true}, {name: "owner", align: "center", width: 80, label: "Owner", resize: true, template: function (task) { if (task.type == gantt.config.types.project) { return ""; } var result = ""; var store = gantt.getDatastore("resource"); var owners = task[gantt.config.resource_property]; if (!owners || !owners.length) { return "Unassigned"; } if(owners.length == 1){ return store.getItem(owners[0]).text; } owners.forEach(function(ownerId) { var owner = store.getItem(ownerId); if (!owner) return; result += "<div class='owner-label' title='" + owner.text + "'>" + owner.text.substr(0, 1) + "</div>"; }); return result; } }, {name: "duration", width: 60, align: "center", resize: true}, {name: "add", width: 44} ]; gantt.config.resource_store = "resource"; gantt.config.resource_property = "owner_id"; gantt.config.order_branch = true; gantt.config.open_tree_initially = true; gantt.config.scale_height = 50; gantt.config.layout = { css: "gantt_container", rows: [ { gravity: 2, cols: [ {view: "grid", group:"grids", scrollY: "scrollVer"}, {resizer: true, width: 1}, {view: "timeline", scrollX: "scrollHor", scrollY: "scrollVer"}, {view: "scrollbar", id: "scrollVer", group:"vertical"} ] }, { resizer: true, width: 1, next: "resources"}, { height: 35, cols: [ { html:"", group:"grids"}, { resizer: true, width: 1}, { html:"<label class='active' >Hours per day <input checked type='radio' name='resource-mode' value='hours'></label>" + "<label>Tasks per day <input type='radio' name='resource-mode' value='tasks'></label>", css:"resource-controls"} ] }, { gravity:1, id: "resources", config: resourceConfig, templates: resourceTemplates, cols: [ { view: "resourceGrid", group:"grids", scrollY: "resourceVScroll" }, { resizer: true, width: 1}, { view: "resourceTimeline", scrollX: "scrollHor", scrollY: "resourceVScroll"}, { view: "scrollbar", id: "resourceVScroll", group:"vertical"} ] }, {view: "scrollbar", id: "scrollHor"} ] }; var resourceMode = "hours"; gantt.attachEvent("onGanttReady", function() { var radios = [].slice.call(gantt.$container.querySelectorAll("[name='resource-mode']")); radios.forEach(function(radio) { gantt.event(radio, "change", function(e) { radios.forEach(function(item) { item.parentNode.classList.toggle("active", e.target === item || e.target.value === item.value); }); if (this.checked) { resourceMode = this.value; gantt.getDatastore(gantt.config.resource_store).refresh(); } }); }); }); gantt.$resourcesStore = 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.parent; item.open = true; return item; } }); gantt.$resourcesStore.attachEvent("onAfterSelect", function(id){ gantt.refreshData(); }); gantt.init("gantt_here"); function toggleGroups(input) { gantt.$groupMode = !gantt.$groupMode; if (gantt.$groupMode) { input.value = "show gantt view"; var groups = gantt.$resourcesStore.getItems().map(function(item){ var group = gantt.copy(item); group.group_id = group.id; group.id = gantt.uid(); return group; }); gantt.groupBy({ groups: groups, relation_property: gantt.config.resource_property, group_id: "group_id", group_text: "text", delimiter: ", ", default_group_label: "Not Assigned" }); } else { input.value = "show resource view"; gantt.groupBy(false); } } gantt.$resourcesStore.attachEvent("onParse", function(){ var people = []; gantt.$resourcesStore.eachItem(function(res){ if(!gantt.$resourcesStore.hasChild(res.id)){ var copy = gantt.copy(res); copy.key = res.id; copy.label = res.text; if(copy.specialty){ copy.label += ", " + copy.specialty; } people.push(copy); } }); gantt.updateCollection("people", people); }); gantt.$resourcesStore.parse([ {id:1, text:"Architect"}, {id:2, text:"Construction worker"}, {id:3, text:"Plasterer"}, {id:4, text:"Plumber"}, {id:5, text:"Electrician"}, {id:6, text:"Handyman"}, {id: 7, text: "Anna", specialty: "Architect", parent:1}, {id: 8, text: "Finn", specialty: "Construction worker", parent:2}, {id: 9, text: "Jake", specialty: "Construction worker", parent:2}, {id: 10, text: "Floe", specialty: "Plasterer", parent:3}, {id: 11, text: "Tom", specialty: "Plumber", parent:4}, {id: 12, text: "Mike", specialty: "Electrician", parent:5}, {id: 13, text: "Joe", specialty: "Handyman", parent:6} ]); gantt.parse(taskData); </script> </body>