Floating options left

This commit is contained in:
2025-08-27 19:56:17 -06:00
parent 082036b1f9
commit d096975365

View File

@@ -1,7 +1,7 @@
'use client';
import { useEffect, useRef, useState } from 'react';
import { Camera, History, VideoOff } from 'lucide-react';
import { Camera, History, VideoOff, Settings, Video, Volume2, Palette, ChevronRight } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; // Assuming shadcn/ui has Select
import { SHOE_DATABASE, type Shoe } from '@/lib/shoe-database';
@@ -24,6 +24,7 @@ export default function HomePage() {
const [isPopupOpen, setPopupOpen] = useState(false);
const [isHistoryOpen, setHistoryOpen] = useState(false);
const [history, setHistory] = useState<Shoe[]>([]);
const [isSettingsPanelOpen, setSettingsPanelOpen] = useState(false);
// Effect to clean up the stream when component unmounts or stream changes
useEffect(() => {
@@ -185,17 +186,38 @@ export default function HomePage() {
<div className="w-16 h-16 bg-white/20 rounded-full animate-pulse"></div>
</div>
{/* Particle Effect */}
{/* Particle Effect - Fixed positions to avoid hydration mismatch */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
{[...Array(20)].map((_, i) => (
{[
{ left: 10, top: 20, delay: 0, duration: 2.5 },
{ left: 80, top: 15, delay: 0.3, duration: 3 },
{ left: 25, top: 70, delay: 0.8, duration: 2.2 },
{ left: 90, top: 40, delay: 1.2, duration: 2.8 },
{ left: 5, top: 85, delay: 0.5, duration: 3.2 },
{ left: 70, top: 25, delay: 1.5, duration: 2.4 },
{ left: 40, top: 60, delay: 0.2, duration: 2.9 },
{ left: 85, top: 75, delay: 1.8, duration: 2.6 },
{ left: 15, top: 45, delay: 0.7, duration: 3.1 },
{ left: 60, top: 10, delay: 1.1, duration: 2.3 },
{ left: 30, top: 90, delay: 0.4, duration: 2.7 },
{ left: 95, top: 55, delay: 1.6, duration: 2.1 },
{ left: 50, top: 30, delay: 0.9, duration: 2.8 },
{ left: 20, top: 65, delay: 0.1, duration: 3.3 },
{ left: 75, top: 80, delay: 1.3, duration: 2.2 },
{ left: 35, top: 5, delay: 0.6, duration: 2.9 },
{ left: 65, top: 50, delay: 1.4, duration: 2.5 },
{ left: 8, top: 35, delay: 1.7, duration: 3.1 },
{ left: 88, top: 95, delay: 0.3, duration: 2.4 },
{ left: 45, top: 22, delay: 1.9, duration: 2.7 }
].map((particle, i) => (
<div
key={i}
className="absolute w-1 h-1 bg-blue-500/30 rounded-full animate-ping"
style={{
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,
animationDelay: `${Math.random() * 2}s`,
animationDuration: `${2 + Math.random() * 2}s`,
left: `${particle.left}%`,
top: `${particle.top}%`,
animationDelay: `${particle.delay}s`,
animationDuration: `${particle.duration}s`,
}}
></div>
))}
@@ -207,22 +229,134 @@ export default function HomePage() {
return (
<>
<video ref={videoRef} autoPlay playsInline muted onCanPlay={() => videoRef.current?.play()} className="h-full w-full object-cover" />
<div className="absolute inset-0 flex flex-col items-center justify-between p-6">
<div className="flex w-full items-start justify-between gap-2">
<div className="w-64">
{/* Settings Panel Trigger - Bouncing Tab */}
<div
className={`absolute left-0 top-1/2 transform -translate-y-1/2 z-50 transition-all duration-500 ease-out ${
isSettingsPanelOpen ? 'translate-x-80' : 'translate-x-0'
}`}
>
<button
onClick={() => setSettingsPanelOpen(!isSettingsPanelOpen)}
className="bg-white/20 backdrop-blur-sm border border-white/30 rounded-r-2xl pl-4 pr-3 py-6 text-white shadow-xl hover:bg-white/30 transition-all duration-300 group animate-bounce"
style={{
animation: isSettingsPanelOpen ? 'none' : 'bounce 2s infinite'
}}
>
<div className="flex items-center space-x-2">
<Settings
size={20}
className={`transition-transform duration-300 ${
isSettingsPanelOpen ? 'rotate-90' : 'group-hover:rotate-12'
}`}
/>
<ChevronRight
size={16}
className={`transition-transform duration-300 ${
isSettingsPanelOpen ? 'rotate-180' : ''
}`}
/>
</div>
<div className="absolute -right-1 top-3 w-2 h-8 bg-gradient-to-r from-transparent to-white/10 rounded-r-full"></div>
</button>
</div>
{/* Settings Panel */}
<div className={`absolute left-0 top-0 bottom-0 w-80 bg-black/80 backdrop-blur-xl border-r border-white/20 transform transition-transform duration-500 ease-out z-40 ${
isSettingsPanelOpen ? 'translate-x-0' : '-translate-x-full'
}`}>
<div className="p-6 h-full flex flex-col">
{/* Panel Header */}
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-bold text-white flex items-center gap-2">
<Settings size={24} className="text-blue-400" />
Configuración
</h2>
<button
onClick={() => setSettingsPanelOpen(false)}
className="text-white/60 hover:text-white transition-colors p-1"
>
</button>
</div>
{/* Camera Selection */}
<div className="mb-6">
<div className="flex items-center gap-2 mb-3">
<Video size={20} className="text-blue-400" />
<label className="text-white font-medium">Cámara</label>
</div>
<Select value={selectedDeviceId} onValueChange={handleCameraChange}>
<SelectTrigger className="w-full bg-black/50 text-white border-white/30">
<SelectTrigger className="w-full bg-white/10 border-white/20 text-white hover:bg-white/20 transition-colors">
<SelectValue placeholder="Seleccionar cámara..." />
</SelectTrigger>
<SelectContent>
<SelectContent className="bg-black/90 backdrop-blur-xl border-white/20">
{videoDevices.map((device) => (
<SelectItem key={device.deviceId} value={device.deviceId}>
<SelectItem key={device.deviceId} value={device.deviceId} className="text-white hover:bg-white/20">
{device.label || `Cámara ${videoDevices.indexOf(device) + 1}`}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Additional Options */}
<div className="space-y-4 flex-1">
{/* Quality Settings */}
<div className="bg-white/5 rounded-lg p-4 border border-white/10">
<div className="flex items-center gap-2 mb-3">
<Palette size={20} className="text-green-400" />
<span className="text-white font-medium">Calidad de Video</span>
</div>
<div className="grid grid-cols-3 gap-2">
{['HD', 'FHD', '4K'].map((quality) => (
<button
key={quality}
className="bg-white/10 hover:bg-blue-500/30 text-white text-sm py-2 px-3 rounded-md transition-colors border border-white/20"
>
{quality}
</button>
))}
</div>
</div>
{/* Audio Settings */}
<div className="bg-white/5 rounded-lg p-4 border border-white/10">
<div className="flex items-center gap-2 mb-3">
<Volume2 size={20} className="text-purple-400" />
<span className="text-white font-medium">Audio</span>
</div>
<div className="flex items-center gap-3">
<button className="bg-white/10 hover:bg-purple-500/30 text-white text-sm py-2 px-4 rounded-md transition-colors border border-white/20">
Silenciado
</button>
<span className="text-white/60 text-sm">Recomendado para mejor rendimiento</span>
</div>
</div>
{/* App Info */}
<div className="bg-gradient-to-r from-blue-500/10 to-purple-500/10 rounded-lg p-4 border border-blue-500/20">
<h3 className="text-white font-medium mb-2">Smart Store Assistant</h3>
<p className="text-white/70 text-sm">
Detector inteligente de Calzado con IA avanzada
</p>
<div className="mt-3 text-xs text-blue-300">
v0.0.3 Powered by Moonshot
</div>
</div>
</div>
{/* Footer */}
<div className="pt-4 border-t border-white/10">
<button className="w-full bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 text-white font-medium py-2 px-4 rounded-lg transition-all duration-200 transform hover:scale-105">
Guardar Configuración
</button>
</div>
</div>
</div>
<div className="absolute inset-0 flex flex-col items-center justify-between p-6">
<div className="flex w-full items-start justify-end gap-2">
<Button variant="ghost" size="icon" className="text-white hover:bg-white/20 hover:text-white" onClick={() => setHistoryOpen(true)}>
<History size={24} />
</Button>