import React, { useEffect, useRef, useState } from "react"; interface StimulsoftViewerProps { requestUrl: string; action?: string; width?: string; height?: string; onClose?: () => void; orderId?: number; model?: string; } /** * StimulsoftViewer Component * Componente para renderizar o Stimulsoft Reports Viewer * Baseado no componente stimulsoft-viewer-angular do portal Angular * * O componente usa a biblioteca Stimulsoft Reports.JS para processar o JSON * retornado pelo servidor e renderizar o viewer */ const StimulsoftViewer: React.FC = ({ requestUrl, action, width = "100%", height = "800px", onClose, orderId, model, }) => { const viewerRef = useRef(null); const iframeRef = useRef(null); const formRef = useRef(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Gerar ID único para o viewer (deve ser declarado antes do useEffect) const viewerIdRef = useRef( `stimulsoft-viewer-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` ); const viewerId = viewerIdRef.current; // Construir a URL base (sem query params, pois vamos usar POST) const baseUrl = action ? requestUrl.replace("{action}", action) : requestUrl; const urlWithoutParams = baseUrl.split("?")[0]; useEffect(() => { let isMounted = true; if (!viewerRef.current) return; // Limpar conteúdo anterior viewerRef.current.innerHTML = ""; // Extrair parâmetros da URL original se não foram fornecidos via props const urlParams = new URLSearchParams(baseUrl.split("?")[1] || ""); const finalOrderId = orderId ? String(orderId) : urlParams.get("orderId") || ""; const finalModel = model || urlParams.get("model") || "A"; // Criar iframe const iframe = document.createElement("iframe"); iframe.name = `stimulsoft-viewer-iframe-${Date.now()}`; iframe.style.width = "100%"; iframe.style.height = "100%"; iframe.style.border = "none"; iframe.style.borderRadius = "0 0 1.5rem 1.5rem"; iframe.title = "Stimulsoft Viewer"; iframe.allow = "fullscreen"; // Criar formulário que faz POST com multipart/form-data // O componente Angular stimulsoft-viewer-angular faz POST com esses parâmetros // O navegador vai criar o boundary automaticamente quando usamos multipart/form-data const form = document.createElement("form"); form.method = "POST"; form.action = urlWithoutParams; form.target = iframe.name; form.setAttribute("enctype", "multipart/form-data"); form.style.display = "none"; // Adicionar campos do formulário conforme a requisição funcional do Angular const fields = { orderId: finalOrderId, model: finalModel, stiweb_component: "Viewer", stiweb_imagesScalingFactor: "1", stiweb_action: "AngularViewerData", }; // Log para debug console.log("StimulsoftViewer - URL:", urlWithoutParams); console.log("StimulsoftViewer - Campos:", fields); Object.entries(fields).forEach(([key, value]) => { const input = document.createElement("input"); input.type = "hidden"; input.name = key; input.value = String(value); form.appendChild(input); }); // Verificar se orderId está vazio (pode causar erro 500) if (!finalOrderId) { setError("Erro: orderId não foi fornecido."); setLoading(false); return; } let loadTimeout: NodeJS.Timeout; let errorDetected = false; const handleIframeLoad = () => { if (isMounted && !errorDetected) { // Verificar se houve erro no iframe (status 500, etc) try { const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document; if (iframeDoc) { const bodyText = iframeDoc.body?.innerText || iframeDoc.body?.textContent || ""; // Se o conteúdo contém indicadores de erro if ( bodyText.includes("500") || bodyText.includes("Internal Server Error") || bodyText.includes("Error") ) { errorDetected = true; setError( "Erro ao carregar o relatório. O servidor retornou um erro." ); setLoading(false); return; } } } catch (e) { // CORS pode impedir acesso ao conteúdo do iframe, mas isso é normal console.log( "Não foi possível verificar conteúdo do iframe (pode ser CORS):", e ); } clearTimeout(loadTimeout); setLoading(false); } }; const handleIframeError = () => { if (isMounted) { errorDetected = true; setError( "Erro ao carregar o relatório. Verifique a URL e a conexão com o servidor." ); setLoading(false); } }; // Timeout para detectar se o iframe não carregou loadTimeout = setTimeout(() => { if (isMounted && loading) { // Verificar se o iframe realmente carregou try { if (iframe.contentWindow && iframe.contentDocument) { // Iframe carregou, mas pode ter demorado handleIframeLoad(); } else { errorDetected = true; setError("Timeout ao carregar o relatório. Verifique a conexão."); setLoading(false); } } catch (e) { // CORS pode impedir acesso, mas isso não significa erro // Se passou muito tempo, pode ser um problema errorDetected = true; setError("Timeout ao carregar o relatório."); setLoading(false); } } }, 30000); // 30 segundos de timeout iframe.addEventListener("load", handleIframeLoad); iframe.addEventListener("error", handleIframeError); // Adicionar iframe primeiro ao DOM e aguardar estar pronto if (viewerRef.current) { viewerRef.current.appendChild(iframe); } formRef.current = form; iframeRef.current = iframe; // Aguardar o iframe estar completamente carregado antes de submeter o formulário const waitForIframeReady = () => { if (!isMounted) return; // Verificar se o iframe está pronto try { // Tentar acessar o contentWindow para verificar se está pronto if (iframe.contentWindow) { // Iframe está pronto, adicionar formulário e submeter // Adicionar formulário ao body do documento document.body.appendChild(form); // Aguardar um pequeno delay antes de submeter setTimeout(() => { if (isMounted && formRef.current) { try { console.log("Submetendo formulário para:", urlWithoutParams); formRef.current.submit(); } catch (e) { console.error("Erro ao submeter formulário:", e); errorDetected = true; setError("Erro ao enviar requisição ao servidor."); setLoading(false); } } }, 50); } else { // Iframe ainda não está pronto, tentar novamente setTimeout(waitForIframeReady, 10); } } catch (e) { // Erro ao acessar contentWindow (pode ser CORS), mas continuar mesmo assim document.body.appendChild(form); setTimeout(() => { if (isMounted && formRef.current) { try { console.log("Submetendo formulário para:", urlWithoutParams); formRef.current.submit(); } catch (err) { console.error("Erro ao submeter formulário:", err); errorDetected = true; setError("Erro ao enviar requisição ao servidor."); setLoading(false); } } }, 100); } }; // Aguardar um pouco antes de verificar se o iframe está pronto setTimeout(waitForIframeReady, 50); return () => { isMounted = false; clearTimeout(loadTimeout); if (iframeRef.current) { iframeRef.current.removeEventListener("load", handleIframeLoad); iframeRef.current.removeEventListener("error", handleIframeError); } // Remover formulário do body se ainda estiver lá if (formRef.current && formRef.current.parentNode) { formRef.current.parentNode.removeChild(formRef.current); } }; }, [baseUrl, urlWithoutParams, orderId, model]); return (
{/*