diff --git a/backend/app/api/routes/auth.py b/backend/app/api/routes/auth.py index 3fe05ad..9bfff4e 100644 --- a/backend/app/api/routes/auth.py +++ b/backend/app/api/routes/auth.py @@ -9,6 +9,7 @@ from pydantic import BaseModel from app.config import get_settings from app.utils.auth import create_token, verify_token +from app.services.sba_api_client import sba_api_client from app.utils.cookies import ( ACCESS_TOKEN_COOKIE, REFRESH_TOKEN_COOKIE, @@ -484,10 +485,22 @@ async def get_current_user_info( discriminator="0", # Discord removed discriminators ) - # TODO: Load user's teams from database - teams = [] + # Load user's teams from SBA API + discord_id = payload["discord_id"] + sba_teams = await sba_api_client.get_teams_by_owner( + discord_id, season=settings.sba_current_season + ) + teams = [ + { + "id": team["id"], + "name": team.get("lname", team.get("sname", "Unknown")), + "owner_id": discord_id, + "league_id": "sba", + } + for team in sba_teams + ] - logger.info(f"User info retrieved for {user.username}") + logger.info(f"User info retrieved for {user.username}, {len(teams)} teams") return UserInfoResponse(user=user, teams=teams) diff --git a/backend/app/config.py b/backend/app/config.py index a703ea0..702e0ce 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -33,6 +33,7 @@ class Settings(BaseSettings): # League APIs sba_api_url: str sba_api_key: str + sba_current_season: int = 13 # Current SBA season number pd_api_url: str pd_api_key: str diff --git a/backend/app/services/sba_api_client.py b/backend/app/services/sba_api_client.py index 2153cc6..7a43b8f 100644 --- a/backend/app/services/sba_api_client.py +++ b/backend/app/services/sba_api_client.py @@ -98,6 +98,43 @@ class SbaApiClient: logger.error(f"Unexpected error fetching teams: {e}") raise + async def get_teams_by_owner(self, discord_id: str, season: int) -> list[dict[str, Any]]: + """ + Fetch teams owned by a specific Discord user. + + Args: + discord_id: Discord user ID (gmid or gmid2) + season: Season number + + Returns: + List of team dictionaries owned by this user + + Example: + teams = await client.get_teams_by_owner("485217045408120833", season=13) + """ + url = f"{self.base_url}/teams" + params = {"season": season, "owner_id": discord_id} + + try: + async with httpx.AsyncClient(timeout=self.timeout) as client: + response = await client.get( + url, headers=self._get_headers(), params=params + ) + response.raise_for_status() + + data = response.json() + teams = data.get("teams", []) + + logger.info(f"Found {len(teams)} teams for owner {discord_id[:8]}...") + return teams + + except httpx.HTTPError as e: + logger.error(f"Failed to fetch teams for owner {discord_id[:8]}...: {e}") + raise + except Exception as e: + logger.error(f"Unexpected error fetching teams by owner: {e}") + raise + async def get_team_by_id(self, team_id: int, season: int = 3) -> dict[str, Any] | None: """ Get a single team by ID, using cache when available. diff --git a/frontend-sba/REFACTORING_PLAN.json b/frontend-sba/REFACTORING_PLAN.json index cd09d4e..ac876a4 100644 --- a/frontend-sba/REFACTORING_PLAN.json +++ b/frontend-sba/REFACTORING_PLAN.json @@ -6,7 +6,7 @@ "totalEstimatedHours": 52.75, "criticalBlockersHours": 6.5, "totalTasks": 47, - "completedTasks": 1 + "completedTasks": 3 }, "categories": { "critical": "Must fix before any production use", diff --git a/frontend-sba/pages/games/[id].vue b/frontend-sba/pages/games/[id].vue index 9e335d3..af217f4 100755 --- a/frontend-sba/pages/games/[id].vue +++ b/frontend-sba/pages/games/[id].vue @@ -403,7 +403,22 @@ const currentDecisionPrompt = computed(() => gameStore.currentDecisionPrompt) const isLoading = ref(true) const connectionStatus = ref<'connecting' | 'connected' | 'disconnected'>('connecting') const showSubstitutions = ref(false) -const myTeamId = ref(null) // TODO: Get from auth/game state + +// Determine which team (if any) the current user owns in this game +const myTeamId = computed(() => { + if (!gameState.value) return null + + const userTeamIds = authStore.userTeams.map(t => t.id) + + if (userTeamIds.includes(gameState.value.home_team_id)) { + return gameState.value.home_team_id + } + if (userTeamIds.includes(gameState.value.away_team_id)) { + return gameState.value.away_team_id + } + + return null // Spectator - doesn't own either team +}) // Dynamic ScoreBoard height tracking const scoreBoardRef = ref(null) @@ -435,9 +450,28 @@ const currentTeam = computed(() => { }) const isMyTurn = computed(() => { - // TODO: Implement actual team ownership logic - // For now, assume it's always the player's turn for testing - return true + if (!myTeamId.value || !gameState.value) return false + + // Determine which team needs to act based on decision phase + if (needsDefensiveDecision.value) { + // Fielding team makes defensive decisions + // Top of inning: home fields, Bottom: away fields + const fieldingTeamId = gameState.value.half === 'top' + ? gameState.value.home_team_id + : gameState.value.away_team_id + return myTeamId.value === fieldingTeamId + } + + if (needsOffensiveDecision.value) { + // Batting team makes offensive decisions + // Top of inning: away bats, Bottom: home bats + const battingTeamId = gameState.value.half === 'top' + ? gameState.value.away_team_id + : gameState.value.home_team_id + return myTeamId.value === battingTeamId + } + + return false }) const decisionPhase = computed(() => {