Skip to content

Commit 7cb9584

Browse files
Lots of changes on UI and forms data structure
1 parent c6e1bf5 commit 7cb9584

File tree

12 files changed

+181
-44
lines changed

12 files changed

+181
-44
lines changed

src/DotNetElements.AppFramework.Abstractions/Drafts/Draft.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ public sealed class Draft<T> : AuditedEntity<Guid>, IUpdateFrom<DraftModel<T>>
88
public Guid OwnerId { get; private init; }
99
public int Version { get; private set; }
1010
public string Content { get; private set; }
11-
public string? Name { get; private init; }
11+
public string? CommitMessage { get; private init; }
1212

13-
public Draft(Guid ownerId, T content, string? name)
13+
public Draft(Guid ownerId, int version, T content, string? commitMessage)
1414
{
1515
OwnerId = ownerId;
16-
Name = name;
16+
Version = version;
17+
CommitMessage = commitMessage;
1718

1819
Content = JsonSerializer.Serialize(content);
19-
Version = 1;
2020
}
2121

2222
#nullable disable
@@ -31,15 +31,20 @@ public void Update(DraftModel<T> from)
3131
return;
3232

3333
Content = updatedContent;
34-
Version++;
34+
Version = from.Version;
35+
}
36+
37+
public T GetTypedContent()
38+
{
39+
return JsonSerializer.Deserialize<T>(Content)!;
3540
}
3641
}
3742

3843
public static class DraftMapper
3944
{
4045
public static Draft<T> MapToEntity<T>(this DraftModel<T> model)
4146
{
42-
return new Draft<T>(model.OwnerId, model.Content, model.Name);
47+
return new Draft<T>(model.OwnerId, model.Version, model.Content, model.CommitMessage);
4348
}
4449

4550
public static DraftModel<T> MapToModel<T>(this Draft<T> entity)
@@ -50,7 +55,7 @@ public static DraftModel<T> MapToModel<T>(this Draft<T> entity)
5055
OwnerId = entity.OwnerId,
5156
Version = entity.Version,
5257
Content = JsonSerializer.Deserialize<T>(entity.Content)!,
53-
Name = entity.Name
58+
CommitMessage = entity.CommitMessage
5459
};
5560
}
5661
}

src/DotNetElements.AppFramework.Abstractions/Drafts/DraftModel.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,27 @@ namespace DotNetElements.AppFramework.Abstractions.Drafts;
44

55
public sealed class DraftModel<T> : Model<Guid>
66
{
7+
[Required]
78
public required Guid OwnerId { get; init; }
9+
10+
[Required]
811
public required int Version { get; init; }
12+
13+
[Required]
914
public required T Content { get; init; }
10-
public string? Name { get; init; }
15+
16+
[MaxLength(255)]
17+
public string? CommitMessage { get; init; }
18+
19+
public static DraftModel<T> CreateNew(Guid ownerId, T content, string? commitMessage)
20+
{
21+
return new DraftModel<T>
22+
{
23+
Id = Guid.NewGuid(),
24+
OwnerId = ownerId,
25+
Version = 1,
26+
Content = content,
27+
CommitMessage = commitMessage
28+
};
29+
}
1130
}

src/DotNetElements.AppFramework.Abstractions/Drafts/DraftsRoutes.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
public static class DraftsRoutes
44
{
5-
public static string BaseUrl(string draftsEndpoint) => $"{draftsEndpoint}/drafts";
6-
public static string CreateOrUpdate(string draftsEndpoint) => BaseUrl(draftsEndpoint);
7-
public static string Delete(string draftsEndpoint) => $"{BaseUrl(draftsEndpoint)}/{{id}}";
8-
public static string GetById(string draftsEndpoint) => $"{BaseUrl(draftsEndpoint)}/{{id}}";
9-
public static string GetDetails(string draftsEndpoint) => $"{BaseUrl(draftsEndpoint)}/{{id}}/details";
5+
public static string CreateOrUpdate(string draftsEndpoint) => draftsEndpoint;
6+
public static string Delete(string draftsEndpoint) => $"{draftsEndpoint}/{{id}}";
7+
public static string GetById(string draftsEndpoint) => $"{draftsEndpoint}/{{id}}";
8+
public static string GetDetails(string draftsEndpoint) => $"{draftsEndpoint}/{{id}}/details";
109

11-
public static string GetCreateOrUpdateUrl(string draftsEndpoint) => BaseUrl(draftsEndpoint);
12-
public static string GetDeleteUrl(string draftsEndpoint, Guid id) => $"{BaseUrl(draftsEndpoint)}/{id}";
13-
public static string GetByIdUrl(string draftsEndpoint, Guid id) => $"{BaseUrl(draftsEndpoint)}/{id}";
14-
public static string GetDetailsUrl(string draftsEndpoint, Guid id) => $"{BaseUrl(draftsEndpoint)}/{id}/details";
10+
public static string GetCreateOrUpdateUrl(string draftsEndpoint) => draftsEndpoint;
11+
public static string GetDeleteUrl(string draftsEndpoint, Guid id) => $"{draftsEndpoint}/{id}";
12+
public static string GetByIdUrl(string draftsEndpoint, Guid id) => $"{draftsEndpoint}/{id}";
13+
public static string GetDetailsUrl(string draftsEndpoint, Guid id) => $"{draftsEndpoint}/{id}/details";
1514
}

src/DotNetElements.AppFramework.AspNet/Drafts/DraftModelBuilderExtensions.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,28 @@ namespace DotNetElements.AppFramework.AspNet.Drafts;
55

66
public static class DraftModelBuilderExtensions
77
{
8-
public static void ConfigureDrafts<TOwner, TContent>(this ModelBuilder modelBuilder, Expression<Func<TOwner, IEnumerable<Draft<TContent>>?>> navigationExpression)
8+
// todo currently not used.
9+
// For this to make sense we would need to update the DraftsService to support multiple drafts per owner.
10+
// We would also need to update the endpoints to support multiple drafts per owner.
11+
//public static void ConfigureDrafts<TOwner, TContent>(this ModelBuilder modelBuilder, Expression<Func<TOwner, IEnumerable<Draft<TContent>>?>> navigationExpression)
12+
// where TOwner : Entity<Guid>
13+
// where TContent : class
14+
//{
15+
// modelBuilder.Entity<TOwner>()
16+
// .HasMany(navigationExpression)
17+
// .WithOne()
18+
// .HasForeignKey(e => e.OwnerId)
19+
// .IsRequired();
20+
//}
21+
22+
public static void ConfigureDrafts<TOwner, TContent>(this ModelBuilder modelBuilder, Expression<Func<TOwner, Draft<TContent>?>> navigationExpression)
923
where TOwner : Entity<Guid>
1024
where TContent : class
1125
{
1226
modelBuilder.Entity<TOwner>()
13-
.HasMany(navigationExpression)
27+
.HasOne(navigationExpression)
1428
.WithOne()
15-
.HasForeignKey(e => e.OwnerId)
29+
.HasForeignKey<Draft<TContent>>(e => e.OwnerId)
1630
.IsRequired();
1731
}
1832
}

src/DotNetElements.AppFramework.AspNet/Drafts/WebApplicationExtensions.cs renamed to src/DotNetElements.AppFramework.AspNet/Drafts/EndpointRouteBuilderExtensions.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,48 @@
22
using DotNetElements.AppFramework.Abstractions.Model;
33
using DotNetElements.AppFramework.AspNet.ResultExtensions;
44
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Routing;
56

67
namespace DotNetElements.AppFramework.AspNet.Drafts;
78

8-
public static class WebApplicationExtensions
9+
public static class EndpointRouteBuilderExtensions
910
{
10-
public static WebApplication UseDrafts<TContent, TDbContext>(WebApplication app, string draftsEndpoint)
11+
public static IEndpointRouteBuilder MapDrafts<TContent, TDbContext>(this IEndpointRouteBuilder endpoints, string draftsEndpoint)
1112
where TContent : class
1213
where TDbContext : DbContext, IDbSetDraft<TContent>
1314
{
1415
// Create or update
15-
app.MapPut(DraftsRoutes.CreateOrUpdate(draftsEndpoint), async (DraftModel<TContent> model, DraftsService<TContent, TDbContext> draftsService) =>
16+
endpoints.MapPut(DraftsRoutes.CreateOrUpdate(draftsEndpoint), async (DraftModel<TContent> model, DraftsService<TContent, TDbContext> draftsService) =>
1617
{
1718
CrudResult<DraftModel<TContent>> updateResult = await draftsService.CreateOrUpdateDraftAsync(model);
1819

1920
return updateResult.MapToHttpResult();
2021
});
2122

2223
// Delete
23-
app.MapDelete(DraftsRoutes.Delete(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
24+
endpoints.MapDelete(DraftsRoutes.Delete(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
2425
{
2526
CrudResult deleteResult = await draftsService.DeleteDraftByIdAsync(id);
2627

2728
return deleteResult.MapToHttpResult();
2829
});
2930

3031
// Get by ID
31-
app.MapGet(DraftsRoutes.GetById(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
32+
endpoints.MapGet(DraftsRoutes.GetById(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
3233
{
3334
CrudResult<DraftModel<TContent>> result = await draftsService.GetDraftById(id);
3435

3536
return result.MapToHttpResult();
3637
});
3738

3839
// Get Audit Details
39-
app.MapGet(DraftsRoutes.GetDetails(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
40+
endpoints.MapGet(DraftsRoutes.GetDetails(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
4041
{
4142
CrudResult<AuditedModelDetails> auditResult = await draftsService.GetDraftAuditDetailsById(id);
4243

4344
return auditResult.MapToHttpResult();
4445
});
4546

46-
return app;
47+
return endpoints;
4748
}
4849
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
3+
namespace DotNetElements.AppFramework.AspNet.Drafts;
4+
5+
public static class ServiceCollectionExtensions
6+
{
7+
public static IServiceCollection AddDraftsService<TContent, TDbContext>(this IServiceCollection services)
8+
where TContent : class
9+
where TDbContext : DbContext, IDbSetDraft<TContent>
10+
{
11+
services.AddModuleService<DraftsService<TContent, TDbContext>, TDbContext>();
12+
13+
return services;
14+
}
15+
}

src/DotNetElements.AppFramework.AspNet/Drafts/WebApplicationBuilderExtensions.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/DotNetElements.AppFramework.MudBlazorExtensions/Components/MudEditFormDialog.razor

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@
1212
@FormContent(Value)
1313
</EditForm>
1414
}
15+
else if (hasError)
16+
{
17+
<MudAlert Severity="Severity.Error">@(errorMessage ?? "Unknown error")</MudAlert>
18+
}
1519
else
1620
{
1721
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
1822
}
1923
</DialogContent>
2024
<DialogActions>
2125
<MudButton OnClick="OnCancelAsync_Internal">@CancelButtonText</MudButton>
22-
@if (Value is not null && editContext is not null)
26+
@if (Value is not null && editContext is not null && SubmitButtonShown)
2327
{
2428
<MudButton Color="Color.Primary" OnClick="OnSubmitAsync_Internal">@SubmitButtonText</MudButton>
2529
}
@@ -43,6 +47,9 @@
4347
[Parameter]
4448
public EventCallback OnCancel { get; set; }
4549

50+
[Parameter]
51+
public bool SubmitButtonShown { get; set; } = true;
52+
4653
[Parameter]
4754
public string SubmitButtonText { get; set; } = "Submit";
4855

@@ -51,6 +58,29 @@
5158

5259
private EditContext? editContext;
5360

61+
private bool hasError;
62+
private string? errorMessage;
63+
64+
public void ShowError(string message)
65+
{
66+
hasError = true;
67+
errorMessage = message;
68+
}
69+
70+
public void ClearError()
71+
{
72+
hasError = false;
73+
errorMessage = null;
74+
}
75+
76+
public bool Validate()
77+
{
78+
ArgumentNullException.ThrowIfNull(editContext);
79+
80+
return editContext.Validate();
81+
}
82+
83+
// todo check how this behaves when a parameter is updated!!!
5484
protected override void OnParametersSet()
5585
{
5686
if (Value is null)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@inherits MudComponentBase
2+
3+
@typeparam TModel
4+
5+
@if (SimpleTable)
6+
{
7+
<td class="py-0 pl-2 @Class" style="@($"width: {Width}px;") @Style">
8+
<TableActionsButton OnClick="() => OnEditEntry.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Edit" Color="Color.Warning" Style="@(EditButtonShown ? "visibility: visible;" : "visibility: hidden;" )" />
9+
<TableActionsButton OnClick="() => OnDeleteEntry.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Delete" Color="Color.Error" Style="@(DeleteButtonShown ? "visibility: visible;" : "visibility: hidden;" )" />
10+
@ChildContent
11+
</td>
12+
}
13+
else
14+
{
15+
<MudTd Class="@($"py-0 pl-2 {@Class}")" Style="@($"width: {Width}px; {@Style}")" DataLabel="Actions">
16+
<TableActionsButton OnClick="() => OnEditEntry.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Edit" Color="Color.Warning" Style="@(EditButtonShown ? "visibility: visible;" : "visibility: hidden;" )" />
17+
<TableActionsButton OnClick="() => OnDeleteEntry.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Delete" Color="Color.Error" Style="@(DeleteButtonShown ? "visibility: visible;" : "visibility: hidden;" )" />
18+
@ChildContent
19+
</MudTd>
20+
}
21+
22+
@code
23+
{
24+
[Parameter, EditorRequired]
25+
public TModel Context { get; set; } = default!;
26+
27+
[Parameter, EditorRequired]
28+
public EventCallback<TModel> OnEditEntry { get; set; }
29+
30+
[Parameter, EditorRequired]
31+
public EventCallback<TModel> OnDeleteEntry { get; set; }
32+
33+
[Parameter]
34+
public bool SimpleTable { get; set; }
35+
36+
[Parameter]
37+
public bool EditButtonShown { get; set; } = true;
38+
39+
[Parameter]
40+
public bool DeleteButtonShown { get; set; } = true;
41+
42+
[Parameter]
43+
public RenderFragment? ChildContent { get; set; }
44+
45+
[Parameter]
46+
public int Width { get; set; } = 140;
47+
}

src/DotNetElements.AppFramework.MudBlazorExtensions/Components/TableActionsButton.razor

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
@code
88
{
9+
// todo add param to specify default style using enum e.g ActionButton.Edit or ActionButton.Delete
10+
// this would set Icon and Color defaults
11+
912
public TableActionsButton()
1013
{
1114
Class = "pa-1";

0 commit comments

Comments
 (0)