diff --git a/src/App.tsx b/src/App.tsx index 4c44de4..3c69e3a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,9 +7,10 @@ import React, { useState } from 'react'; import DeckEditor from './components/DeckEditor'; import Profile from './components/Profile'; import CardSearch from './components/CardSearch'; + import LifeCounter from './components/LifeCounter'; import { AuthProvider, useAuth } from './contexts/AuthContext'; - type Page = 'home' | 'deck' | 'login' | 'collection' | 'edit-deck' | 'profile' | 'search'; + type Page = 'home' | 'deck' | 'login' | 'collection' | 'edit-deck' | 'profile' | 'search' | 'life-counter'; function AppContent() { const [currentPage, setCurrentPage] = useState('home'); @@ -62,6 +63,8 @@ import React, { useState } from 'react'; return ; case 'search': return ; + case 'life-counter': + return ; case 'login': return ; default: diff --git a/src/components/LifeCounter.tsx b/src/components/LifeCounter.tsx new file mode 100644 index 0000000..a43f833 --- /dev/null +++ b/src/components/LifeCounter.tsx @@ -0,0 +1,167 @@ +import React, { useState, useEffect } from 'react'; + import { Plus, Minus } from 'lucide-react'; + + interface Player { + id: number; + name: string; + life: number; + color: string; + } + + const COLORS = ['white', 'blue', 'black', 'red', 'green']; + + export default function LifeCounter() { + const [numPlayers, setNumPlayers] = useState(null); + const [playerNames, setPlayerNames] = useState([]); + const [players, setPlayers] = useState([]); + const [setupComplete, setSetupComplete] = useState(false); + + useEffect(() => { + if (numPlayers !== null) { + setPlayers( + Array.from({ length: numPlayers }, (_, i) => ({ + id: i + 1, + name: playerNames[i] || `Player ${i + 1}`, + life: 20, + color: COLORS[i % COLORS.length], + })) + ); + } + }, [numPlayers, playerNames]); + + const handleNumPlayersChange = (e: React.ChangeEvent) => { + const newNumPlayers = parseInt(e.target.value, 10); + setNumPlayers(newNumPlayers); + setPlayerNames(Array(newNumPlayers).fill('')); + }; + + const handleNameChange = (index: number, newName: string) => { + const updatedNames = [...playerNames]; + updatedNames[index] = newName; + setPlayerNames(updatedNames); + }; + + const updateLife = (playerId: number, change: number) => { + setPlayers((prevPlayers) => + prevPlayers.map((player) => + player.id === playerId ? { ...player, life: player.life + change } : player + ) + ); + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setSetupComplete(true); + }; + + const renderSetupForm = () => ( +
+

Setup Players

+
+
+ + +
+ + {numPlayers !== null && + Array.from({ length: numPlayers }, (_, i) => ( +
+ + handleNameChange(i, e.target.value)} + className="w-full px-4 py-2 bg-gray-800 border border-gray-700 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-white" + placeholder={`Player ${i + 1} Name`} + /> +
+ ))} + + {numPlayers !== null && ( + + )} +
+
+ ); + + const renderLifeCounters = () => ( +
+
+ {players.map((player, index) => { + const angle = (index / players.length) * 360; + const rotation = 360 - angle; + const x = 50 + 40 * Math.cos((angle - 90) * Math.PI / 180); + const y = 50 + 40 * Math.sin((angle - 90) * Math.PI / 180); + + return ( +
+
+

{player.name}

+
{player.life}
+
+ + +
+
+
+ ); + })} +
+
+ ); + + return ( +
+
+

Life Counter

+ {!setupComplete ? renderSetupForm() : renderLifeCounters()} +
+
+ ); + } diff --git a/src/components/Navigation.tsx b/src/components/Navigation.tsx index 3f94673..3e53bca 100644 --- a/src/components/Navigation.tsx +++ b/src/components/Navigation.tsx @@ -1,9 +1,9 @@ import React, { useState, useRef, useEffect } from 'react'; - import { Home, PlusSquare, Library, LogOut, Settings, ChevronDown, Search } from 'lucide-react'; + import { Home, PlusSquare, Library, LogOut, Settings, ChevronDown, Search, Heart, Menu } from 'lucide-react'; import { useAuth } from '../contexts/AuthContext'; import { supabase } from '../lib/supabase'; - type Page = 'home' | 'deck' | 'login' | 'collection' | 'profile' | 'search'; + type Page = 'home' | 'deck' | 'login' | 'collection' | 'profile' | 'search' | 'life-counter'; interface NavigationProps { currentPage: Page; @@ -13,7 +13,9 @@ import React, { useState, useRef, useEffect } from 'react'; export default function Navigation({ currentPage, setCurrentPage }: NavigationProps) { const { user, signOut } = useAuth(); const [showDropdown, setShowDropdown] = useState(false); + const [showMobileMenu, setShowMobileMenu] = useState(false); const dropdownRef = useRef(null); + const mobileMenuRef = useRef(null); const [username, setUsername] = useState(null); useEffect(() => { @@ -39,6 +41,9 @@ import React, { useState, useRef, useEffect } from 'react'; if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setShowDropdown(false); } + if (mobileMenuRef.current && !mobileMenuRef.current.contains(event.target as Node)) { + setShowMobileMenu(false); + } }; document.addEventListener('mousedown', handleClickOutside); @@ -50,6 +55,7 @@ import React, { useState, useRef, useEffect } from 'react'; { id: 'deck' as const, label: 'New Deck', icon: PlusSquare }, { id: 'collection' as const, label: 'Collection', icon: Library }, { id: 'search' as const, label: 'Search', icon: Search }, + { id: 'life-counter' as const, label: 'Life Counter', icon: Heart }, ]; const handleSignOut = async () => { @@ -135,37 +141,54 @@ import React, { useState, useRef, useEffect } from 'react'; {/* Mobile Navigation - Bottom */} diff --git a/src/main.tsx b/src/main.tsx index ea9e363..a206d2d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,11 @@ import { StrictMode } from 'react'; -import { createRoot } from 'react-dom/client'; -import App from './App.tsx'; -import './index.css'; + import { createRoot } from 'react-dom/client'; + import App from './App.tsx'; + import './index.css'; + import './utils/theme.ts'; -createRoot(document.getElementById('root')!).render( - - - -); + createRoot(document.getElementById('root')!).render( + + + + ); diff --git a/src/utils/theme.ts b/src/utils/theme.ts index 84634e9..ff3f33a 100644 --- a/src/utils/theme.ts +++ b/src/utils/theme.ts @@ -1,32 +1,42 @@ export const themeColors = { - red: { - primary: '#ef4444', - secondary: '#b91c1c', - hover: '#dc2626' - }, - green: { - primary: '#22c55e', - secondary: '#15803d', - hover: '#16a34a' - }, - blue: { - primary: '#3b82f6', - secondary: '#1d4ed8', - hover: '#2563eb' - }, - yellow: { - primary: '#eab308', - secondary: '#a16207', - hover: '#ca8a04' - }, - grey: { - primary: '#6b7280', - secondary: '#374151', - hover: '#4b5563' - }, - purple: { - primary: '#a855f7', - secondary: '#7e22ce', - hover: '#9333ea' - } -}; + red: { + primary: '#ef4444', + secondary: '#b91c1c', + hover: '#dc2626' + }, + green: { + primary: '#22c55e', + secondary: '#15803d', + hover: '#16a34a' + }, + blue: { + primary: '#3b82f6', + secondary: '#1d4ed8', + hover: '#2563eb' + }, + yellow: { + primary: '#eab308', + secondary: '#a16207', + hover: '#ca8a04' + }, + grey: { + primary: '#6b7280', + secondary: '#374151', + hover: '#4b5563' + }, + purple: { + primary: '#a855f7', + secondary: '#7e22ce', + hover: '#9333ea' + }, + white: { + primary: '#f8fafc', + secondary: '#e2e8f0', + hover: '#f1f5f9', + }, + black: { + primary: '#0f172a', + secondary: '#1e293b', + hover: '#1e293b', + } + };