Refactor receiver management in wizard popup
Replaced `DxTagBox` with a table-like structure for managing receivers, enabling direct editing of `FullName` and `EmailAddress` fields. Added validation for required fields and duplicate email addresses. Updated `RemoveReceiver` functionality with a new button and improved accessibility. Removed `_receiverTags` and related logic, simplifying state management. Introduced new methods for handling receiver updates. Updated CSS to style the new layout and components. Improved code clarity and user experience.
This commit is contained in:
@@ -333,49 +333,52 @@
|
||||
else
|
||||
{
|
||||
<div class="sender-create-wizard__receiver-tagbox-wrap">
|
||||
<label class="sender-create-wizard__label sender-create-wizard__label-row" for="wizardReceiversTagBox">
|
||||
<label class="sender-create-wizard__label sender-create-wizard__label-row">
|
||||
<span>@L("Receiver", "Empfänger")</span>
|
||||
<MudTooltip Text='@L("SenderWizard_Help_ReceiverList", "Hier sehen und verwalten Sie alle aktuell hinzugefuegten Empfaenger.")'>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Info" Color="Color.Tertiary" Size="Size.Small" Class="sender-create-wizard__help-icon" aria-label='@L("SenderWizard_HelpAria_ReceiverList", "Hilfe zur Empfaengerliste")' />
|
||||
</MudTooltip>
|
||||
</label>
|
||||
<DxTagBox Data="@_receivers"
|
||||
TData="CreateEnvelopeWizardReceiver"
|
||||
TValue="CreateEnvelopeWizardReceiver"
|
||||
AllowCustomTags="true"
|
||||
@bind-Tags="@_receiverTags"
|
||||
@bind-Tags:after="OnReceiverTagsChanged"
|
||||
HideSelectedItems="false"
|
||||
TextFieldName="@nameof(CreateEnvelopeWizardReceiver.EmailAddress)"
|
||||
ListRenderMode="ListRenderMode.Virtual"
|
||||
SearchMode="ListSearchMode.AutoSearch"
|
||||
SearchFilterCondition="ListSearchFilterCondition.Contains"
|
||||
SearchTextParseMode="ListSearchTextParseMode.GroupWordsByAnd"
|
||||
CssClass="sender-create-wizard__receiver-tagbox"
|
||||
InputId="wizardReceiversTagBox"
|
||||
Enabled="@(!IsActionLocked)">
|
||||
<TagDisplayTemplate Context="tagContext">
|
||||
<div class="sender-create-wizard__receiver-tag">
|
||||
@if (tagContext.DataItem is CreateEnvelopeWizardReceiver receiver)
|
||||
{
|
||||
<span class="sender-create-wizard__receiver-tag-name">@receiver.FullName</span>
|
||||
<span class="sender-create-wizard__receiver-tag-email">@receiver.EmailAddress</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="sender-create-wizard__receiver-tag-email">@tagContext.DisplayText</span>
|
||||
}
|
||||
|
||||
<div class="sender-create-wizard__receiver-list" role="table" aria-label="Added receivers">
|
||||
@for (var i = 0; i < _receivers.Count; i++)
|
||||
{
|
||||
var rowIndex = i;
|
||||
var receiver = _receivers[rowIndex];
|
||||
|
||||
<div class="sender-create-wizard__receiver-item" role="row">
|
||||
<div class="sender-create-wizard__form-grid sender-create-wizard__field--full sender-create-wizard__receiver-item-fields">
|
||||
<div class="sender-create-wizard__field">
|
||||
<DxTextBox Text="@receiver.FullName"
|
||||
TextChanged="@(value => OnReceiverListNameChanged(rowIndex, value))"
|
||||
BindValueMode="BindValueMode.OnInput"
|
||||
NullText='@L("FullName", "Vor- und Nachname")'
|
||||
CssClass="w-100"
|
||||
Enabled="@(!IsActionLocked)" />
|
||||
</div>
|
||||
<div class="sender-create-wizard__field">
|
||||
<DxTextBox Text="@receiver.EmailAddress"
|
||||
TextChanged="@(value => OnReceiverListEmailChanged(rowIndex, value))"
|
||||
BindValueMode="BindValueMode.OnInput"
|
||||
NullText='@L("EmailAddress", "E-Mail-Adresse")'
|
||||
CssClass="w-100"
|
||||
Enabled="@(!IsActionLocked)" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="button"
|
||||
class="sender-create-wizard__receiver-tag-remove"
|
||||
@onclick="tagContext.RemoveTagAction"
|
||||
aria-label="Remove receiver"
|
||||
disabled="@IsActionLocked">x</button>
|
||||
class="sender-create-wizard__receiver-remove-btn"
|
||||
@onclick="() => RemoveReceiver(receiver)"
|
||||
disabled="@IsActionLocked"
|
||||
title='@L("Remove", "Entfernen")'
|
||||
aria-label='@L("Remove", "Entfernen")'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" />
|
||||
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</TagDisplayTemplate>
|
||||
<EmptyDataAreaTemplate Context="emptyContext">
|
||||
<div class="sender-create-wizard__tagbox-empty"></div>
|
||||
</EmptyDataAreaTemplate>
|
||||
</DxTagBox>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -520,7 +523,6 @@
|
||||
private string _receiverEmail = string.Empty;
|
||||
private string _receiverSuggestionFilterText = string.Empty;
|
||||
private readonly List<CreateEnvelopeWizardReceiver> _receivers = [];
|
||||
private IEnumerable<string> _receiverTags = [];
|
||||
private readonly List<RecentReceiverSuggestionDto> _recentReceiverSuggestions = [];
|
||||
private bool _isLoadingRecentSuggestions;
|
||||
private bool _recentSuggestionsLoaded;
|
||||
@@ -676,7 +678,6 @@
|
||||
}
|
||||
|
||||
_receivers.Add(new CreateEnvelopeWizardReceiver(fullName, email));
|
||||
_receiverTags = _receivers.Select(x => x.EmailAddress).ToList();
|
||||
_receiverName = string.Empty;
|
||||
_receiverEmail = string.Empty;
|
||||
_validationMessage = null;
|
||||
@@ -689,17 +690,28 @@
|
||||
return Task.CompletedTask;
|
||||
|
||||
_receivers.Remove(receiver);
|
||||
_receiverTags = _receivers.Select(x => x.EmailAddress).ToList();
|
||||
_validationMessage = null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task OnReceiverTagsChanged()
|
||||
private Task OnReceiverListNameChanged(int index, string? value)
|
||||
{
|
||||
var selectedEmails = _receiverTags.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
if (IsActionLocked || index < 0 || index >= _receivers.Count)
|
||||
return Task.CompletedTask;
|
||||
|
||||
_receivers.RemoveAll(x => !selectedEmails.Contains(x.EmailAddress));
|
||||
_receiverTags = _receivers.Select(x => x.EmailAddress).ToList();
|
||||
var current = _receivers[index];
|
||||
_receivers[index] = current with { FullName = value ?? string.Empty };
|
||||
_validationMessage = null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task OnReceiverListEmailChanged(int index, string? value)
|
||||
{
|
||||
if (IsActionLocked || index < 0 || index >= _receivers.Count)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var current = _receivers[index];
|
||||
_receivers[index] = current with { EmailAddress = value ?? string.Empty };
|
||||
_validationMessage = null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -1005,6 +1017,41 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _receivers.Count; i++)
|
||||
{
|
||||
var receiver = _receivers[i];
|
||||
var fullName = receiver.FullName.Trim();
|
||||
var email = receiver.EmailAddress.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fullName))
|
||||
{
|
||||
_validationMessage = L("SenderEditor_Validation_FullNameRequired", "Bitte geben Sie einen Vor- und Nachnamen ein.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(email))
|
||||
{
|
||||
_validationMessage = L("SenderEditor_Validation_EmailRequired", "Bitte geben Sie eine E-Mail-Adresse ein.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EmailValidator.IsValid(email))
|
||||
{
|
||||
_validationMessage = L("SenderEditor_Validation_EmailInvalid", "Bitte geben Sie eine gültige E-Mail-Adresse ein.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var duplicateEmailExists = _receivers
|
||||
.GroupBy(x => x.EmailAddress.Trim(), StringComparer.OrdinalIgnoreCase)
|
||||
.Any(group => group.Count() > 1);
|
||||
|
||||
if (duplicateEmailExists)
|
||||
{
|
||||
_validationMessage = L("SenderEditor_Validation_EmailDuplicate", "Diese E-Mail-Adresse wurde bereits hinzugefügt.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1232,7 +1279,6 @@
|
||||
_receiverEmail = string.Empty;
|
||||
_receiverSuggestionFilterText = string.Empty;
|
||||
_receivers.Clear();
|
||||
_receiverTags = [];
|
||||
_recentReceiverSuggestions.Clear();
|
||||
_isLoadingRecentSuggestions = false;
|
||||
_recentSuggestionsLoaded = false;
|
||||
|
||||
@@ -1513,6 +1513,11 @@
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.sender-create-wizard__receiver-item-fields {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sender-create-wizard__receiver-name {
|
||||
font-size: 0.86rem;
|
||||
font-weight: 700;
|
||||
@@ -1534,6 +1539,30 @@
|
||||
padding: 0.25rem 0.55rem;
|
||||
}
|
||||
|
||||
.sender-create-wizard__receiver-remove-btn {
|
||||
flex: 0 0 auto;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid rgba(220, 38, 38, 0.2);
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: #b91c1c;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sender-create-wizard__receiver-remove-btn:hover:not(:disabled) {
|
||||
background: rgba(220, 38, 38, 0.08);
|
||||
}
|
||||
|
||||
.sender-create-wizard__receiver-remove-btn:disabled {
|
||||
opacity: 0.55;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.sender-create-wizard__upload-stage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1704,6 +1733,11 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sender-create-wizard__receiver-remove-btn {
|
||||
width: auto;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.sender-pdf-dropzone {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user