feat(i18n,admin): modular localization dictionary & admin console demo preview
Build and Push Docker Image / build-and-push (push) Successful in 13m7s
Build and Push Docker Image / build-and-push (push) Successful in 13m7s
This commit is contained in:
+40
-27
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { getDemoStore } from "@/lib/mockData";
|
||||
import { useI18n } from "@/lib/i18n";
|
||||
|
||||
interface AdminUser {
|
||||
id: string;
|
||||
@@ -46,8 +46,8 @@ const INITIAL_ADMIN_USERS: AdminUser[] = [
|
||||
];
|
||||
|
||||
export default function AdminPage() {
|
||||
const router = useRouter();
|
||||
const { data: session, status } = useSession();
|
||||
const { t } = useI18n();
|
||||
|
||||
const [users, setUsers] = useState<AdminUser[]>(INITIAL_ADMIN_USERS);
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -66,39 +66,40 @@ export default function AdminPage() {
|
||||
return 9;
|
||||
});
|
||||
|
||||
// Is this running in Demo mode (no session or unauthenticated)
|
||||
const isDemo = status === "unauthenticated" || !session;
|
||||
|
||||
// 어드민 권한 판별
|
||||
const userRole = session?.user?.role;
|
||||
const userEmail = session?.user?.email;
|
||||
const isAdmin = userRole === "ADMIN" || userEmail?.endsWith("@checkflow.local") || userEmail?.startsWith("admin@");
|
||||
const isAuthorized =
|
||||
isDemo ||
|
||||
userRole === "ADMIN" ||
|
||||
userEmail?.endsWith("@checkflow.local") ||
|
||||
userEmail?.startsWith("admin@") ||
|
||||
userEmail === "admin@checkflow.local";
|
||||
|
||||
// 인증 게이트: 비로그인 시 /login으로 리디렉션
|
||||
useEffect(() => {
|
||||
if (status === "unauthenticated") {
|
||||
router.replace("/login?callbackUrl=/admin");
|
||||
}
|
||||
}, [status, router]);
|
||||
|
||||
// 로딩 중 스피너
|
||||
if (status === "loading" || status === "unauthenticated") {
|
||||
// 로딩 중
|
||||
if (status === "loading") {
|
||||
return (
|
||||
<div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", background: "var(--bg-primary)" }}>
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<div style={{ fontSize: 32, marginBottom: 12 }}>🔒</div>
|
||||
<p style={{ color: "var(--text-secondary)" }}>Checking administrator credentials...</p>
|
||||
<p style={{ color: "var(--text-secondary)" }}>{t("loading") || "Checking administrator credentials..."}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 비인가 사용자(일반 유저) 차단 화면
|
||||
if (!isAdmin) {
|
||||
if (!isAuthorized) {
|
||||
return (
|
||||
<div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", background: "var(--bg-primary)", color: "var(--text-primary)", padding: 24 }}>
|
||||
<div style={{ maxWidth: 440, width: "100%", background: "var(--bg-secondary)", border: "1px solid var(--border)", borderRadius: "var(--radius-lg)", padding: 32, textAlign: "center" }}>
|
||||
<div style={{ maxWidth: 460, width: "100%", background: "var(--bg-secondary)", border: "1px solid var(--border)", borderRadius: "var(--radius-lg)", padding: 32, textAlign: "center" }}>
|
||||
<div style={{ fontSize: 44, marginBottom: 16 }}>🛡️</div>
|
||||
<h2 style={{ fontSize: 20, fontWeight: 700, margin: "0 0 8px" }}>Access Restricted</h2>
|
||||
<h2 style={{ fontSize: 20, fontWeight: 700, margin: "0 0 8px" }}>{t("accessRestricted") || "Access Restricted"}</h2>
|
||||
<p style={{ fontSize: 13, color: "var(--text-secondary)", lineHeight: 1.6, margin: "0 0 24px" }}>
|
||||
This console requires <strong>ADMIN</strong> privileges. Your account (<code>{session?.user?.email}</code>) does not have authorization to view multi-user system telemetry.
|
||||
{t("adminRequired") || "This console requires ADMIN privileges."} (<code>{session?.user?.email}</code>)
|
||||
</p>
|
||||
<div style={{ display: "flex", gap: 12, justifyContent: "center" }}>
|
||||
<Link href="/demo" className="btn btn-ghost">
|
||||
@@ -140,14 +141,21 @@ export default function AdminPage() {
|
||||
return (
|
||||
<div style={{ minHeight: "100vh", background: "var(--bg-primary)", color: "var(--text-primary)", padding: "24px 32px" }}>
|
||||
{/* Top Navbar */}
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", borderBottom: "1px solid var(--border)", paddingBottom: 16, marginBottom: 24 }}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", borderBottom: "1px solid var(--border)", paddingBottom: 16, marginBottom: 24, flexWrap: "wrap", gap: 12 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
||||
<div className="sidebar-logo">👑</div>
|
||||
<div>
|
||||
<h1 style={{ fontSize: 20, fontWeight: 700, margin: 0, letterSpacing: -0.5 }}>
|
||||
CheckFlow Admin Console
|
||||
</h1>
|
||||
<p style={{ fontSize: 12, color: "var(--text-tertiary)", margin: 0 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<h1 style={{ fontSize: 20, fontWeight: 700, margin: 0, letterSpacing: -0.5 }}>
|
||||
CheckFlow Admin Console
|
||||
</h1>
|
||||
{isDemo && (
|
||||
<span className="demo-badge" style={{ fontSize: 11, padding: "2px 8px" }}>
|
||||
Demo Preview
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p style={{ fontSize: 12, color: "var(--text-tertiary)", margin: "2px 0 0" }}>
|
||||
Multi-user platform & self-hosted instance management
|
||||
</p>
|
||||
</div>
|
||||
@@ -155,10 +163,10 @@ export default function AdminPage() {
|
||||
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<Link href="/demo" className="btn btn-ghost btn-sm">
|
||||
← Back to App (Demo)
|
||||
← Back to Demo
|
||||
</Link>
|
||||
<Link href="/" className="btn btn-primary btn-sm">
|
||||
🚀 Open Main Dashboard
|
||||
🚀 Open Dashboard
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -193,7 +201,12 @@ export default function AdminPage() {
|
||||
{/* User Management Section */}
|
||||
<div style={{ background: "var(--bg-secondary)", border: "1px solid var(--border)", borderRadius: "var(--radius-md)", padding: 20 }}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16, flexWrap: "wrap", gap: 12 }}>
|
||||
<h2 style={{ fontSize: 16, fontWeight: 700, margin: 0 }}>Registered Users & Privacy Isolation</h2>
|
||||
<div>
|
||||
<h2 style={{ fontSize: 16, fontWeight: 700, margin: 0 }}>Registered Users & Privacy Isolation</h2>
|
||||
<p style={{ fontSize: 12, color: "var(--text-secondary)", margin: "4px 0 0" }}>
|
||||
Control multi-user accounts, promote administrators, and enforce workspace separation.
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
className="form-input"
|
||||
placeholder="Search users by name or email..."
|
||||
@@ -288,4 +301,4 @@ export default function AdminPage() {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user