mirror of
https://github.com/ceratic/project_vollidioten_website.git
synced 2026-05-14 00:16:47 +02:00
178 lines
5.2 KiB
TypeScript
178 lines
5.2 KiB
TypeScript
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<Player | null> {
|
|
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<Player>) {
|
|
// 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<Project>) {
|
|
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<boolean> {
|
|
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<boolean> {
|
|
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();
|