"use client"; import { useEffect, useState, useMemo } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { Search, ChevronRight, AlertCircle, ArrowUpDown } from "lucide-react"; import Link from "next/link"; import { useCachedFetch } from "@/hooks/useCachedFetch"; interface AdminIssueListItem { id: string; description: string; state: string; priority: number; city: string; created_at: string; department: string; assigned_to: string; category: string; thumbnail: string; locality?: string; } interface Meta { total: number; page: number; limit: number; pages: number; } interface IssuesResponse { items: AdminIssueListItem[]; total: number; page: number; limit: number; pages: number; } export default function IssuesPage() { const router = useRouter(); const searchParams = useSearchParams(); const [page, setPage] = useState(1); const limit = 10; const [search, setSearch] = useState(""); const [status, setStatus] = useState(""); const [priority, setPriority] = useState(""); const [sort, setSort] = useState("created_at"); const [order, setOrder] = useState("desc"); const [debouncedSearch, setDebouncedSearch] = useState(""); useEffect(() => { const statusParam = searchParams.get("status"); if (statusParam) { setStatus(statusParam); } }, [searchParams]); useEffect(() => { const handler = setTimeout(() => { setDebouncedSearch(search); }, 1500); return () => clearTimeout(handler); }, [search]); // Construct Query URL dynamically const queryUrl = useMemo(() => { const query = new URLSearchParams({ page: page.toString(), limit: limit.toString(), sort_by: sort, sort_order: order, }); if (debouncedSearch) query.append("search", debouncedSearch); if (status) query.append("status", status); if (priority) query.append("priority", priority); return `/admin/issues?${query.toString()}`; }, [page, limit, sort, order, debouncedSearch, status, priority]); const { data: issuesData, loading } = useCachedFetch(queryUrl); const issues = issuesData?.items || []; const meta: Meta = { total: issuesData?.total || 0, page: issuesData?.page || 1, limit: issuesData?.limit || 10, pages: issuesData?.pages || 0, }; const handlePageChange = (newPage: number) => { if (newPage > 0 && newPage <= meta.pages) { setPage(newPage); } }; const getStateBadge = (state: string) => { const styles: Record = { reported: "bg-blue-100 text-blue-700 border-blue-200", assigned: "bg-purple-100 text-purple-700 border-purple-200", in_progress: "bg-amber-100 text-amber-700 border-amber-200", pending_verification: "bg-orange-100 text-orange-700 border-orange-200", resolved: "bg-emerald-100 text-emerald-700 border-emerald-200", closed: "bg-slate-100 text-slate-600 border-slate-200", escalated: "bg-red-100 text-red-700 border-red-200 animate-pulse", rejected: "bg-gray-100 text-gray-500 border-gray-200 line-through", verified: "bg-indigo-100 text-indigo-700 border-indigo-200", }; return ( {state.replace("_", " ").toUpperCase()} ); }; return (

Issue Management

Monitor, assign, and resolve reported city issues.

setSearch(e.target.value)} className="w-full pl-10 pr-4 py-2.5 text-sm border border-slate-200 bg-white/70 rounded-xl focus:outline-none focus:ring-4 focus:ring-urban-primary/10 focus:border-urban-primary/40 transition-all font-sans" />
{loading ? (
Loading issues...
) : (
{issues.length === 0 ? ( ) : ( issues.map((issue) => ( )) )}
Issue Location Status Assigned To Action
No issues found matching your filters.
{issue.thumbnail ? ( ) : (
)}
{issue.category || "Uncategorized Issue"}
{issue.description || "No description provided"}
{issue.city || "Unknown"} {issue.locality || ""}
P{issue.priority} {getStateBadge(issue.state)} {issue.assigned_to ? (
{issue.assigned_to.charAt(0)}
{issue.assigned_to}
) : ( Unassigned )} {issue.department && (
{issue.department}
)}
{new Date(issue.created_at).toLocaleDateString()}
{new Date(issue.created_at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", })}
View
)}
Showing{" "} {(meta.page - 1) * meta.limit + 1} {" "} to{" "} {Math.min(meta.page * meta.limit, meta.total)} {" "} of{" "} {meta.total}{" "} results
); }