Config
1) Upload PDF
2) Rodar auditoria
Debug
{debug ? JSON.stringify(debug, null, 2) : "{ }"}
import React, { useEffect, useMemo, useState } from "react"; import { Pie, Bar } from "react-chartjs-2"; import { Chart as ChartJS, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement, Title, } from "chart.js"; import { Upload, Play, RefreshCw, ExternalLink, Download, FileText } from "lucide-react"; ChartJS.register(ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement, Title); const API_BASE_DEFAULT = "https://lcye6sqlvh.execute-api.ca-central-1.amazonaws.com"; function pillClass(status) { const s = (status || "").toUpperCase(); if (s === "SUCCEEDED") return "pill pill-green"; if (s === "FAILED") return "pill pill-red"; return "pill pill-amber"; } async function apiGet(url) { const r = await fetch(url, { method: "GET" }); const txt = await r.text(); let data = null; try { data = JSON.parse(txt); } catch { data = txt; } return { ok: r.ok, status: r.status, data }; } async function apiPost(url, body) { const r = await fetch(url, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), }); const txt = await r.text(); let data = null; try { data = JSON.parse(txt); } catch { data = txt; } return { ok: r.ok, status: r.status, data }; } export default function App() { const [apiBase, setApiBase] = useState(API_BASE_DEFAULT); const [company, setCompany] = useState("SVPM"); const [tsf, setTsf] = useState("TSF-TESTE-Verdadeiro"); const [file, setFile] = useState(null); const [debug, setDebug] = useState(null); const [runs, setRuns] = useState([]); const [runsLoading, setRunsLoading] = useState(false); const [selectedRun, setSelectedRun] = useState(null); const [results, setResults] = useState([]); const [resultsLoading, setResultsLoading] = useState(false); // ---------- UI helpers ---------- const counts = useMemo(() => { const c = { ATENDE: 0, PARCIAL: 0, NAO_ATENDE: 0, OUTROS: 0 }; for (const it of results) { const st = (it.status || "").toUpperCase(); if (st === "ATENDE") c.ATENDE++; else if (st === "PARCIAL") c.PARCIAL++; else if (st === "NAO_ATENDE") c.NAO_ATENDE++; else c.OUTROS++; } return c; }, [results]); const pieData = useMemo(() => ({ labels: ["ATENDE", "PARCIAL", "NAO_ATENDE", "OUTROS"], datasets: [ { label: "Requisitos", data: [counts.ATENDE, counts.PARCIAL, counts.NAO_ATENDE, counts.OUTROS], }, ], }), [counts]); const barData = useMemo(() => ({ labels: ["ATENDE", "PARCIAL", "NAO_ATENDE", "OUTROS"], datasets: [ { label: "Qtd", data: [counts.ATENDE, counts.PARCIAL, counts.NAO_ATENDE, counts.OUTROS] }, ], }), [counts]); const pieOptions = useMemo(() => ({ responsive: true, plugins: { legend: { position: "bottom" } }, }), []); const barOptions = useMemo(() => ({ responsive: true, plugins: { legend: { display: false }, title: { display: true, text: "Distribuição de Status" } }, scales: { y: { beginAtZero: true, ticks: { precision: 0 } } }, }), []); // ---------- Actions ---------- async function refreshRuns() { setRunsLoading(true); try { const { ok, data } = await apiGet(`${apiBase}/runs`); if (ok) setRuns(Array.isArray(data) ? data : (data.items || [])); setDebug({ action: "refreshRuns", ok, data }); } finally { setRunsLoading(false); } } useEffect(() => { refreshRuns(); }, []); async function uploadPdf() { if (!file) return; // 1) get presigned const r1 = await apiPost(`${apiBase}/upload-url`, { filename: file.name, contentType: file.type || "application/pdf", }); if (!r1.ok) { setDebug({ action: "upload-url", ...r1 }); alert("Falha ao obter upload URL."); return; } const { uploadUrl, bucket, key } = r1.data; // 2) PUT file (IMPORTANTE: Content-Type) const put = await fetch(uploadUrl, { method: "PUT", headers: { "Content-Type": file.type || "application/pdf" }, body: file, }); if (!put.ok) { const txt = await put.text(); setDebug({ action: "PUT_S3", ok: put.ok, status: put.status, body: txt }); alert("Falha ao enviar PDF para o S3."); return; } setDebug({ action: "uploadPdf", ok: true, presigned: r1.data }); alert("upload OK"); } async function runAudit() { // você provavelmente já tem POST /run (ou /audit). ajuste se necessário: const r = await apiPost(`${apiBase}/run`, { company, tsf, // se seu backend espera bucket/key do doc, você pode passar aqui também: // document_s3_bucket: "...", // document_s3_key: "...", }); setDebug({ action: "runAudit", ...r }); if (!r.ok) { alert("Falha ao iniciar auditoria."); return; } alert("ok (execução iniciada)"); await refreshRuns(); } async function loadResults(runId) { setSelectedRun(runId); setResults([]); setResultsLoading(true); try { const r = await apiGet(`${apiBase}/runs/${runId}/results`); setDebug({ action: "loadResults", ...r }); if (r.ok && r.data && r.data.items) setResults(r.data.items); } finally { setResultsLoading(false); } } async function openReport(runId) { const r = await apiGet(`${apiBase}/runs/${runId}/report`); setDebug({ action: "openReport", ...r }); if (r.status === 409) { alert("Report ainda não está pronto (RUNNING)."); return; } if (!r.ok) { alert("Erro ao buscar report."); return; } const url = r.data.report_html_url; if (!url) { alert("Report HTML URL ausente."); return; } window.open(url, "_blank", "noopener,noreferrer"); } async function downloadPdf(runId) { const r = await apiGet(`${apiBase}/runs/${runId}/report`); setDebug({ action: "downloadPdf", ...r }); if (!r.ok) { alert("Erro ao buscar report."); return; } if (!r.data.report_pdf_url) { alert("PDF ainda não foi gerado (Caminho B)."); return; } window.open(r.data.report_pdf_url, "_blank", "noopener,noreferrer"); } // ---------- Render ---------- return (
{debug ? JSON.stringify(debug, null, 2) : "{ }"}