espace-paie-odentas/app/signer/[requestId]/[signerId]/components/ProgressBar.tsx
odentas b790faf12c feat: Implémentation complète du système Odentas Sign
- 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)
2025-10-27 19:03:07 +01:00

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>
);
}