Skip to content

Commit

Permalink
implemented JSON serialization and deserialization features
Browse files Browse the repository at this point in the history
  • Loading branch information
suxrobGM committed Jul 23, 2024
1 parent 61e2a98 commit 7a49493
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 49 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ Follow these steps to set up and run the Blazor Form Builder:

Start the Web API project by running the following command script:
```sh
src/run-api.cmd
scripts/run-api.cmd
```

5. **Run the Blazor Application**

Start the Blazor WebAssembly application by running the following command script:
```sh
src/run-app.cmd
scripts/run-app.cmd
```

### Accessing the Applications
Expand Down
6 changes: 3 additions & 3 deletions src/FormBuilder.API/Controllers/FormController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public async Task<IActionResult> GetFormById(string id)
[HttpPost]
[ProducesResponseType(typeof(Result<Form>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Result), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateForm([FromBody] CreateFormDto formDto)
public async Task<IActionResult> CreateForm(CreateFormDto formDto)
{
if (string.IsNullOrEmpty(formDto.FormName))
{
Expand Down Expand Up @@ -91,7 +91,7 @@ public async Task<IActionResult> CreateForm([FromBody] CreateFormDto formDto)
[HttpPut("{id}")]
[ProducesResponseType(typeof(Result), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Result), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdateForm(string id, [FromBody] UpdateFormDto formDto)
public async Task<IActionResult> UpdateForm(string id, UpdateFormDto formDto)
{
var existingForm = await _context.Forms.FindAsync(id);

Expand All @@ -110,7 +110,7 @@ public async Task<IActionResult> UpdateForm(string id, [FromBody] UpdateFormDto
existingForm.FormDesign = formDto.FormDesign;
}

_context.Update(formDto);
_context.Update(existingForm);
await _context.SaveChangesAsync();
return Ok(Result.Succeed());
}
Expand Down
2 changes: 1 addition & 1 deletion src/FormBuilder.API/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"SelfHost": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7000;http://localhost:5000",
"environmentVariables": {
Expand Down
2 changes: 1 addition & 1 deletion src/FormBuilder.DesignerApp/wwwroot/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"FormBuilderOptions": {
"FormApiUrl": "https://localhost:7000/api"
"FormApiUrl": "https://localhost:7000"
}
}
8 changes: 5 additions & 3 deletions src/FormBuilder/Components/FormBuilder.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
@if (_formId is not null)
{
<RadzenFormField Text="Form ID">
<RadzenTextBox Value="@_formId" ReadOnly="true" />
<RadzenTextBox Value="@_formId" ReadOnly="true"/>
</RadzenFormField>
}

<RadzenFormField Text="Form Name">
<RadzenTextBox @bind-Value="_formDefinition.Name"/>
</RadzenFormField>
Expand Down Expand Up @@ -62,10 +62,12 @@
<RadzenButton Class="w-100" Text="Select Field" Click="() => AddField(FieldType.Select)"/>
</Dragable>
</RadzenStack>
<RadzenStack Class="mt-3" Orientation="Orientation.Vertical">
<RadzenStack Class="mt-3" Orientation="Orientation.Vertical" Gap="1rem">
<RadzenText TextStyle="TextStyle.H5" TextAlign="TextAlign.Center">Properties</RadzenText>
<PropertyEditor @bind-SelectedField="_selectedField" FieldTypeChanged="(e) => HandleFieldTypeChanged(e)"/>
<hr/>
<RadzenButton Text="Save Form" Click="SaveFormAsync" Disabled="_isLoading"/>
<RadzenButton Text="Load Form" Click="OpenLoadFormDialogAsync" Disabled="_isLoading"/>
</RadzenStack>
</RadzenColumn>
</RadzenRow>
27 changes: 22 additions & 5 deletions src/FormBuilder/Components/FormBuilder.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public partial class FormBuilder : ComponentBase

[Inject]
private NotificationService NotificationService { get; set; } = default!;

[Inject]
private DialogService DialogService { get; set; } = default!;

#endregion

Expand All @@ -32,7 +35,6 @@ private void AddField(FieldType fieldType)
{
var field = FieldFactory.CreateField(fieldType);
field.Label = fieldType.ToString();

_formDefinition.Fields.Add(field);
SelectField(field);
}
Expand Down Expand Up @@ -69,9 +71,9 @@ private void HandleFieldTypeChanged(FieldTypeChangedArgs args)
SelectField(newField);
}

private void HandleDropField(Action addFieldFn)
private void HandleDropField(Action addFieldCallback)
{
addFieldFn();
addFieldCallback();
}

private void SwapFields(Field targetField, Field droppedField)
Expand Down Expand Up @@ -100,13 +102,28 @@ private async Task SaveFormAsync()

if (result.Success)
{
NotificationService.Notify(NotificationSeverity.Success, "Form saved successfully");
NotificationService.NotifySuccess("Form saved successfully");
}
else
{
NotificationService.Notify(NotificationSeverity.Error, result.Error);
NotificationService.NotifyError(result.Error);
}

_isLoading = false;
}

private Task OpenLoadFormDialogAsync()
{
return DialogService.OpenAsync<LoadFormDialog>("Load Form", new Dictionary<string, object>
{
{ "FormLoaded", EventCallback.Factory.Create<FormCreatedEventArgs>(this, LoadForm) }
});
}

private void LoadForm(FormCreatedEventArgs args)
{
_formId = args.FormId;
_formDefinition = args.FormDefinition;
StateHasChanged();
}
}
27 changes: 27 additions & 0 deletions src/FormBuilder/Components/LoadFormDialog.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@using global::FormBuilder.Shared.Models

<RadzenTemplateForm TItem="FormDto" Data="_formModel" Submit="() => LoadFormAsync()">
<RadzenStack Orientation="Orientation.Vertical">
<RadzenText TextStyle="TextStyle.Body1">
Specify either Form ID or JSON design to load into builder
</RadzenText>
<RadzenFormField Text="Form ID">
<ChildContent>
<RadzenTextBox Name="FormId" @bind-Value="_formModel.Id"/>
</ChildContent>
<Helper>
<RadzenCustomValidator Component="FormId" Text="Either form ID or JSON design is required" Validator="EitherIdOrDesignRequired"/>
</Helper>
</RadzenFormField>
<RadzenFormField Text="Form JSON Design">
<ChildContent>
<RadzenTextArea Name="FormDesign" @bind-Value="_formModel.FormDesign" Rows="15"/>
</ChildContent>
<Helper>
<RadzenCustomValidator Component="FormDesign" Text="Either form ID or JSON design is required" Validator="EitherIdOrDesignRequired"/>
<RadzenCustomValidator Component="FormDesign" Text="Invalid JSON design" Validator="ValidateFormDesign"/>
</Helper>
</RadzenFormField>
<RadzenButton ButtonType="ButtonType.Submit" Text="Load Form" Disabled="_isLoading"/>
</RadzenStack>
</RadzenTemplateForm>
96 changes: 96 additions & 0 deletions src/FormBuilder/Components/LoadFormDialog.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using FormBuilder.Models;
using FormBuilder.Services;
using FormBuilder.Shared.Models;
using Microsoft.AspNetCore.Components;
using Radzen;

namespace FormBuilder.Components;

public partial class LoadFormDialog : ComponentBase
{
private FormDto _formModel = new();
private bool _isLoading;

#region Injected Servicse

[Inject]
private NotificationService NotificationService { get; set; } = default!;

[Inject]
private FormService FormService { get; set; } = default!;

#endregion


#region Parameters

[Parameter]
public EventCallback<FormCreatedEventArgs> FormLoaded { get; set; }

#endregion

private bool EitherIdOrDesignRequired()
{
return !string.IsNullOrEmpty(_formModel.Id) || !string.IsNullOrEmpty(_formModel.FormDesign);
}

private bool ValidateFormDesign()
{
if (string.IsNullOrEmpty(_formModel.FormDesign))
{
return true; // No form design to validate
}

var formDefinition = FormService.DeserializeFormDesign(_formModel.FormDesign);
return formDefinition is not null;
}

private Task LoadFormAsync()
{
if (!string.IsNullOrEmpty(_formModel.Id))
{
return LoadFormByIdAsync(_formModel.Id);
}

if (!string.IsNullOrEmpty(_formModel.FormDesign))
{
return HandleFormDesignDeserialization(_formModel.FormDesign, null);
}

return Task.CompletedTask;
}

private async Task LoadFormByIdAsync(string id)
{
_isLoading = true;
_formModel.Id = id;
var result = await FormService.GetFormByIdAsync(_formModel.Id);

if (result is { Success: true, Data.FormDesign: not null })
{
await HandleFormDesignDeserialization(result.Data.FormDesign, _formModel.Id);
}
else
{
NotificationService.NotifyError(result.Error!);
}

_isLoading = false;
}

private async Task HandleFormDesignDeserialization(string formDesign, string? id)
{
var formDefinition = FormService.DeserializeFormDesign(formDesign);
if (formDefinition != null)
{
await FormLoaded.InvokeAsync(new FormCreatedEventArgs(id, formDefinition));
NotificationService.NotifySuccess("Form loaded successfully.");
}
else
{
NotificationService.NotifyError("Failed to deserialize form design.");
}
}
}

public record FormCreatedEventArgs(string? FormId, FormDefinition FormDefinition);
19 changes: 2 additions & 17 deletions src/FormBuilder/Components/PropertyEditor.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,7 @@ private FieldType InputType

SelectedField.Type = value;
SelectedFieldChanged.InvokeAsync(SelectedField);
FieldTypeChanged.InvokeAsync(new FieldTypeChangedArgs
{
Field = SelectedField,
NewType = value
});
FieldTypeChanged.InvokeAsync(new FieldTypeChangedArgs(SelectedField, value));
}
}

Expand Down Expand Up @@ -154,15 +150,4 @@ private int? SelectedListValue
/// <summary>
/// Parameters for the FieldTypeChanged event.
/// </summary>
public class FieldTypeChangedArgs : EventArgs
{
/// <summary>
/// The field that has changed.
/// </summary>
public required Field Field { get; set; }

/// <summary>
/// The new type of the field.
/// </summary>
public required FieldType NewType { get; set; }
}
public record FieldTypeChangedArgs(Field Field, FieldType NewType);
14 changes: 14 additions & 0 deletions src/FormBuilder/Extensions/NotificationServiceExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Radzen;

public static class NotificationServiceExtensions
{
public static void NotifySuccess(this NotificationService notificationService, string? message)
{
notificationService.Notify(NotificationSeverity.Success, "Success", message);
}

public static void NotifyError(this NotificationService notificationService, string? message)
{
notificationService.Notify(NotificationSeverity.Error, "Error", message);
}
}
10 changes: 6 additions & 4 deletions src/FormBuilder/Models/Field.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
using FormBuilder.Utils;
using System.Text.Json.Serialization;
using FormBuilder.Utils;

namespace FormBuilder.Models;

/// <summary>
/// Represents a model for the form field.
/// </summary>
public abstract class Field
[JsonConverter(typeof(FieldJsonConverter))]
public class Field
{
protected Field()
public Field()
{
if (string.IsNullOrEmpty(Name))
{
Expand Down Expand Up @@ -55,7 +57,7 @@ protected Field()
/// Generic version of the field model with a value of type T.
/// </summary>
/// <typeparam name="T">The type of the field value.</typeparam>
public abstract class Field<T> : Field
public class Field<T> : Field
{
public T? Value { get; set; }
}
Loading

0 comments on commit 7a49493

Please sign in to comment.