diff --git a/public/conversor_relatorio_estruturas.html b/public/conversor_relatorio_estruturas.html new file mode 100644 index 0000000..8e97b2c --- /dev/null +++ b/public/conversor_relatorio_estruturas.html @@ -0,0 +1,695 @@ + + + + + + Conversor de Lista de Peças PDF para Excel + + + + + + +
+
+
+

Extrator & Conversor de Lista de Peças (Corrigido)

+

Conversão 100% no navegador (Client-Side) com regras ajustadas

+
+
Autônomo / Sem Servidor
+
+ +
+ + + +

Arraste e solte o arquivo PDF da Lista de Peças Estruturada aqui

+
Selecionar PDF do Computador
+ +
+ +
+
+ Aguardando arquivo PDF... +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
OFFaseMarca (Nº Peça)Descrição (Nome)Composto?QuantidadeMaterial PrincipalPerfil Principal (Maior Comp.)Comp. Max (mm)Peso Unit. (kg)Peso Total (kg)Tratamento Superficial
+ Nenhum dado extraído ainda. Carregue um PDF acima. +
+
+ +
+
+ + + + diff --git a/src/components/conversores/AdvanceSteelConverter.tsx b/src/components/conversores/AdvanceSteelConverter.tsx index 7830485..6392d56 100644 --- a/src/components/conversores/AdvanceSteelConverter.tsx +++ b/src/components/conversores/AdvanceSteelConverter.tsx @@ -1,10 +1,10 @@ -import React, { useState, useRef } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; -import { Upload, FileSpreadsheet, Download, RefreshCw, Check, AlertCircle, Terminal, FileText } from 'lucide-react'; +import { Upload, FileSpreadsheet, Download, RefreshCw, Terminal, FileText, Code2, AlertTriangle } from 'lucide-react'; import { toast } from 'sonner'; import * as XLSX from 'xlsx'; @@ -75,26 +75,30 @@ interface PdfJsLib { getDocument: (options: { data: ArrayBuffer }) => { promise: Promise }; } -// Carregador dinâmico do PDF.js -const loadPdfJs = async (): Promise => { +// Carregador robusto do PDF.js +const ensurePdfJs = async (): Promise => { const win = window as unknown as { pdfjsLib?: PdfJsLib }; if (win.pdfjsLib) { + win.pdfjsLib.GlobalWorkerOptions.workerSrc = + 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; return win.pdfjsLib; } return new Promise((resolve, reject) => { const script = document.createElement('script'); + script.id = 'pdfjs-script-cdn'; script.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js'; script.onload = () => { const pdfjs = (window as unknown as { pdfjsLib?: PdfJsLib }).pdfjsLib; if (pdfjs) { - pdfjs.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; + pdfjs.GlobalWorkerOptions.workerSrc = + 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; resolve(pdfjs); } else { - reject(new Error('pdfjsLib não foi encontrado após o carregamento.')); + reject(new Error('pdfjsLib não foi encontrado após carregar a CDN.')); } }; - script.onerror = () => reject(new Error('Falha ao carregar a biblioteca PDF.js via CDN.')); + script.onerror = () => reject(new Error('Falha ao carregar script PDF.js via CDN.')); document.head.appendChild(script); }); }; @@ -104,14 +108,23 @@ const AdvanceSteelConverterContent: React.FC = () => { const [isDragging, setIsDragging] = useState(false); const [isProcessing, setIsProcessing] = useState(false); const [extractedData, setExtractedData] = useState([]); - const [logs, setLogs] = useState([]); + const [logs, setLogs] = useState(['> Aguardando seleção do PDF...']); const [statusText, setStatusText] = useState('Aguardando arquivo PDF...'); const [currentFileName, setCurrentFileName] = useState('Lista_Pecas'); + const [useIframeMode, setUseIframeMode] = useState(false); const fileInputRef = useRef(null); + const logEndRef = useRef(null); + + useEffect(() => { + ensurePdfJs().catch((err) => console.warn('Preload PDF.js notice:', err)); + }, []); const addLog = (msg: string) => { setLogs((prev) => [...prev, `> ${msg}`]); + setTimeout(() => { + logEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, 50); }; const handleDragOver = (e: React.DragEvent) => { @@ -185,7 +198,7 @@ const AdvanceSteelConverterContent: React.FC = () => { }); if (currentLine.length > 0) lines.push(currentLine); - addLog(`Total de linhas identificadas: ${lines.length}`); + addLog(`Total de linhas identificadas no PDF: ${lines.length}`); let defaultOf = ''; for (const line of lines) { @@ -215,7 +228,20 @@ const AdvanceSteelConverterContent: React.FC = () => { continue; } - const firstToken = line[0].text; + let firstToken = line[0].text.trim(); + + // MONTAGEM INTELIGENTE DE TOKENS SEPARADOS (Ex: ["B132", "-", "1", "-", "1"] -> "B132-1-1") + if (!firstToken.includes('-') && line.length >= 3) { + let assembled = ''; + for (let k = 0; k < Math.min(line.length, 6); k++) { + assembled += line[k].text.trim(); + if (assembled.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/) || assembled.match(/^([A-Za-z0-9]+)-(\d+)$/)) { + firstToken = assembled; + break; + } + } + } + const markMatch = firstToken.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/) || firstToken.match(/^([A-Za-z0-9]+)-(\d+)$/); if (markMatch) { @@ -357,7 +383,7 @@ const AdvanceSteelConverterContent: React.FC = () => { } } - addLog(`Total de Peças Principais processadas: ${assemblies.length}`); + addLog(`Total de Peças Principais extraídas: ${assemblies.length}`); const finalPieces: ExtractedPiece[] = assemblies.map((asm) => { const hasComponents = asm.components.length > 0; @@ -401,8 +427,16 @@ const AdvanceSteelConverterContent: React.FC = () => { }); setExtractedData(finalPieces); - setStatusText(`${finalPieces.length} peças principais extraídas com sucesso!`); - addLog(`Tabela renderizada com ${finalPieces.length} registros.`); + + if (finalPieces.length === 0) { + setStatusText('Nenhuma peça principal identificada.'); + addLog('AVISO: Nenhuma peça bateu com a máscara (ex: B132-1 ou B132-4-1).'); + toast.warning('PDF lido, mas nenhuma marca de peça foi reconhecida.'); + } else { + setStatusText(`${finalPieces.length} peças principais extraídas com sucesso!`); + addLog(`Tabela renderizada com ${finalPieces.length} registros.`); + toast.success(`${finalPieces.length} peças extraídas do PDF!`); + } }; const handleFile = async (file: File) => { @@ -415,16 +449,14 @@ const AdvanceSteelConverterContent: React.FC = () => { const baseName = file.name.replace(/\.[^/.]+$/, ''); setCurrentFileName(baseName); setStatusText(`Processando: ${file.name}...`); - setLogs([]); + setLogs([`> Carregando arquivo: ${file.name}`]); setIsProcessing(true); - addLog(`Carregando PDF: ${file.name}`); - try { - const pdfjs = await loadPdfJs(); + const pdfjs = await ensurePdfJs(); const arrayBuffer = await file.arrayBuffer(); const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise; - addLog(`PDF carregado com sucesso! Total de páginas: ${pdf.numPages}`); + addLog(`PDF aberto! Total de páginas: ${pdf.numPages}`); const allItems: PdfItem[] = []; for (let p = 1; p <= pdf.numPages; p++) { @@ -444,12 +476,11 @@ const AdvanceSteelConverterContent: React.FC = () => { addLog(`Total de elementos de texto extraídos: ${allItems.length}`); processPdfLines(allItems); - toast.success(`PDF processado com sucesso! ${allItems.length} elementos analisados.`); } catch (err) { console.error(err); const errMsg = err instanceof Error ? err.message : 'Erro desconhecido'; - toast.error(`Erro ao processar o arquivo PDF: ${errMsg}`); - addLog(`ERRO: ${errMsg}`); + toast.error(`Erro ao processar PDF: ${errMsg}`); + addLog(`ERRO CRÍTICO: ${errMsg}`); setStatusText('Falha no processamento.'); } finally { setIsProcessing(false); @@ -495,12 +526,51 @@ const AdvanceSteelConverterContent: React.FC = () => { XLSX.utils.book_append_sheet(wb, ws, 'Lista_Pecas'); const outFileName = `${currentFileName}_Corrigido.xlsx`; XLSX.writeFile(wb, outFileName); - addLog(`Arquivo Excel exportado: ${outFileName}`); + addLog(`Planilha Excel baixada: ${outFileName}`); toast.success(`Planilha Excel "${outFileName}" gerada com sucesso!`); }; + if (useIframeMode) { + return ( +
+
+ + + Modo 100% HTML Original Ativo + + +
+