Performance fixes
Kiro fixed Performance
This commit is contained in:
@@ -3,6 +3,7 @@ import { AuthProvider, useAuth } from '@/context/AuthContext'
|
||||
import { usePageTracking } from '@/hooks/useAnalytics'
|
||||
import { initAnalytics } from '@/lib/analytics'
|
||||
import { useTheme } from '@/hooks/useTheme'
|
||||
import { ErrorBoundary } from '@/components/ErrorBoundary'
|
||||
import { Home } from '@/pages/Home'
|
||||
import { Login } from '@/pages/Login'
|
||||
import { Register } from '@/pages/Register'
|
||||
@@ -135,11 +136,13 @@ function App() {
|
||||
useTheme()
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<AuthProvider>
|
||||
<AppRoutes />
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
<ErrorBoundary>
|
||||
<BrowserRouter>
|
||||
<AuthProvider>
|
||||
<AppRoutes />
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
53
client/src/components/ErrorBoundary.tsx
Normal file
53
client/src/components/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Component, ReactNode } from 'react'
|
||||
import { AlertTriangle } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
interface Props {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
this.state = { hasError: false, error: null }
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): State {
|
||||
return { hasError: true, error }
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: any) {
|
||||
console.error('ErrorBoundary caught:', error, errorInfo)
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-slate-50 dark:bg-slate-900 p-4">
|
||||
<div className="max-w-md w-full bg-white dark:bg-slate-800 rounded-lg shadow-lg p-6 text-center">
|
||||
<AlertTriangle className="w-12 h-12 text-red-500 mx-auto mb-4" />
|
||||
<h1 className="text-xl font-bold text-slate-900 dark:text-slate-100 mb-2">
|
||||
Something went wrong
|
||||
</h1>
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400 mb-6">
|
||||
{this.state.error?.message || 'An unexpected error occurred'}
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => window.location.href = '/'}
|
||||
className="w-full"
|
||||
>
|
||||
Go to Home
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
@@ -19,27 +19,34 @@ function useScrollBow(heroRef: React.RefObject<HTMLElement | null>) {
|
||||
const [progress, setProgress] = useState(0)
|
||||
useEffect(() => {
|
||||
const hero = heroRef?.current
|
||||
// #region agent log
|
||||
fetch('http://127.0.0.1:7245/ingest/e4d1df4e-a6e3-4cf2-a51c-bd8134c263cd',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'Hero.tsx:useScrollBow effect',message:'effect run',data:{hasHero:!!hero,height:hero?.getBoundingClientRect?.()?.height,rectTop:hero?.getBoundingClientRect?.()?.top},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'H4'})}).catch(()=>{});
|
||||
// #endregion
|
||||
if (!hero) return
|
||||
|
||||
let rafId: number | null = null
|
||||
const onScroll = () => {
|
||||
const rect = hero.getBoundingClientRect()
|
||||
const h = rect.height
|
||||
if (h <= 0) return
|
||||
const p = Math.max(0, Math.min(1, -rect.top / h))
|
||||
setProgress((prev) => {
|
||||
if (Math.abs(prev - p) > 0.05) {
|
||||
// #region agent log
|
||||
fetch('http://127.0.0.1:7245/ingest/e4d1df4e-a6e3-4cf2-a51c-bd8134c263cd',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'Hero.tsx:onScroll',message:'progress update',data:{p,rectTop:rect.top,h},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'H1'})}).catch(()=>{});
|
||||
// #endregion
|
||||
if (rafId !== null) return
|
||||
rafId = requestAnimationFrame(() => {
|
||||
const rect = hero.getBoundingClientRect()
|
||||
const h = rect.height
|
||||
if (h <= 0) {
|
||||
rafId = null
|
||||
return
|
||||
}
|
||||
return p
|
||||
const p = Math.max(0, Math.min(1, -rect.top / h))
|
||||
setProgress((prev) => {
|
||||
if (Math.abs(prev - p) > 0.05) {
|
||||
return p
|
||||
}
|
||||
return prev
|
||||
})
|
||||
rafId = null
|
||||
})
|
||||
}
|
||||
onScroll()
|
||||
window.addEventListener('scroll', onScroll, { passive: true })
|
||||
return () => window.removeEventListener('scroll', onScroll)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', onScroll)
|
||||
if (rafId !== null) cancelAnimationFrame(rafId)
|
||||
}
|
||||
}, [heroRef])
|
||||
return progress
|
||||
}
|
||||
@@ -53,14 +60,7 @@ function HeroEdgeCard({ name, quote, position, rotate, side, scrollProgress }: T
|
||||
const transform = `translate(${moveX}px, ${dropY}px) rotate(${rotate})`
|
||||
const opacity = Math.max(0, 1 - scrollProgress * 1.2)
|
||||
const visibility = opacity <= 0 ? 'hidden' : 'visible'
|
||||
// When scrollProgress === 0, do NOT set opacity so CSS hero-edge-in can run (staggered fade-in).
|
||||
// Once user scrolls, we drive opacity from scroll so cards bow out.
|
||||
const styleOpacity = scrollProgress > 0 ? opacity : undefined
|
||||
// #region agent log
|
||||
if (name === HERO_TESTIMONIALS[0].name) {
|
||||
fetch('http://127.0.0.1:7245/ingest/e4d1df4e-a6e3-4cf2-a51c-bd8134c263cd',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'Hero.tsx:HeroEdgeCard',message:'card style',data:{scrollProgress,transform,opacity,styleOpacity,visibility},timestamp:Date.now(),sessionId:'debug-session',runId:'post-fix',hypothesisId:'H1,H2'})}).catch(()=>{});
|
||||
}
|
||||
// #endregion
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -76,14 +76,6 @@ function HeroEdgeCard({ name, quote, position, rotate, side, scrollProgress }: T
|
||||
height={128}
|
||||
className="h-auto max-h-32 w-32 shrink-0 object-contain object-center block m-0"
|
||||
src="/logo.png"
|
||||
onLoad={(e) => {
|
||||
const img = e.currentTarget
|
||||
if (name === HERO_TESTIMONIALS[0].name) {
|
||||
// #region agent log
|
||||
fetch('http://127.0.0.1:7245/ingest/e4d1df4e-a6e3-4cf2-a51c-bd8134c263cd',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'Hero.tsx:img onLoad',message:'image size',data:{naturalWidth:img.naturalWidth,naturalHeight:img.naturalHeight,width:img.width,height:img.height},timestamp:Date.now(),sessionId:'debug-session',runId:'post-fix',hypothesisId:'H5'})}).catch(()=>{});
|
||||
// #endregion
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="max-w-[160px]">
|
||||
<h3 className="text-sm font-medium text-slate-800 dark:text-slate-200">{name}</h3>
|
||||
@@ -97,15 +89,6 @@ export function Hero() {
|
||||
const navigate = useNavigate()
|
||||
const heroRef = useRef<HTMLElement>(null)
|
||||
const scrollProgress = useScrollBow(heroRef)
|
||||
// #region agent log
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => {
|
||||
const el = heroRef.current
|
||||
fetch('http://127.0.0.1:7245/ingest/e4d1df4e-a6e3-4cf2-a51c-bd8134c263cd',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'Hero.tsx:mount',message:'after mount',data:{hasHero:!!el,height:el?.getBoundingClientRect?.()?.height,innerWidth:typeof window!=='undefined'?window.innerWidth:0},timestamp:Date.now(),sessionId:'debug-session',hypothesisId:'H3,H4'})}).catch(()=>{});
|
||||
}, 100)
|
||||
return () => clearTimeout(t)
|
||||
}, [])
|
||||
// #endregion
|
||||
|
||||
const handleCTAClick = () => {
|
||||
// Capture UTM parameters before navigation
|
||||
|
||||
@@ -106,7 +106,7 @@ export function Dashboard() {
|
||||
if (user?.$id) {
|
||||
loadData()
|
||||
}
|
||||
}, [user])
|
||||
}, [user?.$id])
|
||||
|
||||
const loadData = async () => {
|
||||
if (!user?.$id) return
|
||||
|
||||
Reference in New Issue
Block a user