Update AgentDashboard.tsx

This commit is contained in:
fcarraUniSa
2026-02-17 10:42:04 +01:00
committed by GitHub
parent e488657b43
commit 9303bac132

View File

@@ -575,11 +575,10 @@ export const AgentDashboard: React.FC<AgentDashboardProps> = ({
const handleSaveSettings = () => { const handleSaveSettings = () => {
setIsSaving(true); setIsSaving(true);
// Simulate API call // Simulate API call using prop
updateSettings(tempSettings);
setTimeout(() => { setTimeout(() => {
updateSettings(tempSettings);
setIsSaving(false); setIsSaving(false);
showToast("Impostazioni salvate con successo!", 'success');
}, 800); }, 800);
}; };
@@ -759,6 +758,81 @@ export const AgentDashboard: React.FC<AgentDashboardProps> = ({
</div> </div>
<div className="flex-1 p-8 overflow-y-auto"> <div className="flex-1 p-8 overflow-y-auto">
{/* SYSTEM SETTINGS TAB */}
{settingsTab === 'system' && canManageGlobalSettings && (
<div className="space-y-6 animate-fade-in">
<h2 className="text-xl font-bold text-gray-800 mb-6">Limiti e Quote di Sistema</h2>
<div className="grid grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Max Articoli KB</label>
<input type="number" className="w-full border border-gray-300 rounded-md px-3 py-2 bg-white text-gray-900"
value={tempSettings.features.maxKbArticles}
onChange={e => setTempSettings({...tempSettings, features: {...tempSettings.features, maxKbArticles: parseInt(e.target.value)}})} />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Max Agenti</label>
<input type="number" className="w-full border border-gray-300 rounded-md px-3 py-2 bg-white text-gray-900"
value={tempSettings.features.maxAgents}
onChange={e => setTempSettings({...tempSettings, features: {...tempSettings.features, maxAgents: parseInt(e.target.value)}})} />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Max Supervisor</label>
<input type="number" className="w-full border border-gray-300 rounded-md px-3 py-2 bg-white text-gray-900"
value={tempSettings.features.maxSupervisors}
onChange={e => setTempSettings({...tempSettings, features: {...tempSettings.features, maxSupervisors: parseInt(e.target.value)}})} />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Quota Giornaliera AI Articoli</label>
<input type="number" className="w-full border border-gray-300 rounded-md px-3 py-2 bg-white text-gray-900"
value={tempSettings.features.maxAiGeneratedArticles}
onChange={e => setTempSettings({...tempSettings, features: {...tempSettings.features, maxAiGeneratedArticles: parseInt(e.target.value)}})} />
</div>
</div>
<div className="mt-4 flex items-center">
<input type="checkbox" id="kbEnabled" className="mr-2"
checked={tempSettings.features.kbEnabled}
onChange={e => setTempSettings({...tempSettings, features: {...tempSettings.features, kbEnabled: e.target.checked}})} />
<label htmlFor="kbEnabled" className="text-sm text-gray-700">Abilita Knowledge Base Pubblica</label>
</div>
<div className="mt-2 flex items-center">
<input type="checkbox" id="aiAgent" className="mr-2"
checked={tempSettings.features.aiKnowledgeAgentEnabled}
onChange={e => setTempSettings({...tempSettings, features: {...tempSettings.features, aiKnowledgeAgentEnabled: e.target.checked}})} />
<label htmlFor="aiAgent" className="text-sm text-gray-700">Abilita Agente AI Knowledge Extraction</label>
</div>
</div>
)}
{/* GENERAL (BRANDING) TAB */}
{settingsTab === 'general' && canManageGlobalSettings && (
<div className="space-y-6 max-w-lg">
<h2 className="text-xl font-bold text-gray-800 mb-6">Personalizzazione Branding</h2>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Nome Applicazione</label>
<input type="text" className="w-full border border-gray-300 rounded-md px-3 py-2 bg-white text-gray-900"
value={tempSettings.branding.appName}
onChange={e => setTempSettings({...tempSettings, branding: {...tempSettings.branding, appName: e.target.value}})} />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Colore Primario (Hex)</label>
<div className="flex items-center">
<input type="color" className="h-10 w-10 border border-gray-300 rounded-md mr-3 cursor-pointer p-0.5 bg-white"
value={tempSettings.branding.primaryColor}
onChange={e => setTempSettings({...tempSettings, branding: {...tempSettings.branding, primaryColor: e.target.value}})} />
<input type="text" className="flex-1 border border-gray-300 rounded-md px-3 py-2 uppercase bg-white text-gray-900"
value={tempSettings.branding.primaryColor}
onChange={e => setTempSettings({...tempSettings, branding: {...tempSettings.branding, primaryColor: e.target.value}})} />
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">URL Logo</label>
<input type="text" className="w-full border border-gray-300 rounded-md px-3 py-2 bg-white text-gray-900"
value={tempSettings.branding.logoUrl}
onChange={e => setTempSettings({...tempSettings, branding: {...tempSettings.branding, logoUrl: e.target.value}})} />
</div>
</div>
)}
{/* AI CONFIG TAB */} {/* AI CONFIG TAB */}
{settingsTab === 'ai' && canManageTeam && ( {settingsTab === 'ai' && canManageTeam && (
<div className="space-y-6 max-w-2xl animate-fade-in"> <div className="space-y-6 max-w-2xl animate-fade-in">
@@ -833,17 +907,202 @@ export const AgentDashboard: React.FC<AgentDashboardProps> = ({
</div> </div>
)} )}
{settingsTab !== 'ai' && ( {/* USERS TAB */}
<> {settingsTab === 'users' && canManageTeam && (
{settingsTab === 'system' && canManageGlobalSettings && ( <div className="animate-fade-in">
<div className="space-y-8 animate-fade-in"><h2 className="text-xl font-bold text-gray-800 mb-6">Limiti e Quote di Sistema</h2><p className="text-gray-500">Configura i limiti globali.</p></div> <div className="flex justify-between items-center mb-6">
)} <h2 className="text-xl font-bold text-gray-800">Gestione Utenti Frontend</h2>
{settingsTab === 'general' && canManageGlobalSettings && <div className="space-y-6 max-w-lg"><h2 className="text-xl font-bold text-gray-800 mb-6">Personalizzazione Branding</h2>{/* ... */}</div>} <button
{settingsTab === 'users' && canManageTeam && <div><h2 className="text-xl font-bold text-gray-800 mb-6">Gestione Utenti Frontend</h2>{/* ... */}</div>} onClick={() => { setEditingUser(null); setNewUserForm({ name: '', email: '', status: 'active', company: '' }); }}
{settingsTab === 'agents' && canManageTeam && <div><h2 className="text-xl font-bold text-gray-800 mb-6">Gestione Team di Supporto</h2>{/* ... */}</div>} className="text-sm bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-2 rounded-md transition"
{settingsTab === 'queues' && canManageTeam && <div><h2 className="text-xl font-bold text-gray-800 mb-6">Code di Smistamento</h2>{/* ... */}</div>} >
{settingsTab === 'email' && canManageGlobalSettings && <div><h2 className="text-xl font-bold text-gray-800 mb-6">Notifiche & Email</h2>{/* ... */}</div>} Reset Form
</> </button>
</div>
<div className="bg-gray-50 p-4 rounded-lg border border-gray-200 mb-6">
<h3 className="font-bold text-sm text-gray-700 mb-3">{editingUser ? 'Modifica Utente' : 'Aggiungi Nuovo Utente'}</h3>
<div className="grid grid-cols-2 gap-4 mb-4">
<input type="text" placeholder="Nome Completo" className="border p-2 rounded text-sm bg-white text-gray-900" value={newUserForm.name} onChange={e => setNewUserForm({...newUserForm, name: e.target.value})} />
<input type="email" placeholder="Email" className="border p-2 rounded text-sm bg-white text-gray-900" value={newUserForm.email} onChange={e => setNewUserForm({...newUserForm, email: e.target.value})} />
<input type="text" placeholder="Azienda (Opzionale)" className="border p-2 rounded text-sm bg-white text-gray-900" value={newUserForm.company} onChange={e => setNewUserForm({...newUserForm, company: e.target.value})} />
<select className="border p-2 rounded text-sm bg-white text-gray-900" value={newUserForm.status} onChange={e => setNewUserForm({...newUserForm, status: e.target.value as any})}>
<option value="active">Attivo</option>
<option value="inactive">Inattivo</option>
</select>
</div>
<div className="flex justify-end space-x-2">
{editingUser && <button onClick={cancelEditUser} className="px-4 py-2 text-sm text-gray-600 hover:bg-gray-200 rounded">Annulla</button>}
<button onClick={editingUser ? handleUpdateUser : handleAddUser} className="bg-brand-600 text-white px-4 py-2 rounded text-sm hover:bg-brand-700">{editingUser ? 'Aggiorna' : 'Aggiungi'}</button>
</div>
</div>
<div className="overflow-x-auto border border-gray-200 rounded-lg">
<table className="min-w-full text-left bg-white">
<thead className="bg-gray-50 text-gray-500 text-xs uppercase font-medium"><tr><th className="px-4 py-3">Nome</th><th className="px-4 py-3">Email</th><th className="px-4 py-3">Azienda</th><th className="px-4 py-3">Stato</th><th className="px-4 py-3 text-right">Azioni</th></tr></thead>
<tbody className="divide-y divide-gray-100">
{clientUsers.map(user => (
<tr key={user.id} className="hover:bg-gray-50 text-sm">
<td className="px-4 py-3 font-medium text-gray-900">{user.name}</td>
<td className="px-4 py-3 text-gray-500">{user.email}</td>
<td className="px-4 py-3 text-gray-500">{user.company || '-'}</td>
<td className="px-4 py-3"><span className={`px-2 py-0.5 rounded text-xs ${user.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`}>{user.status}</span></td>
<td className="px-4 py-3 text-right space-x-2">
<button onClick={() => handleSendPasswordReset(user.email)} className="text-gray-400 hover:text-brand-600" title="Reset Password"><Key className="w-4 h-4" /></button>
<button onClick={() => handleEditUserClick(user)} className="text-gray-400 hover:text-blue-600"><Edit3 className="w-4 h-4" /></button>
<button onClick={() => removeClientUser(user.id)} className="text-gray-400 hover:text-red-600"><Trash2 className="w-4 h-4" /></button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{/* AGENTS TAB */}
{settingsTab === 'agents' && canManageTeam && (
<div className="animate-fade-in">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-bold text-gray-800">Team di Supporto</h2>
{isAgentQuotaFull ? <span className="text-red-500 text-sm font-bold">Quota Agenti Raggiunta</span> : (
<button onClick={() => { setEditingAgent(null); setNewAgentForm({ name: '', email: '', password: '', role: 'agent', skills: [], queues: [], avatar: '', avatarConfig: {x: 50, y: 50, scale: 1} }); }} className="text-sm bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-2 rounded-md transition">Nuovo Agente</button>
)}
</div>
<div className="bg-gray-50 p-6 rounded-lg border border-gray-200 mb-8">
<h3 className="font-bold text-gray-700 mb-4">{editingAgent ? 'Modifica Profilo Agente' : 'Nuovo Membro del Team'}</h3>
<div className="flex gap-6">
<div className="w-1/3">
<AvatarEditor
initialImage={newAgentForm.avatar || 'https://via.placeholder.com/200'}
initialConfig={newAgentForm.avatarConfig}
onSave={(img, cfg) => handleAvatarSaved(img, cfg, !!editingAgent)}
/>
</div>
<div className="w-2/3 space-y-4">
<div className="grid grid-cols-2 gap-4">
<input type="text" placeholder="Nome Completo" className="border p-2 rounded text-sm bg-white text-gray-900" value={newAgentForm.name} onChange={e => setNewAgentForm({...newAgentForm, name: e.target.value})} />
<input type="email" placeholder="Email Aziendale" className="border p-2 rounded text-sm bg-white text-gray-900" value={newAgentForm.email} onChange={e => setNewAgentForm({...newAgentForm, email: e.target.value})} />
</div>
<div className="grid grid-cols-2 gap-4">
<select className="border p-2 rounded text-sm bg-white text-gray-900" value={newAgentForm.role} onChange={e => setNewAgentForm({...newAgentForm, role: e.target.value as any})}>
<option value="agent">Agente Semplice</option>
<option value="supervisor">Supervisore</option>
<option value="superadmin">Super Admin</option>
</select>
<input type="password" placeholder={editingAgent ? "Lascia vuoto per non cambiare" : "Password provvisoria"} className="border p-2 rounded text-sm bg-white text-gray-900" value={newAgentForm.password} onChange={e => setNewAgentForm({...newAgentForm, password: e.target.value})} />
</div>
<div>
<label className="block text-xs font-bold text-gray-500 uppercase mb-2">Assegnazione Code</label>
<div className="flex flex-wrap gap-2">
{queues.map(q => (
<button
key={q.id}
onClick={() => toggleQueueInForm(q.name, !!editingAgent)}
className={`px-3 py-1 rounded-full text-xs border transition ${newAgentForm.queues?.includes(q.name) ? 'bg-blue-100 border-blue-300 text-blue-800 font-bold' : 'bg-white border-gray-200 text-gray-600 hover:border-gray-300'}`}
>
{q.name}
</button>
))}
</div>
</div>
<div className="flex justify-end space-x-3 pt-2">
{editingAgent && <button onClick={cancelEditAgent} className="px-4 py-2 text-gray-600 hover:bg-gray-200 rounded text-sm">Annulla</button>}
<button onClick={editingAgent ? handleUpdateAgent : handleAddAgent} className="bg-brand-600 text-white px-6 py-2 rounded text-sm hover:bg-brand-700 font-medium">{editingAgent ? 'Salva Modifiche' : 'Crea Agente'}</button>
</div>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{agents.map(agent => (
<div key={agent.id} className="bg-white p-4 rounded-lg border border-gray-200 shadow-sm flex items-start">
<div className="w-12 h-12 rounded-full overflow-hidden mr-4 bg-gray-100 flex-shrink-0">
<img src={agent.avatar || 'https://via.placeholder.com/200'} alt={agent.name} className="w-full h-full object-cover"
style={agent.avatarConfig ? { objectPosition: `${agent.avatarConfig.x}% ${agent.avatarConfig.y}%`, transform: `scale(${agent.avatarConfig.scale})` } : {}}
/>
</div>
<div className="flex-1 min-w-0">
<div className="flex justify-between items-start">
<h4 className="font-bold text-gray-900 truncate">{agent.name}</h4>
<span className={`text-[10px] uppercase font-bold px-1.5 py-0.5 rounded ${agent.role === 'superadmin' ? 'bg-purple-100 text-purple-700' : agent.role === 'supervisor' ? 'bg-amber-100 text-amber-700' : 'bg-blue-100 text-blue-700'}`}>{agent.role}</span>
</div>
<p className="text-xs text-gray-500 mb-2 truncate">{agent.email}</p>
<div className="flex flex-wrap gap-1 mb-2">
{agent.queues.map(q => <span key={q} className="text-[10px] bg-gray-100 text-gray-600 px-1 rounded">{q}</span>)}
</div>
<div className="flex space-x-3 text-xs text-gray-400">
<button onClick={() => handleEditAgentClick(agent)} className="hover:text-blue-600 flex items-center"><Edit3 className="w-3 h-3 mr-1" /> Modifica</button>
{agent.role !== 'superadmin' && <button onClick={() => removeAgent(agent.id)} className="hover:text-red-600 flex items-center"><Trash2 className="w-3 h-3 mr-1" /> Rimuovi</button>}
</div>
</div>
</div>
))}
</div>
</div>
)}
{/* QUEUES TAB */}
{settingsTab === 'queues' && canManageTeam && (
<div className="animate-fade-in max-w-2xl">
<h2 className="text-xl font-bold text-gray-800 mb-6">Code di Smistamento</h2>
<div className="bg-gray-50 p-4 rounded-lg border border-gray-200 mb-6 flex space-x-3 items-end">
<div className="flex-1">
<label className="block text-xs font-bold text-gray-500 uppercase mb-1">Nome Coda</label>
<input type="text" className="w-full border p-2 rounded text-sm bg-white text-gray-900" placeholder="Es. Supporto Tecnico" value={newQueueForm.name} onChange={e => setNewQueueForm({...newQueueForm, name: e.target.value})} />
</div>
<div className="flex-[2]">
<label className="block text-xs font-bold text-gray-500 uppercase mb-1">Descrizione</label>
<input type="text" className="w-full border p-2 rounded text-sm bg-white text-gray-900" placeholder="Descrizione interna..." value={newQueueForm.description} onChange={e => setNewQueueForm({...newQueueForm, description: e.target.value})} />
</div>
<button onClick={handleAddQueue} className="bg-brand-600 text-white px-4 py-2 rounded text-sm font-bold hover:bg-brand-700 h-10">Aggiungi</button>
</div>
<div className="space-y-3">
{queues.map(q => (
<div key={q.id} className="bg-white p-4 rounded-lg border border-gray-200 shadow-sm flex justify-between items-center group">
<div>
<h4 className="font-bold text-gray-900 flex items-center">
<Layers className="w-4 h-4 mr-2 text-gray-400" />
{q.name}
</h4>
<p className="text-sm text-gray-500 ml-6">{q.description}</p>
</div>
<button onClick={() => removeQueue(q.id)} className="text-gray-300 hover:text-red-500 p-2"><X className="w-5 h-5" /></button>
</div>
))}
</div>
</div>
)}
{settingsTab === 'email' && canManageGlobalSettings && (
<div className="animate-fade-in">
<h2 className="text-xl font-bold text-gray-800 mb-6">Configurazione SMTP</h2>
<div className="grid grid-cols-2 gap-4 max-w-2xl">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Host SMTP</label>
<input type="text" className="w-full border p-2 rounded text-sm bg-white text-gray-900" value={tempSettings.smtp.host} onChange={e => setTempSettings({...tempSettings, smtp: {...tempSettings.smtp, host: e.target.value}})} />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Porta</label>
<input type="number" className="w-full border p-2 rounded text-sm bg-white text-gray-900" value={tempSettings.smtp.port} onChange={e => setTempSettings({...tempSettings, smtp: {...tempSettings.smtp, port: parseInt(e.target.value)}})} />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Utente</label>
<input type="text" className="w-full border p-2 rounded text-sm bg-white text-gray-900" value={tempSettings.smtp.user} onChange={e => setTempSettings({...tempSettings, smtp: {...tempSettings.smtp, user: e.target.value}})} />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Password</label>
<input type="password" className="w-full border p-2 rounded text-sm bg-white text-gray-900" value={tempSettings.smtp.pass} onChange={e => setTempSettings({...tempSettings, smtp: {...tempSettings.smtp, pass: e.target.value}})} />
</div>
</div>
<div className="mt-4">
<button onClick={handleTestSmtp} className="text-brand-600 text-sm font-medium hover:underline">{isTestingSmtp ? 'Test in corso...' : 'Test Connessione'}</button>
</div>
</div>
)} )}
{settingsTab !== 'users' && settingsTab !== 'agents' && settingsTab !== 'queues' && ( {settingsTab !== 'users' && settingsTab !== 'agents' && settingsTab !== 'queues' && (