chore: synchronize local fixes to gitea

This commit is contained in:
2026-03-14 00:25:56 +00:00
commit b4ffe72b3e
393 changed files with 71657 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
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>
);
};