forked from baron/baron-sso
tenants 레지스트리 가독성/로케일 적용
This commit is contained in:
@@ -69,7 +69,6 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "../../../components/ui/select";
|
||||
import { Switch } from "../../../components/ui/switch";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -80,7 +79,6 @@ import {
|
||||
} from "../../../components/ui/table";
|
||||
import { Tabs, TabsList, TabsTrigger } from "../../../components/ui/tabs";
|
||||
import { toast } from "../../../components/ui/use-toast";
|
||||
import type { UserProfileResponse } from "../../../lib/adminApi";
|
||||
import {
|
||||
deleteTenantsBulk,
|
||||
exportTenantsCSV,
|
||||
@@ -142,6 +140,51 @@ const getTenantIcon = (type?: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
function getTenantTypeLabel(type?: string) {
|
||||
if (!type) return "-";
|
||||
return t(`domain.tenant_type.${type.toLowerCase()}`, type);
|
||||
}
|
||||
|
||||
function getTenantTypeTextClass(type?: string) {
|
||||
switch (type?.toUpperCase()) {
|
||||
case "COMPANY_GROUP":
|
||||
return "text-sky-700";
|
||||
case "COMPANY":
|
||||
return "text-violet-700";
|
||||
case "ORGANIZATION":
|
||||
return "text-emerald-700";
|
||||
case "USER_GROUP":
|
||||
return "text-amber-700";
|
||||
case "PERSONAL":
|
||||
return "text-slate-700";
|
||||
default:
|
||||
return "text-muted-foreground";
|
||||
}
|
||||
}
|
||||
|
||||
function buildTenantParentPathMap(tenants: TenantSummary[]) {
|
||||
const tenantById = new Map(tenants.map((tenant) => [tenant.id, tenant]));
|
||||
const pathMap = new Map<string, string[]>();
|
||||
|
||||
for (const tenant of tenants) {
|
||||
const names: string[] = [];
|
||||
const visited = new Set<string>();
|
||||
let currentParentId = tenant.parentId;
|
||||
|
||||
while (currentParentId && !visited.has(currentParentId)) {
|
||||
visited.add(currentParentId);
|
||||
const parent = tenantById.get(currentParentId);
|
||||
if (!parent) break;
|
||||
names.unshift(parent.name);
|
||||
currentParentId = parent.parentId;
|
||||
}
|
||||
|
||||
pathMap.set(tenant.id, names);
|
||||
}
|
||||
|
||||
return pathMap;
|
||||
}
|
||||
|
||||
const noImportParentRef = "__none__";
|
||||
|
||||
function tenantParentRef(tenantId: string) {
|
||||
@@ -339,19 +382,6 @@ function TenantListPage() {
|
||||
},
|
||||
});
|
||||
|
||||
const statusMutation = useMutation({
|
||||
mutationFn: ({ tenantId, status }: { tenantId: string; status: string }) =>
|
||||
updateTenant(tenantId, { status }),
|
||||
onSuccess: () => {
|
||||
query.refetch();
|
||||
},
|
||||
onError: () => {
|
||||
toast.error(
|
||||
t("msg.admin.tenants.status_error", "테넌트 상태 변경에 실패했습니다."),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const bulkUpdateStatusMutation = useMutation({
|
||||
mutationFn: async ({
|
||||
tenantIds,
|
||||
@@ -935,8 +965,6 @@ function TenantListPage() {
|
||||
onSelectAll={handleSelectAll}
|
||||
search={search}
|
||||
deletableTenants={deletableTenants}
|
||||
statusMutation={statusMutation}
|
||||
profile={profile}
|
||||
sortConfig={sortConfig}
|
||||
requestSort={requestSort}
|
||||
getSortIcon={getSortIcon}
|
||||
@@ -1513,13 +1541,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<TenantSortKey> | null;
|
||||
requestSort: (key: TenantSortKey) => void;
|
||||
getSortIcon: (key: TenantSortKey) => React.ReactNode;
|
||||
@@ -1536,8 +1557,6 @@ const TenantHierarchyView: React.FC<{
|
||||
onSelectAll,
|
||||
search,
|
||||
deletableTenants,
|
||||
statusMutation,
|
||||
profile,
|
||||
sortConfig,
|
||||
requestSort,
|
||||
getSortIcon,
|
||||
@@ -1558,6 +1577,10 @@ const TenantHierarchyView: React.FC<{
|
||||
() => buildTenantFullTree(tenants, scopeTenantId || undefined, !!search),
|
||||
[scopeTenantId, tenants, search],
|
||||
);
|
||||
const tenantParentPathMap = React.useMemo(
|
||||
() => buildTenantParentPathMap(tenants),
|
||||
[tenants],
|
||||
);
|
||||
|
||||
// Initial expanded state: everything open
|
||||
const [expandedIds, setExpandedIds] = React.useState<Set<string>>(() => {
|
||||
@@ -1682,6 +1705,22 @@ const TenantHierarchyView: React.FC<{
|
||||
const visibleSelectedCount = selectedIds.filter((id) =>
|
||||
visibleSelectableIds.has(id),
|
||||
).length;
|
||||
const normalizedSearch = search.trim();
|
||||
const emptyMessage = React.useMemo(() => {
|
||||
if (normalizedSearch) {
|
||||
return t(
|
||||
"msg.admin.tenants.empty_search",
|
||||
"검색 조건에 맞는 테넌트가 없습니다.",
|
||||
);
|
||||
}
|
||||
if (scopeTenantId) {
|
||||
return t(
|
||||
"msg.admin.tenants.empty_scope",
|
||||
"선택한 범위에 표시할 하위 테넌트가 없습니다.",
|
||||
);
|
||||
}
|
||||
return t("msg.admin.tenants.empty", "아직 등록된 테넌트가 없습니다.");
|
||||
}, [normalizedSearch, scopeTenantId]);
|
||||
|
||||
const renderRow = (
|
||||
node: TenantViewRow,
|
||||
@@ -1923,10 +1962,7 @@ const TenantHierarchyView: React.FC<{
|
||||
colSpan={8}
|
||||
className="py-8 text-center text-muted-foreground"
|
||||
>
|
||||
{t(
|
||||
"msg.admin.tenants.empty",
|
||||
"아직 등록된 테넌트가 없습니다.",
|
||||
)}
|
||||
{emptyMessage}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
@@ -1071,6 +1071,7 @@ user = "General User (Tenant Member)"
|
||||
[ui.admin.tenants]
|
||||
add = "Add Tenant"
|
||||
csv_template = "Template"
|
||||
data_mgmt = "Data Management"
|
||||
delete_selected = "Delete Selected"
|
||||
export_with_ids = "Include UUIDs"
|
||||
export_without_ids = "Export without UUIDs"
|
||||
@@ -1267,6 +1268,15 @@ name = "NAME"
|
||||
slug = "SLUG"
|
||||
status = "STATUS"
|
||||
|
||||
[ui.admin.tenants.view]
|
||||
list = "List"
|
||||
table = "Table"
|
||||
tree = "Tree"
|
||||
|
||||
[ui.admin.tenants.scope]
|
||||
active = "{{name}} descendants"
|
||||
pick = "Select parent scope"
|
||||
|
||||
[ui.admin.tenants.table]
|
||||
actions = "ACTIONS"
|
||||
id = "ID"
|
||||
|
||||
@@ -1074,6 +1074,7 @@ user = "일반 사용자 (Tenant Member)"
|
||||
[ui.admin.tenants]
|
||||
add = "테넌트 추가"
|
||||
csv_template = "템플릿"
|
||||
data_mgmt = "데이터 관리"
|
||||
delete_selected = "선택 삭제"
|
||||
export_with_ids = "UUID 포함"
|
||||
export_without_ids = "UUID 제외 내보내기"
|
||||
@@ -1270,15 +1271,24 @@ name = "NAME"
|
||||
slug = "SLUG"
|
||||
status = "STATUS"
|
||||
|
||||
[ui.admin.tenants.view]
|
||||
list = "평면 목록"
|
||||
table = "평면"
|
||||
tree = "트리"
|
||||
|
||||
[ui.admin.tenants.scope]
|
||||
active = "{{name}} 하위"
|
||||
pick = "상위 범위 선택"
|
||||
|
||||
[ui.admin.tenants.table]
|
||||
actions = "ACTIONS"
|
||||
id = "ID"
|
||||
members = "멤버수"
|
||||
name = "NAME"
|
||||
slug = "SLUG"
|
||||
status = "STATUS"
|
||||
name = "이름"
|
||||
slug = "슬러그"
|
||||
status = "상태"
|
||||
type = "유형"
|
||||
updated = "UPDATED"
|
||||
updated = "수정일"
|
||||
|
||||
[ui.admin.users]
|
||||
csv_template = "템플릿 다운로드"
|
||||
|
||||
@@ -264,6 +264,15 @@ subtitle = "List of owners with top-level permissions for this tenant."
|
||||
|
||||
[msg.admin.tenants.registry]
|
||||
count = "{{count}} tenants loaded."
|
||||
scope_results = "{{count}} tenants under {{name}}"
|
||||
scope_search_results = "{{count}} search results under {{name}}"
|
||||
search_results = "{{count}} search results"
|
||||
table_hint = "Compare IDs, status, and size quickly in the sortable flat list."
|
||||
tree_hint = "Review parent-child relationships and subtree coverage in the hierarchy."
|
||||
|
||||
[msg.admin.tenants]
|
||||
empty_scope = "There are no child tenants to display in the selected scope."
|
||||
empty_search = "No tenants match the current search."
|
||||
|
||||
[msg.admin.tenants.schema]
|
||||
empty = "No custom fields defined. Click \\\\\\\"Add Field\\\\\\\" to begin."
|
||||
@@ -1157,6 +1166,7 @@ user = "TENANT MEMBER"
|
||||
[ui.admin.tenants]
|
||||
add = "Add Tenant"
|
||||
csv_template = "Template"
|
||||
data_mgmt = "Data Management"
|
||||
delete_selected = "Delete Selected"
|
||||
export_with_ids = "Include UUIDs"
|
||||
export_without_ids = "Export without UUIDs"
|
||||
@@ -1441,9 +1451,14 @@ status = "STATUS"
|
||||
|
||||
[ui.admin.tenants.table]
|
||||
actions = "ACTIONS"
|
||||
context = "Parent Path"
|
||||
id = "ID"
|
||||
id_copy = "Copy ID"
|
||||
members = "Members"
|
||||
members_count = "{{count}} members"
|
||||
members_recursive = "including descendants"
|
||||
name = "NAME"
|
||||
root = "Top Level"
|
||||
slug = "SLUG"
|
||||
status = "STATUS"
|
||||
type = "TYPE"
|
||||
|
||||
@@ -765,6 +765,15 @@ subtitle = "이 테넌트의 최상위 권한을 가진 소유자(조직장) 목
|
||||
|
||||
[msg.admin.tenants.registry]
|
||||
count = "총 {{count}}개 테넌트"
|
||||
scope_results = "{{name}} 하위 {{count}}개"
|
||||
scope_search_results = "{{name}} 하위 검색 결과 {{count}}개"
|
||||
search_results = "검색 결과 {{count}}개"
|
||||
table_hint = "정렬 가능한 평면 목록에서 ID, 상태, 규모를 빠르게 비교합니다."
|
||||
tree_hint = "계층 구조를 따라 부모-자식 관계와 하위 범위를 함께 확인합니다."
|
||||
|
||||
[msg.admin.tenants]
|
||||
empty_scope = "선택한 범위에 표시할 하위 테넌트가 없습니다."
|
||||
empty_search = "검색 조건에 맞는 테넌트가 없습니다."
|
||||
|
||||
[msg.admin.tenants.schema]
|
||||
empty = "등록된 커스텀 필드가 없습니다. 필드 추가를 눌러 시작하세요."
|
||||
@@ -1652,6 +1661,7 @@ user = "TENANT MEMBER"
|
||||
|
||||
[ui.admin.tenants]
|
||||
add = "테넌트 추가"
|
||||
data_mgmt = "데이터 관리"
|
||||
delete_selected = "선택 삭제"
|
||||
seed_badge = "초기 설정"
|
||||
title = "테넌트 목록"
|
||||
@@ -1904,13 +1914,18 @@ status = "STATUS"
|
||||
|
||||
[ui.admin.tenants.table]
|
||||
actions = "ACTIONS"
|
||||
context = "상위 경로"
|
||||
id = "ID"
|
||||
id_copy = "ID 복사"
|
||||
members = "멤버수"
|
||||
name = "NAME"
|
||||
slug = "SLUG"
|
||||
status = "STATUS"
|
||||
members_count = "{{count}}명"
|
||||
members_recursive = "하위 포함"
|
||||
name = "이름"
|
||||
root = "최상위"
|
||||
slug = "슬러그"
|
||||
status = "상태"
|
||||
type = "유형"
|
||||
updated = "UPDATED"
|
||||
updated = "수정일"
|
||||
created = "CREATED"
|
||||
created = "CREATED"
|
||||
|
||||
|
||||
@@ -622,6 +622,15 @@ subtitle = ""
|
||||
|
||||
[msg.admin.tenants.registry]
|
||||
count = ""
|
||||
scope_results = ""
|
||||
scope_search_results = ""
|
||||
search_results = ""
|
||||
table_hint = ""
|
||||
tree_hint = ""
|
||||
|
||||
[msg.admin.tenants]
|
||||
empty_scope = ""
|
||||
empty_search = ""
|
||||
|
||||
[msg.admin.tenants.schema]
|
||||
empty = ""
|
||||
@@ -1781,9 +1790,14 @@ status = ""
|
||||
|
||||
[ui.admin.tenants.table]
|
||||
actions = ""
|
||||
context = ""
|
||||
id = ""
|
||||
id_copy = ""
|
||||
members = ""
|
||||
members_count = ""
|
||||
members_recursive = ""
|
||||
name = ""
|
||||
root = ""
|
||||
slug = ""
|
||||
status = ""
|
||||
type = ""
|
||||
|
||||
Reference in New Issue
Block a user