DigitalFormsDashboard.xaml.cs 49 KB

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