< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 92
Coverable lines: 92
Total lines: 260
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 54
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Components/Quests/ArcQuestList.razor

#LineLine coverage
 1@using Chronicis.Shared.DTOs.Quests
 2@using Chronicis.Shared.Enums
 3
 4<div class="arc-quest-list">
 5    <div class="d-flex align-center justify-space-between mb-3">
 6        <MudText Typo="Typo.h6" Style="color: var(--chronicis-beige-gold);">
 7            <MudIcon Icon="@Icons.Material.Filled.Flag" Size="Size.Small" Class="mr-2" />
 8            Quests
 9        </MudText>
 010        @if (_isGm)
 11        {
 12            <MudButton Variant="Variant.Filled"
 13                       Color="Color.Primary"
 14                       Size="Size.Small"
 15                       StartIcon="@Icons.Material.Filled.Add"
 16                       OnClick="CreateQuest"
 17                       Disabled="_isLoading || _isCreating">
 018                @if (_isCreating)
 19                {
 20                    <MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-1" />
 21                }
 22                <span>New Quest</span>
 23            </MudButton>
 24        }
 25    </div>
 26
 027    @if (_loadingError != null)
 28    {
 29        <MudAlert Severity="Severity.Error" Class="mb-4">
 30            <div class="d-flex align-center justify-space-between">
 31                <div>
 32                    <strong>Failed to load quests</strong><br />
 033                    <span style="font-size: 0.875rem;">@_loadingError</span>
 34                </div>
 35                <MudButton Variant="Variant.Text"
 36                           Color="Color.Error"
 37                           OnClick="LoadQuestsAsync">
 38                    Retry
 39                </MudButton>
 40            </div>
 41        </MudAlert>
 42    }
 043    else if (_isLoading)
 44    {
 45        <div class="d-flex align-center gap-2 mb-4">
 46            <MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Small" />
 47            <MudText Typo="Typo.body2" Style="opacity: 0.7;">Loading quests...</MudText>
 48        </div>
 49    }
 050    else if (_quests.Any())
 51    {
 52        <MudList T="QuestDto" Dense="true" Class="mb-4">
 053            @foreach (var quest in _quests.OrderByDescending(q => q.UpdatedAt))
 54            {
 55                <MudListItem T="QuestDto" Class="quest-list-item">
 56                    <div class="d-flex align-center justify-space-between">
 57                        <div class="flex-grow-1">
 58                            <div class="d-flex align-center gap-2">
 059                                <MudText Typo="Typo.body1" Class="quest-title">@quest.Title</MudText>
 60                                <QuestStatusChip Status="@quest.Status" />
 061                                @if (quest.IsGmOnly)
 62                                {
 63                                    <MudChip Size="Size.Small" Color="Color.Warning" Variant="Variant.Text">
 64                                        GM Only
 65                                    </MudChip>
 66                                }
 67                            </div>
 68                            <MudText Typo="Typo.caption" Class="text-muted">
 069                                Updated @quest.UpdatedAt.ToString("MMM d, yyyy")
 070                                @if (quest.UpdateCount > 0)
 71                                {
 072                                    <span> · @quest.UpdateCount update@(quest.UpdateCount == 1 ? "" : "s")</span>
 73                                }
 74                            </MudText>
 75                        </div>
 076                        @if (_isGm)
 77                        {
 78                            <div class="d-flex gap-2">
 79                                <MudIconButton Icon="@Icons.Material.Filled.Edit"
 80                                               Size="Size.Small"
 81                                               Color="Color.Primary"
 082                                               OnClick="@(() => EditQuest(quest))"
 83                                               title="Edit quest" />
 84                                <MudIconButton Icon="@Icons.Material.Filled.Delete"
 85                                               Size="Size.Small"
 86                                               Color="Color.Error"
 087                                               OnClick="@(() => DeleteQuest(quest))"
 88                                               Disabled="_isDeleting"
 89                                               title="Delete quest" />
 90                            </div>
 91                        }
 92                    </div>
 93                </MudListItem>
 94            }
 95        </MudList>
 96    }
 97    else
 98    {
 99        <MudAlert Severity="Severity.Info" Class="mb-4">
 0100            No quests yet. @(_isGm ? "Create your first quest to track objectives and goals!" : "The GM hasn't created a
 101        </MudAlert>
 102    }
 103</div>

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Components/Quests/ArcQuestList.razor.cs

#LineLine coverage
 1using Chronicis.Client.Components.Dialogs;
 2using Chronicis.Client.Services;
 3using Chronicis.Shared.DTOs.Quests;
 4using Microsoft.AspNetCore.Components;
 5using MudBlazor;
 6
 7namespace Chronicis.Client.Components.Quests;
 8
 9public partial class ArcQuestList : ComponentBase
 10{
 11    [Parameter, EditorRequired]
 012    public Guid ArcId { get; set; }
 13
 14    [Parameter]
 015    public bool IsGm { get; set; }
 16
 017    [Inject] private IQuestApiService QuestApi { get; set; } = default!;
 018    [Inject] private IDialogService DialogService { get; set; } = default!;
 019    [Inject] private ISnackbar Snackbar { get; set; } = default!;
 020    [Inject] private ILogger<ArcQuestList> Logger { get; set; } = default!;
 21
 022    private List<QuestDto> _quests = new();
 23    private bool _isLoading;
 24    private bool _isCreating;
 25    private bool _isDeleting;
 26    private bool _isGm;
 27    private string? _loadingError;
 28
 29    protected override async Task OnInitializedAsync()
 30    {
 031        _isGm = IsGm;
 032        await LoadQuestsAsync();
 033    }
 34
 35    protected override async Task OnParametersSetAsync()
 36    {
 037        _isGm = IsGm;
 38
 39        // Reload if ArcId changes
 040        if (ArcId != Guid.Empty)
 41        {
 042            await LoadQuestsAsync();
 43        }
 044    }
 45
 46    private async Task LoadQuestsAsync()
 47    {
 048        _isLoading = true;
 049        _loadingError = null;
 050        StateHasChanged();
 51
 52        try
 53        {
 054            _quests = await QuestApi.GetArcQuestsAsync(ArcId);
 055        }
 056        catch (Exception ex)
 57        {
 058            Logger.LogError(ex, "Failed to load quests for arc {ArcId}", ArcId);
 059            _loadingError = ex.Message;
 060            _quests = new();
 061        }
 62        finally
 63        {
 064            _isLoading = false;
 065            StateHasChanged();
 66        }
 067    }
 68
 69    private async Task CreateQuest()
 70    {
 071        if (!_isGm || _isCreating)
 072            return;
 73
 074        _isCreating = true;
 075        StateHasChanged();
 76
 77        try
 78        {
 079            var parameters = new DialogParameters
 080            {
 081                { "ArcId", ArcId }
 082            };
 83
 084            var dialog = await DialogService.ShowAsync<CreateQuestDialog>("Create Quest", parameters);
 085            var result = await dialog.Result;
 86
 087            if (result != null && !result.Canceled)
 88            {
 089                await LoadQuestsAsync();
 090                Snackbar.Add("Quest created successfully", Severity.Success);
 91            }
 092        }
 093        catch (Exception ex)
 94        {
 095            Logger.LogError(ex, "Failed to create quest");
 096            Snackbar.Add($"Failed to create quest: {ex.Message}", Severity.Error);
 097        }
 98        finally
 99        {
 0100            _isCreating = false;
 0101            StateHasChanged();
 102        }
 0103    }
 104
 105    private void EditQuest(QuestDto quest)
 106    {
 0107        if (!_isGm)
 0108            return;
 109
 110        // This will be handled by the parent ArcDetail page
 111        // which will show the quest editor inline
 112        // For now, just show a placeholder
 0113        Snackbar.Add($"Editing quest: {quest.Title}", Severity.Info);
 0114    }
 115
 116    private async Task DeleteQuest(QuestDto quest)
 117    {
 0118        if (!_isGm || _isDeleting)
 0119            return;
 120
 0121        var confirmed = await DialogService.ShowMessageBox(
 0122            "Delete Quest",
 0123            $"Are you sure you want to delete '{quest.Title}'? This will also delete all quest updates. This action cann
 0124            yesText: "Delete",
 0125            cancelText: "Cancel");
 126
 0127        if (confirmed != true)
 0128            return;
 129
 0130        _isDeleting = true;
 0131        StateHasChanged();
 132
 133        try
 134        {
 0135            var success = await QuestApi.DeleteQuestAsync(quest.Id);
 0136            if (success)
 137            {
 0138                Snackbar.Add("Quest deleted successfully", Severity.Success);
 0139                await LoadQuestsAsync();
 140            }
 141            else
 142            {
 0143                Snackbar.Add("Failed to delete quest", Severity.Error);
 144            }
 0145        }
 0146        catch (Exception ex)
 147        {
 0148            Logger.LogError(ex, "Failed to delete quest {QuestId}", quest.Id);
 0149            Snackbar.Add($"Error deleting quest: {ex.Message}", Severity.Error);
 0150        }
 151        finally
 152        {
 0153            _isDeleting = false;
 0154            StateHasChanged();
 155        }
 0156    }
 157}