Files
zavrsni-rad-otel-app/frontend/src/pages/ops/AuditPage.tsx
2026-05-11 10:58:46 +02:00

109 lines
3.7 KiB
TypeScript

import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { getAuditLog } from "../../api/gateway";
const DOMAIN_OPTIONS = ["", "aw", "wwi", "platform"];
export default function AuditPage() {
const [domain, setDomain] = useState("");
const query = useQuery({
queryKey: ["audit", domain],
queryFn: () => getAuditLog(200, domain || undefined),
staleTime: 30_000,
});
return (
<div className="flex flex-col gap-6 max-w-[1100px] mx-auto">
<div className="flex items-center justify-between flex-wrap gap-3">
<div>
<h1 className="text-2xl font-semibold text-[rgba(233,244,255,0.92)]">Audit Log</h1>
<p className="text-sm text-[rgba(233,244,255,0.5)] mt-1">
All system actions recorded across services
</p>
</div>
<div className="flex items-center gap-3">
<select
className="form-input text-sm"
value={domain}
onChange={(e) => setDomain(e.target.value)}
>
<option value="">All domains</option>
{DOMAIN_OPTIONS.filter(Boolean).map((d) => (
<option key={d} value={d}>
{d.toUpperCase()}
</option>
))}
</select>
<button
className="btn-secondary"
onClick={() => void query.refetch()}
disabled={query.isFetching}
>
{query.isFetching ? "Loading…" : "Refresh"}
</button>
</div>
</div>
<div className="card">
{query.isLoading ? (
<div className="text-[rgba(233,244,255,0.4)] text-sm py-8 text-center">Loading</div>
) : query.isError ? (
<div className="text-red-400 text-sm py-8 text-center">
Failed to load audit log.
</div>
) : (query.data?.length ?? 0) === 0 ? (
<div className="text-[rgba(233,244,255,0.4)] text-sm py-8 text-center">
No entries found.
</div>
) : (
<div className="overflow-x-auto">
<table>
<thead>
<tr>
<th>Time</th>
<th>Action</th>
<th>Domain</th>
<th>Service</th>
<th>Entity</th>
<th>Actor</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{query.data!.map((r) => (
<tr key={r.id}>
<td className="text-xs text-[rgba(233,244,255,0.5)] whitespace-nowrap">
{new Date(r.occurred_at).toLocaleString()}
</td>
<td className="font-mono text-xs">{r.action}</td>
<td className="text-xs">{r.domain}</td>
<td className="text-xs text-[rgba(233,244,255,0.5)]">{r.service}</td>
<td className="text-xs">{r.entity_type}</td>
<td className="text-xs text-[rgba(233,244,255,0.5)] max-w-[120px] truncate">
{r.actor_id}
</td>
<td>
<span
className={
r.status === "success"
? "badge badge-low"
: r.status === "failure"
? "badge badge-high"
: "badge"
}
>
{r.status}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
);
}