Parcourir la source

Started work on replacement Kanban screen

Kenric Nugteren il y a 1 an
Parent
commit
aad72e37f8

+ 1 - 1
prs.desktop/Panels/Tasks/ITaskControl.cs

@@ -40,7 +40,7 @@ namespace PRSDesktop
 
         bool IsReady { get; set; }
 
-        IEnumerable<TaskModel> SelectedModels(TaskModel sender = null);
+        IEnumerable<TaskModel> SelectedModels(TaskModel? sender = null);
 
         string SectionName { get; }
 

+ 195 - 0
prs.desktop/Panels/Tasks/KanbanResources.xaml

@@ -0,0 +1,195 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:wpf="clr-namespace:InABox.Wpf;assembly=InABox.Wpf"
+                    x:Class="PRSDesktop.KanbanResources">
+    <wpf:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
+
+    <Style x:Key="employeeStyle" TargetType="{x:Type TextBlock}">
+        <Style.Triggers>
+            <DataTrigger Binding="{Binding AssignedTo}" Value="">
+                <Setter Property="Visibility" Value="Collapsed" />
+            </DataTrigger>
+        </Style.Triggers>
+    </Style>
+
+    <Style x:Key="managerStyle" TargetType="{x:Type TextBlock}">
+        <Style.Triggers>
+            <DataTrigger Binding="{Binding Manager}" Value="">
+                <Setter Property="Visibility" Value="Collapsed"/>
+            </DataTrigger>
+        </Style.Triggers>
+    </Style>
+
+    <Style x:Key="jobStyle" TargetType="{x:Type TextBlock}">
+        <Style.Triggers>
+            <DataTrigger Binding="{Binding JobNumber}" Value="">
+                <Setter Property="Visibility" Value="Collapsed" />
+            </DataTrigger>
+        </Style.Triggers>
+    </Style>
+
+    <Style x:Key="descriptionStyle" TargetType="{x:Type TextBlock}">
+        <Style.Triggers>
+            <DataTrigger Binding="{Binding Path=Description}" Value="">
+                <Setter Property="Visibility" Value="Collapsed" />
+            </DataTrigger>
+        </Style.Triggers>
+    </Style>
+
+    <Style x:Key="notesStyle" TargetType="{x:Type TextBlock}">
+        <Style.Triggers>
+            <DataTrigger Binding="{Binding Path=Notes}" Value="">
+                <Setter Property="Visibility" Value="Collapsed" />
+            </DataTrigger>
+        </Style.Triggers>
+    </Style>
+    
+    <DataTemplate x:Key="FullKanban">
+        <Border BorderBrush="Gray" BorderThickness="0.75" CornerRadius="5" Background="{Binding ColorKey}"
+                    Margin="0,2,0,2" MouseLeftButtonDown="Border_MouseLeftButtonDown" Tag="{Binding}">
+            <Grid Margin="4">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition x:Name="colImage" Width="Auto" />
+                    <ColumnDefinition x:Name="colCheckbox" Width="Auto" />
+                    <ColumnDefinition x:Name="colDescription" Width="*" />
+                    <ColumnDefinition x:Name="colType" Width="Auto" />
+                    <ColumnDefinition x:Name="colAttach" Width="Auto" />
+                    <ColumnDefinition x:Name="colDueDate" Width="Auto" />
+                </Grid.ColumnDefinitions>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="*" />
+                    <RowDefinition Height="Auto" />
+                </Grid.RowDefinitions>
+
+                <Border Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Width="40" Height="40" CornerRadius="20"
+                            Margin="0,0,4,0" BorderBrush="Black" BorderThickness="1" VerticalAlignment="Top">
+                    <Border.Background>
+                        <ImageBrush ImageSource="{Binding Path=Image}" Stretch="UniformToFill" />
+                    </Border.Background>
+                </Border>
+
+                <CheckBox Grid.Row="0" Grid.Column="1" Margin="0,0,4,0" VerticalAlignment="Center"
+                              IsChecked="{Binding Path=Checked}" Checked="CheckBox_Checked"
+                              Unchecked="CheckBox_Checked" Tag="{Binding}" />
+                <TextBlock Grid.Row="0" Grid.Column="2" Margin="0,0,0,0" FontWeight="DemiBold" FontSize="12"
+                               VerticalAlignment="Center">
+                        <Run Text="{Binding Path=Number}" /><Run Text=": " /><Run Text="{Binding Path=Title}" />
+                </TextBlock>
+                <TextBlock Grid.Row="0" Grid.Column="3" FontSize="12" Margin="4,0,0,0" Text="{Binding Path=Type.Code}"
+                               FontWeight="DemiBold" VerticalAlignment="Center" HorizontalAlignment="Center" />
+                <Image Grid.Row="0" Grid.Column="4" Margin="4,0,4,0"
+                           Source="pack://application:,,,Resources/attachment.png"
+                           Visibility="{Binding Path=Attachments, Converter={StaticResource BoolToVisibilityConverter}}"
+                           Width="16" Height="16" VerticalAlignment="Center" />
+                <TextBlock Grid.Row="0" Grid.Column="5" Text="{Binding DueDate, StringFormat='{}{0:dd MMM yy}'}"
+                               FontWeight="Bold" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center" />
+
+                <TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="5" Margin="0,0,0,0" FontSize="12"
+                               HorizontalAlignment="Left" Text="{Binding Path=Description}" VerticalAlignment="Stretch"
+                               TextWrapping="Wrap" />
+
+                <DockPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="5">
+                    <TextBlock DockPanel.Dock="Right" x:Name="Job" Margin="0" FontWeight="DemiBold" FontSize="12"
+                                   VerticalAlignment="Center" TextAlignment="Right" Style="{StaticResource jobStyle}">
+                            <Run Text="{Binding JobNumber}" /><Run Text=": " /><Run Text="{Binding JobName}" />
+                    </TextBlock>
+                    <TextBlock DockPanel.Dock="Left" x:Name="Staff" FontWeight="DemiBold" FontSize="12"
+                                   VerticalAlignment="Center" Text="{Binding AssignedTo}"
+                                   Style="{StaticResource employeeStyle}" />
+                </DockPanel>
+                <Image Grid.Row="2" Grid.Column="0" Margin="0,0,4,0"
+                           Source="pack://application:,,,Resources/lock.png"
+                           Visibility="{Binding Path=Locked, Converter={StaticResource BoolToVisibilityConverter}}"
+                           Width="20" Height="20" VerticalAlignment="Center" HorizontalAlignment="Center" />
+
+
+            </Grid>
+
+            <Border.ToolTip>
+                <Grid>
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="Auto"/>
+                    </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="Auto"/>
+                    </Grid.ColumnDefinitions>
+                    <TextBlock Grid.Row="0" Grid.Column="0">
+                            <Run FontWeight="DemiBold" Text="{Binding Path=Title}"/>
+                    </TextBlock>
+                    <TextBlock Grid.Row="1" Grid.Column="0" Style="{StaticResource descriptionStyle}">
+                            <Run Text="{Binding Path=Description}"/>
+                    </TextBlock>
+                    <TextBlock Grid.Row="2" Grid.Column="0" Style="{StaticResource notesStyle}">
+                            <LineBreak/>
+                            <Run Text="{Binding Path=Notes}"/>
+                    </TextBlock>
+                </Grid>
+            </Border.ToolTip>
+
+            <Border.ContextMenu>
+                <ContextMenu x:Name="TaskMenu" Tag="{Binding}" Opened="TaskMenu_Opened" />
+
+            </Border.ContextMenu>
+        </Border>
+    </DataTemplate>
+
+    <DataTemplate x:Key="CompactKanban">
+        <Border BorderBrush="Gray" BorderThickness="0.75" CornerRadius="5" Background="{Binding ColorKey}" Margin="0,2,0,2" 
+                MouseLeftButtonDown="Border_MouseLeftButtonDown" Tag="{Binding}">
+
+            <DockPanel Margin="4">
+                <TextBlock DockPanel.Dock="Right" Text="{Binding DueDate, StringFormat='{}{0:dd MMM yy}'}" FontWeight="Bold" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center" />
+
+                <CheckBox DockPanel.Dock="Left" Margin="0,0,4,0" VerticalAlignment="Center" IsChecked="{Binding Path=Checked}" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked" Tag="{Binding}" />
+
+                <TextBlock DockPanel.Dock="Left" x:Name="JobNumber" Margin="0"  FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center" Style="{StaticResource jobStyle}">
+                                    <Run Text="{Binding JobNumber}"/><Run Text="/"/>
+                </TextBlock>
+                <TextBlock DockPanel.Dock="Left" Margin="0,0,0,0" FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center">
+                            <Run Text="{Binding Path=Number}"/><Run Text=": "/>
+                </TextBlock>
+
+                <TextBlock DockPanel.Dock="Left" Margin="0,0,0,0" FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center" Text="{Binding Path=Title}" />
+
+                <TextBlock DockPanel.Dock="Left" x:Name="Manager" Margin="0"  FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center" Style="{StaticResource managerStyle}">
+                        <Run Text=" ("/><Run Text="{Binding Manager}"/><Run Text=")"/>
+                </TextBlock>
+                <TextBlock DockPanel.Dock="Left" x:Name="Employee" Margin="0"  FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center" Style="{StaticResource employeeStyle}">
+                        <Run Text=" ("/><Run Text="{Binding AssignedTo}"/><Run Text=")"/>
+                </TextBlock>
+
+            </DockPanel>
+
+            <Border.ToolTip>
+                <Grid>
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="Auto"/>
+                    </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="Auto"/>
+                    </Grid.ColumnDefinitions>
+                    <TextBlock Grid.Row="0" Grid.Column="0">
+                            <Run FontWeight="DemiBold" Text="{Binding Path=Title}"/>
+                    </TextBlock>
+                    <TextBlock Grid.Row="1" Grid.Column="0" Style="{StaticResource descriptionStyle}">
+                            <Run Text="{Binding Path=Description}"/>
+                    </TextBlock>
+                    <TextBlock Grid.Row="2" Grid.Column="0" Style="{StaticResource notesStyle}">
+                            <LineBreak/>
+                            <Run Text="{Binding Path=Notes}"/>
+                    </TextBlock>
+                </Grid>
+            </Border.ToolTip>
+
+            <Border.ContextMenu>
+                <ContextMenu x:Name="TaskMenu" Tag="{Binding}" Opened="TaskMenu_Opened" />
+            </Border.ContextMenu>
+
+        </Border>
+    </DataTemplate>
+</ResourceDictionary>

+ 41 - 0
prs.desktop/Panels/Tasks/KanbanResources.xaml.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+
+namespace PRSDesktop;
+
+public partial class KanbanResources : ResourceDictionary
+{
+    private static ICommand editTask = new RoutedCommand();
+    private static ICommand selectTask = new RoutedCommand();
+    private static ICommand openTaskMenu = new RoutedCommand();
+
+    public static ICommand EditTask => editTask;
+    public static ICommand SelectTask => selectTask;
+    public static ICommand OpenTaskMenu => openTaskMenu;
+
+    private void Border_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
+    {
+        if ((sender as FrameworkElement)?.Tag is not TaskModel model) return;
+        if(e.ClickCount > 2)
+        {
+            EditTask.Execute(model);
+        }
+    }
+
+    private void CheckBox_Checked(object sender, RoutedEventArgs e)
+    {
+        if ((sender as FrameworkElement)?.Tag is not TaskModel model) return;
+        SelectTask.Execute(model);
+    }
+
+    private void TaskMenu_Opened(object sender, RoutedEventArgs e)
+    {
+        if ((sender as FrameworkElement)?.Tag is not TaskModel model) return;
+        OpenTaskMenu.Execute(model);
+    }
+}

+ 2 - 1
prs.desktop/Panels/Tasks/TaskModel.cs

@@ -8,7 +8,7 @@ using Syncfusion.UI.Xaml.Kanban;
 namespace PRSDesktop;
 
 [DoNotNotify]
-public class TaskModel : KanbanModel
+public class TaskModel
 {
     public BitmapImage Image { get; set; }
     public Color Color { get; set; }
@@ -23,6 +23,7 @@ public class TaskModel : KanbanModel
     public string JobNumber { get; set; }
     public string JobName { get; set; }
     public bool Checked { get; set; }
+    public string Category { get; set; }
     public KanbanType Type { get; set; }
     public int Number { get; set; }
     public bool Locked { get; set; }

+ 27 - 41
prs.desktop/Panels/Tasks/TaskPanel.xaml.cs

@@ -32,7 +32,6 @@ namespace PRSDesktop
         private bool _bTabChanging;
         private KanbanType[] kanbanTypes = null!; // Initialized in Setup()
         
-        public Guid MyID { get; set; } = CoreUtils.FullGuid;
         public IList<KanbanType> KanbanTypes => kanbanTypes;
         public Job Job { get; set; }
         public JobPanelSettings Settings { get; set; }
@@ -48,74 +47,61 @@ namespace PRSDesktop
                 _viewmap[panel.KanbanViewType] = tab;
                 panel.Host = this;
             }
-
-            if (MyID == CoreUtils.FullGuid)
-            {
-                var row = new Client<Employee>()
-                    .Query(new Filter<Employee>(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid), new Columns<Employee>(x => x.ID)).Rows
-                    .FirstOrDefault();
-                if (row != null)
-                    MyID = row.Get<Employee, Guid>(x => x.ID);
-            }
         }
 
         
-        private void ChangeStatus(ITaskControl control, object o, RoutedEventArgs e, string status)
+        private void CompleteTask(ITaskControl control, RoutedEventArgs e, DateTime completed)
         {
-            if (MessageBox.Show($"Are you sure you want to mark the selected tasks as {status}?", "Confirm Change Status",
+            if (MessageBox.Show($"Are you sure you want to complete the selected tasks?", "Confirm Completion",
                     MessageBoxButton.YesNo) != MessageBoxResult.Yes)
                 return;
-            var tasks = (((MenuItem)e.Source).Tag as IEnumerable<TaskModel>)!;
-            Progress.ShowModal("Changing Status", progress =>
+            var tasks = (((FrameworkElement)e.Source).Tag as IEnumerable<TaskModel>)!;
+            Progress.ShowModal("Completing Tasks", progress =>
             {
                 var kanbans = LoadKanbans(tasks, new Columns<Kanban>(x => x.ID, x => x.Completed, x => x.Category));
                 foreach (var kanban in kanbans)
                 {
-                    if(status == "Complete")
-                    {
-                        kanban.Completed = DateTime.Now;
-                    }
-                    kanban.Category = status;
+                    kanban.Completed = completed;
+                    kanban.Category = "Complete";
                 }
 
-                new Client<Kanban>().Save(kanbans, $"Kanban Marked as {status}");
+                new Client<Kanban>().Save(kanbans, $"Kanban Marked as Complete");
             });
             control.Refresh(true);
         }
-        private void CompleteTask(ITaskControl control, RoutedEventArgs e, DateTime completed)
+        private void AddChangeStatusButton(ITaskControl control, TaskModel[] models, MenuItem menu, string header, string status)
         {
-            if (MessageBox.Show($"Are you sure you want to complete the selected tasks?", "Confirm Completion",
+            menu.AddItem(header, null, Tuple.Create(control, models, status), ChangeStatus_Click);
+        }
+
+        private void ChangeStatus_Click(Tuple<ITaskControl, TaskModel[], string> obj)
+        {
+            var (control, tasks, status) = obj;
+            if (MessageBox.Show($"Are you sure you want to mark the selected tasks as {status}?", "Confirm Change Status",
                     MessageBoxButton.YesNo) != MessageBoxResult.Yes)
                 return;
-            var tasks = (((FrameworkElement)e.Source).Tag as IEnumerable<TaskModel>)!;
-            Progress.ShowModal("Completing Tasks", progress =>
+            Progress.ShowModal("Changing Status", progress =>
             {
                 var kanbans = LoadKanbans(tasks, new Columns<Kanban>(x => x.ID, x => x.Completed, x => x.Category));
                 foreach (var kanban in kanbans)
                 {
-                    kanban.Completed = completed;
-                    kanban.Category = "Complete";
+                    if (status == "Complete")
+                    {
+                        kanban.Completed = DateTime.Now;
+                    }
+                    kanban.Category = status;
                 }
 
-                new Client<Kanban>().Save(kanbans, $"Kanban Marked as Complete");
+                new Client<Kanban>().Save(kanbans, $"Kanban Marked as {status}");
             });
             control.Refresh(true);
         }
-        private void AddChangeStatusButton(ITaskControl control, TaskModel[] models, MenuItem menu, string header, string status)
-        {
-            var item = new MenuItem
-            {
-                Tag = models,
-                Header = header
-            };
-            item.Click += (o, e) => ChangeStatus(control, o, e, status);
-            menu.Items.Add(item);
-        }
+
         public bool CanChangeTasks(IEnumerable<TaskModel> models)
         {
             foreach (var task in models)
             {
-                if (!MyID.Equals(task.ManagerID) && !MyID.Equals(task.EmployeeID))
+                if (!App.EmployeeID.Equals(task.ManagerID) && !App.EmployeeID.Equals(task.EmployeeID))
                 {
                     // If you can change others tasks, IsFullControl is true - but we don't check at the beginning of the function
                     // to save checking security tokens every time.
@@ -175,7 +161,7 @@ namespace PRSDesktop
                     digitalForms,
                     kanbanID,
                     () => new Client<Kanban>().Load(new Filter<Kanban>(x => x.ID).IsEqualTo(kanbanID)).First(),
-                    model.EmployeeID == MyID);
+                    model.EmployeeID == App.EmployeeID);
 
                 menu.Items.Add(digitalForms);
             }
@@ -980,8 +966,8 @@ namespace PRSDesktop
                     kanban.Private = false;
                     kanban.JobLink.ID = Job?.ID ?? Guid.Empty;
                     kanban.JobLink.Synchronise(Job ?? new Job());
-                    kanban.EmployeeLink.ID = MyID;
-                    kanban.ManagerLink.ID = MyID;
+                    kanban.EmployeeLink.ID = App.EmployeeID;
+                    kanban.ManagerLink.ID = App.EmployeeID;
                     customise?.Invoke(kanban);
                 });
             if (result != null)

+ 60 - 239
prs.desktop/Panels/Tasks/TasksByStatusControl.xaml

@@ -1,222 +1,30 @@
-<UserControl
-    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:local="clr-namespace:PRSDesktop"
-    xmlns:Kanban="clr-namespace:Syncfusion.UI.Xaml.Kanban;assembly=Syncfusion.SfKanban.WPF"
-    x:Class="PRSDesktop.TasksByStatusControl"
-    mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="400">
-
+<UserControl x:Class="PRSDesktop.TasksByStatusControl"
+             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:local="clr-namespace:PRSDesktop"
+             mc:Ignorable="d" 
+             x:Name="Control"
+             d:DesignHeight="450" d:DesignWidth="800">
     <UserControl.Resources>
-        <local:StatusTasksHeaderTimeConverter x:Key="StatusTasksHeaderTimeConverter" />
-        <local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
-        
-        <Style x:Key="employeeStyle" TargetType="{x:Type TextBlock}">
-            <Style.Triggers>
-                <DataTrigger Binding="{Binding AssignedTo}" Value="">
-                    <Setter Property="Visibility" Value="Collapsed" />
-                </DataTrigger>
-            </Style.Triggers>
-        </Style>
-        
-        <Style x:Key="managerStyle" TargetType="{x:Type TextBlock}">
-            <Style.Triggers>
-                <DataTrigger Binding="{Binding Manager}" Value="">
-                    <Setter Property="Visibility" Value="Collapsed"/>
-                </DataTrigger>
-            </Style.Triggers>
-        </Style>
-        
-        <Style x:Key="jobStyle" TargetType="{x:Type TextBlock}">
-            <Style.Triggers>
-                <DataTrigger Binding="{Binding JobNumber}" Value="">
-                    <Setter Property="Visibility" Value="Collapsed" />
-                </DataTrigger>
-            </Style.Triggers>
-        </Style>
-
-        <Style x:Key="descriptionStyle" TargetType="{x:Type TextBlock}">
-            <Style.Triggers>
-                <DataTrigger Binding="{Binding Path=Description}" Value="">
-                    <Setter Property="Visibility" Value="Collapsed" />
-                </DataTrigger>
-            </Style.Triggers>
-        </Style>
-
-        <Style x:Key="notesStyle" TargetType="{x:Type TextBlock}">
-            <Style.Triggers>
-                <DataTrigger Binding="{Binding Path=Notes}" Value="">
-                    <Setter Property="Visibility" Value="Collapsed" />
-                </DataTrigger>
-            </Style.Triggers>
-        </Style>
-
-        <DataTemplate x:Key="KanbanHeader">
-            <!--<Border BorderThickness="0.75" BorderBrush="Gray" CornerRadius="5,5,0,0" Margin="0,10,0,0" Padding="5" Width="{Binding Converter={StaticResource TaskHeaderWidthConverter}}" Background="WhiteSmoke">-->
-            <StackPanel Orientation="Vertical">
-                <TextBlock Text="{Binding  Header}" FontSize="16" FontWeight="DemiBold" HorizontalAlignment="Left"
-                           Margin="10,0,10,0" />
-                <TextBlock FontSize="12" HorizontalAlignment="Left" Margin="10,0,5,0">
-                    <Run Text="{Binding CardCount}" />
-                    <Run Text="Tasks /" />
-                    <Run Text="{Binding Converter={StaticResource StatusTasksHeaderTimeConverter}, Mode=OneWay}" />
-                    <Run Text="Hours" />
-                </TextBlock>
-            </StackPanel>
-            <!--</Border>-->
-        </DataTemplate>
-
-        <DataTemplate x:Key="FullKanban">
-            <Border BorderBrush="Gray" BorderThickness="0.75" CornerRadius="5" Background="{Binding ColorKey}"
-                    Margin="0,2,0,2" MouseLeftButtonDown="Border_MouseLeftButtonDown" Tag="{Binding}">
-                <Grid Margin="4">
-                    <Grid.ColumnDefinitions>
-                        <ColumnDefinition x:Name="colImage" Width="Auto" />
-                        <ColumnDefinition x:Name="colCheckbox" Width="Auto" />
-                        <ColumnDefinition x:Name="colDescription" Width="*" />
-                        <ColumnDefinition x:Name="colType" Width="Auto" />
-                        <ColumnDefinition x:Name="colAttach" Width="Auto" />
-                        <ColumnDefinition x:Name="colDueDate" Width="Auto" />
-                    </Grid.ColumnDefinitions>
-                    <Grid.RowDefinitions>
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="*" />
-                        <RowDefinition Height="Auto" />
-                    </Grid.RowDefinitions>
-
-                    <Border Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Width="40" Height="40" CornerRadius="20"
-                            Margin="0,0,4,0" BorderBrush="Black" BorderThickness="1" VerticalAlignment="Top">
-                        <Border.Background>
-                            <ImageBrush ImageSource="{Binding Path=Image}" Stretch="UniformToFill" />
-                        </Border.Background>
-                    </Border>
-
-                    <CheckBox Grid.Row="0" Grid.Column="1" Margin="0,0,4,0" VerticalAlignment="Center"
-                              IsChecked="{Binding Path=Checked}" Checked="CheckBox_Checked"
-                              Unchecked="CheckBox_Checked" Tag="{Binding}" />
-                    <TextBlock Grid.Row="0" Grid.Column="2" Margin="0,0,0,0" FontWeight="DemiBold" FontSize="12"
-                               VerticalAlignment="Center">
-                        <Run Text="{Binding Path=Number}" /><Run Text=": " /><Run Text="{Binding Path=Title}" />
-                    </TextBlock>
-                    <TextBlock Grid.Row="0" Grid.Column="3" FontSize="12" Margin="4,0,0,0" Text="{Binding Path=Type.Code}"
-                               FontWeight="DemiBold" VerticalAlignment="Center" HorizontalAlignment="Center" />
-                    <Image Grid.Row="0" Grid.Column="4" Margin="4,0,4,0"
-                           Source="pack://application:,,,Resources/attachment.png"
-                           Visibility="{Binding Path=Attachments, Converter={StaticResource BoolToVisibilityConverter}}"
-                           Width="16" Height="16" VerticalAlignment="Center" />
-                    <TextBlock Grid.Row="0" Grid.Column="5" Text="{Binding DueDate, StringFormat='{}{0:dd MMM yy}'}"
-                               FontWeight="Bold" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center" />
-
-                    <TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="5" Margin="0,0,0,0" FontSize="12"
-                               HorizontalAlignment="Left" Text="{Binding Path=Description}" VerticalAlignment="Stretch"
-                               TextWrapping="Wrap" />
-
-                    <DockPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="5">
-                        <TextBlock DockPanel.Dock="Right" x:Name="Job" Margin="0" FontWeight="DemiBold" FontSize="12"
-                                   VerticalAlignment="Center" TextAlignment="Right" Style="{StaticResource jobStyle}">
-                            <Run Text="{Binding JobNumber}" /><Run Text=": " /><Run Text="{Binding JobName}" />
-                        </TextBlock>
-                        <TextBlock DockPanel.Dock="Left" x:Name="Staff" FontWeight="DemiBold" FontSize="12"
-                                   VerticalAlignment="Center" Text="{Binding AssignedTo}"
-                                   Style="{StaticResource employeeStyle}" />
-                    </DockPanel>
-                    <Image Grid.Row="2" Grid.Column="0" Margin="0,0,4,0"
-                           Source="pack://application:,,,Resources/lock.png"
-                           Visibility="{Binding Path=Locked, Converter={StaticResource BoolToVisibilityConverter}}"
-                           Width="20" Height="20" VerticalAlignment="Center" HorizontalAlignment="Center" />
-
-
-                </Grid>
-
-                <Border.ToolTip>
-                    <Grid>
-                        <Grid.RowDefinitions>
-                            <RowDefinition Height="Auto"/>
-                            <RowDefinition Height="Auto"/>
-                            <RowDefinition Height="Auto"/>
-                        </Grid.RowDefinitions>
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto"/>
-                        </Grid.ColumnDefinitions>
-                        <TextBlock Grid.Row="0" Grid.Column="0">
-                            <Run FontWeight="DemiBold" Text="{Binding Path=Title}"/>
-                        </TextBlock>
-                        <TextBlock Grid.Row="1" Grid.Column="0" Style="{StaticResource descriptionStyle}">
-                            <Run Text="{Binding Path=Description}"/>
-                        </TextBlock>
-                        <TextBlock Grid.Row="2" Grid.Column="0" Style="{StaticResource notesStyle}">
-                            <LineBreak/>
-                            <Run Text="{Binding Path=Notes}"/>
-                        </TextBlock>
-                    </Grid>
-                </Border.ToolTip>
-                
-                <Border.ContextMenu>
-                    <ContextMenu x:Name="TaskMenu" Tag="{Binding}" Opened="TaskMenu_Opened" />
-
-                </Border.ContextMenu>
-            </Border>
-        </DataTemplate>
-
-        <DataTemplate x:Key="CompactKanban">
-            <Border BorderBrush="Gray" BorderThickness="0.75" CornerRadius="5" Background="{Binding ColorKey}" Margin="0,2,0,2" MouseLeftButtonDown="Border_MouseLeftButtonDown"  Tag="{Binding}">
-            
-                <DockPanel Margin="4">
-                    <TextBlock DockPanel.Dock="Right" Text="{Binding DueDate, StringFormat='{}{0:dd MMM yy}'}" FontWeight="Bold" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center" />
-
-                    <CheckBox DockPanel.Dock="Left" Margin="0,0,4,0" VerticalAlignment="Center" IsChecked="{Binding Path=Checked}" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked" Tag="{Binding}" />
-
-                    <TextBlock DockPanel.Dock="Left" x:Name="JobNumber" Margin="0"  FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center" Style="{StaticResource jobStyle}">
-                                    <Run Text="{Binding JobNumber}"/><Run Text="/"/>
-                    </TextBlock>
-                    <TextBlock DockPanel.Dock="Left" Margin="0,0,0,0" FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center">
-                            <Run Text="{Binding Path=Number}"/><Run Text=": "/>
-                    </TextBlock>
-
-                    <TextBlock DockPanel.Dock="Left" Margin="0,0,0,0" FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center" Text="{Binding Path=Title}" />
-
-                    <TextBlock DockPanel.Dock="Left" x:Name="Manager" Margin="0"  FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center" Style="{StaticResource managerStyle}">
-                        <Run Text=" ("/><Run Text="{Binding Manager}"/><Run Text=")"/>
-                    </TextBlock>
-                    <TextBlock DockPanel.Dock="Left" x:Name="Employee" Margin="0"  FontWeight="DemiBold" FontSize="12" VerticalAlignment="Center" Style="{StaticResource employeeStyle}">
-                        <Run Text=" ("/><Run Text="{Binding AssignedTo}"/><Run Text=")"/>
-                    </TextBlock>
-
-                </DockPanel>
-
-                <Border.ToolTip>
-                    <Grid>
-                        <Grid.RowDefinitions>
-                            <RowDefinition Height="Auto"/>
-                            <RowDefinition Height="Auto"/>
-                            <RowDefinition Height="Auto"/>
-                        </Grid.RowDefinitions>
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto"/>
-                        </Grid.ColumnDefinitions>
-                        <TextBlock Grid.Row="0" Grid.Column="0">
-                            <Run FontWeight="DemiBold" Text="{Binding Path=Title}"/>
-                        </TextBlock>
-                        <TextBlock Grid.Row="1" Grid.Column="0" Style="{StaticResource descriptionStyle}">
-                            <Run Text="{Binding Path=Description}"/>
-                        </TextBlock>
-                        <TextBlock Grid.Row="2" Grid.Column="0" Style="{StaticResource notesStyle}">
-                            <LineBreak/>
-                            <Run Text="{Binding Path=Notes}"/>
-                        </TextBlock>
-                    </Grid>
-                </Border.ToolTip>
-
-                <Border.ContextMenu>
-                    <ContextMenu x:Name="TaskMenu" Tag="{Binding}" Opened="TaskMenu_Opened" />
-                </Border.ContextMenu>
-            
-            </Border>
-        </DataTemplate>
-        
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary Source="KanbanResources.xaml"/>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
     </UserControl.Resources>
-
+    <UserControl.CommandBindings>
+        <CommandBinding Command="{x:Static local:KanbanResources.EditTask}"
+                        Executed="EditTask_Executed"
+                        CanExecute="CommandBinding_CanExecute"/>
+        <CommandBinding Command="{x:Static local:KanbanResources.OpenTaskMenu}"
+                        Executed="OpenTaskMenu_Executed"
+                        CanExecute="CommandBinding_CanExecute"/>
+        <CommandBinding Command="{x:Static local:KanbanResources.SelectTask}"
+                        Executed="SelectTask_Executed"
+                        CanExecute="CommandBinding_CanExecute"/>
+    </UserControl.CommandBindings>
     <Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="Auto" x:Name="EmployeeListColumn" />
@@ -233,7 +41,7 @@
             <Label Content="Employee List" HorizontalContentAlignment="Center" />
         </Border>
 
-        <ListView Grid.Column="0" Grid.Row="1" x:Name="Employees" HorizontalAlignment="Stretch"
+        <ListView Grid.Column="0" Grid.Row="1" x:Name="EmployeeList" HorizontalAlignment="Stretch"
                   SelectionChanged="Employees_SelectionChanged" Margin="0,0,4,0" Background="White" BorderBrush="Gray"
                   BorderThickness="0.75">
             <ListView.ItemTemplate>
@@ -285,25 +93,38 @@
 
             </DockPanel>
         </Border>
-
-        <Kanban:SfKanban
-            x:Name="Kanban"
-            Grid.Column="1"
-            Grid.Row="1"
-            Margin="0"
-            HorizontalAlignment="Stretch"
-            VerticalAlignment="Stretch"
-            HorizontalContentAlignment="Stretch"
-            VerticalContentAlignment="Stretch"
-            AutoGenerateColumns="False"
-            BorderBrush="Gray"
-            BorderThickness="0.75"
-            Background="WhiteSmoke"
-            CardTemplate="{StaticResource FullKanban}"
-            ColumnHeaderTemplate="{StaticResource KanbanHeader}"
-            SizeChanged="Kanban_SizeChanged"
-            CardDragStart="Kanban_CardDragStart"
-            CardDragEnd="Kanban_CardDragEnd" />
-
+        <ItemsControl x:Name="ColumnsList" ItemsSource="{Binding ElementName=Control,Path=Columns}">
+            <ItemsControl.ItemsPanel>
+                <ItemsPanelTemplate>
+                    <StackPanel Orientation="Horizontal"/>
+                </ItemsPanelTemplate>
+            </ItemsControl.ItemsPanel>
+            <ItemsControl.ItemTemplate>
+                <DataTemplate>
+                    <Grid ContextMenuOpening="Column_ContextMenuOpening"
+                          Tag="{Binding}">
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="Auto"/>
+                            <RowDefinition Height="*"/>
+                        </Grid.RowDefinitions>
+                        <StackPanel Orientation="Vertical"
+                                    Grid.Row="0">
+                            <TextBlock Text="{Binding Title}" FontSize="16" FontWeight="DemiBold" HorizontalAlignment="Left"
+                                   Margin="10,0,10,0" />
+                            <TextBlock FontSize="12" HorizontalAlignment="Left" Margin="10,0,5,0">
+                            <Run Text="{Binding NumTasks}" />
+                            <Run Text="Tasks /" />
+                            <Run Text="{Binding NumHours, StringFormat={}{0:F2}}"/>
+                            <Run Text="Hours" />
+                            </TextBlock>
+                        </StackPanel>
+                        <ItemsControl Grid.Row="1"
+                                      ItemsSource="{Binding Tasks}"
+                                      ItemTemplate="{StaticResource FullKanban}">
+                        </ItemsControl>
+                    </Grid>
+                </DataTemplate>
+            </ItemsControl.ItemTemplate>
+        </ItemsControl>
     </Grid>
-</UserControl>
+</UserControl>

+ 300 - 1455
prs.desktop/Panels/Tasks/TasksByStatusControl.xaml.cs

@@ -1,1600 +1,445 @@
-using System;
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using InABox.WPF;
+using NPOI.SS.Formula.Functions;
+using Syncfusion.UI.Xaml.Kanban;
+using System;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.Globalization;
 using System.Linq;
-using System.Linq.Expressions;
 using System.Reflection;
-using System.Threading;
+using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Data;
+using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
-using Comal.Classes;
-using InABox.Clients;
-using InABox.Core;
-using InABox.DynamicGrid;
-using InABox.Wpf;
-using InABox.WPF;
-using org.omg.CORBA;
-using Syncfusion.UI.Xaml.Kanban;
-using Color = System.Drawing.Color;
-
-namespace PRSDesktop
-{
-    public class EmployeeModel
-    {
-        public EmployeeModel(Guid id, string name, Guid thumbnail, BitmapImage image)
-        {
-            ID = id;
-            Name = name;
-            Image = image;
-            ThumbnailID = thumbnail;
-        }
-
-        public Guid ID { get; set; }
-        public string Name { get; set; }
-        public BitmapImage Image { get; set; }
-        public Guid ThumbnailID { get; set; }
-    }
+using System.Windows.Navigation;
+using System.Windows.Shapes;
 
-    public class StatusTasksHeaderTimeConverter : IValueConverter
-    {
-        public static IEnumerable<TaskModel> Tasks { get; set; }
+namespace PRSDesktop;
 
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            if (Tasks == null)
-                return "0:00";
+public class TasksByStatusColumn
+{
+    public string Category { get; }
 
-            var dataContext = value as ColumnTag;
-            if (dataContext == null)
-                return "0:00";
+    public string Title { get; }
 
-            var getter = dataContext.GetType().GetProperty("Column", BindingFlags.NonPublic | BindingFlags.Instance);
-            if (getter == null)
-                return "0:00";
+    public int NumTasks { get => Tasks.Count; }
 
-            var column = (KanbanColumn)getter.GetValue(dataContext);
-            if (column == null)
-                return "0:00";
+    public double NumHours { get => Tasks.Sum(x => x.EstimatedTime.TotalHours); }
 
-            double result = 0.0F;
-            foreach (var kanban in Tasks.Where(x => Equals(x.Category, column.Categories)))
-                result += kanban.EstimatedTime.TotalHours;
-            return string.Format("{0:F2}", result);
-        }
+    public List<TaskModel> Tasks { get; }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
-    }
-
-    public class BoolToVisibilityConverter : IValueConverter
+    public TasksByStatusColumn(string category, string title)
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            if (Equals(value, true))
-                return Visibility.Visible;
-            return Visibility.Collapsed;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
+        Category = category;
+        Title = title;
     }
+}
 
-    /// <summary>
-    ///     Interaction logic for KanbanPanel.xaml
-    /// </summary>
-    public partial class TasksByStatusControl : UserControl, ITaskControl
-    {
+public class EmployeeModel
+{
+    public Guid ID { get; set; }
 
-        private enum Suppress
-        {
-            This
-        }
+    public string Name { get; set; }
 
-        private BitmapImage _attachimg = PRSDesktop.Resources.attachment.AsBitmapImage();
+    public BitmapImage? Image { get; set; }
 
-        private readonly ObservableCollection<EmployeeModel> _employeelist = new();
+    public Guid ThumbnailID { get; set; }
 
-        private CoreTable _employees;
+    public EmployeeModel(Guid id, string name, Guid thumbnail, BitmapImage? image)
+    {
+        ID = id;
+        Name = name;
+        Image = image;
+        ThumbnailID = thumbnail;
+    }
+}
 
-        public CoreTable _kanbans;
 
-        private BitmapImage _lockimg = PRSDesktop.Resources.lock_sml.AsBitmapImage();
-        private ObservableCollection<TaskModel> _models = new();
-        private CoreTable _types;
+/// <summary>
+/// Interaction logic for TasksByStatusControl.xaml
+/// </summary>
+public partial class TasksByStatusControl : UserControl, ITaskControl
+{
+    public List<TasksByStatusColumn> Columns = new();
 
-        public List<string> CheckedKanbans = new();
+    private List<EmployeeModel> Employees = new();
 
-        // CoreUtils.FullGuid => All Staff
-        // Guid.Empty => Unallocated
-        // Anything Else => Actual Staff Member
-        private Guid EmployeeID = Guid.Empty;
+    public TasksByStatusControl()
+    {
+        InitializeComponent();
+    }
 
-        private Guid? MyID;
-        private string MyName = "";
-        private string searchtext = "";
-        private Guid selectedtype = CoreUtils.FullGuid;
+    #region Setup
 
-        public TasksByStatusControl()
-        {
-            using (new EventSuppressor(Suppress.This))
-                InitializeComponent();
-        }
+    public void Setup()
+    {
+        var employeesTask = LoadEmployees();
+        var kanbanTypesTask = Task.Run(() => LoadKanbanTypes());
 
-        public string SectionName => "Tasks By Status";
+        SetupToolbar();
 
-        public DataModel DataModel(Selection selection)
-        {
-            var ids = SelectedModels().Select(x => Guid.Parse(x.ID)).ToArray();
-            return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).InList(ids));
-        }
+        SetupColumns();
 
-        private void ResizeColumns()
-        {
-            //if (!bResizeRequired)
-            //    return;
+        employeesTask.Wait();
+        SetupEmployeeList();
 
-            using (var d = Dispatcher.DisableProcessing())
-            {
-                var CollapsedWidth = 50;
-                var CollapsedColumns = 0;
-                Array.ForEach(Kanban.Columns.ToArray(), x => { CollapsedColumns += x.IsExpanded ? 0 : 1; });
-                if (Kanban.Columns.Count > 0 && CollapsedColumns != Kanban.Columns.Count)
-                {
-                    var ColumnWidth = (Kanban.ActualWidth - CollapsedColumns * CollapsedWidth) / (Kanban.Columns.Count - CollapsedColumns) - 2;
-                    if (ColumnWidth != Kanban.ColumnWidth) Kanban.ColumnWidth = ColumnWidth;
-                    //bResizeRequired = false;
-                }
-            }
-        }
+        kanbanTypesTask.Wait();
+        SetupKanbanTypesLookup(kanbanTypesTask.Result);
+    }
 
+    private void SetupToolbar()
+    {
+        IncludeCompleted.Visibility = Security.IsAllowed<CanHideTaskCompletedColumn>() ? Visibility.Visible : Visibility.Collapsed;
+        IncludeCompleted.IsChecked = IncludeCompleted.Visibility == Visibility.Visible ? Host.KanbanSettings.StatusSettings.IncludeCompleted : true;
+        IncludeObserved.IsChecked = Host.KanbanSettings.StatusSettings.IncludeObserved;
+        ViewType.SelectedIndex = Host.KanbanSettings.StatusSettings.CompactView ? 1 : 0;
+    }
 
-        private void TaskMenu_Opened(object sender, RoutedEventArgs e)
-        {
-            Host.PopulateMenu(this, sender as ContextMenu);
-        }
+    #endregion
 
-        private void Kanban_SizeChanged(object sender, SizeChangedEventArgs e)
-        {
-            Kanban.ColumnWidth = Kanban.ActualWidth / Kanban.Columns.Count - 1.0F;
-        }
+    #region Columns
 
-        private void Kanban_CardDragStart(object sender, KanbanDragStartEventArgs e)
-        {
-            var models = SelectedModels(e.SelectedCard.Content as TaskModel);
-            if (models.Any(x => x.Locked))
-                e.IsCancel = true;
-        }
+    private void SetupColumns()
+    {
+        Columns.Clear();
 
-        private void Kanban_CardDragEnd(object sender, KanbanDragEndEventArgs e)
-        {
-            using (new WaitCursor())
-            {
-                var target = e.TargetColumn.Categories;
-                var models = SelectedModels(e.SelectedCard.Content as TaskModel).Where(x => !Equals(x.Category, target)).ToArray();
-                if (!models.Any())
-                    return;
-                var kanbans = Host.LoadKanbans(models, new Columns<Kanban>(x => x.ID, x => x.Category));
-                foreach (var kanban in kanbans)
-                    kanban.Category = target;
-                new Client<Kanban>().Save(kanbans, string.Format("Task Status Updated to {0}", target), (o, err) => { });
-                foreach (var model in models)
-                {
-                    model.Checked = false;
-                    model.Category = target;
-                }
+        Columns.Add(new TasksByStatusColumn("Open", "To Do"));
 
-                FilterKanbans();
-            }
-        }
+        Columns.Add(new TasksByStatusColumn("In Progress", "In Progress"));
 
-        private void Search_KeyUp(object sender, KeyEventArgs e)
-        {
-            if (string.IsNullOrWhiteSpace(Search.Text) || e.Key == Key.Return)
-            {
-                searchtext = Search.Text;
-                Refresh(true);
-            }
-        }
+        Columns.Add(new TasksByStatusColumn("Waiting", "Waiting for Others"));
 
-        private void TaskTypes_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
+        if (Host.KanbanSettings.StatusSettings.IncludeCompleted)
+            Columns.Add(new TasksByStatusColumn("Complete", "Completed"));
+    }
 
-            if (!IsReady || EventSuppressor.IsSet(Suppress.This))
-                return;
-            if (e.AddedItems.Count > 0)
-            {
-                var item = (KeyValuePair<Guid, string>)e.AddedItems[0];
-                selectedtype = item.Key;
-            }
-            else
-            {
-                selectedtype = CoreUtils.FullGuid;
-            }
+    private void Column_ContextMenuOpening(object sender, ContextMenuEventArgs e)
+    {
+        if (sender is not FrameworkElement element || element.Tag is not TasksByStatusColumn column) return;
 
-            Host.KanbanSettings.StatusSettings.SelectedType = selectedtype;
-            Host.SaveSettings();
-            ReloadKanbans();
-        }
+        var menu = element.ContextMenu;
 
-        private void IncludeCompleted_Checked(object sender, RoutedEventArgs e)
-        {
-            if (!IsReady)
-                return;
-            Host.KanbanSettings.StatusSettings.IncludeCompleted = IncludeCompleted.IsChecked.Value;
-            Host.SaveSettings();
-            SetupColumns();
-            ReloadKanbans();
-        }
+        menu.Items.Clear();
 
-        private void IncludeObserved_Checked(object sender, RoutedEventArgs e)
-        {
-            if (!IsReady)
-                return;
-            Host.KanbanSettings.StatusSettings.IncludeObserved = IncludeObserved.IsChecked.Value;
-            Host.SaveSettings();
-            ReloadKanbans();
-        }
+        menu.AddItem("New Task", null, CreateTask);
+        menu.AddItem($"Select all {column.Title} tasks", null, column, SelectAll);
+        menu.AddItem($"Deselect all {column.Title} tasks", null, column, DeselectAll);
+    }
 
-        private void IncludeLocked_Checked(object sender, RoutedEventArgs e)
+    private void SelectColumn(TasksByStatusColumn column, bool selected)
+    {
+        SelectedTasks.Clear();
+        if (selected)
         {
-            if (!IsReady)
-                return;
-            Host.KanbanSettings.StatusSettings.IncludeLocked = IncludeLocked.IsChecked.Value;
-            Host.SaveSettings();
-            ReloadKanbans();
+            SelectedTasks.AddRange(column.Tasks);
+            foreach (var task in Tasks)
+                task.Checked = string.Equals(task.Category, column.Category) ? selected : !selected;
         }
 
-        private void ViewType_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (Kanban != null)
-                Kanban.CardTemplate = ViewType.SelectedIndex > 0
-                    ? Resources["CompactKanban"] as DataTemplate
-                    : Resources["FullKanban"] as DataTemplate;
-            if (!IsReady || EventSuppressor.IsSet(Suppress.This))
-                return;
-            Host.KanbanSettings.StatusSettings.CompactView = ViewType.SelectedIndex > 0;
-            Host.SaveSettings();
+        FilterKanbans();
+    }
 
-        }
+    private void DeselectAll(TasksByStatusColumn column)
+    {
+        SelectColumn(column, false);
+    }
 
-        private static bool UpdatingEmployees = false;
+    private void SelectAll(TasksByStatusColumn column)
+    {
+        SelectColumn(column, true);
+    }
 
-        private void Employees_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (UpdatingEmployees || EventSuppressor.IsSet(Suppress.This))
-                return;
+    #endregion
 
-            if (e.AddedItems.Count == 0)
-            {
-                EmployeeID = Guid.Empty;
-            }
-            else
-            {
-                var model = _employeelist[Employees.SelectedIndex];
-                EmployeeID = model.ID;
-            }
+    #region Kanban Types
 
-            CheckedKanbans.Clear();
-            if (IsReady)
-                Refresh(true);
-        }
+    private IEnumerable<KanbanType> LoadKanbanTypes()
+    {
+        return Client.Query(
+            new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
+            new Columns<KanbanType>(x => x.ID, x => x.Description),
+            new SortOrder<KanbanType>(x => x.Description))
+            .ToObjects<KanbanType>();
+    }
 
-        private void Export_Click(object sender, RoutedEventArgs e)
+    private void SetupKanbanTypesLookup(IEnumerable<KanbanType> kanbanTypes)
+    {
+        if (ClientFactory.IsSupported<KanbanType>())
         {
-            var form = new DynamicExportForm(typeof(Kanban), _kanbans.Columns.Select(x => x.ColumnName));
-            if (form.ShowDialog() != true)
-                return;
-            var export = new Client<Kanban>().Query(
-                GetKanbanFilter(),
-                new Columns<Kanban>(form.Fields),
-                LookupFactory.DefineSort<Kanban>()
-            );
-
-            var employee = "Tasks for All Staff";
-            if (EmployeeID != CoreUtils.FullGuid)
-            {
-                if (EmployeeID == Guid.Empty)
-                {
-                    employee = "Unallocated Tasks";
-                }
-                else
+            var tasktypes = new Dictionary<Guid, string>
                 {
-                    var model = _employeelist.FirstOrDefault(x => x.ID.Equals(EmployeeID));
-                    employee = model == null ? "Tasks for (Unknown)" : "Tasks for " + (model.ID == MyID ? MyName : model.Name);
-                }
+                    { CoreUtils.FullGuid, "All Types" },
+                    { Guid.Empty, "Unallocated Types" }
+                };
+            foreach(var type in kanbanTypes)
+            {
+                tasktypes.Add(type.ID, type.Description);
             }
 
-            ExcelExporter.DoExport<Kanban>(
-                export,
-                string.Format(
-                    "{0} ({1:dd-MMM-yy})",
-                    employee,
-                    DateTime.Today
-                )
-            );
-        }
-
-        #region ITaskControl Support
-
-        public bool IsReady { get; set; }
-
-        public ITaskHost Host { get; set; }
-
-        public KanbanViewType KanbanViewType => KanbanViewType.Status;
+            TaskTypes.ItemsSource = tasktypes;
+            if (tasktypes.ContainsKey(Host.KanbanSettings.StatusSettings.SelectedType))
+                TaskTypes.SelectedValue = Host.KanbanSettings.StatusSettings.SelectedType;
+            else
+                TaskTypes.SelectedValue = CoreUtils.FullGuid;
 
-        public IEnumerable<TaskModel> SelectedModels(TaskModel sender = null)
+            TaskTypesLabel.Visibility = Visibility.Visible;
+            TaskTypes.Visibility = Visibility.Visible;
+        }
+        else
         {
-            var result = _models.Where(x => x.Checked).ToList();
-            if (sender != null && !result.Contains(sender))
-                result.Add(sender);
-            return result;
+            TaskTypesLabel.Visibility = Visibility.Collapsed;
+            TaskTypes.Visibility = Visibility.Collapsed;
         }
+    }
 
-        #endregion
-
-        #region Setup
-
-        public void Setup()
-        {
-            using (new EventSuppressor(Suppress.This))
-            {
-                SetupToolbar();
+    #endregion
 
-                SetupColumns();
+    #region Employees
 
-                SetupData();
+    private EmployeeModel SelectedEmployee;
 
-                SetupKanbanTypesLookup(false);
+    private async Task LoadEmployees()
+    {
+        Employees.Clear();
 
-                SetupMyEmployee();
+        var employeeFilter = Security.IsAllowed<CanViewOthersTasks>()
+            ? new Filter<Employee>(x => x.CanAllocateTasks).IsEqualTo(true)
+                .And(new Filter<Employee>(x => x.FinishDate).IsEqualTo(DateTime.MinValue)
+                    .Or(x => x.FinishDate).IsGreaterThan(DateTime.Today))
+            : new Filter<Employee>(x => x.ID).IsEqualTo(App.EmployeeID);
 
-                SetupEmployeeList();
-            }
-        }
+        var employees = await Task.Run(() => Client.Query<Employee>(
+            employeeFilter,
+            new Columns<Employee>(x => x.ID)
+                .Add(x => x.Thumbnail.ID)
+                .Add(x => x.Name)));
 
-        private void SetupMyEmployee()
+        var anonymous = PRSDesktop.Resources.anonymous.AsBitmapImage();
+        if (Security.IsAllowed<CanViewOthersTasks>())
         {
-            var row = _employees.Rows.FirstOrDefault(r => r.Get<Employee, Guid>(c => c.UserLink.ID) == ClientFactory.UserGuid);
-            if (row != null)
-            {
-                MyID = row.Get<Employee, Guid>(c => c.ID);
-                MyName = row.Get<Employee, string>(x => x.Name);
-            }
+            Employees.Add(new EmployeeModel(CoreUtils.FullGuid, "All Staff", Guid.Empty, PRSDesktop.Resources.everyone.AsBitmapImage()));
+            Employees.Add(new EmployeeModel(Guid.Empty, "Unallocated", Guid.Empty, null));
         }
 
-        private void SetupEmployeeList()
+        EmployeeModel? selected = null;
+        foreach (var employee in employees.ToObjects<Employee>())
         {
-            IEnumerable<CoreRow> active = null;
-            var anonymous = PRSDesktop.Resources.anonymous.AsBitmapImage();
-            if (Security.IsAllowed<CanViewOthersTasks>())
-            {
-                active = _employees.Rows.Where(r =>
-                    r.Get<Employee, bool>(x => x.CanAllocateTasks) && (r.Get<Employee, DateTime>(x => x.FinishDate).IsEmpty() ||
-                                                                       r.Get<Employee, DateTime>(x => x.FinishDate) > DateTime.Today));
-                _employeelist.Add(new EmployeeModel(CoreUtils.FullGuid, "All Staff", Guid.Empty, PRSDesktop.Resources.everyone.AsBitmapImage()));
-                _employeelist.Add(new EmployeeModel(Guid.Empty, "Unallocated", Guid.Empty, null));
-            }
-            else
-            {
-                active = _employees.Rows.Where(r => r.Get<Employee, Guid>(c => c.UserLink.ID).Equals(ClientFactory.UserGuid));
-            }
-
-            EmployeeModel selected = null;
-            foreach (var row in active)
-            {
-                var id = row.Get<Employee, Guid>(x => x.ID);
-                var userid = row.Get<Employee, Guid>(x => x.UserLink.ID);
-                var thumbnailid = row.Get<Employee, Guid>(x => x.Thumbnail.ID);
-                var name = userid.Equals(ClientFactory.UserGuid) ? "My Tasks" : row.Get<Employee, string>(x => x.Name);
+            var name = employee.ID == App.EmployeeID ? "My Tasks" : employee.Name;
 
-                var model = new EmployeeModel(id, name, thumbnailid, anonymous);
+            var model = new EmployeeModel(employee.ID, name, employee.Thumbnail.ID, anonymous);
 
-                if (userid.Equals(ClientFactory.UserGuid))
-                {
-                    _employeelist.Insert(0, model);
-                    selected = model;
-                }
-                else
-                {
-                    _employeelist.Add(model);
-                }
-            }
-
-            if (Security.IsAllowed<CanViewOthersTasks>())
+            if (employee.ID == App.EmployeeID)
             {
-                EmployeeListColumn.Width = new GridLength(1.0F, GridUnitType.Auto);
-                var thumbnails = active
-                    .Select(r => r.EntityLinkID<Employee, ImageDocumentLink>(x => x.Thumbnail) ?? Guid.Empty)
-                    .Where(x => x != Guid.Empty).ToArray();
-                Employees.ItemsSource = _employeelist;
-                Employees.SelectedItem = _employeelist.First();
-                EmployeeID = _employeelist.First().ID;
-                if (thumbnails.Any())
-                    new Client<Document>().Query(
-                        new Filter<Document>(x => x.ID).InList(thumbnails),
-                        new Columns<Document>(x => x.ID, x => x.Data),
-                        null,
-                        (data, error) =>
-                        {
-                            if (data != null)
-                                ProcessThumbnails(data);
-                        }
-                    );
+                Employees.Insert(0, model);
+                selected = model;
             }
             else
             {
-                EmployeeListColumn.Width = new GridLength(0.0F, GridUnitType.Pixel);
-                Employees.ItemsSource = _employeelist;
-                Employees.SelectedItem = _employeelist.First();
-                EmployeeID = _employeelist.First().ID;
+                Employees.Add(model);
             }
         }
+    }
 
-        private void ProcessThumbnails(CoreTable data)
-        {
-            Dispatcher.Invoke(() =>
-            {
-                foreach (var row in data.Rows)
-                {
-                    var id = row.Get<Document, Guid>(x => x.ID);
-                    var model = _employeelist.FirstOrDefault(x => x.ThumbnailID.Equals(id));
-                    if (model != null)
+    private void SetupEmployeeList()
+    {
+        if (Security.IsAllowed<CanViewOthersTasks>())
+        {
+            EmployeeListColumn.Width = new GridLength(1.0F, GridUnitType.Auto);
+            var thumbnails = Employees
+                .Select(e => e.ThumbnailID)
+                .Where(x => x != Guid.Empty).ToArray();
+            EmployeeList.ItemsSource = Employees;
+            SelectedEmployee = Employees.First();
+            EmployeeList.SelectedItem = SelectedEmployee;
+            if (thumbnails.Any())
+                new Client<Document>().Query(
+                    new Filter<Document>(x => x.ID).InList(thumbnails),
+                    new Columns<Document>(x => x.ID, x => x.Data),
+                    null,
+                    (data, error) =>
                     {
-                        model.Image = new BitmapImage();
-                        model.Image.LoadImage(row.Get<Document, byte[]>(x => x.Data));
+                        if (data != null)
+                            ProcessThumbnails(data);
                     }
-                }
-
-                UpdatingEmployees = true;
-                Employees.ItemsSource = null;
-                Employees.ItemsSource = _employeelist;
-                Employees.SelectedItem = _employeelist.First();
-                EmployeeID = _employeelist.First().ID;
-                UpdatingEmployees = false;
-            });
+                );
         }
+        else
+        {
+            EmployeeListColumn.Width = new GridLength(0.0F, GridUnitType.Pixel);
+            EmployeeList.ItemsSource = Employees;
+            SelectedEmployee = Employees.First();
+            EmployeeList.SelectedItem = SelectedEmployee;
+        }
+    }
 
-        private void SetupKanbanTypesLookup(bool reload)
+    private bool _updatingEmployees = false;
+    private void ProcessThumbnails(CoreTable data)
+    {
+        Dispatcher.Invoke(() =>
         {
-            if (ClientFactory.IsSupported<KanbanType>())
+            foreach (var row in data.Rows)
             {
-
-                if (reload)
+                var id = row.Get<Document, Guid>(x => x.ID);
+                var model = Employees.FirstOrDefault(x => x.ThumbnailID.Equals(id));
+                if (model != null)
                 {
-                    _types = new Client<KanbanType>().Query(
-                        new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
-                        new Columns<KanbanType>(x => x.ID, x => x.Description),
-                        new SortOrder<KanbanType>(x => x.Description)
-                    );
+                    model.Image = new BitmapImage();
+                    model.Image.LoadImage(row.Get<Document, byte[]>(x => x.Data));
                 }
-
-                var tasktypes = new Dictionary<Guid, string>
-                {
-                    { CoreUtils.FullGuid, "All Types" },
-                    { Guid.Empty, "Unallocated Types" }
-                };
-                _types.IntoDictionary<KanbanType, Guid, string>(tasktypes, x => x.ID, row => row.Get<KanbanType, string>(x => x.Description));
-
-                TaskTypes.ItemsSource = tasktypes;
-                if (tasktypes.ContainsKey(Host.KanbanSettings.StatusSettings.SelectedType))
-                    TaskTypes.SelectedValue = Host.KanbanSettings.StatusSettings.SelectedType;
-                else
-                    TaskTypes.SelectedValue = CoreUtils.FullGuid;
-
-                TaskTypesLabel.Visibility = Visibility.Visible;
-                TaskTypes.Visibility = Visibility.Visible;
-            }
-            else
-            {
-                TaskTypesLabel.Visibility = Visibility.Collapsed;
-                TaskTypes.Visibility = Visibility.Collapsed;
             }
-        }
 
-        private void SetupToolbar()
-        {
-            IncludeCompleted.Visibility = Security.IsAllowed<CanHideTaskCompletedColumn>() ? Visibility.Visible : Visibility.Collapsed;
-            IncludeCompleted.IsChecked = IncludeCompleted.Visibility == Visibility.Visible ? Host.KanbanSettings.StatusSettings.IncludeCompleted : true;
-            IncludeObserved.IsChecked = Host.KanbanSettings.StatusSettings.IncludeObserved;
-            ViewType.SelectedIndex = Host.KanbanSettings.StatusSettings.CompactView ? 1 : 0;
-        }
+            _updatingEmployees = true;
+            EmployeeList.ItemsSource = null;
+            EmployeeList.ItemsSource = Employees;
+            SelectedEmployee = Employees.First();
+            EmployeeList.SelectedItem = SelectedEmployee;
+            _updatingEmployees = false;
+        });
+    }
 
-        private void SetupColumns()
-        {
-            Kanban.Columns.Clear();
+    #endregion
 
-            var indicatorColorPalette = new IndicatorColorPalette();
-            indicatorColorPalette.Add(new ColorMapping { Key = "Red", Color = Colors.LightSalmon });
-            indicatorColorPalette.Add(new ColorMapping { Key = "Orange", Color = Colors.Orange });
-            indicatorColorPalette.Add(new ColorMapping { Key = "Yellow", Color = Colors.LightYellow });
-            indicatorColorPalette.Add(new ColorMapping { Key = "Green", Color = Colors.LightGreen });
-            Kanban.IndicatorColorPalette = indicatorColorPalette;
+    #region Kanbans
 
-            Kanban.Columns.Add(new KanbanColumn
-            {
-                Categories = "Open",
-                Title = "To Do"
-            });
+    private IEnumerable<TaskModel> Tasks => Columns.SelectMany(x => x.Tasks);
 
-            Kanban.Columns.Add(new KanbanColumn
-            {
-                Categories = "In Progress",
-                Title = "In Progress"
-            });
+    private readonly List<TaskModel> SelectedTasks = new();
 
-            Kanban.Columns.Add(new KanbanColumn
+    private void CreateTask()
+    {
+        var result = Host.CreateKanban(
+            kanban =>
             {
-                Categories = "Waiting",
-                Title = "Waiting for Others"
+                kanban.EmployeeLink.ID = SelectedEmployee.ID != CoreUtils.FullGuid ? SelectedEmployee.ID : App.EmployeeID;
+                kanban.ManagerLink.ID = App.EmployeeID;
+                kanban.ManagerLink.UserLink.ID = ClientFactory.UserGuid;
             });
+        if (result != null)
+            Refresh(true);
+    }
 
-            if (Host.KanbanSettings.StatusSettings.IncludeCompleted)
-                Kanban.Columns.Add(new KanbanColumn
-                {
-                    Categories = "Complete",
-                    Title = "Completed"
-                });
-            Kanban.InvalidateVisual();
-
-            foreach (var column in Kanban.Columns)
-            {
-                var menu = new ContextMenu();
-                menu.Tag = column;
-                var item = new MenuItem { Header = "New Task" };
-                item.Click += CreateTask;
-                menu.Items.Add(item);
-                menu.Items.Add(new Separator());
-                item = new MenuItem { Header = "Select All " + column.Title + " Tasks", Tag = column };
-                item.Click += SelectAll_Click;
-                menu.Items.Add(item);
-                item = new MenuItem { Header = "Unselect All " + column.Title + " Tasks", Tag = column };
-                item.Click += UnSelectAll_Click;
-                menu.Items.Add(item);
-                column.ContextMenu = menu;
-            }
-
-            Kanban.ColumnWidth = Kanban.ActualWidth / Kanban.Columns.Count - 1.0F;
-        }
-
-        private void SetupData()
-        {
-            var query = new MultiQuery();
-
-            query.Add(
-                null,
-                new Columns<Employee>(x => x.ID, x => x.Name, x => x.Thumbnail.ID, x => x.CanAllocateTasks, x => x.Email, x => x.Mobile,
-                    x => x.FinishDate, x => x.UserLink.ID),
-                new SortOrder<Employee>(x => x.Name)
-            );
-
-            if (ClientFactory.IsSupported<KanbanType>())
-                query.Add(
-                    new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
-                    new Columns<KanbanType>(x => x.ID, x => x.Description),
-                    new SortOrder<KanbanType>(x => x.Description)
-                );
-
-            query.Query();
-
-            _employees = query.Get<Employee>();
-            _types = ClientFactory.IsSupported<KanbanType>()
-                ? query.Get<KanbanType>()
-                : null;
-        }
-
-        #endregion
-
-        #region Refresh / Reload
-
-        private Filter<T> GetSearchFilter<T>(Expression<Func<T, object>> expression) where T : Entity, new()
+    private void DoEdit(TaskModel task)
+    {
+        var result = Host.EditReferences(new[] { task });
+        if (result)
         {
-            Filter<T> result = null;
-            var comps = searchtext.Trim().Split(' ');
-            foreach (var comp in comps)
-                result = result == null ? new Filter<T>(expression).Contains(comp) : result.And(expression).Contains(comp);
-            return result;
+            Refresh(true);
         }
+    }
 
-        private Filter<KanbanSubscriber> GetKanbanSubscriberFilter()
-        {
-            var filter = new Filter<KanbanSubscriber>(x => x.Kanban.Closed).IsEqualTo(DateTime.MinValue);
-
-            if (Host.Job != null)
-            {
-                if (Host.Job.ID != Guid.Empty)
-                    filter = filter.And(x => x.Kanban.JobLink.ID).IsEqualTo(Host.Job.ID);
-                else
-                    filter = filter.And(x => x.Kanban.JobLink.ID).None();
-            }
-
-            // All Tasks (EmployeeID.HasValue == false) or Unallocated (EmployeeID = Guid.Empty) are retrieved directly from the Kanban Table
-            // so if we are here, we can assume that we are pulling subscriber data
-            var empfilter = new Filter<KanbanSubscriber>(x => x.Employee.ID).IsEqualTo(EmployeeID);
-            filter.Ands.Add(empfilter);
-
-            if (EmployeeID != MyID)
-                filter = filter.And(x => x.Kanban.Private).IsEqualTo(false);
-
-            //if (!includeobserved)
-            //    filter = filter.And(new Filter<KanbanSubscriber>(x => x.Assignee).IsEqualTo(true).Or(x => x.Manager).IsEqualTo(true));
-
-            //if (!includecompleted)
-            //    filter = filter.And(x => x.Kanban.Completed).IsEqualTo(DateTime.MinValue);
 
-            //if (selectedtype != CoreUtils.FullGuid)
-            //    filter = filter.And(x => x.Kanban.Type.ID).IsEqualTo(selectedtype);
+    private void EditTask_Executed(object sender, ExecutedRoutedEventArgs e)
+    {
+        if (e.Parameter is not TaskModel model) return;
 
-            if (!string.IsNullOrWhiteSpace(searchtext))
-            {
-                var search = GetSearchFilter<KanbanSubscriber>(x => x.Kanban.JobLink.Name);
-                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.JobLink.JobNumber));
-                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.Summary));
-                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.Title));
-                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.ManagerLink.Name));
-                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.EmployeeLink.Name));
+        DoEdit(model);
+    }
 
-                if (int.TryParse(searchtext.Trim(), out var tasknumber))
-                    search.Ors.Add(new Filter<KanbanSubscriber>(x => x.Kanban.Number).IsEqualTo(tasknumber));
+    private void OpenTaskMenu_Executed(object sender, ExecutedRoutedEventArgs e)
+    {
+        if (e.Parameter is not TaskModel model) return;
 
-                filter.Ands.Add(search);
-            }
+        Host.PopulateMenu(this, sender as ContextMenu);
+    }
 
-            return filter;
-        }
+    private void SelectTask_Executed(object sender, ExecutedRoutedEventArgs e)
+    {
+        if (e.Parameter is not TaskModel model) return;
 
-        private Filter<Kanban> GetKanbanFilter()
+        if (!SelectedTasks.Remove(model))
         {
-            var filter = new Filter<Kanban>(x => x.Closed).IsEqualTo(DateTime.MinValue);
-
-            if (Host.Job != null)
-            {
-                if (Host.Job.ID != Guid.Empty)
-                    filter = filter.And(x => x.JobLink.ID).IsEqualTo(Host.Job.ID);
-                else
-                    filter = filter.And(x => x.JobLink.ID).None();
-            }
-
-            if (EmployeeID != CoreUtils.FullGuid)
-            {
-                if (EmployeeID != Guid.Empty)
-                {
-                    var empfilter = new Filter<Kanban>(x => x.EmployeeLink.ID).IsEqualTo(EmployeeID).Or(x => x.ManagerLink.ID).IsEqualTo(EmployeeID);
-                    filter.Ands.Add(empfilter);
-                }
-                else
-                {
-                    filter = filter.And(x => x.EmployeeLink.ID).IsEqualTo(EmployeeID);
-                }
-            }
-
-            if (EmployeeID != MyID)
-                filter = filter.And(x => x.Private).IsEqualTo(false);
-
-            //if (!includecompleted)
-            //    filter = filter.And(x => x.Completed).IsEqualTo(DateTime.MinValue);
-
-            //if (selectedtype != CoreUtils.FullGuid)
-            //    filter = filter.And(x => x.Type.ID).IsEqualTo(selectedtype);
-
-            return filter;
+            SelectedTasks.Add(model);
         }
+    }
 
-        public void Refresh(bool resetselection)
-        {
-            Application.Current.Dispatcher.Invoke(() => { Mouse.OverrideCursor = Cursors.Wait; });
+    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
+    {
+        e.CanExecute = e.Parameter is TaskModel;
+    }
 
-            if (EmployeeID != CoreUtils.FullGuid && EmployeeID != Guid.Empty)
-            {
-                _kanbans = new Client<KanbanSubscriber>().Query(
-                    GetKanbanSubscriberFilter(),
-                    new Columns<KanbanSubscriber>
-                    (
-                        x => x.Kanban.ID,
-                        x => x.Kanban.DueDate,
-                        x => x.Kanban.Completed,
-                        //x => x.Kanban.Description,
-                        x => x.Kanban.Summary,
-                        x => x.Kanban.Category,
-                        x => x.Kanban.EmployeeLink.ID,
-                        x => x.Kanban.ManagerLink.ID,
-                        x => x.Kanban.Notes,
-                        x => x.Kanban.Title,
-                        x => x.Kanban.JobLink.ID,
-                        x => x.Kanban.JobLink.JobNumber,
-                        x => x.Kanban.JobLink.Name,
-                        x => x.Kanban.Type.ID,
-                        x => x.Kanban.Type.Code,
-                        x => x.Kanban.Number,
-                        x => x.Kanban.Attachments,
-                        x => x.Kanban.Locked
-                    ),
-                    new SortOrder<KanbanSubscriber>(x => x.Kanban.DueDate) { Direction = SortDirection.Ascending }
-                );
-                foreach (var column in _kanbans.Columns)
-                    column.ColumnName = column.ColumnName.Replace("Kanban.", "");
-            }
-            else
-            {
-                _kanbans = new Client<Kanban>().Query(
-                    GetKanbanFilter(),
-                    new Columns<Kanban>
-                    (
-                        x => x.ID,
-                        x => x.DueDate,
-                        x => x.Completed,
-                        //x => x.Description,
-                        x => x.Summary,
-                        x => x.Category,
-                        x => x.EmployeeLink.ID,
-                        x => x.ManagerLink.ID,
-                        x => x.Notes,
-                        x => x.Title,
-                        x => x.JobLink.ID,
-                        x => x.JobLink.JobNumber,
-                        x => x.JobLink.Name,
-                        x => x.Type.ID,
-                        x => x.Type.Code,
-                        x => x.Number,
-                        x => x.Attachments,
-                        x => x.Locked
-                    ),
-                    new SortOrder<Kanban>(x => x.DueDate) { Direction = SortDirection.Ascending }
-                );
-            }
+    #endregion
 
-            ReloadKanbans();
+    public void Refresh(bool resetselection)
+    {
+    }
 
-            Application.Current.Dispatcher.Invoke(() => { Mouse.OverrideCursor = null; });
-        }
+    #region ITaskControl
 
-        private void ReloadKanbans()
-        {
-            //SetupColumns();
-            //ResizeColumns();
+    public ITaskHost Host { get; set; }
 
-            _models = new ObservableCollection<TaskModel>();
+    public KanbanViewType KanbanViewType => KanbanViewType.Status;
 
-            foreach (var row in _kanbans.Rows)
-                try
-                {
-                    var empid = row.Get<IKanban, Guid>(e => e.EmployeeLink.ID);
-                    var mgrid = row.EntityLinkID<IKanban, EmployeeLink>(x => x.ManagerLink) ?? Guid.Empty;
-                    var completed = row.Get<IKanban, DateTime>(e => e.Completed);
-                    var locked = row.Get<IKanban, bool>(e => e.Locked);
-                    var type = row.Get<IKanban, Guid>(e => e.Type.ID);
-                    var category = row.Get<IKanban, string>(x => x.Category);
-                    if (string.IsNullOrWhiteSpace(category))
-                        category = "Open";
-
-
-                    var bLockedOK = Host.KanbanSettings.StatusSettings.IncludeLocked || locked == false;
-                    var bObserveOK = EmployeeID == CoreUtils.FullGuid || Host.KanbanSettings.StatusSettings.IncludeObserved || empid == EmployeeID || mgrid == EmployeeID;
-                    var bCompleteOK = Host.KanbanSettings.StatusSettings.IncludeCompleted || completed.IsEmpty();
-                    var bTypeOK = selectedtype == CoreUtils.FullGuid || type == selectedtype;
-                    if (bLockedOK && bCompleteOK && bObserveOK && bTypeOK)
-                    {
-                        var model = new TaskModel();
-                        var EmployeeEntry = _employeelist.Where(x => x.ID.Equals(empid)).FirstOrDefault();
-                        var empimg = EmployeeEntry?.Image;
-
-                        var ManagerEntry = _employeelist.Where(x => x.ID.Equals(mgrid)).FirstOrDefault();
-
-                        //var description = row.Get<IKanban, String>(x => x.Summary);
-                        //if (String.IsNullOrWhiteSpace(description))
-                        //    description = String.Join("\r\n", row.Get<IKanban, String[]>(x => x.Notes)).Split(new String[] { "===============" }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
-                        //if (String.IsNullOrWhiteSpace(description))
-                        //    description = CoreUtils.StripHTML(row.Get<IKanban, String>(x => x.Description));
-                        //if (String.IsNullOrWhiteSpace(description))
-                        //    description = "";
-                        var job = row.Get<IKanban, string>(x => x.JobLink.JobNumber);
-
-                        model.Title = row.Get<IKanban, string>(x => x.Title);
-                        model.ID = row.Get<IKanban, Guid>(x => x.ID).ToString();
-                        model.Description = row.Get<IKanban, string>(x => x.Summary) ?? "";
-                        model.Category = category;
-                        var color = EmployeeID == Guid.Empty || empid == EmployeeID || EmployeeID == CoreUtils.FullGuid
-                            ? TaskModel.KanbanColor(
-                                row.Get<IKanban, DateTime>(x => x.DueDate),
-                                row.Get<IKanban, DateTime>(x => x.Completed))
-                            : mgrid == EmployeeID
-                                ? Color.Silver
-                                : Color.Plum;
-                        if (row.Get<IKanban, bool>(x => x.Locked))
-                            color = color.MixColors(0.5F, Color.White);
-                        model.ColorKey = ImageUtils.ColorToString(color);
-                        model.Image = empimg;
-                        model.ImageURL = null;
-                        model.Attachments = row.Get<IKanban, int>(x => x.Attachments) > 0; // ? _attachimg : null;
-                        model.DueDate = row.Get<IKanban, DateTime>(x => x.DueDate);
-                        model.CompletedDate = row.Get<IKanban, DateTime>(x => x.Completed);
-                        model.Locked = row.Get<IKanban, bool>(x => x.Locked); // ? _lockimg : null;
-
-                        var notes = new List<List<string>> { new() };
-                        if ((row.Get<IKanban, string[]>(x => x.Notes) != null))
-                        {
-
-
-                            foreach (var line in row.Get<IKanban, string[]>(x => x.Notes))
-                            {
-                                if (line == "===================================")
-                                {
-                                    notes.Add(new());
-                                }
-                                else
-                                {
-                                    notes.Last().Add(line);
-                                }
-                            }
-                            model.Notes = string.Join("\n===================================\n", notes.Reverse<List<string>>().Select(x => string.Join('\n', x)));
-                        }
-
-                        model.EmployeeID = empid;
-                        model.ManagerID = mgrid;
-
-                        var sEmp = "";
-                        if (empid != EmployeeID)
-                        {
-                            if (!Entity.IsEntityLinkValid<IKanban, EmployeeLink>(x => x.EmployeeLink, row))
-                            {
-                                sEmp = "Unallocated";
-                            }
-                            else
-                            {
-                                var tuple = _employeelist.FirstOrDefault(x => x.ID.Equals(empid));
-                                sEmp = tuple != null ? tuple.ID == MyID ? MyName : tuple.Name : "";
-                            }
-                        }
-
-                        var sMgr = "";
-                        if (mgrid != EmployeeID)
-                            if (mgrid != Guid.Empty)
-                            {
-                                var tuple = _employeelist.FirstOrDefault(x => x.ID.Equals(mgrid));
-                                sMgr = tuple != null ? tuple.ID == MyID ? MyName : tuple.Name : "";
-                            }
-
-                        if (!string.IsNullOrEmpty(sEmp))
-                        {
-                            if (!string.IsNullOrWhiteSpace(sMgr) && !string.Equals(sMgr, sEmp))
-                                model.AssignedTo = string.Format("Assigned to {0} by {1}", sEmp, sMgr);
-                            else
-                                model.AssignedTo = string.Format("Assigned to {0} ", sEmp);
-                        }
-                        else
-                        {
-                            if (!string.IsNullOrWhiteSpace(sMgr))
-                                model.AssignedTo = string.Format("Allocated by {0} ", sMgr);
-                        }
-
-                        //model.AssignedTo = String.Format("M: {0} / E: {1}", sMgr, sEmp);
-                        model.JobID = row.Get<IKanban, Guid>(x => x.JobLink.ID);
-                        model.JobNumber = row.Get<IKanban, string>(x => x.JobLink.JobNumber);
-                        if (string.IsNullOrWhiteSpace(model.JobNumber))
-                            model.JobNumber = "";
-                        model.JobName = row.Get<IKanban, string>(x => x.JobLink.Name);
-                        model.Checked = CheckedKanbans.Contains(row.Get<IKanban, Guid>(x => x.ID).ToString());
-                        model.Type = new KanbanType
-                        {
-                            ID = row.Get<IKanban, Guid>(x => x.Type.ID),
-                            Code = row.Get<IKanban, string>(x => x.Type.Code)
-                        };
-                        model.Number = row.Get<IKanban, int>(x => x.Number);
-
-                        _models.Add(model);
-                    }
-                }
-                catch (Exception e)
-                {
-                    Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
-                }
+    public bool IsReady { get; set; }
 
-            StatusTasksHeaderTimeConverter.Tasks = _models;
-            FilterKanbans();
-        }
+    public string SectionName => "Tasks By Status";
 
-        private void FilterKanbans()
-        {
-            Progress.ShowModal("Loading", (progress) =>
-            {
-                Dispatcher.BeginInvoke(() => { Progress.Show("Loading"); });
-                if (JobFilterID == Guid.Empty)
-                {
-                    var list = _models
-                         .Where(x => x.Search(Search.Text.Split()))
-                         .OrderBy(x => x.EmployeeID == EmployeeID ? 0 : 1).ThenBy(x => x.DueDate);
-                    Dispatcher.BeginInvoke(() => { Kanban.ItemsSource = list; });
-                }
-                else
-                {
-                    var list = _models
-                        .Where(x => x.Search(Search.Text.Split()) && x.JobSearch(JobFilterID))
-                        .OrderBy(x => x.EmployeeID == EmployeeID ? 0 : 1).ThenBy(x => x.DueDate);
-                    Dispatcher.BeginInvoke(() => { Kanban.ItemsSource = list; });
-                }
-                Task.Run(() => 
-                {
-                    Thread.Sleep(500);
-                    Dispatcher.BeginInvoke(() =>
-                    {
-                        Progress.Close();
-                    });
-                });               
-            });
-        }
+    public DataModel DataModel(Selection selection)
+    {
+        var ids = SelectedModels().Select(x => Guid.Parse(x.ID)).ToArray();
+        return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).InList(ids));
+    }
 
-        #endregion
+    public IEnumerable<TaskModel> SelectedModels(TaskModel? sender = null)
+    {
+        return Enumerable.Empty<TaskModel>();
+    }
 
-        #region Kanban Selection Stuff
+    #endregion
 
-        private void SelectColumn(KanbanColumn column, bool selected)
-        {
-            CheckedKanbans.Clear();
-            if (selected)
-            {
-                CheckedKanbans.AddRange(_models.Where(x => Equals(x.Category, column.Categories)).Select(x => x.ID));
-                foreach (var model in _models)
-                    model.Checked = Equals(model.Category, column.Categories);
-            }
+    private void Employees_SelectionChanged(object sender, SelectionChangedEventArgs e)
+    {
 
-            FilterKanbans();
-        }
+    }
 
-        private void UnSelectAll_Click(object sender, RoutedEventArgs e)
-        {
-            var column = ((MenuItem)sender).Tag as KanbanColumn;
-            SelectColumn(column, false);
-        }
+    private void TaskTypesLabel_OnClick(object sender, RoutedEventArgs e)
+    {
 
-        private void SelectAll_Click(object sender, RoutedEventArgs e)
-        {
-            var column = ((MenuItem)sender).Tag as KanbanColumn;
-            SelectColumn(column, true);
-        }
+    }
 
-        //private IEnumerable<Guid> GetSelectedKanbanIDs(String currentid)
-        //{
-        //    List<Guid> result = new List<Guid>();
-        //    if (!CheckedKanbans.Contains(currentid))
-        //        result.Add(Guid.Parse(currentid));
-        //    CheckedKanbans.ForEach((id) => result.Add(Guid.Parse(id)));
-        //    return result;
-        //}
-
-        //private Kanban[] GetSelectedKanbans(String currentid)
-        //{
-        //    var ids = GetSelectedKanbanIDs(currentid);
-        //    return Host.LoadKanbans(ids).ToArray();
-        //}
-
-        private void CheckBox_Checked(object sender, RoutedEventArgs e)
-        {
-            var task = ((CheckBox)sender).Tag as TaskModel;
-            if (task == null)
-                return;
-            if (CheckedKanbans.Contains(task.ID))
-                CheckedKanbans.Remove(task.ID);
-            else
-                CheckedKanbans.Add(task.ID);
-        }
+    private void TaskTypes_SelectionChanged(object sender, SelectionChangedEventArgs e)
+    {
 
-        #endregion
+    }
 
-        #region Context Menu Actions
+    private void JobFilterBtn_OnClick(object sender, RoutedEventArgs e)
+    {
 
-        private void CreateTask(object sender, RoutedEventArgs e)
-        {
-            CreateKanban();
-        }
+    }
 
-        private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
-        {
-            if (e.ClickCount > 1)
-            {
-                var task = ((Border)sender).Tag as TaskModel;
-                DoEdit(task);
-                e.Handled = true;
-            }
-        }
+    private void Export_Click(object sender, RoutedEventArgs e)
+    {
 
-        private void EditTask_Click(object sender, RoutedEventArgs e)
-        {
-            var task = ((MenuItem)e.Source).Tag as TaskModel;
-            DoEdit(task);
-            e.Handled = true;
-        }
+    }
 
-        //private void CreateSetout_Click(object sender, RoutedEventArgs e)
-        //{
-        //    MenuItem menu = sender as MenuItem;
-        //    TaskModel task = menu.Tag as TaskModel;
-
-        //    if (task.JobID.Equals(Guid.Empty))
-        //    {
-        //        MessageBox.Show("Please link this task to a job before creating a setout!");
-        //        return;
-        //    }
-
-        //    if (MessageBox.Show("This will convert this task into a Setout.\n\nDo you wish to continue?", "Confirmation", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
-        //        return;
-
-        //    ManufacturingTemplate template = new Client<ManufacturingTemplate>().Load(new Filter<ManufacturingTemplate>(x => x.Code).IsEqualTo("PRS")).FirstOrDefault();
-        //    if (template == null)
-        //    {
-        //        MessageBox.Show("[Pressing] Template does not exist!");
-        //        return;
-        //    }
-
-        //    String setoutnumber = "";
-
-        //    Progress.ShowModal("Creating Setout", (progress) =>
-        //    {
-        //        MultiQuery query = new MultiQuery();
-
-        //        query.Add<ManufacturingTemplateStage>(
-        //            new Filter<ManufacturingTemplateStage>(x => x.Template.ID).IsEqualTo(template.ID),
-        //            null,
-        //            new SortOrder<ManufacturingTemplateStage>(x => x.Sequence)
-        //        );
-
-        //        query.Add<Kanban>(
-        //            new Filter<Kanban>(x => x.ID).IsEqualTo(task.ID),
-        //            null,
-        //            null
-        //        );
-
-        //        query.Query();
-
-        //        ManufacturingTemplateStage[] tstages = query.Get<ManufacturingTemplateStage>().Rows.Select(x => x.ToObject<ManufacturingTemplateStage>()).ToArray();
-
-        //        Kanban kanban = query.Get<Kanban>().Rows.FirstOrDefault()?.ToObject<Kanban>();
-
-        //        progress.Report("Creating Setouts");
-        //        CoreTable setouts = new Client<Setout>().Query(
-        //            new Filter<Setout>(x => x.JobLink.ID).IsEqualTo(kanban.JobLink.ID),
-        //            new Columns<Setout>(x => x.JobLink.JobNumber, x => x.Number),
-        //            null
-        //         );
-
-        //        int ireq = 0;
-        //        String sreq = "";
-        //        while (true)
-        //        {
-        //            ireq++;
-        //            sreq = String.Format("{0}-{1:yyMMdd}-{2}", kanban.JobLink.JobNumber, DateTime.Now, ireq);
-        //            if (!setouts.Rows.Any(r => sreq.Equals(r.Get<Setout, String>(c => c.Number))))
-        //                break;
-        //        }
-
-        //        Setout setout = new Setout();
-        //        setout.Number = sreq;
-        //        setout.JobLink.ID = kanban.JobLink.ID; // = new Client<Job>().Load(new Filter<Job>(x => x.ID).IsEqualTo(kanban.JobLink.ID)).FirstOrDefault();
-        //        setout.Reference = kanban.Title;
-
-        //        var notes = kanban.Notes.ToList();
-        //        var description = kanban.Summary;
-        //        if (String.IsNullOrWhiteSpace(description))
-        //            description = CoreUtils.StripHTML(kanban.Description);
-        //        if (!String.IsNullOrWhiteSpace(description))
-        //            notes.Insert(0, description);
-        //        setout.Description = String.Join("\n==========================================\n", notes);
-
-        //        new Client<Setout>().Save(setout, "Created from Task");
-        //        setoutnumber = setout.Number;
-
-        //        progress.Report("Creating Manufacturing Packet");
-
-        //        ManufacturingPacket packet = new ManufacturingPacket()
-        //        {
-        //            Group = template.Factory.Name,
-        //            Serial = template.Code,
-        //            Title = kanban.Title,
-        //            Quantity = 1,
-        //            BarcodeQty = 1,
-        //            DueDate = kanban.DueDate
-        //        };
-        //        packet.SetoutLink.ID = setout.ID;
-        //        //packet.JobLink.ID = setout.JobLink.ID;
-        //        packet.ManufacturingTemplateLink.ID = template.ID;
-        //        packet.ManufacturingTemplateLink.Code = template.Code;
-        //        new Client<ManufacturingPacket>().Save(packet, "Created from Task");
-
-        //        List<ManufacturingPacketStage> pstages = new List<ManufacturingPacketStage>();
-        //        foreach (var tstage in tstages)
-        //        {
-        //            var pstage = new ManufacturingPacketStage()
-        //            {
-        //                Time = tstage.Time,
-        //                Sequence = tstage.Sequence,
-        //                SequenceType = tstage.SequenceType,
-        //                Started = DateTime.MinValue,
-        //                PercentageComplete = 0.0F,
-        //                Completed = DateTime.MinValue,
-        //                QualityChecks = tstage.QualityChecks,
-        //                QualityStatus = QualityStatus.NotChecked,
-        //                QualityNotes = "",
-        //            };
-        //            pstage.Parent.ID = packet.ID;
-        //            pstage.ManufacturingSectionLink.ID = tstage.Section.ID;
-        //            pstage.ManufacturingSectionLink.Name = tstage.Section.Name;
-        //            pstages.Add(pstage);
-        //        }
-        //        new Client<ManufacturingPacketStage>().Save(pstages, "Created from Task", (_, __) => { });
-
-        //        progress.Report("Processing Documents");
-
-
-        //        List<SetoutDocument> _setoutdocuments = new List<SetoutDocument>();
-        //        List<KanbanDocument> _kanbandocuments = new List<KanbanDocument>();
-        //        KanbanDocument[] docrefs = new Client<KanbanDocument>().Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID));
-        //        foreach (var docref in docrefs)
-        //        {
-        //            Guid docid = ProcessSetoutDocument(docref);
-        //            var newdoc = new SetoutDocument();
-        //            newdoc.EntityLink.ID = setout.ID;
-        //            newdoc.DocumentLink.ID = docid;
-        //            _setoutdocuments.Add(newdoc);
-
-        //            if (docid != docref.DocumentLink.ID)
-        //            {
-        //                docref.DocumentLink.ID = docid;
-        //                _kanbandocuments.Add(docref);
-        //            }
-        //        }
-        //        new Client<SetoutDocument>().Save(_setoutdocuments, "Converted from Task", (_, __) => { });
-        //        new Client<KanbanDocument>().Save(_kanbandocuments, "Converted to PDF", (_, __) => { });
-
-        //        SetoutKanban link = new SetoutKanban();
-        //        link.Entity.ID = setout.ID;
-        //        link.Kanban.ID = kanban.ID;
-        //        new Client<SetoutKanban>().Save(link, "Converting Task -> Setout", (_, __) => { });
-
-        //        progress.Report("Updating Task");
-        //        kanban.Title = kanban.Title + " (" + setoutnumber + ")";
-        //        kanban.Locked = true;
-        //        new Client<Kanban>().Save(kanban, "Converting Kanban to Setout");
-
-
-        //    });
-
-        //    MessageBox.Show(String.Format("Created Setout {0}", setoutnumber));
-
-        //    Refresh();
-
-        //}
-
-        //private Guid ProcessSetoutDocument(KanbanDocument docref)
-        //{
-        //    Guid result = docref.DocumentLink.ID;
-
-        //    String ext = System.IO.Path.GetExtension(docref.DocumentLink.FileName).ToLower();
-
-        //    if (ext.EndsWith("txt"))
-        //    {
-        //        var doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault();
-        //        PdfDocument pdf = new PdfDocument();
-        //        PdfPage page = pdf.Pages.Add();
-        //        PdfGraphics graphics = page.Graphics;
-        //        PdfFont font = new PdfStandardFont(PdfFontFamily.Courier, 12);
-        //        String text = System.Text.Encoding.UTF8.GetString(doc.Data);
-        //        graphics.DrawString(text, font, PdfBrushes.Black, new PointF(0, 0));
-        //        MemoryStream ms = new MemoryStream();
-        //        pdf.Save(ms);
-        //        pdf.Close(true);
-        //        byte[] data = ms.ToArray();
-        //        var newdoc = new Document()
-        //        {
-        //            Data = data,
-        //            FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"),
-        //            CRC = CoreUtils.CalculateCRC(data),
-        //            TimeStamp = DateTime.Now,
-        //        };
-        //        new Client<Document>().Save(newdoc, "Converted from Text");
-        //        return newdoc.ID;
-        //    }
-
-        //    else if (ext.EndsWith("png") || ext.EndsWith("bmp") || ext.EndsWith("jpg") || ext.EndsWith("jpeg"))
-        //    {
-        //        var doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault();
-
-        //        PdfBitmap image = new PdfBitmap(new MemoryStream(doc.Data));
-        //        PdfDocument pdf = new PdfDocument();
-        //        pdf.PageSettings.Orientation = image.Height > image.Width ? PdfPageOrientation.Portrait : PdfPageOrientation.Landscape;
-        //        pdf.PageSettings.Size = new SizeF(image.Width, image.Height);
-        //        PdfPage page = pdf.Pages.Add();
-        //        PdfGraphics graphics = page.Graphics;
-        //        graphics.DrawImage(image, 0.0F, 0.0F);
-        //        MemoryStream ms = new MemoryStream();
-        //        pdf.Save(ms);
-        //        pdf.Close(true);
-        //        byte[] data = ms.ToArray();
-        //        var newdoc = new Document()
-        //        {
-        //            Data = data,
-        //            FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"),
-        //            CRC = CoreUtils.CalculateCRC(data),
-        //            TimeStamp = DateTime.Now,
-        //        };
-        //        new Client<Document>().Save(newdoc, "Converted from Image");
-        //        return newdoc.ID;
-        //    }
-
-        //    return result;
-
-        //}
-
-        //private void CreateRequisition_Click(object sender, RoutedEventArgs e)
-        //{
-        //    MenuItem menu = sender as MenuItem;
-        //    TaskModel task = menu.Tag as TaskModel;
-
-        //    if (task.JobID.Equals(Guid.Empty))
-        //    {
-        //        MessageBox.Show("Please link this task to a job before creating a requisition!");
-        //        return;
-        //    }
-
-        //    if (MessageBox.Show("This will convert this task into a Requisition.\n\nDo you wish to continue?", "Confirmation", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
-        //        return;
-
-        //    int requinumber = 0;
-        //    Progress.ShowModal("Creating Requisition", (progress) =>
-        //    {
-        //        Kanban kanban = new Client<Kanban>().Load(new Filter<Kanban>(x => x.ID).IsEqualTo(task.ID)).FirstOrDefault();
-
-        //        Requisition requi = new Requisition();
-        //        requi.JobLink.ID = kanban.JobLink.ID;
-
-        //        requi.RequestedBy.ID = kanban.ManagerLink.ID;
-        //        requi.Employee.ID = Guid.Empty;
-        //        requi.Title = kanban.Title;
-
-        //        requi.Request = String.IsNullOrWhiteSpace(kanban.Summary)
-        //            ? String.IsNullOrWhiteSpace(kanban.Summary)
-        //                ? String.Join("\n", kanban.Notes)
-        //                : CoreUtils.StripHTML(kanban.Description)
-        //            : kanban.Summary;
-
-        //        requi.Notes = kanban.Notes;
-        //        requi.Due = kanban.DueDate;
-        //        new Client<Requisition>().Save(requi, "Created from Task");
-        //        requinumber = requi.Number;
-        //        progress.Report("Updating Documents");
-
-        //        List<RequisitionDocument> _documents = new List<RequisitionDocument>();
-        //        KanbanDocument[] documents = new Client<KanbanDocument>().Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID));
-        //        foreach (var document in documents)
-        //        {
-        //            var newdoc = new RequisitionDocument();
-        //            newdoc.EntityLink.ID = requi.ID;
-        //            newdoc.DocumentLink.ID = document.DocumentLink.ID;
-        //            _documents.Add(newdoc);
-
-        //        }
-        //        new Client<RequisitionDocument>().Save(_documents, "Converted from Task", (_, __) => { });
-
-        //        RequisitionKanban link = new RequisitionKanban();
-        //        link.Entity.ID = requi.ID;
-        //        link.Kanban.ID = kanban.ID;
-        //        new Client<RequisitionKanban>().Save(link, "Converting Task -> Requisition", (_, __) => { });
-
-        //        progress.Report("Updating Task");
-        //        kanban.Category = "Open";
-        //        kanban.Completed = DateTime.MinValue;
-        //        kanban.Locked = true;
-        //        kanban.Title = kanban.Title + " (Requi #" + requi.Number.ToString() + ")";
-        //        new Client<Kanban>().Save(kanban, "Converted to Requisition", (_, __) => { });
-
-        //    });
-
-        //    MessageBox.Show(String.Format("Created Requisition {0}", requinumber));
-        //    Refresh();
-
-        //}
-
-        //private void CreatePurchaseOrder_Click(object sender, RoutedEventArgs e)
-        //{
-        //    MenuItem menu = sender as MenuItem;
-        //    TaskModel task = menu.Tag as TaskModel;
-
-        //    if (MessageBox.Show("This will convert this task into a Purchase Order.\n\nDo you wish to continue?", "Confirmation", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
-        //    {
-        //        String ponumber = "";
-        //        Progress.ShowModal("Creating Purchase Order", (progress) =>
-        //        {
-
-        //            Kanban kanban = new Client<Kanban>().Load(new Filter<Kanban>(x => x.ID).IsEqualTo(task.ID)).FirstOrDefault();
-
-        //            PurchaseOrder order = new PurchaseOrder();
-        //            order.Notes = kanban.Summary;
-        //            order.RaisedBy.ID = kanban.EmployeeLink.ID;
-        //            new Client<PurchaseOrder>().Save(order, "Created from Task Screen");
-        //            ponumber = order.PONumber;
-
-        //            progress.Report("Updating Documents");
-        //            List<PurchaseOrderDocument> docs = new List<PurchaseOrderDocument>();
-        //            var taskdocs = new Client<KanbanDocument>().Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(Guid.Parse(task.ID)));
-        //            foreach (var taskdoc in taskdocs)
-        //            {
-        //                PurchaseOrderDocument doc = new PurchaseOrderDocument();
-        //                doc.DocumentLink.ID = taskdoc.DocumentLink.ID;
-        //                doc.EntityLink.ID = order.ID;
-        //                docs.Add(doc);
-        //            }
-        //            new Client<PurchaseOrderDocument>().Save(docs, "", (_, __) => { });
-
-        //            progress.Report("Creating Links");
-        //            PurchaseOrderKanban link = new PurchaseOrderKanban();
-        //            link.Entity.ID = order.ID;
-        //            link.Kanban.ID = kanban.ID;
-        //            new Client<PurchaseOrderKanban>().Save(link, "Converting Task -> Purchase Order", (_, __) => { });
-
-        //            progress.Report("Updating Task");
-        //            kanban.Category = "Open";
-        //            kanban.Completed = DateTime.MinValue;
-        //            kanban.Locked = true;
-        //            kanban.Title = "(PO#" + order.PONumber.ToString() + ") " + kanban.Title;
-        //            new Client<Kanban>().Save(kanban, "Converted to Purchase Order", (_, __) => { });
-
-        //        });
-
-        //        MessageBox.Show(String.Format("Created Purchase Order {0}", ponumber));
-        //        Refresh();
-
-
-        //    }
-
-        //}
-
-        //private void CreateDelivery_Click(object sender, RoutedEventArgs e)
-        //{
-        //    MenuItem menu = sender as MenuItem;
-        //    TaskModel task = menu.Tag as TaskModel;
-
-        //    if (MessageBox.Show("This will convert this task into a Delivery.\n\nDo you wish to continue?", "Confirmation", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
-        //        return;
-
-        //    int deliverynumber = 0;
-        //    Progress.ShowModal("Creating Delivery", (progress) =>
-        //    {
-        //        Kanban kanban = new Client<Kanban>().Query(new Filter<Kanban>(x => x.ID).IsEqualTo(task.ID)).Rows.FirstOrDefault()?.ToObject<Kanban>();
-
-        //        Delivery delivery = new Delivery();
-        //        delivery.Due = kanban.DueDate;
-        //        delivery.Job.ID = kanban.JobLink.ID;
-        //        delivery.Job.Synchronise(kanban.JobLink);
-        //        delivery.Notes = kanban.Summary;
-        //        delivery.Employee.ID = kanban.ManagerLink.ID;
-        //        delivery.Employee.Synchronise(kanban.ManagerLink);
-        //        new Client<Delivery>().Save(delivery, "Created From Task");
-        //        deliverynumber = delivery.Number;
-
-        //        progress.Report("Updating Documents");
-        //        List<DeliveryDocument> docs = new List<DeliveryDocument>();
-        //        var taskdocs = new Client<KanbanDocument>().Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(Guid.Parse(task.ID)));
-        //        foreach (var taskdoc in taskdocs)
-        //        {
-        //            DeliveryDocument doc = new DeliveryDocument();
-        //            doc.DocumentLink.ID = taskdoc.DocumentLink.ID;
-        //            doc.EntityLink.ID = delivery.ID;
-        //            docs.Add(doc);
-        //        }
-        //        new Client<DeliveryDocument>().Save(docs, "", (_, __) => { });
-
-        //        progress.Report("Creating Links");
-        //        DeliveryKanban link = new DeliveryKanban();
-        //        link.Entity.ID = delivery.ID;
-        //        link.Kanban.ID = kanban.ID;
-        //        new Client<DeliveryKanban>().Save(link, "Converting Task -> Delivery", (_, __) => { });
-
-        //        progress.Report("Updating Task");
-        //        kanban.Category = "Open";
-        //        kanban.Completed = DateTime.MinValue;
-        //        kanban.Locked = true;
-        //        kanban.Title = "(Del#" + delivery.Number.ToString() + ") " + kanban.Title;
-        //        new Client<Kanban>().Save(kanban, "Converted to Delivery", (_, __) => { });
-
-        //    });
-
-        //    MessageBox.Show(String.Format("Created Delivery {0}", deliverynumber));
-        //    Refresh();
-
-        //}
-
-        //private void CloseTask_Click(object sender, RoutedEventArgs e)
-        //{
-        //    if (MessageBox.Show("Are you sure you want to remove the selected tasks from the list?", "Confirm removal", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
-        //        return;
-        //    Progress.ShowModal("Closing Kanbans", (progress) =>
-        //    {
-        //        TaskModel task = ((MenuItem)e.Source).Tag as TaskModel;
-
-        //        Kanban[] kanbans = GetSelectedKanbans(task.ID);
-        //        for (int i = 0; i < kanbans.Length; i++)
-        //        {
-        //            Kanban kanban = kanbans[i];
-        //            kanban.Closed = DateTime.Now;
-        //        }
-        //        new Client<Kanban>().Save(kanbans, "Kanban Marked as Closed");
-        //        CheckedKanbans.Clear();
-
-        //    });
-        //    Refresh();
-
-        //}
-
-        //private void EmailTask_Click(object sender, RoutedEventArgs e)
-        //{
-
-        //    MenuItem menu = sender as MenuItem;
-        //    TaskModel task = menu.Tag as TaskModel;
-        //    Kanban kanban = new Client<Kanban>().Load(new Filter<Kanban>(x => x.ID).IsEqualTo(task.ID)).FirstOrDefault();
-
-        //    List<String> to = new List<string>();
-
-        //    String from = "";
-        //    String salutation = "";
-
-        //    if (MyID.HasValue)
-        //    {
-
-        //        CoreRow me = _employees.Rows.FirstOrDefault(r => r.Get<Employee, Guid>(c => c.ID).Equals(MyID));
-        //        if (me != null)
-        //            from = me.Get<Employee, String>(c => c.Name).Split(' ').FirstOrDefault();
-
-        //        if (kanban.EmployeeLink.ID != MyID.Value)
-        //        {
-        //            CoreRow emp = _employees.Rows.FirstOrDefault(r => r.Get<Employee, Guid>(c => c.ID).Equals(kanban.EmployeeLink.ID));
-        //            if (emp != null)
-        //            {
-        //                String email = emp.Get<Employee, String>(c => c.Email);
-        //                if (!string.IsNullOrEmpty(email))
-        //                {
-        //                    to.Add(email);
-        //                    String name = emp.Get<Employee, String>(c => c.Name).Split(' ').FirstOrDefault();
-        //                    salutation = salutation + (String.IsNullOrEmpty(salutation) ? "Hi " : " and ") + name;
-        //                }
-
-        //            }
-        //        }
-
-        //        if (kanban.ManagerLink.ID != MyID.Value)
-        //        {
-        //            CoreRow emp = _employees.Rows.FirstOrDefault(r => r.Get<Employee, Guid>(c => c.ID).Equals(kanban.ManagerLink.ID));
-        //            if (emp != null)
-        //            {
-        //                String email = emp.Get<Employee, String>(c => c.Email);
-        //                if (!string.IsNullOrEmpty(email))
-        //                {
-        //                    to.Add(email);
-        //                    String name = emp.Get<Employee, String>(c => c.Name).Split(' ').FirstOrDefault();
-        //                    salutation = salutation + (String.IsNullOrEmpty(salutation) ? "Hi " : " and ") + name;
-        //                }
-        //            }
-        //        }
-        //    }
-
-        //    Outlook.Application outlookApp = new Outlook.Application();
-        //    Outlook.MailItem mailItem = (Outlook.MailItem)outlookApp.CreateItem(Outlook.OlItemType.olMailItem);
-        //    mailItem.Subject = "PRS Task: " + kanban.Title;
-        //    mailItem.To = String.Join("; ", to);
-        //    mailItem.HTMLBody = String.Format("{0},<br><br>Please see the above task in PRS.<br><br>Regards,<br><br>{1}<br><br><b><u>Task Description:</u></b><br><i>{2}</i><br><br><b><u>Additional Notes:</u></b><br>{3}", salutation, from, CoreUtils.StripHTML(kanban.Description), String.Join("\r\n", kanban.Notes));
-        //    //mailItem.Attachments.Add(filename, Outlook.OlAttachmentType.olByValue, Type.Missing, Type.Missing);
-        //    mailItem.Display(false);
-        //}
-
-        //private void CompleteTask_Click(object sender, RoutedEventArgs e)
-        //{
-        //    if (MessageBox.Show("Are you sure you want to mark the selected tasks as complete?", "Confirm Completion", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
-        //        return;
-        //    TaskModel task = ((MenuItem)e.Source).Tag as TaskModel;
-        //    Progress.ShowModal("Completing Task", (progress) =>
-        //    {
-
-        //        Kanban[] kanbans = GetSelectedKanbans(task.ID);
-        //        for (int i = 0; i < kanbans.Length; i++)
-        //        {
-        //            Kanban kanban = kanbans[i];
-        //            kanban.Completed = DateTime.Now;
-        //            kanban.Category = "Complete";
-        //        }
-        //        new Client<Kanban>().Save(kanbans, "Kanban Marked as Completed");
-        //        CheckedKanbans.Clear();
-
-        //    });
-        //    Refresh();
-        //}
-
-        #endregion
-
-        #region Kanban Creation / Editing
-
-        public void CreateKanban()
-        {
-            var result = Host.CreateKanban(
-                kanban =>
-                {
-                    kanban.EmployeeLink.ID = EmployeeID != CoreUtils.FullGuid ? EmployeeID : MyID.Value;
-                    kanban.ManagerLink.ID = MyID.Value;
-                    kanban.ManagerLink.UserLink.ID = ClientFactory.UserGuid;
-                });
-            if (result != null)
-                Refresh(true);
-        }
+    private void IncludeCompleted_Checked(object sender, RoutedEventArgs e)
+    {
 
-        private void DoEdit(TaskModel task)
-        {
-            if (task == null)
-                return;
+    }
 
-            var result = Host.EditReferences(new[] { task });
-            if (result)
-                Refresh(true);
-        }
+    private void IncludeObserved_Checked(object sender, RoutedEventArgs e)
+    {
 
-        #endregion
+    }
 
-        private void TaskTypesLabel_OnClick(object sender, RoutedEventArgs e)
-        {
-            var list = new MasterList(typeof(KanbanType));
-            list.ShowDialog();
-            SetupKanbanTypesLookup(true);
-        }
+    private void IncludeLocked_Checked(object sender, RoutedEventArgs e)
+    {
 
-        private void JobFilterBtn_OnClick(object sender, RoutedEventArgs e)
-        {
-            if (JobFilterID != Guid.Empty)
-            {
-                JobFilterBtn.Content = "Filter Job";
-                JobFilterID = Guid.Empty;
-                FilterKanbans();
-                return;
-            }
+    }
 
-            var window = new ThemableWindow();
-            var grid = new JobGrid();
-            grid.Reconfigure(options =>
-            {
-                options.Remove(DynamicGridOption.EditRows);
-                options.Remove(DynamicGridOption.DeleteRows);
-                options.Remove(DynamicGridOption.AddRows);
-                options.Remove(DynamicGridOption.MultiSelect);
-                options.Remove(DynamicGridOption.ExportData);
-                options.Remove(DynamicGridOption.ImportData);
-            });
-            grid.OnSelectItem += (object sender, DynamicGridSelectionEventArgs e) =>
-            {
-                if ((sender as JobGrid).SelectedRows.Count() == 0)
-                    return;
+    private void ViewType_SelectionChanged(object sender, SelectionChangedEventArgs e)
+    {
 
-                else
-                {
-                    var item = (sender as JobGrid).SelectedRows[0];
-                    AddJobFilter(item);
-                    window.Close();
-                }
-            };
-            grid.Refresh(true, true);
-            window.Content = grid;
-            window.ShowDialog();
-        }
+    }
 
-        Guid JobFilterID = Guid.Empty;
+    private void Search_KeyUp(object sender, KeyEventArgs e)
+    {
 
-        private void AddJobFilter(CoreRow item)
-        {
-            JobFilterID = item.Get<Job, Guid>(x => x.ID);
-            JobFilterBtn.Content = item.Get<Job, string>(x => x.JobNumber) + " (click to cancel)";
-            FilterKanbans();
-        }
     }
-}
+}

+ 123 - 0
prs.desktop/Panels/Tasks/TasksByStatusControlOld.xaml

@@ -0,0 +1,123 @@
+<UserControl
+    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:local="clr-namespace:PRSDesktop"
+    xmlns:wpf="clr-namespace:InABox.Wpf;assembly=InABox.Wpf"
+    xmlns:Kanban="clr-namespace:Syncfusion.UI.Xaml.Kanban;assembly=Syncfusion.SfKanban.WPF"
+    x:Class="PRSDesktop.TasksByStatusControlOld"
+    mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="400">
+
+    <UserControl.Resources>
+        <local:StatusTasksHeaderTimeConverter x:Key="StatusTasksHeaderTimeConverter" />
+        <wpf:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
+
+        <DataTemplate x:Key="KanbanHeader">
+            <!--<Border BorderThickness="0.75" BorderBrush="Gray" CornerRadius="5,5,0,0" Margin="0,10,0,0" Padding="5" Width="{Binding Converter={StaticResource TaskHeaderWidthConverter}}" Background="WhiteSmoke">-->
+            <StackPanel Orientation="Vertical">
+                <TextBlock Text="{Binding  Header}" FontSize="16" FontWeight="DemiBold" HorizontalAlignment="Left"
+                           Margin="10,0,10,0" />
+                <TextBlock FontSize="12" HorizontalAlignment="Left" Margin="10,0,5,0">
+                    <Run Text="{Binding CardCount}" />
+                    <Run Text="Tasks /" />
+                    <Run Text="{Binding Converter={StaticResource StatusTasksHeaderTimeConverter}, Mode=OneWay}" />
+                    <Run Text="Hours" />
+                </TextBlock>
+            </StackPanel>
+            <!--</Border>-->
+        </DataTemplate>
+
+        
+    </UserControl.Resources>
+
+    <Grid>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="Auto" x:Name="EmployeeListColumn" />
+            <ColumnDefinition Width="*" />
+        </Grid.ColumnDefinitions>
+
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="*" />
+        </Grid.RowDefinitions>
+
+        <Border Grid.Row="0" Grid.Column="0" CornerRadius="5,5,0,0" BorderBrush="Gray" BorderThickness="0.75"
+                Background="WhiteSmoke" Padding="5" Margin="0,0,4,2">
+            <Label Content="Employee List" HorizontalContentAlignment="Center" />
+        </Border>
+
+        <ListView Grid.Column="0" Grid.Row="1" x:Name="Employees" HorizontalAlignment="Stretch"
+                  SelectionChanged="Employees_SelectionChanged" Margin="0,0,4,0" Background="White" BorderBrush="Gray"
+                  BorderThickness="0.75">
+            <ListView.ItemTemplate>
+                <DataTemplate>
+                    <Grid HorizontalAlignment="Stretch" Margin="4,2,4,2">
+                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
+                            <Border Width="40" Height="40" CornerRadius="20" BorderBrush="Black" BorderThickness="1"
+                                    VerticalAlignment="Center" HorizontalAlignment="Center">
+                                <Border.Background>
+                                    <ImageBrush ImageSource="{Binding Path=Image}" Stretch="UniformToFill" />
+                                </Border.Background>
+                            </Border>
+                            <TextBlock x:Name="PCName" Height="Auto" TextWrapping="Wrap" Width="80" Margin="4,0,0,0"
+                                       TextAlignment="Left" Text="{Binding Name}" VerticalAlignment="Center" />
+                        </StackPanel>
+                    </Grid>
+                </DataTemplate>
+            </ListView.ItemTemplate>
+        </ListView>
+
+        <Border Grid.Row="0" Grid.Column="1" CornerRadius="5,5,0,0" BorderBrush="Gray" BorderThickness="0.75"
+                Background="WhiteSmoke" Padding="5" Margin="0,0,0,2">
+            <DockPanel>
+                <Button DockPanel.Dock="Left" x:Name="TaskTypesLabel" Content="Filter" Padding="4,0" Click="TaskTypesLabel_OnClick" />
+                <ComboBox DockPanel.Dock="Left" x:Name="TaskTypes" Margin="5,0,0,0" Width="150" SelectedIndex="0"
+                          SelectionChanged="TaskTypes_SelectionChanged" VerticalContentAlignment="Center"
+                          SelectedValuePath="Key" DisplayMemberPath="Value" />
+                <Button DockPanel.Dock="Left" x:Name="JobFilterBtn" Content="Filter Job" Padding="4,0" Margin="5,0,0,0" Click="JobFilterBtn_OnClick" />
+                <Label DockPanel.Dock="Left" Content="Search" Margin="5,0,0,0" />
+                <Button DockPanel.Dock="Right" x:Name="Export" Padding="10,0" Margin="5,0,0,0" Content="Export"
+                        Click="Export_Click" />
+                <CheckBox DockPanel.Dock="Right" x:Name="IncludeCompleted" Margin="10,0,5,0"
+                          Content="Include Completed" VerticalAlignment="Center" HorizontalAlignment="Right"
+                          Checked="IncludeCompleted_Checked" Unchecked="IncludeCompleted_Checked" />
+                <CheckBox DockPanel.Dock="Right" x:Name="IncludeObserved" Margin="10,0,5,0" Content="Include Observing"
+                          VerticalAlignment="Center" HorizontalAlignment="Right" Checked="IncludeObserved_Checked"
+                          Unchecked="IncludeObserved_Checked" />
+                <CheckBox DockPanel.Dock="Right" x:Name="IncludeLocked" Margin="10,0,5,0" Content="Include Locked"
+                          VerticalAlignment="Center" HorizontalAlignment="Right" Checked="IncludeLocked_Checked"
+                          Unchecked="IncludeLocked_Checked" />
+                <ComboBox DockPanel.Dock="Right" x:Name="ViewType" Margin="5,0,0,0" Width="100" SelectedIndex="0"
+                          SelectionChanged="ViewType_SelectionChanged" VerticalContentAlignment="Center">
+                    <ComboBoxItem Content="Full" />
+                    <ComboBoxItem Content="Compact" />
+                </ComboBox>
+                <Label DockPanel.Dock="Right" x:Name="ViewTypeLabel" Content="View" Margin="5,0,0,0" />
+                <TextBox DockPanel.Dock="Left" x:Name="Search" Margin="5,0,0,0" HorizontalAlignment="Stretch"
+                         VerticalContentAlignment="Center" KeyUp="Search_KeyUp" />
+
+            </DockPanel>
+        </Border>
+
+        <Kanban:SfKanban
+            x:Name="Kanban"
+            Grid.Column="1"
+            Grid.Row="1"
+            Margin="0"
+            HorizontalAlignment="Stretch"
+            VerticalAlignment="Stretch"
+            HorizontalContentAlignment="Stretch"
+            VerticalContentAlignment="Stretch"
+            AutoGenerateColumns="False"
+            BorderBrush="Gray"
+            BorderThickness="0.75"
+            Background="WhiteSmoke"
+            CardTemplate="{StaticResource FullKanban}"
+            ColumnHeaderTemplate="{StaticResource KanbanHeader}"
+            SizeChanged="Kanban_SizeChanged"
+            CardDragStart="Kanban_CardDragStart"
+            CardDragEnd="Kanban_CardDragEnd" />
+
+    </Grid>
+</UserControl>

+ 960 - 0
prs.desktop/Panels/Tasks/TasksByStatusControlOld.xaml.cs

@@ -0,0 +1,960 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using InABox.DynamicGrid;
+using InABox.Wpf;
+using InABox.WPF;
+using org.omg.CORBA;
+using Syncfusion.UI.Xaml.Kanban;
+using Color = System.Drawing.Color;
+
+namespace PRSDesktop
+{
+    public class StatusTasksHeaderTimeConverter : IValueConverter
+    {
+        public static IEnumerable<TaskModel> Tasks { get; set; }
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (Tasks == null)
+                return "0:00";
+
+            var dataContext = value as ColumnTag;
+            if (dataContext == null)
+                return "0:00";
+
+            var getter = dataContext.GetType().GetProperty("Column", BindingFlags.NonPublic | BindingFlags.Instance);
+            if (getter == null)
+                return "0:00";
+
+            var column = (KanbanColumn)getter.GetValue(dataContext);
+            if (column == null)
+                return "0:00";
+
+            double result = 0.0F;
+            foreach (var kanban in Tasks.Where(x => Equals(x.Category, column.Categories)))
+                result += kanban.EstimatedTime.TotalHours;
+            return string.Format("{0:F2}", result);
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+    /// <summary>
+    ///     Interaction logic for KanbanPanel.xaml
+    /// </summary>
+    public partial class TasksByStatusControlOld : UserControl, ITaskControl
+    {
+
+        private enum Suppress
+        {
+            This
+        }
+
+        private BitmapImage _attachimg = PRSDesktop.Resources.attachment.AsBitmapImage();
+
+        private readonly ObservableCollection<EmployeeModel> _employeelist = new();
+
+        private CoreTable _employees;
+
+        public CoreTable _kanbans;
+
+        private BitmapImage _lockimg = PRSDesktop.Resources.lock_sml.AsBitmapImage();
+        private ObservableCollection<TaskModel> _models = new();
+        private CoreTable _types;
+
+        public List<string> CheckedKanbans = new();
+
+        // CoreUtils.FullGuid => All Staff
+        // Guid.Empty => Unallocated
+        // Anything Else => Actual Staff Member
+        private Guid EmployeeID = Guid.Empty;
+
+        private Guid? MyID;
+        private string MyName = "";
+        private string searchtext = "";
+        private Guid selectedtype = CoreUtils.FullGuid;
+
+        public TasksByStatusControlOld()
+        {
+            using (new EventSuppressor(Suppress.This))
+                InitializeComponent();
+        }
+
+        public string SectionName => "Tasks By Status";
+
+        public DataModel DataModel(Selection selection)
+        {
+            var ids = SelectedModels().Select(x => Guid.Parse(x.ID)).ToArray();
+            return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).InList(ids));
+        }
+
+        private void ResizeColumns()
+        {
+            //if (!bResizeRequired)
+            //    return;
+
+            using (var d = Dispatcher.DisableProcessing())
+            {
+                var CollapsedWidth = 50;
+                var CollapsedColumns = 0;
+                Array.ForEach(Kanban.Columns.ToArray(), x => { CollapsedColumns += x.IsExpanded ? 0 : 1; });
+                if (Kanban.Columns.Count > 0 && CollapsedColumns != Kanban.Columns.Count)
+                {
+                    var ColumnWidth = (Kanban.ActualWidth - CollapsedColumns * CollapsedWidth) / (Kanban.Columns.Count - CollapsedColumns) - 2;
+                    if (ColumnWidth != Kanban.ColumnWidth) Kanban.ColumnWidth = ColumnWidth;
+                    //bResizeRequired = false;
+                }
+            }
+        }
+
+
+        private void TaskMenu_Opened(object sender, RoutedEventArgs e)
+        {
+            Host.PopulateMenu(this, sender as ContextMenu);
+        }
+
+        private void Kanban_SizeChanged(object sender, SizeChangedEventArgs e)
+        {
+            Kanban.ColumnWidth = Kanban.ActualWidth / Kanban.Columns.Count - 1.0F;
+        }
+
+        private void Kanban_CardDragStart(object sender, KanbanDragStartEventArgs e)
+        {
+            var models = SelectedModels(e.SelectedCard.Content as TaskModel);
+            if (models.Any(x => x.Locked))
+                e.IsCancel = true;
+        }
+
+        private void Kanban_CardDragEnd(object sender, KanbanDragEndEventArgs e)
+        {
+            using (new WaitCursor())
+            {
+                var target = e.TargetColumn.Categories;
+                var models = SelectedModels(e.SelectedCard.Content as TaskModel).Where(x => !Equals(x.Category, target)).ToArray();
+                if (!models.Any())
+                    return;
+                var kanbans = Host.LoadKanbans(models, new Columns<Kanban>(x => x.ID, x => x.Category));
+                foreach (var kanban in kanbans)
+                    kanban.Category = target;
+                new Client<Kanban>().Save(kanbans, string.Format("Task Status Updated to {0}", target), (o, err) => { });
+                foreach (var model in models)
+                {
+                    model.Checked = false;
+                    model.Category = target;
+                }
+
+                FilterKanbans();
+            }
+        }
+
+        private void Search_KeyUp(object sender, KeyEventArgs e)
+        {
+            if (string.IsNullOrWhiteSpace(Search.Text) || e.Key == Key.Return)
+            {
+                searchtext = Search.Text;
+                Refresh(true);
+            }
+        }
+
+        private void TaskTypes_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+
+            if (!IsReady || EventSuppressor.IsSet(Suppress.This))
+                return;
+            if (e.AddedItems.Count > 0)
+            {
+                var item = (KeyValuePair<Guid, string>)e.AddedItems[0];
+                selectedtype = item.Key;
+            }
+            else
+            {
+                selectedtype = CoreUtils.FullGuid;
+            }
+
+            Host.KanbanSettings.StatusSettings.SelectedType = selectedtype;
+            Host.SaveSettings();
+            ReloadKanbans();
+        }
+
+        private void IncludeCompleted_Checked(object sender, RoutedEventArgs e)
+        {
+            if (!IsReady)
+                return;
+            Host.KanbanSettings.StatusSettings.IncludeCompleted = IncludeCompleted.IsChecked.Value;
+            Host.SaveSettings();
+            SetupColumns();
+            ReloadKanbans();
+        }
+
+        private void IncludeObserved_Checked(object sender, RoutedEventArgs e)
+        {
+            if (!IsReady)
+                return;
+            Host.KanbanSettings.StatusSettings.IncludeObserved = IncludeObserved.IsChecked.Value;
+            Host.SaveSettings();
+            ReloadKanbans();
+        }
+
+        private void IncludeLocked_Checked(object sender, RoutedEventArgs e)
+        {
+            if (!IsReady)
+                return;
+            Host.KanbanSettings.StatusSettings.IncludeLocked = IncludeLocked.IsChecked.Value;
+            Host.SaveSettings();
+            ReloadKanbans();
+        }
+
+        private void ViewType_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (Kanban != null)
+                Kanban.CardTemplate = ViewType.SelectedIndex > 0
+                    ? Resources["CompactKanban"] as DataTemplate
+                    : Resources["FullKanban"] as DataTemplate;
+            if (!IsReady || EventSuppressor.IsSet(Suppress.This))
+                return;
+            Host.KanbanSettings.StatusSettings.CompactView = ViewType.SelectedIndex > 0;
+            Host.SaveSettings();
+
+        }
+
+        private static bool UpdatingEmployees = false;
+
+        private void Employees_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (UpdatingEmployees || EventSuppressor.IsSet(Suppress.This))
+                return;
+
+            if (e.AddedItems.Count == 0)
+            {
+                EmployeeID = Guid.Empty;
+            }
+            else
+            {
+                var model = _employeelist[Employees.SelectedIndex];
+                EmployeeID = model.ID;
+            }
+
+            CheckedKanbans.Clear();
+            if (IsReady)
+                Refresh(true);
+        }
+
+        private void Export_Click(object sender, RoutedEventArgs e)
+        {
+            var form = new DynamicExportForm(typeof(Kanban), _kanbans.Columns.Select(x => x.ColumnName));
+            if (form.ShowDialog() != true)
+                return;
+            var export = new Client<Kanban>().Query(
+                GetKanbanFilter(),
+                new Columns<Kanban>(form.Fields),
+                LookupFactory.DefineSort<Kanban>()
+            );
+
+            var employee = "Tasks for All Staff";
+            if (EmployeeID != CoreUtils.FullGuid)
+            {
+                if (EmployeeID == Guid.Empty)
+                {
+                    employee = "Unallocated Tasks";
+                }
+                else
+                {
+                    var model = _employeelist.FirstOrDefault(x => x.ID.Equals(EmployeeID));
+                    employee = model == null ? "Tasks for (Unknown)" : "Tasks for " + (model.ID == MyID ? MyName : model.Name);
+                }
+            }
+
+            ExcelExporter.DoExport<Kanban>(
+                export,
+                string.Format(
+                    "{0} ({1:dd-MMM-yy})",
+                    employee,
+                    DateTime.Today
+                )
+            );
+        }
+
+        #region ITaskControl Support
+
+        public bool IsReady { get; set; }
+
+        public ITaskHost Host { get; set; }
+
+        public KanbanViewType KanbanViewType => KanbanViewType.Status;
+
+        public IEnumerable<TaskModel> SelectedModels(TaskModel sender = null)
+        {
+            var result = _models.Where(x => x.Checked).ToList();
+            if (sender != null && !result.Contains(sender))
+                result.Add(sender);
+            return result;
+        }
+
+        #endregion
+
+        #region Setup
+
+        public void Setup()
+        {
+            using (new EventSuppressor(Suppress.This))
+            {
+                SetupToolbar();
+
+                SetupColumns();
+
+                SetupData();
+
+                SetupKanbanTypesLookup(false);
+
+                SetupMyEmployee();
+
+                SetupEmployeeList();
+            }
+        }
+
+        private void SetupMyEmployee()
+        {
+            var row = _employees.Rows.FirstOrDefault(r => r.Get<Employee, Guid>(c => c.UserLink.ID) == ClientFactory.UserGuid);
+            if (row != null)
+            {
+                MyID = row.Get<Employee, Guid>(c => c.ID);
+                MyName = row.Get<Employee, string>(x => x.Name);
+            }
+        }
+
+        private void SetupEmployeeList()
+        {
+            IEnumerable<CoreRow> active = null;
+            var anonymous = PRSDesktop.Resources.anonymous.AsBitmapImage();
+            if (Security.IsAllowed<CanViewOthersTasks>())
+            {
+                active = _employees.Rows.Where(r =>
+                    r.Get<Employee, bool>(x => x.CanAllocateTasks) && (r.Get<Employee, DateTime>(x => x.FinishDate).IsEmpty() ||
+                                                                       r.Get<Employee, DateTime>(x => x.FinishDate) > DateTime.Today));
+                _employeelist.Add(new EmployeeModel(CoreUtils.FullGuid, "All Staff", Guid.Empty, PRSDesktop.Resources.everyone.AsBitmapImage()));
+                _employeelist.Add(new EmployeeModel(Guid.Empty, "Unallocated", Guid.Empty, null));
+            }
+            else
+            {
+                active = _employees.Rows.Where(r => r.Get<Employee, Guid>(c => c.UserLink.ID).Equals(ClientFactory.UserGuid));
+            }
+
+            EmployeeModel selected = null;
+            foreach (var row in active)
+            {
+                var id = row.Get<Employee, Guid>(x => x.ID);
+                var userid = row.Get<Employee, Guid>(x => x.UserLink.ID);
+                var thumbnailid = row.Get<Employee, Guid>(x => x.Thumbnail.ID);
+                var name = userid.Equals(ClientFactory.UserGuid) ? "My Tasks" : row.Get<Employee, string>(x => x.Name);
+
+                var model = new EmployeeModel(id, name, thumbnailid, anonymous);
+
+                if (userid.Equals(ClientFactory.UserGuid))
+                {
+                    _employeelist.Insert(0, model);
+                    selected = model;
+                }
+                else
+                {
+                    _employeelist.Add(model);
+                }
+            }
+
+            if (Security.IsAllowed<CanViewOthersTasks>())
+            {
+                EmployeeListColumn.Width = new GridLength(1.0F, GridUnitType.Auto);
+                var thumbnails = active
+                    .Select(r => r.EntityLinkID<Employee, ImageDocumentLink>(x => x.Thumbnail) ?? Guid.Empty)
+                    .Where(x => x != Guid.Empty).ToArray();
+                Employees.ItemsSource = _employeelist;
+                Employees.SelectedItem = _employeelist.First();
+                EmployeeID = _employeelist.First().ID;
+                if (thumbnails.Any())
+                    new Client<Document>().Query(
+                        new Filter<Document>(x => x.ID).InList(thumbnails),
+                        new Columns<Document>(x => x.ID, x => x.Data),
+                        null,
+                        (data, error) =>
+                        {
+                            if (data != null)
+                                ProcessThumbnails(data);
+                        }
+                    );
+            }
+            else
+            {
+                EmployeeListColumn.Width = new GridLength(0.0F, GridUnitType.Pixel);
+                Employees.ItemsSource = _employeelist;
+                Employees.SelectedItem = _employeelist.First();
+                EmployeeID = _employeelist.First().ID;
+            }
+        }
+
+        private void ProcessThumbnails(CoreTable data)
+        {
+            Dispatcher.Invoke(() =>
+            {
+                foreach (var row in data.Rows)
+                {
+                    var id = row.Get<Document, Guid>(x => x.ID);
+                    var model = _employeelist.FirstOrDefault(x => x.ThumbnailID.Equals(id));
+                    if (model != null)
+                    {
+                        model.Image = new BitmapImage();
+                        model.Image.LoadImage(row.Get<Document, byte[]>(x => x.Data));
+                    }
+                }
+
+                UpdatingEmployees = true;
+                Employees.ItemsSource = null;
+                Employees.ItemsSource = _employeelist;
+                Employees.SelectedItem = _employeelist.First();
+                EmployeeID = _employeelist.First().ID;
+                UpdatingEmployees = false;
+            });
+        }
+
+        private void SetupKanbanTypesLookup(bool reload)
+        {
+            if (ClientFactory.IsSupported<KanbanType>())
+            {
+
+                if (reload)
+                {
+                    _types = new Client<KanbanType>().Query(
+                        new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
+                        new Columns<KanbanType>(x => x.ID, x => x.Description),
+                        new SortOrder<KanbanType>(x => x.Description)
+                    );
+                }
+
+                var tasktypes = new Dictionary<Guid, string>
+                {
+                    { CoreUtils.FullGuid, "All Types" },
+                    { Guid.Empty, "Unallocated Types" }
+                };
+                _types.IntoDictionary<KanbanType, Guid, string>(tasktypes, x => x.ID, row => row.Get<KanbanType, string>(x => x.Description));
+
+                TaskTypes.ItemsSource = tasktypes;
+                if (tasktypes.ContainsKey(Host.KanbanSettings.StatusSettings.SelectedType))
+                    TaskTypes.SelectedValue = Host.KanbanSettings.StatusSettings.SelectedType;
+                else
+                    TaskTypes.SelectedValue = CoreUtils.FullGuid;
+
+                TaskTypesLabel.Visibility = Visibility.Visible;
+                TaskTypes.Visibility = Visibility.Visible;
+            }
+            else
+            {
+                TaskTypesLabel.Visibility = Visibility.Collapsed;
+                TaskTypes.Visibility = Visibility.Collapsed;
+            }
+        }
+
+        private void SetupToolbar()
+        {
+            IncludeCompleted.Visibility = Security.IsAllowed<CanHideTaskCompletedColumn>() ? Visibility.Visible : Visibility.Collapsed;
+            IncludeCompleted.IsChecked = IncludeCompleted.Visibility == Visibility.Visible ? Host.KanbanSettings.StatusSettings.IncludeCompleted : true;
+            IncludeObserved.IsChecked = Host.KanbanSettings.StatusSettings.IncludeObserved;
+            ViewType.SelectedIndex = Host.KanbanSettings.StatusSettings.CompactView ? 1 : 0;
+        }
+
+        private void SetupColumns()
+        {
+            Kanban.Columns.Clear();
+
+            var indicatorColorPalette = new IndicatorColorPalette();
+            indicatorColorPalette.Add(new ColorMapping { Key = "Red", Color = Colors.LightSalmon });
+            indicatorColorPalette.Add(new ColorMapping { Key = "Orange", Color = Colors.Orange });
+            indicatorColorPalette.Add(new ColorMapping { Key = "Yellow", Color = Colors.LightYellow });
+            indicatorColorPalette.Add(new ColorMapping { Key = "Green", Color = Colors.LightGreen });
+            Kanban.IndicatorColorPalette = indicatorColorPalette;
+
+            Kanban.Columns.Add(new KanbanColumn
+            {
+                Categories = "Open",
+                Title = "To Do"
+            });
+
+            Kanban.Columns.Add(new KanbanColumn
+            {
+                Categories = "In Progress",
+                Title = "In Progress"
+            });
+
+            Kanban.Columns.Add(new KanbanColumn
+            {
+                Categories = "Waiting",
+                Title = "Waiting for Others"
+            });
+
+            if (Host.KanbanSettings.StatusSettings.IncludeCompleted)
+                Kanban.Columns.Add(new KanbanColumn
+                {
+                    Categories = "Complete",
+                    Title = "Completed"
+                });
+            Kanban.InvalidateVisual();
+
+            foreach (var column in Kanban.Columns)
+            {
+                var menu = new ContextMenu();
+                menu.Tag = column;
+                var item = new MenuItem { Header = "New Task" };
+                item.Click += CreateTask;
+                menu.Items.Add(item);
+                menu.Items.Add(new Separator());
+                item = new MenuItem { Header = "Select All " + column.Title + " Tasks", Tag = column };
+                item.Click += SelectAll_Click;
+                menu.Items.Add(item);
+                item = new MenuItem { Header = "Unselect All " + column.Title + " Tasks", Tag = column };
+                item.Click += UnSelectAll_Click;
+                menu.Items.Add(item);
+                column.ContextMenu = menu;
+            }
+
+            Kanban.ColumnWidth = Kanban.ActualWidth / Kanban.Columns.Count - 1.0F;
+        }
+
+        private void SetupData()
+        {
+            var query = new MultiQuery();
+
+            query.Add(
+                null,
+                new Columns<Employee>(x => x.ID, x => x.Name, x => x.Thumbnail.ID, x => x.CanAllocateTasks, x => x.Email, x => x.Mobile,
+                    x => x.FinishDate, x => x.UserLink.ID),
+                new SortOrder<Employee>(x => x.Name)
+            );
+
+            if (ClientFactory.IsSupported<KanbanType>())
+                query.Add(
+                    new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
+                    new Columns<KanbanType>(x => x.ID, x => x.Description),
+                    new SortOrder<KanbanType>(x => x.Description)
+                );
+
+            query.Query();
+
+            _employees = query.Get<Employee>();
+            _types = ClientFactory.IsSupported<KanbanType>()
+                ? query.Get<KanbanType>()
+                : null;
+        }
+
+        #endregion
+
+        #region Refresh / Reload
+
+        private Filter<T> GetSearchFilter<T>(Expression<Func<T, object>> expression) where T : Entity, new()
+        {
+            Filter<T> result = null;
+            var comps = searchtext.Trim().Split(' ');
+            foreach (var comp in comps)
+                result = result == null ? new Filter<T>(expression).Contains(comp) : result.And(expression).Contains(comp);
+            return result;
+        }
+
+        private Filter<KanbanSubscriber> GetKanbanSubscriberFilter()
+        {
+            var filter = new Filter<KanbanSubscriber>(x => x.Kanban.Closed).IsEqualTo(DateTime.MinValue);
+
+            if (Host.Job != null)
+            {
+                if (Host.Job.ID != Guid.Empty)
+                    filter = filter.And(x => x.Kanban.JobLink.ID).IsEqualTo(Host.Job.ID);
+                else
+                    filter = filter.And(x => x.Kanban.JobLink.ID).None();
+            }
+
+            // All Tasks (EmployeeID.HasValue == false) or Unallocated (EmployeeID = Guid.Empty) are retrieved directly from the Kanban Table
+            // so if we are here, we can assume that we are pulling subscriber data
+            var empfilter = new Filter<KanbanSubscriber>(x => x.Employee.ID).IsEqualTo(EmployeeID);
+            filter.Ands.Add(empfilter);
+
+            if (EmployeeID != MyID)
+                filter = filter.And(x => x.Kanban.Private).IsEqualTo(false);
+
+            //if (!includeobserved)
+            //    filter = filter.And(new Filter<KanbanSubscriber>(x => x.Assignee).IsEqualTo(true).Or(x => x.Manager).IsEqualTo(true));
+
+            //if (!includecompleted)
+            //    filter = filter.And(x => x.Kanban.Completed).IsEqualTo(DateTime.MinValue);
+
+            //if (selectedtype != CoreUtils.FullGuid)
+            //    filter = filter.And(x => x.Kanban.Type.ID).IsEqualTo(selectedtype);
+
+            if (!string.IsNullOrWhiteSpace(searchtext))
+            {
+                var search = GetSearchFilter<KanbanSubscriber>(x => x.Kanban.JobLink.Name);
+                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.JobLink.JobNumber));
+                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.Summary));
+                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.Title));
+                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.ManagerLink.Name));
+                search.Ors.Add(GetSearchFilter<KanbanSubscriber>(x => x.Kanban.EmployeeLink.Name));
+
+                if (int.TryParse(searchtext.Trim(), out var tasknumber))
+                    search.Ors.Add(new Filter<KanbanSubscriber>(x => x.Kanban.Number).IsEqualTo(tasknumber));
+
+                filter.Ands.Add(search);
+            }
+
+            return filter;
+        }
+
+        private Filter<Kanban> GetKanbanFilter()
+        {
+            var filter = new Filter<Kanban>(x => x.Closed).IsEqualTo(DateTime.MinValue);
+
+            if (Host.Job != null)
+            {
+                if (Host.Job.ID != Guid.Empty)
+                    filter = filter.And(x => x.JobLink.ID).IsEqualTo(Host.Job.ID);
+                else
+                    filter = filter.And(x => x.JobLink.ID).None();
+            }
+
+            if (EmployeeID != CoreUtils.FullGuid)
+            {
+                if (EmployeeID != Guid.Empty)
+                {
+                    var empfilter = new Filter<Kanban>(x => x.EmployeeLink.ID).IsEqualTo(EmployeeID).Or(x => x.ManagerLink.ID).IsEqualTo(EmployeeID);
+                    filter.Ands.Add(empfilter);
+                }
+                else
+                {
+                    filter = filter.And(x => x.EmployeeLink.ID).IsEqualTo(EmployeeID);
+                }
+            }
+
+            if (EmployeeID != MyID)
+                filter = filter.And(x => x.Private).IsEqualTo(false);
+
+            //if (!includecompleted)
+            //    filter = filter.And(x => x.Completed).IsEqualTo(DateTime.MinValue);
+
+            //if (selectedtype != CoreUtils.FullGuid)
+            //    filter = filter.And(x => x.Type.ID).IsEqualTo(selectedtype);
+
+            return filter;
+        }
+
+        public void Refresh(bool resetselection)
+        {
+            Application.Current.Dispatcher.Invoke(() => { Mouse.OverrideCursor = Cursors.Wait; });
+
+            if (EmployeeID != CoreUtils.FullGuid && EmployeeID != Guid.Empty)
+            {
+                _kanbans = new Client<KanbanSubscriber>().Query(
+                    GetKanbanSubscriberFilter(),
+                    new Columns<KanbanSubscriber>
+                    (
+                        x => x.Kanban.ID,
+                        x => x.Kanban.DueDate,
+                        x => x.Kanban.Completed,
+                        //x => x.Kanban.Description,
+                        x => x.Kanban.Summary,
+                        x => x.Kanban.Category,
+                        x => x.Kanban.EmployeeLink.ID,
+                        x => x.Kanban.ManagerLink.ID,
+                        x => x.Kanban.Notes,
+                        x => x.Kanban.Title,
+                        x => x.Kanban.JobLink.ID,
+                        x => x.Kanban.JobLink.JobNumber,
+                        x => x.Kanban.JobLink.Name,
+                        x => x.Kanban.Type.ID,
+                        x => x.Kanban.Type.Code,
+                        x => x.Kanban.Number,
+                        x => x.Kanban.Attachments,
+                        x => x.Kanban.Locked
+                    ),
+                    new SortOrder<KanbanSubscriber>(x => x.Kanban.DueDate) { Direction = SortDirection.Ascending }
+                );
+                foreach (var column in _kanbans.Columns)
+                    column.ColumnName = column.ColumnName.Replace("Kanban.", "");
+            }
+            else
+            {
+                _kanbans = new Client<Kanban>().Query(
+                    GetKanbanFilter(),
+                    new Columns<Kanban>
+                    (
+                        x => x.ID,
+                        x => x.DueDate,
+                        x => x.Completed,
+                        //x => x.Description,
+                        x => x.Summary,
+                        x => x.Category,
+                        x => x.EmployeeLink.ID,
+                        x => x.ManagerLink.ID,
+                        x => x.Notes,
+                        x => x.Title,
+                        x => x.JobLink.ID,
+                        x => x.JobLink.JobNumber,
+                        x => x.JobLink.Name,
+                        x => x.Type.ID,
+                        x => x.Type.Code,
+                        x => x.Number,
+                        x => x.Attachments,
+                        x => x.Locked
+                    ),
+                    new SortOrder<Kanban>(x => x.DueDate) { Direction = SortDirection.Ascending }
+                );
+            }
+
+            ReloadKanbans();
+
+            Application.Current.Dispatcher.Invoke(() => { Mouse.OverrideCursor = null; });
+        }
+
+        private void ReloadKanbans()
+        {
+            //SetupColumns();
+            //ResizeColumns();
+
+            _models = new ObservableCollection<TaskModel>();
+
+            foreach (var row in _kanbans.Rows)
+                try
+                {
+                    var empid = row.Get<IKanban, Guid>(e => e.EmployeeLink.ID);
+                    var mgrid = row.EntityLinkID<IKanban, EmployeeLink>(x => x.ManagerLink) ?? Guid.Empty;
+                    var completed = row.Get<IKanban, DateTime>(e => e.Completed);
+                    var locked = row.Get<IKanban, bool>(e => e.Locked);
+                    var type = row.Get<IKanban, Guid>(e => e.Type.ID);
+                    var category = row.Get<IKanban, string>(x => x.Category);
+                    if (string.IsNullOrWhiteSpace(category))
+                        category = "Open";
+
+
+                    var bLockedOK = Host.KanbanSettings.StatusSettings.IncludeLocked || locked == false;
+                    var bObserveOK = EmployeeID == CoreUtils.FullGuid || Host.KanbanSettings.StatusSettings.IncludeObserved || empid == EmployeeID || mgrid == EmployeeID;
+                    var bCompleteOK = Host.KanbanSettings.StatusSettings.IncludeCompleted || completed.IsEmpty();
+                    var bTypeOK = selectedtype == CoreUtils.FullGuid || type == selectedtype;
+                    if (bLockedOK && bCompleteOK && bObserveOK && bTypeOK)
+                    {
+                        var model = new TaskModel();
+                        var EmployeeEntry = _employeelist.Where(x => x.ID.Equals(empid)).FirstOrDefault();
+                        var empimg = EmployeeEntry?.Image;
+
+                        var ManagerEntry = _employeelist.Where(x => x.ID.Equals(mgrid)).FirstOrDefault();
+
+                        //var description = row.Get<IKanban, String>(x => x.Summary);
+                        //if (String.IsNullOrWhiteSpace(description))
+                        //    description = String.Join("\r\n", row.Get<IKanban, String[]>(x => x.Notes)).Split(new String[] { "===============" }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
+                        //if (String.IsNullOrWhiteSpace(description))
+                        //    description = CoreUtils.StripHTML(row.Get<IKanban, String>(x => x.Description));
+                        //if (String.IsNullOrWhiteSpace(description))
+                        //    description = "";
+                        var job = row.Get<IKanban, string>(x => x.JobLink.JobNumber);
+
+                        model.Title = row.Get<IKanban, string>(x => x.Title);
+                        model.ID = row.Get<IKanban, Guid>(x => x.ID).ToString();
+                        model.Description = row.Get<IKanban, string>(x => x.Summary) ?? "";
+                        model.Category = category;
+                        var color = EmployeeID == Guid.Empty || empid == EmployeeID || EmployeeID == CoreUtils.FullGuid
+                            ? TaskModel.KanbanColor(
+                                row.Get<IKanban, DateTime>(x => x.DueDate),
+                                row.Get<IKanban, DateTime>(x => x.Completed))
+                            : mgrid == EmployeeID
+                                ? Color.Silver
+                                : Color.Plum;
+                        if (row.Get<IKanban, bool>(x => x.Locked))
+                            color = color.MixColors(0.5F, Color.White);
+                        model.ColorKey = ImageUtils.ColorToString(color);
+                        model.Image = empimg;
+                        model.ImageURL = null;
+                        model.Attachments = row.Get<IKanban, int>(x => x.Attachments) > 0; // ? _attachimg : null;
+                        model.DueDate = row.Get<IKanban, DateTime>(x => x.DueDate);
+                        model.CompletedDate = row.Get<IKanban, DateTime>(x => x.Completed);
+                        model.Locked = row.Get<IKanban, bool>(x => x.Locked); // ? _lockimg : null;
+
+                        var notes = new List<List<string>> { new() };
+                        if ((row.Get<IKanban, string[]>(x => x.Notes) != null))
+                        {
+
+
+                            foreach (var line in row.Get<IKanban, string[]>(x => x.Notes))
+                            {
+                                if (line == "===================================")
+                                {
+                                    notes.Add(new());
+                                }
+                                else
+                                {
+                                    notes.Last().Add(line);
+                                }
+                            }
+                            model.Notes = string.Join("\n===================================\n", notes.Reverse<List<string>>().Select(x => string.Join('\n', x)));
+                        }
+
+                        model.EmployeeID = empid;
+                        model.ManagerID = mgrid;
+
+                        var sEmp = "";
+                        if (empid != EmployeeID)
+                        {
+                            if (!Entity.IsEntityLinkValid<IKanban, EmployeeLink>(x => x.EmployeeLink, row))
+                            {
+                                sEmp = "Unallocated";
+                            }
+                            else
+                            {
+                                var tuple = _employeelist.FirstOrDefault(x => x.ID.Equals(empid));
+                                sEmp = tuple != null ? tuple.ID == MyID ? MyName : tuple.Name : "";
+                            }
+                        }
+
+                        var sMgr = "";
+                        if (mgrid != EmployeeID)
+                            if (mgrid != Guid.Empty)
+                            {
+                                var tuple = _employeelist.FirstOrDefault(x => x.ID.Equals(mgrid));
+                                sMgr = tuple != null ? tuple.ID == MyID ? MyName : tuple.Name : "";
+                            }
+
+                        if (!string.IsNullOrEmpty(sEmp))
+                        {
+                            if (!string.IsNullOrWhiteSpace(sMgr) && !string.Equals(sMgr, sEmp))
+                                model.AssignedTo = string.Format("Assigned to {0} by {1}", sEmp, sMgr);
+                            else
+                                model.AssignedTo = string.Format("Assigned to {0} ", sEmp);
+                        }
+                        else
+                        {
+                            if (!string.IsNullOrWhiteSpace(sMgr))
+                                model.AssignedTo = string.Format("Allocated by {0} ", sMgr);
+                        }
+
+                        //model.AssignedTo = String.Format("M: {0} / E: {1}", sMgr, sEmp);
+                        model.JobID = row.Get<IKanban, Guid>(x => x.JobLink.ID);
+                        model.JobNumber = row.Get<IKanban, string>(x => x.JobLink.JobNumber);
+                        if (string.IsNullOrWhiteSpace(model.JobNumber))
+                            model.JobNumber = "";
+                        model.JobName = row.Get<IKanban, string>(x => x.JobLink.Name);
+                        model.Checked = CheckedKanbans.Contains(row.Get<IKanban, Guid>(x => x.ID).ToString());
+                        model.Type = new KanbanType
+                        {
+                            ID = row.Get<IKanban, Guid>(x => x.Type.ID),
+                            Code = row.Get<IKanban, string>(x => x.Type.Code)
+                        };
+                        model.Number = row.Get<IKanban, int>(x => x.Number);
+
+                        _models.Add(model);
+                    }
+                }
+                catch (Exception e)
+                {
+                    Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
+                }
+
+            StatusTasksHeaderTimeConverter.Tasks = _models;
+            FilterKanbans();
+        }
+
+        private void FilterKanbans()
+        {
+            Progress.ShowModal("Loading", (progress) =>
+            {
+                Dispatcher.BeginInvoke(() => { Progress.Show("Loading"); });
+                if (JobFilterID == Guid.Empty)
+                {
+                    var list = _models
+                         .Where(x => x.Search(Search.Text.Split()))
+                         .OrderBy(x => x.EmployeeID == EmployeeID ? 0 : 1).ThenBy(x => x.DueDate);
+                    Dispatcher.BeginInvoke(() => { Kanban.ItemsSource = list; });
+                }
+                else
+                {
+                    var list = _models
+                        .Where(x => x.Search(Search.Text.Split()) && x.JobSearch(JobFilterID))
+                        .OrderBy(x => x.EmployeeID == EmployeeID ? 0 : 1).ThenBy(x => x.DueDate);
+                    Dispatcher.BeginInvoke(() => { Kanban.ItemsSource = list; });
+                }
+                Task.Run(() => 
+                {
+                    Thread.Sleep(500);
+                    Dispatcher.BeginInvoke(() =>
+                    {
+                        Progress.Close();
+                    });
+                });               
+            });
+        }
+
+        #endregion
+
+        private void TaskTypesLabel_OnClick(object sender, RoutedEventArgs e)
+        {
+            var list = new MasterList(typeof(KanbanType));
+            list.ShowDialog();
+            SetupKanbanTypesLookup(true);
+        }
+
+        private void JobFilterBtn_OnClick(object sender, RoutedEventArgs e)
+        {
+            if (JobFilterID != Guid.Empty)
+            {
+                JobFilterBtn.Content = "Filter Job";
+                JobFilterID = Guid.Empty;
+                FilterKanbans();
+                return;
+            }
+
+            var window = new ThemableWindow();
+            var grid = new JobGrid();
+            grid.Reconfigure(options =>
+            {
+                options.Remove(DynamicGridOption.EditRows);
+                options.Remove(DynamicGridOption.DeleteRows);
+                options.Remove(DynamicGridOption.AddRows);
+                options.Remove(DynamicGridOption.MultiSelect);
+                options.Remove(DynamicGridOption.ExportData);
+                options.Remove(DynamicGridOption.ImportData);
+            });
+            grid.OnSelectItem += (object sender, DynamicGridSelectionEventArgs e) =>
+            {
+                if ((sender as JobGrid).SelectedRows.Count() == 0)
+                    return;
+
+                else
+                {
+                    var item = (sender as JobGrid).SelectedRows[0];
+                    AddJobFilter(item);
+                    window.Close();
+                }
+            };
+            grid.Refresh(true, true);
+            window.Content = grid;
+            window.ShowDialog();
+        }
+
+        Guid JobFilterID = Guid.Empty;
+
+        private void AddJobFilter(CoreRow item)
+        {
+            JobFilterID = item.Get<Job, Guid>(x => x.ID);
+            JobFilterBtn.Content = item.Get<Job, string>(x => x.JobNumber) + " (click to cancel)";
+            FilterKanbans();
+        }
+    }
+}

+ 2 - 1
prs.desktop/Panels/Tasks/TasksByUserControl.xaml

@@ -4,6 +4,7 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:dynamicgrid="clr-namespace:InABox.DynamicGrid;assembly=InABox.Wpf"
+             xmlns:wpf="clr-namespace:InABox.Wpf;assembly=InABox.Wpf"
              xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
              xmlns:kanban="clr-namespace:Syncfusion.UI.Xaml.Kanban;assembly=Syncfusion.SfKanban.WPF"
              xmlns:local="clr-namespace:PRSDesktop"
@@ -12,7 +13,7 @@
     <UserControl.Resources>
         <local:UserTasksHeaderImageConverter x:Key="UserTasksHeaderImageConverter" />
         <local:UserTasksHeaderTimeConverter x:Key="UserTasksHeaderTimeConverter" />
-        <local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
+        <wpf:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
         <!--<local:TaskHeaderWidthConverter x:Key="TaskHeaderWidthConverter"/>-->
 
         <Style x:Key="employeeStyle" TargetType="{x:Type TextBlock}">