Skip to content

feat: Campaign Dashboard UI (DM Oversight) (spec #23) #24

@Fortinbra

Description

@Fortinbra

Feature Spec: Campaign Dashboard UI (DM Oversight)

Field Value
Feature Campaign Dashboard UI (DM Oversight)
Issue #TBD
Status Draft
Author The Doctor
Date 2026-04-10

Overview

What it does

Provides a Blazor WASM dashboard for the Human DM to monitor active campaigns. The dashboard surfaces active session status, player roster, encounter state, narrative log tail, and dice roll history, plus quick actions (trigger encounter, award loot, send DM message, toggle autopilot).

Why it's needed

The Human DM needs a single pane of glass to oversee gameplay without switching between Discord and multiple UI pages. This dashboard consolidates essential controls and telemetry to keep sessions smooth and auditable.

Out of scope

  • Real-time voice or video features
  • Full analytics/reporting suite
  • Editing world lore or rulesets (separate specs)

Architecture Notes (The Doctor)

Projects / layers touched

  • DungeonMaster.Web — dashboard page + components
  • DungeonMaster.Api — dashboard query + quick-action endpoints
  • DungeonMaster.Core — dashboard query service
  • DungeonMaster.Shared — dashboard DTOs

New interfaces in DungeonMaster.Core

  • ICampaignDashboardQueryService — composes dashboard snapshot

Data flow

Dashboard loads /campaigns/{id}/dashboard
  → API calls ICampaignDashboardQueryService
  → Query composes session, encounter, narrative log tail, dice history
  → Web renders panels + quick actions

Integration points with existing systems


Domain Model

Records

public sealed record CampaignDashboardSnapshot(
    Guid CampaignId,
    Guid? ActiveSessionId,
    bool AutopilotEnabled,
    IReadOnlyList<PlayerSummary> Players,
    EncounterSummary? ActiveEncounter,
    IReadOnlyList<NarrativeLogEntry> NarrativeTail,
    IReadOnlyList<DiceRollEntry> DiceHistory);

API Contract (Rory)

Endpoints

  • GET /api/campaigns/{id}/dashboard — returns CampaignDashboardSnapshot
  • POST /api/campaigns/{id}/dashboard/trigger-encounter — DM-only quick action
  • POST /api/campaigns/{id}/dashboard/award-loot — DM-only quick action
  • POST /api/campaigns/{id}/dashboard/dm-message — DM-only; posts to Discord channel
  • PUT /api/campaigns/{id}/dashboard/autopilot — toggle autopilot

UI Specification (Clara)

Page / component breakdown

  • CampaignDashboard (/campaigns/{id}/dashboard) — main DM oversight page

Blazor component list

Component File Purpose
CampaignDashboard Pages/Campaigns/CampaignDashboard.razor / .razor.cs Layout container and data loading
EncounterPanel Components/Dashboard/EncounterPanel.razor / .razor.cs Active encounter status, HP bars, turn order
NarrativeTailPanel Components/Dashboard/NarrativeTailPanel.razor / .razor.cs Last N narrative log entries
DiceHistoryPanel Components/Dashboard/DiceHistoryPanel.razor / .razor.cs Recent dice rolls
QuickActionsPanel Components/Dashboard/QuickActionsPanel.razor / .razor.cs Trigger encounter, award loot, DM message, autopilot toggle
PlayerRosterPanel Components/Dashboard/PlayerRosterPanel.razor / .razor.cs Player cards with HP/AC summary

Theme / styling requirements

  • Use var(--color-surface) for panel backgrounds
  • Use var(--spacing-md) for panel padding
  • Use var(--color-accent) for active encounter highlights
  • No hardcoded colors or inline styles

User interaction flow

  1. DM navigates to dashboard page for campaign
  2. Page loads snapshot and displays panels
  3. DM uses quick actions (trigger encounter, award loot, send message)
  4. Autopilot toggle updates state and refreshes snapshot

Test Scenarios (Danny) ⚠️ COMPLETE BEFORE IMPLEMENTATION

Happy path

  1. Given a campaign with an active session
    When the DM loads the dashboard
    Then the snapshot shows session status, players, and narrative tail

  2. Given the DM clicks "Toggle Autopilot"
    When the API confirms
    Then the UI updates the toggle and refreshes the snapshot

Edge cases

  1. Given there is no active encounter
    When the dashboard loads
    Then EncounterPanel shows an empty-state message

  2. Given the narrative log is empty
    When NarrativeTailPanel renders
    Then a placeholder message is shown without errors

Error / failure cases

  1. Given the API returns 403
    When the DM loads the dashboard
    Then the page shows an authorization error state

  2. Given quick action fails
    When the DM triggers it
    Then an error toast is shown and no state changes occur


Acceptance Criteria

Functional

  • Dashboard shows active session, players, encounter, narrative tail, and dice history
  • Quick actions trigger corresponding API endpoints
  • Autopilot toggle updates campaign/session state
  • All components use code-behind pattern

Non-functional

  • Snapshot loads within 500 ms under normal load
  • UI uses CSS custom properties only
  • bUnit tests cover all panels

Dependencies


Agent Work Breakdown

Agent Task Depends On
The Doctor Approve spec
Danny Write failing bUnit tests Spec approved
Clara Implement dashboard components Danny's tests
Rory Implement dashboard API/query service Core entities
Danny Confirm all tests pass All implementation

Definition of Done

  • Test Scenarios section completed and approved by The Doctor before implementation
  • All failing tests written by Danny before implementation (TDD)
  • All tests written and passing (bUnit + API tests)
  • Code reviewed and approved by The Doctor
  • Code-behind pattern enforced for all Razor components
  • CSS uses custom properties only
  • GitHub issue closed and linked to merged PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestspecSpecification work

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions