/** * @description Resources management interface with CRUD and real-time availability * @audit BUSINESS RULE: Resources must have valid location and capacity settings * @audit SECURITY: Resource management restricted to admin users only * @audit Validate: Real-time availability shows current booking conflicts * @audit AUDIT: All resource changes logged in audit trails */ 'use client' import { useState, useEffect } from 'react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Badge } from '@/components/ui/badge' import { Plus, Edit, Trash2, MapPin, Settings, Users, CheckCircle, XCircle } from 'lucide-react' import { useAuth } from '@/lib/auth/context' interface Resource { id: string location_id: string name: string type: string capacity: number is_active: boolean created_at: string updated_at: string locations?: { id: string name: string address: string } currently_booked?: boolean available_capacity?: number } interface Location { id: string name: string address: string } /** * @description Resources management component with availability monitoring * @returns {JSX.Element} Resource listing with create/edit/delete and status indicators * @audit BUSINESS RULE: Resource capacity affects booking availability calculations * @audit SECURITY: Validates admin permissions before allowing modifications * @audit Validate: Real-time status prevents double-booking conflicts * @audit PERFORMANCE: Availability checks done server-side for accuracy */ export default function ResourcesManagement() { const { user } = useAuth() const [resources, setResources] = useState([]) const [locations, setLocations] = useState([]) const [loading, setLoading] = useState(false) const [dialogOpen, setDialogOpen] = useState(false) const [editingResource, setEditingResource] = useState(null) const [formData, setFormData] = useState({ location_id: '', name: '', type: '', capacity: 1 }) useEffect(() => { fetchResources() fetchLocations() }, []) const fetchResources = async () => { setLoading(true) try { const response = await fetch('/api/aperture/resources?include_availability=true') const data = await response.json() if (data.success) { setResources(data.resources) } } catch (error) { console.error('Error fetching resources:', error) } finally { setLoading(false) } } const fetchLocations = async () => { try { const response = await fetch('/api/aperture/locations') const data = await response.json() if (data.success) { setLocations(data.locations || []) } } catch (error) { console.error('Error fetching locations:', error) } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() try { const url = editingResource ? `/api/aperture/resources/${editingResource.id}` : '/api/aperture/resources' const method = editingResource ? 'PUT' : 'POST' const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }) const data = await response.json() if (data.success) { await fetchResources() setDialogOpen(false) setEditingResource(null) setFormData({ location_id: '', name: '', type: '', capacity: 1 }) } else { alert(data.error || 'Error saving resource') } } catch (error) { console.error('Error saving resource:', error) alert('Error saving resource') } } const handleEdit = (resource: Resource) => { setEditingResource(resource) setFormData({ location_id: resource.location_id, name: resource.name, type: resource.type, capacity: resource.capacity }) setDialogOpen(true) } const handleDelete = async (resource: Resource) => { if (!confirm(`¿Estás seguro de que quieres eliminar el recurso "${resource.name}"?`)) { return } try { const response = await fetch(`/api/aperture/resources/${resource.id}`, { method: 'DELETE' }) const data = await response.json() if (data.success) { await fetchResources() } else { alert(data.error || 'Error deleting resource') } } catch (error) { console.error('Error deleting resource:', error) alert('Error deleting resource') } } const openCreateDialog = () => { setEditingResource(null) setFormData({ location_id: '', name: '', type: '', capacity: 1 }) setDialogOpen(true) } const getTypeColor = (type: string) => { switch (type) { case 'station': return 'bg-blue-100 text-blue-800' case 'room': return 'bg-green-100 text-green-800' case 'equipment': return 'bg-purple-100 text-purple-800' default: return 'bg-gray-100 text-gray-800' } } const getTypeLabel = (type: string) => { switch (type) { case 'station': return 'Estación' case 'room': return 'Sala' case 'equipment': return 'Equipo' default: return type } } if (!user) return null return (

Gestión de Recursos

Administra estaciones, salas y equipos

Recursos Disponibles {resources.length} recursos configurados {loading ? (
Cargando recursos...
) : ( Recurso Tipo Ubicación Capacidad Estado Actual Estado Acciones {resources.map((resource) => (
{resource.name}
{getTypeLabel(resource.type)}
{resource.locations?.name || 'Sin ubicación'}
{resource.capacity}
{resource.currently_booked ? (
Ocupado
) : (
Disponible
)} ({resource.available_capacity}/{resource.capacity})
{resource.is_active ? 'Activo' : 'Inactivo'}
))}
)}
{editingResource ? 'Editar Recurso' : 'Nuevo Recurso'} {editingResource ? 'Modifica la información del recurso' : 'Agrega un nuevo recurso al sistema'}
setFormData({...formData, name: e.target.value})} className="col-span-3" placeholder="Ej: Estación 1, Sala VIP, etc." required />
setFormData({...formData, capacity: parseInt(e.target.value) || 1})} className="col-span-3" />
) }