1
0
forked from baron/baron-sso

devfront RP 상세 탭 i18n 및 순서 일관화

This commit is contained in:
2026-04-15 17:18:04 +09:00
parent dd93a3450a
commit 8d0982b89c
9 changed files with 144 additions and 108 deletions

View File

@@ -29,6 +29,7 @@ import {
import { fetchClient, fetchConsents, revokeConsent } from "../../lib/devApi";
import { t } from "../../lib/i18n";
import { cn } from "../../lib/utils";
import { ClientDetailTabs } from "./ClientDetailTabs";
function ClientConsentsPage() {
const params = useParams();
@@ -214,29 +215,7 @@ function ClientConsentsPage() {
</Badge>
</div>
</div>
<div className="flex gap-6 overflow-x-auto border-b border-border pb-3 text-sm font-bold">
<Link
to={`/clients/${clientId}`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.connection", "Federation")}
</Link>
<span className="whitespace-nowrap border-b-2 border-primary pb-1 text-primary">
{t("ui.dev.clients.details.tab.consents", "Consent & Users")}
</span>
<Link
to={`/clients/${clientId}/settings`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.settings", "Settings")}
</Link>
<Link
to={`/clients/${clientId}/relationships`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.relationships", "Relationships")}
</Link>
</div>
<ClientDetailTabs activeTab="consents" clientId={clientId} />
</header>
<Card className="glass-panel">

View File

@@ -0,0 +1,54 @@
import { Link } from "react-router-dom";
import { t } from "../../lib/i18n";
import { cn } from "../../lib/utils";
type ClientDetailTab = "connection" | "consents" | "settings" | "relationships";
interface ClientDetailTabsProps {
activeTab: ClientDetailTab;
clientId: string;
}
const tabOrder: Array<{
key: ClientDetailTab;
href: (clientId: string) => string;
}> = [
{ key: "connection", href: (clientId) => `/clients/${clientId}` },
{ key: "consents", href: (clientId) => `/clients/${clientId}/consents` },
{ key: "settings", href: (clientId) => `/clients/${clientId}/settings` },
{
key: "relationships",
href: (clientId) => `/clients/${clientId}/relationships`,
},
];
export function ClientDetailTabs({
activeTab,
clientId,
}: ClientDetailTabsProps) {
return (
<div className="flex gap-6 overflow-x-auto border-b border-border pb-3 text-sm font-bold">
{tabOrder.map((tab) => {
const isActive = tab.key === activeTab;
return isActive ? (
<span
key={tab.key}
className="whitespace-nowrap border-b-2 border-primary pb-1 text-primary"
>
{t(`ui.dev.clients.details.tab.${tab.key}`)}
</span>
) : (
<Link
key={tab.key}
to={tab.href(clientId)}
className={cn(
"whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground",
)}
>
{t(`ui.dev.clients.details.tab.${tab.key}`)}
</Link>
);
})}
</div>
);
}

View File

@@ -38,6 +38,7 @@ import {
} from "../../lib/devApi";
import { t } from "../../lib/i18n";
import { cn } from "../../lib/utils";
import { ClientDetailTabs } from "./ClientDetailTabs";
function ClientDetailsPage() {
const params = useParams();
@@ -253,32 +254,7 @@ function ClientDetailsPage() {
: t("msg.common.loading", "Loading...")}
</Badge>
</div>
<div className="flex gap-6 border-b border-border">
<Link
to={`/clients/${clientId}`}
className="border-b-2 border-primary pb-3 text-sm font-bold text-primary"
>
{t("ui.dev.clients.details.tab.connection", "Federation")}
</Link>
<Link
to={`/clients/${clientId}/consents`}
className="pb-3 text-sm font-bold text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.consents", "Consent & Users")}
</Link>
<Link
to={`/clients/${clientId}/settings`}
className="pb-3 text-sm font-bold text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.settings", "Settings")}
</Link>
<Link
to={`/clients/${clientId}/relationships`}
className="pb-3 text-sm font-bold text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.relationships", "Relationships")}
</Link>
</div>
<ClientDetailTabs activeTab="connection" clientId={clientId} />
</div>
<div className="grid gap-8 lg:grid-cols-2">

View File

@@ -43,6 +43,7 @@ import type {
} from "../../lib/devApi";
import { t } from "../../lib/i18n";
import { cn } from "../../lib/utils";
import { ClientDetailTabs } from "./ClientDetailTabs";
interface ScopeItem {
id: string;
@@ -665,33 +666,9 @@ function ClientGeneralPage() {
</Badge>
)}
</div>
<div className="flex gap-6 overflow-x-auto border-b border-border pb-3 text-sm font-bold">
{!isCreate && (
<>
<Link
to={`/clients/${clientId}`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.connection", "Federation")}
</Link>
<Link
to={`/clients/${clientId}/consents`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.consents", "Consent & Users")}
</Link>
<Link
to={`/clients/${clientId}/relationships`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.relationships", "Relationships")}
</Link>
<span className="whitespace-nowrap border-b-2 border-primary pb-1 text-primary">
{t("ui.dev.clients.details.tab.settings", "Settings")}
</span>
</>
)}
</div>
{!isCreate && (
<ClientDetailTabs activeTab="settings" clientId={clientId} />
)}
</header>
{/* 1. Application Identity */}

View File

@@ -30,6 +30,7 @@ import {
removeClientRelation,
} from "../../lib/devApi";
import { t } from "../../lib/i18n";
import { ClientDetailTabs } from "./ClientDetailTabs";
const relationOptions = [
"admins",
@@ -48,9 +49,8 @@ function ClientRelationsPage() {
const params = useParams();
const queryClient = useQueryClient();
const clientId = params.id ?? "";
const [relation, setRelation] = useState<(typeof relationOptions)[number]>(
"config_editor",
);
const [relation, setRelation] =
useState<(typeof relationOptions)[number]>("config_editor");
const [userId, setUserId] = useState("");
const { data: clientData } = useQuery({
@@ -86,7 +86,9 @@ function ClientRelationsPage() {
userId: userId.trim(),
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["client-relations", clientId] });
queryClient.invalidateQueries({
queryKey: ["client-relations", clientId],
});
setUserId("");
toast(
t(
@@ -115,7 +117,9 @@ function ClientRelationsPage() {
mutationFn: (payload: { relation: string; subject: string }) =>
removeClientRelation(clientId, payload.relation, payload.subject),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["client-relations", clientId] });
queryClient.invalidateQueries({
queryKey: ["client-relations", clientId],
});
toast(
t(
"msg.dev.clients.relationships.removed",
@@ -191,10 +195,7 @@ function ClientRelationsPage() {
<span>{clientData?.client?.name || clientId}</span>
<span>/</span>
<span className="text-foreground font-semibold">
{t(
"ui.dev.clients.details.tab.relationships",
"Relationships",
)}
{t("ui.dev.clients.details.tab.relationships", "Relationships")}
</span>
</nav>
<div className="flex items-center gap-2">
@@ -231,29 +232,7 @@ function ClientRelationsPage() {
</Badge>
</div>
</div>
<div className="flex gap-6 overflow-x-auto border-b border-border pb-3 text-sm font-bold">
<Link
to={`/clients/${clientId}`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.connection", "Federation")}
</Link>
<Link
to={`/clients/${clientId}/consents`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.consents", "Consent & Users")}
</Link>
<Link
to={`/clients/${clientId}/settings`}
className="whitespace-nowrap border-b-2 border-transparent text-muted-foreground hover:text-foreground"
>
{t("ui.dev.clients.details.tab.settings", "Settings")}
</Link>
<span className="whitespace-nowrap border-b-2 border-primary pb-1 text-primary">
{t("ui.dev.clients.details.tab.relationships", "Relationships")}
</span>
</div>
<ClientDetailTabs activeTab="relationships" clientId={clientId} />
</header>
<Card className="glass-panel">

View File

@@ -1363,6 +1363,7 @@ title = "Security Note"
connection = "Federation"
consents = "Consent & Users"
settings = "Settings"
relationships = "Relationships"
[ui.dev.clients.general]
create = "Create Application"

View File

@@ -1362,6 +1362,7 @@ title = "보안 메모"
connection = "연동 설정"
consents = "동의 및 사용자"
settings = "설정"
relationships = "관계"
[ui.dev.clients.general]
create = "앱 생성"

View File

@@ -1363,6 +1363,7 @@ title = ""
connection = ""
consents = ""
settings = ""
relationships = ""
[ui.dev.clients.general]
create = ""