Files
Lars Behrends 065a6e657d feat: add world map functionality and admin map management
- Added world map page with interactive marker display
- Implemented admin map management for marker CRUD operations
- Added map layers and markers seed data to database
- Integrated new routes for map functionality
- Updated database configuration for production environment
- Added documentation page route
- Enhanced package.json with required dependencies for map features
2026-01-02 05:08:07 +01:00

166 lines
5.8 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Routes, Route, useNavigate, useLocation } from 'react-router-dom';
import Layout from './components/Layout';
import Dashboard from './pages/Dashboard';
import Players from './pages/Players';
import PlayerProfile from './pages/PlayerProfile';
import SetupGuide from './pages/SetupGuide';
import Projects from './pages/Projects';
import Organizations from './pages/Organizations';
import Cities from './pages/Cities';
import CityProfile from './pages/CityProfile';
import ProjectProfile from './pages/ProjectProfile';
import DatapackGenerator from './pages/DatapackGenerator';
import DatabaseManager from './pages/DatabaseManager';
import LinkPlayer from './pages/LinkPlayer';
import AdminPage from './pages/Admin';
import AdminMapManagement from './pages/AdminMapManagement';
import WorldMap from './pages/WorldMap';
import EditMarker from './pages/EditMarker';
import DocumentationPage from './pages/Dokumentation';
import { dbService } from './services/DatabaseService';
import { authService } from './services/AuthService';
import { DiscordUser } from './types';
function App() {
const navigate = useNavigate();
const location = useLocation();
// Auth state
const [user, setUser] = useState<DiscordUser | null>(null);
const [authChecked, setAuthChecked] = useState(false);
// State for data from DB (Async)
const [players, setPlayers] = useState(dbService.getPlayers());
const [orgs, setOrgs] = useState(dbService.getOrgs());
const [projects, setProjects] = useState(dbService.getProjects());
// Subscribe to DB updates (when fetch completes or edits happen)
useEffect(() => {
const unsub = dbService.subscribe(() => {
setPlayers([...dbService.getPlayers()]);
setOrgs([...dbService.getOrgs()]);
setProjects([...dbService.getProjects()]);
});
return unsub;
}, []);
// Subscribe to auth updates
useEffect(() => {
const unsub = authService.subscribe((currentUser) => {
setUser(currentUser);
setAuthChecked(true);
// If user is logged in but not linked, redirect to link page
if (currentUser && !currentUser.linkedPlayerUuid) {
navigate('/link-player');
} else if (currentUser && currentUser.linkedPlayerUuid) {
// User is fully authenticated, redirect from link page if needed
if (location.pathname === '/link-player') {
navigate('/');
}
}
});
return unsub;
}, [navigate, location.pathname]);
const navigateToPlayer = (id: string) => {
navigate(`/players/${id}`);
};
const navigateToProject = (id: string) => {
navigate(`/projects/${id}`);
};
const navigateToOrg = (id: string) => {
const org = orgs.find(o => o.id === id);
if (org?.type === 'City') {
navigate(`/cities/${id}`);
} else {
navigate(`/organizations/${id}`);
}
};
const handleNavigate = (path: string) => {
navigate(path);
};
// Determine active tab from current path
const getActiveTab = () => {
const path = location.pathname;
if (path === '/') return 'dashboard';
if (path.startsWith('/players')) return 'players';
if (path.startsWith('/cities')) return 'cities';
if (path.startsWith('/projects')) return 'projects';
if (path.startsWith('/organizations')) return 'organizations';
if (path.startsWith('/admin')) return 'admin';
if (path === '/setup') return 'setup';
if (path === '/datapack') return 'datapack';
if (path === '/link-player') return 'link-player';
return 'dashboard';
};
const activeTab = getActiveTab();
return (
<Layout activeTab={activeTab} onNavigate={handleNavigate}>
<Routes>
{/* Auth Route */}
<Route path="/link-player" element={<LinkPlayer />} />
{/* Main Routes */}
<Route path="/" element={<Dashboard />} />
<Route path="/setup" element={<SetupGuide />} />
<Route path="/datapack" element={<DatapackGenerator />} />
{/* Player Routes */}
<Route path="/players" element={<Players onSelectPlayer={navigateToPlayer} />} />
<Route path="/players/:id" element={<PlayerProfile />} />
{/* City Routes */}
<Route path="/cities" element={<Cities onSelectCity={(id) => navigateToOrg(id)} />} />
<Route path="/cities/:id" element={<CityProfile />} />
{/* Organization Routes */}
<Route path="/organizations" element={<Organizations onSelectOrg={(id) => navigateToOrg(id)} />} />
<Route path="/organizations/:id" element={<CityProfile />} />
{/* Project Routes */}
<Route path="/projects" element={<Projects onSelectProject={(id) => navigateToProject(id)} />} />
<Route path="/projects/:id" element={<ProjectProfile
onBack={() => navigate('/projects')}
onSelectPlayer={navigateToPlayer}
onSelectOrg={navigateToOrg}
/>} />
{/* Admin Routes */}
<Route path="/admin" element={<AdminPage onBack={() => navigate('/')} />} />
<Route path="/admin/map-management" element={<AdminMapManagement />} />
{/* Map Route */}
<Route path="/world-map" element={<WorldMap />} />
<Route path="/admin/edit-marker/:markerId" element={<EditMarker />} />
{/* Dokumentation Route */}
<Route path="/dokumentation" element={<DocumentationPage />} />
{/* Fallback */}
<Route path="*" element={
<div className="flex flex-col items-center justify-center h-[50vh] text-textMuted">
<div className="text-6xl mb-4">404</div>
<p>Seite nicht gefunden</p>
<button
onClick={() => navigate('/')}
className="mt-4 text-accentInfo hover:underline"
>
Zurück zur Startseite
</button>
</div>
} />
</Routes>
</Layout>
);
}
export default App;