All checks were successful
Build and Push Docker Images / docker (push) Successful in 2m15s
🎨 Complete UI redesign of admin panel with professional color scheme: ## New Modern Maritime Color Palette - Deep Sea Blue (#1D2939) - Sidebar background - Sail White (#F8F9FA) - Main background - Maritime Teal (#008B8B) - Primary accent - Sea Green (#1E8449) - Success/environmental theme - Muted Gold (#D68910) - Revenue highlights - Royal Purple (#884EA0) - Brand accent - Off-White (#EAECEF) - Text on dark backgrounds ## Admin Panel Features - ✅ JWT-based authentication system - ✅ Protected routes with middleware - ✅ Elegant sidebar navigation with Puffin logo - ✅ Dashboard with stat cards (Orders, CO₂, Revenue, Fulfillment) - ✅ Monaco harbor image background on login page - ✅ Responsive glassmorphism design - ✅ WCAG AA contrast compliance ## New Files - app/admin/ - Admin pages (login, dashboard, orders) - app/api/admin/ - Auth API routes (login, logout, verify) - components/admin/ - AdminSidebar component - lib/auth.ts - JWT authentication utilities - public/monaco_high_res.jpg - Luxury background image ## Updated - tailwind.config.js - Custom maritime color palette - package.json - Added jsonwebtoken dependency - app/layout.tsx - RootLayoutClient integration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
112 lines
3.4 KiB
TypeScript
112 lines
3.4 KiB
TypeScript
'use client';
|
|
|
|
import { useRouter, usePathname } from 'next/navigation';
|
|
import { LayoutDashboard, Package, LogOut } from 'lucide-react';
|
|
import { motion } from 'framer-motion';
|
|
import Image from 'next/image';
|
|
|
|
interface NavItem {
|
|
label: string;
|
|
href: string;
|
|
icon: React.ReactNode;
|
|
}
|
|
|
|
export function AdminSidebar() {
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
|
|
const navItems: NavItem[] = [
|
|
{
|
|
label: 'Dashboard',
|
|
href: '/admin/dashboard',
|
|
icon: <LayoutDashboard size={20} />,
|
|
},
|
|
{
|
|
label: 'Orders',
|
|
href: '/admin/orders',
|
|
icon: <Package size={20} />,
|
|
},
|
|
];
|
|
|
|
const handleLogout = async () => {
|
|
try {
|
|
await fetch('/api/admin/auth/logout', {
|
|
method: 'POST',
|
|
});
|
|
router.push('/admin/login');
|
|
} catch (error) {
|
|
console.error('Logout failed:', error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="w-64 min-h-screen bg-deep-sea-blue text-off-white flex flex-col fixed left-0 top-0 bottom-0 shadow-2xl">
|
|
{/* Logo */}
|
|
<div className="p-6 border-b border-off-white/10">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
className="flex items-center space-x-3"
|
|
>
|
|
<div className="w-10 h-10 bg-white rounded-full flex items-center justify-center shadow-lg p-1">
|
|
<Image
|
|
src="/puffinOffset.png"
|
|
alt="Puffin Offset Logo"
|
|
width={32}
|
|
height={32}
|
|
className="object-contain"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<h1 className="font-bold text-lg text-off-white">Puffin Admin</h1>
|
|
<p className="text-xs text-off-white/80 font-medium">Management Portal</p>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 px-4 py-6 space-y-2">
|
|
{navItems.map((item, index) => {
|
|
const isActive = pathname === item.href;
|
|
|
|
return (
|
|
<motion.button
|
|
key={item.href}
|
|
initial={{ opacity: 0, x: -20 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
transition={{ delay: index * 0.1 }}
|
|
onClick={() => router.push(item.href)}
|
|
className={`w-full flex items-center space-x-3 px-4 py-3 rounded-lg transition-all ${
|
|
isActive
|
|
? 'bg-maritime-teal text-white shadow-lg font-semibold'
|
|
: 'text-off-white/80 hover:bg-maritime-teal/20 hover:text-off-white font-medium'
|
|
}`}
|
|
>
|
|
{item.icon}
|
|
<span>{item.label}</span>
|
|
</motion.button>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* User Info & Logout */}
|
|
<div className="p-4 border-t border-off-white/10 space-y-2">
|
|
<div className="px-4 py-2 bg-off-white/5 rounded-lg">
|
|
<p className="text-xs text-off-white/70 font-medium">Signed in as</p>
|
|
<p className="text-sm font-semibold text-off-white">Administrator</p>
|
|
</div>
|
|
|
|
<motion.button
|
|
whileHover={{ scale: 1.02 }}
|
|
whileTap={{ scale: 0.98 }}
|
|
onClick={handleLogout}
|
|
className="w-full flex items-center space-x-3 px-4 py-3 rounded-lg bg-red-500/20 hover:bg-red-500/30 text-red-200 hover:text-white transition-all font-medium"
|
|
>
|
|
<LogOut size={20} />
|
|
<span>Logout</span>
|
|
</motion.button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|