import { useCallback, useMemo, useState } from 'react'; import { Alert, Badge, Button, Divider, Group, Loader, Stack, Text, SegmentedControl } from '@mantine/core'; import { useTranslation } from 'react-i18next'; import type { SignatureValidationReportEntry } from '../../../types/validateSignature'; import type { ValidateSignatureOperationHook } from '../../../hooks/tools/validateSignature/useValidateSignatureOperation'; import './reportView/styles.css'; import FitText from '../../shared/FitText'; import { SuggestedToolsSection } from '../shared/SuggestedToolsSection'; interface ValidateSignatureResultsProps { operation: ValidateSignatureOperationHook; results: SignatureValidationReportEntry[]; isLoading: boolean; errorMessage: string | null; reportAvailable?: boolean; } const useFileSummary = (results: SignatureValidationReportEntry[]) => { return useMemo(() => { if (results.length === 0) { return { fileCount: 0, signatureCount: 0, fullyValidCount: 0 }; } let signatureCount = 0; let fullyValidCount = 0; results.forEach((result) => { signatureCount += result.signatures.length; result.signatures.forEach((signature) => { const isValid = signature.valid; if (isValid) { fullyValidCount += 1; } }); }); return { fileCount: results.length, signatureCount, fullyValidCount, }; }, [results]); }; const findFileByExtension = (files: File[], extension: string) => { return files.find((file) => file.name.toLowerCase().endsWith(extension)); }; const ValidateSignatureResults = ({ operation, results, isLoading, errorMessage, }: ValidateSignatureResultsProps) => { const { t } = useTranslation(); const summary = useFileSummary(results); const pdfFile = useMemo(() => findFileByExtension(operation.files, '.pdf'), [operation.files]); const csvFile = useMemo(() => findFileByExtension(operation.files, '.csv'), [operation.files]); const jsonFile = useMemo(() => findFileByExtension(operation.files, '.json'), [operation.files]); const [selectedType, setSelectedType] = useState<'pdf' | 'csv' | 'json'>('pdf'); const selectedFile = useMemo(() => { if (selectedType === 'pdf') return pdfFile ?? null; if (selectedType === 'csv') return csvFile ?? null; return jsonFile ?? null; }, [selectedType, pdfFile, csvFile, jsonFile]); const selectedDownloadLabel = useMemo(() => { if (selectedType === 'pdf') return t('validateSignature.downloadPdf', 'Download PDF Report'); if (selectedType === 'csv') return t('validateSignature.downloadCsv', 'Download CSV'); return t('validateSignature.downloadJson', 'Download JSON'); }, [selectedType, t]); const downloadTypeOptions = [ { label: t('validateSignature.downloadType.pdf', 'PDF'), value: 'pdf' }, { label: t('validateSignature.downloadType.csv', 'CSV'), value: 'csv' }, { label: t('validateSignature.downloadType.json', 'JSON'), value: 'json' }, ]; const handleDownload = useCallback((file: File) => { const blobUrl = URL.createObjectURL(file); const link = document.createElement('a'); link.href = blobUrl; link.download = file.name; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(blobUrl); }, []); // Show the big loader only while we're still waiting for the first results. if (isLoading && results.length === 0) { return ( {t('validateSignature.processing', 'Validating signatures...')} ); } if (!isLoading && results.length === 0) { return ( {t('validateSignature.noResults', 'Run the validation to generate a report.')} ); } return ( {/* While results are visible but background work continues (e.g. generating files), show a light inline indicator without blocking downloads UI. */} {isLoading && results.length > 0 && ( {t('validateSignature.finalizing', 'Preparing downloads...')} )} {errorMessage && ( {errorMessage} )} {t('validateSignature.report.filesEvaluated', '{{count}} files evaluated', { count: summary.fileCount, })} {t('validateSignature.report.signaturesFound', '{{count}} signatures detected', { count: summary.signatureCount, })} {summary.signatureCount > 0 && ( {t('validateSignature.report.signaturesValid', '{{count}} fully valid', { count: summary.fullyValidCount, })} )} {results.map((result) => { const hasError = Boolean(result.error); const hasSignatures = result.signatures.length > 0; const allValid = hasSignatures && result.signatures.every((signature) => signature.valid); const badgeLabel = hasError ? t('validateSignature.status.invalid', 'Invalid') : hasSignatures ? allValid ? t('validateSignature.status.valid', 'Valid') : t('validateSignature.status.invalid', 'Invalid') : t('validateSignature.noSignaturesShort', 'No signatures'); const badgeClass = hasError ? 'status-badge status-badge--invalid' : hasSignatures ? allValid ? 'status-badge status-badge--valid' : 'status-badge status-badge--warning' : 'status-badge status-badge--neutral'; return (
{badgeLabel}
{t('validateSignature.report.signatureCountLabel', '{{count}} signatures', { count: result.signatures.length, })} {!result.error && result.signatures.length === 0 && ( {t('validateSignature.noSignatures', 'No digital signatures found in this document')} )}
); })}
{t('validateSignature.report.downloads', 'Downloads')} setSelectedType(v as 'pdf' | 'csv' | 'json')} data={downloadTypeOptions} /> {selectedType === 'pdf' && !pdfFile && ( {t('validateSignature.report.noPdf', 'PDF report will be available after a successful validation.')} )}
); }; export default ValidateSignatureResults;