Files
project_vollidioten_website/App.tsx
Lars Behrends d1b797a320 feat: Initialize project with Vite, React, and TypeScript
Sets up the foundational structure for the Obsidian | RP Plattform. This includes configuring Vite as the build tool, integrating React for the UI, and establishing TypeScript for type safety. Also includes initial styling and placeholder data to define the application's core interfaces.
2025-12-28 02:15:09 +01:00

168 lines
6.2 KiB
TypeScript

import React, { useState } from 'react';
import Layout from './components/Layout';
import Dashboard from './pages/Dashboard';
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 { MOCK_PLAYERS, MOCK_ORGS, MOCK_PROJECTS } from './constants';
import { Icons } from './components/IconSet';
function App() {
const [activeTab, setActiveTab] = useState('dashboard');
const [selectedPlayerId, setSelectedPlayerId] = useState<string | null>(null);
const [selectedCityId, setSelectedCityId] = useState<string | null>(null);
const [selectedProjectId, setSelectedProjectId] = useState<string | null>(null);
const [selectedOrgId, setSelectedOrgId] = useState<string | null>(null);
const handleNavigate = (tab: string) => {
setActiveTab(tab);
if (tab !== 'players') setSelectedPlayerId(null);
if (tab !== 'cities') setSelectedCityId(null);
if (tab !== 'projects') setSelectedProjectId(null);
if (tab !== 'organizations') setSelectedOrgId(null);
};
// Helper to jump to a player from another view
const navigateToPlayer = (id: string) => {
setSelectedPlayerId(id);
setActiveTab('players');
};
// Helper to jump to a project from another view
const navigateToProject = (id: string) => {
setSelectedProjectId(id);
setActiveTab('projects');
};
// Helper to jump to an org/city
const navigateToOrg = (id: string) => {
const org = MOCK_ORGS.find(o => o.id === id);
if (org?.type === 'City') {
setSelectedCityId(id);
setActiveTab('cities');
} else {
setSelectedOrgId(id);
setActiveTab('organizations');
}
};
const renderContent = () => {
if (activeTab === 'dashboard') return <Dashboard />;
if (activeTab === 'projects') {
if (selectedProjectId) {
const project = MOCK_PROJECTS.find(p => p.id === selectedProjectId);
if (project) return (
<ProjectProfile
project={project}
onBack={() => setSelectedProjectId(null)}
onSelectPlayer={navigateToPlayer}
onSelectOrg={navigateToOrg}
/>
);
}
return <Projects onSelectProject={setSelectedProjectId} />;
}
if (activeTab === 'organizations') {
if (selectedOrgId) {
const org = MOCK_ORGS.find(o => o.id === selectedOrgId);
if (org) return (
<CityProfile
city={org}
onBack={() => setSelectedOrgId(null)}
backLabel="Zurück zum Verzeichnis"
onSelectPlayer={navigateToPlayer}
onSelectProject={navigateToProject}
/>
);
}
return <Organizations onSelectOrg={setSelectedOrgId} />;
}
if (activeTab === 'setup') return <SetupGuide />;
if (activeTab === 'datapack') return <DatapackGenerator />;
if (activeTab === 'cities') {
if (selectedCityId) {
const city = MOCK_ORGS.find(o => o.id === selectedCityId);
if (city) return (
<CityProfile
city={city}
onBack={() => setSelectedCityId(null)}
backLabel="Zurück zu Städte"
onSelectPlayer={navigateToPlayer}
onSelectProject={navigateToProject}
/>
);
}
return <Cities onSelectCity={setSelectedCityId} />;
}
if (activeTab === 'players') {
if (selectedPlayerId) {
const player = MOCK_PLAYERS.find(p => p.uuid === selectedPlayerId);
if (player) return <PlayerProfile player={player} onBack={() => setSelectedPlayerId(null)} />;
}
return (
<div className="animate-in fade-in">
<div className="flex justify-between items-center mb-6">
<h2 className="text-2xl font-bold">Bürgerverzeichnis</h2>
<div className="relative">
<Icons.Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-textMuted" />
<input
type="text"
placeholder="Filtern nach Tag oder Name..."
className="bg-surfaceHighlight border border-border rounded-lg pl-10 pr-4 py-2 text-sm text-textMain focus:border-accentInfo focus:outline-none w-64 transition-colors"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
{MOCK_PLAYERS.map(player => (
<div
key={player.uuid}
onClick={() => setSelectedPlayerId(player.uuid)}
className="group bg-surface border border-border p-4 rounded-xl cursor-pointer hover:border-accentInfo/50 transition-all duration-200 hover:shadow-card"
>
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-md flex items-center justify-center font-bold text-lg text-textMuted group-hover:text-textMain transition-colors">
<img src={"https://minotar.net/armor/bust/"+player.username+"/500.png"}></img>
</div>
<div>
<div className="font-semibold text-textMain group-hover:text-accentInfo transition-colors">{player.username}</div>
<div className="text-xs text-textMuted mt-1 flex gap-2">
{player.tags.slice(0, 2).map(t => <span key={t}>{t}</span>)}
</div>
</div>
</div>
</div>
))}
</div>
</div>
);
}
return (
<div className="flex flex-col items-center justify-center h-[50vh] text-textMuted">
<Icons.Box className="w-12 h-12 mb-4 opacity-20" />
<p>Modul <strong>{activeTab}</strong> wird derzeit gewartet.</p>
</div>
);
};
return (
<Layout activeTab={activeTab} onNavigate={handleNavigate}>
{renderContent()}
</Layout>
);
}
export default App;