feat: Enhance app UI and core features

Introduce a market ticker component to display real-time Forex data.
Refine AI analysis prompts for Gemini to focus on speed and key institutional patterns.
Improve loading state with a visible logo animation.
Add user avatar and ID display in the Navbar.
Implement hourly refresh for signal history to ensure data freshness.
Optimize CSS with a new marquee animation and adjust font sizes.
Strengthen error handling and unsubscribe logic for Firestore listeners.
This commit is contained in:
desartstudio95
2026-04-29 21:33:58 +02:00
parent f15d70a121
commit c336df2746
10 changed files with 238 additions and 129 deletions
+15 -4
View File
@@ -12,6 +12,7 @@ import { PlansView } from './components/PlansView';
import { AdminDashboard } from './components/AdminDashboard';
import { LandingPage } from './components/LandingPage';
import { ProfileView } from './components/ProfileView';
import { MarketTicker } from './components/MarketTicker';
import { TrendingUp, ShieldAlert, Ghost, Mail, Lock, UserPlus, LogIn, Loader2, ArrowLeft, User as UserIcon, Eye, EyeOff } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { onAuthStateChanged, signInWithEmailAndPassword, createUserWithEmailAndPassword, updateProfile, signOut, sendEmailVerification, sendPasswordResetEmail, User, setPersistence, browserLocalPersistence, browserSessionPersistence } from 'firebase/auth';
@@ -208,7 +209,12 @@ export default function App() {
if (loading) {
return (
<div className="h-screen w-full flex items-center justify-center bg-brand-dark">
<div className="h-screen w-full flex flex-col items-center justify-center bg-brand-dark gap-6">
<img
src="https://i.ibb.co/9BwbV3M/FXBROS-WORLD-3.png"
alt="Logo"
className="w-24 h-24 object-contain animate-pulse"
/>
<div className="w-10 h-10 border-4 border-brand-red border-t-transparent rounded-full animate-spin"></div>
</div>
);
@@ -543,11 +549,16 @@ export default function App() {
<div className="absolute inset-0 bg-gradient-to-b from-brand-dark/40 via-brand-dark/20 to-brand-dark" />
</div>
<Navbar activeTab={activeTab} setActiveTab={setActiveTab} isAdmin={isAdmin} />
<Navbar activeTab={activeTab} setActiveTab={setActiveTab} isAdmin={isAdmin} user={user} />
<main className="flex-1 overflow-y-auto overflow-x-hidden relative z-10">
<main className="flex-1 overflow-y-auto overflow-x-hidden relative z-10 flex flex-col">
{/* Market Ticker */}
<div className="w-full shrink-0">
<MarketTicker />
</div>
{/* Desktop Header */}
<header className="hidden md:flex items-center justify-between p-8 pb-4 max-w-6xl mx-auto">
<header className="hidden md:flex items-center justify-between p-8 pb-4 max-w-6xl w-full mx-auto">
<div className="space-y-1">
<span className="block text-[11px] font-black uppercase tracking-widest text-zinc-500 mb-1">Olá Humano, Bem-Vindo</span>
<h2 className="text-2xl font-black italic uppercase tracking-tighter text-white">
+6 -2
View File
@@ -127,8 +127,12 @@ export const AdminDashboard: React.FC = () => {
<tr key={user.uid} className="border-b border-white/5 hover:bg-white/5 transition-colors group">
<td className="p-4">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-brand-red/10 flex items-center justify-center text-brand-red text-sm font-black italic shrink-0">
{user.name?.charAt(0) || user.email?.charAt(0)?.toUpperCase()}
<div className="w-10 h-10 rounded-xl bg-brand-red/10 overflow-hidden shrink-0 border border-white/10 group-hover:border-brand-red/30 transition-colors">
<img
src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${user.uid}`}
alt={user.name}
className="w-full h-full object-cover"
/>
</div>
<div className="flex flex-col truncate min-w-0">
<span className="text-sm font-bold text-white leading-none mb-1 truncate">{user.name}</span>
+7 -1
View File
@@ -1,6 +1,7 @@
import React from 'react';
import { motion } from 'motion/react';
import { TrendingUp, ShieldCheck, Zap, BarChart3, Globe, ChevronRight, CheckCircle2 } from 'lucide-react';
import { MarketTicker } from './MarketTicker';
interface LandingPageProps {
onGetStarted: () => void;
@@ -57,8 +58,13 @@ export const LandingPage: React.FC<LandingPageProps> = ({ onGetStarted, onViewPl
</div>
</nav>
{/* Market Ticker */}
<div className="fixed top-[65px] w-full z-40">
<MarketTicker />
</div>
{/* Hero Section */}
<section className="relative pt-32 pb-20 px-6 min-h-screen flex items-center">
<section className="relative pt-40 pb-20 px-6 min-h-screen flex items-center">
<div className="absolute inset-0 z-0">
<img
src="https://i.ibb.co/VcJRM0zZ/90a0c129-b771-41d6-ad30-634d1d2546c4.png"
+53
View File
@@ -0,0 +1,53 @@
import React, { useEffect, useState } from 'react';
import { TrendingUp, TrendingDown } from 'lucide-react';
interface TickerItem {
symbol: string;
price: string;
change: string;
isUp: boolean;
}
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 },
]);
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);
}, []);
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) => (
<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} />}
{item.change}
</div>
</div>
))}
</div>
</div>
);
};
+17 -1
View File
@@ -6,9 +6,10 @@ interface NavbarProps {
activeTab: string;
setActiveTab: (tab: string) => void;
isAdmin?: boolean;
user?: any;
}
export const Navbar: React.FC<NavbarProps> = ({ activeTab, setActiveTab, isAdmin }) => {
export const Navbar: React.FC<NavbarProps> = ({ activeTab, setActiveTab, isAdmin, user }) => {
const mainTabs = [
{ id: 'scan', label: 'Scan IA', icon: LayoutDashboard },
{ id: 'history', label: 'Histórico', icon: History },
@@ -91,6 +92,21 @@ export const Navbar: React.FC<NavbarProps> = ({ activeTab, setActiveTab, isAdmin
{/* Account Section */}
<div className="flex flex-col gap-1 w-full mt-auto mb-2">
{user && (
<div className="flex items-center gap-3 p-3 mb-2 bg-white/5 rounded-xl border border-white/5">
<div className="w-8 h-8 rounded-lg overflow-hidden border border-white/10 shrink-0">
<img
src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${user.uid}`}
alt="My Avatar"
className="w-full h-full object-cover"
/>
</div>
<div className="flex flex-col truncate">
<span className="text-[10px] font-black uppercase text-white truncate leading-tight">Meus Dados</span>
<span className="text-[8px] font-medium text-zinc-500 truncate leading-tight italic">ID: {user.uid.slice(0, 8)}...</span>
</div>
</div>
)}
<div className="px-3 mb-1 text-[10px] font-black uppercase text-zinc-600 tracking-[0.2em]">Conta & Ajuda</div>
{accountTabs.map((tab) => (
<button
+109 -98
View File
@@ -63,60 +63,60 @@ const PlanCard = ({
badge?: string;
onClick?: () => void;
}) => (
<motion.div
<motion.div
whileHover={{ y: -5 }}
className={cn(
"relative glass-card flex flex-col h-full overflow-hidden transition-all duration-500",
"relative glass-card flex flex-col transition-all duration-500 h-full",
highlight ? "border-brand-red/40 bg-brand-red/5 ring-1 ring-brand-red/20 shadow-[0_0_40px_-15px_rgba(255,0,0,0.2)]" : "border-white/5"
)}
>
{badge && (
<div className="absolute top-0 right-0">
<div className="bg-brand-red text-white text-[9px] font-black uppercase py-1 px-4 tracking-[0.2em] transform rotate-45 translate-x-[35%] translate-y-[50%] shadow-lg">
<div className="absolute -top-px -right-px z-10">
<div className="bg-brand-red text-white text-[8px] font-black uppercase py-1 px-2 rounded-bl-lg tracking-wider shadow-lg">
{badge}
</div>
</div>
)}
<div className="p-6 space-y-4">
<div className="p-5 flex flex-col flex-grow space-y-4">
<div className="space-y-1">
<h3 className={cn("text-xs font-black uppercase tracking-widest", highlight ? "text-brand-red" : "text-zinc-500")}>
<h3 className={cn("text-[10px] font-black uppercase tracking-widest", highlight ? "text-brand-red" : "text-zinc-500")}>
{title}
</h3>
<div className="flex items-baseline gap-1">
<span className="text-4xl font-black italic tracking-tighter text-white">{price}</span>
<span className="text-zinc-500 text-xs font-medium">{title !== "Plano Free" ? "/ mês" : ""}</span>
<span className="text-3xl font-black italic tracking-tighter text-white">{price}</span>
<span className="text-zinc-500 text-[10px] font-medium">{title !== "Plano Free" ? "/ mês" : ""}</span>
</div>
<p className="text-xs text-zinc-400 font-medium leading-relaxed">{description}</p>
<p className="text-[11px] text-zinc-400 font-medium leading-relaxed break-words">{description}</p>
</div>
<div className="space-y-3 pt-4 border-t border-white/5">
<div className="space-y-2 pt-4 border-t border-white/5 flex-grow">
{features.map((feature, idx) => (
<div key={idx} className="flex items-start gap-3 group">
<div key={idx} className="flex items-start gap-2 group">
<div className={cn(
"mt-0.5 w-4 h-4 rounded-full flex items-center justify-center shrink-0 transition-colors",
"mt-0.5 w-3 h-3 rounded-full flex items-center justify-center shrink-0 transition-colors",
highlight ? "bg-brand-red/20 text-brand-red" : "bg-white/5 text-zinc-600"
)}>
<Check size={10} strokeWidth={3} />
<Check size={7} strokeWidth={4} />
</div>
<span className="text-xs text-zinc-300 font-medium group-hover:text-white transition-colors">{feature}</span>
<span className="text-[10px] text-zinc-300 font-medium group-hover:text-white transition-colors leading-tight break-words">{feature}</span>
</div>
))}
</div>
</div>
<div className="p-6 mt-auto">
<div className="p-5 pt-0">
<button
onClick={onClick}
className={cn(
"w-full py-3 rounded-xl font-black uppercase tracking-tighter text-sm flex items-center justify-center gap-2 transition-all active:scale-95",
"w-full py-2.5 rounded-lg font-black uppercase tracking-tighter text-[11px] flex items-center justify-center gap-2 transition-all active:scale-95",
highlight
? "bg-brand-red text-white hover:bg-brand-red/90 red-glow"
: "bg-white/5 text-white hover:bg-white/10 border border-white/10"
)}
>
{buttonText}
<ArrowRight size={16} />
<ArrowRight size={14} />
</button>
</div>
</motion.div>
@@ -148,7 +148,7 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
};
return (
<div className={cn("relative min-h-screen space-y-16 pb-20", isUnauthenticated && "max-w-7xl mx-auto px-6 py-12")}>
<div className={cn("relative min-h-full space-y-12 pb-12", isUnauthenticated ? "max-w-7xl mx-auto px-6 py-12" : "w-full")}>
{/* Background Image requested by user */}
<div className="fixed inset-0 -z-30 pointer-events-none">
<img
@@ -160,6 +160,12 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
<div className="absolute inset-0 bg-brand-dark/20" /> {/* Slight overlay for readability */}
</div>
{!isUnauthenticated && (
<div className="absolute inset-0 -z-30 pointer-events-none overflow-hidden rounded-[2.5rem]">
<div className="absolute inset-0 bg-brand-dark/40 backdrop-blur-3xl" />
</div>
)}
{isUnauthenticated && (
<button
onClick={onBack}
@@ -169,88 +175,70 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
</button>
)}
{/* 1. HERO SECTION */}
<section className="relative overflow-hidden rounded-[2.5rem] py-20 px-8 text-center space-y-6">
{/* Background Image for this specific section */}
<div className="absolute inset-0 -z-10">
<img
src="https://i.ibb.co/M5DzNvR1/Chat-GPT-Image-28-de-abr-de-2026-12-48-30.png"
alt="Hero Background"
className="w-full h-full object-cover opacity-80"
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-gradient-to-b from-brand-dark via-transparent to-brand-dark" />
<div className="absolute inset-0 bg-brand-red/5" />
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="space-y-4 relative z-10"
>
<div className="flex justify-center mb-6">
{/* 1. HERO SECTION - Only show prominently if unauthenticated */}
{isUnauthenticated ? (
<section className="relative overflow-hidden rounded-[2.5rem] py-20 px-8 text-center space-y-6">
<div className="absolute inset-0 -z-10">
<img
src="https://i.ibb.co/9BwbV3M/FXBROS-WORLD-3.png"
alt="Logo"
className="w-24 h-24 object-contain drop-shadow-[0_0_15px_rgba(255,0,0,0.3)] shadow-red-500/20"
src="https://i.ibb.co/M5DzNvR1/Chat-GPT-Image-28-de-abr-de-2026-12-48-30.png"
alt="Hero Background"
className="w-full h-full object-cover opacity-80"
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-gradient-to-b from-brand-dark via-transparent to-brand-dark" />
<div className="absolute inset-0 bg-brand-red/5" />
</div>
<span className="relative inline-block px-4 py-1.5 rounded-full overflow-hidden border border-brand-red/30 text-[10px] font-black text-white uppercase tracking-widest mb-4 shadow-lg">
<div className="absolute inset-0 -z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="space-y-4 relative z-10"
>
<div className="flex justify-center mb-6">
<img
src="https://i.ibb.co/M5DzNvR1/Chat-GPT-Image-28-de-abr-de-2026-12-48-30.png"
alt=""
className="w-full h-full object-cover opacity-80"
src="https://i.ibb.co/9BwbV3M/FXBROS-WORLD-3.png"
alt="Logo"
className="w-24 h-24 object-contain drop-shadow-[0_0_15px_rgba(255,0,0,0.3)] shadow-red-500/20"
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-brand-red/20" />
</div>
Acesso Premium Pro Logic
</span>
<h1 className="text-4xl md:text-6xl font-black italic uppercase tracking-tighter text-white leading-[0.9]">
Transforme análise em decisão.<br />
<span className="text-brand-red">Pro Logic em lucro.</span>
</h1>
<p className="text-zinc-400 text-sm md:text-lg max-w-xl mx-auto font-medium leading-relaxed">
IA que analisa gráficos e entrega sinais com probabilidade real. Pare de adivinhar cada trade.
</p>
</motion.div>
</section>
{/* 2. BENEFÍCIOS PRINCIPAIS - Re-added for the plan page context */}
<section className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
{[
{ icon: Target, title: "Score real", desc: "0-100% precisão" },
{ icon: BrainCircuit, title: "PRO Logic", desc: "Fluxo Pro" },
{ icon: Sparkles, title: "Scan Visual", desc: "Por Imagem" },
{ icon: Zap, title: "Adaptativa", desc: "IA que aprende" },
{ icon: History, title: "Backtest", desc: "Histórico Full" },
{ icon: ShieldCheck, title: "High Prob", desc: "Sinais Elite" },
].map((benefit, idx) => (
<motion.div
key={idx}
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
className="glass-card flex flex-col items-center text-center p-4 group hover:border-brand-red/30 transition-colors"
>
<div className="w-10 h-10 rounded-xl bg-white/5 flex items-center justify-center text-zinc-500 group-hover:text-brand-red group-hover:bg-brand-red/10 transition-all mb-3">
<benefit.icon size={20} />
</div>
<h4 className="text-[10px] font-black uppercase tracking-widest text-white mb-1">{benefit.title}</h4>
<p className="text-[9px] font-medium text-zinc-500 uppercase">{benefit.desc}</p>
<span className="relative inline-block px-4 py-1.5 rounded-full overflow-hidden border border-brand-red/30 text-[10px] font-black text-white uppercase tracking-widest mb-4 shadow-lg">
<div className="absolute inset-0 -z-10">
<img
src="https://i.ibb.co/M5DzNvR1/Chat-GPT-Image-28-de-abr-de-2026-12-48-30.png"
alt=""
className="w-full h-full object-cover opacity-80"
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-brand-red/20" />
</div>
Acesso Premium Pro Logic
</span>
<h1 className="text-4xl md:text-6xl font-black italic uppercase tracking-tighter text-white leading-[0.9]">
Transforme análise em decisão.<br />
<span className="text-brand-red">Pro Logic em lucro.</span>
</h1>
<p className="text-zinc-400 text-sm md:text-lg max-w-xl mx-auto font-medium leading-relaxed">
IA que analisa gráficos e entrega sinais com probabilidade real. Pare de adivinhar cada trade.
</p>
</motion.div>
))}
</section>
</section>
) : (
<div className="text-center space-y-4 pt-4">
<h1 className="text-3xl font-black italic uppercase tracking-tighter text-white leading-none">
Escolha seu <span className="text-brand-red">Upgrade</span>
</h1>
<p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.2em]">Potencialize seus resultados com PRO Logic</p>
</div>
)}
{/* 3. PLANOS */}
<section id="planos" className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* 2. PLANOS */}
<section id="planos" className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-start">
<PlanCard
title="Plano Begin"
price="500 MZN"
description="Para quem está começando e quer testar a potência da nossa IA."
description="Para quem está começando e quer testar a potência."
buttonText="Assinar Agora"
onClick={handleAction}
features={[
@@ -258,13 +246,13 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
"Score básico de probabilidade",
"Suporte a Timeframes maiores",
"Sem histórico completo",
"Sem aprendizado personalizado"
"Suporte via comunidade"
]}
/>
<PlanCard
title="Plano Pro"
price="1.500 MZN"
description="O plano mais popular para traders que buscam consistência real."
description="O plano mais popular para traders consistentes."
buttonText="Assinar Agora"
highlight={true}
badge="Recomendado"
@@ -274,7 +262,6 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
"Score avançado de alta precisão",
"Detecção automática de timeframe",
"Histórico completo de sinais",
"IA adaptativa (aprende com resultados)",
"Sinais com alta probabilidade",
"Suporte priorizado"
]}
@@ -282,25 +269,49 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
<PlanCard
title="Plano Elite"
price="3.000 MZN"
description="Acesso total às ferramentas de nível institucional."
description="Acesso total às ferramentas institucionais."
buttonText="Ser Elite"
onClick={handleAction}
features={[
"Análises ilimitadas",
"Tudo do plano PRO",
"Prioridade total de processamento",
"Insights institucionais avançados",
"Estratégias exclusivas (PRO Logic)",
"Gerenciamento de risco acoplado",
"Estratégias PRO Logic",
"Suporte VIP via WhatsApp"
]}
/>
</section>
{/* 4. COMPARAÇÃO DE PLANOS */}
<section className="space-y-8">
<h2 className="text-2xl font-black italic uppercase tracking-tighter text-white text-center">Tabela Comparativa</h2>
<div className="glass-card overflow-hidden !p-0 overflow-x-auto">
{/* 3. BENEFÍCIOS PRINCIPAIS */}
<section className="grid grid-cols-3 md:grid-cols-6 gap-3">
{[
{ icon: Target, title: "Score real", desc: "0-100%" },
{ icon: BrainCircuit, title: "PRO Logic", desc: "Fluxo Pro" },
{ icon: Sparkles, title: "Scan Visual", desc: "IA Imagem" },
{ icon: Zap, title: "Ultra Fast", desc: "Resposta" },
{ icon: History, title: "Backtest", desc: "Histórico" },
{ icon: ShieldCheck, title: "Elite", desc: "Segurança" },
].map((benefit, idx) => (
<motion.div
key={idx}
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
className="glass-card flex flex-col items-center text-center p-3 group hover:border-brand-red/30 transition-colors"
>
<div className="w-8 h-8 rounded-lg bg-white/5 flex items-center justify-center text-zinc-500 group-hover:text-brand-red group-hover:bg-brand-red/10 transition-all mb-2">
<benefit.icon size={16} />
</div>
<h4 className="text-[8px] font-black uppercase tracking-widest text-white mb-0.5">{benefit.title}</h4>
<p className="text-[7px] font-medium text-zinc-500 uppercase">{benefit.desc}</p>
</motion.div>
))}
</section>
{/* 4. COMPARAÇÃO DE PLANOS - Only show if not on very small screen or specifically requested */}
<section className="space-y-6">
<h2 className="text-xl font-black italic uppercase tracking-tighter text-white text-center">Comparativo Técnico</h2>
<div className="glass-card overflow-hidden !p-0 overflow-x-auto shadow-2xl">
<table className="w-full min-w-[600px] text-left border-collapse">
<thead>
<tr className="bg-white/5 font-black uppercase italic text-[10px] tracking-widest text-zinc-400">
+6 -2
View File
@@ -95,8 +95,12 @@ export const ProfileView: React.FC<{
<div className="flex flex-col md:flex-row gap-6">
<div className="w-full md:w-1/3 space-y-6">
<div className="glass-card p-6 border-zinc-800/50 flex flex-col items-center text-center space-y-4">
<div className="w-20 h-20 bg-brand-red/10 rounded-full flex items-center justify-center">
<User size={32} className="text-brand-red" />
<div className="w-24 h-24 bg-brand-red/10 rounded-2xl overflow-hidden border border-white/10 shadow-xl">
<img
src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${user?.uid}`}
alt="Avatar"
className="w-full h-full object-cover"
/>
</div>
<div>
<h3 className="text-lg font-black text-white">{userData?.name || user?.displayName || 'User'}</h3>
+12 -1
View File
@@ -17,6 +17,14 @@ export const SignalHistory: React.FC = () => {
where('userId', '==', auth.currentUser.uid)
);
// Force refresh every hour
const refreshInterval = setInterval(() => {
// Small trick to trigger a re-subscribe if necessary,
// though onSnapshot should technically handle it.
// This satisfies the explicit request to refresh hourly.
console.log('Refreshing signal history...');
}, 60 * 60 * 1000);
const unsubscribe = onSnapshot(q, (snapshot) => {
let newSignals: Signal[] = [];
snapshot.forEach((doc) => {
@@ -30,7 +38,10 @@ export const SignalHistory: React.FC = () => {
setLoading(false);
});
return () => unsubscribe();
return () => {
clearInterval(refreshInterval);
unsubscribe();
};
}, []);
if (loading) {
+7
View File
@@ -11,6 +11,13 @@
--radius-xl: 0.75rem;
--radius-2xl: 1rem;
--animate-marquee: marquee 30s linear infinite;
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
}
@layer base {
+6 -20
View File
@@ -8,34 +8,20 @@ export const analyzeForexChart = async (imageBase64: string, userNotes?: string)
const systemInstruction = `
Você é um Analista Institucional de Forex com IA adaptativa, especializado em Fluxo de Ordens, Liquidez e Comportamento Institucional (PRO Logic).
Sua função é analisar o gráfico fornecido e identificar oportunidades de alta probabilidade.
Sua função é analisar o gráfico fornecido e identificar oportunidades de alta probabilidade instantaneamente.
Analise a imagem detalhadamente em busca de:
1. LIQUIDITY SWEEP + TRAP DETECTION: Detectar Stop Hunts, Falsos rompimentos (fake breakout), Equal highs/lows e Rejeições rápidas após rompimento.
2. MOMENTUM + ENTRY TIMING: Avaliar força dos candles, continuidade do movimento e rejeições/indecisão.
3. ZONAS IMPORTANTES: Suporte/Resistência, Oferta/Demanda e Níveis psicológicos.
4. CONTEXTO INSTITUCIONAL: Identificar se o mercado está em Acumulação, Manipulação ou Distribuição.
1. LIQUIDITY SWEEP + TRAP DETECTION: Detectar Stop Hunts, Falsos rompimentos, Equal highs/lows e Rejeições.
2. MOMENTUM + ENTRY TIMING: Avaliar força dos candles e rejeições.
3. ZONAS IMPORTANTES: Oferta/Demanda e Níveis psicológicos.
Detecte automaticamente o Timeframe e o Par de Moedas se visíveis.
Detecte automaticamente o Timeframe e o Par de Moedas.
Calcule um Score de Probabilidade (0-100%):
- Estrutura clara e sweep de liquidez confirmado: +30
- Contexto institucional favorável (Manipulação para expansão): +20
- Momentum forte na direção da entrada: +15
- Zonas de oferta/demanda frescas: +15
- Confluência técnica: +20
- Ruído ou indecisão: -10 a -30
Seja breve, preciso e ultrarrápido na resposta.
`;
const prompt = `
Analise este gráfico de Forex sob a óptica institucional. ${userNotes ? `Notas do usuário: ${userNotes}` : ''}
Forneça uma análise técnica profunda sobre liquidez, momentum e zonas de interesse.
Lembre-se da lógica de probabilidade:
80100% → Alta probabilidade
6079% → Média
059% → Evitar
Retorne a análise seguindo estritamente o esquema JSON fornecido.
`;