import { MOCK_PLAYERS, MOCK_ORGS, MOCK_PROJECTS } from '../constants'; import { Player, Organization, Project } from '../types'; const API_URL = 'https://vollidioten.ceraticsoft.de/api'; const MOCK_DATA_HEADER = 'X-Mock-Data'; class DatabaseService { private players: Player[] = MOCK_PLAYERS; private orgs: Organization[] = MOCK_ORGS; private projects: Project[] = MOCK_PROJECTS; private listeners: Function[] = []; constructor() { this.fetchAll(); } // Try to fetch real data from backend 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`) ]); // Check if we're getting mock data from nginx fallback const isMockData = pRes.headers.get(MOCK_DATA_HEADER) === 'true' || oRes.headers.get(MOCK_DATA_HEADER) === 'true' || prRes.headers.get(MOCK_DATA_HEADER) === 'true'; 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(); if (isMockData) { console.warn("Backend unavailable - using mock data from nginx fallback"); } else { console.log("✅ Connected to Backend Database - using real data"); } } else { console.warn(`API returned errors: players=${pRes.status}, orgs=${oRes.status}, projects=${prRes.status}`); console.warn("Using built-in mock data"); // Data is already set to mock data in constructor } } catch (e) { console.warn("Backend not available, using built-in Mock Data.", e); // Fallback is already set in constructor } } 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); } 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();