浏览代码

First draft of dashboard system

Kenric Nugteren 5 月之前
父节点
当前提交
5e22bec994

+ 10 - 0
prs.desktop/Dashboards/CustomDashboard.xaml

@@ -0,0 +1,10 @@
+<UserControl x:Class="PRSDesktop.CustomDashboardWidget"
+             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" 
+             d:DesignHeight="450" d:DesignWidth="800">
+    <ContentControl x:Name="ContentControl"/>
+</UserControl>

+ 89 - 0
prs.desktop/Dashboards/CustomDashboard.xaml.cs

@@ -0,0 +1,89 @@
+using InABox.Configuration;
+using InABox.Core;
+using InABox.Wpf.Dashboard;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.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;
+
+namespace PRSDesktop;
+
+public class CustomDashboardProperties : IDashboardProperties, IUserConfigurationSettings
+{
+    public string DashboardName { get; set; } = "";
+}
+
+public class CustomDashboardElement : DFLayoutElement<CustomDashboardProperties>
+{
+}
+
+/// <summary>
+/// Interaction logic for CustomDashboard.xaml
+/// </summary>
+public partial class CustomDashboardWidget : UserControl, IDashboardWidget<CustomDashboardProperties>
+{
+    public CustomDashboardWidget()
+    {
+        InitializeComponent();
+    }
+
+    public CustomDashboardProperties Properties { get; set; } = null!;
+
+    public event LoadSettings<CustomDashboardProperties>? LoadSettings;
+    public event SaveSettings<CustomDashboardProperties>? SaveSettings;
+
+    private DynamicDashboardDataComponent? DataComponent;
+    private IDynamicDashboardDataPresenter? DataPresenter;
+
+    public void Setup()
+    {
+        var customDashboard = new GlobalConfiguration<GlobalUtilityDashboardSettings>().Load()
+            .CustomDashboards.FirstOrDefault(x => x.Name == Properties.DashboardName);
+        if(customDashboard is null)
+        {
+            return;
+        }
+        var dashboard = DynamicDashboardUtils.Deserialize(customDashboard.Layout);
+        if(dashboard is null)
+        {
+            return;
+        }
+
+        DataComponent = dashboard.DataComponent;
+        DataPresenter = dashboard.DataPresenter;
+
+        if(DataPresenter is null)
+        {
+            return;
+        }
+
+        DataPresenter.DataComponent = DataComponent;
+        var control = DataPresenter.Setup();
+
+        ContentControl.Content = control;
+    }
+
+    public void Refresh()
+    {
+        var data = DataComponent?.RunQuery();
+        if (data is null) return;
+
+        DataPresenter?.Refresh(data);
+    }
+
+    public void Shutdown(CancelEventArgs? cancel)
+    {
+        DataPresenter?.Shutdown(cancel);
+    }
+}

+ 76 - 78
prs.desktop/Dashboards/DashboardContainer.xaml.cs

@@ -17,106 +17,104 @@ using System.Windows.Shapes;
 using InABox.Configuration;
 using InABox.Wpf;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+/// <summary>
+/// Interaction logic for DashboardContainer.xaml
+/// </summary>
+public partial class DashboardContainer : UserControl
 {
-    /// <summary>
-    /// Interaction logic for DashboardContainer.xaml
-    /// </summary>
-    public partial class DashboardContainer : UserControl
+    private ICorePanel _panel;
+
+    public ICorePanel Panel { get => _panel; }
+
+    private DashboardContainer(ICorePanel panel, string name)
     {
-        private ICorePanel _panel;
+        InitializeComponent();
 
-        public ICorePanel Panel { get => _panel; }
+        _panel = panel;
+        DashboardName.Content = name;
 
-        private DashboardContainer(ICorePanel panel, string name)
+        if(panel is IActionsDashboard)
         {
-            InitializeComponent();
-
-            _panel = panel;
-            DashboardName.Content = name;
-
-            if(panel is IActionsDashboard)
-            {
-                Settings.Visibility = Visibility.Visible;
-            }
-            else
-            {
-                Settings.Visibility = Visibility.Collapsed;
-            }
-
-            if(panel is IHeaderDashboard headerDashboard)
-            {
-                RefreshHeader(headerDashboard.Header);
-
-                headerDashboard.Header.HeaderChanged += Header_HeaderChanged;
-            }
+            Settings.Visibility = Visibility.Visible;
+        }
+        else
+        {
+            Settings.Visibility = Visibility.Collapsed;
         }
 
-        private void RefreshHeader(DashboardHeader header)
+        if(panel is IHeaderDashboard headerDashboard)
         {
-            if (HeaderItems.Children.Count > 0)
-            {
-                HeaderItems.Children.Clear();
-            }
-            if (HeaderItemsRight.Children.Count > 0)
-            {
-                HeaderItemsRight.Children.Clear();
-            }
-
-            foreach (var item in header.GetLeftElements())
-            {
-                HeaderItems.Children.Add(item);
-            }
-            foreach (var item in header.GetRightElements())
-            {
-                HeaderItemsRight.Children.Add(item);
-            }
+            RefreshHeader(headerDashboard.Header);
+
+            headerDashboard.Header.HeaderChanged += Header_HeaderChanged;
         }
+    }
 
-        private void Header_HeaderChanged(DashboardHeader header)
+    private void RefreshHeader(DashboardHeader header)
+    {
+        if (HeaderItems.Children.Count > 0)
+        {
+            HeaderItems.Children.Clear();
+        }
+        if (HeaderItemsRight.Children.Count > 0)
         {
-            RefreshHeader(header);
+            HeaderItemsRight.Children.Clear();
         }
 
-        public static DashboardContainer Create<TWidget, TGroup, TProperties>(DFLayoutElement<TProperties> element, string name)
-            where TWidget : FrameworkElement, IDashboardWidget<TGroup, TProperties>, new()
-            where TGroup : DashboardWidgetGroup
-            where TProperties : IConfigurationSettings, IDashboardProperties, new()
+        foreach (var item in header.GetLeftElements())
         {
-            var result = new TWidget();
+            HeaderItems.Children.Add(item);
+        }
+        foreach (var item in header.GetRightElements())
+        {
+            HeaderItemsRight.Children.Add(item);
+        }
+    }
 
-            element.Properties ??= new TProperties();
-            result.Properties = element.Properties;
+    private void Header_HeaderChanged(DashboardHeader header)
+    {
+        RefreshHeader(header);
+    }
+
+    public static DashboardContainer Create<TWidget, TProperties>(DFLayoutElement<TProperties> element, string name)
+        where TWidget : FrameworkElement, IDashboardWidget<TProperties>, new()
+        where TProperties : IConfigurationSettings, IDashboardProperties, new()
+    {
+        var result = new TWidget();
 
-            result.Setup();
+        element.Properties ??= new TProperties();
+        result.Properties = element.Properties;
 
-            var container = new DashboardContainer(result, name);
+        result.Setup();
 
-            container.ContentControl.Content = result;
-            container._panel = result;
+        var container = new DashboardContainer(result, name);
+
+        container.ContentControl.Content = result;
+        container._panel = result;
+
+        return container;
+    }
 
-            return container;
+    private void Settings_Click(object sender, RoutedEventArgs e)
+    {
+        var menu = new ContextMenu();
+
+        if (Panel is IActionsDashboard actions)
+        {
+            actions.BuildActionsMenu(menu);
         }
 
-        private void Settings_Click(object sender, RoutedEventArgs e)
+        if (menu.Items[^1] is Separator separator)
         {
-            var menu = new ContextMenu();
-
-            if (Panel is IActionsDashboard actions)
-            {
-                actions.BuildActionsMenu(menu);
-            }
-
-            if (menu.Items[^1] is Separator separator)
-            {
-                menu.Items.Remove(separator);
-            }
-            if (menu.Items.Count == 0)
-            {
-                menu.AddItem("No Actions", null, null, false);
-            }
-
-            menu.IsOpen = true;
+            menu.Items.Remove(separator);
         }
+        if (menu.Items.Count == 0)
+        {
+            menu.AddItem("No Actions", null, null, false);
+        }
+
+        menu.IsOpen = true;
     }
 }

+ 6 - 2
prs.desktop/Dashboards/IDashboardWidget.cs

@@ -8,8 +8,7 @@ namespace PRSDesktop
 {
     public interface IDashboardProperties { }
 
-    public interface IDashboardWidget<TGroup, TProperties> : ICorePanel
-        where TGroup : DashboardWidgetGroup
+    public interface IDashboardWidget<TProperties> : ICorePanel
         where TProperties : IConfigurationSettings, IDashboardProperties
     {
         TProperties Properties { get; set; }
@@ -18,6 +17,11 @@ namespace PRSDesktop
         
         event SaveSettings<TProperties> SaveSettings;
     }
+    public interface IDashboardWidget<TGroup, TProperties> : IDashboardWidget<TProperties>
+        where TGroup : DashboardWidgetGroup
+        where TProperties : IConfigurationSettings, IDashboardProperties
+    {
+    }
 
     public interface IActionsDashboard
     {

+ 133 - 19
prs.desktop/Dashboards/UtilityDashboard.xaml.cs

@@ -15,6 +15,9 @@ using InABox.Wpf;
 using InABox.WPF.Themes;
 using Microsoft.Xaml.Behaviors.Core;
 using Syncfusion.Windows.Tools.Controls;
+using PRSDesktop.Dashboards;
+using InABox.Wpf.Dashboard.Editor;
+using InABox.Wpf.Dashboard;
 
 namespace PRSDesktop
 {
@@ -32,13 +35,24 @@ namespace PRSDesktop
         public string Layout { get; set; }
     }
 
+    public class CustomDashboard : BaseObject
+    {
+        public string Name { get; set; }
+
+        [NullEditor]
+        public string Layout { get; set; }
+    }
+
     public class GlobalUtilityDashboardSettings : IGlobalConfigurationSettings
     {
         public List<DashboardFavourite> Favourites { get; set; }
 
+        public List<CustomDashboard> CustomDashboards { get; set; }
+
         public GlobalUtilityDashboardSettings()
         {
             Favourites = new();
+            CustomDashboards = new();
         }
     }
 
@@ -486,19 +500,26 @@ namespace PRSDesktop
             }
         }
 
-        private FrameworkElement CreateElement<TWidget, TGroup, TProperties>(DynamicFormDesignGrid grid, DFLayoutElement<TProperties> element)
-            where TWidget : FrameworkElement, IDashboardWidget<TGroup, TProperties>, new()
-            where TGroup : DashboardWidgetGroup
+        private FrameworkElement CreateElement<TWidget, TProperties>(DynamicFormDesignGrid grid, DFLayoutElement<TProperties> element)
+            where TWidget : FrameworkElement, IDashboardWidget<TProperties>, new()
             where TProperties : IConfigurationSettings, IDashboardProperties, new()
         {
             if (!_panels.ContainsKey(grid))
                 _panels[grid] = new List<ICorePanel>();
 
-            var dashboardName = GetDashboardElements()
-                .Where(x => x.DashboardElement == element.GetType())
-                .FirstOrDefault()?.WidgetCaption ?? "Unknown Dashboard";
+            string dashboardName;
+            if(element is CustomDashboardElement custom)
+            {
+                dashboardName = custom.Properties.DashboardName;
+            }
+            else
+            {
+                dashboardName = GetDashboardElements()
+                    .Where(x => x.DashboardElement == element.GetType())
+                    .FirstOrDefault()?.WidgetCaption ?? "Unknown Dashboard";
+            }
 
-            var container = DashboardContainer.Create<TWidget, TGroup, TProperties>(element, dashboardName);
+            var container = DashboardContainer.Create<TWidget, TProperties>(element, dashboardName);
 
             _panels[grid].Add(container.Panel);
 
@@ -507,21 +528,27 @@ namespace PRSDesktop
 
         private FrameworkElement OnCreateElement(object sender, DynamicFormCreateElementArgs e)
         {
-            var widgetType = GetVisibleDashboardElements().Where(x => x.DashboardElement == e.Element.GetType()).FirstOrDefault();
-            if(widgetType == null)
+            var method = typeof(UtilityDashboard).GetMethod(nameof(CreateElement), BindingFlags.Instance | BindingFlags.NonPublic)!;
+            if(e.Element is CustomDashboardElement custom)
             {
-                var border = new Border
+                method = method.MakeGenericMethod(typeof(CustomDashboardWidget), typeof(CustomDashboardProperties));
+            }
+            else
+            {
+                var widgetType = GetVisibleDashboardElements().Where(x => x.DashboardElement == e.Element.GetType()).FirstOrDefault();
+                if(widgetType == null)
                 {
-                    BorderBrush = new SolidColorBrush(Colors.Gray),
-                    BorderThickness = new Thickness(0.0),
-                    Margin = new Thickness(0.0),
-                    Background = ThemeManager.WorkspaceBackgroundBrush //new SolidColorBrush(Colors.Silver);
-                };
-                return border;
+                    var border = new Border
+                    {
+                        BorderBrush = new SolidColorBrush(Colors.Gray),
+                        BorderThickness = new Thickness(0.0),
+                        Margin = new Thickness(0.0),
+                        Background = ThemeManager.WorkspaceBackgroundBrush //new SolidColorBrush(Colors.Silver);
+                    };
+                    return border;
+                }
+                method = method.MakeGenericMethod(widgetType.Widget, widgetType.Properties);
             }
-            var method = typeof(UtilityDashboard)
-                .GetMethod(nameof(CreateElement), BindingFlags.Instance | BindingFlags.NonPublic)!
-                .MakeGenericMethod(widgetType.Widget, widgetType.Group, widgetType.Properties);
             return (method.Invoke(this, new object[] { sender, e.Element }) as FrameworkElement)!;
         }
         
@@ -557,6 +584,7 @@ namespace PRSDesktop
                     }
                 }
             }
+
             return _dashboardElements;
         }
 
@@ -584,6 +612,14 @@ namespace PRSDesktop
                 grid.AddElement(widget.DashboardElement, widget.WidgetCaption, widget.GroupCaption, true);
             }
 
+            var customDashboards = new GlobalConfiguration<GlobalUtilityDashboardSettings>().Load().CustomDashboards;
+            grid.AddElement(typeof(CustomDashboardElement), "Custom", "Custom", visible: false);
+            foreach(var customDashboard in customDashboards)
+            {
+                grid.AddElementAction(customDashboard.Name, null, "Custom", customDashboard, AddCustom_Click);
+            }
+            grid.AddElementAction<object?>("Create New", InABox.Wpf.Resources.add, "Custom", null, CreateNewCustom_Click);
+
             grid.ShowBorders = false;
             grid.OnCreateElement += OnCreateElement;
             grid.OnAfterDesign += OnAfterDesign;
@@ -608,6 +644,84 @@ namespace PRSDesktop
             return border;
         }
 
+        private DFLayoutElement? AddCustom_Click(CustomDashboard dashboard)
+        {
+            var element = new CustomDashboardElement();
+            element.Properties = new CustomDashboardProperties
+            {
+                DashboardName = dashboard.Name
+            };
+            return element;
+        }
+
+        private DFLayoutElement? CreateNewCustom_Click(object? tag)
+        {
+            var grid = new Grid();
+            grid.AddRow(GridUnitType.Auto);
+            grid.AddRow(GridUnitType.Auto);
+            grid.AddRow(GridUnitType.Star);
+
+            grid.AddColumn(GridUnitType.Auto);
+            grid.AddColumn(GridUnitType.Star);
+
+            grid.AddChild(new Label
+            {
+                Content = "Dashboard Name:",
+                VerticalAlignment = VerticalAlignment.Center
+            }, 0, 0);
+            var textBox = new TextBox
+            {
+                Background = Colors.LightYellow.ToBrush(),
+                Padding = new Thickness(5),
+                VerticalContentAlignment = VerticalAlignment.Center
+            };
+            grid.AddChild(textBox, 0, 1);
+
+            grid.AddChild(new Separator
+            {
+                Margin = new(5)
+            }, 1, 0, colSpan: 2);
+
+            var dashboard = new DynamicDashboard();
+
+            var editor = new DynamicDashboardEditor(dashboard);
+            grid.AddChild(editor, 2, 0, colSpan: 2);
+
+            var dlg = new DynamicContentDialog(grid)
+            {
+                Title = "Create new dashboard",
+                SizeToContent = SizeToContent.Height
+            };
+
+            textBox.TextChanged += (o, e) =>
+            {
+                dlg.CanSave = !textBox.Text.IsNullOrWhiteSpace();
+            };
+
+            if(dlg.ShowDialog() == true)
+            {
+                var config = new GlobalConfiguration<GlobalUtilityDashboardSettings>();
+                var settings = config.Load();
+                settings.CustomDashboards.Add(new CustomDashboard
+                {
+                    Layout = DynamicDashboardUtils.Serialize(editor.GetDashboard()),
+                    Name = textBox.Text
+                });
+                config.Save(settings);
+
+                var element = new CustomDashboardElement();
+                element.Properties = new CustomDashboardProperties
+                {
+                    DashboardName = textBox.Text
+                };
+                return element;
+            }
+            else
+            {
+                return null;
+            }
+        }
+
         private void OnAfterRender(DynamicFormDesignGrid sender)
         {
             if (!sender.IsDesigning)

+ 1 - 1
prs.desktop/Panels/Invoices/InvoiceGrid.cs

@@ -92,7 +92,7 @@ namespace PRSDesktop
         {
             if(Master == null || Master.ID == Guid.Empty || Master.JobType == JobType.Service)
             {
-                base.DoAdd(OpenEditorOnDirectEdit);
+                base.DoAdd(openEditorOnDirectEdit);
             }
             else
             {

+ 1 - 1
prs.desktop/Panels/Jobs/JobScopes/JobScopeDocumentGrid.cs

@@ -32,7 +32,7 @@ public class JobScopeDocumentGrid : DynamicDocumentGrid<JobScopeDocument, JobSco
             MessageWindow.ShowMessage("Please select a scope.", "No scope selected.");
             return;
         }
-        base.DoAdd(OpenEditorOnDirectEdit);
+        base.DoAdd(openEditorOnDirectEdit);
     }
 
     public override void SaveItem(JobScopeDocument item)

+ 1 - 1
prs.desktop/Panels/Staging/Manufacturing/StagingMaterialListGrid.cs

@@ -48,7 +48,7 @@ namespace PRSDesktop
                     Refresh(false, true);
                 }
             }
-            base.DoAdd(OpenEditorOnDirectEdit);
+            base.DoAdd(openEditorOnDirectEdit);
         }
 
         private Document ReadFile(string file)

+ 1 - 1
prs.desktop/Panels/Suppliers/Bills/SupplierBillPanel.xaml.cs

@@ -511,7 +511,7 @@ public partial class SupplierBillPanel : UserControl, IPanel<Bill>, IPropertiesP
         ReloadBills(force: true);
     }
 
-    private void Bills_OnCustomiseEditor(IDynamicEditorForm sender, Bill items, DynamicGridColumn column, BaseEditor editor)
+    private void Bills_OnCustomiseEditor(IDynamicEditorForm sender, Bill[] items, DynamicGridColumn column, BaseEditor editor)
     {
         if(column.ColumnName == "Approved" || ApprovalSetID.IsEqualTo(column.ColumnName))
         {