DigitalFormsDashboard.xaml.cs 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. using com.sun.tools.doclets.formats.html;
  2. using Comal.Classes;
  3. using InABox.Clients;
  4. using InABox.Core;
  5. using InABox.DynamicGrid;
  6. using InABox.Reports;
  7. using InABox.Reports.Common;
  8. using InABox.Scripting;
  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.Windows.Shared;
  16. using Syncfusion.XlsIO;
  17. using System;
  18. using System.Collections;
  19. using System.Collections.Generic;
  20. using System.Data;
  21. using System.Diagnostics;
  22. using System.Diagnostics.CodeAnalysis;
  23. using System.Linq;
  24. using System.Linq.Expressions;
  25. using System.Reflection;
  26. using System.Text;
  27. using System.Text.RegularExpressions;
  28. using System.Threading;
  29. using System.Threading.Tasks;
  30. using System.Windows;
  31. using System.Windows.Controls;
  32. using System.Windows.Data;
  33. using System.Windows.Documents;
  34. using System.Windows.Input;
  35. using System.Windows.Media;
  36. using System.Windows.Media.Imaging;
  37. using System.Windows.Navigation;
  38. using System.Windows.Shapes;
  39. using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs;
  40. namespace PRSDesktop
  41. {
  42. public enum DateFilterType
  43. {
  44. Today,
  45. Yesterday,
  46. Week,
  47. SevenDays,
  48. Month,
  49. ThirtyDays,
  50. Year,
  51. TwelveMonths,
  52. Custom
  53. }
  54. public class DFFilter : BaseObject
  55. {
  56. [EditorSequence(1)]
  57. [TextBoxEditor]
  58. public string Name { get; set; }
  59. [EditorSequence(2)]
  60. [FilterEditor]
  61. public string Filter { get; set; }
  62. protected override void Init()
  63. {
  64. base.Init();
  65. Name = "";
  66. Filter = "";
  67. }
  68. }
  69. public class DigitalFormsDashboardProperties : IDashboardProperties
  70. {
  71. public bool ShowJobFilter { get; set; } = false;
  72. public bool ShowDateFilter { get; set; } = true;
  73. public Guid JobID { get; set; }
  74. public DateFilterType DateFilterType { get; set; } = DateFilterType.Today;
  75. public DateTime FromDate { get; set; }
  76. public DateTime ToDate { get; set; }
  77. public Dictionary<string, List<DFFilter>> Filters { get; set; } = new();
  78. }
  79. public class DigitalFormsDashboardElement : DashboardElement<DigitalFormsDashboard, Common, DigitalFormsDashboardProperties> { }
  80. /// <summary>
  81. /// Interaction logic for DigitalFormsDashboard.xaml
  82. /// </summary>
  83. public partial class DigitalFormsDashboard : UserControl,
  84. IDashboardWidget<Common, DigitalFormsDashboardProperties>,
  85. IRequiresSecurity<CanViewDigitalFormsDashbaord>,
  86. IHeaderDashboard, IActionsDashboard
  87. {
  88. public DigitalFormsDashboardProperties Properties { get; set; }
  89. private List<DigitalForm> DigitalForms;
  90. private List<Job> Jobs;
  91. private Dictionary<string, string> Categories;
  92. public DashboardHeader Header { get; set; } = new();
  93. private bool IsQAForm = false;
  94. private List<QAQuestion> Questions = new();
  95. public DigitalFormsDashboard()
  96. {
  97. InitializeComponent();
  98. }
  99. public void Setup()
  100. {
  101. var results = Client.QueryMultiple(
  102. new KeyedQueryDef<DigitalForm>(new Filter<DigitalForm>(x => x.Active).IsEqualTo(true)),
  103. new KeyedQueryDef<Job>(
  104. LookupFactory.DefineFilter<Job>(),
  105. new Columns<Job>(x => x.ID)
  106. .Add(x => x.JobNumber)
  107. .Add(x => x.Name)));
  108. DigitalForms = results.Get<DigitalForm>().ToList<DigitalForm>();
  109. var categories = new DigitalFormCategoryLookups(null);
  110. categories.OnAfterGenerateLookups += (sender, entries) => { entries.Insert(0, new LookupEntry("", "Select Category")); };
  111. Categories = categories.AsTable("AppliesTo")
  112. .ToDictionary("AppliesTo", "Display")
  113. .Cast<KeyValuePair<object, string>>()
  114. .ToDictionary(x => (x.Key as string)!, x => x.Value);
  115. Jobs = results.Get<Job>().ToList<Job>();
  116. Jobs.Insert(0, new Job { ID = Guid.Empty, JobNumber = "ALL", Name = "All Jobs" });
  117. SetupHeader();
  118. SetupFilters();
  119. }
  120. #region Header
  121. private ComboBox CategoryBox;
  122. private ComboBox FormBox;
  123. private ComboBox JobBox;
  124. private ComboBox DateTypeBox;
  125. private Label FromLabel;
  126. private DatePicker FromPicker;
  127. private Label ToLabel;
  128. private DatePicker ToPicker;
  129. private Button Print;
  130. private static Dictionary<DateFilterType, string> FilterTypes = new()
  131. {
  132. { DateFilterType.Today, "Today" },
  133. { DateFilterType.Yesterday, "Yesterday" },
  134. { DateFilterType.Week, "Week to Date" },
  135. { DateFilterType.SevenDays, "Last 7 Days" },
  136. { DateFilterType.Month, "Month to Date" },
  137. { DateFilterType.ThirtyDays, "Last 30 Days" },
  138. { DateFilterType.Year, "Year to Date" },
  139. { DateFilterType.TwelveMonths, "Last 12 Months" },
  140. { DateFilterType.Custom, "Custom" }
  141. };
  142. public void SetupHeader()
  143. {
  144. CategoryBox = new ComboBox {
  145. Width = 150,
  146. VerticalContentAlignment = VerticalAlignment.Center,
  147. Margin = new Thickness(0, 0, 5, 0)
  148. };
  149. CategoryBox.ItemsSource = Categories;
  150. CategoryBox.SelectedValuePath = "Key";
  151. CategoryBox.DisplayMemberPath = "Value";
  152. CategoryBox.SelectionChanged += Category_SelectionChanged;
  153. FormBox = new ComboBox
  154. {
  155. Width = 250,
  156. VerticalContentAlignment = VerticalAlignment.Center,
  157. Margin = new Thickness(0, 0, 5, 0),
  158. IsEnabled = false
  159. };
  160. FormBox.SelectionChanged += FormBox_SelectionChanged;
  161. FormBox.ItemsSource = new Dictionary<DigitalForm, string> { };
  162. JobBox = new ComboBox
  163. {
  164. Width = 250,
  165. Margin = new Thickness(0, 0, 5, 0),
  166. VerticalContentAlignment = VerticalAlignment.Center
  167. };
  168. JobBox.ItemsSource = Jobs.ToDictionary(x => x.ID, x => $"{x.JobNumber} : {x.Name}");
  169. JobBox.SelectedIndex = 0;
  170. JobBox.SelectedValuePath = "Key";
  171. JobBox.DisplayMemberPath = "Value";
  172. JobBox.SelectionChanged += JobBox_SelectionChanged;
  173. DateTypeBox = new ComboBox
  174. {
  175. Width = 120,
  176. VerticalContentAlignment = VerticalAlignment.Center
  177. };
  178. DateTypeBox.ItemsSource = FilterTypes;
  179. DateTypeBox.SelectedValuePath = "Key";
  180. DateTypeBox.DisplayMemberPath = "Value";
  181. DateTypeBox.SelectedValue = Properties.DateFilterType;
  182. DateTypeBox.SelectionChanged += DateTypeBox_SelectionChanged;
  183. FromLabel = new Label { Content = "From", VerticalContentAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 5, 0) };
  184. FromPicker = new DatePicker {
  185. Width = 100,
  186. Background = new SolidColorBrush(Colors.LightYellow),
  187. VerticalContentAlignment = VerticalAlignment.Center,
  188. FirstDayOfWeek = DayOfWeek.Monday,
  189. Margin = new Thickness(0, 0, 5, 0)
  190. };
  191. FromPicker.SelectedDateChanged += FromPicker_SelectedDateChanged;
  192. ToLabel = new Label { Content = "To", VerticalContentAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 5, 0) };
  193. ToPicker = new DatePicker
  194. {
  195. Width = 100,
  196. Background = new SolidColorBrush(Colors.LightYellow),
  197. VerticalContentAlignment = VerticalAlignment.Center,
  198. FirstDayOfWeek = DayOfWeek.Monday,
  199. Margin = new Thickness(0, 0, 5, 0)
  200. };
  201. ToPicker.SelectedDateChanged += ToPicker_SelectedDateChanged;
  202. Print = new Button
  203. {
  204. Width = 25,
  205. Height = 25,
  206. Content = new Image { Source = PRSDesktop.Resources.printer.AsBitmapImage() }
  207. };
  208. Print.Click += Print_Click;
  209. Header.BeginUpdate()
  210. .Clear()
  211. .Add(CategoryBox)
  212. .Add(FormBox)
  213. .Add(JobBox)
  214. .Add(DateTypeBox)
  215. .Add(FromLabel)
  216. .Add(FromPicker)
  217. .Add(ToLabel)
  218. .Add(ToPicker)
  219. .AddRight(Print);
  220. Header.EndUpdate();
  221. }
  222. private void Print_Click(object sender, RoutedEventArgs e)
  223. {
  224. var menu = new ContextMenu();
  225. foreach (var report in ReportUtils.LoadReports(SectionName, DataModel(Selection.None)))
  226. {
  227. menu.AddItem(report.Name, PRSDesktop.Resources.printer, report, PrintReport_Click);
  228. }
  229. if (Security.IsAllowed<CanDesignReports>())
  230. {
  231. menu.AddSeparatorIfNeeded();
  232. menu.AddItem("Manage Reports", PRSDesktop.Resources.printer, ManageReports_Click);
  233. }
  234. menu.IsOpen = true;
  235. }
  236. private void PrintReport_Click(ReportTemplate obj)
  237. {
  238. Selection selection;
  239. if (obj.SelectedRecords && obj.AllRecords)
  240. selection = RecordSelectionDialog.Execute();
  241. else if (obj.SelectedRecords)
  242. selection = Selection.Selected;
  243. else if (obj.AllRecords)
  244. selection = Selection.All;
  245. else
  246. selection = Selection.None;
  247. ReportUtils.PreviewReport(obj, DataModel(selection), false, Security.IsAllowed<CanDesignReports>());
  248. }
  249. private void ManageReports_Click()
  250. {
  251. var manager = new ReportManager()
  252. {
  253. DataModel = DataModel(Selection.None),
  254. Section = SectionName,
  255. Populate = true
  256. };
  257. manager.ShowDialog();
  258. }
  259. private void Search_KeyUp(object sender, KeyEventArgs e)
  260. {
  261. Refresh();
  262. }
  263. private void JobBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  264. {
  265. Properties.JobID = (Guid)JobBox.SelectedValue;
  266. Refresh();
  267. }
  268. private void SetDateFilterVisibility(bool visible)
  269. {
  270. var visibility = visible ? Visibility.Visible : Visibility.Collapsed;
  271. FromLabel.Visibility = visibility;
  272. FromPicker.Visibility = visibility;
  273. ToLabel.Visibility = visibility;
  274. ToPicker.Visibility = visibility;
  275. DateTypeBox.Visibility = visibility;
  276. }
  277. private void SetJobFilterVisibility(bool visible)
  278. {
  279. var visibility = visible ? Visibility.Visible : Visibility.Collapsed;
  280. JobBox.Visibility = visibility;
  281. }
  282. private void DateTypeBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  283. {
  284. var filterType = (DateFilterType)DateTypeBox.SelectedValue;
  285. Properties.DateFilterType = filterType;
  286. if(filterType == DateFilterType.Custom)
  287. {
  288. if (FromPicker.SelectedDate == null || FromPicker.SelectedDate == DateTime.MinValue)
  289. {
  290. Properties.FromDate = DateTime.Today;
  291. }
  292. else
  293. {
  294. Properties.FromDate = FromPicker.SelectedDate.Value;
  295. }
  296. if (ToPicker.SelectedDate == null || ToPicker.SelectedDate == DateTime.MinValue)
  297. {
  298. Properties.ToDate = DateTime.Today;
  299. }
  300. else
  301. {
  302. Properties.ToDate = ToPicker.SelectedDate.Value;
  303. }
  304. }
  305. SetupDateFilters();
  306. Refresh();
  307. }
  308. private void FromPicker_SelectedDateChanged(object? sender, SelectionChangedEventArgs e)
  309. {
  310. Properties.FromDate = FromPicker.SelectedDate ?? DateTime.Today;
  311. }
  312. private void ToPicker_SelectedDateChanged(object? sender, SelectionChangedEventArgs e)
  313. {
  314. Properties.ToDate = ToPicker.SelectedDate ?? DateTime.Today;
  315. }
  316. private void Category_SelectionChanged(object sender, SelectionChangedEventArgs e)
  317. {
  318. SetCategory((CategoryBox.SelectedValue as string)!);
  319. var jobLink = FormType is not null ? GetJobLink("", FormType) : "";
  320. if (string.IsNullOrWhiteSpace(jobLink))
  321. {
  322. var jobID = Properties.JobID;
  323. JobBox.SelectedValue = Guid.Empty;
  324. JobBox.IsEnabled = false;
  325. Properties.JobID = jobID;
  326. }
  327. else
  328. {
  329. JobBox.SelectedValue = Properties.JobID;
  330. JobBox.IsEnabled = true;
  331. }
  332. if (ParentType is null)
  333. {
  334. FormBox.IsEnabled = false;
  335. FormBox.ItemsSource = new Dictionary<DigitalForm, string> { };
  336. }
  337. else
  338. {
  339. var forms = DigitalForms.Where(x => x.AppliesTo == ParentType.Name).ToList();
  340. forms.Insert(0, new DigitalForm { ID = Guid.Empty, Description = "Select Form" });
  341. FormBox.ItemsSource = forms;
  342. FormBox.DisplayMemberPath = "Description";
  343. FormBox.SelectedIndex = 0;
  344. FormBox.IsEnabled = true;
  345. }
  346. Refresh();
  347. }
  348. private void FormBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  349. {
  350. Form = (FormBox.SelectedValue as DigitalForm)!;
  351. Refresh();
  352. }
  353. #endregion
  354. private string SectionName
  355. {
  356. get
  357. {
  358. if (Form is null || Form.ID == Guid.Empty)
  359. return "Digital Forms";
  360. return Form.ID.ToString() ?? "Digital Forms";
  361. }
  362. }
  363. private DataModel DataModel(Selection selection)
  364. {
  365. if(FormType is null || Form is null || Form.ID == Guid.Empty)
  366. {
  367. return new AutoDataModel<DigitalForm>(new Filter<DigitalForm>().None());
  368. }
  369. IFilter filter;
  370. switch (selection)
  371. {
  372. case Selection.Selected:
  373. var formids = DataGrid.SelectedItems.Select(x => (x as DataRowView)!.Row["ID"]).ToArray();
  374. filter = Filter.Create<Entity>(FormType, x => x.ID).InList(formids);
  375. break;
  376. case Selection.All:
  377. filter = Filter.Create(FormType).All();
  378. break;
  379. case Selection.None:
  380. default:
  381. filter = Filter.Create(FormType).None();
  382. break;
  383. }
  384. return (Activator.CreateInstance(typeof(DigitalFormReportDataModel<>)!
  385. .MakeGenericType(FormType), new object?[] { filter, Form.ID }) as DataModel)!;
  386. }
  387. public void BuildActionsMenu(ContextMenu menu)
  388. {
  389. menu.AddCheckItem<object?>("Show Date Filter", null, ToggleDateFilter, Properties.ShowDateFilter);
  390. menu.AddCheckItem<object?>("Show Job Filter", null, ToggleJobFilter, Properties.ShowJobFilter);
  391. menu.AddSeparator();
  392. if (ParentType is not null)
  393. {
  394. menu.AddSeparatorIfNeeded();
  395. if (Properties.Filters.TryGetValue(ParentType.Name, out var filters))
  396. {
  397. var i = 0;
  398. var items = new List<MenuItem>();
  399. foreach (var filter in filters)
  400. {
  401. items.Add(menu.AddCheckItem(
  402. filter.Name,
  403. new Tuple<int, string, List<MenuItem>>(i, filter.Filter, items),
  404. ExecuteFilter,
  405. i == CustomFilterIndex));
  406. ++i;
  407. }
  408. }
  409. menu.AddSeparatorIfNeeded();
  410. menu.AddItem("Manage Filters", InABox.DynamicGrid.Properties.Resources.filter, ManageFilters_Click);
  411. }
  412. menu.AddSeparatorIfNeeded();
  413. menu.AddItem("Export", InABox.DynamicGrid.Properties.Resources.doc_xls, Export_Click, Form is not null && Form.ID != Guid.Empty);
  414. var loadingModules = menu.AddItem("Loading Modules...", null, null, false);
  415. Task.Run(() =>
  416. {
  417. return CustomModuleUtils.LoadCustomModuleThumbnails(SectionName, DataModel(Selection.None));
  418. }).ContinueWith((task) =>
  419. {
  420. var modules = task.Result;
  421. var index = menu.Items.IndexOf(loadingModules);
  422. menu.Items.RemoveAt(index);
  423. foreach (var (module, image) in modules)
  424. {
  425. menu.AddItem(module.Name, image, module, ExecuteModule_Click, index: index);
  426. ++index;
  427. }
  428. }, TaskScheduler.FromCurrentSynchronizationContext());
  429. if (Security.IsAllowed<CanCustomiseModules>())
  430. {
  431. menu.AddSeparatorIfNeeded();
  432. menu.AddItem("Manage Modules", PRSDesktop.Resources.script, ManageModules_Click);
  433. }
  434. }
  435. private void Export_Click()
  436. {
  437. var formName = Regex.Replace(Form?.Description ?? "", "[^ a-zA-Z0-9]", string.Empty);
  438. var filename = string.Format("{0} - {1} - {2:yyyy-MM-dd} - {3:yyyy-MM-dd}.xlsx", ParentType!.Name, formName, From, To);
  439. var options = new ExcelExportingOptions();
  440. options.ExcelVersion = ExcelVersion.Excel2013;
  441. options.ExportStackedHeaders = true;
  442. var excelEngine = DataGrid.ExportToExcel(DataGrid.View, options);
  443. var workBook = excelEngine.Excel.Workbooks[0];
  444. var sheet = workBook.Worksheets[0];
  445. sheet.Name = "Summary";
  446. sheet.UsedRange.AutofitRows();
  447. sheet.UsedRange.AutofitColumns();
  448. sheet = workBook.Worksheets.Create("Questions");
  449. sheet.Move(0);
  450. var questions = new Client<QAQuestion>().Query(new Filter<QAQuestion>(x => x.Form.ID).IsEqualTo(Form!.ID));
  451. sheet.Range[1, 1].Text = Form?.Description ?? "";
  452. sheet.Range[1, 1, 1, 3].Merge();
  453. var i = 1;
  454. foreach (var row in questions.Rows)
  455. if (!row.Get<QAQuestion, QAAnswer>(x => x.Answer).Equals(QAAnswer.Comment))
  456. {
  457. sheet.Range[i + 2, 1].Text = string.Format("{0}.", i);
  458. sheet.Range[i + 2, 2].Text = string.Format("{0}", row.Get<QAQuestion, string>(x => x.Question));
  459. sheet.Range[i + 2, 3].Text = string.Format("[{0}]", row.Get<QAQuestion, string>(x => x.Code));
  460. i++;
  461. }
  462. sheet.UsedRange.AutofitRows();
  463. sheet.UsedRange.AutofitColumns();
  464. try
  465. {
  466. workBook.SaveAs(filename);
  467. var startInfo = new ProcessStartInfo(filename);
  468. startInfo.Verb = "open";
  469. startInfo.UseShellExecute = true;
  470. Process.Start(startInfo);
  471. }
  472. catch
  473. {
  474. MessageBox.Show(string.Format("Unable to Save/Launch [{0}]!\n\nIs the file already open?", filename));
  475. }
  476. }
  477. private void ManageFilters_Click()
  478. {
  479. var filters = Properties.Filters.GetValueOrDefault(ParentType!.Name) ?? new List<DFFilter>();
  480. var gridFilters = new DynamicGridFilters();
  481. gridFilters.AddRange(filters.Select(x => new DynamicGridFilter { Name = x.Name, Filter = x.Filter }));
  482. var grid = new DynamicGridFilterEditor(gridFilters, FormType!);
  483. if (grid.ShowDialog() == true)
  484. {
  485. Properties.Filters[ParentType!.Name] = grid.Filters.Select(x => new DFFilter { Name = x.Name, Filter = x.Filter }).ToList();
  486. if (CustomFilterIndex != null)
  487. {
  488. Refresh();
  489. }
  490. }
  491. }
  492. private void ExecuteFilter(Tuple<int, string, List<MenuItem>> tag, bool isChecked)
  493. {
  494. var (index, filter, items) = tag;
  495. if (isChecked)
  496. {
  497. var i = 0;
  498. foreach (var item in items)
  499. {
  500. item.IsChecked = i == index;
  501. ++i;
  502. }
  503. }
  504. if (isChecked)
  505. {
  506. CustomFilter = Serialization.Deserialize(typeof(Filter<>).MakeGenericType(FormType!), filter) as IFilter;
  507. CustomFilterIndex = index;
  508. Refresh();
  509. }
  510. else if (index == CustomFilterIndex)
  511. {
  512. CustomFilter = null;
  513. CustomFilterIndex = null;
  514. Refresh();
  515. }
  516. }
  517. private void ExecuteModule_Click(CustomModule obj)
  518. {
  519. if (!string.IsNullOrWhiteSpace(obj.Script))
  520. try
  521. {
  522. Selection selection;
  523. if (obj.SelectedRecords && obj.AllRecords)
  524. selection = RecordSelectionDialog.Execute();
  525. else if (obj.SelectedRecords)
  526. selection = Selection.Selected;
  527. else if (obj.AllRecords)
  528. selection = Selection.All;
  529. else
  530. selection = Selection.None;
  531. var result = ScriptDocument.RunCustomModule(DataModel(selection), new Dictionary<string, object[]>(), obj.Script);
  532. if (result)
  533. Refresh();
  534. }
  535. catch (CompileException c)
  536. {
  537. MessageBox.Show(c.Message);
  538. }
  539. catch (Exception err)
  540. {
  541. MessageBox.Show(CoreUtils.FormatException(err));
  542. }
  543. else
  544. MessageBox.Show("Unable to load " + obj.Name);
  545. }
  546. private void ManageModules_Click()
  547. {
  548. var section = SectionName;
  549. var dataModel = DataModel(Selection.Selected);
  550. var manager = new CustomModuleManager()
  551. {
  552. Section = section,
  553. DataModel = dataModel
  554. };
  555. manager.ShowDialog();
  556. }
  557. private void ToggleDateFilter(object? tag, bool isChecked)
  558. {
  559. Properties.ShowDateFilter = isChecked;
  560. SetDateFilterVisibility(Properties.ShowDateFilter);
  561. }
  562. private void ToggleJobFilter(object? tag, bool isChecked)
  563. {
  564. Properties.ShowJobFilter = isChecked;
  565. SetJobFilterVisibility(Properties.ShowJobFilter);
  566. Refresh();
  567. }
  568. #region Filtering
  569. private DateTime From { get; set; }
  570. private DateTime To { get; set; }
  571. private bool IsEntityForm { get; set; }
  572. private Type? ParentType { get; set; }
  573. private Type? FormType { get; set; }
  574. private DigitalForm? Form { get; set; }
  575. private IFilter? CustomFilter { get; set; }
  576. private int? CustomFilterIndex { get; set; }
  577. private readonly Dictionary<string, string> QuestionCodes = new();
  578. private static int WeekDay(DateTime date)
  579. {
  580. if (date.DayOfWeek == DayOfWeek.Sunday)
  581. return 7;
  582. return (int)date.DayOfWeek - 1;
  583. }
  584. private void SetupDateFilters()
  585. {
  586. switch (Properties.DateFilterType)
  587. {
  588. case DateFilterType.Today:
  589. From = DateTime.Today;
  590. To = DateTime.Today;
  591. break;
  592. case DateFilterType.Yesterday:
  593. From = DateTime.Today.AddDays(-1);
  594. To = DateTime.Today.AddDays(-1);
  595. break;
  596. case DateFilterType.Week:
  597. From = DateTime.Today.AddDays(-WeekDay(DateTime.Today));
  598. To = DateTime.Today;
  599. break;
  600. case DateFilterType.SevenDays:
  601. From = DateTime.Today.AddDays(-6);
  602. To = DateTime.Today;
  603. break;
  604. case DateFilterType.Month:
  605. From = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
  606. To = DateTime.Today;
  607. break;
  608. case DateFilterType.ThirtyDays:
  609. From = DateTime.Today.AddDays(-29);
  610. To = DateTime.Today;
  611. break;
  612. case DateFilterType.Year:
  613. From = new DateTime(DateTime.Today.Year, 1, 1);
  614. To = DateTime.Today;
  615. break;
  616. case DateFilterType.TwelveMonths:
  617. From = DateTime.Today.AddYears(-1).AddDays(1);
  618. To = DateTime.Today;
  619. break;
  620. case DateFilterType.Custom:
  621. From = Properties.FromDate;
  622. To = Properties.ToDate;
  623. break;
  624. }
  625. DateTypeBox.SelectedValue = Properties.DateFilterType;
  626. FromPicker.SelectedDate = From;
  627. ToPicker.SelectedDate = To;
  628. var enabledPicker = Properties.DateFilterType == DateFilterType.Custom;
  629. FromPicker.IsEnabled = enabledPicker;
  630. ToPicker.IsEnabled = enabledPicker;
  631. }
  632. private void SetupJobFilter()
  633. {
  634. JobBox.SelectedValue = Properties.JobID;
  635. }
  636. private void SetupFilters()
  637. {
  638. SetupDateFilters();
  639. SetupJobFilter();
  640. SetDateFilterVisibility(Properties.ShowDateFilter);
  641. SetJobFilterVisibility(Properties.ShowJobFilter);
  642. }
  643. #region Categories
  644. private static Dictionary<string, Tuple<Type, Type>>? FormInstanceTypes;
  645. private static readonly Dictionary<Type, List<Tuple<string, string>>> parentColumns = new()
  646. {
  647. { typeof(Kanban), new() { new("Parent.Number", "Task No") } },
  648. { typeof(Job), new() { new("Parent.JobNumber", "Job No") } },
  649. { typeof(JobITP), new() { new("Parent.Code", "Code") } },
  650. { typeof(Assignment), new() { new("Parent.Number", "Ass. No") } },
  651. { typeof(TimeSheet), new() { } },
  652. { typeof(LeaveRequest), new() { } },
  653. { typeof(Employee), new() { new("Parent.Code", "Employee") } },
  654. { typeof(PurchaseOrderItem), new() { new("Parent.PONumber", "PO No") } },
  655. };
  656. private static bool CategoryToType(string category, [NotNullWhen(true)] out Type? formType, [NotNullWhen(true)] out Type? parentType)
  657. {
  658. FormInstanceTypes ??= CoreUtils.TypeList(
  659. AppDomain.CurrentDomain.GetAssemblies(),
  660. x => !x.IsAbstract && x.GetInterfaces().Contains(typeof(IDigitalFormInstance))
  661. ).Select(x =>
  662. {
  663. var inter = x.GetInterfaces()
  664. .Where(x => x.IsGenericType && x.GetGenericTypeDefinition().Equals(typeof(IDigitalFormInstance<>))).FirstOrDefault();
  665. if (inter is not null)
  666. {
  667. var link = inter.GenericTypeArguments[0];
  668. var entityLinkDef = link.GetSuperclassDefinition(typeof(EntityLink<>));
  669. if (entityLinkDef is not null)
  670. {
  671. var entityType = entityLinkDef.GenericTypeArguments[0];
  672. return new Tuple<string, Type, Type>(entityType.Name, x, entityType);
  673. }
  674. }
  675. return null;
  676. }).Where(x => x is not null).ToDictionary(x => x!.Item1, x => new Tuple<Type, Type>(x!.Item2, x!.Item3));
  677. if (!FormInstanceTypes.TryGetValue(category, out var result))
  678. {
  679. formType = null;
  680. parentType = null;
  681. return false;
  682. }
  683. formType = result.Item1;
  684. parentType = result.Item2;
  685. return true;
  686. }
  687. private void SetCategory(string category)
  688. {
  689. CustomFilter = null;
  690. CustomFilterIndex = null;
  691. if (!CategoryToType(category, out var formType, out var parentType))
  692. {
  693. IsEntityForm = false;
  694. ParentType = null;
  695. FormType = null;
  696. return;
  697. }
  698. IsEntityForm = formType.IsSubclassOfRawGeneric(typeof(EntityForm<,>));
  699. ParentType = parentType;
  700. FormType = formType;
  701. }
  702. #endregion
  703. private string GetJobLink(string prefix, Type type)
  704. {
  705. var props = type.GetProperties().Where(x =>
  706. x.PropertyType.BaseType != null && x.PropertyType.BaseType.IsGenericType &&
  707. x.PropertyType.BaseType.GetGenericTypeDefinition() == typeof(EntityLink<>));
  708. foreach (var prop in props)
  709. {
  710. if (prop.PropertyType == typeof(JobLink))
  711. return (string.IsNullOrEmpty(prefix) ? "" : prefix + ".") + prop.Name;
  712. var result = GetJobLink((string.IsNullOrEmpty(prefix) ? "" : prefix + ".") + prop.Name, prop.PropertyType);
  713. if (!string.IsNullOrEmpty(result))
  714. return result;
  715. }
  716. return "";
  717. }
  718. /// <summary>
  719. /// Find a link from the form type to an associated <see cref="Job"/>, allowing us to filter based on jobs.
  720. /// </summary>
  721. /// <returns>The property name of the <see cref="JobLink"/>.</returns>
  722. private string GetJobLink<T>() where T : IDigitalFormInstance
  723. => GetJobLink("", typeof(T));
  724. private IKeyedQueryDef GetFormQuery<T>()
  725. where T : Entity, IRemotable, IPersistent, IDigitalFormInstance, new()
  726. {
  727. var sort = LookupFactory.DefineSort<T>();
  728. var jobLink = GetJobLink<T>();
  729. var filter = new Filter<T>(x => x.FormCompleted).IsGreaterThanOrEqualTo(From)
  730. .And(x => x.FormCompleted).IsLessThan(To.AddDays(1))
  731. .And(x => x.Form.ID).IsEqualTo(Form!.ID);
  732. if (Properties.JobID != Guid.Empty && Properties.ShowJobFilter)
  733. {
  734. filter.And(jobLink + ".ID").IsEqualTo(Properties.JobID);
  735. }
  736. if (CustomFilter is not null)
  737. {
  738. filter.And(CustomFilter);
  739. }
  740. var columns = new Columns<T>(x => x.ID)
  741. .Add(x => x.Form.ID)
  742. .Add(x => x.FormData)
  743. .Add(x => x.FormCompleted)
  744. .Add(x => x.FormCompletedBy.UserID)
  745. .Add(x => x.Location.Timestamp)
  746. .Add(x => x.Location.Latitude)
  747. .Add(x => x.Location.Longitude);
  748. var parentcols = LookupFactory.DefineColumns(ParentType!);
  749. foreach (var col in parentcols.ColumnNames())
  750. columns.Add("Parent." + col);
  751. if (parentColumns.TryGetValue(ParentType!, out var pColumns))
  752. {
  753. foreach (var (field, name) in pColumns)
  754. {
  755. columns.Add(field);
  756. }
  757. }
  758. if (IsEntityForm)
  759. columns.Add("Processed");
  760. if (!string.IsNullOrWhiteSpace(jobLink))
  761. columns.Add(jobLink + ".ID");
  762. return new KeyedQueryDef<T>(filter, columns, sort);
  763. }
  764. #endregion
  765. private void LoadDataIntoGrid(List<DigitalFormVariable> variables, List<QAQuestion> questions, CoreTable formData, List<string> additionalColumns, CoreTable? jobITPs)
  766. {
  767. var data = new DataTable();
  768. data.Columns.Add("ID", typeof(Guid));
  769. data.Columns.Add("Form_ID", typeof(Guid));
  770. data.Columns.Add("Parent_ID", typeof(Guid));
  771. data.Columns.Add("Location_Timestamp", typeof(DateTime));
  772. data.Columns.Add("Location_Latitude", typeof(double));
  773. data.Columns.Add("Location_Longitude", typeof(double));
  774. data.Columns.Add("FormData", typeof(string));
  775. if (ParentType == typeof(JobITP))
  776. {
  777. data.Columns.Add("Job No", typeof(string));
  778. }
  779. if (parentColumns.TryGetValue(ParentType!, out var pColumns))
  780. {
  781. foreach (var (field, name) in pColumns)
  782. {
  783. data.Columns.Add(name, typeof(string));
  784. }
  785. }
  786. data.Columns.Add("Description", typeof(string));
  787. data.Columns.Add("Completed", typeof(DateTime));
  788. data.Columns.Add("Completed By", typeof(string));
  789. if (IsEntityForm)
  790. data.Columns.Add("Processed", typeof(bool));
  791. if (variables.Any())
  792. {
  793. foreach (var variable in variables)
  794. {
  795. var code = variable.Code.Replace("/", " ");
  796. QuestionCodes[code] = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(code.ToLower());
  797. data.Columns.Add(code, typeof(string));
  798. }
  799. }
  800. else if (questions.Any())
  801. {
  802. Questions = questions;
  803. Progress.SetMessage("Loading Checks");
  804. QAGrid.Clear();
  805. QAGrid.LoadChecks(Form!.Description, Questions, new Dictionary<Guid, object>());
  806. QAGrid.CollapseMargins();
  807. var i = 1;
  808. foreach (var question in Questions)
  809. {
  810. var id = question.ID.ToString();
  811. if (!question.Answer.Equals(QAAnswer.Comment))
  812. {
  813. data.Columns.Add(id, typeof(string));
  814. var code = question.Code;
  815. QuestionCodes[id] = string.IsNullOrEmpty(code) ? string.Format("{0}.", i) : code;
  816. i++;
  817. }
  818. }
  819. }
  820. foreach (var row in formData.Rows)
  821. {
  822. var form = (row.ToObject(FormType!) as IDigitalFormInstance)!;
  823. if (!string.IsNullOrWhiteSpace(form.FormData))
  824. {
  825. var dataRow = data.NewRow();
  826. dataRow["ID"] = form.ID;
  827. dataRow["Form_ID"] = form.Form.ID;
  828. dataRow["Parent_ID"] = form.ParentID();
  829. dataRow["Location_Timestamp"] = form.Location.Timestamp;
  830. dataRow["Location_Latitude"] = form.Location.Latitude;
  831. dataRow["Location_Longitude"] = form.Location.Longitude;
  832. dataRow["FormData"] = form.FormData;
  833. var desc = new List<string>();
  834. foreach (var col in additionalColumns)
  835. {
  836. var val = row[col];
  837. if (val != null && val is not Guid)
  838. desc.Add(val.ToString() ?? "");
  839. }
  840. dataRow["Description"] = string.Join(" : ", desc);
  841. dataRow["Completed"] = form.FormCompleted;
  842. dataRow["Completed By"] = form.FormCompletedBy.UserID;
  843. if (IsEntityForm)
  844. dataRow["Processed"] = (bool?)row["Processed"] ?? false;
  845. if (ParentType == typeof(JobITP))
  846. {
  847. var jobITP = jobITPs!.Rows.FirstOrDefault(x => x.Get<JobITP, Guid>(x => x.ID) == form.ParentID());
  848. if(jobITP is not null)
  849. {
  850. var jobID = jobITP.Get<JobITP, Guid>(x => x.Job.ID);
  851. dataRow["Job No"] = Jobs.Where(x => x.ID == jobID).FirstOrDefault()?.JobNumber;
  852. }
  853. }
  854. if (pColumns != null)
  855. {
  856. foreach (var (field, name) in pColumns)
  857. {
  858. dataRow[name] = row[field]?.ToString();
  859. }
  860. }
  861. //datarow["Job No"] = (String)row[JobLink + ".JobNumber"];
  862. var bHasData = false;
  863. if (variables.Any())
  864. {
  865. var dict = Serialization.Deserialize<Dictionary<string, object>>(form.FormData);
  866. foreach (var key in dict.Keys)
  867. {
  868. var variable = variables.FirstOrDefault(x => string.Equals(key, x.Code));
  869. var type = variable?.FieldType();
  870. if (variable != null)
  871. {
  872. var value = variable.ParseValue(dict[key]);
  873. var format = variable.FormatValue(value);
  874. var sKey = key.Replace("/", " ");
  875. if (data.Columns.Contains(sKey))
  876. {
  877. dataRow[sKey] = format;
  878. bHasData = true;
  879. }
  880. }
  881. }
  882. }
  883. else
  884. {
  885. var dict = Serialization.Deserialize<Dictionary<Guid, object>>(form.FormData);
  886. foreach (var key in dict.Keys)
  887. if (data.Columns.Contains(key.ToString()))
  888. {
  889. dataRow[key.ToString()] = dict[key];
  890. bHasData = true;
  891. }
  892. }
  893. if (bHasData)
  894. data.Rows.Add(dataRow);
  895. }
  896. }
  897. DataGrid.ItemsSource = data;
  898. IsQAForm = !variables.Any() && questions.Any();
  899. QAGrid.Visibility = IsQAForm ? Visibility.Visible : Visibility.Collapsed;
  900. DataGrid.Visibility = Visibility.Visible;
  901. }
  902. private void RefreshData<TForm>()
  903. where TForm : Entity, IRemotable, IPersistent, IDigitalFormInstance, new()
  904. {
  905. var formQuery = GetFormQuery<TForm>();
  906. var queries = new List<IKeyedQueryDef>()
  907. {
  908. new KeyedQueryDef<QAQuestion>(new Filter<QAQuestion>(x => x.Form.ID).IsEqualTo(Form!.ID)),
  909. new KeyedQueryDef<DigitalFormVariable>(
  910. new Filter<DigitalFormVariable>(x => x.Form.ID).IsEqualTo(Form.ID),
  911. null,
  912. new SortOrder<DigitalFormVariable>(x => x.Sequence)),
  913. formQuery
  914. };
  915. if (ParentType == typeof(JobITPForm))
  916. {
  917. queries.Add(new KeyedQueryDef<JobITP>(
  918. new Filter<JobITP>(x => x.ID).InQuery((formQuery.Filter as Filter<JobITPForm>)!, x => x.Parent.ID),
  919. new Columns<JobITP>(x => x.ID, x => x.Job.JobNumber)));
  920. }
  921. var results = Client.QueryMultiple(queries);
  922. var questions = results.Get<QAQuestion>().ToList<QAQuestion>();
  923. var variables = results.Get<DigitalFormVariable>().ToList<DigitalFormVariable>();
  924. var formData = results.Get(formQuery.Key);
  925. LoadDataIntoGrid(
  926. variables, questions,
  927. formData,
  928. formQuery.Columns!.ColumnNames().Where(x => x != "ID" && x.StartsWith("Parent.")).ToList(),
  929. ParentType == typeof(JobITP) ? results.Get<JobITP>() : null);
  930. }
  931. public void Refresh()
  932. {
  933. Progress.Show("Refreshing");
  934. try
  935. {
  936. Questions.Clear();
  937. QAGrid.Clear();
  938. QAGrid.LoadChecks("", Array.Empty<QAQuestion>(), new Dictionary<Guid, object>());
  939. DataGrid.ItemsSource = null;
  940. if (ParentType is null || FormType is null || Form is null || Form.ID == Guid.Empty)
  941. {
  942. QAGrid.Visibility = Visibility.Collapsed;
  943. DataGrid.Visibility = Visibility.Collapsed;
  944. return;
  945. }
  946. Progress.SetMessage("Loading Data");
  947. var refreshMethod = typeof(DigitalFormsDashboard).GetMethod(nameof(RefreshData), BindingFlags.Instance | BindingFlags.NonPublic)!.MakeGenericMethod(FormType);
  948. refreshMethod.Invoke(this, Array.Empty<object?>());
  949. }
  950. finally
  951. {
  952. Progress.Close();
  953. }
  954. }
  955. public void Shutdown()
  956. {
  957. }
  958. #region DataGrid Configuration
  959. private void DataGrid_AutoGeneratingColumn(object sender, Syncfusion.UI.Xaml.Grid.AutoGeneratingColumnArgs e)
  960. {
  961. e.Column.TextAlignment = TextAlignment.Center;
  962. e.Column.HorizontalHeaderContentAlignment = HorizontalAlignment.Center;
  963. e.Column.ColumnSizer = GridLengthUnitType.None;
  964. var value = (e.Column.ValueBinding as Binding)!;
  965. if (value.Path.Path.Equals("ID") || value.Path.Path.Equals("Form_ID") || value.Path.Path.Equals("Parent_ID") ||
  966. value.Path.Path.Equals("FormData") || value.Path.Path.Equals("Location_Latitude") || value.Path.Path.Equals("Location_Longitude"))
  967. {
  968. e.Cancel = true;
  969. }
  970. else if (value.Path.Path.Equals("Location_Timestamp"))
  971. {
  972. e.Column = new GridImageColumn();
  973. e.Column.Width = DataGrid.RowHeight;
  974. e.Column.HeaderStyle = Resources["TemplateHeaderStyle"] as Style;
  975. e.Column.HeaderText = "";
  976. e.Column.Padding = new Thickness(4);
  977. e.Column.ValueBinding = new Binding
  978. {
  979. Path = new PropertyPath(value.Path.Path),
  980. Converter = new MileStoneImageConverter()
  981. };
  982. e.Column.MappingName = "Location.Timestamp";
  983. }
  984. else if (ParentType is not null && parentColumns.TryGetValue(ParentType, out var pColumns) && pColumns.Any(x => x.Item2.Equals(value.Path.Path)))
  985. {
  986. e.Column.ColumnSizer = GridLengthUnitType.Auto;
  987. e.Column.HeaderStyle = Resources["TemplateHeaderStyle"] as Style;
  988. }
  989. else if (value.Path.Path.Equals("Job No"))
  990. {
  991. e.Column.Width = 60;
  992. e.Column.HeaderStyle = Resources["TemplateHeaderStyle"] as Style;
  993. }
  994. else if (value.Path.Path.Equals("Description"))
  995. {
  996. e.Column.TextAlignment = TextAlignment.Left;
  997. e.Column.HorizontalHeaderContentAlignment = HorizontalAlignment.Left;
  998. e.Column.Width = 450;
  999. e.Column.HeaderStyle = Resources["TemplateHeaderStyle"] as Style;
  1000. }
  1001. else if (value.Path.Path.Equals("Completed"))
  1002. {
  1003. e.Column.Width = 100;
  1004. e.Column.HeaderStyle = Resources["TemplateHeaderStyle"] as Style;
  1005. (e.Column as GridDateTimeColumn)!.Pattern = DateTimePattern.CustomPattern;
  1006. (e.Column as GridDateTimeColumn)!.CustomPattern = "dd MMM yy hh:mm";
  1007. }
  1008. else if (value.Path.Path.Equals("Completed By"))
  1009. {
  1010. e.Column.Width = 100;
  1011. e.Column.HeaderStyle = Resources["TemplateHeaderStyle"] as Style;
  1012. }
  1013. else if (value.Path.Path.Equals("Processed"))
  1014. {
  1015. e.Column.Width = 100;
  1016. e.Column.HeaderStyle = Resources["TemplateHeaderStyle"] as Style;
  1017. }
  1018. else
  1019. {
  1020. var data = DataGrid.ItemsSource as DataTable;
  1021. //int index = data.Columns.IndexOf(e.Column.MappingName) - 2;
  1022. //Style style = new Style(typeof(GridCell));
  1023. //e.Column.CellStyle = style;
  1024. e.Column.Width = 100;
  1025. e.Column.HeaderStyle = Resources["TemplateHeaderStyle"] as Style;
  1026. e.Column.HeaderText = QuestionCodes[e.Column.MappingName];
  1027. }
  1028. }
  1029. private Entity? GetEntityForm<T>(Guid id) where T : Entity, IDigitalFormInstance, IRemotable, IPersistent, new()
  1030. {
  1031. var columns = new Columns<T>(x => x.ID)
  1032. .Add(x => x.FormCompleted)
  1033. .Add(x => x.FormData)
  1034. .Add(x => x.Form.ID)
  1035. .Add(x => x.Form.Description);
  1036. if (typeof(T).HasInterface(typeof(IDigitalFormInstance<>)))
  1037. {
  1038. columns.Add("Parent.ID");
  1039. }
  1040. return new Client<T>().Query(
  1041. new Filter<T>(x => x.ID).IsEqualTo(id),
  1042. columns).Rows.FirstOrDefault()?.ToObject<T>();
  1043. }
  1044. private void DataGrid_CellDoubleTapped(object sender, Syncfusion.UI.Xaml.Grid.GridCellDoubleTappedEventArgs e)
  1045. {
  1046. if (e.RowColumnIndex.RowIndex == 0)
  1047. return;
  1048. var table = (DataGrid.ItemsSource as DataTable)!;
  1049. var formid = (Guid)table.Rows[e.RowColumnIndex.RowIndex - 1]["Form_ID"];
  1050. var formdata = (string)table.Rows[e.RowColumnIndex.RowIndex - 1]["FormData"];
  1051. var id = (Guid)table.Rows[e.RowColumnIndex.RowIndex - 1]["ID"];
  1052. if (FormType is null) return;
  1053. if (IsQAForm)
  1054. {
  1055. var formData = Serialization.Deserialize<Dictionary<string, object>>(formdata);
  1056. var values = new Dictionary<Guid, object>();
  1057. foreach(var (idStr, value) in formData)
  1058. {
  1059. if(Guid.TryParse(idStr, out var codeID))
  1060. {
  1061. values[codeID] = value;
  1062. }
  1063. }
  1064. QAGrid.Clear();
  1065. QAGrid.LoadChecks(Form!.Description, Questions, values);
  1066. QAGrid.CollapseMargins();
  1067. return;
  1068. }
  1069. var entityForm = typeof(DigitalFormsDashboard)
  1070. .GetMethod(nameof(GetEntityForm), BindingFlags.NonPublic | BindingFlags.Instance)!
  1071. .MakeGenericMethod(FormType)
  1072. .Invoke(this, new object[] { id }) as IDigitalFormInstance;
  1073. if (entityForm is not null)
  1074. {
  1075. if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel))
  1076. {
  1077. dataModel.Update(null);
  1078. /*typeof(QADashboard)
  1079. .GetMethod(nameof(SaveEntityForm), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!
  1080. .MakeGenericMethod(formType)
  1081. .Invoke(this, new object[] { entityForm });*/
  1082. Refresh();
  1083. }
  1084. }
  1085. }
  1086. private void DataGrid_CellTapped(object sender, Syncfusion.UI.Xaml.Grid.GridCellTappedEventArgs e)
  1087. {
  1088. if (e.RowColumnIndex.ColumnIndex == 0)
  1089. {
  1090. var timestamp = (DateTime)(e.Record as DataRowView)!.Row["Location_Timestamp"];
  1091. var latitude = (double)(e.Record as DataRowView)!.Row["Location_Latitude"];
  1092. var longitude = (double)(e.Record as DataRowView)!.Row["Location_Longitude"];
  1093. var form = new MapForm(latitude, longitude, timestamp);
  1094. form.ShowDialog();
  1095. }
  1096. }
  1097. #endregion
  1098. }
  1099. }