- Added support for Google Gemini, OpenAI, Anthropic, and Azure OpenAI - Implemented API key validation with auto model detection - Added Error Boundary for better error handling - Migrated PDF generation to native jsPDF (better quality) - Added PWA support with offline capabilities - Implemented tests with Vitest - Fixed language consistency (PT-BR) - Improved accessibility (ARIA)
111 lines
5.6 KiB
TypeScript
111 lines
5.6 KiB
TypeScript
import React from 'react';
|
|
import type { ReportData, ComplianceItem } from '../types';
|
|
|
|
interface PrintableReportProps {
|
|
report: ReportData;
|
|
}
|
|
|
|
const formatDate = (dateString: string) => {
|
|
// Tries to parse common date formats like DD.MM.YYYY or YYYY-MM-DD
|
|
const parts = dateString.split(/[.\-/]/);
|
|
if (parts.length === 3) {
|
|
// Assuming DD.MM.YYYY
|
|
if (parts[0].length === 2) return `${parts[0]}/${parts[1]}/${parts[2]}`;
|
|
// Assuming YYYY-MM-DD
|
|
if (parts[0].length === 4) return `${parts[2]}/${parts[1]}/${parts[0]}`;
|
|
}
|
|
return dateString; // fallback
|
|
};
|
|
|
|
const TableRow: React.FC<{item: ComplianceItem}> = ({item}) => (
|
|
<tr className="border-b text-xs">
|
|
<td className="py-1 px-2 font-medium">{item.property || item.element || item.test}</td>
|
|
<td className="py-1 px-2">{item.norm}</td>
|
|
<td className="py-1 px-2 font-bold">{item.certificate}</td>
|
|
<td className={`py-1 px-2 font-bold ${item.status === 'OK' ? 'text-green-700' : 'text-red-700'}`}>{item.status}</td>
|
|
</tr>
|
|
)
|
|
|
|
export const PrintableReport: React.FC<PrintableReportProps> = ({ report }) => {
|
|
const { identification, compliance, overPerformance, equivalents, confidence } = report;
|
|
const jsonData = JSON.stringify(report);
|
|
|
|
return (
|
|
<div
|
|
id="printable-report-container"
|
|
data-report={jsonData}
|
|
style={{ width: '210mm', minHeight: '297mm' }}
|
|
className="bg-white text-gray-900 p-8 font-sans flex flex-col"
|
|
>
|
|
<header className="flex justify-between items-start pb-2 border-b-2 border-gray-400">
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-blue-800">SteelBase</h1>
|
|
<p className="text-sm">Análise de Qualidade Industrial com IA</p>
|
|
</div>
|
|
<div className="text-right">
|
|
<h2 className="text-2xl font-bold">Relatório de Análise Técnica</h2>
|
|
<p className="text-sm">Documento gerado em: {new Date().toLocaleDateString('pt-BR')}</p>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="flex-grow">
|
|
<section className="mt-4 border border-gray-300 rounded p-3 grid grid-cols-2 gap-x-6 gap-y-2 text-sm">
|
|
<div><strong>Produto:</strong> {identification.product}</div>
|
|
<div><strong>Norma Principal:</strong> {identification.standards}</div>
|
|
<div><strong>Fabricante:</strong> {identification.manufacturer}</div>
|
|
<div><strong>Nº Certificado:</strong> {identification.certificateNumber}</div>
|
|
<div><strong>Lotes:</strong> {identification.batches}</div>
|
|
<div><strong>Corridas:</strong> {identification.heats}</div>
|
|
<div><strong>Quantidade:</strong> {identification.quantity}</div>
|
|
<div><strong>Data Emissão:</strong> {formatDate(identification.certificateDate)}</div>
|
|
</section>
|
|
|
|
<section className="mt-4">
|
|
<h3 className="text-base font-bold text-gray-800">Resumo da Análise</h3>
|
|
<div className="mt-1 border border-gray-300 rounded p-2 text-xs">
|
|
<p>O material de {identification.product} atende aos requisitos da norma {identification.standards}. Status Geral de Conformidade: <span className={`font-extrabold ${compliance.status === 'CONFORME' ? 'text-green-700' : 'text-red-700'}`}>{compliance.status}</span>.</p>
|
|
{compliance.status === 'CONFORME' && overPerformance.length > 0 && (
|
|
<p className="mt-1">O material excede os requisitos normativos em pontos-chave, como {overPerformance.map(item => item.property).join(', ')}.</p>
|
|
)}
|
|
</div>
|
|
</section>
|
|
|
|
<section className="mt-4">
|
|
<h3 className="text-base font-bold text-gray-800">Análise de Composição Química</h3>
|
|
<table className="w-full mt-1 border-collapse border border-gray-300">
|
|
<thead className="bg-gray-200 text-left text-xs uppercase">
|
|
<tr><th className="py-1 px-2">Elemento</th><th className="py-1 px-2">Norma</th><th className="py-1 px-2">Certificado</th><th className="py-1 px-2">Status</th></tr>
|
|
</thead>
|
|
<tbody>{compliance.chemical.map((item, i) => <TableRow key={i} item={item} />)}</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section className="mt-4">
|
|
<h3 className="text-base font-bold text-gray-800">Análise de Propriedades Mecânicas</h3>
|
|
<table className="w-full mt-1 border-collapse border border-gray-300">
|
|
<thead className="bg-gray-200 text-left text-xs uppercase">
|
|
<tr><th className="py-1 px-2">Propriedade</th><th className="py-1 px-2">Norma</th><th className="py-1 px-2">Certificado</th><th className="py-1 px-2">Status</th></tr>
|
|
</thead>
|
|
<tbody>{compliance.mechanical.map((item, i) => <TableRow key={i} item={item} />)}</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section className="mt-4">
|
|
<h3 className="text-base font-bold text-gray-800">Normas Equivalentes</h3>
|
|
<div className="mt-1 grid grid-cols-4 gap-2">
|
|
{equivalents.map((item, i) => (
|
|
<div key={i} className="bg-gray-100 border border-gray-300 rounded p-2 text-center">
|
|
<p className="text-xs font-semibold text-gray-600">{item.system}</p>
|
|
<p className="text-sm font-bold text-blue-800">{item.norm}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer className="text-center text-xs text-gray-500 pt-2 border-t border-gray-300">
|
|
Relatório gerado por SteelBase | Confiança da Análise: {confidence}%
|
|
</footer>
|
|
</div>
|
|
);
|
|
}; |