Add edit functionality for EinAb interactions: implement UpdateInteraction method, extend InteractionDialog for edit mode, and add edit button in InteractionRow
This commit is contained in:
@@ -45,11 +45,29 @@ else
|
||||
|
||||
@foreach (var interaction in Interactions)
|
||||
{
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="d-flex justify-content-end align-items-center interaction-row-action-group">
|
||||
@if ((Prospect is not { Complete: true } || interaction.Type == InteractionType.Complete) && AllowInteraction)
|
||||
{
|
||||
<a style="display: inline-block;"" href=""><i class="fa-solid fa-square-xmark" @onclick="async () => { if (RemoveClick != null) await RemoveClick.Invoke(interaction.Id); }" @onclick:preventDefault></i></a>
|
||||
} else {
|
||||
<div class="d-flex align-items-center interaction-row-actions">
|
||||
@if (AllowEdit)
|
||||
{
|
||||
<Button Size="Size.Small"
|
||||
Class="interaction-row-action interaction-row-action-edit"
|
||||
title="Eintrag bearbeiten"
|
||||
Clicked="@(async () => { if (EditClick != null) await EditClick.Invoke(interaction.Id); })">
|
||||
<i class="fa-solid fa-pen-to-square"></i>
|
||||
</Button>
|
||||
}
|
||||
<Button Size="Size.Small"
|
||||
Class="interaction-row-action interaction-row-action-delete"
|
||||
title="Eintrag löschen"
|
||||
Clicked="@(async () => { if (RemoveClick != null) await RemoveClick.Invoke(interaction.Id); })">
|
||||
<i class="fa-solid fa-square-xmark"></i>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>•</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -62,6 +62,18 @@ namespace FoodsharingSiegen.Server.Controls
|
||||
[Parameter]
|
||||
public Func<Guid, Task>? RemoveClick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether editing existing interactions is allowed.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool AllowEdit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback invoked when an interaction is edited.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<Guid, Task>? EditClick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the type (ab)
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,38 @@
|
||||
tr.done th {
|
||||
.interaction-row-action-group {
|
||||
gap: 0.25rem;
|
||||
min-width: 2.7rem;
|
||||
}
|
||||
|
||||
.interaction-row-actions {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.interaction-row-action {
|
||||
width: 1rem;
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
min-height: 1.25rem;
|
||||
padding: 0;
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.7rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.interaction-row-action i {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.interaction-row-action-edit i {
|
||||
color: #1f2f3a;
|
||||
}
|
||||
|
||||
.interaction-row-action-delete i {
|
||||
color: rgb(153, 0, 0);
|
||||
}
|
||||
|
||||
tr.done th {
|
||||
color: #64ae24;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@
|
||||
AllowInteraction="@(StateFilter == ProspectStateFilter.OnBoarding && CurrentUser.IsInGroup(UserGroup.WelcomeTeam, UserGroup.Ambassador))"
|
||||
AddClick="AddInteraction"
|
||||
RemoveClick="@RemoveInteraction"
|
||||
AllowEdit="true"
|
||||
EditClick="@EditInteraction"
|
||||
Multiple="true"
|
||||
Minimum="3"
|
||||
ButtonIconClass="fa-solid fa-plus"
|
||||
|
||||
@@ -62,6 +62,24 @@ namespace FoodsharingSiegen.Server.Controls
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Method EditInteraction
|
||||
|
||||
private async Task EditInteraction(Guid interactionId)
|
||||
{
|
||||
if (Prospect == null || OnDataChanged == null) return;
|
||||
|
||||
var interaction = Prospect.Interactions.FirstOrDefault(x => x.Id == interactionId);
|
||||
if (interaction == null || !InteractionDialog.IsEditable(interaction.Type)) return;
|
||||
|
||||
var headerText = $"{interaction.Type.Translate(AppSettings)} für {Prospect.Name} bearbeiten";
|
||||
|
||||
Func<Task> onSuccess = async () => await OnDataChanged();
|
||||
|
||||
await InteractionDialog.ShowAsync(ModalService, new(interaction.Type, Prospect.Id, headerText, onSuccess, interaction));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Method DeleteProspectAsync
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace FoodsharingSiegen.Server.Data
|
||||
return $"hat dem Neuling {audit.Data1} folgendes hinzugefügt: {audit.Data2}";
|
||||
case AuditType.RemoveInteraction:
|
||||
return $"hat eine Interaktion bei {audit.Data1} gelöscht.";
|
||||
case AuditType.EditInteraction:
|
||||
return $"hat eine Interaktion bei {audit.Data1} bearbeitet: {audit.Data2}";
|
||||
case AuditType.DeleteProspectImages:
|
||||
return $"hat die Bilder von {audit.Data1} gelöscht.";
|
||||
case AuditType.ViewProspectImages:
|
||||
|
||||
@@ -174,6 +174,53 @@ namespace FoodsharingSiegen.Server.Data.Service
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method UpdateInteraction
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing interaction with the specified values.
|
||||
/// </summary>
|
||||
/// <param name="interaction">The interaction with updated values.</param>
|
||||
/// <returns>A task containing an operation result of interaction.</returns>
|
||||
public async Task<OperationResult<Interaction>> UpdateInteraction(Interaction interaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entityInteraction = await Context.Interactions!.FirstOrDefaultAsync(x => x.Id == interaction.Id);
|
||||
if (entityInteraction == null) return new(new Exception("Interaction not found"));
|
||||
|
||||
entityInteraction.Date = interaction.Date;
|
||||
entityInteraction.Info1 = interaction.Info1;
|
||||
entityInteraction.Info2 = interaction.Info2;
|
||||
entityInteraction.Feedback = interaction.Feedback;
|
||||
entityInteraction.FeedbackInfo = interaction.FeedbackInfo;
|
||||
entityInteraction.Alert = interaction.Alert;
|
||||
|
||||
var prospect = await Context.Prospects!.FirstOrDefaultAsync(x => x.Id == entityInteraction.ProspectID);
|
||||
if (prospect != null)
|
||||
{
|
||||
prospect.Modified = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
if (prospect != null)
|
||||
{
|
||||
await AuditService.Insert(AuditType.EditInteraction, prospect.Name, entityInteraction.Type.ToString());
|
||||
}
|
||||
|
||||
Context.Entry(entityInteraction).State = EntityState.Detached;
|
||||
if (prospect != null) Context.Entry(prospect).State = EntityState.Detached;
|
||||
|
||||
return new(entityInteraction);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new(e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method UpdateAsync
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -76,5 +76,5 @@
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<Button Color="Color.Secondary" Clicked="@ModalService.Hide">Abbrechen</Button>
|
||||
<Button Color="Color.Primary" Clicked="@AddInteractionAsync" Class="ml-2">OK</Button>
|
||||
<Button Color="Color.Primary" Clicked="@SaveAsync" Class="ml-2">@(IsEditMode ? "Speichern" : "OK")</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Blazorise;
|
||||
using FoodsharingSiegen.Contracts;
|
||||
using FoodsharingSiegen.Contracts.Entity;
|
||||
using FoodsharingSiegen.Contracts.Enums;
|
||||
using FoodsharingSiegen.Server.BaseClasses;
|
||||
@@ -7,7 +8,7 @@ using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace FoodsharingSiegen.Server.Dialogs
|
||||
{
|
||||
public record InteractionDialogParameter(InteractionType Type, Guid ProspectId, string HeaderText, Func<Task> OnSuccess);
|
||||
public record InteractionDialogParameter(InteractionType Type, Guid ProspectId, string HeaderText, Func<Task> OnSuccess, Interaction? ExistingInteraction = null);
|
||||
|
||||
public partial class InteractionDialog : FsBase
|
||||
{
|
||||
@@ -44,6 +45,19 @@ namespace FoodsharingSiegen.Server.Dialogs
|
||||
[Parameter]
|
||||
public bool ShowNotNeeded { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool IsEditMode { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method IsEditable
|
||||
|
||||
public static bool IsEditable(InteractionType type) => type switch
|
||||
{
|
||||
InteractionType.EinAb => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Method ShowAsync
|
||||
@@ -93,12 +107,29 @@ namespace FoodsharingSiegen.Server.Dialogs
|
||||
_ => false
|
||||
};
|
||||
|
||||
var interaction = new Interaction
|
||||
{
|
||||
Type = parameter.Type,
|
||||
Date = DateTime.UtcNow,
|
||||
ProspectID = parameter.ProspectId
|
||||
};
|
||||
var isEditMode = parameter.ExistingInteraction != null;
|
||||
var interaction = isEditMode
|
||||
? new Interaction
|
||||
{
|
||||
Id = parameter.ExistingInteraction!.Id,
|
||||
Type = parameter.ExistingInteraction.Type,
|
||||
Date = parameter.ExistingInteraction.Date,
|
||||
ProspectID = parameter.ExistingInteraction.ProspectID,
|
||||
UserID = parameter.ExistingInteraction.UserID,
|
||||
Info1 = parameter.ExistingInteraction.Info1,
|
||||
Info2 = parameter.ExistingInteraction.Info2,
|
||||
Feedback = parameter.ExistingInteraction.Feedback,
|
||||
FeedbackInfo = parameter.ExistingInteraction.FeedbackInfo,
|
||||
Alert = parameter.ExistingInteraction.Alert,
|
||||
NotNeeded = parameter.ExistingInteraction.NotNeeded,
|
||||
Created = parameter.ExistingInteraction.Created
|
||||
}
|
||||
: new Interaction
|
||||
{
|
||||
Type = parameter.Type,
|
||||
Date = DateTime.UtcNow,
|
||||
ProspectID = parameter.ProspectId
|
||||
};
|
||||
|
||||
await modalService.Show<InteractionDialog>(parameter.HeaderText, p =>
|
||||
{
|
||||
@@ -108,29 +139,41 @@ namespace FoodsharingSiegen.Server.Dialogs
|
||||
p.Add(nameof(Info2Name), info2Name);
|
||||
p.Add(nameof(ShowAlert), showAlert);
|
||||
p.Add(nameof(ShowNotNeeded), showNotNeeded);
|
||||
p.Add(nameof(IsEditMode), isEditMode);
|
||||
p.Add(nameof(OnSuccess), parameter.OnSuccess);
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Method AddInteractionAsync
|
||||
#region Private Method SaveAsync
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new interaction for the current user using the ProspectService and hides the modal.
|
||||
/// Saves the interaction (add or update) and hides the modal on success.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous operation.
|
||||
/// </returns>
|
||||
private async Task AddInteractionAsync()
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
Interaction.UserID = CurrentUser.Id;
|
||||
OperationResult<Interaction> result;
|
||||
|
||||
var addR = await ProspectService.AddInteraction(Interaction);
|
||||
if (IsEditMode)
|
||||
{
|
||||
result = await ProspectService.UpdateInteraction(Interaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Interaction.UserID = CurrentUser.Id;
|
||||
result = await ProspectService.AddInteraction(Interaction);
|
||||
}
|
||||
|
||||
await ModalService.Hide();
|
||||
|
||||
if (addR.Success && OnSuccess != null) await OnSuccess.Invoke();
|
||||
if (result.Success)
|
||||
{
|
||||
await ModalService.Hide();
|
||||
if (OnSuccess != null) await OnSuccess.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
await Notification.Error(result.Exception?.Message ?? "Unbekannter Fehler beim Speichern.", "Fehler");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user