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(); // 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(); // 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 }; };