From 0de4e1ba10983fa87a15635cfde6d177472e62cb28bd3ee8ae1a509e276b8a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= <93217193+Daniel-Garmig@users.noreply.github.com> Date: Tue, 18 Aug 2026 22:17:16 +0200 Subject: [PATCH] GuestLog - General Improvements. - GuestDocument details - Added navigation to details for stays. - StayEntry details - Added dialog to see details of GuestDocuments. - StayEntry List - Added filter for result table. - StayEntry New - Show suggested guests on GuestSearchDialog so no searchs are required in some cases. - NavMenu - Removed default navigation links. --- .../Controls/GuestDocumentSearchDialog.razor | 53 +++++++++++++++---- .../Forms/StayEntryFilterForm.razor | 44 +++++++++++++++ .../Components/Forms/StayEntryForm.razor | 37 +++++++++++-- .../Components/Layout/NavMenu.razor | 18 ++++--- .../Pages/GuestDocuments/GuestDocDetail.razor | 44 +++++++++++---- .../Pages/StayEntries/StayEntryDetail.razor | 6 ++- .../Pages/StayEntries/StayEntryList.razor | 13 ++++- src/Somnia.GuestLog/GuestConsts.cs | 3 ++ .../Models/DTOs/StayEntryFilterDTO.cs | 10 ++++ .../Resources/LanguageLocale.resx | 7 +++ .../Services/IStayEntryService.cs | 1 + .../Services/StayEntryService.cs | 27 ++++++++++ 12 files changed, 227 insertions(+), 36 deletions(-) create mode 100644 src/Somnia.GuestLog/Components/Forms/StayEntryFilterForm.razor create mode 100644 src/Somnia.GuestLog/Models/DTOs/StayEntryFilterDTO.cs diff --git a/src/Somnia.GuestLog/Components/Controls/GuestDocumentSearchDialog.razor b/src/Somnia.GuestLog/Components/Controls/GuestDocumentSearchDialog.razor index cd0f3b1..29c03d3 100644 --- a/src/Somnia.GuestLog/Components/Controls/GuestDocumentSearchDialog.razor +++ b/src/Somnia.GuestLog/Components/Controls/GuestDocumentSearchDialog.razor @@ -34,6 +34,14 @@ @if (!isLoading) { + @* Only show on first load *@ + @if (loadCount == 1) + { + + @LanguageLocale.control_GuestDocumentSearchDialog_txtSuggestedGuests + + } + @foreach (GuestDocumentDTO result in results) @@ -74,10 +82,39 @@ private GuestDocumentDTO? SelectedResult { get; set; } private bool isLoading = false; + private int loadCount = 0; + + + protected override async Task OnInitializedAsync() + { + //Load last 5 guests. + await LoadGuests(limit: 5); + } private void Cancel() => MudDialog.Cancel(); + + private async Task LoadGuests(int limit = 25) + { + + try + { + isLoading = true; + results = await guestDocumentService.GetDtoListByFilterAsync(filter); + isLoading = false; + loadCount++; + } + catch (Exception ex) + { + logger.LogError(ex, "GuestDocument Search returned an error!"); + Snackbar.Add($"Error searching: {ex.Message}", Severity.Error); + } + } + + + #region Event Handlers + private async Task BtnSearchGuestDocuments() { if (filter.IsEmpty) @@ -86,17 +123,8 @@ return; } - try - { - isLoading = true; - results = await guestDocumentService.GetDtoListByFilterAsync(filter); - isLoading = false; - } - catch (Exception ex) - { - logger.LogError(ex, "GuestDocument Search returned an error!"); - Snackbar.Add($"Error searching: {ex.Message}", Severity.Error); - } + //Load unlimited guests using current filter. + await LoadGuests(); } private async Task btnSelectGuest() @@ -114,4 +142,7 @@ MudDialog.Close(dialogResult); return; } + + #endregion + } \ No newline at end of file diff --git a/src/Somnia.GuestLog/Components/Forms/StayEntryFilterForm.razor b/src/Somnia.GuestLog/Components/Forms/StayEntryFilterForm.razor new file mode 100644 index 0000000..751c328 --- /dev/null +++ b/src/Somnia.GuestLog/Components/Forms/StayEntryFilterForm.razor @@ -0,0 +1,44 @@ +@using System.ComponentModel +@using Somnia.GuestLog.Models.DTOs +@using Somnia.GuestLog.Services + +@inject IDataListService dataListService; + + + + + + + + + + + + @foreach (string paymentType in + dataListService.GetDataListByName(GuestConsts.DataList_PaymentMethods)) + { + + @dataListService.GetLocalizedValue(GuestConsts.DataList_PaymentMethods, paymentType) + + } + + + + + +@code { + + [Parameter, Bindable(true)] + public StayEntryFilterDTO Filter { get; set; } = new(); + + [Parameter] + public EventCallback FilterChanged { get; set; } + + [Parameter] + public bool DateRequired { get; set; } = true; + +} \ No newline at end of file diff --git a/src/Somnia.GuestLog/Components/Forms/StayEntryForm.razor b/src/Somnia.GuestLog/Components/Forms/StayEntryForm.razor index f8491b0..991ef81 100644 --- a/src/Somnia.GuestLog/Components/Forms/StayEntryForm.razor +++ b/src/Somnia.GuestLog/Components/Forms/StayEntryForm.razor @@ -69,10 +69,7 @@ @LanguageLocale.dto_GuestDocument_Name @LanguageLocale.dto_GuestDocument_Surname @LanguageLocale.dto_GuestDocument_Surname2 - @if (!ReadOnly) - { - @LanguageLocale.general_actions - } + @LanguageLocale.general_actions @@ -97,6 +94,13 @@ OnClick="@(() => BtnDeleteGuest(context.DocumentType!, context.DocumentNum!))" /> } + @if (ReadOnly) + { + + + + } @@ -181,7 +185,15 @@ private async Task BtnAddGuest() { - var dialog = await dialogService.ShowAsync(); + DialogOptions dialogOptions = new() + { + MaxWidth = MaxWidth.Medium, + FullWidth = true, + BackdropClick = false, + CloseOnEscapeKey = true, + }; + + var dialog = await dialogService.ShowAsync(title: "", options: dialogOptions); DialogResult? result = await dialog.Result; if (result is null || result.Canceled) @@ -228,6 +240,21 @@ .ToList(); } + + private async Task BtnViewGuestDetails(GuestDocumentDTO item) + { + var parameters = + new DialogParameters { { x => x.DocumentDTO, item } }; + var options = new DialogOptions() + { + FullScreen = true, + CloseButton = true, + CloseOnEscapeKey = true, + }; + + await dialogService.ShowAsync("GuestDoc Details", parameters, options); + } + #endregion } \ No newline at end of file diff --git a/src/Somnia.GuestLog/Components/Layout/NavMenu.razor b/src/Somnia.GuestLog/Components/Layout/NavMenu.razor index 048b526..6969cad 100644 --- a/src/Somnia.GuestLog/Components/Layout/NavMenu.razor +++ b/src/Somnia.GuestLog/Components/Layout/NavMenu.razor @@ -1,12 +1,16 @@ - - + Home + + @* Counter - Weather + *@ - Guests - Stays + + @LanguageLocale.navMenu_GuestDocumentList + + + @LanguageLocale.navMenu_StayEntryList + - - diff --git a/src/Somnia.GuestLog/Components/Pages/GuestDocuments/GuestDocDetail.razor b/src/Somnia.GuestLog/Components/Pages/GuestDocuments/GuestDocDetail.razor index 3ba30d6..940f0db 100644 --- a/src/Somnia.GuestLog/Components/Pages/GuestDocuments/GuestDocDetail.razor +++ b/src/Somnia.GuestLog/Components/Pages/GuestDocuments/GuestDocDetail.razor @@ -2,6 +2,7 @@ @using Somnia.GuestLog.Models.DTOs; @using Somnia.GuestLog.Services; +@using Microsoft.AspNetCore.WebUtilities; @inject ISnackbar Snackbar @inject IDialogService DialogService @@ -42,6 +43,7 @@ @LanguageLocale.dto_StayEntry_DateOut @LanguageLocale.dto_StayEntry_RoomCount @LanguageLocale.dto_StayEntry_RoomNumber + @LanguageLocale.general_actions @@ -56,6 +58,9 @@ @context.RoomNumber + + + @LanguageLocale.general_noResults @@ -110,16 +115,6 @@ } } - private void BtnEdit() - { - Snackbar.Add("Not implemented yet!", Severity.Info); - } - - private void BtnReturn() - { - Navigation.NavigateTo(GuestConsts.Page_GuestDocs_List); - } - private async Task LoadTabGuestStays() { //Only load when there is no data. @@ -137,4 +132,33 @@ guestStayList = await stayService.GetStaysWithGuest(documentModel.DocumentType, documentModel.DocumentNum); guestStaysLoading = false; } + + + #region Event Handlers + + private void BtnEdit() + { + Snackbar.Add("Not implemented yet!", Severity.Info); + } + + private void BtnReturn() + { + Navigation.NavigateTo(GuestConsts.Page_GuestDocs_List); + } + + + private void BtnViewStayDetails(StayEntryDTO item) + { + string itemId = $"{item.Id ?? -1}"; + string uri = GuestConsts.Page_StayEntry_Detail.Replace(GuestConsts.Page_StayEntry_Detail_Param_Id, itemId); + + string relativeUri = Navigation.ToBaseRelativePath(Navigation.Uri); + uri = QueryHelpers.AddQueryString(uri, [ + new KeyValuePair(GuestConsts.QueryParam_ReturnUrl, relativeUri) + ]); + Navigation.NavigateTo(uri); + } + + #endregion + } \ No newline at end of file diff --git a/src/Somnia.GuestLog/Components/Pages/StayEntries/StayEntryDetail.razor b/src/Somnia.GuestLog/Components/Pages/StayEntries/StayEntryDetail.razor index fed7887..a1df66e 100644 --- a/src/Somnia.GuestLog/Components/Pages/StayEntries/StayEntryDetail.razor +++ b/src/Somnia.GuestLog/Components/Pages/StayEntries/StayEntryDetail.razor @@ -36,6 +36,9 @@ [Parameter] public long StayId { get; set; } + [SupplyParameterFromQuery(Name = GuestConsts.QueryParam_ReturnUrl)] + public string? ReturnUrl { get; set; } + StayEntryDTO? stayModel = null; protected override async Task OnInitializedAsync() @@ -64,7 +67,8 @@ private void BtnReturn() { - Navigation.NavigateTo(GuestConsts.Page_StayEntry_List); + string uri = ReturnUrl ?? GuestConsts.Page_StayEntry_List; + Navigation.NavigateTo(uri); } } \ No newline at end of file diff --git a/src/Somnia.GuestLog/Components/Pages/StayEntries/StayEntryList.razor b/src/Somnia.GuestLog/Components/Pages/StayEntries/StayEntryList.razor index 7fed61c..af4ecda 100644 --- a/src/Somnia.GuestLog/Components/Pages/StayEntries/StayEntryList.razor +++ b/src/Somnia.GuestLog/Components/Pages/StayEntries/StayEntryList.razor @@ -24,6 +24,10 @@ + + + + @@ -66,6 +70,12 @@ private bool _showAllData = false; private const int resultLimit = 200; + private StayEntryFilterDTO stayFilter = new StayEntryFilterDTO() + { + DateInStart = DateTime.Today.AddDays(-14), + DateInEnd = DateTime.Today, + }; + private IEnumerable stayEntryList = []; private MudDataGrid _dataGrid = null!; @@ -82,8 +92,7 @@ _dataLoading = true; int dataLimit = _showAllData ? 0 : resultLimit; - stayEntryList = await StayEntryService.GetDtoListAsync(); - //stayEntryList = await StayEntryService.GetDtoListAsync(filter: filter, limit: dataLimit); + stayEntryList = await StayEntryService.GetDtoListByFilterAsync(filter: stayFilter, limit: dataLimit); if (showMsg) { diff --git a/src/Somnia.GuestLog/GuestConsts.cs b/src/Somnia.GuestLog/GuestConsts.cs index ed23428..4ea0342 100644 --- a/src/Somnia.GuestLog/GuestConsts.cs +++ b/src/Somnia.GuestLog/GuestConsts.cs @@ -14,6 +14,9 @@ public static class GuestConsts public const string Page_StayEntry_Detail_Param_Id = "{StayId:long}"; + public const string QueryParam_ReturnUrl = "returnUrl"; + + public const string DataList_DocumentTypes = "DocumentType"; public const string DataList_GenderOptions = "GenderOption"; public const string DataList_CountryCodes = "CountryCode"; diff --git a/src/Somnia.GuestLog/Models/DTOs/StayEntryFilterDTO.cs b/src/Somnia.GuestLog/Models/DTOs/StayEntryFilterDTO.cs new file mode 100644 index 0000000..104639c --- /dev/null +++ b/src/Somnia.GuestLog/Models/DTOs/StayEntryFilterDTO.cs @@ -0,0 +1,10 @@ +namespace Somnia.GuestLog.Models.DTOs; + +public class StayEntryFilterDTO +{ + public DateTime? DateInStart { get; set; } + public DateTime? DateInEnd { get; set; } + + public string? RoomNumber { get; set; } + public string? PaymentMethod { get; set; } +} \ No newline at end of file diff --git a/src/Somnia.GuestLog/Resources/LanguageLocale.resx b/src/Somnia.GuestLog/Resources/LanguageLocale.resx index e781f48..b17142a 100644 --- a/src/Somnia.GuestLog/Resources/LanguageLocale.resx +++ b/src/Somnia.GuestLog/Resources/LanguageLocale.resx @@ -166,6 +166,8 @@ No Results! Show all elements Use '%' as wildcard + Date Start + Date End Address City @@ -173,6 +175,10 @@ State Country Zip Code + + Guests + Stays + Guest Documents Document List @@ -204,5 +210,6 @@ Search Guest Document Use Guest + Suggested Guests \ No newline at end of file diff --git a/src/Somnia.GuestLog/Services/IStayEntryService.cs b/src/Somnia.GuestLog/Services/IStayEntryService.cs index 2c63954..e3f2068 100644 --- a/src/Somnia.GuestLog/Services/IStayEntryService.cs +++ b/src/Somnia.GuestLog/Services/IStayEntryService.cs @@ -12,6 +12,7 @@ public interface IStayEntryService public Task GetDtoByIdAsync(long id, StayEntryQueryConfig? queryConfig = null); public Task> GetDtoListAsync(StayEntryQueryConfig? queryConfig = null); + public Task> GetDtoListByFilterAsync(StayEntryFilterDTO filter, int limit = 0, StayEntryQueryConfig? queryConfig = null); public Task IsGuestInStayInPeriod(string docType, string docNum, DateTime startTime, DateTime endTime); diff --git a/src/Somnia.GuestLog/Services/StayEntryService.cs b/src/Somnia.GuestLog/Services/StayEntryService.cs index daeed77..dccba19 100644 --- a/src/Somnia.GuestLog/Services/StayEntryService.cs +++ b/src/Somnia.GuestLog/Services/StayEntryService.cs @@ -5,6 +5,7 @@ using Somnia.GuestLog.Models; using Somnia.GuestLog.Models.DTOs; using Somnia.GuestLog.Models.Entities; using Somnia.GuestLog.Models.QueryConfigs; +using Somnia.GuestLog.Utils; namespace Somnia.GuestLog.Services; @@ -135,6 +136,32 @@ public class StayEntryService( return dbList.Select(e => GetDtoFromDb(e, queryConfig)); } + public async Task> GetDtoListByFilterAsync(StayEntryFilterDTO filter, int limit = 0, StayEntryQueryConfig? queryConfig = null) + { + using var dbContext = contextFactory.CreateDbContext(); + + var query = dbContext.StayEntries + .AddQueryConfig(queryConfig) + .AsQueryable(); + + query = query.WhereIf(filter.DateInStart is not null && filter.DateInEnd is not null, + e => e.DateIn >= filter.DateInStart!.Value.ToUniversalTime() + && e.DateIn < filter.DateInEnd!.Value.ToUniversalTime()); + query = query.WhereIf(!string.IsNullOrEmpty(filter.RoomNumber), + e => EF.Functions.Like(e.RoomNumber, $"%{filter.RoomNumber}%")); + query = query.WhereIf(!string.IsNullOrEmpty(filter.PaymentMethod), + e => e.PaymentMethod == filter.PaymentMethod); + + if (limit > 0) + { + query = query.Take(limit); + } + + var dbList = await query.ToListAsync(); + + return dbList.Select(x => GetDtoFromDb(x, queryConfig)); + } + public async Task IsGuestInStayInPeriod(string docType, string docNum, DateTime startTime, DateTime endTime) {