diff --git a/adminfront/src/features/tenants/routes/TenantListPage.tsx b/adminfront/src/features/tenants/routes/TenantListPage.tsx index 0fbfec4b..d49d7443 100644 --- a/adminfront/src/features/tenants/routes/TenantListPage.tsx +++ b/adminfront/src/features/tenants/routes/TenantListPage.tsx @@ -1,9 +1,4 @@ -import { - type UseMutationResult, - useInfiniteQuery, - useMutation, - useQuery, -} from "@tanstack/react-query"; +import { useInfiniteQuery, useMutation, useQuery } from "@tanstack/react-query"; import { useVirtualizer } from "@tanstack/react-virtual"; import type { AxiosError } from "axios"; import { @@ -41,7 +36,6 @@ import { Button } from "../../../components/ui/button"; import { Card, CardContent, - CardDescription, CardHeader, CardTitle, } from "../../../components/ui/card"; @@ -69,7 +63,6 @@ import { SelectTrigger, SelectValue, } from "../../../components/ui/select"; -import { Switch } from "../../../components/ui/switch"; import { Table, TableBody, @@ -89,7 +82,6 @@ import { type TenantImportDetail, type TenantImportResult, type TenantSummary, - type UserProfileResponse, updateTenant, } from "../../../lib/adminApi"; import { t } from "../../../lib/i18n"; @@ -416,27 +408,6 @@ function TenantListPage() { }, }); - const statusMutation = useMutation({ - mutationFn: ({ - tenantId, - status, - }: { - tenantId: string; - status: string; - }) => updateTenant(tenantId, { status }), - onSuccess: () => { - query.refetch(); - toast.success( - t("msg.admin.tenants.bulk.update_success", "선택한 테넌트들의 상태가 수정되었습니다."), - ); - }, - onError: () => { - toast.error( - t("msg.admin.tenants.bulk.update_error", "테넌트 일괄 상태 변경에 실패했습니다."), - ); - }, - }); - const handleApplyBulkStatus = () => { if (selectedIds.length === 0 || !selectedBulkStatus) return; bulkUpdateStatusMutation.mutate({ @@ -504,7 +475,6 @@ function TenantListPage() { ? t("msg.admin.tenants.fetch_error", "테넌트 목록 조회에 실패했습니다.") : null; - const tenantTotal = query.data?.pages[0]?.total ?? 0; const hanmacFamilyTenantId = React.useMemo(() => { const envTenantId = import.meta.env.VITE_HANMAC_FAMILY_TENANT_ID; if (typeof envTenantId === "string" && envTenantId.trim()) { @@ -962,15 +932,6 @@ function TenantListPage() { {t("ui.admin.tenants.registry.title", "Tenant Registry")} - - {t( - "msg.admin.tenants.registry.count", - "총 {{count}}개의 테넌트가 등록되어 있습니다.", - { - count: scopeTenantId ? scopedTenants.length : tenantTotal, - }, - )} - @@ -988,8 +949,6 @@ function TenantListPage() { onSelectAll={handleSelectAll} search={search} deletableTenants={deletableTenants} - statusMutation={statusMutation} - profile={profile} sortConfig={sortConfig} requestSort={requestSort} getSortIcon={getSortIcon} @@ -1566,13 +1525,6 @@ const TenantHierarchyView: React.FC<{ onSelectAll: (checked: boolean) => void; search: string; deletableTenants: TenantSummary[]; - statusMutation: UseMutationResult< - TenantSummary, - Error, - { tenantId: string; status: string }, - unknown - >; - profile: UserProfileResponse | undefined; sortConfig: SortConfig | null; requestSort: (key: TenantSortKey) => void; getSortIcon: (key: TenantSortKey) => React.ReactNode; @@ -1589,8 +1541,6 @@ const TenantHierarchyView: React.FC<{ onSelectAll, search, deletableTenants, - statusMutation, - profile, sortConfig, requestSort, getSortIcon, @@ -1829,69 +1779,99 @@ const TenantHierarchyView: React.FC<{ className="mr-2 flex-shrink-0 text-muted-foreground" /> -
- - {node.name} - - {isSeedTenant(node) && ( - +
+ - {t("ui.admin.tenants.seed_badge", "초기 설정")} - - )} + {node.name} + + {isSeedTenant(node) && ( + + {t("ui.admin.tenants.seed_badge", "초기 설정")} + + )} +
+ {(() => { + const parentPath = tenantParentPathMap.get(node.id) ?? []; + return ( +

+ {parentPath.length > 0 + ? parentPath.join(" / ") + : t("ui.admin.tenants.path.root", "최상위")} +

+ ); + })()}
- {node.id} + + {node.id} + - - {node.type} + + {getTenantTypeLabel(node.type)} + + + + + {node.slug} + + + + + {node.status === "active" + ? t("ui.common.status.active", "활성") + : t("ui.common.status.inactive", "비활성")} - {node.slug} -
- - statusMutation.mutate({ - tenantId: node.id, - status: checked ? "active" : "inactive", - }) - } - disabled={ - statusMutation.isPending || - node.id === profile?.tenantId || - isSeedTenant(node) - } - aria-label={t( - "ui.admin.tenants.toggle_status", - "{{name}} 활성 상태", - { name: node.name }, - )} - /> - - {t(`ui.common.status.${node.status}`, node.status)} +
+ + {t("ui.admin.tenants.table.members_count", "{{count}}명", { + count: node.recursiveMemberCount, + })} + + + {t("ui.admin.tenants.table.members_recursive", "하위 포함")}
- - {node.recursiveMemberCount} - - - {node.updatedAt - ? new Date(node.updatedAt).toLocaleString("ko-KR") - : "-"} + + {node.updatedAt ? ( +
+ + {new Date(node.updatedAt).toLocaleDateString("ko-KR")} + + + {new Date(node.updatedAt).toLocaleTimeString("ko-KR")} + +
+ ) : ( + - + )}
); diff --git a/adminfront/src/locales/en.toml b/adminfront/src/locales/en.toml index 214f0287..c4915449 100644 --- a/adminfront/src/locales/en.toml +++ b/adminfront/src/locales/en.toml @@ -1280,7 +1280,9 @@ pick = "Select parent scope" [ui.admin.tenants.table] actions = "ACTIONS" id = "ID" +members_count = "{{count}} members" members = "Members" +members_recursive = "Includes descendants" name = "NAME" slug = "SLUG" status = "STATUS" diff --git a/adminfront/src/locales/ko.toml b/adminfront/src/locales/ko.toml index 474c4aa9..1e788dfb 100644 --- a/adminfront/src/locales/ko.toml +++ b/adminfront/src/locales/ko.toml @@ -1283,7 +1283,9 @@ pick = "상위 범위 선택" [ui.admin.tenants.table] actions = "ACTIONS" id = "ID" +members_count = "{{count}}명" members = "멤버수" +members_recursive = "하위 포함" name = "이름" slug = "슬러그" status = "상태" diff --git a/adminfront/src/locales/template.toml b/adminfront/src/locales/template.toml index 09849111..4808dab7 100644 --- a/adminfront/src/locales/template.toml +++ b/adminfront/src/locales/template.toml @@ -1291,6 +1291,8 @@ slug = "" status = "" [ui.admin.tenants.table] +members_count = "" +members_recursive = "" actions = "" id = "" members = ""