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.
This commit is contained in:
Lars Behrends
2025-12-28 02:15:09 +01:00
parent 4ab4a1d64a
commit d1b797a320
23 changed files with 2514 additions and 8 deletions

101
pages/Organizations.tsx Normal file
View File

@@ -0,0 +1,101 @@
import React, { useState } from 'react';
import { MOCK_ORGS } from '../constants';
import { Organization } from '../types';
import { Icons } from '../components/IconSet';
const OrgCard = ({ org, onClick }: { org: Organization; onClick: () => void }) => (
<div
onClick={onClick}
className="bg-surface border border-border rounded-xl p-6 hover:border-accentInfo/40 transition-all duration-200 hover:shadow-card group flex flex-col h-full relative overflow-hidden cursor-pointer"
>
<div className="flex justify-between items-start mb-4">
<div className={`w-10 h-10 rounded-lg flex items-center justify-center text-lg font-bold border border-white/5 ${
org.type === 'City' ? 'bg-blue-500/10 text-blue-400' :
org.type === 'Guild' ? 'bg-amber-500/10 text-amber-400' :
'bg-purple-500/10 text-purple-400'
}`}>
{org.name.charAt(0)}
</div>
<span className={`text-[10px] uppercase font-bold px-2 py-0.5 rounded-full border ${
org.status === 'active' ? 'bg-accentSuccess/10 text-accentSuccess border-accentSuccess/20' : 'bg-textMuted/10 text-textMuted border-textMuted/20'
}`}>
{org.status}
</span>
</div>
<h3 className="text-xl font-bold text-textMain mb-1 group-hover:text-accentInfo transition-colors">
{org.name}
</h3>
<div className="text-xs text-textMuted font-mono mb-4">{org.type}</div>
<p className="text-sm text-textMuted mb-6 flex-1 leading-relaxed">
{org.description}
</p>
<div className="pt-4 border-t border-white/5 flex items-center gap-4 text-xs text-textMuted">
<div className="flex items-center gap-2">
<Icons.Users className="w-4 h-4" />
<span>{org.memberCount} Mitglieder</span>
</div>
</div>
</div>
);
const Organizations: React.FC<{ onSelectOrg: (id: string) => void }> = ({ onSelectOrg }) => {
const [filter, setFilter] = useState<'all' | Organization['type']>('all');
const filteredOrgs = MOCK_ORGS.filter(org =>
filter === 'all' ? true : org.type === filter
);
const tabs = [
{ id: 'all', label: 'Alle Organisationen' },
{ id: 'City', label: 'Städte' },
{ id: 'Guild', label: 'Gilden' },
{ id: 'Company', label: 'Firmen' },
];
return (
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-2">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-3xl font-bold mb-1">Organisationen</h1>
<p className="text-textMuted">Offizielle Fraktionen, Städte und registrierte Gilden.</p>
</div>
<button className="bg-textMain text-background hover:bg-white font-medium px-4 py-2 rounded-lg text-sm transition-colors flex items-center gap-2">
<span>+ Org registrieren</span>
</button>
</div>
<div className="flex flex-wrap gap-2 border-b border-border pb-1">
{tabs.map(tab => (
<button
key={tab.id}
onClick={() => setFilter(tab.id as any)}
className={`px-4 py-2 text-sm font-medium rounded-t-lg transition-colors relative top-[1px] ${
filter === tab.id
? 'text-textMain border-b-2 border-accentInfo bg-surfaceHighlight/20'
: 'text-textMuted hover:text-textMain hover:bg-white/5'
}`}
>
{tab.label}
</button>
))}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{filteredOrgs.map(org => (
<OrgCard key={org.id} org={org} onClick={() => onSelectOrg(org.id)} />
))}
</div>
{filteredOrgs.length === 0 && (
<div className="text-center py-20 text-textMuted">
<p>Keine Organisationen gefunden.</p>
</div>
)}
</div>
);
};
export default Organizations;