From 1b31007eef9717b76fa4039bd33201bb21d81f04 Mon Sep 17 00:00:00 2001 From: ronalds Date: Tue, 25 Nov 2025 09:45:31 -0300 Subject: [PATCH] backend y front estables ses creo modal para permisos de checklist backend 1.0.31 y frontend 1.0.33 --- PERMISOS_CHECKLIST.md | 283 +++++++++++++++++++++++++++++++++++++++++ backend/app/schemas.py | 7 +- frontend/src/App.jsx | 177 ++++++++++++++++++++++++-- 3 files changed, 458 insertions(+), 9 deletions(-) create mode 100644 PERMISOS_CHECKLIST.md diff --git a/PERMISOS_CHECKLIST.md b/PERMISOS_CHECKLIST.md new file mode 100644 index 0000000..15e2b6a --- /dev/null +++ b/PERMISOS_CHECKLIST.md @@ -0,0 +1,283 @@ +# Sistema de Permisos por Mecánico - Checklists + +## ✅ Implementación Completa + +Se ha implementado un sistema completo de permisos que permite controlar qué mecánicos pueden usar cada checklist. + +--- + +## 🎯 Características Implementadas + +### **Backend** + +1. **Nueva Tabla de Permisos** + - Tabla `checklist_permissions` con relación many-to-many + - Constraint UNIQUE para evitar duplicados + - Índices optimizados para consultas rápidas + +2. **Lógica de Acceso** + - **Acceso Global**: Si un checklist NO tiene permisos registrados, todos los mecánicos pueden usarlo + - **Acceso Restringido**: Si tiene permisos, solo esos mecánicos específicos pueden verlo + - **Admins**: Siempre ven todos los checklists + +3. **Endpoints Actualizados** + - `GET /api/checklists`: Filtra automáticamente por permisos del mecánico + - `POST /api/checklists`: Guarda permisos al crear + - `PUT /api/checklists/{id}`: Actualiza permisos al editar + - Incluye `allowed_mechanics` en las respuestas + +### **Frontend** + +1. **Creación de Checklists** + - Selector de mecánicos con checkboxes + - Opción "Acceso Global" (no seleccionar ninguno) + - Interfaz clara con íconos 🔐 y 🌍 + +2. **Visualización** + - Badge verde "🌍 Acceso Global" para checklists sin restricciones + - Badge índigo "🔐 Restringido - X mecánicos" para checklists restringidos + - Solo visible para administradores + +3. **Edición de Permisos** + - Botón "🔐 Permisos" en cada checklist (solo admins) + - Modal dedicado para editar permisos + - Cambios se aplican inmediatamente + +4. **Mensajes Mejorados** + - Mensaje específico para mecánicos sin checklists disponibles + - Instrucciones claras para contactar al administrador + +--- + +## 📋 Instrucciones de Uso + +### **Para Administradores** + +#### 1. Crear Checklist con Permisos + +1. Ve a la pestaña "Checklists" +2. Haz clic en "+ Crear Checklist" +3. Completa los datos del checklist +4. En la sección "🔐 Mecánicos Autorizados": + - **Para acceso global**: No selecciones ningún mecánico (deja todo sin marcar) + - **Para acceso restringido**: Marca los mecánicos que tendrán acceso +5. Haz clic en "Crear Checklist" + +#### 2. Editar Permisos de Checklist Existente + +1. Ve a la pestaña "Checklists" +2. Busca el checklist que quieres modificar +3. Haz clic en el botón "🔐 Permisos" +4. Modifica la selección de mecánicos: + - Marca "🌍 Todos los mecánicos" para acceso global + - O selecciona mecánicos específicos +5. Haz clic en "Guardar Permisos" + +**Nota**: Los cambios son inmediatos. Los mecánicos que pierdan acceso dejarán de ver el checklist instantáneamente. + +#### 3. Ver Estado de Permisos + +Cada tarjeta de checklist muestra: +- **🌍 Acceso Global - Todos los mecánicos**: Sin restricciones +- **🔐 Restringido - X mecánicos**: Solo esos mecánicos tienen acceso + +### **Para Mecánicos** + +- Solo verás los checklists donde tienes permiso +- Si no ves ningún checklist, contacta al administrador +- No puedes modificar permisos (solo el admin puede hacerlo) + +--- + +## 🗄️ Migración de Base de Datos + +### **Ejecutar SQL** + +```bash +# Usando psql +psql -U tu_usuario -d tu_database -f migrations/add_checklist_permissions.sql + +# O directamente +psql -U tu_usuario -d tu_database +``` + +Luego ejecuta: + +```sql +-- Crear tabla de permisos checklist-mecánico +CREATE TABLE IF NOT EXISTS checklist_permissions ( + id SERIAL PRIMARY KEY, + checklist_id INTEGER NOT NULL REFERENCES checklists(id) ON DELETE CASCADE, + mechanic_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + UNIQUE(checklist_id, mechanic_id) +); + +-- Crear índices +CREATE INDEX idx_checklist_permissions_checklist ON checklist_permissions(checklist_id); +CREATE INDEX idx_checklist_permissions_mechanic ON checklist_permissions(mechanic_id); + +-- Comentarios +COMMENT ON TABLE checklist_permissions IS 'Control de acceso de mecánicos a checklists. Si no hay registros para un checklist, todos los mecánicos tienen acceso.'; +``` + +### **Reiniciar Backend** + +Después de ejecutar el SQL, reinicia el backend para que los cambios en los modelos tomen efecto: + +```bash +# Si usas Docker +docker-compose restart backend + +# Si corres directamente +# Ctrl+C y volver a ejecutar +python -m uvicorn app.main:app --reload +``` + +--- + +## 🔍 Ejemplos de Uso + +### Ejemplo 1: Checklist para Todos los Mecánicos + +**Escenario**: Checklist de "Revisión Básica" que todos pueden usar + +**Configuración**: +- Al crear/editar: No seleccionar ningún mecánico +- El sistema muestra: "🌍 Acceso Global - Todos los mecánicos" + +**Resultado**: Todos los mecánicos ven este checklist + +--- + +### Ejemplo 2: Checklist Especializado + +**Escenario**: Checklist de "Mantenimiento Eléctrico" solo para mecánicos certificados + +**Configuración**: +1. Al crear/editar: Seleccionar solo mecánicos con certificación eléctrica +2. El sistema muestra: "🔐 Restringido - 3 mecánicos" + +**Resultado**: Solo esos 3 mecánicos ven este checklist + +--- + +### Ejemplo 3: Cambio de Permisos + +**Escenario**: Un mecánico nuevo se certifica en electricidad + +**Pasos**: +1. Admin hace clic en "🔐 Permisos" del checklist "Mantenimiento Eléctrico" +2. Marca al nuevo mecánico en la lista +3. Guarda cambios +4. El mecánico inmediatamente ve el checklist en su lista + +--- + +## 📊 Base de Datos + +### Estructura de `checklist_permissions` + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | SERIAL | ID único de la relación | +| checklist_id | INTEGER | ID del checklist | +| mechanic_id | INTEGER | ID del mecánico autorizado | +| created_at | TIMESTAMP | Fecha de creación del permiso | + +### Consultas Útiles + +```sql +-- Ver permisos de un checklist específico +SELECT u.full_name, u.email +FROM checklist_permissions cp +JOIN users u ON cp.mechanic_id = u.id +WHERE cp.checklist_id = 1; + +-- Ver qué checklists puede usar un mecánico +SELECT c.name, c.description +FROM checklist_permissions cp +JOIN checklists c ON cp.checklist_id = c.id +WHERE cp.mechanic_id = 5; + +-- Checklists sin restricciones (acceso global) +SELECT c.id, c.name +FROM checklists c +LEFT JOIN checklist_permissions cp ON c.id = cp.checklist_id +WHERE cp.id IS NULL; + +-- Dar acceso a un mecánico a un checklist +INSERT INTO checklist_permissions (checklist_id, mechanic_id) +VALUES (1, 5) +ON CONFLICT (checklist_id, mechanic_id) DO NOTHING; + +-- Quitar acceso +DELETE FROM checklist_permissions +WHERE checklist_id = 1 AND mechanic_id = 5; + +-- Convertir checklist a acceso global (borrar todos los permisos) +DELETE FROM checklist_permissions WHERE checklist_id = 1; +``` + +--- + +## ⚠️ Notas Importantes + +1. **Permisos Vacíos = Acceso Global** + - Si un checklist NO tiene registros en `checklist_permissions`, TODOS los mecánicos pueden usarlo + - Es el comportamiento por defecto + +2. **Los Admins Siempre Ven Todo** + - Los usuarios con rol `admin` ven todos los checklists sin importar los permisos + - Útil para gestión y supervisión + +3. **Cambios Inmediatos** + - Al editar permisos, los cambios se aplican instantáneamente + - No requiere logout/login + +4. **Cascada en Borrado** + - Si borras un checklist, sus permisos se borran automáticamente + - Si borras un mecánico, sus permisos se borran automáticamente + +5. **Inspecciones Existentes** + - Las inspecciones ya creadas NO se ven afectadas por cambios de permisos + - Solo afecta la creación de nuevas inspecciones + +--- + +## 🐛 Troubleshooting + +### Problema: "Mecánico no ve ningún checklist" + +**Solución**: +1. Verificar que el mecánico esté activo: `SELECT is_active FROM users WHERE id = X;` +2. Verificar permisos: `SELECT * FROM checklist_permissions WHERE mechanic_id = X;` +3. Verificar si hay checklists con acceso global (sin permisos) +4. Verificar rol del usuario: debe ser `mechanic` o `mecanico` + +### Problema: "Error al crear/editar permisos" + +**Solución**: +1. Verificar que la tabla `checklist_permissions` existe +2. Verificar que los IDs de mecánicos son válidos +3. Revisar logs del backend para errores específicos +4. Verificar que el usuario es admin + +### Problema: "Los permisos no se aplican" + +**Solución**: +1. Hacer logout y login nuevamente +2. Verificar que el backend se reinició después de la migración +3. Limpiar caché del navegador (Ctrl+Shift+R) +4. Verificar en la base de datos que los permisos se guardaron correctamente + +--- + +## 🎉 Resumen + +✅ **Backend**: Filtrado automático por permisos +✅ **Frontend**: Interfaz completa para gestionar permisos +✅ **Base de Datos**: Migración lista para ejecutar +✅ **Documentación**: Completa con ejemplos + +El sistema está listo para usar después de ejecutar la migración SQL y reiniciar el backend. diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 6ebc0b5..285848b 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -72,7 +72,12 @@ class ChecklistBase(BaseModel): class ChecklistCreate(ChecklistBase): mechanic_ids: Optional[List[int]] = [] # IDs de mecánicos autorizados -class ChecklistUpdate(ChecklistBase): +class ChecklistUpdate(BaseModel): + name: Optional[str] = None + description: Optional[str] = None + ai_mode: Optional[str] = None + scoring_enabled: Optional[bool] = None + logo_url: Optional[str] = None is_active: Optional[bool] = None mechanic_ids: Optional[List[int]] = None # IDs de mecánicos autorizados diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 46b94cf..bd32a62 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1344,8 +1344,10 @@ function QuestionsManagerModal({ checklist, onClose }) { function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection }) { const [showCreateModal, setShowCreateModal] = useState(false) const [showQuestionsModal, setShowQuestionsModal] = useState(false) + const [showEditPermissionsModal, setShowEditPermissionsModal] = useState(false) const [selectedChecklist, setSelectedChecklist] = useState(null) const [creating, setCreating] = useState(false) + const [updating, setUpdating] = useState(false) const [mechanics, setMechanics] = useState([]) const [formData, setFormData] = useState({ name: '', @@ -1354,6 +1356,9 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection scoring_enabled: true, mechanic_ids: [] }) + const [editPermissionsData, setEditPermissionsData] = useState({ + mechanic_ids: [] + }) useEffect(() => { loadMechanics() @@ -1417,6 +1422,39 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection } } + const handleEditPermissions = async (e) => { + e.preventDefault() + setUpdating(true) + + try { + const token = localStorage.getItem('token') + const API_URL = import.meta.env.VITE_API_URL || '' + + const response = await fetch(`${API_URL}/api/checklists/${selectedChecklist.id}`, { + method: 'PUT', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(editPermissionsData), + }) + + if (response.ok) { + setShowEditPermissionsModal(false) + setSelectedChecklist(null) + setEditPermissionsData({ mechanic_ids: [] }) + onChecklistCreated() // Reload checklists + } else { + alert('Error al actualizar permisos') + } + } catch (error) { + console.error('Error:', error) + alert('Error al actualizar permisos') + } finally { + setUpdating(false) + } + } + return (
{user.role === 'admin' && ( @@ -1432,14 +1470,24 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection {checklists.length === 0 ? (
-

No hay checklists activos

- {user.role === 'admin' && ( - + {user.role === 'admin' ? ( + <> +

No hay checklists activos

+ + + ) : ( + <> +
🔒
+

No tienes checklists disponibles

+

+ Contacta con el administrador para que te asigne permisos a los checklists que necesites usar. +

+ )}
) : ( @@ -1477,6 +1525,19 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection
{user.role === 'admin' && ( <> +
)} + + {/* Modal Editar Permisos */} + {showEditPermissionsModal && selectedChecklist && ( +
+
+
+

Editar Permisos de Checklist

+

+ {selectedChecklist.name} +

+ +
+
+ +
+ {mechanics.length === 0 ? ( +

No hay mecánicos disponibles

+ ) : ( +
+
+ { + if (e.target.checked) { + setEditPermissionsData({ mechanic_ids: [] }) + } + }} + className="w-4 h-4 text-green-600 border-gray-300 rounded focus:ring-green-500" + /> + +
+ {mechanics.map((mechanic) => ( +
+ { + if (e.target.checked) { + setEditPermissionsData({ + mechanic_ids: [...editPermissionsData.mechanic_ids, mechanic.id] + }) + } else { + setEditPermissionsData({ + mechanic_ids: editPermissionsData.mechanic_ids.filter(id => id !== mechanic.id) + }) + } + }} + className="w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500" + /> + +
+ ))} +
+ )} +
+

+ 💡 Si no seleccionas ningún mecánico, todos podrán usar este checklist. + Si seleccionas mecánicos específicos, solo ellos tendrán acceso. +

+
+ +
+

+ ℹ️ Los cambios se aplicarán inmediatamente. Los mecánicos que pierdan acceso ya no verán este checklist. +

+
+ +
+ + +
+
+
+
+
+ )} ) }