import type { TenantSummary } from "../../../lib/adminApi"; import { buildTenantFullTree, type TenantNode } from "../../../lib/tenantTree"; export type TenantViewMode = "tree" | "table"; export type TenantViewRow = TenantNode & { depth: number }; export function tenantMatchesListSearch( tenant: Pick, search: string, ) { const normalizedSearch = search.trim().toLowerCase(); if (!normalizedSearch) return true; return [tenant.name, tenant.slug, tenant.id, tenant.type] .filter(Boolean) .some((value) => value.toLowerCase().includes(normalizedSearch)); } function collectTenantTreeRows( nodes: TenantNode[], depth: number, rows: TenantViewRow[], ) { for (const node of nodes) { rows.push({ ...node, depth }); collectTenantTreeRows(node.children, depth + 1, rows); } } function collectTenantDescendantIds( tenantId: string, tenants: TenantSummary[], ) { const childrenByParent = new Map(); for (const tenant of tenants) { if (!tenant.parentId) continue; const children = childrenByParent.get(tenant.parentId) ?? []; children.push(tenant); childrenByParent.set(tenant.parentId, children); } const ids: string[] = []; const visitedIds = new Set(); const visit = (parentId: string) => { for (const child of childrenByParent.get(parentId) ?? []) { if (visitedIds.has(child.id)) continue; visitedIds.add(child.id); ids.push(child.id); visit(child.id); } }; visit(tenantId); return ids; } export function filterTenantsByScope( tenants: TenantSummary[], scopeTenantId: string, ) { if (!scopeTenantId) return tenants; const descendantIds = new Set( collectTenantDescendantIds(scopeTenantId, tenants), ); return tenants.filter((tenant) => descendantIds.has(tenant.id)); } export function getTenantViewRows( tenants: TenantSummary[], viewMode: TenantViewMode, scopeTenantId = "", isSearchActive = false, ): TenantViewRow[] { const { subTree } = buildTenantFullTree( tenants, scopeTenantId || undefined, isSearchActive, ); const treeRows: TenantViewRow[] = []; collectTenantTreeRows(subTree, 0, treeRows); if (viewMode === "tree") { return treeRows; } const rowsById = new Map(treeRows.map((row) => [row.id, row])); const flatSource = scopeTenantId ? filterTenantsByScope(tenants, scopeTenantId) : tenants; return flatSource.map((tenant) => ({ ...(rowsById.get(tenant.id) ?? { ...tenant, children: [], recursiveMemberCount: Number(tenant.memberCount) || 0, }), depth: 0, })); } export function resolveTenantSelectionIds({ currentIds, tenant, checked, tenants, deletableTenants, }: { currentIds: string[]; tenant: TenantSummary; checked: boolean; tenants: TenantSummary[]; deletableTenants: TenantSummary[]; }) { const allowedIds = new Set(deletableTenants.map((item) => item.id)); const targetIds = [ tenant.id, ...collectTenantDescendantIds(tenant.id, tenants), ].filter((id) => allowedIds.has(id)); const next = new Set(currentIds.filter((id) => allowedIds.has(id))); if (checked) { for (const id of targetIds) { next.add(id); } } else { for (const id of targetIds) { next.delete(id); } } return Array.from(next); }