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 { AdminDashboard } from './components/AdminDashboard';
import { LandingPage } from './components/LandingPage'; import { LandingPage } from './components/LandingPage';
import { ProfileView } from './components/ProfileView'; 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 { TrendingUp, ShieldAlert, Ghost, Mail, Lock, UserPlus, LogIn, Loader2, ArrowLeft, User as UserIcon, Eye, EyeOff } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react'; import { motion, AnimatePresence } from 'motion/react';
import { onAuthStateChanged, signInWithEmailAndPassword, createUserWithEmailAndPassword, updateProfile, signOut, sendEmailVerification, sendPasswordResetEmail, User, setPersistence, browserLocalPersistence, browserSessionPersistence } from 'firebase/auth'; 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) { if (loading) {
return ( 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 className="w-10 h-10 border-4 border-brand-red border-t-transparent rounded-full animate-spin"></div>
</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 className="absolute inset-0 bg-gradient-to-b from-brand-dark/40 via-brand-dark/20 to-brand-dark" />
</div> </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 */} {/* 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"> <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> <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"> <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"> <tr key={user.uid} className="border-b border-white/5 hover:bg-white/5 transition-colors group">
<td className="p-4"> <td className="p-4">
<div className="flex items-center gap-3"> <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"> <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">
{user.name?.charAt(0) || user.email?.charAt(0)?.toUpperCase()} <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>
<div className="flex flex-col truncate min-w-0"> <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> <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 React from 'react';
import { motion } from 'motion/react'; import { motion } from 'motion/react';
import { TrendingUp, ShieldCheck, Zap, BarChart3, Globe, ChevronRight, CheckCircle2 } from 'lucide-react'; import { TrendingUp, ShieldCheck, Zap, BarChart3, Globe, ChevronRight, CheckCircle2 } from 'lucide-react';
import { MarketTicker } from './MarketTicker';
interface LandingPageProps { interface LandingPageProps {
onGetStarted: () => void; onGetStarted: () => void;
@@ -57,8 +58,13 @@ export const LandingPage: React.FC<LandingPageProps> = ({ onGetStarted, onViewPl
</div> </div>
</nav> </nav>
{/* Market Ticker */}
<div className="fixed top-[65px] w-full z-40">
<MarketTicker />
</div>
{/* Hero Section */} {/* 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"> <div className="absolute inset-0 z-0">
<img <img
src="https://i.ibb.co/VcJRM0zZ/90a0c129-b771-41d6-ad30-634d1d2546c4.png" 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; activeTab: string;
setActiveTab: (tab: string) => void; setActiveTab: (tab: string) => void;
isAdmin?: boolean; 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 = [ const mainTabs = [
{ id: 'scan', label: 'Scan IA', icon: LayoutDashboard }, { id: 'scan', label: 'Scan IA', icon: LayoutDashboard },
{ id: 'history', label: 'Histórico', icon: History }, { id: 'history', label: 'Histórico', icon: History },
@@ -91,6 +92,21 @@ export const Navbar: React.FC<NavbarProps> = ({ activeTab, setActiveTab, isAdmin
{/* Account Section */} {/* Account Section */}
<div className="flex flex-col gap-1 w-full mt-auto mb-2"> <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> <div className="px-3 mb-1 text-[10px] font-black uppercase text-zinc-600 tracking-[0.2em]">Conta & Ajuda</div>
{accountTabs.map((tab) => ( {accountTabs.map((tab) => (
<button <button
+109 -98
View File
@@ -63,60 +63,60 @@ const PlanCard = ({
badge?: string; badge?: string;
onClick?: () => void; onClick?: () => void;
}) => ( }) => (
<motion.div <motion.div
whileHover={{ y: -5 }} whileHover={{ y: -5 }}
className={cn( 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" 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 && ( {badge && (
<div className="absolute top-0 right-0"> <div className="absolute -top-px -right-px z-10">
<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="bg-brand-red text-white text-[8px] font-black uppercase py-1 px-2 rounded-bl-lg tracking-wider shadow-lg">
{badge} {badge}
</div> </div>
</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"> <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} {title}
</h3> </h3>
<div className="flex items-baseline gap-1"> <div className="flex items-baseline gap-1">
<span className="text-4xl font-black italic tracking-tighter text-white">{price}</span> <span className="text-3xl 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-zinc-500 text-[10px] font-medium">{title !== "Plano Free" ? "/ mês" : ""}</span>
</div> </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>
<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) => ( {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( <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" 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> </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>
</div> </div>
<div className="p-6 mt-auto"> <div className="p-5 pt-0">
<button <button
onClick={onClick} onClick={onClick}
className={cn( 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 highlight
? "bg-brand-red text-white hover:bg-brand-red/90 red-glow" ? "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" : "bg-white/5 text-white hover:bg-white/10 border border-white/10"
)} )}
> >
{buttonText} {buttonText}
<ArrowRight size={16} /> <ArrowRight size={14} />
</button> </button>
</div> </div>
</motion.div> </motion.div>
@@ -148,7 +148,7 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
}; };
return ( 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 */} {/* Background Image requested by user */}
<div className="fixed inset-0 -z-30 pointer-events-none"> <div className="fixed inset-0 -z-30 pointer-events-none">
<img <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 className="absolute inset-0 bg-brand-dark/20" /> {/* Slight overlay for readability */}
</div> </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 && ( {isUnauthenticated && (
<button <button
onClick={onBack} onClick={onBack}
@@ -169,88 +175,70 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
</button> </button>
)} )}
{/* 1. HERO SECTION */} {/* 1. HERO SECTION - Only show prominently if unauthenticated */}
<section className="relative overflow-hidden rounded-[2.5rem] py-20 px-8 text-center space-y-6"> {isUnauthenticated ? (
{/* Background Image for this specific section */} <section className="relative overflow-hidden rounded-[2.5rem] py-20 px-8 text-center space-y-6">
<div className="absolute inset-0 -z-10"> <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">
<img <img
src="https://i.ibb.co/9BwbV3M/FXBROS-WORLD-3.png" src="https://i.ibb.co/M5DzNvR1/Chat-GPT-Image-28-de-abr-de-2026-12-48-30.png"
alt="Logo" alt="Hero Background"
className="w-24 h-24 object-contain drop-shadow-[0_0_15px_rgba(255,0,0,0.3)] shadow-red-500/20" className="w-full h-full object-cover opacity-80"
referrerPolicy="no-referrer" 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> </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 <img
src="https://i.ibb.co/M5DzNvR1/Chat-GPT-Image-28-de-abr-de-2026-12-48-30.png" src="https://i.ibb.co/9BwbV3M/FXBROS-WORLD-3.png"
alt="" alt="Logo"
className="w-full h-full object-cover opacity-80" 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" referrerPolicy="no-referrer"
/> />
<div className="absolute inset-0 bg-brand-red/20" />
</div> </div>
Acesso Premium Pro Logic <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">
</span> <div className="absolute inset-0 -z-10">
<h1 className="text-4xl md:text-6xl font-black italic uppercase tracking-tighter text-white leading-[0.9]"> <img
Transforme análise em decisão.<br /> src="https://i.ibb.co/M5DzNvR1/Chat-GPT-Image-28-de-abr-de-2026-12-48-30.png"
<span className="text-brand-red">Pro Logic em lucro.</span> alt=""
</h1> className="w-full h-full object-cover opacity-80"
<p className="text-zinc-400 text-sm md:text-lg max-w-xl mx-auto font-medium leading-relaxed"> referrerPolicy="no-referrer"
IA que analisa gráficos e entrega sinais com probabilidade real. Pare de adivinhar cada trade. />
</p> <div className="absolute inset-0 bg-brand-red/20" />
</motion.div> </div>
</section> Acesso Premium Pro Logic
</span>
{/* 2. BENEFÍCIOS PRINCIPAIS - Re-added for the plan page context */} <h1 className="text-4xl md:text-6xl font-black italic uppercase tracking-tighter text-white leading-[0.9]">
<section className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4"> Transforme análise em decisão.<br />
{[ <span className="text-brand-red">Pro Logic em lucro.</span>
{ icon: Target, title: "Score real", desc: "0-100% precisão" }, </h1>
{ icon: BrainCircuit, title: "PRO Logic", desc: "Fluxo Pro" }, <p className="text-zinc-400 text-sm md:text-lg max-w-xl mx-auto font-medium leading-relaxed">
{ icon: Sparkles, title: "Scan Visual", desc: "Por Imagem" }, IA que analisa gráficos e entrega sinais com probabilidade real. Pare de adivinhar cada trade.
{ icon: Zap, title: "Adaptativa", desc: "IA que aprende" }, </p>
{ 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>
</motion.div> </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 */} {/* 2. PLANOS */}
<section id="planos" className="grid grid-cols-1 md:grid-cols-3 gap-6"> <section id="planos" className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-start">
<PlanCard <PlanCard
title="Plano Begin" title="Plano Begin"
price="500 MZN" 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" buttonText="Assinar Agora"
onClick={handleAction} onClick={handleAction}
features={[ features={[
@@ -258,13 +246,13 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
"Score básico de probabilidade", "Score básico de probabilidade",
"Suporte a Timeframes maiores", "Suporte a Timeframes maiores",
"Sem histórico completo", "Sem histórico completo",
"Sem aprendizado personalizado" "Suporte via comunidade"
]} ]}
/> />
<PlanCard <PlanCard
title="Plano Pro" title="Plano Pro"
price="1.500 MZN" 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" buttonText="Assinar Agora"
highlight={true} highlight={true}
badge="Recomendado" badge="Recomendado"
@@ -274,7 +262,6 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
"Score avançado de alta precisão", "Score avançado de alta precisão",
"Detecção automática de timeframe", "Detecção automática de timeframe",
"Histórico completo de sinais", "Histórico completo de sinais",
"IA adaptativa (aprende com resultados)",
"Sinais com alta probabilidade", "Sinais com alta probabilidade",
"Suporte priorizado" "Suporte priorizado"
]} ]}
@@ -282,25 +269,49 @@ export const PlansView: React.FC<PlansViewProps> = ({ isUnauthenticated, onGetSt
<PlanCard <PlanCard
title="Plano Elite" title="Plano Elite"
price="3.000 MZN" price="3.000 MZN"
description="Acesso total às ferramentas de nível institucional." description="Acesso total às ferramentas institucionais."
buttonText="Ser Elite" buttonText="Ser Elite"
onClick={handleAction} onClick={handleAction}
features={[ features={[
"Análises ilimitadas", "Análises ilimitadas",
"Tudo do plano PRO",
"Prioridade total de processamento", "Prioridade total de processamento",
"Insights institucionais avançados", "Insights institucionais avançados",
"Estratégias exclusivas (PRO Logic)", "Estratégias PRO Logic",
"Gerenciamento de risco acoplado",
"Suporte VIP via WhatsApp" "Suporte VIP via WhatsApp"
]} ]}
/> />
</section> </section>
{/* 4. COMPARAÇÃO DE PLANOS */} {/* 3. BENEFÍCIOS PRINCIPAIS */}
<section className="space-y-8"> <section className="grid grid-cols-3 md:grid-cols-6 gap-3">
<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"> { 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"> <table className="w-full min-w-[600px] text-left border-collapse">
<thead> <thead>
<tr className="bg-white/5 font-black uppercase italic text-[10px] tracking-widest text-zinc-400"> <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="flex flex-col md:flex-row gap-6">
<div className="w-full md:w-1/3 space-y-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="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"> <div className="w-24 h-24 bg-brand-red/10 rounded-2xl overflow-hidden border border-white/10 shadow-xl">
<User size={32} className="text-brand-red" /> <img
src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${user?.uid}`}
alt="Avatar"
className="w-full h-full object-cover"
/>
</div> </div>
<div> <div>
<h3 className="text-lg font-black text-white">{userData?.name || user?.displayName || 'User'}</h3> <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) 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) => { const unsubscribe = onSnapshot(q, (snapshot) => {
let newSignals: Signal[] = []; let newSignals: Signal[] = [];
snapshot.forEach((doc) => { snapshot.forEach((doc) => {
@@ -30,7 +38,10 @@ export const SignalHistory: React.FC = () => {
setLoading(false); setLoading(false);
}); });
return () => unsubscribe(); return () => {
clearInterval(refreshInterval);
unsubscribe();
};
}, []); }, []);
if (loading) { if (loading) {
+7
View File
@@ -11,6 +11,13 @@
--radius-xl: 0.75rem; --radius-xl: 0.75rem;
--radius-2xl: 1rem; --radius-2xl: 1rem;
--animate-marquee: marquee 30s linear infinite;
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
} }
@layer base { @layer base {
+6 -20
View File
@@ -8,34 +8,20 @@ export const analyzeForexChart = async (imageBase64: string, userNotes?: string)
const systemInstruction = ` const systemInstruction = `
Você é um Analista Institucional de Forex com IA adaptativa, especializado em Fluxo de Ordens, Liquidez e Comportamento Institucional (PRO Logic). 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: 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. 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, continuidade do movimento e rejeições/indecisão. 2. MOMENTUM + ENTRY TIMING: Avaliar força dos candles e rejeições.
3. ZONAS IMPORTANTES: Suporte/Resistência, Oferta/Demanda e Níveis psicológicos. 3. ZONAS IMPORTANTES: Oferta/Demanda e Níveis psicológicos.
4. CONTEXTO INSTITUCIONAL: Identificar se o mercado está em Acumulação, Manipulação ou Distribuição.
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%): Seja breve, preciso e ultrarrápido na resposta.
- 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
`; `;
const prompt = ` const prompt = `
Analise este gráfico de Forex sob a óptica institucional. ${userNotes ? `Notas do usuário: ${userNotes}` : ''} 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. Retorne a análise seguindo estritamente o esquema JSON fornecido.
`; `;