feat: Implement Phase 4 - Application Architecture & Routing

Phase 4 establishes the core application structure with SvelteKit routing,
data loading, error handling, and state management.

## Routing & Data Loading
- Created root layout load function (+layout.ts) with app version and feature flags
- Implemented comprehensive error boundary (+error.svelte) with status-based messages
- Added page loaders for homepage, matches, players, and about routes
- Homepage loader fetches featured matches via API with error fallback
- Matches loader supports URL query parameters (map, player_id, limit)

## State Management (Svelte Stores)
- preferences.ts: User settings with localStorage persistence
  * Theme selection (cs2dark, cs2light, auto)
  * Favorite players tracking
  * Advanced stats toggle, date format preferences
- search.ts: Search state with recent searches (localStorage)
- toast.ts: Toast notification system with auto-dismiss
  * Success, error, warning, info types
  * Configurable duration and dismissibility

## UI Components
- Toast.svelte: Individual notification with Lucide icons
- ToastContainer.svelte: Fixed top-right toast display
- Integrated ToastContainer into root layout

## Fixes
- Fixed Svelte 5 deprecation warnings (removed <svelte:component> in runes mode)
- Updated homepage to use PageData from loader
- Added proper type safety across all load functions

## Testing
- Type check: 0 errors, 0 warnings
- Production build: Successful
- All Phase 4 core objectives completed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-04 20:47:49 +01:00
parent 09ce400cd7
commit 24b990ac62
15 changed files with 688 additions and 97 deletions

View File

@@ -0,0 +1,49 @@
import type { PageLoad } from './$types';
import { api } from '$lib/api';
/**
* Matches listing page data loader
*/
export const load: PageLoad = async ({ url }) => {
// Get query parameters
const map = url.searchParams.get('map') || undefined;
const playerIdStr = url.searchParams.get('player_id');
const playerId = playerIdStr ? Number(playerIdStr) : undefined;
const limit = Number(url.searchParams.get('limit')) || 50;
try {
// Load matches with filters
const matchesData = await api.matches.getMatches({
limit,
map,
player_id: playerId
});
return {
matches: matchesData.matches,
hasMore: matchesData.has_more,
nextPageTime: matchesData.next_page_time,
filters: {
map,
playerId
},
meta: {
title: 'Browse Matches - CS2.WTF',
description: 'Browse and search through CS2 matchmaking games with detailed filters.'
}
};
} catch (error) {
console.error('Failed to load matches:', error);
// Return empty state on error
return {
matches: [],
hasMore: false,
filters: { map, playerId },
meta: {
title: 'Browse Matches - CS2.WTF',
description: 'Browse and search through CS2 matchmaking games with detailed filters.'
}
};
}
};