< Summary

Information
Class: Chronicis.Client.Services.BreadcrumbService
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/BreadcrumbService.cs
Line coverage
100%
Covered lines: 47
Uncovered lines: 0
Coverable lines: 47
Total lines: 162
Line coverage: 100%
Branch coverage
100%
Covered branches: 28
Total branches: 28
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ForWorld(...)100%22100%
ForCampaign(...)100%22100%
ForArc(...)100%22100%
ForArticle(...)100%1212100%
BuildArticleUrl(...)100%44100%
BuildArticleUrlToIndex(...)100%66100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/BreadcrumbService.cs

#LineLine coverage
 1using Chronicis.Shared.DTOs;
 2using MudBlazor;
 3
 4namespace Chronicis.Client.Services;
 5
 6/// <summary>
 7/// Service for building breadcrumb navigation consistently across the application.
 8/// Single source of truth for hierarchy: Dashboard → World → [Article hierarchy...]
 9/// </summary>
 10public interface IBreadcrumbService
 11{
 12    /// <summary>
 13    /// Build breadcrumbs for a World detail page.
 14    /// Result: Dashboard → World (current, disabled)
 15    /// </summary>
 16    List<BreadcrumbItem> ForWorld(WorldDto world, bool currentDisabled = true);
 17
 18    /// <summary>
 19    /// Build breadcrumbs for a Campaign detail page.
 20    /// Result: Dashboard → World → Campaign (current, disabled)
 21    /// </summary>
 22    List<BreadcrumbItem> ForCampaign(CampaignDto campaign, WorldDto world, bool currentDisabled = true);
 23
 24    /// <summary>
 25    /// Build breadcrumbs for an Arc detail page.
 26    /// Result: Dashboard → World → Campaign → Arc (current, disabled)
 27    /// </summary>
 28    List<BreadcrumbItem> ForArc(ArcDto arc, CampaignDto campaign, WorldDto world, bool currentDisabled = true);
 29
 30    /// <summary>
 31    /// Build breadcrumbs for an Article from API breadcrumb data.
 32    /// Result: Dashboard → World → [Parent Articles...] → Article (current, disabled)
 33    /// The API breadcrumbs already include the world as the first element.
 34    /// </summary>
 35    List<BreadcrumbItem> ForArticle(List<BreadcrumbDto> apiBreadcrumbs);
 36
 37    /// <summary>
 38    /// Build the full article URL path from API breadcrumbs.
 39    /// Returns: /article/world-slug/article-slug/child-slug
 40    /// </summary>
 41    string BuildArticleUrl(List<BreadcrumbDto> breadcrumbs);
 42
 43    /// <summary>
 44    /// Build the full article URL path for a specific article within the breadcrumb trail.
 45    /// Returns: /article/world-slug/...up-to-specified-index
 46    /// </summary>
 47    string BuildArticleUrlToIndex(List<BreadcrumbDto> breadcrumbs, int index);
 48}
 49
 50/// <summary>
 51/// Implementation of breadcrumb building service.
 52/// </summary>
 53public class BreadcrumbService : IBreadcrumbService
 54{
 55    /// <summary>
 56    /// Build breadcrumbs for a World detail page.
 57    /// </summary>
 58    public List<BreadcrumbItem> ForWorld(WorldDto world, bool currentDisabled = true)
 59    {
 360        return new List<BreadcrumbItem>
 361        {
 362            new("Dashboard", href: "/dashboard"),
 363            new(world.Name, href: currentDisabled ? null : $"/world/{world.Id}", disabled: currentDisabled)
 364        };
 65    }
 66
 67    /// <summary>
 68    /// Build breadcrumbs for a Campaign detail page.
 69    /// </summary>
 70    public List<BreadcrumbItem> ForCampaign(CampaignDto campaign, WorldDto world, bool currentDisabled = true)
 71    {
 372        return new List<BreadcrumbItem>
 373        {
 374            new("Dashboard", href: "/dashboard"),
 375            new(world.Name, href: $"/world/{world.Id}"),
 376            new(campaign.Name, href: currentDisabled ? null : $"/campaign/{campaign.Id}", disabled: currentDisabled)
 377        };
 78    }
 79
 80    /// <summary>
 81    /// Build breadcrumbs for an Arc detail page.
 82    /// </summary>
 83    public List<BreadcrumbItem> ForArc(ArcDto arc, CampaignDto campaign, WorldDto world, bool currentDisabled = true)
 84    {
 385        return new List<BreadcrumbItem>
 386        {
 387            new("Dashboard", href: "/dashboard"),
 388            new(world.Name, href: $"/world/{world.Id}"),
 389            new(campaign.Name, href: $"/campaign/{campaign.Id}"),
 390            new(arc.Name, href: currentDisabled ? null : $"/arc/{arc.Id}", disabled: currentDisabled)
 391        };
 92    }
 93
 94    /// <summary>
 95    /// Build breadcrumbs for an Article from API breadcrumb data.
 96    /// The API breadcrumbs include the world as the first element (IsWorld=true).
 97    /// </summary>
 98    public List<BreadcrumbItem> ForArticle(List<BreadcrumbDto> apiBreadcrumbs)
 99    {
 5100        var result = new List<BreadcrumbItem>
 5101        {
 5102            new("Dashboard", href: "/dashboard")
 5103        };
 104
 5105        if (apiBreadcrumbs == null || apiBreadcrumbs.Count == 0)
 2106            return result;
 107
 18108        for (int i = 0; i < apiBreadcrumbs.Count; i++)
 109        {
 6110            var crumb = apiBreadcrumbs[i];
 6111            var isLast = i == apiBreadcrumbs.Count - 1;
 112
 6113            if (crumb.IsWorld)
 114            {
 115                // World breadcrumb - link to world detail page
 3116                result.Add(new BreadcrumbItem(
 3117                    crumb.Title,
 3118                    href: isLast ? null : $"/world/{crumb.Id}",
 3119                    disabled: isLast));
 120            }
 121            else
 122            {
 123                // Article breadcrumb - build path up to this point
 3124                var path = BuildArticleUrlToIndex(apiBreadcrumbs, i);
 3125                result.Add(new BreadcrumbItem(
 3126                    crumb.Title,
 3127                    href: isLast ? null : path,
 3128                    disabled: isLast));
 129            }
 130        }
 131
 3132        return result;
 133    }
 134
 135    /// <summary>
 136    /// Build the full article URL path from API breadcrumbs.
 137    /// </summary>
 138    public string BuildArticleUrl(List<BreadcrumbDto> breadcrumbs)
 139    {
 4140        if (breadcrumbs == null || breadcrumbs.Count == 0)
 2141            return "/dashboard";
 142
 6143        var slugs = breadcrumbs.Select(b => b.Slug);
 2144        return $"/article/{string.Join("/", slugs)}";
 145    }
 146
 147    /// <summary>
 148    /// Build the article URL path up to a specific index in the breadcrumb trail.
 149    /// </summary>
 150    public string BuildArticleUrlToIndex(List<BreadcrumbDto> breadcrumbs, int index)
 151    {
 9152        if (breadcrumbs == null || breadcrumbs.Count == 0 || index < 0)
 3153            return "/dashboard";
 154
 155        // Clamp index to valid range
 6156        index = Math.Min(index, breadcrumbs.Count - 1);
 157
 158        // Take slugs up to and including the specified index
 18159        var slugs = breadcrumbs.Take(index + 1).Select(b => b.Slug);
 6160        return $"/article/{string.Join("/", slugs)}";
 161    }
 162}