Skip to content

Commit

Permalink
Lots of small improvements to CrudTable and CrudService.
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix-CodingClimber committed Apr 9, 2024
1 parent 82040df commit af41cdb
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 19 deletions.
10 changes: 8 additions & 2 deletions src/DotNetElements.Web.Blazor/AuditedModelDetailsRow.razor
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
<tr>
<td colspan="100">
<MudGrid Spacing="2">
<MudItem xs="12">
<MudItem xs="11">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>ID</b></MudText>
<MudText Typo="Typo.caption">@Context.Value.Id</MudText>
</MudStack>
</MudItem>
<MudItem xs="1" Class="d-flex flex-row align-start justify-end">
<CrudTableActionsButton OnClick="() => Context.DetailsShown = false" Icon="@Icons.Material.Outlined.ExpandLess" Variant="Variant.Outlined" Color="Color.Default" Size="Size.Small" />
</MudItem>
<MudItem xs="4">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>Creation Time</b></MudText>
Expand All @@ -38,12 +41,15 @@
<MudTr>
<MudTd colspan="100" Style="border-bottom: 3px solid var(--mud-palette-table-lines);" DataLabel="Row Details">
<MudGrid Spacing="2">
<MudItem xs="12">
<MudItem xs="11">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>ID</b></MudText>
<MudText Typo="Typo.caption">@Context.Value.Id</MudText>
</MudStack>
</MudItem>
<MudItem xs="1" Class="d-flex flex-row align-start justify-end">
<CrudTableActionsButton OnClick="() => Context.DetailsShown = false" Icon="@Icons.Material.Outlined.ExpandLess" Variant="Variant.Outlined" Color="Color.Default" Size="Size.Small" />
</MudItem>
<MudItem xs="4">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>Creation Time</b></MudText>
Expand Down
59 changes: 52 additions & 7 deletions src/DotNetElements.Web.Blazor/CrudActionsMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,38 @@
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Dense="true" Class="@($"dne-crud-actions-menu {Class}")" Style="@Style" Size="MenuButtonSize" AnchorOrigin="@AnchorOrigin" TransformOrigin="@TransformOrigin">
@if (EditButtonShown)
{
<MudMenuItem OnClick="() => OnEditEntry.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Edit" Disabled="EditButtonDisabled" IconColor="Color.Warning">Edit</MudMenuItem>
<MudMenuItem OnClick="OnEditEntry_Internal" Icon="@Icons.Material.Outlined.Edit" Disabled="EditButtonDisabled" IconColor="Color.Warning">Edit</MudMenuItem>
}
@if (DeleteButtonShown)
{
<MudMenuItem OnClick="() => OnDeleteEntry.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Delete" Disabled="DeleteButtonDisabled" IconColor="Color.Error">Delete</MudMenuItem>
<MudMenuItem OnClick="OnDeleteEntry_Internal" Icon="@Icons.Material.Outlined.Delete" Disabled="DeleteButtonDisabled" IconColor="Color.Error">Delete</MudMenuItem>
}
@if (HardDeleteButtonShown)
{
<MudMenuItem OnClick="OnHardDeleteEntry_Internal" Icon="@Icons.Material.Outlined.DeleteForever" Disabled="HardDeleteButtonDisabled" IconColor="Color.Error">Delete Forever</MudMenuItem>
}
@if (DetailsButtonShown)
{
<MudMenuItem OnClick="() => OnShowEntryDetails.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Info" Disabled="DetailsButtonDisabled" IconColor="Color.Default">Info</MudMenuItem>
<MudMenuItem OnClick="OnShowEntryDetails_Internal" Icon="@Icons.Material.Outlined.Info" Disabled="DetailsButtonDisabled" IconColor="Color.Default">Info</MudMenuItem>
}
@ChildContent
</MudMenu>

@code
{
[Parameter, EditorRequired]
[Parameter]
public TModel Context { get; set; } = default!;

[Parameter, EditorRequired]
[Parameter]
public EventCallback<TModel> OnEditEntry { get; set; }

[Parameter, EditorRequired]
[Parameter]
public EventCallback<TModel> OnDeleteEntry { get; set; }

[Parameter, EditorRequired]
[Parameter]
public EventCallback<TModel> OnHardDeleteEntry { get; set; }

[Parameter]
public EventCallback<TModel> OnShowEntryDetails { get; set; }

[Parameter]
Expand All @@ -58,6 +65,9 @@
[Parameter]
public bool DeleteButtonShown { get; set; } = true;

[Parameter]
public bool HardDeleteButtonShown { get; set; } = false;

[Parameter]
public bool DetailsButtonShown { get; set; } = true;

Expand All @@ -67,9 +77,44 @@
[Parameter]
public bool DeleteButtonDisabled { get; set; }

[Parameter]
public bool HardDeleteButtonDisabled { get; set; }

[Parameter]
public bool DetailsButtonDisabled { get; set; }

[Parameter]
public RenderFragment? ChildContent { get; set; }

public Task OnEditEntry_Internal()
{
if (OnEditEntry.HasDelegate)
return OnEditEntry.InvokeAsync(Context);

return Task.CompletedTask;
}

public Task OnDeleteEntry_Internal()
{
if (OnDeleteEntry.HasDelegate)
return OnDeleteEntry.InvokeAsync(Context);

return Task.CompletedTask;
}

public Task OnHardDeleteEntry_Internal()
{
if (OnHardDeleteEntry.HasDelegate)
return OnHardDeleteEntry.InvokeAsync(Context);

return Task.CompletedTask;
}

public Task OnShowEntryDetails_Internal()
{
if (OnShowEntryDetails.HasDelegate)
return OnShowEntryDetails.InvokeAsync(Context);

return Task.CompletedTask;
}
}
1 change: 1 addition & 0 deletions src/DotNetElements.Web.Blazor/CrudOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public string GetAllWithDetailsEndpoint

public string GetDetailsEndpoint<TKey>(TKey id) => $"{BaseEndpointUri}/{id}/details";
public string GetByIdEndpoint<TKey>(TKey id) => $"{BaseEndpointUri}/{id}";
public string HardDeleteEndpoint() => $"{BaseEndpointUri}/hardDelete";

public CrudOptions(string baseEndpointUri)
{
Expand Down
3 changes: 3 additions & 0 deletions src/DotNetElements.Web.Blazor/CrudService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ public virtual async Task<Result> DeleteEntryAsync(TModel model)
}

// todo use everywhere
protected void NotifyUserCreateResult(Result result) => NotifyUser(result, "Entry saved", "Failed to save entry");
protected void NotifyUserUpdateResult(Result result) => NotifyUser(result, "Changes saved", "Failed to save changes");
protected void NotifyUserDeleteResult(Result result) => NotifyUser(result, "Entry deleted", "Failed to delete entry");
protected void NotifyUser(Result result, string messageOk, string messageFail)
{
// todo add logging
Expand Down
5 changes: 4 additions & 1 deletion src/DotNetElements.Web.Blazor/CrudServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ public virtual async Task<Result<List<TModel>>> GetAllEntriesAsync()
}

// todo use everywhere
protected void NotifyUserIfFailed(Result result, string message= "Failed to fetch entries from server")
protected void NotifyUserIfGetByIdFailed(Result result) => NotifyUserIfFailed(result, "Failed to fetch entry from server");
protected void NotifyUserIfGetAllFailed(Result result) => NotifyUserIfFailed(result, "Failed to fetch entries from server");

protected void NotifyUserIfFailed(Result result, string message)
{
// todo add logging
// todo wrap Snackbar call in bool option NotifyUser
Expand Down
59 changes: 52 additions & 7 deletions src/DotNetElements.Web.Blazor/CrudTableActionsMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,38 @@
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Dense="true" Class="@($"dne-crud-actions-menu {Class}")" Style="@($"vertical-align: middle; {Style}")" Size="MenuButtonSize" AnchorOrigin="@AnchorOrigin" TransformOrigin="@TransformOrigin">
@if (EditButtonShown)
{
<MudMenuItem OnClick="() => OnEditEntry.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Edit" Disabled="EditButtonDisabled" IconColor="Color.Warning">Edit</MudMenuItem>
<MudMenuItem OnClick="OnEditEntry_Internal" Icon="@Icons.Material.Outlined.Edit" Disabled="EditButtonDisabled" IconColor="Color.Warning">Edit</MudMenuItem>
}
@if (DeleteButtonShown)
{
<MudMenuItem OnClick="() => OnDeleteEntry.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Delete" Disabled="DeleteButtonDisabled" IconColor="Color.Error">Delete</MudMenuItem>
<MudMenuItem OnClick="OnDeleteEntry_Internal" Icon="@Icons.Material.Outlined.Delete" Disabled="DeleteButtonDisabled" IconColor="Color.Error">Delete</MudMenuItem>
}
@if (HardDeleteButtonShown)
{
<MudMenuItem OnClick="OnHardDeleteEntry_Internal" Icon="@Icons.Material.Outlined.DeleteForever" Disabled="HardDeleteButtonDisabled" IconColor="Color.Error">Delete Forever</MudMenuItem>
}
@if (DetailsButtonShown)
{
<MudMenuItem OnClick="() => OnShowEntryDetails.InvokeAsync(Context)" Icon="@Icons.Material.Outlined.Info" Disabled="DetailsButtonDisabled" IconColor="Color.Default">Info</MudMenuItem>
<MudMenuItem OnClick="OnShowEntryDetails_Internal" Icon="@Icons.Material.Outlined.Info" Disabled="DetailsButtonDisabled" IconColor="Color.Default">Info</MudMenuItem>
}
@ChildContent
</MudMenu>

@code
{
[Parameter, EditorRequired]
[Parameter]
public ModelWithDetails<TModel, TDetails> Context { get; set; } = default!;

[Parameter, EditorRequired]
[Parameter]
public EventCallback<ModelWithDetails<TModel, TDetails>> OnEditEntry { get; set; }

[Parameter, EditorRequired]
[Parameter]
public EventCallback<ModelWithDetails<TModel, TDetails>> OnDeleteEntry { get; set; }

[Parameter, EditorRequired]
[Parameter]
public EventCallback<ModelWithDetails<TModel, TDetails>> OnHardDeleteEntry { get; set; }

[Parameter]
public EventCallback<ModelWithDetails<TModel, TDetails>> OnShowEntryDetails { get; set; }

[Parameter]
Expand All @@ -58,6 +65,9 @@
[Parameter]
public bool DeleteButtonShown { get; set; } = true;

[Parameter]
public bool HardDeleteButtonShown { get; set; } = false;

[Parameter]
public bool DetailsButtonShown { get; set; } = true;

Expand All @@ -67,9 +77,44 @@
[Parameter]
public bool DeleteButtonDisabled { get; set; }

[Parameter]
public bool HardDeleteButtonDisabled { get; set; }

[Parameter]
public bool DetailsButtonDisabled { get; set; }

[Parameter]
public RenderFragment? ChildContent { get; set; }

public Task OnEditEntry_Internal()
{
if(OnEditEntry.HasDelegate)
return OnEditEntry.InvokeAsync(Context);

return Task.CompletedTask;
}

public Task OnDeleteEntry_Internal()
{
if (OnDeleteEntry.HasDelegate)
return OnDeleteEntry.InvokeAsync(Context);

return Task.CompletedTask;
}

public Task OnHardDeleteEntry_Internal()
{
if (OnHardDeleteEntry.HasDelegate)
return OnHardDeleteEntry.InvokeAsync(Context);

return Task.CompletedTask;
}

public Task OnShowEntryDetails_Internal()
{
if (OnShowEntryDetails.HasDelegate)
return OnShowEntryDetails.InvokeAsync(Context);

return Task.CompletedTask;
}
}
20 changes: 20 additions & 0 deletions src/DotNetElements.Web.Blazor/CrudTableBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@ protected virtual async Task OnDeleteEntry(ModelWithDetails<TModel, TDetails> co

Result result = await HttpClient.DeleteWithResultAsync(Options.BaseEndpointUri, context.Value);

if (result.IsOk)
{
Snackbar.Add("Entry deleted", Severity.Success);
TableEntries.Remove(context); // todo remove here and rename method to OnSoftDeleteEntry
}
else
{
Snackbar.Add("Failed to delete entry", Severity.Error);
}
}

protected virtual async Task OnHardDeleteEntry(ModelWithDetails<TModel, TDetails> context)
{
Result canDelete = await DialogService.ShowDeleteDialogAsync($"Delete {Options.DeleteEntryLabel} forever?", Options.DeleteEntryValue.Invoke(context.Value), Options.DeleteEntryLabel);

if (canDelete.IsFail)
return;

Result result = await HttpClient.DeleteWithResultAsync(Options.HardDeleteEndpoint(), context.Value);

if (result.IsOk)
{
Snackbar.Add("Entry deleted", Severity.Success);
Expand Down
6 changes: 5 additions & 1 deletion src/DotNetElements.Web.Blazor/DeleteDialog.razor
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">
<MudIcon Icon="@Icons.Material.Filled.DeleteForever" Color="Color.Error" Class="mr-3 mb-n1" />
<MudIcon Icon="@(IsHardDelete ? Icons.Material.Filled.DeleteForever : Icons.Material.Filled.Delete)" Color="Color.Error" Class="mr-3 mb-n1" />
@DialogInstance.Title
</MudText>
</TitleContent>
<DialogContent>
@if (AdditionalMessage is not null)
{
<MudText>@AdditionalMessage</MudText>
}
<MudTextField Value="@ItemValue" Label="@ItemLabel" FullWidth="true" ReadOnly="true" />
</DialogContent>
<DialogActions>
Expand Down
6 changes: 6 additions & 0 deletions src/DotNetElements.Web.Blazor/DeleteDialog.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ public partial class DeleteDialog : ComponentBase
[Parameter, EditorRequired]
public string ItemLabel { get; set; } = default!;

[Parameter]
public bool IsHardDelete { get; set; }

[Parameter]
public string? AdditionalMessage { get; set; }

private void OnConfirm()
{
DialogInstance.Close(DialogResult.Ok(true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,29 @@ public static class DialogeServiceExtensions
DisableBackdropClick = false,
};

public static async Task<Result> ShowDeleteDialogAsync(this IDialogService dialogService, string title, string itemValue, string itemLabel)
// todo rename to ShowSoftDeleteDialogAsync
public static async Task<Result> ShowDeleteDialogAsync(this IDialogService dialogService, string title, string itemValue, string itemLabel, string? additionalMessage = null)
{
var dialogParameters = new DialogParameters<DeleteDialog>
{
{ x => x.IsHardDelete, false },
{ x => x.AdditionalMessage, additionalMessage },
{ x => x.ItemValue, itemValue },
{ x => x.ItemLabel, itemLabel }
};

IDialogReference dialog = await dialogService.ShowAsync<DeleteDialog>(title, dialogParameters);
DialogResult result = await dialog.Result;

return result.Canceled ? Result.Fail("Canceled by user") : Result.Ok();
}

public static async Task<Result> ShowHardDeleteDialogAsync(this IDialogService dialogService, string title, string itemValue, string itemLabel, string? additionalMessage = null)
{
var dialogParameters = new DialogParameters<DeleteDialog>
{
{ x => x.IsHardDelete, true },
{ x => x.AdditionalMessage, additionalMessage },
{ x => x.ItemValue, itemValue },
{ x => x.ItemLabel, itemLabel }
};
Expand Down
3 changes: 3 additions & 0 deletions src/DotNetElements.Web.Blazor/ReadOnlyCrudService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,7 @@ public virtual async Task<Result<List<ModelWithDetails<TModel, TDetails>>>> GetA

return result;
}

// todo use everywhere
protected void NotifyUserIfGetDetailsFailed(Result result) => NotifyUserIfFailed(result, "Failed to fetch details.");
}

0 comments on commit af41cdb

Please sign in to comment.