Implement complete frontend with admin panel and theme system

- Add admin authentication and dashboard
- Create event management pages (list, create, edit, archive)
- Implement gallery enhancements (search, sorting, bulk download)
- Add email configuration and archive management pages
- Integrate Umami analytics with tracking throughout the app
- Add comprehensive error boundaries and loading states
- Implement accessibility features (WCAG 2.1 AA compliance)
- Create theme system with preset themes and customization
- Add branding settings and company information management
- Fix backend database initialization and health check
- Configure proper API URLs and environment variables

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-06 22:04:45 +02:00
parent 6c82958c79
commit 28632e8970
53 changed files with 13843 additions and 181 deletions
@@ -0,0 +1,105 @@
import React from 'react';
import { NavLink, useLocation } from 'react-router-dom';
import {
LayoutDashboard,
Calendar,
Mail,
Archive,
BarChart3,
Settings,
Camera,
X,
Palette
} from 'lucide-react';
interface AdminSidebarProps {
isOpen: boolean;
onClose: () => void;
}
interface NavItem {
name: string;
href: string;
icon: React.ComponentType<{ className?: string }>;
}
const navigation: NavItem[] = [
{ name: 'Dashboard', href: '/admin/dashboard', icon: LayoutDashboard },
{ name: 'Events', href: '/admin/events', icon: Calendar },
{ name: 'Archives', href: '/admin/archives', icon: Archive },
{ name: 'Analytics', href: '/admin/analytics', icon: BarChart3 },
{ name: 'Email Settings', href: '/admin/email', icon: Mail },
{ name: 'Branding', href: '/admin/branding', icon: Palette },
{ name: 'Settings', href: '/admin/settings', icon: Settings },
];
export const AdminSidebar: React.FC<AdminSidebarProps> = ({ isOpen, onClose }) => {
const location = useLocation();
return (
<div
className={`fixed inset-y-0 left-0 z-50 w-64 bg-white border-r border-neutral-200 transform transition-transform duration-200 ease-in-out lg:translate-x-0 lg:static ${
isOpen ? 'translate-x-0' : '-translate-x-full'
}`}
>
<div className="flex flex-col h-full">
{/* Logo/Brand */}
<div className="flex items-center justify-between h-16 px-6 border-b border-neutral-200">
<div className="flex items-center">
<Camera className="w-8 h-8 text-primary-600" />
<span className="ml-2 text-xl font-bold text-neutral-900">Photo Admin</span>
</div>
<button
onClick={onClose}
className="lg:hidden text-neutral-400 hover:text-neutral-600"
>
<X className="w-6 h-6" />
</button>
</div>
{/* Navigation */}
<nav className="flex-1 px-4 py-4 space-y-1 overflow-y-auto">
{navigation.map((item) => {
const isActive = location.pathname === item.href ||
(item.href !== '/admin/dashboard' && location.pathname.startsWith(item.href));
return (
<NavLink
key={item.name}
to={item.href}
onClick={() => onClose()}
className={`flex items-center px-3 py-2 text-sm font-medium rounded-lg transition-colors ${
isActive
? 'bg-primary-50 text-primary-700'
: 'text-neutral-700 hover:bg-neutral-100 hover:text-neutral-900'
}`}
>
<item.icon className={`w-5 h-5 mr-3 ${
isActive ? 'text-primary-600' : 'text-neutral-400'
}`} />
{item.name}
</NavLink>
);
})}
</nav>
{/* Storage Info */}
<div className="p-4 border-t border-neutral-200">
<div className="bg-neutral-100 rounded-lg p-3">
<div className="flex items-center justify-between text-sm">
<span className="text-neutral-700">Storage Used</span>
<span className="font-medium text-neutral-900">2.4 GB</span>
</div>
<div className="mt-2 w-full bg-neutral-200 rounded-full h-2">
<div
className="bg-primary-600 h-2 rounded-full transition-all duration-300"
style={{ width: '24%' }}
/>
</div>
<p className="text-xs text-neutral-600 mt-1">24% of 10 GB</p>
</div>
</div>
</div>
</div>
);
};