import React,{useState,useMemo,useTransition,useEffect}from'react';interface AnalyticsData{stage:string;revenue:number;count:number;winRate:number}const useDebounce=<T,>(value:T,delay:number):T=>{const[debouncedValue,setDebouncedValue]=useState(value);useEffect(()=>{const handler=setTimeout(()=>setDebouncedValue(value),delay);return()=>clearTimeout(handler)},[value,delay]);return debouncedValue};const useToast=()=>{const[toasts,setToasts]=useState<Array<{id:number;message:string}>>([]);const show=(message:string)=>{const id=Date.now();setToasts(prev=>[...prev,{id,message}]);setTimeout(()=>setToasts(prev=>prev.filter(t=>t.id!==id)),3000)};return{toasts,show}};const mockData:AnalyticsData[]=[{stage:'Prospecting',revenue:125000,count:45,winRate:15},{stage:'Qualification',revenue:480000,count:32,winRate:25},{stage:'Proposal',revenue:950000,count:18,winRate:55},{stage:'Negotiation',revenue:1200000,count:12,winRate:75}];const App:React.FC=()=>{const[searchQuery,setSearchQuery]=useState('');const[isPending,startTransition]=useTransition();const{toasts,show}=useToast();const debouncedSearch=useDebounce(searchQuery,300);const filteredData=useMemo(()=>{return mockData.filter(d=>d.stage.toLowerCase().includes(debouncedSearch.toLowerCase()))},[debouncedSearch]);const stats=useMemo(()=>({totalRevenue:mockData.reduce((sum,d)=>sum+d.revenue,0),totalOpps:mockData.reduce((sum,d)=>sum+d.count,0),avgWinRate:Math.round(mockData.reduce((sum,d)=>sum+d.winRate,0)/mockData.length),forecast:mockData.reduce((sum,d)=>sum+(d.revenue*d.winRate/100),0)}),[]);const handleSearch=(e:React.ChangeEvent<HTMLInputElement>)=>{startTransition(()=>{setSearchQuery(e.target.value)})};const handleViewStage=(data:AnalyticsData)=>{show(`Viewing ${data.stage} stage`)};return(<div className="app-container"><header className="app-header"><h1>Sales Analytics</h1><p>Revenue by stage, win rates, and forecast</p></header><div className="stats-grid"><div className="stat-card"><div className="stat-label">Total Revenue</div><div className="stat-value">${(stats.totalRevenue/1000000).toFixed(1)}M</div></div><div className="stat-card"><div className="stat-label">Total Opportunities</div><div className="stat-value">{stats.totalOpps}</div></div><div className="stat-card"><div className="stat-label">Avg Win Rate</div><div className="stat-value">{stats.avgWinRate}%</div></div><div className="stat-card"><div className="stat-label">Forecast</div><div className="stat-value">${(stats.forecast/1000000).toFixed(1)}M</div></div></div><div className="controls"><input type="text" placeholder="Search by stage..." value={searchQuery} onChange={handleSearch} className="search-input"/></div>{filteredData.length===0?(<div className="empty-state"><div className="empty-icon">📊</div><h3>No data found</h3><p>Try adjusting your search</p></div>):(<div className="data-grid"><table><thead><tr><th>Stage</th><th>Revenue</th><th>Opportunities</th><th>Win Rate</th><th>Actions</th></tr></thead><tbody className={isPending?'loading':''}>{filteredData.map(d=>(<tr key={d.stage}><td><strong>{d.stage}</strong></td><td>${(d.revenue/1000).toFixed(0)}K</td><td>{d.count}</td><td>{d.winRate}%</td><td><button className="btn-primary" onClick={()=>handleViewStage(d)}>View</button></td></tr>))}</tbody></table></div>)}<div className="toast-container">{toasts.map(toast=>(<div key={toast.id} className="toast">{toast.message}</div>))}</div></div>)};export default App;
