turash/bugulma/frontend/lib/graphUtils.ts

99 lines
3.3 KiB
TypeScript

import { SECTORS } from '@/constants.tsx';
import { Organization } from '@/types.ts';
export interface GraphNode {
id: string;
label: string;
x: number;
y: number;
color: string;
size: number;
orgCount: number;
}
export interface GraphLink {
source: string;
target: string;
value: number;
}
export const generateGraphData = (
organizations: Organization[],
t: (key: string) => string
): { nodes: GraphNode[]; links: GraphLink[] } => {
const sectorMap = new Map<string, string[]>(); // Map sector nameKey to org IDs
SECTORS.forEach((s) => sectorMap.set(s.nameKey, []));
organizations.forEach((org) => {
if (sectorMap.has(org.sector)) {
sectorMap.get(org.sector)!.push(org.id);
}
});
// Create nodes
const angleStep = (2 * Math.PI) / SECTORS.length;
const radius = 150;
const nodes: GraphNode[] = SECTORS.map((sector, i) => {
const orgCount = sectorMap.get(sector.nameKey)!.length;
// Get sector name - try .name path first, then fallback to object extraction
let sectorName = t(`${sector.nameKey}.name`);
// If translation returned the key itself (not found), try accessing the object directly
if (sectorName === `${sector.nameKey}.name` || !sectorName) {
// The t() function should automatically extract .name from objects, but if that fails,
// try getting the raw translation and extracting name manually
const rawTranslation = t(sector.nameKey);
if (rawTranslation && rawTranslation !== sector.nameKey) {
sectorName = rawTranslation;
} else {
// Final fallback: extract readable name from key (e.g., "sectors.construction" -> "Construction")
const keyPart = sector.nameKey.replace('sectors.', '');
sectorName = keyPart.charAt(0).toUpperCase() + keyPart.slice(1).replace(/_/g, ' ');
}
}
return {
id: sector.nameKey,
label: sectorName,
x: 200 + radius * Math.cos(angleStep * i - Math.PI / 2),
y: 200 + radius * Math.sin(angleStep * i - Math.PI / 2),
color: sector.colorKey,
size: Math.max(10, (orgCount || 0) * 2),
orgCount: orgCount,
};
});
// Create links
const linksMap = new Map<string, number>(); // key: "source-target"
for (let i = 0; i < organizations.length; i++) {
for (let j = i + 1; j < organizations.length; j++) {
const orgA = organizations[i];
const orgB = organizations[j];
// Safely check for connections with null/undefined guards
const orgAOffers = orgA.offers || [];
const orgANeeds = orgA.needs || [];
const orgBOffers = orgB.offers || [];
const orgBNeeds = orgB.needs || [];
const hasConnection =
orgAOffers.some((o) =>
orgBNeeds.some((n) => n.resource.toLowerCase().includes(o.resource.toLowerCase()))
) ||
orgBOffers.some((o) =>
orgANeeds.some((n) => n.resource.toLowerCase().includes(o.resource.toLowerCase()))
);
if (hasConnection && orgA.sector !== orgB.sector) {
const linkKey = [orgA.sector, orgB.sector].sort().join('-');
linksMap.set(linkKey, (linksMap.get(linkKey) || 0) + 1);
}
}
}
const links: GraphLink[] = Array.from(linksMap.entries()).map(([key, value]) => {
const [source, target] = key.split('-');
return { source, target, value };
});
return { nodes, links };
};