1
0
forked from baron/baron-sso
Files
baron-sso/adminfront/src/features/tenants/routes/TenantDetailPage.tsx
2026-06-15 20:05:47 +09:00

142 lines
5.1 KiB
TypeScript

import { useQuery } from "@tanstack/react-query";
import { Copy } from "lucide-react";
import { Link, Outlet, useLocation, useParams } from "react-router-dom";
import { Button } from "../../../components/ui/button";
import { fetchTenant } from "../../../lib/adminApi";
import { t } from "../../../lib/i18n";
import { useTenantPermission } from "../hooks/useTenantPermission";
function TenantDetailPage() {
const params = useParams<{ tenantId: string }>();
const tenantId = params.tenantId ?? "";
const location = useLocation();
const tenantQuery = useQuery({
queryKey: ["tenant", tenantId],
queryFn: () => fetchTenant(tenantId),
enabled: tenantId.length > 0,
});
const { hasPermission } = useTenantPermission(tenantId);
const isPermissionsTab = location.pathname.includes("/permissions");
const isOrganizationTab = location.pathname.includes("/organization");
const isWorksmobileTab = location.pathname.includes("/worksmobile");
return (
<div className="space-y-8">
<header className="flex flex-wrap items-start justify-between gap-4">
<div className="space-y-2">
<div
className="flex flex-wrap items-center gap-3"
data-testid="tenant-detail-title-row"
>
<h2 className="text-3xl font-semibold">
{tenantQuery.data?.name ??
t("ui.admin.tenants.detail.loading", "불러오는 중...")}
</h2>
{tenantQuery.data?.id && (
<div
className="flex items-center gap-1.5"
data-testid="tenant-detail-uuid"
>
<code className="select-all rounded-md border border-border bg-muted/40 px-2 py-1 font-mono text-xs text-foreground">
{tenantQuery.data.id}
</code>
<Button
type="button"
variant="ghost"
size="icon"
className="h-8 w-8"
onClick={() => {
void navigator.clipboard?.writeText(tenantQuery.data.id);
}}
aria-label="테넌트 UUID 복사"
title="테넌트 UUID 복사"
data-testid="tenant-detail-copy-uuid"
>
<Copy className="h-4 w-4" />
</Button>
</div>
)}
</div>
<p className="text-sm text-[var(--color-muted)]">
{t(
"ui.admin.tenants.detail.header_subtitle",
"테넌트 정보를 수정하거나 연동 설정을 관리합니다.",
)}
</p>
</div>
</header>
{/* Tabs */}
<div className="flex border-b border-border">
<Link
to={`/tenants/${tenantId}`}
className={`px-6 py-3 text-sm font-medium transition-colors relative ${
!isPermissionsTab &&
!location.pathname.includes("/schema") &&
!isWorksmobileTab &&
!isOrganizationTab
? "text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t("ui.admin.tenants.detail.tab_profile", "프로필")}
</Link>
<Link
to={`/tenants/${tenantId}/permissions`}
className={`px-6 py-3 text-sm font-medium transition-colors relative ${
isPermissionsTab
? "text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t("ui.admin.tenants.detail.tab_permissions", "권한")}
</Link>
<Link
to={`/tenants/${tenantId}/organization`}
className={`px-6 py-3 text-sm font-medium transition-colors relative ${
isOrganizationTab
? "text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t("ui.admin.tenants.detail.tab_organization", "조직 관리")}
</Link>
{hasPermission("view") && (
<Link
to={`/tenants/${tenantId}/schema`}
className={`px-6 py-3 text-sm font-medium transition-colors relative ${
location.pathname.includes("/schema")
? "text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t("ui.admin.tenants.detail.tab_schema", "사용자 스키마")}
</Link>
)}
{hasPermission("view") && (
<Link
to={`/tenants/${tenantId}/relations`}
className={`px-6 py-3 text-sm font-medium transition-colors relative ${
location.pathname.includes("/relations")
? "text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t("ui.admin.tenants.detail.tab_relations", "세부 권한")}
</Link>
)}
</div>
{/* Outlet for nested routes */}
<div>
<Outlet />
</div>
</div>
);
}
export default TenantDetailPage;