From 8d2222de8f16d2907884d5321a936550ba8123f6 Mon Sep 17 00:00:00 2001 From: beo3000 Date: Thu, 25 Dec 2025 21:36:43 +0100 Subject: [PATCH] select Person and Quick-Assign expenses --- docs/prompts.md | 4 +- .../Pages/Days/AddPersonExpenseDialog.razor | 11 ++ .../Components/Pages/Days/DayDetails.razor | 120 +++++++++++++++--- .../Pages/Days/DayDetails.razor.css | 21 +++ 4 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 src/Koogle.Web/Components/Pages/Days/DayDetails.razor.css diff --git a/docs/prompts.md b/docs/prompts.md index 1eb2336..24f261d 100644 --- a/docs/prompts.md +++ b/docs/prompts.md @@ -1,7 +1,7 @@ ## Optimierung Spieltag-Details -Die Erfassung von Strafen muss möglichst schnell und komfortabel mögiich sein. Dafür muss die Seite DayDetails opimiert werden. -Es soll möglich sein eine Person in der Teilnehmerliste auszuwählen. Bei der Erfassung einer neuen Strafe kann diese Person direkt vorbelegt werden. Außerdem soll zusätzlich zum Button "Strafe hinzufügen" ein Menü-Button angezeigt werden, der eine Kurzwahl aller Strafen mit der Option "IsOneClick" ermöglicht. +Die Erfassung von Strafen muss möglichst schnell und komfortabel mögiich sein. Dafür muss die Seite DayDetails optimiert werden. +Es soll möglich sein eine Person in der Teilnehmerliste auszuwählen. Bei der Erfassung einer neuen Strafe soll diese Person direkt vorbelegt werden. Außerdem soll zusätzlich zum Button "Strafe hinzufügen" ein Menü-Button angezeigt werden, der eine Kurzwahl aller Strafen mit der Option "IsOneClick" ermöglicht. Der Benutzer soll visuell leicht erkennen können, ob und welcher Teilnehmer aktuell markiert wurde. diff --git a/src/Koogle.Web/Components/Pages/Days/AddPersonExpenseDialog.razor b/src/Koogle.Web/Components/Pages/Days/AddPersonExpenseDialog.razor index f9c80c9..3cba2ab 100644 --- a/src/Koogle.Web/Components/Pages/Days/AddPersonExpenseDialog.razor +++ b/src/Koogle.Web/Components/Pages/Days/AddPersonExpenseDialog.razor @@ -124,17 +124,28 @@ [Parameter] public Guid DayId { get; set; } + [Parameter] + public Guid? PreselectedPersonId { get; set; } + private MudForm? _form; private bool _isValid; private ExpenseDto? _selectedExpense; private DayParticipantDto? _selectedParticipant; private decimal _customPrice; + private bool _initialized; private bool IsInverseMode => _selectedExpense?.IsInverse == true; protected override void OnParametersSet() { base.OnParametersSet(); + + if (!_initialized && PreselectedPersonId.HasValue) + { + _selectedParticipant = Participants.FirstOrDefault(p => p.PersonId == PreselectedPersonId.Value); + _initialized = true; + } + if (_selectedExpense is not null) { _customPrice = _selectedExpense.Price; diff --git a/src/Koogle.Web/Components/Pages/Days/DayDetails.razor b/src/Koogle.Web/Components/Pages/Days/DayDetails.razor index 7324bea..a294c87 100644 --- a/src/Koogle.Web/Components/Pages/Days/DayDetails.razor +++ b/src/Koogle.Web/Components/Pages/Days/DayDetails.razor @@ -99,16 +99,27 @@ else } else { - + @foreach (var participant in Day.Participants.OrderBy(p => p.PersonName)) { - + var isSelected = _selectedParticipantId == participant.PersonId; + - - @participant.PersonName[0] - - @participant.PersonName + + + @participant.PersonName[0] + + + @participant.PersonName @if (participant.PersonStatus == PersonStatus.Guest) { Gast @@ -119,12 +130,21 @@ else + OnClick="@(() => RemoveParticipant(participant))" + OnClickStopPropagation="true" /> } } + @if (_selectedParticipantId.HasValue) + { + + + @SelectedParticipant?.PersonName ausgewählt + + + } } @@ -204,14 +224,36 @@ else @if (Day.Status != DayStatus.Closed) { - - Strafe hinzufügen - + + @if (OneClickExpenses.Count > 0 && _selectedParticipantId.HasValue) + { + + @foreach (var expense in OneClickExpenses.OrderBy(e => e.Name)) + { + + + @expense.Name + @expense.Price.ToString("C") + + + } + + } + + Strafe hinzufügen + + } @@ -331,6 +373,10 @@ else private DayDto? Day => DayState.Value.SelectedDay; private IReadOnlyList Expenses => DayState.Value.SelectedDayExpenses; + private Guid? _selectedParticipantId; + private DayParticipantDto? SelectedParticipant => Day?.Participants.FirstOrDefault(p => p.PersonId == _selectedParticipantId); + private IReadOnlyList OneClickExpenses => DayState.Value.AvailableExpenses.Where(e => e.IsOneClick && !e.IsVariable).ToList(); + private decimal TotalExpenseAmount => Expenses.Sum(e => e.Price); private decimal OpenExpenseAmount => Expenses.Where(e => e.PersonExpenseStatus == PersonExpenseStatus.Open).Sum(e => e.Price); private decimal PaidExpenseAmount => Expenses.Where(e => e.PersonExpenseStatus == PersonExpenseStatus.Done).Sum(e => e.Price); @@ -359,6 +405,16 @@ else Dispatcher.Dispatch(new ClearDayErrorAction()); } + private void ToggleParticipantSelection(Guid personId) + { + _selectedParticipantId = _selectedParticipantId == personId ? null : personId; + } + + private void ClearParticipantSelection() + { + _selectedParticipantId = null; + } + private void NavigateBack() { NavigationManager.NavigateTo("/days"); @@ -546,7 +602,8 @@ else { { "AvailableExpenses", availableExpenses }, { "Participants", Day.Participants }, - { "DayId", Day.Id } + { "DayId", Day.Id }, + { "PreselectedPersonId", _selectedParticipantId } }; var dialog = await DialogService.ShowAsync("Strafe hinzufügen", parameters); @@ -567,6 +624,37 @@ else } } + private void QuickAssignExpense(ExpenseDto expense) + { + if (Day is null || !_selectedParticipantId.HasValue) return; + + if (expense.IsInverse) + { + var dto = new CreateInversePersonExpenseDto + { + ExcludedPersonId = _selectedParticipantId.Value, + ExpenseId = expense.Id, + DayId = Day.Id, + GameId = null + }; + Dispatcher.Dispatch(new CreateInversePersonExpenseAction(dto)); + Snackbar.Add($"{expense.Name} für alle außer {SelectedParticipant?.PersonName}...", Severity.Info); + } + else + { + var dto = new CreatePersonExpenseDto + { + PersonId = _selectedParticipantId.Value, + ExpenseId = expense.Id, + DayId = Day.Id, + GameId = null, + Price = null + }; + Dispatcher.Dispatch(new CreatePersonExpenseAction(dto)); + Snackbar.Add($"{expense.Name} für {SelectedParticipant?.PersonName}...", Severity.Info); + } + } + private void MarkAsPaid(PersonExpenseDto expense) { var dto = new UpdatePersonExpenseStatusDto diff --git a/src/Koogle.Web/Components/Pages/Days/DayDetails.razor.css b/src/Koogle.Web/Components/Pages/Days/DayDetails.razor.css new file mode 100644 index 0000000..31a21e8 --- /dev/null +++ b/src/Koogle.Web/Components/Pages/Days/DayDetails.razor.css @@ -0,0 +1,21 @@ +.participant-list ::deep .mud-list-item { + cursor: pointer; + transition: background-color 0.2s ease; +} + +.selectable-participant { + border-left: 3px solid transparent; +} + +.selectable-participant:hover { + background-color: var(--mud-palette-action-default-hover); +} + +.selected-participant { + background-color: color-mix(in srgb, var(--mud-palette-success) 15%, transparent); + border-left: 3px solid var(--mud-palette-success); +} + +.selected-participant:hover { + background-color: color-mix(in srgb, var(--mud-palette-success) 25%, transparent); +}