diff --git a/JSMR.UI.Blazor/App.razor b/JSMR.UI.Blazor/App.razor index 5018329..d64f6af 100644 --- a/JSMR.UI.Blazor/App.razor +++ b/JSMR.UI.Blazor/App.razor @@ -1,4 +1,5 @@ -@using JSMR.UI.Blazor.Services +@using JSMR.UI.Blazor.Components.Authentication +@using JSMR.UI.Blazor.Services @inject SessionState Session @@ -6,22 +7,24 @@ - - - - - - - Not found - -

Sorry, there's nothing at this address.

-
-
-
+ + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
+
@code { protected override async Task OnInitializedAsync() { - await Session.RefreshAsync(); + //await Session.RefreshAsync(); } } \ No newline at end of file diff --git a/JSMR.UI.Blazor/Components/Authentication/AuthenticationGate.razor b/JSMR.UI.Blazor/Components/Authentication/AuthenticationGate.razor new file mode 100644 index 0000000..5c097f5 --- /dev/null +++ b/JSMR.UI.Blazor/Components/Authentication/AuthenticationGate.razor @@ -0,0 +1,72 @@ +@using JSMR.UI.Blazor.Services +@using Microsoft.AspNetCore.Components.Routing + +@inject SessionState Session +@inject NavigationManager Navigation + +@if (!_ready) +{ +

Loading...

+} +else +{ + @ChildContent +} + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } + + private bool _ready; + + // Add any routes you want public here. + // Use absolute-path form (leading slash). + private static readonly HashSet _allowAnonymous = new(StringComparer.OrdinalIgnoreCase) + { + "/login", + "/login/", // optional + }; + + protected override async Task OnInitializedAsync() + { + Navigation.LocationChanged += OnLocationChanged; + + // One-time refresh at app start + await Session.RefreshAsync(); + _ready = true; + + await EnforceAsync(); + } + + private async void OnLocationChanged(object? sender, LocationChangedEventArgs e) + { + // If your Session can change based on navigation/cookies, you *may* refresh here, + // but avoid doing it on every navigation unless necessary. + // await Session.RefreshAsync(); + + await EnforceAsync(); + } + + private Task EnforceAsync() + { + if (!_ready) return Task.CompletedTask; + + var path = "/" + Navigation.ToBaseRelativePath(Navigation.Uri); + var qIndex = path.IndexOf('?', StringComparison.Ordinal); + if (qIndex >= 0) path = path[..qIndex]; + + // allow anonymous routes + if (_allowAnonymous.Contains(path)) + return Task.CompletedTask; + + if (!Session.IsAuthenticated) + { + var returnUrl = Uri.EscapeDataString(Navigation.Uri); + Navigation.NavigateTo($"/login?returnUrl={returnUrl}", forceLoad: false); + } + + return Task.CompletedTask; + } + + public void Dispose() + => Navigation.LocationChanged -= OnLocationChanged; +} \ No newline at end of file diff --git a/JSMR.UI.Blazor/Components/Chip.razor b/JSMR.UI.Blazor/Components/Chip.razor index 17d8d27..5174406 100644 --- a/JSMR.UI.Blazor/Components/Chip.razor +++ b/JSMR.UI.Blazor/Components/Chip.razor @@ -5,7 +5,11 @@
@if (Graphic != null) { - + + } @ChildContent
@@ -15,7 +19,12 @@ else @if (Graphic != null) { - + + } @ChildContent @@ -32,6 +41,9 @@ else [Parameter] public IconVarient? IconVarient { get; set; } + [Parameter] + public SizeVarient? IconSize { get; set; } + [Parameter] public ColorVarient Color { get; set; } = ColorVarient.Primary; diff --git a/JSMR.UI.Blazor/Components/Icon.razor b/JSMR.UI.Blazor/Components/Icon.razor index 89eaa96..b9507f2 100644 --- a/JSMR.UI.Blazor/Components/Icon.razor +++ b/JSMR.UI.Blazor/Components/Icon.razor @@ -7,7 +7,7 @@ public Graphic Graphic { get; set; } [Parameter] - public SizeVarient Size { get; set; } = SizeVarient.Medium; + public SizeVarient Size { get; set; } = SizeVarient.Small; [Parameter] public IconVarient Varient { get; set; } = IconVarient.None; @@ -25,7 +25,7 @@ [ $"j-icon", $"j-icon-{graphic}", - $"j-icon-size-{Size.ToString().ToLower()}", + $"size-{Size.ToString().ToLower()}", $"background-color-{Color.ToString().ToLower()}" ]; diff --git a/JSMR.UI.Blazor/Components/JProduct.razor b/JSMR.UI.Blazor/Components/JProduct.razor index 6447ba5..582741c 100644 --- a/JSMR.UI.Blazor/Components/JProduct.razor +++ b/JSMR.UI.Blazor/Components/JProduct.razor @@ -42,12 +42,12 @@ } -
+ @*
@foreach (var tag in Product.Tags) { - @* *@ + } -
+
*@
diff --git a/JSMR.UI.Blazor/Enums/Graphic.cs b/JSMR.UI.Blazor/Enums/Graphic.cs index 88ac5ff..13691a3 100644 --- a/JSMR.UI.Blazor/Enums/Graphic.cs +++ b/JSMR.UI.Blazor/Enums/Graphic.cs @@ -15,6 +15,7 @@ public enum Graphic Circle, Tag, Person, + Avatar, Sort, Grid, Age, diff --git a/JSMR.UI.Blazor/Layout/LoginLayout.razor b/JSMR.UI.Blazor/Layout/LoginLayout.razor new file mode 100644 index 0000000..b64fee8 --- /dev/null +++ b/JSMR.UI.Blazor/Layout/LoginLayout.razor @@ -0,0 +1,5 @@ +@inherits LayoutComponentBase + + \ No newline at end of file diff --git a/JSMR.UI.Blazor/Layout/MainLayout.razor b/JSMR.UI.Blazor/Layout/MainLayout.razor index 0eeb0fa..824da10 100644 --- a/JSMR.UI.Blazor/Layout/MainLayout.razor +++ b/JSMR.UI.Blazor/Layout/MainLayout.razor @@ -1,27 +1,30 @@ -@using JSMR.UI.Blazor.Services +@using JSMR.UI.Blazor.Components +@using JSMR.UI.Blazor.Services @inject SessionState Session +@inject NavigationManager Navigation @inherits LayoutComponentBase -
- @if (Session.IsAuthenticated) - { - Logged in as @Session.Me?.name (@Session.Me?.role) - Logout - } - else - { - Login - } -
- JSMR - + @* *@ + + @if (Session.IsAuthenticated) + { + @* Logged in as @Session.Me?.Name (@Session.Me?.Role) *@ + + @Session.Me?.Name @* (@Session.Me?.Role) *@ + + Logout + } + else + { + Login + } @@ -89,4 +92,10 @@ { Session.Changed -= OnSessionChanged; } + + private async Task LogoutAsync() + { + await Session.LogoutAsync(); + Navigation.NavigateTo("/login"); + } } \ No newline at end of file diff --git a/JSMR.UI.Blazor/Pages/Login.razor b/JSMR.UI.Blazor/Pages/Login.razor index 6a25bbc..c8d0fef 100644 --- a/JSMR.UI.Blazor/Pages/Login.razor +++ b/JSMR.UI.Blazor/Pages/Login.razor @@ -1,4 +1,5 @@ @page "/login" +@layout LoginLayout @using JSMR.UI.Blazor.Services @@ -9,29 +10,23 @@ @if (Session.IsAuthenticated) { -

You're already logged in as @Session.Me?.name.

+

You're already logged in as @Session.Me?.Name.

} else {
-
-
- -
-
-
- -
- -
- -
- - @if (!string.IsNullOrWhiteSpace(error)) - { -

@error

- } + + + + + Login + @if (!string.IsNullOrWhiteSpace(error)) + { +

@error

+ } +
+
} diff --git a/JSMR.UI.Blazor/Pages/VoiceWorks.razor b/JSMR.UI.Blazor/Pages/VoiceWorks.razor index af5ca2d..716b77c 100644 --- a/JSMR.UI.Blazor/Pages/VoiceWorks.razor +++ b/JSMR.UI.Blazor/Pages/VoiceWorks.razor @@ -12,7 +12,6 @@ @inherits SearchPageBase - Voice Works

Voice Works

@@ -42,7 +41,6 @@ } -
@code { [Inject] diff --git a/JSMR.UI.Blazor/Services/AuthenticationClient.cs b/JSMR.UI.Blazor/Services/AuthenticationClient.cs index 05df3e9..0f5ecdc 100644 --- a/JSMR.UI.Blazor/Services/AuthenticationClient.cs +++ b/JSMR.UI.Blazor/Services/AuthenticationClient.cs @@ -25,5 +25,5 @@ public class AuthenticationClient(HttpClient http) return await resp.Content.ReadFromJsonAsync(cancellationToken: ct); } - public sealed record MeResponse(string? name, string? id, string? role); + public sealed record MeResponse(string? Name, string? Id, string? Role); } \ No newline at end of file diff --git a/JSMR.UI.Blazor/wwwroot/css/app.css b/JSMR.UI.Blazor/wwwroot/css/app.css index 4569813..60c6732 100644 --- a/JSMR.UI.Blazor/wwwroot/css/app.css +++ b/JSMR.UI.Blazor/wwwroot/css/app.css @@ -659,8 +659,14 @@ code { .j-chip.is-clickable { cursor: pointer; user-select: none; + --chip-hover-alpha: 0.2; + transition: .2s linear; } + .j-chip.is-clickable:hover { + background: rgb(var(--chip-rgb) / var(--chip-hover-alpha)); + } + .j-chip.varient-filled { padding: .4rem .8rem; border-radius: 1rem; @@ -703,6 +709,21 @@ code { width: 16px; } +.j-icon.size-small { + width: 1rem; + height: 1rem; +} + +.j-icon.size-medium { + width: 1.5rem; + height: 1.5rem; +} + +.j-icon.size-large { + width: 2rem; + height: 2rem; +} + .j-icon-color-yellow { background: #ffe073; } @@ -784,6 +805,14 @@ code { mask-image: url("../svg/person-fill.svg"); } +.j-icon-avatar { + mask-image: url("../svg/person-circle.svg"); +} + +.j-icon-avatar-fill { + mask-image: url("../svg/person-circle.svg"); +} + .j-icon-sort { mask-image: url("../svg/sort.svg"); } diff --git a/JSMR.UI.Blazor/wwwroot/svg/person-circle.svg b/JSMR.UI.Blazor/wwwroot/svg/person-circle.svg new file mode 100644 index 0000000..af6f2b4 --- /dev/null +++ b/JSMR.UI.Blazor/wwwroot/svg/person-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file