Files
csgowtf/src/lib/stores/search.ts
vikingowl 24b990ac62 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>
2025-11-04 20:47:49 +01:00

119 lines
2.5 KiB
TypeScript

import { writable, derived } from 'svelte/store';
import { browser } from '$app/environment';
/**
* Search state store
* Manages search queries and recent searches
*/
export interface SearchState {
query: string;
recentSearches: string[];
filters: {
map?: string;
playerId?: number;
dateFrom?: string;
dateTo?: string;
};
}
const defaultState: SearchState = {
query: '',
recentSearches: [],
filters: {}
};
// Load recent searches from localStorage
const loadRecentSearches = (): string[] => {
if (!browser) return [];
try {
const stored = localStorage.getItem('cs2wtf-recent-searches');
if (stored) {
return JSON.parse(stored);
}
} catch (error) {
console.error('Failed to load recent searches:', error);
}
return [];
};
// Create the store
const createSearchStore = () => {
const { subscribe, set, update } = writable<SearchState>({
...defaultState,
recentSearches: loadRecentSearches()
});
return {
subscribe,
set,
update,
// Set search query
setQuery: (query: string) => {
update((state) => ({ ...state, query }));
},
// Clear search query
clearQuery: () => {
update((state) => ({ ...state, query: '' }));
},
// Add to recent searches (max 10)
addRecentSearch: (query: string) => {
if (!query.trim()) return;
update((state) => {
const recent = [query, ...state.recentSearches.filter((q) => q !== query)].slice(0, 10);
if (browser) {
localStorage.setItem('cs2wtf-recent-searches', JSON.stringify(recent));
}
return { ...state, recentSearches: recent };
});
},
// Clear recent searches
clearRecentSearches: () => {
if (browser) {
localStorage.removeItem('cs2wtf-recent-searches');
}
update((state) => ({ ...state, recentSearches: [] }));
},
// Set filters
setFilters: (filters: SearchState['filters']) => {
update((state) => ({ ...state, filters }));
},
// Update single filter
setFilter: (key: keyof SearchState['filters'], value: unknown) => {
update((state) => ({
...state,
filters: { ...state.filters, [key]: value }
}));
},
// Clear filters
clearFilters: () => {
update((state) => ({ ...state, filters: {} }));
},
// Reset entire search state
reset: () => {
set({ ...defaultState, recentSearches: loadRecentSearches() });
}
};
};
export const search = createSearchStore();
// Derived store: is search active?
export const isSearchActive = derived(
search,
($search) => $search.query.length > 0 || Object.keys($search.filters).length > 0
);