Преглед на файлове

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 година
родител
ревизия
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
-
-    }
-}