using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using InABox.Clients; using InABox.Core; using InABox.Wpf; using InABox.Wpf.Reports; using InABox.WPF; namespace InABox.DynamicGrid; public class DynamicFormEditButton : INotifyPropertyChanged { public delegate void DynamicFormEditButtonDelegate(DynamicFormEditWindow window, DynamicFormEditButton button); private object? _content; public object? Content { get => _content; set { _content = value; OnPropertyChanged(); } } public DynamicFormEditButtonDelegate Action { get; set; } public DynamicFormEditButton(object? content, DynamicFormEditButtonDelegate action) { Content = content; Action = action; } public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } /// /// Interaction logic for FormDesigner.xaml /// public partial class DynamicFormEditWindow : Window, IDynamicFormWindow { public delegate void CustomiseDynamicFormEditWindow(DynamicFormEditWindow window); public enum FormResult { None, Cancel, Save, Complete } public DynamicFormEditWindow() { InitializeComponent(); Grid.OnChanged += Grid_OnChanged; //Complete.IsEnabled = Security.IsAllowed(); } public DynamicFormDesignGrid Grid => Preview; public FormMode Mode { get => Grid.Mode; private set => Grid.Mode = value; } public FormResult Result = FormResult.None; public IDigitalFormDataModel? DataModel { get => Grid.DataModel; set { Grid.DataModel = value; RefreshEnabled(); } } public ObservableCollection CustomButtons { get; set; } = new ObservableCollection(); private bool IsReopening = false; private bool HasChanged = false; private bool HasUnsavedChanges => Grid.IsChanged || HasChanged || DataModel!.Instance.ID == Guid.Empty; private DFLayoutType _type; public DFLayoutType Type { get => _type; set { _type = value; Width = _type == DFLayoutType.Mobile ? 600 : 1000; Height = 800; } } public DFSaveStorage SaveValues() => Grid.SaveValues(); public void LoadValues(DFLoadStorage storage) { Grid.LoadValues(storage); RefreshEnabled(); } private void RefreshEnabled() { var formInstance = DataModel!.Instance; var completed = formInstance.FormCompleted; CompletedDate.Content = !completed.IsEmpty() ? $"Completed {completed:d MMM yyyy} at {completed:hh:mm tt}" : "Not completed yet"; if (completed.IsEmpty() && DFUtils.CanEditForm(formInstance.GetType(), formInstance, DataModel.Entity)) { Mode = FormMode.Filling; } else { Mode = Security.IsAllowed() ? FormMode.Editing : FormMode.ReadOnly; } if (Mode == FormMode.Editing || Mode == FormMode.Filling || Mode == FormMode.Preview) { SaveForm.IsEnabled = HasUnsavedChanges; if(Mode == FormMode.Editing) { SaveForm.Content = "Save Form"; } else { SaveForm.Content = "Save Progress"; } } else { if (Security.IsAllowed()) { SaveForm.IsEnabled = HasUnsavedChanges; SaveForm.Content = "Save Form"; } else { SaveForm.IsEnabled = false; SaveForm.Content = "Save Progress"; } } if (!completed.IsEmpty() && Security.IsAllowed()) { CompleteForm.Content = "Re-open form"; CompleteForm.IsEnabled = true; IsReopening = true; } else { CompleteForm.Content = "Complete form"; CompleteForm.IsEnabled = (Mode == FormMode.Filling || Security.IsAllowed()) && DataModel?.Instance.FormCompleted == DateTime.MinValue; IsReopening = false; } } private void Grid_OnChanged(DynamicFormDesignGrid sender, string fieldName) { if (Mode == FormMode.Editing || Mode == FormMode.Filling || Mode == FormMode.Preview) { SaveForm.IsEnabled = true; } } protected override void OnClosing(CancelEventArgs e) { if (DialogResult == null) { Result = FormResult.Cancel; DialogResult = false; } base.OnClosing(e); } private void Complete() { if (!Grid.Validate(out var messages)) { MessageBox.Show(string.Join('\n', messages)); return; } if (MessageBox.Show("Are you sure you want to complete this form?", "Confirm Completion", MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Result = FormResult.Complete; DialogResult = true; } } private void Reopen() { HasChanged = true; DataModel!.Instance.FormCompleted = DateTime.MinValue; DataModel!.Instance.FormCompletedBy.Clear(); RefreshEnabled(); } private void CompleteForm_Click(object sender, RoutedEventArgs e) { if (IsReopening) Reopen(); else Complete(); } private void Button_Click(object sender, RoutedEventArgs e) { if (sender is not Button btn || btn.Tag is not DynamicFormEditButton button) return; button.Action(this, button); } private DataModel? GetReportDataModel() { var dataModel = DataModel; if (dataModel is null) return null; var formType = dataModel.Instance.GetType(); var filter = Filter.Create(formType, x => x.ID).IsEqualTo(dataModel.Instance.ID); return (Activator.CreateInstance(typeof(DigitalFormReportDataModel<>)! .MakeGenericType(formType), new object?[] { filter, dataModel.Instance.Form.ID }) as DataModel)!; } private void PrintForm_Click(object sender, RoutedEventArgs e) { var model = GetReportDataModel(); var dataModel = DataModel; if (model is null || dataModel is null) return; (model as IDigitalFormReportDataModel)!.AddFormData(dataModel.Instance.ID, SaveValues().ToLoadStorage()); var menu = new ContextMenu(); ReportUtils.PopulateMenu(menu, dataModel.Instance.Form.ID.ToString(), model, false); if(menu.Items.Count == 0) { menu.AddItem("No reports", null, null, enabled: false); } menu.IsOpen = true; } private void SaveForm_Click(object sender, RoutedEventArgs e) { if (DataModel?.Instance.FormCompleted.IsEmpty() == false && !Grid.Validate(out var messages)) { MessageBox.Show(string.Join('\n', messages)); return; } if (MessageBox.Show("Are you sure you want to save this form?", "Confirm Save", MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Result = FormResult.Save; DialogResult = true; } } public static Columns FormColumns() where TForm : IDigitalFormInstance { return (FormColumns(typeof(TForm)) as Columns)!; } public static IColumns FormColumns(Type TForm) { return Columns.Create(TForm, ColumnTypeFlags.None) .Add(x => x.ID) .Add(x=>x.Number) .Add(x=>x.Description) .Add(x => x.FormCompleted) .Add(x => x.FormData) .Add(x => x.BlobData) .Add(x => x.Form.ID) .Add(x => x.Form.Description) .Add(x => x.Form.DescriptionExpression) .Add("Parent.ID"); } public static bool EditDigitalForm( IDigitalFormInstance formInstance, [NotNullWhen(true)] out IDigitalFormDataModel? dataModel, Entity? parent = null, CustomiseDynamicFormEditWindow? customise = null) { dataModel = null; DigitalFormLayout layout = null; DigitalFormVariable[] variables = null; DFLoadStorage? values = null; String error = ""; Progress.ShowModal("Loading Form", (progress) => { var formid = formInstance.Form.ID; values = DigitalForm.DeserializeFormData(formInstance); var results = Client.QueryMultiple( new KeyedQueryDef(new Filter(x => x.Form.ID).IsEqualTo(formid)), new KeyedQueryDef( new Filter(x => x.Form.ID).IsEqualTo(formid) .And(x => x.Active).IsEqualTo(true) .And(x => x.Layout).IsNotEqualTo(""))); variables = results[nameof(DigitalFormVariable)].Rows.Select(x => x.ToObject()).ToArray(); var desktopLayout = results[nameof(DigitalFormLayout)] .Rows.FirstOrDefault(x => x.Get(x => x.Type) == DFLayoutType.Desktop) ?.ToObject(); layout = desktopLayout ?? results[nameof(DigitalFormLayout)].ToObjects().FirstOrDefault(); if (layout != null) { if (parent is null) { var parentlink = CoreUtils.HasProperty(formInstance.GetType(), "Parent") ? CoreUtils.GetPropertyValue(formInstance, "Parent") as IEntityLink : null; var parenttype = parentlink?.GetType().BaseType?.GetGenericArguments().FirstOrDefault(); if (parenttype != null && parentlink != null) { var parentid = parentlink.ID; var filter = Filter.Create(parenttype); filter.Expression = CoreUtils.GetMemberExpression(parenttype, "ID"); filter.Operator = Operator.IsEqualTo; filter.Value = parentid; var client = (Activator.CreateInstance(typeof(Client<>).MakeGenericType(parenttype)) as Client)!; parent = client.Query(filter, null, null).Rows.FirstOrDefault()?.ToObject(parenttype) as Entity; } if (parent == null) { Logger.Send(LogType.Error, "", $"Form parent is null; Form Type: {formInstance.GetType()}; Parent Type: {parenttype}; Form ID: {formInstance.ID}"); error = "An error occurred while loading the form: Form Entity is null"; } } } else error = "No layout found for form!"; }); if (!String.IsNullOrWhiteSpace(error)) { MessageBox.Show(error); return false; } var form = new DynamicFormEditWindow { Type = layout.Type, Title = string.Format("Viewing {0}", formInstance.Form.Description) }; form.LoadLayout(layout, variables); try { dataModel = formInstance.CreateDataModel(parent!); dataModel.Variables = variables; dataModel.OnModelSaved += (model) => { DFUtils.OnSave(formInstance.GetType(), formInstance, parent!); }; form.DataModel = dataModel; } catch (Exception e) { Logger.Send(LogType.Error, ClientFactory.UserID, $"Error during Edit Form / CreateDataModel: {CoreUtils.FormatException(e)}"); } form.Initialize(); if (values is not null) form.LoadValues(values); customise?.Invoke(form); if (form.ShowDialog() == true) { if (form.Result == FormResult.Complete) { formInstance.FormCompleted = DateTime.Now; formInstance.FormCompletedBy.ID = ClientFactory.UserGuid; formInstance.FormCompletedBy.UserID = ClientFactory.UserID; /*formInstance.FormCompleted = form.Completed ? formInstance.FormCompleted.IsEmpty() ? DateTime.Now : formInstance.FormCompleted : DateTime.MinValue; formInstance.FormCompletedBy.ID = form.Completed ? !formInstance.FormCompletedBy.IsValid() ? ClientFactory.UserGuid : formInstance.FormCompletedBy.ID : Guid.Empty; formInstance.FormCompletedBy.UserID = form.Completed ? string.IsNullOrWhiteSpace(formInstance.FormCompletedBy.UserID) ? ClientFactory.UserID : formInstance.FormCompletedBy.UserID : "";*/ } DigitalForm.SerializeFormData(formInstance, variables, form.SaveValues()); } return form.Result == FormResult.Save || form.Result == FormResult.Complete; } public static bool EditDigitalForm(Guid formID, [NotNullWhen(true)] out IDigitalFormDataModel? dataModel) where TForm : Entity, IDigitalFormInstance, IRemotable, IPersistent, new() { var form = (new Client() .Query( new Filter(x => x.ID).IsEqualTo(formID), FormColumns()) .Rows.FirstOrDefault()?.ToObject()) ?? throw new Exception($"{typeof(TForm)} {formID} does not exist"); return EditDigitalForm(form, out dataModel); } private void DynamicFormWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (DialogResult != true && HasUnsavedChanges) { if (MessageBox.Show("This form has unsaved changes. Do you wish to discard them?", "Discard Changes?", MessageBoxButton.YesNo) != MessageBoxResult.Yes) { e.Cancel = true; } } } }