'use client'; import { useState, useEffect } from 'react'; import { inventoryApi } from '@/lib/api'; import PageShell from '@/components/PageShell'; import { Shield, ShieldAlert, UserPlus, User, Trash2, Tag, Plus, AlertTriangle, LogOut, Database, History, Lock, Edit2, X, Server, Globe, Wifi, WifiOff, Layers, ChevronDown, Clock, HardDrive, Download, RotateCcw } from 'lucide-react'; import { toast } from 'react-hot-toast'; import { cn } from '@/lib/utils'; export default function AdminPage() { const [users, setUsers] = useState([]); const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); // LDAP Config State const [ldapConfig, setLdapConfig] = useState({ ldap_enabled: false, server_uri: 'ldap://192.168.84.107:3890', base_dn: 'dc=example,dc=com', user_template: 'cn={username},ou=people,dc=example,dc=com', groups_dn: 'ou=groups', use_tls: false, role_mappings: [] }); const [testingLdap, setTestingLdap] = useState(false); // Edit States const [editingUser, setEditingUser] = useState(null); const [editUserForm, setEditUserForm] = useState({ username: '', password: '', role: 'user' }); const [editingCategory, setEditingCategory] = useState(null); const [editCatForm, setEditCatForm] = useState({ name: '', description: '' }); // DB Management State 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); useEffect(() => { loadData(); }, []); const loadData = async () => { setLoading(true); try { const [u, c, l, b, s, st] = await Promise.all([ inventoryApi.getUsers(), inventoryApi.getCategories(), inventoryApi.getLdapConfig(), inventoryApi.getDbBackups(), inventoryApi.getDbStats(), inventoryApi.getDbSettings() ]); setUsers(u); setCategories(c); if (l && l.server_uri) setLdapConfig(l); setBackups(b); setDbStats(s); setDbSettings(st); } catch (err: any) { console.error(err); toast.error("Failed to load admin data"); } finally { setLoading(false); } }; const handleAddUser = async () => { const name = prompt("Enter new username:"); if (!name) return; const pwd = 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 (!confirm(`Delete user ${username}?`)) return; try { await inventoryApi.deleteUser(id); toast.success("User removed"); loadData(); } catch (err: any) { toast.error("Delete failed"); } }; const handleAddCategory = async () => { const name = prompt("New category name:"); if (!name) return; const desc = 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 (!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 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 handleLogout = () => { import('@/lib/auth').then(m => m.clearAuth()); window.location.href = '/login'; }; 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 handleRestore = async (filename: string) => { if (!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 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 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 formatSize = (bytes: number) => { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; }; return (

System Admin

Enterprise Control Center

{/* User Management Section */}

User Accounts

Manage local and network synchronized identities

{/* Local Users Group */}

Local Users

{loading ? (
Loading...
) : ( users.filter(u => (u.origin || 'local') === 'local').map(u => (
{u.role === 'admin' ? : }

{u.username}

{u.role}
{u.username !== 'Admin' && ( )}
)) )}
{/* enterprise Users Group */} {users.some(u => u.origin === 'ldap') && (

Enterprise Users (LDAP)

{users.filter(u => u.origin === 'ldap').map(u => (

{u.username}

Ldap Profile {u.role}
Cached Profile
))}
)}
{/* Enterprise Integration (LDAP) Section */}

Enterprise Integration

Configure directory services and single sign-on synchronization

Offline Only LDAP Active
setLdapConfig({ ...ldapConfig, server_uri: e.target.value })} className="w-full bg-slate-950 border border-slate-800 rounded-2xl py-3 pl-10 pr-4 text-sm text-white outline-none focus:border-indigo-500/30 transition-all font-mono" />
setLdapConfig({ ...ldapConfig, base_dn: e.target.value })} className="w-full bg-slate-950 border border-slate-800 rounded-2xl py-3 px-4 text-sm text-white outline-none focus:border-indigo-500/30 transition-all font-mono" />
setLdapConfig({ ...ldapConfig, user_template: e.target.value })} className="w-full bg-slate-950 border border-slate-800 rounded-2xl py-3 px-4 text-sm text-white outline-none focus:border-indigo-500/30 transition-all font-mono" />
setLdapConfig({ ...ldapConfig, groups_dn: e.target.value })} className="w-full bg-slate-950 border border-slate-800 rounded-2xl py-3 px-4 text-sm text-white outline-none focus:border-indigo-500/30 transition-all font-mono" />
m.role === 'admin')?.group || ''} onChange={(e) => { const newMappings = [...(ldapConfig.role_mappings || [])]; const idx = newMappings.findIndex((m: any) => m.role === 'admin'); if (idx >= 0) newMappings[idx] = { ...newMappings[idx], group: e.target.value }; else newMappings.push({ role: 'admin', group: e.target.value }); setLdapConfig({ ...ldapConfig, role_mappings: newMappings }); }} className="w-full bg-slate-950 border border-slate-800 rounded-2xl py-3 px-4 text-sm text-white outline-none focus:border-indigo-500/30 transition-all font-mono" />
m.role === 'user')?.group || ''} onChange={(e) => { const newMappings = [...(ldapConfig.role_mappings || [])]; const idx = newMappings.findIndex((m: any) => m.role === 'user'); if (idx >= 0) newMappings[idx] = { ...newMappings[idx], group: e.target.value }; else newMappings.push({ role: 'user', group: e.target.value }); setLdapConfig({ ...ldapConfig, role_mappings: newMappings }); }} className="w-full bg-slate-950 border border-slate-800 rounded-2xl py-3 px-4 text-sm text-white outline-none focus:border-indigo-500/30 transition-all font-mono" />

Synchronization Shield

Enabling LDAP will allow users from your network domain to log in using their enterprise credentials. Local account passwords will still function as a fail-safe backup.

LDAPS / TLS Encryption
{ldapConfig.use_tls && (
Ignore Certificate Validation (Self-signed)
)}
{/* Category Management Section */}

Category Groups

Classify inventory items into logical clusters

{loading ? (
Loading Categories...
) : ( categories.map(cat => (

{cat.name}

{cat.description || 'General Purpose Group'}

)) )} {categories.length === 0 && !loading && (
No categories defined
)}
{/* System Integrity (Compacted and left-aligned) */}

System Integrity

Hybrid storage model active. Real-time synchronization between local memory and cloud-hosted SQLite instance.

Storage Status
Online
Local Archives
{dbStats.backup_count} Files {formatSize(dbStats.total_size_bytes)}
{/* Database & Continuity Card */}

Database & Continuity

Snapshot management and disaster recovery tools

{/* Scheduling Configuration */}
Retention & Automation
handleUpdateDbSettings({ ...dbSettings, retention_count: parseInt(e.target.value) || 1 })} className="w-full bg-slate-900 border border-slate-800 rounded-xl py-2 px-4 text-sm text-white outline-none focus:border-amber-500/30 transition-all font-mono" />

Automated backups are stored in /data/backups. Restoring data will overwrite the current live primary database.

{/* Backup History List */}
Available Snapshots Sorted by newest
{backups.length === 0 ? (
No backups available on disk
) : ( backups.map((b) => (

{b.filename}

{new Date(b.created_at).toLocaleString()} {formatSize(b.size_bytes)}
)) )}
{/* Edit User Modal */} {editingUser && (

Edit Profile

setEditUserForm({ ...editUserForm, username: e.target.value })} className="w-full bg-slate-950 border border-slate-800 focus:border-primary rounded-2xl py-4 px-5 text-white outline-none transition-all disabled:opacity-50" /> {editingUser.username === 'Admin' &&

Default Admin name is protected

}
setEditUserForm({ ...editUserForm, password: e.target.value })} className="w-full bg-slate-950 border border-slate-800 focus:border-primary rounded-2xl py-4 px-5 text-white/50 focus:text-white outline-none transition-all placeholder:text-slate-800" />
)} {/* Edit Category Modal */} {editingCategory && (

Edit Group

setEditCatForm({ ...editCatForm, name: e.target.value })} className="w-full bg-slate-950 border border-slate-800 focus:border-primary rounded-2xl py-4 px-5 text-white outline-none transition-all" />