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
This commit is contained in:
@@ -29,7 +29,7 @@ func (r *HomeRepository) GetResearchProjects(ctx context.Context) ([]models.Rese
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var projects []models.ResearchProject
|
projects := make([]models.ResearchProject, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var p models.ResearchProject
|
var p models.ResearchProject
|
||||||
var kwJSON string
|
var kwJSON string
|
||||||
@@ -115,7 +115,7 @@ func (r *HomeRepository) GetResearchAreas(ctx context.Context) ([]models.Researc
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var areas []models.ResearchArea
|
areas := make([]models.ResearchArea, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var a models.ResearchArea
|
var a models.ResearchArea
|
||||||
if err := rows.Scan(&a.ID, &a.NameEn, &a.NameKr, &a.DisplayOrder); err != nil {
|
if err := rows.Scan(&a.ID, &a.NameEn, &a.NameKr, &a.DisplayOrder); err != nil {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (r *LecturesRepository) GetSemestersWithCourses(ctx context.Context) ([]mod
|
|||||||
}
|
}
|
||||||
defer semRows.Close()
|
defer semRows.Close()
|
||||||
|
|
||||||
var semesters []models.SemesterWithCourses
|
semesters := make([]models.SemesterWithCourses, 0)
|
||||||
semMap := make(map[int]int)
|
semMap := make(map[int]int)
|
||||||
|
|
||||||
for semRows.Next() {
|
for semRows.Next() {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (r *MembersRepository) GetMembers(ctx context.Context) ([]models.Member, er
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var members []models.Member
|
members := make([]models.Member, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var m models.Member
|
var m models.Member
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
@@ -94,7 +94,7 @@ func (r *MembersRepository) GetAlumni(ctx context.Context) ([]models.Alumnus, er
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var alumni []models.Alumnus
|
alumni := make([]models.Alumnus, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var a models.Alumnus
|
var a models.Alumnus
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (r *PublicationsRepository) GetPublications(ctx context.Context, category s
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var pubs []models.Publication
|
pubs := make([]models.Publication, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var p models.Publication
|
var p models.Publication
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
@@ -101,7 +101,7 @@ func (r *PublicationsRepository) GetHighlights(ctx context.Context) ([]models.Pu
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var pubs []models.Publication
|
pubs := make([]models.Publication, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var p models.Publication
|
var p models.Publication
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
@@ -127,7 +127,7 @@ func (r *PublicationsRepository) GetPatents(ctx context.Context) ([]models.Paten
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var patents []models.Patent
|
patents := make([]models.Patent, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var p models.Patent
|
var p models.Patent
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (r *StandardizationRepository) GetStandardsBodiesWithProjects(ctx context.C
|
|||||||
}
|
}
|
||||||
defer bodyRows.Close()
|
defer bodyRows.Close()
|
||||||
|
|
||||||
var bodies []models.StandardsBodyWithProjects
|
bodies := make([]models.StandardsBodyWithProjects, 0)
|
||||||
bodyMap := make(map[int]int)
|
bodyMap := make(map[int]int)
|
||||||
|
|
||||||
for bodyRows.Next() {
|
for bodyRows.Next() {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import React, { useEffect, useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { AdminHeader, AlertBanner } from "./_components/AdminComponents";
|
import { AdminHeader, AlertBanner } from "./_components/AdminComponents";
|
||||||
import {
|
import {
|
||||||
adminGetStatsSummary,
|
|
||||||
adminGetResearchProjects,
|
adminGetResearchProjects,
|
||||||
adminGetResearchAreas,
|
adminGetResearchAreas,
|
||||||
adminGetMembers,
|
adminGetMembers,
|
||||||
@@ -13,11 +12,9 @@ import {
|
|||||||
adminGetPatents,
|
adminGetPatents,
|
||||||
adminGetSemesters,
|
adminGetSemesters,
|
||||||
adminGetStandardsBodies,
|
adminGetStandardsBodies,
|
||||||
AdminStatsSummary,
|
|
||||||
} from "../../lib/adminApi";
|
} from "../../lib/adminApi";
|
||||||
|
|
||||||
export default function AdminDashboardPage() {
|
export default function AdminDashboardPage() {
|
||||||
const [stats, setStats] = useState<AdminStatsSummary | null>(null);
|
|
||||||
const [counts, setCounts] = useState<{
|
const [counts, setCounts] = useState<{
|
||||||
projects: number;
|
projects: number;
|
||||||
areas: number;
|
areas: number;
|
||||||
@@ -51,7 +48,6 @@ export default function AdminDashboardPage() {
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const [
|
const [
|
||||||
statsData,
|
|
||||||
projects,
|
projects,
|
||||||
areas,
|
areas,
|
||||||
members,
|
members,
|
||||||
@@ -62,7 +58,6 @@ export default function AdminDashboardPage() {
|
|||||||
semesters,
|
semesters,
|
||||||
bodies,
|
bodies,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
adminGetStatsSummary().catch(() => null),
|
|
||||||
adminGetResearchProjects().catch(() => []),
|
adminGetResearchProjects().catch(() => []),
|
||||||
adminGetResearchAreas().catch(() => []),
|
adminGetResearchAreas().catch(() => []),
|
||||||
adminGetMembers().catch(() => []),
|
adminGetMembers().catch(() => []),
|
||||||
@@ -74,25 +69,33 @@ export default function AdminDashboardPage() {
|
|||||||
adminGetStandardsBodies().catch(() => []),
|
adminGetStandardsBodies().catch(() => []),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (statsData) setStats(statsData);
|
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 = semesters.reduce((acc, s) => acc + (s.courses?.length || 0), 0);
|
const totalCourses = safeSemesters.reduce((acc, s) => acc + (s?.courses?.length || 0), 0);
|
||||||
const totalDocs = bodies.reduce(
|
const totalDocs = safeBodies.reduce(
|
||||||
(acc, b) => acc + (b.projects?.reduce((pAcc, p) => pAcc + (p.documents?.length || 0), 0) || 0),
|
(acc, b) => acc + ((b?.projects || []).reduce((pAcc, p) => pAcc + (p?.documents?.length || 0), 0) || 0),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
setCounts({
|
setCounts({
|
||||||
projects: projects.length,
|
projects: safeProjects.length,
|
||||||
areas: areas.length,
|
areas: safeAreas.length,
|
||||||
members: members.length,
|
members: safeMembers.length,
|
||||||
alumni: alumni.length,
|
alumni: safeAlumni.length,
|
||||||
intlPubs: intlPubs.length,
|
intlPubs: safeIntlPubs.length,
|
||||||
domPubs: domPubs.length,
|
domPubs: safeDomPubs.length,
|
||||||
patents: patents.length,
|
patents: safePatents.length,
|
||||||
semesters: semesters.length,
|
semesters: safeSemesters.length,
|
||||||
courses: totalCourses,
|
courses: totalCourses,
|
||||||
bodies: bodies.length,
|
bodies: safeBodies.length,
|
||||||
docs: totalDocs,
|
docs: totalDocs,
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|||||||
@@ -149,12 +149,6 @@ export interface AdminStandardsBody {
|
|||||||
projects: AdminStandardProjectGroup[];
|
projects: AdminStandardProjectGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminStatsSummary {
|
|
||||||
intl_publications: number;
|
|
||||||
standardization_docs: number;
|
|
||||||
patents: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function request<T>(
|
async function request<T>(
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
options: RequestInit = {},
|
options: RequestInit = {},
|
||||||
@@ -208,11 +202,6 @@ export async function adminVerifyToken(token: string): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Stats Summary
|
|
||||||
export async function adminGetStatsSummary(): Promise<AdminStatsSummary> {
|
|
||||||
return request<AdminStatsSummary>("/stats/summary");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Research Projects
|
// 2. Research Projects
|
||||||
export async function adminGetResearchProjects(): Promise<AdminResearchProject[]> {
|
export async function adminGetResearchProjects(): Promise<AdminResearchProject[]> {
|
||||||
return request<AdminResearchProject[]>("/research-projects");
|
return request<AdminResearchProject[]>("/research-projects");
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ def verify():
|
|||||||
try:
|
try:
|
||||||
def fetch_json(endpoint):
|
def fetch_json(endpoint):
|
||||||
req = urllib.request.Request(f"{API_BASE_URL}{endpoint}", headers={"User-Agent": "DataVerifier"})
|
req = urllib.request.Request(f"{API_BASE_URL}{endpoint}", headers={"User-Agent": "DataVerifier"})
|
||||||
with urllib.request.urlopen(req, timeout=3) as resp:
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||||
data = json.loads(resp.read().decode("utf-8"))
|
data = json.loads(resp.read().decode("utf-8"))
|
||||||
return data.get("data", [])
|
return data.get("data", [])
|
||||||
|
|
||||||
@@ -202,8 +202,7 @@ def verify():
|
|||||||
|
|
||||||
print(" ✅ Live REST API endpoints serve 100% losslessly verified data")
|
print(" ✅ Live REST API endpoints serve 100% losslessly verified data")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" ⚠️ Live API transient query skipped ({e})")
|
errors.append(f"Live API verification error: {e}")
|
||||||
# If SQLite DB was already verified, don't fail the whole static gate on background live server fluctuation
|
|
||||||
else:
|
else:
|
||||||
print(f"\nℹ️ Live Go API server not responding at {API_BASE_URL} (tested offline DB mode)")
|
print(f"\nℹ️ Live Go API server not responding at {API_BASE_URL} (tested offline DB mode)")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user