import { useState, useEffect } from 'react'; import { inventoryApi } from '@/lib/api'; import { toast } from 'react-hot-toast'; export function useAdmin() { const [users, setUsers] = useState([]); const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [ldapConfig, setLdapConfig] = useState({ ldap_enabled: false, server_uri: '', base_dn: '', user_template: '', groups_dn: '', use_tls: false, role_mappings: [] }); const [testingLdap, setTestingLdap] = useState(false); const [editingUser, setEditingUser] = useState(null); const [editUserForm, setEditUserForm] = useState({ username: '', password: '', role: 'user' }); const [editingCategory, setEditingCategory] = useState(null); const [editCatForm, setEditCatForm] = useState({ name: '', description: '' }); const [backups, setBackups] = useState([]); const [dbStats, setDbStats] = useState({ backup_count: 0, total_size_bytes: 0 }); const [dbSettings, setDbSettings] = useState({ retention_count: 10, schedule_hour: 3, schedule_freq_days: 1 }); const [isBackingUp, setIsBackingUp] = useState(false); const [isImporting, setIsImporting] = useState(false); const [aiPrompt, setAiPrompt] = useState(""); const [aiConfig, setAiConfig] = useState(null); const [aiKeys, setAiKeys] = useState({ gemini: '', claude: '' }); const [isSavingPrompt, setIsSavingPrompt] = useState(false); const [isSavingKeys, setIsSavingKeys] = useState(false); const [isTestingKeys, setIsTestingKeys] = useState<{gemini: boolean, claude: boolean}>( {gemini: false, claude: false} ); useEffect(() => { loadData(); }, []); const loadData = async () => { setLoading(true); try { const [u, c, l, b, s, st, promptRes, ai] = await Promise.all([ inventoryApi.getUsers(), inventoryApi.getCategories(), inventoryApi.getLdapConfig(), inventoryApi.getDbBackups(), inventoryApi.getDbStats(), inventoryApi.getDbSettings(), inventoryApi.getAiPrompt(), inventoryApi.getAiConfig() ]); setUsers(u); setCategories(c); if (l && l.server_uri) setLdapConfig(l); setBackups(b); setDbStats(s); setDbSettings(st); setAiPrompt(promptRes.value); setAiConfig(ai); } catch (err: any) { console.error(err); toast.error("Failed to load admin data"); } finally { setLoading(false); } }; const handleAddUser = async () => { const name = window.prompt("Enter new username:"); if (!name) return; const pwd = window.prompt("Enter password for " + name + ":"); if (!pwd) return; try { await inventoryApi.createUser({ username: name, password: pwd, role: 'user' }); toast.success("User created successfully"); loadData(); } catch (err: any) { toast.error("Failed to create user"); } }; const handleDeleteUser = async (id: number, username: string) => { if (username === 'Admin') return; if (!window.confirm(`Delete user ${username}?`)) return; try { await inventoryApi.deleteUser(id); toast.success("User removed"); loadData(); } catch (err: any) { toast.error("Delete failed"); } }; const handleUpdateUserSubmit = async () => { if (!editingUser) return; try { const payload: any = { username: editUserForm.username, role: editUserForm.role }; if (editUserForm.password) payload.password = editUserForm.password; await inventoryApi.updateUser(editingUser.id, payload); toast.success("User updated successfully"); setEditingUser(null); loadData(); } catch (err: any) { toast.error("Update failed"); } }; const handleUpdateAiProvider = async (provider: string) => { try { await inventoryApi.updateAiProvider(provider); toast.success(`AI provider switched to ${provider}`); loadData(); } catch (err: any) { toast.error("Failed to switch AI provider"); } }; const handleSaveAiKeys = async () => { if (!aiKeys.gemini && !aiKeys.claude) { toast.error("Enter at least one API key to save"); return; } setIsSavingKeys(true); try { await inventoryApi.updateAiKeys({ gemini_api_key: aiKeys.gemini || undefined, claude_api_key: aiKeys.claude || undefined }); toast.success("AI Configuration keys updated successfully"); setAiKeys({ gemini: '', claude: '' }); loadData(); } catch (err: any) { toast.error("Failed to update AI keys"); } finally { setIsSavingKeys(false); } }; const handleTestAiKey = async (provider: 'gemini' | 'claude') => { const keyToTest = provider === 'gemini' ? aiKeys.gemini : aiKeys.claude; setIsTestingKeys(prev => ({ ...prev, [provider]: true })); try { const res = await inventoryApi.testAiKey(provider, keyToTest); toast.success(res.message || `${provider.toUpperCase()} connection successful!`); } catch (err: any) { const msg = err.response?.data?.detail || `Failed to test ${provider} key`; toast.error(msg); } finally { setIsTestingKeys(prev => ({ ...prev, [provider]: false })); } }; const handleAddCategory = async () => { const name = window.prompt("New category name:"); if (!name) return; const desc = window.prompt("Description (optional):"); try { await inventoryApi.createCategory({ name, description: desc }); toast.success("Category added"); loadData(); } catch (err: any) { toast.error("Failed to add category"); } }; const handleUpdateCategorySubmit = async () => { if (!editingCategory) return; try { await inventoryApi.updateCategory(editingCategory.id, editCatForm); toast.success("Category updated"); setEditingCategory(null); loadData(); } catch (err: any) { toast.error("Update failed"); } }; const handleDeleteCategory = async (id: number, name: string) => { if (!window.confirm(`Delete category ${name}?`)) return; try { await inventoryApi.deleteCategory(id); toast.success("Category removed"); loadData(); } catch (err: any) { toast.error(err.response?.data?.detail || "Delete failed"); } }; const handleCreateBackup = async () => { setIsBackingUp(true); try { await inventoryApi.triggerBackup(); toast.success("Snapshot created successfully"); loadData(); } catch (err: any) { toast.error("Backup failed"); } finally { setIsBackingUp(false); } }; const handleUpdateDbSettings = async (newSettings: any) => { try { await inventoryApi.updateDbSettings(newSettings); toast.success("System policy updated"); setDbSettings(newSettings); } catch (err: any) { toast.error("Failed to update settings"); } }; const handleUpdatePrompt = async () => { setIsSavingPrompt(true); try { await inventoryApi.updateAiPrompt(aiPrompt); toast.success("AI Extraction Prompt updated"); } catch (err: any) { toast.error("Failed to update AI prompt"); } finally { setIsSavingPrompt(false); } }; const handleUpdateLdap = async () => { setLoading(true); try { await inventoryApi.updateLdapConfig(ldapConfig); toast.success("Enterprise configuration updated"); } catch (err: any) { toast.error("Failed to update LDAP config"); } finally { setLoading(false); } }; const handleTestLdap = async () => { setTestingLdap(true); try { const res = await inventoryApi.testLdapConnection(ldapConfig); if (res.status === 'success') { toast.success(res.message || "LDAP Connection Successful!"); } else { toast.error(`LDAP Error: ${res.message || "Unknown error"}`); } } catch (err: any) { toast.error(`Connection failed: ${err.response?.data?.detail || err.message}`); } finally { setTestingLdap(false); } }; const handleRestore = async (filename: string) => { if (!window.confirm(`DANGEROUS: Restore database from ${filename}? Current data will be replaced. A rollback snapshot will be created automatically.`)) return; const loadingToast = toast.loading("Restoring database..."); try { await inventoryApi.restoreDatabase(filename); toast.success("Database restored! Reloading system...", { id: loadingToast }); setTimeout(() => window.location.reload(), 2000); } catch (err: any) { toast.error("Restore failed", { id: loadingToast }); } }; const handleExportDb = async () => { try { const blob = await inventoryApi.exportDb(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `inventory_backup_${new Date().toISOString().split('T')[0]}.db`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); toast.success("Database exported successfully"); } catch (err: any) { toast.error("Export failed"); } }; const handleImportDb = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (!window.confirm("DANGEROUS: You are about to REPLACE the entire database with this file. All current data will be lost. Proceed?")) { e.target.value = ''; return; } setIsImporting(true); const formData = new FormData(); formData.append('file', file); const loadingToast = toast.loading("Importing database..."); try { await inventoryApi.importDb(formData); toast.success("Database imported! Reloading system...", { id: loadingToast }); setTimeout(() => window.location.reload(), 2000); } catch (err: any) { toast.error("Import failed: " + (err.response?.data?.detail || err.message), { id: loadingToast }); } finally { setIsImporting(false); e.target.value = ''; } }; const handleLogout = () => { import('@/lib/auth').then(m => m.clearAuth()); window.location.href = '/login'; }; return { users, categories, loading, ldapConfig, setLdapConfig, testingLdap, editingUser, setEditingUser, editUserForm, setEditUserForm, editingCategory, setEditingCategory, editCatForm, setEditCatForm, backups, dbStats, dbSettings, isBackingUp, isImporting, aiPrompt, setAiPrompt, aiConfig, aiKeys, setAiKeys, isSavingPrompt, isSavingKeys, isTestingKeys, refreshData: loadData, handleAddUser, handleDeleteUser, handleUpdateUserSubmit, handleUpdateLdap, handleTestLdap, handleUpdateAiProvider, handleSaveAiKeys, handleTestAiKey, handleUpdatePrompt, handleAddCategory, handleUpdateCategorySubmit, handleDeleteCategory, handleCreateBackup, handleUpdateDbSettings, handleRestore, handleExportDb, handleImportDb, handleLogout }; }