1
0
forked from baron/baron-sso
Files
baron-sso/orgfront/src/features/orgchart/pickerTree.ts

187 lines
5.1 KiB
TypeScript

import type { TenantSummary, UserSummary } from "../../lib/adminApi";
import { type TenantNode, buildTenantFullTree } from "../../lib/tenantTree";
import type { OrgPickerTreeNode } from "./pickerTypes";
import { filterTenantsByVisibility } from "./tenantVisibility";
import { getOrgChartUserDisplayName } from "./userDisplay";
function getUserTenantSlug(user: UserSummary) {
return (
user.companyCode?.toLowerCase() || user.tenantSlug?.toLowerCase() || ""
);
}
function isOrgFrontTenantType(tenant: TenantSummary) {
return ["COMPANY_GROUP", "COMPANY", "ORGANIZATION", "USER_GROUP"].includes(
tenant.type.toUpperCase(),
);
}
function getCompanyGroupId(node: TenantNode, allTenants: TenantSummary[]) {
let cursor: TenantSummary | undefined = node;
const byId = new Map(allTenants.map((tenant) => [tenant.id, tenant]));
while (cursor?.parentId) {
const parent = byId.get(cursor.parentId);
if (!parent) break;
cursor = parent;
}
return cursor?.type === "COMPANY_GROUP" ? cursor.id : node.id;
}
function isHanmacFamilyCompanyGroup(tenant: TenantSummary) {
return (
tenant.type.toUpperCase() === "COMPANY_GROUP" &&
tenant.slug.toLowerCase() === "hanmac-family"
);
}
function findTenantByRef(tenants: TenantSummary[], ref?: string) {
const normalizedRef = ref?.trim().toLowerCase();
if (!normalizedRef) return undefined;
return (
tenants.find((tenant) => tenant.slug.toLowerCase() === normalizedRef) ??
tenants.find((tenant) => tenant.id === ref)
);
}
function tenantToPickerNode(
tenant: TenantNode,
usersBySlug: Map<string, UserSummary[]>,
): OrgPickerTreeNode {
const tenantChildren = tenant.children.map((child) =>
tenantToPickerNode(child, usersBySlug),
);
const userChildren = (usersBySlug.get(tenant.slug.toLowerCase()) || []).map(
(user) => ({
type: "user" as const,
id: user.id,
name: getOrgChartUserDisplayName(user, tenant),
parentId: tenant.id,
user,
children: [],
}),
);
return {
type: "tenant",
id: tenant.id,
name: tenant.name,
parentId: tenant.parentId ?? null,
tenant,
children: [...userChildren, ...tenantChildren],
};
}
function findTenantNode(
roots: TenantNode[],
tenantRef: string,
): TenantNode | undefined {
const findBySlug = (node: TenantNode): TenantNode | undefined => {
if (node.slug.toLowerCase() === tenantRef.trim().toLowerCase()) {
return node;
}
for (const child of node.children) {
const match = findBySlug(child);
if (match) return match;
}
return undefined;
};
const findById = (node: TenantNode): TenantNode | undefined => {
if (node.id === tenantRef) return node;
for (const child of node.children) {
const match = findById(child);
if (match) return match;
}
return undefined;
};
for (const root of roots) {
const slugMatch = findBySlug(root);
if (slugMatch) return slugMatch;
}
for (const root of roots) {
const idMatch = findById(root);
if (idMatch) return idMatch;
}
return undefined;
}
export function buildOrgPickerTree({
tenants,
users,
rootTenantId,
tenantId,
}: {
tenants: TenantSummary[];
users: UserSummary[];
rootTenantId?: string;
tenantId?: string;
}) {
const visibleTenants = filterTenantsByVisibility(
tenants.filter(isOrgFrontTenantType),
"internal",
);
const usersBySlug = new Map<string, UserSummary[]>();
for (const user of users) {
if (user.status !== "active") continue;
const slug = getUserTenantSlug(user);
if (!slug) continue;
const list = usersBySlug.get(slug) || [];
list.push(user);
usersBySlug.set(slug, list);
}
const companyGroup =
findTenantByRef(visibleTenants, rootTenantId) ??
visibleTenants.find(isHanmacFamilyCompanyGroup) ??
visibleTenants.find((tenant) => tenant.type === "COMPANY_GROUP") ??
visibleTenants.find((tenant) => !tenant.parentId);
if (!companyGroup) return { roots: [], companies: [], companyGroupId: "" };
const { currentBase } = buildTenantFullTree(visibleTenants, companyGroup.id);
const groupNode =
currentBase ??
buildTenantFullTree(visibleTenants).subTree.find(
(node) => node.id === companyGroup.id,
);
if (!groupNode) return { roots: [], companies: [], companyGroupId: "" };
const companies = groupNode.children.filter(
(node) => node.type === "COMPANY",
);
const scopedRoot = tenantId
? findTenantNode([groupNode], tenantId)
: groupNode;
const filteredRoots = scopedRoot ? [scopedRoot] : [];
const roots = filteredRoots.map((node) =>
tenantToPickerNode(node, usersBySlug),
);
return {
roots,
companies: companies.map((company) => ({
id: company.id,
name: company.name,
companyGroupTenantId: getCompanyGroupId(company, tenants),
})),
companyGroupId: companyGroup.id,
};
}
export function flattenDescendants(node: OrgPickerTreeNode) {
const descendants: OrgPickerTreeNode[] = [];
const walk = (current: OrgPickerTreeNode) => {
for (const child of current.children) {
descendants.push(child);
walk(child);
}
};
walk(node);
return descendants;
}