diff --git a/demo.html b/demo.html index 9fdc571..bfda647 100644 --- a/demo.html +++ b/demo.html @@ -138,15 +138,16 @@ textarea { window.project1.Clear(); var tasks = [ - {"Order":1,"ID":1,"Name":"Task A","Description":"","Tag":null,"StartDate":"2024-08-11T23:00:00.000Z","FinishDate":"2024-08-14T23:00:00.000Z","StartDelay":0,"Duration":3,"PredecessorTaskID":null,"IsCollated":false,"CollatedTaskID":null,"CalcWorkHours":22.5,"ActuWorkHours":null,"Progress":0,"Resources":[],"Level":0,"PredecessorTaskNo":null}, - {"Order":2,"ID":2,"Name":"Task B","Description":"","Tag":null,"StartDate":"2024-08-14T23:00:00.000Z","FinishDate":"2024-08-28T23:00:00.000Z","StartDelay":0,"Duration":14,"PredecessorTaskID":1,"IsCollated":false,"CollatedTaskID":null,"CalcWorkHours":105,"ActuWorkHours":null,"Progress":0,"Resources":[],"Level":0,"PredecessorTaskNo":1}, - {"Order":3,"ID":5,"Name":"Task B1","Description":"","Tag":null,"StartDate":"2024-08-29T23:00:00.000Z","FinishDate":"2024-09-26T23:00:00.000Z","StartDelay":1,"Duration":28,"PredecessorTaskID":2,"IsCollated":true,"CollatedTaskID":null,"CalcWorkHours":210,"ActuWorkHours":null,"Progress":25,"Resources":[],"Level":0,"PredecessorTaskNo":2}, - {"Order":4,"ID":6,"Name":"Task B11","Description":"","Tag":null,"StartDate":"2024-09-02T23:00:00.000Z","FinishDate":"2024-09-26T23:00:00.000Z","StartDelay":4,"Duration":24,"PredecessorTaskID":null,"IsCollated":false,"CollatedTaskID":5,"CalcWorkHours":180,"ActuWorkHours":null,"Progress":0,"Resources":[],"Level":1,"PredecessorTaskNo":null}, - {"Order":5,"ID":7,"Name":"Task B12","Description":"","Tag":null,"StartDate":"2024-08-30T23:00:00.000Z","FinishDate":"2024-09-25T23:00:00.000Z","StartDelay":1,"Duration":26,"PredecessorTaskID":null,"IsCollated":false,"CollatedTaskID":5,"CalcWorkHours":0,"ActuWorkHours":null,"Progress":50,"Resources":[],"Level":1,"PredecessorTaskNo":null}, - {"Order":6,"ID":8,"Name":"Task E","Description":"","Tag":null,"StartDate":"2024-08-11T23:00:00.000Z","FinishDate":"2024-08-26T23:00:00.000Z","StartDelay":0,"Duration":15,"PredecessorTaskID":null,"IsCollated":true,"CollatedTaskID":null,"CalcWorkHours":112.5,"ActuWorkHours":null,"Progress":0,"Resources":[],"Level":0,"PredecessorTaskNo":null}, - {"Order":7,"ID":3,"Name":"Task C","Description":"","Tag":null,"StartDate":"2024-08-26T23:00:00.000Z","FinishDate":"2024-09-13T23:00:00.000Z","StartDelay":0,"Duration":18,"PredecessorTaskID":8,"IsCollated":false,"CollatedTaskID":null,"CalcWorkHours":135,"ActuWorkHours":null,"Progress":0,"Resources":[],"Level":0,"PredecessorTaskNo":6}, - {"Order":8,"ID":4,"Name":"Task D","Description":"","Tag":null,"StartDate":"2024-09-14T23:00:00.000Z","FinishDate":"2024-09-24T23:00:00.000Z","StartDelay":1,"Duration":10,"PredecessorTaskID":3,"IsCollated":false,"CollatedTaskID":null,"CalcWorkHours":0,"ActuWorkHours":null,"Progress":0,"Resources":[],"Level":0,"PredecessorTaskNo":7}, - {"Order":9,"ID":9,"Name":"Task E1","Description":"","Tag":null,"StartDate":"2024-08-14T23:00:00.000Z","FinishDate":"2024-08-26T23:00:00.000Z","StartDelay":3,"Duration":12,"PredecessorTaskID":null,"IsCollated":false,"CollatedTaskID":8,"CalcWorkHours":90,"ActuWorkHours":null,"Progress":0,"Resources":[],"Level":1,"PredecessorTaskNo":null} + {"ID":1,"Name":"Task A","Description":"","Tag":null,"StartDate":"2024-08-11T23:00:00.000Z","StartDelay":0,"Duration":3,"PredecessorTaskID":null,"IsCollated":false,"CollatedTaskID":null,"ActuWorkHours":null,"Progress":0,"Resources":[]}, + {"ID":2,"Name":"Task B","Description":"","Tag":null,"StartDate":"2024-08-14T23:00:00.000Z","StartDelay":0,"Duration":14,"PredecessorTaskID":1,"IsCollated":false,"CollatedTaskID":null,"ActuWorkHours":null,"Progress":0,"Resources":[]}, + {"ID":5,"Name":"Task B1","Description":"","Tag":null,"StartDate":"2024-08-29T23:00:00.000Z","StartDelay":1,"Duration":28,"PredecessorTaskID":2,"IsCollated":true,"CollatedTaskID":null,"ActuWorkHours":null,"Progress":25,"Resources":[]}, + {"ID":6,"Name":"Task B11","Description":"","Tag":null,"StartDate":"2024-09-02T23:00:00.000Z","StartDelay":4,"Duration":24,"PredecessorTaskID":null,"IsCollated":false,"CollatedTaskID":5,"ActuWorkHours":null,"Progress":0,"Resources":[]}, + {"ID":7,"Name":"Task B12","Description":"","Tag":null,"StartDate":"2024-08-30T23:00:00.000Z","StartDelay":1,"Duration":26,"PredecessorTaskID":null,"IsCollated":false,"CollatedTaskID":5,"ActuWorkHours":null,"Progress":50,"Resources":[]}, + {"ID":8,"Name":"Task E","Description":"","Tag":null,"StartDate":"2024-08-11T23:00:00.000Z","StartDelay":0,"Duration":15,"PredecessorTaskID":null,"IsCollated":true,"CollatedTaskID":null,"ActuWorkHours":null,"Progress":0,"Resources":[]}, + {"ID":3,"Name":"Task C","Description":"","Tag":null,"StartDate":"2024-08-26T23:00:00.000Z","StartDelay":0,"Duration":18,"PredecessorTaskID":8,"IsCollated":false,"CollatedTaskID":null,"ActuWorkHours":null,"Progress":0,"Resources":[]}, + {"ID":4,"Name":"Task D","Description":"","Tag":null,"StartDate":"2024-09-14T23:00:00.000Z","StartDelay":1,"Duration":10,"PredecessorTaskID":3,"IsCollated":false,"CollatedTaskID":null,"ActuWorkHours":null,"Progress":0,"Resources":[]}, + {"ID":9,"Name":"Task E1","Description":"","Tag":null,"StartDate":"2024-08-14T23:00:00.000Z","StartDelay":3,"Duration":12,"PredecessorTaskID":null,"IsCollated":false,"CollatedTaskID":8,"ActuWorkHours":null,"Progress":0,"Resources":[]}, + {"ID":10,"Name":"Task C2","Description":"","Tag":null,"StartDate":"2024-08-26T23:00:00.000Z","StartDelay":0,"Duration":18,"PredecessorTaskID":8,"IsCollated":false,"CollatedTaskID":null,"ActuWorkHours":null,"Progress":0,"Resources":[]}, ]; tasks.forEach((e, i) => { @@ -182,7 +183,7 @@ textarea { window.project1.Clear(); var tasks = [ - {"ID": 1, "Name": "task a","StartDelay": 0,"Duration": Math.randomN(0, 28),"PredecessorTaskID": null,"IsCollated": false,"CollatedTaskID": null}, + {"ID": 1, "Name": "task a","StartDelay": 0,"Duration": Math.randomN(0, 28),"PredecessorTaskID": null,"IsCollated": false,"CollatedTaskID": null}, {"ID": 2,"Name": "task b","StartDelay": Math.randomN(0, 5),"Duration": Math.randomN(0, 28),"PredecessorTaskID": 1,"IsCollated": false,"CollatedTaskID": null}, {"ID": 5,"Name": "task b1","StartDelay": Math.randomN(0, 5),"Duration": Math.randomN(0, 28),"PredecessorTaskID": 2,"IsCollated": true,"CollatedTaskID": null}, {"ID": 6,"Name": "task b11","StartDelay": Math.randomN(0, 5),"Duration": Math.randomN(0, 28),"PredecessorTaskID": null,"IsCollated": false,"CollatedTaskID": 5}, diff --git a/package.json b/package.json index fb135cf..0e371c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "LiteRyzJS/Project", - "version": "0.2.0.256", + "version": "0.2.0.508", "devDependencies": { "css-loader": "^7.1.2", "sass": "^1.77.8", diff --git a/src/project/gantt-chart.js b/src/project/gantt-chart.js index 4413493..1dbe292 100644 --- a/src/project/gantt-chart.js +++ b/src/project/gantt-chart.js @@ -298,7 +298,7 @@ class GanttChart { style = a.Options.Row.CollatedTask; } else if (a.Tasks[i].CollatedTaskID != null) { style = a.Options.Row.ChildTask; - } else if (a.Tasks[i].PredecessorTaskNo == null) { + } else if (a.Tasks[i].PredecessorTaskID == null) { style = a.Options.Row.OrphanTask; } @@ -320,7 +320,7 @@ class GanttChart { continue; } - if (a.Tasks[i].PredecessorTaskNo == null) { + if (a.Tasks[i].PredecessorTaskID == null) { continue; } @@ -329,7 +329,7 @@ class GanttChart { const paddingX = (a.Options.Row.CollatedTask.Height + a.Options.BorderWidth); const offsetX = (rectangle.X + paddingX); - const predecessorTask = a.Tasks.first("Order", a.Tasks[i].PredecessorTaskNo); + const predecessorTask = a.Tasks.first("ID", a.Tasks[i].PredecessorTaskID); if (predecessorTask == null) { continue; } diff --git a/src/project/project-node-model.js b/src/project/project-node-model.js new file mode 100644 index 0000000..4d27008 --- /dev/null +++ b/src/project/project-node-model.js @@ -0,0 +1,24 @@ +import ProjectTaskModel from './project-task-model.js'; + +import '../references/extensions.dist.js'; + + +class ProjectNodeModel extends ProjectTaskModel{ + Order = null; + StartDate = null; + FinishDate = null; + CalcWorkHours = 0; + Level = 0; + Tasks = []; + + + constructor(options) { + super(); + + Object.assign(this, options); + } + +} + + +export default ProjectNodeModel; \ No newline at end of file diff --git a/src/project/project-planner.js b/src/project/project-planner.js index b75cfc3..0de5d43 100644 --- a/src/project/project-planner.js +++ b/src/project/project-planner.js @@ -8,7 +8,6 @@ class ProjectPlanner { const a = this; a.Container = el; - a.Columns = [ null, null, @@ -20,6 +19,7 @@ class ProjectPlanner { "Resource Names", null ]; + a.Tasks = []; a.#initialiseComponents(); } @@ -52,6 +52,8 @@ class ProjectPlanner { Load(tasks) { const a = this; + a.Tasks = tasks; + if (!a.Container.classList.contains("literyzjs-project")) { a.Container.classList.add("literyzjs-project"); } @@ -125,7 +127,7 @@ class ProjectPlanner { htmlContent += "" + task.Duration + " day" + (parseInt(task.Duration) == 1 ? "" : "s") + ""; htmlContent += "" + new Date(task.StartDate).toLocaleDateString() + ""; htmlContent += "" + new Date(task.FinishDate).toLocaleDateString() + ""; - htmlContent += "" + (task.PredecessorTaskNo ?? "") + ""; + htmlContent += "" + a.#findTaskNo(task.PredecessorTaskID) + ""; htmlContent += ""; htmlContent += ""; htmlContent += ""; @@ -133,6 +135,18 @@ class ProjectPlanner { return htmlContent; } + #findTaskNo(id) { + const a = this; + + if (id == null) { + return ""; + } + + const item = a.Tasks.first("ID", id); + + return (item == null) ? "" : item.Order; + } + } diff --git a/src/project/project-task-model.js b/src/project/project-task-model.js new file mode 100644 index 0000000..091722f --- /dev/null +++ b/src/project/project-task-model.js @@ -0,0 +1,26 @@ +import '../references/extensions.dist.js'; + + +class ProjectTaskModel { + ID = null; + Name = ""; + Description = ""; + Tag = null; + StartDelay = 0; // Days + Duration = 1; // Days + PredecessorTaskID = null; + IsCollated = false; + CollatedTaskID = null; + ActuWorkHours = null; + Progress = 0; + Resources = []; + + + constructor(options) { + Object.assign(this, options); + } + +} + + +export default ProjectTaskModel; \ No newline at end of file diff --git a/src/project/project.js b/src/project/project.js index 682f6e0..600967f 100644 --- a/src/project/project.js +++ b/src/project/project.js @@ -1,3 +1,6 @@ +import ProjectTaskModel from './project-task-model.js'; +import ProjectNodeModel from './project-node-model.js'; + import '../references/extensions.dist.js'; @@ -6,33 +9,12 @@ class Project { const a = this; const _options = Object.assign(a.DefaultOptions, options); - a.Debug = false; + a.Debug = true; a.Project = _options; a.Tasks = []; } - get NewTask() { - return { - // Order: null, - ID: null, - Name: "", - Description: "", - Tag: null, - // StartDate: null, // new Date(), - // FinishDate: null, // new Date(), - StartDelay: 0, // Days - Duration: 1, // Days - PredecessorTaskID: null, - IsCollated: false, - CollatedTaskID: null, - // CalcWorkHours: 0, - ActuWorkHours: null, - Progress: 0, - Resources: [] - }; - } - get StartDate() { const a = this; @@ -72,35 +54,11 @@ class Project { }; } - get #newTaskNode() { - return { - Order: null, - ID: null, - Name: "", - Description: "", - Tag: null, - StartDate: null, // new Date(), - FinishDate: null, // new Date(), - StartDelay: 0, // Days - Duration: 1, // Days - PredecessorTaskID: null, - IsCollated: false, - CollatedTaskID: null, - CalcWorkHours: 0, - ActuWorkHours: null, - Progress: 0, - Resources: [], - Level: 0, - PredecessorTaskNo: null, - Tasks: [] - }; - } - AddTask(task) { const a = this; - const newTask = Object.assign(a.NewTask, task); - const newTaskNode = Object.assign(a.#newTaskNode, newTask); + const newTask = new ProjectTaskModel(task); + const newTaskNode = new ProjectNodeModel(newTask); if ((newTaskNode.PredecessorTaskID == null) && (newTaskNode.CollatedTaskID == null)) { a.Tasks.push(newTaskNode); @@ -232,19 +190,18 @@ class Project { a.Sort(); // Get flat references - let result = a.Tasks.flatten("Tasks"); + let allTasks = a.Tasks.flatten("Tasks"); // Reset calculated values - for (var i=0; i Set StartDate from Predecessor"); + } + } + } else if (task.CollatedTaskID != null) { + const groupTask = allTasks.first("ID", task.CollatedTaskID); + if (groupTask != null) { + if (groupTask.StartDate != null) { + task.StartDate = Date.addDays(groupTask.StartDate, task.StartDelay); + + a.#log("> Set StartDate from Group"); + } + } + } else { + task.StartDate = Date.addDays(a.Project.StartDate, task.StartDelay); + + a.#log("> Set StartDate from Project"); } - const predecessor = a.FindTask(array[i].PredecessorTaskID); - if (predecessor == null) { - continue; + // if (task.IsCollated) { + if (task.Tasks.length > 0) { + a.#recalculateTask(allTasks, task.Tasks); + + a.#log("> Calc inner"); + } + // } + + if (task.StartDate != null) { + if (task.IsCollated) { + + const childTasks = task.Tasks.select("CollatedTaskID", task.ID); + if (childTasks.length <= 0) { + task.FinishDate = task.StartDate; + task.Duration = 0; + + a.#log("> Set FinishDate from Group (0)"); + } else { + + if (!childTasks.any("FinishDate", null)) { + task.FinishDate = childTasks.orderByDesc("FinishDate")[0].FinishDate; + task.Duration = Date.diffDays(task.StartDate, task.FinishDate); + + a.#log("> Set FinishDate from Group"); + } + } + + } else { + task.FinishDate = Date.addDays(task.StartDate, task.Duration); + + a.#log("> Set FinishDate"); + } } - array[i].PredecessorTaskNo = predecessor.Order; } } - #recalculateCollatedTask(array, index) { - let node2 = array.select("CollatedTaskID", array[index].ID); - if (node2.length <= 0) { - // No children - array[index].Duration = 0; - - return new Date(array[index].StartDate); - } - - // Not ready, calculation pending - if (node2.any("FinishDate", null)) { - array[index].Duration = 0; - - return null; - } - - let node2FinishDate = new Date(array[index].StartDate); - node2.forEach(e => { - if (e.FinishDate > node2FinishDate) { - node2FinishDate = e.FinishDate; - } - }); - - array[index].Progress = Math.average(node2.toList("Progress")); - array[index].FinishDate = new Date(node2FinishDate); - array[index].Duration = Date.diffDays(array[index].StartDate, array[index].FinishDate); - } - #sortNode(node) { const a = this;