- Remplacement de DocuSeal par solution souveraine Odentas Sign - Système d'authentification OTP pour signataires (bcryptjs + JWT) - 8 routes API: send-otp, verify-otp, sign, pdf-url, positions, status, webhook, signers - Interface moderne avec canvas de signature et animations (framer-motion, confetti) - Système de templates pour auto-détection des positions de signature (CDDU, RG, avenants) - PDF viewer avec @react-pdf-viewer (compatible Next.js) - Stockage S3: source/, signatures/, evidence/, signed/, certs/ - Tables Supabase: sign_requests, signers, sign_positions, sign_events, sign_assets - Evidence bundle automatique (JSON metadata + timestamps) - Templates emails: OTP et completion - Scripts Lambda prêts: pades-sign (KMS seal) et tsaStamp (RFC3161) - Mode test détecté automatiquement (emails whitelist) - Tests complets avec PDF CDDU réel (2 signataires)
86 lines
3 KiB
TypeScript
86 lines
3 KiB
TypeScript
'use client';
|
|
|
|
import { motion } from 'framer-motion';
|
|
import { Check } from 'lucide-react';
|
|
|
|
interface ProgressBarProps {
|
|
currentStep: number;
|
|
totalSteps: number;
|
|
}
|
|
|
|
export default function ProgressBar({ currentStep, totalSteps }: ProgressBarProps) {
|
|
const steps = [
|
|
{ number: 1, label: 'Vérification' },
|
|
{ number: 2, label: 'Signature' },
|
|
];
|
|
|
|
return (
|
|
<div className="bg-white border-b border-slate-200 py-6">
|
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="flex items-center justify-between">
|
|
{steps.map((step, index) => {
|
|
const isCompleted = currentStep > step.number;
|
|
const isCurrent = currentStep === step.number;
|
|
|
|
return (
|
|
<div key={step.number} className="flex items-center flex-1">
|
|
{/* Step circle */}
|
|
<div className="flex flex-col items-center">
|
|
<motion.div
|
|
initial={{ scale: 0.8, opacity: 0 }}
|
|
animate={{ scale: 1, opacity: 1 }}
|
|
transition={{ delay: index * 0.1 }}
|
|
className={`
|
|
w-10 h-10 rounded-full flex items-center justify-center font-bold text-sm
|
|
transition-all duration-300
|
|
${
|
|
isCompleted
|
|
? 'bg-green-600 text-white'
|
|
: isCurrent
|
|
? 'bg-indigo-600 text-white ring-4 ring-indigo-100'
|
|
: 'bg-slate-200 text-slate-500'
|
|
}
|
|
`}
|
|
>
|
|
{isCompleted ? (
|
|
<Check className="w-5 h-5" />
|
|
) : (
|
|
step.number
|
|
)}
|
|
</motion.div>
|
|
<motion.p
|
|
initial={{ opacity: 0, y: 5 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: index * 0.1 + 0.1 }}
|
|
className={`
|
|
mt-2 text-xs font-medium whitespace-nowrap
|
|
${
|
|
isCurrent || isCompleted
|
|
? 'text-slate-900'
|
|
: 'text-slate-500'
|
|
}
|
|
`}
|
|
>
|
|
{step.label}
|
|
</motion.p>
|
|
</div>
|
|
|
|
{/* Connector line */}
|
|
{index < steps.length - 1 && (
|
|
<div className="flex-1 h-1 mx-4 bg-slate-200 rounded-full overflow-hidden">
|
|
<motion.div
|
|
initial={{ width: 0 }}
|
|
animate={{ width: isCompleted ? '100%' : '0%' }}
|
|
transition={{ duration: 0.5, delay: index * 0.1 }}
|
|
className="h-full bg-green-600"
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|