feat(usability): explain booking fields and validate decimal input (U06)
This commit is contained in:
@@ -1,30 +1,45 @@
|
||||
@inject IEntryService EntryService
|
||||
|
||||
<div class="dialog-backdrop">
|
||||
<div class="dialog-content" @onclick:stopPropagation="true">
|
||||
<h5>@(EditEntry != null ? "Buchung bearbeiten" : "Neue Buchung")</h5>
|
||||
<div class="dialog-content booking-dialog" role="dialog" aria-modal="true" aria-labelledby="entry-dialog-title">
|
||||
<h5 id="entry-dialog-title">@(EditEntry != null ? "Buchung bearbeiten" : "Einnahme oder Ausgabe erfassen") – @AccountName</h5>
|
||||
|
||||
<div class="form-container">
|
||||
<label class="form-label" for="entry-type">Art</label>
|
||||
@if (EditEntry == null)
|
||||
{
|
||||
<label class="form-label">Art</label>
|
||||
<select @bind="entryType">
|
||||
<select id="entry-type" @bind="entryType">
|
||||
<option value="@EntryType.Income">Einnahme</option>
|
||||
<option value="@EntryType.Expense">Ausgabe</option>
|
||||
</select>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div><strong>@(entryType == EntryType.Income ? "Einnahme" : "Ausgabe")</strong>
|
||||
<p class="text-muted">Falsche Art? Diese Buchung abbrechen, in der Kontoliste löschen und mit der richtigen Art neu erfassen.</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<label class="form-label">Datum</label>
|
||||
<input type="date" @bind="date" />
|
||||
<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")" />
|
||||
@if (date == default) { <p id="entry-date-help" class="text-danger">Bitte ein gültiges Datum eingeben.</p> }
|
||||
</div>
|
||||
|
||||
<label class="form-label">Bezeichnung</label>
|
||||
<input type="text" class="form-control" @bind="title" placeholder="Beschreibung" />
|
||||
<label class="form-label" for="entry-title">Beschreibung</label>
|
||||
<div>
|
||||
<input id="entry-title" type="text" class="form-control" @bind="title" @bind:event="oninput" placeholder="z. B. Mitgliedsbeitrag oder Porto" aria-describedby="entry-title-help" aria-invalid="@(string.IsNullOrWhiteSpace(title) ? "true" : "false")" />
|
||||
@if (string.IsNullOrWhiteSpace(title)) { <p id="entry-title-help" class="text-danger">Bitte beschreiben, wofür das Geld eingenommen oder ausgegeben wurde.</p> }
|
||||
</div>
|
||||
|
||||
<label class="form-label">Betrag (€)</label>
|
||||
<input type="number" class="form-control no-spinner" @bind="amount" step="0.01" min="0.01" />
|
||||
<label class="form-label" for="entry-amount">Betrag (€)</label>
|
||||
<div>
|
||||
<input id="entry-amount" type="text" inputmode="decimal" class="form-control" @bind="amountText" @bind:event="oninput" placeholder="12,50" aria-describedby="entry-amount-help" aria-invalid="@(!ValidAmount ? "true" : "false")" />
|
||||
<p id="entry-amount-help" class="@(!ValidAmount ? "text-danger" : "text-muted")">Betrag ohne Minuszeichen, größer als 0. Komma oder Punkt, höchstens zwei Nachkommastellen. Keine Tausendertrenner oder Währungszeichen, auch beim Einfügen. Es wird nicht gerundet.</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.
|
||||
@@ -33,12 +48,10 @@
|
||||
}
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button class="btn btn-success" @onclick="Save"
|
||||
disabled="@(string.IsNullOrWhiteSpace(title) || amount <= 0 || (OutsideYear && (!confirmDifferentYear || confirmedYear != date.Year)))">
|
||||
<i class="bi bi-check-lg"></i> @(EditEntry != null ? "Speichern" : "Hinzufügen")
|
||||
<button class="btn btn-success" @onclick="Save" disabled="@(!CanSave)">
|
||||
<i class="bi bi-check-lg"></i> Speichern
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary" @onclick="Cancel"><i class="bi bi-x-lg"></i> Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -27,12 +27,20 @@ public partial class AddEntryDialog
|
||||
private int? confirmedYear;
|
||||
private bool OutsideYear => ContextYear.HasValue && date.Year != ContextYear.Value;
|
||||
|
||||
[Parameter] public string AccountName { get; set; } = string.Empty;
|
||||
private bool CanSave => !string.IsNullOrWhiteSpace(title) && ValidAmount && date != default && (!OutsideYear || (confirmDifferentYear && confirmedYear == date.Year));
|
||||
|
||||
#region Fields
|
||||
|
||||
private EntryType entryType = EntryType.Income;
|
||||
private DateTime date = DateTime.Today;
|
||||
private string dateText = DateTime.Today.ToString("yyyy-MM-dd");
|
||||
private DateTime date => Duempelkas.App.Services.BookingInput.Date(dateText);
|
||||
private string title = string.Empty;
|
||||
private decimal amount;
|
||||
private string amountText = string.Empty;
|
||||
private bool ValidAmount => Duempelkas.App.Services.BookingInput.TryAmount(amountText, out _);
|
||||
private decimal amount => Duempelkas.App.Services.BookingInput.TryAmount(amountText, out var value) ? value : 0;
|
||||
private bool initialized;
|
||||
private int? initializedEntryId;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -40,12 +48,15 @@ public partial class AddEntryDialog
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (initialized && initializedEntryId == EditEntry?.Id) return;
|
||||
initialized = true;
|
||||
initializedEntryId = EditEntry?.Id;
|
||||
if (EditEntry != null)
|
||||
{
|
||||
entryType = EditEntry.Type;
|
||||
date = EditEntry.Date;
|
||||
dateText = EditEntry.Date.ToString("yyyy-MM-dd");
|
||||
title = EditEntry.Title;
|
||||
amount = EditEntry.Amount;
|
||||
amountText = EditEntry.Amount.ToString("0.00", System.Globalization.CultureInfo.GetCultureInfo("de-DE"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +66,7 @@ public partial class AddEntryDialog
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
if (OutsideYear && (!confirmDifferentYear || confirmedYear != date.Year)) return;
|
||||
if (!CanSave) return;
|
||||
if (EditEntry != null)
|
||||
{
|
||||
await EntryService.UpdateEntryAsync(EditEntry.Id, date, title.Trim(), amount);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@inject IAccountService AccountService
|
||||
|
||||
<div class="dialog-backdrop">
|
||||
<div class="dialog-content" @onclick:stopPropagation="true">
|
||||
<div class="dialog-content booking-dialog" @onclick:stopPropagation="true">
|
||||
<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>
|
||||
@@ -18,13 +18,14 @@
|
||||
</select>
|
||||
|
||||
<label class="form-label">Datum</label>
|
||||
<input type="date" class="form-control" @bind="date" />
|
||||
<input type="date" class="form-control" value="@dateText" @oninput="e => dateText = e.Value?.ToString() ?? string.Empty" />
|
||||
|
||||
<label class="form-label">Bezeichnung</label>
|
||||
<input type="text" class="form-control" @bind="title" placeholder="Beschreibung der Umbuchung" />
|
||||
<input type="text" class="form-control" @bind="title" @bind:event="oninput" placeholder="Beschreibung der Umbuchung" />
|
||||
|
||||
<label class="form-label">Betrag (€)</label>
|
||||
<input type="number" class="form-control no-spinner" @bind="amount" step="0.01" min="0.01" />
|
||||
<div><input type="text" inputmode="decimal" class="form-control" @bind="amountText" @bind:event="oninput" aria-describedby="transfer-amount-help" />
|
||||
<p id="transfer-amount-help" class="@(!ValidAmount ? "text-danger" : "text-muted")">Betrag ohne Minuszeichen, größer als 0; Komma oder Punkt, höchstens zwei Nachkommastellen. Keine Tausendertrenner oder Währungszeichen; keine Rundung.</p></div>
|
||||
</div>
|
||||
|
||||
@if (OutsideYear)
|
||||
|
||||
@@ -30,15 +30,20 @@ public partial class AddTransferDialog
|
||||
|
||||
private List<AccountSummaryDto> accounts = new();
|
||||
private int targetAccountId;
|
||||
private DateTime date = DateTime.Today;
|
||||
private string dateText = DateTime.Today.ToString("yyyy-MM-dd");
|
||||
private DateTime date => Duempelkas.App.Services.BookingInput.Date(dateText);
|
||||
private string title = string.Empty;
|
||||
private decimal amount;
|
||||
private string amountText = string.Empty;
|
||||
private bool ValidAmount => Duempelkas.App.Services.BookingInput.TryAmount(amountText, out _);
|
||||
private decimal amount => Duempelkas.App.Services.BookingInput.TryAmount(amountText, out var value) ? value : 0;
|
||||
private bool initialized;
|
||||
private int? initializedEntryId;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
private bool CanSave => targetAccountId > 0 && !string.IsNullOrWhiteSpace(title) && amount > 0;
|
||||
private bool CanSave => targetAccountId > 0 && targetAccountId != SourceAccountId && !string.IsNullOrWhiteSpace(title) && ValidAmount && date != default && (!OutsideYear || (confirmDifferentYear && confirmedYear == date.Year));
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -51,12 +56,15 @@ public partial class AddTransferDialog
|
||||
accounts = await AccountService.GetAllAccountsAsync();
|
||||
}
|
||||
|
||||
if (initialized && initializedEntryId == EditEntry?.Id) return;
|
||||
initialized = true;
|
||||
initializedEntryId = EditEntry?.Id;
|
||||
if (EditEntry != null)
|
||||
{
|
||||
targetAccountId = EditEntry.LinkedAccountId ?? 0;
|
||||
date = EditEntry.Date;
|
||||
dateText = EditEntry.Date.ToString("yyyy-MM-dd");
|
||||
title = EditEntry.Title;
|
||||
amount = EditEntry.Amount;
|
||||
amountText = EditEntry.Amount.ToString("0.00", System.Globalization.CultureInfo.GetCultureInfo("de-DE"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +74,7 @@ public partial class AddTransferDialog
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
if (OutsideYear && (!confirmDifferentYear || confirmedYear != date.Year)) return;
|
||||
if (!CanSave) return;
|
||||
if (EditEntry != null)
|
||||
{
|
||||
await EntryService.UpdateTransferAsync(EditEntry.Id, targetAccountId, date, title.Trim(), amount);
|
||||
|
||||
Reference in New Issue
Block a user