From f5dae81193eb16be86d19e7f2adfafe4e402e3ce Mon Sep 17 00:00:00 2001 From: KoalaTrapLord Date: Thu, 12 Mar 2026 03:47:20 +0000 Subject: [PATCH] =?UTF-8?q?v27.4:=20Fix=20blank=20Collection/Decks=20pages?= =?UTF-8?q?=20=E2=80=94=20API=20response=20unwrapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - collection.ts: unwrap {items} -> cards, map camelCase params (pageSize, sortField, etc) - decks.ts: unwrap {decks} from listDecks, {cards} from getDeckCards, normalize field names (commander_name->commander, total_cards->card_count, role_tag->category) - decks.ts: transform getDeckAnalysis from backend shape (counts_by_type, color_pips) to frontend shape (card_count, land_count, type_distribution, etc) - types: SetInfo.card_count now optional (backend doesn't provide it) - Installer now bundles pre-built React dist/ as fallback --- .../commander-ai-lab-ui/src/api/collection.ts | 33 ++++++++++-- frontend/commander-ai-lab-ui/src/api/decks.ts | 50 +++++++++++++++++-- .../commander-ai-lab-ui/src/types/index.ts | 2 +- 3 files changed, 77 insertions(+), 8 deletions(-) diff --git a/frontend/commander-ai-lab-ui/src/api/collection.ts b/frontend/commander-ai-lab-ui/src/api/collection.ts index bac5240..86d61a0 100644 --- a/frontend/commander-ai-lab-ui/src/api/collection.ts +++ b/frontend/commander-ai-lab-ui/src/api/collection.ts @@ -2,9 +2,32 @@ import { get, post, patch, postForm } from './client' import type { CollectionCard, CollectionFilters, SetInfo, ScanResult } from '../types' export async function searchCollection(filters: CollectionFilters) { - return get<{ cards: CollectionCard[]; total: number; page: number; page_size: number }>( - '/api/collection', filters as Record + // Map frontend snake_case filter names to backend camelCase param names + const params: Record = {} + if (filters.q) params.q = filters.q + if (filters.page) params.page = filters.page + if (filters.page_size) params.pageSize = filters.page_size + if (filters.sort_by) params.sortField = filters.sort_by + if (filters.sort_dir) params.sortDir = filters.sort_dir + if (filters.color) params.colors = filters.color + if (filters.type_line) params.types = filters.type_line + if (filters.rarity) params.rarity = filters.rarity + if (filters.set_code) params.setCode = filters.set_code + if (filters.cmc_min !== undefined) params.cmcMin = filters.cmc_min + if (filters.cmc_max !== undefined) params.cmcMax = filters.cmc_max + if (filters.price_min !== undefined) params.priceMin = filters.price_min + if (filters.price_max !== undefined) params.priceMax = filters.price_max + if (filters.owned_only !== undefined) params.owned_only = filters.owned_only + + const res = await get<{ items?: CollectionCard[]; cards?: CollectionCard[]; total: number; page: number; pageSize?: number; page_size?: number }>( + '/api/collection', params ) + return { + cards: res.items || res.cards || [], + total: res.total || 0, + page: res.page || 1, + page_size: res.pageSize || res.page_size || 48, + } } export async function getCard(cardId: number) { @@ -16,11 +39,13 @@ export async function updateCard(cardId: number, updates: Partial('/api/collection/sets') + const res = await get('/api/collection/sets') + return Array.isArray(res) ? res : (res as { sets: SetInfo[] }).sets || [] } export async function getKeywords() { - return get('/api/collection/keywords') + const res = await get('/api/collection/keywords') + return Array.isArray(res) ? res : (res as { keywords: string[] }).keywords || [] } export async function exportCollection(format: string = 'csv') { diff --git a/frontend/commander-ai-lab-ui/src/api/decks.ts b/frontend/commander-ai-lab-ui/src/api/decks.ts index c401706..b0560ae 100644 --- a/frontend/commander-ai-lab-ui/src/api/decks.ts +++ b/frontend/commander-ai-lab-ui/src/api/decks.ts @@ -2,7 +2,17 @@ import { get, post, put, del, patch } from './client' import type { Deck, DeckCard, DeckAnalysis, EdhRecsResponse, CollectionRecsResponse, PplxStatus } from '../types' export async function listDecks() { - return get('/api/decks') + const res = await get<{ decks: Record[] } | Record[]>('/api/decks') + const raw = Array.isArray(res) ? res : (res as { decks: Record[] }).decks || [] + return raw.map(d => ({ + ...d, + commander: d.commander || d.commander_name || '', + card_count: d.card_count ?? d.total_cards ?? 0, + total_price: d.total_price ?? 0, + color_identity: d.color_identity || [], + created_date: d.created_date || d.created_at || '', + updated_date: d.updated_date || d.updated_at || '', + })) as Deck[] } export async function getDeck(deckId: number) { @@ -26,7 +36,13 @@ export async function deleteAllDecks() { } export async function getDeckCards(deckId: number) { - return get(`/api/decks/${deckId}/cards`) + const res = await get<{ cards: Record[]; total: number } | Record[]>(`/api/decks/${deckId}/cards`) + const raw = Array.isArray(res) ? res : (res as { cards: Record[] }).cards || [] + return raw.map(c => ({ + ...c, + name: c.name || c.card_name || '', + category: c.category || c.role_tag || '', + })) as DeckCard[] } export async function addCardToDeck(deckId: number, card: { name: string; scryfall_id?: string; quantity?: number; category?: string }) { @@ -42,7 +58,35 @@ export async function updateDeckCard(deckId: number, cardId: number, updates: Pa } export async function getDeckAnalysis(deckId: number) { - return get(`/api/decks/${deckId}/analysis`) + const raw = await get>(`/api/decks/${deckId}/analysis`) + // Backend returns: counts_by_type, targets, deltas, mana_curve, color_pips, total_cards, roles + // Frontend DeckAnalysis expects: card_count, land_count, creature_count, noncreature_count, avg_cmc, mana_curve, color_distribution, type_distribution, total_price, owned_count, missing_count + const counts = (raw.counts_by_type || {}) as Record + const totalCards = (raw.total_cards as number) || 0 + const landCount = counts['Land'] || 0 + const creatureCount = counts['Creature'] || 0 + const manaCurve = (raw.mana_curve || {}) as Record + const colorPips = (raw.color_pips || {}) as Record + // Compute avg CMC from mana curve + let totalMana = 0, nonLandCards = 0 + for (const [cmc, count] of Object.entries(manaCurve)) { + const cmcNum = cmc === '6+' ? 6 : Number(cmc) + totalMana += cmcNum * count + nonLandCards += count + } + return { + card_count: totalCards, + land_count: landCount, + creature_count: creatureCount, + noncreature_count: totalCards - landCount - creatureCount, + avg_cmc: nonLandCards > 0 ? totalMana / nonLandCards : 0, + mana_curve: manaCurve as Record, + color_distribution: colorPips, + type_distribution: counts, + total_price: 0, + owned_count: totalCards, + missing_count: 0, + } as DeckAnalysis } export async function getCollectionRecs(deckId: number) { diff --git a/frontend/commander-ai-lab-ui/src/types/index.ts b/frontend/commander-ai-lab-ui/src/types/index.ts index 5e76930..be14c52 100644 --- a/frontend/commander-ai-lab-ui/src/types/index.ts +++ b/frontend/commander-ai-lab-ui/src/types/index.ts @@ -57,7 +57,7 @@ export interface CollectionStats { export interface SetInfo { code: string name: string - card_count: number + card_count?: number } // ── Scan ─────────────────────────────────────────────────────