import { Link } from 'react-router-dom'; import React, { useEffect, useState } from 'react'; import { FaArrowRight, FaBullseye, FaCheck, FaChevronDown, FaChevronUp, FaRegLightbulb, FaSpinner, FaTimes, } from 'react-icons/fa'; import { HiSparkles } from 'react-icons/hi'; import type { IInsight, IInsightReport, } from '../../service/agent-analytics/agent-analytics.interface'; import { dismissAiInsight, fetchAiInsights, } from '../../service/agent-analytics/agent-analytics.service'; import { createCampaign } from '../../service/campaigns/campaigns.service'; const INSIGHTS_TO = '/agent-analytics?tab=insights'; const MAX_VISIBLE = 5; const PRIORITY_STYLES: Record = { high: { dot: 'bg-red-500', badge: 'bg-red-50 text-red-600' }, medium: { dot: 'bg-yellow-500', badge: 'bg-amber-50 text-amber-600' }, low: { dot: 'bg-green-500', badge: 'bg-emerald-50 text-emerald-600' }, }; /** * Dashboard "Latest insights" widget — most recent active AI recommendations * with the same dismiss / create-campaign actions as the Agent Analytics tab. * * @param {object} props - Recomaze auth. * @return {React.ReactElement} The insights section. */ export function LatestInsights({ token }: { token: string }) { const [report, setReport] = useState(null); const [loading, setLoading] = useState(true); const [expandedId, setExpandedId] = useState(null); const [dismissingId, setDismissingId] = useState(null); const [creatingId, setCreatingId] = useState(null); const [campaignDone, setCampaignDone] = useState>(new Set()); const [campaignError, setCampaignError] = useState(null); useEffect(() => { if (!token) return; let cancelled = false; setLoading(true); void (async () => { try { const res = await fetchAiInsights(); if (!cancelled) setReport(res?.report ?? null); } catch { // Benign: the store may have no insights report yet. } finally { if (!cancelled) setLoading(false); } })(); return () => { cancelled = true; }; }, [token]); const activeInsights = (report?.insights ?? []).filter( insight => !insight.dismissed ); const visibleInsights = activeInsights.slice(0, MAX_VISIBLE); const moreCount = Math.max(0, activeInsights.length - visibleInsights.length); const handleDismiss = async (insight: IInsight) => { if (!report) return; setDismissingId(insight.id); try { await dismissAiInsight(report.report_id, insight.id); setReport(prev => prev ? { ...prev, insights: prev.insights.map(entry => entry.id === insight.id ? { ...entry, dismissed: true } : entry ), } : prev ); } catch { console.warn('[Dashboard] dismiss insight failed'); } finally { setDismissingId(null); } }; const handleCreateCampaign = async (insight: IInsight) => { const campaign = insight.suggested_campaign; if (!campaign) return; setCreatingId(insight.id); setCampaignError(null); try { await createCampaign({ name: campaign.name, goal: campaign.goal, target_categories: campaign.target_categories, tone_of_voice: campaign.tone_of_voice, priority: campaign.priority, description: campaign.description, }); setCampaignDone(prev => new Set(prev).add(insight.id)); } catch (campaignErr) { setCampaignError( campaignErr instanceof Error ? campaignErr.message : 'Failed to create campaign' ); } finally { setCreatingId(null); } }; return (

Latest insights

{activeInsights.length > 0 ? `${activeInsights.length} active recommendation${ activeInsights.length === 1 ? '' : 's' }` : 'AI-generated recommendations from your conversations'}

View all
{campaignError && (
{campaignError}
)} {loading ? ( ) : visibleInsights.length === 0 ? ( ) : (
{visibleInsights.map(insight => ( setExpandedId(current => current === insight.id ? null : insight.id ) } onDismiss={handleDismiss} onCreateCampaign={handleCreateCampaign} /> ))} {moreCount > 0 && ( +{moreCount} more insight{moreCount === 1 ? '' : 's'} )}
)}
); } function InsightCard({ insight, expanded, campaignDone, isDismissing, isCreating, onToggle, onDismiss, onCreateCampaign, }: { insight: IInsight; expanded: boolean; campaignDone: boolean; isDismissing: boolean; isCreating: boolean; onToggle: () => void; onDismiss: (insight: IInsight) => void; onCreateCampaign: (insight: IInsight) => void; }) { const style = PRIORITY_STYLES[insight.priority] ?? PRIORITY_STYLES.low; return (
{expanded && (
{insight.metrics?.length > 0 && (
{insight.metrics.map((metric, index) => (

{metric.label}

{metric.value}

))}
)} {insight.suggested_campaign && (

Suggested action: Create a targeted campaign

{campaignDone ? (
Campaign created
) : ( )}
)}
Open in analytics
)}
); } function CampaignField({ label, value, capitalize, }: { label: string; value: string; capitalize?: boolean; }) { return (

{label}

{value}
); } function SkeletonRows() { return (
{Array.from({ length: 3 }).map((_, index) => (
))}
); } function EmptyState() { return (

No insights yet

Recommendations appear here as visitors chat with your agent.

); }