← back to Homesonspec
apps/admin/src/app/sources/page.tsx
50 lines
import { prisma } from "@homesonspec/database";
import { CreatedChip } from "@homesonspec/shared-ui";
export const dynamic = "force-dynamic";
export default async function SourcesPage() {
const sources = await prisma.sourceRegistry.findMany({
orderBy: { key: "asc" },
include: { _count: { select: { snapshots: true, staged: true, homes: true } } },
});
return (
<div>
<h1 className="text-2xl font-bold">Source registry</h1>
<p className="mt-1 text-sm text-neutral-500">
Every source records its collection method, permissions, media rights, rate limits, and health.
Publishing stops automatically when a source degrades.
</p>
<div className="mt-4 grid gap-4 md:grid-cols-2">
{sources.map((source) => (
<div key={source.id} className="rounded-xl border border-neutral-200 bg-white p-4 shadow-sm" data-testid="source-card">
<div className="flex items-center justify-between">
<h2 className="font-semibold">{source.name}</h2>
<span className={`rounded-full px-2 py-0.5 text-xs font-medium ${
source.health === "HEALTHY" ? "bg-emerald-100 text-emerald-800" :
source.health === "DEGRADED" ? "bg-amber-100 text-amber-800" :
source.health === "PAUSED" ? "bg-neutral-200 text-neutral-600" :
"bg-red-100 text-red-800"}`}>
{source.health}
</span>
</div>
<dl className="mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-xs">
<div><dt className="text-neutral-500">Key</dt><dd className="font-mono">{source.key}</dd></div>
<div><dt className="text-neutral-500">Method</dt><dd>{source.collectionMethod}</dd></div>
<div><dt className="text-neutral-500">Media rights</dt><dd>{source.mediaRights}</dd></div>
<div><dt className="text-neutral-500">Rate limit</dt><dd>{source.rateLimitRpm ? `${source.rateLimitRpm}/min` : "n/a"}</dd></div>
<div><dt className="text-neutral-500">Fresh interval</dt><dd>{source.freshIntervalHours}h</dd></div>
<div><dt className="text-neutral-500">Aging interval</dt><dd>{source.agingIntervalHours}h</dd></div>
<div><dt className="text-neutral-500">Snapshots</dt><dd>{source._count.snapshots}</dd></div>
<div><dt className="text-neutral-500">Staged / homes</dt><dd>{source._count.staged} / {source._count.homes}</dd></div>
</dl>
{source.notes && <p className="mt-2 text-xs text-neutral-500">{source.notes}</p>}
<div className="mt-2"><CreatedChip date={source.createdAt} /></div>
</div>
))}
</div>
</div>
);
}