'use client' import {useState} from 'react' import PropTypes from 'prop-types' import Button from '@mui/material/Button' import CircularProgress from '@mui/material/CircularProgress' import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf' import {formatDate} from '@/lib/format.js' // Helper function to render markdown to HTML const renderMarkdownToHtml = async content => { if (!content) { return 'Aucun contenu disponible' } // Check if content contains markdown syntax const hasMarkdown = content.includes('**') || content.includes('*') || content.includes('#') || content.includes('[') || content.includes('`') || content.includes('> ') || content.includes('- ') || content.includes('1. ') if (!hasMarkdown) { // Simple text with line breaks return content.replaceAll('\n', '
') } try { // Dynamic import of markdown parser const {marked} = await import('marked') // Configure marked for better PDF rendering marked.setOptions({ breaks: true, // Convert \n to
gfm: true, // GitHub flavored markdown headerIds: false, // No header IDs needed for PDF mangle: false // Don't mangle email addresses }) return marked(content) } catch (error) { console.warn('Failed to parse markdown, falling back to plain text:', error) return content.replaceAll('\n', '
') } } export default function ExportPdfButton({versionData, size = 'medium', variant = 'outlined'}) { const [isExporting, setIsExporting] = useState(false) const handleExportPdf = async () => { setIsExporting(true) try { const {default: jsPDF} = await import('jspdf') const {default: html2canvas} = await import('html2canvas') // Créer un élément temporaire avec le contenu formaté const tempDiv = document.createElement('div') tempDiv.style.cssText = ` padding: 40px; font-family: 'Roboto', Arial, sans-serif; line-height: 1.6; color: #333; background-color: white; width: 210mm; position: fixed; top: -9999px; left: -9999px; z-index: -1; ` // Add CSS styles for markdown elements const styleElement = document.createElement('style') styleElement.textContent = ` .pdf-content h1, .pdf-content h2, .pdf-content h3, .pdf-content h4, .pdf-content h5, .pdf-content h6 { margin: 20px 0 10px 0; font-weight: bold; color: #333; } .pdf-content h1 { font-size: 24px; color: #1976d2; } .pdf-content h2 { font-size: 20px; } .pdf-content h3 { font-size: 18px; } .pdf-content h4 { font-size: 16px; } .pdf-content p { margin: 10px 0; } .pdf-content strong, .pdf-content b { font-weight: bold; } .pdf-content em, .pdf-content i { font-style: italic; } .pdf-content ul, .pdf-content ol { margin: 10px 0; padding-left: 25px; } .pdf-content li { margin: 5px 0; } .pdf-content blockquote { margin: 15px 0; padding: 10px 20px; border-left: 4px solid #1976d2; background-color: #f5f5f5; font-style: italic; } .pdf-content code { background-color: #f5f5f5; padding: 2px 4px; border-radius: 3px; font-family: 'Courier New', monospace; font-size: 13px; } .pdf-content pre { background-color: #f5f5f5; padding: 15px; border-radius: 5px; overflow-x: auto; margin: 15px 0; } .pdf-content pre code { background: none; padding: 0; } .pdf-content a { color: #1976d2; text-decoration: underline; } .pdf-content hr { border: none; border-top: 1px solid #ccc; margin: 20px 0; } .pdf-content table { border-collapse: collapse; margin: 15px 0; width: 100%; } .pdf-content th, .pdf-content td { border: 1px solid #ccc; padding: 8px; text-align: left; } .pdf-content th { background-color: #f5f5f5; font-weight: bold; } ` document.head.append(styleElement) const authorName = versionData.user_created?.split('-')[0] || 'Système' const createdAt = new Date(versionData.date_created) const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000)) const voteStatus = createdAt < threeDaysAgo ? 'fermé' : 'ouvert' const voteColor = voteStatus === 'ouvert' ? '#2e7d32' : '#d32f2f' // Render markdown content to HTML const renderedContent = await renderMarkdownToHtml(versionData.delta?.contenu) tempDiv.innerHTML = `

${versionData.name}

Auteur : @${authorName}

Date de création : ${formatDate(versionData.date_created, 'PPpp')}

Statut du vote : ${voteStatus}

Contenu

${renderedContent}
Exporté depuis Konstitisyon.la le ${formatDate(new Date(), 'PPpp')}
` document.body.append(tempDiv) // Générer le canvas avec une meilleure qualité const canvas = await html2canvas(tempDiv, { scale: 2, useCORS: true, allowTaint: true, backgroundColor: '#ffffff', width: tempDiv.scrollWidth, height: tempDiv.scrollHeight }) tempDiv.remove() styleElement.remove() // Clean up styles // Créer le PDF const imgData = canvas.toDataURL('image/png', 1) const pdf = new jsPDF('p', 'mm', 'a4') // eslint-disable-line new-cap const pageWidth = pdf.internal.pageSize.getWidth() const pageHeight = pdf.internal.pageSize.getHeight() const imgWidth = pageWidth const imgHeight = (canvas.height * imgWidth) / canvas.width let heightLeft = imgHeight let position = 0 // Ajouter la première page pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight) heightLeft -= pageHeight // Ajouter des pages supplémentaires si nécessaire while (heightLeft >= 0) { position = heightLeft - imgHeight pdf.addPage() pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight) heightLeft -= pageHeight } // Nom du fichier sécurisé const fileName = `${versionData.name.replaceAll(/[^a-zA-Z\d\s]/g, '').replaceAll(/\s+/g, '_')}_${formatDate(new Date(), 'yyyy-MM-dd')}.pdf` pdf.save(fileName) } catch (error) { console.error('Erreur lors de l\'export PDF:', error) } finally { setIsExporting(false) } } return ( ) } ExportPdfButton.propTypes = { versionData: PropTypes.object.isRequired, size: PropTypes.oneOf(['small', 'medium', 'large']), variant: PropTypes.oneOf(['text', 'outlined', 'contained']) }