-
{file.name}
-
- {(file.size / 1024).toFixed(1)} KB
+ ) : (
+
+
+
+
+ 전체 행
+
+
{result.totalRows}
+
+
+
+ 처리 완료
+
+
+ {result.processed}
+
+
+
+
+ 사용자 생성/업데이트
+
+
+ {result.userCreated} / {result.userUpdated}
+
+
+
+
+ 조직(테넌트) 생성
+
+
+ {result.tenantCreated}
- )}
-
+
+ {result.errors.length > 0 && (
+
+
+
+ 오류 목록 ({result.errors.length})
+
+
+ {result.errors.map((err, idx) => (
+
+ {err}
+
+ ))}
+
+
+ )}
+
+ )}
-
+ {!result ? (
+
+ ) : (
+
+ )}
diff --git a/adminfront/src/features/tenants/routes/TenantListPage.tsx b/adminfront/src/features/tenants/routes/TenantListPage.tsx
index 4b7b712c..e23ffe1c 100644
--- a/adminfront/src/features/tenants/routes/TenantListPage.tsx
+++ b/adminfront/src/features/tenants/routes/TenantListPage.tsx
@@ -13,7 +13,7 @@ import {
CardHeader,
CardTitle,
} from "../../../components/ui/card";
-import { OrgChartUploadModal } from "../components/OrgChartUploadModal";
+import { Checkbox } from "../../../components/ui/checkbox";
import {
Table,
TableBody,
@@ -25,13 +25,17 @@ import {
import {
type TenantSummary,
deleteTenant,
+ deleteTenantsBulk,
fetchMe,
fetchTenants,
} from "../../../lib/adminApi";
import { t } from "../../../lib/i18n";
+import { OrgChartUploadModal } from "../components/OrgChartUploadModal";
function TenantListPage() {
const navigate = useNavigate();
+ const [selectedIds, setSelectedIds] = React.useState
([]);
+
const { data: profile } = useQuery({
queryKey: ["me"],
queryFn: fetchMe,
@@ -41,7 +45,6 @@ function TenantListPage() {
React.useEffect(() => {
if (profile?.role === "tenant_admin") {
const manageableCount = profile.manageableTenants?.length ?? 0;
- // If only 1 in array, OR array is empty but we have a primary tenantId
if (
(manageableCount === 1 || manageableCount === 0) &&
profile.tenantId
@@ -67,6 +70,14 @@ function TenantListPage() {
},
});
+ const deleteBulkMutation = useMutation({
+ mutationFn: (ids: string[]) => deleteTenantsBulk(ids),
+ onSuccess: () => {
+ setSelectedIds([]);
+ query.refetch();
+ },
+ });
+
if (
profile &&
profile.role !== "super_admin" &&
@@ -84,7 +95,6 @@ function TenantListPage() {
);
}
- // While redirecting (only if exactly one manageable tenant)
if (
profile?.role === "tenant_admin" &&
(profile.manageableTenants?.length ?? 0) <= 1
@@ -101,8 +111,40 @@ function TenantListPage() {
const tenants = query.data?.items ?? [];
- // [New] Find a primary COMPANY_GROUP tenant to act as the root for matrix org charts
- const rootTenant = tenants.find((t) => t.type === "COMPANY_GROUP") || tenants[0];
+ const handleSelectAll = (checked: boolean) => {
+ if (checked) {
+ setSelectedIds(tenants.map((t) => t.id));
+ } else {
+ setSelectedIds([]);
+ }
+ };
+
+ const handleSelect = (id: string, checked: boolean) => {
+ if (checked) {
+ setSelectedIds((prev) => [...prev, id]);
+ } else {
+ setSelectedIds((prev) => prev.filter((i) => i !== id));
+ }
+ };
+
+ const handleDeleteBulk = () => {
+ if (selectedIds.length === 0) return;
+ if (
+ !window.confirm(
+ t(
+ "msg.admin.tenants.delete_bulk_confirm",
+ "선택한 {{count}}개 테넌트를 삭제할까요?",
+ { count: selectedIds.length },
+ ),
+ )
+ ) {
+ return;
+ }
+ deleteBulkMutation.mutate(selectedIds);
+ };
+
+ const rootTenant =
+ tenants.find((t) => t.type === "COMPANY_GROUP") || tenants[0];
const handleDelete = (tenantId: string, tenantName: string) => {
if (
@@ -134,16 +176,34 @@ function TenantListPage() {