EmployeeQualificationDashboard.xaml.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. using Comal.Classes;
  2. using InABox.Clients;
  3. using InABox.Core;
  4. using InABox.DynamicGrid;
  5. using InABox.WPF;
  6. using Microsoft.Win32;
  7. using Org.BouncyCastle.Crypto;
  8. using PRSDesktop.WidgetGroups;
  9. using Syncfusion.UI.Xaml.Grid.Helpers;
  10. using Syncfusion.UI.Xaml.ScrollAxis;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Data;
  14. using System.Globalization;
  15. using System.Linq;
  16. using System.Windows;
  17. using System.Windows.Controls;
  18. using System.Windows.Data;
  19. using System.Windows.Input;
  20. using System.Windows.Media;
  21. namespace PRSDesktop
  22. {
  23. class CellBackgroundConverter : IValueConverter
  24. {
  25. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  26. {
  27. if (value is null || value is not DateTime date)
  28. return DependencyProperty.UnsetValue;
  29. if (date == DateTime.MinValue || date == DateTime.MaxValue)
  30. return DependencyProperty.UnsetValue;
  31. var diff = date - DateTime.Now;
  32. if (diff <= TimeSpan.Zero)
  33. return new SolidColorBrush(Colors.Salmon);
  34. else if (diff.TotalDays < 7)
  35. return new SolidColorBrush(Colors.Orange);
  36. else if (diff.TotalDays < 30)
  37. return new SolidColorBrush(Colors.Yellow);
  38. return DependencyProperty.UnsetValue;
  39. }
  40. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  41. {
  42. throw new NotImplementedException();
  43. }
  44. }
  45. public class CellContentConverter : IValueConverter
  46. {
  47. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  48. {
  49. if (value is null || value is not DateTime date || date == DateTime.MinValue)
  50. return DependencyProperty.UnsetValue;
  51. if(date == DateTime.MaxValue)
  52. return "Perm.";
  53. return date.ToString("dd/MM/yy");
  54. }
  55. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  56. {
  57. throw new NotImplementedException();
  58. }
  59. }
  60. public class EmployeeQualificationDashboardProperties : IDashboardProperties
  61. {
  62. public Dictionary<Guid, bool>? Employees { get; set; } = null;
  63. public Dictionary<Guid, bool>? Qualifications { get; set; } = null;
  64. }
  65. public class EmployeeQualificationDashboardElement : DashboardElement<EmployeeQualificationDashboard, HumanResources, EmployeeQualificationDashboardProperties> { }
  66. /// <summary>
  67. /// Interaction logic for EmployeeQualificationDashboard.xaml
  68. /// </summary>
  69. public partial class EmployeeQualificationDashboard : UserControl, IDashboardWidget<HumanResources, EmployeeQualificationDashboardProperties>, IRequiresCanView<EmployeeQualification>, IActionsDashboard
  70. {
  71. private DataTable DataTable;
  72. private CoreTable CoreTable;
  73. private List<Guid> AllEmployees;
  74. private Dictionary<Guid, bool> Employees;
  75. private Dictionary<Guid, bool> Qualifications;
  76. private Dictionary<Guid, string> ColumnHeaders;
  77. private List<Guid> QualificationIDs;
  78. private List<Guid> EmployeeIDs;
  79. private Filter<Employee> EmployeeFilter { get; set; } = new Filter<Employee>().All()
  80. .And(new Filter<Employee>(x => x.StartDate).IsEqualTo(DateTime.MinValue)
  81. .Or(x => x.StartDate).IsLessThan(DateTime.Now))
  82. .And(new Filter<Employee>(x => x.FinishDate).IsEqualTo(DateTime.MinValue)
  83. .Or(x => x.FinishDate).IsGreaterThan(DateTime.Now));
  84. public EmployeeQualificationDashboardProperties Properties { get; set; }
  85. public EmployeeQualificationDashboard()
  86. {
  87. InitializeComponent();
  88. }
  89. #region CorePanel Stuff
  90. public void Refresh()
  91. {
  92. Properties.Qualifications = Qualifications;
  93. Properties.Employees = Employees;
  94. CoreTable = new();
  95. CoreTable.TableName = "Qualifications";
  96. ColumnHeaders = new();
  97. var employeeFilter = EmployeeFilter;
  98. var results = Client.QueryMultiple(
  99. new KeyedQueryDef<Employee>(nameof(Employee),
  100. new Filter<Employee>(x => x.ID).InList(Employees.Where(x => x.Value).Select(x => x.Key).ToArray()),
  101. new Columns<Employee>(x => x.ID, x => x.Name)),
  102. new KeyedQueryDef<EmployeeQualification>(nameof(EmployeeQualification),
  103. new Filter<EmployeeQualification>(x => x.Employee.ID).InQuery(employeeFilter, x => x.ID),
  104. new Columns<EmployeeQualification>(
  105. x => x.Employee.ID,
  106. x => x.Qualification.ID,
  107. x => x.Expiry)),
  108. new KeyedQueryDef<Qualification>(nameof(Qualification),
  109. new Filter<Qualification>(x => x.ID).InList(Qualifications.Where(x => x.Value).Select(x => x.Key).ToArray()),
  110. new Columns<Qualification>(x => x.ID, x => x.Description)));
  111. CoreTable.Columns.Add(new CoreColumn() { ColumnName = "Employee", DataType = typeof(string) });
  112. var columns = new List<Guid>();
  113. var qualifications = results[nameof(Qualification)];
  114. foreach (var qualification in qualifications.Rows)
  115. {
  116. var qID = qualification.Get<Qualification, Guid>(x => x.ID);
  117. if (!columns.Contains(qID))
  118. {
  119. CoreTable.Columns.Add(new CoreColumn() { ColumnName = qID.ToString(), DataType = typeof(DateTime) });
  120. columns.Add(qID);
  121. ColumnHeaders.Add(qID, qualification.Get<Qualification, string>(x => x.Description));
  122. }
  123. }
  124. QualificationIDs = columns;
  125. EmployeeIDs = new();
  126. var employeeQualifications = results[nameof(EmployeeQualification)];
  127. foreach (var employee in results[nameof(Employee)].Rows)
  128. {
  129. var employeeID = employee.Get<Employee, Guid>(x => x.ID);
  130. EmployeeIDs.Add(employeeID);
  131. List<object?> values = new()
  132. {
  133. employee["Name"]
  134. };
  135. var thisQualifications = employeeQualifications.Rows
  136. .Where(x => x.Get<EmployeeQualification, Guid>(x => x.Employee.ID) == employeeID)
  137. .ToList();
  138. foreach (var qID in columns)
  139. {
  140. var employeeQualification = thisQualifications
  141. .Where(x => x.Get<EmployeeQualification, Guid>(x => x.Qualification.ID) == qID)
  142. .FirstOrDefault();
  143. if(employeeQualification != null)
  144. {
  145. var expiry = employeeQualification.Get<EmployeeQualification, DateTime>(x => x.Expiry);
  146. if(expiry == DateTime.MinValue || expiry == DateTime.MaxValue)
  147. {
  148. values.Add(DateTime.MaxValue);
  149. }
  150. else
  151. {
  152. values.Add(expiry);
  153. }
  154. }
  155. else
  156. {
  157. values.Add(null);
  158. }
  159. }
  160. var row = CoreTable.NewRow();
  161. row.LoadValues(values);
  162. CoreTable.Rows.Add(row);
  163. }
  164. DataTable = CoreTable.ToDataTable();
  165. DataGrid.ItemsSource = DataTable;
  166. foreach(var column in CoreTable.Columns)
  167. {
  168. if(Guid.TryParse(column.ColumnName, out var qID))
  169. {
  170. column.ColumnName = ColumnHeaders[qID];
  171. }
  172. }
  173. }
  174. public static bool IsEmployeeActive(Employee employee)
  175. => (employee.StartDate == DateTime.MinValue || employee.StartDate < DateTime.Now)
  176. && (employee.FinishDate == DateTime.MinValue || employee.FinishDate > DateTime.Now);
  177. public void Setup()
  178. {
  179. var employeeFilter = EmployeeFilter;
  180. var results = Client.QueryMultiple(
  181. new KeyedQueryDef<Employee>(nameof(Employee),
  182. new Filter<Employee>().All(),
  183. new Columns<Employee>(x => x.ID, x => x.StartDate, x => x.FinishDate)),
  184. new KeyedQueryDef<Qualification>(nameof(Qualification),
  185. new Filter<Qualification>().All(),
  186. new Columns<Qualification>(x => x.ID)));
  187. var employees = results[nameof(Employee)].ToObjects<Employee>().ToDictionary(
  188. x => x.ID,
  189. x => IsEmployeeActive(x));
  190. if (Properties.Employees is not null)
  191. {
  192. employees = employees
  193. .ToDictionary(
  194. x => x.Key,
  195. x => !Properties.Employees.ContainsKey(x.Key) || Properties.Employees[x.Key]);
  196. }
  197. var qualifications = results[nameof(Qualification)].Rows.Select(x => x.Get<Qualification, Guid>(x => x.ID)).ToDictionary(x => x, x => true);
  198. if(Properties.Qualifications is not null)
  199. {
  200. qualifications = qualifications
  201. .ToDictionary(
  202. x => x.Key,
  203. x => !Properties.Qualifications.ContainsKey(x.Key) || Properties.Qualifications[x.Key]);
  204. }
  205. Employees = employees;
  206. Qualifications = qualifications;
  207. DataGrid.CellDoubleTapped += DataGrid_CellDoubleTapped;
  208. }
  209. public void Shutdown()
  210. {
  211. }
  212. #endregion
  213. #region Actions
  214. private void DoExport()
  215. {
  216. var newTable = new CoreTable() { TableName = CoreTable.TableName };
  217. newTable.LoadColumns(CoreTable.Columns);
  218. foreach (var row in CoreTable.Rows)
  219. {
  220. var newRow = newTable.NewRow();
  221. foreach (var value in row.Values)
  222. {
  223. object? newValue;
  224. if (value is DateTime date)
  225. {
  226. if (date == DateTime.MaxValue)
  227. newValue = "Permanent";
  228. else if (date == DateTime.MinValue)
  229. newValue = "";
  230. else
  231. newValue = date;
  232. }
  233. else
  234. {
  235. newValue = value;
  236. }
  237. newRow.Values.Add(newValue);
  238. }
  239. newTable.Rows.Add(newRow);
  240. }
  241. ExcelExporter.DoExport(newTable, "EmployeeQualifications");
  242. }
  243. public void BuildActionsMenu(ContextMenu menu)
  244. {
  245. if (Security.CanExport<EmployeeQualification>())
  246. {
  247. menu.AddItem("Export", null, DoExport);
  248. }
  249. }
  250. #endregion
  251. #region Grid Events
  252. private EmployeeQualification? GetCellEmployeeQualification(int row, int column)
  253. {
  254. var employeeID = EmployeeIDs[row - 1];
  255. var qualificationID = QualificationIDs[column - 1];
  256. var qualification = new Client<EmployeeQualification>()
  257. .Query(
  258. new Filter<EmployeeQualification>(x => x.Employee.ID).IsEqualTo(employeeID)
  259. .And(x => x.Qualification.ID).IsEqualTo(qualificationID));
  260. return qualification.Rows.FirstOrDefault()?.ToObject<EmployeeQualification>();
  261. }
  262. private object? GetCellData(int row, int column)
  263. {
  264. var cell = DataGrid.GetGridCellInfo(new RowColumnIndex(row, column));
  265. if (!cell.IsDataRowCell)
  266. return null;
  267. var propertyCollection = DataGrid.View.GetPropertyAccessProvider();
  268. return propertyCollection.GetValue(cell.RowData, cell.Column.MappingName);
  269. }
  270. private void DoEditQualification(int row, int column)
  271. {
  272. var qualification = GetCellEmployeeQualification(row, column);
  273. if (qualification is null)
  274. return;
  275. var grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(EmployeeQualification)) as DynamicDataGrid<EmployeeQualification>;
  276. if (grid!.EditItems(new[] { qualification }))
  277. {
  278. new Client<EmployeeQualification>().Save(qualification, "Edited by user from dashboard");
  279. Refresh();
  280. }
  281. }
  282. private void DataGrid_CellDoubleTapped(object? sender, Syncfusion.UI.Xaml.Grid.GridCellDoubleTappedEventArgs e)
  283. {
  284. var rowIndex = e.RowColumnIndex.RowIndex;
  285. var columnIndex = e.RowColumnIndex.ColumnIndex;
  286. DoEditQualification(rowIndex, columnIndex);
  287. }
  288. private void DataGrid_AutoGeneratingColumn(object sender, Syncfusion.UI.Xaml.Grid.AutoGeneratingColumnArgs e)
  289. {
  290. if (e.Column.ValueBinding is not Binding value) return;
  291. if (Guid.TryParse(e.Column.HeaderText, out var qID))
  292. {
  293. e.Column.HeaderStyle = Resources["QualificationHeaderStyle"] as Style;
  294. e.Column.HeaderText = ColumnHeaders[qID];
  295. e.Column.Width = 55;
  296. e.Column.TextAlignment = TextAlignment.Center;
  297. e.Column.ShowHeaderToolTip = true;
  298. var style = new Style();
  299. style.Setters.Add(new Setter(BackgroundProperty,
  300. new Binding(value.Path.Path) { Converter = new CellBackgroundConverter() }));
  301. e.Column.CellStyle = style;
  302. e.Column.DisplayBinding = new Binding
  303. { Path = new PropertyPath(e.Column.MappingName), Converter = new CellContentConverter() };
  304. }
  305. else
  306. {
  307. e.Column.HeaderStyle = Resources["EmployeeHeaderStyle"] as Style;
  308. }
  309. }
  310. private void PopulateCellMenu(ContextMenu menu, int row, int column)
  311. {
  312. var data = GetCellData(row, column);
  313. if (data is null || data is not DateTime)
  314. {
  315. menu.AddItem("No Qualification", null, null, false);
  316. return;
  317. }
  318. menu.AddItem("Edit Qualification", null, new Tuple<int, int>(row, column), EditQualification_Click);
  319. }
  320. private void EditQualification_Click(Tuple<int, int> obj)
  321. {
  322. DoEditQualification(obj.Item1, obj.Item2);
  323. }
  324. private void DataGrid_ContextMenuOpening(object sender, ContextMenuEventArgs e)
  325. {
  326. var vc = DataGrid.GetVisualContainer();
  327. var p = Mouse.GetPosition(vc);
  328. var rci = vc.PointToCellRowColumnIndex(p);
  329. var menu = DataGrid.ContextMenu;
  330. menu.Items.Clear();
  331. if(rci.RowIndex == 0)
  332. {
  333. var selectQualification = new MenuItem() { Header = "Select Qualifications" };
  334. selectQualification.Click += SelectQualification_Click;
  335. menu.Items.Add(selectQualification);
  336. if(rci.ColumnIndex > 0)
  337. {
  338. var column = CoreTable.Columns[rci.ColumnIndex];
  339. var hideQualification = new MenuItem() { Header = $"Hide '{column.ColumnName}'" };
  340. hideQualification.Click += (sender, e) =>
  341. {
  342. Qualifications[QualificationIDs[rci.ColumnIndex - 1]] = false;
  343. Refresh();
  344. };
  345. menu.Items.Add(hideQualification);
  346. }
  347. }
  348. if(rci.ColumnIndex == 0)
  349. {
  350. var selectEmployee = new MenuItem() { Header = "Select Employees" };
  351. selectEmployee.Click += SelectEmployee_Click;
  352. menu.Items.Add(selectEmployee);
  353. if (rci.RowIndex > 0)
  354. {
  355. var row = CoreTable.Rows[rci.RowIndex - 1];
  356. var hideEmployee = new MenuItem() { Header = $"Hide '{row["Employee"]}'" };
  357. hideEmployee.Click += (sender, e) =>
  358. {
  359. Employees[EmployeeIDs[rci.RowIndex - 1]] = false;
  360. Refresh();
  361. };
  362. menu.Items.Add(hideEmployee);
  363. }
  364. }
  365. if(rci.RowIndex > 0 && rci.ColumnIndex > 0)
  366. {
  367. PopulateCellMenu(menu, rci.RowIndex, rci.ColumnIndex);
  368. }
  369. }
  370. private void SelectEmployee_Click(object sender, RoutedEventArgs e)
  371. {
  372. var window = new EntitySelectionWindow(typeof(Employee), Employees.Where(x => x.Value).Select(x => x.Key).ToHashSet(), typeof(EmployeeSelectionGrid));
  373. window.ShowDialog();
  374. Employees = Employees.ToDictionary(
  375. x => x.Key,
  376. x => window.Entities.Contains(x.Key));
  377. Refresh();
  378. }
  379. private void SelectQualification_Click(object sender, RoutedEventArgs e)
  380. {
  381. var window = new EntitySelectionWindow(typeof(Qualification), Qualifications.Where(x => x.Value).Select(x => x.Key).ToHashSet());
  382. window.ShowDialog();
  383. Qualifications = Qualifications.ToDictionary(
  384. x => x.Key,
  385. x => window.Entities.Contains(x.Key));
  386. Refresh();
  387. }
  388. #endregion
  389. }
  390. class EmployeeSelectionGrid : EntitySelectionGrid<Employee>
  391. {
  392. public override void ConfigureColumns(DynamicGridColumns columns)
  393. {
  394. columns.Clear();
  395. columns.Add<Employee, string>(x => x.Name, 0, "Name", "", Alignment.MiddleLeft);
  396. }
  397. }
  398. }