feat(sender): add envelope delete popup and client call
This commit is contained in:
@@ -12,6 +12,8 @@ namespace EnvelopeGenerator.Server.Client.Services;
|
|||||||
public class EnvelopeService(IHttpClientFactory clientFactory)
|
public class EnvelopeService(IHttpClientFactory clientFactory)
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web);
|
private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web);
|
||||||
|
private sealed record DeleteEnvelopeRequest(string Reason);
|
||||||
|
private sealed record ErrorResponse(string? Message);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetches envelopes from the API with optional filters.
|
/// Fetches envelopes from the API with optional filters.
|
||||||
@@ -61,4 +63,35 @@ public class EnvelopeService(IHttpClientFactory clientFactory)
|
|||||||
|
|
||||||
return await response.Content.ReadFromJsonAsync<IEnumerable<EnvelopeDto>>(_jsonOptions, cancel);
|
return await response.Content.ReadFromJsonAsync<IEnumerable<EnvelopeDto>>(_jsonOptions, cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(int envelopeId, string reason, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var httpClient = clientFactory.CreateClient("EnvelopeGenerator.Server");
|
||||||
|
using var request = new HttpRequestMessage(HttpMethod.Delete, $"/api/Envelope/{envelopeId}")
|
||||||
|
{
|
||||||
|
Content = JsonContent.Create(new DeleteEnvelopeRequest(reason), options: _jsonOptions)
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await httpClient.SendAsync(request, cancel);
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string? message = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var error = await response.Content.ReadFromJsonAsync<ErrorResponse>(_jsonOptions, cancel);
|
||||||
|
message = error?.Message;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
message = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var statusCode = (int)response.StatusCode;
|
||||||
|
var reasonPhrase = response.ReasonPhrase ?? "Unknown error";
|
||||||
|
throw new HttpRequestException(
|
||||||
|
$"Failed to delete envelope. Status: {statusCode} ({reasonPhrase}){(string.IsNullOrWhiteSpace(message) ? string.Empty : $" - {message}")}",
|
||||||
|
null,
|
||||||
|
response.StatusCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -521,6 +521,54 @@
|
|||||||
</BodyContentTemplate>
|
</BodyContentTemplate>
|
||||||
</DxPopup>
|
</DxPopup>
|
||||||
|
|
||||||
|
<DxPopup @bind-Visible="@_deletePopupVisible"
|
||||||
|
HeaderText="Umschlag löschen"
|
||||||
|
Width="560px"
|
||||||
|
MaxWidth="95vw"
|
||||||
|
ShowFooter="true"
|
||||||
|
CloseOnOutsideClick="false"
|
||||||
|
ShowCloseButton="true"
|
||||||
|
CloseOnEscape="true"
|
||||||
|
ShowHeader="true"
|
||||||
|
CssClass="sender-delete-popup">
|
||||||
|
<BodyContentTemplate>
|
||||||
|
<div style="display:flex;flex-direction:column;gap:0.75rem;">
|
||||||
|
<p style="margin:0;color:#374151;font-size:0.9rem;line-height:1.45;">
|
||||||
|
Bitte geben Sie einen Grund an, warum der Umschlag gelöscht/zurückgezogen werden soll.
|
||||||
|
</p>
|
||||||
|
@if (_selectedEnvelope is not null)
|
||||||
|
{
|
||||||
|
<p style="margin:0;color:#6b7280;font-size:0.825rem;">
|
||||||
|
Umschlag: <strong>#@_selectedEnvelope.Id - @_selectedEnvelope.Title</strong>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<DxMemo @bind-Text="_deleteReason"
|
||||||
|
NullText="Begründung eingeben..."
|
||||||
|
Rows="5"
|
||||||
|
MaxLength="2000"
|
||||||
|
CssClass="w-100" />
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_deleteErrorMessage))
|
||||||
|
{
|
||||||
|
<div class="alert alert-danger" style="margin:0;padding:0.5rem 0.75rem;font-size:0.85rem;">
|
||||||
|
@_deleteErrorMessage
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</BodyContentTemplate>
|
||||||
|
<FooterContentTemplate>
|
||||||
|
<div style="display:flex;justify-content:flex-end;gap:0.5rem;width:100%;">
|
||||||
|
<DxButton Text="Abbrechen"
|
||||||
|
RenderStyle="ButtonRenderStyle.Secondary"
|
||||||
|
Click="CloseDeletePopup"
|
||||||
|
Enabled="@(!_isDeletingEnvelope)" />
|
||||||
|
<DxButton Text="Löschen"
|
||||||
|
RenderStyle="ButtonRenderStyle.Danger"
|
||||||
|
Click="ConfirmDeleteEnvelopeAsync"
|
||||||
|
Enabled="@(!_isDeletingEnvelope)" />
|
||||||
|
</div>
|
||||||
|
</FooterContentTemplate>
|
||||||
|
</DxPopup>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private IEnumerable<EnvelopeDto>? _allEnvelopes;
|
private IEnumerable<EnvelopeDto>? _allEnvelopes;
|
||||||
private IEnumerable<EnvelopeDto>? _activeEnvelopes;
|
private IEnumerable<EnvelopeDto>? _activeEnvelopes;
|
||||||
@@ -549,6 +597,11 @@
|
|||||||
private bool _exportSelectedRowsOnly = false;
|
private bool _exportSelectedRowsOnly = false;
|
||||||
private bool _isExporting = false;
|
private bool _isExporting = false;
|
||||||
|
|
||||||
|
private bool _deletePopupVisible = false;
|
||||||
|
private bool _isDeletingEnvelope = false;
|
||||||
|
private string _deleteReason = string.Empty;
|
||||||
|
private string? _deleteErrorMessage;
|
||||||
|
|
||||||
// Stable keys used to store/restore each grid's layout in UserPreferences.GridLayouts
|
// Stable keys used to store/restore each grid's layout in UserPreferences.GridLayouts
|
||||||
private const string GridKeyActive = "sender.active-envelopes";
|
private const string GridKeyActive = "sender.active-envelopes";
|
||||||
private const string GridKeyCompleted = "sender.completed-envelopes";
|
private const string GridKeyCompleted = "sender.completed-envelopes";
|
||||||
@@ -681,7 +734,51 @@
|
|||||||
void DeleteEnvelope()
|
void DeleteEnvelope()
|
||||||
{
|
{
|
||||||
if (_selectedEnvelope == null) return;
|
if (_selectedEnvelope == null) return;
|
||||||
// TODO: Show delete confirmation dialog
|
_deleteReason = string.Empty;
|
||||||
|
_deleteErrorMessage = null;
|
||||||
|
_deletePopupVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseDeletePopup()
|
||||||
|
{
|
||||||
|
if (_isDeletingEnvelope)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_deletePopupVisible = false;
|
||||||
|
_deleteErrorMessage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task ConfirmDeleteEnvelopeAsync()
|
||||||
|
{
|
||||||
|
if (_selectedEnvelope is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_deleteReason))
|
||||||
|
{
|
||||||
|
_deleteErrorMessage = "Bitte geben Sie einen Löschgrund an.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDeletingEnvelope = true;
|
||||||
|
_deleteErrorMessage = null;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await EnvelopeService.DeleteAsync(_selectedEnvelope.Id, _deleteReason.Trim());
|
||||||
|
_deletePopupVisible = false;
|
||||||
|
_selectedEnvelope = null;
|
||||||
|
await LoadEnvelopesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_deleteErrorMessage = ex.Message;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isDeletingEnvelope = false;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task LogoutAsync()
|
async Task LogoutAsync()
|
||||||
|
|||||||
Reference in New Issue
Block a user