mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-28 10:26:33 +02:00
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.
This commit is contained in:
parent
ad795eb830
commit
e4d020799b
58 changed files with 11315 additions and 661 deletions
|
|
@ -0,0 +1,196 @@
|
|||
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>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue