doc compare
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { AdminDiagnosticsDto, BackupGuidanceDto, SupportLogEntryDto } from "@mrp/shared";
|
||||
import type { AdminDiagnosticsDto, BackupGuidanceDto, SupportLogEntryDto, SupportLogFiltersDto, SupportLogListDto } from "@mrp/shared";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
@@ -21,8 +21,28 @@ export function AdminDiagnosticsPage() {
|
||||
const { token } = useAuth();
|
||||
const [diagnostics, setDiagnostics] = useState<AdminDiagnosticsDto | null>(null);
|
||||
const [backupGuidance, setBackupGuidance] = useState<BackupGuidanceDto | null>(null);
|
||||
const [supportLogs, setSupportLogs] = useState<SupportLogEntryDto[]>([]);
|
||||
const [supportLogData, setSupportLogData] = useState<SupportLogListDto | null>(null);
|
||||
const [status, setStatus] = useState("Loading diagnostics...");
|
||||
const [supportLogLevel, setSupportLogLevel] = useState<"ALL" | SupportLogEntryDto["level"]>("ALL");
|
||||
const [supportLogSource, setSupportLogSource] = useState("ALL");
|
||||
const [supportLogQuery, setSupportLogQuery] = useState("");
|
||||
const [supportLogWindowDays, setSupportLogWindowDays] = useState<"ALL" | "1" | "7" | "14">("ALL");
|
||||
|
||||
function buildSupportLogFilters(): SupportLogFiltersDto {
|
||||
const now = new Date();
|
||||
const start =
|
||||
supportLogWindowDays === "ALL"
|
||||
? undefined
|
||||
: new Date(now.getTime() - Number.parseInt(supportLogWindowDays, 10) * 24 * 60 * 60 * 1000).toISOString();
|
||||
|
||||
return {
|
||||
level: supportLogLevel === "ALL" ? undefined : supportLogLevel,
|
||||
source: supportLogSource === "ALL" ? undefined : supportLogSource,
|
||||
query: supportLogQuery.trim() || undefined,
|
||||
start,
|
||||
limit: 100,
|
||||
};
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) {
|
||||
@@ -31,14 +51,14 @@ export function AdminDiagnosticsPage() {
|
||||
|
||||
let active = true;
|
||||
|
||||
Promise.all([api.getAdminDiagnostics(token), api.getBackupGuidance(token), api.getSupportLogs(token)])
|
||||
Promise.all([api.getAdminDiagnostics(token), api.getBackupGuidance(token), api.getSupportLogs(token, buildSupportLogFilters())])
|
||||
.then(([nextDiagnostics, nextBackupGuidance, nextSupportLogs]) => {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
setDiagnostics(nextDiagnostics);
|
||||
setBackupGuidance(nextBackupGuidance);
|
||||
setSupportLogs(nextSupportLogs);
|
||||
setSupportLogData(nextSupportLogs);
|
||||
setStatus("Diagnostics loaded.");
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
@@ -51,7 +71,7 @@ export function AdminDiagnosticsPage() {
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [token]);
|
||||
}, [token, supportLogLevel, supportLogSource, supportLogQuery, supportLogWindowDays]);
|
||||
|
||||
if (!diagnostics || !backupGuidance) {
|
||||
return <div className="rounded-[20px] border border-line/70 bg-surface/90 p-4 text-sm text-muted shadow-panel">{status}</div>;
|
||||
@@ -62,7 +82,7 @@ export function AdminDiagnosticsPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
const snapshot = await api.getSupportSnapshot(token);
|
||||
const snapshot = await api.getSupportSnapshotWithFilters(token, buildSupportLogFilters());
|
||||
const blob = new Blob([JSON.stringify(snapshot, null, 2)], { type: "application/json" });
|
||||
const objectUrl = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
@@ -78,7 +98,7 @@ export function AdminDiagnosticsPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
const logs = await api.getSupportLogs(token);
|
||||
const logs = await api.getSupportLogs(token, buildSupportLogFilters());
|
||||
const blob = new Blob([JSON.stringify(logs, null, 2)], { type: "application/json" });
|
||||
const objectUrl = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
@@ -86,15 +106,20 @@ export function AdminDiagnosticsPage() {
|
||||
link.download = `mrp-codex-support-logs-${new Date().toISOString().replace(/[:.]/g, "-")}.json`;
|
||||
link.click();
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(objectUrl), 60_000);
|
||||
setSupportLogs(logs);
|
||||
setSupportLogData(logs);
|
||||
setStatus("Support logs exported.");
|
||||
}
|
||||
|
||||
const supportLogs = supportLogData?.entries ?? [];
|
||||
const supportLogSummary = supportLogData?.summary;
|
||||
const supportLogSources = supportLogData?.availableSources ?? [];
|
||||
|
||||
const summaryCards = [
|
||||
["Server time", formatDateTime(diagnostics.serverTime)],
|
||||
["Node runtime", diagnostics.nodeVersion],
|
||||
["Audit events", diagnostics.auditEventCount.toString()],
|
||||
["Support logs", diagnostics.supportLogCount.toString()],
|
||||
["Retention", `${supportLogSummary?.retentionDays ?? 0} days`],
|
||||
["Active users", `${diagnostics.activeUserCount} / ${diagnostics.userCount}`],
|
||||
["Sessions to review", diagnostics.reviewSessionCount.toString()],
|
||||
["Sales docs", diagnostics.salesDocumentCount.toString()],
|
||||
@@ -290,7 +315,52 @@ export function AdminDiagnosticsPage() {
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Support Logs</p>
|
||||
<h3 className="mt-2 text-lg font-bold text-text">Recent runtime warnings and failures</h3>
|
||||
</div>
|
||||
<p className="text-sm text-muted">{supportLogs.length} entries loaded</p>
|
||||
<p className="text-sm text-muted">
|
||||
{supportLogSummary ? `${supportLogSummary.filteredCount} of ${supportLogSummary.totalCount} entries` : "No entries loaded"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-3 md:grid-cols-2 xl:grid-cols-5">
|
||||
<label className="block">
|
||||
<span className="mb-2 block text-sm font-semibold text-text">Search</span>
|
||||
<input
|
||||
value={supportLogQuery}
|
||||
onChange={(event) => setSupportLogQuery(event.target.value)}
|
||||
placeholder="Message, source, context"
|
||||
className="w-full rounded-2xl border border-line/70 bg-page px-3 py-2 text-sm text-text outline-none"
|
||||
/>
|
||||
</label>
|
||||
<label className="block">
|
||||
<span className="mb-2 block text-sm font-semibold text-text">Level</span>
|
||||
<select value={supportLogLevel} onChange={(event) => setSupportLogLevel(event.target.value as "ALL" | SupportLogEntryDto["level"])} className="w-full rounded-2xl border border-line/70 bg-page px-3 py-2 text-sm text-text outline-none">
|
||||
<option value="ALL">All levels</option>
|
||||
<option value="ERROR">Error</option>
|
||||
<option value="WARN">Warn</option>
|
||||
<option value="INFO">Info</option>
|
||||
</select>
|
||||
</label>
|
||||
<label className="block">
|
||||
<span className="mb-2 block text-sm font-semibold text-text">Source</span>
|
||||
<select value={supportLogSource} onChange={(event) => setSupportLogSource(event.target.value)} className="w-full rounded-2xl border border-line/70 bg-page px-3 py-2 text-sm text-text outline-none">
|
||||
<option value="ALL">All sources</option>
|
||||
{supportLogSources.map((source) => (
|
||||
<option key={source} value={source}>{source}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="block">
|
||||
<span className="mb-2 block text-sm font-semibold text-text">Window</span>
|
||||
<select value={supportLogWindowDays} onChange={(event) => setSupportLogWindowDays(event.target.value as "ALL" | "1" | "7" | "14")} className="w-full rounded-2xl border border-line/70 bg-page px-3 py-2 text-sm text-text outline-none">
|
||||
<option value="ALL">All retained</option>
|
||||
<option value="1">Last 24 hours</option>
|
||||
<option value="7">Last 7 days</option>
|
||||
<option value="14">Last 14 days</option>
|
||||
</select>
|
||||
</label>
|
||||
<div className="rounded-2xl border border-line/70 bg-page/70 px-3 py-3 text-sm text-muted">
|
||||
<div>Errors: {supportLogSummary?.levelCounts.ERROR ?? 0}</div>
|
||||
<div>Warnings: {supportLogSummary?.levelCounts.WARN ?? 0}</div>
|
||||
<div>Info: {supportLogSummary?.levelCounts.INFO ?? 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-line/70 text-sm">
|
||||
@@ -322,6 +392,13 @@ export function AdminDiagnosticsPage() {
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
{supportLogs.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={5} className="px-3 py-6 text-center text-sm text-muted">
|
||||
No support logs matched the current filters.
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user