/** * @description Complete staff management interface with CRUD operations * @audit BUSINESS RULE: Staff management requires admin/manager role permissions * @audit SECURITY: All operations validate user permissions before API calls * @audit Validate: Staff creation validates location and role constraints * @audit AUDIT: All staff modifications logged through API 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 { Avatar } from '@/components/ui/avatar' import { Plus, Edit, Trash2, Phone, MapPin, Clock, Users } from 'lucide-react' import { useAuth } from '@/lib/auth/context' interface StaffMember { id: string user_id?: string location_id: string role: string display_name: string phone?: string is_active: boolean created_at: string updated_at: string locations?: { id: string name: string address: string } schedule?: any[] } interface Location { id: string name: string address: string } /** * @description Staff management component with full CRUD interface * @returns {JSX.Element} Staff listing with create/edit/delete modals * @audit BUSINESS RULE: Staff roles determine system access permissions * @audit SECURITY: Component validates admin/manager role on mount * @audit Validate: Form validations prevent invalid staff data creation * @audit PERFORMANCE: Lazy loads staff data with location relationships */ export default function StaffManagement() { const { user } = useAuth() const [staff, setStaff] = useState([]) const [locations, setLocations] = useState([]) const [loading, setLoading] = useState(false) const [dialogOpen, setDialogOpen] = useState(false) const [editingStaff, setEditingStaff] = useState(null) const [formData, setFormData] = useState({ location_id: '', role: '', display_name: '', phone: '' }) useEffect(() => { fetchStaff() fetchLocations() }, []) const fetchStaff = async () => { setLoading(true) try { const response = await fetch('/api/aperture/staff?include_schedule=true') const data = await response.json() if (data.success) { setStaff(data.staff) } } catch (error) { console.error('Error fetching staff:', 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 = editingStaff ? `/api/aperture/staff/${editingStaff.id}` : '/api/aperture/staff' const method = editingStaff ? '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 fetchStaff() setDialogOpen(false) setEditingStaff(null) setFormData({ location_id: '', role: '', display_name: '', phone: '' }) } else { alert(data.error || 'Error saving staff member') } } catch (error) { console.error('Error saving staff:', error) alert('Error saving staff member') } } const handleEdit = (member: StaffMember) => { setEditingStaff(member) setFormData({ location_id: member.location_id, role: member.role, display_name: member.display_name, phone: member.phone || '' }) setDialogOpen(true) } const handleDelete = async (member: StaffMember) => { if (!confirm(`¿Estás seguro de que quieres desactivar a ${member.display_name}?`)) { return } try { const response = await fetch(`/api/aperture/staff/${member.id}`, { method: 'DELETE' }) const data = await response.json() if (data.success) { await fetchStaff() } else { alert(data.error || 'Error deleting staff member') } } catch (error) { console.error('Error deleting staff:', error) alert('Error deleting staff member') } } const openCreateDialog = () => { setEditingStaff(null) setFormData({ location_id: '', role: '', display_name: '', phone: '' }) setDialogOpen(true) } const getRoleColor = (role: string) => { switch (role) { case 'admin': return 'bg-red-100 text-red-800' case 'manager': return 'bg-purple-100 text-purple-800' case 'staff': return 'bg-blue-100 text-blue-800' case 'artist': return 'bg-green-100 text-green-800' case 'kiosk': return 'bg-gray-100 text-gray-800' default: return 'bg-gray-100 text-gray-800' } } if (!user) return null return (

Gestión de Staff

Administra el equipo de trabajo

Miembros del Equipo {staff.length} miembros activos {loading ? (
Cargando staff...
) : ( Miembro Rol Ubicación Contacto Estado Acciones {staff.map((member) => (
n[0]).join('').toUpperCase().slice(0, 2)} />
{member.display_name}
{member.schedule && member.schedule.length > 0 && (
{member.schedule.length} días disponibles
)}
{member.role}
{member.locations?.name || 'Sin ubicación'}
{member.phone && (
{member.phone}
)}
{member.is_active ? 'Activo' : 'Inactivo'}
))}
)}
{editingStaff ? 'Editar Miembro' : 'Nuevo Miembro de Staff'} {editingStaff ? 'Modifica la información del miembro' : 'Agrega un nuevo miembro al equipo'}
setFormData({...formData, display_name: e.target.value})} className="col-span-3" required />
setFormData({...formData, phone: e.target.value})} className="col-span-3" />
) }