- Audit RGPD complet: création RGPD_AUDIT_LOCALISATION_DONNEES.md - Suppression complète intégration n8n (API route, hooks, env vars) - Suppression variables Airtable (env vars) - Confirmation GoCardless: serveurs EEE + SCC - 8/9 services confirmés UE (89% compliance) - Ajout message info dans modale signature salarie (scroll down)
251 lines
8.3 KiB
TypeScript
251 lines
8.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { XCircle, CheckCircle2 } from "lucide-react";
|
|
import Script from "next/script";
|
|
|
|
interface DocuSealSignatureModalProps {
|
|
isOpen: boolean;
|
|
docusealId: string;
|
|
onClose: () => void;
|
|
onSignatureCompleted: () => void;
|
|
}
|
|
|
|
export default function DocuSealSignatureModal({
|
|
isOpen,
|
|
docusealId,
|
|
onClose,
|
|
onSignatureCompleted
|
|
}: DocuSealSignatureModalProps) {
|
|
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
const [docusealLoaded, setDocusealLoaded] = useState(false);
|
|
const [showCompletedModal, setShowCompletedModal] = useState(false);
|
|
|
|
// Ouvrir/fermer le dialog
|
|
useEffect(() => {
|
|
const dialog = dialogRef.current;
|
|
if (!dialog) return;
|
|
|
|
if (isOpen) {
|
|
if (!dialog.open) {
|
|
try {
|
|
dialog.showModal();
|
|
} catch (err) {
|
|
console.warn('Impossible d\'ouvrir le modal', err);
|
|
}
|
|
}
|
|
} else {
|
|
if (dialog.open) {
|
|
dialog.close();
|
|
}
|
|
}
|
|
}, [isOpen]);
|
|
|
|
// Écouter l'événement de complétion DocuSeal
|
|
useEffect(() => {
|
|
if (!docusealLoaded || !isOpen) return;
|
|
|
|
const formElement = document.getElementById('docusealFormModal');
|
|
if (!formElement) {
|
|
console.log('⚠️ docusealFormModal non trouvé');
|
|
return;
|
|
}
|
|
|
|
// Chercher l'élément <docuseal-form> à l'intérieur
|
|
const docusealForm = formElement.querySelector('docuseal-form') as HTMLElement | null;
|
|
if (!docusealForm) {
|
|
console.log('⚠️ Element <docuseal-form> non trouvé');
|
|
return;
|
|
}
|
|
|
|
const handleCompleted = (e: any) => {
|
|
console.log('✅ [SIGNATURE SALARIE] Event completed reçu:', e.detail);
|
|
setShowCompletedModal(true);
|
|
};
|
|
|
|
console.log('🎯 Ajout du listener "completed" sur docuseal-form');
|
|
docusealForm.addEventListener('completed', handleCompleted);
|
|
|
|
return () => {
|
|
console.log('🧹 Retrait du listener "completed"');
|
|
docusealForm.removeEventListener('completed', handleCompleted);
|
|
};
|
|
}, [docusealLoaded, isOpen]);
|
|
|
|
// Empêcher la fermeture du dialog si la confirmation est affichée
|
|
useEffect(() => {
|
|
const dialog = dialogRef.current;
|
|
if (!dialog) return;
|
|
|
|
const handleCancel = (e: Event) => {
|
|
if (showCompletedModal) {
|
|
e.preventDefault();
|
|
// Ré-ouvrir le dialog immédiatement
|
|
setTimeout(() => {
|
|
try {
|
|
if (dialog && !dialog.open) {
|
|
dialog.showModal();
|
|
}
|
|
} catch (err) {
|
|
console.warn('Impossible de ré-ouvrir le modal', err);
|
|
}
|
|
}, 0);
|
|
return;
|
|
}
|
|
};
|
|
|
|
const handleClose = () => {
|
|
if (showCompletedModal) {
|
|
// Si la confirmation est affichée, ré-ouvrir le dialog
|
|
setTimeout(() => {
|
|
try {
|
|
if (dialog && !dialog.open) {
|
|
dialog.showModal();
|
|
}
|
|
} catch (err) {
|
|
console.warn('Impossible de ré-ouvrir le modal', err);
|
|
}
|
|
}, 0);
|
|
}
|
|
};
|
|
|
|
dialog.addEventListener('cancel', handleCancel);
|
|
dialog.addEventListener('close', handleClose);
|
|
|
|
return () => {
|
|
dialog.removeEventListener('cancel', handleCancel);
|
|
dialog.removeEventListener('close', handleClose);
|
|
};
|
|
}, [showCompletedModal]);
|
|
|
|
const handleCloseCompleted = () => {
|
|
setShowCompletedModal(false);
|
|
const dialog = dialogRef.current;
|
|
if (dialog) dialog.close();
|
|
onSignatureCompleted();
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
return (
|
|
<>
|
|
{/* Charger le script DocuSeal */}
|
|
<Script
|
|
src="https://cdn.docuseal.com/js/form.js"
|
|
onLoad={() => setDocusealLoaded(true)}
|
|
strategy="afterInteractive"
|
|
/>
|
|
|
|
{/* Dialog modal */}
|
|
<dialog
|
|
ref={dialogRef}
|
|
className="backdrop:bg-black/60 bg-transparent p-0 rounded-2xl shadow-2xl max-w-4xl w-full max-h-[90vh] z-50"
|
|
onClose={() => {
|
|
if (!showCompletedModal) {
|
|
onClose();
|
|
}
|
|
}}
|
|
>
|
|
<div className="bg-white rounded-2xl h-full flex flex-col max-h-[90vh]">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between px-6 py-4 border-b bg-gradient-to-r from-blue-50 to-indigo-50 flex-shrink-0">
|
|
<div className="flex-1">
|
|
<h2 className="text-xl font-semibold text-slate-900">
|
|
Signature électronique du contrat
|
|
</h2>
|
|
<p className="text-xs text-slate-600 mt-1 flex items-center gap-1">
|
|
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
Descendez tout en bas du contrat pour accéder au champs de signature.
|
|
</p>
|
|
</div>
|
|
<button
|
|
onClick={() => {
|
|
if (!showCompletedModal) {
|
|
const dialog = dialogRef.current;
|
|
if (dialog) dialog.close();
|
|
onClose();
|
|
}
|
|
}}
|
|
disabled={showCompletedModal}
|
|
className={`p-2 rounded-lg transition-colors ${
|
|
showCompletedModal
|
|
? 'opacity-50 cursor-not-allowed'
|
|
: 'hover:bg-white/60'
|
|
}`}
|
|
aria-label="Fermer"
|
|
>
|
|
<XCircle className="w-5 h-5 text-slate-500" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Body avec DocuSeal */}
|
|
<div className="flex-1 overflow-y-auto p-6 min-h-0">
|
|
<div
|
|
id="docusealFormModal"
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
<docuseal-form
|
|
data-src="https://docuseal.eu/s/${docusealId}"
|
|
data-language="fr"
|
|
data-with-send-copy-button="false"
|
|
data-allow-to-resubmit="false"
|
|
data-allow-typed-signature="false"
|
|
data-remember-signature="true"
|
|
data-with-title="false"
|
|
data-completed-message-body="Le document a été signé."
|
|
>
|
|
</docuseal-form>
|
|
`
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</dialog>
|
|
|
|
{/* Modal de confirmation après signature */}
|
|
{showCompletedModal && (
|
|
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 p-4">
|
|
<div className="bg-white rounded-2xl shadow-2xl max-w-lg w-full overflow-hidden">
|
|
{/* Header */}
|
|
<div className="p-6 bg-emerald-50 border-b border-emerald-100">
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-12 h-12 rounded-full bg-emerald-100 flex items-center justify-center">
|
|
<CheckCircle2 className="w-6 h-6 text-emerald-600" />
|
|
</div>
|
|
<div>
|
|
<h2 className="text-xl font-semibold text-slate-900">Signature reçue avec succès</h2>
|
|
<p className="text-sm text-slate-600 mt-1">Votre signature électronique a bien été enregistrée.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="p-6 space-y-4">
|
|
<div className="bg-slate-50 border border-slate-200 rounded-lg p-4">
|
|
<ul className="text-sm text-slate-700 space-y-2 list-disc pl-5">
|
|
<li>Votre contrat est en cours de traitement.</li>
|
|
<li>Le contrat signé sera disponible au téléchargement dans quelques instants.</li>
|
|
<li>Vous recevrez également une copie par email.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<div className="px-6 py-4 bg-gray-50 border-t flex justify-end">
|
|
<button
|
|
onClick={handleCloseCompleted}
|
|
className="px-6 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white rounded-lg font-medium transition-colors"
|
|
>
|
|
J'ai compris
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|