feat: Fehler-melden + Datenherkunft auf Kontakte & Würfe erweitern
- Kontakt-Detailseite: „Fehler melden"- und „Datenherkunft"-Button. - Wurf-Ansicht: „Datenherkunft"-Button (Feedback war bereits vorhanden). - Feedback-Entity um loses, nullable ContactId erweitert (kein FK → übersteht Ingest-Wipe); Migration AddFeedbackContactId. - Contact.Provenance + Litter.Provenance (nullable text); Migration AddContactLitterProvenance; im Ingest gemappt und in den DTOs zurückgegeben. - Import: build_entity_provenance() generalisiert; Kontakte (sourceFiles, Züchter/Abnehmer-Hinweise) und Würfe (Wurfchronik vs. Diagramm-rekonstruiert, Geschwister-Merge) erhalten Herkunftsdaten in resolved_import.json. - Frontend: ProvenanceDialog generalisiert (EntityProvenance + entityLabel). Tests erweitert (Ingest-Round-trip Kontakt/Wurf, contact-scoped Feedback übersteht Wipe). dotnet(212)/vitest(129)/playwright(36)/tsc/eslint grün. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,21 +9,23 @@
|
||||
*/
|
||||
import { useEffect } from 'react'
|
||||
import { de } from '../strings/de'
|
||||
import type { GerbilProvenance } from '../api/types'
|
||||
import type { EntityProvenance } from '../api/types'
|
||||
import './provenanceDialog.css'
|
||||
|
||||
interface ProvenanceDialogProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
/** Raw provenance JSON string from Gerbil.provenance (null = no import data). */
|
||||
/** Raw provenance JSON string from the entity (Gerbil/Contact/Litter; null = no import data). */
|
||||
provenance?: string | null
|
||||
/** Optional entity label (e.g. "dieses Tiers", "dieses Kontakts") for the intro line. */
|
||||
entityLabel?: string
|
||||
}
|
||||
|
||||
/** Parse the JSON provenance string defensively; null on absence / parse error. */
|
||||
function parseProvenance(raw?: string | null): GerbilProvenance | null {
|
||||
function parseProvenance(raw?: string | null): EntityProvenance | null {
|
||||
if (!raw || !raw.trim()) return null
|
||||
try {
|
||||
const p = JSON.parse(raw) as Partial<GerbilProvenance>
|
||||
const p = JSON.parse(raw) as Partial<EntityProvenance>
|
||||
return {
|
||||
sourceFiles: Array.isArray(p.sourceFiles) ? p.sourceFiles : [],
|
||||
mergedRecordCount: typeof p.mergedRecordCount === 'number' ? p.mergedRecordCount : 0,
|
||||
@@ -37,7 +39,7 @@ function parseProvenance(raw?: string | null): GerbilProvenance | null {
|
||||
}
|
||||
}
|
||||
|
||||
export default function ProvenanceDialog({ open, onClose, provenance }: ProvenanceDialogProps) {
|
||||
export default function ProvenanceDialog({ open, onClose, provenance, entityLabel }: ProvenanceDialogProps) {
|
||||
// Close on Escape (only while mounted).
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
@@ -84,7 +86,7 @@ export default function ProvenanceDialog({ open, onClose, provenance }: Provenan
|
||||
<p className="provenance__empty">{t.empty}</p>
|
||||
) : (
|
||||
<>
|
||||
<p className="provenance__intro">{t.intro}</p>
|
||||
<p className="provenance__intro">{entityLabel ? t.introFor(entityLabel) : t.intro}</p>
|
||||
|
||||
<section className="provenance__section">
|
||||
<div className="provenance__section-title">
|
||||
|
||||
Reference in New Issue
Block a user