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

97
src/routes/+error.svelte Normal file
View File

@@ -0,0 +1,97 @@
<script lang="ts">
import { page } from '$app/stores';
import Button from '$lib/components/ui/Button.svelte';
import Card from '$lib/components/ui/Card.svelte';
import { Home, ArrowLeft } from 'lucide-svelte';
// Get error information
const error = $page.error;
const status = $page.status;
// Determine error message
const getErrorMessage = (status: number): string => {
switch (status) {
case 404:
return "We couldn't find the page you're looking for.";
case 500:
return 'Something went wrong on our end. Please try again later.';
case 503:
return 'Service temporarily unavailable. Please check back soon.';
default:
return 'An unexpected error occurred.';
}
};
const getErrorTitle = (status: number): string => {
switch (status) {
case 404:
return 'Page Not Found';
case 500:
return 'Internal Server Error';
case 503:
return 'Service Unavailable';
default:
return 'Error';
}
};
</script>
<svelte:head>
<title>{status} - {getErrorTitle(status)} | CS2.WTF</title>
</svelte:head>
<div class="container mx-auto flex min-h-[60vh] items-center justify-center px-4 py-16">
<Card padding="lg" class="w-full max-w-2xl">
<div class="text-center">
<!-- Error Code -->
<div class="mb-4 text-8xl font-bold text-primary">
{status}
</div>
<!-- Error Title -->
<h1 class="mb-4 text-3xl font-bold text-base-content">
{getErrorTitle(status)}
</h1>
<!-- Error Message -->
<p class="mb-8 text-lg text-base-content/70">
{getErrorMessage(status)}
</p>
<!-- Debug Info (only in development) -->
{#if import.meta.env?.DEV && error}
<div class="mb-8 rounded-lg bg-base-300 p-4 text-left">
<p class="mb-2 font-mono text-sm text-error">
<strong>Debug Info:</strong>
</p>
<pre class="overflow-x-auto text-xs text-base-content/80">{JSON.stringify(
error,
null,
2
)}</pre>
</div>
{/if}
<!-- Action Buttons -->
<div class="flex flex-col justify-center gap-4 sm:flex-row">
<Button variant="secondary" href="javascript:history.back()">
<ArrowLeft class="mr-2 h-5 w-5" />
Go Back
</Button>
<Button variant="primary" href="/">
<Home class="mr-2 h-5 w-5" />
Go Home
</Button>
</div>
<!-- Help Text -->
<p class="mt-8 text-sm text-base-content/50">
If this problem persists, please
<a href="https://somegit.dev/CSGOWTF/csgowtf/issues" class="link-hover link text-primary">
report it on GitHub
</a>
</p>
</div>
</Card>
</div>