Assessor_2.0/src/components/FindingCard/FindingCard.tsx

59 lines
2.8 KiB
TypeScript

import { memo } from 'react';
import { RelatedFindingRing, useRelatedFindings } from '../../features/related-findings/RelatedFindings';
import type { FindingRecord } from '../../domain/finding';
import { normalizeSeverity } from '../../profiles/purple-knight/presentation';
import { toolIdForRecord } from '../../importers/status-normalization';
import { findingOutcomeMark, resultStatusFieldFor } from '../../features/linked-results/result-message';
import { policyDiagnosticBadges } from '../../profiles/group-policy/categories';
import { highlightText } from '../HighlightText/highlight-text';
export function findingCardStatus(record: FindingRecord, fallbackToolId?: string) {
return resultStatusFieldFor(record, fallbackToolId);
}
interface Props {
record: FindingRecord;
title: string;
bookmarked: boolean;
selected?: boolean;
/** Werkzeug-ID des Datasets; entscheidet ueber die werkzeuggebundene Statusnormalisierung. */
toolId?: string;
search?: string;
onOpen(): void;
}
function FindingCardComponent({ record, title, bookmarked, selected, toolId, search, onOpen }: Props) {
const groupMarked = useRelatedFindings()?.selection?.group.recordIds.includes(record.id);
const severityField = record.fields.find(field => /^(severity|criticality|priority)$/i.test(field.label) && field.value);
const severity = normalizeSeverity(severityField?.value);
const outcomeMark = findingOutcomeMark(record, toolId);
const resolvedToolId = toolIdForRecord(record, toolId);
const diagnostics = policyDiagnosticBadges(record, resolvedToolId);
return (
<div data-record-id={record.id} className={`record-card-with-group${groupMarked ? ' is-group-marked' : ''}`}>
<button
aria-current={selected ? 'true' : undefined}
className={`record-card severity-${severity}${bookmarked ? ' is-workspace' : ''}${selected ? ' active' : ''}`}
type="button"
onClick={onOpen}
>
<span className="record-title">{highlightText(title, search)}{bookmarked && <span className="action-pill">Workspace</span>}</span>
{(outcomeMark || diagnostics.length > 0) && <span className="record-meta-line">
{outcomeMark && <span className="record-meta outcome-mark" data-outcome={outcomeMark.outcome}>{highlightText(outcomeMark.label, search)}</span>}
{diagnostics.map(diagnostic => <span className="record-diagnostic-badge" data-policy-diagnostic={diagnostic.toLowerCase()} key={diagnostic}>{diagnostic}</span>)}
</span>}
</button>
<RelatedFindingRing record={record}/>
</div>
);
}
export const FindingCard = memo(FindingCardComponent, (previous, next) =>
previous.record === next.record &&
previous.title === next.title &&
previous.bookmarked === next.bookmarked &&
previous.selected === next.selected &&
previous.toolId === next.toolId &&
previous.search === next.search
);