"use client"; import { useMemo } from "react"; import { useQuery } from "convex/react"; import type { Id } from "@/convex/_generated/dataModel"; import { api } from "@/convex/_generated/api"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Globe } from "lucide-react"; type UsedSkill = { id?: string; name: string; purpose?: string; category?: string; source?: string; version?: string; }; type SkillSummary = { name: string; purpose: string; summary: string; }; type LeadContext = { _id: Id<"leads">; companyName?: string; websiteDomain?: string; websiteUrl?: string; city?: string; niche?: string; }; type SkillAwareAudit = { _id: Id<"audits">; slug: string; checkedDomain: string; status: "draft" | "approved" | "published" | "deactivated"; checkedPages: string[]; createdAt?: number; updatedAt?: number; usedSkills?: UsedSkill[]; skillSummaries?: SkillSummary[]; internalSummary?: string | null; }; type AuditFindingEvidenceRef = { id: string; type: | "crawl_page" | "technical_check" | "screenshot" | "pagespeed" | "jina_excerpt" | "generation_stage"; label: string; sourceUrl?: string; }; type AuditFinding = { _id: string; skillId: string; claim: string; recommendation: string; customerBenefit: string; severity: 1 | 2 | 3; confidence: number; evidenceRefs: AuditFindingEvidenceRef[]; reviewStatus: "pending" | "accepted" | "rejected"; }; 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; findings: AuditFinding[]; sourceSummaries: { checkedPages: CheckedPageEvidence[]; }; } | null; const statusText: Record = { draft: "Entwurf", approved: "Freigegeben", published: "Veröffentlicht", deactivated: "Deaktiviert", }; function getStatusLabel(status: SkillAwareAudit["status"]) { return statusText[status] ?? "Unbekannt"; } function getPageKindLabel(pageKind: string | null) { const labels: Record = { 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 evidenceTypeLabel(type: AuditFindingEvidenceRef["type"]) { const labels: Record = { crawl_page: "Crawl", technical_check: "Technik", screenshot: "Screenshot", pagespeed: "PageSpeed", jina_excerpt: "Reader", generation_stage: "KI-Stufe", }; return labels[type] ?? type; } function leadSummary(lead: LeadContext | null | undefined) { if (!lead) { return "Kein Lead-Kontext gespeichert"; } const detail = [lead.city, lead.niche].filter(Boolean).join(" • "); let leadDomain = lead.websiteDomain ?? "—"; if (!leadDomain && lead.websiteUrl) { try { leadDomain = new URL(lead.websiteUrl).hostname; } catch { leadDomain = lead.websiteUrl; } } return ( <>

{lead.companyName ?? "Lead ohne Name"}

{detail || "Kein Kontext textlich"}

{leadDomain}

); } export function AuditDetail({ id }: { id: string | Id<"audits"> }) { const result = useQuery(api.audits.getDetail, { id: id as Id<"audits">, }) as AuditDetailResult | undefined; const audit = result?.audit; const lead = result?.lead; const usedSkills = useMemo(() => { const summaries = audit?.skillSummaries ?? []; return (audit?.usedSkills ?? []).map((skill) => { const summary = summaries.find( (candidate) => candidate.name === skill.name || candidate.name === skill.id, ); return { ...skill, purpose: summary?.purpose ?? skill.purpose, summary: summary?.summary, }; }); }, [audit]); const findings = useMemo(() => result?.findings ?? [], [result]); const checkedPageEvidence = useMemo( () => result?.sourceSummaries.checkedPages ?? [], [result], ); if (result === null) { return ( Audit nicht gefunden Der gewünschte Audit-Datensatz konnte nicht geladen werden. ); } if (audit === undefined) { return ( Audit wird geladen... ); } return (
Audit-Detail #{audit.slug}

{audit.checkedDomain}

Status

{getStatusLabel(audit.status)}

Seitenanzahl

{audit.checkedPages.length}

Lead-Kontext

{leadSummary(lead)}
{audit.internalSummary ? (

Interne Notiz

{audit.internalSummary}

) : null}
Geprüfte Befunde Verifizierte Aussagen mit konkreten Belegen aus Crawl, Screenshots und Messungen. {findings.length === 0 ? (

Noch keine verifizierten Befunde gespeichert.

) : (
    {findings.map((finding) => (
  • {finding.claim}

    {finding.customerBenefit}

    Priorität {finding.severity} {Math.round(finding.confidence * 100)}% sicher

    Empfehlung: {finding.recommendation}

    {finding.evidenceRefs.map((ref) => ( Quelle: {evidenceTypeLabel(ref.type)} · {ref.label} ))}
  • ))}
)}
Geprüfte Seiten Kompakte Evidence aus Website-Enrichment und Screenshot-Erfassung. {checkedPageEvidence.length === 0 ? (

Keine Seiten-Evidence gespeichert

) : (
    {checkedPageEvidence.map((page, index) => (
  • {page.title ?? page.finalUrl ?? page.sourceUrl ?? page.url}

    {page.finalUrl ?? page.sourceUrl ?? page.url}

    {getPageKindLabel(page.pageKind)}
    {page.visibleTextExcerpt ? (

    {page.visibleTextExcerpt}

    ) : null}
    Meta: {metaSignalText(page)} Kontaktformular:{" "} {signalText(page.hasContactFormSignal, "Signal", "Kein Signal")} CTA: {signalText(page.hasContactCtaSignal, "Signal", "Kein Signal")} Interne Links: {page.brokenInternalLinkCount ?? "Unbekannt"}
    {page.screenshots.length > 0 ? (
    {page.screenshots.map((screenshot) => (
    {/* eslint-disable-next-line @next/next/no-img-element */} {`${screenshot.viewport
    {screenshot.viewport === "desktop" ? "Desktop" : "Mobil"} {screenshot.sourceUrl}
    ))}
    ) : null}
  • ))}
)}
Verwendete Skills Skills, die an diesem Audit beteiligt wurden. {usedSkills.length === 0 ? (

Keine Skills gespeichert

) : (
    {usedSkills.map((skill, index) => (
  • {skill.name}

    {skill.purpose ?? "Keine Zweckbeschreibung"}

    {"summary" in skill && skill.summary ? (

    {skill.summary}

    ) : null}

    {skill.category ? {skill.category} : null} {skill.version ? {skill.version} : null} {skill.source ? {skill.source} : null}

  • ))}
)}
); }