forked from baron/baron-sso
devfront RP 상세 탭 i18n 및 순서 일관화
This commit is contained in:
@@ -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">
|
||||
|
||||
54
devfront/src/features/clients/ClientDetailTabs.tsx
Normal file
54
devfront/src/features/clients/ClientDetailTabs.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -1363,6 +1363,7 @@ title = "Security Note"
|
||||
connection = "Federation"
|
||||
consents = "Consent & Users"
|
||||
settings = "Settings"
|
||||
relationships = "Relationships"
|
||||
|
||||
[ui.dev.clients.general]
|
||||
create = "Create Application"
|
||||
|
||||
@@ -1362,6 +1362,7 @@ title = "보안 메모"
|
||||
connection = "연동 설정"
|
||||
consents = "동의 및 사용자"
|
||||
settings = "설정"
|
||||
relationships = "관계"
|
||||
|
||||
[ui.dev.clients.general]
|
||||
create = "앱 생성"
|
||||
|
||||
@@ -1363,6 +1363,7 @@ title = ""
|
||||
connection = ""
|
||||
consents = ""
|
||||
settings = ""
|
||||
relationships = ""
|
||||
|
||||
[ui.dev.clients.general]
|
||||
create = ""
|
||||
|
||||
Reference in New Issue
Block a user