diff --git a/src/client/src/components/PackCard.tsx b/src/client/src/components/PackCard.tsx index 1d970dc..b2196ac 100644 --- a/src/client/src/components/PackCard.tsx +++ b/src/client/src/components/PackCard.tsx @@ -9,6 +9,8 @@ interface PackCardProps { } const ListItem: React.FC<{ card: DraftCard }> = ({ card }) => { + const isFoil = (card: DraftCard) => card.finish === 'foil'; + const getRarityColorClass = (rarity: string) => { switch (rarity) { case 'common': return 'bg-black text-white border-slate-600'; @@ -22,15 +24,21 @@ const ListItem: React.FC<{ card: DraftCard }> = ({ card }) => { return (
  • - + {card.name} + {isFoil(card) && ( + + FOIL + + )}
    {card.image && (
    -
    - {card.name} +
    + {isFoil(card) &&
    } + {card.name}
    )} @@ -43,6 +51,7 @@ export const PackCard: React.FC = ({ pack, viewMode }) => { const rares = pack.cards.filter(c => c.rarity === 'rare'); const uncommons = pack.cards.filter(c => c.rarity === 'uncommon'); const commons = pack.cards.filter(c => c.rarity === 'common'); + const isFoil = (card: DraftCard) => card.finish === 'foil'; const copyPackToClipboard = () => { const text = pack.cards.map(c => c.name).join('\n'); @@ -95,7 +104,10 @@ export const PackCard: React.FC = ({ pack, viewMode }) => { {viewMode === 'grid' && (
    {pack.cards.map((card) => ( -
    +
    + {isFoil(card) &&
    } + {isFoil(card) &&
    FOIL
    } + {card.image ? ( {card.name} ) : ( diff --git a/src/client/src/modules/cube/CubeManager.tsx b/src/client/src/modules/cube/CubeManager.tsx index b1f87f3..a6cde6b 100644 --- a/src/client/src/modules/cube/CubeManager.tsx +++ b/src/client/src/modules/cube/CubeManager.tsx @@ -129,7 +129,14 @@ export const CubeManager: React.FC = ({ packs, setPacks, onGoT identifiers.forEach(id => { const card = scryfallService.getCachedCard(id.type === 'id' ? { id: id.value } : { name: id.value }); if (card) { - for (let i = 0; i < id.quantity; i++) expandedCards.push(card); + for (let i = 0; i < id.quantity; i++) { + // Clone card to attach unique properties like finish + const expandedCard = { ...card }; + if (id.finish) { + expandedCard.finish = id.finish; + } + expandedCards.push(expandedCard); + } } }); } diff --git a/src/client/src/services/CardParserService.ts b/src/client/src/services/CardParserService.ts index daeec41..4f73a1f 100644 --- a/src/client/src/services/CardParserService.ts +++ b/src/client/src/services/CardParserService.ts @@ -2,6 +2,7 @@ export interface CardIdentifier { type: 'id' | 'name'; value: string; quantity: number; + finish?: 'foil' | 'normal'; } export class CardParserService { @@ -11,13 +12,29 @@ export class CardParserService { const uuidRegex = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i; lines.forEach(line => { - if (line.toLowerCase().startsWith('quantity') || line.toLowerCase().startsWith('count,name')) return; + // Skip header + if (line.toLowerCase().startsWith('quantity') && line.toLowerCase().includes('name')) return; const idMatch = line.match(uuidRegex); const cleanLineForQty = line.replace(/['"]/g, ''); const quantityMatch = cleanLineForQty.match(/^(\d+)[xX\s,;]/); const quantity = quantityMatch ? parseInt(quantityMatch[1], 10) : 1; + // Detect Finish from CSV (Comma Separated) + let finish: 'foil' | 'normal' | undefined = undefined; + const parts = line.split(','); + if (parts.length >= 3) { + // Assuming format: Quantity,Name,Finish,... + // If the line started with a number, parts[0] is quantity. parts[1] is name. parts[2] is Finish. + // We should be careful about commas in names, but the user example shows a clean structure. + // If the name is quoted, split(',') might be naive, but valid for the provided example. + // Let's assume the user provided format: Quantity,Name,Finish,Edition Name,Scryfall ID + + const possibleFinish = parts[2].trim().toLowerCase(); + if (possibleFinish === 'foil' || possibleFinish === 'etched') finish = 'foil'; + else if (possibleFinish === 'normal') finish = 'normal'; + } + let identifier: { type: 'id' | 'name', value: string } | null = null; if (idMatch) { @@ -28,7 +45,6 @@ export class CardParserService { let name = cleanLine.replace(/^(\d+)[xX\s,;]+/, '').trim(); // Remove set codes in parentheses/brackets e.g. (M20), [STA] - // This regex looks for ( starts, anything inside, ) ends, or same for [] name = name.replace(/\s*[\(\[].*?[\)\]]/g, ''); // Remove trailing collector numbers (digits at the very end) @@ -44,16 +60,11 @@ export class CardParserService { } if (identifier) { - // Return one entry per quantity? Or aggregated? - // The original code pushed multiple entries to an array. - // For a parser service, returning the count is better, but to match logic: - // "for (let i = 0; i < quantity; i++) rawCardList.push(identifier);" - // I will return one object with Quantity property to be efficient. - rawCardList.push({ type: identifier.type, value: identifier.value, - quantity: quantity + quantity: quantity, + finish: finish }); } }); diff --git a/src/client/src/services/PackGeneratorService.ts b/src/client/src/services/PackGeneratorService.ts index 8177991..d2a57cd 100644 --- a/src/client/src/services/PackGeneratorService.ts +++ b/src/client/src/services/PackGeneratorService.ts @@ -10,6 +10,7 @@ export interface DraftCard { set: string; setCode: string; setType: string; + finish?: 'foil' | 'normal'; } export interface Pack { @@ -71,7 +72,8 @@ export class PackGeneratorService { image: cardData.image_uris?.normal || cardData.card_faces?.[0]?.image_uris?.normal || '', set: cardData.set_name, setCode: cardData.set, - setType: setType + setType: setType, + finish: cardData.finish }; // Add to pools diff --git a/src/client/src/services/ScryfallService.ts b/src/client/src/services/ScryfallService.ts index d680af4..d99fd44 100644 --- a/src/client/src/services/ScryfallService.ts +++ b/src/client/src/services/ScryfallService.ts @@ -10,6 +10,7 @@ export interface ScryfallCard { colors?: string[]; image_uris?: { normal: string }; card_faces?: { image_uris: { normal: string } }[]; + finish?: 'foil' | 'normal'; // Manual override from import } export class ScryfallService {