Forráskód Böngészése

Upgraded Syncfusion to v29_x
Added Sites List to Live Maps View and improved Marker displays

frankvandenbos 1 hónapja
szülő
commit
d347fd08bf

+ 1 - 1
prs.classes/Settings/LiveMapsSettings.cs

@@ -5,7 +5,7 @@ namespace Comal.Classes
 {
     public class LiveMapsSettings : IUserConfigurationSettings
     {
+        public GPSLocationType? GeofenceType { get; set; }
         public Guid GroupID { get; set; }
-        public Guid ItemID { get; set; }
     }
 }

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

@@ -53,7 +53,7 @@ namespace PRSDesktop
 
         public App()
         {
-            SyncfusionLicenseProvider.RegisterLicense(CoreUtils.SyncfusionLicense(SyncfusionVersion.v25_2));
+            SyncfusionLicenseProvider.RegisterLicense(CoreUtils.SyncfusionLicense(SyncfusionVersion.v29_x));
             //CoreUtils.GoogleAPIKey = "AIzaSyAVReknl_sP2VLz5SUD8R-sZh1KDVCcWis";
         }
 

+ 0 - 11
prs.desktop/Dashboards/Projects/JobDocumentStatusChart.xaml.cs

@@ -2,26 +2,15 @@
 using InABox.Clients;
 using InABox.Core;
 using InABox.WPF;
-using java.nio.file;
 using Syncfusion.Data.Extensions;
 using Syncfusion.UI.Xaml.Charts;
-using Syncfusion.Windows.Controls.Gantt.Chart;
 using Syncfusion.Windows.Tools.Controls;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Reactive.Linq;
-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 System.Windows.Navigation;
-using System.Windows.Shapes;
 using InABox.Configuration;
 using System.ComponentModel;
 

+ 17 - 17
prs.desktop/PRSDesktop.csproj

@@ -920,24 +920,24 @@
       <PackageReference Include="Scriban" Version="5.10.0" />
       <PackageReference Include="SharpAvi" Version="3.0.1" />
       <PackageReference Include="SharpVectors.Wpf" Version="1.8.4.2" />
-      <PackageReference Include="Syncfusion.DataGridExcelExport.Wpf" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Gantt.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Grid.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Licensing" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Pdf.Wpf" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.SfBarcode.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.SfChart.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.SfDiagram.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.SfKanban.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.SfMaps.WPF" Version="25.2.6" />
+      <PackageReference Include="Syncfusion.DataGridExcelExport.Wpf" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Gantt.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Grid.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Licensing" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Pdf.Wpf" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.SfBarcode.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.SfChart.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.SfDiagram.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.SfKanban.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.SfMaps.WPF" Version="29.2.7" />
       <PackageReference Include="Syncfusion.SfSchedule.WPF" Version="18.3.0.53" />
-      <PackageReference Include="Syncfusion.SfScheduler.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.SfSpreadsheet.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Themes.FluentLight.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Themes.MaterialLight.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Themes.Office2019White.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Themes.SystemTheme.WPF" Version="25.2.6" />
-      <PackageReference Include="Syncfusion.Tools.WPF" Version="25.2.6" />
+      <PackageReference Include="Syncfusion.SfScheduler.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.SfSpreadsheet.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Themes.FluentLight.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Themes.MaterialLight.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Themes.Office2019White.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Themes.SystemTheme.WPF" Version="29.2.7" />
+      <PackageReference Include="Syncfusion.Tools.WPF" Version="29.2.7" />
       <PackageReference Include="System.Collections.Immutable" Version="9.0.4" />
       <PackageReference Include="System.Drawing.Common" Version="8.0.6" />
       <PackageReference Include="System.IO.Ports" Version="8.0.0" />

+ 1 - 2
prs.desktop/Panels/Jobs/Stages/JobStagesPanel.xaml

@@ -4,7 +4,7 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
-             xmlns:local="clr-namespace:PRSDesktop" xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:local="clr-namespace:PRSDesktop"
              xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"
              mc:Ignorable="d"
              d:DesignHeight="450" d:DesignWidth="800">
@@ -18,7 +18,6 @@
         <syncfusion:GanttControl
             x:Name="Gantt"
             Grid.Row="0"
-            VisualStyle="Metro"
             GridWidth="490"
             ChartWidth="*"
             ScheduleRangePadding="0"

+ 34 - 22
prs.desktop/Panels/Jobs/Stages/JobStagesPanel.xaml.cs

@@ -17,6 +17,7 @@ using Microsoft.Win32;
 using net.sf.mpxj;
 using net.sf.mpxj.MpxjUtilities;
 using net.sf.mpxj.reader;
+using Syncfusion.UI.Xaml.TreeGrid;
 using Syncfusion.Windows.Controls.Gantt;
 using Syncfusion.Windows.Controls.Grid;
 using Syncfusion.XlsIO;
@@ -131,42 +132,53 @@ namespace PRSDesktop
         {
             if (Gantt.GanttGrid != null)
             {
-                Gantt.GanttGrid.Model.Options.ListBoxSelectionMode = GridSelectionMode.One;
-                Gantt.GanttGrid.Model.Sizer.AllowAutoCalculateSize = true;
-                Gantt.GanttGrid.Model.Sizer.ListenToSizeChanged = true;
-                Gantt.GanttGrid.Model.Options.ColumnSizer = GridControlLengthUnitType.Star;
+                //Gantt.GanttGrid.Model.Options.ListBoxSelectionMode = GridSelectionMode.One;
+                //Gantt.GanttGrid.Model.Sizer.AllowAutoCalculateSize = true;
+                //Gantt.GanttGrid.Model.Sizer.ListenToSizeChanged = true;
+                //Gantt.GanttGrid.Model.Options.ColumnSizer = GridControlLengthUnitType.Star;
                 Gantt.GanttGrid.RowHeaderWidth = 0;
                 Gantt.GanttGrid.ShowRowHeader = false;
 
                 Gantt.GanttGrid.Columns.Clear();
                 //Gantt.GanttGrid.Columns.Add(new Syncfusion.Windows.Controls.Grid.GridTreeColumn("TaskId") { Width = 60F, HeaderText = "#", StyleInfo = new Syncfusion.Windows.Controls.Grid.GridStyleInfo() { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center } });
-                Gantt.GanttGrid.Columns.Add(new GridTreeColumn("TaskName")
+                Gantt.GanttGrid.Columns.Add(new TreeGridTextColumn()
                 {
-                    Width = 220F, PercentWidth = 100F, HeaderText = "Description",
-                    StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center }
+                    MappingName ="TaskName",
+                    Width = 220F, 
+                    //PercentWidth = 100F, 
+                    HeaderText = "Description",
+                    //StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center }
                 });
-                Gantt.GanttGrid.Columns.Add(new GridTreeColumn("StartDate")
+                Gantt.GanttGrid.Columns.Add(new TreeGridDateTimeColumn()
                 {
-                    Width = 70F, HeaderText = "Start",
-                    StyleInfo = new GridStyleInfo
-                        { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
+                    MappingName ="StartDate",
+                    Width = 70F, 
+                    HeaderText = "Start",
+                    //StyleInfo = new GridStyleInfo
+                    //    { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
                 });
-                Gantt.GanttGrid.Columns.Add(new GridTreeColumn("Manpower")
+                Gantt.GanttGrid.Columns.Add(new TreeGridNumericColumn()
                 {
-                    Width = 60F, HeaderText = "Hrs",
-                    StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center }
+                    MappingName ="Manpower",
+                    Width = 60F, 
+                    HeaderText = "Hrs",
+                    //StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center }
                 });
-                Gantt.GanttGrid.Columns.Add(new GridTreeColumn("Percentage")
+                Gantt.GanttGrid.Columns.Add(new TreeGridNumericColumn()
                 {
-                    Width = 40F, HeaderText = "%",
-                    StyleInfo = new GridStyleInfo
-                        { VerticalAlignment = VerticalAlignment.Center, Format = "#0.##%", HorizontalAlignment = HorizontalAlignment.Center }
+                    MappingName ="Percentage",
+                    Width = 40F, 
+                    HeaderText = "%",
+                    //StyleInfo = new GridStyleInfo
+                    //    { VerticalAlignment = VerticalAlignment.Center, Format = "#0.##%", HorizontalAlignment = HorizontalAlignment.Center }
                 });
-                Gantt.GanttGrid.Columns.Add(new GridTreeColumn("FinishDate")
+                Gantt.GanttGrid.Columns.Add(new TreeGridDateTimeColumn()
                 {
-                    Width = 70F, HeaderText = "Due",
-                    StyleInfo = new GridStyleInfo
-                        { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
+                    MappingName ="FinishDate",
+                    Width = 70F, 
+                    HeaderText = "Due",
+                    //StyleInfo = new GridStyleInfo
+                    //    { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
                 });
             }
         }

+ 298 - 150
prs.desktop/Panels/LiveMaps/LiveMapsPanel.xaml

@@ -20,185 +20,333 @@
         <prsDesktop:TestConverter x:Key="TestConverter" />
         <prsDesktop:EquipmentThumbnailConverter x:Key="EquipmentThumbnailConverter" />
         <prsDesktop:EquipmentColorConverter x:Key="EquipmentColorConverter" />
-        <DataTemplate x:Key="MarkerTemplate" DataType="{x:Type prsDesktop:MapMarker}">
-            <Border BorderBrush="Gray" BorderThickness="0.75" CornerRadius="0,5,5,5" Padding="2" Background="LightYellow" Margin="0,0,0,0" >
-                <TextBlock Text="{Binding Label}" TextAlignment="Center"/>
+        <prsDesktop:GeoFenceImageConverter x:Key="GeoFenceImageConverter" />
+        <prsDesktop:MarkerAlignmentConverter x:Key="MarkerAlignmentConverter"/>
+        
+        <DataTemplate x:Key="AssetMarkerTemplate" DataType="{x:Type prsDesktop:AssetMapMarker}">
+            
+            <Border 
+                BorderBrush="Purple" 
+                BorderThickness="0.75" 
+                CornerRadius="10" 
+                Padding="2" 
+                Width="20"
+                Height="20"
+                Margin="-10,-10,10,10"
+                >
+                <Border.Background>
+                    <SolidColorBrush Color="Orchid" Opacity ="0.8" />
+                </Border.Background>
+                <Border.ToolTip>
+                    <Label Content ="{Binding Label}" />
+                </Border.ToolTip>
             </Border>
+            
+        </DataTemplate>
+        
+        <DataTemplate x:Key="SiteMarkerTemplate" DataType="{x:Type prsDesktop:SiteMapMarker}">
+            
+            <Border 
+                BorderBrush="Gray" 
+                BorderThickness="0.75" 
+                CornerRadius="0,5,5,5" 
+                Padding="2">
+                <Label Content ="{Binding Label}" />
+                <Border.Background>
+                    <SolidColorBrush Color="LightYellow" Opacity ="0.8" />
+                </Border.Background>
+            </Border>
+            
+        </DataTemplate>
+
+        <prsDesktop:MarkerTemplateSelector x:Key="markerTemplateSelector"
+                                           SiteMarkerTemplate="{StaticResource SiteMarkerTemplate}"
+                                           AssetMarkerTemplate="{StaticResource AssetMarkerTemplate}"/> 
+        
+        <Style x:Key="EquipmentListBoxItemContainerStyle" TargetType="ListBoxItem">
+            <Setter Property="Height" Value="50" /> 
+            <Setter Property="HorizontalContentAlignment" Value="Stretch" />  
+            <Setter Property="VerticalContentAlignment" Value="Stretch" /> 
+            <Setter Property="Padding" Value="0,0,0,0" />
+            <Setter Property="Margin" Value="-1" />
+        </Style> 
+        
+        <Style x:Key="SiteListBoxItemContainerStyle" TargetType="ListBoxItem">
+            <Setter Property="Height" Value="60" /> 
+            <Setter Property="HorizontalContentAlignment" Value="Stretch" />  
+            <Setter Property="VerticalContentAlignment" Value="Stretch" /> 
+            <Setter Property="Padding" Value="0,0,0,0" />
+            <Setter Property="Margin" Value="-1" />
+        </Style> 
+        
+        <DataTemplate x:Key="EquipmentListBoxItemTemplate" DataType="{x:Type classes:Equipment}">
+            
+            <Grid>
+                <Grid.Background>
+                    <SolidColorBrush 
+                        Opacity="0.5" 
+                        Color="{Binding ., Converter={StaticResource EquipmentColorConverter}}" />
+                </Grid.Background>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="50"/>
+                    <ColumnDefinition Width="Auto"/>
+                    <ColumnDefinition Width="*"/>
+                    <ColumnDefinition Width="Auto"/>
+                </Grid.ColumnDefinitions>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="*"/>
+                    <RowDefinition Height="*"/>
+                    <RowDefinition Height="Auto"/>
+                </Grid.RowDefinitions>
+                <Image
+                    Grid.Row="0"
+                    Grid.Column="0"
+                    Grid.RowSpan="2"
+                    Margin="5"
+                    Source="{Binding GroupLink.Thumbnail.ID, Converter={StaticResource EquipmentThumbnailConverter}}"
+                    VerticalAlignment="Center"
+                    HorizontalAlignment="Center"/>
+                <Label 
+                    Grid.Row="0"
+                    Grid.Column="1"
+                    Grid.ColumnSpan="3"
+                    Content="{Binding Description}"
+                    FontWeight="DemiBold"/> 
+                <Label 
+                    Grid.Row="1"
+                    Grid.Column="1"
+                    Content="{Binding Code}"/>
+                <Label 
+                    Grid.Row="1"
+                    Grid.Column="2"
+                    HorizontalContentAlignment="Center"
+                    Content="{Binding TrackerLink.Location.Timestamp}"
+                    ContentStringFormat="{}{0:dd-MMM-yy}"/> 
+                <Label 
+                    Grid.Row="1"
+                    Grid.Column="3"
+                    Content="{Binding TrackerLink.DeviceID}"/> 
+                <Border 
+                    Grid.Row="2"
+                    Grid.Column="0"
+                    Grid.ColumnSpan="4"
+                    BorderThickness="0,0.75,0,0"
+                    BorderBrush="Gray"
+                    Margin="0"
+                    Padding="0"
+                    Height="0.75"/>
+                    
+            </Grid>
+
+        </DataTemplate>
+        
+        <DataTemplate x:Key="SiteListBoxItemTemplate" DataType="{x:Type classes:GeoFence}">
+            
+            <Grid>
+                <Grid.Background>
+                    <SolidColorBrush Opacity="0.5" Color="LightYellow" />
+                </Grid.Background>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="60"/>
+                    <ColumnDefinition Width="*"/>
+                </Grid.ColumnDefinitions>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="Auto"/>
+                    <RowDefinition Height="*"/>
+                    <RowDefinition Height="Auto"/>
+                    <RowDefinition Height="Auto"/>
+                </Grid.RowDefinitions>
+                <Image
+                    Grid.Row="0"
+                    Grid.Column="0"
+                    Grid.RowSpan="3"
+                    Margin="10"
+                    Source="{Binding Type, Converter={StaticResource GeoFenceImageConverter}}"
+                    VerticalAlignment="Center"
+                    HorizontalAlignment="Center"/>
+                <TextBlock 
+                    Grid.Row="0"
+                    Grid.Column="1"
+                    FontWeight="DemiBold">
+                    <Run Text="{Binding Code}" />
+                    <Run Text=" - " />
+                    <Run Text="{Binding Name}" />
+                </TextBlock>
+                
+                <TextBlock 
+                    Grid.Row="1"
+                    Grid.Column="1"
+                    VerticalAlignment="Center">
+                    <Run Text="{Binding Street}"/>
+                </TextBlock>
+                    
+                <TextBlock
+                    Grid.Row="2"
+                    Grid.Column="1">
+                    <Run Text="{Binding City}"/>
+                    <Run Text="{Binding State}"/>
+                    <Run Text="{Binding PostCode}"/>
+                </TextBlock>
+                <Border 
+                    Grid.Row="3"
+                    Grid.Column="0"
+                    Grid.ColumnSpan="2"
+                    BorderThickness="0,0.75,0,0"
+                    BorderBrush="Gray"
+                    Margin="0"
+                    Padding="0"
+                    Height="0.75"/>
+            </Grid>
+
+
         </DataTemplate>
-        <!-- <DataTemplate x:Key="MapMarkerTemplate" DataType="{x:Type prsDesktop:MapMarker}"> -->
-        <!--     <Border  -->
-        <!--         CornerRadius="5"  -->
-        <!--         BorderBrush="Navy"  -->
-        <!--         BorderThickness="2"  -->
-        <!--         Background="Transparent" > -->
-        <!--         <Label Content="{Binding Label}" /> -->
-        <!--     </Border> -->
-        <!-- </DataTemplate> -->
         
     </UserControl.Resources>
     
     <dynamicGrid:DynamicSplitPanel
         View="Combined"
         AllowableViews="Combined"
-        Anchor="Detail"
-        AnchorWidth="300">
+        Anchor="Master"
+        AnchorWidth="300"
+    >
         
         <dynamicGrid:DynamicSplitPanel.Master>
-            <syncfusion:SfMap 
-                x:Name="Map" 
-                Grid.Column="1" 
-                Grid.Row="0" 
-                BorderBrush="Gray"
-                BorderThickness="0.75" 
-                >
-                <syncfusion:SfMap.Layers>
-                    <syncfusion:ImageryLayer
-                        x:Name="ImageryLayer"
-                        Markers="{Binding Sites, Mode=TwoWay}"
-                        Center="{Binding Center, Mode=TwoWay}"
-                        Radius="{Binding Radius, Mode=TwoWay}"
-                        DistanceType="KiloMeter"
-                        MarkerTemplate="{StaticResource MarkerTemplate}"
-                        MarkerHorizontalAlignment="Far"
-                        MarkerVerticalAlignment="Far">
-                        <syncfusion:ImageryLayer.SubShapeFileLayers>
-                            <syncfusion:SubShapeFileLayer 
-                                MapElements="{Binding Elements}" >
-                            </syncfusion:SubShapeFileLayer>
-                        </syncfusion:ImageryLayer.SubShapeFileLayers>
-                        
-                        <!-- <syncfusion:ImageryLayer.MarkerTemplate> -->
-                        <!--     <DataTemplate> -->
-                        <!--         <Grid Margin="-12,-30,0,0"> -->
-                        <!--             <Canvas> -->
-                        <!--                 <Image Source="../../Resources/mapmarker.png" Height="30" ToolTip="Hi There"/> -->
-                        <!--             </Canvas> -->
-                        <!--         </Grid> -->
-                        <!--     </DataTemplate> -->
-                        <!-- </syncfusion:ImageryLayer.MarkerTemplate> -->
-                    </syncfusion:ImageryLayer>
-                </syncfusion:SfMap.Layers>
-            </syncfusion:SfMap>
-        </dynamicGrid:DynamicSplitPanel.Master>
-
-        
-        <dynamicGrid:DynamicSplitPanel.Detail>
             <DockPanel>
-                
                 <ComboBox
                     DockPanel.Dock="Top"
-                    x:Name="ViewGroup" 
+                    x:Name="ViewSites" 
                     Padding="5"
-                    Margin="0,0,0,0"
                     VerticalContentAlignment="Center"
-                    ItemsSource="{Binding Groups}"
-                    SelectedItem="{Binding SelectedGroup, Mode=TwoWay}"
-                    DisplayMemberPath="Description" />
+                    ItemsSource="{Binding SiteTypes}"
+                    SelectedItem="{Binding SelectedSiteType, Mode=TwoWay}"
+                    SelectedValuePath="Item1"
+                    DisplayMemberPath="Item2" />
 
-                <Border
-                    DockPanel.Dock="Top"
-                    BorderBrush="Gray" 
-                    BorderThickness="0.75" 
-                    Margin="0,5,0,0" 
-                    Background="White">
-                    <syncfusion:CalendarEdit 
-                        x:Name="Date" 
-                        ShowAbbreviatedMonthNames="True"
-                        AllowMultiplySelection="False" 
-                        Date="{Binding Date, Mode=TwoWay}"
-                        BorderBrush="Transparent" />
-                </Border>
-                
                 <TextBox 
                     DockPanel.Dock="Top" 
-                    x:Name="Search" 
-                    Margin="0,5,0,0"
+                    x:Name="SearchSites" 
                     Background="LightYellow"
                     Padding="5"
+                    Margin="0,4,0,0"
                     HorizontalContentAlignment="Left" 
                     VerticalContentAlignment="Center"
-                    wpf:TextBoxUtils.Placeholder="Search"
-                    Text="{Binding Search, Mode=TwoWay}"/>
-                
+                    wpf:TextBoxUtils.Placeholder="Search Sites"
+                    Text="{Binding EquipmentSearch, Mode=TwoWay}"/>
+
                 <ListBox 
-                    x:Name="Markers" 
                     DockPanel.Dock="Top"
-                    Margin="0,5,0,0"
-                    ItemsSource="{Binding Visible}"
+                    Margin="0,4,0,0"
+                    ItemsSource="{Binding VisibleSites}"
                     BorderThickness="0.75"
-                    SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
+                    BorderBrush="Gray"
+                    SelectedItem="{Binding SelectedSite, Mode=TwoWay}"
                     Padding="0"
                     ScrollViewer.HorizontalScrollBarVisibility="Disabled"
-                    ScrollViewer.VerticalScrollBarVisibility="Auto">
-                    <ListBox.ItemContainerStyle> 
-                        <Style TargetType="ListBoxItem">
-                            <Setter Property="Height" Value="50" /> 
-                            <Setter Property="HorizontalContentAlignment" Value="Stretch" />  
-                            <Setter Property="VerticalContentAlignment" Value="Stretch" /> 
-                            <Setter Property="Padding" Value="0,0,0,0" />
-                            <Setter Property="Margin" Value="-1" />
-                        </Style> 
-                    </ListBox.ItemContainerStyle> 
-                    <ListBox.ItemTemplate>
-                        <DataTemplate DataType="{x:Type classes:Equipment}">
-                            
-                            <Grid Background="{Binding ., Converter={StaticResource EquipmentColorConverter}}">
-                                <Grid.ColumnDefinitions>
-                                    <ColumnDefinition Width="40"/>
-                                    <ColumnDefinition Width="Auto"/>
-                                    <ColumnDefinition Width="*"/>
-                                    <ColumnDefinition Width="Auto"/>
-                                </Grid.ColumnDefinitions>
-                                <Grid.RowDefinitions>
-                                    <RowDefinition Height="*"/>
-                                    <RowDefinition Height="*"/>
-                                    <RowDefinition Height="Auto"/>
-                                </Grid.RowDefinitions>
-                                <Image
-                                    Grid.Row="0"
-                                    Grid.Column="0"
-                                    Grid.RowSpan="2"
-                                    Margin="2"
-                                    Source="{Binding GroupLink.Thumbnail.ID, Converter={StaticResource EquipmentThumbnailConverter}}" />
-                                <Label 
-                                    Grid.Row="0"
-                                    Grid.Column="1"
-                                    Grid.ColumnSpan="3"
-                                    Content="{Binding Description}"
-                                    FontWeight="DemiBold"/> 
-                                <Label 
-                                    Grid.Row="1"
-                                    Grid.Column="1"
-                                    Content="{Binding Code}"/>
-                                <Label 
-                                    Grid.Row="1"
-                                    Grid.Column="2"
-                                    HorizontalContentAlignment="Center"
-                                    Content="{Binding TrackerLink.Location.Timestamp}"
-                                    ContentStringFormat="{}{0:dd-MMM-yy}"/> 
-                                <Label 
-                                    Grid.Row="1"
-                                    Grid.Column="3"
-                                    Content="{Binding TrackerLink.DeviceID}"/> 
-                                <Border 
-                                    Grid.Row="2"
-                                    Grid.Column="0"
-                                    Grid.ColumnSpan="4"
-                                    BorderThickness="0,0.75,0,0"
-                                    BorderBrush="Gray"
-                                    Margin="0"
-                                    Padding="0"
-                                    Height="0.75"/>
-                                    
-                            </Grid>
-
-                        </DataTemplate>
-                    </ListBox.ItemTemplate>
+                    ScrollViewer.VerticalScrollBarVisibility="Auto"
+                    ItemContainerStyle="{StaticResource SiteListBoxItemContainerStyle}"
+                    ItemTemplate="{StaticResource SiteListBoxItemTemplate}"/>
 
-                </ListBox>
-                
             </DockPanel>
-        </dynamicGrid:DynamicSplitPanel.Detail>
+ 
+        </dynamicGrid:DynamicSplitPanel.Master>
+        
+        <dynamicGrid:DynamicSplitPanel.Detail>
+    
+            <dynamicGrid:DynamicSplitPanel
+                View="Combined"
+                AllowableViews="Combined"
+                Anchor="Detail"
+                AnchorWidth="300"
+                >
+                
+                <dynamicGrid:DynamicSplitPanel.Master>
+                    <syncfusion:SfMap 
+                        x:Name="Map" 
+                        Grid.Column="1" 
+                        Grid.Row="0" 
+                        BorderBrush="Gray"
+                        BorderThickness="0.75"
+                        ZoomedIn="Map_OnZoomed"
+                        ZoomedOut="Map_OnZoomed"
+                        Panned="Map_OnPanned"
+                        >
+                        <syncfusion:SfMap.Layers>
+                            <syncfusion:ImageryLayer
+                                x:Name="ImageryLayer"
+                                Center="{Binding Center, Mode=TwoWay}"
+                                Radius="{Binding Radius, Mode=TwoWay}"
+                                DistanceType="KiloMeter"
+                                Markers="{Binding Markers, Mode=TwoWay}" 
+                                MarkerTemplateSelector="{StaticResource markerTemplateSelector}"
+                                MarkerHorizontalAlignment="Far"
+                                MarkerVerticalAlignment="Far">
+                                <syncfusion:ImageryLayer.SubShapeFileLayers>
+                                    <syncfusion:SubShapeFileLayer 
+                                        MapElements="{Binding Elements}" >
+                                    </syncfusion:SubShapeFileLayer>
+                                </syncfusion:ImageryLayer.SubShapeFileLayers>
+                                
+                            </syncfusion:ImageryLayer>
+                        </syncfusion:SfMap.Layers>
+                    </syncfusion:SfMap>
+                </dynamicGrid:DynamicSplitPanel.Master>
+                
+                
+                <dynamicGrid:DynamicSplitPanel.Detail>
+                    <DockPanel>
+                        <ComboBox
+                            DockPanel.Dock="Top"
+                            x:Name="ViewGroup" 
+                            Padding="5"
+                            VerticalContentAlignment="Center"
+                            ItemsSource="{Binding EquipmentGroups}"
+                            SelectedItem="{Binding SelectedEquipmentGroup, Mode=TwoWay}"
+                            DisplayMemberPath="Description" />
+                        
+                        <TextBox 
+                            DockPanel.Dock="Top" 
+                            x:Name="SearchAssets" 
+                            Background="LightYellow"
+                            Padding="5"
+                            Margin="0,4,0,0"
+                            HorizontalContentAlignment="Left" 
+                            VerticalContentAlignment="Center"
+                            wpf:TextBoxUtils.Placeholder="Search"
+                            Text="{Binding EquipmentSearch, Mode=TwoWay}"/>
+                        
+                        <Border
+                            DockPanel.Dock="Top"
+                            BorderBrush="Gray" 
+                            BorderThickness="0.75" 
+                            Margin="0,4,0,0"
+                            Background="White">
+                            <syncfusion:CalendarEdit 
+                                x:Name="Date" 
+                                ShowAbbreviatedMonthNames="True"
+                                AllowMultiplySelection="False" 
+                                Date="{Binding Date, Mode=TwoWay}"
+                                BorderBrush="Transparent" />
+                        </Border>
+                        
+                        <ListBox 
+                            x:Name="Markers" 
+                            DockPanel.Dock="Top"
+                            Margin="0,4,0,0"
+                            ItemsSource="{Binding VisibleEquipment}"
+                            BorderThickness="0.75"
+                            SelectedItem="{Binding SelectedEquipment, Mode=TwoWay}"
+                            Padding="0"
+                            ScrollViewer.HorizontalScrollBarVisibility="Disabled"
+                            ScrollViewer.VerticalScrollBarVisibility="Auto"
+                            ItemContainerStyle="{StaticResource EquipmentListBoxItemContainerStyle}"
+                            ItemTemplate="{StaticResource EquipmentListBoxItemTemplate}" />
+                        
+                    </DockPanel>
+ 
+                </dynamicGrid:DynamicSplitPanel.Detail>
+
+            </dynamicGrid:DynamicSplitPanel>
 
+        </dynamicGrid:DynamicSplitPanel.Detail>
     </dynamicGrid:DynamicSplitPanel>
 </UserControl>

+ 45 - 6
prs.desktop/Panels/LiveMaps/LiveMapsPanel.xaml.cs

@@ -10,9 +10,11 @@ using InABox.Wpf;
 using Syncfusion.UI.Xaml.Maps;
 using System.ComponentModel;
 using System.Linq;
+using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using Geocoding;
+using InABox.DynamicGrid;
 using InABox.WPF;
 using Microsoft.Xaml.Behaviors.Core;
 using net.sf.mpxj;
@@ -22,6 +24,19 @@ using Location = InABox.Core.Location;
 namespace PRSDesktop
 {
 
+    public class MarkerAlignmentConverter : AbstractConverter<Border?, Thickness>
+    {
+        public override Thickness Convert(Border? value)
+        {
+
+            return new Thickness(
+                (value?.Width / 2.0) ?? 0.0,
+                (value?.Height / 2.0) ?? 0.0,
+                0.0,
+                0.0);
+        }
+    }
+    
     public class EquipmentThumbnailConverter : AbstractConverter<Guid, BitmapImage?>
     {
         
@@ -35,21 +50,38 @@ namespace PRSDesktop
         }
     }
     
-    public class EquipmentColorConverter : AbstractConverter<Equipment?, System.Windows.Media.Brush>
+    public class EquipmentColorConverter : AbstractConverter<Equipment?, System.Windows.Media.Color>
     {
         
         public static GPSTrackerLocation[] Pings { get; set; }
         
-        public override System.Windows.Media.Brush Convert(Equipment? value)
+        public override System.Windows.Media.Color Convert(Equipment? value)
         {
             if (value != null)
             {
                 if (Pings?.Any(x=>x.Tracker.ID == value.TrackerLink.ID) == true)
-                    return new SolidColorBrush(Colors.LightYellow) { Opacity = 0.5};
+                    return Colors.LightYellow;
             }
-            return new SolidColorBrush(Colors.LightGray) { Opacity = 0.5};
+            return Colors.LightGray;
         }
     }
+    
+    public class MarkerTemplateSelector : DataTemplateSelector 
+    { 
+        public DataTemplate? AssetMarkerTemplate { get; set; } 
+        public DataTemplate? SiteMarkerTemplate { get; set; } 
+
+        public override DataTemplate? SelectTemplate(object? item, DependencyObject container) 
+        { 
+            if (item is CustomDataSymbol customDataSymbol && customDataSymbol.Data != null) 
+            {  
+                return customDataSymbol.Data is SiteMapMarker
+                    ? SiteMarkerTemplate
+                    : AssetMarkerTemplate; 
+            } 
+            return base.SelectTemplate(item, container); 
+        } 
+    }
 
 
     /// <summary>
@@ -101,8 +133,15 @@ namespace PRSDesktop
         }
 
         public event DataModelUpdateEvent? OnUpdateDataModel;
-
-        
         
+        private void Map_OnPanned(object sender, PanEventArgs args)
+        {
+            ViewModel.SelectedSite = null;
+        }
+
+        private void Map_OnZoomed(object sender, ZoomEventArgs args)
+        {
+            ViewModel.SelectedSite = null;
+        }
     }
 }

+ 230 - 124
prs.desktop/Panels/LiveMaps/LiveMapsPanelViewModel.cs

@@ -3,9 +3,11 @@ using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Drawing;
+using System.Globalization;
 using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Windows;
+using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Media.Imaging;
 using Comal.Classes;
@@ -13,6 +15,8 @@ using ExCSS;
 using InABox.Clients;
 using InABox.Configuration;
 using InABox.Core;
+using InABox.DynamicGrid;
+using InABox.Wpf;
 using InABox.WPF;
 using Syncfusion.UI.Xaml.Diagram;
 using Syncfusion.UI.Xaml.Maps;
@@ -21,113 +25,196 @@ using Point = System.Windows.Point;
 
 namespace PRSDesktop;
 
-public class MapMarker
+public abstract class AbstractMapMarker
 {
-    public string Name { get; set; } = "Hi There";
     public string Label { get; set; } = "";
     public string Longitude { get; set; }
     public string Latitude { get; set; }
 }
 
+public class SiteMapMarker : AbstractMapMarker
+{
+
+}
+
+public class AssetMapMarker : AbstractMapMarker { }
+
+
+
+public class GeoFenceImageConverter : AbstractConverter<GPSLocationType, BitmapImage?>
+{
+
+    private Dictionary<GPSLocationType, BitmapImage?> _images = new()
+    {
+        { GPSLocationType.Office, PRSDesktop.Resources.mapmarker.AsBitmapImage() },
+        { GPSLocationType.Supplier, PRSDesktop.Resources.supplier.AsBitmapImage() },
+        { GPSLocationType.Employee, PRSDesktop.Resources.employee.AsBitmapImage() },
+        { GPSLocationType.Job, PRSDesktop.Resources.project.AsBitmapImage() },
+    };
+
+    public override BitmapImage? Convert(GPSLocationType value)
+    {
+        if (_images.TryGetValue(value, out var image))
+            return image;
+        return null;
+    }
+}
+
 public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
 {
-    private EquipmentGroup[]? _groups;
-    private Equipment[]? _items;
-    private EquipmentGroup? _selectedGroup;
-    private string? _search;
+    private EquipmentGroup[]? _equipmentGroups;
+    private Equipment[]? _equipment;
+    private EquipmentGroup? _selectedEquipmentGroup;
+    private System.Tuple<GPSLocationType?, string> _selectedSiteType;
+    private GeoFence? _selectedSite;
+    private string? _equipmentSearch;
     private DateTime _date = DateTime.Today;
-    private Equipment? _selectedItem;
+    private Equipment? _selectedEquipment;
     private ObservableCollection<Point>? _waypoints;
     private Point _center;
     private double _radius;
-    private GeoFence[]? _geofences;
+    private GeoFence[]? _sites;
     private ObservableCollection<MapElement> _elements;
-    private ObservableCollection<MapMarker>? _sites;
-
-    public EquipmentGroup[]? Groups
+    private ObservableCollection<AbstractMapMarker>? _markers;
+    
+    #region Equipment
+    
+    public EquipmentGroup[]? EquipmentGroups
     {
-        get => _groups;
-        set => SetField(ref _groups, value);
+        get => _equipmentGroups;
+        set => SetField(ref _equipmentGroups, value);
     }
     
-    public EquipmentGroup? SelectedGroup
+    public EquipmentGroup? SelectedEquipmentGroup
     {
-        get => _selectedGroup;
+        get => _selectedEquipmentGroup;
         set
         {
-            SetField(ref _selectedGroup, value);
-            SelectedItem = null;
+            if (value == _selectedEquipmentGroup)
+                return;
+            SetField(ref _selectedEquipmentGroup, value);
+            SelectedEquipment = null;
             SaveSettings();
-            OnPropertyChanged(nameof(Visible));
+            OnPropertyChanged(nameof(VisibleEquipment));
+            ReloadLocations();
         }
     }
 
-    public Equipment[]? Items
+    public Equipment[]? Equipment
     {
-        get => _items;
-        set => SetField(ref _items, value);
+        get => _equipment;
+        set => SetField(ref _equipment, value);
     }
-
-    public GeoFence[]? GeoFences
+    
+    public string? EquipmentSearch
     {
-        get => _geofences;
+        get => _equipmentSearch;
         set
         {
-            _fencesMap.Clear();
-            if (value is not null)
+            SetField(ref _equipmentSearch, value);
+            OnPropertyChanged(nameof(VisibleEquipment));
+        }
+    }
+    
+    public Equipment[]? VisibleEquipment => Equipment?.Where(x =>
+        (
+            (Equals(SelectedEquipmentGroup?.ID,CoreUtils.FullGuid)
+            || Equals(x.GroupLink.ID,SelectedEquipmentGroup?.ID)
+        ) && (
+            string.IsNullOrEmpty(EquipmentSearch) 
+            || x.Code.Contains(EquipmentSearch,StringComparison.CurrentCultureIgnoreCase)
+            || x.Description.Contains(EquipmentSearch,StringComparison.CurrentCultureIgnoreCase)
+        )
+    )).ToArray();
+    
+    public Equipment? SelectedEquipment
+    {
+        get => _selectedEquipment;
+        set
+        {
+            SetField(ref _selectedEquipment, value);
+            if (value != null)
             {
-                foreach (var fence in value ?? [])
-                    _fencesMap[fence] = Serialization.Deserialize<GeoFenceDefinition>(fence.Geofence) ?? new GeoFenceDefinition();
+                SelectedSite = null;
+                ReloadLocations();
             }
-            SetField(ref _geofences, value);
         }
     }
-    
-    private Dictionary<GeoFence,GeoFenceDefinition> _fencesMap = new();
 
-    public string? Search
+    #endregion
+    
+    #region Sites
+    
+    public System.Tuple<GPSLocationType?, string>[] SiteTypes =>
+    [
+        new System.Tuple<GPSLocationType?, string>(null, "All Sites"),
+        new System.Tuple<GPSLocationType?, string>(GPSLocationType.Office, "Office Location"),
+        new System.Tuple<GPSLocationType?, string>(GPSLocationType.Job, "Job Sites"),
+        new System.Tuple<GPSLocationType?, string>(GPSLocationType.Supplier, "Suppliers")
+    ];
+    
+    public System.Tuple<GPSLocationType?, string> SelectedSiteType
     {
-        get => _search;
+        get => _selectedSiteType;
         set
         {
-            SetField(ref _search, value);
-            OnPropertyChanged(nameof(Visible));
+            SetField(ref _selectedSiteType, value);
+            SaveSettings();
+            OnPropertyChanged(nameof(VisibleSites));
+            SelectedSite = null;
         }
     }
     
-    public Equipment[]? Visible => Items?.Where(x =>
-        x.GroupLink.ID == (SelectedGroup?.ID ?? CoreUtils.FullGuid) 
-        && (
-            string.IsNullOrEmpty(Search) 
-            || x.Code.Contains(Search,StringComparison.CurrentCultureIgnoreCase)
-            || x.Description.Contains(Search,StringComparison.CurrentCultureIgnoreCase)
-        )
-    ).ToArray();
-
-    
-    public DateTime Date
+    public GeoFence[]? Sites
     {
-        get => _date;
+        get => _sites;
         set
         {
-            SetField(ref _date, value);
-            ReloadLocations();
-            OnPropertyChanged(nameof(Visible));
+            _sitesMap.Clear();
+            if (value is not null)
+            {
+                foreach (var site in value ?? [])
+                    _sitesMap[site] = Serialization.Deserialize<GeoFenceDefinition>(site.Geofence) ?? new GeoFenceDefinition();
+            }
+            SetField(ref _sites, value);
         }
     }
 
+    public GeoFence[]? VisibleSites => Sites?
+        .Where(x => (_selectedSiteType?.Item1 == null) || Equals(x.Type, _selectedSiteType?.Item1))
+        .ToArray();
+
+    
+    private Dictionary<GeoFence,GeoFenceDefinition> _sitesMap = new();
 
-    public Equipment? SelectedItem
+    public GeoFence? SelectedSite
+    {
+        get => _selectedSite;
+        set
+        {
+            SetField(ref _selectedSite, value);
+
+            if (value != null)
+            {
+                SelectedEquipment = null;
+                CenterMap(value, null);
+            }
+        }
+    }
+    
+    #endregion
+    
+    public DateTime Date
     {
-        get => _selectedItem;
+        get => _date;
         set
         {
-            SetField(ref _selectedItem, value);
+            SetField(ref _date, value);
             ReloadLocations();
         }
     }
     
-    private void CenterMap()
+    private void CenterMap(GeoFence? site, IEnumerable<Point>? points)
     {
         var nwLon = double.MaxValue;
         var nwLat = double.MinValue;
@@ -135,18 +222,18 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
         var seLat = double.MaxValue;
 
         
-        var points = new List<Point>();
+        var allpoints = new List<Point>();
         
-        if (_waypoints != null)
-            points.AddRange(_waypoints);
+        if (points != null)
+            allpoints.AddRange(points);
         
-        if (_geofences != null)
+        if (site != null)
         {
-            foreach (var geofence in _fencesMap.Where(x=>x.Key.Type == GPSLocationType.Office))
-                points.AddRange(geofence.Value.Coordinates.Select(x => new Point(x.Latitude, x.Longitude)));
+            foreach (var fence in _sitesMap.Where(x=>x.Key == site))
+                allpoints.AddRange(fence.Value.Coordinates.Select(x => new Point(x.Latitude, x.Longitude)));
         }
         
-        foreach (var point in points)
+        foreach (var point in allpoints)
         {
             var lat = point.X;
             var lon = point.Y;
@@ -165,13 +252,15 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
         Center = new Point(cLat, cLon);
         var c = new Location { Latitude = cLat, Longitude = cLon };
         var nw = new Location { Latitude = nwLat, Longitude = nwLon };
-        Radius = Math.Max(1.0, c.DistanceTo(nw, UnitOfLength.Kilometers) / 2.0F);
+        Radius = Math.Max(0.25, c.DistanceTo(nw, UnitOfLength.Kilometers) / 2.0F);
         
     }
 
     private void ReloadLocations()
     {
-        var trackerids = Visible?.Select(x => x.TrackerLink.ID).ToArray() ?? [];
+        var trackerids = VisibleEquipment?.Select(x => x.TrackerLink.ID).ToArray() ?? [];
+        var selected = SelectedEquipment?.TrackerLink.ID ?? CoreUtils.FullGuid;
+        
         Client.Query(
             new Filter<GPSTrackerLocation>(x=>x.Tracker.ID).InList(trackerids)
                 .And(x => x.Location.Timestamp).IsGreaterThanOrEqualTo(Date.Date)
@@ -187,50 +276,66 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
             {
                 Dispatcher.BeginInvoke(() =>
                 {
-                    Point[]? others = null;
+                    GPSTrackerLocation[] pings = [];
                     if (data is null)
                     {
-                        EquipmentColorConverter.Pings = [];
+                        EquipmentColorConverter.Pings = pings;
                         Waypoints = null;
-                        Sites = null;
+                        Markers = null;
                     }
                     else
                     {
-                        EquipmentColorConverter.Pings = data.ToArray<GPSTrackerLocation>();
-                        others = data.Rows
-                            .Where(r => r.Get<GPSTrackerLocation,Guid>(c=>c.Tracker.ID) != (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid))
-                            .Select(r => new Point()
+                        List<AbstractMapMarker> markers = new();
+                        
+                        pings = data.ToArray<GPSTrackerLocation>();
+                        EquipmentColorConverter.Pings = pings;
+
+                        var otherassets = pings
+                            .Where(x=>x.Tracker.ID != selected)
+                            .GroupBy(x => x.Tracker.ID)
+                            .Where(x=>x.Any())
+                            .Select(x=>x.Last() 
+                        );
+
+                        var assetmarkers = new List<AssetMapMarker>();
+
+                        foreach (var asset in otherassets)
+                        {
+                            var eq = _equipment?.FirstOrDefault(e=>e.TrackerLink.ID == asset.Tracker.ID);
+                            if (eq != null)
+                            {
+                                var marker = new AssetMapMarker()
                                 {
-                                    X = r.Get<GPSTrackerLocation, double>(c => c.Location.Latitude),
-                                    Y = r.Get<GPSTrackerLocation, double>(c => c.Location.Longitude)
-                                }
-                            ).ToArray();
+                                    Latitude = $"{asset.Location.Latitude}",
+                                    Longitude = $"{asset.Location.Longitude}",
+                                    Label = $"{eq.Code}\n{eq.Description}\nLast seen: {asset.Location.Timestamp}"
+                                };
+                                markers.Add(marker);
+                            }
+                        }
+                        
+                        var waypoints = pings
+                            .Where(x => x.Tracker.ID == selected)
+                            .ToArray();
                         
-                        var locations = data.Rows
-                            .Where(r => r.Get<GPSTrackerLocation,Guid>(c=>c.Tracker.ID) == (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid))
-                            .Select(r => new Point(
-                                    r.Get<GPSTrackerLocation, double>(c => c.Location.Latitude),
-                                    r.Get<GPSTrackerLocation, double>(c => c.Location.Longitude)
-                                )
-                            )?.ToArray() ?? [];
-                        Waypoints = new ObservableCollection<Point>(locations);
+                        Waypoints = new ObservableCollection<Point>(waypoints.Select(x => new Point(x.Location.Latitude, x.Location.Longitude)));
                         
-                        Dictionary<GeoFence, MapMarker> sites = new Dictionary<GeoFence, MapMarker>();
+                        var sites = new Dictionary<GeoFence, SiteMapMarker>();
                         GeoFence? curFence = null;
                         
-                        foreach (var row in data.Rows.Where(r => r.Get<GPSTrackerLocation,Guid>(c=>c.Tracker.ID) == (SelectedItem?.TrackerLink.ID ?? CoreUtils.FullGuid)))
+                        foreach (var waypoint in waypoints)
                         {
-                            var time = $"{row.Get<GPSTrackerLocation, DateTime>(c => c.Location.Timestamp):h:mm tt}";
-                            var geopoint = new GeoPoint(row.Get<GPSTrackerLocation, double>(c => c.Location.Latitude), row.Get<GPSTrackerLocation, double>(c => c.Location.Longitude));
+                            var time = $"{waypoint.Location.Timestamp:h:mm tt}";
+                            var geopoint = new GeoPoint(waypoint.Location.Latitude, waypoint.Location.Longitude);
                             bool bFound = false;
                             
-                            foreach (var geofence in _fencesMap)
+                            foreach (var geofence in _sitesMap)
                             {
                                 
                                 if (geofence.Value.Contains(geopoint))
                                 {
                                     if (!sites.ContainsKey(geofence.Key))
-                                        sites[geofence.Key] = new MapMarker()
+                                        sites[geofence.Key] = new SiteMapMarker()
                                         {
                                             Latitude = $"{geopoint.Latitude}",
                                             Longitude = $"{geopoint.Longitude}"
@@ -250,7 +355,7 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
                                     }
                                     else
                                         timelist.Add($"{time} - {time}");
-
+                        
                                     sites[geofence.Key].Label = string.Join("\n", timelist);
                                     curFence = geofence.Key;
                                     bFound = true;
@@ -260,12 +365,13 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
                             if (!bFound)
                                 curFence = null;
                         }
-                        Sites = new ObservableCollection<MapMarker>(sites.Values);
-                    
+                        markers.AddRange(sites.Values);
+
+                        Markers = new ObservableCollection<AbstractMapMarker>(markers);
                     }
-                    OnPropertyChanged(nameof(Visible));
-                    RecalculateLayers(others);
-                    CenterMap();
+                    OnPropertyChanged(nameof(VisibleEquipment));
+                    RecalculateLayers();
+                    CenterMap(_sites?.FirstOrDefault(x=>x.Type == GPSLocationType.Office), pings.Select(x=>new Point(x.Location.Latitude, x.Location.Longitude)));
                 });
             }
         );
@@ -278,12 +384,12 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
         set => SetField(ref _elements, value);
     }
 
-    private void RecalculateLayers(Point[]? markers)
+    private void RecalculateLayers()
     {
         var elements = new ObservableCollection<MapElement>();
-        if (_geofences?.Any() == true)
+        if (_sites?.Any() == true)
         {
-            foreach (var geofence in _geofences)
+            foreach (var geofence in _sites)
             {
                 var definition = Serialization.Deserialize<GeoFenceDefinition>(geofence.Geofence) ?? new GeoFenceDefinition();
                 var polygon = new MapPolygon()
@@ -300,23 +406,7 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
 
             }
         }
-
-        if (markers?.Any() == true)
-        {
-            foreach (var marker in markers.Distinct())
-            {
-                var circle = new MapCircle()
-                {
-                    Center = marker,
-                    Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Magenta) { Opacity = 0.2 },
-                    Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Black),
-                    StrokeThickness = 0,
-                    Radius = 10
-                };
-                elements.Add(circle);
-            }
-        }
-
+        
         if (_waypoints?.Any( ) == true)
         {
             var line = new MapPolyline()
@@ -357,10 +447,10 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
     
     public ICommand RefreshCommand { get; set; }
     
-    public ObservableCollection<MapMarker>? Sites
+    public ObservableCollection<AbstractMapMarker>? Markers
     {
-        get => _sites;
-        set => SetField(ref _sites, value);
+        get => _markers;
+        set => SetField(ref _markers, value);
     }
 
     public ObservableCollection<Point>? Waypoints
@@ -390,13 +480,18 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
 
     public void SaveSettings()
     {
-        Settings.GroupID = _selectedGroup?.ID ?? Guid.Empty;
-        Settings.ItemID = _selectedItem?.ID ?? Guid.Empty;
+        if (bRefreshing)
+            return;
+        Settings.GeofenceType = _selectedSiteType.Item1;
+        Settings.GroupID = _selectedEquipmentGroup?.ID ?? Guid.Empty;
         new UserConfiguration<LiveMapsSettings>().Save(Settings);
     }
         
+    private bool bRefreshing = false;
+    
     public void Refresh()
     {
+        
         MultiQuery query = new MultiQuery();
 
         query.Add(new Filter<GeoFence>().All());
@@ -421,6 +516,7 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
                 .Add(x=>x.GroupLink.Thumbnail.ID)
                 .Add(x=>x.TrackerLink.ID)
                 .Add(x=>x.TrackerLink.Location.Timestamp)
+                .Add(x=>x.TrackerLink.Location.Address)
                 .Add(x=>x.TrackerLink.DeviceID)
                 .Add(x=>x.TrackerLink.BatteryLevel)
         );
@@ -428,6 +524,7 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
         {
             Dispatcher.BeginInvoke(() =>
             {
+                bRefreshing = true;
                 foreach (var row in query.Get<Document>().Rows)
                 {
                     var img = ImageUtils.LoadImage(row.Get<Document, byte[]>(x => x.Data));
@@ -436,11 +533,20 @@ public class LiveMapsPanelViewModel : DependencyObject, INotifyPropertyChanged
                 // EquipmentThumbnailConverter.Cache = query.Get<Document>()
                 //     .ToDictionary<Document, Guid, BitmapImage?>(x => x.ID,
                 //         x => ImageUtils.LoadImage(x.Data));
-                GeoFences = query.Get<GeoFence>().ToArray<GeoFence>();
-                Groups = query.Get<EquipmentGroup>().ToArray<EquipmentGroup>();
-                SelectedGroup = _groups?.FirstOrDefault(x=>x.ID == _selectedGroup?.ID);
-                Items = query.Get<Equipment>().ToArray<Equipment>();
-                SelectedItem = _items?.FirstOrDefault(x=>x.ID == _selectedItem?.ID);
+                Sites = query.Get<GeoFence>().ToArray<GeoFence>();
+                SelectedSiteType = SiteTypes.FirstOrDefault(x => x.Item1 == Settings.GeofenceType) ?? SiteTypes.First();
+                SelectedSite = null;
+                
+                var groups = query.Get<EquipmentGroup>().ToList<EquipmentGroup>();
+                groups.Insert(0,new EquipmentGroup() { ID = CoreUtils.FullGuid, Description="(All Groups)"});
+                EquipmentGroups = groups.ToArray();
+                SelectedEquipmentGroup = EquipmentGroups?.FirstOrDefault(x=>x.ID == Settings.GroupID);
+                
+                Equipment = query.Get<Equipment>().ToArray<Equipment>();
+                SelectedEquipment = null;
+                
+                bRefreshing = false;
+                ReloadLocations();
             });
         });
     }

+ 0 - 1
prs.desktop/Panels/Tasks/TaskPlannerControl.xaml

@@ -32,7 +32,6 @@
         <syncfusion:GanttControl
             x:Name="Gantt"
             Grid.Row="1"
-            VisualStyle="Metro"
             GridWidth="490"
             ChartWidth="*"
             ScheduleRangePadding="0"

+ 49 - 32
prs.desktop/Panels/Tasks/TaskPlannerControl.xaml.cs

@@ -11,9 +11,11 @@ using InABox.Clients;
 using InABox.Core;
 using InABox.DynamicGrid;
 using InABox.WPF;
+using Syncfusion.UI.Xaml.TreeGrid;
 using Syncfusion.Windows.Controls.Gantt;
 using Syncfusion.Windows.Controls.Grid;
 using GridSelectionMode = Syncfusion.Windows.Controls.Grid.GridSelectionMode;
+using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs;
 
 namespace PRSDesktop
 {
@@ -84,10 +86,10 @@ namespace PRSDesktop
         {
             if (Gantt.GanttGrid != null)
             {
-                Gantt.GanttGrid.Model.Options.ListBoxSelectionMode = GridSelectionMode.One;
-                Gantt.GanttGrid.Model.Sizer.AllowAutoCalculateSize = true;
-                Gantt.GanttGrid.Model.Sizer.ListenToSizeChanged = true;
-                Gantt.GanttGrid.Model.Options.ColumnSizer = GridControlLengthUnitType.Star;
+                //Gantt.GanttGrid.Model.Options.ListBoxSelectionMode = GridSelectionMode.One;
+                //Gantt.GanttGrid.Model.Sizer.AllowAutoCalculateSize = true;
+                //Gantt.GanttGrid.Model.Sizer.ListenToSizeChanged = true;
+                //Gantt.GanttGrid.Model.Options.ColumnSizer = GridControlLengthUnitType.Star;
                 Gantt.GanttGrid.RowHeaderWidth = 0;
                 Gantt.GanttGrid.ShowRowHeader = false;
 
@@ -103,33 +105,47 @@ namespace PRSDesktop
                 return;
             Gantt.GanttGrid.Columns.Clear();
             //Gantt.GanttGrid.Columns.Add(new Syncfusion.Windows.Controls.Grid.GridTreeColumn("TaskId") { Width = 60F, HeaderText = "#", StyleInfo = new Syncfusion.Windows.Controls.Grid.GridStyleInfo() { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center } });
-            Gantt.GanttGrid.Columns.Add(new GridTreeColumn("TaskName")
+            Gantt.GanttGrid.Columns.Add(new TreeGridTextColumn()
             {
-                Width = 220F, PercentWidth = 100F, HeaderText = "Description",
-                StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center }
+                MappingName = "TaskName",
+                Width = 220F, 
+                HeaderText = "Description",
+                VerticalAlignment = VerticalAlignment.Center
             });
-            Gantt.GanttGrid.Columns.Add(new GridTreeColumn("StartDate")
+            Gantt.GanttGrid.Columns.Add(new TreeGridDateTimeColumn()
             {
-                Width = 70F, HeaderText = "Start",
-                StyleInfo = new GridStyleInfo
-                    { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
+                MappingName = "StartDate",
+                Width = 70F, 
+                HeaderText = "Start",
+                VerticalAlignment = VerticalAlignment.Center
+                //StyleInfo = new GridStyleInfo
+                //    { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
             });
-            Gantt.GanttGrid.Columns.Add(new GridTreeColumn("Manpower")
+            Gantt.GanttGrid.Columns.Add(new TreeGridNumericColumn()
             {
-                Width = 60F, HeaderText = "Hrs",
-                StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center }
+                MappingName = "Manpower",
+                Width = 60F, 
+                HeaderText = "Hrs",
+                //StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center }
+                VerticalAlignment = VerticalAlignment.Center
             });
-            Gantt.GanttGrid.Columns.Add(new GridTreeColumn("Percentage")
+            Gantt.GanttGrid.Columns.Add(new TreeGridNumericColumn()
             {
-                Width = 40F, HeaderText = "%",
-                StyleInfo = new GridStyleInfo
-                    { VerticalAlignment = VerticalAlignment.Center, Format = "#0.##%", HorizontalAlignment = HorizontalAlignment.Center }
+                MappingName = "Percentage",
+                Width = 40F, 
+                HeaderText = "%",
+                //StyleInfo = new GridStyleInfo
+                //    { VerticalAlignment = VerticalAlignment.Center, Format = "#0.##%", HorizontalAlignment = HorizontalAlignment.Center }
+                VerticalAlignment = VerticalAlignment.Center
             });
-            Gantt.GanttGrid.Columns.Add(new GridTreeColumn("FinishDate")
+            Gantt.GanttGrid.Columns.Add(new TreeGridDateTimeColumn()
             {
-                Width = 70F, HeaderText = "Due",
-                StyleInfo = new GridStyleInfo
-                    { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
+                MappingName = "FinishDate",
+                Width = 70F, 
+                HeaderText = "Due",
+                //StyleInfo = new GridStyleInfo
+                //    { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
+                VerticalAlignment = VerticalAlignment.Center
             });
         }
 
@@ -506,16 +522,7 @@ namespace PRSDesktop
                 task.PropertyChanged += Task_PropertyChanged;
             }
         }
-
-        private void SelectedType_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (EventSuppressor.IsSet(Suppress.This))
-            {
-                Host.KanbanSettings.PlannerSettings.SelectedType = SelectedType.SelectedValue == null ? Guid.Empty : (Guid)SelectedType.SelectedValue;
-                Host.SaveSettings();
-                Refresh();
-            }
-        }
+        
 
         private void IncludeCompleted_Checked(object sender, RoutedEventArgs e)
         {
@@ -554,5 +561,15 @@ namespace PRSDesktop
             list.ShowDialog();
             LoadKanbanTypes();
         }
+
+        private void SelectedType_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (EventSuppressor.IsSet(Suppress.This))
+            {
+                Host.KanbanSettings.PlannerSettings.SelectedType = SelectedType.SelectedValue == null ? Guid.Empty : (Guid)SelectedType.SelectedValue;
+                Host.SaveSettings();
+                Refresh();
+            }
+        }
     }
 }

+ 2 - 3
prs.desktop/Utils/Gantt/GanttSetupBehaviour.cs

@@ -2,7 +2,6 @@
 using System.Windows.Controls;
 using Microsoft.Xaml.Behaviors;
 using Syncfusion.Windows.Controls.Gantt;
-using Syncfusion.Windows.Controls.Gantt.Chart;
 using Syncfusion.Windows.Controls.Grid;
 using DependencyObjectExtensions = Syncfusion.Windows.Controls.Gantt.DependencyObjectExtensions;
 
@@ -25,7 +24,7 @@ namespace PRSDesktop
         /// <param name="e">The <see cref="System.Windows.RoutedEventArgs" /> instance containing the event data.</param>
         private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
         {
-            if (AssociatedObject.GanttGrid != null) AssociatedObject.GanttGrid.UpdateMode = UpdateMode.PropertyChanged;
+            //if (AssociatedObject.GanttGrid != null) AssociatedObject.GanttGrid.UpdateMode = UpdateMode.PropertyChanged;
 
             var chart = DependencyObjectExtensions.FindName<GanttChart>(AssociatedObject, "PART_GanttChart");
             if (chart != null)
@@ -44,7 +43,7 @@ namespace PRSDesktop
         private void ChartScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
         {
             // To sync the GanttGrid's vertical scroll with GanttChart's Vertical scroll offset.
-            AssociatedObject.GanttGrid.InternalGrid.SetVerticalOffset(e.VerticalOffset);
+            //AssociatedObject.GanttGrid.InternalGrid.SetVerticalOffset(e.VerticalOffset);
         }
 
         /// <summary>

+ 11 - 11
prs.mobile.new/PRS.Mobile/PRS.Mobile.csproj

@@ -27,17 +27,17 @@
         </PackageReference>
         <PackageReference Include="Serilog" Version="4.0.0" />
         <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
-        <PackageReference Include="Syncfusion.Xamarin.DocIO" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.DocIORenderer" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfDiagram" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfImageEditor" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfMaps" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfMaskedEdit" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfPdfViewer" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfSchedule" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfSignaturePad" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfTabView" Version="26.1.39" />
-        <PackageReference Include="Syncfusion.Xamarin.SfTreeView" Version="26.1.39" />
+        <PackageReference Include="Syncfusion.Xamarin.DocIO" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.DocIORenderer" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfDiagram" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfImageEditor" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfMaps" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfMaskedEdit" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfPdfViewer" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfSchedule" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfSignaturePad" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfTabView" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Xamarin.SfTreeView" Version="29.2.7" />
         <PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.6" />
         <PackageReference Include="Xamarin.Essentials" Version="1.8.1" />
         <PackageReference Include="Xamarin.Forms" Version="5.0.0.2662" />

+ 2 - 2
prs.server/PRSServer.csproj

@@ -101,8 +101,8 @@
         <PackageReference Include="PropertyChanged.Fody" Version="4.1.0" />
         <PackageReference Include="RazorEngine.NetCore" Version="3.1.0" />
         <PackageReference Include="Stripe.net" Version="44.10.0" />
-        <PackageReference Include="Syncfusion.Licensing" Version="25.2.6" />
-        <PackageReference Include="Syncfusion.Pdf.Wpf" Version="25.2.6" />
+        <PackageReference Include="Syncfusion.Licensing" Version="29.2.7" />
+        <PackageReference Include="Syncfusion.Pdf.Wpf" Version="29.2.7" />
         <PackageReference Include="Syncfusion.Shared.WPF.Classic" Version="19.4.0.56" />
         <PackageReference Include="System.Drawing.Common" Version="8.0.6" />
         <PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />

+ 1 - 1
prs.stores/PRSStores.csproj

@@ -10,7 +10,7 @@
 
   <ItemGroup>
     <PackageReference Include="Nominatim.API" Version="2.1.0" />
-    <PackageReference Include="Syncfusion.Pdf.Wpf" Version="25.2.6" />
+    <PackageReference Include="Syncfusion.Pdf.Wpf" Version="29.2.7" />
   </ItemGroup>
 
   <ItemGroup>