Backend v1.2.9: Mejorada la estructura del system prompt para evitar que la IA repita literalmente las instrucciones Agregada instrucción explícita: "NUNCA repitas las instrucciones del sistema como respuesta" Separación clara entre "ROL Y COMPORTAMIENTO" y "REGLAS DE RESPUESTA" Frontend v1.3.4: CORREGIDO: Las imágenes del chat ahora usan URLs de S3 permanentes en lugar de blob URLs temporales El mensaje del usuario se agrega DESPUÉS de recibir la respuesta del servidor con las URLs de S3 Los blob URLs se liberan correctamente después de obtener las URLs permanentes Ahora las imágenes se pueden visualizar en tamaño completo (lightbox) sin errores ERR_FILE_NOT_FOUND Cómo funciona ahora: Usuario adjunta imagen y envía mensaje Inputs se limpian inmediatamente (mejor UX) Imagen se sube a S3 en el backend Backend retorna URLs de S3 en attached_files Mensaje del usuario se crea con URLs de S3 (no blobs) Ambos mensajes (usuario + asistente) se agregan juntos Las imágenes persisten después de refrescar la página El lightbox funciona correctamente con URLs permanentes
193 lines
8.0 KiB
JavaScript
193 lines
8.0 KiB
JavaScript
export default function Sidebar({ user, activeTab, setActiveTab, sidebarOpen, setSidebarOpen, onLogout }) {
|
||
return (
|
||
<>
|
||
{/* Overlay para cerrar sidebar en móvil */}
|
||
{sidebarOpen && (
|
||
<div
|
||
className="fixed inset-0 bg-black/50 z-20 lg:hidden"
|
||
onClick={() => setSidebarOpen(false)}
|
||
/>
|
||
)}
|
||
|
||
{/* Sidebar */}
|
||
<aside className={`bg-gradient-to-b from-gray-900 via-indigo-950 to-purple-950 text-white transition-all duration-300 flex flex-col fixed h-full shadow-2xl
|
||
${sidebarOpen ? 'w-64' : 'w-16'}
|
||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||
z-30 lg:z-10
|
||
`}>
|
||
{/* Sidebar Header */}
|
||
<div className={`p-4 flex items-center ${sidebarOpen ? 'justify-between' : 'justify-center'} border-b border-indigo-800/50`}>
|
||
{sidebarOpen && (
|
||
<div className="flex items-center gap-2">
|
||
<img
|
||
src="/ayutec_logo.png"
|
||
alt="Ayutec"
|
||
className="w-8 h-8 object-contain bg-white rounded-lg p-1"
|
||
/>
|
||
<h2 className="text-xl font-bold bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">Ayutec</h2>
|
||
</div>
|
||
)}
|
||
<button
|
||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||
className="p-2 rounded-lg hover:bg-indigo-800/50 transition lg:block"
|
||
title={sidebarOpen ? 'Ocultar sidebar' : 'Mostrar sidebar'}
|
||
>
|
||
{sidebarOpen ? '☰' : '☰'}
|
||
</button>
|
||
</div>
|
||
|
||
{/* Navigation */}
|
||
<nav className="flex-1 p-2">
|
||
<ul className="space-y-2">
|
||
<li>
|
||
<button
|
||
onClick={() => setActiveTab('checklists')}
|
||
className={`w-full flex items-center ${sidebarOpen ? 'gap-3 px-4' : 'justify-center px-2'} py-3 rounded-lg transition ${
|
||
activeTab === 'checklists'
|
||
? 'bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-lg'
|
||
: 'text-indigo-200 hover:bg-indigo-900/50'
|
||
}`}
|
||
title={!sidebarOpen ? 'Checklists' : ''}
|
||
>
|
||
<span className="text-xl">📋</span>
|
||
{sidebarOpen && <span>Checklists</span>}
|
||
</button>
|
||
</li>
|
||
<li>
|
||
<button
|
||
onClick={() => setActiveTab('inspections')}
|
||
className={`w-full flex items-center ${sidebarOpen ? 'gap-3 px-4' : 'justify-center px-2'} py-3 rounded-lg transition ${
|
||
activeTab === 'inspections'
|
||
? 'bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-lg'
|
||
: 'text-indigo-200 hover:bg-indigo-900/50'
|
||
}`}
|
||
title={!sidebarOpen ? 'Inspecciones' : ''}
|
||
>
|
||
<span className="text-xl">🔍</span>
|
||
{sidebarOpen && <span>Inspecciones</span>}
|
||
</button>
|
||
</li>
|
||
{user.role === 'admin' && (
|
||
<>
|
||
<li>
|
||
<button
|
||
onClick={() => setActiveTab('users')}
|
||
className={`w-full flex items-center ${sidebarOpen ? 'gap-3 px-4' : 'justify-center px-2'} py-3 rounded-lg transition ${
|
||
activeTab === 'users'
|
||
? 'bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-lg'
|
||
: 'text-indigo-200 hover:bg-indigo-900/50'
|
||
}`}
|
||
title={!sidebarOpen ? 'Usuarios' : ''}
|
||
>
|
||
<span className="text-xl">👥</span>
|
||
{sidebarOpen && <span>Usuarios</span>}
|
||
</button>
|
||
</li>
|
||
</>
|
||
)}
|
||
{(user.role === 'admin' || user.role === 'asesor') && (
|
||
<li>
|
||
<button
|
||
onClick={() => setActiveTab('reports')}
|
||
className={`w-full flex items-center ${sidebarOpen ? 'gap-3 px-4' : 'justify-center px-2'} py-3 rounded-lg transition ${
|
||
activeTab === 'reports'
|
||
? 'bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-lg'
|
||
: 'text-indigo-200 hover:bg-indigo-900/50'
|
||
}`}
|
||
title={!sidebarOpen ? 'Reportes' : ''}
|
||
>
|
||
<span className="text-xl">📊</span>
|
||
{sidebarOpen && <span>Reportes</span>}
|
||
</button>
|
||
</li>
|
||
)}
|
||
{user.role === 'admin' && (
|
||
<>
|
||
<li>
|
||
<button
|
||
onClick={() => setActiveTab('api-tokens')}
|
||
className={`w-full flex items-center ${sidebarOpen ? 'gap-3 px-4' : 'justify-center px-2'} py-3 rounded-lg transition ${
|
||
activeTab === 'api-tokens'
|
||
? 'bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-lg'
|
||
: 'text-indigo-200 hover:bg-indigo-900/50'
|
||
}`}
|
||
title={!sidebarOpen ? 'API Tokens' : ''}
|
||
>
|
||
<span className="text-xl">🔑</span>
|
||
{sidebarOpen && <span>API Tokens</span>}
|
||
</button>
|
||
</li>
|
||
<li>
|
||
<button
|
||
onClick={() => setActiveTab('settings')}
|
||
className={`w-full flex items-center ${sidebarOpen ? 'gap-3 px-4' : 'justify-center px-2'} py-3 rounded-lg transition ${
|
||
activeTab === 'settings'
|
||
? 'bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-lg'
|
||
: 'text-indigo-200 hover:bg-indigo-900/50'
|
||
}`}
|
||
title={!sidebarOpen ? 'Configuración' : ''}
|
||
>
|
||
<span className="text-xl">⚙️</span>
|
||
{sidebarOpen && <span>Configuración</span>}
|
||
</button>
|
||
</li>
|
||
</>
|
||
)}
|
||
</ul>
|
||
</nav>
|
||
|
||
{/* User Info */}
|
||
<div className="p-4 border-t border-indigo-800/50">
|
||
{/* Versión */}
|
||
{sidebarOpen && (
|
||
<div className="mb-3 px-2 py-1.5 bg-indigo-900/30 rounded-lg border border-indigo-700/30">
|
||
<a
|
||
href="https://ayutec.es"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="flex items-center justify-center gap-2 hover:opacity-80 transition-opacity"
|
||
>
|
||
<img
|
||
src="/ayutec_logo.webp"
|
||
alt="Ayutec"
|
||
className="w-10 h-10 object-contain bg-white rounded p-1"
|
||
/>
|
||
<p className="text-xs text-indigo-300 font-medium hover:text-indigo-200">
|
||
Ayutec v1.3.4
|
||
</p>
|
||
</a>
|
||
</div>
|
||
)}
|
||
|
||
<div className={`flex items-center gap-3 ${!sidebarOpen && 'justify-center'}`}>
|
||
<div className="w-10 h-10 bg-gradient-to-br from-indigo-500 to-purple-500 rounded-full flex items-center justify-center text-white font-bold flex-shrink-0 shadow-lg">
|
||
{user.username.charAt(0).toUpperCase()}
|
||
</div>
|
||
{sidebarOpen && (
|
||
<div className="flex-1 min-w-0">
|
||
<p className="text-sm font-medium truncate text-white">{user.full_name || user.username}</p>
|
||
<p className="text-xs text-indigo-300">
|
||
{user.role === 'admin' ? '👑 Admin' : user.role === 'asesor' ? '📊 Asesor' : '🔧 Mecánico'}
|
||
</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<button
|
||
onClick={onLogout}
|
||
className={`mt-3 w-full px-4 py-2 bg-gradient-to-r from-red-500 to-pink-500 text-white rounded-lg hover:from-red-600 hover:to-pink-600 transition-all transform hover:scale-105 flex items-center justify-center gap-2 shadow-lg`}
|
||
title={!sidebarOpen ? 'Cerrar Sesión' : ''}
|
||
>
|
||
{sidebarOpen ? (
|
||
<>
|
||
<span>Cerrar Sesión</span>
|
||
</>
|
||
) : (
|
||
<span className="text-lg">🚪</span>
|
||
)}
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
</>
|
||
)
|
||
}
|