forked from baron/baron-sso
187 lines
5.1 KiB
TypeScript
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;
|
|
}
|