Consume governed recipients and add operational checks

This commit is contained in:
2026-07-31 22:48:07 +02:00
parent fa4eb39e0b
commit 82ddc0c34c
15 changed files with 354 additions and 14 deletions
@@ -74,6 +74,14 @@ export default function AddressSourceImportDialog({
() => sources.find((source) => source.source_id === selectedSourceId) ?? null,
[selectedSourceId, sources]
);
const exclusionCounts = useMemo(() => {
const counts = new Map<string, number>();
for (const item of snapshot?.excluded ?? []) {
const reason = item.reason_code || item.status || "excluded";
counts.set(reason, (counts.get(reason) ?? 0) + 1);
}
return [...counts.entries()].sort((left, right) => right[1] - left[1]);
}, [snapshot]);
useEffect(() => {
if (filteredSources.some((source) => source.source_id === selectedSourceId)) {
@@ -276,14 +284,26 @@ export default function AddressSourceImportDialog({
</dd>
</div>
<div>
<dt>Recipients</dt>
<dd>{snapshot.recipients.length}</dd>
<dt>Included</dt>
<dd>{snapshot.included_count}</dd>
</div>
<div>
<dt>Excluded</dt>
<dd>{snapshot.excluded_count}</dd>
</div>
<div>
<dt>Revision</dt>
<dd className="mono-small">{snapshot.source_revision}</dd>
</div>
</dl>
{snapshot.excluded_count > 0 && (
<DismissibleAlert tone="warning" compact dismissible={false}>
{snapshot.excluded_count} contact point{snapshot.excluded_count === 1 ? " was" : "s were"} excluded by effective communication-governance facts.
{exclusionCounts.length > 0 && (
<span> {exclusionCounts.map(([reason, count]) => `${reason}: ${count}`).join(" · ")}</span>
)}
</DismissibleAlert>
)}
<AddressSourceRecipientPreviewGrid recipients={snapshot.recipients.slice(0, 20)} />
{snapshot.recipients.length > 20 && (
<p className="muted small-note">
@@ -98,7 +98,27 @@ export function createAddressSourceImportProvenance(
source_id: snapshot.source_id,
source_label: snapshot.source_label,
source_revision: snapshot.source_revision,
source_provenance: snapshot.provenance,
source_provenance: {
...snapshot.provenance,
generated_at: snapshot.generated_at,
purpose: snapshot.purpose,
included_count: snapshot.included_count,
excluded_count: snapshot.excluded_count,
included_decisions: snapshot.recipients.map((recipient) => ({
contact_id: recipient.contact_id,
email: recipient.email,
provenance: recipient.provenance
})),
exclusions: snapshot.excluded.map((item) => ({
contact_id: item.contact_id,
channel: item.channel,
target: item.target,
status: item.status,
reason_code: item.reason_code,
explanation: item.explanation,
provenance: item.provenance
}))
},
filename: null,
sheet_name: null,
encoding: null,