@page "/user-decks" @using System.Text.Json @implements IDisposable User Decks Management - Chrono DB

User Decks Database (`UserDeck`)

Browse, add, edit, and delete deck builds with native JSONB collection mappings.

@if (!string.IsNullOrEmpty(alertMessage)) { }
@if (!string.IsNullOrEmpty(searchTerm)) { }
Showing @decks.Count deck(s)
@if (isLoading) {
Loading decks from database...
} else if (!decks.Any()) {
No User Decks Found

No decks were found matching your search.

} else {
@foreach (var deck in decks) { var totalCards = deck.Cards?.Count ?? 0; var uniqueCards = deck.Cards?.Distinct().Count() ?? 0; var diverCount = deck.Divers?.Count ?? 0; }
Deck Name Season Cards (JSONB) Divers Notes Updated (UTC) Actions
@deck.Name
ID: @deck.Id
@(string.IsNullOrEmpty(deck.Season) ? "General" : deck.Season) @totalCards cards @diverCount
@(string.IsNullOrEmpty(deck.Notes) ? "-" : deck.Notes)
@deck.UpdatedAt.ToString("yyyy-MM-dd HH:mm")
}
@if (showModal) { } @if (showViewModal && viewedDeck != null) { } @if (showDeleteModal && deckToDelete != null) { } @code { private List decks = []; private List allCardNames = []; private List agentCardNames = []; private string searchTerm = ""; private bool isLoading = true; private bool isSaving; private string? alertMessage; private bool alertIsSuccess; private bool showModal; private bool isEditing; private UserDeck currentDeck = new(); private string newCardName = ""; private int newCardCount = 1; private string newDiverName = ""; private bool showViewModal; private UserDeck? viewedDeck; private bool showDeleteModal; private UserDeck? deckToDelete; protected override async Task OnInitializedAsync() { ConfigService.OnConfigurationChanged += HandleConfigChanged; try { allCardNames = CardDatabase.Cards .Select(c => c.Name) .Where(n => !string.IsNullOrWhiteSpace(n)) .Distinct() .OrderBy(n => n) .ToList(); agentCardNames = CardDatabase.Cards .Where(c => c.IsAgent) .Select(c => c.Name) .Where(n => !string.IsNullOrWhiteSpace(n)) .Distinct() .OrderBy(n => n) .ToList(); } catch { allCardNames = []; agentCardNames = []; } await LoadDecksAsync(); } private void HandleConfigChanged() { _ = LoadDecksAsync(); } private async Task LoadDecksAsync() { isLoading = true; try { decks = await DbService.GetUserDecksAsync(searchTerm); } catch (Exception ex) { alertIsSuccess = false; alertMessage = "Error loading user decks: " + ex.Message; } finally { isLoading = false; } } private async Task FilterDecks() { decks = await DbService.GetUserDecksAsync(searchTerm); } private async Task ClearSearch() { searchTerm = ""; await LoadDecksAsync(); } private void OpenAddModal() { isEditing = false; currentDeck = new UserDeck { Id = Guid.NewGuid(), Name = "", Season = "Season 1", UpdatedAt = DateTime.UtcNow, Cards = new List(), Divers = new List() }; newCardName = ""; newCardCount = 1; newDiverName = ""; showModal = true; } private void OpenEditModal(UserDeck deck) { isEditing = true; currentDeck = new UserDeck { Id = deck.Id, Name = deck.Name, Season = deck.Season, Notes = deck.Notes, UpdatedAt = deck.UpdatedAt, Cards = deck.Cards?.ToList() ?? new List(), Divers = deck.Divers?.ToList() ?? new List() }; newCardName = ""; newCardCount = 1; newDiverName = ""; showModal = true; } private void CloseModal() { showModal = false; } private void AddCardToDeck() { if (string.IsNullOrWhiteSpace(newCardName)) return; currentDeck.Cards ??= new List(); for (var i = 0; i < newCardCount; i++) { currentDeck.Cards.Add(newCardName.Trim()); } newCardName = ""; newCardCount = 1; } private void RemoveOneCardFromDeck(string cardName) { currentDeck.Cards?.Remove(cardName); } private void RemoveAllCardsFromDeck(string cardName) { currentDeck.Cards?.RemoveAll(c => c == cardName); } private void AddDiverToDeck() { if (string.IsNullOrWhiteSpace(newDiverName)) return; currentDeck.Divers ??= new List(); if (!currentDeck.Divers.Contains(newDiverName.Trim(), StringComparer.OrdinalIgnoreCase)) { currentDeck.Divers.Add(newDiverName.Trim()); } newDiverName = ""; } private void RemoveDiverFromDeck(string diver) { currentDeck.Divers?.Remove(diver); } private async Task SaveDeckAsync() { if (string.IsNullOrWhiteSpace(currentDeck.Name)) { alertIsSuccess = false; alertMessage = "Deck name cannot be empty."; return; } isSaving = true; var result = await DbService.SaveUserDeckAsync(currentDeck, !isEditing); isSaving = false; alertIsSuccess = result.Success; alertMessage = result.Message; if (result.Success) { showModal = false; await LoadDecksAsync(); } } private void OpenViewModal(UserDeck deck) { viewedDeck = deck; showViewModal = true; } private string GetFormattedDeckJson(UserDeck deck) { return JsonSerializer.Serialize(deck, new JsonSerializerOptions { WriteIndented = true }); } private void OpenDeleteModal(UserDeck deck) { deckToDelete = deck; showDeleteModal = true; } private async Task ConfirmDeleteAsync() { if (deckToDelete == null) return; isSaving = true; var result = await DbService.DeleteUserDeckAsync(deckToDelete.Id); isSaving = false; alertIsSuccess = result.Success; alertMessage = result.Message; showDeleteModal = false; deckToDelete = null; await LoadDecksAsync(); } private async Task SeedSampleDecksAsync() { var result = await DbService.SeedSampleDataAsync(); alertIsSuccess = result.Success; alertMessage = result.Message; await LoadDecksAsync(); } public void Dispose() { ConfigService.OnConfigurationChanged -= HandleConfigChanged; } }