feat(usability): confirm saves and guard booking submissions against duplicates (U07)

This commit is contained in:
2026-11-02 06:38:42 +01:00
parent 76ba79b63e
commit 22e509d982
17 changed files with 373 additions and 118 deletions
@@ -14,7 +14,7 @@ public partial class AddTransferDialog
public EntryDto? EditEntry { get; set; }
[Parameter]
public EventCallback<DateTime> OnSave { get; set; }
public EventCallback<BookingSaved> OnSave { get; set; }
[Parameter]
public EventCallback OnCancel { get; set; }
@@ -26,6 +26,12 @@ public partial class AddTransferDialog
private int? confirmedYear;
private bool OutsideYear => ContextYear.HasValue && date.Year != ContextYear.Value;
private bool isSaving;
private bool completed;
private BookingSaved? committedResult;
private string? saveError;
private string? saveDetails;
#region Fields
private List<AccountSummaryDto> accounts = new();
@@ -74,20 +80,40 @@ public partial class AddTransferDialog
private async Task Save()
{
if (!CanSave) return;
if (EditEntry != null)
if (isSaving || completed || (committedResult == null && !CanSave)) return;
isSaving = true;
saveError = null;
saveDetails = null;
try
{
await EntryService.UpdateTransferAsync(EditEntry.Id, targetAccountId, date, title.Trim(), amount);
if (committedResult == null)
{
var id = EditEntry?.Id ?? 0;
if (EditEntry != null)
await EntryService.UpdateTransferAsync(EditEntry.Id, targetAccountId, date, title.Trim(), amount);
else
id = await EntryService.CreateTransferAsync(SourceAccountId, targetAccountId, date, title.Trim(), amount);
committedResult = new(id, date, "Umbuchung", amount, EditEntry != null);
}
// If refreshing the page fails after commit, retry only notification,
// never create a second booking.
await OnSave.InvokeAsync(committedResult);
completed = true;
}
else
catch (Exception ex)
{
await EntryService.CreateTransferAsync(SourceAccountId, targetAccountId, date, title.Trim(), amount);
saveError = committedResult == null
? "Speichern fehlgeschlagen. Die Eingaben bleiben erhalten. Bitte erneut versuchen."
: "Die Buchung ist gespeichert. Die Ansicht konnte nicht aktualisiert werden. Bitte Ansicht aktualisieren; es wird keine weitere Buchung erzeugt.";
saveDetails = ex.Message;
}
await OnSave.InvokeAsync(date);
finally { isSaving = false; }
}
private async Task Cancel() => await OnCancel.InvokeAsync();
private async Task Cancel()
{
if (!isSaving) await OnCancel.InvokeAsync();
}
#endregion
}