/** * Admin API client for /console dashboard. * Preserves database IDs and handles authenticated mutating requests (POST, PUT, DELETE). */ export function getAdminApiBaseUrl(): string { if (typeof window !== "undefined") { // Browser / CSR context: same-origin relative path by default. return process.env.NEXT_PUBLIC_API_BASE_URL || "/api/v1"; } // Server / build-time context: defensive fallback during static prerendering return ( process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8080/api/v1" ); } export interface AdminResearchProject { id: number; slug: string; title: string; abstract: string; keywords: string[]; organization?: string; standards_org?: string; period?: string; funder?: string; created_at?: string; updated_at?: string; } export interface AdminResearchArea { id: number; name_en: string; name_kr?: string; display_order: number; } export interface AdminMember { id: number; name_ko: string; name_en: string; degree: string; affiliation?: string; email?: string; is_advisor: boolean; display_order: number; created_at?: string; } export interface AdminAlumnus { id: number; name_ko: string; name_en: string; degree: string; graduated_at: string; major?: string; current_position?: string; created_at?: string; } export interface AdminPublication { id: number; category: "intl-journal-conf" | "domestic-journal-conf"; title: string; authors?: string; venue: string; volume?: string; published_at: string; kci: boolean; doi?: string; is_highlight: boolean; created_at?: string; } export interface AdminPatent { id: number; title: string; inventors: string; application_no: string; application_at: string; registration_no?: string; registration_at?: string; country: string; published_at: string; created_at?: string; } export interface AdminCourse { id: number; semester_id: number; code: string; name_en: string; name_kr?: string; note?: string; display_order: number; } export interface AdminSemester { id: number; year: number; term: "Spring" | "Fall"; courses: AdminCourse[]; } export interface AdminStandardDocument { id: number; body_id: number; wg: string; project_name: string; title: string; doc_ref: string; status: "IS" | "TS" | "TR" | "CDV" | "WDTR" | "InProgress"; published_at?: string; } export interface AdminStandardProjectGroup { wg: string; name: string; documents: AdminStandardDocument[]; } export interface AdminStandardsBody { id: number; org: string; full_name: string; scope: "international" | "domestic"; period: string; role?: string; display_order: number; projects: AdminStandardProjectGroup[]; } export interface AdminStatsSummary { intl_publications: number; standardization_docs: number; patents: number; } async function request( endpoint: string, options: RequestInit = {}, token?: string ): Promise { const headers = new Headers(options.headers || {}); if (token) { headers.set("Authorization", `Bearer ${token}`); } if (options.body && typeof options.body === "string") { headers.set("Content-Type", "application/json"); } const baseUrl = getAdminApiBaseUrl(); const url = `${baseUrl}${endpoint}`; const response = await fetch(url, { ...options, headers, cache: "no-store", }); if (response.status === 204) { return {} as T; } const json = await response.json().catch(() => ({})); if (!response.ok) { const errorMsg = json?.error?.message || `Request failed with status ${response.status}`; throw new Error(errorMsg); } return json.data; } // 0. Auth Verification export async function adminVerifyToken(token: string): Promise { try { await request<{ status: string }>("/admin/verify", { method: "GET" }, token); return true; } catch { return false; } } // 1. Stats Summary export async function adminGetStatsSummary(): Promise { return request("/stats/summary"); } // 2. Research Projects export async function adminGetResearchProjects(): Promise { return request("/research-projects"); } export async function adminCreateResearchProject( token: string, data: Omit ): Promise { return request("/research-projects", { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdateResearchProject( token: string, id: number, data: Partial ): Promise { return request(`/research-projects/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeleteResearchProject(token: string, id: number): Promise { return request(`/research-projects/${id}`, { method: "DELETE" }, token); } // 3. Research Areas export async function adminGetResearchAreas(): Promise { return request("/research-areas"); } export async function adminCreateResearchArea( token: string, data: Omit ): Promise { return request("/research-areas", { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdateResearchArea( token: string, id: number, data: Partial ): Promise { return request(`/research-areas/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeleteResearchArea(token: string, id: number): Promise { return request(`/research-areas/${id}`, { method: "DELETE" }, token); } // 4. Members & Alumni export async function adminGetMembers(): Promise { return request("/members"); } export async function adminCreateMember( token: string, data: Omit ): Promise { return request("/members", { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdateMember( token: string, id: number, data: Partial ): Promise { return request(`/members/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeleteMember(token: string, id: number): Promise { return request(`/members/${id}`, { method: "DELETE" }, token); } export async function adminGetAlumni(): Promise { return request("/alumni"); } export async function adminCreateAlumnus( token: string, data: Omit ): Promise { return request("/alumni", { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdateAlumnus( token: string, id: number, data: Partial ): Promise { return request(`/alumni/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeleteAlumnus(token: string, id: number): Promise { return request(`/alumni/${id}`, { method: "DELETE" }, token); } // 5. Publications & Patents export async function adminGetPublications(category?: string): Promise { const query = category ? `?category=${category}` : ""; return request(`/publications${query}`); } export async function adminCreatePublication( token: string, data: Omit ): Promise { return request("/publications", { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdatePublication( token: string, id: number, data: Partial ): Promise { return request(`/publications/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeletePublication(token: string, id: number): Promise { return request(`/publications/${id}`, { method: "DELETE" }, token); } export async function adminGetPatents(): Promise { return request("/patents"); } export async function adminCreatePatent( token: string, data: Omit ): Promise { return request("/patents", { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdatePatent( token: string, id: number, data: Partial ): Promise { return request(`/patents/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeletePatent(token: string, id: number): Promise { return request(`/patents/${id}`, { method: "DELETE" }, token); } // 6. Lectures (Semesters & Courses) export async function adminGetSemesters(): Promise { return request("/semesters"); } export async function adminCreateSemester( token: string, data: Omit ): Promise { return request("/semesters", { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdateSemester( token: string, id: number, data: Partial ): Promise { return request(`/semesters/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeleteSemester(token: string, id: number): Promise { return request(`/semesters/${id}`, { method: "DELETE" }, token); } export async function adminCreateCourse( token: string, semesterId: number, data: Omit ): Promise { return request(`/semesters/${semesterId}/courses`, { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdateCourse( token: string, id: number, data: Partial ): Promise { return request(`/courses/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeleteCourse(token: string, id: number): Promise { return request(`/courses/${id}`, { method: "DELETE" }, token); } // 7. Standardization (Bodies & Documents) export async function adminGetStandardsBodies(): Promise { return request("/standards-bodies"); } export async function adminCreateStandardsBody( token: string, data: Omit ): Promise { return request("/standards-bodies", { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdateStandardsBody( token: string, id: number, data: Partial ): Promise { return request(`/standards-bodies/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeleteStandardsBody(token: string, id: number): Promise { return request(`/standards-bodies/${id}`, { method: "DELETE" }, token); } export async function adminCreateStandardDocument( token: string, bodyId: number, data: Omit ): Promise { return request(`/standards-bodies/${bodyId}/documents`, { method: "POST", body: JSON.stringify(data), }, token); } export async function adminUpdateStandardDocument( token: string, id: number, data: Partial ): Promise { return request(`/standard-documents/${id}`, { method: "PUT", body: JSON.stringify(data), }, token); } export async function adminDeleteStandardDocument(token: string, id: number): Promise { return request(`/standard-documents/${id}`, { method: "DELETE" }, token); }