DigitalFormsDashboard.xaml.cs 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847
  1. using Comal.Classes;
  2. using InABox.Clients;
  3. using InABox.Core;
  4. using InABox.DynamicGrid;
  5. using InABox.Reports;
  6. using InABox.Core.Reports;
  7. using InABox.Scripting;
  8. using InABox.WPF;
  9. using InABox.Wpf;
  10. using PRSDesktop.Configuration;
  11. using PRSDesktop.Forms;
  12. using PRSDesktop.WidgetGroups;
  13. using Syncfusion.UI.Xaml.Grid;
  14. using Syncfusion.UI.Xaml.Grid.Converter;
  15. using Syncfusion.XlsIO;
  16. using System;
  17. using System.Collections;
  18. using System.Collections.Generic;
  19. using System.Data;
  20. using System.Diagnostics;
  21. using System.Diagnostics.CodeAnalysis;
  22. using System.Linq;
  23. using System.Linq.Expressions;
  24. using System.Reflection;
  25. using System.Text;
  26. using System.Text.RegularExpressions;
  27. using System.Threading;
  28. using System.Threading.Tasks;
  29. using System.Windows;
  30. using System.Windows.Controls;
  31. using System.Windows.Data;
  32. using System.Windows.Documents;
  33. using System.Windows.Input;
  34. using System.Windows.Media;
  35. using InABox.Configuration;
  36. using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs;
  37. using InABox.Wpf.Reports;
  38. using System.ComponentModel;
  39. using Syncfusion.Windows.Shared;
  40. using System.Globalization;
  41. using System.Windows.Media.Imaging;
  42. using System.Drawing;
  43. using Image = System.Windows.Controls.Image;
  44. using Microsoft.Win32;
  45. using Syncfusion.CompoundFile.DocIO.Net;
  46. using System.IO;
  47. using Columns = InABox.Core.Columns;
  48. using PanelAction = InABox.Wpf.PanelAction;
  49. namespace PRSDesktop;
  50. public enum DateFilterType
  51. {
  52. Today,
  53. Yesterday,
  54. Week,
  55. SevenDays,
  56. Month,
  57. ThirtyDays,
  58. Year,
  59. TwelveMonths,
  60. Custom
  61. }
  62. public class DFFilter : BaseObject
  63. {
  64. [EditorSequence(1)]
  65. [TextBoxEditor]
  66. public string Name { get; set; } = "";
  67. [EditorSequence(2)]
  68. [FilterEditor]
  69. public string Filter { get; set; } = "";
  70. }
  71. public class DigitalFormsDashboardProperties : IUserConfigurationSettings, IDashboardProperties
  72. {
  73. public bool ShowJobFilter { get; set; } = false;
  74. public bool ShowDateFilter { get; set; } = true;
  75. public bool ShowIncompleteForms { get; set; } = false;
  76. public bool UseIconsForFormTypes { get; set; } = false;
  77. public string? Category { get; set; }
  78. public Guid SelectedForm { get; set; }
  79. public Guid JobID { get; set; }
  80. public DateFilterType DateFilterType { get; set; } = DateFilterType.Today;
  81. public DateTime FromDate { get; set; }
  82. public DateTime ToDate { get; set; }
  83. public Dictionary<string, List<DFFilter>> Filters { get; set; } = new();
  84. }
  85. public class DigitalFormsDashboardElement : DashboardElement<DigitalFormsDashboard, Common, DigitalFormsDashboardProperties> { }
  86. /// <summary>
  87. /// Interaction logic for DigitalFormsDashboard.xaml
  88. /// </summary>
  89. public partial class DigitalFormsDashboard : UserControl,
  90. IDashboardWidget<Common, DigitalFormsDashboardProperties>,
  91. IBasePanel,
  92. IRequiresSecurity<CanViewDigitalFormsDashbaord>,
  93. IHeaderDashboard, IActionsDashboard
  94. {
  95. public DigitalFormsDashboardProperties Properties { get; set; }
  96. public event LoadSettings<DigitalFormsDashboardProperties>? LoadSettings;
  97. public event SaveSettings<DigitalFormsDashboardProperties>? SaveSettings;
  98. private List<DigitalForm> DigitalForms;
  99. private List<Job> Jobs;
  100. private List<Tuple<string, Type?, string>> Categories;
  101. public DashboardHeader Header { get; set; } = new();
  102. private bool IsQAForm = false;
  103. private List<QAQuestion> Questions = new();
  104. private bool IsSetup = false;
  105. public DigitalFormsDashboard()
  106. {
  107. InitializeComponent();
  108. Grid.OnGetRowStyle = Grid_OnGetRowStyle;
  109. }
  110. public void Setup()
  111. {
  112. var results = Client.QueryMultiple(
  113. new KeyedQueryDef<DigitalForm>(new Filter<DigitalForm>(x => x.Active).IsEqualTo(true)),
  114. new KeyedQueryDef<Job>(
  115. LookupFactory.DefineFilter<Job>(),
  116. Columns.None<Job>().Add(x => x.ID)
  117. .Add(x => x.JobNumber)
  118. .Add(x => x.Name)));
  119. DigitalForms = results.Get<DigitalForm>().ToObjects<DigitalForm>().ToList();
  120. var categories = new DigitalFormCategoryLookups(null);
  121. categories.OnAfterGenerateLookups += (sender, entries) => { entries.Insert(0, new LookupEntry("", "Select Category")); };
  122. Categories = categories.AsTable("AppliesTo")
  123. .Rows.Select(x =>
  124. {
  125. var appliesTo = x.Get<string>("AppliesTo");
  126. if (CategoryToType(appliesTo, out var formType, out var parentType))
  127. {
  128. return new Tuple<string, Type?, string>(appliesTo, formType, x.Get<string>("Display"));
  129. }
  130. else
  131. {
  132. return new Tuple<string, Type?, string>(appliesTo, null, x.Get<string>("Display"));
  133. }
  134. }).ToList();
  135. Grid.Categories = Categories;
  136. Jobs = results.Get<Job>().ToObjects<Job>().ToList();
  137. Jobs.Insert(0, new Job { ID = Guid.Empty, JobNumber = "ALL", Name = "All Jobs" });
  138. SetupHeader();
  139. SetupFilters();
  140. IsSetup = true;
  141. }
  142. #region Header
  143. private StackPanel CategoryButtonPanel;
  144. private Dictionary<Type, Button> CategoryButtons = new();
  145. private ComboBox CategoryBox;
  146. private ComboBox FormBox;
  147. private ComboBox JobBox;
  148. private ComboBox IncompleteFormsBox;
  149. private ComboBox DateTypeBox;
  150. private Label FromLabel;
  151. private DatePicker FromPicker;
  152. private Label ToLabel;
  153. private DatePicker ToPicker;
  154. private Button Print;
  155. private Button FilterBtn;
  156. private static Dictionary<DateFilterType, string> FilterTypes = new()
  157. {
  158. { DateFilterType.Today, "Today" },
  159. { DateFilterType.Yesterday, "Yesterday" },
  160. { DateFilterType.Week, "Week to Date" },
  161. { DateFilterType.SevenDays, "Last 7 Days" },
  162. { DateFilterType.Month, "Month to Date" },
  163. { DateFilterType.ThirtyDays, "Last 30 Days" },
  164. { DateFilterType.Year, "Year to Date" },
  165. { DateFilterType.TwelveMonths, "Last 12 Months" },
  166. { DateFilterType.Custom, "Custom" }
  167. };
  168. private static readonly SolidColorBrush EnabledBrush = new SolidColorBrush(Colors.LightYellow);
  169. private static readonly SolidColorBrush DisabledBrush = new SolidColorBrush(Colors.LightGray);
  170. private static readonly Dictionary<Type, Bitmap> CategoryImages = new()
  171. {
  172. { typeof(AssignmentForm), PRSDesktop.Resources.assignments },
  173. { typeof(KanbanForm), PRSDesktop.Resources.kanban },
  174. { typeof(JobForm), PRSDesktop.Resources.project },
  175. { typeof(JobITPForm), PRSDesktop.Resources.checklist },
  176. { typeof(EmployeeForm), PRSDesktop.Resources.employees },
  177. { typeof(LeaveRequestForm), PRSDesktop.Resources.leave },
  178. { typeof(ManufacturingPacketStage), PRSDesktop.Resources.factory },
  179. { typeof(TimeSheetForm), PRSDesktop.Resources.time },
  180. { typeof(PurchaseOrderItemForm), PRSDesktop.Resources.purchase },
  181. { typeof(DeliveryForm), PRSDesktop.Resources.truck },
  182. };
  183. public void SetupHeader()
  184. {
  185. CategoryBox = new ComboBox
  186. {
  187. Width = 150,
  188. VerticalContentAlignment = VerticalAlignment.Center,
  189. Margin = new Thickness(0, 0, 5, 0)
  190. };
  191. CategoryBox.ItemsSource = Categories;
  192. CategoryBox.SelectedValuePath = "Item1";
  193. CategoryBox.DisplayMemberPath = "Item3";
  194. CategoryBox.SelectedValue = Properties.Category;
  195. CategoryBox.SelectionChanged += Category_SelectionChanged;
  196. CategoryButtonPanel = new StackPanel
  197. {
  198. Orientation = Orientation.Horizontal,
  199. Margin = new Thickness(0, 0, 5, 0)
  200. };
  201. CategoryButtons.Clear();
  202. foreach(var (appliesTo, category, display) in Categories)
  203. {
  204. if(category is null)
  205. {
  206. continue;
  207. }
  208. var button = new Button();
  209. button.Tag = appliesTo;
  210. button.Margin = new Thickness(0, 0, 2, 0);
  211. button.BorderBrush = new SolidColorBrush(Colors.Gray);
  212. button.BorderThickness = new Thickness(0.75);
  213. button.Width = 25D;
  214. button.Padding = new Thickness(2);
  215. button.ToolTip = category.EntityName().Split('.').Last().SplitCamelCase();
  216. if (CategoryImages.TryGetValue(category, out var image))
  217. {
  218. button.Content = new Image { Source = image.AsBitmapImage() };
  219. }
  220. else
  221. {
  222. button.Content = display;
  223. }
  224. button.Click += CatagoryButton_Click;
  225. CategoryButtons.Add(category, button);
  226. CategoryButtonPanel.Children.Add(button);
  227. }
  228. if (Properties.UseIconsForFormTypes)
  229. {
  230. CategoryButtonPanel.Visibility = Visibility.Visible;
  231. CategoryBox.Visibility = Visibility.Collapsed;
  232. }
  233. else
  234. {
  235. CategoryButtonPanel.Visibility = Visibility.Collapsed;
  236. CategoryBox.Visibility = Visibility.Visible;
  237. }
  238. FormBox = new ComboBox
  239. {
  240. Width = 250,
  241. VerticalContentAlignment = VerticalAlignment.Center,
  242. Margin = new Thickness(0, 0, 5, 0),
  243. IsEnabled = false
  244. };
  245. FormBox.SelectionChanged += FormBox_SelectionChanged;
  246. FormBox.ItemsSource = new Dictionary<DigitalForm, string> { };
  247. JobBox = new ComboBox
  248. {
  249. Width = 250,
  250. Margin = new Thickness(0, 0, 5, 0),
  251. VerticalContentAlignment = VerticalAlignment.Center
  252. };
  253. JobBox.ItemsSource = Jobs.ToDictionary(x => x.ID, x => $"{x.JobNumber} : {x.Name}");
  254. JobBox.SelectedIndex = 0;
  255. JobBox.SelectedValuePath = "Key";
  256. JobBox.DisplayMemberPath = "Value";
  257. JobBox.SelectionChanged += JobBox_SelectionChanged;
  258. DateTypeBox = new ComboBox
  259. {
  260. Width = 120,
  261. VerticalContentAlignment = VerticalAlignment.Center
  262. };
  263. DateTypeBox.ItemsSource = FilterTypes;
  264. DateTypeBox.SelectedValuePath = "Key";
  265. DateTypeBox.DisplayMemberPath = "Value";
  266. DateTypeBox.SelectedValue = Properties.DateFilterType;
  267. DateTypeBox.SelectionChanged += DateTypeBox_SelectionChanged;
  268. FromLabel = new Label { Content = "From", VerticalContentAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 5, 0) };
  269. FromPicker = new DatePicker
  270. {
  271. Width = 100,
  272. Background = new SolidColorBrush(Colors.LightYellow),
  273. VerticalContentAlignment = VerticalAlignment.Center,
  274. FirstDayOfWeek = DayOfWeek.Monday,
  275. Margin = new Thickness(0, 0, 5, 0)
  276. };
  277. FromPicker.SelectedDateChanged += FromPicker_SelectedDateChanged;
  278. ToLabel = new Label { Content = "To", VerticalContentAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 5, 0) };
  279. ToPicker = new DatePicker
  280. {
  281. Width = 100,
  282. Background = new SolidColorBrush(Colors.LightYellow),
  283. VerticalContentAlignment = VerticalAlignment.Center,
  284. FirstDayOfWeek = DayOfWeek.Monday,
  285. Margin = new Thickness(0, 0, 5, 0)
  286. };
  287. ToPicker.SelectedDateChanged += ToPicker_SelectedDateChanged;
  288. IncompleteFormsBox = new ComboBox
  289. {
  290. Width = 130,
  291. Margin = new Thickness(0, 0, 5, 0),
  292. VerticalContentAlignment = VerticalAlignment.Center
  293. };
  294. IncompleteFormsBox.ItemsSource = new List<Tuple<string, bool>>
  295. {
  296. new Tuple<string, bool>("Completed Forms", false),
  297. new Tuple<string, bool>("All Forms", true)
  298. };
  299. IncompleteFormsBox.DisplayMemberPath = "Item1";
  300. IncompleteFormsBox.SelectedValuePath = "Item2";
  301. IncompleteFormsBox.SelectedValue = Properties.ShowIncompleteForms;
  302. IncompleteFormsBox.SelectionChanged += IncompleteFormsBox_SelectionChanged;
  303. Print = new Button
  304. {
  305. Width = 25,
  306. Height = 25,
  307. Content = new Image { Source = PRSDesktop.Resources.printer.AsBitmapImage() }
  308. };
  309. Print.Click += Print_Click;
  310. FilterBtn = new Button
  311. {
  312. Width = 25,
  313. Height = 25,
  314. Content = new Image { Source = InABox.Wpf.Resources.filter.AsBitmapImage() },
  315. Margin = new Thickness(0, 0, 5, 0)
  316. };
  317. FilterBtn.Click += Filter_Click;
  318. Header.BeginUpdate()
  319. .Clear()
  320. .Add(CategoryBox)
  321. .Add(CategoryButtonPanel)
  322. .Add(FormBox)
  323. .Add(JobBox)
  324. .Add(DateTypeBox)
  325. .Add(FromLabel)
  326. .Add(FromPicker)
  327. .Add(ToLabel)
  328. .Add(ToPicker)
  329. .Add(IncompleteFormsBox)
  330. .AddRight(FilterBtn)
  331. .AddRight(Print)
  332. .EndUpdate();
  333. UpdateCategory(Properties.Category);
  334. }
  335. private void IncompleteFormsBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  336. {
  337. Properties.ShowIncompleteForms = (bool)IncompleteFormsBox.SelectedValue;
  338. Refresh();
  339. }
  340. private void Filter_Click(object sender, RoutedEventArgs e)
  341. {
  342. var menu = new ContextMenu();
  343. menu.AddCheckItem("Use Form Type Icons", ToggleFormTypeIcons, isChecked: Properties.UseIconsForFormTypes);
  344. menu.AddCheckItem("Show Date Filter", ToggleDateFilter, Properties.ShowDateFilter);
  345. menu.AddCheckItem("Show Job Filter", ToggleJobFilter, Properties.ShowJobFilter);
  346. menu.AddSeparator();
  347. if (ParentType is not null)
  348. {
  349. if (Properties.Filters.TryGetValue(ParentType.Name, out var filters))
  350. {
  351. var i = 0;
  352. var items = new List<MenuItem>();
  353. foreach (var filter in filters)
  354. {
  355. items.Add(menu.AddCheckItem(
  356. filter.Name,
  357. new Tuple<int, string, List<MenuItem>>(i, filter.Filter, items),
  358. ExecuteFilter,
  359. i == CustomFilterIndex));
  360. ++i;
  361. }
  362. }
  363. menu.AddSeparatorIfNeeded();
  364. menu.AddItem("Manage Filters", InABox.Wpf.Resources.filter, ManageFilters_Click);
  365. }
  366. menu.IsOpen = true;
  367. }
  368. private void ToggleFormTypeIcons(bool isChecked)
  369. {
  370. Properties.UseIconsForFormTypes = !Properties.UseIconsForFormTypes;
  371. if (Properties.UseIconsForFormTypes)
  372. {
  373. CategoryButtonPanel.Visibility = Visibility.Visible;
  374. CategoryBox.Visibility = Visibility.Collapsed;
  375. }
  376. else
  377. {
  378. CategoryButtonPanel.Visibility = Visibility.Collapsed;
  379. CategoryBox.Visibility = Visibility.Visible;
  380. }
  381. }
  382. private void Print_Click(object sender, RoutedEventArgs e)
  383. {
  384. var menu = new ContextMenu();
  385. foreach (var report in ReportUtils.LoadReports(SectionName, DataModel(Selection.None)))
  386. {
  387. menu.AddItem(report.Name, PRSDesktop.Resources.printer, report, PrintReport_Click);
  388. }
  389. if (Security.IsAllowed<CanDesignReports>())
  390. {
  391. menu.AddSeparatorIfNeeded();
  392. menu.AddItem("Manage Reports", PRSDesktop.Resources.printer, ManageReports_Click);
  393. }
  394. menu.IsOpen = true;
  395. }
  396. private void PrintReport_Click(ReportTemplate obj)
  397. {
  398. Selection selection;
  399. if (obj.SelectedRecords && obj.AllRecords)
  400. selection = RecordSelectionDialog.Execute();
  401. else if (obj.SelectedRecords)
  402. selection = Selection.Selected;
  403. else if (obj.AllRecords)
  404. selection = Selection.All;
  405. else
  406. selection = Selection.None;
  407. ReportUtils.PreviewReport(obj, DataModel(selection), false, Security.IsAllowed<CanDesignReports>());
  408. }
  409. private void ManageReports_Click()
  410. {
  411. var manager = new ReportManager()
  412. {
  413. DataModel = DataModel(Selection.None),
  414. Section = SectionName,
  415. Populate = true
  416. };
  417. manager.ShowDialog();
  418. }
  419. private void Search_KeyUp(object sender, KeyEventArgs e)
  420. {
  421. Refresh();
  422. }
  423. private void JobBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  424. {
  425. Properties.JobID = (Guid)JobBox.SelectedValue;
  426. Refresh();
  427. }
  428. private void SetDateFilterVisibility(bool visible)
  429. {
  430. var visibility = visible ? Visibility.Visible : Visibility.Collapsed;
  431. FromLabel.Visibility = visibility;
  432. FromPicker.Visibility = visibility;
  433. ToLabel.Visibility = visibility;
  434. ToPicker.Visibility = visibility;
  435. DateTypeBox.Visibility = visibility;
  436. }
  437. private void SetJobFilterVisibility(bool visible)
  438. {
  439. var visibility = visible ? Visibility.Visible : Visibility.Collapsed;
  440. JobBox.Visibility = visibility;
  441. }
  442. private void DateTypeBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  443. {
  444. var filterType = (DateFilterType)DateTypeBox.SelectedValue;
  445. Properties.DateFilterType = filterType;
  446. if (filterType == DateFilterType.Custom)
  447. {
  448. if (FromPicker.SelectedDate == null || FromPicker.SelectedDate == DateTime.MinValue)
  449. {
  450. Properties.FromDate = DateTime.Today;
  451. }
  452. else
  453. {
  454. Properties.FromDate = FromPicker.SelectedDate.Value;
  455. }
  456. if (ToPicker.SelectedDate == null || ToPicker.SelectedDate == DateTime.MinValue)
  457. {
  458. Properties.ToDate = DateTime.Today;
  459. }
  460. else
  461. {
  462. Properties.ToDate = ToPicker.SelectedDate.Value;
  463. }
  464. }
  465. SetupDateFilters();
  466. Refresh();
  467. }
  468. private void FromPicker_SelectedDateChanged(object? sender, SelectionChangedEventArgs e)
  469. {
  470. Properties.FromDate = FromPicker.SelectedDate ?? DateTime.Today;
  471. Refresh();
  472. }
  473. private void ToPicker_SelectedDateChanged(object? sender, SelectionChangedEventArgs e)
  474. {
  475. Properties.ToDate = ToPicker.SelectedDate ?? DateTime.Today;
  476. Refresh();
  477. }
  478. private bool _changing = false;
  479. private void UpdateCategory(string? category)
  480. {
  481. _changing = true;
  482. Properties.Category = category;
  483. SetCategory(Properties.Category);
  484. foreach(var (type, button) in CategoryButtons)
  485. {
  486. if(type == FormType)
  487. {
  488. button.Background = EnabledBrush;
  489. }
  490. else
  491. {
  492. button.Background = DisabledBrush;
  493. }
  494. }
  495. var jobLink = FormType is not null ? GetJobLink("", FormType,true) : "";
  496. if (string.IsNullOrWhiteSpace(jobLink))
  497. {
  498. var jobID = Properties.JobID;
  499. JobBox.SelectedValue = Guid.Empty;
  500. JobBox.IsEnabled = false;
  501. Properties.JobID = jobID;
  502. }
  503. else
  504. {
  505. JobBox.SelectedValue = Properties.JobID;
  506. JobBox.IsEnabled = true;
  507. }
  508. string changeableJobLink = "";
  509. string changeableScopeLink = "";
  510. if ((FormType != null) && ChangeableLinks.TryGetValue(FormType, out Tuple<string,string> _tuple))
  511. {
  512. changeableJobLink = _tuple.Item1;
  513. changeableScopeLink = _tuple.Item2;
  514. }
  515. if (_jobAction != null)
  516. _jobAction.Visibility = string.IsNullOrWhiteSpace(changeableJobLink) ? Visibility.Collapsed : Visibility.Visible;
  517. if (_jobScopeAction != null)
  518. _jobScopeAction.Visibility = string.IsNullOrWhiteSpace(changeableScopeLink) ? Visibility.Collapsed : Visibility.Visible;
  519. if (ParentType is null)
  520. {
  521. FormBox.IsEnabled = false;
  522. FormBox.ItemsSource = new Dictionary<DigitalForm, string> { };
  523. }
  524. else
  525. {
  526. var forms = DigitalForms.Where(x => x.AppliesTo == ParentType.Name).ToList();
  527. forms.Insert(0, new DigitalForm { ID = Guid.Empty, Description = "Select Form" });
  528. FormBox.ItemsSource = forms;
  529. if (Properties.SelectedForm != Guid.Empty && forms.Where(x => x.ID == Properties.SelectedForm).FirstOrDefault() is DigitalForm form)
  530. {
  531. FormBox.SelectedItem = form;
  532. }
  533. else
  534. {
  535. FormBox.SelectedIndex = 0;
  536. }
  537. FormBox.DisplayMemberPath = "Description";
  538. FormBox.IsEnabled = true;
  539. }
  540. _changing = false;
  541. OnUpdateDataModel?.Invoke(SectionName, DataModel(Selection.None));
  542. }
  543. private void CatagoryButton_Click(object sender, RoutedEventArgs e)
  544. {
  545. UpdateCategory(((sender as Button)!.Tag as string)!);
  546. Refresh();
  547. }
  548. private void Category_SelectionChanged(object sender, SelectionChangedEventArgs e)
  549. {
  550. UpdateCategory((CategoryBox.SelectedValue as string)!);
  551. Refresh();
  552. }
  553. private void FormBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  554. {
  555. Form = FormBox.SelectedValue as DigitalForm;
  556. Properties.SelectedForm = Form?.ID ?? Guid.Empty;
  557. if (!_changing)
  558. {
  559. OnUpdateDataModel?.Invoke(SectionName, DataModel(Selection.None));
  560. }
  561. Refresh();
  562. }
  563. #endregion
  564. #region IBasePanel
  565. public bool IsReady { get; set; }
  566. public event DataModelUpdateEvent? OnUpdateDataModel;
  567. private PanelAction? _jobAction = null;
  568. private PanelAction? _jobScopeAction = null;
  569. public void CreateToolbarButtons(IPanelHost host)
  570. {
  571. host.CreatePanelAction(new PanelAction("Export Forms", PRSDesktop.Resources.disk, action => SaveToFolder_Click()));
  572. _jobAction ??= new PanelAction("Set Job Number", PRSDesktop.Resources.project, SetJobNumber);
  573. host.CreatePanelAction(_jobAction);
  574. _jobScopeAction ??= new PanelAction(
  575. "Set Job Scope",
  576. PRSDesktop.Resources.project,
  577. null,
  578. LoadJobScopes
  579. );
  580. host.CreatePanelAction(_jobScopeAction);
  581. }
  582. private IPanelActionEntry[] LoadJobScopes(PanelAction arg)
  583. {
  584. List<IPanelActionEntry> result = new();
  585. var linkcolumn = GetJobLink("", FormType, true);
  586. if (!string.IsNullOrWhiteSpace(linkcolumn))
  587. {
  588. linkcolumn = $"{linkcolumn}.ID";
  589. var jobid = Grid.SelectedRows.Select(x => x[linkcolumn]).FirstOrDefault();
  590. if (jobid != null && !Guid.Equals(jobid, Guid.Empty))
  591. {
  592. var scopes =Client.Query<JobScope>(
  593. new Filter<JobScope>(x => x.Job.ID).IsEqualTo((Guid)jobid),
  594. Columns.None<JobScope>()
  595. .Add(x => x.ID)
  596. .Add(x => x.Number)
  597. .Add(x => x.Description)
  598. ).ToObjects<JobScope>();
  599. foreach (var scope in scopes)
  600. {
  601. var entry = new PanelActionEntry<Guid>($"{scope.Number}: {scope.Description}",
  602. PRSDesktop.Resources.project, scope.ID, SetJobScope);
  603. result.Add(entry);
  604. }
  605. }
  606. }
  607. return result.ToArray();
  608. }
  609. private void SetJobScope(PanelActionEntry<Guid> scopeid)
  610. {
  611. Type? _type = FormType == typeof(JobForm)
  612. ? FormType
  613. : ParentType;
  614. if (_type == null)
  615. return;
  616. string _idCol = FormType == typeof(JobForm)
  617. ? "ID"
  618. : "Parent.ID";
  619. var _ids = Grid.SelectedRows.Select(x => x[_idCol]).OfType<Guid>().Distinct().ToArray();
  620. Progress.ShowModal("Updating Job Scopes", progress =>
  621. {
  622. var _updates = ClientFactory.CreateClient(_type)
  623. .Query(
  624. Filter.Create(_type, "ID", Operator.InList, _ids),
  625. Columns.Create(_type, ColumnTypeFlags.Required)
  626. ).ToObjects(_type).ToArray();
  627. var _scopelink = GetJobScopeLink("", _type, FormType == typeof(JobForm) ? 0 : 1);
  628. foreach (var _update in _updates)
  629. CoreUtils.SetPropertyValue(_update, _scopelink + ".ID", scopeid.Data);
  630. ClientFactory.CreateClient(_type).Save(_updates, "Updated Job Number");
  631. });
  632. Refresh();
  633. }
  634. private void SetJobNumber(PanelAction obj)
  635. {
  636. var dlg = new MultiSelectDialog<Job>(
  637. LookupFactory.DefineFilter<Job>(),
  638. Columns.None<Job>()
  639. .Add(x => x.ID)
  640. .Add(x => x.JobNumber)
  641. .Add(x => x.Name)
  642. , false
  643. );
  644. if (dlg.ShowDialog())
  645. {
  646. Guid _jobID = dlg.IDs().FirstOrDefault();
  647. Type? _type = FormType == typeof(JobForm)
  648. ? FormType
  649. : ParentType;
  650. if (_type == null)
  651. return;
  652. string _idCol = FormType == typeof(JobForm)
  653. ? "ID"
  654. : "Parent.ID";
  655. var _ids = Grid.SelectedRows.Select(x => x[_idCol]).OfType<Guid>().Distinct().ToArray();
  656. Progress.ShowModal("Updating Job Numbers", progress =>
  657. {
  658. var _updates = ClientFactory.CreateClient(_type)
  659. .Query(
  660. Filter.Create(_type, "ID", Operator.InList, _ids),
  661. Columns.Create(_type, ColumnTypeFlags.Required)
  662. ).ToObjects(_type).ToArray();
  663. var _joblink = GetJobLink("", _type, false);
  664. foreach (var _update in _updates)
  665. CoreUtils.SetPropertyValue(_update, _joblink + ".ID", _jobID);
  666. ClientFactory.CreateClient(_type).Save(_updates, "Updated Job Number");
  667. });
  668. Refresh();
  669. }
  670. }
  671. public void Heartbeat(TimeSpan time)
  672. {
  673. }
  674. public Dictionary<string, object[]> Selected()
  675. {
  676. return new Dictionary<string, object[]>();
  677. }
  678. #endregion
  679. public string SectionName
  680. {
  681. get
  682. {
  683. if (Form is null || Form.ID == Guid.Empty)
  684. return "Digital Forms";
  685. return Form.ID.ToString() ?? "Digital Forms";
  686. }
  687. }
  688. public DataModel DataModel(Selection selection)
  689. {
  690. if (FormType is null || Form is null || Form.ID == Guid.Empty)
  691. {
  692. return new AutoDataModel<DigitalForm>(new Filter<DigitalForm>().None());
  693. }
  694. IFilter filter;
  695. switch (selection)
  696. {
  697. case Selection.Selected:
  698. var formids = Grid.SelectedRows.ToArray(x => x.Get<Guid>("ID"));
  699. filter = Filter.Create<Entity>(FormType, x => x.ID).InList(formids);
  700. break;
  701. case Selection.All:
  702. filter = Filter.Create(FormType).All();
  703. break;
  704. case Selection.None:
  705. default:
  706. filter = Filter.Create(FormType).None();
  707. break;
  708. }
  709. return (Activator.CreateInstance(typeof(DigitalFormReportDataModel<>)!
  710. .MakeGenericType(FormType), new object?[] { filter, Form.ID }) as DataModel)!;
  711. }
  712. public void BuildActionsMenu(ContextMenu menu)
  713. {
  714. menu.AddItem("Export", InABox.Wpf.Resources.doc_xls, Export_Click, Form is not null && Form.ID != Guid.Empty);
  715. var loadingModules = menu.AddItem("Loading Modules...", null, null, false);
  716. Task.Run(() =>
  717. {
  718. return CustomModuleUtils.LoadCustomModuleThumbnails(SectionName, DataModel(Selection.None));
  719. }).ContinueWith((task) =>
  720. {
  721. var modules = task.Result;
  722. var index = menu.Items.IndexOf(loadingModules);
  723. menu.Items.RemoveAt(index);
  724. foreach (var (module, image) in modules)
  725. {
  726. menu.AddItem(module.Name, image, module, ExecuteModule_Click, index: index);
  727. ++index;
  728. }
  729. }, TaskScheduler.FromCurrentSynchronizationContext());
  730. if (Security.IsAllowed<CanCustomiseModules>())
  731. {
  732. menu.AddSeparatorIfNeeded();
  733. menu.AddItem("Manage Modules", PRSDesktop.Resources.script, ManageModules_Click);
  734. }
  735. }
  736. private void SaveToFolder_Click()
  737. {
  738. if (Form is null || FormType is null)
  739. {
  740. MessageWindow.ShowMessage("Please select a form first.", "Select form");
  741. return;
  742. }
  743. var model = DataModel(Selection.None);
  744. var reports = ReportUtils.LoadReports(Form.ID.ToString(), model).Where(x => x.Visible).ToList();
  745. var method = typeof(DigitalFormsDashboard).GetMethod("SaveToFolder", BindingFlags.Instance | BindingFlags.NonPublic)!.MakeGenericMethod(FormType);
  746. if (reports.Count == 0)
  747. {
  748. MessageWindow.ShowMessage("No reports are currently defined for this Digital Form!", "No report found");
  749. return;
  750. }
  751. if(reports.Count == 1)
  752. {
  753. method.Invoke(this, new object[] { reports[0] });
  754. return;
  755. }
  756. var menu = new ContextMenu();
  757. foreach (var report in reports)
  758. menu.AddItem(report.Name, null, report, r => method.Invoke(this, new[] { r }));
  759. menu.IsOpen = true;
  760. }
  761. private void SaveToFolder<TForm>(ReportTemplate report)
  762. where TForm : Entity, IDigitalFormInstance, IRemotable, IPersistent, new()
  763. {
  764. using (var dialog = new System.Windows.Forms.FolderBrowserDialog())
  765. {
  766. var result = dialog.ShowDialog();
  767. if(result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(dialog.SelectedPath))
  768. {
  769. var data = Grid.SelectedRows;
  770. Progress.ShowModal("Saving forms", progress =>
  771. {
  772. foreach (var row in data)
  773. {
  774. var id = row.Get<Guid>("ID");
  775. var number = row.Get<string>("Number");
  776. progress.Report($"Saving form {number}");
  777. var dataModel = new DigitalFormReportDataModel<TForm>(
  778. new Filter<TForm>(x => x.ID).IsEqualTo(id),
  779. Form!.ID);
  780. var pdfData = ReportUtils.ReportToPDF(report, dataModel, true);
  781. var expr = dataModel.EvaluateExpression(Form.ExportExpression)?.Trim();
  782. var filename = String.IsNullOrWhiteSpace(expr)
  783. ? number
  784. : $"{number} - {CoreUtils.SanitiseFileName(expr)}";
  785. File.WriteAllBytes(Path.Combine(dialog.SelectedPath, Path.ChangeExtension(filename, ".pdf")), pdfData);
  786. }
  787. });
  788. Process.Start("explorer.exe" , dialog.SelectedPath);
  789. }
  790. }
  791. }
  792. private void Export_Click()
  793. {
  794. // var formName = Regex.Replace(Form?.Description ?? "", "[^ a-zA-Z0-9]", "");
  795. // var filename = string.Format("{0} - {1} - {2:yyyy-MM-dd} - {3:yyyy-MM-dd}.xlsx", ParentType!.Name, formName, From, To);
  796. // var options = new ExcelExportingOptions();
  797. // options.ExcelVersion = ExcelVersion.Excel2013;
  798. // options.ExportStackedHeaders = true;
  799. // var excelEngine = DataGrid.ExportToExcel(DataGrid.View, options);
  800. // var workBook = excelEngine.Excel.Workbooks[0];
  801. // var sheet = workBook.Worksheets[0];
  802. // sheet.Name = "Summary";
  803. // sheet.UsedRange.AutofitRows();
  804. // sheet.UsedRange.AutofitColumns();
  805. // sheet = workBook.Worksheets.Create("Questions");
  806. // sheet.Move(0);
  807. // var questions = new Client<QAQuestion>().Query(new Filter<QAQuestion>(x => x.Form.ID).IsEqualTo(Form!.ID));
  808. // sheet.Range[1, 1].Text = Form?.Description ?? "";
  809. // sheet.Range[1, 1, 1, 3].Merge();
  810. // var i = 1;
  811. // foreach (var row in questions.Rows)
  812. // if (!row.Get<QAQuestion, QAAnswer>(x => x.Answer).Equals(QAAnswer.Comment))
  813. // {
  814. // sheet.Range[i + 2, 1].Text = string.Format("{0}.", i);
  815. // sheet.Range[i + 2, 2].Text = string.Format("{0}", row.Get<QAQuestion, string>(x => x.Question));
  816. // sheet.Range[i + 2, 3].Text = string.Format("[{0}]", row.Get<QAQuestion, string>(x => x.Code));
  817. // i++;
  818. // }
  819. // sheet.UsedRange.AutofitRows();
  820. // sheet.UsedRange.AutofitColumns();
  821. // try
  822. // {
  823. // workBook.SaveAs(filename);
  824. // var startInfo = new ProcessStartInfo(filename);
  825. // startInfo.Verb = "open";
  826. // startInfo.UseShellExecute = true;
  827. // Process.Start(startInfo);
  828. // }
  829. // catch
  830. // {
  831. // MessageBox.Show(string.Format("Unable to Save/Launch [{0}]!\n\nIs the file already open?", filename));
  832. // }
  833. }
  834. private void ManageFilters_Click()
  835. {
  836. var filters = Properties.Filters.GetValueOrDefault(ParentType!.Name) ?? new List<DFFilter>();
  837. var gridFilters = new CoreFilterDefinitions();
  838. gridFilters.AddRange(filters.Select(x => new CoreFilterDefinition { Name = x.Name, Filter = x.Filter }));
  839. var grid = new DynamicGridFilterEditor(gridFilters, FormType!);
  840. if (grid.ShowDialog() == true)
  841. {
  842. Properties.Filters[ParentType!.Name] = grid.Filters.Select(x => new DFFilter { Name = x.Name, Filter = x.Filter }).ToList();
  843. if (CustomFilterIndex != null)
  844. {
  845. Refresh();
  846. }
  847. }
  848. }
  849. private void ExecuteFilter(Tuple<int, string, List<MenuItem>> tag, bool isChecked)
  850. {
  851. var (index, filter, items) = tag;
  852. if (isChecked)
  853. {
  854. var i = 0;
  855. foreach (var item in items)
  856. {
  857. item.IsChecked = i == index;
  858. ++i;
  859. }
  860. }
  861. if (isChecked)
  862. {
  863. CustomFilter = Serialization.Deserialize(typeof(Filter<>).MakeGenericType(FormType!), filter) as IFilter;
  864. CustomFilterIndex = index;
  865. Refresh();
  866. }
  867. else if (index == CustomFilterIndex)
  868. {
  869. CustomFilter = null;
  870. CustomFilterIndex = null;
  871. Refresh();
  872. }
  873. }
  874. private void ExecuteModule_Click(CustomModule obj)
  875. {
  876. if (!string.IsNullOrWhiteSpace(obj.Script))
  877. try
  878. {
  879. Selection selection;
  880. if (obj.SelectedRecords && obj.AllRecords)
  881. selection = RecordSelectionDialog.Execute();
  882. else if (obj.SelectedRecords)
  883. selection = Selection.Selected;
  884. else if (obj.AllRecords)
  885. selection = Selection.All;
  886. else
  887. selection = Selection.None;
  888. var result = ScriptDocument.RunCustomModule(DataModel(selection), new Dictionary<string, object[]>(), obj.Script);
  889. if (result)
  890. Refresh();
  891. }
  892. catch (CompileException c)
  893. {
  894. MessageBox.Show(c.Message);
  895. }
  896. catch (Exception err)
  897. {
  898. MessageBox.Show(CoreUtils.FormatException(err));
  899. }
  900. else
  901. MessageBox.Show("Unable to load " + obj.Name);
  902. }
  903. private void ManageModules_Click()
  904. {
  905. var section = SectionName;
  906. var dataModel = DataModel(Selection.Selected);
  907. var manager = new CustomModuleManager()
  908. {
  909. Section = section,
  910. DataModel = dataModel
  911. };
  912. manager.ShowDialog();
  913. }
  914. private void ToggleDateFilter(bool isChecked)
  915. {
  916. Properties.ShowDateFilter = isChecked;
  917. SetDateFilterVisibility(Properties.ShowDateFilter);
  918. }
  919. private void ToggleJobFilter(bool isChecked)
  920. {
  921. Properties.ShowJobFilter = isChecked;
  922. SetJobFilterVisibility(Properties.ShowJobFilter);
  923. Refresh();
  924. }
  925. #region Filtering
  926. private DateTime From { get; set; }
  927. private DateTime To { get; set; }
  928. private bool IsEntityForm { get; set; }
  929. private Type? _parentType;
  930. private Type? ParentType
  931. {
  932. get => _parentType;
  933. set
  934. {
  935. _parentType = value;
  936. Grid.ParentType = value;
  937. }
  938. }
  939. private Type? _formType;
  940. private Type? FormType
  941. {
  942. get => _formType;
  943. set
  944. {
  945. _formType = value;
  946. Grid.FormType = value;
  947. }
  948. }
  949. private DigitalForm? Form { get; set; }
  950. private IFilter? CustomFilter { get; set; }
  951. private int? CustomFilterIndex { get; set; }
  952. private static int WeekDay(DateTime date)
  953. {
  954. if (date.DayOfWeek == DayOfWeek.Sunday)
  955. return 7;
  956. return (int)date.DayOfWeek - 1;
  957. }
  958. private void SetupDateFilters()
  959. {
  960. switch (Properties.DateFilterType)
  961. {
  962. case DateFilterType.Today:
  963. From = DateTime.Today;
  964. To = DateTime.Today;
  965. break;
  966. case DateFilterType.Yesterday:
  967. From = DateTime.Today.AddDays(-1);
  968. To = DateTime.Today.AddDays(-1);
  969. break;
  970. case DateFilterType.Week:
  971. From = DateTime.Today.AddDays(-WeekDay(DateTime.Today));
  972. To = DateTime.Today;
  973. break;
  974. case DateFilterType.SevenDays:
  975. From = DateTime.Today.AddDays(-6);
  976. To = DateTime.Today;
  977. break;
  978. case DateFilterType.Month:
  979. From = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
  980. To = DateTime.Today;
  981. break;
  982. case DateFilterType.ThirtyDays:
  983. From = DateTime.Today.AddDays(-29);
  984. To = DateTime.Today;
  985. break;
  986. case DateFilterType.Year:
  987. From = new DateTime(DateTime.Today.Year, 1, 1);
  988. To = DateTime.Today;
  989. break;
  990. case DateFilterType.TwelveMonths:
  991. From = DateTime.Today.AddYears(-1).AddDays(1);
  992. To = DateTime.Today;
  993. break;
  994. case DateFilterType.Custom:
  995. From = Properties.FromDate;
  996. To = Properties.ToDate;
  997. break;
  998. }
  999. DateTypeBox.SelectedValue = Properties.DateFilterType;
  1000. FromPicker.SelectedDate = From;
  1001. ToPicker.SelectedDate = To;
  1002. var enabledPicker = Properties.DateFilterType == DateFilterType.Custom;
  1003. FromPicker.IsEnabled = enabledPicker;
  1004. ToPicker.IsEnabled = enabledPicker;
  1005. }
  1006. private void SetupJobFilter()
  1007. {
  1008. JobBox.SelectedValue = Properties.JobID;
  1009. }
  1010. private void SetupFilters()
  1011. {
  1012. SetupDateFilters();
  1013. SetupJobFilter();
  1014. SetDateFilterVisibility(Properties.ShowDateFilter);
  1015. SetJobFilterVisibility(Properties.ShowJobFilter);
  1016. }
  1017. #region Categories
  1018. private static Dictionary<string, Tuple<Type, Type>>? FormInstanceTypes;
  1019. public static readonly Dictionary<Type, List<Tuple<string, string>>> ParentColumns = new()
  1020. {
  1021. { typeof(Kanban), new() { new("Parent.Number", "Task No") } },
  1022. { typeof(JobITP), new() { new("Parent.Code", "Code") } },
  1023. { typeof(Assignment), new() { new("Parent.Number", "Ass. No") } },
  1024. { typeof(Employee), new() { new("Parent.Code", "Employee") } },
  1025. { typeof(PurchaseOrderItem), new() { new("Parent.PONumber", "PO No") } }
  1026. };
  1027. public static Dictionary<Type, Tuple<string, string>> ChangeableLinks = new()
  1028. {
  1029. { typeof(JobForm), new( "Parent.ID", "Scope.ID") },
  1030. { typeof(AssignmentForm), new( "JobLink.ID", "JobScope.ID") },
  1031. { typeof(KanbanForm), new( "JobLink.ID", "") }
  1032. };
  1033. private static bool CategoryToType(string category, [NotNullWhen(true)] out Type? formType, [NotNullWhen(true)] out Type? parentType)
  1034. {
  1035. if (FormInstanceTypes == null)
  1036. {
  1037. var instancetypes = CoreUtils.Entities.Where(
  1038. x => x.GetInterfaces().Contains(typeof(IDigitalFormInstance))
  1039. ).Select(x =>
  1040. {
  1041. var inter = x.GetInterfaces()
  1042. .Where(x => x.IsGenericType && x.GetGenericTypeDefinition().Equals(typeof(IDigitalFormInstance<>)))
  1043. .FirstOrDefault();
  1044. if (inter is not null)
  1045. {
  1046. var link = inter.GenericTypeArguments[0];
  1047. var entityLinkDef = link.GetSuperclassDefinition(typeof(EntityLink<>));
  1048. if (entityLinkDef is not null)
  1049. {
  1050. var entityType = entityLinkDef.GenericTypeArguments[0];
  1051. return new Tuple<string, Type, Type>(entityType.Name, x, entityType);
  1052. }
  1053. }
  1054. return null;
  1055. }).Where(x => x is not null);
  1056. FormInstanceTypes =
  1057. instancetypes.ToDictionary(x => x!.Item1, x => new Tuple<Type, Type>(x!.Item2, x!.Item3));
  1058. }
  1059. if (!FormInstanceTypes.TryGetValue(category, out var result))
  1060. {
  1061. formType = null;
  1062. parentType = null;
  1063. return false;
  1064. }
  1065. formType = result.Item1;
  1066. parentType = result.Item2;
  1067. return true;
  1068. }
  1069. private void SetCategory(string? category)
  1070. {
  1071. CustomFilter = null;
  1072. CustomFilterIndex = null;
  1073. if (category is null || !CategoryToType(category, out var formType, out var parentType))
  1074. {
  1075. IsEntityForm = false;
  1076. ParentType = null;
  1077. FormType = null;
  1078. return;
  1079. }
  1080. IsEntityForm = formType.IsSubclassOfRawGeneric(typeof(EntityForm<,,>));
  1081. ParentType = parentType;
  1082. FormType = formType;
  1083. }
  1084. #endregion
  1085. private static string GetJobLink(string prefix, Type type, bool recursive)
  1086. {
  1087. if (type == null)
  1088. return "null";
  1089. var props = type.GetProperties().Where(x =>
  1090. x.PropertyType.BaseType != null && x.PropertyType.BaseType.IsGenericType &&
  1091. x.PropertyType.BaseType.GetGenericTypeDefinition() == typeof(EntityLink<>));
  1092. foreach (var prop in props)
  1093. {
  1094. if (prop.PropertyType == typeof(JobLink))
  1095. return (string.IsNullOrEmpty(prefix) ? "" : prefix + ".") + prop.Name;
  1096. if (recursive)
  1097. {
  1098. var result = GetJobLink((string.IsNullOrEmpty(prefix) ? "" : prefix + ".") + prop.Name,
  1099. prop.PropertyType, recursive);
  1100. if (!string.IsNullOrEmpty(result))
  1101. return result;
  1102. }
  1103. }
  1104. return "";
  1105. }
  1106. private static string GetJobScopeLink(string prefix, Type type, int recurselevel)
  1107. {
  1108. var props = type.GetProperties().Where(x =>
  1109. x.PropertyType.BaseType != null && x.PropertyType.BaseType.IsGenericType &&
  1110. x.PropertyType.BaseType.GetGenericTypeDefinition() == typeof(EntityLink<>));
  1111. foreach (var prop in props)
  1112. {
  1113. if (prop.PropertyType == typeof(JobScopeLink))
  1114. return (string.IsNullOrEmpty(prefix) ? "" : prefix + ".") + prop.Name;
  1115. if (recurselevel > 0)
  1116. {
  1117. var result = GetJobScopeLink((string.IsNullOrEmpty(prefix) ? "" : prefix + ".") + prop.Name,
  1118. prop.PropertyType, recurselevel - 1);
  1119. if (!string.IsNullOrEmpty(result))
  1120. return result;
  1121. }
  1122. }
  1123. return "";
  1124. }
  1125. /// <summary>
  1126. /// Find a link from the form type to an associated <see cref="Job"/>, allowing us to filter based on jobs.
  1127. /// </summary>
  1128. /// <returns>The property name of the <see cref="JobLink"/>.</returns>
  1129. private static string GetJobLink<T>(bool recursive) where T : IDigitalFormInstance
  1130. => GetJobLink("", typeof(T), recursive);
  1131. private static string GetJobScopeLink<T>(int recurselevel) where T : IDigitalFormInstance
  1132. => GetJobScopeLink("", typeof(T), recurselevel);
  1133. private IKeyedQueryDef GetFormQuery<T>()
  1134. where T : Entity, IRemotable, IPersistent, IDigitalFormInstance, new()
  1135. {
  1136. var sort = LookupFactory.DefineSort<T>();
  1137. var jobLink = GetJobLink<T>(true);
  1138. var jobScopeLink = GetJobScopeLink<T>(typeof(T) == typeof(JobForm) ? 0 : 1);
  1139. var completedFilter = new Filter<T>(x => x.FormCompleted).IsGreaterThanOrEqualTo(From)
  1140. .And(x => x.FormCompleted).IsLessThan(To.AddDays(1));
  1141. if (Properties.ShowIncompleteForms)
  1142. {
  1143. completedFilter.Or(x => x.FormCompleted).IsEqualTo(FilterConstant.Null);
  1144. }
  1145. var filter = new Filter<T>(x => x.Form.ID).IsEqualTo(Form!.ID)
  1146. .And(x => x.FormCancelled).IsEqualTo(DateTime.MinValue)
  1147. .And(completedFilter);
  1148. if (Properties.JobID != Guid.Empty && Properties.ShowJobFilter)
  1149. {
  1150. filter.And(jobLink + ".ID").IsEqualTo(Properties.JobID);
  1151. }
  1152. if (CustomFilter is not null)
  1153. {
  1154. filter.And(CustomFilter);
  1155. }
  1156. var columns = Columns.None<T>().Add(x => x.ID)
  1157. .Add(x => x.Number)
  1158. .Add(x => x.Description)
  1159. .Add(x => x.CreatedBy)
  1160. .Add(x => x.Created)
  1161. .Add(x => x.Form.ID)
  1162. .Add(x => x.FormData)
  1163. .Add(x => x.FormStarted)
  1164. .Add(x => x.FormCompleted)
  1165. .Add(x => x.FormCompletedBy.UserID)
  1166. .Add(x => x.Location.Timestamp)
  1167. .Add(x => x.Location.Latitude)
  1168. .Add(x => x.Location.Longitude);
  1169. if (IsEntityForm)
  1170. columns.Add(x => x.FormProcessed); //"Processed");
  1171. var parentcols = LookupFactory.DefineColumns(ParentType!);
  1172. foreach (var col in parentcols.ColumnNames())
  1173. columns.Add("Parent." + col);
  1174. if (ParentColumns.TryGetValue(ParentType!, out var pColumns))
  1175. {
  1176. foreach (var (field, name) in pColumns)
  1177. columns.Add(field);
  1178. }
  1179. if (!string.IsNullOrWhiteSpace(jobLink))
  1180. {
  1181. columns.Add(jobLink + ".ID");
  1182. columns.Add(jobLink + ".JobNumber");
  1183. }
  1184. if (!string.IsNullOrWhiteSpace(jobScopeLink))
  1185. {
  1186. columns.Add(jobScopeLink + ".ID");
  1187. columns.Add(jobScopeLink + ".Number");
  1188. }
  1189. return new KeyedQueryDef<T>(filter, columns, sort);
  1190. }
  1191. #endregion
  1192. private void LoadDataIntoGrid(List<DigitalFormVariable> variables, List<QAQuestion> questions, CoreTable formData, List<string> additionalColumns, CoreTable? jobITPs)
  1193. {
  1194. var cData = new CoreTable();
  1195. cData.AddColumn<Guid>("ID");
  1196. cData.AddColumn<Guid>("Form.ID");
  1197. cData.AddColumn<Guid>("Parent.ID");
  1198. cData.AddColumn<DateTime>("Location.Timestamp");
  1199. cData.AddColumn<double>("Location.Latitude");
  1200. cData.AddColumn<double>("Location.Longitude");
  1201. cData.AddColumn<string>("FormData");
  1202. cData.AddColumn<string>("Number");
  1203. if (ParentColumns.TryGetValue(ParentType!, out var pColumns))
  1204. {
  1205. foreach (var (field, name) in pColumns)
  1206. cData.AddColumn<string>(field);
  1207. }
  1208. var jobLink = GetJobLink("",FormType!,true);
  1209. Grid.JobLink = jobLink;
  1210. if (!string.IsNullOrWhiteSpace(jobLink))
  1211. {
  1212. if (!string.Equals(jobLink, "Parent"))
  1213. cData.AddColumn<Guid>(jobLink + ".ID");
  1214. cData.AddColumn<string>(jobLink + ".JobNumber");
  1215. }
  1216. var jobScopeLink = GetJobScopeLink("",FormType!,FormType == typeof(JobForm) ? 0 : 1);
  1217. Grid.JobScopeLink = jobLink;
  1218. if (!string.IsNullOrWhiteSpace(jobScopeLink))
  1219. {
  1220. cData.AddColumn<Guid>(jobScopeLink + ".ID");
  1221. cData.AddColumn<string>(jobScopeLink + ".Number");
  1222. }
  1223. cData.AddColumn<string>("Description");
  1224. cData.AddColumn<string>("Parent.Description");
  1225. cData.AddColumn<DateTime>("Created");
  1226. cData.AddColumn<string>("Created By");
  1227. cData.AddColumn<DateTime>("Completed");
  1228. cData.AddColumn<string>("Completed By");
  1229. if (IsEntityForm)
  1230. cData.AddColumn<bool>("Processed");
  1231. if (variables.Count != 0)
  1232. {
  1233. foreach (var variable in variables)
  1234. {
  1235. var code = variable.Code.Replace("/", " ");
  1236. Grid.QuestionCodes[code] = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(code.ToLower());
  1237. try
  1238. {
  1239. cData.AddColumn<string>(code);
  1240. }
  1241. catch (DuplicateNameException e)
  1242. {
  1243. MessageWindow.ShowError($"Error: duplicate variable code {code}", e, title: "Duplicate code");
  1244. }
  1245. }
  1246. }
  1247. else if (questions.Count != 0)
  1248. {
  1249. Questions = questions;
  1250. Progress.SetMessage("Loading Checks");
  1251. QAGrid.Clear();
  1252. QAGrid.LoadChecks(Form!.Description, Questions, new Dictionary<Guid, object>());
  1253. QAGrid.CollapseMargins();
  1254. var i = 1;
  1255. foreach (var question in Questions)
  1256. {
  1257. var id = question.ID.ToString();
  1258. if (!question.Answer.Equals(QAAnswer.Comment))
  1259. {
  1260. cData.AddColumn<string>(id);
  1261. var code = question.Code;
  1262. Grid.QuestionCodes[id] = string.IsNullOrEmpty(code) ? string.Format("{0}.", i) : code;
  1263. i++;
  1264. }
  1265. }
  1266. }
  1267. foreach (var row in formData.Rows)
  1268. {
  1269. var form = (row.ToObject(FormType!) as IDigitalFormInstance)!;
  1270. if (true) //(!string.IsNullOrWhiteSpace(form.FormData))
  1271. {
  1272. var dataRow = cData.NewRow();
  1273. dataRow["ID"] = form.ID;
  1274. dataRow["Form.ID"] = form.Form.ID;
  1275. dataRow["Parent.ID"] = form.ParentID();
  1276. dataRow["Location.Timestamp"] = form.Location.Timestamp;
  1277. dataRow["Location.Latitude"] = form.Location.Latitude;
  1278. dataRow["Location.Longitude"] = form.Location.Longitude;
  1279. dataRow["FormData"] = form.FormData;
  1280. dataRow["Number"] = form.Number;
  1281. dataRow["Description"] = form.Description;
  1282. var desc = new List<string>();
  1283. foreach (var col in additionalColumns)
  1284. {
  1285. var val = row[col];
  1286. if (val != null && val is not Guid)
  1287. desc.Add(val.ToString() ?? "");
  1288. }
  1289. dataRow["Parent.Description"] = string.Join(" : ", desc);
  1290. dataRow["Created"] = (form as Entity)!.Created.IsEmpty()
  1291. ? form.FormStarted
  1292. : (form as Entity)!.Created;
  1293. dataRow["Created By"] = (form as Entity)!.CreatedBy;
  1294. dataRow["Completed"] = form.FormCompleted;
  1295. dataRow["Completed By"] = form.FormCompletedBy.UserID;
  1296. if (IsEntityForm)
  1297. dataRow["Processed"] = form.FormProcessed > DateTime.MinValue;
  1298. // if (ParentType == typeof(JobITP))
  1299. // {
  1300. // var jobITP = jobITPs!.Rows.FirstOrDefault(x => x.Get<JobITP, Guid>(x => x.ID) == form.ParentID());
  1301. // if (jobITP is not null)
  1302. // {
  1303. // var jobID = jobITP.Get<JobITP, Guid>(x => x.Job.ID);
  1304. // dataRow["Job No"] = Jobs.Where(x => x.ID == jobID).FirstOrDefault()?.JobNumber;
  1305. // }
  1306. // }
  1307. if (pColumns != null)
  1308. {
  1309. foreach (var (field, name) in pColumns)
  1310. dataRow[field] = $"{row[field]}";
  1311. }
  1312. if (!string.IsNullOrWhiteSpace(jobLink))
  1313. {
  1314. dataRow[jobLink + ".ID"] = row[jobLink + ".ID"];
  1315. dataRow[jobLink + ".JobNumber"] = row[jobLink + ".JobNumber"]?.ToString();
  1316. }
  1317. if (!string.IsNullOrWhiteSpace(jobScopeLink))
  1318. {
  1319. dataRow[jobScopeLink + ".ID"] = row[jobScopeLink + ".ID"];
  1320. dataRow[jobScopeLink + ".Number"] = row[jobScopeLink + ".Number"]?.ToString();
  1321. }
  1322. var bHasData = false;
  1323. if (variables.Count != 0)
  1324. {
  1325. var dict = Serialization.Deserialize<Dictionary<string, object?>>(form.FormData);
  1326. if (dict is not null)
  1327. {
  1328. var storage = new DFLoadStorage(dict, null);
  1329. foreach (var variable in variables)
  1330. {
  1331. var value = variable.Deserialize(storage.GetEntry(variable.Code));
  1332. var format = variable.FormatValue(value);
  1333. var sKey = variable.Code.Replace("/", " ");
  1334. if (cData.HasColumn(sKey))
  1335. {
  1336. dataRow[sKey] = format;
  1337. bHasData = true;
  1338. }
  1339. }
  1340. }
  1341. }
  1342. else
  1343. {
  1344. var dict = Serialization.Deserialize<Dictionary<Guid, object>>(form.FormData);
  1345. if (dict is not null)
  1346. foreach (var key in dict.Keys)
  1347. {
  1348. var colName = key.ToString();
  1349. if (cData.HasColumn(colName))
  1350. {
  1351. dataRow[colName] = dict[key];
  1352. bHasData = true;
  1353. }
  1354. }
  1355. }
  1356. cData.Rows.Add(dataRow);
  1357. }
  1358. }
  1359. Grid.ItemsSource = cData;
  1360. IsQAForm = variables.Count == 0 && questions.Count != 0;
  1361. QAGrid.Visibility = IsQAForm ? Visibility.Visible : Visibility.Collapsed;
  1362. Grid.Visibility = Visibility.Visible;
  1363. }
  1364. private void RefreshData<TForm>()
  1365. where TForm : Entity, IRemotable, IPersistent, IDigitalFormInstance, new()
  1366. {
  1367. var formQuery = GetFormQuery<TForm>();
  1368. var queries = new List<IKeyedQueryDef>()
  1369. {
  1370. new KeyedQueryDef<QAQuestion>(new Filter<QAQuestion>(x => x.Form.ID).IsEqualTo(Form!.ID)),
  1371. new KeyedQueryDef<DigitalFormVariable>(
  1372. new Filter<DigitalFormVariable>(x => x.Form.ID).IsEqualTo(Form.ID),
  1373. null,
  1374. new SortOrder<DigitalFormVariable>(x => x.Sequence)),
  1375. formQuery
  1376. };
  1377. if (ParentType == typeof(JobITP))
  1378. {
  1379. queries.Add(new KeyedQueryDef<JobITP>(
  1380. new Filter<JobITP>(x => x.ID).InQuery((formQuery.Filter as Filter<JobITPForm>)!, x => x.Parent.ID),
  1381. Columns.None<JobITP>().Add(x => x.ID, x => x.Job.JobNumber)));
  1382. }
  1383. var results = Client.QueryMultiple(queries);
  1384. var questions = results.Get<QAQuestion>().ToObjects<QAQuestion>().ToList();
  1385. var variables = results.Get<DigitalFormVariable>().ToObjects<DigitalFormVariable>().ToList();
  1386. var formData = results.Get(formQuery.Key);
  1387. LoadDataIntoGrid(
  1388. variables, questions,
  1389. formData,
  1390. formQuery.Columns!.ColumnNames().Where(x => x.StartsWith("Parent.")).ToList(),
  1391. ParentType == typeof(JobITP) ? results.Get<JobITP>() : null);
  1392. }
  1393. public void Refresh()
  1394. {
  1395. if (!IsSetup) return;
  1396. Questions.Clear();
  1397. QAGrid.Clear();
  1398. QAGrid.LoadChecks("", Array.Empty<QAQuestion>(), new Dictionary<Guid, object>());
  1399. if (ParentType is null || FormType is null || Form is null || Form.ID == Guid.Empty)
  1400. {
  1401. QAGrid.Visibility = Visibility.Collapsed;
  1402. Grid.Visibility = Visibility.Collapsed;
  1403. return;
  1404. }
  1405. var refreshMethod = typeof(DigitalFormsDashboard).GetMethod(nameof(RefreshData), BindingFlags.Instance | BindingFlags.NonPublic)!.MakeGenericMethod(FormType);
  1406. refreshMethod.Invoke(this, Array.Empty<object?>());
  1407. }
  1408. public void Shutdown(CancelEventArgs? cancel)
  1409. {
  1410. }
  1411. #region DataGrid Configuration
  1412. private DynamicGridStyle Grid_OnGetRowStyle(CoreRow row, DynamicGridStyle defaultstyle)
  1413. {
  1414. if (row.Get<DateTime>("Completed") == DateTime.MinValue)
  1415. {
  1416. return new DynamicGridRowStyle(defaultstyle) { Background = Colors.LightSalmon.ToBrush() };
  1417. }
  1418. else
  1419. {
  1420. return defaultstyle;
  1421. }
  1422. }
  1423. private Entity? GetEntityForm<T>(Guid id) where T : Entity, IDigitalFormInstance, IRemotable, IPersistent, new()
  1424. {
  1425. var columns = DynamicFormEditWindow.FormColumns<T>();
  1426. return new Client<T>().Query(
  1427. new Filter<T>(x => x.ID).IsEqualTo(id),
  1428. columns).Rows.FirstOrDefault()?.ToObject<T>();
  1429. }
  1430. #endregion
  1431. private void Grid_OnSelectItem(object sender, DynamicGridSelectionEventArgs e)
  1432. {
  1433. var linkcolumn = GetJobLink("", FormType, true);
  1434. if (!string.IsNullOrWhiteSpace(linkcolumn) && _jobScopeAction != null)
  1435. {
  1436. linkcolumn = $"{linkcolumn}.ID";
  1437. var jobids = (e.Rows ?? []).Select(x => x[linkcolumn]).Distinct().ToArray();
  1438. _jobScopeAction.IsEnabled = jobids.Length == 1;
  1439. }
  1440. }
  1441. private void Grid_OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args)
  1442. {
  1443. if (args.Row is null) return;
  1444. var formid = args.Row.Get<Guid>("Form.ID");
  1445. var formdata = args.Row.Get<string>("FormData");
  1446. var id = args.Row.Get<Guid>("ID");
  1447. if (FormType is null) return;
  1448. if (IsQAForm)
  1449. {
  1450. var values = new Dictionary<Guid, object>();
  1451. var formData = Serialization.Deserialize<Dictionary<string, object>>(formdata);
  1452. if (formData is not null)
  1453. {
  1454. foreach (var (idStr, value) in formData)
  1455. {
  1456. if (Guid.TryParse(idStr, out var codeID))
  1457. {
  1458. values[codeID] = value;
  1459. }
  1460. }
  1461. }
  1462. QAGrid.Clear();
  1463. QAGrid.LoadChecks(Form!.Description, Questions, values);
  1464. QAGrid.CollapseMargins();
  1465. return;
  1466. }
  1467. var entityForm = typeof(DigitalFormsDashboard)
  1468. .GetMethod(nameof(GetEntityForm), BindingFlags.NonPublic | BindingFlags.Instance)!
  1469. .MakeGenericMethod(FormType)
  1470. .Invoke(this, new object[] { id }) as IDigitalFormInstance;
  1471. if (entityForm is not null)
  1472. {
  1473. if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel))
  1474. {
  1475. dataModel.Update(null);
  1476. /*typeof(QADashboard)
  1477. .GetMethod(nameof(SaveEntityForm), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!
  1478. .MakeGenericMethod(formType)
  1479. .Invoke(this, new object[] { entityForm });*/
  1480. Refresh();
  1481. }
  1482. }
  1483. }
  1484. }
  1485. public class DigitalFormsDashboardGrid : CoreTableGrid
  1486. {
  1487. public string? JobLink { get; set; }
  1488. public string? JobScopeLink { get; set; }
  1489. public Type? ParentType { get; set; }
  1490. public Type? FormType { get; set; }
  1491. public List<Tuple<string, Type?, string>> Categories { get; set; } = new();
  1492. public readonly Dictionary<string, string> QuestionCodes = new();
  1493. protected override void Init()
  1494. {
  1495. base.Init();
  1496. ActionColumns.Add(new DynamicImageColumn(Location_Image, Location_Click) { Position = DynamicActionColumnPosition.Start });
  1497. }
  1498. protected override void DoReconfigure(DynamicGridOptions options)
  1499. {
  1500. base.DoReconfigure(options);
  1501. options.FilterRows = true;
  1502. }
  1503. private bool Location_Click(CoreRow? row)
  1504. {
  1505. if (row is null) return false;
  1506. var timestamp = row.Get<DateTime>("Location.Timestamp");
  1507. if (timestamp == DateTime.MinValue) return false;
  1508. var latitude = row.Get<double>("Location.Latitude");
  1509. var longitude = row.Get<double>("Location.Longitude");
  1510. var form = new MapForm(latitude, longitude, timestamp);
  1511. form.ShowDialog();
  1512. return false;
  1513. }
  1514. private BitmapImage? Location_Image(CoreRow? row)
  1515. {
  1516. return row?.Get<DateTime>("Location.Timestamp") == DateTime.MinValue ? null : PRSDesktop.Resources.milestone.AsBitmapImage();
  1517. }
  1518. public override DynamicGridColumns GenerateColumns()
  1519. {
  1520. var columns = base.GenerateColumns();
  1521. var newCols = new DynamicGridColumns();
  1522. foreach(var column in columns)
  1523. {
  1524. column.Alignment = Alignment.MiddleCenter;
  1525. if (column.ColumnName == "ID" || column.ColumnName == "Form.ID"
  1526. || column.ColumnName == "Parent.ID" || column.ColumnName == "FormData"
  1527. || column.ColumnName == "Location.Latitude" || column.ColumnName == "Location.Longitude")
  1528. {
  1529. continue;
  1530. }
  1531. else if (column.ColumnName == "Number")
  1532. {
  1533. column.Width = 80;
  1534. column.Alignment = Alignment.MiddleCenter;
  1535. }
  1536. else if (column.ColumnName == "Description")
  1537. {
  1538. column.Width = 250;
  1539. column.Alignment = Alignment.MiddleLeft;
  1540. }
  1541. else if (column.ColumnName == "Location.Timestamp")
  1542. {
  1543. continue;
  1544. }
  1545. else if (ParentType is not null && DigitalFormsDashboard.ParentColumns.TryGetValue(ParentType, out var pColumns)
  1546. && pColumns.FirstOrDefault(x => x.Item1 == column.ColumnName)?.Item2 is string header)
  1547. {
  1548. column.Caption = header;
  1549. }
  1550. else if (column.ColumnName == JobLink + ".JobNumber")
  1551. {
  1552. column.Width = 60;
  1553. column.Caption = "Job No";
  1554. }
  1555. else if (column.ColumnName == JobScopeLink + ".Number")
  1556. {
  1557. column.Width = 50;
  1558. column.Caption = "Scope";
  1559. }
  1560. else if (column.ColumnName == "Parent.Description")
  1561. {
  1562. column.Caption = Categories.FirstOrDefault(x => x.Item2 == FormType)?.Item3 ?? "Parent";
  1563. column.Alignment = Alignment.MiddleLeft;
  1564. column.Width = 250;
  1565. }
  1566. else if (column.ColumnName == "Completed")
  1567. {
  1568. column.Width = 100;
  1569. column.Format = "dd MMM yy hh:mm";
  1570. }
  1571. else if (column.ColumnName == "Completed By")
  1572. {
  1573. column.Width = 100;
  1574. }
  1575. else if (column.ColumnName == "Processed")
  1576. {
  1577. column.Width = 100;
  1578. }
  1579. else if (column.ColumnName == "Created By")
  1580. {
  1581. column.Width = 100;
  1582. }
  1583. else if (column.ColumnName == "Created")
  1584. {
  1585. column.Width = 100;
  1586. column.Format = "dd MMM yy hh:mm";
  1587. }
  1588. else if (QuestionCodes.TryGetValue(column.ColumnName, out var questionHeader))
  1589. {
  1590. column.Width = 100;
  1591. column.Caption = questionHeader;
  1592. }
  1593. else
  1594. {
  1595. continue;
  1596. }
  1597. newCols.Add(column);
  1598. }
  1599. return newCols;
  1600. }
  1601. }