Files
ChronoCCG/Chrono/Server/Components/Pages/MyDecks.razor
T
2026-07-03 12:26:41 -04:00

182 lines
5.9 KiB
Plaintext

@page "/my-decks"
@rendermode InteractiveServer
@using Microsoft.EntityFrameworkCore
@inject AppDbContext Db
@inject NavigationManager Nav
<PageTitle>My Decks - Chrono CCG</PageTitle>
<div class="my-decks-container">
<div class="my-decks-header">
<div>
<h1>My Decks</h1>
<p class="text-secondary">@decks.Count deck@(decks.Count != 1 ? "s" : "") saved</p>
</div>
<a href="/deck-builder" class="btn btn-primary">
<i class="bi bi-plus-lg me-1"></i> New Deck
</a>
</div>
@if (loading)
{
<div class="decks-loading">
<span class="spinner-border spinner-border-sm me-2"></span> Loading decks...
</div>
}
else if (decks.Count == 0)
{
<div class="decks-empty">
<i class="bi bi-journal-x"></i>
<p>No decks yet. Create your first deck!</p>
<a href="/deck-builder" class="btn btn-outline-primary">
<i class="bi bi-plus-lg me-1"></i> Build a Deck
</a>
</div>
}
else
{
<div class="decks-grid">
@foreach (var deck in decks)
{
var cardCount = deck.Cards.Count;
var uniqueCount = deck.Cards.Distinct().Count();
var isValid = cardCount == 40 && deck.Divers.Count == 2;
<div class="deck-card" @onclick="() => EditDeck(deck.Id)">
<div class="deck-card-header">
<div class="deck-name">@(string.IsNullOrEmpty(deck.Name) ? "Untitled Deck" : deck.Name)</div>
<span class="deck-valid-badge @(isValid ? "valid" : "invalid")">
<i class="bi @(isValid ? "bi-check-circle-fill" : "bi-exclamation-circle-fill")"></i>
@(isValid ? "Valid" : $"{cardCount}/40")
</span>
</div>
@if (!string.IsNullOrEmpty(deck.Season))
{
<div class="deck-season">
<i class="bi bi-calendar3"></i> @deck.Season
</div>
}
<div class="deck-meta">
<span><i class="bi bi-stack"></i> @cardCount cards</span>
<span><i class="bi bi-files"></i> @uniqueCount unique</span>
<span><i class="bi bi-person-arms-up"></i> @deck.Divers.Count/2 divers</span>
</div>
@if (deck.Divers.Count > 0)
{
<div class="deck-divers">
@foreach (var diver in deck.Divers)
{
<span class="diver-chip"><i class="bi bi-person-arms-up"></i> @diver</span>
}
</div>
}
@if (!string.IsNullOrEmpty(deck.Notes))
{
<div class="deck-notes-preview">@deck.Notes</div>
}
<div class="deck-footer">
<span class="deck-updated">
<i class="bi bi-clock"></i> @deck.UpdatedAt.ToLocalTime().ToString("MMM d, yyyy h:mm tt")
</span>
<button class="btn btn-sm btn-outline-danger deck-delete"
@onclick:stopPropagation="true"
@onclick="() => ConfirmDelete(deck)">
<i class="bi bi-trash3"></i>
</button>
</div>
</div>
}
</div>
}
</div>
@if (deckToDelete != null)
{
<div class="modal-backdrop" @onclick="CancelDelete"></div>
<div class="confirm-modal">
<h5>Delete Deck</h5>
<p>Delete <strong>@(string.IsNullOrEmpty(deckToDelete.Name) ? "Untitled Deck" : deckToDelete.Name)</strong>? This cannot be undone.</p>
<div class="d-flex gap-2 justify-content-end">
<button class="btn btn-outline-secondary" @onclick="CancelDelete">Cancel</button>
<button class="btn btn-danger" @onclick="DeleteDeck" disabled="@deleting">
@if (deleting) { <span class="spinner-border spinner-border-sm me-1"></span> }
Delete
</button>
</div>
</div>
}
@code {
private List<UserDeck> decks = [];
private bool loading = true;
private UserDeck? deckToDelete;
private bool deleting;
protected override async Task OnInitializedAsync()
{
await LoadDecks();
}
private async Task LoadDecks()
{
loading = true;
try
{
decks = await Db.UserDecks.OrderByDescending(d => d.UpdatedAt).ToListAsync();
}
catch (Exception ex)
{
Console.WriteLine($"[WARNING] Could not load decks: {ex.Message}");
decks = [];
}
finally
{
loading = false;
}
}
private void EditDeck(Guid id)
{
Nav.NavigateTo($"/deck-builder/{id}");
}
private void ConfirmDelete(UserDeck deck)
{
deckToDelete = deck;
}
private void CancelDelete()
{
deckToDelete = null;
}
private async Task DeleteDeck()
{
if (deckToDelete == null) return;
deleting = true;
try
{
var deck = await Db.UserDecks.FindAsync(deckToDelete.Id);
if (deck != null)
{
Db.UserDecks.Remove(deck);
await Db.SaveChangesAsync();
}
}
catch (Exception ex)
{
Console.WriteLine($"[WARNING] Could not delete deck: {ex.Message}");
}
finally
{
deleting = false;
deckToDelete = null;
await LoadDecks();
}
}
}