Verif DOB salarié signature
This commit is contained in:
parent
3eb696b45d
commit
8644e8860e
7 changed files with 500 additions and 37 deletions
93
app/api/emails/contact-support/route.ts
Normal file
93
app/api/emails/contact-support/route.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { sendUniversalEmailV2, EmailDataV2 } from '@/lib/emailTemplateService';
|
||||
|
||||
/**
|
||||
* POST /api/emails/contact-support
|
||||
*
|
||||
* Route API pour envoyer un message de contact au support Odentas
|
||||
* Utilise le système universel d'emails v2
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
console.log('=== API Contact Support ===');
|
||||
|
||||
try {
|
||||
// 1. Récupération et validation des données
|
||||
const data = await request.json();
|
||||
console.log('📦 Données reçues:', {
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
messageLength: data.message?.length || 0
|
||||
});
|
||||
|
||||
const { name, email, message } = data;
|
||||
|
||||
// Validation des champs requis
|
||||
if (!name || !email || !message) {
|
||||
console.error('❌ Champs requis manquants');
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Tous les champs sont requis',
|
||||
success: false
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validation format email
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
console.error('❌ Format email invalide');
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Format d\'email invalide',
|
||||
success: false
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Préparation des données de l'email
|
||||
const emailData: EmailDataV2 = {
|
||||
name: name,
|
||||
email: email,
|
||||
message: message,
|
||||
submittedAt: new Date().toLocaleString('fr-FR', {
|
||||
dateStyle: 'long',
|
||||
timeStyle: 'short'
|
||||
})
|
||||
};
|
||||
|
||||
console.log('📧 Préparation de l\'envoi de l\'email au support');
|
||||
|
||||
// 3. Envoi de l'email via le système universel v2
|
||||
const messageId = await sendUniversalEmailV2({
|
||||
type: 'contact-support',
|
||||
toEmail: 'paie@odentas.fr',
|
||||
data: emailData,
|
||||
});
|
||||
|
||||
console.log('✅ Email de contact envoyé avec succès:', {
|
||||
messageId,
|
||||
from: email,
|
||||
name: name
|
||||
});
|
||||
|
||||
// 4. Retour du succès
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
messageId
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de l\'envoi de l\'email de contact:', error);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Échec de l\'envoi du message',
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error'
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { sendUniversalEmailV2, EmailDataV2 } from '@/lib/emailTemplateService';
|
||||
import { ENV } from '@/lib/cleanEnv';
|
||||
import { createSbServiceRole } from '@/lib/supabaseServer';
|
||||
|
||||
/**
|
||||
* POST /api/emails/signature-salarie
|
||||
|
|
@ -108,7 +109,30 @@ export async function POST(request: NextRequest) {
|
|||
reference
|
||||
});
|
||||
|
||||
// 5. Retour du succès avec le messageId SES
|
||||
// 5. Stocker le signature_link dans cddu_contracts pour permettre la vérification
|
||||
if (contractId && signatureLink) {
|
||||
console.log('💾 Stockage du signature_link dans cddu_contracts...');
|
||||
|
||||
try {
|
||||
const supabase = createSbServiceRole();
|
||||
const { error: updateError } = await supabase
|
||||
.from('cddu_contracts')
|
||||
.update({ signature_link: signatureLink })
|
||||
.eq('id', contractId);
|
||||
|
||||
if (updateError) {
|
||||
console.error('⚠️ Erreur lors de la mise à jour du signature_link:', updateError);
|
||||
// Ne pas bloquer le flux, l'email est déjà envoyé
|
||||
} else {
|
||||
console.log('✅ signature_link stocké avec succès');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('⚠️ Exception lors du stockage du signature_link:', err);
|
||||
// Ne pas bloquer le flux
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Retour du succès avec le messageId SES
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
messageId,
|
||||
|
|
|
|||
|
|
@ -26,29 +26,94 @@ export async function POST(request: NextRequest) {
|
|||
);
|
||||
}
|
||||
|
||||
console.log('🔍 Vérification pour docuseal_id:', docuseal_id);
|
||||
console.log('🔍 Vérification pour docuseal_id (slug):', docuseal_id);
|
||||
|
||||
// Créer le client Supabase avec service role
|
||||
const supabase = createSbServiceRole();
|
||||
|
||||
// 1. Récupérer l'email du salarié depuis la soumission DocuSeal
|
||||
// On cherche dans cddu_contracts pour trouver le contrat lié au slug DocuSeal
|
||||
// 1. Appeler l'API DocuSeal via le proxy interne pour récupérer les submissions et trouver celle avec ce slug
|
||||
console.log('📞 Appel API DocuSeal (via proxy interne) pour trouver la submission avec le slug:', docuseal_id);
|
||||
|
||||
// Récupérer les submissions récentes via le proxy interne
|
||||
let submissionId: string | null = null;
|
||||
|
||||
try {
|
||||
// On récupère les 100 dernières submissions via le proxy interne
|
||||
console.log('📞 Calling internal DocuSeal proxy: /api/docuseal/submissions?limit=100');
|
||||
|
||||
const docusealResponse = await fetch(`${process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'}/api/docuseal/submissions?limit=100`, {
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
});
|
||||
|
||||
console.log('📡 DocuSeal proxy response status:', docusealResponse.status);
|
||||
|
||||
if (!docusealResponse.ok) {
|
||||
const errorText = await docusealResponse.text();
|
||||
console.error('❌ Erreur DocuSeal proxy:', docusealResponse.status, errorText);
|
||||
throw new Error(`DocuSeal proxy error: ${docusealResponse.status} - ${errorText}`);
|
||||
}
|
||||
|
||||
const submissionsData = await docusealResponse.json();
|
||||
console.log('📋 Type de données reçues:', typeof submissionsData, Array.isArray(submissionsData) ? 'array' : 'object');
|
||||
|
||||
// DocuSeal peut retourner soit un array, soit un objet avec data
|
||||
const submissions = Array.isArray(submissionsData) ? submissionsData : (submissionsData.data || []);
|
||||
console.log('📋 Nombre de submissions récupérées:', submissions.length);
|
||||
|
||||
// Chercher la submission qui contient ce slug dans un submitter
|
||||
for (const submission of submissions) {
|
||||
const submitters = submission.submitters || [];
|
||||
const foundSubmitter = submitters.find((s: any) => s.slug === docuseal_id);
|
||||
|
||||
if (foundSubmitter) {
|
||||
submissionId = submission.id;
|
||||
console.log('✅ Submission trouvée:', submissionId, 'pour le slug:', docuseal_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!submissionId) {
|
||||
console.error('❌ Aucune submission trouvée avec le slug:', docuseal_id);
|
||||
return NextResponse.json(
|
||||
{ error: 'Document introuvable', verified: false },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de l\'appel DocuSeal:', error);
|
||||
console.error('❌ Type d\'erreur:', error instanceof Error ? error.message : String(error));
|
||||
console.error('❌ Stack:', error instanceof Error ? error.stack : 'N/A');
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Erreur lors de la récupération du document',
|
||||
verified: false,
|
||||
details: error instanceof Error ? error.message : 'Unknown error'
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Chercher le contrat dans cddu_contracts avec ce docuseal_submission_id
|
||||
console.log('🔍 Recherche du contrat avec docuseal_submission_id:', submissionId);
|
||||
|
||||
const { data: contract, error: contractError } = await supabase
|
||||
.from('cddu_contracts')
|
||||
.select('employee_id, salarie_email')
|
||||
.eq('docuseal_employee_slug', docuseal_id)
|
||||
.select('id, employee_id, contract_number')
|
||||
.eq('docuseal_submission_id', submissionId)
|
||||
.maybeSingle();
|
||||
|
||||
if (contractError) {
|
||||
console.error('❌ Erreur lors de la récupération du contrat:', contractError);
|
||||
console.error('❌ Erreur lors de la recherche du contrat:', contractError);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur lors de la vérification', verified: false },
|
||||
{ error: 'Erreur lors de la recherche du contrat', verified: false },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!contract) {
|
||||
console.error('❌ Aucun contrat trouvé pour docuseal_id:', docuseal_id);
|
||||
console.error('❌ Aucun contrat trouvé pour submission_id:', submissionId);
|
||||
return NextResponse.json(
|
||||
{ error: 'Document introuvable', verified: false },
|
||||
{ status: 404 }
|
||||
|
|
@ -56,14 +121,15 @@ export async function POST(request: NextRequest) {
|
|||
}
|
||||
|
||||
console.log('📄 Contrat trouvé:', {
|
||||
id: contract.id,
|
||||
employee_id: contract.employee_id,
|
||||
salarie_email: contract.salarie_email
|
||||
contract_number: contract.contract_number
|
||||
});
|
||||
|
||||
// 2. Récupérer la date de naissance du salarié depuis la table salaries
|
||||
// 2. Récupérer les infos du salarié depuis la table salaries (y compris l'email)
|
||||
const { data: salarie, error: salarieError } = await supabase
|
||||
.from('salaries')
|
||||
.select('date_naissance, prenom, nom')
|
||||
.select('date_naissance, prenom, nom, adresse_mail')
|
||||
.eq('id', contract.employee_id)
|
||||
.maybeSingle();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Calendar, Shield, AlertCircle, Loader2 } from "lucide-react";
|
||||
import { Calendar, Shield, AlertCircle, Loader2, Lock } from "lucide-react";
|
||||
|
||||
interface BirthdateVerificationModalProps {
|
||||
docuseal_id: string;
|
||||
|
|
@ -54,7 +54,7 @@ export default function BirthdateVerificationModal({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm animate-in fade-in duration-200">
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/70 backdrop-blur-md animate-in fade-in duration-200">
|
||||
<div className="w-full max-w-md bg-white rounded-2xl shadow-2xl border border-gray-200 overflow-hidden animate-in zoom-in-95 duration-300">
|
||||
{/* Header avec gradient */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-blue-700 px-6 py-5">
|
||||
|
|
@ -114,17 +114,15 @@ export default function BirthdateVerificationModal({
|
|||
<AlertCircle className="w-5 h-5 text-red-600 flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-red-900">{error}</p>
|
||||
{attempts >= 2 && (
|
||||
<p className="text-xs text-red-700 mt-1">
|
||||
Besoin d'aide ? Contactez-nous à{' '}
|
||||
<a
|
||||
href="mailto:paie@odentas.fr"
|
||||
className="underline hover:text-red-900"
|
||||
>
|
||||
paie@odentas.fr
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
<p className="text-xs text-red-700 mt-1">
|
||||
Besoin d'aide ? Contactez-nous à{' '}
|
||||
<a
|
||||
href="mailto:paie@odentas.fr"
|
||||
className="underline hover:text-red-900"
|
||||
>
|
||||
paie@odentas.fr
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -151,9 +149,12 @@ export default function BirthdateVerificationModal({
|
|||
|
||||
{/* Footer avec info */}
|
||||
<div className="pt-4 border-t border-gray-200">
|
||||
<p className="text-xs text-gray-500 text-center leading-relaxed">
|
||||
🔒 Vos données sont protégées et ne sont utilisées que pour vérifier votre identité.
|
||||
Cette étape garantit que seul vous pouvez accéder à votre contrat.
|
||||
<p className="text-xs text-gray-500 text-center leading-relaxed flex items-center justify-center gap-1.5">
|
||||
<Lock className="w-3.5 h-3.5 flex-shrink-0" />
|
||||
<span>
|
||||
Vos données sont protégées et ne sont utilisées que pour vérifier votre identité.
|
||||
Cette étape garantit que seul vous pouvez accéder à votre contrat.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
224
app/signature-salarie/ContactModal.tsx
Normal file
224
app/signature-salarie/ContactModal.tsx
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { X, Send, Mail, User, MessageSquare, CheckCircle2, Loader2 } from "lucide-react";
|
||||
|
||||
interface ContactModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function ContactModal({ isOpen, onClose }: ContactModalProps) {
|
||||
const [name, setName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
setIsSending(true);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/emails/contact-support', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name,
|
||||
email,
|
||||
message,
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok && data.success) {
|
||||
setSuccess(true);
|
||||
// Fermer après 2 secondes
|
||||
setTimeout(() => {
|
||||
onClose();
|
||||
// Reset form
|
||||
setName("");
|
||||
setEmail("");
|
||||
setMessage("");
|
||||
setSuccess(false);
|
||||
}, 2000);
|
||||
} else {
|
||||
setError(data.error || 'Erreur lors de l\'envoi du message');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de l\'envoi:', err);
|
||||
setError('Erreur lors de l\'envoi du message. Veuillez réessayer.');
|
||||
} finally {
|
||||
setIsSending(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-md animate-in fade-in duration-200">
|
||||
<div className="w-full max-w-lg bg-white rounded-2xl shadow-2xl border border-gray-200 overflow-hidden animate-in zoom-in-95 duration-300">
|
||||
{/* Header */}
|
||||
<div className="relative bg-gradient-to-r from-blue-600 to-blue-700 px-6 py-5">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 p-2 hover:bg-white/20 rounded-lg transition-colors"
|
||||
type="button"
|
||||
>
|
||||
<X className="w-5 h-5 text-white" />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-white/20 rounded-lg backdrop-blur-sm">
|
||||
<Mail className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-white">
|
||||
Contactez l'équipe Odentas
|
||||
</h2>
|
||||
<p className="text-blue-100 text-sm mt-0.5">
|
||||
Nous sommes là pour vous aider
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Corps */}
|
||||
<div className="p-6">
|
||||
{success ? (
|
||||
<div className="py-8 text-center animate-in zoom-in duration-300">
|
||||
<div className="inline-flex p-3 bg-green-100 rounded-full mb-4">
|
||||
<CheckCircle2 className="w-12 h-12 text-green-600" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||
Message envoyé avec succès !
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
Notre équipe vous répondra dans les plus brefs délais.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{/* Nom */}
|
||||
<div className="space-y-2">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium text-gray-700 flex items-center gap-2"
|
||||
>
|
||||
<User className="w-4 h-4 text-gray-500" />
|
||||
Nom complet
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
disabled={isSending}
|
||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
placeholder="Jean Dupont"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div className="space-y-2">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-medium text-gray-700 flex items-center gap-2"
|
||||
>
|
||||
<Mail className="w-4 h-4 text-gray-500" />
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
disabled={isSending}
|
||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
placeholder="jean.dupont@exemple.fr"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div className="space-y-2">
|
||||
<label
|
||||
htmlFor="message"
|
||||
className="block text-sm font-medium text-gray-700 flex items-center gap-2"
|
||||
>
|
||||
<MessageSquare className="w-4 h-4 text-gray-500" />
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
value={message}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
required
|
||||
disabled={isSending}
|
||||
rows={5}
|
||||
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all disabled:bg-gray-100 disabled:cursor-not-allowed resize-none"
|
||||
placeholder="Décrivez votre demande ou question..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Message d'erreur */}
|
||||
{error && (
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
||||
<p className="text-sm text-red-900">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Boutons */}
|
||||
<div className="flex gap-3 pt-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
disabled={isSending}
|
||||
className="flex-1 px-4 py-2.5 border border-gray-300 text-gray-700 font-medium rounded-lg hover:bg-gray-50 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSending}
|
||||
className="flex-1 bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 text-white font-medium py-2.5 px-4 rounded-lg transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 shadow-lg hover:shadow-xl"
|
||||
>
|
||||
{isSending ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
Envoi...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Send className="w-4 h-4" />
|
||||
Envoyer
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Info contact */}
|
||||
<div className="pt-4 border-t border-gray-200">
|
||||
<p className="text-xs text-gray-500 text-center">
|
||||
Vous pouvez également nous contacter directement à{' '}
|
||||
<a
|
||||
href="mailto:paie@odentas.fr"
|
||||
className="text-blue-600 hover:text-blue-700 underline"
|
||||
>
|
||||
paie@odentas.fr
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { AlertTriangle, Mail } from "lucide-react";
|
||||
import { usePageTitle } from "@/hooks/usePageTitle";
|
||||
import Script from "next/script";
|
||||
import BirthdateVerificationModal from "./BirthdateVerificationModal";
|
||||
import ContactModal from "./ContactModal";
|
||||
|
||||
export default function SignatureSalarieContent() {
|
||||
const searchParams = useSearchParams();
|
||||
|
|
@ -13,6 +14,7 @@ export default function SignatureSalarieContent() {
|
|||
const [showError, setShowError] = useState(false);
|
||||
const [docusealLoaded, setDocusealLoaded] = useState(false);
|
||||
const [isVerified, setIsVerified] = useState(false);
|
||||
const [showContactModal, setShowContactModal] = useState(false);
|
||||
|
||||
// Définir le titre de la page
|
||||
usePageTitle("Signature électronique");
|
||||
|
|
@ -125,14 +127,30 @@ export default function SignatureSalarieContent() {
|
|||
/>
|
||||
|
||||
<div className="min-h-screen bg-white">
|
||||
{/* Header simple avec logo */}
|
||||
<div className="bg-white border-b border-gray-200 px-6 py-4">
|
||||
<div className="flex items-center justify-center">
|
||||
<img
|
||||
src="https://ci3.googleusercontent.com/meips/ADKq_NaYk41Pm5XLYFH2kxxXdCOkuRa2Ji2vLeI41LRtc9oBmfS7-NhGtPUzYhw7arh9LdEyrUS--rk3mW1WSrKTtKjsTtiUcZVwfYFyqvV8YSZ8ZFJtQzqG43CgPmcZsSdWmIrxmPATULmFaFEpIIO-IdbHkat3RBeqwDQ=s0-d-e1-ft#https://newstaging.odentas.fr/wp-content/uploads/2025/08/Odentas-Logo-Bleu-Fond-Transparent-4-1.png"
|
||||
alt="Odentas"
|
||||
className="h-8"
|
||||
/>
|
||||
{/* Header avec logo agrandi et bouton contact */}
|
||||
<div className="bg-white border-b border-gray-200 px-6 py-5">
|
||||
<div className="max-w-7xl mx-auto flex items-center justify-between">
|
||||
<div className="flex-1"></div>
|
||||
<div className="flex flex-col items-center justify-center flex-1 gap-2">
|
||||
<img
|
||||
src="https://ci3.googleusercontent.com/meips/ADKq_NaYk41Pm5XLYFH2kxxXdCOkuRa2Ji2vLeI41LRtc9oBmfS7-NhGtPUzYhw7arh9LdEyrUS--rk3mW1WSrKTtKjsTtiUcZVwfYFyqvV8YSZ8ZFJtQzqG43CgPmcZsSdWmIrxmPATULmFaFEpIIO-IdbHkat3RBeqwDQ=s0-d-e1-ft#https://newstaging.odentas.fr/wp-content/uploads/2025/08/Odentas-Logo-Bleu-Fond-Transparent-4-1.png"
|
||||
alt="Odentas"
|
||||
className="h-12"
|
||||
/>
|
||||
<h1 className="text-lg font-semibold text-gray-800">
|
||||
Signature Électronique
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex-1 flex justify-end">
|
||||
<button
|
||||
onClick={() => setShowContactModal(true)}
|
||||
className="inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium text-white bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 rounded-lg transition-all duration-200 shadow-lg hover:shadow-xl"
|
||||
>
|
||||
<Mail className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Contactez l'équipe Odentas</span>
|
||||
<span className="sm:hidden">Contact</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -159,6 +177,12 @@ export default function SignatureSalarieContent() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal de contact */}
|
||||
<ContactModal
|
||||
isOpen={showContactModal}
|
||||
onClose={() => setShowContactModal(false)}
|
||||
/>
|
||||
|
||||
<style jsx global>{`
|
||||
/* Styles spécifiques pour Safari iOS si nécessaire */
|
||||
.safari_only {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export type EmailTypeV2 =
|
|||
| 'support-reply' // Réponse du staff à un ticket support
|
||||
| 'support-ticket-created' // Notification interne : nouveau ticket créé
|
||||
| 'support-ticket-reply' // Notification interne : réponse utilisateur à un ticket
|
||||
| 'contact-support' // Formulaire de contact public vers le support
|
||||
// Accès / habilitations
|
||||
| 'account-activation'
|
||||
| 'access-updated'
|
||||
|
|
@ -964,6 +965,36 @@ const EMAIL_TEMPLATES_V2: Record<EmailTypeV2, EmailTemplateV2> = {
|
|||
{ label: 'Réponse', key: 'userMessage' },
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
'contact-support': {
|
||||
subject: '[CONTACT] Nouveau message de {{name}}',
|
||||
title: '📧 Nouveau message de contact',
|
||||
greeting: 'Équipe Support',
|
||||
mainMessage: 'Vous avez reçu un nouveau message via le formulaire de contact.',
|
||||
ctaText: 'Répondre par email',
|
||||
footerText: 'Message envoyé depuis le formulaire de contact Odentas.',
|
||||
preheaderText: 'Nouveau message de contact',
|
||||
colors: {
|
||||
headerColor: STANDARD_COLORS.HEADER,
|
||||
titleColor: '#0F172A',
|
||||
buttonColor: STANDARD_COLORS.BUTTON,
|
||||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||||
cardBackgroundColor: '#FFFFFF',
|
||||
cardBorder: '#E5E7EB',
|
||||
cardTitleColor: '#0F172A',
|
||||
},
|
||||
infoCard: [
|
||||
{ label: 'Nom', key: 'name' },
|
||||
{ label: 'Email', key: 'email' },
|
||||
{ label: 'Envoyé le', key: 'submittedAt' },
|
||||
],
|
||||
detailsCard: {
|
||||
title: 'Message',
|
||||
rows: [
|
||||
{ label: 'Contenu', key: 'message' },
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue