< Summary

Information
Class: Chronicis.Api.Controllers.DashboardController
Assembly: Chronicis.Api
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Api/Controllers/DashboardController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 108
Coverable lines: 108
Total lines: 175
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 38
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%
GetDashboard()0%930300%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Api/Controllers/DashboardController.cs

#LineLine coverage
 1using Chronicis.Api.Data;
 2using Chronicis.Api.Infrastructure;
 3using Chronicis.Api.Services;
 4using Chronicis.Shared.DTOs;
 5using Chronicis.Shared.Enums;
 6using Microsoft.AspNetCore.Authorization;
 7using Microsoft.AspNetCore.Mvc;
 8using Microsoft.EntityFrameworkCore;
 9
 10namespace Chronicis.Api.Controllers;
 11
 12/// <summary>
 13/// API endpoints for Dashboard data.
 14/// </summary>
 15[ApiController]
 16[Route("dashboard")]
 17[Authorize]
 18public class DashboardController : ControllerBase
 19{
 20    private readonly ChronicisDbContext _context;
 21    private readonly IPromptService _promptService;
 22    private readonly ICurrentUserService _currentUserService;
 23    private readonly ILogger<DashboardController> _logger;
 24
 025    public DashboardController(
 026        ChronicisDbContext context,
 027        IPromptService promptService,
 028        ICurrentUserService currentUserService,
 029        ILogger<DashboardController> logger)
 30    {
 031        _context = context;
 032        _promptService = promptService;
 033        _currentUserService = currentUserService;
 034        _logger = logger;
 035    }
 36
 37    /// <summary>
 38    /// GET /api/dashboard - Get aggregated dashboard data for the current user.
 39    /// </summary>
 40    [HttpGet]
 41    public async Task<ActionResult<DashboardDto>> GetDashboard()
 42    {
 043        var user = await _currentUserService.GetRequiredUserAsync();
 044        _logger.LogDebug("Getting dashboard for user {UserId}", user.Id);
 45
 46        // Get all worlds the user has access to (via membership)
 047        var worldIds = await _context.WorldMembers
 048            .Where(wm => wm.UserId == user.Id)
 049            .Select(wm => wm.WorldId)
 050            .ToListAsync();
 51
 52        // Get world data with campaigns and arcs
 053        var worlds = await _context.Worlds
 054            .Where(w => worldIds.Contains(w.Id))
 055            .Include(w => w.Campaigns)
 056                .ThenInclude(c => c.Arcs)
 057            .Include(w => w.Articles)
 058            .OrderBy(w => w.Name)
 059            .ToListAsync();
 60
 61        // Get claimed characters for this user across all worlds
 062        var claimedCharacters = await _context.Articles
 063            .Where(a => a.PlayerId == user.Id && a.Type == ArticleType.Character)
 064            .Where(a => a.WorldId.HasValue && worldIds.Contains(a.WorldId.Value))
 065            .Select(a => new ClaimedCharacterDto
 066            {
 067                Id = a.Id,
 068                Title = a.Title ?? "Unnamed Character",
 069                IconEmoji = a.IconEmoji,
 070                WorldId = a.WorldId!.Value,
 071                WorldName = a.World != null ? a.World.Name : "Unknown World",
 072                ModifiedAt = a.ModifiedAt,
 073                CreatedAt = a.CreatedAt
 074            })
 075            .ToListAsync();
 76
 77        // Build dashboard worlds
 078        var dashboardWorlds = new List<DashboardWorldDto>();
 079        foreach (var world in worlds)
 80        {
 081            var dashboardWorld = new DashboardWorldDto
 082            {
 083                Id = world.Id,
 084                Name = world.Name,
 085                Slug = world.Slug,
 086                Description = world.Description,
 087                CreatedAt = world.CreatedAt,
 088                ArticleCount = world.Articles?.Count ?? 0,
 089                Campaigns = new List<DashboardCampaignDto>(),
 090                MyCharacters = new List<DashboardCharacterDto>()
 091            };
 92
 93            // Find the world root article (if exists)
 094            var worldRoot = world.Articles?.FirstOrDefault(a => a.ParentId == null && a.Type == ArticleType.WikiArticle)
 095            dashboardWorld.WorldRootArticleId = worldRoot?.Id;
 96
 97            // Build campaign data
 098            if (world.Campaigns != null)
 99            {
 0100                foreach (var campaign in world.Campaigns.OrderByDescending(c => c.IsActive).ThenBy(c => c.Name))
 101                {
 0102                    var sessionCount = world.Articles?.Count(a => a.CampaignId == campaign.Id && a.Type == ArticleType.S
 103
 0104                    var dashboardCampaign = new DashboardCampaignDto
 0105                    {
 0106                        Id = campaign.Id,
 0107                        Name = campaign.Name,
 0108                        Description = campaign.Description,
 0109                        CreatedAt = campaign.CreatedAt,
 0110                        StartedAt = campaign.StartedAt,
 0111                        IsActive = campaign.IsActive,
 0112                        SessionCount = sessionCount,
 0113                        ArcCount = campaign.Arcs?.Count ?? 0
 0114                    };
 115
 116                    // Get current/active arc
 0117                    var activeArc = campaign.Arcs?
 0118                        .Where(a => a.IsActive)
 0119                        .OrderByDescending(a => a.SortOrder)
 0120                        .FirstOrDefault();
 121
 0122                    if (activeArc != null)
 123                    {
 0124                        var arcSessionCount = world.Articles?.Count(a => a.ArcId == activeArc.Id && a.Type == ArticleTyp
 0125                        var latestSession = world.Articles?
 0126                            .Where(a => a.ArcId == activeArc.Id && a.Type == ArticleType.Session)
 0127                            .OrderByDescending(a => a.SessionDate ?? a.CreatedAt)
 0128                            .FirstOrDefault();
 129
 0130                        dashboardCampaign.CurrentArc = new DashboardArcDto
 0131                        {
 0132                            Id = activeArc.Id,
 0133                            Name = activeArc.Name,
 0134                            Description = activeArc.Description,
 0135                            SessionCount = arcSessionCount,
 0136                            LatestSessionDate = latestSession?.SessionDate ?? latestSession?.CreatedAt
 0137                        };
 138                    }
 139
 0140                    dashboardWorld.Campaigns.Add(dashboardCampaign);
 141                }
 142            }
 143
 144            // Get user's characters in this world
 0145            var myCharactersInWorld = claimedCharacters
 0146                .Where(c => c.WorldId == world.Id)
 0147                .Select(c => new DashboardCharacterDto
 0148                {
 0149                    Id = c.Id,
 0150                    Title = c.Title,
 0151                    IconEmoji = c.IconEmoji,
 0152                    ModifiedAt = c.ModifiedAt,
 0153                    CreatedAt = c.CreatedAt
 0154                })
 0155                .ToList();
 156
 0157            dashboardWorld.MyCharacters = myCharactersInWorld;
 158
 0159            dashboardWorlds.Add(dashboardWorld);
 160        }
 161
 0162        var dashboard = new DashboardDto
 0163        {
 0164            UserDisplayName = user.DisplayName,
 0165            Worlds = dashboardWorlds,
 0166            ClaimedCharacters = claimedCharacters,
 0167            Prompts = new List<PromptDto>()
 0168        };
 169
 170        // Generate contextual prompts
 0171        dashboard.Prompts = _promptService.GeneratePrompts(dashboard);
 172
 0173        return Ok(dashboard);
 0174    }
 175}