Parcourir la source

Merge branch 'master' into frank

Frank vandenBos il y a 3 semaines
Parent
commit
11b4edbf6b

+ 185 - 0
PRS.Avalonia/PRS.Avalonia/Components/FormsEditor/Fields/DFSignaturePadControl.cs

@@ -0,0 +1,185 @@
+using Avalonia.Controls;
+using Avalonia.Media;
+using InABox.Avalonia;
+using InABox.Avalonia.Components;
+using InABox.Avalonia.Converters;
+using InABox.Core;
+using Svg;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Topten.RichTextKit.Editor;
+
+namespace PRS.Avalonia.DigitalForms;
+
+internal class DFSignaturePadControl : DigitalFormFieldControl<DFLayoutSignaturePad, DFLayoutSignaturePadProperties, byte[], byte[]?>
+{
+    private Border Border = null!;
+
+    private Button Save = null!;
+    private Button Apply = null!;
+    private Button Clear = null!;
+
+    private InkCanvas Canvas = null!;
+    private Image Image = null!;
+
+    private byte[]? _value = null;
+
+    private bool _enabled = true;
+    private bool _changed = false;
+
+    private IBrush? _backgroundColor = new SolidColorBrush(Colors.LightYellow);
+    private IBrush? _disabledBackground = new SolidColorBrush(Colors.LightGray);
+
+    protected override Control Create()
+    {
+        Border = new Border();
+        Border.Classes.Add("Standard");
+
+        var grid = new Grid();
+        grid.AddColumn(GridUnitType.Star);
+        grid.AddColumn(GridUnitType.Star);
+        grid.AddColumn(GridUnitType.Star);
+
+        grid.AddRow(GridUnitType.Star);
+        grid.AddRow(GridUnitType.Auto);
+
+        Canvas = new InkCanvas();
+        Image = new Image
+        {
+            IsVisible = false
+        };
+
+        Save = CreateButton("Save New", SaveSignature, 0, false);
+        Apply = CreateButton("Apply Saved", ApplySignature, 1, ViewModelBase.Repositories.Me.Signature?.Any() == true);
+        Clear = CreateButton("Clear", ClearSignature, 2, false);
+
+        Canvas.LineColour = Colors.Blue;
+
+        Canvas.Changed += (o, e) =>
+        {
+            _changed = true;
+            UpdateStatus();
+            ChangeField();
+        };
+
+        grid.Children.Add(Save);
+        grid.Children.Add(Apply);
+        grid.Children.Add(Clear);
+        grid.AddChild(Canvas, 0, 0, rowSpan: 1, colSpan: 3);
+        grid.AddChild(Image, 0, 0, rowSpan: 1, colSpan: 3);
+
+        Border.Child = grid;
+
+        return Border;
+    }
+
+    private void ClearSignature()
+    {
+        Canvas.Clear();
+        _value = null;
+        UpdateValue();
+        ChangeField();
+    }
+
+    private void ApplySignature()
+    {
+        _value = ViewModelBase.Repositories.Me.Signature;
+        UpdateValue();
+        ChangeField();
+    }
+
+    private void SaveSignature()
+    {
+        var data = Canvas.SaveImage();
+        _value = data;
+        ViewModelBase.Repositories.Me.Signature = data;
+        ViewModelBase.Repositories.Me.Save("Updated Signature");
+
+        UpdateValue();
+        ChangeField();
+    }
+
+    private void UpdateValue()
+    {
+        if(_value?.Any() == true)
+        {
+            Image.Source = ByteArrayToImageSourceConverter.Instance.Convert(_value);
+        }
+        else
+        {
+            Canvas.Clear();
+        }
+        _changed = false;
+
+        UpdateStatus();
+    }
+
+    private void UpdateStatus()
+    {
+        Canvas.IsVisible = _value?.Any() != true && _enabled;
+        Image.IsVisible = !Canvas.IsVisible;
+
+        Save.IsVisible = _enabled;
+        Apply.IsVisible = _enabled;
+        Clear.IsVisible = _enabled;
+
+        Save.IsEnabled = _enabled && _changed;
+        Apply.IsEnabled = _enabled && ViewModelBase.Repositories.Me.Signature?.Any() == true;
+        Clear.IsEnabled = _enabled && (_value?.Any() == true || _changed);
+        Border.Background = Canvas.IsVisible ? _backgroundColor : _disabledBackground;
+    }
+
+    private Button CreateButton(string text, Action clicked, int column, bool enabled)
+    {
+        var button = new Button();
+        button.Content = text;
+        button.Classes.Add("Standard");
+        Grid.SetRow(button, 1);
+        Grid.SetColumn(button, column);
+        button.Click += (o, e) => clicked();
+        button.IsEnabled = enabled;
+        return button;
+    }
+
+    public override byte[] GetValue()
+    {
+        return GetSerializedValue() ?? [];
+    }
+
+    public override byte[]? GetSerializedValue()
+    {
+        return Canvas.SaveImage();
+    }
+
+    public override void SetBackground(IBrush brush, bool defaultColour)
+    {
+        _backgroundColor = brush;
+        Border.Background = Canvas.IsVisible ? _backgroundColor : _disabledBackground;
+    }
+
+    public override void SetEnabled(bool enabled)
+    {
+        _enabled = enabled;
+        IsEnabled = _enabled;
+        UpdateStatus();
+    }
+
+    public override void SetSerializedValue(byte[]? value)
+    {
+        SetValue(value);
+    }
+
+    public override void SetValue(byte[]? value)
+    {
+        _value = value;
+        UpdateValue();
+    }
+
+    protected override bool IsEmpty()
+    {
+        return _value is not null;
+    }
+}

+ 1 - 1
PRS.Avalonia/PRS.Avalonia/Components/FormsList/FormsList.axaml.cs

@@ -231,7 +231,7 @@ public partial class FormsList : UserControl
         }, args =>
         {
             formModel.SelectFilter(args.Filter);
-            formModel.Search(x => x.AppliesTo == typeof(TForm).Name);
+            formModel.Search(x => x.AppliesTo == typeof(TParent).Name);
             return formModel.Refresh(args.Force);
         }))?.FirstOrDefault();
     }

+ 4 - 1
PRS.Avalonia/PRS.Avalonia/Modules/DocumentScanner/DocumentScannerEditorView.axaml

@@ -3,6 +3,7 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:modules="using:PRS.Avalonia.Modules"
+			 xmlns:components="using:InABox.Avalonia.Components"
 			 xmlns:converters="using:InABox.Avalonia.Converters"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              x:Class="PRS.Avalonia.Modules.DocumentScannerEditorView"
@@ -12,7 +13,9 @@
 			<RowDefinition Height="*"/>
 			<RowDefinition Height="150"/>
 		</Grid.RowDefinitions>
-		<Image Grid.Row="0" Source="{Binding Document.Data,Converter={x:Static converters:ByteArrayToImageSourceConverter.Instance}}"/>
+		<components:ImageEditor Name="Editor" Grid.Row="0"
+								Source="{Binding Document.Data,Converter={x:Static converters:ByteArrayToImageSourceConverter.Instance}}"
+								Changed="ImageEditor_Changed"/>
 		<TextBox Grid.Row="1"
 				 Watermark="Enter Note Here"
 				 Text="{Binding DataEntryDocument.Note}"

+ 29 - 0
PRS.Avalonia/PRS.Avalonia/Modules/DocumentScanner/DocumentScannerEditorView.axaml.cs

@@ -1,13 +1,42 @@
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
+using System;
+using System.IO;
+using System.Threading.Tasks;
 
 namespace PRS.Avalonia.Modules;
 
 public partial class DocumentScannerEditorView : UserControl
 {
+    private DocumentScannerEditorViewModel? ViewModel;
+
     public DocumentScannerEditorView()
     {
         InitializeComponent();
     }
+
+    protected override void OnDataContextChanged(EventArgs e)
+    {
+        base.OnDataContextChanged(e);
+
+        ViewModel = DataContext as DocumentScannerEditorViewModel;
+        if(ViewModel != null)
+        {
+            ViewModel.GetImage = GetImage;
+        }
+    }
+
+    private byte[] GetImage()
+    {
+        return Editor.SaveImage();
+    }
+
+    private void ImageEditor_Changed(object? sender, EventArgs e)
+    {
+        if(ViewModel != null)
+        {
+            ViewModel.ImageChanged = true;
+        }
+    }
 }

+ 18 - 0
PRS.Avalonia/PRS.Avalonia/Modules/DocumentScanner/DocumentScannerEditorViewModel.cs

@@ -1,6 +1,7 @@
 using CommunityToolkit.Mvvm.ComponentModel;
 using InABox.Avalonia;
 using InABox.Avalonia.Components;
+using InABox.Avalonia.Platform;
 using InABox.Core;
 using System;
 using System.Collections.Generic;
@@ -23,6 +24,12 @@ internal partial class DocumentScannerEditorViewModel : ModuleViewModel
     [ObservableProperty]
     private DocumentModel? _documentModel;
 
+    [ObservableProperty]
+    private Func<byte[]>? _getImage;
+
+    [ObservableProperty]
+    private bool _imageChanged;
+
     public DocumentScannerEditorViewModel()
     {
         PrimaryMenu.Add(new AvaloniaMenuItem(Images.save, Save));
@@ -55,6 +62,17 @@ internal partial class DocumentScannerEditorViewModel : ModuleViewModel
     {
         ProgressVisible = true;
 
+        if (ImageChanged && GetImage is not null && Document is not null)
+        {
+            var imgData = GetImage();
+            Document.Data = imgData;
+            Document.CRC = CoreUtils.CalculateCRC(imgData);
+            Document.TimeStamp = DateTime.Now;
+            await Document.SaveAsync("Updated from Mobile Device");
+
+            DataEntryDocument.Thumbnail = PlatformTools.ImageTools.CreateThumbnail(imgData, 256, 256);
+        }
+
         await DataEntryDocument.SaveAsync("Updated from Mobile Device");
 
         ProgressVisible = false;

+ 1 - 1
PRS.Avalonia/PRS.Avalonia/Modules/MyHR/MyHRDetails/MyHRDetailsViewModel.cs

@@ -10,7 +10,7 @@ public partial class MyHRDetailsViewModel : ModuleViewModel
 {
     [ObservableProperty] private string? _email;
 
-    [ObservableProperty] private EmployeeShell? _me;
+    [ObservableProperty] private EmployeeDetailShell? _me;
 
     [ObservableProperty] private IImage? _signature;
 

+ 2 - 2
PRS.Avalonia/PRS.Avalonia/Repositories/EmployeeDetail/EmployeeDetailShell.cs

@@ -28,9 +28,9 @@ public class EmployeeDetailShell : Shell<EmployeeDetailModel, Employee>
         set => Set(value);
     }
 
-    public byte[] Signature
+    public byte[]? Signature
     {
-        get => Get<byte[]>();
+        get => Get<byte[]?>();
         set => Set(value);
     }
 

+ 4 - 4
PRS.Avalonia/PRS.Avalonia/RepositoryLayer.cs

@@ -25,7 +25,7 @@ public partial class RepositoryLayer : ObservableObject
     {
         Data = data;
 
-        MeModel = new EmployeeModel(Data,
+        MeModel = new EmployeeDetailModel(Data,
             () => new Filter<Employee>(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid));
 
         MyAlerts = new EmployeeAlertModel(Data,
@@ -44,9 +44,9 @@ public partial class RepositoryLayer : ObservableObject
         );
     }
 
-    public EmployeeModel MeModel { get; private set; }
+    public EmployeeDetailModel MeModel { get; private set; }
 
-    public EmployeeShell? MaybeMe
+    public EmployeeDetailShell? MaybeMe
     {
         get
         {
@@ -55,7 +55,7 @@ public partial class RepositoryLayer : ObservableObject
         }
     }
 
-    public EmployeeShell Me => MaybeMe ?? throw new Exception("Invalid employee");
+    public EmployeeDetailShell Me => MaybeMe ?? throw new Exception("Invalid employee");
 
     public EmployeeAlertModel MyAlerts { get; private set; }
 

+ 11 - 1
PRS.Avalonia/PRS.Avalonia/TestView.axaml

@@ -7,5 +7,15 @@
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              x:Class="PRS.Avalonia.TestView"
 			 x:DataType="prs:TestViewModel">
-	<components:ImageEditor Source="{SvgImage ../Images/cross.svg}"/>
+	<Grid>
+		<components:ImageEditor Name="Editor"
+								Source="{Binding Source}"
+								BorderBrush="Black"
+								BorderThickness="1"
+								CornerRadius="4"
+								IsVisible="{Binding IsEditing}"
+								ShowButtons="False"
+								Mode="Ellipse"/>
+		<Image Name="Image" Source="{Binding Source}" IsVisible="{Binding !IsEditing}"/>
+	</Grid>
 </UserControl>

+ 20 - 0
PRS.Avalonia/PRS.Avalonia/TestView.axaml.cs

@@ -1,6 +1,9 @@
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
+using Avalonia.Media.Imaging;
+using InABox.Avalonia.Components;
+using System;
 
 namespace PRS.Avalonia;
 
@@ -10,4 +13,21 @@ public partial class TestView : UserControl
     {
         InitializeComponent();
     }
+
+    protected override void OnDataContextChanged(EventArgs e)
+    {
+        base.OnDataContextChanged(e);
+
+        if(DataContext is TestViewModel model)
+        {
+            model.GetBitmap = GetBitmap;
+        }
+    }
+
+    private Bitmap? GetBitmap()
+    {
+        var image = Editor.GetImage();
+        Editor.Reset();
+        return image;
+    }
 }

+ 27 - 1
PRS.Avalonia/PRS.Avalonia/TestViewModel.cs

@@ -1,4 +1,7 @@
-using CommunityToolkit.Mvvm.Input;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
 using InABox.Avalonia;
 using PRS.Avalonia.Modules;
 using System;
@@ -13,4 +16,27 @@ public partial class TestViewModel : ModuleViewModel
 {
     public override string Title => "Test";
 
+    [ObservableProperty]
+    private IImage? _source = Images.cross;
+
+    [ObservableProperty]
+    private bool _isEditing = true;
+
+    [ObservableProperty]
+    private Func<Bitmap?>? _getBitmap;
+
+    public TestViewModel()
+    {
+        PrimaryMenu.Add(new(Images.tick, Toggle));
+    }
+
+    private Task<bool> Toggle()
+    {
+        if (IsEditing)
+        {
+            Source = GetBitmap?.Invoke();
+        }
+        IsEditing = !IsEditing;
+        return Task.FromResult(true);
+    }
 }