167 lines
9.0 KiB
JavaScript

import React, { useState } from 'react';
import { Head, router } from '@inertiajs/react';
import AdminLayout from '@/Layouts/AdminLayout';
import {
Search,
Filter,
History,
ShieldCheck,
User,
CreditCard,
Scale,
AlertTriangle,
Eye,
Globe,
Terminal,
ArrowRight
} from 'lucide-react';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Badge } from '@/components/ui/badge';
export default function AuditLogsHub({ logs, search, category }) {
const [searchTerm, setSearchTerm] = useState(search || '');
const [selectedCategory, setSelectedCategory] = useState(category || 'all');
const handleSearchSubmit = (e) => {
e.preventDefault();
router.get('/admin/audit-logs', {
search: searchTerm,
category: selectedCategory
}, {
preserveState: true
});
};
const handleCategorySelect = (cat) => {
setSelectedCategory(cat);
router.get('/admin/audit-logs', {
search: searchTerm,
category: cat
});
};
return (
<AdminLayout title="System Compliance Audit Logs">
<Head title="System Audit Logs" />
<div className="font-sans max-w-7xl mx-auto space-y-6">
{/* Header text */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-xl font-bold text-gray-900 tracking-tight">Centralized Compliance & Security Audit Logs</h1>
<p className="text-xs text-gray-500 mt-0.5">Track every critical administrative event, auto-OCR verification, stripe checkout charge, and user moderation updates.</p>
</div>
</div>
{/* Filters Row */}
<div className="bg-white p-4 rounded-2xl border border-slate-200 shadow-sm flex flex-col md:flex-row gap-4 items-center justify-between">
<form onSubmit={handleSearchSubmit} className="flex flex-1 items-center space-x-3 w-full md:w-auto">
<div className="relative flex-1 max-w-md">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
<input
type="text"
placeholder="Search logs by IP, actor, message or ticket ID..."
className="w-full pl-10 pr-4 py-2.5 bg-slate-50 border border-slate-100 rounded-xl text-sm font-medium focus:bg-white focus:ring-4 focus:ring-teal-500/10 outline-none transition-all"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
</div>
<button type="submit" className="px-4 py-2.5 bg-slate-900 hover:bg-slate-800 text-white rounded-xl text-xs font-black uppercase tracking-wider">
Search Logs
</button>
</form>
{/* Category tabs */}
<div className="flex items-center gap-1 bg-slate-100 p-1 rounded-xl overflow-x-auto w-full md:w-auto">
{[
{ label: 'All Logs', val: 'all' },
{ label: 'Admin Actions', val: 'admin_action' },
{ label: 'OCR Checks', val: 'verification' },
{ label: 'Payments', val: 'payment' },
{ label: 'Disputes', val: 'dispute' },
{ label: 'User Logins', val: 'user_activity' }
].map((cat) => (
<button
key={cat.val}
onClick={() => handleCategorySelect(cat.val)}
className={`px-3 py-1.5 rounded-lg text-[9px] font-black uppercase tracking-widest whitespace-nowrap transition-all ${
selectedCategory === cat.val
? 'bg-white text-[#0F6E56] shadow-sm'
: 'text-gray-500 hover:text-gray-900'
}`}
>
{cat.label}
</button>
))}
</div>
</div>
{/* Audit Grid table */}
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<Table>
<TableHeader>
<TableRow className="bg-slate-50/50 hover:bg-slate-50/50">
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14 pl-8">Event ID / Category</TableHead>
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Actor Account</TableHead>
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Activity Description</TableHead>
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">IP Address</TableHead>
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14 text-right pr-8">Timestamp</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{logs && logs.length > 0 ? (
logs.map((log) => (
<TableRow key={log.id} className="hover:bg-slate-50/50 transition-colors">
<TableCell className="py-4 pl-8">
<div className="flex items-center space-x-2">
<Badge className={`border-none text-[8px] font-black uppercase tracking-wider ${
log.category === 'admin_action' ? 'bg-red-50 text-red-700' :
log.category === 'verification' ? 'bg-blue-50 text-blue-700' :
log.category === 'payment' ? 'bg-emerald-50 text-emerald-700' :
log.category === 'dispute' ? 'bg-amber-50 text-amber-700' : 'bg-slate-100 text-slate-600'
}`}>{log.category.replace('_', ' ')}</Badge>
<span className="font-mono text-[9px] text-slate-400 font-bold">{log.id}</span>
</div>
</TableCell>
<TableCell className="text-xs font-bold text-slate-700">
<div className="flex items-center space-x-2">
<User className="w-3.5 h-3.5 text-slate-400" />
<span>{log.user}</span>
</div>
</TableCell>
<TableCell className="text-xs font-semibold text-slate-600 leading-normal max-w-sm">
{log.action}
</TableCell>
<TableCell className="text-xs font-mono font-medium text-slate-400">
<div className="flex items-center space-x-1">
<Globe className="w-3.5 h-3.5 text-slate-300" />
<span>{log.ip_address}</span>
</div>
</TableCell>
<TableCell className="text-xs text-slate-400 text-right pr-8 font-mono">{log.timestamp}</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={5} className="py-20 text-center">
<Terminal className="w-12 h-12 text-slate-200 mx-auto mb-3" />
<div className="font-bold text-slate-400">No security audit logs found matching your criteria.</div>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</div>
</AdminLayout>
);
}