/**
* Connection Path — presentational view. Pure: renders the shortest-path result
* (or the prompt / no-path / same / missing states). No data fetching, no clock.
*/
import { Fragment } from 'react';
import { useT } from '../../lib/i18n/I18nContext';
import type { ConnectionPathResult } from '../../lib/memory/connectionPath';
import Button from '../ui/Button';
interface ConnectionPathPanelProps {
result: ConnectionPathResult | null;
hasGraph: boolean;
loading?: boolean;
error?: string | null;
onRetry?: () => void;
}
const ConnectionPathPanel = ({
result,
hasGraph,
loading,
error,
onRetry,
}: ConnectionPathPanelProps) => {
const { t } = useT();
const intro = (
{t('connectionPath.title')}
{t('connectionPath.intro')}
);
if (loading) {
return (
{intro}
{[0, 1, 2].map(i => (
))}
);
}
if (error) {
return (
{intro}
{t('connectionPath.errorPrefix')} {error}
{onRetry && (
)}
);
}
if (!hasGraph) {
return (
{intro}
{t('connectionPath.empty')}
{t('connectionPath.emptyHint')}
);
}
if (!result) {
return (
{intro}
{t('connectionPath.prompt')}
);
}
let message: string | null = null;
if (result.reason === 'same') message = t('connectionPath.sameMessage');
else if (result.reason === 'missing-source')
message = t('connectionPath.missingSource').replace('{entity}', result.source);
else if (result.reason === 'missing-target')
message = t('connectionPath.missingTarget').replace('{entity}', result.target);
else if (result.reason === 'no-path')
message = t('connectionPath.noPath')
.replace('{source}', result.source)
.replace('{target}', result.target);
const nodeChip = (id: string, isEndpoint: boolean) => (
{id}
);
return (
{intro}
{message ? (
{message}
) : (
{t('connectionPath.resultHeading')}
{t('connectionPath.pathSummary').replace('{length}', String(result.length))}
- {nodeChip(result.source, true)}
{result.hops.map((hop, i) => (
-
{hop.forward ? `${hop.predicate} →` : `← ${hop.predicate}`}
- {nodeChip(hop.to, i === result.hops.length - 1)}
))}
)}
);
};
export default ConnectionPathPanel;