/** * InsightsView - Dashboard component for displaying and managing project insights * * Features: * - Displays insights grouped by all supported insight categories * - Manual insight generation trigger * - Per-insight dismiss action * - Per-insight task creation action * - Loading, error, and empty states * - Accessible feedback for all actions */ import { useCallback, useEffect, useState } from "react"; import { Sparkles, RefreshCw, X, Plus, AlertCircle, CheckCircle, Lightbulb, Building, Users, LineChart, TrendingUp, MoreVertical, ExternalLink, Archive, Clock, } from "lucide-react"; import { useInsights, INSIGHT_CATEGORIES, CATEGORY_LABELS, type InsightSection } from "../hooks/useInsights"; import type { InsightCategory } from "@fusion/core"; import type { ToastType } from "../hooks/useToast"; import { createTask } from "../api"; interface InsightsViewProps { projectId?: string; addToast: (message: string, type?: ToastType) => void; onClose?: () => void; onCreateTask?: (title: string, description: string) => void; } // Category icons mapping const CATEGORY_ICONS: Record> = { architecture: Building, quality: CheckCircle, workflow: Clock, performance: TrendingUp, reliability: RefreshCw, security: AlertCircle, ux: Users, testability: Archive, documentation: ExternalLink, dependency: Plus, features: Lightbulb, competitive_analysis: Users, research: LineChart, trends: TrendingUp, other: Sparkles, }; export function InsightsView({ projectId, addToast, onClose, onCreateTask }: InsightsViewProps) { const { sections, loading, error, latestRun, isRunInFlight, runError, refresh, runInsights, dismiss, createTask: createTaskFromInsight, dismissStates, createTaskStates, totalCount, } = useInsights(projectId); // Track inline feedback messages const [statusMessage, setStatusMessage] = useState(null); const [statusType, setStatusType] = useState<"success" | "error" | "info">("info"); // Clear status message after delay useEffect(() => { if (statusMessage) { const timer = setTimeout(() => setStatusMessage(null), 5000); return () => clearTimeout(timer); } }, [statusMessage]); // Handle manual run const handleRun = useCallback(async () => { try { setStatusMessage("Generating insights..."); setStatusType("info"); await runInsights(); setStatusMessage("Insight generation started"); setStatusType("success"); addToast("Insight generation started", "success"); } catch (err) { const message = err instanceof Error ? err.message : "Failed to start generation"; setStatusMessage(message); setStatusType("error"); addToast(message, "error"); } }, [runInsights, addToast]); // Handle dismiss const handleDismiss = useCallback( async (id: string, title: string) => { try { setStatusMessage(`Dismissing "${title}"...`); setStatusType("info"); await dismiss(id); setStatusMessage(`Dismissed "${title}"`); setStatusType("success"); addToast(`Insight dismissed: ${title}`, "success"); } catch (err) { const message = err instanceof Error ? err.message : "Failed to dismiss insight"; setStatusMessage(message); setStatusType("error"); addToast(message, "error"); } }, [dismiss, addToast], ); // Handle create task const handleCreateTask = useCallback( async (id: string, title: string) => { try { setStatusMessage(`Creating task from "${title}"...`); setStatusType("info"); const taskData = await createTaskFromInsight(id); if (taskData && onCreateTask) { onCreateTask(taskData.title, taskData.description); } setStatusMessage(`Task created from "${title}"`); setStatusType("success"); addToast(`Task created: ${taskData?.title ?? title}`, "success"); } catch (err) { const message = err instanceof Error ? err.message : "Failed to create task"; setStatusMessage(message); setStatusType("error"); addToast(message, "error"); } }, [createTaskFromInsight, onCreateTask, addToast], ); // Render section const renderSection = (section: InsightSection) => { const IconComponent = CATEGORY_ICONS[section.category] ?? Sparkles; const isAnyActionInFlight = section.items.some((item) => dismissStates.get(item.id)?.running || createTaskStates.get(item.id)?.running) ?? false; return (

{section.label}

{section.items.length}
{section.items.length === 0 ? (

No insights in this category

) : (
    {section.items.map((insight) => { const dismissState = dismissStates.get(insight.id); const createState = createTaskStates.get(insight.id); const isDismissInFlight = dismissState?.running ?? false; const isCreateInFlight = createState?.running ?? false; return (
  • {insight.title}

    {insight.content && (

    {insight.content}

    )}
    {insight.status} {insight.createdAt && ( {new Date(insight.createdAt).toLocaleDateString()} )}
  • ); })}
)}
); }; return (

Insights

{totalCount} total
{onClose && ( )}
{/* Inline status region */}
{statusMessage && (
{statusType === "success" && } {statusType === "error" && } {statusType === "info" && } {statusMessage}
)}
{/* Run error callout */} {runError && (
{runError}
)} {/* Latest run info */} {latestRun && (
Latest run: {latestRun.status} {latestRun.status === "completed" && ( <> — {latestRun.insightsCreated} created, {latestRun.insightsUpdated} updated )} {latestRun.status === "failed" && latestRun.error && ( <> — {latestRun.error} )}
)} {loading ? (

Loading insights...

) : error ? (

{error}

) : totalCount === 0 ? (

No insights yet

Generate insights to get AI-powered recommendations for your project.

) : (
{sections.map(renderSection)}
)}
); }