Files
picpeak/frontend/src/components/admin/AdminHeader.tsx
T
paul 225d017718 Fix multiple production issues and add password change functionality
- Fixed frontend API URL configuration to use correct port 3002
- Fixed create event functionality by adding proper endpoint and fixing JSON parsing
- Fixed email settings save functionality by importing logActivity correctly
- Fixed admin settings save functionality by using api client instead of direct fetch
- Implemented password change functionality with modal and backend endpoint
- Added updated_at column to admin_users table
- Fixed all mock data issues - now using real backend data throughout

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-07 10:25:38 +02:00

172 lines
6.7 KiB
TypeScript

import React, { useState, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { Menu, User, LogOut, Settings, Bell, Lock } from 'lucide-react';
import { format } from 'date-fns';
import { useAdminAuth } from '../../contexts';
import { useOnClickOutside } from '../../hooks/useOnClickOutside';
import { PasswordChangeModal } from './PasswordChangeModal';
interface AdminHeaderProps {
onMenuClick: () => void;
}
export const AdminHeader: React.FC<AdminHeaderProps> = ({ onMenuClick }) => {
const navigate = useNavigate();
const { user, logout } = useAdminAuth();
const [showUserMenu, setShowUserMenu] = useState(false);
const [showNotifications, setShowNotifications] = useState(false);
const [showPasswordModal, setShowPasswordModal] = useState(false);
const userMenuRef = useRef<HTMLDivElement>(null);
const notificationRef = useRef<HTMLDivElement>(null);
useOnClickOutside(userMenuRef, () => setShowUserMenu(false));
useOnClickOutside(notificationRef, () => setShowNotifications(false));
const handleLogout = () => {
logout();
navigate('/admin/login');
};
// Mock notifications
const notifications = [
{
id: 1,
type: 'warning',
message: '3 events expiring in the next 7 days',
time: new Date(),
},
{
id: 2,
type: 'success',
message: 'Wedding Smith-Jones archived successfully',
time: new Date(Date.now() - 3600000),
},
];
return (
<header className="sticky top-0 z-30 bg-white border-b border-neutral-200">
<div className="px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
{/* Mobile menu button */}
<button
onClick={onMenuClick}
className="lg:hidden text-neutral-500 hover:text-neutral-700"
>
<Menu className="w-6 h-6" />
</button>
{/* Desktop breadcrumb or page title could go here */}
<div className="hidden lg:block">
<h2 className="text-lg font-semibold text-neutral-900">
{format(new Date(), 'EEEE, MMMM d, yyyy')}
</h2>
</div>
{/* Right side actions */}
<div className="flex items-center gap-3">
{/* Notifications */}
<div className="relative" ref={notificationRef}>
<button
onClick={() => setShowNotifications(!showNotifications)}
className="relative p-2 text-neutral-500 hover:text-neutral-700 hover:bg-neutral-100 rounded-lg transition-colors"
>
<Bell className="w-5 h-5" />
{notifications.length > 0 && (
<span className="absolute top-1 right-1 w-2 h-2 bg-red-500 rounded-full" />
)}
</button>
{/* Notifications dropdown */}
{showNotifications && (
<div className="absolute right-0 mt-2 w-80 bg-white rounded-lg shadow-lg border border-neutral-200 py-2">
<div className="px-4 py-2 border-b border-neutral-100">
<h3 className="text-sm font-semibold text-neutral-900">Notifications</h3>
</div>
<div className="max-h-96 overflow-y-auto">
{notifications.map((notification) => (
<div
key={notification.id}
className="px-4 py-3 hover:bg-neutral-50 cursor-pointer"
>
<p className="text-sm text-neutral-900">{notification.message}</p>
<p className="text-xs text-neutral-500 mt-1">
{format(notification.time, 'h:mm a')}
</p>
</div>
))}
</div>
<div className="px-4 py-2 border-t border-neutral-100">
<button className="text-sm text-primary-600 hover:text-primary-700">
View all notifications
</button>
</div>
</div>
)}
</div>
{/* User menu */}
<div className="relative" ref={userMenuRef}>
<button
onClick={() => setShowUserMenu(!showUserMenu)}
className="flex items-center gap-3 p-2 hover:bg-neutral-100 rounded-lg transition-colors"
>
<div className="text-right hidden sm:block">
<p className="text-sm font-medium text-neutral-900">{user?.username}</p>
<p className="text-xs text-neutral-500">{user?.email}</p>
</div>
<div className="w-8 h-8 bg-primary-600 rounded-full flex items-center justify-center">
<User className="w-5 h-5 text-white" />
</div>
</button>
{/* User dropdown */}
{showUserMenu && (
<div className="absolute right-0 mt-2 w-56 bg-white rounded-lg shadow-lg border border-neutral-200 py-1">
<div className="px-4 py-2 border-b border-neutral-100 sm:hidden">
<p className="text-sm font-medium text-neutral-900">{user?.username}</p>
<p className="text-xs text-neutral-500">{user?.email}</p>
</div>
<button
onClick={() => {
setShowUserMenu(false);
navigate('/admin/settings');
}}
className="w-full px-4 py-2 text-left text-sm text-neutral-700 hover:bg-neutral-50 flex items-center gap-3"
>
<Settings className="w-4 h-4" />
Settings
</button>
<button
onClick={() => {
setShowUserMenu(false);
setShowPasswordModal(true);
}}
className="w-full px-4 py-2 text-left text-sm text-neutral-700 hover:bg-neutral-50 flex items-center gap-3"
>
<Lock className="w-4 h-4" />
Change Password
</button>
<button
onClick={handleLogout}
className="w-full px-4 py-2 text-left text-sm text-neutral-700 hover:bg-neutral-50 flex items-center gap-3"
>
<LogOut className="w-4 h-4" />
Sign Out
</button>
</div>
)}
</div>
</div>
</div>
</div>
{/* Password Change Modal */}
<PasswordChangeModal
isOpen={showPasswordModal}
onClose={() => setShowPasswordModal(false)}
/>
</header>
);
};