espace-paie-odentas/app/signer/[requestId]/[signerId]/components/PDFImageViewer.tsx

204 lines
6.6 KiB
TypeScript

'use client';
import { useState, useEffect, useRef } from 'react';
import { Loader2, AlertCircle } from 'lucide-react';
interface SignPosition {
page: number;
x: number; // En pourcentages (%) - depuis le PDF original
y: number; // En pourcentages (%) - depuis le PDF original
width: number; // En pourcentages (%) - depuis le PDF original
height: number; // En pourcentages (%) - depuis le PDF original
role: string;
}
interface PDFImageViewerProps {
pdfUrl: string;
positions: SignPosition[];
currentSignerRole: string;
requestId: string;
sessionToken: string;
}
interface PageImage {
pageNumber: number;
imageUrl: string;
width: number;
height: number;
naturalWidth?: number; // Dimensions réelles du JPEG chargé
naturalHeight?: number;
}
export default function PDFImageViewer({
pdfUrl,
positions,
currentSignerRole,
requestId,
sessionToken,
}: PDFImageViewerProps) {
const [pageImages, setPageImages] = useState<PageImage[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function convertPdfToImages() {
try {
setIsLoading(true);
setError(null);
console.log('[PDFImageViewer] Début conversion', {
requestId,
pdfUrl,
hasSessionToken: !!sessionToken,
sessionTokenLength: sessionToken?.length,
sessionTokenPreview: sessionToken?.substring(0, 20) + '...',
});
// Appel API pour convertir le PDF en images
const response = await fetch(`/api/odentas-sign/requests/${requestId}/pdf-to-images`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${sessionToken}`,
},
body: JSON.stringify({
pdfUrl,
}),
});
if (!response.ok) {
const data = await response.json();
console.error('[PDFImageViewer] Erreur response:', {
status: response.status,
statusText: response.statusText,
data,
});
throw new Error(data.error || 'Erreur lors de la conversion du PDF');
}
const data = await response.json();
setPageImages(data.pages || []);
} catch (err) {
console.error('[PDFImageViewer] Erreur:', err);
setError(err instanceof Error ? err.message : 'Erreur de chargement');
} finally {
setIsLoading(false);
}
}
if (pdfUrl) {
convertPdfToImages();
}
}, [pdfUrl, requestId, sessionToken]);
if (isLoading) {
return (
<div className="h-full bg-slate-50 rounded-xl flex flex-col items-center justify-center">
<Loader2 className="w-12 h-12 text-indigo-600 animate-spin mb-4" />
<p className="text-slate-600 font-medium">Conversion du PDF en cours...</p>
</div>
);
}
if (error) {
return (
<div className="h-full bg-red-50 rounded-xl flex flex-col items-center justify-center p-6">
<AlertCircle className="w-12 h-12 text-red-500 mb-4" />
<p className="text-red-700 font-medium mb-2">Erreur de chargement</p>
<p className="text-red-600 text-sm text-center">{error}</p>
</div>
);
}
if (pageImages.length === 0) {
return (
<div className="h-full bg-slate-50 rounded-xl flex flex-col items-center justify-center">
<p className="text-slate-500">Aucune page à afficher</p>
</div>
);
}
return (
<div className="h-full overflow-y-auto space-y-4">
{pageImages.map((page) => {
// Filtrer les positions de signature pour cette page et ce rôle
const pagePositions = positions.filter(
(pos) => pos.page === page.pageNumber && pos.role === currentSignerRole
);
console.log('[PDFImageViewer] Filtrage positions:', {
page: page.pageNumber,
currentSignerRole,
allPositions: positions.length,
filteredPositions: pagePositions.length,
positions: positions.map(p => ({ page: p.page, role: p.role })),
filtered: pagePositions,
});
return (
<div
key={page.pageNumber}
className="relative bg-white border border-slate-200 rounded-lg overflow-hidden"
style={{
aspectRatio: `${page.width} / ${page.height}`,
}}
>
{/* Image de la page PDF */}
<img
src={page.imageUrl}
alt={`Page ${page.pageNumber}`}
className="w-full h-full object-contain"
/>
{/* Zones de signature superposées */}
{pagePositions.map((pos, idx) => {
// Les positions arrivent maintenant en POURCENTAGES directement !
// Plus besoin de conversion, on les applique tel quel
console.log('[PDFImageViewer] Position signature:', {
page: page.pageNumber,
role: pos.role,
percentFromDB: {
left: pos.x.toFixed(2),
top: pos.y.toFixed(2),
width: pos.width.toFixed(2),
height: pos.height.toFixed(2)
}
});
return (
<div
key={idx}
className="absolute border-2 border-dashed border-indigo-500 bg-indigo-100/30 pointer-events-none"
style={{
left: `${pos.x}%`,
top: `${pos.y}%`,
width: `${pos.width}%`,
height: `${pos.height}%`,
}}
>
<div className="absolute inset-0 flex items-center justify-center">
<div className="bg-white/95 px-3 py-2 rounded-lg shadow-sm border border-indigo-300">
<p className="text-xs font-semibold text-indigo-700 whitespace-nowrap">
Votre signature sera apposée ici
</p>
</div>
</div>
</div>
);
})}
{/* Numéro de page - en haut à droite */}
<div className="absolute top-2 right-2 bg-slate-900/70 text-white text-xs px-2 py-1 rounded">
Page {page.pageNumber}
</div>
{/* Numéro de page - en bas à droite */}
<div className="absolute bottom-2 right-2 bg-slate-900/70 text-white text-xs px-2 py-1 rounded">
Page {page.pageNumber}
</div>
</div>
);
})}
</div>
);
}