using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.WPF; using java.nio.file; using Syncfusion.Data.Extensions; using Syncfusion.UI.Xaml.Charts; using Syncfusion.Windows.Controls.Gantt.Chart; using Syncfusion.Windows.Tools.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using InABox.Configuration; namespace PRSDesktop { public class JobDocumentItemViewModel { public string Group { get; set; } public string MileStoneCode { get; set; } public JobDocumentStatusChart.StatusType Status { get; set; } public int Count { get; set; } public SolidColorBrush Colour { get; set; } } public class JobDocumentStatusChartProperties : IUserConfigurationSettings, IDashboardProperties { public JobDocumentStatusChart.ItemType ItemType { get; set; } = JobDocumentStatusChart.ItemType.Sets; public JobDocumentStatusChart.StatusType[]? StatusTypes { get; set; } = null; public Guid[] MileStones { get; set; } = Array.Empty(); public Guid JobID { get; set; } } public class JobDocumentStatusChartElement : DashboardElement { } /// /// Interaction logic for JobDocumentStatusChart.xaml /// public partial class JobDocumentStatusChart : UserControl, IDashboardWidget, IRequiresCanView, IHeaderDashboard { private Dictionary MileStoneColours { get; set; } private Dictionary MilestoneTypes { get; set; } private List> JobLookups { get; set; } public JobDocumentStatusChartProperties Properties { get; set; } public event LoadSettings? LoadSettings; public event SaveSettings? SaveSettings; public DashboardHeader Header { get; } = new(); public enum ItemType { Sets, Documents } public enum StatusType { Failed, Incomplete, Submitted, Approved } public JobDocumentStatusChart() { InitializeComponent(); } public void Setup() { JobLookups = new Client() .Query(null, LookupFactory.DefineColumns()) .Rows.Select(x => new KeyValuePair( (Guid?)x["ID"] ?? Guid.Empty, LookupFactory.FormatLookup(x.ToDictionary(new[] { "ID" }), Array.Empty()))) .ToList(); JobLookups.Insert(0, new(Guid.Empty, "Select Job")); var pallete = new ChartColorModel().GetMetroBrushes() .Select(x => (x as SolidColorBrush)?.Color) .Where(x => x != null) .Select(x => x!.Value).ToList(); MilestoneTypes = new Client() .Query(null, new Columns(x => x.ID) .Add(x => x.Code) .Add(x => x.Description)) .ToObjects().ToDictionary(x => x.ID, x => x); int i = 0; MileStoneColours = MilestoneTypes.ToDictionary(x => x.Key, x => pallete[i++ % pallete.Count]); SetupHeader(); } #region Header private ComboBox JobBox; private ComboBox ItemTypeBox; private ComboBoxAdv MilestoneBox; private ComboBoxAdv StatusTypeBox; private void SetupHeader() { JobBox = new ComboBox { Margin = new Thickness(5, 0, 0, 0) }; JobBox.ItemsSource = JobLookups; JobBox.SelectedValuePath = "Key"; JobBox.DisplayMemberPath = "Value"; JobBox.SelectedValue = Properties.JobID; JobBox.SelectionChanged += JobBox_SelectionChanged; ItemTypeBox = new ComboBox { Margin = new Thickness(5, 0, 0, 0) }; ItemTypeBox.ItemsSource = Enum.GetValues(); ItemTypeBox.SelectedValue = Properties.ItemType; ItemTypeBox.SelectionChanged += ItemTypeBox_SelectionChanged; MilestoneBox = new ComboBoxAdv { VerticalAlignment = VerticalAlignment.Stretch, VerticalContentAlignment = VerticalAlignment.Center, IsEditable = false, AllowMultiSelect = true, Width = 150, DefaultText = "Select Milestones", Margin = new Thickness(5, 0, 0, 0) }; var items = MilestoneTypes.ToDictionary(x => x.Key, x => $"{x.Value.Code}: {x.Value.Description}"); MilestoneBox.ItemsSource = items; MilestoneBox.SelectedValuePath = "Key"; MilestoneBox.DisplayMemberPath = "Value"; MilestoneBox.SelectedItems = Properties.MileStones.Select(x => items.Where(y => x == y.Key).FirstOrDefault()).ToObservableCollection(); MilestoneBox.SelectionChanged += MileStoneBox_SelectionChanged; StatusTypeBox = new ComboBoxAdv { VerticalAlignment = VerticalAlignment.Stretch, VerticalContentAlignment = VerticalAlignment.Center, IsEditable = false, AllowMultiSelect = true, Width = 150, DefaultText = "Select Status", Margin = new Thickness(5, 0, 0, 0) }; StatusTypeBox.ItemsSource = Enum.GetValues(); StatusTypeBox.SelectedItems = GetStatusTypes().ToObservableCollection(); StatusTypeBox.SelectionChanged += StatusTypeBox_SelectionChanged; Header.BeginUpdate() .Add(JobBox) .Add(ItemTypeBox) .Add(StatusTypeBox) .Add(MilestoneBox) .EndUpdate(); } private void MileStoneBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { Properties.MileStones = MilestoneBox.SelectedItems.Cast>().Select(x => x.Key).ToArray(); Refresh(); } private void JobBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { Properties.JobID = (Guid)JobBox.SelectedValue; Refresh(); } private void ItemTypeBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { Properties.ItemType = (ItemType)ItemTypeBox.SelectedValue; Refresh(); } private void StatusTypeBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { Properties.StatusTypes = StatusTypeBox.SelectedItems.Cast().OrderBy(x => x).ToArray(); Refresh(); } #endregion private void ClearView(string message = "No Data") { NoDataLabel.Content = message; Pie.Visibility = Visibility.Collapsed; ChartRow.Height = new GridLength(0); LabelRow.Height = new GridLength(1, GridUnitType.Star); } private void ShowView() { Pie.Visibility = Visibility.Visible; ChartRow.Height = new GridLength(1, GridUnitType.Star); LabelRow.Height = new GridLength(0); } private StatusType[] GetStatusTypes() { Properties.StatusTypes ??= Enum.GetValues().OrderBy(x => x).ToArray(); return Properties.StatusTypes; } private StatusType ConvertStatus(JobDocumentSetMileStoneStatus status) { switch (status) { case JobDocumentSetMileStoneStatus.Approved: return StatusType.Approved; case JobDocumentSetMileStoneStatus.Submitted: return StatusType.Submitted; case JobDocumentSetMileStoneStatus.Rejected: case JobDocumentSetMileStoneStatus.Cancelled: return StatusType.Failed; case JobDocumentSetMileStoneStatus.NotStarted: case JobDocumentSetMileStoneStatus.InProgress: case JobDocumentSetMileStoneStatus.OnHold: case JobDocumentSetMileStoneStatus.InfoRequired: case JobDocumentSetMileStoneStatus.Unknown: default: return StatusType.Incomplete; } } private Filter GetStatusFilter() { var statusTypes = GetStatusTypes(); if (statusTypes.Length == 0) return new Filter().All(); var statuses = new List(); if (statusTypes.Contains(StatusType.Incomplete)) { statuses.Add(JobDocumentSetMileStoneStatus.NotStarted); statuses.Add(JobDocumentSetMileStoneStatus.InProgress); statuses.Add(JobDocumentSetMileStoneStatus.OnHold); statuses.Add(JobDocumentSetMileStoneStatus.InfoRequired); statuses.Add(JobDocumentSetMileStoneStatus.Unknown); } if (statusTypes.Contains(StatusType.Approved)) { statuses.Add(JobDocumentSetMileStoneStatus.Approved); } if (statusTypes.Contains(StatusType.Submitted)) { statuses.Add(JobDocumentSetMileStoneStatus.Submitted); } if (statusTypes.Contains(StatusType.Failed)) { statuses.Add(JobDocumentSetMileStoneStatus.Rejected); statuses.Add(JobDocumentSetMileStoneStatus.Cancelled); } if (statuses.Count == 0) return new Filter().None(); //return new Filter(x => x.Status).InList(statuses.ToArray()); var filter = new Filter(x => x.Status).IsEqualTo(statuses[0]); for(int i = 1; i < statuses.Count; ++i) { filter.Or(x => x.Status).IsEqualTo(statuses[i]); } return filter; } private Filter GetMileStoneFilter() { if (Properties.MileStones.Length == 0) return new Filter().All(); return new Filter(x => x.Type.ID).InList(Properties.MileStones); } public void Refresh() { if (Properties.JobID == Guid.Empty) { ClearView("Please select a Job"); return; } var statusFilter = GetStatusFilter(); var milestoneFilter = GetMileStoneFilter(); var columns = new Columns(x => x.ID) .Add(x => x.Type.ID) .Add(x => x.Status); if (Properties.ItemType == ItemType.Documents) { columns.Add(x => x.Attachments); } var milestones = new Client() .Query( new Filter(x => x.DocumentSet.Job.ID).IsEqualTo(Properties.JobID) .And(statusFilter) .And(milestoneFilter), columns); if (!milestones.Rows.Any()) { ClearView(); return; } var grouping = milestones .ToObjects() .GroupBy(x => new { x.Type.ID, Status = ConvertStatus(x.Status) }) .OrderBy(x => x.Key.ID).ThenBy(x => x.Key.Status); var statusTypes = GetStatusTypes(); if (statusTypes.Length == 0) statusTypes = Enum.GetValues(); float i = 0f; var statusShades = statusTypes.ToDictionary(x => x, x => -0.5f + (++i / statusTypes.Length) * 0.5f); var data = grouping.Select(x => { if (!MilestoneTypes.TryGetValue(x.Key.ID, out var milestoneType)) return null; if (!MileStoneColours.TryGetValue(x.Key.ID, out var milestoneColour)) return null; return new JobDocumentItemViewModel { Group = $"{milestoneType.Code} : {x.Key.Status}", Count = Properties.ItemType == ItemType.Documents ? x.Sum(x => x.Attachments) : x.Count(), MileStoneCode = milestoneType.Code, Status = x.Key.Status, Colour = new SolidColorBrush(ImageUtils.AdjustBrightness(milestoneColour, statusShades[x.Key.Status])) }; }).Where(x => x is not null).Select(x => x!).ToList(); Legend.ItemsSource = MilestoneTypes.Select(x => { var colour = MileStoneColours[x.Key]; return new { Text = x.Value.Code, Colour = new SolidColorBrush(colour) }; }).ToList(); Pie.ItemsSource = data; ShowView(); } public void Shutdown() { } } }