diff --git a/package-lock.json b/package-lock.json index 59dc246..502c97e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,14 @@ "version": "0.0.0", "dependencies": { "@radix-ui/react-avatar": "^1.1.9", + "@radix-ui/react-checkbox": "^1.3.1", "@radix-ui/react-dialog": "^1.1.13", + "@radix-ui/react-dropdown-menu": "^2.1.14", "@radix-ui/react-label": "^2.1.6", "@radix-ui/react-select": "^2.2.4", + "@radix-ui/react-separator": "^1.1.6", "@radix-ui/react-slot": "^1.2.2", + "@radix-ui/react-tabs": "^1.1.11", "@radix-ui/react-toast": "^1.2.13", "@tailwindcss/vite": "^4.1.6", "axios": "^1.9.0", @@ -20,9 +24,11 @@ "clsx": "^2.1.1", "framer-motion": "^12.11.0", "lucide-react": "^0.510.0", + "next-themes": "^0.4.6", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.6.0", + "sonner": "^2.0.3", "tailwind-merge": "^3.3.0" }, "devDependencies": { @@ -1097,6 +1103,36 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.1.tgz", + "integrity": "sha512-xTaLKAO+XXMPK/BpVTSaAAhlefmvMSACjIhK9mGsImvX2ljcTDm8VGR1CuS1uYcNdR5J+oiOhoJZc5un6bh3VQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "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-collection": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.6.tgz", @@ -1225,6 +1261,35 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.14.tgz", + "integrity": "sha512-lzuyNjoWOoaMFE/VC5FnAAYM16JmQA8ZmucOXtlhm2kKR5TSU95YLAueQ4JYuRmUJmBvSqXaVFGIfuukybwZJQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.14", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-controllable-state": "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-focus-guards": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", @@ -1302,6 +1367,46 @@ } } }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.14.tgz", + "integrity": "sha512-0zSiBAIFq9GSKoSH5PdEaQeRB3RnEGxC+H2P0egtnKoKKLNBH8VBHyVO6/jskhjAezhOIplyRUj7U2lds9A+Yg==", + "license": "MIT", + "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-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.6", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.6", + "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-roving-focus": "1.1.9", + "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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-popper": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.6.tgz", @@ -1401,6 +1506,37 @@ } } }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.9.tgz", + "integrity": "sha512-ZzrIFnMYHHCNqSNCsuN6l7wlewBEq0O0BCSBkabJMFXVO51LRUTq71gLP1UxFvmrXElqmPjA5VX7IqC9VpazAQ==", + "license": "MIT", + "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-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@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" + }, + "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-select": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.4.tgz", @@ -1443,6 +1579,29 @@ } } }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.6.tgz", + "integrity": "sha512-Izof3lPpbCfTM7WDta+LRkz31jem890VjEvpVRoWQNKpDUMMVffuyq854XPGP1KYGWWmjmYvHvPFeocWhFCy1w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.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-slot": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", @@ -1460,6 +1619,36 @@ } } }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.11.tgz", + "integrity": "sha512-4FiKSVoXqPP/KfzlB7lwwqoFV6EPwkrrqGp9cUYXjwDYHhvpnqq79P+EPHKcdoTE7Rl8w/+6s9rTlsfXHES9GA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-roving-focus": "1.1.9", + "@radix-ui/react-use-controllable-state": "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-toast": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.13.tgz", @@ -4420,6 +4609,16 @@ "node": ">= 0.6" } }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -5148,6 +5347,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sonner": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz", + "integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/package.json b/package.json index 5a12e20..7e296b6 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,14 @@ }, "dependencies": { "@radix-ui/react-avatar": "^1.1.9", + "@radix-ui/react-checkbox": "^1.3.1", "@radix-ui/react-dialog": "^1.1.13", + "@radix-ui/react-dropdown-menu": "^2.1.14", "@radix-ui/react-label": "^2.1.6", "@radix-ui/react-select": "^2.2.4", + "@radix-ui/react-separator": "^1.1.6", "@radix-ui/react-slot": "^1.2.2", + "@radix-ui/react-tabs": "^1.1.11", "@radix-ui/react-toast": "^1.2.13", "@tailwindcss/vite": "^4.1.6", "axios": "^1.9.0", @@ -22,9 +26,11 @@ "clsx": "^2.1.1", "framer-motion": "^12.11.0", "lucide-react": "^0.510.0", + "next-themes": "^0.4.6", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.6.0", + "sonner": "^2.0.3", "tailwind-merge": "^3.3.0" }, "devDependencies": { diff --git a/src/App.tsx b/src/App.tsx index 732be04..b910afb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,11 +2,13 @@ import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import MigrantProfilePage from "./pages/MigrantProfilePage"; import NotFoundPage from "./pages/NotFoundPage"; import "./App.css"; -import LoginPage from "./components/LoginPage"; -import Migrants from "./components/Migrants"; +import LoginPage from "./components/admin/LoginPage"; +import Migrants from "./components/admin/Migrants"; import ProfileSettings from "./components/ui/ProfileSettings"; -import AdminDashboardPage from "./pages/AdminDashboardPage"; +import AdminDashboardPage from "./components/admin/AdminDashboard"; import HomePage from "./pages/HomePage"; +import RegisterPage from "./components/admin/Register"; +import AddMigrantPage from "./components/admin/AddMigrant"; function App() { return ( @@ -15,7 +17,9 @@ function App() { } /> } /> } /> + } /> } /> + } /> } /> } /> } /> diff --git a/src/components/AdminDashboard.tsx b/src/components/AdminDashboard.tsx deleted file mode 100644 index 61aae16..0000000 --- a/src/components/AdminDashboard.tsx +++ /dev/null @@ -1,360 +0,0 @@ -"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") - 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("/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 deleted file mode 100644 index 57c6485..0000000 --- a/src/components/AdminDashboard/DashboardLayout.tsx +++ /dev/null @@ -1,21 +0,0 @@ -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 deleted file mode 100644 index 9050f1f..0000000 --- a/src/components/AdminDashboard/Header.tsx +++ /dev/null @@ -1,25 +0,0 @@ -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/Sidebar.tsx b/src/components/AdminDashboard/Sidebar.tsx deleted file mode 100644 index f946f1e..0000000 --- a/src/components/AdminDashboard/Sidebar.tsx +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 71889a8..0000000 --- a/src/components/AdminDashboard/StatsCards.tsx +++ /dev/null @@ -1,65 +0,0 @@ -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/Migrants.tsx b/src/components/Migrants.tsx deleted file mode 100644 index 59b765e..0000000 --- a/src/components/Migrants.tsx +++ /dev/null @@ -1,602 +0,0 @@ -"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("/login") - } - - // Check authentication and load data - useEffect(() => { - const token = localStorage.getItem("adminToken") - if (!token) { - navigate("/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/admin/AddMigrant.tsx b/src/components/admin/AddMigrant.tsx new file mode 100644 index 0000000..e5f26db --- /dev/null +++ b/src/components/admin/AddMigrant.tsx @@ -0,0 +1,126 @@ +"use client" + +import { useState } from "react"; +import { ArrowLeft, Save } from "lucide-react"; +import { Link } from "react-router-dom"; +import apiService from "@/services/apiService"; +import { Button } from "@/components/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + +import Header from "../layout/Header"; +import Sidebar from "../layout/Sidebar"; +import { PersonalInfoTab } from "./migrant/PersonalInfoTab"; +import { MigrationDetailsTab } from "./migrant/MigrationDetailsTab"; +import { LocationsTab } from "./migrant/LocationsTab"; +import { InterneeDetailsTab } from "./migrant/InterneeDetailsTab"; +import { PhotosTab } from "./migrant/PhotosTab"; +import { NotesTab } from "./migrant/NotesTab"; + +export default function AddUserPage() { + const [formData, setFormData] = useState({ + surname: "", + christian_name: "", + full_name: "", + date_of_birth: "", + date_of_death: "", + place_of_birth: "", + home_at_death: "", + occupation: "", + names_of_parents: "", + names_of_children: "", + data_source: "", + reference: "", + cav: "", + id_card_no: "", + date_of_arrival_australia: "", + date_of_arrival_nt: "", + date_of_naturalisation: "", + corps_issued: "", + no_of_cert: "", + issued_at: "", + }); + + const handleInputChange = (e: React.ChangeEvent) => { + const { id, value } = e.target; + setFormData(prev => ({ ...prev, [id]: value })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const res = await apiService.createPerson(formData); + alert("Migrant created successfully!"); + console.log(res); + } catch (err) { + alert("Failed to create migrant."); + console.error(err); + } + }; + + return ( +
+ +
+
+
+
+ + + +

+ Add New Migrant +

+
+ +
+ + + Personal Information + Migration Details + Locations + Internee Details + Photos & Documents + Additional Notes + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+
+ ); +} diff --git a/src/components/admin/AdminDashboard.tsx b/src/components/admin/AdminDashboard.tsx new file mode 100644 index 0000000..63919d7 --- /dev/null +++ b/src/components/admin/AdminDashboard.tsx @@ -0,0 +1,172 @@ +import { useState } from "react"; +import { + BarChart3, + Calendar, + Clock, + Database, + FileText, + PlusCircle, + Search, + User, + Users, +} from "lucide-react"; +import { Link } from "react-router-dom"; + +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@/components/ui/tabs"; + +import Header from "../layout/Header"; +import Sidebar from "../layout/Sidebar"; +import RecentActivityList from "../common/RecentActivity"; +import StatCard from "../common/StatCard"; + +export default function DashboardPage() { + const [searchQuery, setSearchQuery] = useState(""); + + return ( +
+ +
+
+
+
+

+ Welcome, Admin +

+

+ Here's an overview of your Italian Migrants Database +

+
+ +
+ } + /> + } + /> + } + /> + } + /> +
+ +
+ + + Quick Search + Find migrant records quickly + + +
+ + setSearchQuery(e.target.value)} + /> +
+
+ + + +
+
+
+ + + + Migration Trends + Yearly migration patterns + + +
+ {[35, 45, 20, 30, 75, 60, 40, 80, 90, 50].map((height, i) => ( +
+
+ {1950 + i * 5}: {height} migrants +
+
+ {1950 + i * 5} +
+ ))} +
+
+
+
+ +
+ +
+ + Recent Activity + Pending Reviews + + + + +
+ + + + + + + + + + +
+ +

No Pending Reviews

+

All migrant records have been reviewed.

+
+
+
+
+
+
+
+
+
+ ); +} diff --git a/src/components/LoginPage.tsx b/src/components/admin/LoginPage.tsx similarity index 93% rename from src/components/LoginPage.tsx rename to src/components/admin/LoginPage.tsx index 320d941..055406b 100644 --- a/src/components/LoginPage.tsx +++ b/src/components/admin/LoginPage.tsx @@ -7,6 +7,7 @@ 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"; +import apiService from "@/services/apiService" export default function LoginPage() { const [email, setEmail] = useState("") @@ -23,19 +24,20 @@ export default function LoginPage() { // 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") + const response = await apiService.login({ + email, + password, + }) + console.log("Response:", response.data) + alert("Login successful!") + navigate("/admin") + } catch (error: any) { + console.error("Error submitting form:", error) + if (error.response && error.response.data && error.response.data.message) { + setError(error.response.data.message) } else { - setError("Invalid email or password") + setError("Login failed. Please check your input and try again.") } - } catch (err) { - setError("Authentication failed. Please try again.") } finally { setIsLoading(false) } diff --git a/src/components/admin/Migrants.tsx b/src/components/admin/Migrants.tsx new file mode 100644 index 0000000..d4ef695 --- /dev/null +++ b/src/components/admin/Migrants.tsx @@ -0,0 +1,245 @@ +"use client" + +import { useState } from "react" +import { ArrowUpDown, Download, Filter, MoreHorizontal, PlusCircle, Search, Trash2, Upload } from "lucide-react" +import {Link} from "react-router-dom" + +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Checkbox } from "@/components/ui/checkbox" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" +import { Input } from "@/components/ui/input" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" + +import Header from "@/components/layout/Header" +import Sidebar from "@/components/layout/Sidebar" + +// Sample data for migrants +const migrants = [ + { + id: 1, + name: "Marco Rossi", + birthDate: "1935-05-12", + birthPlace: "Rome, Italy", + arrivalDate: "1952-08-23", + occupation: "Carpenter", + hasPhotos: true, + }, + { + id: 2, + name: "Sofia Bianchi", + birthDate: "1942-11-03", + birthPlace: "Naples, Italy", + arrivalDate: "1960-02-15", + occupation: "Seamstress", + hasPhotos: true, + }, + { + id: 3, + name: "Antonio Esposito", + birthDate: "1928-07-22", + birthPlace: "Milan, Italy", + arrivalDate: "1950-10-05", + occupation: "Farmer", + hasPhotos: false, + }, + { + id: 4, + name: "Lucia Romano", + birthDate: "1940-03-18", + birthPlace: "Florence, Italy", + arrivalDate: "1958-06-30", + occupation: "Teacher", + hasPhotos: true, + }, + { + id: 5, + name: "Giuseppe Colombo", + birthDate: "1932-09-08", + birthPlace: "Venice, Italy", + arrivalDate: "1955-12-10", + occupation: "Fisherman", + hasPhotos: false, + }, +] + +export default function MigrantsPage() { + const [searchQuery, setSearchQuery] = useState("") + const [selectedMigrants, setSelectedMigrants] = useState([]) + + const toggleSelectAll = () => { + if (selectedMigrants.length === migrants.length) { + setSelectedMigrants([]) + } else { + setSelectedMigrants(migrants.map((m) => m.id)) + } + } + + const toggleSelectMigrant = (id: number) => { + if (selectedMigrants.includes(id)) { + setSelectedMigrants(selectedMigrants.filter((m) => m !== id)) + } else { + setSelectedMigrants([...selectedMigrants, id]) + } + } + + + + const filteredMigrants = migrants.filter( + (migrant) => + migrant.name.toLowerCase().includes(searchQuery.toLowerCase()) || + migrant.birthPlace.toLowerCase().includes(searchQuery.toLowerCase()) || + migrant.occupation.toLowerCase().includes(searchQuery.toLowerCase()), + ) + + return ( +
+ +
+
+
+
+

Migrants Database

+ + + +
+ + + + Search & Filter + + + +
+
+ + setSearchQuery(e.target.value)} + /> +
+
+ + +
+
+ {selectedMigrants.length > 0 && ( + + )} +
+
+
+
+ + + +
+ + + + + 0} + onCheckedChange={toggleSelectAll} + aria-label="Select all" + /> + + +
+ Name + +
+
+ +
+ Birth Date + +
+
+ Birth Place + +
+ Arrival Date + +
+
+ Occupation + Photos + Actions +
+
+ + {filteredMigrants.length === 0 ? ( + + + No migrants found matching your search criteria. + + + ) : ( + filteredMigrants.map((migrant) => ( + + + toggleSelectMigrant(migrant.id)} + aria-label={`Select ${migrant.name}`} + /> + + {migrant.name} + {new Date(migrant.birthDate).toLocaleDateString()} + {migrant.birthPlace} + {new Date(migrant.arrivalDate).toLocaleDateString()} + {migrant.occupation} + + {migrant.hasPhotos ? ( + + Yes + + ) : ( + + No + + )} + + + + + + + + + Edit + + Delete + + + + + )) + )} + +
+
+
+
+
+
+
+ ) +} diff --git a/src/components/admin/Register.tsx b/src/components/admin/Register.tsx new file mode 100644 index 0000000..217f399 --- /dev/null +++ b/src/components/admin/Register.tsx @@ -0,0 +1,231 @@ +"use client" + +import { useState } from "react" +import { motion } from "framer-motion" +import { Eye, EyeOff, Lock, Mail } from "lucide-react" +import { Link, useNavigate } from "react-router-dom" +import apiService from "@/services/apiService" + +export default function SimpleForm() { + const [error, setError] = useState("") + const [showPassword, setShowPassword] = useState(false) + const [isLoading, setIsLoading] = useState(false) + + const [email, setEmail] = useState("") + const [password, setPassword] = useState("") + const [name, setName] = useState("") + + const navigate = useNavigate() + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setIsLoading(true) + setError("") + + try { + const response = await apiService.register({ + name, + email, + password, + }) + + console.log("Response:", response.data) + alert("Registration successful!") + setName("") + setEmail("") + setPassword("") + } catch (error: any) { + console.error("Error submitting form:", error) + if (error.response && error.response.data && error.response.data.message) { + setError(error.response.data.message) + } else { + setError("Registration failed. Please check your input and try again.") + } + } finally { + setIsLoading(false) + navigate("/login") + } + } + + return ( +
+
+ +
+
+ + +
+ +
+ +

Admin Access

+

+ Northern Territory Italian Migration History +

+
+
+ + + {error && ( + + {error} + + )} + +
+
+ +
+
+ +
+ setName(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="John Doe" + required + /> +
+
+ +
+ +
+
+ +
+ 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 + /> +
+ +
+
+
+ + + {isLoading ? ( +
+ + + + + Signing in... +
+ ) : ( + "Sign in" + )} +
+
+ +
+
+ or +
+
+ +
+ + Return to public site + +
+ + + + + © {new Date().getFullYear()} Northern Territory Italian Migration History Project + +
+ ) +} diff --git a/src/components/admin/migrant/InterneeDetailsTab.tsx b/src/components/admin/migrant/InterneeDetailsTab.tsx new file mode 100644 index 0000000..4980fcb --- /dev/null +++ b/src/components/admin/migrant/InterneeDetailsTab.tsx @@ -0,0 +1,41 @@ +// InterneeDetailsTab.jsx +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; + +export function InterneeDetailsTab() { + return ( + + + Internee Details + Information about internment (if applicable) + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +