select Person and Quick-Assign expenses
This commit is contained in:
parent
033a3ebd4e
commit
8d2222de8f
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
## Optimierung Spieltag-Details
|
## Optimierung Spieltag-Details
|
||||||
Die Erfassung von Strafen muss möglichst schnell und komfortabel mögiich sein. Dafür muss die Seite DayDetails opimiert werden.
|
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 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.
|
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.
|
Der Benutzer soll visuell leicht erkennen können, ob und welcher Teilnehmer aktuell markiert wurde.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,17 +124,28 @@
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Guid DayId { get; set; }
|
public Guid DayId { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Guid? PreselectedPersonId { get; set; }
|
||||||
|
|
||||||
private MudForm? _form;
|
private MudForm? _form;
|
||||||
private bool _isValid;
|
private bool _isValid;
|
||||||
private ExpenseDto? _selectedExpense;
|
private ExpenseDto? _selectedExpense;
|
||||||
private DayParticipantDto? _selectedParticipant;
|
private DayParticipantDto? _selectedParticipant;
|
||||||
private decimal _customPrice;
|
private decimal _customPrice;
|
||||||
|
private bool _initialized;
|
||||||
|
|
||||||
private bool IsInverseMode => _selectedExpense?.IsInverse == true;
|
private bool IsInverseMode => _selectedExpense?.IsInverse == true;
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
|
|
||||||
|
if (!_initialized && PreselectedPersonId.HasValue)
|
||||||
|
{
|
||||||
|
_selectedParticipant = Participants.FirstOrDefault(p => p.PersonId == PreselectedPersonId.Value);
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (_selectedExpense is not null)
|
if (_selectedExpense is not null)
|
||||||
{
|
{
|
||||||
_customPrice = _selectedExpense.Price;
|
_customPrice = _selectedExpense.Price;
|
||||||
|
|
|
||||||
|
|
@ -99,16 +99,27 @@ else
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudList T="DayParticipantDto" Dense="true">
|
<MudList T="DayParticipantDto" Dense="true" Class="participant-list">
|
||||||
@foreach (var participant in Day.Participants.OrderBy(p => p.PersonName))
|
@foreach (var participant in Day.Participants.OrderBy(p => p.PersonName))
|
||||||
{
|
{
|
||||||
<MudListItem T="DayParticipantDto">
|
var isSelected = _selectedParticipantId == participant.PersonId;
|
||||||
|
<MudListItem T="DayParticipantDto"
|
||||||
|
Class="@(isSelected ? "selected-participant" : "selectable-participant")"
|
||||||
|
OnClick="@(() => ToggleParticipantSelection(participant.PersonId))">
|
||||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Style="width: 100%">
|
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Style="width: 100%">
|
||||||
<MudStack Row="true" AlignItems="AlignItems.Center">
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
<MudAvatar Size="Size.Small" Color="@(participant.PersonStatus == PersonStatus.Member ? Color.Primary : Color.Secondary)">
|
<MudBadge Visible="@isSelected"
|
||||||
@participant.PersonName[0]
|
Color="Color.Success"
|
||||||
</MudAvatar>
|
Icon="@Icons.Material.Filled.Check"
|
||||||
<MudText>@participant.PersonName</MudText>
|
Overlap="true"
|
||||||
|
Bordered="true">
|
||||||
|
<MudAvatar Size="Size.Small"
|
||||||
|
Color="@(isSelected ? Color.Success : (participant.PersonStatus == PersonStatus.Member ? Color.Primary : Color.Secondary))"
|
||||||
|
Style="@(isSelected ? "border: 2px solid var(--mud-palette-success)" : "")">
|
||||||
|
@participant.PersonName[0]
|
||||||
|
</MudAvatar>
|
||||||
|
</MudBadge>
|
||||||
|
<MudText Style="@(isSelected ? "font-weight: 600" : "")">@participant.PersonName</MudText>
|
||||||
@if (participant.PersonStatus == PersonStatus.Guest)
|
@if (participant.PersonStatus == PersonStatus.Guest)
|
||||||
{
|
{
|
||||||
<MudChip T="string" Size="Size.Small" Variant="Variant.Outlined" Color="Color.Secondary">Gast</MudChip>
|
<MudChip T="string" Size="Size.Small" Variant="Variant.Outlined" Color="Color.Secondary">Gast</MudChip>
|
||||||
|
|
@ -119,12 +130,21 @@ else
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.PersonRemove"
|
<MudIconButton Icon="@Icons.Material.Filled.PersonRemove"
|
||||||
Size="Size.Small"
|
Size="Size.Small"
|
||||||
Color="Color.Error"
|
Color="Color.Error"
|
||||||
OnClick="@(() => RemoveParticipant(participant))" />
|
OnClick="@(() => RemoveParticipant(participant))"
|
||||||
|
OnClickStopPropagation="true" />
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudListItem>
|
</MudListItem>
|
||||||
}
|
}
|
||||||
</MudList>
|
</MudList>
|
||||||
|
@if (_selectedParticipantId.HasValue)
|
||||||
|
{
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mt-2">
|
||||||
|
<MudChip T="string" Color="Color.Success" Size="Size.Small" OnClose="ClearParticipantSelection">
|
||||||
|
@SelectedParticipant?.PersonName ausgewählt
|
||||||
|
</MudChip>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
@ -204,14 +224,36 @@ else
|
||||||
</MudText>
|
</MudText>
|
||||||
@if (Day.Status != DayStatus.Closed)
|
@if (Day.Status != DayStatus.Closed)
|
||||||
{
|
{
|
||||||
<MudButton Variant="Variant.Text"
|
<MudStack Row="true" Spacing="1">
|
||||||
Color="Color.Primary"
|
@if (OneClickExpenses.Count > 0 && _selectedParticipantId.HasValue)
|
||||||
StartIcon="@Icons.Material.Filled.Add"
|
{
|
||||||
OnClick="OpenAddExpenseDialog"
|
<MudMenu Icon="@Icons.Material.Filled.FlashOn"
|
||||||
Size="Size.Small"
|
Color="Color.Warning"
|
||||||
Disabled="@(Day.Participants.Count == 0)">
|
Variant="Variant.Filled"
|
||||||
Strafe hinzufügen
|
Size="Size.Small"
|
||||||
</MudButton>
|
Title="Schnellzuweisung"
|
||||||
|
AnchorOrigin="Origin.BottomRight"
|
||||||
|
TransformOrigin="Origin.TopRight">
|
||||||
|
@foreach (var expense in OneClickExpenses.OrderBy(e => e.Name))
|
||||||
|
{
|
||||||
|
<MudMenuItem OnClick="@(() => QuickAssignExpense(expense))">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Style="min-width: 200px">
|
||||||
|
<MudText>@expense.Name</MudText>
|
||||||
|
<MudText Color="Color.Secondary">@expense.Price.ToString("C")</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudMenuItem>
|
||||||
|
}
|
||||||
|
</MudMenu>
|
||||||
|
}
|
||||||
|
<MudButton Variant="Variant.Text"
|
||||||
|
Color="Color.Primary"
|
||||||
|
StartIcon="@Icons.Material.Filled.Add"
|
||||||
|
OnClick="OpenAddExpenseDialog"
|
||||||
|
Size="Size.Small"
|
||||||
|
Disabled="@(Day.Participants.Count == 0)">
|
||||||
|
Strafe hinzufügen
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
|
|
@ -331,6 +373,10 @@ else
|
||||||
private DayDto? Day => DayState.Value.SelectedDay;
|
private DayDto? Day => DayState.Value.SelectedDay;
|
||||||
private IReadOnlyList<PersonExpenseDto> Expenses => DayState.Value.SelectedDayExpenses;
|
private IReadOnlyList<PersonExpenseDto> Expenses => DayState.Value.SelectedDayExpenses;
|
||||||
|
|
||||||
|
private Guid? _selectedParticipantId;
|
||||||
|
private DayParticipantDto? SelectedParticipant => Day?.Participants.FirstOrDefault(p => p.PersonId == _selectedParticipantId);
|
||||||
|
private IReadOnlyList<ExpenseDto> OneClickExpenses => DayState.Value.AvailableExpenses.Where(e => e.IsOneClick && !e.IsVariable).ToList();
|
||||||
|
|
||||||
private decimal TotalExpenseAmount => Expenses.Sum(e => e.Price);
|
private decimal TotalExpenseAmount => Expenses.Sum(e => e.Price);
|
||||||
private decimal OpenExpenseAmount => Expenses.Where(e => e.PersonExpenseStatus == PersonExpenseStatus.Open).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);
|
private decimal PaidExpenseAmount => Expenses.Where(e => e.PersonExpenseStatus == PersonExpenseStatus.Done).Sum(e => e.Price);
|
||||||
|
|
@ -359,6 +405,16 @@ else
|
||||||
Dispatcher.Dispatch(new ClearDayErrorAction());
|
Dispatcher.Dispatch(new ClearDayErrorAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleParticipantSelection(Guid personId)
|
||||||
|
{
|
||||||
|
_selectedParticipantId = _selectedParticipantId == personId ? null : personId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearParticipantSelection()
|
||||||
|
{
|
||||||
|
_selectedParticipantId = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void NavigateBack()
|
private void NavigateBack()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo("/days");
|
NavigationManager.NavigateTo("/days");
|
||||||
|
|
@ -546,7 +602,8 @@ else
|
||||||
{
|
{
|
||||||
{ "AvailableExpenses", availableExpenses },
|
{ "AvailableExpenses", availableExpenses },
|
||||||
{ "Participants", Day.Participants },
|
{ "Participants", Day.Participants },
|
||||||
{ "DayId", Day.Id }
|
{ "DayId", Day.Id },
|
||||||
|
{ "PreselectedPersonId", _selectedParticipantId }
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialog = await DialogService.ShowAsync<AddPersonExpenseDialog>("Strafe hinzufügen", parameters);
|
var dialog = await DialogService.ShowAsync<AddPersonExpenseDialog>("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)
|
private void MarkAsPaid(PersonExpenseDto expense)
|
||||||
{
|
{
|
||||||
var dto = new UpdatePersonExpenseStatusDto
|
var dto = new UpdatePersonExpenseStatusDto
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue