"use client"; import axios from "axios"; import React, { useState, useEffect } from "react"; import { useStoreState } from "@hooks/store"; // Helper dicts/types const outcomeColors: Record = { FULFILLED: "text-green-700 bg-green-100", REJECTED: "text-red-700 bg-red-100", IN_PROGRESS: "text-blue-700 bg-blue-100", CANCELLED: "text-gray-600 bg-gray-100", OTHER: "text-yellow-800 bg-yellow-100", }; const requestTypeLabels: Record = { NEW_USER: "New User", CHANGE_LEVEL: "Change Level", DELETE: "Removal", }; function formatDate(val?: string) { if (!val) return "--"; return new Date(val).toLocaleString(undefined, { year: "numeric", month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit", }); } // Minimal types type User = { id: number; name: string; email: string; role?: string; scientist?: { level: string } | null; }; type Request = { id: number; createdAt: string; requestType: string; requestingUser: User; outcome: string; }; export default function RequestManagementPage() { const user = useStoreState((s) => s.user); // All hooks first! const [requests, setRequests] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [actionLoading, setActionLoading] = useState(null); const [actionError, setActionError] = useState(null); const [actionSuccess, setActionSuccess] = useState(null); // User role logic must remain invariant per render const userRole = user?.role as string | undefined; const isAdmin = userRole === "ADMIN"; const isSeniorScientist = userRole === "SCIENTIST" && user?.scientist?.level === "SENIOR"; const userId = user?.id; // Requests fetch useEffect(() => { setLoading(true); setError(null); axios .get("/api/requests") .then((res) => { setRequests(res.data.requests || []); setLoading(false); }) .catch(() => { setError("Failed to load requests."); setLoading(false); }); }, []); // Filtering for non-admins to only their requests const filteredRequests = React.useMemo( () => (isAdmin ? requests : requests.filter((r) => r.requestingUser.id === userId)), [isAdmin, requests, userId] ); // Sorted: newest first filteredRequests.sort((a, b) => (b.createdAt ?? "").localeCompare(a.createdAt ?? "")); async function handleAction(requestId: number, action: "FULFILLED" | "REJECTED") { setActionLoading(requestId); setActionError(null); setActionSuccess(null); try { const res = await axios.post("/api/requests", { id: requestId, outcome: action }); setRequests((prev) => prev.map((r) => (r.id === requestId ? { ...r, outcome: action } : r))); setActionSuccess("Request updated."); } catch (err: any) { setActionError(err.response?.data?.error || "Failed to update request"); } finally { setActionLoading(null); } } // Unauthorized access should return early, but not before hooks! if (!isAdmin && !isSeniorScientist) { return (

Unauthorized Access

You do not have access to this page.
); } return (

{isAdmin ? "All Requests" : "My Requests"}

View {isAdmin ? "and manage pending" : "your"} requests related to scientist management.

{loading ? (
Loading...
) : error ? (
{error}
) : (
{isAdmin && } {filteredRequests.length === 0 ? ( ) : ( filteredRequests.map((req) => ( {isAdmin && ( )} )) )}
Date Type Requested By StatusActions
No requests found.
{formatDate(req.createdAt)} {requestTypeLabels[req.requestType] || req.requestType} {req.requestingUser.name} ({req.requestingUser.email}) {req.outcome.replace(/_/g, " ")} {req.outcome === "IN_PROGRESS" ? (
) : ( No actions )} {actionError && actionLoading === req.id &&
{actionError}
} {actionSuccess && actionLoading === req.id && (
{actionSuccess}
)}
)}
); }