1
0
forked from baron/baron-sso

adminfront 상단 화면 i18n 정리

This commit is contained in:
2026-05-18 13:14:19 +09:00
parent 279bfae9ec
commit 222dc6f4a4
16 changed files with 1090 additions and 67 deletions

View File

@@ -0,0 +1,36 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it } from "vitest";
import { createI18nMock } from "../../test/i18nMock";
import AuthPage from "./AuthPage";
vi.mock("../../lib/i18n", () => createI18nMock());
function renderPage() {
const queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
});
return render(
<QueryClientProvider client={queryClient}>
<AuthPage />
</QueryClientProvider>,
);
}
describe("AuthPage", () => {
beforeEach(() => {
window.localStorage.setItem("locale", "en");
});
it("renders localized auth guard labels in English", () => {
renderPage();
expect(screen.getByText("Auth Guard")).toBeInTheDocument();
expect(screen.getByText("ReBAC permission checker")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Check permission" })).toBeInTheDocument();
});
});

View File

@@ -1,4 +1,5 @@
import { KeyRound } from "lucide-react";
import { t } from "../../lib/i18n";
import PermissionChecker from "./components/PermissionChecker";
function AuthPage() {
@@ -6,15 +7,15 @@ function AuthPage() {
<div className="space-y-6">
<div className="flex flex-wrap items-end justify-between gap-4">
<div className="space-y-1">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted-foreground">
Admin auth
</p>
<h2 className="flex items-center gap-2 text-2xl font-semibold tracking-tight">
<KeyRound size={22} className="text-primary" />
{t("ui.admin.auth_guard.title", "Auth Guard")}
</h2>
<p className="text-sm text-muted-foreground">
ReBAC .
{t(
"ui.admin.auth_guard.subtitle",
"Verify admin privileges and ReBAC relationships against the policy engine.",
)}
</p>
</div>
</div>

View File

@@ -12,6 +12,7 @@ import {
import { Input } from "../../../components/ui/input";
import { Label } from "../../../components/ui/label";
import apiClient from "../../../lib/apiClient";
import { t } from "../../../lib/i18n";
type CheckPermissionResponse = {
allowed: boolean;
@@ -48,48 +49,83 @@ function PermissionChecker() {
<CardHeader>
<CardTitle className="flex items-center gap-2">
<ShieldAlert size={20} className="text-primary" />
ReBAC
{t(
"ui.admin.auth_guard.checker.title",
"ReBAC permission checker",
)}
</CardTitle>
<CardDescription>
(Subject) (Object) Ory
Keto를 .
{t(
"ui.admin.auth_guard.checker.description",
"Check in real time whether a subject has access to a resource through Ory Keto.",
)}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<div className="space-y-2">
<Label>Namespace</Label>
<Label>
{t("ui.admin.auth_guard.checker.namespace.label", "Namespace")}
</Label>
<select
value={namespace}
onChange={(e) => setNamespace(e.target.value)}
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
>
<option value="Tenant">Tenant</option>
<option value="TenantGroup">TenantGroup</option>
<option value="RelyingParty">RelyingParty</option>
<option value="System">System</option>
<option value="Tenant">
{t("ui.admin.auth_guard.checker.namespace.tenant", "Tenant")}
</option>
<option value="TenantGroup">
{t(
"ui.admin.auth_guard.checker.namespace.tenant_group",
"TenantGroup",
)}
</option>
<option value="RelyingParty">
{t(
"ui.admin.auth_guard.checker.namespace.relying_party",
"RelyingParty",
)}
</option>
<option value="System">
{t("ui.admin.auth_guard.checker.namespace.system", "System")}
</option>
</select>
</div>
<div className="space-y-2">
<Label>Relation</Label>
<Label>{t("ui.admin.auth_guard.checker.relation", "Relation")}</Label>
<Input
placeholder="view, manage, admins..."
placeholder={t(
"ui.admin.auth_guard.checker.relation_placeholder",
"view, manage, admins...",
)}
value={relation}
onChange={(e) => setRelation(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>Object ID</Label>
<Label>{t("ui.admin.auth_guard.checker.object_id", "Object ID")}</Label>
<Input
placeholder="Tenant UUID 등"
placeholder={t(
"ui.admin.auth_guard.checker.object_id_placeholder",
"Tenant UUID, etc.",
)}
value={object}
onChange={(e) => setObject(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>Subject (User:ID)</Label>
<Label>
{t(
"ui.admin.auth_guard.checker.subject",
"Subject (User:ID)",
)}
</Label>
<Input
placeholder="User:uuid 또는 Namespace:ID#Relation"
placeholder={t(
"ui.admin.auth_guard.checker.subject_placeholder",
"User:uuid or Namespace:ID#Relation",
)}
value={subject}
onChange={(e) => setSubject(e.target.value)}
/>
@@ -102,7 +138,9 @@ function PermissionChecker() {
disabled={!object || !subject || checkMutation.isPending}
className="w-full px-12 md:w-auto"
>
{checkMutation.isPending ? "검증 중..." : "권한 확인 실행"}
{checkMutation.isPending
? t("ui.admin.auth_guard.checker.checking", "Checking...")
: t("ui.admin.auth_guard.checker.check", "Check permission")}
</Button>
</div>
@@ -117,18 +155,33 @@ function PermissionChecker() {
{result.allowed ? (
<>
<CheckCircle2 size={48} />
<div className="text-xl font-bold">Access ALLOWED</div>
<div className="text-xl font-bold">
{t(
"ui.admin.auth_guard.checker.allowed",
"Access ALLOWED",
)}
</div>
<p className="text-center text-sm opacity-80">
. (
)
{t(
"ui.admin.auth_guard.checker.allowed_description",
"The subject has access to the requested resource, including inherited permissions.",
)}
</p>
</>
) : (
<>
<XCircle size={48} />
<div className="text-xl font-bold">Access DENIED</div>
<div className="text-xl font-bold">
{t(
"ui.admin.auth_guard.checker.denied",
"Access DENIED",
)}
</div>
<p className="text-center text-sm opacity-80">
.
{t(
"ui.admin.auth_guard.checker.denied_description",
"The subject does not have access to the requested resource.",
)}
</p>
</>
)}