Przeglądaj źródła

avalonia: finished site forms

Kenric Nugteren 1 miesiąc temu
rodzic
commit
83d43dc8b3

+ 9 - 17
PRS.Avalonia/PRS.Avalonia/Components/FormsEditor/DigitalFormViewer.axaml.cs

@@ -19,8 +19,6 @@ public partial class DigitalFormViewer : UserControl, IDFRenderer
         AvaloniaProperty.Register<DigitalFormViewer, IDigitalFormInstance>(nameof(Form));
     public static readonly StyledProperty<Entity> EntityProperty =
         AvaloniaProperty.Register<DigitalFormViewer, Entity>(nameof(Entity));
-    public static readonly StyledProperty<DFLayout> LayoutProperty =
-        AvaloniaProperty.Register<DigitalFormViewer, DFLayout>(nameof(Layout));
     public static readonly StyledProperty<bool> ReadOnlyProperty =
         AvaloniaProperty.Register<DigitalFormViewer, bool>(nameof(ReadOnly));
 
@@ -34,14 +32,21 @@ public partial class DigitalFormViewer : UserControl, IDFRenderer
         get => GetValue(EntityProperty);
     }
 
+    private DFLayout _layout;
     public DFLayout Layout
     {
-        get => GetValue(LayoutProperty);
+        get => _layout;
+        set
+        {
+            _layout = value;
+            Layout.Renderer = this;
+            Initialise();
+        }
     }
 
     public bool ReadOnly { get; set; }
 
-    private DFLoadStorage RetainedData { get; set; }
+    private DFLoadStorage RetainedData { get; set; } = new();
 
     private bool _changing;
     private bool _isChanged;
@@ -54,19 +59,6 @@ public partial class DigitalFormViewer : UserControl, IDFRenderer
 
     private static double RowMinHeight = 35;
 
-    static DigitalFormViewer()
-    {
-        LayoutProperty.Changed.AddClassHandler<DigitalFormViewer>(Layout_Changed);
-    }
-
-    private static void Layout_Changed(DigitalFormViewer viewer, AvaloniaPropertyChangedEventArgs args)
-    {
-        if (viewer.Layout is null) return;
-
-        viewer.Layout.Renderer = viewer;
-        viewer.Initialise();
-    }
-
     public DigitalFormViewer()
     {
         InitializeComponent();

+ 1 - 1
PRS.Avalonia/PRS.Avalonia/Components/FormsEditor/DigitalFormsHostView.axaml

@@ -9,7 +9,7 @@
 	<TabControl TabStripPlacement="Bottom">
 		<TabItem Header="Form">
 			<ScrollViewer>
-				<forms:DigitalFormViewer Layout="{Binding Layout}"
+				<forms:DigitalFormViewer Name="Viewer"
 										 Entity="{Binding Parent}"
 										 Form="{Binding Form}"/>
 			</ScrollViewer>

+ 34 - 0
PRS.Avalonia/PRS.Avalonia/Components/FormsEditor/DigitalFormsHostView.axaml.cs

@@ -1,6 +1,11 @@
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
+using Avalonia.Threading;
+using CommunityToolkit.Mvvm.ComponentModel;
+using DynamicData.Binding;
+using ReactiveUI;
+using System;
 
 namespace PRS.Avalonia.DigitalForms;
 
@@ -10,4 +15,33 @@ public partial class DigitalFormsHostView : UserControl
     {
         InitializeComponent();
     }
+
+    protected override void OnDataContextChanged(EventArgs e)
+    {
+        base.OnDataContextChanged(e);
+
+        var model = DataContext as IDigitalFormsHostViewModel;
+        if (model is null) return;
+
+        model.WhenAnyValue(x => x.Layout).Subscribe(value =>
+        {
+            if(value is not null)
+            {
+                Viewer.Layout = value;
+            }
+        });
+        model.SaveValues = Viewer.SaveValues;
+        model.LoadValues = Viewer.LoadValues;
+        model.Validate = () =>
+        {
+            if (Viewer.Validate(out var errors))
+            {
+                return null;
+            }
+            else
+            {
+                return errors;
+            }
+        };
+    }
 }

+ 116 - 4
PRS.Avalonia/PRS.Avalonia/Components/FormsEditor/DigitalFormsHostViewModel.cs

@@ -1,11 +1,17 @@
-using Comal.Classes;
+using Avalonia.Data;
+using Avalonia.Media;
+using Avalonia.Threading;
+using Comal.Classes;
 using CommunityToolkit.Mvvm.ComponentModel;
 using InABox.Avalonia;
+using InABox.Avalonia.Components;
 using InABox.Clients;
 using InABox.Core;
+using PRS.Avalonia.Dialogs;
 using PRS.Avalonia.Modules;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -34,13 +40,19 @@ public class DigitalFormCacheModel<TParent, TParentLink, TForm> : ISerializeBina
     }
 }
 
-public interface IDigitalFormsHostViewModel
+public interface IDigitalFormsHostViewModel : INotifyPropertyChanged
 {
     Entity Parent { get; }
 
     IDigitalFormInstance Form { get; }
 
     DFLayout Layout { get; }
+
+    Action<DFLoadStorage> LoadValues { set; }
+
+    Func<DFSaveStorage> SaveValues { set; }
+
+    Func<List<string>?> Validate { set; }
 }
 
 public partial class DigitalFormsHostViewModel<TModel, TShell, TParent, TParentLink, TForm> : ModuleViewModel, IDigitalFormsHostViewModel
@@ -58,6 +70,9 @@ public partial class DigitalFormsHostViewModel<TModel, TShell, TParent, TParentL
     [ObservableProperty]
     private Guid _instanceID;
 
+    [ObservableProperty]
+    private DigitalForm _digitalForm;
+
     [ObservableProperty]
     private DigitalFormLayout _digitalFormLayout;
 
@@ -85,6 +100,18 @@ public partial class DigitalFormsHostViewModel<TModel, TShell, TParent, TParentL
     [ObservableProperty]
     private DFLayout _layout;
 
+    [ObservableProperty]
+    private DateTime _timeStarted = DateTime.MinValue;
+
+    [ObservableProperty]
+    private Action<DFLoadStorage> _loadValues = null!;
+
+    [ObservableProperty]
+    private Func<DFSaveStorage> _saveValues = null!;
+
+    [ObservableProperty]
+    private Func<List<string>?> _validate = null!;
+
     public event Action? OnSaved;
 
     private string CacheFileName => $"{typeof(TForm)}.{InstanceID}";
@@ -122,6 +149,7 @@ public partial class DigitalFormsHostViewModel<TModel, TShell, TParent, TParentL
     {
         await Model.RefreshAsync(DataAccess.Status == ConnectionStatus.Connected);
 
+        DigitalForm = Model.FirstOrDefault(x => x.ID == FormID)!.Entity;
         Variables = Model.Variables.Where(x => x.FormID == FormID).Select(x => x.Entity).ToArray();
         DigitalFormLayout = Model.Layouts.Where(x => x.FormID == FormID).First().Entity;
 
@@ -145,6 +173,7 @@ public partial class DigitalFormsHostViewModel<TModel, TShell, TParent, TParentL
                         x => x.FormCompleted,
                         x => x.FormCompletedBy.ID,
                         x => x.Created,
+                        x => x.FormStarted,
                         x => x.FormOpen,
                         x => x.BlobData))
                 .ToObjects<TForm>()
@@ -167,18 +196,101 @@ public partial class DigitalFormsHostViewModel<TModel, TShell, TParent, TParentL
         var layout = new DFLayout();
         layout.LoadLayout(DigitalFormLayout.Layout);
         layout.LoadVariables(Variables);
-        Layout = layout;
+
+
+        Dispatcher.UIThread.Invoke(() =>
+        {
+            Layout = layout;
+            LoadValues(DigitalForm.DeserializeFormData(Form) ?? new());
+        });
 
         NewForm = Form.FormData.IsNullOrWhiteSpace();
         ReadOnly = Form.FormCompleted != DateTime.MinValue;
 
+        TimeStarted = DateTime.Now;
+
         ProgressVisible = false;
         return TimeSpan.Zero;
     }
 
     private async Task<bool> SaveForm()
     {
-        throw new NotImplementedException();
+        string[] options;
+        if(DigitalForm.AppliesTo.Equals(nameof(Kanban)) || DigitalForm.AppliesTo.Equals(nameof(Job)))
+        {
+            options = ["Save Progress", "Complete Form", "Complete and Duplicate", "Delete Form"];
+        }
+        else
+        {
+            options = ["Save Progress", "Complete Form", "Delete Form"];
+        }
+        var chosenOption = await OptionSelector.Execute("Select Option", options);
+        if(chosenOption is null)
+        {
+            return true;
+        }
+
+        var completeDuplicate = chosenOption == "Complete and Duplicate";
+        var complete = chosenOption == "Complete Form" || completeDuplicate;
+        var delete = chosenOption == "Delete Form";
+
+        if(complete)
+        {
+            var errors = Dispatcher.UIThread.Invoke(Validate);
+            if(errors is not null)
+            {
+                await MessageDialog.ShowMessage($"Could not save form:\n - {string.Join("\n - ", errors)}\nPlease address these issues and try again.");
+                return true;
+            }
+        }
+
+        ProgressVisible = true;
+        DigitalForm.SerializeFormData(Form, Variables, SaveValues());
+
+        await Task.Run(() =>
+        {
+            if(Form.FormStarted == DateTime.MinValue)
+            {
+                Form.FormStarted = TimeStarted;
+            }
+            Form.FormOpen += (DateTime.Now - TimeStarted);
+
+            if(delete)
+            {
+                Form.FormCancelled = DateTime.Now;
+            }
+            else if (complete)
+            {
+                Form.FormCompleted = DateTime.Now;
+                Form.FormCompletedBy.ID = ClientFactory.UserGuid;
+                Form.FormCompletedBy.UserID = ClientFactory.UserID;
+                if(App.GPS is not null)
+                {
+                    Form.Location.Longitude = App.GPS.Longitude;
+                    Form.Location.Latitude = App.GPS.Latitude;
+                    Form.Location.Timestamp = Form.FormCompleted;
+                }
+            }
+
+            DigitalFormDataModel.Update(Form, Parent);
+        });
+        ProgressVisible = false;
+
+        if (completeDuplicate)
+        {
+            var formid = Form.Form.ID;
+            Form = new TForm();
+            Form.Parent.ID = Parent.ID;
+            Form.Form.ID = formid;
+            Dispatcher.UIThread.Invoke(() => LoadValues(new()));
+        }
+        else
+        {
+            Navigation.Back();
+            OnSaved?.Invoke();
+        }
+
+        return true;
     }
 
     public static void EditForm(TModel model, TShell shell, TParent parent)

+ 15 - 7
PRS.Avalonia/PRS.Avalonia/Components/FormsList/FormsList.axaml.cs

@@ -6,6 +6,7 @@ using Avalonia.Media;
 using Avalonia.Threading;
 using CommunityToolkit.Mvvm.ComponentModel;
 using CommunityToolkit.Mvvm.Input;
+using DynamicData.Binding;
 using InABox.Avalonia;
 using InABox.Avalonia.Components;
 using InABox.Avalonia.Converters;
@@ -74,8 +75,8 @@ public delegate bool FormsListSearchEvent(object sender, FormsListSearchEventArg
 
 public partial class FormsList : UserControl
 {
-    public static readonly StyledProperty<bool> SeparateHistoryProperty =
-        AvaloniaProperty.Register<FormsList, bool>(nameof(SeparateHistory), true);
+    // public static readonly StyledProperty<bool> SeparateHistoryProperty =
+    //     AvaloniaProperty.Register<FormsList, bool>("SeparateHistory", true);
     public static readonly StyledProperty<string> AppliesToProperty =
         AvaloniaProperty.Register<FormsList, string>(nameof(AppliesTo), "");
     public static readonly StyledProperty<ICoreRepository?> ModelProperty =
@@ -83,11 +84,8 @@ public partial class FormsList : UserControl
     public static readonly StyledProperty<ICommand?> FormClickedProperty =
         AvaloniaProperty.Register<FormsList, ICommand?>(nameof(FormClicked));
 
-    public bool SeparateHistory
-    {
-        get => GetValue(SeparateHistoryProperty);
-        set => SetValue(SeparateHistoryProperty, value);
-    }
+    public bool SeparateHistory { get; set; } = true;
+
     public string AppliesTo
     {
         get => GetValue(AppliesToProperty);
@@ -120,6 +118,16 @@ public partial class FormsList : UserControl
         InitializeComponent();
     }
 
+    // static FormsList()
+    // {
+    //     SeparateHistoryProperty.Changed.AddClassHandler<FormsList>(SeparateHistory_Changed);
+    // }
+
+    // private static void SeparateHistory_Changed(FormsList list, AvaloniaPropertyChangedEventArgs args)
+    // {
+    //     list._separateHistory = list.GetValue(SeparateHistoryProperty);
+    // }
+
     private bool FilterShell(IShell shell)
     {
         if (shell is not IDigitalFormInstanceShell formShell) return false;

+ 10 - 1
PRS.Avalonia/PRS.Avalonia/Modules/Site/SiteItps/SiteITPFormsView.axaml.cs

@@ -2,19 +2,28 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using PRS.Avalonia.Components;
+using System;
 
 namespace PRS.Avalonia.Modules;
 
 public partial class SiteITPFormsView : UserControl
 {
+    private SiteITPFormsViewModel Model = null!;
+
     public SiteITPFormsView()
     {
         InitializeComponent();
     }
 
+    protected override void OnDataContextChanged(EventArgs e)
+    {
+        base.OnDataContextChanged(e);
+        Model = (DataContext as SiteITPFormsViewModel)!;
+    }
+
     private bool FormsList_Search(object sender, FormsListSearchEventArgs args)
     {
         if (args.Shell is not JobITPFormShell shell) return false;
-        return (DataContext as SiteITPFormsViewModel)?.Search(shell) ?? false;
+        return Model.Search(shell);
     }
 }