/** * Register Page - Xpeditis * * Modern registration page with split-screen design */ 'use client'; import { useState, useEffect } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import Link from 'next/link'; import Image from 'next/image'; import { register } from '@/lib/api'; import { verifyInvitation, type InvitationResponse } from '@/lib/api/invitations'; import type { OrganizationType } from '@/types/api'; export default function RegisterPage() { const router = useRouter(); const searchParams = useSearchParams(); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); // Organization fields const [organizationName, setOrganizationName] = useState(''); const [organizationType, setOrganizationType] = useState('FREIGHT_FORWARDER'); const [street, setStreet] = useState(''); const [city, setCity] = useState(''); const [state, setState] = useState(''); const [postalCode, setPostalCode] = useState(''); const [country, setCountry] = useState('FR'); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); // Invitation-related state const [invitationToken, setInvitationToken] = useState(null); const [invitation, setInvitation] = useState(null); const [isVerifyingInvitation, setIsVerifyingInvitation] = useState(false); // Verify invitation token on mount useEffect(() => { const token = searchParams.get('token'); if (token) { setIsVerifyingInvitation(true); verifyInvitation(token) .then(invitationData => { setInvitation(invitationData); setInvitationToken(token); // Pre-fill user information from invitation setEmail(invitationData.email); setFirstName(invitationData.firstName); setLastName(invitationData.lastName); }) .catch(err => { setError('Le lien d\'invitation est invalide ou expiré.'); }) .finally(() => { setIsVerifyingInvitation(false); }); } }, [searchParams]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(''); // Validate passwords match if (password !== confirmPassword) { setError('Les mots de passe ne correspondent pas'); return; } // Validate password length if (password.length < 12) { setError('Le mot de passe doit contenir au moins 12 caractères'); return; } // Validate organization fields only if NOT using invitation if (!invitationToken) { if (!organizationName.trim()) { setError('Le nom de l\'organisation est requis'); return; } if (!street.trim() || !city.trim() || !postalCode.trim() || !country.trim()) { setError('Tous les champs d\'adresse sont requis'); return; } } setIsLoading(true); try { await register({ email, password, firstName, lastName, // If invitation token exists, use it; otherwise provide organization data ...(invitationToken ? { invitationToken } : { organization: { name: organizationName, type: organizationType, street, city, state: state || undefined, postalCode, country: country.toUpperCase(), }, }), }); router.push('/dashboard'); } catch (err: any) { setError(err.message || 'Erreur lors de la création du compte'); } finally { setIsLoading(false); } }; return (
{/* Left Side - Form */}
{/* Logo */}
Xpeditis
{/* Header */}

{invitation ? 'Accepter l\'invitation' : 'Créer un compte'}

{invitation ? `Vous avez été invité à rejoindre une organisation` : 'Commencez votre essai gratuit dès aujourd\'hui'}

{/* Verifying Invitation Loading */} {isVerifyingInvitation && (

Vérification de l'invitation...

)} {/* Success Message for Invitation */} {invitation && !error && (

Invitation valide ! Créez votre mot de passe pour rejoindre l'organisation.

)} {/* Error Message */} {error && (

{error}

)} {/* Form */}
{/* First Name & Last Name */}
setFirstName(e.target.value)} className="input w-full" placeholder="Jean" disabled={isLoading || !!invitation} />
setLastName(e.target.value)} className="input w-full" placeholder="Dupont" disabled={isLoading || !!invitation} />
{/* Email */}
setEmail(e.target.value)} className="input w-full" placeholder="jean.dupont@entreprise.com" autoComplete="email" disabled={isLoading || !!invitation} />
{/* Password */}
setPassword(e.target.value)} className="input w-full" placeholder="••••••••••••" autoComplete="new-password" disabled={isLoading} />

Au moins 12 caractères

{/* Confirm Password */}
setConfirmPassword(e.target.value)} className="input w-full" placeholder="••••••••••••" autoComplete="new-password" disabled={isLoading} />
{/* Organization Section - Only show if NOT using invitation */} {!invitation && (

Informations de votre organisation

{/* Organization Name */}
setOrganizationName(e.target.value)} className="input w-full" placeholder="Acme Logistics" disabled={isLoading} />
{/* Organization Type */}
{/* Street Address */}
setStreet(e.target.value)} className="input w-full" placeholder="123 Rue de la République" disabled={isLoading} />
{/* City & Postal Code */}
setCity(e.target.value)} className="input w-full" placeholder="Paris" disabled={isLoading} />
setPostalCode(e.target.value)} className="input w-full" placeholder="75001" disabled={isLoading} />
{/* State & Country */}
setState(e.target.value)} className="input w-full" placeholder="Île-de-France" disabled={isLoading} />
setCountry(e.target.value)} className="input w-full" placeholder="FR" maxLength={2} disabled={isLoading} />
)} {/* Submit Button */} {/* Terms */}

En créant un compte, vous acceptez nos{' '} Conditions d'utilisation {' '} et notre{' '} Politique de confidentialité

{/* Sign In Link */}

Vous avez déjà un compte ?{' '} Se connecter

{/* Footer Links */}
Centre d'aide Contactez-nous Confidentialité Conditions
{/* Right Side - Brand Features (same as login) */}

Rejoignez des milliers d'entreprises

Simplifiez votre logistique maritime et gagnez du temps sur chaque expédition.

Essai gratuit de 30 jours

Testez toutes les fonctionnalités sans engagement

Sécurité maximale

Vos données sont protégées et chiffrées

Support 24/7

Notre équipe est là pour vous accompagner

2k+
Entreprises
150+
Pays couverts
24/7
Support
); }