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

177
pages/Projects.tsx Normal file
View File

@@ -0,0 +1,177 @@
import React, { useState } from 'react';
import { MOCK_PROJECTS } from '../constants';
import { Project } from '../types';
import { Icons } from '../components/IconSet';
interface ProjectsProps {
onSelectProject?: (id: string) => void;
}
const StatusBadge = ({ status, hiring }: { status: Project['status'], hiring: boolean }) => {
const styles = {
'active': 'bg-accentInfo/10 text-accentInfo border-accentInfo/20',
'recruiting': 'bg-accentSuccess/10 text-accentSuccess border-accentSuccess/20',
'private': 'bg-textMuted/10 text-textMuted border-textMuted/20',
'completed': 'bg-textMuted/10 text-textMuted border-textMuted/20',
};
const labels = {
'active': 'Aktives Geschäft',
'recruiting': 'Offener Story-Arc',
'private': 'Privat / Versteckt',
'completed': 'Archiviert'
};
return (
<div className="flex gap-2">
<span className={`text-[10px] uppercase font-bold px-2 py-0.5 rounded-full border ${styles[status]}`}>
{labels[status]}
</span>
{hiring && (
<span className="text-[10px] uppercase font-bold px-2 py-0.5 rounded-full border bg-purple-500/10 text-purple-400 border-purple-500/20 animate-pulse">
Stellen
</span>
)}
</div>
);
};
const VentureCard = ({ project, onClick }: { project: Project, onClick?: () => void }) => (
<div
onClick={onClick}
className="bg-surface border border-border rounded-xl p-5 hover:border-accentInfo/40 transition-all duration-200 hover:shadow-card group flex flex-col h-full relative overflow-hidden cursor-pointer"
>
{/* Background accent for certain types */}
{project.category === 'Black Market' && (
<div className="absolute top-0 right-0 w-20 h-20 bg-red-900/10 blur-xl rounded-full -mr-10 -mt-10 pointer-events-none" />
)}
<div className="flex justify-between items-start mb-3 relative z-10">
<div className={`text-xs font-mono px-2 py-1 rounded border border-white/5 ${
project.category === 'Story Arc' ? 'bg-orange-500/10 text-orange-400' :
project.category === 'Black Market' ? 'bg-red-500/10 text-red-400' :
'bg-surfaceHighlight text-textMuted'
}`}>
{project.category}
</div>
<StatusBadge status={project.status} hiring={project.hiring} />
</div>
<h3 className="text-lg font-bold text-textMain mb-1 group-hover:text-accentInfo transition-colors relative z-10">
{project.title}
</h3>
<div className="flex items-center gap-2 mb-4 text-xs text-textMuted relative z-10">
<span>Inhaber</span>
<span className="text-textMain font-medium bg-white/5 px-1.5 py-0.5 rounded flex items-center gap-1.5">
<div className="w-3 h-3 rounded-full bg-gradient-to-tr from-accentInfo to-blue-400"></div>
{project.owner}
</span>
</div>
<p className="text-sm text-textMuted mb-6 flex-1 leading-relaxed relative z-10">
{project.description}
</p>
<div className="pt-4 border-t border-white/5 space-y-3 relative z-10">
{/* Reputation / Progress Bar */}
<div>
<div className="flex justify-between text-xs mb-1">
<span className="text-textMuted">
{project.category === 'Story Arc' ? 'Story Fortschritt' : 'Ruf'}
</span>
<span className="font-mono text-textMain">{project.progress}%</span>
</div>
<div className="h-1 bg-surfaceHighlight rounded-full overflow-hidden">
<div
className={`h-full rounded-full ${
project.category === 'Black Market' ? 'bg-red-500/70' : 'bg-accentInfo/70'
}`}
style={{ width: `${project.progress}%` }}
/>
</div>
</div>
<div className="flex items-center justify-between text-xs text-textMuted">
<div className="flex items-center gap-2">
<Icons.Users className="w-3 h-3" />
<span>{project.employees.length} Mitglieder</span>
</div>
<div className="flex gap-3">
{project.shopCatalog && project.shopCatalog.length > 0 && (
<div className="flex items-center gap-1 text-accentInfo">
<Icons.ShoppingBag className="w-3 h-3" />
<span className="font-bold">Shop</span>
</div>
)}
{project.foundedDate && <span>Gegr. {project.foundedDate}</span>}
</div>
</div>
</div>
</div>
);
const Projects: React.FC<ProjectsProps> = ({ onSelectProject }) => {
const [filter, setFilter] = useState<'all' | Project['status']>('all');
const filteredProjects = MOCK_PROJECTS.filter(p =>
filter === 'all' ? true : p.status === filter
);
const tabs = [
{ id: 'all', label: 'Alle Unternehmen' },
{ id: 'active', label: 'Aktive Firmen' },
{ id: 'recruiting', label: 'Story Arcs' },
{ id: 'private', label: 'Privat' },
];
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">Unternehmen & Projekte</h1>
<p className="text-textMuted">Spielergeführte Firmen, aktive Rollenspiel-Stränge und Dienstleister.</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>+ Firma registrieren</span>
</button>
</div>
{/* Filter Tabs */}
<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>
{/* Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{filteredProjects.map(project => (
<VentureCard
key={project.id}
project={project}
onClick={() => onSelectProject && onSelectProject(project.id)}
/>
))}
</div>
{filteredProjects.length === 0 && (
<div className="text-center py-20 text-textMuted">
<p>Keine Unternehmen in dieser Kategorie gefunden.</p>
</div>
)}
</div>
);
};
export default Projects;