< Summary

Information
Class: Chronicis.Client.Pages.Search
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Pages/Search.razor
Line coverage
0%
Covered lines: 0
Uncovered lines: 42
Coverable lines: 42
Total lines: 156
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 26
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
get_SearchQuery()100%210%
OnParametersSetAsync()0%620%
PerformSearch()100%210%
NavigateToArticle(...)0%2040%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Pages/Search.razor

#LineLine coverage
 1@page "/search"
 2@attribute [Authorize]
 3@inject ISearchApiService SearchApi
 4@inject IBreadcrumbService BreadcrumbService
 5@inject NavigationManager Navigation
 6@inject ITreeStateService TreeState
 7
 8@code {
 9    private readonly ILogger<Search> _logger;
 10
 11    // Direct constructor injection
 012    public Search(ILogger<Search> logger)
 13    {
 014        _logger = logger;
 015    }
 16}
 17
 18<MudPaper Elevation="2" Class="chronicis-article-card chronicis-fade-in">
 019    @if (_isLoading)
 20    {
 21        <div class="d-flex flex-column align-center mt-8">
 22            <MudProgressCircular Indeterminate="true" Color="Color.Primary" Size="Size.Large" />
 23            <MudText Typo="Typo.h6" Class="mt-4">Searching...</MudText>
 24        </div>
 25    }
 026    else if (_results == null)
 27    {
 28        <div class="text-center mt-8">
 29            <MudIcon Icon="@Icons.Material.Filled.Search" Size="Size.Large" Color="Color.Secondary" Class="mb-4" />
 30            <MudText Typo="Typo.h5" Color="Color.Secondary">Enter a search term to begin</MudText>
 31        </div>
 32    }
 033    else if (_results.TotalResults == 0)
 34    {
 35        <MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Class="mt-4">
 036            <MudText Typo="Typo.h6">No results found for "<strong>@_results.Query</strong>"</MudText>
 37            <MudText Typo="Typo.body2" Class="mt-2">Try different keywords or check your spelling</MudText>
 38        </MudAlert>
 39    }
 40    else
 41    {
 42        <div class="mb-4">
 43            <MudText Typo="Typo.h4">
 44                Search Results
 45            </MudText>
 46            <MudText Typo="Typo.body1" Class="mt-2" Color="Color.Secondary">
 047                Found <strong>@_results.TotalResults</strong> result@(_results.TotalResults == 1 ? "" : "s") for "<stron
 48            </MudText>
 49        </div>
 50
 51        @* Title Matches *@
 052        @if (_results.TitleMatches.Any())
 53        {
 54            <div class="search-group mt-6">
 55                <MudText Typo="Typo.h6" Class="search-group-header mb-3">
 56                    <MudIcon Icon="@Icons.Material.Filled.Title" Class="mr-2" />
 057                    Title Matches (@_results.TitleMatches.Count)
 58                </MudText>
 59
 060                @foreach (var result in _results.TitleMatches)
 61                {
 062                    <SearchResultCard Result="@result" Query="@_results.Query" OnClick="@(() => NavigateToArticle(result
 63                }
 64            </div>
 65        }
 66
 67        @* Content Matches *@
 068        @if (_results.BodyMatches.Any())
 69        {
 70            <div class="search-group mt-6">
 71                <MudText Typo="Typo.h6" Class="search-group-header mb-3">
 72                    <MudIcon Icon="@Icons.Material.Filled.Description" Class="mr-2" />
 073                    Content Matches (@_results.BodyMatches.Count)
 74                </MudText>
 75
 076                @foreach (var result in _results.BodyMatches)
 77                {
 078                    <SearchResultCard Result="@result" Query="@_results.Query" OnClick="@(() => NavigateToArticle(result
 79                }
 80            </div>
 81        }
 82
 83        @* Hashtag Matches *@
 084        @if (_results.HashtagMatches.Any())
 85        {
 86            <div class="search-group mt-6">
 87                <MudText Typo="Typo.h6" Class="search-group-header mb-3">
 88                    <MudIcon Icon="@Icons.Material.Filled.Tag" Class="mr-2" />
 089                    Hashtag Matches (@_results.HashtagMatches.Count)
 90                </MudText>
 91
 092                @foreach (var result in _results.HashtagMatches)
 93                {
 094                    <SearchResultCard Result="@result" Query="@_results.Query" OnClick="@(() => NavigateToArticle(result
 95                }
 96            </div>
 97        }
 98    }
 99</MudPaper>
 100
 101@code {
 102    [SupplyParameterFromQuery(Name = "q")]
 0103    public string? SearchQuery { get; set; }
 104
 105    private GlobalSearchResultsDto? _results;
 106    private bool _isLoading;
 107
 108    protected override async Task OnParametersSetAsync()
 109    {
 0110        if (!string.IsNullOrWhiteSpace(SearchQuery))
 111        {
 0112            await PerformSearch();
 113        }
 114        else
 115        {
 0116            _results = null;
 117        }
 0118    }
 119
 120    private async Task PerformSearch()
 121    {
 0122        _isLoading = true;
 0123        StateHasChanged();
 124
 125        try
 126        {
 0127            _results = await SearchApi.SearchContentAsync(SearchQuery!);
 0128        }
 0129        catch (Exception ex)
 130        {
 0131            _logger.LogErrorSanitized(ex, "Search error for query: {Query}", SearchQuery);
 0132            _results = null;
 0133        }
 134        finally
 135        {
 0136            _isLoading = false;
 0137            StateHasChanged();
 138        }
 0139    }
 140
 141    private void NavigateToArticle(ArticleSearchResultDto result)
 142    {
 0143        TreeState.ExpandPathToAndSelect(result.Id);
 144
 145        // Build path from ancestor breadcrumbs
 0146        if (result.AncestorPath != null && result.AncestorPath.Any())
 147        {
 0148            var path = BreadcrumbService.BuildArticleUrl(result.AncestorPath);
 0149            Navigation.NavigateTo(path);
 150        }
 151        else
 152        {
 0153            Navigation.NavigateTo($"/article/{result.Slug}");
 154        }
 0155    }
 156}