Untitled
unknown
plain_text
a year ago
7.3 kB
14
Indexable
import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Search, TrendingUp, TrendingDown, Star, Plus } from 'lucide-react';
interface Asset {
symbol: string;
name: string;
price: number;
change: number;
changePercent: number;
type: 'stock' | 'crypto';
}
const AssetTracker = () => {
const [searchTerm, setSearchTerm] = useState('');
const [watchlist, setWatchlist] = useState<string[]>(['AAPL', 'BTC', 'TSLA']);
const [assets] = useState<Asset[]>([
{ symbol: 'AAPL', name: 'Apple Inc.', price: 182.52, change: 2.43, changePercent: 1.35, type: 'stock' },
{ symbol: 'BTC', name: 'Bitcoin', price: 43250.00, change: -1250.30, changePercent: -2.81, type: 'crypto' },
{ symbol: 'TSLA', name: 'Tesla Inc.', price: 248.42, change: 5.23, changePercent: 2.15, type: 'stock' },
{ symbol: 'ETH', name: 'Ethereum', price: 2650.75, change: 125.40, changePercent: 4.97, type: 'crypto' },
{ symbol: 'GOOGL', name: 'Alphabet Inc.', price: 138.21, change: -0.87, changePercent: -0.62, type: 'stock' },
{ symbol: 'MSFT', name: 'Microsoft Corp.', price: 378.85, change: 3.21, changePercent: 0.85, type: 'stock' },
]);
const filteredAssets = assets.filter(asset =>
asset.symbol.toLowerCase().includes(searchTerm.toLowerCase()) ||
asset.name.toLowerCase().includes(searchTerm.toLowerCase())
);
const watchlistAssets = assets.filter(asset => watchlist.includes(asset.symbol));
const addToWatchlist = (symbol: string) => {
if (!watchlist.includes(symbol)) {
setWatchlist([...watchlist, symbol]);
}
};
const removeFromWatchlist = (symbol: string) => {
setWatchlist(watchlist.filter(s => s !== symbol));
};
return (
<div className="space-y-6">
<div className="text-center">
<h2 className="text-3xl font-bold text-foreground mb-2">Market Tracker</h2>
<p className="text-muted-foreground">Track stocks, crypto, and build your watchlist</p>
</div>
{/* Search */}
<div className="relative max-w-md mx-auto">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
<Input
placeholder="Search assets..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
/>
</div>
{/* Watchlist */}
{watchlistAssets.length > 0 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Star className="h-5 w-5 text-accent" />
Your Watchlist
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-4">
{watchlistAssets.map((asset) => (
<div key={asset.symbol} className="flex items-center justify-between p-4 rounded-lg bg-card border border-border hover:shadow-md transition-shadow">
<div className="flex items-center gap-4">
<div>
<div className="flex items-center gap-2">
<span className="font-semibold text-foreground">{asset.symbol}</span>
<Badge variant={asset.type === 'crypto' ? 'secondary' : 'outline'}>
{asset.type}
</Badge>
</div>
<span className="text-sm text-muted-foreground">{asset.name}</span>
</div>
</div>
<div className="flex items-center gap-4">
<div className="text-right">
<div className="font-semibold text-foreground">
${asset.price.toLocaleString()}
</div>
<div className={`flex items-center gap-1 text-sm ${
asset.change >= 0 ? 'text-success' : 'text-destructive'
}`}>
{asset.change >= 0 ? <TrendingUp className="h-3 w-3" /> : <TrendingDown className="h-3 w-3" />}
{asset.changePercent >= 0 ? '+' : ''}{asset.changePercent.toFixed(2)}%
</div>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => removeFromWatchlist(asset.symbol)}
className="text-muted-foreground hover:text-destructive"
>
<Star className="h-4 w-4 fill-current" />
</Button>
</div>
</div>
))}
</div>
</CardContent>
</Card>
)}
{/* All Assets */}
<Card>
<CardHeader>
<CardTitle>All Assets</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-4">
{filteredAssets.map((asset) => (
<div key={asset.symbol} className="flex items-center justify-between p-4 rounded-lg bg-card border border-border hover:shadow-md transition-shadow">
<div className="flex items-center gap-4">
<div>
<div className="flex items-center gap-2">
<span className="font-semibold text-foreground">{asset.symbol}</span>
<Badge variant={asset.type === 'crypto' ? 'secondary' : 'outline'}>
{asset.type}
</Badge>
</div>
<span className="text-sm text-muted-foreground">{asset.name}</span>
</div>
</div>
<div className="flex items-center gap-4">
<div className="text-right">
<div className="font-semibold text-foreground">
${asset.price.toLocaleString()}
</div>
<div className={`flex items-center gap-1 text-sm ${
asset.change >= 0 ? 'text-success' : 'text-destructive'
}`}>
{asset.change >= 0 ? <TrendingUp className="h-3 w-3" /> : <TrendingDown className="h-3 w-3" />}
{asset.changePercent >= 0 ? '+' : ''}{asset.changePercent.toFixed(2)}%
</div>
</div>
{!watchlist.includes(asset.symbol) && (
<Button
variant="ghost"
size="sm"
onClick={() => addToWatchlist(asset.symbol)}
className="text-muted-foreground hover:text-accent"
>
<Plus className="h-4 w-4" />
</Button>
)}
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
);
};
export default AssetTracker;Editor is loading...
Leave a Comment