/** * Graph Cohesion — presentational view. Pure: renders the cohesion summary * tiles (entities / connections / triangles), the network averages, and a * brokerage ranking of the loosest-neighbourhood entities. No data fetching, * no clock, no randomness. */ import { useMemo } from 'react'; import { useT } from '../../lib/i18n/I18nContext'; import { type CohesionResult, findBrokers } from '../../lib/memory/graphCohesion'; const MAX_ROWS = 25; interface GraphCohesionPanelProps { result: CohesionResult | null; loading?: boolean; error?: string | null; onRetry?: () => void; } const GraphCohesionPanel = ({ result, loading, error, onRetry }: GraphCohesionPanelProps) => { const { t } = useT(); const brokers = useMemo(() => (result ? findBrokers(result, MAX_ROWS) : []), [result]); const intro = (

{t('graphCohesion.title')}

{t('graphCohesion.intro')}

); if (loading) { return (
{intro}
{[0, 1, 2].map(i => (
))}
{[0, 1, 2, 3].map(i => (
))}
); } if (error) { return (
{intro}

{t('graphCohesion.errorPrefix')} {error}

{onRetry && ( )}
); } if (!result || result.nodes.length === 0) { return (
{intro}

{t('graphCohesion.empty')}

{t('graphCohesion.emptyHint')}

); } return (
{intro} {/* Metric tiles */}
{[ { label: t('graphCohesion.metricEntities'), value: result.nodeCount }, { label: t('graphCohesion.metricConnections'), value: result.edgeCount }, { label: t('graphCohesion.metricTriangles'), value: result.triangleCount }, ].map(tile => (
{tile.label}
{tile.value}
))}

{t('graphCohesion.summaryCaption') .replace('{avg}', result.averageClustering.toFixed(2)) .replace('{transitivity}', result.transitivity.toFixed(2))}

{/* Brokerage ranking: loosest neighbourhoods first (structural holes). */} {brokers.length === 0 ? (

{t('graphCohesion.noBrokers')}

) : (

{t('graphCohesion.rankedHeading')}

{brokers.map((node, i) => ( ))}
{t('graphCohesion.colRank')} {t('graphCohesion.colEntity')} {t('graphCohesion.colCohesion')} {t('graphCohesion.colLinks')}
{i + 1} {node.id} {node.localClustering === 0 && ( {t('graphCohesion.brokerBadge')} )}
{node.localClustering.toFixed(2)}
{node.degree}
)}
); }; export default GraphCohesionPanel;