From 0991128b3016375d71e459e992c68f56bc6ae1d7 Mon Sep 17 00:00:00 2001 From: MaddoScientisto Date: Mon, 20 Apr 2026 23:17:35 +0200 Subject: [PATCH] feat: implement preview work units preference with local storage support --- Components/Pages/CalendarView.razor | 19 +++++++++++++ Components/Pages/MonthlySummary.razor | 19 +++++++++++++ wwwroot/app.css | 40 +++++++++++++++++++++++---- wwwroot/theme.js | 31 +++++++++++++++++++++ 4 files changed, 104 insertions(+), 5 deletions(-) diff --git a/Components/Pages/CalendarView.razor b/Components/Pages/CalendarView.razor index 8052c58..890463d 100644 --- a/Components/Pages/CalendarView.razor +++ b/Components/Pages/CalendarView.razor @@ -199,6 +199,8 @@ else @code { [Parameter] public string? YearMonth { get; set; } + private const string IncludePreviewPreferenceKey = "worktracker.includePreviewWorkUnits"; + private DateOnly firstOfMonth; private bool loading = true; private List weeks = []; @@ -222,6 +224,22 @@ else await LoadMonth(); } + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!firstRender) + { + return; + } + + var savedIncludePreview = await JS.InvokeAsync("workTrackerPreferences.getBool", IncludePreviewPreferenceKey); + if (savedIncludePreview.HasValue && savedIncludePreview.Value != includePreviewTotals) + { + includePreviewTotals = savedIncludePreview.Value; + await LoadMonth(); + await InvokeAsync(StateHasChanged); + } + } + private async Task LoadMonth() { loading = true; @@ -269,6 +287,7 @@ else private async Task OnIncludePreviewTotalsChanged(ChangeEventArgs e) { includePreviewTotals = e.Value is bool value && value; + await JS.InvokeVoidAsync("workTrackerPreferences.setBool", IncludePreviewPreferenceKey, includePreviewTotals); await LoadMonth(); } diff --git a/Components/Pages/MonthlySummary.razor b/Components/Pages/MonthlySummary.razor index ed4ba65..8f45fe5 100644 --- a/Components/Pages/MonthlySummary.razor +++ b/Components/Pages/MonthlySummary.razor @@ -5,6 +5,7 @@ @using System.Globalization @inject global::WorkTracker.Services.WorkDays.IWorkDayService WorkDayService +@inject IJSRuntime JS Monthly Summary @@ -209,6 +210,7 @@ else if (viewMode == SummaryViewMode.Timesheet && timesheet is not null) [Parameter] public string? YearMonth { get; set; } private static readonly CultureInfo ItalianCulture = CultureInfo.GetCultureInfo("it-IT"); + private const string IncludePreviewPreferenceKey = "worktracker.includePreviewWorkUnits"; private DateOnly currentMonth; private bool loading = true; @@ -231,9 +233,26 @@ else if (viewMode == SummaryViewMode.Timesheet && timesheet is not null) await LoadSummary(); } + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!firstRender) + { + return; + } + + var savedIncludePreview = await JS.InvokeAsync("workTrackerPreferences.getBool", IncludePreviewPreferenceKey); + if (savedIncludePreview.HasValue && savedIncludePreview.Value != includePreview) + { + includePreview = savedIncludePreview.Value; + await LoadSummary(); + await InvokeAsync(StateHasChanged); + } + } + private async Task OnIncludePreviewChanged(ChangeEventArgs e) { includePreview = e.Value is bool value && value; + await JS.InvokeVoidAsync("workTrackerPreferences.setBool", IncludePreviewPreferenceKey, includePreview); await LoadSummary(); } diff --git a/wwwroot/app.css b/wwwroot/app.css index 90768ef..a2f6721 100644 --- a/wwwroot/app.css +++ b/wwwroot/app.css @@ -715,22 +715,28 @@ h1:focus { .timesheet-summary-table thead th { background-color: var(--wt-summary-head-bg); - white-space: nowrap; + white-space: normal; } .timesheet-summary-table th, .timesheet-summary-table td { min-width: 2.2rem; - padding: 0.25rem 0.12rem; + padding: 0.35rem 0.16rem; vertical-align: middle; + line-height: 1.2; } .timesheet-summary-sticky-column { position: sticky; left: 0; z-index: 2; - min-width: 15rem !important; + min-width: 6.25rem !important; + max-width: 6.25rem; background-color: var(--wt-summary-sticky-bg); + white-space: normal; + overflow-wrap: anywhere; + word-break: break-word; + text-align: left; } .timesheet-summary-table thead .timesheet-summary-sticky-column { @@ -739,8 +745,11 @@ h1:focus { } .timesheet-summary-total-column { + position: sticky; + right: 0; + z-index: 2; background-color: var(--wt-summary-head-bg); - min-width: 3.3rem !important; + min-width: 3.5rem !important; } .timesheet-summary-table tbody tr:nth-child(odd) td, @@ -748,6 +757,18 @@ h1:focus { background-color: var(--wt-summary-row-alt); } +.timesheet-summary-table tbody tr:nth-child(odd) .timesheet-summary-total-column { + background-color: var(--wt-summary-row-alt); +} + +.timesheet-summary-table tbody .timesheet-summary-total-column { + background-color: var(--wt-summary-sticky-bg); +} + +.timesheet-summary-table thead .timesheet-summary-total-column { + z-index: 3; +} + .timesheet-summary-table .timesheet-summary-day-danger { background-color: #f8d7da !important; } @@ -821,7 +842,16 @@ h1:focus { @media (max-width: 767.98px) { .timesheet-summary-sticky-column { - min-width: 12rem !important; + min-width: 4.75rem !important; + max-width: 4.75rem; + padding-left: 0.3rem !important; + padding-right: 0.3rem !important; + font-size: 0.82rem; + } + + .timesheet-summary-total-column { + min-width: 3.1rem !important; + font-size: 0.82rem; } .timesheet-summary-day-popup { diff --git a/wwwroot/theme.js b/wwwroot/theme.js index 9a9d794..cf7b2a2 100644 --- a/wwwroot/theme.js +++ b/wwwroot/theme.js @@ -55,4 +55,35 @@ window.workTrackerTheme = (() => { }; })(); +window.workTrackerPreferences = { + getBool(key) { + try { + const value = localStorage.getItem(key); + if (value === null) { + return null; + } + + if (value === "true") { + return true; + } + + if (value === "false") { + return false; + } + + return null; + } + catch { + return null; + } + }, + setBool(key, value) { + try { + localStorage.setItem(key, value ? "true" : "false"); + } + catch { + } + } +}; + window.workTrackerTheme.init(); \ No newline at end of file