fix: .gitignore logs removed.
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
# Logs
|
# Logs
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { History, RefreshCcw } from "lucide-react";
|
||||||
|
import { DataTable } from "../../components/custom/CustomTable";
|
||||||
|
import type { ColumnDef } from '../../components/custom/CustomTable';
|
||||||
|
import CustomButton from "../../components/custom/CustomButton";
|
||||||
|
import { apiClient } from "../../lib/apiClient";
|
||||||
|
|
||||||
|
interface AuditLog extends Record<string, any> {
|
||||||
|
id: string;
|
||||||
|
module_name: string;
|
||||||
|
action_type: string;
|
||||||
|
entity_name: string;
|
||||||
|
performed_by_email: string;
|
||||||
|
ip_address: string;
|
||||||
|
description: string;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuditLogListResponse {
|
||||||
|
items: AuditLog[];
|
||||||
|
total: number;
|
||||||
|
limit: number;
|
||||||
|
offset: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LogsPage = () => {
|
||||||
|
const [logs, setLogs] = useState<AuditLog[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
const fetchLogs = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const response = await apiClient.get<AuditLogListResponse>(
|
||||||
|
'/api/admin/audit-logs/',
|
||||||
|
{ silent: true }
|
||||||
|
);
|
||||||
|
setLogs(response.items);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch logs", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchLogs();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const columns: ColumnDef<AuditLog>[] = [
|
||||||
|
{ key: "module_name", header: "Module", searchable: true },
|
||||||
|
{
|
||||||
|
key: "action_type",
|
||||||
|
header: "Action",
|
||||||
|
render: (row) => (
|
||||||
|
<span className={`px-2 py-1 rounded text-xs font-bold ${row.action_type === 'CREATE' ? 'bg-green-100 text-green-700' :
|
||||||
|
row.action_type === 'UPDATE' ? 'bg-blue-100 text-blue-700' :
|
||||||
|
row.action_type === 'DELETE' ? 'bg-red-100 text-red-700' : 'bg-gray-100'
|
||||||
|
}`}>
|
||||||
|
{row.action_type}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ key: "description", header: "Description" },
|
||||||
|
{ key: "performed_by_email", header: "Performed By", searchable: true },
|
||||||
|
{ key: "ip_address", header: "IP Address" },
|
||||||
|
{
|
||||||
|
key: "created_at",
|
||||||
|
header: "Timestamp",
|
||||||
|
render: (row) => new Date(row.created_at).toLocaleString()
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 space-y-6">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<h1 className="text-2xl font-bold text-(--text-primary) flex items-center gap-2">
|
||||||
|
<History size={24} /> System Logs
|
||||||
|
</h1>
|
||||||
|
<CustomButton
|
||||||
|
variant="outlined"
|
||||||
|
onClick={fetchLogs}
|
||||||
|
loading={isLoading}
|
||||||
|
leftIcon={<RefreshCcw size={16} />}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</CustomButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DataTable<AuditLog>
|
||||||
|
data={logs}
|
||||||
|
columns={columns}
|
||||||
|
isLoading={isLoading}
|
||||||
|
exportFileName="audit_logs.csv"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LogsPage;
|
||||||
Reference in New Issue
Block a user