|
|
import React from 'react'; |
|
|
import { BrowserRouter as Router, Routes, Route, Navigate, Link } from 'react-router-dom'; |
|
|
import Hero from './components/Hero'; |
|
|
import SingleAnalysis from './components/SingleAnalysis'; |
|
|
import BatchAnalysis from './components/BatchAnalysis'; |
|
|
|
|
|
const App: React.FC = () => { |
|
|
const [isPlaying, setIsPlaying] = React.useState(false); |
|
|
const audioRef = React.useRef<HTMLAudioElement>(null); |
|
|
|
|
|
React.useEffect(() => { |
|
|
const audio = audioRef.current; |
|
|
if (!audio) return; |
|
|
|
|
|
audio.volume = 0.5; |
|
|
|
|
|
|
|
|
const playPromise = audio.play(); |
|
|
if (playPromise !== undefined) { |
|
|
playPromise |
|
|
.then(() => setIsPlaying(true)) |
|
|
.catch((error) => { |
|
|
console.log("Autoplay blocked. Waiting for user interaction.", error); |
|
|
setIsPlaying(false); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const handleUserInteraction = () => { |
|
|
if (audio.paused) { |
|
|
audio.play() |
|
|
.then(() => { |
|
|
setIsPlaying(true); |
|
|
|
|
|
document.removeEventListener('click', handleUserInteraction); |
|
|
}) |
|
|
.catch(e => console.error("Play failed even after interaction:", e)); |
|
|
} |
|
|
}; |
|
|
|
|
|
document.addEventListener('click', handleUserInteraction); |
|
|
|
|
|
return () => { |
|
|
document.removeEventListener('click', handleUserInteraction); |
|
|
}; |
|
|
}, []); |
|
|
|
|
|
const toggleAudio = () => { |
|
|
if (audioRef.current) { |
|
|
if (isPlaying) { |
|
|
audioRef.current.pause(); |
|
|
} else { |
|
|
audioRef.current.play(); |
|
|
} |
|
|
setIsPlaying(!isPlaying); |
|
|
} |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<Router> |
|
|
<div className="text-white selection:bg-cyan-500/30 selection:text-cyan-200"> |
|
|
{/* Global Nav / Logo - Fixed and High Z-Index */} |
|
|
<div className="fixed top-0 left-0 w-full p-6 z-50 pointer-events-none flex justify-between items-start"> |
|
|
<Link to="/" className="inline-flex items-center gap-3 px-4 py-2 rounded-full bg-slate-900/80 backdrop-blur-md border border-white/10 shadow-lg pointer-events-auto hover:border-cyan-500/50 transition-all cursor-pointer group"> |
|
|
{/* Logo Icon */} |
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center overflow-hidden bg-white/5"> |
|
|
<img src="/static/logo.png" alt="Logo" className="w-full h-full object-cover" /> |
|
|
</div> |
|
|
<span className="font-bold tracking-widest text-lg text-white group-hover:text-cyan-400 transition-colors">Samsung Prism Prototype</span> |
|
|
</Link> |
|
|
|
|
|
{/* Audio Toggle */} |
|
|
<div className="relative"> |
|
|
{!isPlaying && ( |
|
|
<div className="absolute right-14 top-1/2 -translate-y-1/2 z-50 flex items-center"> |
|
|
{/* Gradient Border Container */} |
|
|
<div className="relative p-[2px] rounded-full bg-gradient-to-r from-cyan-400 via-purple-500 to-pink-500 animate-pulse shadow-[0_0_15px_rgba(34,211,238,0.5)]"> |
|
|
{/* Inner Glass Content */} |
|
|
<div className="bg-slate-950/90 backdrop-blur-sm rounded-full px-5 py-2.5 flex items-center gap-2"> |
|
|
<span className="text-xs font-bold !text-white whitespace-nowrap" style={{ color: '#ffffff', mixBlendMode: 'normal' }}> |
|
|
✨ Don't miss the magic! 🎧 |
|
|
</span> |
|
|
</div> |
|
|
{/* Arrow pointing to button */} |
|
|
<div className="absolute top-1/2 -right-1.5 -translate-y-1/2 w-3 h-3 bg-gradient-to-r from-purple-500 to-pink-500 rotate-45 transform origin-center -z-10" /> |
|
|
</div> |
|
|
</div> |
|
|
)} |
|
|
<button |
|
|
onClick={toggleAudio} |
|
|
className={`pointer-events-auto w-10 h-10 rounded-full bg-slate-900/80 backdrop-blur-md border border-white/10 flex items-center justify-center hover:bg-white/10 transition-all group ${!isPlaying ? 'animate-pulse ring-2 ring-cyan-500/50' : ''}`} |
|
|
title={isPlaying ? "Mute Background Music" : "Play Background Music"} |
|
|
> |
|
|
{isPlaying ? ( |
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-5 h-5 text-cyan-400 group-hover:scale-110 transition-transform"> |
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.426-1.643 1.087-2.146.24-.184.459-.387.653-.611H6.75z" /> |
|
|
</svg> |
|
|
) : ( |
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-5 h-5 text-slate-400 group-hover:text-white transition-colors"> |
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M17.25 9.75L19.5 12m0 0l2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.426-1.643 1.087-2.146.24-.184.459-.387.653-.611H6.75z" /> |
|
|
</svg> |
|
|
)} |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<audio |
|
|
ref={audioRef} |
|
|
src="/static/background.mp3" |
|
|
loop |
|
|
onError={(e) => console.error("Audio failed to load:", e)} |
|
|
/> |
|
|
|
|
|
<main className="animate-fade-in"> |
|
|
<Routes> |
|
|
<Route path="/" element={<Hero />} /> |
|
|
<Route path="/single" element={<SingleAnalysis />} /> |
|
|
<Route path="/batch" element={<BatchAnalysis />} /> |
|
|
<Route path="*" element={<Navigate to="/" replace />} /> |
|
|
</Routes> |
|
|
</main> |
|
|
</div> |
|
|
</Router> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default App; |