feat: integrate dynamic audit logs and dashboard quick tasks from API
This commit is contained in:
parent
13bc5f9078
commit
84674ea206
2 changed files with 100 additions and 77 deletions
|
|
@ -1,56 +1,87 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { API_BASE_URL } from '../config';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
|
||||
const AuditLogs = () => {
|
||||
const [logs] = useState([
|
||||
{ id: 221, admin: 'admin', action: 'approve_seller', target: 'Seller #2', details: 'KYC verified for ElectroHub', time: '2026-08-15 15:30:22' },
|
||||
{ id: 220, admin: 'admin', action: 'issue_refund', target: 'Order #ORD88761', details: '₹2,499.00 processed via PhonePe', time: '2026-08-15 14:15:10' },
|
||||
{ id: 219, admin: 'moderator', action: 'create_return', target: 'Order #ORD88120', details: 'Shadowfax AWB SF_RET_AWB_776152', time: '2026-08-14 11:22:45' },
|
||||
{ id: 218, admin: 'admin', action: 'hold_product', target: 'Product #101', details: 'Held: Stock verification pending', time: '2026-08-12 09:05:00' },
|
||||
]);
|
||||
const { token } = useAuth();
|
||||
const [logs, setLogs] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLogs = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const res = await fetch(`${API_BASE_URL}/audit-logs/`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
if (!res.ok) throw new Error('Failed to fetch system audit logs');
|
||||
const data = await res.json();
|
||||
setLogs(data);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchLogs();
|
||||
}, [token]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="page-title">Admin System Audit Logs</h1>
|
||||
|
||||
<div className="glass-card">
|
||||
<div className="table-container">
|
||||
<table className="custom-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Log ID</th>
|
||||
<th>Admin User</th>
|
||||
<th>Action</th>
|
||||
<th>Target Object</th>
|
||||
<th>Operation Details</th>
|
||||
<th>Timestamp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{logs.map(log => (
|
||||
<tr key={log.id}>
|
||||
<td>#{log.id}</td>
|
||||
<td><span style={{ fontFamily: 'monospace' }}>{log.admin}</span></td>
|
||||
<td>
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
background: 'rgba(139, 92, 246, 0.1)',
|
||||
color: 'var(--accent-hover)',
|
||||
padding: '2px 8px',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '600'
|
||||
}}>
|
||||
{log.action}
|
||||
</span>
|
||||
</td>
|
||||
<td style={{ fontWeight: '500' }}>{log.target}</td>
|
||||
<td>{log.details}</td>
|
||||
<td style={{ color: 'var(--text-secondary)' }}>{log.time}</td>
|
||||
{loading ? (
|
||||
<div style={{ padding: '40px', textAlign: 'center' }}>Loading audit trail logs...</div>
|
||||
) : error ? (
|
||||
<div style={{ padding: '20px', color: 'var(--color-danger)', textAlign: 'center' }}>Error: {error}</div>
|
||||
) : (
|
||||
<div className="table-container">
|
||||
<table className="custom-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Log ID</th>
|
||||
<th>Admin User</th>
|
||||
<th>Action</th>
|
||||
<th>Target Object</th>
|
||||
<th>Operation Details</th>
|
||||
<th>Timestamp</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</thead>
|
||||
<tbody>
|
||||
{logs.map(log => (
|
||||
<tr key={log.id}>
|
||||
<td>#{log.id}</td>
|
||||
<td><span style={{ fontFamily: 'monospace', fontWeight: 'bold' }}>{log.admin_username || log.admin || 'system'}</span></td>
|
||||
<td>
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
background: 'rgba(139, 92, 246, 0.1)',
|
||||
color: 'var(--accent-hover)',
|
||||
padding: '2px 8px',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '600'
|
||||
}}>
|
||||
{log.action}
|
||||
</span>
|
||||
</td>
|
||||
<td style={{ fontWeight: '500' }}>
|
||||
{log.target_type} #{log.target_id || ''}
|
||||
</td>
|
||||
<td>{typeof log.details === 'object' ? JSON.stringify(log.details) : log.details}</td>
|
||||
<td style={{ color: 'var(--text-secondary)' }}>
|
||||
{log.performed_at ? new Date(log.performed_at).toLocaleString() : log.time}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ const Dashboard = () => {
|
|||
pending_approvals: 0,
|
||||
total_revenue: 0,
|
||||
active_products: 0,
|
||||
refunds_issued: 0
|
||||
refunds_issued: 0,
|
||||
chart_labels: [],
|
||||
chart_sales: [],
|
||||
quick_tasks: []
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
|
@ -47,11 +50,11 @@ const Dashboard = () => {
|
|||
];
|
||||
|
||||
const chartData = {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'],
|
||||
labels: stats.chart_labels.length > 0 ? stats.chart_labels : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Platform Sales (₹ Lakhs)',
|
||||
data: [2.5, 3.8, 3.2, 5.4, 6.1, 7.8, 9.2, stats.total_revenue / 100000],
|
||||
data: stats.chart_sales.length > 0 ? stats.chart_sales : [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
borderColor: '#8b5cf6',
|
||||
backgroundColor: 'rgba(139, 92, 246, 0.2)',
|
||||
tension: 0.4,
|
||||
|
|
@ -115,36 +118,25 @@ const Dashboard = () => {
|
|||
|
||||
<div className="glass-card" style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
||||
<h3 className="section-title">Quick Tasks</h3>
|
||||
<div style={{
|
||||
padding: '12px',
|
||||
background: 'rgba(255,255,255,0.02)',
|
||||
border: '1px solid var(--glass-border)',
|
||||
borderRadius: 'var(--border-radius)',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
<div style={{ fontWeight: '600', marginBottom: '4px' }}>Validate New Sellers</div>
|
||||
<p style={{ color: 'var(--text-secondary)', fontSize: '12px' }}>Verify uploaded KYC Aadhar and PAN records.</p>
|
||||
</div>
|
||||
<div style={{
|
||||
padding: '12px',
|
||||
background: 'rgba(255,255,255,0.02)',
|
||||
border: '1px solid var(--glass-border)',
|
||||
borderRadius: 'var(--border-radius)',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
<div style={{ fontWeight: '600', marginBottom: '4px' }}>Pending Withdrawal Requests</div>
|
||||
<p style={{ color: 'var(--text-secondary)', fontSize: '12px' }}>Approve wallet transfers and input Bank UTR details.</p>
|
||||
</div>
|
||||
<div style={{
|
||||
padding: '12px',
|
||||
background: 'rgba(255,255,255,0.02)',
|
||||
border: '1px solid var(--glass-border)',
|
||||
borderRadius: 'var(--border-radius)',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
<div style={{ fontWeight: '600', marginBottom: '4px' }}>Shadowfax Reverse Log</div>
|
||||
<p style={{ color: 'var(--text-secondary)', fontSize: '12px' }}>Check failed doorstep quality check (QC) status.</p>
|
||||
</div>
|
||||
{stats.quick_tasks && stats.quick_tasks.length > 0 ? (
|
||||
stats.quick_tasks.map((task, idx) => (
|
||||
<div key={idx} style={{
|
||||
padding: '12px',
|
||||
background: 'rgba(255,255,255,0.02)',
|
||||
border: '1px solid var(--glass-border)',
|
||||
borderRadius: 'var(--border-radius)',
|
||||
fontSize: '14px'
|
||||
}}>
|
||||
<div style={{ fontWeight: '600', marginBottom: '4px', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>{task.title}</span>
|
||||
{task.count > 0 && <span style={{ color: 'var(--color-warning)', fontWeight: 'bold' }}>⚠️</span>}
|
||||
</div>
|
||||
<p style={{ color: 'var(--text-secondary)', fontSize: '12px', margin: 0 }}>{task.desc}</p>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div style={{ padding: '20px', textAlign: 'center', color: 'var(--text-muted)' }}>No pending tasks!</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue