diff --git a/frontend/components/Campaigns.tsx b/frontend/components/Campaigns.tsx index 85be4be..8451a70 100755 --- a/frontend/components/Campaigns.tsx +++ b/frontend/components/Campaigns.tsx @@ -1205,50 +1205,50 @@ const CampaignDetail: React.FC<{ reportRootEl.style.top = '0px'; reportRootEl.style.zIndex = '-1'; document.body.appendChild(reportRootEl); - + const reactRoot = ReactDOM.createRoot(reportRootEl); - + try { const proofsWithLatestVersion = proofsToExport.map(p => ({ ...p, versions: [p.versions[0]], // Only render the latest version })); - + reactRoot.render(); - await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for render and images - - const { default: jspdf } = await import('jspdf'); - const { default: html2canvas } = await import('html2canvas'); + await new Promise(resolve => setTimeout(resolve, 1500)); // Wait for render and images const reportContent = reportRootEl.children[0] as HTMLElement; if (!reportContent) throw new Error("PDF report element not found"); - - const canvas = await html2canvas(reportContent, { scale: 2, useCORS: true }); - - const imgData = canvas.toDataURL('image/png'); - const pdf = new jspdf('p', 'mm', 'a4', true); - const pdfWidth = pdf.internal.pageSize.getWidth(); - const pdfHeight = pdf.internal.pageSize.getHeight(); - const canvasWidth = canvas.width; - const canvasHeight = canvas.height; - const ratio = canvasWidth / pdfWidth; - const pagedCanvasHeight = canvasHeight / ratio; - - let heightLeft = pagedCanvasHeight; - let position = 0; - - pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, pagedCanvasHeight, undefined, 'FAST'); - heightLeft -= pdfHeight; - - while (heightLeft > 0) { - position -= pdfHeight; - pdf.addPage(); - pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, pagedCanvasHeight, undefined, 'FAST'); - heightLeft -= pdfHeight; - } - - pdf.save(`${fileName.replace(/[^a-zA-Z0-9]/g, '_')}.pdf`); + + const safeTitle = fileName.replace(/[^a-zA-Z0-9 _-]/g, '_'); + const printWindow = window.open('', '_blank'); + if (!printWindow) throw new Error("Popup blocked. Please allow popups for this site and try again."); + + printWindow.document.write(` + + + ${safeTitle} + + + +${reportContent.outerHTML} +`); + printWindow.document.close(); + + await new Promise(resolve => { + const timeout = setTimeout(resolve, 3000); + printWindow.onload = () => { clearTimeout(timeout); resolve(); }; + }); + + printWindow.print(); + setTimeout(() => { try { printWindow.close(); } catch (_) {} }, 1000); } catch (error) { console.error("Failed to generate PDF:", error); @@ -1618,38 +1618,40 @@ const ProofDetailView: React.FC<{ reactRoot.render(); - await new Promise(resolve => setTimeout(resolve, 1000)); - - const { default: jspdf } = await import('jspdf'); - const { default: html2canvas } = await import('html2canvas'); + await new Promise(resolve => setTimeout(resolve, 1500)); const reportContent = reportRootEl.children[0] as HTMLElement; if (!reportContent) throw new Error("PDF report element not found"); - const canvas = await html2canvas(reportContent, { scale: 2, useCORS: true }); - - const imgData = canvas.toDataURL('image/png'); - const pdf = new jspdf('p', 'mm', 'a4', true); - const pdfWidth = pdf.internal.pageSize.getWidth(); - const pdfHeight = pdf.internal.pageSize.getHeight(); - const ratio = canvas.width / pdfWidth; - const pagedCanvasHeight = canvas.height / ratio; - - let heightLeft = pagedCanvasHeight; - let position = 0; - - pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, pagedCanvasHeight, undefined, 'FAST'); - heightLeft -= pdfHeight; - - while (heightLeft > 0) { - position -= pdfHeight; - pdf.addPage(); - pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, pagedCanvasHeight, undefined, 'FAST'); - heightLeft -= pdfHeight; - } - const fileName = `${campaignName} - ${proof.proofName} V${selectedVersion.version} Report`; - pdf.save(`${fileName.replace(/[^a-zA-Z0-9]/g, '_')}.pdf`); + const safeTitle = fileName.replace(/[^a-zA-Z0-9 _-]/g, '_'); + const printWindow = window.open('', '_blank'); + if (!printWindow) throw new Error("Popup blocked. Please allow popups for this site and try again."); + + printWindow.document.write(` + + + ${safeTitle} + + + +${reportContent.outerHTML} +`); + printWindow.document.close(); + + await new Promise(resolve => { + const timeout = setTimeout(resolve, 3000); + printWindow.onload = () => { clearTimeout(timeout); resolve(); }; + }); + + printWindow.print(); + setTimeout(() => { try { printWindow.close(); } catch (_) {} }, 1000); } catch (error) { console.error("Failed to generate PDF:", error); diff --git a/frontend/components/PDFReport.tsx b/frontend/components/PDFReport.tsx index 8859e5f..715c040 100755 --- a/frontend/components/PDFReport.tsx +++ b/frontend/components/PDFReport.tsx @@ -1,7 +1,5 @@ import React from 'react'; -import type { AgentReview, SubReview, RagStatus, OverallStatus } from '../types'; -import { BarclaysLogo } from './icons/BarclaysLogo'; -import { OliverLogo } from './icons/OliverLogo'; +import type { AgentReview, RagStatus } from '../types'; import { LegalIcon } from './icons/LegalIcon'; import { BrandIcon } from './icons/BrandIcon'; import { ChannelIcon } from './icons/ChannelIcon'; @@ -9,6 +7,7 @@ import { ChannelIcon } from './icons/ChannelIcon'; interface PDFReportProps { campaignName: string; proofs: any[]; + baseUrl?: string; } /** @@ -75,9 +74,9 @@ const formatFeedbackTextForPDF = (text: string): React.ReactNode => {

{renderBoldMarkdownForPDF(introLines.join(' '))}

)} {bulletGroups.length > 0 && ( -
    +
      {bulletGroups.map((group, index) => ( -
    • +
    • {group.map((line, lineIdx) => ( {lineIdx > 0 &&
      } @@ -110,7 +109,7 @@ const RagStatusBadge: React.FC<{ status: RagStatus }> = ({ status }) => { }; -export const PDFReport: React.FC = ({ campaignName, proofs }) => { +export const PDFReport: React.FC = ({ campaignName, proofs, baseUrl = window.location.origin }) => { const today = new Date().toLocaleDateString('en-GB', { day: '2-digit', month: 'long', @@ -125,8 +124,8 @@ export const PDFReport: React.FC = ({ campaignName, proofs }) => {/* --- Cover Page --- */}
      - - + Mod Comms AI — In partnership with Barclays +
      Oliver

      AI Compliance & Brand Report

      @@ -203,7 +202,7 @@ export const PDFReport: React.FC = ({ campaignName, proofs }) => {/* Detailed Agent Feedback */}
      {agentReviews.map(({ title, review, icon }) => ( -
      +

      {icon} {title} @@ -214,8 +213,8 @@ export const PDFReport: React.FC = ({ campaignName, proofs }) => {review.issues && review.issues.length > 0 && (
      Key Actions:
      -
        - {review.issues.map((issue, i) =>
      • {issue}
      • )} +
          + {review.issues.map((issue, i) =>
        • {issue}
        • )}
      )} diff --git a/frontend/components/icons/BarclaysLogo.tsx b/frontend/components/icons/BarclaysLogo.tsx index 4c3cc0e..9630db2 100755 --- a/frontend/components/icons/BarclaysLogo.tsx +++ b/frontend/components/icons/BarclaysLogo.tsx @@ -1,6 +1,6 @@ import React from 'react'; -// Shield with checkmark - Heroicons "shield-check" (outline) +// Shield with checkmark - used as Barclays brand icon in UI (Login, LoadingVisual) export const BarclaysLogo: React.FC> = (props) => (