import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Download, FileText, File as FileIcon, X, AlertTriangle, ShieldAlert } from 'lucide-react'; import { apiClient, exportProjectDocument, getExportGateStatus } from '../../api/client'; import toast from 'react-hot-toast'; interface ExportGateGuidance { reason: string; tab?: string; actions: string[]; } interface ExportModalProps { isOpen: boolean; onClose: () => void; projectId: string; } export const ExportModal: React.FC = ({ isOpen, onClose, projectId }) => { const [format, setFormat] = useState<'pdf' | 'docx'>('pdf'); const [template, setTemplate] = useState('official'); const [isExporting, setIsExporting] = useState(false); const [gateStatus, setGateStatus] = useState<{ blocked: boolean; reasons: string[]; guidance: ExportGateGuidance[]; summary?: string; } | null>(null); useEffect(() => { if (!isOpen || !projectId) return; getExportGateStatus(projectId) .then(setGateStatus) .catch(() => setGateStatus(null)); }, [isOpen, projectId]); if (!isOpen) return null; const parseExportError = async (error: any): Promise => { let detail = error?.message; if (!detail && error?.response?.data) { if (error.response.data instanceof Blob) { try { const text = await error.response.data.text(); const parsed = JSON.parse(text); const d = parsed.detail; if (d?.guidance?.length) { setGateStatus({ blocked: true, reasons: d.reasons || [], guidance: d.guidance, summary: d.message, }); } detail = typeof d === 'string' ? d : d?.message || JSON.stringify(d); } catch { detail = 'Błąd generowania pliku'; } } else { const d = error.response.data.detail; if (d?.guidance?.length) { setGateStatus({ blocked: true, reasons: d.reasons || [], guidance: d.guidance, summary: d.message, }); } detail = typeof d === 'string' ? d : d?.message || JSON.stringify(d); } } return detail || 'Wystąpił błąd podczas generowania dokumentu.'; }; const handleExport = async () => { try { setIsExporting(true); toast.loading('Generowanie dokumentu...', { id: 'export' }); const response = await exportProjectDocument(projectId, format, template); // Create a blob URL and trigger download const blob = new Blob([response.data], { type: response.headers['content-type'] as string }); const url = window.URL.createObjectURL(blob); // Generate filename based on headers or default let fileName = `Wniosek_${projectId}.${format}`; const contentDisposition = response.headers['content-disposition']; if (contentDisposition && contentDisposition.indexOf('filename=') !== -1) { const matches = contentDisposition.match(/filename="?([^"]+)"?/); if (matches && matches[1]) { fileName = matches[1]; } } const link = document.createElement('a'); link.href = url; link.setAttribute('download', fileName); document.body.appendChild(link); link.click(); link.remove(); window.URL.revokeObjectURL(url); toast.success('Pomyślnie wyeksportowano dokument! Świadectwo Zgodności jest dostępne w panelu Final Document.', { id: 'export' }); // Cycle 12: Quick access to certificate after export setTimeout(() => { toast((t) => (
Chcesz pobrać Świadectwo Zgodności?
), { duration: 8000, id: 'certificate-hint' }); }, 1200); onClose(); } catch (error: any) { console.error(error); const detail = await parseExportError(error); toast.error(detail, { id: 'export', duration: 8000 }); } finally { setIsExporting(false); } }; return (

Eksportuj Wniosek

{gateStatus?.blocked && (
Eksport zablokowany — bramka jakości

{gateStatus.summary || 'Ukończ wymagane kroki przed pobraniem pliku.'}

{(gateStatus.guidance || []).map((g, idx) => (
{g.reason}
    {g.actions.map((a, i) =>
  1. {a}
  2. )}
))}
Eksport na własne ryzyko możliwy tylko po świadomym potwierdzeniu w API (override).
)} {/* FORMAT WIDGET */}
{/* TEMPLATE WIDGET */}
{[ { id: 'standard', name: 'Standardowy', desc: 'Dobry na start (Arial 11, klasyczny układ)' }, { id: 'official', name: 'Urzędowy', desc: 'Całkowicie ustrukturyzowany, formalny układ zalecany do urzędów' }, { id: 'modern', name: 'Nowoczesny', desc: 'Więcej przestrzeni i oddechu z subtelnymi akcentami kolorystycznymi' } ].map(tpl => ( ))}
{/* TIP FOR DOCX */} {format === 'docx' && (
ℹ️ W wygenerowanym pliku DOCX spis treści jest początkowo pusty. Po otwarciu dokumentu w Microsoft Word naciśnij F9, aby zaktualizować stronę.
)}
); };