JobDocumentStatusChart.xaml.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. using Comal.Classes;
  2. using InABox.Clients;
  3. using InABox.Core;
  4. using InABox.WPF;
  5. using java.nio.file;
  6. using Syncfusion.Data.Extensions;
  7. using Syncfusion.UI.Xaml.Charts;
  8. using Syncfusion.Windows.Controls.Gantt.Chart;
  9. using Syncfusion.Windows.Tools.Controls;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Reactive.Linq;
  14. using System.Text;
  15. using System.Threading.Tasks;
  16. using System.Windows;
  17. using System.Windows.Controls;
  18. using System.Windows.Data;
  19. using System.Windows.Documents;
  20. using System.Windows.Input;
  21. using System.Windows.Media;
  22. using System.Windows.Media.Imaging;
  23. using System.Windows.Navigation;
  24. using System.Windows.Shapes;
  25. using InABox.Configuration;
  26. namespace PRSDesktop
  27. {
  28. public class JobDocumentItemViewModel
  29. {
  30. public string Group { get; set; }
  31. public string MileStoneCode { get; set; }
  32. public JobDocumentStatusChart.StatusType Status { get; set; }
  33. public int Count { get; set; }
  34. public SolidColorBrush Colour { get; set; }
  35. }
  36. public class JobDocumentStatusChartProperties : IUserConfigurationSettings, IDashboardProperties
  37. {
  38. public JobDocumentStatusChart.ItemType ItemType { get; set; } = JobDocumentStatusChart.ItemType.Sets;
  39. public JobDocumentStatusChart.StatusType[]? StatusTypes { get; set; } = null;
  40. public Guid[] MileStones { get; set; } = Array.Empty<Guid>();
  41. public Guid JobID { get; set; }
  42. }
  43. public class JobDocumentStatusChartElement : DashboardElement<JobDocumentStatusChart, WidgetGroups.Projects, JobDocumentStatusChartProperties> { }
  44. /// <summary>
  45. /// Interaction logic for JobDocumentStatusChart.xaml
  46. /// </summary>
  47. public partial class JobDocumentStatusChart : UserControl,
  48. IDashboardWidget<WidgetGroups.Projects, JobDocumentStatusChartProperties>,
  49. IRequiresCanView<JobDocumentSetMileStone>,
  50. IHeaderDashboard
  51. {
  52. private Dictionary<Guid, Color> MileStoneColours { get; set; }
  53. private Dictionary<Guid, JobDocumentSetMileStoneType> MilestoneTypes { get; set; }
  54. private List<KeyValuePair<Guid, string>> JobLookups { get; set; }
  55. public JobDocumentStatusChartProperties Properties { get; set; }
  56. public event LoadSettings<JobDocumentStatusChartProperties>? LoadSettings;
  57. public event SaveSettings<JobDocumentStatusChartProperties>? SaveSettings;
  58. public DashboardHeader Header { get; } = new();
  59. public enum ItemType
  60. {
  61. Sets,
  62. Documents
  63. }
  64. public enum StatusType
  65. {
  66. Failed,
  67. Incomplete,
  68. Submitted,
  69. Approved
  70. }
  71. public JobDocumentStatusChart()
  72. {
  73. InitializeComponent();
  74. }
  75. public void Setup()
  76. {
  77. JobLookups = new Client<Job>()
  78. .Query(null,
  79. LookupFactory.DefineColumns<Job>())
  80. .Rows.Select(x => new KeyValuePair<Guid, string>(
  81. (Guid?)x["ID"] ?? Guid.Empty,
  82. LookupFactory.FormatLookup<Job>(x.ToDictionary(new[] { "ID" }), Array.Empty<string>())))
  83. .ToList();
  84. JobLookups.Insert(0, new(Guid.Empty, "Select Job"));
  85. var pallete = new ChartColorModel().GetMetroBrushes()
  86. .Select(x => (x as SolidColorBrush)?.Color)
  87. .Where(x => x != null)
  88. .Select(x => x!.Value).ToList();
  89. MilestoneTypes = new Client<JobDocumentSetMileStoneType>()
  90. .Query(null,
  91. new Columns<JobDocumentSetMileStoneType>(x => x.ID)
  92. .Add(x => x.Code)
  93. .Add(x => x.Description))
  94. .ToObjects<JobDocumentSetMileStoneType>().ToDictionary(x => x.ID, x => x);
  95. int i = 0;
  96. MileStoneColours = MilestoneTypes.ToDictionary(x => x.Key, x => pallete[i++ % pallete.Count]);
  97. SetupHeader();
  98. }
  99. #region Header
  100. private ComboBox JobBox;
  101. private ComboBox ItemTypeBox;
  102. private ComboBoxAdv MilestoneBox;
  103. private ComboBoxAdv StatusTypeBox;
  104. private void SetupHeader()
  105. {
  106. JobBox = new ComboBox
  107. {
  108. Margin = new Thickness(5, 0, 0, 0)
  109. };
  110. JobBox.ItemsSource = JobLookups;
  111. JobBox.SelectedValuePath = "Key";
  112. JobBox.DisplayMemberPath = "Value";
  113. JobBox.SelectedValue = Properties.JobID;
  114. JobBox.SelectionChanged += JobBox_SelectionChanged;
  115. ItemTypeBox = new ComboBox
  116. {
  117. Margin = new Thickness(5, 0, 0, 0)
  118. };
  119. ItemTypeBox.ItemsSource = Enum.GetValues<ItemType>();
  120. ItemTypeBox.SelectedValue = Properties.ItemType;
  121. ItemTypeBox.SelectionChanged += ItemTypeBox_SelectionChanged;
  122. MilestoneBox = new ComboBoxAdv
  123. {
  124. VerticalAlignment = VerticalAlignment.Stretch,
  125. VerticalContentAlignment = VerticalAlignment.Center,
  126. IsEditable = false,
  127. AllowMultiSelect = true,
  128. Width = 150,
  129. DefaultText = "Select Milestones",
  130. Margin = new Thickness(5, 0, 0, 0)
  131. };
  132. var items = MilestoneTypes.ToDictionary(x => x.Key, x => $"{x.Value.Code}: {x.Value.Description}");
  133. MilestoneBox.ItemsSource = items;
  134. MilestoneBox.SelectedValuePath = "Key";
  135. MilestoneBox.DisplayMemberPath = "Value";
  136. MilestoneBox.SelectedItems = Properties.MileStones.Select(x => items.Where(y => x == y.Key).FirstOrDefault()).ToObservableCollection();
  137. MilestoneBox.SelectionChanged += MileStoneBox_SelectionChanged;
  138. StatusTypeBox = new ComboBoxAdv
  139. {
  140. VerticalAlignment = VerticalAlignment.Stretch,
  141. VerticalContentAlignment = VerticalAlignment.Center,
  142. IsEditable = false,
  143. AllowMultiSelect = true,
  144. Width = 150,
  145. DefaultText = "Select Status",
  146. Margin = new Thickness(5, 0, 0, 0)
  147. };
  148. StatusTypeBox.ItemsSource = Enum.GetValues<StatusType>();
  149. StatusTypeBox.SelectedItems = GetStatusTypes().ToObservableCollection();
  150. StatusTypeBox.SelectionChanged += StatusTypeBox_SelectionChanged;
  151. Header.BeginUpdate()
  152. .Add(JobBox)
  153. .Add(ItemTypeBox)
  154. .Add(StatusTypeBox)
  155. .Add(MilestoneBox)
  156. .EndUpdate();
  157. }
  158. private void MileStoneBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  159. {
  160. Properties.MileStones = MilestoneBox.SelectedItems.Cast<KeyValuePair<Guid, string>>().Select(x => x.Key).ToArray();
  161. Refresh();
  162. }
  163. private void JobBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  164. {
  165. Properties.JobID = (Guid)JobBox.SelectedValue;
  166. Refresh();
  167. }
  168. private void ItemTypeBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  169. {
  170. Properties.ItemType = (ItemType)ItemTypeBox.SelectedValue;
  171. Refresh();
  172. }
  173. private void StatusTypeBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  174. {
  175. Properties.StatusTypes = StatusTypeBox.SelectedItems.Cast<StatusType>().OrderBy(x => x).ToArray();
  176. Refresh();
  177. }
  178. #endregion
  179. private void ClearView(string message = "No Data")
  180. {
  181. NoDataLabel.Content = message;
  182. Pie.Visibility = Visibility.Collapsed;
  183. ChartRow.Height = new GridLength(0);
  184. LabelRow.Height = new GridLength(1, GridUnitType.Star);
  185. }
  186. private void ShowView()
  187. {
  188. Pie.Visibility = Visibility.Visible;
  189. ChartRow.Height = new GridLength(1, GridUnitType.Star);
  190. LabelRow.Height = new GridLength(0);
  191. }
  192. private StatusType[] GetStatusTypes()
  193. {
  194. Properties.StatusTypes ??= Enum.GetValues<StatusType>().OrderBy(x => x).ToArray();
  195. return Properties.StatusTypes;
  196. }
  197. private StatusType ConvertStatus(JobDocumentSetMileStoneStatus status)
  198. {
  199. switch (status)
  200. {
  201. case JobDocumentSetMileStoneStatus.Approved:
  202. return StatusType.Approved;
  203. case JobDocumentSetMileStoneStatus.Submitted:
  204. return StatusType.Submitted;
  205. case JobDocumentSetMileStoneStatus.Rejected:
  206. case JobDocumentSetMileStoneStatus.Cancelled:
  207. return StatusType.Failed;
  208. case JobDocumentSetMileStoneStatus.NotStarted:
  209. case JobDocumentSetMileStoneStatus.InProgress:
  210. case JobDocumentSetMileStoneStatus.OnHold:
  211. case JobDocumentSetMileStoneStatus.InfoRequired:
  212. case JobDocumentSetMileStoneStatus.Unknown:
  213. default:
  214. return StatusType.Incomplete;
  215. }
  216. }
  217. private Filter<JobDocumentSetMileStone> GetStatusFilter()
  218. {
  219. var statusTypes = GetStatusTypes();
  220. if (statusTypes.Length == 0)
  221. return new Filter<JobDocumentSetMileStone>().All();
  222. var statuses = new List<JobDocumentSetMileStoneStatus>();
  223. if (statusTypes.Contains(StatusType.Incomplete))
  224. {
  225. statuses.Add(JobDocumentSetMileStoneStatus.NotStarted);
  226. statuses.Add(JobDocumentSetMileStoneStatus.InProgress);
  227. statuses.Add(JobDocumentSetMileStoneStatus.OnHold);
  228. statuses.Add(JobDocumentSetMileStoneStatus.InfoRequired);
  229. statuses.Add(JobDocumentSetMileStoneStatus.Unknown);
  230. }
  231. if (statusTypes.Contains(StatusType.Approved))
  232. {
  233. statuses.Add(JobDocumentSetMileStoneStatus.Approved);
  234. }
  235. if (statusTypes.Contains(StatusType.Submitted))
  236. {
  237. statuses.Add(JobDocumentSetMileStoneStatus.Submitted);
  238. }
  239. if (statusTypes.Contains(StatusType.Failed))
  240. {
  241. statuses.Add(JobDocumentSetMileStoneStatus.Rejected);
  242. statuses.Add(JobDocumentSetMileStoneStatus.Cancelled);
  243. }
  244. if (statuses.Count == 0)
  245. return new Filter<JobDocumentSetMileStone>().None();
  246. //return new Filter<JobDocumentSetMileStone>(x => x.Status).InList(statuses.ToArray());
  247. var filter = new Filter<JobDocumentSetMileStone>(x => x.Status).IsEqualTo(statuses[0]);
  248. for(int i = 1; i < statuses.Count; ++i)
  249. {
  250. filter.Or(x => x.Status).IsEqualTo(statuses[i]);
  251. }
  252. return filter;
  253. }
  254. private Filter<JobDocumentSetMileStone> GetMileStoneFilter()
  255. {
  256. if (Properties.MileStones.Length == 0)
  257. return new Filter<JobDocumentSetMileStone>().All();
  258. return new Filter<JobDocumentSetMileStone>(x => x.Type.ID).InList(Properties.MileStones);
  259. }
  260. public void Refresh()
  261. {
  262. if (Properties.JobID == Guid.Empty)
  263. {
  264. ClearView("Please select a Job");
  265. return;
  266. }
  267. var statusFilter = GetStatusFilter();
  268. var milestoneFilter = GetMileStoneFilter();
  269. var columns = new Columns<JobDocumentSetMileStone>(x => x.ID)
  270. .Add(x => x.Type.ID)
  271. .Add(x => x.Status);
  272. if (Properties.ItemType == ItemType.Documents)
  273. {
  274. columns.Add(x => x.Attachments);
  275. }
  276. var milestones = new Client<JobDocumentSetMileStone>()
  277. .Query(
  278. new Filter<JobDocumentSetMileStone>(x => x.DocumentSet.Job.ID).IsEqualTo(Properties.JobID)
  279. .And(statusFilter)
  280. .And(milestoneFilter),
  281. columns);
  282. if (!milestones.Rows.Any())
  283. {
  284. ClearView();
  285. return;
  286. }
  287. var grouping = milestones
  288. .ToObjects<JobDocumentSetMileStone>()
  289. .GroupBy(x => new { x.Type.ID, Status = ConvertStatus(x.Status) })
  290. .OrderBy(x => x.Key.ID).ThenBy(x => x.Key.Status);
  291. var statusTypes = GetStatusTypes();
  292. if (statusTypes.Length == 0)
  293. statusTypes = Enum.GetValues<StatusType>();
  294. float i = 0f;
  295. var statusShades = statusTypes.ToDictionary(x => x, x => -0.5f + (++i / statusTypes.Length) * 0.5f);
  296. var data = grouping.Select(x =>
  297. {
  298. if (!MilestoneTypes.TryGetValue(x.Key.ID, out var milestoneType)) return null;
  299. if (!MileStoneColours.TryGetValue(x.Key.ID, out var milestoneColour)) return null;
  300. return new JobDocumentItemViewModel
  301. {
  302. Group = $"{milestoneType.Code} : {x.Key.Status}",
  303. Count = Properties.ItemType == ItemType.Documents ? x.Sum(x => x.Attachments) : x.Count(),
  304. MileStoneCode = milestoneType.Code,
  305. Status = x.Key.Status,
  306. Colour = new SolidColorBrush(ImageUtils.AdjustBrightness(milestoneColour, statusShades[x.Key.Status]))
  307. };
  308. }).Where(x => x is not null).Select(x => x!).ToList();
  309. Legend.ItemsSource = MilestoneTypes.Select(x =>
  310. {
  311. var colour = MileStoneColours[x.Key];
  312. return new { Text = x.Value.Code, Colour = new SolidColorBrush(colour) };
  313. }).ToList();
  314. Pie.ItemsSource = data;
  315. ShowView();
  316. }
  317. public void Shutdown()
  318. {
  319. }
  320. }
  321. }