mirror of
https://github.com/ceratic/project_vollidioten_website.git
synced 2026-05-14 00:16:47 +02:00
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:
91
components/IconSet.tsx
Normal file
91
components/IconSet.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import React from 'react';
|
||||
|
||||
// UI Icons (Lucide-style wrappers)
|
||||
export const Icons = {
|
||||
Home: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
|
||||
),
|
||||
Users: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
||||
),
|
||||
Map: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21"/><line x1="9" x2="9" y1="3" y2="18"/><line x1="15" x2="15" y1="6" y2="21"/></svg>
|
||||
),
|
||||
Layers: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg>
|
||||
),
|
||||
Terminal: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><polyline points="4 17 10 11 4 5"/><line x1="12" x2="20" y1="19" y2="19"/></svg>
|
||||
),
|
||||
Box: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" x2="12" y1="22.08" y2="12"/></svg>
|
||||
),
|
||||
Search: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/></svg>
|
||||
),
|
||||
Crown: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="m2 4 3 12h14l3-12-6 7-4-7-4 7-6-7zm3 16h14"/></svg>
|
||||
),
|
||||
Shield: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
||||
),
|
||||
Coins: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><circle cx="8" cy="8" r="6"/><path d="M18.09 10.37A6 6 0 1 1 10.34 18"/><path d="M7 6h1v4"/><path d="M17.121 6.364l1.414 1.414"/><path d="M15.536 11H13.5"/></svg>
|
||||
),
|
||||
Scroll: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M19 4H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zM4 10h16M4 14h16M4 18h16"/></svg>
|
||||
),
|
||||
ShoppingBag: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>
|
||||
),
|
||||
Tag: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M12 2H2v10l9.29 9.29c.94.94 2.48.94 3.42 0l6.58-6.58c.94-.94.94-2.48 0-3.42L12 2Z"/><path d="M7 7h.01"/></svg>
|
||||
),
|
||||
Hammer: ({ className }: { className?: string }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="m15 12-8.5 8.5c-.83.83-2.17.83-3 0 0 0 0 0 0 0a2.12 2.12 0 0 1 0-3L12 9"/><path d="M17.64 15 22 10.64"/><path d="m20.91 11.7-1.25-1.25c-.6-.6-.93-1.4-.93-2.25V7.86c0-.55-.45-1-1-1H16.4c-.84 0-1.65-.33-2.25-.93L12.9 4.68c-.6-.6-1.4-.93-2.25-.93H4.86c-.55 0-1 .45-1 1v1.36c0 .84.33 1.65.93 2.25L12 15.64"/></svg>
|
||||
)
|
||||
};
|
||||
|
||||
// Algorithmic Minecraft Item Icons (Vector)
|
||||
export const ItemIcon = ({ type, className }: { type: string; className?: string }) => {
|
||||
const commonClasses = `w-full h-full ${className}`;
|
||||
|
||||
if (type === 'tool') {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" className={commonClasses} fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="square">
|
||||
<path d="M24 8 L28 4 L24 8 Z" fill="currentColor" className="text-accentInfo/50" />
|
||||
<path d="M22 10 L26 6" className="text-accentInfo" />
|
||||
<path d="M10 22 L22 10" className="text-textMain" />
|
||||
<path d="M4 28 L8 24" className="text-textMuted" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'block') {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" className={commonClasses} fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M16 2 L2 9 L16 16 L30 9 L16 2 Z" fill="currentColor" className="text-textMuted/20" />
|
||||
<path d="M2 9 V 23 L16 30 V 16" fill="currentColor" className="text-textMuted/40" />
|
||||
<path d="M30 9 V 23 L16 30" fill="currentColor" className="text-textMuted/10" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'consumable') {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" className={commonClasses} fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M10 12 C10 6 22 6 22 12 C22 18 16 26 16 26 C16 26 10 18 10 12" fill="currentColor" className="text-accentWarn/20" />
|
||||
<path d="M16 6 V 4" strokeLinecap="round" />
|
||||
<path d="M18 6 V 3" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
// Misc / Default
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" className={commonClasses} fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<circle cx="16" cy="16" r="10" className="text-textMuted/20" />
|
||||
<path d="M16 6 V 26 M 6 16 H 26" className="text-accentInfo/50" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
63
components/InventoryGrid.tsx
Normal file
63
components/InventoryGrid.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Item } from '../types';
|
||||
import { ItemIcon } from './IconSet';
|
||||
|
||||
interface InventoryGridProps {
|
||||
items: (Item | null)[];
|
||||
}
|
||||
|
||||
const InventoryGrid: React.FC<InventoryGridProps> = ({ items }) => {
|
||||
return (
|
||||
<div className="bg-surface rounded-xl border border-border p-4 shadow-card">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-sm font-bold uppercase tracking-wider text-textMuted">Inventar</h3>
|
||||
<div className="text-xs text-textMuted bg-surfaceHighlight px-2 py-1 rounded">
|
||||
{items.filter(i => i !== null).length} / {items.length} Plätze
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-5 sm:grid-cols-9 gap-2">
|
||||
{items.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="group relative aspect-square bg-surfaceHighlight/50 border border-white/5 rounded-md transition-all duration-200 hover:scale-105 hover:bg-surfaceHighlight hover:border-accentInfo/30 hover:shadow-glow hover:z-10 cursor-pointer flex items-center justify-center"
|
||||
>
|
||||
{item ? (
|
||||
<>
|
||||
<div className="w-8 h-8 text-textMuted group-hover:text-accentInfo transition-colors">
|
||||
<ItemIcon type={item.type} />
|
||||
</div>
|
||||
|
||||
{/* Count Badge */}
|
||||
{item.count > 1 && (
|
||||
<span className="absolute bottom-1 right-1 font-mono text-[10px] font-bold text-textMain leading-none drop-shadow-md">
|
||||
{item.count}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Tooltip */}
|
||||
<div className="absolute opacity-0 group-hover:opacity-100 transition-opacity duration-200 bottom-full left-1/2 -translate-x-1/2 mb-2 w-max max-w-[150px] z-50 pointer-events-none">
|
||||
<div className="bg-surface border border-border rounded p-2 shadow-xl text-xs">
|
||||
<div className={`font-semibold ${item.rarity === 'epic' ? 'text-accentInfo' : 'text-textMain'}`}>
|
||||
{item.name}
|
||||
</div>
|
||||
{item.nbtSummary && (
|
||||
<div className="text-[10px] text-textMuted mt-1 border-t border-white/10 pt-1">
|
||||
{item.nbtSummary}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
// Empty slot styling
|
||||
<div className="absolute inset-2 border border-dashed border-white/5 rounded-sm" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InventoryGrid;
|
||||
126
components/Layout.tsx
Normal file
126
components/Layout.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Icons } from './IconSet';
|
||||
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
activeTab: string;
|
||||
onNavigate: (tab: string) => void;
|
||||
}
|
||||
|
||||
const NavItem = ({
|
||||
active,
|
||||
label,
|
||||
onClick
|
||||
}: {
|
||||
active: boolean;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
}) => (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`text-sm font-medium transition-colors duration-200 px-1 py-4 border-b-2 ${
|
||||
active
|
||||
? 'text-textMain border-accentInfo'
|
||||
: 'text-textMuted border-transparent hover:text-textMain hover:border-border'
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
|
||||
const Layout: React.FC<LayoutProps> = ({ children, activeTab, onNavigate }) => {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col font-sans">
|
||||
{/* Top Header - Website Style */}
|
||||
<header className="sticky top-0 z-40 bg-background/80 backdrop-blur-md border-b border-border">
|
||||
<div className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
|
||||
<div className="flex items-center gap-10">
|
||||
{/* Logo */}
|
||||
<div
|
||||
className="flex items-center gap-3 cursor-pointer group"
|
||||
onClick={() => onNavigate('dashboard')}
|
||||
>
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-accentInfo to-blue-900 rounded flex items-center justify-center shadow-glow group-hover:shadow-lg transition-shadow">
|
||||
<span className="font-bold text-white text-sm">P.V.</span>
|
||||
</div>
|
||||
<span className="font-bold text-lg tracking-tight text-textMain">Projekt: Vollidion</span>
|
||||
</div>
|
||||
|
||||
{/* Desktop Nav */}
|
||||
<nav className="hidden md:flex items-center gap-6 h-full">
|
||||
<NavItem active={activeTab === 'dashboard'} label="Übersicht" onClick={() => onNavigate('dashboard')} />
|
||||
<NavItem active={activeTab === 'cities'} label="Städte" onClick={() => onNavigate('cities')} />
|
||||
<NavItem active={activeTab === 'players'} label="Bürger" onClick={() => onNavigate('players')} />
|
||||
{/* <NavItem active={activeTab === 'organizations'} label="Organisationen" onClick={() => onNavigate('organizations')} />*/}
|
||||
<NavItem active={activeTab === 'projects'} label="Unternehmen" onClick={() => onNavigate('projects')} />
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={() => onNavigate('datapack')}
|
||||
className="hidden md:flex items-center gap-2 text-xs font-medium text-textMain hover:text-accentInfo transition-colors"
|
||||
>
|
||||
<Icons.Box className="w-3 h-3" />
|
||||
<span>Datapack holen</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onNavigate('setup')}
|
||||
className="hidden md:flex items-center gap-2 text-xs font-medium text-textMuted hover:text-accentInfo transition-colors border border-border rounded-full px-4 py-1.5 hover:bg-surfaceHighlight"
|
||||
>
|
||||
<Icons.Terminal className="w-3 h-3" />
|
||||
<span>Admin Setup</span>
|
||||
</button>
|
||||
|
||||
{/* Mobile Menu Toggle */}
|
||||
<button
|
||||
className="md:hidden text-textMuted hover:text-textMain"
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
>
|
||||
<Icons.Layers className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Nav Dropdown */}
|
||||
{mobileMenuOpen && (
|
||||
<div className="md:hidden border-t border-border bg-surface px-6 py-4 space-y-4 shadow-xl">
|
||||
<div onClick={() => { onNavigate('dashboard'); setMobileMenuOpen(false); }} className="block py-2 text-textMuted hover:text-textMain">Übersicht</div>
|
||||
<div onClick={() => { onNavigate('cities'); setMobileMenuOpen(false); }} className="block py-2 text-textMuted hover:text-textMain">Städte</div>
|
||||
<div onClick={() => { onNavigate('players'); setMobileMenuOpen(false); }} className="block py-2 text-textMuted hover:text-textMain">Bürger</div>
|
||||
<div onClick={() => { onNavigate('organizations'); setMobileMenuOpen(false); }} className="block py-2 text-textMuted hover:text-textMain">Organisationen</div>
|
||||
<div onClick={() => { onNavigate('projects'); setMobileMenuOpen(false); }} className="block py-2 text-textMuted hover:text-textMain">Unternehmen</div>
|
||||
<div onClick={() => { onNavigate('datapack'); setMobileMenuOpen(false); }} className="block py-2 text-textMain">Datapack holen</div>
|
||||
<div onClick={() => { onNavigate('setup'); setMobileMenuOpen(false); }} className="block py-2 text-accentInfo font-mono text-sm border-t border-white/5 pt-4">Admin Setup >_</div>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{/* Main Content - Page Flow */}
|
||||
<main className="flex-1 w-full max-w-7xl mx-auto px-6 py-12 md:py-16">
|
||||
<div className="animate-in fade-in duration-700 slide-in-from-bottom-4">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Footer - Adds to the "Website" feel */}
|
||||
<footer className="border-t border-border mt-auto bg-surface/30">
|
||||
<div className="max-w-7xl mx-auto px-6 py-10 flex flex-col md:flex-row justify-between items-center text-sm text-textMuted">
|
||||
<div className="flex items-center gap-2 mb-4 md:mb-0 opacity-50">
|
||||
<div className="w-4 h-4 bg-textMuted rounded-full"></div>
|
||||
<p>© 2024 Obsidian Platform</p>
|
||||
</div>
|
||||
<div className="flex gap-8">
|
||||
<span className="cursor-pointer hover:text-textMain transition-colors">Dokumentation</span>
|
||||
<span className="cursor-pointer hover:text-textMain transition-colors">Server Status</span>
|
||||
<span className="cursor-pointer hover:text-textMain transition-colors">Datenschutz</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
Reference in New Issue
Block a user