SurfSense/surfsense_browser_extension/sidepanel/widgets/TokenAnalysisWidget.tsx
API Test Bot e4d020799b feat(crypto): add SurfSense 2.0 Crypto Co-Pilot UI components
Frontend - Web Dashboard:
- Add crypto dashboard page with Watchlist, Alerts, Market, Profile tabs
- Add 11 tool-ui components for inline chat display
- Add crypto components (ChainIcon, SafetyBadge, PriceDisplay, etc.)
- Add modals (AddTokenModal, CreateAlertModal)
- Add mock data for development

Frontend - Browser Extension:
- Add shared components (ChainIcon, RiskBadge, PriceDisplay, SuggestionCard)
- Add crypto components (SafetyScoreDisplay, WatchlistPanel, AlertConfigModal)
- Add chat enhancements (WelcomeScreen, ThinkingStepsDisplay)
- Add widget components for inline display
- Enhance TokenInfoCard, ChatHeader, ChatInput, ChatInterface

Documentation:
- Add conversational UX specification
- Add UX analysis report
- Update extension UX design

This implements the Conversational UX paradigm where crypto features
are AI-callable tools that render inline in the chat interface.
2026-02-04 02:19:57 +07:00

196 lines
7.7 KiB
TypeScript

import { cn } from "~/lib/utils";
import { Shield, TrendingUp, TrendingDown, Users, AlertTriangle, CheckCircle, Star, Bell } from "lucide-react";
import { Button } from "@/routes/ui/button";
import { ChainIcon } from "../components/shared/ChainIcon";
export interface TokenAnalysisData {
/** Token symbol */
symbol: string;
/** Token name */
name?: string;
/** Blockchain */
chain: string;
/** Current price */
price: string;
/** 24h price change */
priceChange24h: number;
/** Market cap */
marketCap?: string;
/** 24h volume */
volume24h?: string;
/** Liquidity */
liquidity?: string;
/** Safety score (0-100) */
safetyScore?: number;
/** Holder count */
holderCount?: number;
/** Top 10 holder percentage */
top10HolderPercent?: number;
}
export interface TokenAnalysisWidgetProps {
/** Token analysis data */
data: TokenAnalysisData;
/** Whether token is in watchlist */
isInWatchlist?: boolean;
/** Callback when add to watchlist is clicked */
onAddToWatchlist?: () => void;
/** Callback when set alert is clicked */
onSetAlert?: () => void;
/** Callback when analyze further is clicked */
onAnalyzeFurther?: () => void;
/** Additional class names */
className?: string;
}
/**
* TokenAnalysisWidget - Full token analysis card embedded in chat
*
* Displays comprehensive token analysis including price, safety score,
* and key metrics. Used when AI responds to token research queries.
*/
export function TokenAnalysisWidget({
data,
isInWatchlist = false,
onAddToWatchlist,
onSetAlert,
onAnalyzeFurther,
className,
}: TokenAnalysisWidgetProps) {
const getSafetyColor = (score?: number) => {
if (!score) return "text-muted-foreground";
if (score >= 80) return "text-green-500";
if (score >= 60) return "text-yellow-500";
return "text-red-500";
};
const getSafetyLabel = (score?: number) => {
if (!score) return "Unknown";
if (score >= 80) return "Low Risk";
if (score >= 60) return "Medium Risk";
return "High Risk";
};
return (
<div className={cn(
"rounded-lg border bg-card p-4 my-2",
className
)}>
{/* Header */}
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<span className="text-lg">📊</span>
<span className="font-medium text-sm">TokenAnalysisCard</span>
</div>
<ChainIcon chain={data.chain} size="sm" />
</div>
{/* Token info */}
<div className="flex items-center gap-3 mb-3 pb-3 border-b">
<div className="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center">
<span className="text-lg">🪙</span>
</div>
<div className="flex-1">
<div className="flex items-center gap-2">
<span className="font-bold">{data.symbol}</span>
{data.name && (
<span className="text-xs text-muted-foreground">{data.name}</span>
)}
</div>
<div className="flex items-center gap-2">
<span className="font-medium">{data.price}</span>
<span className={cn(
"flex items-center gap-0.5 text-sm",
data.priceChange24h >= 0 ? "text-green-500" : "text-red-500"
)}>
{data.priceChange24h >= 0 ? (
<TrendingUp className="h-3 w-3" />
) : (
<TrendingDown className="h-3 w-3" />
)}
{data.priceChange24h >= 0 ? "+" : ""}{data.priceChange24h.toFixed(1)}%
</span>
</div>
</div>
<button
onClick={onAddToWatchlist}
className={cn(
"p-2 rounded-full transition-colors",
isInWatchlist
? "text-yellow-500 bg-yellow-500/10"
: "text-muted-foreground hover:text-yellow-500 hover:bg-yellow-500/10"
)}
>
<Star className={cn("h-5 w-5", isInWatchlist && "fill-current")} />
</button>
</div>
{/* Metrics grid */}
<div className="grid grid-cols-2 gap-2 mb-3 text-sm">
{data.marketCap && (
<div className="bg-muted/50 rounded p-2">
<p className="text-xs text-muted-foreground">Market Cap</p>
<p className="font-medium">{data.marketCap}</p>
</div>
)}
{data.volume24h && (
<div className="bg-muted/50 rounded p-2">
<p className="text-xs text-muted-foreground">24h Volume</p>
<p className="font-medium">{data.volume24h}</p>
</div>
)}
{data.liquidity && (
<div className="bg-muted/50 rounded p-2">
<p className="text-xs text-muted-foreground">Liquidity</p>
<p className="font-medium">{data.liquidity}</p>
</div>
)}
{data.safetyScore !== undefined && (
<div className="bg-muted/50 rounded p-2">
<p className="text-xs text-muted-foreground">Safety Score</p>
<p className={cn("font-medium flex items-center gap-1", getSafetyColor(data.safetyScore))}>
<Shield className="h-3 w-3" />
{data.safetyScore}/100 ({getSafetyLabel(data.safetyScore)})
</p>
</div>
)}
</div>
{/* Holder info */}
{(data.holderCount || data.top10HolderPercent) && (
<div className="flex items-center gap-4 mb-3 text-xs text-muted-foreground">
{data.holderCount && (
<span className="flex items-center gap-1">
<Users className="h-3 w-3" />
{data.holderCount.toLocaleString()} holders
</span>
)}
{data.top10HolderPercent && (
<span className={cn(
"flex items-center gap-1",
data.top10HolderPercent > 50 ? "text-yellow-500" : ""
)}>
{data.top10HolderPercent > 50 && <AlertTriangle className="h-3 w-3" />}
Top 10: {data.top10HolderPercent}%
</span>
)}
</div>
)}
{/* Action buttons */}
<div className="flex gap-2">
<Button size="sm" variant="outline" onClick={onAddToWatchlist} className="flex-1">
<Star className="h-3 w-3 mr-1" />
{isInWatchlist ? "In Watchlist" : "Add to Watchlist"}
</Button>
<Button size="sm" variant="outline" onClick={onSetAlert}>
<Bell className="h-3 w-3" />
</Button>
<Button size="sm" variant="default" onClick={onAnalyzeFurther}>
Analyze More
</Button>
</div>
</div>
);
}