import { Player, Organization, Project } from '../types'; const API_URL = 'https://vollidioten.ceraticsoft.de/api'; class DatabaseService { private players: Player[] = []; private orgs: Organization[] = []; private projects: Project[] = []; private listeners: Function[] = []; constructor() { this.fetchAll(); } // Fetch data from backend - always try to load fresh data async fetchAll() { try { console.log("Fetching data from API..."); const [pRes, oRes, prRes] = await Promise.all([ fetch(`${API_URL}/players`), fetch(`${API_URL}/orgs`), fetch(`${API_URL}/projects`) ]); if (pRes.ok && oRes.ok && prRes.ok) { const playersData = await pRes.json(); const orgsData = await oRes.json(); const projectsData = await prRes.json(); console.log(`Loaded ${playersData.length} players, ${orgsData.length} orgs, ${projectsData.length} projects from API`); this.players = playersData; this.orgs = orgsData; this.projects = projectsData; this.notify(); console.log("✅ Connected to Backend Database - using real data"); } else { console.warn(`API returned errors: players=${pRes.status}, orgs=${oRes.status}, projects=${prRes.status}`); throw new Error("Backend unavailable"); } } catch (e) { console.warn("Backend not available, data not loaded.", e); throw e; // Re-throw so components can handle the error } } subscribe(cb: Function) { this.listeners.push(cb); return () => { this.listeners = this.listeners.filter(l => l !== cb); }; } notify() { this.listeners.forEach(cb => cb()); } // --- READ --- getPlayers(): Player[] { return this.players; } getOrgs(): Organization[] { return this.orgs; } getProjects(): Project[] { return this.projects; } getPlayer(uuid: string): Player | undefined { return this.players.find(p => p.uuid === uuid); } // Fetch individual player data directly from API (for profile pages) async fetchPlayer(uuid: string): Promise { try { const response = await fetch(`${API_URL}/players/${uuid}`); if (response.ok) { const playerData = await response.json(); // Update local cache this.players = this.players.map(p => p.uuid === uuid ? playerData : p); if (!this.players.find(p => p.uuid === uuid)) { this.players.push(playerData); } this.notify(); return playerData; } } catch (e) { console.warn("Failed to fetch individual player data", e); } return null; } getOrg(id: string): Organization | undefined { return this.orgs.find(o => o.id === id); } getProject(id: string): Project | undefined { return this.projects.find(p => p.id === id); } // --- WRITE (Optimistic UI + API Call) --- async updatePlayer(uuid: string, updates: Partial) { // 1. Optimistic Update this.players = this.players.map(p => p.uuid === uuid ? { ...p, ...updates } : p); this.notify(); // 2. API Call try { await fetch(`${API_URL}/players/${uuid}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates), credentials: 'include' // Sends session cookie }); } catch (e) { console.error("Failed to save player update", e); } } async updateProject(id: string, updates: Partial) { this.projects = this.projects.map(p => p.id === id ? { ...p, ...updates } : p); this.notify(); try { await fetch(`${API_URL}/projects/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates), credentials: 'include' }); } catch (e) { console.error("Failed to save project update", e); } } // --- PROJECT MANAGEMENT --- async createProject(projectData: { title: string; description: string; category: Project['category'] }): Promise { try { const response = await fetch(`${API_URL}/projects`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(projectData), }); if (response.ok) { // Reload data to include the new project await this.fetchAll(); return true; } return false; } catch (e) { console.error("Failed to create project", e); return false; } } // --- LINKING --- async linkPlayer(playerUuid: string): Promise { try { const res = await fetch(`${API_URL}/link-user`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ playerUuid }), credentials: 'include' }); if (res.ok) { // Reload data to reflect linking (might unlock edit buttons) await this.fetchAll(); return true; } return false; } catch(e) { console.error("Link failed", e); return false; } } } export const dbService = new DatabaseService();