1
0
forked from baron/baron-sso
Files
baron-sso/adminfront/src/features/tenants/routes/tenantListView.ts

151 lines
3.8 KiB
TypeScript

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<TenantSummary, "id" | "name" | "slug" | "type">,
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));
}
export function getTenantSearchMatchIds(
rows: Array<Pick<TenantSummary, "id" | "name" | "slug" | "type">>,
search: string,
) {
if (!search.trim()) return [];
return rows
.filter((row) => tenantMatchesListSearch(row, search))
.map((row) => row.id);
}
export function filterTenantViewRowsBySearch<T extends TenantViewRow>(
rows: T[],
search: string,
) {
if (!search.trim()) return rows;
return rows.filter((row) => tenantMatchesListSearch(row, search));
}
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<string, TenantSummary[]>();
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<string>();
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.totalMemberCount ?? 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);
}