← back to Homesonspec
apps/admin/src/app/validation/page.tsx
61 lines
import { prisma } from "@homesonspec/database";
import { CreatedChip } from "@homesonspec/shared-ui";
export const dynamic = "force-dynamic";
export default async function ValidationLogPage({
searchParams,
}: {
searchParams: Promise<{ failed?: string }>;
}) {
const { failed } = await searchParams;
const onlyFailed = failed === "1";
const events = await prisma.validationEvent.findMany({
where: onlyFailed ? { passed: false } : {},
orderBy: { runAt: "desc" },
take: 100,
include: { stagedRecord: { select: { entityType: true, canonicalKey: true } } },
});
return (
<div>
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold">Validation events</h1>
<a href={onlyFailed ? "/validation" : "/validation?failed=1"} className="text-sm text-teal-700 underline">
{onlyFailed ? "Show all" : "Failed only"}
</a>
</div>
<div className="mt-4 overflow-x-auto rounded-xl border border-neutral-200 bg-white shadow-sm">
<table className="w-full text-left text-sm">
<thead className="border-b border-neutral-200 text-xs uppercase text-neutral-500">
<tr>
<th className="px-3 py-2">Rule</th>
<th className="px-3 py-2">Severity</th>
<th className="px-3 py-2">Result</th>
<th className="px-3 py-2">Entity</th>
<th className="px-3 py-2">Message</th>
<th className="px-3 py-2">Run</th>
</tr>
</thead>
<tbody>
{events.map((event) => (
<tr key={event.id} className="border-b border-neutral-100">
<td className="px-3 py-1.5 font-mono text-xs">{event.ruleId}</td>
<td className="px-3 py-1.5">{event.severity}</td>
<td className="px-3 py-1.5">
<span className={event.passed ? "text-emerald-700" : "font-semibold text-red-700"}>
{event.passed ? "pass" : "FAIL"}
</span>
</td>
<td className="px-3 py-1.5 text-xs">{event.stagedRecord.entityType}</td>
<td className="px-3 py-1.5 text-xs text-neutral-600">{event.message ?? "—"}</td>
<td className="px-3 py-1.5"><CreatedChip date={event.runAt} /></td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}