Fix MVP audit evidence pipeline
This commit is contained in:
@@ -38,9 +38,40 @@ type SkillAwareAudit = {
|
||||
internalSummary?: string | null;
|
||||
};
|
||||
|
||||
type CheckedPageScreenshot = {
|
||||
id: Id<"_storage">;
|
||||
url: string;
|
||||
viewport: "desktop" | "mobile";
|
||||
sourceUrl: string;
|
||||
width: number;
|
||||
height: number;
|
||||
createdAt: number;
|
||||
};
|
||||
|
||||
type CheckedPageEvidence = {
|
||||
url: string;
|
||||
sourceUrl: string | null;
|
||||
finalUrl: string | null;
|
||||
pageKind: string | null;
|
||||
title: string | null;
|
||||
metaDescription: string | null;
|
||||
headings: string[];
|
||||
visibleTextExcerpt: string | null;
|
||||
hasContactFormSignal: boolean | null;
|
||||
hasContactCtaSignal: boolean | null;
|
||||
usesHttps: boolean | null;
|
||||
missingMetaDescription: boolean | null;
|
||||
brokenInternalLinkCount: number | null;
|
||||
screenshots: CheckedPageScreenshot[];
|
||||
createdAt: number | null;
|
||||
};
|
||||
|
||||
type AuditDetailResult = {
|
||||
audit: SkillAwareAudit;
|
||||
lead: LeadContext | null;
|
||||
sourceSummaries: {
|
||||
checkedPages: CheckedPageEvidence[];
|
||||
};
|
||||
} | null;
|
||||
|
||||
const statusText: Record<string, string> = {
|
||||
@@ -54,6 +85,42 @@ function getStatusLabel(status: SkillAwareAudit["status"]) {
|
||||
return statusText[status] ?? "Unbekannt";
|
||||
}
|
||||
|
||||
function getPageKindLabel(pageKind: string | null) {
|
||||
const labels: Record<string, string> = {
|
||||
contact: "Kontakt",
|
||||
homepage: "Startseite",
|
||||
imprint: "Impressum",
|
||||
other: "Unterseite",
|
||||
service: "Leistung",
|
||||
};
|
||||
|
||||
return pageKind ? labels[pageKind] ?? pageKind : "Geprüft";
|
||||
}
|
||||
|
||||
function signalText(value: boolean | null, positive: string, negative: string) {
|
||||
if (value === null) {
|
||||
return "Unbekannt";
|
||||
}
|
||||
|
||||
return value ? positive : negative;
|
||||
}
|
||||
|
||||
function metaSignalText(page: CheckedPageEvidence) {
|
||||
if (page.metaDescription) {
|
||||
return "Vorhanden";
|
||||
}
|
||||
|
||||
if (page.missingMetaDescription === true) {
|
||||
return "Fehlt";
|
||||
}
|
||||
|
||||
if (page.missingMetaDescription === false) {
|
||||
return "Vorhanden";
|
||||
}
|
||||
|
||||
return "Unbekannt";
|
||||
}
|
||||
|
||||
function leadSummary(lead: LeadContext | null | undefined) {
|
||||
if (!lead) {
|
||||
return "Kein Lead-Kontext gespeichert";
|
||||
@@ -90,6 +157,10 @@ export function AuditDetail({ id }: { id: string | Id<"audits"> }) {
|
||||
const lead = result?.lead;
|
||||
|
||||
const usedSkills = useMemo(() => audit?.usedSkills ?? [], [audit]);
|
||||
const checkedPageEvidence = useMemo(
|
||||
() => result?.sourceSummaries.checkedPages ?? [],
|
||||
[result],
|
||||
);
|
||||
|
||||
if (result === null) {
|
||||
return (
|
||||
@@ -149,6 +220,89 @@ export function AuditDetail({ id }: { id: string | Id<"audits"> }) {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Geprüfte Seiten</CardTitle>
|
||||
<CardDescription>
|
||||
Kompakte Evidence aus Website-Enrichment und Screenshot-Erfassung.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{checkedPageEvidence.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">Keine Seiten-Evidence gespeichert</p>
|
||||
) : (
|
||||
<ul className="grid gap-3">
|
||||
{checkedPageEvidence.map((page, index) => (
|
||||
<li
|
||||
className="rounded-md border p-3 text-sm"
|
||||
key={`${page.url}-${index}`}
|
||||
>
|
||||
<div className="flex flex-wrap items-start justify-between gap-2">
|
||||
<div className="min-w-0">
|
||||
<p className="break-words font-medium">
|
||||
{page.title ?? page.finalUrl ?? page.sourceUrl ?? page.url}
|
||||
</p>
|
||||
<p className="mt-1 break-all text-xs text-muted-foreground">
|
||||
{page.finalUrl ?? page.sourceUrl ?? page.url}
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="outline">{getPageKindLabel(page.pageKind)}</Badge>
|
||||
</div>
|
||||
|
||||
{page.visibleTextExcerpt ? (
|
||||
<p className="mt-3 line-clamp-3 text-sm text-muted-foreground">
|
||||
{page.visibleTextExcerpt}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<Badge variant={page.missingMetaDescription === true ? "secondary" : "outline"}>
|
||||
Meta: {metaSignalText(page)}
|
||||
</Badge>
|
||||
<Badge variant="outline">
|
||||
Kontaktformular:{" "}
|
||||
{signalText(page.hasContactFormSignal, "Signal", "Kein Signal")}
|
||||
</Badge>
|
||||
<Badge variant="outline">
|
||||
CTA: {signalText(page.hasContactCtaSignal, "Signal", "Kein Signal")}
|
||||
</Badge>
|
||||
<Badge variant="outline">
|
||||
Interne Links: {page.brokenInternalLinkCount ?? "Unbekannt"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{page.screenshots.length > 0 ? (
|
||||
<div className="mt-3 grid gap-2 sm:grid-cols-2">
|
||||
{page.screenshots.map((screenshot) => (
|
||||
<figure
|
||||
className="overflow-hidden rounded-md border bg-muted/20"
|
||||
key={`${screenshot.id}-${screenshot.viewport}`}
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={screenshot.url}
|
||||
alt={`${screenshot.viewport === "desktop" ? "Desktop" : "Mobile"} Screenshot von ${screenshot.sourceUrl}`}
|
||||
width={screenshot.width}
|
||||
height={screenshot.height}
|
||||
className="aspect-[16/10] w-full object-cover"
|
||||
/>
|
||||
<figcaption className="flex items-center justify-between gap-2 border-t px-2 py-1 text-xs text-muted-foreground">
|
||||
<span className="font-medium">
|
||||
{screenshot.viewport === "desktop" ? "Desktop" : "Mobil"}
|
||||
</span>
|
||||
<span className="truncate">{screenshot.sourceUrl}</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Verwendete Skills</CardTitle>
|
||||
|
||||
Reference in New Issue
Block a user