DigitalFormsDashboard.xaml.cs 52 KB

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