Files
igny8/frontend/src/components/tasks/RelationshipMap.tsx
2026-01-01 21:42:04 +00:00

135 lines
4.5 KiB
TypeScript

import React from "react";
import { Link } from "react-router-dom";
import { ArrowRightIcon } from "../../icons";
import Button from "../ui/button/Button";
export interface RelationshipData {
taskId: number;
taskTitle: string;
clusterId?: number | null;
clusterName?: string | null;
ideaId?: number | null;
ideaTitle?: string | null;
keywordIds?: number[];
keywordNames?: string[];
}
interface RelationshipMapProps {
task: RelationshipData;
onNavigate?: (type: "cluster" | "idea" | "keyword", id: number) => void;
className?: string;
}
const RelationshipMap: React.FC<RelationshipMapProps> = ({
task,
onNavigate,
className = "",
}) => {
const hasRelationships = task.clusterId || task.ideaId || (task.keywordIds && task.keywordIds.length > 0);
if (!hasRelationships) {
return (
<div className={`text-xs text-gray-500 dark:text-gray-400 ${className}`}>
No relationships mapped
</div>
);
}
return (
<div className={`space-y-2 ${className}`}>
{/* Keywords → Cluster → Idea → Task Flow */}
<div className="flex flex-wrap items-center gap-2 text-xs">
{/* Keywords */}
{task.keywordIds && task.keywordIds.length > 0 && (
<>
<div className="flex flex-wrap items-center gap-1">
{task.keywordNames?.slice(0, 3).map((keyword, idx) => (
<Button
key={idx}
onClick={() => onNavigate?.("keyword", task.keywordIds![idx])}
variant="ghost"
tone="neutral"
size="xs"
>
{keyword}
</Button>
))}
{task.keywordIds.length > 3 && (
<span className="px-2 py-0.5 rounded bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400">
+{task.keywordIds.length - 3}
</span>
)}
</div>
{task.clusterId && <ArrowRightIcon className="size-3 text-gray-400" />}
</>
)}
{/* Cluster */}
{task.clusterId && (
<>
<Link
to={`/planner/clusters?highlight=${task.clusterId}`}
onClick={(e) => {
if (onNavigate) {
e.preventDefault();
onNavigate("cluster", task.clusterId!);
}
}}
className="px-2 py-0.5 rounded bg-brand-50 dark:bg-brand-500/15 text-brand-600 dark:text-brand-400 hover:bg-brand-100 dark:hover:bg-brand-500/25 transition-colors font-medium"
>
{task.clusterName || `Cluster #${task.clusterId}`}
</Link>
{task.ideaId && <ArrowRightIcon className="size-3 text-gray-400" />}
</>
)}
{/* Idea */}
{task.ideaId && (
<>
<Link
to={`/planner/ideas?highlight=${task.ideaId}`}
onClick={(e) => {
if (onNavigate) {
e.preventDefault();
onNavigate("idea", task.ideaId!);
}
}}
className="px-2 py-0.5 rounded bg-success-50 dark:bg-success-500/15 text-success-600 dark:text-success-400 hover:bg-success-100 dark:hover:bg-success-500/25 transition-colors font-medium"
>
{task.ideaTitle || `Idea #${task.ideaId}`}
</Link>
<ArrowRightIcon className="size-3 text-gray-400" />
</>
)}
{/* Task (current) */}
<span className="px-2 py-0.5 rounded bg-warning-50 dark:bg-warning-500/15 text-warning-600 dark:text-warning-400 font-medium">
Task
</span>
</div>
{/* Relationship Summary */}
<div className="flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400 pt-1 border-t border-gray-200 dark:border-gray-800">
{task.clusterId && (
<span>
Cluster: <span className="font-medium text-gray-700 dark:text-gray-300">{task.clusterName || `#${task.clusterId}`}</span>
</span>
)}
{task.ideaId && (
<span>
Idea: <span className="font-medium text-gray-700 dark:text-gray-300">{task.ideaTitle || `#${task.ideaId}`}</span>
</span>
)}
{task.keywordIds && task.keywordIds.length > 0 && (
<span>
Keywords: <span className="font-medium text-gray-700 dark:text-gray-300">{task.keywordIds.length}</span>
</span>
)}
</div>
</div>
);
};
export default RelationshipMap;