EmployeeQualificationDashboard.xaml.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  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 List<Guid>? Employees { get; set; } = null;
  63. public List<Guid>? 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 HashSet<Guid> Employees;
  74. private HashSet<Guid> Qualifications;
  75. private Dictionary<Guid, string> ColumnHeaders;
  76. private List<Guid> QualificationIDs;
  77. private List<Guid> EmployeeIDs;
  78. private Filter<Employee> EmployeeFilter { get; set; } = new Filter<Employee>().All()
  79. .And(new Filter<Employee>(x => x.StartDate).IsEqualTo(DateTime.MinValue)
  80. .Or(x => x.StartDate).IsLessThan(DateTime.Now))
  81. .And(new Filter<Employee>(x => x.FinishDate).IsEqualTo(DateTime.MinValue)
  82. .Or(x => x.FinishDate).IsGreaterThan(DateTime.Now));
  83. public EmployeeQualificationDashboardProperties Properties { get; set; }
  84. public EmployeeQualificationDashboard()
  85. {
  86. InitializeComponent();
  87. }
  88. #region CorePanel Stuff
  89. public void Refresh()
  90. {
  91. Properties.Qualifications = Qualifications.ToList();
  92. Properties.Employees = Employees.ToList();
  93. CoreTable = new();
  94. CoreTable.TableName = "Qualifications";
  95. ColumnHeaders = new();
  96. var employeeFilter = EmployeeFilter;
  97. var results = Client.QueryMultiple(
  98. new KeyedQueryDef<Employee>(nameof(Employee),
  99. employeeFilter,
  100. new Columns<Employee>(x => x.ID, x => x.Name)),
  101. new KeyedQueryDef<EmployeeQualification>(nameof(EmployeeQualification),
  102. new Filter<EmployeeQualification>(x => x.Employee.ID).InQuery(employeeFilter, x => x.ID),
  103. new Columns<EmployeeQualification>(
  104. x => x.Employee.ID,
  105. x => x.Qualification.ID,
  106. x => x.Expiry)),
  107. new KeyedQueryDef<Qualification>(nameof(Qualification),
  108. new Filter<Qualification>(x => x.ID).InList(Qualifications.ToArray()),
  109. new Columns<Qualification>(x => x.ID, x => x.Description)));
  110. CoreTable.Columns.Add(new CoreColumn() { ColumnName = "Employee", DataType = typeof(string) });
  111. var columns = new List<Guid>();
  112. var qualifications = results[nameof(Qualification)];
  113. foreach (var qualification in qualifications.Rows)
  114. {
  115. var qID = qualification.Get<Qualification, Guid>(x => x.ID);
  116. if (!columns.Contains(qID))
  117. {
  118. CoreTable.Columns.Add(new CoreColumn() { ColumnName = qID.ToString(), DataType = typeof(DateTime) });
  119. columns.Add(qID);
  120. ColumnHeaders.Add(qID, qualification.Get<Qualification, string>(x => x.Description));
  121. }
  122. }
  123. QualificationIDs = columns;
  124. EmployeeIDs = new();
  125. var employeeQualifications = results[nameof(EmployeeQualification)];
  126. foreach (var employee in results[nameof(Employee)].Rows)
  127. {
  128. var employeeID = employee.Get<Employee, Guid>(x => x.ID);
  129. if (!Employees.Contains(employeeID))
  130. {
  131. continue;
  132. }
  133. EmployeeIDs.Add(employeeID);
  134. List<object?> values = new()
  135. {
  136. employee["Name"]
  137. };
  138. var thisQualifications = employeeQualifications.Rows
  139. .Where(x => x.Get<EmployeeQualification, Guid>(x => x.Employee.ID) == employeeID)
  140. .ToList();
  141. foreach (var qID in columns)
  142. {
  143. var employeeQualification = thisQualifications
  144. .Where(x => x.Get<EmployeeQualification, Guid>(x => x.Qualification.ID) == qID)
  145. .FirstOrDefault();
  146. if(employeeQualification != null)
  147. {
  148. var expiry = employeeQualification.Get<EmployeeQualification, DateTime>(x => x.Expiry);
  149. if(expiry == DateTime.MinValue || expiry == DateTime.MaxValue)
  150. {
  151. values.Add(DateTime.MaxValue);
  152. }
  153. else
  154. {
  155. values.Add(expiry);
  156. }
  157. }
  158. else
  159. {
  160. values.Add(null);
  161. }
  162. }
  163. var row = CoreTable.NewRow();
  164. row.LoadValues(values);
  165. CoreTable.Rows.Add(row);
  166. }
  167. DataTable = CoreTable.ToDataTable();
  168. DataGrid.ItemsSource = DataTable;
  169. foreach(var column in CoreTable.Columns)
  170. {
  171. if(Guid.TryParse(column.ColumnName, out var qID))
  172. {
  173. column.ColumnName = ColumnHeaders[qID];
  174. }
  175. }
  176. }
  177. public void Setup()
  178. {
  179. var employees = Properties.Employees?.ToHashSet();
  180. var qualifications = Properties.Qualifications?.ToHashSet();
  181. if (employees == null || qualifications == null)
  182. {
  183. var employeeFilter = EmployeeFilter;
  184. var results = Client.QueryMultiple(
  185. new KeyedQueryDef<Employee>(nameof(Employee), employeeFilter, new Columns<Employee>(x => x.ID, x => x.StartDate, x => x.FinishDate)),
  186. new KeyedQueryDef<Qualification>(nameof(Qualification),
  187. new Filter<Qualification>().All(),
  188. new Columns<Qualification>(x => x.ID)));
  189. employees ??= results[nameof(Employee)].Rows.Select(x => x.Get<Employee, Guid>(x => x.ID)).ToHashSet();
  190. qualifications ??= results[nameof(Qualification)].Rows.Select(x => x.Get<Qualification, Guid>(x => x.ID)).ToHashSet();
  191. }
  192. Employees = employees;
  193. Qualifications = qualifications;
  194. DataGrid.CellDoubleTapped += DataGrid_CellDoubleTapped;
  195. }
  196. public void Shutdown()
  197. {
  198. }
  199. #endregion
  200. #region Actions
  201. private void DoExport()
  202. {
  203. var newTable = new CoreTable() { TableName = CoreTable.TableName };
  204. newTable.LoadColumns(CoreTable.Columns);
  205. foreach (var row in CoreTable.Rows)
  206. {
  207. var newRow = newTable.NewRow();
  208. foreach (var value in row.Values)
  209. {
  210. object? newValue;
  211. if (value is DateTime date)
  212. {
  213. if (date == DateTime.MaxValue)
  214. newValue = "Permanent";
  215. else if (date == DateTime.MinValue)
  216. newValue = "";
  217. else
  218. newValue = date;
  219. }
  220. else
  221. {
  222. newValue = value;
  223. }
  224. newRow.Values.Add(newValue);
  225. }
  226. newTable.Rows.Add(newRow);
  227. }
  228. ExcelExporter.DoExport(newTable, "EmployeeQualifications");
  229. }
  230. public void BuildActionsMenu(ContextMenu menu)
  231. {
  232. if (Security.CanExport<EmployeeQualification>())
  233. {
  234. menu.AddItem("Export", null, DoExport);
  235. }
  236. }
  237. #endregion
  238. #region Grid Events
  239. private EmployeeQualification? GetCellEmployeeQualification(int row, int column)
  240. {
  241. var employeeID = EmployeeIDs[row - 1];
  242. var qualificationID = QualificationIDs[column - 1];
  243. var qualification = new Client<EmployeeQualification>()
  244. .Query(
  245. new Filter<EmployeeQualification>(x => x.Employee.ID).IsEqualTo(employeeID)
  246. .And(x => x.Qualification.ID).IsEqualTo(qualificationID));
  247. return qualification.Rows.FirstOrDefault()?.ToObject<EmployeeQualification>();
  248. }
  249. private object? GetCellData(int row, int column)
  250. {
  251. var cell = DataGrid.GetGridCellInfo(new RowColumnIndex(row, column));
  252. if (!cell.IsDataRowCell)
  253. return null;
  254. var propertyCollection = DataGrid.View.GetPropertyAccessProvider();
  255. return propertyCollection.GetValue(cell.RowData, cell.Column.MappingName);
  256. }
  257. private void DoEditQualification(int row, int column)
  258. {
  259. var qualification = GetCellEmployeeQualification(row, column);
  260. if (qualification is null)
  261. return;
  262. var grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(EmployeeQualification)) as DynamicDataGrid<EmployeeQualification>;
  263. if (grid!.EditItems(new[] { qualification }))
  264. {
  265. new Client<EmployeeQualification>().Save(qualification, "Edited by user from dashboard");
  266. Refresh();
  267. }
  268. }
  269. private void DataGrid_CellDoubleTapped(object? sender, Syncfusion.UI.Xaml.Grid.GridCellDoubleTappedEventArgs e)
  270. {
  271. var rowIndex = e.RowColumnIndex.RowIndex;
  272. var columnIndex = e.RowColumnIndex.ColumnIndex;
  273. DoEditQualification(rowIndex, columnIndex);
  274. }
  275. private void DataGrid_AutoGeneratingColumn(object sender, Syncfusion.UI.Xaml.Grid.AutoGeneratingColumnArgs e)
  276. {
  277. if (e.Column.ValueBinding is not Binding value) return;
  278. if (Guid.TryParse(e.Column.HeaderText, out var qID))
  279. {
  280. e.Column.HeaderStyle = Resources["QualificationHeaderStyle"] as Style;
  281. e.Column.HeaderText = ColumnHeaders[qID];
  282. e.Column.Width = 55;
  283. e.Column.TextAlignment = TextAlignment.Center;
  284. e.Column.ShowHeaderToolTip = true;
  285. var style = new Style();
  286. style.Setters.Add(new Setter(BackgroundProperty,
  287. new Binding(value.Path.Path) { Converter = new CellBackgroundConverter() }));
  288. e.Column.CellStyle = style;
  289. e.Column.DisplayBinding = new Binding
  290. { Path = new PropertyPath(e.Column.MappingName), Converter = new CellContentConverter() };
  291. }
  292. else
  293. {
  294. e.Column.HeaderStyle = Resources["EmployeeHeaderStyle"] as Style;
  295. }
  296. }
  297. private void PopulateCellMenu(ContextMenu menu, int row, int column)
  298. {
  299. var data = GetCellData(row, column);
  300. if (data is null || data is not DateTime)
  301. {
  302. menu.AddItem("No Qualification", null, null, false);
  303. return;
  304. }
  305. menu.AddItem("Edit Qualification", null, new Tuple<int, int>(row, column), EditQualification_Click);
  306. }
  307. private void EditQualification_Click(Tuple<int, int> obj)
  308. {
  309. DoEditQualification(obj.Item1, obj.Item2);
  310. }
  311. private void DataGrid_ContextMenuOpening(object sender, ContextMenuEventArgs e)
  312. {
  313. var vc = DataGrid.GetVisualContainer();
  314. var p = Mouse.GetPosition(vc);
  315. var rci = vc.PointToCellRowColumnIndex(p);
  316. var menu = DataGrid.ContextMenu;
  317. menu.Items.Clear();
  318. if(rci.RowIndex == 0)
  319. {
  320. var selectQualification = new MenuItem() { Header = "Select Qualifications" };
  321. selectQualification.Click += SelectQualification_Click;
  322. menu.Items.Add(selectQualification);
  323. if(rci.ColumnIndex > 0)
  324. {
  325. var column = CoreTable.Columns[rci.ColumnIndex];
  326. var hideQualification = new MenuItem() { Header = $"Hide '{column.ColumnName}'" };
  327. hideQualification.Click += (sender, e) =>
  328. {
  329. Qualifications.Remove(QualificationIDs[rci.ColumnIndex - 1]);
  330. Refresh();
  331. };
  332. menu.Items.Add(hideQualification);
  333. }
  334. }
  335. if(rci.ColumnIndex == 0)
  336. {
  337. var selectEmployee = new MenuItem() { Header = "Select Employees" };
  338. selectEmployee.Click += SelectEmployee_Click;
  339. menu.Items.Add(selectEmployee);
  340. if (rci.RowIndex > 0)
  341. {
  342. var row = CoreTable.Rows[rci.RowIndex - 1];
  343. var hideEmployee = new MenuItem() { Header = $"Hide '{row["Employee"]}'" };
  344. hideEmployee.Click += (sender, e) =>
  345. {
  346. Employees.Remove(EmployeeIDs[rci.RowIndex - 1]);
  347. Refresh();
  348. };
  349. menu.Items.Add(hideEmployee);
  350. }
  351. }
  352. if(rci.RowIndex > 0 && rci.ColumnIndex > 0)
  353. {
  354. PopulateCellMenu(menu, rci.RowIndex, rci.ColumnIndex);
  355. }
  356. }
  357. private void SelectEmployee_Click(object sender, RoutedEventArgs e)
  358. {
  359. var window = new EntitySelectionWindow(typeof(Employee), Employees, typeof(EmployeeSelectionGrid));
  360. window.ShowDialog();
  361. Employees = window.Entities;
  362. Refresh();
  363. }
  364. private void SelectQualification_Click(object sender, RoutedEventArgs e)
  365. {
  366. var window = new EntitySelectionWindow(typeof(Qualification), Qualifications);
  367. window.ShowDialog();
  368. Qualifications = window.Entities;
  369. Refresh();
  370. }
  371. #endregion
  372. }
  373. class EmployeeSelectionGrid : EntitySelectionGrid<Employee>
  374. {
  375. public override void ConfigureColumns(DynamicGridColumns columns)
  376. {
  377. columns.Clear();
  378. columns.Add<Employee, string>(x => x.Name, 0, "Name", "", Alignment.MiddleLeft);
  379. }
  380. }
  381. }