Files
landing_page/refer_landing_page/app/console/page.tsx
T
Godopu d3dc7f19d9 refactor: eliminate dead code, redundant API queries, and enforce empty JSON array slices
- Remove redundant adminGetStatsSummary query and unused AdminStatsSummary interface
- Initialize empty repository slices with make([]T, 0) to ensure empty JSON arrays instead of null
- Add optional chaining in dashboard aggregations for strict null-safety
- Passed 100% unanimous peer reviews from planner-reviewer-claude-01 and reviewer-cline-01
2026-08-25 15:47:31 +09:00

191 lines
5.7 KiB
TypeScript

"use client";
import React, { useEffect, useState } from "react";
import Link from "next/link";
import { AdminHeader, AlertBanner } from "./_components/AdminComponents";
import {
adminGetResearchProjects,
adminGetResearchAreas,
adminGetMembers,
adminGetAlumni,
adminGetPublications,
adminGetPatents,
adminGetSemesters,
adminGetStandardsBodies,
} from "../../lib/adminApi";
export default function AdminDashboardPage() {
const [counts, setCounts] = useState<{
projects: number;
areas: number;
members: number;
alumni: number;
intlPubs: number;
domPubs: number;
patents: number;
semesters: number;
courses: number;
bodies: number;
docs: number;
}>({
projects: 0,
areas: 0,
members: 0,
alumni: 0,
intlPubs: 0,
domPubs: 0,
patents: 0,
semesters: 0,
courses: 0,
bodies: 0,
docs: 0,
});
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function loadData() {
try {
setIsLoading(true);
const [
projects,
areas,
members,
alumni,
intlPubs,
domPubs,
patents,
semesters,
bodies,
] = await Promise.all([
adminGetResearchProjects().catch(() => []),
adminGetResearchAreas().catch(() => []),
adminGetMembers().catch(() => []),
adminGetAlumni().catch(() => []),
adminGetPublications("intl-journal-conf").catch(() => []),
adminGetPublications("domestic-journal-conf").catch(() => []),
adminGetPatents().catch(() => []),
adminGetSemesters().catch(() => []),
adminGetStandardsBodies().catch(() => []),
]);
const safeProjects = projects || [];
const safeAreas = areas || [];
const safeMembers = members || [];
const safeAlumni = alumni || [];
const safeIntlPubs = intlPubs || [];
const safeDomPubs = domPubs || [];
const safePatents = patents || [];
const safeSemesters = semesters || [];
const safeBodies = bodies || [];
const totalCourses = safeSemesters.reduce((acc, s) => acc + (s?.courses?.length || 0), 0);
const totalDocs = safeBodies.reduce(
(acc, b) => acc + ((b?.projects || []).reduce((pAcc, p) => pAcc + (p?.documents?.length || 0), 0) || 0),
0
);
setCounts({
projects: safeProjects.length,
areas: safeAreas.length,
members: safeMembers.length,
alumni: safeAlumni.length,
intlPubs: safeIntlPubs.length,
domPubs: safeDomPubs.length,
patents: safePatents.length,
semesters: safeSemesters.length,
courses: totalCourses,
bodies: safeBodies.length,
docs: totalDocs,
});
} catch (err: any) {
setError(err?.message || "Failed to load dashboard data from backend API.");
} finally {
setIsLoading(false);
}
}
loadData();
}, []);
const cards = [
{
title: "Research Projects",
count: counts.projects,
sub: `${counts.areas} Research Areas`,
href: "/console/research-projects",
color: "border-cobalt/30 hover:border-cobalt",
},
{
title: "Members & Alumni",
count: counts.members,
sub: `${counts.alumni} Alumni Records`,
href: "/console/members",
color: "border-emerald-500/30 hover:border-emerald-500",
},
{
title: "Publications & Patents",
count: counts.intlPubs + counts.domPubs,
sub: `${counts.intlPubs} Intl / ${counts.domPubs} Dom / ${counts.patents} Patents`,
href: "/console/publications",
color: "border-blue-500/30 hover:border-blue-500",
},
{
title: "Lectures & Courses",
count: counts.semesters,
sub: `${counts.courses} Total Courses across Semesters`,
href: "/console/lectures",
color: "border-amber-500/30 hover:border-amber-500",
},
{
title: "Standardization",
count: counts.bodies,
sub: `${counts.docs} Standard Documents`,
href: "/console/standardization",
color: "border-purple-500/30 hover:border-purple-500",
},
{
title: "Research Areas",
count: counts.areas,
sub: "Active Laboratory Focus Areas",
href: "/console/research-areas",
color: "border-indigo-500/30 hover:border-indigo-500",
},
];
return (
<div>
<AdminHeader
title="Administrative Overview"
description="Live status of all ANL laboratory records and datasets stored in the backend."
/>
{error && <AlertBanner type="error" message={error} onClose={() => setError(null)} />}
{isLoading ? (
<div className="py-12 text-center text-sm text-ink/70">Loading dashboard statistics...</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{cards.map((card) => (
<Link
key={card.title}
href={card.href}
className={`p-6 bg-paper rounded-lg border ${card.color} transition-all shadow-sm flex flex-col justify-between`}
>
<div>
<h2 className="text-base font-semibold text-ink">{card.title}</h2>
<div className="text-3xl font-extrabold text-cobalt mt-3">{card.count}</div>
<p className="text-xs text-ink/70 mt-1">{card.sub}</p>
</div>
<div className="mt-4 pt-4 border-t border-line text-xs font-medium text-cobalt flex items-center justify-between">
<span>Manage Records</span>
<span>&rarr;</span>
</div>
</Link>
))}
</div>
)}
</div>
);
}