fix(ui): refine amount entry, date warnings and booking actions
This commit is contained in:
@@ -26,15 +26,14 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<details class="entry-action-menu">
|
||||
<summary aria-label="@($"Aktionen für {Entry.DisplayId} {Entry.Title}")">Aktionen</summary>
|
||||
<button type="button" class="btn btn-outline-secondary" @onclick="() => OnEdit.InvokeAsync(Entry.Id)" aria-label="@($"{Entry.DisplayId} {Entry.Title} bearbeiten")">
|
||||
<i class="bi bi-pencil" aria-hidden="true"></i> Bearbeiten
|
||||
<div class="entry-action-buttons">
|
||||
<button type="button" class="btn btn-outline-secondary" title="Buchung bearbeiten" @onclick="() => OnEdit.InvokeAsync(Entry.Id)" aria-label="@($"{Entry.DisplayId} {Entry.Title} bearbeiten")">
|
||||
<i class="bi bi-pencil" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger" @onclick="() => OnDelete.InvokeAsync(Entry.Id)" aria-label="@($"{Entry.DisplayId} {Entry.Title} löschen")">
|
||||
<i class="bi bi-trash" aria-hidden="true"></i> Löschen
|
||||
<button type="button" class="btn btn-outline-danger" title="Buchung löschen" @onclick="() => OnDelete.InvokeAsync(Entry.Id)" aria-label="@($"{Entry.DisplayId} {Entry.Title} löschen")">
|
||||
<i class="bi bi-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
</details>
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
}
|
||||
@if (Entries.Any(e => e.IsDeleted))
|
||||
{
|
||||
<label class="history-toggle"><input type="checkbox" @bind="showDeleted" /> Gelöschte Buchungen anzeigen (@Entries.Count(e => e.IsDeleted))</label>
|
||||
<div class="form-check form-switch history-toggle">
|
||||
<input id="show-deleted" class="form-check-input" type="checkbox" role="switch" @bind="showDeleted" />
|
||||
<label class="form-check-label" for="show-deleted">Gelöschte Buchungen anzeigen (@Entries.Count(e => e.IsDeleted))</label>
|
||||
</div>
|
||||
}
|
||||
<div class="table-responsive entry-list">
|
||||
<table class="table table-hover table-sm align-middle">
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<input id="@Id" type="text" inputmode="decimal" class="form-control" data-amount-guard
|
||||
data-allow-negative="@AllowNegative.ToString().ToLowerInvariant()"
|
||||
@bind:get="Value" @bind:set="SetValue" @bind:event="oninput" disabled="@Disabled"
|
||||
aria-describedby="@(Id + "-rejected")" />
|
||||
<span id="@(Id + "-rejected")" class="input-rejection text-danger" role="status">@rejection</span>
|
||||
@bind:get="Value" @bind:set="SetValue" @bind:event="oninput" disabled="@Disabled" />
|
||||
|
||||
@code {
|
||||
[Parameter] public string Id { get; set; } = "amount";
|
||||
@@ -10,15 +8,12 @@
|
||||
[Parameter] public EventCallback<string> ValueChanged { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
[Parameter] public bool AllowNegative { get; set; }
|
||||
private string? rejection;
|
||||
private async Task SetValue(string value)
|
||||
{
|
||||
if (!BookingInput.IsAmountDraft(value, AllowNegative))
|
||||
{
|
||||
rejection = "Diese Eingabe wurde nicht übernommen. Bitte einen Betrag wie 12,50 eingeben.";
|
||||
return;
|
||||
}
|
||||
rejection = null;
|
||||
await ValueChanged.InvokeAsync(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<input @attributes="Attributes" id="@Id" type="date" value="@renderedValue" @oninput="Input" @onblur="CommitAsync" />
|
||||
|
||||
@code {
|
||||
[Parameter] public string Id { get; set; } = "booking-date";
|
||||
[Parameter] public string Value { get; set; } = string.Empty;
|
||||
[Parameter] public EventCallback<string> ValueChanged { get; set; }
|
||||
[Parameter] public EventCallback<bool> PendingChangesChanged { get; set; }
|
||||
[Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? Attributes { get; set; }
|
||||
private string? lastValue;
|
||||
private string renderedValue = string.Empty;
|
||||
private string draft = string.Empty;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (Value != lastValue) draft = renderedValue = Value;
|
||||
lastValue = Value;
|
||||
}
|
||||
|
||||
private async Task Input(ChangeEventArgs args)
|
||||
{
|
||||
draft = args.Value?.ToString() ?? string.Empty;
|
||||
await PendingChangesChanged.InvokeAsync(draft != Value);
|
||||
}
|
||||
|
||||
public async Task CommitAsync()
|
||||
{
|
||||
// Native calendars close when their surrounding dialog layout changes. Validate only
|
||||
// after leaving the date field, so year warnings never interrupt calendar navigation.
|
||||
lastValue = draft;
|
||||
await ValueChanged.InvokeAsync(lastValue);
|
||||
await PendingChangesChanged.InvokeAsync(false);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@inject ToastService Toasts
|
||||
@inject IEntryService EntryService
|
||||
|
||||
<ModalDialog @ref="modal" Title="Einnahme oder Ausgabe" OnCancel="OnCancel" Busy="isSaving" Dirty="@(committedResult == null && IsDirty)">
|
||||
<ModalDialog @ref="modal" Title="Einnahme oder Ausgabe" OnCancel="OnCancel" Busy="isSaving" Dirty="@(committedResult == null && (IsDirty || pendingDateChanges))">
|
||||
<h5 id="entry-dialog-title">@(EditEntry != null ? "Buchung bearbeiten" : "Einnahme oder Ausgabe erfassen") – @AccountName</h5>
|
||||
|
||||
<form @onsubmit="Save" @onsubmit:preventDefault="true" aria-busy="@(isSaving ? "true" : "false")">
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<label class="form-label" for="entry-date">Datum</label>
|
||||
<div>
|
||||
<input id="entry-date" type="date" value="@dateText" @oninput="e => dateText = e.Value?.ToString() ?? string.Empty" aria-describedby="entry-date-help" aria-invalid="@(date == default ? "true" : "false")" />
|
||||
<BookingDateInput @ref="bookingDate" Id="entry-date" @bind-Value="dateText" PendingChangesChanged="pending => pendingDateChanges = pending" aria-describedby="entry-date-help" aria-invalid="@(date == default ? "true" : "false")" />
|
||||
@if (date == default) { <p id="entry-date-help" class="text-danger">Bitte ein gültiges Datum eingeben.</p> }
|
||||
</div>
|
||||
|
||||
@@ -43,9 +43,12 @@
|
||||
|
||||
@if (OutsideYear && date != default)
|
||||
{
|
||||
<div class="alert alert-warning" role="status">
|
||||
Datum @date.ToString("dd.MM.yyyy"): Diese Buchung liegt außerhalb von Jahr @ContextYear und wird dort nicht angezeigt.
|
||||
<div class="alert alert-warning booking-year-warning" role="status">
|
||||
<i class="bi bi-exclamation-triangle-fill" aria-hidden="true"></i>
|
||||
<div><strong>Das Buchungsdatum liegt in einem anderen Jahr.</strong>
|
||||
<p>Du erfasst die Buchung am @date.ToString("dd.MM.yyyy") und damit außerhalb des angezeigten Jahres @ContextYear. Sie wird im Jahr @date.Year gespeichert und erscheint erst, wenn du dieses Jahr oder „Alle Buchungen“ auswählst.</p>
|
||||
<label><input type="checkbox" checked="@(confirmDifferentYear && confirmedYear == date.Year)" @onchange="e => { confirmDifferentYear = e.Value is true; confirmedYear = date.Year; }" /> Ich möchte die Buchung im Jahr @date.Year speichern.</label>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -59,4 +62,4 @@
|
||||
</form>
|
||||
</ModalDialog>
|
||||
|
||||
@code { private ModalDialog? modal; }
|
||||
@code { private ModalDialog? modal; private BookingDateInput? bookingDate; private bool pendingDateChanges; }
|
||||
|
||||
@@ -76,6 +76,8 @@ public partial class AddEntryDialog
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
if (isSaving || completed) return;
|
||||
if (committedResult == null && bookingDate != null) await bookingDate.CommitAsync();
|
||||
if (isSaving || completed || (committedResult == null && !CanSave)) return;
|
||||
var busyToast = Toasts.Show("Wird gespeichert …", persistent: true);
|
||||
isSaving = true;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@inject IEntryService EntryService
|
||||
@inject IAccountService AccountService
|
||||
|
||||
<ModalDialog @ref="modal" Title="Geld zwischen Konten verschieben" OnCancel="OnCancel" Busy="isSaving" Dirty="@(committedResult == null && IsDirty)">
|
||||
<ModalDialog @ref="modal" Title="Geld zwischen Konten verschieben" OnCancel="OnCancel" Busy="isSaving" Dirty="@(committedResult == null && (IsDirty || pendingDateChanges))">
|
||||
<h5 class="mb-3">@(EditEntry != null ? "Umbuchung bearbeiten" : "Neue Umbuchung")</h5>
|
||||
|
||||
<p class="text-muted">Erfasst nur die Buchung. Führt keine Banküberweisung aus.</p>
|
||||
@@ -30,7 +30,7 @@
|
||||
</select>
|
||||
|
||||
<label class="form-label" for="transfer-date">Datum</label>
|
||||
<input id="transfer-date" type="date" class="form-control" value="@dateText" @oninput="e => dateText = e.Value?.ToString() ?? string.Empty" />
|
||||
<BookingDateInput @ref="bookingDate" Id="transfer-date" class="form-control" @bind-Value="dateText" PendingChangesChanged="pending => pendingDateChanges = pending" />
|
||||
|
||||
<label class="form-label" for="transfer-title">Bezeichnung</label>
|
||||
<input id="transfer-title" type="text" class="form-control" @bind="title" @bind:event="oninput" placeholder="Beschreibung der Umbuchung" />
|
||||
@@ -40,11 +40,14 @@
|
||||
<p class="text-muted">Betrag ohne Minuszeichen.</p></div>
|
||||
</div>
|
||||
|
||||
@if (OutsideYear)
|
||||
@if (OutsideYear && date != default)
|
||||
{
|
||||
<div class="alert alert-warning" role="status">
|
||||
Datum @date.ToString("dd.MM.yyyy"): Diese Buchung liegt außerhalb von Jahr @ContextYear und wird dort nicht angezeigt.
|
||||
<div class="alert alert-warning booking-year-warning" role="status">
|
||||
<i class="bi bi-exclamation-triangle-fill" aria-hidden="true"></i>
|
||||
<div><strong>Das Buchungsdatum liegt in einem anderen Jahr.</strong>
|
||||
<p>Du erfasst die Buchung am @date.ToString("dd.MM.yyyy") und damit außerhalb des angezeigten Jahres @ContextYear. Sie wird im Jahr @date.Year gespeichert und erscheint erst, wenn du dieses Jahr oder „Alle Buchungen“ auswählst.</p>
|
||||
<label><input type="checkbox" checked="@(confirmDifferentYear && confirmedYear == date.Year)" @onchange="e => { confirmDifferentYear = e.Value is true; confirmedYear = date.Year; }" /> Ich möchte die Buchung im Jahr @date.Year speichern.</label>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -58,4 +61,4 @@
|
||||
</form>
|
||||
</ModalDialog>
|
||||
|
||||
@code { private ModalDialog? modal; }
|
||||
@code { private ModalDialog? modal; private BookingDateInput? bookingDate; private bool pendingDateChanges; }
|
||||
|
||||
@@ -91,6 +91,8 @@ public partial class AddTransferDialog
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
if (isSaving || completed) return;
|
||||
if (committedResult == null && bookingDate != null) await bookingDate.CommitAsync();
|
||||
if (isSaving || completed || (committedResult == null && !CanSave)) return;
|
||||
var busyToast = Toasts.Show("Wird gespeichert …", persistent: true);
|
||||
isSaving = true;
|
||||
|
||||
Reference in New Issue
Block a user