using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using InABox.WPF; using Syncfusion.Windows.Controls.Gantt; using Syncfusion.Windows.Controls.Grid; using GridSelectionMode = Syncfusion.Windows.Controls.Grid.GridSelectionMode; namespace PRSDesktop { /// /// Interaction logic for TaskPlannerControl.xaml /// public partial class TaskPlannerControl : UserControl, ITaskControl { private enum Suppress { This } private CoreTable _kanbans; private CoreTable _relationships; private readonly ObservableCollection _resources = new(); private CoreTable _stages; private readonly ObservableCollection _tasks = new(); private bool bLoading; public TaskPlannerControl() { using (new EventSuppressor(Suppress.This)) { InitializeComponent(); Gantt.ItemsSource = _tasks; Gantt.ResourceCollection = _resources; } } public void Setup() { LoadKanbanTypes(); } private void LoadKanbanTypes() { using (new EventSuppressor(Suppress.This)) { var types = new Dictionary { { CoreUtils.FullGuid, "All Task Types" }, { Guid.Empty, "Uncategorized Tasks" } }; new Client().Query(new Filter(x => x.Hidden).IsEqualTo(false)) .IntoDictionary(types, x => x.ID, x => x.Description); SelectedType.ItemsSource = types; } } public void Refresh(bool resetselection) { Refresh(); } public void Shutdown() { } private void Gantt_TemplateApplied(object sender, TemplateAppliedEventArgs args) { if (Gantt.GanttGrid != null) { Gantt.GanttGrid.Model.Options.ListBoxSelectionMode = GridSelectionMode.One; Gantt.GanttGrid.Model.Sizer.AllowAutoCalculateSize = true; Gantt.GanttGrid.Model.Sizer.ListenToSizeChanged = true; Gantt.GanttGrid.Model.Options.ColumnSizer = GridControlLengthUnitType.Star; Gantt.GanttGrid.RowHeaderWidth = 0; Gantt.GanttGrid.ShowRowHeader = false; CreateGanttColumns(); //Gantt.GanttGrid.ReadOnly = true; } } private void CreateGanttColumns() { if (Gantt.GanttGrid == null) return; Gantt.GanttGrid.Columns.Clear(); //Gantt.GanttGrid.Columns.Add(new Syncfusion.Windows.Controls.Grid.GridTreeColumn("TaskId") { Width = 60F, HeaderText = "#", StyleInfo = new Syncfusion.Windows.Controls.Grid.GridStyleInfo() { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("TaskName") { Width = 220F, PercentWidth = 100F, HeaderText = "Description", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("StartDate") { Width = 70F, HeaderText = "Start", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("Manpower") { Width = 60F, HeaderText = "Hrs", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("Percentage") { Width = 40F, HeaderText = "%", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, Format = "#0.##%", HorizontalAlignment = HorizontalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("FinishDate") { Width = 70F, HeaderText = "Due", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center } }); } private void LoadData() { var query = new MultiQuery(); query.Add( new QueryDef( new Filter(x => x.Closed).IsEqualTo(DateTime.MinValue).And(x => x.JobLink.ID).IsEqualTo(Host.ParentID), new Columns ( x => x.ID, x => x.Created, x => x.DueDate, x => x.Completed, x => x.Description, x => x.Summary, x => x.Category, x => x.EmployeeLink.ID, x => x.EmployeeLink.Name, x => x.ManagerLink.ID, x => x.Title, x => x.Notes, x => x.Number, x => x.Attachments, x => x.Type.Code, x => x.StartDate, x => x.EstimatedTime, x => x.ActualTime, x => x.Type.ID ), new SortOrder(x => x.DueDate).ThenBy(x => x.StartDate) ), typeof(Kanban) ); query.Add( new QueryDef( new Filter(x => x.Job.ID).IsEqualTo(Host.ParentID), null, null ), typeof(JobStage) ); query.Add( new QueryDef( new Filter(x => x.Parent.ID).IsEqualTo(Host.ParentID), new Columns( x => x.ID, x => x.Predecessor.ID, x => x.Successor.ID, x => x.Type, x => x.Parent.ID ), null ), typeof(KanbanRelationship) ); query.Query(); _kanbans = query.Get(typeof(Kanban)); _stages = query.Get(typeof(JobStage)); _relationships = query.Get(typeof(KanbanRelationship)); } public void Refresh() { bLoading = true; using (new WaitCursor()) { try { LoadData(); _tasks.Clear(); var startmarker = new GanttTask { StartDate = DateTime.Today, FinishDate = DateTime.Today, TaskName = "Start of Job", IsMileStone = true }; var endmarker = new GanttTask { StartDate = DateTime.Today, FinishDate = DateTime.Today, TaskName = "End of Job", IsMileStone = true }; var curstart = DateTime.MaxValue; var curend = DateTime.MinValue; _tasks.Add(startmarker); foreach (var row in _kanbans.Rows) if ((Host.KanbanSettings.PlannerSettings.IncludeCompleted || row.Get(c => c.Completed).IsEmpty()) && (Host.KanbanSettings.PlannerSettings.SelectedType == CoreUtils.FullGuid || row.Get(c => c.Type.ID) == Host.KanbanSettings.PlannerSettings.SelectedType)) { var task = new GanttTask(); LoadTask(row, task); if (task.StartDate < curstart) curstart = task.StartDate; if (task.FinishDate > curend) curend = task.FinishDate; _tasks.Add(task); } _tasks.Add(endmarker); foreach (var row in _relationships.Rows) { var predtask = _tasks.FirstOrDefault(x => x.ID == row.Get(c => c.Predecessor.ID)); var succtask = _tasks.FirstOrDefault(x => x.ID == row.Get(c => c.Successor.ID)); if (predtask != null && succtask != null) { var type = row.Get(x => x.Type); var relationship = type == GanttRelationshipType.FinishToFinish ? GanttTaskRelationship.FinishToFinish : type == GanttRelationshipType.FinishToStart ? GanttTaskRelationship.FinishToStart : type == GanttRelationshipType.StartToFinish ? GanttTaskRelationship.StartToFinish : GanttTaskRelationship.StartToStart; succtask.Predecessor.Add(new Predecessor { GanttTaskIndex = predtask.TaskId, GanttTaskRelationship = relationship }); } } var _striplines = new List(); foreach (var row in _stages.Rows) { if (!_striplines.Any()) { var startline = new StripLineInfo(); startline.Content = "Start of Job"; startline.StartDate = row.Get(c => c.StartDate); startline.EndDate = startline.StartDate; startline.HorizontalContentAlignment = HorizontalAlignment.Center; startline.VerticalContentAlignment = VerticalAlignment.Center; startline.Background = Brushes.Gold; startline.RepeatBehavior = Repeat.None; _striplines.Add(startline); } var stripline = new StripLineInfo(); stripline.Content = row.Get(c => c.Name); stripline.StartDate = row.Get(c => c.EndDate); stripline.EndDate = stripline.StartDate; stripline.HorizontalContentAlignment = HorizontalAlignment.Center; stripline.VerticalContentAlignment = VerticalAlignment.Center; stripline.Background = Brushes.Gold; stripline.RepeatBehavior = Repeat.None; _striplines.Add(stripline); if (stripline.StartDate < curstart) curstart = stripline.StartDate; if (stripline.EndDate > curend) curend = stripline.EndDate; } Gantt.StripLines = _striplines; Gantt.ShowStripLines = true; Gantt.UseOnDemandSchedule = true; if (curstart != DateTime.MaxValue) { startmarker.StartDate = curstart.Date; startmarker.FinishDate = curstart.Date; } if (curend != DateTime.MinValue) { endmarker.StartDate = curend.Date.AddDays(1); endmarker.FinishDate = curend.AddDays(1); } } finally { bLoading = false; } foreach (var task in _tasks) task.PropertyChanged += Task_PropertyChanged; } } private void LoadTask(CoreRow row, GanttTask task) { var start = row.Get(x => x.StartDate); if (start.IsEmpty()) start = row.Get(x => x.Created); var estimated = row.Get(x => x.EstimatedTime); if (estimated.TotalMilliseconds == 0L) estimated = TimeSpan.FromHours(1); var actual = row.Get(x => x.ActualTime); var progress = estimated.TotalMilliseconds != 0L ? actual.TotalMilliseconds * 100L / estimated.TotalMilliseconds : 0F; task.TaskId = row.Get(c => c.Number); task.ID = row.Get(x => x.ID); task.TaskName = string.Format("{0} - {1}", row.Get(c => c.Number), row.Get(x => x.Title)); task.StartDate = start.Date; task.FinishDate = row.Get(x => x.DueDate).Date.AddDays(1).AddMilliseconds(-1); task.Manpower = estimated; task.Percentage = progress; task.IsMileStone = false; var empid = row.EntityLinkID(x => x.EmployeeLink) ?? Guid.Empty; if (empid != Guid.Empty) { var resource = _resources.FirstOrDefault(x => (x as GanttResource).Guid == empid) as GanttResource; if (resource == null) { resource = new GanttResource { ID = _resources.Count, Guid = empid, Name = row.Get(c => c.EmployeeLink.Name) }; _resources.Add(resource); } task.Resources = new ObservableCollection { resource }; } } private void Task_PropertyChanged(object sender, PropertyChangedEventArgs args) { if (bLoading) return; if (!string.Equals(args.PropertyName, "StartDate") && !string.Equals(args.PropertyName, "FinishDate")) return; var row = _kanbans.Rows.FirstOrDefault(r => r.Get(x => x.ID) == ((GanttTask)sender).ID); var kanban = row?.ToObject(); var task = sender as GanttTask; var bChanged = false; if (task != null && kanban != null) { if (string.Equals(args.PropertyName, "FinishDate")) { kanban.DueDate = task.FinishDate.Date; bChanged = true; } else if (string.Equals(args.PropertyName, "StartDate")) { kanban.StartDate = task.StartDate.Date; bChanged = true; } } if (bChanged) { row.Set(x => x.DueDate, kanban.DueDate.Date); row.Set(x => x.StartDate, kanban.StartDate.Date); new Client().Save(kanban, "Updated by Planner", (o, e) => { }); } } private void Gantt_RelationshipEstablished(object sender, GanttRelationshipEstablishedEventArgs args) { var pred = args.StartTask as GanttTask; var succ = args.EndTask as GanttTask; if (pred == null || pred.IsMileStone) throw new Exception("Cannot make a connection here"); if (succ == null || succ.IsMileStone) throw new Exception("Cannot make a connection here"); var relationship = new KanbanRelationship(); relationship.Parent.ID = Host.ParentID; relationship.Predecessor.ID = pred.ID; relationship.Successor.ID = succ.ID; relationship.Type = args.Relationship == GanttTaskRelationship.FinishToFinish ? GanttRelationshipType.FinishToFinish : args.Relationship == GanttTaskRelationship.FinishToStart ? GanttRelationshipType.FinishToStart : args.Relationship == GanttTaskRelationship.StartToFinish ? GanttRelationshipType.StartToFinish : GanttRelationshipType.StartToStart; new Client().Save(relationship, "", (o, e) => { var row = _relationships.NewRow(); _relationships.LoadRow(row, relationship); _relationships.Rows.Add(row); }); } private void GanttContextMenu_Opened(object sender, RoutedEventArgs e) { var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask; UnlinkTaskMenu.IsEnabled = task != null && task.Predecessor.Any(); UnlinkTaskMenu.Tag = task; } private void UnlinkTaskMenu_Click(object sender, RoutedEventArgs args) { var successor = (sender as MenuItem).Tag as GanttTask; if (successor == null) return; var deletes = new List(); foreach (var link in successor.Predecessor) { var predecessor = _tasks.FirstOrDefault(x => x.TaskId == link.GanttTaskIndex); if (predecessor != null) { var rows = _relationships.Rows.Where(r => r.Get(c => c.Successor.ID).Equals(successor.ID) && r.Get(c => c.Predecessor.ID).Equals(predecessor.ID)).ToArray(); foreach (var row in rows) { deletes.Add(row.ToObject()); _relationships.Rows.Remove(row); } } } if (deletes.Any()) new Client().Delete(deletes, "", (o, e) => { }); successor.Predecessor.Clear(); } private void EditTask_Click(object sender, RoutedEventArgs e) { DoEditTask(); } private void DoEditTask() { var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask; if (task == null) return; var row = _kanbans.Rows.FirstOrDefault(r => r.Get(x => x.ID) == task.ID); var kanban = row?.ToObject(); if (kanban != null) { var bOK = new KanbanGrid().EditItems(new[] { kanban }); if (bOK) { _kanbans.LoadRow(row, kanban); LoadTask(row, task); } } } private void AddTask_Click(object sender, RoutedEventArgs e) { var kanban = new Kanban(); kanban.JobLink.ID = Host.ParentID; var bOK = new KanbanGrid().EditItems(new[] { kanban }); if (bOK) { var row = _kanbans.NewRow(); _kanbans.LoadRow(row, kanban); var task = new GanttTask(); LoadTask(row, task); _tasks.Add(task); task.PropertyChanged += Task_PropertyChanged; } } private void SelectedType_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (EventSuppressor.IsSet(Suppress.This)) { Host.KanbanSettings.PlannerSettings.SelectedType = SelectedType.SelectedValue == null ? Guid.Empty : (Guid)SelectedType.SelectedValue; Host.SaveSettings(); Refresh(); } } private void IncludeCompleted_Checked(object sender, RoutedEventArgs e) { Host.KanbanSettings.PlannerSettings.IncludeCompleted = IncludeCompleted.IsChecked == true; Host.SaveSettings(); Refresh(); } #region ITaskControl Support public KanbanViewType KanbanViewType => KanbanViewType.Planner; public ITaskHost Host { get; set; } public bool IsReady { get; set; } public string SectionName => "Task Planner"; public DataModel DataModel(Selection selection) { return new AutoDataModel(new Filter(x => x.ID).IsEqualTo(Guid.Empty)); } public IEnumerable SelectedModels(TaskModel sender = null) { MessageBox.Show("TaskPlannerControl.SelectedModels() is not Implemented!"); return new TaskModel[] { }; } #endregion private void TaskTypeButton_OnClick(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(KanbanType)); list.ShowDialog(); LoadKanbanTypes(); } } }