From 238dff62439e232dd943a7cf1a7250737b0dcab4 Mon Sep 17 00:00:00 2001 From: Sukhrob Ilyosbekov Date: Tue, 30 Jul 2024 21:37:31 -0400 Subject: [PATCH] added validators, added field specific additional properties in the property editor, refactored numeric model classes --- .../Components/FieldPropertyEditor.razor | 89 ++++++++++++++++++ ....razor.cs => FieldPropertyEditor.razor.cs} | 90 ++++++++----------- src/FormBuilder/Components/FormEditor.razor | 11 ++- .../Components/FormEditor.razor.cs | 16 ++-- src/FormBuilder/Components/FormField.razor | 76 +++++++++++++--- src/FormBuilder/Components/FormField.razor.cs | 3 +- .../Components/PropertyEditor.razor | 67 -------------- .../FieldJsonConverter.cs | 6 +- .../Converters/ValidatorJsonConverter.cs | 47 ++++++++++ src/FormBuilder/Factories/FieldFactory.cs | 24 ++++- src/FormBuilder/FormBuilderOptions.cs | 6 +- src/FormBuilder/Models/DateField.cs | 6 +- src/FormBuilder/Models/EmailValidator.cs | 7 ++ src/FormBuilder/Models/Field.cs | 46 ++++++---- src/FormBuilder/Models/FieldType.cs | 4 +- src/FormBuilder/Models/LengthValidator.cs | 9 ++ src/FormBuilder/Models/NumericDoubleField.cs | 9 -- src/FormBuilder/Models/NumericField.cs | 32 +++++++ src/FormBuilder/Models/NumericIntField.cs | 9 -- .../Models/NumericRangeValidator.cs | 9 ++ src/FormBuilder/Models/RequiredValidator.cs | 9 ++ src/FormBuilder/Models/SelectField.cs | 5 +- src/FormBuilder/Models/SelectOption.cs | 10 --- src/FormBuilder/Models/TextField.cs | 6 +- src/FormBuilder/Models/Validator.cs | 26 ++++++ src/FormBuilder/Models/ValidatorType.cs | 9 ++ src/FormBuilder/Services/FormService.cs | 4 +- src/FormBuilder/Services/IFormService.cs | 6 -- 28 files changed, 423 insertions(+), 218 deletions(-) create mode 100644 src/FormBuilder/Components/FieldPropertyEditor.razor rename src/FormBuilder/Components/{PropertyEditor.razor.cs => FieldPropertyEditor.razor.cs} (61%) delete mode 100644 src/FormBuilder/Components/PropertyEditor.razor rename src/FormBuilder/{Utils => Converters}/FieldJsonConverter.cs (88%) create mode 100644 src/FormBuilder/Converters/ValidatorJsonConverter.cs create mode 100644 src/FormBuilder/Models/EmailValidator.cs create mode 100644 src/FormBuilder/Models/LengthValidator.cs delete mode 100644 src/FormBuilder/Models/NumericDoubleField.cs create mode 100644 src/FormBuilder/Models/NumericField.cs delete mode 100644 src/FormBuilder/Models/NumericIntField.cs create mode 100644 src/FormBuilder/Models/NumericRangeValidator.cs create mode 100644 src/FormBuilder/Models/RequiredValidator.cs delete mode 100644 src/FormBuilder/Models/SelectOption.cs create mode 100644 src/FormBuilder/Models/Validator.cs create mode 100644 src/FormBuilder/Models/ValidatorType.cs delete mode 100644 src/FormBuilder/Services/IFormService.cs diff --git a/src/FormBuilder/Components/FieldPropertyEditor.razor b/src/FormBuilder/Components/FieldPropertyEditor.razor new file mode 100644 index 0000000..158c0d6 --- /dev/null +++ b/src/FormBuilder/Components/FieldPropertyEditor.razor @@ -0,0 +1,89 @@ +@using FormBuilder.Models + +@if (Field is not null) +{ + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ + @if (Field is TextField textField) + { + + + + } + + @if (Field is NumericField numericIntField) + { + + } + @if (Field is NumericField numericDecimalField) + { + + } + + @if (Field is DateField dateField) + { + + + + } + + @if (Field is SelectField) + { + + + + + + @if (SelectedListId is not null) + { + + + + + } + } + + +
+} diff --git a/src/FormBuilder/Components/PropertyEditor.razor.cs b/src/FormBuilder/Components/FieldPropertyEditor.razor.cs similarity index 61% rename from src/FormBuilder/Components/PropertyEditor.razor.cs rename to src/FormBuilder/Components/FieldPropertyEditor.razor.cs index cf8887f..65b342b 100644 --- a/src/FormBuilder/Components/PropertyEditor.razor.cs +++ b/src/FormBuilder/Components/FieldPropertyEditor.razor.cs @@ -8,12 +8,13 @@ namespace FormBuilder.Components; /// /// Component that allows editing of field properties in the form builder. /// -public partial class PropertyEditor : ComponentBase +public partial class FieldPropertyEditor : ComponentBase { private DropDownEnumItem[] _inputTypes = DropDownEnumItem.CreateItems(); private IEnumerable _listValues = []; private IEnumerable _listIds = []; private int _listIdCount; + private bool _fetchedInitialListIds; #region Injected Services @@ -31,13 +32,13 @@ public partial class PropertyEditor : ComponentBase /// The currently selected field to edit. /// [Parameter] - public Field? SelectedField { get; set; } + public Field? Field { get; set; } /// /// Event that is triggered when the selected field's property changes. /// [Parameter] - public EventCallback SelectedFieldChanged { get; set; } + public EventCallback FieldChanged { get; set; } /// /// Event that is triggered when a field property changes such as label, placeholder, etc. @@ -52,110 +53,93 @@ public partial class PropertyEditor : ComponentBase private string? Label { - get => SelectedField?.Label; + get => Field?.Label; set { - if (SelectedField is null || SelectedField.Label == value) + if (Field is null || Field.Label == value) { return; } - SelectedField.Label = value; - SelectedFieldChanged.InvokeAsync(SelectedField); - PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(SelectedField, nameof(Field.Label), value)); + Field.Label = value; + FieldChanged.InvokeAsync(Field); + PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(Field, nameof(Models.Field.Label), value)); } } private string? Placeholder { - get => SelectedField?.Placeholder; + get => Field?.Placeholder; set { - if (SelectedField is null || SelectedField.Placeholder == value) + if (Field is null || Field.Placeholder == value) { return; } - SelectedField.Placeholder = value; - SelectedFieldChanged.InvokeAsync(SelectedField); - PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(SelectedField, nameof(Field.Placeholder), value)); + Field.Placeholder = value; + FieldChanged.InvokeAsync(Field); + PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(Field, nameof(Models.Field.Placeholder), value)); } } private FieldType InputType { - get => SelectedField?.Type ?? FieldType.Text; + get => Field?.Type ?? FieldType.Text; set { - if (SelectedField is null || SelectedField.Type == value) + if (Field is null || Field.Type == value) { return; } - SelectedField.Type = value; - SelectedFieldChanged.InvokeAsync(SelectedField); - PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(SelectedField, nameof(Field.Type), value)); - } - } - - private bool Required - { - get => SelectedField?.Required ?? false; - set - { - if (SelectedField is null || SelectedField.Required == value) - { - return; - } - - SelectedField.Required = value; - SelectedFieldChanged.InvokeAsync(SelectedField); - PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(SelectedField, nameof(Field.Required), value)); + FieldChanged.InvokeAsync(Field); + PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(Field, nameof(Models.Field.Type), value)); } } private bool ReadOnly { - get => SelectedField?.ReadOnly ?? false; + get => Field?.ReadOnly ?? false; set { - if (SelectedField is null || SelectedField.ReadOnly == value) + if (Field is null || Field.ReadOnly == value) { return; } - SelectedField.ReadOnly = value; - SelectedFieldChanged.InvokeAsync(SelectedField); - PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(SelectedField, nameof(Field.ReadOnly), value)); + Field.ReadOnly = value; + FieldChanged.InvokeAsync(Field); + PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(Field, nameof(Models.Field.ReadOnly), value)); } } private bool Disabled { - get => SelectedField?.Disabled ?? false; + get => Field?.Disabled ?? false; set { - if (SelectedField is null || SelectedField.Disabled == value) + if (Field is null || Field.Disabled == value) { return; } - SelectedField.Disabled = value; - SelectedFieldChanged.InvokeAsync(SelectedField); - PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(SelectedField, nameof(Field.Disabled), value)); + Field.Disabled = value; + FieldChanged.InvokeAsync(Field); + PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(Field, nameof(Models.Field.Disabled), value)); } } private int? SelectedListId { - get => (SelectedField as SelectField)?.ListId; + get => (Field as SelectField)?.ListId; set { - if (SelectedField is SelectField selectField && selectField.ListId != value) + if (Field is SelectField selectField && selectField.ListId != value) { selectField.ListId = value; - SelectedFieldChanged.InvokeAsync(SelectedField); - PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(SelectedField, nameof(SelectField.ListId), value)); + FieldChanged.InvokeAsync(Field); + PropertyChanged.InvokeAsync(new FieldPropertyChangedArgs(Field, nameof(SelectField.ListId), value)); _ = FetchListValuesAsync(value); } } @@ -179,10 +163,14 @@ private bool ListValuesLoading #endregion - protected override async Task OnInitializedAsync() + protected override async Task OnParametersSetAsync() { - // Load the first 10 list IDs - await LoadListIdValuesAsync(new LoadDataArgs {Top = 10}); + if (Field is SelectField && !_fetchedInitialListIds) + { + // Load the first 10 list IDs after the field is set and only once + await LoadListIdValuesAsync(new LoadDataArgs {Top = 10}); + _fetchedInitialListIds = true; + } } private async Task LoadListIdValuesAsync(LoadDataArgs args) diff --git a/src/FormBuilder/Components/FormEditor.razor b/src/FormBuilder/Components/FormEditor.razor index a5a55ea..23c7b23 100644 --- a/src/FormBuilder/Components/FormEditor.razor +++ b/src/FormBuilder/Components/FormEditor.razor @@ -1,4 +1,4 @@ -@using global::FormBuilder.Models +@using FormBuilder.Models @@ -56,8 +56,8 @@ - - + + @@ -69,7 +69,10 @@ Properties - + +
diff --git a/src/FormBuilder/Components/FormEditor.razor.cs b/src/FormBuilder/Components/FormEditor.razor.cs index c7abec1..44cf00d 100644 --- a/src/FormBuilder/Components/FormEditor.razor.cs +++ b/src/FormBuilder/Components/FormEditor.razor.cs @@ -92,7 +92,7 @@ private Task HandleFieldPropertyChanged(FieldPropertyChangedArgs args) { if (args is { PropertyName: nameof(Field.Type), NewValue: FieldType fieldType }) { - ChangeFieldType(args.Field, fieldType); + ChangeFieldType(fieldType, args.Field); } return UpdateFormDesignJsonAsync(); @@ -102,18 +102,12 @@ private Task HandleFieldPropertyChanged(FieldPropertyChangedArgs args) /// Changes the field type if the new field type is different from the current one. /// Creates a new field with the new type and copies the properties from the old field. ///
- /// + /// /// - private void ChangeFieldType(Field field, FieldType newType) + private void ChangeFieldType(FieldType newType, Field oldField) { - var newField = FieldFactory.CreateField(newType); - newField.Label = field.Label; - newField.Placeholder = field.Placeholder; - newField.Required = field.Required; - newField.ReadOnly = field.ReadOnly; - newField.Disabled = field.Disabled; - - var index = _formDefinition.Fields.IndexOf(field); + var newField = FieldFactory.CreateFieldFrom(newType, oldField); + var index = _formDefinition.Fields.IndexOf(oldField); _formDefinition.Fields[index] = newField; // Replace the old field with the new one, old one will be deleted by GC SelectedField = newField; } diff --git a/src/FormBuilder/Components/FormField.razor b/src/FormBuilder/Components/FormField.razor index d34ef79..76efa4d 100644 --- a/src/FormBuilder/Components/FormField.razor +++ b/src/FormBuilder/Components/FormField.razor @@ -1,4 +1,4 @@ -@using global::FormBuilder.Models +@using FormBuilder.Models @Prepend @@ -10,6 +10,7 @@ Name="@textField.Name" @bind-Value="textField.Value" Placeholder="@textField.Placeholder" + MaxLength="@textField.MaxLength" ReadOnly="Disabled"> break; @@ -22,19 +23,29 @@ ReadOnly="Disabled"> break; - case NumericIntField numericIntField: + case NumericField numericIntField: break; - case NumericDoubleField numericDoubleField: + case NumericField numericField: break; @@ -43,6 +54,7 @@ Name="@dateField.Name" @bind-Value="dateField.Value" Placeholder="@dateField.Placeholder" + DateFormat="@dateField.DateFormat" ReadOnly="Disabled"> break; @@ -50,12 +62,54 @@ @Append - @if (Field.Required) + @if (!string.IsNullOrEmpty(Field.Hint)) { - - * required - - + @Field.Hint + } + + @foreach(var validator in Field.Validators) + { + switch (validator) + { + case RequiredValidator requiredValidator: + @if (requiredValidator.ShowRequiredHint) + { + + * required + + } + + + break; + case EmailValidator emailValidator: + + + break; + case LengthValidator lengthValidator: + + + break; + case RangeValidator rangeValidator: + + + break; + } } diff --git a/src/FormBuilder/Components/FormField.razor.cs b/src/FormBuilder/Components/FormField.razor.cs index a2227ca..16654e0 100644 --- a/src/FormBuilder/Components/FormField.razor.cs +++ b/src/FormBuilder/Components/FormField.razor.cs @@ -1,4 +1,5 @@ -using FormBuilder.Models; +using System.Globalization; +using FormBuilder.Models; using Microsoft.AspNetCore.Components; namespace FormBuilder.Components; diff --git a/src/FormBuilder/Components/PropertyEditor.razor b/src/FormBuilder/Components/PropertyEditor.razor deleted file mode 100644 index b83a01e..0000000 --- a/src/FormBuilder/Components/PropertyEditor.razor +++ /dev/null @@ -1,67 +0,0 @@ -@using global::FormBuilder.Models - -@if (SelectedField is not null) -{ - - - - - - - - - - - - - - - -
- - -
-
- - -
-
- - -
- - @if (SelectedField is SelectField) - { - - - - - - @if (SelectedListId is not null) - { - - - - - } - } -
-
-} diff --git a/src/FormBuilder/Utils/FieldJsonConverter.cs b/src/FormBuilder/Converters/FieldJsonConverter.cs similarity index 88% rename from src/FormBuilder/Utils/FieldJsonConverter.cs rename to src/FormBuilder/Converters/FieldJsonConverter.cs index a5d46d0..42b60ec 100644 --- a/src/FormBuilder/Utils/FieldJsonConverter.cs +++ b/src/FormBuilder/Converters/FieldJsonConverter.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; using FormBuilder.Models; -namespace FormBuilder.Utils; +namespace FormBuilder.Converters; internal class FieldJsonConverter : JsonConverter { @@ -26,8 +26,8 @@ public override Field Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe Field? field = enumFieldType switch { FieldType.Text => JsonSerializer.Deserialize(rootElement.GetRawText(), options), - FieldType.NumericInt => JsonSerializer.Deserialize(rootElement.GetRawText(), options), - FieldType.NumericDouble => JsonSerializer.Deserialize(rootElement.GetRawText(), options), + FieldType.NumericInt => JsonSerializer.Deserialize>(rootElement.GetRawText(), options), + FieldType.NumericDecimal => JsonSerializer.Deserialize>(rootElement.GetRawText(), options), FieldType.Select => JsonSerializer.Deserialize(rootElement.GetRawText(), options), FieldType.Date => JsonSerializer.Deserialize(rootElement.GetRawText(), options), _ => throw new NotSupportedException($"The value of the field type '{enumFieldType}' is not supported"), diff --git a/src/FormBuilder/Converters/ValidatorJsonConverter.cs b/src/FormBuilder/Converters/ValidatorJsonConverter.cs new file mode 100644 index 0000000..9638655 --- /dev/null +++ b/src/FormBuilder/Converters/ValidatorJsonConverter.cs @@ -0,0 +1,47 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using FormBuilder.Models; + +namespace FormBuilder.Converters; + +internal class ValidatorJsonConverter : JsonConverter +{ + public override Validator Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var jsonDoc = JsonDocument.ParseValue(ref reader); + var rootElement = jsonDoc.RootElement; + + if (!rootElement.TryGetProperty("type", out var validatorTypeProperty)) + { + throw new JsonException("The property 'type' is missing from the validator"); + } + + if (!validatorTypeProperty.TryGetInt32(out var validatorType)) + { + throw new JsonException("The value of the property 'type' is not an integer"); + } + + var enumValidatorType = Enum.Parse(validatorType.ToString()); + + Validator? validator = enumValidatorType switch + { + ValidatorType.Required => JsonSerializer.Deserialize(rootElement.GetRawText(), options), + ValidatorType.Length => JsonSerializer.Deserialize(rootElement.GetRawText(), options), + ValidatorType.Email => JsonSerializer.Deserialize(rootElement.GetRawText(), options), + ValidatorType.Range => JsonSerializer.Deserialize(rootElement.GetRawText(), options), + _ => throw new NotSupportedException($"The value of the validator type '{enumValidatorType}' is not supported"), + }; + + if (validator is null) + { + throw new JsonException($"Failed to deserialize field of type '{enumValidatorType}'"); + } + + return validator; + } + + public override void Write(Utf8JsonWriter writer, Validator value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } +} diff --git a/src/FormBuilder/Factories/FieldFactory.cs b/src/FormBuilder/Factories/FieldFactory.cs index 2b9b71b..c88a3c9 100644 --- a/src/FormBuilder/Factories/FieldFactory.cs +++ b/src/FormBuilder/Factories/FieldFactory.cs @@ -20,11 +20,31 @@ public static Field CreateField(FieldType fieldType) return fieldType switch { FieldType.Text => new TextField(), - FieldType.NumericInt => new NumericIntField(), - FieldType.NumericDouble => new NumericDoubleField(), + FieldType.NumericInt => new NumericField(), + FieldType.NumericDecimal => new NumericField(), FieldType.Select => new SelectField(), FieldType.Date => new DateField(), _ => new TextField() }; } + + /// + /// Creates a new field model based on the provided fieldType. + /// Copies the properties from the old field to the new field. + /// + /// The type of the field to create. + /// + /// A generic instance of the field based on the provided fieldType. + /// + public static Field CreateFieldFrom(FieldType newFieldType, Field oldField) + { + var newField = CreateField(newFieldType); + newField.Label = oldField.Label; + newField.Placeholder = oldField.Placeholder; + newField.ReadOnly = oldField.ReadOnly; + newField.Disabled = oldField.Disabled; + newField.Hint = oldField.Hint; + newField.Validators = oldField.Validators; + return newField; + } } diff --git a/src/FormBuilder/FormBuilderOptions.cs b/src/FormBuilder/FormBuilderOptions.cs index ed6c0ad..c74373a 100644 --- a/src/FormBuilder/FormBuilderOptions.cs +++ b/src/FormBuilder/FormBuilderOptions.cs @@ -1,6 +1,10 @@ -namespace FormBuilder; +using FormBuilder.Shared.Models; + +namespace FormBuilder; public record FormBuilderOptions { public string? FormApiHost { get; init; } + + public Func>? GetFormById { get; init; } } diff --git a/src/FormBuilder/Models/DateField.cs b/src/FormBuilder/Models/DateField.cs index bb93d4f..6cddab6 100644 --- a/src/FormBuilder/Models/DateField.cs +++ b/src/FormBuilder/Models/DateField.cs @@ -6,8 +6,6 @@ /// public class DateField : Field { - public DateField() - { - Type = FieldType.Date; - } + public override FieldType Type => FieldType.Date; + public string? DateFormat { get; set; } } diff --git a/src/FormBuilder/Models/EmailValidator.cs b/src/FormBuilder/Models/EmailValidator.cs new file mode 100644 index 0000000..e54036a --- /dev/null +++ b/src/FormBuilder/Models/EmailValidator.cs @@ -0,0 +1,7 @@ +namespace FormBuilder.Models; + +public class EmailValidator : Validator +{ + public override ValidatorType Type => ValidatorType.Email; + public override string Text { get; set; } = "Invalid email address"; +} diff --git a/src/FormBuilder/Models/Field.cs b/src/FormBuilder/Models/Field.cs index b117760..b500f12 100644 --- a/src/FormBuilder/Models/Field.cs +++ b/src/FormBuilder/Models/Field.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using FormBuilder.Converters; using FormBuilder.Utils; namespace FormBuilder.Models; @@ -7,21 +8,27 @@ namespace FormBuilder.Models; /// Represents a model for the form field. /// [JsonConverter(typeof(FieldJsonConverter))] -public class Field +public abstract class Field { - public Field() + private string? _name; + + /// + /// Field name. If not provided, it will be generated. + /// + public string Name { - if (string.IsNullOrEmpty(Name)) + get { - Name = Generator.GenerateShortId($"{Type}_".ToLower()); + if (string.IsNullOrEmpty(_name)) + { + _name = Generator.GenerateShortId($"{Type}_".ToLower()); + } + + return _name; } + set => _name = value; } - /// - /// Field name. If not provided, it will be generated. - /// - public string Name { get; set; } - /// /// Field label. /// @@ -33,14 +40,9 @@ public Field() public string? Placeholder { get; set; } /// - /// Field type such as TextField, NumericIntField, NumericDoubleField, SelectField, DateField. + /// Field type such as TextField, NumericIntField, NumericDecimalField, SelectField, DateField. /// - public FieldType Type { get; set; } - - /// - /// Determines if the field is required to be filled. - /// - public bool Required { get; set; } + public abstract FieldType Type { get; } /// /// Whether the field is read-only. @@ -51,13 +53,23 @@ public Field() /// Whether the field is disabled. /// public bool Disabled { get; set; } + + /// + /// The hint text to be displayed below the field. + /// + public string? Hint { get; set; } + + /// + /// List of validators to be applied to the field. + /// + public List Validators { get; set; } = []; } /// /// Generic version of the field model with a value of type T. /// /// The type of the field value. -public class Field : Field +public abstract class Field : Field { public T? Value { get; set; } } diff --git a/src/FormBuilder/Models/FieldType.cs b/src/FormBuilder/Models/FieldType.cs index 7328b7a..96cd591 100644 --- a/src/FormBuilder/Models/FieldType.cs +++ b/src/FormBuilder/Models/FieldType.cs @@ -13,8 +13,8 @@ public enum FieldType [Description("Numeric (Int)")] NumericInt = 2, - [Description("Numeric (Double)")] - NumericDouble = 3, + [Description("Numeric (Decimal)")] + NumericDecimal = 3, [Description("Date")] Date = 4, diff --git a/src/FormBuilder/Models/LengthValidator.cs b/src/FormBuilder/Models/LengthValidator.cs new file mode 100644 index 0000000..4a01278 --- /dev/null +++ b/src/FormBuilder/Models/LengthValidator.cs @@ -0,0 +1,9 @@ +namespace FormBuilder.Models; + +public class LengthValidator : Validator +{ + public override ValidatorType Type => ValidatorType.Length; + public override string Text { get; set; } = "Invalid length"; + public int? MinLength { get; set; } + public int? MaxLength { get; set; } +} diff --git a/src/FormBuilder/Models/NumericDoubleField.cs b/src/FormBuilder/Models/NumericDoubleField.cs deleted file mode 100644 index 5c8d2c7..0000000 --- a/src/FormBuilder/Models/NumericDoubleField.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FormBuilder.Models; - -public class NumericDoubleField : Field -{ - public NumericDoubleField() - { - Type = FieldType.NumericDouble; - } -} diff --git a/src/FormBuilder/Models/NumericField.cs b/src/FormBuilder/Models/NumericField.cs new file mode 100644 index 0000000..2f710e6 --- /dev/null +++ b/src/FormBuilder/Models/NumericField.cs @@ -0,0 +1,32 @@ +namespace FormBuilder.Models; + +public class NumericField : Field where T : struct +{ + public NumericField() + { + if (typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(short)) + { + Type = FieldType.NumericInt; + } + else if (typeof(T) == typeof(uint) || typeof(T) == typeof(ulong) || typeof(T) == typeof(ushort)) + { + Type = FieldType.NumericInt; + Min = 0; + } + else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(double) || typeof(T) == typeof(float)) + { + Type = FieldType.NumericDecimal; + } + else + { + throw new InvalidOperationException("Unsupported numeric type."); + } + } + + public override FieldType Type { get; } + public decimal? Min { get; set; } + public decimal? Max { get; set; } + public string Step { get; set; } = "1"; + public bool ShowUpDown { get; set; } = true; + public string? Format { get; set; } +} diff --git a/src/FormBuilder/Models/NumericIntField.cs b/src/FormBuilder/Models/NumericIntField.cs deleted file mode 100644 index 1c76139..0000000 --- a/src/FormBuilder/Models/NumericIntField.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FormBuilder.Models; - -public class NumericIntField : Field -{ - public NumericIntField() - { - Type = FieldType.NumericInt; - } -} diff --git a/src/FormBuilder/Models/NumericRangeValidator.cs b/src/FormBuilder/Models/NumericRangeValidator.cs new file mode 100644 index 0000000..883f704 --- /dev/null +++ b/src/FormBuilder/Models/NumericRangeValidator.cs @@ -0,0 +1,9 @@ +namespace FormBuilder.Models; + +public class RangeValidator : Validator +{ + public override ValidatorType Type => ValidatorType.Range; + public override string Text { get; set; } = "Not in the valid range"; + public int Min { get; set; } + public int Max { get; set; } +} diff --git a/src/FormBuilder/Models/RequiredValidator.cs b/src/FormBuilder/Models/RequiredValidator.cs new file mode 100644 index 0000000..5e521d9 --- /dev/null +++ b/src/FormBuilder/Models/RequiredValidator.cs @@ -0,0 +1,9 @@ +namespace FormBuilder.Models; + +public class RequiredValidator : Validator +{ + public override ValidatorType Type => ValidatorType.Required; + public override string Text { get; set; } = "Required"; + public bool IsRequired { get; set; } = true; + public bool ShowRequiredHint { get; set; } = true; +} diff --git a/src/FormBuilder/Models/SelectField.cs b/src/FormBuilder/Models/SelectField.cs index 27af5de..a13d0e2 100644 --- a/src/FormBuilder/Models/SelectField.cs +++ b/src/FormBuilder/Models/SelectField.cs @@ -6,10 +6,7 @@ /// public class SelectField : Field { - public SelectField() - { - Type = FieldType.Select; - } + public override FieldType Type => FieldType.Select; /// /// The list ID that corresponds to the list of options for this field. diff --git a/src/FormBuilder/Models/SelectOption.cs b/src/FormBuilder/Models/SelectOption.cs deleted file mode 100644 index e10804f..0000000 --- a/src/FormBuilder/Models/SelectOption.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace FormBuilder.Models; - -/// -/// Represents a select option for a select field. -/// -public class SelectOption -{ - public string? Text { get; set; } - public int? Value { get; set; } -} diff --git a/src/FormBuilder/Models/TextField.cs b/src/FormBuilder/Models/TextField.cs index 63658c2..b6da344 100644 --- a/src/FormBuilder/Models/TextField.cs +++ b/src/FormBuilder/Models/TextField.cs @@ -2,8 +2,6 @@ public class TextField : Field { - public TextField() - { - Type = FieldType.Text; - } + public override FieldType Type => FieldType.Text; + public long? MaxLength { get; set; } } diff --git a/src/FormBuilder/Models/Validator.cs b/src/FormBuilder/Models/Validator.cs new file mode 100644 index 0000000..2be6067 --- /dev/null +++ b/src/FormBuilder/Models/Validator.cs @@ -0,0 +1,26 @@ +using System.Text.Json.Serialization; +using FormBuilder.Converters; + +namespace FormBuilder.Models; + +/// +/// Represents a validator that can be applied to a form field. +/// +[JsonConverter(typeof(ValidatorJsonConverter))] +public abstract class Validator +{ + /// + /// The type of validator. + /// + public abstract ValidatorType Type { get; } + + /// + /// The error text to be displayed when the validation fails. + /// + public abstract string Text { get; set; } + + /// + /// Whether the validation message should be shown as a popup instead of inline. + /// + public bool ShowAsPopup { get; set; } +} diff --git a/src/FormBuilder/Models/ValidatorType.cs b/src/FormBuilder/Models/ValidatorType.cs new file mode 100644 index 0000000..39f4681 --- /dev/null +++ b/src/FormBuilder/Models/ValidatorType.cs @@ -0,0 +1,9 @@ +namespace FormBuilder.Models; + +public enum ValidatorType +{ + Required = 1, + Length = 2, + Email = 3, + Range = 4, +} diff --git a/src/FormBuilder/Services/FormService.cs b/src/FormBuilder/Services/FormService.cs index 1dda014..7f041f7 100644 --- a/src/FormBuilder/Services/FormService.cs +++ b/src/FormBuilder/Services/FormService.cs @@ -2,14 +2,14 @@ using System.Text; using System.Text.Json; using System.Text.Json.Serialization; +using FormBuilder.Converters; using FormBuilder.Models; using FormBuilder.Shared.Models; -using FormBuilder.Utils; using Microsoft.Extensions.Caching.Memory; namespace FormBuilder.Services; -public class FormService +internal class FormService { private readonly HttpClient _httpClient; private readonly JsonSerializerOptions _jsonSerializerDefaultOptions; diff --git a/src/FormBuilder/Services/IFormService.cs b/src/FormBuilder/Services/IFormService.cs deleted file mode 100644 index 44c61f7..0000000 --- a/src/FormBuilder/Services/IFormService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FormBuilder.Services; - -public interface IFormService -{ - -}