DigitalFormsDashboard.xaml.cs 49 KB

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