From bd3a7bae27f4c5acff2332df4e46b34f8433ec51 Mon Sep 17 00:00:00 2001 From: Lukeshan Thananchayan Date: Mon, 19 May 2025 13:56:19 +0100 Subject: [PATCH] reverted to user only admin page user page still requires prisma dependant bits --- src/app/administrator/page.tsx | 676 +++++---------------------------- 1 file changed, 105 insertions(+), 571 deletions(-) diff --git a/src/app/administrator/page.tsx b/src/app/administrator/page.tsx index 9a45517..5ea03fe 100644 --- a/src/app/administrator/page.tsx +++ b/src/app/administrator/page.tsx @@ -1,302 +1,72 @@ "use client"; import React, { useState, useRef } from "react"; - -// The roles in UserSeed are ALL CAPS as per your data type Role = "ADMIN" | "GUEST" | "SCIENTIST"; -// Forward declarations if you don't have these yet -// You can replace Scientist or User with real types if you have them - -type Scientist = { +const roleLabels: Record = { + ADMIN: "Admin", + GUEST: "Guest", + SCIENTIST: "Scientist", + }; +type User = { id: number; - createdAt: Date | string; + email: string; name: string; - level: string; // "JUNIOR" | "SENIOR" - userId: number; - user: User; - superiorId?: number | null; - superior?: Scientist | null; - subordinates: Scientist[]; - earthquakes: Earthquake[]; - observatories: Observatory[]; - artefacts: Artefact[]; - }; - -type Earthquake = { - id: number; - createdAt: Date | string; - updatedAt: Date | string; - date: Date | string; - location: string; - latitude: string; - longitude: string; - magnitude: number; - depth: number; - creatorId?: number | null; - creator?: Scientist | null; - artefacts: Artefact[]; - observatories: Observatory[]; + role: Role; + password: string; + createdAt: string; }; -type Observatory = { - id: number; - createdAt: Date | string; - updatedAt: Date | string; - name: string; - location: string; - longitude: string; - latitude: string; - dateEstablished?: number | null; - functional: boolean; - seismicSensorOnline: boolean; - creatorId?: number | null; - creator?: Scientist | null; - earthquakes: Earthquake[]; -}; -type Artefact = { - id: number; - createdAt: Date | string; - updatedAt: Date | string; - type: string; - warehouseArea: string; - earthquakeId: number; - earthquake: Earthquake; - creatorId?: number | null; - creator?: Scientist | null; - required: boolean; - shopPrice?: number | null; - purchasedById?: number | null; - purchasedBy?: User | null; - pickedUp: boolean; - }; - -type User= { - id: number; - createdAt: string; - name: string; - email: string; - passwordHash: string; - role: Role; // "ADMIN" | "GUEST" | "SCIENTIST" - scientist : Scientist | null - purchasedArtefacts: Artefact[]; -}; - -// Example users with scientist references - const initialUsers: User[] = [ - { - id: 1, - createdAt: "2024-06-20T08:00:00Z", - name: "Dr. Alice Volcano", - email: "alice.volcano@example.com", - passwordHash: "hashed_password_1", - role: "SCIENTIST", - scientist: { - id: 101, - createdAt: "2024-06-18T09:00:00Z", - name: "Dr. Alice Volcano", - level: "SENIOR", - userId: 1, - user: null as any, // Will be populated after object creation to avoid circular reference, see note below - superiorId: null, - superior: null, - subordinates: [], - earthquakes: [ - { - id: 201, - createdAt: "2024-06-01T04:00:00Z", - updatedAt: "2024-06-10T10:00:00Z", - date: "2024-06-01T05:00:00Z", - location: "Fuego Ridge", - latitude: "14.23", - longitude: "-90.88", - magnitude: 7.2, - depth: 10.1, - creatorId: 101, - creator: null, - artefacts: [], - observatories: [], - } - ], - observatories: [ - { - id: 301, - createdAt: "2024-01-01T08:00:00Z", - updatedAt: "2024-06-05T16:30:00Z", - name: "Central Vulcanology Lab", - location: "Fuego City", - longitude: "-90.88", - latitude: "14.23", - dateEstablished: 2011, - functional: true, - seismicSensorOnline: true, - creatorId: 101, - creator: null, - earthquakes: [], - } - ], - artefacts: [ - { - id: 401, - createdAt: "2024-06-11T09:00:00Z", - updatedAt: "2024-06-12T10:45:00Z", - type: "Lava", - warehouseArea: "ZoneA-Shelf1", - earthquakeId: 201, - earthquake: { - id: 201, - createdAt: "2024-06-01T04:00:00Z", - updatedAt: "2024-06-10T10:00:00Z", - date: "2024-06-01T05:00:00Z", - location: "Fuego Ridge", - latitude: "14.23", - longitude: "-90.88", - magnitude: 7.2, - depth: 10.1, - creatorId: 101, - creator: null, - artefacts: [], - observatories: [], - }, - creatorId: 101, - creator: null, - required: true, - shopPrice: 500.0, - purchasedById: null, - purchasedBy: null, - pickedUp: false, - } - ], - }, - purchasedArtefacts: [], - }, - { - id: 2, - createdAt: "2024-06-21T08:00:00Z", - name: "Dr. Bob Lava", - email: "bob.lava@example.com", - passwordHash: "hashed_password_2", - role: "SCIENTIST", - scientist: { - id: 102, - createdAt: "2024-06-19T13:00:00Z", - name: "Dr. Bob Lava", - level: "JUNIOR", - userId: 2, - user: null as any, // Populated after object creation if circular needed - superiorId: 101, - superior: null, - subordinates: [], - earthquakes: [], - observatories: [], - artefacts: [], - }, - purchasedArtefacts: [], - } + { email: "john@example.com", name: "John Doe", role: "ADMIN", password: "secret1", createdAt: "2024-06-21T09:15:01Z" ,id:1}, + { email: "jane@example.com", name: "Jane Smith", role: "GUEST", password: "secret2", createdAt: "2024-06-21T10:01:09Z" ,id:2}, + { email: "bob@example.com", name: "Bob Brown", role: "SCIENTIST", password: "secret3", createdAt: "2024-06-21T12:13:45Z" ,id:3}, + { email: "alice@example.com", name: "Alice Johnson",role: "GUEST", password: "secret4", createdAt: "2024-06-20T18:43:20Z" ,id:4}, + { email: "eve@example.com", name: "Eve Black", role: "ADMIN", password: "secret5", createdAt: "2024-06-20T19:37:10Z" ,id:5}, + { email: "dave@example.com", name: "Dave Clark", role: "GUEST", password: "pw", createdAt: "2024-06-19T08:39:10Z" ,id:6}, + { email: "fred@example.com", name: "Fred Fox", role: "GUEST", password: "pw", createdAt: "2024-06-19T09:11:52Z" ,id:7}, + { email: "ginny@example.com", name: "Ginny Hall", role: "SCIENTIST", password: "pw", createdAt: "2024-06-17T14:56:27Z" ,id:8}, + { email: "harry@example.com", name: "Harry Lee", role: "ADMIN", password: "pw", createdAt: "2024-06-16T19:28:11Z" ,id:9}, + { email: "ivy@example.com", name: "Ivy Volt", role: "ADMIN", password: "pw", createdAt: "2024-06-15T21:04:05Z" ,id:10}, + { email: "kate@example.com", name: "Kate Moss", role: "SCIENTIST", password: "pw", createdAt: "2024-06-14T11:16:35Z" ,id:11}, + { email: "leo@example.com", name: "Leo Garrison", role: "GUEST", password: "pw", createdAt: "2024-06-12T08:02:51Z" ,id:12}, + { email: "isaac@example.com", name: "Isaac Yang", role: "GUEST", password: "pw", createdAt: "2024-06-12T15:43:29Z" ,id:13}, ]; - - // Optionally, populate the scientist.user field with the parent user (avoiding circular reference on first creation): -if (initialUsers[0].scientist && initialUsers[1].scientist){ - initialUsers[0].scientist.user = initialUsers[0]; - initialUsers[1].scientist.user = initialUsers[1]; - initialUsers[1].scientist.superior = initialUsers[1].scientist;} - - const allArtefacts: Artefact[] = [ - { - id: 401, - createdAt: "2024-06-11T09:00:00Z", - updatedAt: "2024-06-12T10:45:00Z", - type: "Lava", - warehouseArea: "ZoneA-Shelf1", - earthquakeId: 201, - earthquake: { - id: 201, - createdAt: "2024-06-01T04:00:00Z", - updatedAt: "2024-06-10T10:00:00Z", - date: "2024-06-01T05:00:00Z", - location: "Fuego Ridge", - latitude: "14.23", - longitude: "-90.88", - magnitude: 7.2, - depth: 10.1, - creatorId: 101, - creator: null, - artefacts: [], - observatories: [], - }, - creatorId: 101, - creator: null, - required: true, - shopPrice: 500.0, - purchasedById: null, - purchasedBy: null, - pickedUp: false, - } - ]; - const allEarthquakes: Earthquake[] = [ - { - id: 201, - createdAt: "2024-06-01T04:00:00Z", - updatedAt: "2024-06-10T10:00:00Z", - date: "2024-06-01T05:00:00Z", - location: "Fuego Ridge", - latitude: "14.23", - longitude: "-90.88", - magnitude: 7.2, - depth: 10.1, - creatorId: 101, - creator: null, - artefacts: [], - observatories: [], - } - ]; - const allScientists: Scientist[] = initialUsers.map(u => u.scientist as Scientist).filter(Boolean); - -// Human readable role labels -const roleLabels: Record = { - ADMIN: "Admin", - GUEST: "Guest", - SCIENTIST: "Scientist" -}; -const allRoles: Role[] = ["ADMIN", "GUEST", "SCIENTIST"]; - -const sortFields = [ +const sortFields = [ // Sort box options { label: "Name", value: "name" }, - { label: "Email", value: "email" } + { label: "Email", value: "email" }, ] as const; -type SortField = (typeof sortFields)[number]["value"]; + +type SortField = typeof sortFields[number]["value"]; type SortDir = "asc" | "desc"; const dirLabels: Record = { asc: "ascending", desc: "descending" }; const fieldLabels: Record = { name: "Name", email: "Email" }; export default function AdminPage() { - const [users, setUsers] = useState(initialUsers); - const [selectedEmail, setSelectedEmail] = useState(null); - const [editUser, setEditUser] = useState(null); - const [showPassword, setShowPassword] = useState(false); - const [showAssignmentPanel, setShowAssignmentPanel] = useState(null); + const [users, setUsers] = useState(initialUsers); + const [selectedEmail, setSelectedEmail] = useState(null); + // Local edit state for SCIENTIST form + const [editUser, setEditUser] = useState(null); + // Reset editUser when the selected user changes React.useEffect(() => { if (!selectedEmail) setEditUser(null); else { const user = users.find(u => u.email === selectedEmail); - // Add a dummy password field just for edit box (not stored) - setEditUser(user ? { ...user, password: "" } : null); + setEditUser(user ? { ...user } : null); } }, [selectedEmail, users]); // Search/filter/sort state - const [searchField, setSearchField] = useState("name"); + const [searchField, setSearchField] = useState<"name" | "email">("name"); const [searchText, setSearchText] = useState(""); - // The filter UI must use ALL CAPS for roles in UserSeed const [roleFilter, setRoleFilter] = useState("all"); const [sortField, setSortField] = useState("name"); const [sortDir, setSortDir] = useState("asc"); + // Dropdown states const [filterDropdownOpen, setFilterDropdownOpen] = useState(false); const [sortDropdownOpen, setSortDropdownOpen] = useState(false); const filterDropdownRef = useRef(null); const sortDropdownRef = useRef(null); - // Dropdown auto-close React.useEffect(() => { const handleClick = (e: MouseEvent) => { if ( @@ -310,17 +80,7 @@ export default function AdminPage() { return () => document.removeEventListener("mousedown", handleClick); }, []); - // Handler: assign/unassign scientist - const handleAssignScientist = (scientistId: number) => { - setEditUser(prev => - prev ? { - ...prev, - scientist: allScientists.find(s => s.id === scientistId) || null, - } : null - ); - }; - - // Filter, search, sort logic + // Filtering, searching, sorting logic const filteredUsers = users.filter( (user) => roleFilter === "all" || user.role === roleFilter ); @@ -332,7 +92,7 @@ export default function AdminPage() { return sortDir === "asc" ? cmp : -cmp; }); - // Edit form handler (special-case password, don't modify passwordHash) + // Form input change handler const handleEditChange = (e: React.ChangeEvent) => { if (!editUser) return; const { name, value } = e.target; @@ -341,14 +101,15 @@ export default function AdminPage() { ); }; - // Selected "actual" user object - const selectedUser = users.find(u => u.email === selectedEmail); + // Update button logic (compare original selectedUser and editUser) + const selectedUser = users.find((u) => u.email === selectedEmail); const isEditChanged = React.useMemo(() => { if (!editUser || !selectedUser) return false; + // Compare primitive fields return ( editUser.name !== selectedUser.name || editUser.role !== selectedUser.role || - (editUser.password && editUser.password !== "") // Only trigger update if changed + editUser.password !== selectedUser.password ); }, [editUser, selectedUser]); @@ -358,14 +119,14 @@ export default function AdminPage() { if (!editUser) return; setUsers(prev => prev.map(u => - u.email === editUser.email - ? { ...editUser } // Copies ALL user properties, including scientist, artefacts, etc. - : u + u.email === editUser.email ? { ...editUser } : u ) ); + // After successful update, update selectedUser local state + // (editUser will auto-sync due to useEffect on users) }; - // Delete user + // Delete user logic const handleDelete = () => { if (!selectedUser) return; if (!window.confirm(`Are you sure you want to delete "${selectedUser.name}"? This cannot be undone.`)) return; @@ -374,30 +135,34 @@ export default function AdminPage() { setEditUser(null); }; + const allRoles: Role[] = ["ADMIN", "GUEST", "SCIENTIST"]; + + // Tooltip handling for email field const [showEmailTooltip, setShowEmailTooltip] = useState(false); return (
{/* SIDEBAR */} -
+
{/* Search Bar */}
- setSearchText(e.target.value)} - /> - + /> +
{/* Filter and Sort Buttons */}
@@ -405,22 +170,21 @@ export default function AdminPage() {
{filterDropdownOpen && (
@@ -429,7 +193,7 @@ export default function AdminPage() { key={role} onClick={() => { setRoleFilter(role); setFilterDropdownOpen(false); }} className={`w-full text-left px-3 py-1 hover:bg-blue-50 border-b border-gray-100 last:border-0 - ${roleFilter === role ? "font-bold text-blue-600" : ""}`} + ${roleFilter===role ? "font-bold text-blue-600" : ""}`} > {roleLabels[role]} @@ -456,7 +220,7 @@ export default function AdminPage() { setSortDropdownOpen(false); }} className={`w-full text-left px-3 py-2 hover:bg-blue-50 border-b border-gray-100 last:border-0 - ${sortField === opt.value ? "font-bold text-blue-600" : ""}`} + ${sortField===opt.value ? "font-bold text-blue-600" : ""}`} > {opt.label} @@ -506,15 +270,17 @@ export default function AdminPage() { {/* MAIN PANEL */}
{editUser ? ( -
-

Edit User

-
{ e.preventDefault(); }}> - {/* Creation Time */} +
+

Edit User

+
- - {editUser.createdAt} + + {editUser.createdAt} +
+
+ + {editUser.id}
- {/* Email */}
- {/* Name */}
- {/* Role */}
- +
- {/* Scientist assignment */}
- - - {/* Show all scientist data */} - {editUser.scientist && ( -
- -
- {editUser.scientist - ? `${editUser.scientist.name} (${editUser.scientist.level})` - : None - } -
- -
- )} + +
- {/* Password - eye toggle only (view, not edit) */} -
- -
- - -
-
- {/* Trigger artefact assignment panel */} -
- - -
- Currently assigned: {editUser.purchasedArtefacts.map(a => a.type).join(", ") || "None"} -
-
- - {/* Trigger earthquake assignment panel */} -
- - -
- {editUser.scientist - ? "Current: " + (editUser.scientist.earthquakes.map(e => e.location).join(", ") || "None") - : "Assign a scientist to manage earthquakes"} -
-
-
@@ -674,168 +368,8 @@ export default function AdminPage() { Select a user...
)} - - {/* Right-side assignment panel */} - {showAssignmentPanel && editUser &&( -
-
- - {showAssignmentPanel === 'artefact' ? 'Assign Artefacts' : 'Assign Earthquakes'} - - -
-
- {showAssignmentPanel === 'artefact' && ( - <> - {allArtefacts.map(a => ( - - ))} - - )} - {showAssignmentPanel === 'earthquake' && editUser &&( - <> - {!editUser.scientist - ?
Assign a scientist first.
- : allEarthquakes.map(eq => ( - - ))} - - )} - {showAssignmentPanel === 'scientist' && editUser.scientist &&( - <> - {/* Scientist edit form */} -
- {/* Name */} -
- - - setEditUser(prev => prev && prev.scientist ? { - ...prev, - scientist: { ...prev.scientist, name: e.target.value } - } : prev) - } - /> -
- {/* Level */} -
- - -
- {/* Assign/Remove Superior */} -
- - -
- {/* Subordinates readonly */} -
- -
- {editUser.scientist.subordinates.length - ? editUser.scientist.subordinates.map(sc => sc.name).join(", ") - : None} -
-
-
- - )} -
-
- -
- )}
-
); } \ No newline at end of file