Browse Source

Removed QADashboard and added DashboardContainerPanel to remove duplicate code and instead delegate through the actual dashboard.
Fixed issue where reports didn't exist on forms panel.

Kenric Nugteren 1 year ago
parent
commit
86559b1601

+ 87 - 25
prs.desktop/Dashboards/Common/DigitalFormsDashboard.xaml.cs

@@ -1,5 +1,4 @@
-using com.sun.tools.doclets.formats.html;
-using Comal.Classes;
+using Comal.Classes;
 using InABox.Clients;
 using InABox.Core;
 using InABox.DynamicGrid;
@@ -12,7 +11,6 @@ using PRSDesktop.Forms;
 using PRSDesktop.WidgetGroups;
 using Syncfusion.UI.Xaml.Grid;
 using Syncfusion.UI.Xaml.Grid.Converter;
-using Syncfusion.Windows.Shared;
 using Syncfusion.XlsIO;
 using System;
 using System.Collections;
@@ -33,13 +31,12 @@ using System.Windows.Data;
 using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
 using InABox.Configuration;
 using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs;
 using InABox.Wpf.Reports;
 using System.ComponentModel;
+using Syncfusion.Windows.Shared;
+using System.Globalization;
 
 namespace PRSDesktop
 {
@@ -67,6 +64,19 @@ namespace PRSDesktop
         public string Filter { get; set; } = "";
     }
 
+    internal class MileStoneImageConverter : IValueConverter
+    {
+        public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return Equals(value, DateTime.MinValue) ? null : Resources.milestone.AsBitmapImage();
+        }
+
+        public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return null;
+        }
+    }
+
     public class DigitalFormsDashboardProperties : IUserConfigurationSettings, IDashboardProperties
     {
         public bool ShowJobFilter { get; set; } = false;
@@ -91,8 +101,9 @@ namespace PRSDesktop
     /// <summary>
     /// Interaction logic for DigitalFormsDashboard.xaml
     /// </summary>
-    public partial class DigitalFormsDashboard : UserControl, 
-        IDashboardWidget<Common, DigitalFormsDashboardProperties>, 
+    public partial class DigitalFormsDashboard : UserControl,
+        IDashboardWidget<Common, DigitalFormsDashboardProperties>,
+        IBasePanel,
         IRequiresSecurity<CanViewDigitalFormsDashbaord>,
         IHeaderDashboard, IActionsDashboard
     {
@@ -177,7 +188,8 @@ namespace PRSDesktop
 
         public void SetupHeader()
         {
-            CategoryBox = new ComboBox {
+            CategoryBox = new ComboBox
+            {
                 Width = 150,
                 VerticalContentAlignment = VerticalAlignment.Center,
                 Margin = new Thickness(0, 0, 5, 0)
@@ -222,7 +234,8 @@ namespace PRSDesktop
             DateTypeBox.SelectionChanged += DateTypeBox_SelectionChanged;
 
             FromLabel = new Label { Content = "From", VerticalContentAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 5, 0) };
-            FromPicker = new DatePicker {
+            FromPicker = new DatePicker
+            {
                 Width = 100,
                 Background = new SolidColorBrush(Colors.LightYellow),
                 VerticalContentAlignment = VerticalAlignment.Center,
@@ -378,7 +391,7 @@ namespace PRSDesktop
         {
             var filterType = (DateFilterType)DateTypeBox.SelectedValue;
             Properties.DateFilterType = filterType;
-            if(filterType == DateFilterType.Custom)
+            if (filterType == DateFilterType.Custom)
             {
                 if (FromPicker.SelectedDate == null || FromPicker.SelectedDate == DateTime.MinValue)
                 {
@@ -441,7 +454,7 @@ namespace PRSDesktop
                 forms.Insert(0, new DigitalForm { ID = Guid.Empty, Description = "Select Form" });
                 FormBox.ItemsSource = forms;
 
-                if(Properties.SelectedForm != Guid.Empty && forms.Where(x => x.ID == Properties.SelectedForm).FirstOrDefault() is DigitalForm form)
+                if (Properties.SelectedForm != Guid.Empty && forms.Where(x => x.ID == Properties.SelectedForm).FirstOrDefault() is DigitalForm form)
                 {
                     FormBox.SelectedItem = form;
                 }
@@ -454,6 +467,8 @@ namespace PRSDesktop
 
                 FormBox.IsEnabled = true;
             }
+
+            OnUpdateDataModel?.Invoke(SectionName, DataModel(Selection.None));
         }
 
         private void Category_SelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -466,12 +481,36 @@ namespace PRSDesktop
         {
             Form = FormBox.SelectedValue as DigitalForm;
             Properties.SelectedForm = Form?.ID ?? Guid.Empty;
+
+            OnUpdateDataModel?.Invoke(SectionName, DataModel(Selection.None));
+
             Refresh();
         }
 
         #endregion
 
-        private string SectionName
+        #region IBasePanel
+
+        public bool IsReady { get; set; }
+
+        public event DataModelUpdateEvent? OnUpdateDataModel;
+
+        public void CreateToolbarButtons(IPanelHost host)
+        {
+        }
+
+        public void Heartbeat(TimeSpan time)
+        {
+        }
+
+        public Dictionary<string, object[]> Selected()
+        {
+            return new Dictionary<string, object[]>();
+        }
+
+        #endregion
+
+        public string SectionName
         {
             get
             {
@@ -480,9 +519,9 @@ namespace PRSDesktop
                 return Form.ID.ToString() ?? "Digital Forms";
             }
         }
-        private DataModel DataModel(Selection selection)
+        public DataModel DataModel(Selection selection)
         {
-            if(FormType is null || Form is null || Form.ID == Guid.Empty)
+            if (FormType is null || Form is null || Form.ID == Guid.Empty)
             {
                 return new AutoDataModel<DigitalForm>(new Filter<DigitalForm>().None());
             }
@@ -871,7 +910,7 @@ namespace PRSDesktop
                 .And(x => x.FormCompleted).IsLessThan(To.AddDays(1))
                 .And(x => x.Form.ID).IsEqualTo(Form!.ID)
                 .And(x => x.FormCancelled).IsEqualTo(DateTime.MinValue);
-            
+
             if (Properties.JobID != Guid.Empty && Properties.ShowJobFilter)
             {
                 filter.And(jobLink + ".ID").IsEqualTo(Properties.JobID);
@@ -882,18 +921,20 @@ namespace PRSDesktop
             }
 
             var columns = new Columns<T>(x => x.ID)
+                .Add(x => x.Number)
+                .Add(x => x.CreatedBy)
+                .Add(x => x.Created)
                 .Add(x => x.Form.ID)
                 .Add(x => x.FormData)
                 .Add(x => x.FormCompleted)
                 .Add(x => x.FormCompletedBy.UserID)
-                .Add(x => x.FormProcessed)
                 .Add(x => x.Location.Timestamp)
                 .Add(x => x.Location.Latitude)
                 .Add(x => x.Location.Longitude);
 
             if (IsEntityForm)
                 columns.Add(x => x.FormProcessed); //"Processed");
-            
+
             var parentcols = LookupFactory.DefineColumns(ParentType!);
             foreach (var col in parentcols.ColumnNames())
                 columns.Add("Parent." + col);
@@ -923,6 +964,7 @@ namespace PRSDesktop
             data.Columns.Add("Location_Latitude", typeof(double));
             data.Columns.Add("Location_Longitude", typeof(double));
             data.Columns.Add("FormData", typeof(string));
+            data.Columns.Add("Number", typeof(string));
 
             if (ParentType == typeof(JobITP))
             {
@@ -937,8 +979,8 @@ namespace PRSDesktop
             }
 
             data.Columns.Add("Description", typeof(string));
-            data.Columns.Add("Created By", typeof(string));
             data.Columns.Add("Created", typeof(DateTime));
+            data.Columns.Add("Created By", typeof(string));
             data.Columns.Add("Completed", typeof(DateTime));
             data.Columns.Add("Completed By", typeof(string));
             if (IsEntityForm)
@@ -954,7 +996,7 @@ namespace PRSDesktop
                     {
                         data.Columns.Add(code, typeof(string));
                     }
-                    catch(DuplicateNameException)
+                    catch (DuplicateNameException)
                     {
                         MessageBox.Show($"Error: duplicate variable code {code}");
                     }
@@ -996,6 +1038,7 @@ namespace PRSDesktop
                     dataRow["Location_Latitude"] = form.Location.Latitude;
                     dataRow["Location_Longitude"] = form.Location.Longitude;
                     dataRow["FormData"] = form.FormData;
+                    dataRow["Number"] = form.Number;
 
                     var desc = new List<string>();
                     foreach (var col in additionalColumns)
@@ -1007,16 +1050,18 @@ namespace PRSDesktop
 
                     dataRow["Description"] = string.Join(" : ", desc);
 
+                    dataRow["Created"] = (form as Entity)!.Created;
+                    dataRow["Created By"] = (form as Entity)!.CreatedBy;
                     dataRow["Completed"] = form.FormCompleted;
                     dataRow["Completed By"] = form.FormCompletedBy.UserID;
 
                     if (IsEntityForm)
-                        dataRow["Processed"] = ((DateTime?)row["FormProcessed"] ?? DateTime.MinValue) > DateTime.MinValue;
+                        dataRow["Processed"] = form.FormProcessed > DateTime.MinValue;
 
                     if (ParentType == typeof(JobITP))
                     {
                         var jobITP = jobITPs!.Rows.FirstOrDefault(x => x.Get<JobITP, Guid>(x => x.ID) == form.ParentID());
-                        if(jobITP is not null)
+                        if (jobITP is not null)
                         {
                             var jobID = jobITP.Get<JobITP, Guid>(x => x.Job.ID);
                             dataRow["Job No"] = Jobs.Where(x => x.ID == jobID).FirstOrDefault()?.JobNumber;
@@ -1037,7 +1082,7 @@ namespace PRSDesktop
                     if (variables.Any())
                     {
                         var dict = Serialization.Deserialize<Dictionary<string, object?>>(form.FormData);
-                        if(dict is not null)
+                        if (dict is not null)
                         {
                             var storage = new DFLoadStorage(dict, null);
                             foreach (var variable in variables)
@@ -1056,7 +1101,7 @@ namespace PRSDesktop
                     else
                     {
                         var dict = Serialization.Deserialize<Dictionary<Guid, object>>(form.FormData);
-                        if(dict is not null)
+                        if (dict is not null)
                             foreach (var key in dict.Keys)
                                 if (data.Columns.Contains(key.ToString()))
                                 {
@@ -1152,6 +1197,11 @@ namespace PRSDesktop
             {
                 e.Cancel = true;
             }
+            else if (value.Path.Path.Equals("Number"))
+            {
+                e.Column.Width = 80;
+                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
+            }
             else if (value.Path.Path.Equals("Location_Timestamp"))
             {
                 e.Column = new GridImageColumn();
@@ -1200,6 +1250,18 @@ namespace PRSDesktop
                 e.Column.Width = 100;
                 e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
             }
+            else if (value.Path.Path.Equals("Created By"))
+            {
+                e.Column.Width = 100;
+                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
+            }
+            else if (value.Path.Path.Equals("Created"))
+            {
+                e.Column.Width = 100;
+                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
+                (e.Column as GridDateTimeColumn).Pattern = DateTimePattern.CustomPattern;
+                (e.Column as GridDateTimeColumn).CustomPattern = "dd MMM yy hh:mm";
+            }
             else
             {
                 var data = DataGrid.ItemsSource as DataTable;
@@ -1238,7 +1300,7 @@ namespace PRSDesktop
             {
                 var values = new Dictionary<Guid, object>();
                 var formData = Serialization.Deserialize<Dictionary<string, object>>(formdata);
-                if(formData is not null)
+                if (formData is not null)
                 {
                     foreach (var (idStr, value) in formData)
                     {

+ 1 - 1
prs.desktop/Dashboards/DashboardContainer.xaml.cs

@@ -107,7 +107,7 @@ namespace PRSDesktop
                 actions.BuildActionsMenu(menu);
             }
 
-            if (menu.Items[menu.Items.Count - 1] is Separator separator)
+            if (menu.Items[^1] is Separator separator)
             {
                 menu.Items.Remove(separator);
             }

+ 178 - 0
prs.desktop/Dashboards/DashboardContainerPanel.cs

@@ -0,0 +1,178 @@
+using AvalonDock.Controls;
+using FastReport.Export.Dxf.Sections;
+using InABox.Configuration;
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace PRSDesktop;
+
+public class DashboardContainerPanel<TDashboard, TGroup, TProperties> : UserControl, IBasePanel
+    where TDashboard : IDashboardWidget<TGroup, TProperties>, IBasePanel, new()
+    where TGroup : DashboardWidgetGroup
+    where TProperties : IUserConfigurationSettings, IDashboardProperties, new()
+{
+    public TDashboard Dashboard { get; set; }
+
+    private StackPanel HeaderItemsLeft;
+    private StackPanel HeaderItemsRight;
+
+    public DashboardContainerPanel()
+    {
+        Dashboard = new TDashboard
+        {
+            Properties = new UserConfiguration<TProperties>().Load()
+        };
+
+        var border = new Border
+        {
+            BorderBrush = new SolidColorBrush(Colors.Gray),
+            BorderThickness = new Thickness(0)
+        };
+
+        var grid = new Grid();
+        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
+        grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
+
+        var headerBorder = new Border
+        {
+            BorderBrush = new SolidColorBrush(Colors.Gray),
+            BorderThickness = new Thickness(0.75),
+            CornerRadius = new CornerRadius(5, 5, 0, 0),
+            Background = new SolidColorBrush(Colors.WhiteSmoke)
+        };
+
+        var dock = new DockPanel
+        {
+            Background = new SolidColorBrush(Colors.Transparent)
+        };
+
+        HeaderItemsRight = new StackPanel
+        {
+            Margin = new Thickness(5),
+            Orientation = Orientation.Horizontal
+        };
+
+        HeaderItemsLeft = new StackPanel
+        {
+            Margin = new Thickness(5),
+            Orientation = Orientation.Horizontal
+        };
+
+        DockPanel.SetDock(HeaderItemsRight, Dock.Right);
+        dock.Children.Add(HeaderItemsRight);
+        DockPanel.SetDock(HeaderItemsLeft, Dock.Left);
+        dock.Children.Add(HeaderItemsLeft);
+
+        headerBorder.Child = dock;
+
+        var content = new ContentControl
+        {
+            Margin = new Thickness(0, 2, 0, 0),
+            Content = Dashboard
+        };
+
+        Grid.SetRow(headerBorder, 0);
+        Grid.SetRow(content, 1);
+
+        grid.Children.Add(headerBorder);
+        grid.Children.Add(content);
+
+        border.Child = grid;
+
+        Content = border;
+
+        if (Dashboard is IHeaderDashboard headerDashboard)
+        {
+            RefreshHeader(headerDashboard.Header);
+
+            headerDashboard.Header.HeaderChanged += Header_HeaderChanged;
+        }
+    }
+
+    private void RefreshHeader(DashboardHeader header)
+    {
+        if (HeaderItemsLeft.Children.Count > 0)
+        {
+            HeaderItemsLeft.Children.Clear();
+        }
+        if (HeaderItemsRight.Children.Count > 0)
+        {
+            HeaderItemsRight.Children.Clear();
+        }
+
+        foreach (var item in header.GetLeftElements())
+        {
+            HeaderItemsLeft.Children.Add(item);
+        }
+        foreach (var item in header.GetRightElements())
+        {
+            HeaderItemsRight.Children.Add(item);
+        }
+    }
+
+    private void Header_HeaderChanged(DashboardHeader header)
+    {
+        RefreshHeader(header);
+    }
+
+    #region IBasePanel
+
+    public bool IsReady { get => Dashboard.IsReady; set => Dashboard.IsReady = true; }
+
+    public string SectionName => Dashboard.SectionName;
+
+    public event DataModelUpdateEvent? OnUpdateDataModel
+    {
+        add => Dashboard.OnUpdateDataModel += value;
+        remove => Dashboard.OnUpdateDataModel -= value;
+    }
+
+    public void CreateToolbarButtons(IPanelHost host)
+    {
+        Dashboard.CreateToolbarButtons(host);
+    }
+
+    public DataModel DataModel(Selection selection)
+    {
+        return Dashboard.DataModel(selection);
+    }
+
+    public void Heartbeat(TimeSpan time)
+    {
+        Dashboard.Heartbeat(time);
+    }
+
+    public void Refresh()
+    {
+        Dashboard.Refresh();
+    }
+
+    public Dictionary<string, object[]> Selected()
+    {
+        return Dashboard.Selected();
+    }
+
+    public void Setup()
+    {
+        Dashboard.Setup();
+    }
+
+    public void Shutdown(CancelEventArgs? cancel)
+    {
+        Dashboard.Shutdown(cancel);
+        if(cancel?.Cancel != true)
+        {
+            new UserConfiguration<TProperties>().Save(Dashboard.Properties);
+        }
+    }
+
+    #endregion
+}

+ 4 - 0
prs.desktop/MainWindow.xaml

@@ -839,6 +839,10 @@
                                    LargeIcon="pack://application:,,,/Resources/checklist.png"
                                    Click="DigitalFormsCompletedFormsButton_Click" MinWidth="60" />
                 </fluent:RibbonGroupBox>
+
+                <fluent:RibbonGroupBox x:Name="DigitalFormReports" Width="Auto" MinWidth="60" Header="Print"
+                                       LauncherClick="ManageReportsMenu_Click" Visibility="Collapsed"
+                                       IsLauncherVisible="False" />
             </fluent:RibbonTabItem>
 
             <fluent:RibbonTabItem x:Name="DashboardsTab" Header="Dashboards" IsSelected="False" Visibility="Collapsed">

+ 1 - 1
prs.desktop/MainWindow.xaml.cs

@@ -2918,7 +2918,7 @@ namespace PRSDesktop
 
         private void DigitalFormsCompletedFormsButton_Click(object sender, RoutedEventArgs e)
         {
-            LoadWindow<QADashboard>((Fluent.Button)sender);
+            LoadWindow<CompletedFormsPanel>((Fluent.Button)sender);
         }
 
         #endregion

+ 13 - 0
prs.desktop/Panels/DigitalForms/CompletedFormsPanel.cs

@@ -0,0 +1,13 @@
+using InABox.Core;
+using PRSDesktop.WidgetGroups;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PRSDesktop;
+
+public class CompletedFormsPanel : DashboardContainerPanel<DigitalFormsDashboard, Common, DigitalFormsDashboardProperties>, IPanel<DigitalForm>
+{
+}

+ 0 - 125
prs.desktop/Panels/DigitalForms/QADashboard.xaml

@@ -1,125 +0,0 @@
-<UserControl x:Class="PRSDesktop.QADashboard"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-             xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
-             xmlns:dynamicgrid="clr-namespace:InABox.DynamicGrid;assembly=InABox.Wpf"
-             xmlns:wpf="clr-namespace:InABox.WPF;assembly=InABox.Wpf"
-             mc:Ignorable="d"
-             d:DesignHeight="450" d:DesignWidth="1500">
-
-    <UserControl.Resources>
-
-        <!-- ~1~<local:LeaveContentConverter x:Key="LeaveContentConverter"/>@1@ -->
-        <!-- <Style x:Key="DataContentCellStyle" TargetType="{x:Type syncfusion:GridCell}"> -->
-        <!--     ~1~<Setter Property="TextBlock.Text" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource LeaveContentConverter}}" />@1@ -->
-        <!-- </Style> -->
-
-    </UserControl.Resources>
-
-
-    <Grid Background="WhiteSmoke">
-        <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="*" />
-        </Grid.ColumnDefinitions>
-        <Grid.RowDefinitions>
-            <RowDefinition Height="42" />
-            <RowDefinition Height="*" />
-        </Grid.RowDefinitions>
-
-        <Border Grid.Row="0" CornerRadius="5,5,0,0" BorderBrush="Gray" BorderThickness="0.75" Background="WhiteSmoke"
-                Padding="5" Margin="0,0,0,2">
-
-            <DockPanel HorizontalAlignment="Stretch">
-
-                <ComboBox x:Name="Category" DockPanel.Dock="Left" Width="150" SelectedIndex="0"
-                          SelectionChanged="Category_SelectionChanged" VerticalContentAlignment="Center"
-                          Margin="0,0,5,0" SelectedValuePath="Key" DisplayMemberPath="Value" />
-
-                <ComboBox x:Name="Form" DockPanel.Dock="Left" Width="250" SelectedIndex="0"
-                          SelectionChanged="Form_SelectionChanged" VerticalContentAlignment="Center" Margin="0,0,5,0"
-                          SelectedValuePath="Key" DisplayMemberPath="Value" />
-
-                <ComboBox x:Name="Jobs" DockPanel.Dock="Left" Width="250" SelectedIndex="0"
-                          SelectionChanged="Jobs_SelectionChanged" VerticalContentAlignment="Center" Margin="0,0,5,0"
-                          SelectedValuePath="Key" DisplayMemberPath="Value" />
-
-                <ComboBox x:Name="DateRange" DockPanel.Dock="Left" Width="120" SelectedIndex="0"
-                          SelectionChanged="DateRange_SelectionChanged" VerticalContentAlignment="Center"
-                          Margin="0,0,5,0">
-                    <ComboBox.Items>
-                        <ComboBoxItem Content="Today" />
-                        <ComboBoxItem Content="Yesterday" />
-                        <ComboBoxItem Content="Week To Date" />
-                        <ComboBoxItem Content="Last 7 Days" />
-                        <ComboBoxItem Content="Month To Date" />
-                        <ComboBoxItem Content="Last 30 Days" />
-                        <ComboBoxItem Content="Year To Date" />
-                        <ComboBoxItem Content="Last 12 Months" />
-                        <ComboBoxItem Content="Custom" />
-                    </ComboBox.Items>
-                </ComboBox>
-
-                <Label DockPanel.Dock="Left" VerticalContentAlignment="Center" Content="From" Margin="0,0,5,0" />
-                <DatePicker DockPanel.Dock="Left" x:Name="FromDate" Width="100" Background="LightYellow"
-                            SelectedDateChanged="FromDate_SelectedDateChanged" VerticalContentAlignment="Center"
-                            FirstDayOfWeek="Monday" IsEnabled="False" />
-
-                <Label DockPanel.Dock="Left" VerticalContentAlignment="Center" Content="To" Margin="5,0,5,0" />
-                <DatePicker DockPanel.Dock="Left" x:Name="ToDate" Width="100" Background="LightYellow"
-                            SelectedDateChanged="ToDate_SelectedDateChanged" VerticalContentAlignment="Center"
-                            FirstDayOfWeek="Monday" IsEnabled="False" />
-
-                <Button DockPanel.Dock="Right" x:Name="Export" Content="Export" Click="Export_Click" Margin="5,0,0,0"
-                        Padding="5,0,5,0" Width="60" />
-
-                <Label DockPanel.Dock="Left" VerticalContentAlignment="Center" Content="Search" Margin="0,0,5,0" />
-                <TextBox x:Name="Search" HorizontalAlignment="Stretch" VerticalContentAlignment="Center"
-                         KeyUp="Search_KeyUp" />
-
-
-            </DockPanel>
-
-        </Border>
-
-        <Border Grid.Row="1" CornerRadius="0" BorderBrush="Gray" BorderThickness="0.75" Background="WhiteSmoke"
-                Padding="0" Margin="0">
-
-            <Grid>
-                <Grid.ColumnDefinitions>
-                    <ColumnDefinition Width="Auto" />
-                    <ColumnDefinition Width="*" />
-                </Grid.ColumnDefinitions>
-
-                <wpf:QAGrid x:Name="qaGrid" Grid.Column="0" BorderThickness="0" MaxWidth="500" />
-
-                <DockPanel x:Name="DigitalFormsDock" Grid.Column="1" MaxWidth="500">
-                    <DockPanel DockPanel.Dock="Bottom" Margin="0,5,0,0">
-                        <Label Content="Layout Type" DockPanel.Dock="Left" />
-                        <ComboBox DockPanel.Dock="Left" />
-                    </DockPanel>
-                    <dynamicgrid:DynamicFormDesignGrid DockPanel.Dock="Top" />
-                </DockPanel>
-
-                <syncfusion:SfDataGrid
-                    Background="White"
-                    x:Name="dataGrid"
-                    Grid.Column="1"
-                    AutoGenerateColumns="True"
-                    AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
-                    RowHeight="30"
-                    AllowSorting="False"
-                    SelectionUnit="Row"
-                    NavigationMode="Cell"
-                    SelectionMode="Extended"
-                    Margin="0,5,5,5"
-                    CellDoubleTapped="DataGrid_CellDoubleTapped"
-                    CellTapped="DataGrid_CellTapped" />
-
-            </Grid>
-        </Border>
-
-    </Grid>
-
-</UserControl>

+ 0 - 936
prs.desktop/Panels/DigitalForms/QADashboard.xaml.cs

@@ -1,936 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Data;
-using System.Diagnostics;
-using System.Globalization;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Input;
-using Comal.Classes;
-using InABox.Clients;
-using InABox.Core;
-using InABox.DynamicGrid;
-using InABox.Reports;
-using InABox.Core.Reports;
-using InABox.WPF;
-using PRSDesktop.Forms;
-using PRSDesktop.WidgetGroups;
-using Syncfusion.UI.Xaml.Grid;
-using Syncfusion.UI.Xaml.Grid.Converter;
-using Syncfusion.Windows.Shared;
-using Syncfusion.XlsIO;
-using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs;
-using System.ComponentModel;
-using Columns = InABox.Core.Columns;
-
-namespace PRSDesktop
-{
-    internal class MileStoneImageConverter : IValueConverter
-    {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return Equals(value, DateTime.MinValue) ? null : Resources.milestone.AsBitmapImage();
-        }
-
-        public object ConvertBack(object value, Type targetType,
-            object parameter, CultureInfo culture)
-        {
-            return null;
-        }
-    }
-
-    /// <summary>
-    ///     Interaction logic for QADashboard.xaml
-    /// </summary>
-    public partial class QADashboard : UserControl, IPanel<DigitalForm>
-    {
-        private bool _changing;
-
-        private DateTime _from;
-
-        private string _search = "";
-        private DateTime _to;
-
-        private CoreTable allforms;
-
-        private readonly Dictionary<string, string> QuestionCodes = new();
-
-        public bool IsReady { get; set; }
-
-        public event DataModelUpdateEvent? OnUpdateDataModel;
-
-        private static readonly Dictionary<Type, List<Tuple<string, string>>> parentColumns = new()
-        {
-            { typeof(Kanban), new() { new("Parent.Number", "Task No") } },
-            { typeof(Job), new() { new("Parent.JobNumber", "Job No") } },
-            { typeof(JobITP), new() { new("Parent.Code", "Code") } },
-            { typeof(Assignment), new() { new("Parent.Number", "Ass. No") } },
-            { typeof(TimeSheet), new() { } },
-            { typeof(LeaveRequest), new() { } },
-            { typeof(Employee), new() { new("Parent.Code", "Employee") } },
-            { typeof(PurchaseOrderItem), new() { new("Parent.PONumber", "PO No") } },
-        };
-
-        private Type? parentType;
-        private Type? formType;
-
-        #region DataGrid Configuration
-
-        private void DataGrid_AutoGeneratingColumn(object sender, AutoGeneratingColumnArgs e)
-        {
-            e.Column.TextAlignment = TextAlignment.Center;
-            e.Column.HorizontalHeaderContentAlignment = HorizontalAlignment.Center;
-            e.Column.ColumnSizer = GridLengthUnitType.None;
-
-            var value = e.Column.ValueBinding as Binding;
-            if (value.Path.Path.Equals("ID") || value.Path.Path.Equals("Form_ID") || value.Path.Path.Equals("Parent_ID") ||
-                value.Path.Path.Equals("FormData") || value.Path.Path.Equals("Location_Latitude") || value.Path.Path.Equals("Location_Longitude"))
-            {
-                e.Cancel = true;
-            }
-            else if (value.Path.Path.Equals("Number"))
-            {
-                e.Column.Width = 80;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-            }
-            else if (value.Path.Path.Equals("Location_Timestamp"))
-            {
-                e.Column = new GridImageColumn();
-                e.Column.Width = dataGrid.RowHeight;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-                e.Column.HeaderText = "";
-                e.Column.Padding = new Thickness(4);
-                e.Column.ValueBinding = new Binding
-                {
-                    Path = new PropertyPath(value.Path.Path),
-                    Converter = new MileStoneImageConverter()
-                };
-                e.Column.MappingName = "Location.Timestamp";
-            }
-            else if (parentColumns.TryGetValue(parentType, out var pColumns) && pColumns.Any(x => x.Item2.Equals(value.Path.Path)))
-            {
-                e.Column.ColumnSizer = GridLengthUnitType.Auto;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-            }
-            else if (value.Path.Path.Equals("Job No"))
-            {
-                e.Column.Width = 60;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-            }
-            else if (value.Path.Path.Equals("Description"))
-            {
-                e.Column.TextAlignment = TextAlignment.Left;
-                e.Column.HorizontalHeaderContentAlignment = HorizontalAlignment.Left;
-                e.Column.Width = 450;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-            }
-            else if (value.Path.Path.Equals("Completed"))
-            {
-                e.Column.Width = 100;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-                (e.Column as GridDateTimeColumn).Pattern = DateTimePattern.CustomPattern;
-                (e.Column as GridDateTimeColumn).CustomPattern = "dd MMM yy hh:mm";
-            }
-            else if (value.Path.Path.Equals("Completed By"))
-            {
-                e.Column.Width = 100;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-            }
-            else if (value.Path.Path.Equals("Processed"))
-            {
-                e.Column.Width = 100;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-            }
-            else if (value.Path.Path.Equals("Created By"))
-            {
-                e.Column.Width = 100;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-            }
-            else if (value.Path.Path.Equals("Created"))
-            {
-                e.Column.Width = 100;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-                (e.Column as GridDateTimeColumn).Pattern = DateTimePattern.CustomPattern;
-                (e.Column as GridDateTimeColumn).CustomPattern = "dd MMM yy hh:mm";
-            }
-            else
-            {
-                var data = dataGrid.ItemsSource as DataTable;
-                //int index = data.Columns.IndexOf(e.Column.MappingName) - 2;
-                //Style style = new Style(typeof(GridCell));
-                //e.Column.CellStyle = style;
-                e.Column.Width = 100;
-                e.Column.HeaderStyle = Application.Current.Resources["TemplateHeaderStyle"] as Style;
-                e.Column.HeaderText = QuestionCodes[e.Column.MappingName];
-            }
-        }
-
-        #endregion
-
-        private Entity? GetEntityForm<T>(Guid id) where T : Entity, IDigitalFormInstance, IRemotable, IPersistent, new()
-        {
-            var columns = DynamicFormEditWindow.FormColumns<T>();
-
-            return new Client<T>().Query(
-                new Filter<T>(x => x.ID).IsEqualTo(id),
-                columns).Rows.FirstOrDefault()?.ToObject<T>();
-        }
-
-        private void SaveEntityForm<T>(T entityForm) where T : Entity, IDigitalFormInstance, IRemotable, IPersistent, new()
-        {
-            new Client<T>().Save(entityForm, "Edited by user");
-        }
-
-        private void DataGrid_CellDoubleTapped(object sender, GridCellDoubleTappedEventArgs e)
-        {
-            if (e.RowColumnIndex.RowIndex == 0)
-                return;
-            var table = dataGrid.ItemsSource as DataTable;
-            var formid = (Guid)table.Rows[e.RowColumnIndex.RowIndex - 1]["Form_ID"];
-            var formdata = (string)table.Rows[e.RowColumnIndex.RowIndex - 1]["FormData"];
-            var id = (Guid)table.Rows[e.RowColumnIndex.RowIndex - 1]["ID"];
-
-            if (formType == null) return;
-
-            IDigitalFormInstance? entityForm = null;
-            Progress.ShowModal("Loading Form", (s) =>
-            {
-                entityForm = typeof(QADashboard)
-                    .GetMethod(nameof(GetEntityForm), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!
-                    .MakeGenericMethod(formType)
-                    .Invoke(this, new object[] { id }) as IDigitalFormInstance;
-            });
-
-            if (entityForm != null)
-            {
-                if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel))
-                {
-                    dataModel.Update(null);
-                    /*typeof(QADashboard)
-                        .GetMethod(nameof(SaveEntityForm), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!
-                        .MakeGenericMethod(formType)
-                        .Invoke(this, new object[] { entityForm });*/
-                    Refresh();
-                }
-            }
-            /*
-            var query = new MultiQuery();
-
-            query.Add(
-                new QueryDef<DigitalFormVariable>(
-                    new Filter<DigitalFormVariable>(x => x.Form.ID).IsEqualTo(formid),
-                    null,
-                    null
-                ),
-                typeof(DigitalFormVariable)
-            );
-
-            query.Add(
-                new QueryDef<DigitalFormLayout>(
-                    new Filter<DigitalFormLayout>(x => x.Form.ID).IsEqualTo(formid).And(x => x.Active).IsEqualTo(true),
-                    null,
-                    null
-                ),
-                typeof(DigitalFormLayout)
-            );
-
-            query.Query();
-
-            var variables = query.Get(typeof(DigitalFormVariable)).Rows.Select(x => x.ToObject<DigitalFormVariable>());
-            var layout = query.Get(typeof(DigitalFormLayout)).Rows.FirstOrDefault()?.ToObject<DigitalFormLayout>();
-
-            if (layout == null)
-            {
-                MessageBox.Show("No Active Layouts Found!");
-                return;
-            }
-
-            var form = new DynamicFormWindow();
-            form.Designing = false;
-            form.HideViewButton = true;
-            form.ReadOnly = true;
-            form.Variables = variables.ToArray();
-            form.Type = layout.Type;
-
-            var f = new DFLayout();
-            if (!string.IsNullOrWhiteSpace(layout.Layout))
-            {
-                f.LoadLayout(layout.Layout);
-            }
-            else
-            {
-                f = new DFLayout();
-                f.RowHeights.Add("Auto");
-                f.ColumnWidths.AddRange(new[] { "*", "Auto" });
-            }
-
-            f.LoadVariables(variables);
-
-            form.Form = f;
-
-            if (!string.IsNullOrWhiteSpace(formdata))
-                form.Values = Serialization.Deserialize<Dictionary<string, object>>(formdata);
-
-            form.ShowDialog();*/
-        }
-
-        private void DataGrid_CellTapped(object sender, GridCellTappedEventArgs e)
-        {
-            if (e.RowColumnIndex.ColumnIndex == 0)
-            {
-                var timestamp = (DateTime)(e.Record as DataRowView).Row["Location_Timestamp"];
-                var latitude = (double)(e.Record as DataRowView).Row["Location_Latitude"];
-                var longitude = (double)(e.Record as DataRowView).Row["Location_Longitude"];
-
-                var form = new MapForm(latitude, longitude, timestamp);
-                form.ShowDialog();
-            }
-        }
-
-        #region IPanel Interface
-
-        public QADashboard()
-        {
-            _from = DateTime.Today;
-            _to = DateTime.Today;
-
-            InitializeComponent();
-            SetDates(_from, _to, false);
-        }
-
-
-        public void CreateToolbarButtons(IPanelHost host)
-        {
-        }
-
-        public void Setup()
-        {
-            var query = new MultiQuery();
-            query.Add(
-                new QueryDef<DigitalForm>(
-                    new Filter<DigitalForm>(x => x.Active).IsEqualTo(true),
-                    null,
-                    null
-                ),
-                typeof(DigitalForm)
-            );
-            query.Add(
-                new QueryDef<Job>(
-                    LookupFactory.DefineFilter<Job>(),
-                    new Columns<Job>(x => x.ID, x => x.JobNumber, x => x.Name),
-                    null
-                ),
-                typeof(Job)
-            );
-            query.Query();
-
-            allforms = query.Get(typeof(DigitalForm));
-
-
-            var cats = new DigitalFormCategoryLookups(null);
-            cats.OnAfterGenerateLookups += (sender, entries) => { entries.Insert(0, new LookupEntry("", "Select Category")); };
-
-            Category.ItemsSource = cats.AsTable("AppliesTo").ToDictionary("AppliesTo", "Display");
-
-            var jobs = query.Get(typeof(Job));
-            var alljobs = jobs.NewRow();
-            alljobs.Set<Job, Guid>(x => x.ID, Guid.Empty);
-            alljobs.Set<Job, string>(x => x.JobNumber, "ALL");
-            alljobs.Set<Job, string>(x => x.Name, "All Jobs");
-            jobs.Rows.Insert(0, alljobs);
-            Jobs.ItemsSource = jobs.ToDictionary(x => x.ID, new Expression<Func<Job, object>>[] { x => x.JobNumber, x => x.Name }, x => x.JobNumber);
-
-            //Dictionary<Guid, String> joblist = new Dictionary<Guid, string>() { { Guid.Empty, "All Jobs" } };
-            //CoreTable jobs = new Client<Job>().Query(
-            //   LookupFactory.DefineFilter<Job>(),
-            //   LookupFactory.DefineColumns<Job>(),
-            //   LookupFactory.DefineSort<Job>()
-            //);
-            //foreach (var row in jobs.Rows)
-            //{
-            //    //if (row.Get<Employee, String>(x => x.Group.Description).Equals("FACTORY"))
-            //    joblist[row.Get<Job, Guid>(x => x.ID)] = String.Format("{0} - {1}", row.Get<Job, String>(x => x.JobNumber), row.Get<Job, String>(x => x.Name));
-            //}
-            //Jobs.ItemsSource = joblist;
-        }
-
-        public void Shutdown(CancelEventArgs? cancel)
-        {
-        }
-
-        public string GetJobLink(string prefix, Type type)
-        {
-            var props = type.GetProperties().Where(x =>
-                x.PropertyType.BaseType != null && x.PropertyType.BaseType.IsGenericType &&
-                x.PropertyType.BaseType.GetGenericTypeDefinition() == typeof(EntityLink<>));
-            foreach (var prop in props)
-            {
-                if (prop.PropertyType == typeof(JobLink))
-                    return (string.IsNullOrEmpty(prefix) ? "" : prefix + ".") + prop.Name;
-                var result = GetJobLink((string.IsNullOrEmpty(prefix) ? "" : prefix + ".") + prop.Name, prop.PropertyType);
-                if (!string.IsNullOrEmpty(result))
-                    return result;
-            }
-
-            return "";
-        }
-
-        public Type CategoryToType(string category)
-        {
-            var instances = CoreUtils.TypeList(
-                AppDomain.CurrentDomain.GetAssemblies(),
-                x => !x.IsAbstract && x.GetInterfaces().Contains(typeof(IDigitalFormInstance))
-            ).ToArray();
-
-            foreach (var instance in instances)
-            {
-                var interfaces = instance.GetInterfaces()
-                    .Where(x => x.IsGenericType && x.GetGenericTypeDefinition().Equals(typeof(IDigitalFormInstance<>)));
-                var links = interfaces.Select(x => x.GenericTypeArguments.First());
-                var link = links.FirstOrDefault(l => l.GetInheritedGenericTypeArguments().Any(i => string.Equals(i.Name, category)));
-                if (link != null)
-                {
-                    return instance;
-                }
-            }
-
-            return null;
-        }
-
-        public void Refresh()
-        {
-            Progress.Show("Refreshing");
-            try
-            {
-                qaGrid.Clear();
-                qaGrid.LoadChecks("", new QAQuestion[] { }, new Dictionary<Guid, object>());
-                dataGrid.ItemsSource = null;
-
-                var category = Category.SelectedValue as string;
-
-
-                if (string.IsNullOrWhiteSpace(category))
-                {
-                    DigitalFormsDock.Visibility = Visibility.Collapsed;
-
-                    qaGrid.Visibility = Visibility.Collapsed;
-                    dataGrid.Visibility = Visibility.Collapsed;
-                    return;
-                }
-
-                var form = (KeyValuePair<Guid, string>)Form.SelectedItem;
-                if (form.Key == Guid.Empty)
-                {
-                    DigitalFormsDock.Visibility = Visibility.Collapsed;
-                    qaGrid.Visibility = Visibility.Collapsed;
-                    dataGrid.Visibility = Visibility.Collapsed;
-                    return;
-                }
-
-                var type = CategoryToType(category);
-                if (type == null)
-                    return;
-                formType = type;
-
-                parentType = CoreUtils.TypeList(
-                    AppDomain.CurrentDomain.GetAssemblies(),
-                    x => !x.IsAbstract && string.Equals(x.Name, category)
-                ).FirstOrDefault();
-                var parentcols = LookupFactory.DefineColumns(parentType);
-
-                Progress.SetMessage("Loading Data");
-
-                var jobid = Jobs.SelectedValue != null ? (Guid)Jobs.SelectedValue : Guid.Empty;
-                var isEntityForm = type.IsSubclassOfRawGeneric(typeof(EntityForm<,,>));
-
-                var query = new MultiQuery();
-                query.Add(
-                    new QueryDef<QAQuestion>(
-                        new Filter<QAQuestion>(x => x.Form.ID).IsEqualTo(form.Key),
-                        null,
-                        null
-                    ),
-                    typeof(QAQuestion)
-                );
-                query.Add(
-                    new QueryDef<DigitalFormVariable>(
-                        new Filter<DigitalFormVariable>(x => x.Form.ID).IsEqualTo(form.Key),
-                        null,
-                        new SortOrder<DigitalFormVariable>(x => x.Sequence)
-                    ),
-                    typeof(DigitalFormVariable)
-                );
-
-                var columns = LookupFactory.DefineColumns(type);
-                var sort = LookupFactory.DefineSort(type);
-
-                var JobLink = GetJobLink("", type);
-                
-                var filter = Filter.Create(type, "FormCompleted").IsGreaterThanOrEqualTo(_from)
-                    .And("FormCompleted").IsLessThan(_to.AddDays(1))
-                    .And("FormCancelled").IsEqualTo(DateTime.MinValue)
-                    .And("Form.ID").IsEqualTo(form.Key);
-                
-                if (jobid != Guid.Empty)
-                    filter = filter.And($"{JobLink}.ID").IsEqualTo(jobid);
-                
-                var cols = Columns.Create(type)
-                    .Add("ID")
-                    .Add("Number")
-                    .Add("CreatedBy")
-                    .Add("Created")
-                    .Add("Form.ID")
-                    .Add("FormData")
-                    .Add("FormCompleted")
-                    .Add("FormCompletedBy.UserID")
-                    .Add("Location.Timestamp")
-                    .Add("Location.Latitude")
-                    .Add("Location.Longitude");
-                
-                if(isEntityForm)
-                    cols.Add("FormProcessed");
-                
-                if (!string.IsNullOrWhiteSpace(JobLink))
-                    cols.Add(JobLink + ".JobNumber");
-                
-                foreach (var col in parentcols.ColumnNames())
-                    cols.Add("Parent." + col);
-                if(parentColumns.TryGetValue(parentType, out var pColumns))
-                {
-                    foreach(var (field, name) in pColumns)
-                        cols.Add(field);
-                }
-                
-                var querytype = typeof(QueryDef<>).MakeGenericType(type);
-                
-                query.Add(Activator.CreateInstance(querytype, filter, cols, sort) as IQueryDef, type);
-
-                if(parentType == typeof(JobITP))
-                {
-                    query.Add(
-                        new Filter<JobITP>(x => x.ID).InQuery(filter as Filter<JobITPForm>, x => x.Parent.ID),
-                        new Columns<JobITP>(x => x.ID, x => x.Job.JobNumber));
-                }
-
-                query.Query();
-
-
-                var questions =
-                    query.Get(typeof(QAQuestion)); // new Client<QAQuestion>().Query(new Filter<QAQuestion>(x => x.QAForm.ID).IsEqualTo(form.Key));
-                var variables = query.Get(typeof(DigitalFormVariable)).Rows.Select(x => x.ToObject<DigitalFormVariable>()).ToArray();
-                var formdata = query.Get(type);
-
-                var data = new DataTable();
-                data.Columns.Add("ID", typeof(Guid));
-                data.Columns.Add("Form_ID", typeof(Guid));
-                data.Columns.Add("Parent_ID", typeof(Guid));
-                data.Columns.Add("Location_Timestamp", typeof(DateTime));
-                data.Columns.Add("Location_Latitude", typeof(double));
-                data.Columns.Add("Location_Longitude", typeof(double));
-                data.Columns.Add("FormData", typeof(string));
-                data.Columns.Add("Number", typeof(String));
-
-                if (parentType == typeof(JobITP))
-                {
-                    data.Columns.Add("Job No", typeof(string));
-                }
-                if (pColumns != null)
-                {
-                    foreach (var (field, name) in pColumns)
-                        data.Columns.Add(name, typeof(string));
-                }
-
-                data.Columns.Add("Description", typeof(string));
-                data.Columns.Add("Created", typeof(DateTime));
-                data.Columns.Add("Created By", typeof(string));
-                data.Columns.Add("Completed", typeof(DateTime));
-                data.Columns.Add("Completed By", typeof(string));
-        
-                if (isEntityForm)
-                    data.Columns.Add("Processed", typeof(bool));
-
-                if (variables.Any())
-                {
-                    foreach (var variable in variables)
-                    {
-                        var code = variable.Code.Replace("/", " ");
-                        QuestionCodes[code] = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(code.ToLower());
-                        data.Columns.Add(code, typeof(string));
-                    }
-                }
-                else if (questions.Rows.Any())
-                {
-                    Progress.SetMessage("Loading Checks");
-                    qaGrid.Clear();
-                    qaGrid.LoadChecks(form.Value, questions.Rows.Select(x => x.ToObject<QAQuestion>()), new Dictionary<Guid, object>());
-                    qaGrid.CollapseMargins();
-
-                    var i = 1;
-                    foreach (var row in questions.Rows)
-                    {
-                        var id = row.Get<QAQuestion, Guid>(x => x.ID).ToString();
-                        if (!row.Get<QAQuestion, QAAnswer>(x => x.Answer).Equals(QAAnswer.Comment))
-                        {
-                            data.Columns.Add(id, typeof(string));
-                            var code = row.Get<QAQuestion, string>(x => x.Code);
-                            QuestionCodes[id] = string.IsNullOrEmpty(code) ? string.Format("{0}.", i) : code;
-                            i++;
-                        }
-                    }
-                }
-
-                foreach (var row in formdata.Rows)
-                {
-                    var qadata = row["FormData"] != null ? row["FormData"].ToString() : "";
-                    if (!string.IsNullOrWhiteSpace(qadata))
-                    {
-                        var datarow = data.NewRow();
-
-                        datarow["ID"] = (Guid)row["ID"];
-                        datarow["Form_ID"] = (Guid)row["Form.ID"];
-                        datarow["Parent_ID"] = (Guid)row["Parent.ID"];
-                        datarow["Location_Timestamp"] = (DateTime)row["Location.Timestamp"];
-                        datarow["Location_Latitude"] = (double)row["Location.Latitude"];
-                        datarow["Location_Longitude"] = (double)row["Location.Longitude"];
-                        datarow["FormData"] = (string)row["FormData"];
-                        datarow["Number"] = (string)row["Number"];
-
-                        var desc = new List<string>();
-                        foreach (var col in parentcols.ColumnNames().Where(x => x != "ID"))
-                        {
-                            var val = row["Parent." + col];
-                            if (val != null && val.GetType() != typeof(Guid))
-                                desc.Add(val.ToString());
-                        }
-
-                        datarow["Description"] = string.Join(" : ", desc);
-                        datarow["Created By"] = (string)row["CreatedBy"];
-                        datarow["Created"] = (DateTime)row["Created"];
-                        datarow["Completed"] = (DateTime)row["FormCompleted"];
-                        datarow["Completed By"] = (string)row["FormCompletedBy.UserID"];                
-
-                        if (isEntityForm)
-                            datarow["Processed"] = ((DateTime?)row["FormProcessed"] ?? DateTime.MinValue) > DateTime.MinValue;
-
-                        if(parentType == typeof(JobITP))
-                        {
-                            var jobITP = query.Get<JobITP>().Rows.FirstOrDefault(x => (Guid)x["ID"] == (Guid)row["Parent.ID"]);
-                            datarow["Job No"] = jobITP?["Job.JobNumber"];
-                        }
-
-                        if (pColumns != null)
-                        {
-                            foreach (var (field, name) in pColumns)
-                            {
-                                datarow[name] = row[field].ToString();
-                            }
-                        }
-
-                        //datarow["Job No"] = (String)row[JobLink + ".JobNumber"];
-
-                        var bHasData = false;
-                        if (variables.Any())
-                        {
-                            var dict = Serialization.Deserialize<Dictionary<string, object?>>(qadata);
-                            if (dict is not null)
-                            {
-                                var storage = new DFLoadStorage(dict, null);
-                                foreach(var variable in variables)
-                                {
-                                    var value = variable.Deserialize(storage.GetEntry(variable.Code));
-                                    var format = variable.FormatValue(value);
-                                    var sKey = variable.Code.Replace("/", " ");
-                                    if (data.Columns.Contains(sKey))
-                                    {
-                                        datarow[sKey] = format;
-                                        bHasData = true;
-                                    }
-                                }
-                            }
-                        }
-                        else
-                        {
-                            var dict = Serialization.Deserialize<Dictionary<Guid, object>>(qadata);
-                            foreach (var key in dict.Keys)
-                                if (data.Columns.Contains(key.ToString()))
-                                {
-                                    datarow[key.ToString()] = dict[key];
-                                    bHasData = true;
-                                }
-                        }
-
-
-                        if (bHasData)
-                            data.Rows.Add(datarow);
-                    }
-                }
-
-                dataGrid.ItemsSource = data;
-
-                qaGrid.Visibility = !variables.Any() && questions.Rows.Any() ? Visibility.Visible : Visibility.Collapsed;
-                dataGrid.Visibility = Visibility.Visible;
-            }
-            finally
-            {
-                Progress.Close();
-            }
-        }
-
-        public string SectionName
-        {
-            get {
-                var form = (KeyValuePair<Guid, string>)Form.SelectedItem;
-                if (Category.SelectedValue != null && form.Key != Guid.Empty)
-                {
-                    return form.Key.ToString();
-                }
-                return "Digital Forms";
-            }
-        }
-
-        public DataModel DataModel(Selection selection)
-        {
-            if ((Form.SelectedItem == null) || (String.IsNullOrWhiteSpace(Category.SelectedValue as String)))
-                return new AutoDataModel<DigitalForm>(new Filter<DigitalForm>().None());
-                
-            Type formtype = CategoryToType(Category.SelectedValue as String);
-            
-            var form = (KeyValuePair<Guid, string>)Form.SelectedItem;
-            if (formtype != null && (form.Key != Guid.Empty))
-            {
-                
-                IFilter filter;
-                switch (selection)
-                {
-                    case Selection.Selected:
-                        var formids = dataGrid.SelectedItems.Select(x => (Guid)(x as DataRowView).Row["ID"]).ToArray();
-
-                        filter = (Activator.CreateInstance(typeof(Filter<>).MakeGenericType(formtype)) as IFilter)!;
-                        filter.Expression = CoreUtils.CreateMemberExpression(formtype, "ID");
-                        filter.Operator = Operator.InList;
-                        filter.Value = formids;
-                        break;
-                    case Selection.All:
-                        filter = Filter.Create(formtype).All();
-                        break;
-                    case Selection.None:
-                    default:
-                        filter = (Activator.CreateInstance(typeof(Filter<>).MakeGenericType(formtype)) as IFilter)!.None();
-                        break;
-                }
-
-                return (Activator.CreateInstance(typeof(DigitalFormReportDataModel<>)!
-                    .MakeGenericType(formtype), new object?[] { filter, form.Key }) as DataModel)!;
-            }
-
-            return new AutoDataModel<DigitalForm>(new Filter<DigitalForm>().None());
-        }
-
-        public Dictionary<string, object[]> Selected()
-        {
-            return new Dictionary<string, object[]>();
-        }
-
-        public void Heartbeat(TimeSpan time)
-        {
-        }
-
-        #endregion
-
-        #region Toolbar Handling
-
-        private void Category_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            _changing = true;
-
-            var category = Category.SelectedValue as string;
-
-            var type = CategoryToType(category);
-            var JobLink = type != null ? GetJobLink("", type) : "";
-            if (string.IsNullOrWhiteSpace(JobLink))
-            {
-                Jobs.SelectedValue = Guid.Empty;
-                Jobs.IsEnabled = false;
-            }
-            else
-            {
-                Jobs.IsEnabled = true;
-            }
-
-
-            var forms = new Dictionary<Guid, string> { { Guid.Empty, "" } };
-
-            if (!string.IsNullOrWhiteSpace(category))
-            {
-                forms[Guid.Empty] = "Select Form";
-                var rows = allforms.Rows.Where(r => string.Equals(r.Get<DigitalForm, string>(c => c.AppliesTo), category));
-                foreach (var row in rows)
-                    forms[row.Get<DigitalForm, Guid>(x => x.ID)] = row.Get<DigitalForm, string>(x => x.Description);
-            }
-
-
-            Form.ItemsSource = forms;
-
-            _changing = false;
-            Form.SelectedIndex = 0;
-            Form.IsEnabled = !string.IsNullOrWhiteSpace(category);
-
-            OnUpdateDataModel?.Invoke(SectionName, DataModel(Selection.None));
-        }
-
-        private void Form_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (IsReady && !_changing)
-            {
-                Refresh();
-                OnUpdateDataModel?.Invoke(SectionName, DataModel(Selection.None));
-            }
-        }
-
-        private void Jobs_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (IsReady && !_changing)
-                Refresh();
-        }
-
-        private int WeekDay(DateTime date)
-        {
-            if (date.DayOfWeek == DayOfWeek.Sunday)
-                return 7;
-            return (int)date.DayOfWeek - 1;
-        }
-
-        private void SetDates(DateTime? from, DateTime? to, bool enable)
-        {
-            if (_changing)
-                return;
-            _changing = true;
-
-            _from = from.HasValue ? from.Value : DateTime.Today;
-            FromDate.SelectedDate = from;
-            FromDate.IsEnabled = enable;
-
-            _to = to.HasValue ? to.Value : DateTime.Today;
-            ToDate.SelectedDate = to;
-            ToDate.IsEnabled = enable;
-
-            _changing = false;
-            if (!enable)
-                Refresh();
-        }
-
-        private void DateRange_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (!IsReady)
-                return;
-
-            if (DateRange.SelectedIndex == 0) // Week To Date
-                SetDates(DateTime.Today, DateTime.Today, false);
-            else if (DateRange.SelectedIndex == 1) // Week To Date
-                SetDates(DateTime.Today.AddDays(-1), DateTime.Today.AddDays(-1), false);
-            else if (DateRange.SelectedIndex == 2) // Week To Date
-                SetDates(DateTime.Today.AddDays(0 - WeekDay(DateTime.Today)), DateTime.Today, false);
-            else if (DateRange.SelectedIndex == 3) // Last 7 Days
-                SetDates(DateTime.Today.AddDays(-6), DateTime.Today, false);
-            else if (DateRange.SelectedIndex == 4) // Month To Date
-                SetDates(new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1), DateTime.Today, false);
-            else if (DateRange.SelectedIndex == 5) // Last 30 days
-                SetDates(DateTime.Today.AddDays(-29), DateTime.Today, false);
-            else if (DateRange.SelectedIndex == 6) // Year To Date
-                SetDates(new DateTime(DateTime.Today.Year, 1, 1), DateTime.Today, false);
-            else if (DateRange.SelectedIndex == 7) // Last 12 Months
-                SetDates(DateTime.Today.AddYears(-1).AddDays(1), DateTime.Today, false);
-            else if (DateRange.SelectedIndex == 8) // Custom
-                SetDates(FromDate.SelectedDate.Value, ToDate.SelectedDate.Value, true);
-        }
-
-        private void FromDate_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (IsReady && !_changing)
-            {
-                _from = FromDate.SelectedDate.Value.Date;
-                Refresh();
-            }
-        }
-
-        private void ToDate_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (IsReady && !_changing)
-            {
-                _to = ToDate.SelectedDate.Value.Date;
-                Refresh();
-            }
-        }
-
-        private void Search_KeyUp(object sender, KeyEventArgs e)
-        {
-            if (string.IsNullOrWhiteSpace(Search.Text) || e.Key == Key.Return)
-            {
-                _search = Search.Text;
-                Refresh();
-            }
-        }
-
-        private void Export_Click(object sender, RoutedEventArgs e)
-        {
-            var cat = Category.SelectedValue as string;
-            //KeyValuePair<Type, String> cat = (KeyValuePair<Type, String>)Category.SelectedItem;
-            var form = (KeyValuePair<Guid, string>)Form.SelectedItem;
-            var formname = Regex.Replace(form.Value, "[^ a-zA-Z0-9]", "");
-            var filename = string.Format("{0} - {1} - {2:yyyy-MM-dd} - {3:yyyy-MM-dd}.xlsx", cat, formname, FromDate.SelectedDate,
-                ToDate.SelectedDate);
-
-            var options = new ExcelExportingOptions();
-            options.ExcelVersion = ExcelVersion.Excel2013;
-            options.ExportStackedHeaders = true;
-            var excelEngine = dataGrid.ExportToExcel(dataGrid.View, options);
-            var workBook = excelEngine.Excel.Workbooks[0];
-            var sheet = workBook.Worksheets[0];
-            sheet.Name = "Summary";
-            sheet.UsedRange.AutofitRows();
-            sheet.UsedRange.AutofitColumns();
-
-            sheet = workBook.Worksheets.Create("Questions");
-            sheet.Move(0);
-            var questions = new Client<QAQuestion>().Query(new Filter<QAQuestion>(x => x.Form.ID).IsEqualTo(form.Key));
-            sheet.Range[1, 1].Text = form.Value;
-            sheet.Range[1, 1, 1, 3].Merge();
-            var i = 1;
-            foreach (var row in questions.Rows)
-                if (!row.Get<QAQuestion, QAAnswer>(x => x.Answer).Equals(QAAnswer.Comment))
-                {
-                    sheet.Range[i + 2, 1].Text = string.Format("{0}.", i);
-                    sheet.Range[i + 2, 2].Text = string.Format("{0}", row.Get<QAQuestion, string>(x => x.Question));
-                    sheet.Range[i + 2, 3].Text = string.Format("[{0}]", row.Get<QAQuestion, string>(x => x.Code));
-                    i++;
-                }
-
-            sheet.UsedRange.AutofitRows();
-            sheet.UsedRange.AutofitColumns();
-
-            try
-            {
-                workBook.SaveAs(filename);
-
-                var startInfo = new ProcessStartInfo(filename);
-                startInfo.Verb = "open";
-                startInfo.UseShellExecute = true;
-                Process.Start(startInfo);
-            }
-            catch
-            {
-                MessageBox.Show(string.Format("Unable to Save/Launch [{0}]!\n\nIs the file already open?", filename));
-            }
-        }
-
-        #endregion
-
-    }
-}