- Added props for handling presentation and screen sharing actions. - Implemented buttons for opening presentation panel and changing layouts (grid/focus) and modes (video/audio). - Updated UI to reflect active presentations and added functionality to clear presentations. fix: Adjust StudioLeftSidebar and StudioRightPanel for full height - Modified styles to ensure both sidebars occupy full height of the container. feat: Introduce ParticipantsPanel for managing participants in the conference - Created ParticipantsPanel component to display connected and invited participants. - Integrated API calls to fetch invited participants and handle connection status. feat: Implement PresentationPanel for sharing presentations - Developed PresentationPanel component to handle file uploads and screen sharing. refactor: Update StudioVideoArea to support layout and mode changes - Refactored StudioVideoArea to accept layout and mode props, rendering appropriate conference views. feat: Add AudioConference and VideoConference prefabs for audio and video handling - Created AudioConference and VideoConference components to manage respective media streams. feat: Introduce Chat component for real-time messaging - Developed Chat component to facilitate messaging between participants. feat: Implement ControlBar for user controls in the conference - Created ControlBar component for managing participant actions like leaving the conference and toggling audio/video. feat: Add PreJoin component for pre-conference setup - Developed PreJoin component to allow users to preview video before joining the conference. chore: Update Vite configuration for better module resolution - Enhanced Vite config to include path aliases for easier imports across the project. chore: Add TypeScript definitions for environment variables - Created env.d.ts to define types for environment variables used in the project.
180 lines
7.0 KiB
TypeScript
180 lines
7.0 KiB
TypeScript
import { useState } from 'react'
|
|
import { MdAdd, MdMoreVert, MdEdit, MdDelete, MdContentCopy } from 'react-icons/md'
|
|
import { DEMO_SCENES } from '../config/demo'
|
|
|
|
interface Scene {
|
|
id: string
|
|
name: string
|
|
thumbnail: string
|
|
active: boolean
|
|
}
|
|
|
|
const StudioLeftSidebar = () => {
|
|
const [scenes, setScenes] = useState<Scene[]>(DEMO_SCENES)
|
|
|
|
const [openMenuId, setOpenMenuId] = useState<string | null>(null)
|
|
|
|
const handleSceneClick = (sceneId: string) => {
|
|
setScenes(scenes.map(scene => ({
|
|
...scene,
|
|
active: scene.id === sceneId
|
|
})))
|
|
}
|
|
|
|
const handleAddScene = () => {
|
|
const newScene: Scene = {
|
|
id: Date.now().toString(),
|
|
name: `Nueva Escena ${scenes.length + 1}`,
|
|
thumbnail: '/placeholder-scene.jpg',
|
|
active: false
|
|
}
|
|
setScenes([...scenes, newScene])
|
|
}
|
|
|
|
const handleDeleteScene = (sceneId: string) => {
|
|
if (scenes.length > 1) {
|
|
setScenes(scenes.filter(scene => scene.id !== sceneId))
|
|
}
|
|
setOpenMenuId(null)
|
|
}
|
|
|
|
const handleDuplicateScene = (sceneId: string) => {
|
|
const sceneToDuplicate = scenes.find(scene => scene.id === sceneId)
|
|
if (sceneToDuplicate) {
|
|
const newScene: Scene = {
|
|
...sceneToDuplicate,
|
|
id: Date.now().toString(),
|
|
name: `${sceneToDuplicate.name} (copia)`,
|
|
active: false
|
|
}
|
|
setScenes([...scenes, newScene])
|
|
}
|
|
setOpenMenuId(null)
|
|
}
|
|
|
|
return (
|
|
<div className="w-64 h-full bg-gray-800 border-r border-gray-700 flex flex-col">
|
|
{/* Header de Escenas */}
|
|
<div className="p-4 border-b border-gray-700">
|
|
<h3 className="text-white font-semibold text-sm flex items-center gap-2">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z" />
|
|
</svg>
|
|
Escenas
|
|
</h3>
|
|
</div>
|
|
|
|
{/* Lista de Escenas */}
|
|
<div className="flex-1 overflow-y-auto p-2">
|
|
{scenes.map((scene) => (
|
|
<div
|
|
key={scene.id}
|
|
className={`group relative flex items-center gap-3 p-2 rounded-lg mb-2 cursor-pointer transition-all ${
|
|
scene.active
|
|
? 'bg-pink-500/20 border border-pink-500/50'
|
|
: 'bg-gray-700/50 hover:bg-gray-700 border border-transparent'
|
|
}`}
|
|
onClick={() => handleSceneClick(scene.id)}
|
|
>
|
|
{/* Thumbnail */}
|
|
<div className={`w-16 h-10 rounded overflow-hidden flex-shrink-0 ${
|
|
scene.active ? 'ring-2 ring-pink-500' : ''
|
|
}`}>
|
|
<div className="w-full h-full bg-gray-600 flex items-center justify-center">
|
|
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Nombre */}
|
|
<span className={`flex-1 text-sm truncate ${
|
|
scene.active ? 'text-white font-medium' : 'text-gray-300'
|
|
}`}>
|
|
{scene.name}
|
|
</span>
|
|
|
|
{/* Menú de opciones */}
|
|
<div className="relative">
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
setOpenMenuId(openMenuId === scene.id ? null : scene.id)
|
|
}}
|
|
className="p-1 rounded hover:bg-gray-600 text-gray-400 hover:text-white opacity-0 group-hover:opacity-100 transition-opacity"
|
|
>
|
|
<MdMoreVert size={18} />
|
|
</button>
|
|
|
|
{/* Dropdown menu */}
|
|
{openMenuId === scene.id && (
|
|
<div className="absolute right-0 top-full mt-1 w-40 bg-gray-700 rounded-lg shadow-lg border border-gray-600 py-1 z-50">
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
handleDuplicateScene(scene.id)
|
|
}}
|
|
className="w-full px-3 py-2 text-left text-sm text-gray-300 hover:bg-gray-600 flex items-center gap-2"
|
|
>
|
|
<MdContentCopy size={16} />
|
|
Duplicar
|
|
</button>
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
// TODO: Implementar edición
|
|
setOpenMenuId(null)
|
|
}}
|
|
className="w-full px-3 py-2 text-left text-sm text-gray-300 hover:bg-gray-600 flex items-center gap-2"
|
|
>
|
|
<MdEdit size={16} />
|
|
Renombrar
|
|
</button>
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
handleDeleteScene(scene.id)
|
|
}}
|
|
className="w-full px-3 py-2 text-left text-sm text-red-400 hover:bg-gray-600 flex items-center gap-2"
|
|
disabled={scenes.length === 1}
|
|
>
|
|
<MdDelete size={16} />
|
|
Eliminar
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
{/* Botón agregar escena */}
|
|
<button
|
|
onClick={handleAddScene}
|
|
className="w-full flex items-center justify-center gap-2 p-3 rounded-lg border-2 border-dashed border-gray-600 text-gray-400 hover:border-pink-500 hover:text-pink-500 transition-all mt-2"
|
|
>
|
|
<MdAdd size={20} />
|
|
<span className="text-sm font-medium">Nueva Escena</span>
|
|
</button>
|
|
</div>
|
|
|
|
{/* Acciones rápidas */}
|
|
<div className="p-3 border-t border-gray-700 space-y-2">
|
|
<button className="w-full flex items-center gap-3 px-3 py-2 text-sm text-gray-300 hover:text-white hover:bg-gray-700 rounded-lg transition-colors">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
Agregar Elemento
|
|
</button>
|
|
<button className="w-full flex items-center gap-3 px-3 py-2 text-sm text-gray-300 hover:text-white hover:bg-gray-700 rounded-lg transition-colors">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
</svg>
|
|
Añadir Multimedia
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default StudioLeftSidebar
|