1
0
forked from baron/baron-sso

i18n refresh and frontend fixes

This commit is contained in:
Lectom C Han
2026-02-10 19:15:51 +09:00
parent 2441c64598
commit b6d3b69cda
44 changed files with 8603 additions and 1760 deletions

View File

@@ -8,37 +8,73 @@ import {
ShieldCheck,
Sparkles,
} from "lucide-react";
import { t } from "../../lib/i18n";
const guardHighlights = [
{
title: "RP 정책 통제",
body: "Relying Party 상태를 활성/비활성으로 관리하고 정책 변경을 기록합니다.",
metric: "Policy",
titleKey: "ui.dev.dashboard.guard.policy.title",
titleFallback: "RP 정책 통제",
bodyKey: "msg.dev.dashboard.guard.policy.body",
bodyFallback:
"Relying Party 상태를 활성/비활성으로 관리하고 정책 변경을 기록합니다.",
metricKey: "ui.dev.dashboard.guard.policy.metric",
metricFallback: "Policy",
},
{
title: "Consent 흐름",
body: "사용자 Consent를 조회하고 필요 시 회수해 리스크를 제어합니다.",
metric: "Consent",
titleKey: "ui.dev.dashboard.guard.consent.title",
titleFallback: "Consent 흐름",
bodyKey: "msg.dev.dashboard.guard.consent.body",
bodyFallback:
"사용자 Consent를 조회하고 필요 시 회수해 리스크를 제어합니다.",
metricKey: "ui.dev.dashboard.guard.consent.metric",
metricFallback: "Consent",
},
{
title: "Hydra Admin",
body: "Hydra Admin API를 통해 RP 등록 현황을 동기화합니다.",
metric: "Hydra",
titleKey: "ui.dev.dashboard.guard.hydra.title",
titleFallback: "Hydra Admin",
bodyKey: "msg.dev.dashboard.guard.hydra.body",
bodyFallback: "Hydra Admin API를 통해 RP 등록 현황을 동기화합니다.",
metricKey: "ui.dev.dashboard.guard.hydra.metric",
metricFallback: "Hydra",
},
];
const stackReadiness = [
"React 19 + Vite 7, strict TS, Router v6 data router.",
"TanStack Query 5로 RP/Consent 데이터를 캐시합니다.",
"Axios 클라이언트에서 Bearer + 테넌트 헤더를 주입합니다.",
"Tailwind + shadcn/ui로 devfront 톤을 맞춥니다.",
"Hydra Admin API 연동을 위한 프록시 엔드포인트 준비.",
{
key: "msg.dev.dashboard.stack.react",
fallback: "React 19 + Vite 7, strict TS, Router v6 data router.",
},
{
key: "msg.dev.dashboard.stack.query",
fallback: "TanStack Query 5로 RP/Consent 데이터를 캐시합니다.",
},
{
key: "msg.dev.dashboard.stack.axios",
fallback: "Axios 클라이언트에서 Bearer + 테넌트 헤더를 주입합니다.",
},
{
key: "msg.dev.dashboard.stack.tailwind",
fallback: "Tailwind + shadcn/ui로 devfront 톤을 맞춥니다.",
},
{
key: "msg.dev.dashboard.stack.proxy",
fallback: "Hydra Admin API 연동을 위한 프록시 엔드포인트 준비.",
},
];
const nextSteps = [
"RP 등록/수정/삭제 워크플로우 추가",
"Consent 검색 필터 고도화 및 CSV 내보내기",
"권한 가드 및 감사 로그 연동",
{
key: "msg.dev.dashboard.next.rp_workflow",
fallback: "RP 등록/수정/삭제 워크플로우 추가",
},
{
key: "msg.dev.dashboard.next.consent_filters",
fallback: "Consent 검색 필터 고도화 및 CSV 내보내기",
},
{
key: "msg.dev.dashboard.next.audit_guard",
fallback: "권한 가드 및 감사 로그 연동",
},
];
function DashboardPage() {
@@ -50,41 +86,63 @@ function DashboardPage() {
<div className="space-y-3 max-w-2xl">
<div className="inline-flex items-center gap-2 rounded-full border border-[var(--color-border)] px-3 py-1 text-xs uppercase tracking-[0.2em] text-[var(--color-muted)]">
<Sparkles size={14} />
devfront ready
{t("ui.dev.dashboard.ready_badge", "devfront ready")}
</div>
<h2 className="text-3xl font-semibold leading-tight">
RP Consent
<span className="text-[var(--color-accent)]"> </span>
.
{t(
"msg.dev.dashboard.hero.title_prefix",
"RP 등록 현황과 Consent 상태를",
)}
<span className="text-[var(--color-accent)]">
{t("msg.dev.dashboard.hero.title_emphasis", " 하나의 화면")}
</span>
{t("msg.dev.dashboard.hero.title_suffix", "에서 관리합니다.")}
</h2>
<p className="text-[var(--color-muted)]">
Hydra Admin API와 RP , , Consent
devfront에서 .
{t(
"msg.dev.dashboard.hero.body",
"Hydra Admin API와 동기화된 RP 목록, 상태 토글, Consent 회수까지 devfront에서 처리하도록 준비합니다.",
)}
</p>
<div className="flex flex-wrap gap-3 text-sm">
<span className="rounded-full bg-[rgba(54,211,153,0.16)] px-3 py-2 text-[var(--color-accent)]">
RP registry synced
{t("ui.dev.dashboard.badge.rp_synced", "RP registry synced")}
</span>
<span className="rounded-full border border-[var(--color-border)] px-3 py-2 text-[var(--color-muted)]">
Consent guard ready
{t(
"ui.dev.dashboard.badge.consent_guard",
"Consent guard ready",
)}
</span>
<span className="rounded-full bg-[rgba(249,168,38,0.16)] px-3 py-2 font-semibold text-[var(--color-accent-strong)]">
Policy toggle enabled
{t(
"ui.dev.dashboard.badge.policy_toggle",
"Policy toggle enabled",
)}
</span>
</div>
</div>
<div className="grid gap-3 text-sm">
<div className="flex items-center gap-2 rounded-xl border border-[var(--color-border)] bg-[rgba(255,255,255,0.02)] px-4 py-3 text-[var(--color-muted)]">
<ShieldCheck size={16} />
RP dev scope에서만
{t(
"msg.dev.dashboard.notice.dev_scope",
"RP 정책은 dev scope에서만 적용",
)}
</div>
<div className="flex items-center gap-2 rounded-xl border border-[var(--color-border)] bg-[rgba(255,255,255,0.02)] px-4 py-3 text-[var(--color-muted)]">
<KeyRound size={16} />
Consent
{t(
"msg.dev.dashboard.notice.consent_audit",
"Consent 회수는 감사 로그와 연계",
)}
</div>
<div className="flex items-center gap-2 rounded-xl border border-[var(--color-border)] bg-[rgba(255,255,255,0.02)] px-4 py-3 text-[var(--color-muted)]">
<Database size={16} />
Hydra Admin
{t(
"msg.dev.dashboard.notice.hydra_health",
"Hydra Admin 상태 체크 준비",
)}
</div>
</div>
</div>
@@ -93,21 +151,25 @@ function DashboardPage() {
<section className="grid gap-4 md:grid-cols-3">
{guardHighlights.map((item) => (
<div
key={item.title}
key={item.titleKey}
className="relative overflow-hidden rounded-xl border border-[var(--color-border)] bg-[var(--color-panel)] p-5 transition hover:-translate-y-1 hover:shadow-[0_16px_48px_rgba(7,15,26,0.4)]"
>
<div className="absolute inset-0 bg-[radial-gradient(circle_at_25%_25%,rgba(54,211,153,0.12),transparent_45%)]" />
<div className="relative flex items-center justify-between gap-2">
<div className="text-xs uppercase tracking-[0.2em] text-[var(--color-muted)]">
{item.metric}
{t(item.metricKey, item.metricFallback)}
</div>
<span className="rounded-full border border-[var(--color-border)] px-3 py-1 text-[11px] text-[var(--color-muted)]">
active
{t("ui.common.status.active", "active")}
</span>
</div>
<div className="relative mt-3 space-y-2">
<h3 className="text-lg font-semibold">{item.title}</h3>
<p className="text-sm text-[var(--color-muted)]">{item.body}</p>
<h3 className="text-lg font-semibold">
{t(item.titleKey, item.titleFallback)}
</h3>
<p className="text-sm text-[var(--color-muted)]">
{t(item.bodyKey, item.bodyFallback)}
</p>
</div>
</div>
))}
@@ -118,29 +180,31 @@ function DashboardPage() {
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-xs uppercase tracking-[0.2em] text-[var(--color-muted)]">
Stack readiness
{t("ui.dev.dashboard.stack.title", "Stack readiness")}
</p>
<h3 className="text-xl font-semibold">Devfront baseline</h3>
<h3 className="text-xl font-semibold">
{t("ui.dev.dashboard.stack.subtitle", "Devfront baseline")}
</h3>
</div>
<button
type="button"
className="inline-flex items-center gap-2 rounded-full border border-[var(--color-border)] px-3 py-2 text-sm text-[var(--color-muted)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent)]"
>
Setup notes
{t("ui.dev.dashboard.stack.notes", "Setup notes")}
<ArrowRight size={14} />
</button>
</div>
<div className="mt-4 grid gap-3 md:grid-cols-2">
{stackReadiness.map((item) => (
<div
key={item}
key={item.key}
className="flex items-center gap-3 rounded-xl border border-[var(--color-border)] bg-[rgba(255,255,255,0.02)] px-4 py-3"
>
<CheckCircle2
size={16}
className="text-[var(--color-accent)]"
/>
<p className="text-sm">{item}</p>
<p className="text-sm">{t(item.key, item.fallback)}</p>
</div>
))}
</div>
@@ -148,19 +212,23 @@ function DashboardPage() {
<div className="rounded-2xl border border-[var(--color-border)] bg-[var(--color-panel)] p-6">
<p className="text-xs uppercase tracking-[0.2em] text-[var(--color-muted)]">
Next actions
{t("ui.dev.dashboard.next.title", "Next actions")}
</p>
<h3 className="mt-2 text-xl font-semibold">Ship the RP controls</h3>
<h3 className="mt-2 text-xl font-semibold">
{t("ui.dev.dashboard.next.subtitle", "Ship the RP controls")}
</h3>
<div className="mt-4 space-y-3">
{nextSteps.map((item, idx) => (
<div
key={item}
key={item.key}
className="flex gap-3 rounded-xl border border-[var(--color-border)] bg-[rgba(255,255,255,0.02)] px-4 py-3"
>
<div className="grid h-8 w-8 place-items-center rounded-full bg-[rgba(249,168,38,0.12)] text-sm font-semibold text-[var(--color-accent-strong)]">
{idx + 1}
</div>
<p className="text-sm text-[var(--color-text)]">{item}</p>
<p className="text-sm text-[var(--color-text)]">
{t(item.key, item.fallback)}
</p>
</div>
))}
</div>
@@ -171,16 +239,18 @@ function DashboardPage() {
<div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
<div>
<p className="text-xs uppercase tracking-[0.2em] text-[var(--color-muted)]">
Ops board
{t("ui.dev.dashboard.ops.title", "Ops board")}
</p>
<h3 className="text-xl font-semibold"> </h3>
<h3 className="text-xl font-semibold">
{t("ui.dev.dashboard.ops.subtitle", "현재 관측")}
</h3>
</div>
<div className="flex items-center gap-2 text-sm text-[var(--color-muted)]">
<span className="rounded-full border border-[var(--color-border)] px-3 py-2">
Consent grants
{t("ui.dev.dashboard.ops.tag.consent", "Consent grants")}
</span>
<span className="rounded-full border border-[var(--color-border)] px-3 py-2">
RP status
{t("ui.dev.dashboard.ops.tag.rp_status", "RP status")}
</span>
</div>
</div>
@@ -188,23 +258,32 @@ function DashboardPage() {
<div className="rounded-xl border border-[var(--color-border)] bg-[rgba(255,255,255,0.02)] p-4">
<div className="flex items-center gap-2 text-[var(--color-muted)]">
<BarChart3 size={16} />
RP
{t("ui.dev.dashboard.ops.card.rp_requests", "RP 요청 추이")}
</div>
<p className="mt-3 text-2xl font-semibold"> </p>
<p className="mt-3 text-2xl font-semibold">
{t("ui.common.status.pending", "준비 중")}
</p>
</div>
<div className="rounded-xl border border-[var(--color-border)] bg-[rgba(255,255,255,0.02)] p-4">
<div className="flex items-center gap-2 text-[var(--color-muted)]">
<Activity size={16} />
Consent
{t(
"ui.dev.dashboard.ops.card.consent_revoked",
"Consent 회수 건수",
)}
</div>
<p className="mt-3 text-2xl font-semibold"> </p>
<p className="mt-3 text-2xl font-semibold">
{t("ui.common.status.pending", "준비 중")}
</p>
</div>
<div className="rounded-xl border border-[var(--color-border)] bg-[rgba(255,255,255,0.02)] p-4">
<div className="flex items-center gap-2 text-[var(--color-muted)]">
<Database size={16} />
Hydra
{t("ui.dev.dashboard.ops.card.hydra_status", "Hydra 상태")}
</div>
<p className="mt-3 text-2xl font-semibold"></p>
<p className="mt-3 text-2xl font-semibold">
{t("ui.common.status.ok", "정상")}
</p>
</div>
</div>
</section>