GuestLog - StayEntry pages fixes

- Details/New - Fixed time field binding. Now it binds directly to the Model instead of using temp TimeSpans.
- New - Fixed StayEntry validation to allow creating Stays with a DateOut in the past.
- List - Added ordering to db results so limit behaves in a deterministic way. Fixed default search so stays created today are shown.
This commit is contained in:
2026-08-24 20:50:38 +02:00
parent 511bd023f2
commit b18bd7caa6
8 changed files with 67 additions and 29 deletions
@@ -0,0 +1,6 @@
namespace Somnia.GuestLog.Components.Forms;
public enum FormMode
{
Create, ReadOnly, Edit
}
@@ -21,12 +21,16 @@
<MudDivider DividerType="DividerType.Middle" Class="mb-4" /> <MudDivider DividerType="DividerType.Middle" Class="mb-4" />
<MudStack Row style="@FieldMarginY"> <MudStack Row style="@FieldMarginY">
<MudDatePicker @bind-Date="Model.DateIn" For="@(() => Model.DateIn)" <MudDatePicker @ref="_datePickerDateIn" @bind-Date="Model.DateIn" For="@(() => Model.DateIn)"
Label="@LanguageLocale.dto_StayEntry_DateIn" Clearable OpenTo="OpenTo.Date" Editable /> Label="@LanguageLocale.dto_StayEntry_DateIn" Clearable OpenTo="OpenTo.Date" Editable
<MudTimePicker @bind-Time="TimeIn" Label=" " Editable /> AutoClose="true" />
<MudDatePicker @bind-Date="Model.DateOut" For="@(() => Model.DateOut)" <MudTimePicker Time="Model.DateIn?.TimeOfDay" Label=" " Editable
Label="@LanguageLocale.dto_StayEntry_DateOut" Clearable OpenTo="OpenTo.Date" Editable /> TimeChanged="@((newVal) => Model.DateIn = GetDateTimeFromDateAndTime(Model.DateIn, newVal))" />
<MudTimePicker @bind-Time="TimeOut" Label=" " Editable /> <MudDatePicker @ref="_datePickerDateOut" @bind-Date="Model.DateOut" For="@(() => Model.DateOut)"
Label="@LanguageLocale.dto_StayEntry_DateOut" Clearable OpenTo="OpenTo.Date" Editable
AutoClose="true" />
<MudTimePicker Time="Model.DateOut?.TimeOfDay" Label=" " Editable
TimeChanged="@((newVal) => Model.DateOut = GetDateTimeFromDateAndTime(Model.DateOut, newVal))" />
</MudStack> </MudStack>
<MudStack Row style="@FieldMarginY"> <MudStack Row style="@FieldMarginY">
<MudTextField @bind-Value="Model.RoomCount" For="@(() => Model.RoomCount)" <MudTextField @bind-Value="Model.RoomCount" For="@(() => Model.RoomCount)"
@@ -113,14 +117,16 @@
[Parameter] [Parameter]
public StayEntryDTO Model { private get; set; } = new(); public StayEntryDTO Model { private get; set; } = new();
[Parameter] [Parameter, EditorRequired]
public bool ReadOnly { get; set; } = false; public FormMode FormMode { get; set; }
private TimeSpan? TimeIn = DateTime.Now.TimeOfDay;
private TimeSpan? TimeOut = new(hours: 12, minutes: 0, seconds: 0); private bool ReadOnly => FormMode == FormMode.ReadOnly;
private MudForm _form = null!; private MudForm _form = null!;
private MudDatePicker _datePickerDateIn = null!;
private MudDatePicker _datePickerDateOut = null!;
private const string FieldMarginY = "margin-block: 0.5em"; private const string FieldMarginY = "margin-block: 0.5em";
private IEnumerable<string> PaymentMethodSet private IEnumerable<string> PaymentMethodSet
@@ -138,7 +144,20 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
Model.DateIn = DateTime.Today; //Initialize values when not ReadOnly.
if (FormMode == FormMode.Create)
{
Model.DateIn = DateTime.Now;
DateTime dateOut = DateTime.Today.AddDays(1);
Model.DateOut = new(
year: dateOut.Year, month: dateOut.Month, day: dateOut.Day,
hour: 12, minute: 0, second: 0
);
//Init Guest List
Model.GuestDocuments = [];
}
} }
#region Public Methods #region Public Methods
@@ -158,10 +177,6 @@
public StayEntryDTO GetModel() public StayEntryDTO GetModel()
{ {
//Update model times.
Model.DateIn = GetDateTimeFromDateAndTime(Model.DateIn, TimeIn);
Model.DateOut = GetDateTimeFromDateAndTime(Model.DateOut, TimeOut);
return Model; return Model;
} }
@@ -185,6 +200,8 @@
private async Task BtnAddGuest() private async Task BtnAddGuest()
{ {
Debug.Assert(Model.GuestDocuments != null);
DialogOptions dialogOptions = new() DialogOptions dialogOptions = new()
{ {
MaxWidth = MaxWidth.Medium, MaxWidth = MaxWidth.Medium,
@@ -210,7 +227,7 @@
} }
//Check if this guest is already added. //Check if this guest is already added.
if (Model.GuestDocuments is not null && Model.GuestDocuments.Count > 0) if (Model.GuestDocuments.Count > 0)
{ {
bool alreadyAdded = Model.GuestDocuments.Any(e => e.DocumentType == resultData.DocumentType bool alreadyAdded = Model.GuestDocuments.Any(e => e.DocumentType == resultData.DocumentType
&& e.DocumentNum == resultData.DocumentNum); && e.DocumentNum == resultData.DocumentNum);
@@ -221,12 +238,6 @@
} }
} }
//Init List.
if (Model.GuestDocuments is null)
{
Model.GuestDocuments = [];
}
Model.GuestDocuments.Add(resultData); Model.GuestDocuments.Add(resultData);
} }
@@ -27,7 +27,8 @@
@if (stayModel is not null) @if (stayModel is not null)
{ {
<StayEntryForm Model="stayModel" ReadOnly="true" /> <StayEntryForm Model="stayModel" FormMode="FormMode.ReadOnly" />
<MudText Class="mt-4">@CreateTimeString</MudText>
} }
@@ -41,6 +42,22 @@
StayEntryDTO? stayModel = null; StayEntryDTO? stayModel = null;
private string? CreateTimeString
{
get
{
if (stayModel is null)
{
return null;
}
List<string> textList = [];
textList.Add($"Created at: {stayModel.CreateTime?.ToString() ?? "???"}");
textList.Add($"Last Update Time: {stayModel.UpdateTime?.ToString() ?? "???"}");
return string.Join("; ", textList);
}
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@* if (StayId is default) @* if (StayId is default)
@@ -73,7 +73,7 @@
private StayEntryFilterDTO stayFilter = new StayEntryFilterDTO() private StayEntryFilterDTO stayFilter = new StayEntryFilterDTO()
{ {
DateInStart = DateTime.Today.AddDays(-14), DateInStart = DateTime.Today.AddDays(-14),
DateInEnd = DateTime.Today, DateInEnd = DateTime.Today.AddDays(1),
}; };
private IEnumerable<StayEntryDTO> stayEntryList = []; private IEnumerable<StayEntryDTO> stayEntryList = [];
@@ -27,7 +27,7 @@
</MudButton> </MudButton>
</MudStack> </MudStack>
<StayEntryForm @ref="_form" Model="@newStayModel" /> <StayEntryForm @ref="_form" Model="@newStayModel" FormMode="FormMode.Create" />
@code { @code {
StayEntryForm _form = null!; StayEntryForm _form = null!;
+2 -2
View File
@@ -38,8 +38,8 @@ internal static class EntityMapping
return new() return new()
{ {
Id = entity.Id, Id = entity.Id,
DateIn = entity.DateIn, DateIn = entity.DateIn.ToLocalTime(),
DateOut = entity.DateOut, DateOut = entity.DateOut.ToLocalTime(),
RoomCount = entity.RoomCount, RoomCount = entity.RoomCount,
RoomNumber = entity.RoomNumber, RoomNumber = entity.RoomNumber,
PaymentMethod = entity.PaymentMethod, PaymentMethod = entity.PaymentMethod,
@@ -153,12 +153,15 @@ public class StayEntryService(
query = query.WhereIf(!string.IsNullOrEmpty(filter.PaymentMethod), query = query.WhereIf(!string.IsNullOrEmpty(filter.PaymentMethod),
e => e.PaymentMethod == filter.PaymentMethod); e => e.PaymentMethod == filter.PaymentMethod);
query = query.OrderByDescending(e => e.DateIn);
if (limit > 0) if (limit > 0)
{ {
query = query.Take(limit); query = query.Take(limit);
} }
var dbList = await query.ToListAsync(); var dbList = await query
.ToListAsync();
return dbList.Select(x => GetDtoFromDb(x, queryConfig)); return dbList.Select(x => GetDtoFromDb(x, queryConfig));
} }
@@ -183,6 +186,7 @@ public class StayEntryService(
var rs = await dbContext.StayEntries var rs = await dbContext.StayEntries
.Where(s => s.GuestDocuments.Any(g => g.DocumentType == docType && g.DocumentNum == docNum)) .Where(s => s.GuestDocuments.Any(g => g.DocumentType == docType && g.DocumentNum == docNum))
.AddQueryConfig(queryConfig) .AddQueryConfig(queryConfig)
.OrderByDescending(e => e.DateIn)
.ToListAsync(); .ToListAsync();
return rs.Select(e => GetDtoFromDb(e, queryConfig)); return rs.Select(e => GetDtoFromDb(e, queryConfig));
@@ -14,7 +14,7 @@ public class StayEntryDTOValidator : ValueValidator<StayEntryDTO>
RuleFor(e => e.DateOut) RuleFor(e => e.DateOut)
.NotEmpty() .NotEmpty()
.GreaterThan(DateTime.Today); .GreaterThan(e => e.DateIn);
RuleFor(e => e.RoomCount) RuleFor(e => e.RoomCount)
.NotEmpty() .NotEmpty()