diff --git a/package-lock.json b/package-lock.json index 07aa04b..e58312e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@radix-ui/react-label": "^2.1.6", "@radix-ui/react-select": "^2.2.4", "@radix-ui/react-slot": "^1.2.2", + "@radix-ui/react-toast": "^1.2.13", "@tailwindcss/vite": "^4.1.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -1458,6 +1459,39 @@ } } }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.13.tgz", + "integrity": "sha512-e/e43mQAwgYs8BY4y9l99xTK6ig1bK2uXsFLOMn9IZ16lAgulSTsotcPHVT2ZlSb/ye6Sllq7IgyDB8dGhpeXQ==", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", diff --git a/package.json b/package.json index db6675f..3df0450 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-label": "^2.1.6", "@radix-ui/react-select": "^2.2.4", "@radix-ui/react-slot": "^1.2.2", + "@radix-ui/react-toast": "^1.2.13", "@tailwindcss/vite": "^4.1.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/src/App.tsx b/src/App.tsx index da80843..8ec8cbc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,11 +3,19 @@ import HomePage from "./pages/HomePage"; import MigrantProfilePage from "./pages/MigrantProfilePage"; import NotFoundPage from "./pages/NotFoundPage"; import "./App.css"; +import LoginPage from "./components/LoginPage"; +import Migrants from "./components/Migrants"; +import ProfileSettings from "./components/ui/ProfileSettings"; +import AdminDashboardPage from "./pages/AdminDashboardPage"; function App() { return ( + } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/src/components/AdminDashboard.tsx b/src/components/AdminDashboard.tsx new file mode 100644 index 0000000..bc2e9b7 --- /dev/null +++ b/src/components/AdminDashboard.tsx @@ -0,0 +1,360 @@ +"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 ( +
+
+
+

Loading dashboard...

+
+
+ ) + } + + return ( +
+ {/* Sidebar */} +
+
+

Italian Migrants

+

Northern Territory DB

+
+ + + +
+
+
+ +
+ Admin User +
+
+
+ + {/* Main content */} +
+ {/* Header */} +
+
+

Admin Portal

+
+
+ + +
+ + {/* Notifications */} + + + {/* Profile dropdown */} +
+ + + {isProfileDropdownOpen && ( + + setIsProfileDropdownOpen(false)} + > + Your Profile + + + Settings + + + + )} +
+
+
+
+ + {/* Dashboard content */} +
+ {/* Stats cards */} + +
+
+
+

Total Migrants

+

256

+
+
+ +
+
+
+12 this month
+
+ +
+
+
+

Total Records

+

1,248

+
+
+ +
+
+
+86 this month
+
+ +
+
+
+

Recent Updates

+

24

+
+
+ +
+
+
Last update: Today
+
+ +
+
+
+

Pending Tasks

+

8

+
+
+ +
+
+
3 require attention
+
+
+ + {/* Quick actions */} + +
+

Quick Actions

+
+
+ + + Add New Migrant + + + +
+
+ + {/* Recent activity */} + +
+

Recent Activity

+
+
+
+ {[ + { 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) => ( + +
+ {activity.type === "add" ? ( + + ) : activity.type === "update" ? ( + + ) : activity.type === "delete" ? ( + + ) : activity.type === "report" ? ( + + ) : ( + + )} +
+
+

{activity.action}

+

+ By {activity.user} • {activity.time} +

+
+
+ ))} +
+
+
+
+
+
+ ) +} diff --git a/src/components/AdminDashboard/DashboardLayout.tsx b/src/components/AdminDashboard/DashboardLayout.tsx new file mode 100644 index 0000000..57c6485 --- /dev/null +++ b/src/components/AdminDashboard/DashboardLayout.tsx @@ -0,0 +1,21 @@ +import Sidebar from "./Sidebar" +import Header from "./Header" +import StatsCards from "./StatsCards" +import QuickActions from "./QuickActions" +import RecentActivity from "./RecentActivity" + +export default function DashboardLayout() { + return ( +
+ +
+
+
+ + + +
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/AdminDashboard/Header.tsx b/src/components/AdminDashboard/Header.tsx new file mode 100644 index 0000000..9050f1f --- /dev/null +++ b/src/components/AdminDashboard/Header.tsx @@ -0,0 +1,25 @@ +import { Search, Bell, ChevronDown } from "lucide-react" + +export default function Header() { + return ( +
+
+ + +
+
+ +
+
+ A +
+ +
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/AdminDashboard/QuickActions.tsx b/src/components/AdminDashboard/QuickActions.tsx new file mode 100644 index 0000000..e562b69 --- /dev/null +++ b/src/components/AdminDashboard/QuickActions.tsx @@ -0,0 +1,40 @@ +import { motion } from "framer-motion" +import { Link } from "react-router-dom" +import { PlusCircle, FileText, Database } from "lucide-react" + +export default function QuickActions() { + return ( + +
+

Quick Actions

+
+
+ + + Add New Migrant + + + +
+
+ ) +} \ No newline at end of file diff --git a/src/components/AdminDashboard/RecentActivity.tsx b/src/components/AdminDashboard/RecentActivity.tsx new file mode 100644 index 0000000..e3572ae --- /dev/null +++ b/src/components/AdminDashboard/RecentActivity.tsx @@ -0,0 +1,68 @@ +import { motion } from "framer-motion" +import { PlusCircle, FileText, Users, BarChart2, Database } from "lucide-react" + +export default function RecentActivity() { + return ( + +
+

Recent Activity

+
+
+
+ {[ + { 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) => ( + +
+ {activity.type === "add" ? ( + + ) : activity.type === "update" ? ( + + ) : activity.type === "delete" ? ( + + ) : activity.type === "report" ? ( + + ) : ( + + )} +
+
+

{activity.action}

+

+ By {activity.user} • {activity.time} +

+
+
+ ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/AdminDashboard/Sidebar.tsx b/src/components/AdminDashboard/Sidebar.tsx new file mode 100644 index 0000000..f946f1e --- /dev/null +++ b/src/components/AdminDashboard/Sidebar.tsx @@ -0,0 +1,53 @@ +import { Link } from "react-router-dom" +import { Home, User, FileText, Database, Settings } from "lucide-react" + +export default function Sidebar() { + return ( +
+
+

Italian Migrants

+

Northern Territory DB

+
+ + + +
+
+
+ +
+ Admin User +
+
+
+ ) + } \ No newline at end of file diff --git a/src/components/AdminDashboard/StatsCards.tsx b/src/components/AdminDashboard/StatsCards.tsx new file mode 100644 index 0000000..71889a8 --- /dev/null +++ b/src/components/AdminDashboard/StatsCards.tsx @@ -0,0 +1,65 @@ +import { motion } from "framer-motion" +import { Users, Database, Clock, Calendar } from "lucide-react" + +export default function StatsCards() { + return ( + +
+
+
+

Total Migrants

+

256

+
+
+ +
+
+
+12 this month
+
+ +
+
+
+

Total Records

+

1,248

+
+
+ +
+
+
+86 this month
+
+ +
+
+
+

Recent Updates

+

24

+
+
+ +
+
+
Last update: Today
+
+ +
+
+
+

Pending Tasks

+

8

+
+
+ +
+
+
3 require attention
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/LoginPage.tsx b/src/components/LoginPage.tsx new file mode 100644 index 0000000..320d941 --- /dev/null +++ b/src/components/LoginPage.tsx @@ -0,0 +1,223 @@ +"use client" + +import type React from "react" + +import { useState } from "react" +import { motion } from "framer-motion" +import { useNavigate } from "react-router-dom" +import { Eye, EyeOff, Lock, Mail } from "lucide-react" +import { Link } from "react-router-dom"; + +export default function LoginPage() { + const [email, setEmail] = useState("") + const [password, setPassword] = useState("") + const [showPassword, setShowPassword] = useState(false) + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState("") + const navigate = useNavigate() + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setError("") + setIsLoading(true) + + // Simulate API call for authentication + try { + // In a real application, this would be an API call to your Laravel backend + await new Promise((resolve) => setTimeout(resolve, 1500)) + + // For demo purposes, hardcoded check + if (email === "admin@example.com" && password === "password") { + // Store token in localStorage or cookies + localStorage.setItem("adminToken", "demo-token-12345") + navigate("/admin") + } else { + setError("Invalid email or password") + } + } catch (err) { + setError("Authentication failed. Please try again.") + } finally { + setIsLoading(false) + } + } + + return ( +
+
+ +
+
+ + +
+ +
+ +

Admin Access

+

Northern Territory Italian Migration History

+
+
+ + + {error && ( + + {error} + + )} + +
+
+ +
+
+ +
+ setEmail(e.target.value)} + className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-[#01796F] focus:border-[#01796F]" + placeholder="admin@example.com" + required + /> +
+
+ +
+ +
+
+ +
+ setPassword(e.target.value)} + className="block w-full pl-10 pr-10 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-[#01796F] focus:border-[#01796F]" + placeholder="••••••••" + required + /> +
+ +
+
+
+ +
+
+ + +
+ +
+ + Forgot password? + +
+
+ + + {isLoading ? ( +
+ + + + + Signing in... +
+ ) : ( + "Sign in" + )} +
+
+ +
+
+ or +
+
+ +
+ + Return to public site + +
+ + + + + © {new Date().getFullYear()} Northern Territory Italian Migration History Project + +
+ ) +} diff --git a/src/components/Migrants.tsx b/src/components/Migrants.tsx new file mode 100644 index 0000000..4345a2c --- /dev/null +++ b/src/components/Migrants.tsx @@ -0,0 +1,602 @@ +"use client" + +import { useState, useEffect } from "react" +import { useNavigate } from "react-router-dom" +import { motion } from "framer-motion" +import { Link } from "react-router-dom" +import { + Home, + User, + FileText, + Database, + Settings, + Search, + Filter, + MoreHorizontal, + Edit, + Upload, + Trash2, + Plus, + ChevronUp, +} from "lucide-react" + +interface Migrant { + id: number + name: string + birthDate: string + arrivalDate: string + origin: string + status: "Verified" | "Incomplete" | "Pending" + photos: number +} + +export default function Migrants() { + const [isFirstLoad, setIsFirstLoad] = useState(true) + const [migrants, setMigrants] = useState([]) + const [selectedMigrants, setSelectedMigrants] = useState([]) + const [activeDropdown, setActiveDropdown] = useState(null) + const [searchQuery, setSearchQuery] = useState("") + const [arrivalDateMin, setArrivalDateMin] = useState("") + const [arrivalDateMax, setArrivalDateMax] = useState("") + const navigate = useNavigate() + + // Add a handleLogout function that clears the adminNavigation flag + const handleLogout = () => { + localStorage.removeItem("adminToken") + localStorage.removeItem("adminNavigation") // Clear navigation flag on logout + + + navigate("/admin/login") + } + + // Check authentication and load data + useEffect(() => { + 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) + + // Simulate API call to fetch migrants data without loading state + setMigrants([ + { + id: 3, + name: "Antonio Esposito", + birthDate: "2/18/1940", + arrivalDate: "9/5/1962", + origin: "Rome, Italy", + status: "Verified", + photos: 5, + }, + { + id: 8, + name: "Giovanna Ferraro", + birthDate: "3/11/1955", + arrivalDate: "8/5/1978", + origin: "Turin, Italy", + status: "Incomplete", + photos: 0, + }, + { + id: 5, + name: "Giuseppe Colombo", + birthDate: "4/7/1935", + arrivalDate: "11/28/1955", + origin: "Venice, Italy", + status: "Verified", + photos: 2, + }, + { + id: 4, + name: "Lucia Romano", + birthDate: "8/30/1958", + arrivalDate: "1/10/1980", + origin: "Milan, Italy", + status: "Incomplete", + photos: 0, + }, + { + id: 1, + name: "Marco Rossi", + birthDate: "5/12/1945", + arrivalDate: "3/15/1968", + origin: "Sicily, Italy", + status: "Verified", + photos: 3, + }, + { + id: 6, + name: "Maria Ricci", + birthDate: "12/15/1950", + arrivalDate: "6/20/1972", + origin: "Florence, Italy", + status: "Pending", + photos: 1, + }, + { + id: 7, + name: "Paolo Marino", + birthDate: "9/22/1943", + arrivalDate: "4/17/1965", + origin: "Palermo, Italy", + status: "Verified", + photos: 4, + }, + ]) + } 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 API call to fetch migrants data + setTimeout(() => { + setMigrants([ + { + id: 3, + name: "Antonio Esposito", + birthDate: "2/18/1940", + arrivalDate: "9/5/1962", + origin: "Rome, Italy", + status: "Verified", + photos: 5, + }, + { + id: 8, + name: "Giovanna Ferraro", + birthDate: "3/11/1955", + arrivalDate: "8/5/1978", + origin: "Turin, Italy", + status: "Incomplete", + photos: 0, + }, + { + id: 5, + name: "Giuseppe Colombo", + birthDate: "4/7/1935", + arrivalDate: "11/28/1955", + origin: "Venice, Italy", + status: "Verified", + photos: 2, + }, + { + id: 4, + name: "Lucia Romano", + birthDate: "8/30/1958", + arrivalDate: "1/10/1980", + origin: "Milan, Italy", + status: "Incomplete", + photos: 0, + }, + { + id: 1, + name: "Marco Rossi", + birthDate: "5/12/1945", + arrivalDate: "3/15/1968", + origin: "Sicily, Italy", + status: "Verified", + photos: 3, + }, + { + id: 6, + name: "Maria Ricci", + birthDate: "12/15/1950", + arrivalDate: "6/20/1972", + origin: "Florence, Italy", + status: "Pending", + photos: 1, + }, + { + id: 7, + name: "Paolo Marino", + birthDate: "9/22/1943", + arrivalDate: "4/17/1965", + origin: "Palermo, Italy", + status: "Verified", + photos: 4, + }, + ]) + setIsFirstLoad(false) + }, 1000) + } + } + }, [isFirstLoad, navigate]) + + const handleSelectMigrant = (id: number) => { + setSelectedMigrants((prev) => (prev.includes(id) ? prev.filter((migrantId) => migrantId !== id) : [...prev, id])) + } + + const handleSelectAll = () => { + if (selectedMigrants.length === migrants.length) { + setSelectedMigrants([]) + } else { + setSelectedMigrants(migrants.map((migrant) => migrant.id)) + } + } + + const handleDropdownToggle = (id: number) => { + setActiveDropdown(activeDropdown === id ? null : id) + } + + + const filteredMigrants = migrants.filter((migrant) => migrant.name.toLowerCase().includes(searchQuery.toLowerCase())) + + // If it's the first load, show a loading state + if (isFirstLoad) { + return ( +
+
+
+

Loading migrants data...

+
+
+ ) + } + + return ( +
+ {/* Sidebar */} +
+
+

Italian Migrants

+

Northern Territory DB

+
+ + + + {/* Add this to the bottom user section in the sidebar */} +
+ +
+
+ + {/* Main content */} +
+ {/* Header */} +
+
+

Admin Portal

+
Northern Territory
+
+
+ + {/* Migrants Management content */} +
+ +
+
+

Migrants Management

+

Manage and organize migrant records

+
+ +
+ + {/* Migrants Database */} +
+
+

Migrants Database

+
+ + {/* Search and filters */} +
+
+
+ + setSearchQuery(e.target.value)} + className="pl-10 pr-4 py-2 w-full border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#01796F] focus:border-[#01796F]" + /> +
+ +
+ Arrival Date: + setArrivalDateMin(e.target.value)} + className="border border-gray-300 rounded-md px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-[#01796F] focus:border-[#01796F] w-32" + /> + to + setArrivalDateMax(e.target.value)} + className="border border-gray-300 rounded-md px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-[#01796F] focus:border-[#01796F] w-32" + /> +
+
+ +
+ +
+ Show: + +
+
+ +
+
+
+ + {/* Table */} +
+ + + + + + + + + + + + + + + + {filteredMigrants.map((migrant) => ( + + + + + + + + + + + + ))} + +
+
+ 0} + onChange={handleSelectAll} + /> +
+
+ ID + +
+ Name + +
+
+ Birth Date + + Arrival Date + + Origin + + Status + + Photos + + Actions +
+ handleSelectMigrant(migrant.id)} + /> + {migrant.id} +
+
+
+
{migrant.name}
+
+
+
{migrant.birthDate}{migrant.arrivalDate}{migrant.origin} + + {migrant.status} + + {migrant.photos} + + {activeDropdown === migrant.id && ( +
+ + + +
+ )} +
+
+ + {/* Pagination */} +
+
+ + +
+
+
+

+ Showing 1 to{" "} + {filteredMigrants.length} of{" "} + {filteredMigrants.length} results +

+
+
+ +
+
+
+
+
+
+
+
+ ) +} diff --git a/src/components/ui/ProfileSettings.tsx b/src/components/ui/ProfileSettings.tsx new file mode 100644 index 0000000..1540825 --- /dev/null +++ b/src/components/ui/ProfileSettings.tsx @@ -0,0 +1,509 @@ +"use client" + +import type React from "react" + +import { useState, useEffect } from "react" +import { useNavigate } from "react-router-dom" +import { motion } from "framer-motion" +import { Camera, Save, User, Mail, MapPin, Lock, Bell, Home } from "lucide-react" +import { Link } from "react-router-dom" + +interface UserProfile { + firstName: string + lastName: string + email: string + jobTitle: string + department: string + bio: string + avatar: string +} + +export default function ProfileSettings() { + const [isFirstLoad, setIsFirstLoad] = useState(true) + const [isLoading, setIsLoading] = useState(false) + const [activeTab, setActiveTab] = useState("profile") + const [profile, setProfile] = useState({ + firstName: "Admin", + lastName: "User", + email: "admin@example.com", + jobTitle: "Database Administrator", + department: "IT", + bio: "Experienced database administrator with a focus on migration data management.", + avatar: "", + }) + const navigate = useNavigate() + + // Add a handleLogout function that clears the adminNavigation flag + const handleLogout = () => { + localStorage.removeItem("adminToken") + localStorage.removeItem("adminNavigation") // Clear navigation flag on logout + + navigate("/admin/login") + } + + 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 profile data + const timer = setTimeout(() => { + setIsFirstLoad(false) + }, 500) // Reduced loading time for better UX + + return () => clearTimeout(timer) + } + } + }, [isFirstLoad, navigate]) + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target + setProfile((prev) => ({ ...prev, [name]: value })) + } + + const handleSave = () => { + setIsLoading(true) + + // Simulate API call + setTimeout(() => { + setIsLoading(false) + }, 500) // Reduced loading time for better UX + } + + const handleAvatarChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0] + if (file) { + const reader = new FileReader() + reader.onload = (event) => { + if (event.target?.result) { + + } + } + reader.readAsDataURL(file) + } + } + + // If it's the first load, show a loading state + if (isFirstLoad) { + return ( +
+
+
+

Loading profile...

+
+
+ ) + } + + return ( +
+ {/* Sidebar */} +
+
+

Italian Migrants

+

Northern Territory DB

+
+ + + + {/* Update the bottom user section in the sidebar */} +
+ +
+
+ + {/* Main content */} +
+ {/* Header */} +
+
+

Admin Portal

+
Northern Territory
+
+
+ + {/* Settings content */} +
+ +
+
+

Settings

+

Manage your account preferences

+
+ +
+ + {/* Tabs */} +
+ + + +
+ + {/* Profile Tab Content */} + {activeTab === "profile" && ( +
+

Profile Information

+

+ Update your personal information and how it appears on your profile. +

+ +
+
+
+
+ {profile.avatar ? ( + Profile + ) : ( +
+ +
+ )} +
+ +
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+ +