migrants-nt-web/src/components/AdminDashboard.tsx

361 lines
14 KiB
TypeScript

"use client"
import { useEffect, useState } from "react"
import { useNavigate } from "react-router-dom"
import { motion } from "framer-motion"
import {
Users,
Database,
FileText,
Settings,
BarChart2,
Calendar,
Clock,
PlusCircle,
Search,
User,
Bell,
Home,
} from "lucide-react"
import { Link } from "react-router-dom"
export default function AdminDashboard() {
const [isFirstLoad, setIsFirstLoad] = useState(true)
const [isProfileDropdownOpen, setIsProfileDropdownOpen] = useState(false)
const navigate = useNavigate()
useEffect(() => {
// Check if user is authenticated
const token = localStorage.getItem("adminToken")
if (!token) {
navigate("/admin/login")
return
}
// Check if we're navigating from another admin page
const hasVisitedAdmin = localStorage.getItem("adminNavigation")
// Skip loading if we're navigating from another admin page
if (hasVisitedAdmin) {
setIsFirstLoad(false)
} else {
// Set flag to indicate we've visited an admin page
localStorage.setItem("adminNavigation", "true")
// Only show loading state on first load
if (isFirstLoad) {
// Simulate loading dashboard data
const timer = setTimeout(() => {
setIsFirstLoad(false)
// Show welcome toast
}, 500) // Reduced loading time for better UX
return () => clearTimeout(timer)
}
}
}, [isFirstLoad, navigate])
const handleLogout = () => {
localStorage.removeItem("adminToken")
localStorage.removeItem("adminNavigation") // Clear navigation flag on logout
navigate("/admin/login")
}
// If it's the first load, show a loading state
if (isFirstLoad) {
return (
<div className="min-h-screen flex items-center justify-center bg-[#E8DCCA]/10">
<div className="text-center">
<div className="w-16 h-16 border-4 border-[#9B2335] border-t-transparent rounded-full animate-spin mx-auto"></div>
<p className="mt-4 text-gray-600">Loading dashboard...</p>
</div>
</div>
)
}
return (
<div className="min-h-screen flex bg-[#E8DCCA]/10">
{/* Sidebar */}
<div className="w-64 bg-[#1A2A57] text-white">
<div className="p-4 border-b border-[#1A2A57]/30">
<h2 className="text-xl font-serif font-bold">Italian Migrants</h2>
<p className="text-sm text-white/70">Northern Territory DB</p>
</div>
<nav className="mt-6 px-4">
<div className="space-y-1">
<Link to="/admin" className="flex items-center px-4 py-3 text-white bg-[#1A2A57]/40 rounded-md">
<Home className="h-5 w-5 mr-3" />
Dashboard
</Link>
<Link
to="/admin/migrants"
className="flex items-center px-4 py-3 text-white/80 hover:bg-[#1A2A57]/40 rounded-md"
>
<User className="h-5 w-5 mr-3" />
Migrants
</Link>
<Link to="#" className="flex items-center px-4 py-3 text-white/80 hover:bg-[#1A2A57]/40 rounded-md">
<FileText className="h-5 w-5 mr-3" />
Reports
</Link>
<Link to="#" className="flex items-center px-4 py-3 text-white/80 hover:bg-[#1A2A57]/40 rounded-md">
<Database className="h-5 w-5 mr-3" />
Database
</Link>
<Link
to="/admin/settings/profile"
className="flex items-center px-4 py-3 text-white/80 hover:bg-[#1A2A57]/40 rounded-md"
>
<Settings className="h-5 w-5 mr-3" />
Settings
</Link>
</div>
</nav>
<div className="absolute bottom-0 w-64 p-4 border-t border-[#1A2A57]/30">
<div className="flex items-center">
<div className="h-8 w-8 rounded-full bg-white text-[#1A2A57] flex items-center justify-center">
<User className="h-5 w-5" />
</div>
<span className="ml-2 text-sm">Admin User</span>
</div>
</div>
</div>
{/* Main content */}
<div className="flex-1 overflow-auto">
{/* Header */}
<div className="bg-white shadow-sm border-b border-gray-200">
<div className="flex justify-between items-center px-6 py-4">
<h1 className="text-xl font-medium text-[#1A2A57]">Admin Portal</h1>
<div className="flex items-center space-x-4">
<div className="relative">
<input
type="text"
placeholder="Search records..."
className="pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#01796F] focus:border-[#01796F]"
/>
<Search className="absolute left-3 top-2.5 h-5 w-5 text-gray-400" />
</div>
{/* Notifications */}
<button
className="relative p-2 text-gray-500 hover:text-gray-700 focus:outline-none"
>
<Bell className="h-5 w-5" />
<span className="absolute top-0 right-0 h-2 w-2 rounded-full bg-[#9B2335]"></span>
</button>
{/* Profile dropdown */}
<div className="relative">
<button
className="h-8 w-8 rounded-full bg-[#9B2335] text-white flex items-center justify-center focus:outline-none"
onClick={() => setIsProfileDropdownOpen(!isProfileDropdownOpen)}
>
<User className="h-4 w-4" />
</button>
{isProfileDropdownOpen && (
<motion.div
className="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-10"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2 }}
>
<Link
to="/admin/settings/profile"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={() => setIsProfileDropdownOpen(false)}
>
Your Profile
</Link>
<Link
to="#"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
>
Settings
</Link>
<button
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={handleLogout}
>
Sign out
</button>
</motion.div>
)}
</div>
</div>
</div>
</div>
{/* Dashboard content */}
<div className="p-6">
{/* Stats cards */}
<motion.div
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
>
<div className="bg-white rounded-lg shadow p-6 border-t-4 border-[#9B2335]">
<div className="flex justify-between items-start">
<div>
<p className="text-gray-500 text-sm">Total Migrants</p>
<h3 className="text-3xl font-bold mt-1">256</h3>
</div>
<div className="p-2 bg-[#9B2335]/10 rounded-md">
<Users className="h-6 w-6 text-[#9B2335]" />
</div>
</div>
<div className="mt-4 text-sm text-green-600">+12 this month</div>
</div>
<div className="bg-white rounded-lg shadow p-6 border-t-4 border-[#01796F]">
<div className="flex justify-between items-start">
<div>
<p className="text-gray-500 text-sm">Total Records</p>
<h3 className="text-3xl font-bold mt-1">1,248</h3>
</div>
<div className="p-2 bg-[#01796F]/10 rounded-md">
<Database className="h-6 w-6 text-[#01796F]" />
</div>
</div>
<div className="mt-4 text-sm text-green-600">+86 this month</div>
</div>
<div className="bg-white rounded-lg shadow p-6 border-t-4 border-[#FFC857]">
<div className="flex justify-between items-start">
<div>
<p className="text-gray-500 text-sm">Recent Updates</p>
<h3 className="text-3xl font-bold mt-1">24</h3>
</div>
<div className="p-2 bg-[#FFC857]/10 rounded-md">
<Clock className="h-6 w-6 text-[#FFC857]" />
</div>
</div>
<div className="mt-4 text-sm text-blue-600">Last update: Today</div>
</div>
<div className="bg-white rounded-lg shadow p-6 border-t-4 border-[#1A2A57]">
<div className="flex justify-between items-start">
<div>
<p className="text-gray-500 text-sm">Pending Tasks</p>
<h3 className="text-3xl font-bold mt-1">8</h3>
</div>
<div className="p-2 bg-[#1A2A57]/10 rounded-md">
<Calendar className="h-6 w-6 text-[#1A2A57]" />
</div>
</div>
<div className="mt-4 text-sm text-orange-500">3 require attention</div>
</div>
</motion.div>
{/* Quick actions */}
<motion.div
className="bg-white rounded-lg shadow mb-8"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.3 }}
>
<div className="px-6 py-4 border-b border-gray-200">
<h2 className="text-lg font-medium">Quick Actions</h2>
</div>
<div className="p-6 grid grid-cols-1 md:grid-cols-3 gap-4">
<Link
to="/admin/migrants"
className="flex items-center justify-center px-4 py-3 bg-[#9B2335] text-white rounded-md hover:bg-[#9B2335]/90"
>
<PlusCircle className="h-5 w-5 mr-2" />
Add New Migrant
</Link>
<button
className="flex items-center justify-center px-4 py-3 bg-[#01796F] text-white rounded-md hover:bg-[#01796F]/90"
>
<FileText className="h-5 w-5 mr-2" />
Generate Report
</button>
<button
className="flex items-center justify-center px-4 py-3 bg-[#1A2A57] text-white rounded-md hover:bg-[#1A2A57]/90"
>
<Database className="h-5 w-5 mr-2" />
Import Records
</button>
</div>
</motion.div>
{/* Recent activity */}
<motion.div
className="bg-white rounded-lg shadow"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.4 }}
>
<div className="px-6 py-4 border-b border-gray-200">
<h2 className="text-lg font-medium">Recent Activity</h2>
</div>
<div className="p-6">
<div className="space-y-6">
{[
{ action: "Added new migrant", user: "Admin", time: "10 minutes ago", type: "add" },
{ action: "Updated record #1248", user: "Admin", time: "2 hours ago", type: "update" },
{ action: "Generated monthly report", user: "System", time: "Yesterday", type: "report" },
{ action: "Deleted duplicate record", user: "Admin", time: "2 days ago", type: "delete" },
{ action: "Imported 15 new records", user: "Admin", time: "1 week ago", type: "import" },
].map((activity, index) => (
<motion.div
key={index}
className="flex items-start"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.5 + index * 0.1 }}
>
<div
className={`p-2 rounded-full mr-4 ${
activity.type === "add"
? "bg-green-100 text-green-600"
: activity.type === "update"
? "bg-blue-100 text-blue-600"
: activity.type === "delete"
? "bg-red-100 text-red-600"
: activity.type === "report"
? "bg-purple-100 text-purple-600"
: "bg-yellow-100 text-yellow-600"
}`}
>
{activity.type === "add" ? (
<PlusCircle className="h-5 w-5" />
) : activity.type === "update" ? (
<FileText className="h-5 w-5" />
) : activity.type === "delete" ? (
<Users className="h-5 w-5" />
) : activity.type === "report" ? (
<BarChart2 className="h-5 w-5" />
) : (
<Database className="h-5 w-5" />
)}
</div>
<div>
<p className="font-medium">{activity.action}</p>
<p className="text-sm text-gray-500">
By {activity.user} {activity.time}
</p>
</div>
</motion.div>
))}
</div>
</div>
</motion.div>
</div>
</div>
</div>
)
}