Files
GPI/src/client/components/reports/AnalyticalReport.tsx

187 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useRef, useLayoutEffect } from 'react';
import { format, isValid } from 'date-fns';
import type { Project } from '../../types';
import '../../styles/reports.css';
interface AnalyticalReportProps {
project: Project;
logoUrl?: string;
}
const ProgressFill: React.FC<{ progress: number }> = ({ progress }) => {
const fillRef = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
if (fillRef.current) {
fillRef.current.style.setProperty('--progress', `${progress}%`);
}
}, [progress]);
return <div ref={fillRef} className="evol-fill" />;
};
export const AnalyticalReport: React.FC<AnalyticalReportProps> = ({ project, logoUrl }) => {
const inspections = project.inspections || [];
const sumWeight = inspections.reduce((acc, curr) => acc + (curr.weightKg || 0), 0);
const totalWeight = project.weightKg || 0;
const progress = totalWeight > 0 ? Math.min(Math.round((sumWeight / totalWeight) * 100), 100) : 0;
// Período
const startDate = project.startDate ? new Date(project.startDate) : null;
const endDate = project.endDate ? new Date(project.endDate) : null;
const periodStr = (startDate && isValid(startDate) && endDate && isValid(endDate))
? `${format(startDate, 'MM/yyyy')} ${format(endDate, 'MM/yyyy')}`
: '--/----';
return (
<div className="report-container print:block hidden" id="analytical-report">
<header className="report-header">
<div className="brand">
{logoUrl ? (
<img src={logoUrl} alt="Logo" className="brand-logo" />
) : (
<div className="logo-placeholder"></div>
)}
</div>
<div className="text-center">
<div className="brand-title">RELATÓRIO ANALÍTICO DE OBRA</div>
<div className="brand-subtitle">
Detalhamento de Inspeções, Aplicativos e Esquemas de Pintura
</div>
</div>
<div className="meta">
<div><strong>Data:</strong> {format(new Date(), 'dd/MM/yyyy')}</div>
<div><strong>Obra:</strong> {project.name.toUpperCase()}</div>
</div>
</header>
<section className="summary">
<div className="summary-item">
<div className="summary-label">Evolução Real</div>
<div className="summary-value">{progress}%</div>
<div className="evol-bar">
<ProgressFill progress={progress} />
</div>
</div>
<div className="summary-item">
<div className="summary-label">Peso Medido (kgf)</div>
<div className="summary-value">{sumWeight.toLocaleString('pt-BR')}</div>
<div className="summary-sub">de {totalWeight.toLocaleString('pt-BR')} total</div>
</div>
<div className="summary-item">
<div className="summary-label">Responsável</div>
<div className="summary-value text-11pt">{project.technician || '________________'}</div>
<div className="summary-sub">Técnico Encarregado</div>
</div>
<div className="summary-item">
<div className="summary-label">Período de Obra</div>
<div className="summary-value text-11pt">{periodStr}</div>
<div className="summary-sub">Cronograma Previsto</div>
</div>
</section>
<div className="section-title">
<h2>ESQUEMA DE PINTURA REQUERIDO</h2>
<span>Especificação técnica por demão</span>
</div>
<table className="table">
<thead>
<tr>
<th className="w-20">Etapa</th>
<th className="w-40">Produto</th>
<th className="w-20">Cor</th>
<th className="w-20">EPS (μm)</th>
</tr>
</thead>
<tbody>
{project.paintingSchemes?.map((s, idx) => (
<tr key={idx}>
<td className="font-bold uppercase">{s.coat || s.type}</td>
<td>{s.name.toUpperCase()}</td>
<td>{s.color || '--'}</td>
<td className="font-bold">{s.epsMin}-{s.epsMax} μm</td>
</tr>
))}
{(!project.paintingSchemes || project.paintingSchemes.length === 0) && (
<tr><td colSpan={4} className="text-center p-10mm text-gray-muted">Nenhum esquema definido</td></tr>
)}
</tbody>
</table>
<div className="grid-2col">
<div>
<div className="section-title">
<h2>INSPEÇÕES REALIZADAS</h2>
</div>
<table className="table">
<thead>
<tr>
<th className="w-25">Data</th>
<th className="w-40">Peça / Área</th>
<th className="w-20">Peso</th>
<th className="w-15">Status</th>
</tr>
</thead>
<tbody>
{inspections.slice(0, 15).map((insp, idx) => (
<tr key={idx}>
<td>{insp.date ? format(new Date(insp.date), 'dd/MM/yy') : '--'}</td>
<td className="uppercase font-medium text-8pt">{insp.pieceDescription}</td>
<td className="font-bold">{insp.weightKg?.toLocaleString('pt-BR')}</td>
<td>
<span className={`badge ${insp.appearance === 'approved' ? 'badge-ok' : 'badge-err'}`}>
{insp.appearance === 'approved' ? 'OK' : 'REJ'}
</span>
</td>
</tr>
))}
{inspections.length === 0 && (
<tr><td colSpan={4} className="text-center p-5mm text-gray-muted">Sem registros</td></tr>
)}
</tbody>
</table>
</div>
<div>
<div className="section-title">
<h2>REGISTROS DE APLICAÇÃO</h2>
</div>
<table className="table">
<thead>
<tr>
<th className="w-25">Data</th>
<th className="w-35">Etapa</th>
<th className="w-20">EPS Seca</th>
<th className="w-20">Pintor</th>
</tr>
</thead>
<tbody>
{project.applicationRecords?.slice(0, 15).map((record, idx) => (
<tr key={idx}>
<td>{record.date ? format(new Date(record.date), 'dd/MM/yy') : '--'}</td>
<td className="uppercase font-medium text-8pt">{record.coatStage}</td>
<td className="font-bold">{record.dryThicknessCalc || '--'} μm</td>
<td className="uppercase text-7pt">{record.operator?.split(' ')[0]}</td>
</tr>
))}
{(!project.applicationRecords || project.applicationRecords.length === 0) && (
<tr><td colSpan={4} className="text-center p-5mm text-gray-muted">Sem registros</td></tr>
)}
</tbody>
</table>
</div>
</div>
<footer className="report-footer">
<div className="system-title">
SteelPaint - Gestão de Pintura Industrial
</div>
<div>
Gerado em {format(new Date(), 'dd/MM/yyyy')} às {format(new Date(), 'HH:mm')}h
</div>
<div className="sig-group">
<div className="sig-line">Responsável Qualidade<span></span></div>
</div>
</footer>
</div>
);
};