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,151 @@
|
|||
import { cn } from "~/lib/utils";
|
||||
import { TrendingUp, TrendingDown, Bell, Trash2, Search, Plus } from "lucide-react";
|
||||
import { Button } from "@/routes/ui/button";
|
||||
import { ChainIcon } from "../components/shared/ChainIcon";
|
||||
|
||||
export interface WatchlistItem {
|
||||
id: string;
|
||||
symbol: string;
|
||||
name?: string;
|
||||
chain: string;
|
||||
price: string;
|
||||
priceChange24h: number;
|
||||
alertCount?: number;
|
||||
}
|
||||
|
||||
export interface WatchlistWidgetProps {
|
||||
/** List of tokens in watchlist */
|
||||
tokens: WatchlistItem[];
|
||||
/** Callback when analyze token is clicked */
|
||||
onAnalyze?: (token: WatchlistItem) => void;
|
||||
/** Callback when remove token is clicked */
|
||||
onRemove?: (tokenId: string) => void;
|
||||
/** Callback when add token is clicked */
|
||||
onAddToken?: () => void;
|
||||
/** Callback when clear all is clicked */
|
||||
onClearAll?: () => void;
|
||||
/** Additional class names */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* WatchlistWidget - Embedded watchlist widget for chat interface
|
||||
*
|
||||
* Displays user's watchlist inline in the chat conversation.
|
||||
* Supports quick actions like analyze, remove, and add tokens.
|
||||
*/
|
||||
export function WatchlistWidget({
|
||||
tokens,
|
||||
onAnalyze,
|
||||
onRemove,
|
||||
onAddToken,
|
||||
onClearAll,
|
||||
className,
|
||||
}: WatchlistWidgetProps) {
|
||||
if (tokens.length === 0) {
|
||||
return (
|
||||
<div className={cn(
|
||||
"rounded-lg border bg-card p-4 my-2",
|
||||
className
|
||||
)}>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-lg">📋</span>
|
||||
<span className="font-medium text-sm">Your Watchlist</span>
|
||||
</div>
|
||||
<div className="text-center py-4 text-muted-foreground text-sm">
|
||||
Your watchlist is empty. Add tokens to track them!
|
||||
</div>
|
||||
<Button size="sm" variant="outline" onClick={onAddToken} className="w-full">
|
||||
<Plus className="h-3 w-3 mr-1" />
|
||||
Add Token
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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">WatchlistWidget</span>
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground">{tokens.length} tokens</span>
|
||||
</div>
|
||||
|
||||
{/* Token list */}
|
||||
<div className="space-y-2 mb-3">
|
||||
{tokens.map((token) => (
|
||||
<div
|
||||
key={token.id}
|
||||
className="flex items-center gap-3 p-2 rounded-md bg-muted/50 hover:bg-muted transition-colors"
|
||||
>
|
||||
{/* Token info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-sm">{token.symbol}</span>
|
||||
<ChainIcon chain={token.chain} size="sm" />
|
||||
{token.alertCount && token.alertCount > 0 && (
|
||||
<span className="flex items-center gap-0.5 text-xs text-primary">
|
||||
<Bell className="h-3 w-3" />
|
||||
{token.alertCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">{token.price}</p>
|
||||
</div>
|
||||
|
||||
{/* Price change */}
|
||||
<div className={cn(
|
||||
"flex items-center gap-1 text-xs font-medium",
|
||||
token.priceChange24h >= 0 ? "text-green-500" : "text-red-500"
|
||||
)}>
|
||||
{token.priceChange24h >= 0 ? (
|
||||
<TrendingUp className="h-3 w-3" />
|
||||
) : (
|
||||
<TrendingDown className="h-3 w-3" />
|
||||
)}
|
||||
{token.priceChange24h >= 0 ? "+" : ""}{token.priceChange24h.toFixed(1)}%
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
className="p-1 hover:bg-background rounded text-muted-foreground hover:text-foreground"
|
||||
onClick={() => onAnalyze?.(token)}
|
||||
title="Analyze"
|
||||
>
|
||||
<Search className="h-3 w-3" />
|
||||
</button>
|
||||
<button
|
||||
className="p-1 hover:bg-destructive/10 rounded text-muted-foreground hover:text-destructive"
|
||||
onClick={() => onRemove?.(token.id)}
|
||||
title="Remove"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Footer actions */}
|
||||
<div className="flex gap-2 pt-2 border-t">
|
||||
<Button size="sm" variant="outline" onClick={onAddToken} className="flex-1">
|
||||
<Plus className="h-3 w-3 mr-1" />
|
||||
Add Token
|
||||
</Button>
|
||||
{tokens.length > 0 && (
|
||||
<Button size="sm" variant="ghost" onClick={onClearAll} className="text-destructive">
|
||||
Clear All
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue