From 7e1cd5f9bd8b638bf8be3fcea45adac6daf90431 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Tue, 25 Nov 2025 17:39:35 +0100 Subject: [PATCH] improve suggest mana and add rule color identity commander --- src/components/DeckManager.tsx | 248 ++++++++++++++++++++++----------- 1 file changed, 169 insertions(+), 79 deletions(-) diff --git a/src/components/DeckManager.tsx b/src/components/DeckManager.tsx index 0aeaa35..f93179e 100644 --- a/src/components/DeckManager.tsx +++ b/src/components/DeckManager.tsx @@ -6,7 +6,7 @@ import { useAuth } from '../contexts/AuthContext'; import { supabase } from '../lib/supabase'; import { validateDeck } from '../utils/deckValidation'; import MagicCard from './MagicCard'; -import { ManaCost } from './ManaCost'; +import { ManaCost, ManaSymbol } from './ManaCost'; interface DeckManagerProps { initialDeck?: Deck; @@ -26,7 +26,8 @@ interface DeckManagerProps { const suggestLandCountAndDistribution = ( cards: { card; quantity: number }[], - format: string + format: string, + commanderColors: string[] = [] ) => { const formatRules = { standard: { minCards: 60 }, @@ -64,6 +65,16 @@ const suggestLandCountAndDistribution = ( } }); + // For commander, filter out colors not in commander's color identity + if (format === 'commander' && commanderColors.length > 0) { + for (const color in colorCounts) { + if (!commanderColors.includes(color)) { + totalColorSymbols -= colorCounts[color as keyof typeof colorCounts]; + colorCounts[color as keyof typeof colorCounts] = 0; + } + } + } + const landDistribution: { [key: string]: number } = {}; for (const color in colorCounts) { const proportion = @@ -96,6 +107,20 @@ const suggestLandCountAndDistribution = ( return { landCount: landsToAdd, landDistribution }; }; +// Get commander color identity +const getCommanderColors = (commander: Card | null): string[] => { + if (!commander) return []; + return commander.colors || []; +}; + +// Check if a card's colors are valid for the commander +const isCardValidForCommander = (card: Card, commanderColors: string[]): boolean => { + if (commanderColors.length === 0) return true; // No commander restriction + const cardColors = card.colors || []; + // Every color in the card must be in the commander's colors + return cardColors.every(color => commanderColors.includes(color)); +}; + export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) { const [currentDeckId, setCurrentDeckId] = useState(initialDeck?.id || null); const [searchQuery, setSearchQuery] = useState(''); @@ -396,11 +421,17 @@ export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) { const validation = validateDeck(currentDeck); + // Commander color identity validation + const commanderColors = deckFormat === 'commander' ? getCommanderColors(commander) : []; + const invalidCards = deckFormat === 'commander' && commander + ? selectedCards.filter(({ card }) => !isCardValidForCommander(card, commanderColors)) + : []; + const deckSize = selectedCards.reduce((acc, curr) => acc + curr.quantity, 0); const { landCount: suggestedLandCountValue, landDistribution: suggestedLands, - } = suggestLandCountAndDistribution(selectedCards, deckFormat); + } = suggestLandCountAndDistribution(selectedCards, deckFormat, commanderColors); const totalPrice = selectedCards.reduce((acc, { card, quantity }) => { const isBasicLand = @@ -556,10 +587,14 @@ export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) { ? card.card_faces[currentFaceIndex]?.name || card.name : card.name; + const isValidForCommander = deckFormat !== 'commander' || !commander || isCardValidForCommander(card, commanderColors); + return (
setHoveredCard(card)} onMouseLeave={() => setHoveredCard(null)} onClick={() => setSelectedCard(card)} @@ -606,6 +641,12 @@ export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) { x{inCollection} in collection
)} + {!isValidForCommander && ( +
+ + Not in commander colors +
+ )} {/* Add/Quantity Controls */} @@ -690,27 +731,39 @@ export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) { {deckFormat === 'commander' && ( - +
+ + {commander && commanderColors.length > 0 && ( +
+ Commander Colors: +
+ {commanderColors.map(color => ( + + ))} +
+
+ )} +
)}
@@ -744,6 +797,26 @@ export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) {
)} + {/* Commander Color Identity Warning */} + {deckFormat === 'commander' && commander && invalidCards.length > 0 && ( +
+
+ +
+

Commander Color Identity Warning

+

+ The following cards don't match your commander's color identity: +

+
    + {invalidCards.map(({ card }) => ( +
  • {card.name}
  • + ))} +
+
+
+
+ )} +

@@ -751,71 +824,88 @@ export default function DeckManager({ initialDeck, onSave }: DeckManagerProps) {

- {selectedCards.map(({ card, quantity }) => ( -
setHoveredCard(card)} - onMouseLeave={() => setHoveredCard(null)} - onClick={() => setSelectedCard(card)} - > - {card.name} -
-

{card.name}

- {card.prices?.usd && ( -
${card.prices.usd}
- )} -
-
e.stopPropagation()}> - - updateCardQuantity(card.id, parseInt(e.target.value)) - } - min="1" - className="w-14 px-2 py-1 bg-gray-600 border border-gray-500 rounded text-center text-sm" + {selectedCards.map(({ card, quantity }) => { + const isValidForCommander = deckFormat !== 'commander' || !commander || isCardValidForCommander(card, commanderColors); + + return ( +
setHoveredCard(card)} + onMouseLeave={() => setHoveredCard(null)} + onClick={() => setSelectedCard(card)} + > + {card.name} - +
+

{card.name}

+ {card.prices?.usd && ( +
${card.prices.usd}
+ )} + {!isValidForCommander && ( +
+ + Not in commander colors +
+ )} +
+
e.stopPropagation()}> + + updateCardQuantity(card.id, parseInt(e.target.value)) + } + min="1" + className="w-14 px-2 py-1 bg-gray-600 border border-gray-500 rounded text-center text-sm" + /> + +
-
- ))} + ); + })}
Total Price: ${totalPrice.toFixed(2)}
- {deckSize > 0 && ( -
- Suggested Land Count: {suggestedLandCountValue} - {Object.entries(suggestedLands).map(([landType, count]) => ( -
- {landType}: {count} -
- ))} + {deckSize > 0 && suggestedLandCountValue > 0 && ( +
+
+ Suggested Lands + {suggestedLandCountValue} total +
+
+ {Object.entries(suggestedLands).map(([landType, count]) => + count > 0 ? ( +
+ + {count} +
+ ) : null + )} +
+
)} - {deckSize > 0 && ( - - )} -
{!isLoadingCollection && getMissingCards().length > 0 && (