feat: Enhance analysis view and market ticker

Adds a "View History" button to the AnalysisView, allowing users to navigate directly to the signal history.
Updates the MarketTicker component to fetch real-time data from Binance, displaying a dynamic list of cryptocurrency and forex tickers.
This commit is contained in:
desartstudio95
2026-05-07 19:02:04 +02:00
parent 30cf7ce1dc
commit 15b618dda3
3 changed files with 68 additions and 32 deletions
+1 -1
View File
@@ -619,7 +619,7 @@ export default function App() {
exit={{ opacity: 0, x: -15 }}
transition={{ duration: 0.25, ease: 'easeOut' }}
>
{activeTab === 'scan' && <AnalysisView userData={userData} />}
{activeTab === 'scan' && <AnalysisView userData={userData} onGoToHistory={() => setActiveTab('history')} />}
{activeTab === 'history' && <SignalHistory />}
{activeTab === 'stats' && <DashboardStats />}
{activeTab === 'profile' && <ProfileView user={user} userData={userData} onUpdate={setUserData} onDeleted={handleLogout} />}
+18 -7
View File
@@ -9,7 +9,7 @@ import { ref, uploadBytes, getDownloadURL } from 'firebase/storage';
import { storage, auth, db, handleFirestoreError, OperationType } from '../lib/firebase';
import { collection, addDoc, serverTimestamp, query, where, getDocs, Timestamp } from 'firebase/firestore';
export const AnalysisView: React.FC<{ userData?: any }> = ({ userData }) => {
export const AnalysisView: React.FC<{ userData?: any, onGoToHistory?: () => void }> = ({ userData, onGoToHistory }) => {
const [file, setFile] = useState<File | null>(null);
const [preview, setPreview] = useState<string | null>(null);
const [isAnalyzing, setIsAnalyzing] = useState(false);
@@ -456,12 +456,23 @@ export const AnalysisView: React.FC<{ userData?: any }> = ({ userData }) => {
</div>
</div>
<button
onClick={reset}
className="w-full py-3 text-zinc-600 hover:text-white font-black text-xs uppercase tracking-widest transition-colors"
>
NOVO SCAN INSTITUCIONAL
</button>
<div className="flex flex-col gap-2">
<button
onClick={reset}
className="w-full py-3 text-zinc-600 hover:text-white font-black text-xs uppercase tracking-widest transition-colors"
>
NOVO SCAN INSTITUCIONAL
</button>
{onGoToHistory && (
<button
onClick={onGoToHistory}
className="w-full py-2 text-zinc-500 hover:text-white font-medium text-[10px] uppercase tracking-widest transition-colors"
>
Ver Histórico de Sinais
</button>
)}
</div>
</motion.div>
)}
</div>
+49 -24
View File
@@ -8,41 +8,66 @@ interface TickerItem {
isUp: boolean;
}
const BINANCE_SYMBOLS = [
'BTCUSDT',
'ETHUSDT',
'SOLUSDT',
'EURUSDT',
'GBPUSDT',
'AUDUSDT',
'BNBUSDT',
'XRPUSDT',
];
export const MarketTicker: React.FC = () => {
const [items, setItems] = useState<TickerItem[]>([
{ symbol: 'EURUSD', price: '1.08542', change: '+0.12%', isUp: true },
{ symbol: 'GBPUSD', price: '1.26410', change: '-0.05%', isUp: false },
{ symbol: 'USDJPY', price: '151.420', change: '+0.25%', isUp: true },
{ symbol: 'XAUUSD', price: '2345.12', change: '+0.84%', isUp: true },
{ symbol: 'BTCUSD', price: '64240.5', change: '-1.20%', isUp: false },
{ symbol: 'NAS100', price: '18240.2', change: '+0.45%', isUp: true },
{ symbol: 'AUDUSD', price: '0.65120', change: '+0.08%', isUp: true },
{ symbol: 'USDCAD', price: '1.35410', change: '-0.02%', isUp: false },
]);
const [items, setItems] = useState<TickerItem[]>(
BINANCE_SYMBOLS.map(sym => ({
symbol: sym.replace('USDT', ''),
price: '...',
change: '...',
isUp: true
}))
);
useEffect(() => {
const interval = setInterval(() => {
setItems(prev => prev.map(item => {
const changeVal = (Math.random() * 0.0002) - 0.0001;
const newPrice = parseFloat(item.price) + parseFloat(item.price) * changeVal;
return {
...item,
price: newPrice.toFixed(item.symbol.includes('JPY') ? 3 : 5)
};
}));
}, 3000);
return () => clearInterval(interval);
const streams = BINANCE_SYMBOLS.map(s => `${s.toLowerCase()}@ticker`).join('/');
const ws = new WebSocket(`wss://stream.binance.com:9443/ws/${streams}`);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data && data.s && data.c && data.P) {
setItems(prev => {
const newItems = [...prev];
const index = newItems.findIndex(item => item.symbol === data.s.replace('USDT', ''));
if (index !== -1) {
const currentPrice = parseFloat(data.c);
const priceChangePercent = parseFloat(data.P);
newItems[index] = {
...newItems[index],
price: currentPrice >= 1 ? currentPrice.toFixed(2) : currentPrice.toFixed(4),
change: `${priceChangePercent >= 0 ? '+' : ''}${priceChangePercent.toFixed(2)}%`,
isUp: priceChangePercent >= 0
};
}
return newItems;
});
}
};
return () => {
ws.close();
};
}, []);
return (
<div className="w-full bg-brand-dark/50 border-y border-white/5 py-2 overflow-hidden select-none">
<div className="flex animate-marquee whitespace-nowrap">
{[...items, ...items].map((item, idx) => (
{[...items, ...items, ...items].map((item, idx) => (
<div key={`${item.symbol}-${idx}`} className="flex items-center gap-6 px-8 border-r border-white/5 last:border-r-0">
<span className="text-[10px] font-black tracking-widest text-zinc-400 uppercase">{item.symbol}</span>
<span className="text-xs font-mono font-bold tabular-nums text-white">{item.price}</span>
<div className={`flex items-center gap-1 text-[10px] font-black ${item.isUp ? 'text-green-500' : 'text-brand-red'}`}>
{item.isUp ? <TrendingUp size={12} /> : <TrendingDown size={12} />}
<div className={`flex items-center gap-1 text-[10px] font-black ${item.change !== '...' ? (item.isUp ? 'text-green-500' : 'text-brand-red') : 'text-zinc-500'}`}>
{item.change !== '...' && (item.isUp ? <TrendingUp size={12} /> : <TrendingDown size={12} />)}
{item.change}
</div>
</div>