lad-website/frontend/src/app/admin/dashboard/page.tsx
TheMaddax 742da52659 Complete admin dashboard CSS migration with high contrast theme fixes
 Security separation: Move admin styles to secure admin.css (auth-only)
 CSS cleanup: Clean globals.css for public access only
 High contrast fixes: Resolve button visibility and CTA backgrounds
 Menu highlighting: Implement CSS variable-based approach with gold accents
 Accessibility: Maintain WCAG AAA compliance across all four themes
 Architecture: Semantic CSS classes replace inline dark: classes

- Created frontend/src/styles/admin.css with secure admin-only styles
- Cleaned frontend/src/app/globals.css removing 1200+ lines of admin code
- Updated admin components to use semantic CSS classes
- Fixed high contrast active menu highlighting with gold borders
- Implemented CSS containment and injection prevention
- All admin dashboard migration objectives achieved
2025-06-02 20:59:37 -06:00

266 lines
9.5 KiB
TypeScript

import React from 'react';
import Link from 'next/link';
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Admin Dashboard | Olathe Club of the Deaf',
description: 'Admin dashboard for the Olathe Club of the Deaf website',
};
// Stats Card Component
const StatCard = ({ title, value, icon, href }: { title: string; value: string; icon: React.ReactNode; href: string }) => (
<Link href={href} className="admin-stat-card admin-gold-accent hover:shadow-md transition-shadow">
<div className="flex items-center">
<div className="admin-stat-icon">
{icon}
</div>
<div className="ml-5">
<p className="admin-stat-title">{title}</p>
<p className="admin-stat-value">{value}</p>
</div>
</div>
</Link>
);
// Simple icons
const MembersIcon = () => (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
);
const EventsIcon = () => (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
);
const ContactIcon = () => (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
);
const VideosIcon = () => (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
);
export default function AdminDashboardPage() {
// In a real app, these would be fetched from the API
const stats = {
members: "47",
activeMembers: "41",
events: "12",
videos: "8",
documents: "15",
unreadContacts: "3"
};
// Upcoming events - in a real app, these would be fetched from the API
const upcomingEvents = [
{
id: '1',
title: 'Monthly Social',
date: 'Apr 15, 2025',
location: 'Olathe Community Center',
status: 'Published'
},
{
id: '2',
title: 'ASL Workshop',
date: 'Apr 22, 2025',
location: 'OCD Meeting Room',
status: 'Draft'
},
{
id: '3',
title: 'Board Meeting',
date: 'May 5, 2025',
location: 'Zoom Meeting',
status: 'Published'
}
];
// Membership expirations - in a real app, these would be fetched from the API
const expiringMemberships = [
{
id: '101',
name: 'John Smith',
expiryDate: 'Apr 30, 2025',
type: 'Regular',
status: 'Expiring Soon'
},
{
id: '102',
name: 'Sarah Johnson',
expiryDate: 'May 15, 2025',
type: 'Family',
status: 'Expiring Soon'
},
{
id: '103',
name: 'Robert Lee',
expiryDate: 'Mar 28, 2025',
type: 'Regular',
status: 'Expired'
}
];
return (
<div>
<div className="mb-8">
<h2 className="admin-page-title text-lg">Overview</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
<StatCard
title="Total Members"
value={stats.members}
icon={<MembersIcon />}
href="/admin/members"
/>
<StatCard
title="Active Members"
value={stats.activeMembers}
icon={<MembersIcon />}
href="/admin/members/active"
/>
<StatCard
title="Upcoming Events"
value={stats.events}
icon={<EventsIcon />}
href="/admin/events"
/>
<StatCard
title="Unread Messages"
value={stats.unreadContacts}
icon={<ContactIcon />}
href="/admin/contact"
/>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
{/* Upcoming Events */}
<div className="admin-card admin-gold-accent">
<div className="admin-card-header">
<h3 className="admin-card-title admin-gold-title">Upcoming Events</h3>
<Link href="/admin/events" className="admin-card-link">
View All
</Link>
</div>
<div className="admin-list">
{upcomingEvents.map((event) => (
<div key={event.id} className="admin-list-item">
<div className="flex items-center justify-between">
<div>
<h4 className="admin-list-title">{event.title}</h4>
<p className="admin-list-subtitle">{event.date} {event.location}</p>
</div>
<span className={`admin-badge ${
event.status === 'Published'
? 'admin-badge-success'
: 'admin-badge-default'
}`}>
{event.status}
</span>
</div>
</div>
))}
</div>
<div className="mt-4">
<Link
href="/admin/events/create"
className="btn-primary inline-flex items-center text-sm py-2 px-4"
>
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
Add Event
</Link>
</div>
</div>
{/* Membership Expirations */}
<div className="admin-card admin-gold-accent">
<div className="admin-card-header">
<h3 className="admin-card-title admin-gold-title">Membership Expirations</h3>
<Link href="/admin/members" className="admin-card-link">
View All
</Link>
</div>
<div className="admin-list">
{expiringMemberships.map((member) => (
<div key={member.id} className="admin-list-item">
<div className="flex items-center justify-between">
<div>
<h4 className="admin-list-title">{member.name}</h4>
<p className="admin-list-subtitle">{member.expiryDate} {member.type}</p>
</div>
<span className={`admin-badge ${
member.status === 'Expired'
? 'admin-badge-error'
: 'admin-badge-warning'
}`}>
{member.status}
</span>
</div>
</div>
))}
</div>
<div className="mt-4">
<Link
href="/admin/members/notify"
className="btn-primary inline-flex items-center text-sm py-2 px-4"
>
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
Send Renewal Notices
</Link>
</div>
</div>
</div>
{/* Quick Actions */}
<div className="mb-8">
<h3 className="admin-page-title text-lg">Quick Actions</h3>
<div className="admin-quick-actions">
<Link href="/admin/events/create" className="admin-quick-action">
<div className="admin-quick-action-icon">
<EventsIcon />
</div>
<span className="admin-quick-action-text">New Event</span>
</Link>
<Link href="/admin/members/add" className="admin-quick-action">
<div className="admin-quick-action-icon">
<MembersIcon />
</div>
<span className="admin-quick-action-text">Add Member</span>
</Link>
<Link href="/admin/videos/upload" className="admin-quick-action">
<div className="admin-quick-action-icon">
<VideosIcon />
</div>
<span className="admin-quick-action-text">Upload Video</span>
</Link>
<Link href="/admin/content/edit" className="admin-quick-action">
<div className="admin-quick-action-icon">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</div>
<span className="admin-quick-action-text">Edit Pages</span>
</Link>
</div>
</div>
</div>
);
}