CLAUDE: Implement game creation and lineup submission workflow
Complete implementation of pre-game setup flow allowing players to create games and submit lineups before gameplay starts. Backend Changes: - Extended games.py with create game, lineup submission, and game start endpoints - Added teams.py roster endpoint with season filtering - Enhanced SBA API client with player data fetching and caching - Comprehensive validation for lineup submission (position conflicts, DH rules) Frontend Changes: - Redesigned create.vue with improved team selection and game options - Enhanced index.vue with active/pending game filtering and navigation - Added lineup/[id].vue for interactive lineup builder with drag-and-drop - Implemented auth.client.ts plugin for client-side auth initialization - Added comprehensive TypeScript types for API contracts - Updated middleware for better auth handling Key Features: - Game creation with home/away team selection - Full lineup builder with position assignment and batting order - DH rule validation (pitcher can be excluded from batting order) - Season-based roster filtering (Season 3) - Auto-start game when both lineups submitted - Real-time game list updates Workflow: 1. Create game → select teams → set options 2. Submit home lineup → validate positions/order 3. Submit away lineup → validate positions/order 4. Game auto-starts → navigates to game page 5. WebSocket connection → loads game state Ready for Phase F4 - connecting gameplay UI to complete the at-bat loop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
58b5deb88e
commit
a87d149788
@ -1,7 +1,12 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter, HTTPException
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
|
||||||
|
from app.core.state_manager import state_manager
|
||||||
|
from app.database.operations import DatabaseOperations
|
||||||
|
from app.services.lineup_service import lineup_service
|
||||||
|
|
||||||
logger = logging.getLogger(f"{__name__}.games")
|
logger = logging.getLogger(f"{__name__}.games")
|
||||||
|
|
||||||
@ -18,34 +23,384 @@ class GameListItem(BaseModel):
|
|||||||
away_team_id: int
|
away_team_id: int
|
||||||
|
|
||||||
|
|
||||||
|
class CreateGameRequest(BaseModel):
|
||||||
|
"""Request model for creating a new game"""
|
||||||
|
|
||||||
|
name: str = Field(..., description="Game name")
|
||||||
|
home_team_id: int = Field(..., description="Home team ID")
|
||||||
|
away_team_id: int = Field(..., description="Away team ID")
|
||||||
|
is_ai_opponent: bool = Field(default=False, description="Is AI opponent")
|
||||||
|
season: int = Field(default=3, description="Season number")
|
||||||
|
league_id: str = Field(default="sba", description="League ID (sba or pd)")
|
||||||
|
|
||||||
|
|
||||||
|
class CreateGameResponse(BaseModel):
|
||||||
|
"""Response model for game creation"""
|
||||||
|
|
||||||
|
game_id: str
|
||||||
|
message: str
|
||||||
|
status: str
|
||||||
|
|
||||||
|
|
||||||
|
class LineupPlayerRequest(BaseModel):
|
||||||
|
"""Single player in lineup request"""
|
||||||
|
|
||||||
|
player_id: int = Field(..., description="SBA player ID")
|
||||||
|
position: str = Field(..., description="Defensive position (P, C, 1B, etc.)")
|
||||||
|
batting_order: int | None = Field(
|
||||||
|
None, ge=1, le=9, description="Batting order (1-9), null for pitcher in DH lineup"
|
||||||
|
)
|
||||||
|
|
||||||
|
@field_validator("position")
|
||||||
|
@classmethod
|
||||||
|
def validate_position(cls, v: str) -> str:
|
||||||
|
"""Ensure position is valid"""
|
||||||
|
valid = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "DH"]
|
||||||
|
if v not in valid:
|
||||||
|
raise ValueError(f"Position must be one of {valid}")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
class SubmitLineupsRequest(BaseModel):
|
||||||
|
"""
|
||||||
|
Request model for submitting lineups for both teams.
|
||||||
|
|
||||||
|
Supports both 9-player (pitcher bats) and 10-player (DH) configurations:
|
||||||
|
- 9 players: All have batting orders 1-9
|
||||||
|
- 10 players: Pitcher has batting_order=null, others have 1-9
|
||||||
|
"""
|
||||||
|
|
||||||
|
home_lineup: list[LineupPlayerRequest] = Field(
|
||||||
|
..., min_length=9, max_length=10, description="Home team starting lineup (9-10 players)"
|
||||||
|
)
|
||||||
|
away_lineup: list[LineupPlayerRequest] = Field(
|
||||||
|
..., min_length=9, max_length=10, description="Away team starting lineup (9-10 players)"
|
||||||
|
)
|
||||||
|
|
||||||
|
@field_validator("home_lineup", "away_lineup")
|
||||||
|
@classmethod
|
||||||
|
def validate_lineup(cls, v: list[LineupPlayerRequest]) -> list[LineupPlayerRequest]:
|
||||||
|
"""
|
||||||
|
Validate lineup structure for 9-player or 10-player (DH) configurations.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- 9 players: All must have batting orders 1-9
|
||||||
|
- 10 players: Exactly 9 have batting orders 1-9, pitcher has null
|
||||||
|
"""
|
||||||
|
lineup_size = len(v)
|
||||||
|
|
||||||
|
# Check batting orders
|
||||||
|
batters = [p for p in v if p.batting_order is not None]
|
||||||
|
batting_orders = [p.batting_order for p in batters]
|
||||||
|
|
||||||
|
if len(batting_orders) != 9:
|
||||||
|
raise ValueError(f"Must have exactly 9 batters with batting orders, got {len(batting_orders)}")
|
||||||
|
|
||||||
|
if set(batting_orders) != {1, 2, 3, 4, 5, 6, 7, 8, 9}:
|
||||||
|
raise ValueError("Batting orders must be exactly 1-9 with no duplicates")
|
||||||
|
|
||||||
|
# Check positions
|
||||||
|
positions = [p.position for p in v]
|
||||||
|
required_positions = ["P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"]
|
||||||
|
|
||||||
|
for req_pos in required_positions:
|
||||||
|
if req_pos not in positions:
|
||||||
|
raise ValueError(f"Missing required position: {req_pos}")
|
||||||
|
|
||||||
|
# For 10-player lineup, must have DH and pitcher must not bat
|
||||||
|
if lineup_size == 10:
|
||||||
|
if "DH" not in positions:
|
||||||
|
raise ValueError("10-player lineup must include DH position")
|
||||||
|
|
||||||
|
pitchers = [p for p in v if p.position == "P"]
|
||||||
|
if len(pitchers) != 1:
|
||||||
|
raise ValueError("Must have exactly 1 pitcher")
|
||||||
|
|
||||||
|
if pitchers[0].batting_order is not None:
|
||||||
|
raise ValueError("Pitcher cannot have batting order in DH lineup")
|
||||||
|
|
||||||
|
# For 9-player lineup, pitcher must bat (no DH)
|
||||||
|
elif lineup_size == 9:
|
||||||
|
if "DH" in positions:
|
||||||
|
raise ValueError("9-player lineup cannot include DH")
|
||||||
|
|
||||||
|
pitchers = [p for p in v if p.position == "P"]
|
||||||
|
if len(pitchers) != 1:
|
||||||
|
raise ValueError("Must have exactly 1 pitcher")
|
||||||
|
|
||||||
|
if pitchers[0].batting_order is None:
|
||||||
|
raise ValueError("Pitcher must have batting order when no DH")
|
||||||
|
|
||||||
|
# Check player uniqueness
|
||||||
|
player_ids = [p.player_id for p in v]
|
||||||
|
if len(set(player_ids)) != len(player_ids):
|
||||||
|
raise ValueError("Players cannot be duplicated in lineup")
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
class SubmitLineupsResponse(BaseModel):
|
||||||
|
"""Response model for lineup submission"""
|
||||||
|
|
||||||
|
game_id: str
|
||||||
|
message: str
|
||||||
|
home_lineup_count: int
|
||||||
|
away_lineup_count: int
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", response_model=list[GameListItem])
|
@router.get("/", response_model=list[GameListItem])
|
||||||
async def list_games():
|
async def list_games():
|
||||||
"""
|
"""
|
||||||
List all games
|
List all games from the database
|
||||||
|
|
||||||
TODO Phase 2: Implement game listing with database query
|
Returns basic game information for all games in the system.
|
||||||
|
TODO: Add user filtering, pagination, and more sophisticated queries
|
||||||
"""
|
"""
|
||||||
logger.info("List games endpoint called (stub)")
|
try:
|
||||||
return []
|
logger.info("Fetching games list from database")
|
||||||
|
|
||||||
|
db_ops = DatabaseOperations()
|
||||||
|
|
||||||
|
# Get all games from database (for now - later we can add filters)
|
||||||
|
from app.database.session import AsyncSessionLocal
|
||||||
|
from app.models.db_models import Game
|
||||||
|
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
from sqlalchemy import select
|
||||||
|
result = await session.execute(
|
||||||
|
select(Game).order_by(Game.created_at.desc())
|
||||||
|
)
|
||||||
|
games = result.scalars().all()
|
||||||
|
|
||||||
|
# Convert to response model
|
||||||
|
game_list = [
|
||||||
|
GameListItem(
|
||||||
|
game_id=str(game.id),
|
||||||
|
league_id=game.league_id,
|
||||||
|
status=game.status,
|
||||||
|
home_team_id=game.home_team_id,
|
||||||
|
away_team_id=game.away_team_id,
|
||||||
|
)
|
||||||
|
for game in games
|
||||||
|
]
|
||||||
|
|
||||||
|
logger.info(f"Retrieved {len(game_list)} games from database")
|
||||||
|
return game_list
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f"Failed to list games: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to list games: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{game_id}")
|
@router.get("/{game_id}")
|
||||||
async def get_game(game_id: str):
|
async def get_game(game_id: str):
|
||||||
"""
|
"""
|
||||||
Get game details
|
Get game details including team IDs and status.
|
||||||
|
|
||||||
TODO Phase 2: Implement game retrieval
|
Args:
|
||||||
|
game_id: Game identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Game information including home/away team IDs
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
400: Invalid game_id format
|
||||||
|
404: Game not found
|
||||||
"""
|
"""
|
||||||
logger.info(f"Get game {game_id} endpoint called (stub)")
|
try:
|
||||||
return {"game_id": game_id, "message": "Game retrieval stub"}
|
# Validate game_id format
|
||||||
|
try:
|
||||||
|
game_uuid = UUID(game_id)
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid game_id format")
|
||||||
|
|
||||||
|
logger.info(f"Fetching game details for {game_id}")
|
||||||
|
|
||||||
|
# Get game from state manager
|
||||||
|
if game_uuid not in state_manager._states:
|
||||||
|
raise HTTPException(status_code=404, detail=f"Game {game_id} not found")
|
||||||
|
|
||||||
|
state = state_manager._states[game_uuid]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"game_id": game_id,
|
||||||
|
"status": state.status,
|
||||||
|
"home_team_id": state.home_team_id,
|
||||||
|
"away_team_id": state.away_team_id,
|
||||||
|
"league_id": state.league_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f"Failed to fetch game {game_id}: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to fetch game: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/")
|
@router.post("/", response_model=CreateGameResponse)
|
||||||
async def create_game():
|
async def create_game(request: CreateGameRequest):
|
||||||
"""
|
"""
|
||||||
Create new game
|
Create new game
|
||||||
|
|
||||||
TODO Phase 2: Implement game creation
|
Creates a new game in the state manager and database.
|
||||||
|
Note: Lineups must be added separately before the game can be started.
|
||||||
"""
|
"""
|
||||||
logger.info("Create game endpoint called (stub)")
|
try:
|
||||||
return {"message": "Game creation stub"}
|
# Generate game ID
|
||||||
|
game_id = uuid4()
|
||||||
|
logger.info(
|
||||||
|
f"Creating game {game_id}: {request.home_team_id} vs {request.away_team_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Validate teams are different
|
||||||
|
if request.home_team_id == request.away_team_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400, detail="Home and away teams must be different"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create game in state manager (in-memory)
|
||||||
|
state = await state_manager.create_game(
|
||||||
|
game_id=game_id,
|
||||||
|
league_id=request.league_id,
|
||||||
|
home_team_id=request.home_team_id,
|
||||||
|
away_team_id=request.away_team_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save to database
|
||||||
|
db_ops = DatabaseOperations()
|
||||||
|
await db_ops.create_game(
|
||||||
|
game_id=game_id,
|
||||||
|
league_id=request.league_id,
|
||||||
|
home_team_id=request.home_team_id,
|
||||||
|
away_team_id=request.away_team_id,
|
||||||
|
game_mode="friendly" if not request.is_ai_opponent else "ai",
|
||||||
|
visibility="public",
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Game {game_id} created successfully - status: {state.status}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return CreateGameResponse(
|
||||||
|
game_id=str(game_id),
|
||||||
|
message=f"Game '{request.name}' created successfully. Add lineups to start the game.",
|
||||||
|
status=state.status,
|
||||||
|
)
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f"Failed to create game: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to create game: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{game_id}/lineups", response_model=SubmitLineupsResponse)
|
||||||
|
async def submit_lineups(game_id: str, request: SubmitLineupsRequest):
|
||||||
|
"""
|
||||||
|
Submit lineups for both teams.
|
||||||
|
|
||||||
|
Accepts complete lineups for home and away teams. Supports both:
|
||||||
|
- 9-player lineups (pitcher bats, no DH)
|
||||||
|
- 10-player lineups (universal DH, pitcher doesn't bat)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
game_id: Game identifier
|
||||||
|
request: Lineup data for both teams
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Confirmation with lineup counts
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
400: Invalid lineup structure or validation error
|
||||||
|
404: Game not found
|
||||||
|
500: Database or API error
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Validate game_id format
|
||||||
|
try:
|
||||||
|
game_uuid = UUID(game_id)
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid game_id format")
|
||||||
|
|
||||||
|
logger.info(f"Submitting lineups for game {game_id}")
|
||||||
|
|
||||||
|
# Get game state from memory or load basic info from database
|
||||||
|
state = state_manager.get_state(game_uuid)
|
||||||
|
if not state:
|
||||||
|
logger.info(f"Game {game_id} not in memory, loading from database")
|
||||||
|
|
||||||
|
# Load basic game info from database
|
||||||
|
db_ops = DatabaseOperations()
|
||||||
|
game_data = await db_ops.load_game_state(game_uuid)
|
||||||
|
|
||||||
|
if not game_data:
|
||||||
|
raise HTTPException(status_code=404, detail=f"Game {game_id} not found")
|
||||||
|
|
||||||
|
game_info = game_data['game']
|
||||||
|
|
||||||
|
# Recreate game state in memory (without lineups - we're about to add them)
|
||||||
|
state = await state_manager.create_game(
|
||||||
|
game_id=game_uuid,
|
||||||
|
league_id=game_info['league_id'],
|
||||||
|
home_team_id=game_info['home_team_id'],
|
||||||
|
away_team_id=game_info['away_team_id'],
|
||||||
|
home_team_is_ai=game_info.get('home_team_is_ai', False),
|
||||||
|
away_team_is_ai=game_info.get('away_team_is_ai', False),
|
||||||
|
auto_mode=game_info.get('auto_mode', False)
|
||||||
|
)
|
||||||
|
logger.info(f"Recreated game {game_id} in memory from database")
|
||||||
|
|
||||||
|
# Process home team lineup
|
||||||
|
home_count = 0
|
||||||
|
for player in request.home_lineup:
|
||||||
|
await lineup_service.add_sba_player_to_lineup(
|
||||||
|
game_id=game_uuid,
|
||||||
|
team_id=state.home_team_id,
|
||||||
|
player_id=player.player_id,
|
||||||
|
position=player.position,
|
||||||
|
batting_order=player.batting_order,
|
||||||
|
is_starter=True,
|
||||||
|
)
|
||||||
|
home_count += 1
|
||||||
|
|
||||||
|
logger.info(f"Added {home_count} players to home team lineup")
|
||||||
|
|
||||||
|
# Process away team lineup
|
||||||
|
away_count = 0
|
||||||
|
for player in request.away_lineup:
|
||||||
|
await lineup_service.add_sba_player_to_lineup(
|
||||||
|
game_id=game_uuid,
|
||||||
|
team_id=state.away_team_id,
|
||||||
|
player_id=player.player_id,
|
||||||
|
position=player.position,
|
||||||
|
batting_order=player.batting_order,
|
||||||
|
is_starter=True,
|
||||||
|
)
|
||||||
|
away_count += 1
|
||||||
|
|
||||||
|
logger.info(f"Added {away_count} players to away team lineup")
|
||||||
|
|
||||||
|
# Automatically start the game after lineups are submitted
|
||||||
|
from app.core.game_engine import game_engine
|
||||||
|
try:
|
||||||
|
await game_engine.start_game(game_uuid)
|
||||||
|
logger.info(f"Game {game_id} started successfully after lineup submission")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to auto-start game {game_id}: {e}")
|
||||||
|
# Don't fail the lineup submission if game start fails
|
||||||
|
# User can manually start if needed
|
||||||
|
|
||||||
|
return SubmitLineupsResponse(
|
||||||
|
game_id=game_id,
|
||||||
|
message=f"Lineups submitted successfully. Home: {home_count} players, Away: {away_count} players.",
|
||||||
|
home_lineup_count=home_count,
|
||||||
|
away_lineup_count=away_count,
|
||||||
|
)
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f"Failed to submit lineups for game {game_id}: {e}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Failed to submit lineups: {str(e)}"
|
||||||
|
)
|
||||||
|
|||||||
@ -57,3 +57,31 @@ async def get_teams(season: int = Query(..., description="Season number (e.g., 3
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to fetch teams: {e}", exc_info=True)
|
logger.error(f"Failed to fetch teams: {e}", exc_info=True)
|
||||||
raise HTTPException(status_code=500, detail="Failed to fetch teams")
|
raise HTTPException(status_code=500, detail="Failed to fetch teams")
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{team_id}/roster")
|
||||||
|
async def get_team_roster(
|
||||||
|
team_id: int,
|
||||||
|
season: int = Query(..., description="Season number (e.g., 3)"),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get roster for a specific team from SBA API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
team_id: Team ID
|
||||||
|
season: Season number to fetch roster for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of players on the team's roster
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.info(f"Fetching roster for team {team_id}, season {season}")
|
||||||
|
roster = await sba_api_client.get_roster(team_id=team_id, season=season)
|
||||||
|
|
||||||
|
return {"count": len(roster), "players": roster}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to fetch roster for team {team_id}: {e}", exc_info=True)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Failed to fetch roster for team {team_id}"
|
||||||
|
)
|
||||||
|
|||||||
@ -130,6 +130,46 @@ class SbaApiClient:
|
|||||||
logger.error(f"Unexpected error fetching player {player_id}: {e}")
|
logger.error(f"Unexpected error fetching player {player_id}: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
async def get_roster(self, team_id: int, season: int) -> list[dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Fetch roster for a specific team from SBA API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
team_id: Team ID
|
||||||
|
season: Season number (e.g., 3 for Season 3)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of player dictionaries from the SBA API
|
||||||
|
|
||||||
|
Example:
|
||||||
|
roster = await client.get_roster(team_id=35, season=3)
|
||||||
|
for player in roster:
|
||||||
|
print(f"{player['name']}: {player['position']}")
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/players"
|
||||||
|
params = {"season": season, "team_id": team_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()
|
||||||
|
players = data.get("players", [])
|
||||||
|
count = data.get("count", len(players))
|
||||||
|
|
||||||
|
logger.info(f"Loaded {count} players for team {team_id}, season {season}")
|
||||||
|
return players
|
||||||
|
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"Failed to fetch roster for team {team_id}: {e}")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error fetching roster: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
async def get_players_batch(self, player_ids: list[int]) -> dict[int, SbaPlayer]:
|
async def get_players_batch(self, player_ids: list[int]) -> dict[int, SbaPlayer]:
|
||||||
"""
|
"""
|
||||||
Fetch multiple players in parallel.
|
Fetch multiple players in parallel.
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
"nuxt": "^4.1.3",
|
"nuxt": "^4.1.3",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
|
"vue-draggable-plus": "^0.6.0",
|
||||||
"vue-router": "^4.6.3",
|
"vue-router": "^4.6.3",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -452,6 +453,8 @@
|
|||||||
|
|
||||||
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
|
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
|
||||||
|
|
||||||
|
"@types/sortablejs": ["@types/sortablejs@1.15.9", "", {}, "sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ=="],
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@6.21.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/type-utils": "6.21.0", "@typescript-eslint/utils": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA=="],
|
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@6.21.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/type-utils": "6.21.0", "@typescript-eslint/utils": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA=="],
|
||||||
|
|
||||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="],
|
"@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="],
|
||||||
@ -1962,6 +1965,8 @@
|
|||||||
|
|
||||||
"vue-devtools-stub": ["vue-devtools-stub@0.1.0", "", {}, "sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ=="],
|
"vue-devtools-stub": ["vue-devtools-stub@0.1.0", "", {}, "sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ=="],
|
||||||
|
|
||||||
|
"vue-draggable-plus": ["vue-draggable-plus@0.6.0", "", { "dependencies": { "@types/sortablejs": "^1.15.8" } }, "sha512-G5TSfHrt9tX9EjdG49InoFJbt2NYk0h3kgjgKxkFWr3ulIUays0oFObr5KZ8qzD4+QnhtALiRwIqY6qul4egqw=="],
|
||||||
|
|
||||||
"vue-eslint-parser": ["vue-eslint-parser@9.4.3", "", { "dependencies": { "debug": "^4.3.4", "eslint-scope": "^7.1.1", "eslint-visitor-keys": "^3.3.0", "espree": "^9.3.1", "esquery": "^1.4.0", "lodash": "^4.17.21", "semver": "^7.3.6" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg=="],
|
"vue-eslint-parser": ["vue-eslint-parser@9.4.3", "", { "dependencies": { "debug": "^4.3.4", "eslint-scope": "^7.1.1", "eslint-visitor-keys": "^3.3.0", "espree": "^9.3.1", "esquery": "^1.4.0", "lodash": "^4.17.21", "semver": "^7.3.6" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg=="],
|
||||||
|
|
||||||
"vue-router": ["vue-router@4.6.3", "", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg=="],
|
"vue-router": ["vue-router@4.6.3", "", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg=="],
|
||||||
@ -2044,10 +2049,6 @@
|
|||||||
|
|
||||||
"@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
"@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||||
|
|
||||||
"@eslint/eslintrc/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"@humanwhocodes/config-array/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||||
|
|
||||||
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
@ -2060,8 +2061,6 @@
|
|||||||
|
|
||||||
"@nuxt/cli/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
|
"@nuxt/cli/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
|
||||||
|
|
||||||
"@nuxt/devtools/@nuxt/kit": ["@nuxt/kit@3.19.3", "", { "dependencies": { "c12": "^3.3.0", "consola": "^3.4.2", "defu": "^6.1.4", "destr": "^2.0.5", "errx": "^0.1.0", "exsolve": "^1.0.7", "ignore": "^7.0.5", "jiti": "^2.6.1", "klona": "^2.0.6", "knitwork": "^1.2.0", "mlly": "^1.8.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "rc9": "^2.1.2", "scule": "^1.3.0", "semver": "^7.7.2", "std-env": "^3.9.0", "tinyglobby": "^0.2.15", "ufo": "^1.6.1", "unctx": "^2.4.1", "unimport": "^5.4.1", "untyped": "^2.0.0" } }, "sha512-ze46EW5xW+UxDvinvPkYt2MzR355Az1lA3bpX8KDialgnCwr+IbkBij/udbUEC6ZFbidPkfK1eKl4ESN7gMY+w=="],
|
|
||||||
|
|
||||||
"@nuxt/devtools/local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
"@nuxt/devtools/local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
||||||
|
|
||||||
"@nuxt/devtools/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"@nuxt/devtools/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
@ -2070,8 +2069,6 @@
|
|||||||
|
|
||||||
"@nuxt/devtools/vite": ["vite@7.1.11", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "tsx"], "bin": "bin/vite.js" }, "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg=="],
|
"@nuxt/devtools/vite": ["vite@7.1.11", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "tsx"], "bin": "bin/vite.js" }, "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg=="],
|
||||||
|
|
||||||
"@nuxt/devtools-kit/@nuxt/kit": ["@nuxt/kit@3.19.3", "", { "dependencies": { "c12": "^3.3.0", "consola": "^3.4.2", "defu": "^6.1.4", "destr": "^2.0.5", "errx": "^0.1.0", "exsolve": "^1.0.7", "ignore": "^7.0.5", "jiti": "^2.6.1", "klona": "^2.0.6", "knitwork": "^1.2.0", "mlly": "^1.8.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "rc9": "^2.1.2", "scule": "^1.3.0", "semver": "^7.7.2", "std-env": "^3.9.0", "tinyglobby": "^0.2.15", "ufo": "^1.6.1", "unctx": "^2.4.1", "unimport": "^5.4.1", "untyped": "^2.0.0" } }, "sha512-ze46EW5xW+UxDvinvPkYt2MzR355Az1lA3bpX8KDialgnCwr+IbkBij/udbUEC6ZFbidPkfK1eKl4ESN7gMY+w=="],
|
|
||||||
|
|
||||||
"@nuxt/devtools-kit/vite": ["vite@7.1.11", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "tsx"], "bin": "bin/vite.js" }, "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg=="],
|
"@nuxt/devtools-kit/vite": ["vite@7.1.11", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "tsx"], "bin": "bin/vite.js" }, "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg=="],
|
||||||
|
|
||||||
"@nuxt/devtools-wizard/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"@nuxt/devtools-wizard/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
@ -2080,8 +2077,6 @@
|
|||||||
|
|
||||||
"@nuxt/schema/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"@nuxt/schema/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
"@nuxt/telemetry/@nuxt/kit": ["@nuxt/kit@3.19.3", "", { "dependencies": { "c12": "^3.3.0", "consola": "^3.4.2", "defu": "^6.1.4", "destr": "^2.0.5", "errx": "^0.1.0", "exsolve": "^1.0.7", "ignore": "^7.0.5", "jiti": "^2.6.1", "klona": "^2.0.6", "knitwork": "^1.2.0", "mlly": "^1.8.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "rc9": "^2.1.2", "scule": "^1.3.0", "semver": "^7.7.2", "std-env": "^3.9.0", "tinyglobby": "^0.2.15", "ufo": "^1.6.1", "unctx": "^2.4.1", "unimport": "^5.4.1", "untyped": "^2.0.0" } }, "sha512-ze46EW5xW+UxDvinvPkYt2MzR355Az1lA3bpX8KDialgnCwr+IbkBij/udbUEC6ZFbidPkfK1eKl4ESN7gMY+w=="],
|
|
||||||
|
|
||||||
"@nuxt/telemetry/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
"@nuxt/telemetry/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||||
|
|
||||||
"@nuxt/telemetry/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"@nuxt/telemetry/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
@ -2096,8 +2091,6 @@
|
|||||||
|
|
||||||
"@nuxtjs/tailwindcss/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"@nuxtjs/tailwindcss/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
"@pinia/nuxt/@nuxt/kit": ["@nuxt/kit@3.19.3", "", { "dependencies": { "c12": "^3.3.0", "consola": "^3.4.2", "defu": "^6.1.4", "destr": "^2.0.5", "errx": "^0.1.0", "exsolve": "^1.0.7", "ignore": "^7.0.5", "jiti": "^2.6.1", "klona": "^2.0.6", "knitwork": "^1.2.0", "mlly": "^1.8.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "rc9": "^2.1.2", "scule": "^1.3.0", "semver": "^7.7.2", "std-env": "^3.9.0", "tinyglobby": "^0.2.15", "ufo": "^1.6.1", "unctx": "^2.4.1", "unimport": "^5.4.1", "untyped": "^2.0.0" } }, "sha512-ze46EW5xW+UxDvinvPkYt2MzR355Az1lA3bpX8KDialgnCwr+IbkBij/udbUEC6ZFbidPkfK1eKl4ESN7gMY+w=="],
|
|
||||||
|
|
||||||
"@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
"@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
||||||
|
|
||||||
"@rollup/plugin-commonjs/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
"@rollup/plugin-commonjs/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||||
@ -2118,10 +2111,6 @@
|
|||||||
|
|
||||||
"@vitejs/plugin-vue-jsx/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.44", "", {}, "sha512-g6eW7Zwnr2c5RADIoqziHoVs6b3W5QTQ4+qbpfjbkMJ9x+8Og211VW/oot2dj9dVwaK/UyC6Yo+02gV+wWQVNg=="],
|
"@vitejs/plugin-vue-jsx/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.44", "", {}, "sha512-g6eW7Zwnr2c5RADIoqziHoVs6b3W5QTQ4+qbpfjbkMJ9x+8Og211VW/oot2dj9dVwaK/UyC6Yo+02gV+wWQVNg=="],
|
||||||
|
|
||||||
"@vitest/runner/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
|
||||||
|
|
||||||
"@vitest/snapshot/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
|
||||||
|
|
||||||
"@vue-macros/common/local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
"@vue-macros/common/local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
||||||
|
|
||||||
"@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
"@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||||
@ -2134,8 +2123,6 @@
|
|||||||
|
|
||||||
"@vue/devtools-kit/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
"@vue/devtools-kit/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
||||||
|
|
||||||
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
|
||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"archiver-utils/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
"archiver-utils/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||||
@ -2144,20 +2131,12 @@
|
|||||||
|
|
||||||
"c12/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"c12/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
"cache-content-type/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
|
||||||
|
|
||||||
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
|
||||||
|
|
||||||
"clean-regexp/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
"clean-regexp/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||||
|
|
||||||
"clipboardy/execa": ["execa@9.6.0", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw=="],
|
"clipboardy/execa": ["execa@9.6.0", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw=="],
|
||||||
|
|
||||||
"clipboardy/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
"clipboardy/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
||||||
|
|
||||||
"cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
||||||
|
|
||||||
"cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
|
||||||
|
|
||||||
"cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
"cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
"compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
"compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||||
@ -2184,8 +2163,6 @@
|
|||||||
|
|
||||||
"eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
"eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||||
|
|
||||||
"eslint/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||||
|
|
||||||
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||||
@ -2198,16 +2175,12 @@
|
|||||||
|
|
||||||
"eslint-plugin-n/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
"eslint-plugin-n/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||||
|
|
||||||
"eslint-plugin-n/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"eslint-plugin-node/eslint-plugin-es": ["eslint-plugin-es@3.0.1", "", { "dependencies": { "eslint-utils": "^2.0.0", "regexpp": "^3.0.0" }, "peerDependencies": { "eslint": ">=4.19.1" } }, "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ=="],
|
"eslint-plugin-node/eslint-plugin-es": ["eslint-plugin-es@3.0.1", "", { "dependencies": { "eslint-utils": "^2.0.0", "regexpp": "^3.0.0" }, "peerDependencies": { "eslint": ">=4.19.1" } }, "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ=="],
|
||||||
|
|
||||||
"eslint-plugin-node/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="],
|
"eslint-plugin-node/eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="],
|
||||||
|
|
||||||
"eslint-plugin-node/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
"eslint-plugin-node/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||||
|
|
||||||
"eslint-plugin-node/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"eslint-plugin-node/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"eslint-plugin-node/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
"eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="],
|
"eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="],
|
||||||
@ -2244,8 +2217,6 @@
|
|||||||
|
|
||||||
"listhen/clipboardy": ["clipboardy@4.0.0", "", { "dependencies": { "execa": "^8.0.1", "is-wsl": "^3.1.0", "is64bit": "^2.0.0" } }, "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w=="],
|
"listhen/clipboardy": ["clipboardy@4.0.0", "", { "dependencies": { "execa": "^8.0.1", "is-wsl": "^3.1.0", "is64bit": "^2.0.0" } }, "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w=="],
|
||||||
|
|
||||||
"listhen/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
|
||||||
|
|
||||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
@ -2270,8 +2241,6 @@
|
|||||||
|
|
||||||
"open/is-docker": ["is-docker@2.2.1", "", { "bin": "cli.js" }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
|
"open/is-docker": ["is-docker@2.2.1", "", { "bin": "cli.js" }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
|
||||||
|
|
||||||
"path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
|
||||||
|
|
||||||
"pinia/@vue/devtools-api": ["@vue/devtools-api@7.7.7", "", { "dependencies": { "@vue/devtools-kit": "^7.7.7" } }, "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg=="],
|
"pinia/@vue/devtools-api": ["@vue/devtools-api@7.7.7", "", { "dependencies": { "@vue/devtools-kit": "^7.7.7" } }, "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg=="],
|
||||||
|
|
||||||
"pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
@ -2284,8 +2253,6 @@
|
|||||||
|
|
||||||
"postcss-minify-selectors/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
"postcss-minify-selectors/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
||||||
|
|
||||||
"postcss-nested/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
|
||||||
|
|
||||||
"postcss-nesting/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
"postcss-nesting/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
||||||
|
|
||||||
"postcss-unique-selectors/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
"postcss-unique-selectors/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
||||||
@ -2308,8 +2275,6 @@
|
|||||||
|
|
||||||
"rollup-plugin-visualizer/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
"rollup-plugin-visualizer/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
||||||
|
|
||||||
"safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
|
||||||
|
|
||||||
"send/encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
"send/encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||||
|
|
||||||
"send/fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
"send/fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||||
@ -2326,14 +2291,6 @@
|
|||||||
|
|
||||||
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
"string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
|
||||||
|
|
||||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
|
||||||
|
|
||||||
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
|
||||||
|
|
||||||
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
|
||||||
|
|
||||||
"stylehacks/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
"stylehacks/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
||||||
|
|
||||||
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
||||||
@ -2342,20 +2299,14 @@
|
|||||||
|
|
||||||
"tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
"tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||||
|
|
||||||
"tailwindcss/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
|
||||||
|
|
||||||
"tailwindcss/jiti": ["jiti@1.21.7", "", { "bin": "bin/jiti.js" }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
|
"tailwindcss/jiti": ["jiti@1.21.7", "", { "bin": "bin/jiti.js" }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
|
||||||
|
|
||||||
"tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
|
||||||
|
|
||||||
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||||
|
|
||||||
"test-exclude/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
"test-exclude/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
"tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": "lib/cli.js" }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
|
"tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": "lib/cli.js" }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
|
||||||
|
|
||||||
"type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
|
||||||
|
|
||||||
"unenv/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"unenv/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
"unimport/local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
"unimport/local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
||||||
@ -2370,14 +2321,10 @@
|
|||||||
|
|
||||||
"unplugin-vue-router/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"unplugin-vue-router/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
"untun/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
|
||||||
|
|
||||||
"unwasm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"unwasm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
"vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": "bin/esbuild" }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
"vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": "bin/esbuild" }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
||||||
|
|
||||||
"vite-node/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
|
||||||
|
|
||||||
"vite-plugin-checker/npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
|
"vite-plugin-checker/npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
|
||||||
|
|
||||||
"vite-plugin-inspect/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
"vite-plugin-inspect/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
||||||
@ -2390,52 +2337,30 @@
|
|||||||
|
|
||||||
"vite-plugin-vue-tracer/vite": ["vite@7.1.11", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "tsx"], "bin": "bin/vite.js" }, "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg=="],
|
"vite-plugin-vue-tracer/vite": ["vite@7.1.11", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "tsx"], "bin": "bin/vite.js" }, "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg=="],
|
||||||
|
|
||||||
"vitest/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
|
||||||
|
|
||||||
"whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
"whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
|
|
||||||
"which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
|
||||||
|
|
||||||
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
|
|
||||||
"wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
"wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||||
|
|
||||||
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
|
|
||||||
"wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
||||||
|
|
||||||
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
||||||
|
|
||||||
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
|
||||||
|
|
||||||
"wsl-utils/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
"wsl-utils/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
||||||
|
|
||||||
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||||
|
|
||||||
"@eslint/eslintrc/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"@humanwhocodes/config-array/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||||
|
|
||||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||||
|
|
||||||
"@mapbox/node-pre-gyp/nopt/abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="],
|
"@mapbox/node-pre-gyp/nopt/abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="],
|
||||||
|
|
||||||
"@nuxt/devtools-kit/@nuxt/kit/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
|
||||||
|
|
||||||
"@pinia/nuxt/@nuxt/kit/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
"@typescript-eslint/typescript-estree/globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
"@typescript-eslint/typescript-estree/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
|
|
||||||
"accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
|
||||||
|
|
||||||
"cache-content-type/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
|
||||||
|
|
||||||
"clipboardy/execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
|
"clipboardy/execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
|
||||||
|
|
||||||
"clipboardy/execa/human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
|
"clipboardy/execa/human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
|
||||||
@ -2446,12 +2371,6 @@
|
|||||||
|
|
||||||
"clipboardy/execa/strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
|
"clipboardy/execa/strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
|
||||||
|
|
||||||
"cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
|
||||||
|
|
||||||
"cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
|
||||||
|
|
||||||
"cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
||||||
|
|
||||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
|
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
|
||||||
@ -2460,24 +2379,14 @@
|
|||||||
|
|
||||||
"eslint-plugin-es/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="],
|
"eslint-plugin-es/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="],
|
||||||
|
|
||||||
"eslint-plugin-n/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"eslint-plugin-node/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="],
|
"eslint-plugin-node/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="],
|
||||||
|
|
||||||
"eslint-plugin-node/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
|
|
||||||
"http-assert/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
"http-assert/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
||||||
|
|
||||||
"http-assert/http-errors/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
|
|
||||||
|
|
||||||
"koa-send/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
"koa-send/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
||||||
|
|
||||||
"koa-send/http-errors/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
|
|
||||||
|
|
||||||
"koa/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
"koa/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
||||||
|
|
||||||
"lazystream/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
"lazystream/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||||
@ -2494,38 +2403,24 @@
|
|||||||
|
|
||||||
"readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
|
|
||||||
"replace-in-file/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"resolve-path/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
"resolve-path/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
||||||
|
|
||||||
"resolve-path/http-errors/inherits": ["inherits@2.0.3", "", {}, "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="],
|
"resolve-path/http-errors/inherits": ["inherits@2.0.3", "", {}, "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="],
|
||||||
|
|
||||||
"resolve-path/http-errors/setprototypeof": ["setprototypeof@1.1.0", "", {}, "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="],
|
"resolve-path/http-errors/setprototypeof": ["setprototypeof@1.1.0", "", {}, "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="],
|
||||||
|
|
||||||
"resolve-path/http-errors/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
|
|
||||||
|
|
||||||
"rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"rollup-plugin-visualizer/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
|
"rollup-plugin-visualizer/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
|
||||||
|
|
||||||
"rollup-plugin-visualizer/open/is-docker": ["is-docker@2.2.1", "", { "bin": "cli.js" }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
|
"rollup-plugin-visualizer/open/is-docker": ["is-docker@2.2.1", "", { "bin": "cli.js" }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
|
||||||
|
|
||||||
"rollup-plugin-visualizer/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
|
||||||
|
|
||||||
"send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
"send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||||
|
|
||||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
|
||||||
|
|
||||||
"string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
|
||||||
|
|
||||||
"tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
"tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|
||||||
"test-exclude/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"test-exclude/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
|
|
||||||
"type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
|
||||||
|
|
||||||
"vite-plugin-checker/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
"vite-plugin-checker/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
||||||
|
|
||||||
"vite-plugin-inspect/unplugin-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"vite-plugin-inspect/unplugin-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
@ -2576,10 +2471,6 @@
|
|||||||
|
|
||||||
"vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
"vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
||||||
|
|
||||||
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
|
||||||
|
|
||||||
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
|
||||||
|
|
||||||
"wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
"wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||||
|
|
||||||
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||||
@ -2588,10 +2479,6 @@
|
|||||||
|
|
||||||
"read-pkg-up/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
"read-pkg-up/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
||||||
|
|
||||||
"replace-in-file/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
"read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
*
|
*
|
||||||
* Protects routes that require authentication.
|
* Protects routes that require authentication.
|
||||||
* Redirects to login if user is not authenticated.
|
* Redirects to login if user is not authenticated.
|
||||||
|
*
|
||||||
|
* Note: Auth state is initialized by the auth.client.ts plugin before this middleware runs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useAuthStore } from '~/store/auth'
|
import { useAuthStore } from '~/store/auth'
|
||||||
@ -10,17 +12,27 @@ import { useAuthStore } from '~/store/auth'
|
|||||||
export default defineNuxtRouteMiddleware((to, from) => {
|
export default defineNuxtRouteMiddleware((to, from) => {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
// Initialize auth from localStorage if not already done
|
console.log('[Auth Middleware]', {
|
||||||
if (process.client && !authStore.isAuthenticated) {
|
path: to.path,
|
||||||
authStore.initializeAuth()
|
isAuthenticated: authStore.isAuthenticated,
|
||||||
}
|
isTokenValid: authStore.isTokenValid,
|
||||||
|
hasUser: !!authStore.currentUser,
|
||||||
|
})
|
||||||
|
|
||||||
// Allow access if authenticated
|
// Allow access if authenticated and token is valid
|
||||||
if (authStore.isAuthenticated) {
|
if (authStore.isAuthenticated && authStore.isTokenValid) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If token expired but we have a refresh token, try refreshing
|
||||||
|
if (authStore.isAuthenticated && !authStore.isTokenValid && process.client) {
|
||||||
|
console.log('[Auth Middleware] Token expired, attempting refresh')
|
||||||
|
// Don't await - let it refresh in background and redirect for now
|
||||||
|
authStore.refreshAccessToken()
|
||||||
|
}
|
||||||
|
|
||||||
// Redirect to login with return URL
|
// Redirect to login with return URL
|
||||||
|
console.log('[Auth Middleware] Redirecting to login')
|
||||||
return navigateTo({
|
return navigateTo({
|
||||||
path: '/auth/login',
|
path: '/auth/login',
|
||||||
query: { redirect: to.fullPath },
|
query: { redirect: to.fullPath },
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"nuxt": "^4.1.3",
|
"nuxt": "^4.1.3",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
|
"vue-draggable-plus": "^0.6.0",
|
||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -32,16 +32,19 @@
|
|||||||
<select
|
<select
|
||||||
id="homeTeam"
|
id="homeTeam"
|
||||||
v-model="formData.homeTeamId"
|
v-model="formData.homeTeamId"
|
||||||
|
:disabled="isLoadingTeams"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent transition"
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent transition disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
<option value="" disabled>Select home team</option>
|
<option value="" disabled>
|
||||||
|
{{ isLoadingTeams ? 'Loading teams...' : 'Select home team' }}
|
||||||
|
</option>
|
||||||
<option
|
<option
|
||||||
v-for="team in authStore.userTeams"
|
v-for="team in availableTeams"
|
||||||
:key="team.id"
|
:key="team.id"
|
||||||
:value="team.id"
|
:value="team.id"
|
||||||
>
|
>
|
||||||
{{ team.name }} ({{ team.abbreviation }})
|
{{ team.lname }} ({{ team.abbrev }})
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -54,56 +57,24 @@
|
|||||||
<select
|
<select
|
||||||
id="awayTeam"
|
id="awayTeam"
|
||||||
v-model="formData.awayTeamId"
|
v-model="formData.awayTeamId"
|
||||||
|
:disabled="isLoadingTeams"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent transition"
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent transition disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
<option value="" disabled>Select away team</option>
|
<option value="" disabled>
|
||||||
|
{{ isLoadingTeams ? 'Loading teams...' : 'Select away team' }}
|
||||||
|
</option>
|
||||||
<option
|
<option
|
||||||
v-for="team in authStore.userTeams"
|
v-for="team in availableTeams"
|
||||||
:key="team.id"
|
:key="team.id"
|
||||||
:value="team.id"
|
:value="team.id"
|
||||||
:disabled="team.id === formData.homeTeamId"
|
:disabled="team.id === formData.homeTeamId"
|
||||||
>
|
>
|
||||||
{{ team.name }} ({{ team.abbreviation }})
|
{{ team.lname }} ({{ team.abbrev }})
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Game Mode -->
|
|
||||||
<div class="mb-6">
|
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
Game Mode
|
|
||||||
</label>
|
|
||||||
<div class="space-y-3">
|
|
||||||
<label class="flex items-center p-4 border border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50 transition">
|
|
||||||
<input
|
|
||||||
v-model="formData.isAiOpponent"
|
|
||||||
type="radio"
|
|
||||||
:value="false"
|
|
||||||
name="gameMode"
|
|
||||||
class="mr-3 text-primary focus:ring-primary"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<div class="font-medium text-gray-900">Human vs Human</div>
|
|
||||||
<div class="text-sm text-gray-600">Play against another player</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<label class="flex items-center p-4 border border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50 transition">
|
|
||||||
<input
|
|
||||||
v-model="formData.isAiOpponent"
|
|
||||||
type="radio"
|
|
||||||
:value="true"
|
|
||||||
name="gameMode"
|
|
||||||
class="mr-3 text-primary focus:ring-primary"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<div class="font-medium text-gray-900">Human vs AI</div>
|
|
||||||
<div class="text-sm text-gray-600">Play against the computer</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error Message -->
|
<!-- Error Message -->
|
||||||
<div
|
<div
|
||||||
v-if="error"
|
v-if="error"
|
||||||
@ -122,27 +93,19 @@
|
|||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading || isLoadingTeams"
|
||||||
class="px-6 py-3 bg-primary hover:bg-blue-700 disabled:bg-gray-400 text-white font-semibold rounded-lg transition disabled:cursor-not-allowed"
|
class="px-6 py-3 bg-primary hover:bg-blue-700 disabled:bg-gray-400 text-white font-semibold rounded-lg transition disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{{ isLoading ? 'Creating...' : 'Create Game' }}
|
{{ isLoadingTeams ? 'Loading...' : isLoading ? 'Creating...' : 'Create Game' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Info Box -->
|
|
||||||
<div class="mt-6 bg-blue-50 border border-blue-200 rounded-lg p-6">
|
|
||||||
<h3 class="font-bold text-blue-900 mb-2">Coming in Phase F6</h3>
|
|
||||||
<p class="text-blue-800 text-sm">
|
|
||||||
This is a placeholder form. Full game creation with team selection, lineup management,
|
|
||||||
and game settings will be implemented in Phase F6 (Game Management).
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAuthStore } from '~/store/auth'
|
import { useAuthStore } from '~/store/auth'
|
||||||
|
import type { SbaTeam } from '~/types/api'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ['auth'], // Require authentication
|
middleware: ['auth'], // Require authentication
|
||||||
@ -150,15 +113,33 @@ definePageMeta({
|
|||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
const isLoadingTeams = ref(true)
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
|
const availableTeams = ref<SbaTeam[]>([])
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
name: '',
|
name: '',
|
||||||
homeTeamId: '',
|
homeTeamId: '',
|
||||||
awayTeamId: '',
|
awayTeamId: '',
|
||||||
isAiOpponent: false,
|
})
|
||||||
|
|
||||||
|
// Fetch teams on component mount
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
isLoadingTeams.value = true
|
||||||
|
const teams = await $fetch<SbaTeam[]>(`${config.public.apiUrl}/api/teams/`, {
|
||||||
|
params: { season: 3 },
|
||||||
|
})
|
||||||
|
availableTeams.value = teams
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('Failed to load teams:', err)
|
||||||
|
error.value = 'Failed to load teams. Please refresh the page.'
|
||||||
|
} finally {
|
||||||
|
isLoadingTeams.value = false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleCreateGame = async () => {
|
const handleCreateGame = async () => {
|
||||||
@ -177,20 +158,26 @@ const handleCreateGame = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Phase F6: Call API to create game
|
// Call API to create game
|
||||||
// const response = await $fetch('/api/games', {
|
const response = await $fetch<{ game_id: string; message: string; status: string }>(
|
||||||
// method: 'POST',
|
`${config.public.apiUrl}/api/games/`,
|
||||||
// body: formData.value
|
{
|
||||||
// })
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
name: formData.value.name,
|
||||||
|
home_team_id: formData.value.homeTeamId,
|
||||||
|
away_team_id: formData.value.awayTeamId,
|
||||||
|
is_ai_opponent: false, // SBA only supports human vs human
|
||||||
|
season: 3,
|
||||||
|
league_id: 'sba',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// For now, just show a placeholder success message
|
// Redirect to lineup builder
|
||||||
alert('Game creation will be implemented in Phase F6')
|
router.push(`/games/lineup/${response.game_id}`)
|
||||||
|
|
||||||
// Redirect to games list
|
|
||||||
// router.push(`/games/${response.game_id}`)
|
|
||||||
router.push('/games')
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
error.value = err.message || 'Failed to create game'
|
error.value = err.data?.detail || err.message || 'Failed to create game'
|
||||||
console.error('Create game error:', err)
|
console.error('Create game error:', err)
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
|
|||||||
@ -43,10 +43,57 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Loading State -->
|
||||||
|
<div v-if="loading" class="bg-white rounded-lg shadow-md p-12 text-center">
|
||||||
|
<div class="w-16 h-16 mx-auto mb-4 border-4 border-primary border-t-transparent rounded-full animate-spin"></div>
|
||||||
|
<p class="text-gray-900 font-semibold">Loading games...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Error State -->
|
||||||
|
<div v-else-if="error" class="bg-red-50 border border-red-200 rounded-lg p-6">
|
||||||
|
<p class="text-red-800 font-semibold">Failed to load games</p>
|
||||||
|
<p class="text-red-600 text-sm mt-2">{{ error }}</p>
|
||||||
|
<button
|
||||||
|
@click="fetchGames"
|
||||||
|
class="mt-4 px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition"
|
||||||
|
>
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Games List -->
|
<!-- Games List -->
|
||||||
<div v-if="activeTab === 'active'">
|
<div v-else-if="activeTab === 'active'">
|
||||||
|
<!-- Games List -->
|
||||||
|
<div v-if="activeGames.length > 0" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
<NuxtLink
|
||||||
|
v-for="game in activeGames"
|
||||||
|
:key="game.game_id"
|
||||||
|
:to="`/games/${game.game_id}`"
|
||||||
|
class="bg-white rounded-lg shadow-md hover:shadow-xl transition p-6"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-start mb-4">
|
||||||
|
<span :class="[
|
||||||
|
'px-3 py-1 rounded-full text-sm font-semibold',
|
||||||
|
game.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'
|
||||||
|
]">
|
||||||
|
{{ game.status === 'active' ? 'In Progress' : 'Pending Lineups' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-600">Away</span>
|
||||||
|
<span class="font-bold">Team {{ game.away_team_id }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-600">Home</span>
|
||||||
|
<span class="font-bold">Team {{ game.home_team_id }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Empty State -->
|
<!-- Empty State -->
|
||||||
<div class="bg-white rounded-lg shadow-md p-12 text-center">
|
<div v-else class="bg-white rounded-lg shadow-md p-12 text-center">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-16 w-16 mx-auto text-gray-400 mb-4"
|
class="h-16 w-16 mx-auto text-gray-400 mb-4"
|
||||||
@ -74,16 +121,37 @@
|
|||||||
Create Your First Game
|
Create Your First Game
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO Phase F6: Replace with actual games list -->
|
|
||||||
<!-- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
<GameCard v-for="game in activeGames" :key="game.id" :game="game" />
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="activeTab === 'completed'">
|
<div v-else-if="activeTab === 'completed'">
|
||||||
|
<!-- Completed Games List -->
|
||||||
|
<div v-if="completedGames.length > 0" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
<NuxtLink
|
||||||
|
v-for="game in completedGames"
|
||||||
|
:key="game.game_id"
|
||||||
|
:to="`/games/${game.game_id}`"
|
||||||
|
class="bg-white rounded-lg shadow-md hover:shadow-xl transition p-6"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-start mb-4">
|
||||||
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-gray-100 text-gray-800">
|
||||||
|
Completed
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-600">Away</span>
|
||||||
|
<span class="font-bold">Team {{ game.away_team_id }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-600">Home</span>
|
||||||
|
<span class="font-bold">Team {{ game.home_team_id }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Empty State -->
|
<!-- Empty State -->
|
||||||
<div class="bg-white rounded-lg shadow-md p-12 text-center">
|
<div v-else class="bg-white rounded-lg shadow-md p-12 text-center">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-16 w-16 mx-auto text-gray-400 mb-4"
|
class="h-16 w-16 mx-auto text-gray-400 mb-4"
|
||||||
@ -105,8 +173,6 @@
|
|||||||
You haven't completed any games yet.
|
You haven't completed any games yet.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO Phase F6: Replace with actual completed games list -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -116,11 +182,52 @@ definePageMeta({
|
|||||||
middleware: ['auth'], // Require authentication
|
middleware: ['auth'], // Require authentication
|
||||||
})
|
})
|
||||||
|
|
||||||
const activeTab = ref<'active' | 'completed'>('active')
|
import { useAuthStore } from '~/store/auth'
|
||||||
|
|
||||||
// TODO Phase F6: Fetch games from API
|
const activeTab = ref<'active' | 'completed'>('active')
|
||||||
// const { data: activeGames } = await useFetch('/api/games?status=active')
|
const config = useRuntimeConfig()
|
||||||
// const { data: completedGames } = await useFetch('/api/games?status=completed')
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
// Games data
|
||||||
|
const games = ref<any[]>([])
|
||||||
|
const loading = ref(true)
|
||||||
|
const error = ref<string | null>(null)
|
||||||
|
|
||||||
|
// Fetch games from API (client-side only)
|
||||||
|
async function fetchGames() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
error.value = null
|
||||||
|
|
||||||
|
const response = await $fetch<any[]>(`${config.public.apiUrl}/api/games/`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${authStore.token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
games.value = response
|
||||||
|
console.log('[Games Page] Fetched games:', games.value)
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('[Games Page] Failed to fetch games:', err)
|
||||||
|
error.value = err.message || 'Failed to load games'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter games by status
|
||||||
|
const activeGames = computed(() => {
|
||||||
|
return games.value?.filter(g => g.status === 'active' || g.status === 'pending') || []
|
||||||
|
})
|
||||||
|
|
||||||
|
const completedGames = computed(() => {
|
||||||
|
return games.value?.filter(g => g.status === 'completed' || g.status === 'final') || []
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fetch on mount (client-side)
|
||||||
|
onMounted(() => {
|
||||||
|
fetchGames()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
548
frontend-sba/pages/games/lineup/[id].vue
Normal file
548
frontend-sba/pages/games/lineup/[id].vue
Normal file
@ -0,0 +1,548 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import type { SbaPlayer, LineupPlayerRequest, SubmitLineupsRequest } from '~/types'
|
||||||
|
import ActionButton from '~/components/UI/ActionButton.vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
|
||||||
|
// Game and team data
|
||||||
|
const gameId = ref(route.params.id as string)
|
||||||
|
const homeTeamId = ref<number | null>(null)
|
||||||
|
const awayTeamId = ref<number | null>(null)
|
||||||
|
const season = ref(3)
|
||||||
|
|
||||||
|
// Active tab (home or away)
|
||||||
|
type TeamTab = 'home' | 'away'
|
||||||
|
const activeTab = ref<TeamTab>('away') // Away bats first
|
||||||
|
|
||||||
|
// Roster data
|
||||||
|
const homeRoster = ref<SbaPlayer[]>([])
|
||||||
|
const awayRoster = ref<SbaPlayer[]>([])
|
||||||
|
const loadingRoster = ref(false)
|
||||||
|
const submittingLineups = ref(false)
|
||||||
|
|
||||||
|
// Lineup state - 10 slots each (1-9 batting, 10 pitcher)
|
||||||
|
interface LineupSlot {
|
||||||
|
player: SbaPlayer | null
|
||||||
|
position: string | null
|
||||||
|
battingOrder: number | null // 1-9 for batters, null for pitcher
|
||||||
|
}
|
||||||
|
|
||||||
|
const homeLineup = ref<LineupSlot[]>(Array(10).fill(null).map((_, i) => ({
|
||||||
|
player: null,
|
||||||
|
position: null,
|
||||||
|
battingOrder: i < 9 ? i + 1 : null // Slots 0-8 are batting order 1-9, slot 9 is pitcher
|
||||||
|
})))
|
||||||
|
|
||||||
|
const awayLineup = ref<LineupSlot[]>(Array(10).fill(null).map((_, i) => ({
|
||||||
|
player: null,
|
||||||
|
position: null,
|
||||||
|
battingOrder: i < 9 ? i + 1 : null
|
||||||
|
})))
|
||||||
|
|
||||||
|
// Available roster for dragging (players not in lineup)
|
||||||
|
const availableHomeRoster = computed(() => {
|
||||||
|
const usedPlayerIds = new Set(homeLineup.value.filter(s => s.player).map(s => s.player!.id))
|
||||||
|
return homeRoster.value.filter(p => !usedPlayerIds.has(p.id))
|
||||||
|
})
|
||||||
|
|
||||||
|
const availableAwayRoster = computed(() => {
|
||||||
|
const usedPlayerIds = new Set(awayLineup.value.filter(s => s.player).map(s => s.player!.id))
|
||||||
|
return awayRoster.value.filter(p => !usedPlayerIds.has(p.id))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Current lineup based on active tab
|
||||||
|
const currentLineup = computed(() => activeTab.value === 'home' ? homeLineup.value : awayLineup.value)
|
||||||
|
const currentRoster = computed(() => activeTab.value === 'home' ? availableHomeRoster.value : availableAwayRoster.value)
|
||||||
|
|
||||||
|
// Slot 10 (pitcher) should be disabled if P is selected in batting order
|
||||||
|
const pitcherSlotDisabled = computed(() => {
|
||||||
|
const lineup = currentLineup.value
|
||||||
|
return lineup.slice(0, 9).some(slot => slot.position === 'P')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
const duplicatePositions = computed(() => {
|
||||||
|
const lineup = currentLineup.value
|
||||||
|
const positionCounts = new Map<string, number>()
|
||||||
|
|
||||||
|
lineup.forEach(slot => {
|
||||||
|
if (slot.player && slot.position) {
|
||||||
|
positionCounts.set(slot.position, (positionCounts.get(slot.position) || 0) + 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return Array.from(positionCounts.entries())
|
||||||
|
.filter(([_, count]) => count > 1)
|
||||||
|
.map(([pos, _]) => pos)
|
||||||
|
})
|
||||||
|
|
||||||
|
const validationErrors = computed(() => {
|
||||||
|
const errors: string[] = []
|
||||||
|
|
||||||
|
// Check both lineups
|
||||||
|
const homeErrors = validateLineup(homeLineup.value, 'Home')
|
||||||
|
const awayErrors = validateLineup(awayLineup.value, 'Away')
|
||||||
|
|
||||||
|
return [...homeErrors, ...awayErrors]
|
||||||
|
})
|
||||||
|
|
||||||
|
function validateLineup(lineup: LineupSlot[], teamName: string): string[] {
|
||||||
|
const errors: string[] = []
|
||||||
|
|
||||||
|
// Check if P is in batting order
|
||||||
|
const pitcherInBattingOrder = lineup.slice(0, 9).some(s => s.position === 'P')
|
||||||
|
const requiredSlots = pitcherInBattingOrder ? 9 : 10
|
||||||
|
|
||||||
|
// Check if all slots filled
|
||||||
|
const filledSlots = lineup.filter(s => s.player).length
|
||||||
|
if (filledSlots < requiredSlots) {
|
||||||
|
errors.push(`${teamName}: Fill all ${requiredSlots} lineup slots`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for missing positions
|
||||||
|
const missingPositions = lineup.filter(s => s.player && !s.position).length
|
||||||
|
if (missingPositions > 0) {
|
||||||
|
errors.push(`${teamName}: ${missingPositions} player${missingPositions > 1 ? 's' : ''} missing position`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for duplicate positions
|
||||||
|
const positionCounts = new Map<string, number>()
|
||||||
|
lineup.forEach(slot => {
|
||||||
|
if (slot.player && slot.position) {
|
||||||
|
positionCounts.set(slot.position, (positionCounts.get(slot.position) || 0) + 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const duplicates = Array.from(positionCounts.entries())
|
||||||
|
.filter(([_, count]) => count > 1)
|
||||||
|
.map(([pos, _]) => pos)
|
||||||
|
|
||||||
|
if (duplicates.length > 0) {
|
||||||
|
errors.push(`${teamName}: Duplicate positions - ${duplicates.join(', ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
const canSubmit = computed(() => {
|
||||||
|
return validationErrors.value.length === 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get player's available positions
|
||||||
|
function getPlayerPositions(player: SbaPlayer): string[] {
|
||||||
|
const positions: string[] = []
|
||||||
|
for (let i = 1; i <= 8; i++) {
|
||||||
|
const pos = player[`pos_${i}` as keyof SbaPlayer]
|
||||||
|
if (pos && typeof pos === 'string') {
|
||||||
|
positions.push(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Always add DH as an option for all players
|
||||||
|
if (!positions.includes('DH')) {
|
||||||
|
positions.push('DH')
|
||||||
|
}
|
||||||
|
return positions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drag handlers
|
||||||
|
function handleRosterDrag(player: SbaPlayer, toSlotIndex: number, fromSlotIndex?: number) {
|
||||||
|
const lineup = activeTab.value === 'home' ? homeLineup.value : awayLineup.value
|
||||||
|
|
||||||
|
// If dragging from another slot, swap or move
|
||||||
|
if (fromSlotIndex !== undefined && fromSlotIndex !== toSlotIndex) {
|
||||||
|
const fromSlot = lineup[fromSlotIndex]
|
||||||
|
const toSlot = lineup[toSlotIndex]
|
||||||
|
|
||||||
|
// Swap players
|
||||||
|
const tempPlayer = toSlot.player
|
||||||
|
const tempPosition = toSlot.position
|
||||||
|
|
||||||
|
toSlot.player = fromSlot.player
|
||||||
|
toSlot.position = fromSlot.position
|
||||||
|
|
||||||
|
fromSlot.player = tempPlayer
|
||||||
|
fromSlot.position = tempPosition
|
||||||
|
} else if (fromSlotIndex === undefined) {
|
||||||
|
// Adding from roster pool
|
||||||
|
lineup[toSlotIndex].player = player
|
||||||
|
|
||||||
|
// For pitcher slot (index 9), always use 'P'
|
||||||
|
if (toSlotIndex === 9) {
|
||||||
|
lineup[toSlotIndex].position = 'P'
|
||||||
|
} else {
|
||||||
|
// Auto-suggest first available position
|
||||||
|
const availablePositions = getPlayerPositions(player)
|
||||||
|
if (availablePositions.length > 0) {
|
||||||
|
lineup[toSlotIndex].position = availablePositions[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePlayer(slotIndex: number) {
|
||||||
|
const lineup = activeTab.value === 'home' ? homeLineup.value : awayLineup.value
|
||||||
|
lineup[slotIndex].player = null
|
||||||
|
lineup[slotIndex].position = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch game data
|
||||||
|
async function fetchGameData() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${config.public.apiUrl}/api/games/${gameId.value}`)
|
||||||
|
const data = await response.json()
|
||||||
|
homeTeamId.value = data.home_team_id
|
||||||
|
awayTeamId.value = data.away_team_id
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch game data:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch roster for a team
|
||||||
|
async function fetchRoster(teamId: number) {
|
||||||
|
try {
|
||||||
|
loadingRoster.value = true
|
||||||
|
const response = await fetch(`${config.public.apiUrl}/api/teams/${teamId}/roster?season=${season.value}`)
|
||||||
|
const data = await response.json()
|
||||||
|
return data.players as SbaPlayer[]
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch roster for team ${teamId}:`, error)
|
||||||
|
return []
|
||||||
|
} finally {
|
||||||
|
loadingRoster.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit lineups
|
||||||
|
async function submitLineups() {
|
||||||
|
if (!canSubmit.value || submittingLineups.value) return
|
||||||
|
|
||||||
|
submittingLineups.value = true
|
||||||
|
|
||||||
|
// Build request
|
||||||
|
const homeLineupRequest: LineupPlayerRequest[] = homeLineup.value
|
||||||
|
.filter(s => s.player)
|
||||||
|
.map(s => ({
|
||||||
|
player_id: s.player!.id,
|
||||||
|
position: s.position!,
|
||||||
|
batting_order: s.battingOrder
|
||||||
|
}))
|
||||||
|
|
||||||
|
const awayLineupRequest: LineupPlayerRequest[] = awayLineup.value
|
||||||
|
.filter(s => s.player)
|
||||||
|
.map(s => ({
|
||||||
|
player_id: s.player!.id,
|
||||||
|
position: s.position!,
|
||||||
|
batting_order: s.battingOrder
|
||||||
|
}))
|
||||||
|
|
||||||
|
const request: SubmitLineupsRequest = {
|
||||||
|
home_lineup: homeLineupRequest,
|
||||||
|
away_lineup: awayLineupRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Submitting lineup request:', JSON.stringify(request, null, 2))
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${config.public.apiUrl}/api/games/${gameId.value}/lineups`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(request)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json()
|
||||||
|
console.error('Lineup submission error:', error)
|
||||||
|
|
||||||
|
// Handle Pydantic validation errors
|
||||||
|
if (error.detail && Array.isArray(error.detail)) {
|
||||||
|
const messages = error.detail.map((err: any) => {
|
||||||
|
if (err.loc) {
|
||||||
|
const location = err.loc.join(' → ')
|
||||||
|
return `${location}: ${err.msg}`
|
||||||
|
}
|
||||||
|
return err.msg || JSON.stringify(err)
|
||||||
|
})
|
||||||
|
throw new Error(`Validation errors:\n${messages.join('\n')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(typeof error.detail === 'string' ? error.detail : JSON.stringify(error.detail))
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
console.log('Lineups submitted:', result)
|
||||||
|
|
||||||
|
// Redirect to game page
|
||||||
|
router.push(`/games/${gameId.value}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to submit lineups:', error)
|
||||||
|
alert(error instanceof Error ? error.message : 'Failed to submit lineups')
|
||||||
|
} finally {
|
||||||
|
submittingLineups.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
onMounted(async () => {
|
||||||
|
await fetchGameData()
|
||||||
|
|
||||||
|
if (homeTeamId.value) {
|
||||||
|
homeRoster.value = await fetchRoster(homeTeamId.value)
|
||||||
|
}
|
||||||
|
if (awayTeamId.value) {
|
||||||
|
awayRoster.value = await fetchRoster(awayTeamId.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="min-h-screen bg-gray-900 text-white p-4">
|
||||||
|
<div class="max-w-6xl mx-auto">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-3xl font-bold mb-2">Build Your Lineup</h1>
|
||||||
|
<p class="text-gray-400">Drag players from the roster to the lineup slots</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabs -->
|
||||||
|
<div class="flex gap-2 mb-6 border-b border-gray-700">
|
||||||
|
<button
|
||||||
|
@click="activeTab = 'away'"
|
||||||
|
:class="[
|
||||||
|
'px-6 py-3 font-semibold transition-colors',
|
||||||
|
activeTab === 'away'
|
||||||
|
? 'bg-blue-600 text-white border-b-2 border-blue-500'
|
||||||
|
: 'text-gray-400 hover:text-white'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
Away Lineup
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="activeTab = 'home'"
|
||||||
|
:class="[
|
||||||
|
'px-6 py-3 font-semibold transition-colors',
|
||||||
|
activeTab === 'home'
|
||||||
|
? 'bg-blue-600 text-white border-b-2 border-blue-500'
|
||||||
|
: 'text-gray-400 hover:text-white'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
Home Lineup
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Loading state -->
|
||||||
|
<div v-if="loadingRoster" class="text-center py-12">
|
||||||
|
<div class="text-xl">Loading roster...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main content -->
|
||||||
|
<div v-else class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
|
<!-- Roster Pool (Sticky on desktop) -->
|
||||||
|
<div class="lg:col-span-1">
|
||||||
|
<div class="lg:sticky lg:top-4">
|
||||||
|
<h2 class="text-xl font-bold mb-4">Available Players</h2>
|
||||||
|
<div class="bg-gray-800 rounded-lg p-4 space-y-2 max-h-[calc(100vh-8rem)] overflow-y-auto">
|
||||||
|
<div
|
||||||
|
v-for="player in currentRoster"
|
||||||
|
:key="player.id"
|
||||||
|
@dragstart="(e) => e.dataTransfer?.setData('player', JSON.stringify(player))"
|
||||||
|
draggable="true"
|
||||||
|
class="bg-gray-700 rounded p-3 cursor-move hover:bg-gray-600 transition-colors"
|
||||||
|
>
|
||||||
|
<div class="font-semibold">{{ player.name }}</div>
|
||||||
|
<div class="text-sm text-gray-400">
|
||||||
|
{{ getPlayerPositions(player).join(', ') || 'No positions' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="currentRoster.length === 0" class="text-gray-500 text-center py-4">
|
||||||
|
All players assigned
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lineup Slots -->
|
||||||
|
<div class="lg:col-span-2">
|
||||||
|
<h2 class="text-xl font-bold mb-4">Lineup</h2>
|
||||||
|
|
||||||
|
<!-- Batting order slots (1-9) -->
|
||||||
|
<div class="space-y-3 mb-6">
|
||||||
|
<div
|
||||||
|
v-for="(slot, index) in currentLineup.slice(0, 9)"
|
||||||
|
:key="index"
|
||||||
|
class="bg-gray-800 rounded-lg p-4"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<!-- Batting order number -->
|
||||||
|
<div class="text-2xl font-bold text-gray-500 w-8">
|
||||||
|
{{ index + 1 }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Player slot -->
|
||||||
|
<div
|
||||||
|
class="flex-1"
|
||||||
|
@drop.prevent="(e) => {
|
||||||
|
const playerData = e.dataTransfer?.getData('player')
|
||||||
|
const fromSlotData = e.dataTransfer?.getData('fromSlot')
|
||||||
|
if (playerData) {
|
||||||
|
const player = JSON.parse(playerData) as SbaPlayer
|
||||||
|
const fromSlot = fromSlotData ? parseInt(fromSlotData) : undefined
|
||||||
|
handleRosterDrag(player, index, fromSlot)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
@dragover.prevent
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="slot.player"
|
||||||
|
class="bg-blue-900 rounded p-3 cursor-move"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="(e) => {
|
||||||
|
e.dataTransfer?.setData('player', JSON.stringify(slot.player))
|
||||||
|
e.dataTransfer?.setData('fromSlot', index.toString())
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-semibold">{{ slot.player.name }}</div>
|
||||||
|
<div class="text-sm text-gray-400 mt-1">
|
||||||
|
Available: {{ getPlayerPositions(slot.player).join(', ') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
@click="removePlayer(index)"
|
||||||
|
class="text-red-400 hover:text-red-300 ml-2"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="border-2 border-dashed border-gray-600 rounded p-4 text-center text-gray-500">
|
||||||
|
Drop player here
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Position selector -->
|
||||||
|
<div class="w-32">
|
||||||
|
<select
|
||||||
|
v-if="slot.player"
|
||||||
|
v-model="slot.position"
|
||||||
|
:class="[
|
||||||
|
'w-full bg-gray-700 border rounded px-3 py-2',
|
||||||
|
duplicatePositions.includes(slot.position || '')
|
||||||
|
? 'border-red-500 font-bold text-red-400'
|
||||||
|
: 'border-gray-600'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<option :value="null">Position</option>
|
||||||
|
<option
|
||||||
|
v-for="pos in getPlayerPositions(slot.player)"
|
||||||
|
:key="pos"
|
||||||
|
:value="pos"
|
||||||
|
>
|
||||||
|
{{ pos }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<div v-else class="text-gray-600 text-sm text-center">
|
||||||
|
-
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pitcher slot (10) -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h3 class="text-lg font-semibold mb-3 flex items-center gap-2">
|
||||||
|
Pitcher (Non-Batting)
|
||||||
|
<span v-if="pitcherSlotDisabled" class="text-sm text-yellow-400">
|
||||||
|
(Disabled - Pitcher batting)
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'bg-gray-800 rounded-lg p-4',
|
||||||
|
pitcherSlotDisabled && 'opacity-50 pointer-events-none'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<div class="text-2xl font-bold text-gray-500 w-8">P</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex-1"
|
||||||
|
@drop.prevent="(e) => {
|
||||||
|
const playerData = e.dataTransfer?.getData('player')
|
||||||
|
const fromSlotData = e.dataTransfer?.getData('fromSlot')
|
||||||
|
if (playerData) {
|
||||||
|
const player = JSON.parse(playerData) as SbaPlayer
|
||||||
|
const fromSlot = fromSlotData ? parseInt(fromSlotData) : undefined
|
||||||
|
handleRosterDrag(player, 9, fromSlot)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
@dragover.prevent
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="currentLineup[9].player"
|
||||||
|
class="bg-blue-900 rounded p-3 cursor-move"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="(e) => {
|
||||||
|
e.dataTransfer?.setData('player', JSON.stringify(currentLineup[9].player))
|
||||||
|
e.dataTransfer?.setData('fromSlot', '9')
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-semibold">{{ currentLineup[9].player.name }}</div>
|
||||||
|
<div class="text-sm text-gray-400 mt-1">
|
||||||
|
Available: {{ getPlayerPositions(currentLineup[9].player).join(', ') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
@click="removePlayer(9)"
|
||||||
|
class="text-red-400 hover:text-red-300 ml-2"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="border-2 border-dashed border-gray-600 rounded p-4 text-center text-gray-500">
|
||||||
|
Drop pitcher here
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-32">
|
||||||
|
<div class="text-gray-600 text-sm text-center font-semibold">P</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Validation errors -->
|
||||||
|
<div v-if="validationErrors.length > 0" class="bg-red-900/30 border border-red-500 rounded-lg p-4 mb-4">
|
||||||
|
<div class="font-semibold mb-2">Please fix the following errors:</div>
|
||||||
|
<ul class="list-disc list-inside space-y-1">
|
||||||
|
<li v-for="error in validationErrors" :key="error" class="text-sm">
|
||||||
|
{{ error }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Submit button -->
|
||||||
|
<ActionButton
|
||||||
|
variant="success"
|
||||||
|
size="lg"
|
||||||
|
full-width
|
||||||
|
:disabled="!canSubmit"
|
||||||
|
:loading="submittingLineups"
|
||||||
|
@click="submitLineups"
|
||||||
|
>
|
||||||
|
{{ submittingLineups ? 'Submitting Lineups...' : (canSubmit ? 'Submit Lineups & Start Game' : 'Complete Both Lineups to Continue') }}
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
21
frontend-sba/plugins/auth.client.ts
Normal file
21
frontend-sba/plugins/auth.client.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Auth Plugin - Client Side Only
|
||||||
|
*
|
||||||
|
* Initializes authentication state from localStorage before any navigation occurs.
|
||||||
|
* This ensures the auth middleware has the correct state when checking authentication.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useAuthStore } from '~/store/auth'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin(() => {
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
// Initialize auth from localStorage on app load
|
||||||
|
authStore.initializeAuth()
|
||||||
|
|
||||||
|
console.log('[Auth Plugin] Initialized auth state:', {
|
||||||
|
isAuthenticated: authStore.isAuthenticated,
|
||||||
|
isTokenValid: authStore.isTokenValid,
|
||||||
|
hasUser: !!authStore.currentUser
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -86,6 +86,28 @@ export interface Team {
|
|||||||
league_id: 'sba' | 'pd'
|
league_id: 'sba' | 'pd'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SBA Team data (from /api/teams endpoint)
|
||||||
|
*/
|
||||||
|
export interface SbaTeam {
|
||||||
|
id: number
|
||||||
|
abbrev: string
|
||||||
|
sname: string // Short name (e.g., "Geese")
|
||||||
|
lname: string // Long name (e.g., "Everett Geese")
|
||||||
|
color: string // Hex color code
|
||||||
|
manager_legacy: string
|
||||||
|
gmid: string | null
|
||||||
|
gmid2: string | null
|
||||||
|
division: {
|
||||||
|
id: number
|
||||||
|
division_name: string
|
||||||
|
division_abbrev: string
|
||||||
|
league_name: string
|
||||||
|
league_abbrev: string
|
||||||
|
season: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User's teams response
|
* User's teams response
|
||||||
*/
|
*/
|
||||||
@ -177,3 +199,24 @@ export interface RefreshTokenResponse {
|
|||||||
access_token: string
|
access_token: string
|
||||||
expires_in: number
|
expires_in: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/games/:id/lineups
|
||||||
|
*/
|
||||||
|
export interface LineupPlayerRequest {
|
||||||
|
player_id: number
|
||||||
|
position: string
|
||||||
|
batting_order: number | null // null for pitcher in DH lineup
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubmitLineupsRequest {
|
||||||
|
home_lineup: LineupPlayerRequest[] // 9-10 players
|
||||||
|
away_lineup: LineupPlayerRequest[] // 9-10 players
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubmitLineupsResponse {
|
||||||
|
game_id: string
|
||||||
|
message: string
|
||||||
|
home_lineup_count: number
|
||||||
|
away_lineup_count: number
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user