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 }} exit={{ opacity: 0, x: -15 }}
transition={{ duration: 0.25, ease: 'easeOut' }} transition={{ duration: 0.25, ease: 'easeOut' }}
> >
{activeTab === 'scan' && <AnalysisView userData={userData} />} {activeTab === 'scan' && <AnalysisView userData={userData} onGoToHistory={() => setActiveTab('history')} />}
{activeTab === 'history' && <SignalHistory />} {activeTab === 'history' && <SignalHistory />}
{activeTab === 'stats' && <DashboardStats />} {activeTab === 'stats' && <DashboardStats />}
{activeTab === 'profile' && <ProfileView user={user} userData={userData} onUpdate={setUserData} onDeleted={handleLogout} />} {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 { storage, auth, db, handleFirestoreError, OperationType } from '../lib/firebase';
import { collection, addDoc, serverTimestamp, query, where, getDocs, Timestamp } from 'firebase/firestore'; 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 [file, setFile] = useState<File | null>(null);
const [preview, setPreview] = useState<string | null>(null); const [preview, setPreview] = useState<string | null>(null);
const [isAnalyzing, setIsAnalyzing] = useState(false); const [isAnalyzing, setIsAnalyzing] = useState(false);
@@ -456,12 +456,23 @@ export const AnalysisView: React.FC<{ userData?: any }> = ({ userData }) => {
</div> </div>
</div> </div>
<button <div className="flex flex-col gap-2">
onClick={reset} <button
className="w-full py-3 text-zinc-600 hover:text-white font-black text-xs uppercase tracking-widest transition-colors" 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> 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> </motion.div>
)} )}
</div> </div>
+49 -24
View File
@@ -8,41 +8,66 @@ interface TickerItem {
isUp: boolean; isUp: boolean;
} }
const BINANCE_SYMBOLS = [
'BTCUSDT',
'ETHUSDT',
'SOLUSDT',
'EURUSDT',
'GBPUSDT',
'AUDUSDT',
'BNBUSDT',
'XRPUSDT',
];
export const MarketTicker: React.FC = () => { export const MarketTicker: React.FC = () => {
const [items, setItems] = useState<TickerItem[]>([ const [items, setItems] = useState<TickerItem[]>(
{ symbol: 'EURUSD', price: '1.08542', change: '+0.12%', isUp: true }, BINANCE_SYMBOLS.map(sym => ({
{ symbol: 'GBPUSD', price: '1.26410', change: '-0.05%', isUp: false }, symbol: sym.replace('USDT', ''),
{ symbol: 'USDJPY', price: '151.420', change: '+0.25%', isUp: true }, price: '...',
{ symbol: 'XAUUSD', price: '2345.12', change: '+0.84%', isUp: true }, change: '...',
{ symbol: 'BTCUSD', price: '64240.5', change: '-1.20%', isUp: false }, isUp: true
{ 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 },
]);
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const streams = BINANCE_SYMBOLS.map(s => `${s.toLowerCase()}@ticker`).join('/');
setItems(prev => prev.map(item => { const ws = new WebSocket(`wss://stream.binance.com:9443/ws/${streams}`);
const changeVal = (Math.random() * 0.0002) - 0.0001;
const newPrice = parseFloat(item.price) + parseFloat(item.price) * changeVal; ws.onmessage = (event) => {
return { const data = JSON.parse(event.data);
...item, if (data && data.s && data.c && data.P) {
price: newPrice.toFixed(item.symbol.includes('JPY') ? 3 : 5) setItems(prev => {
}; const newItems = [...prev];
})); const index = newItems.findIndex(item => item.symbol === data.s.replace('USDT', ''));
}, 3000); if (index !== -1) {
return () => clearInterval(interval); 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 ( return (
<div className="w-full bg-brand-dark/50 border-y border-white/5 py-2 overflow-hidden select-none"> <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"> <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"> <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-[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> <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'}`}> <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.isUp ? <TrendingUp size={12} /> : <TrendingDown size={12} />} {item.change !== '...' && (item.isUp ? <TrendingUp size={12} /> : <TrendingDown size={12} />)}
{item.change} {item.change}
</div> </div>
</div> </div>