import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { trpc } from "@/lib/trpc"; import { useRoute } from "wouter"; import { Skeleton } from "@/components/ui/skeleton"; import { useState, useMemo } from "react"; import { toast } from "sonner"; import { format } from "date-fns"; import { fr } from "date-fns/locale"; import { CheckCircle, XCircle, AlertCircle, Calendar, MapPin, Users } from "lucide-react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { APP_LOGO, APP_TITLE } from "@/const"; export default function Inscription() { const [, params] = useRoute("/inscription/:lien"); const lien = params?.lien || ""; const [formData, setFormData] = useState({ nom: "", prenom: "", email: "", codeEtablissement: "", fonction: "autre" as "directeur" | "chef_service" | "autre", }); const [selectedSequenceId, setSelectedSequenceId] = useState(null); const [inscriptionSuccess, setInscriptionSuccess] = useState(false); const [inscriptionStatut, setInscriptionStatut] = useState(""); const { data: formation, isLoading: loadingFormation } = trpc.formations.getByLien.useQuery({ lien }); const { data: sequences, isLoading: loadingSessions } = trpc.sequences.listByFormation.useQuery( { formationId: formation?.id || 0 }, { enabled: !!formation?.id } ); const utils = trpc.useUtils(); const inscriptionMutation = trpc.inscriptions.inscrire.useMutation({ onSuccess: (data) => { setInscriptionSuccess(true); setInscriptionStatut(data.statut); utils.sequences.listByFormation.invalidate({ formationId: formation?.id || 0 }); utils.apprenants.list.invalidate(); // Invalider la liste des apprenants if (data.statut === "confirmee") { toast.success("Inscription confirmée avec succès !"); } else { toast.info("Vous êtes inscrit en liste d'attente."); } }, onError: (error) => { toast.error(error.message); }, }); const createApprenantMutation = trpc.apprenants.create.useMutation(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedSequenceId) { toast.error("Veuillez sélectionner une sequence"); return; } // Vérifier si le public cible correspond à la fonction de l'apprenant const selectedSequence = sequences?.find(s => s.id === selectedSequenceId); if (selectedSequence && selectedSequence.publicCible) { const publicCibleMap: Record = { "directeur": "directeur", "chef_service": "chef_service", "autre": "autre" }; if (publicCibleMap[selectedSequence.publicCible] !== formData.fonction) { const publicCibleLabel = selectedSequence.publicCible === "directeur" ? "Directeurs" : selectedSequence.publicCible === "chef_service" ? "Chefs de service" : "Autre"; const fonctionLabel = formData.fonction === "directeur" ? "Directeur" : formData.fonction === "chef_service" ? "Chef de service" : "Autre"; toast.info( `Information : Cette séquence est destinée aux ${publicCibleLabel}, mais vous êtes inscrit(e) en tant que ${fonctionLabel}. Vous pouvez tout de même vous inscrire.`, { duration: 8000 } ); // Attendre 2 secondes pour que l'utilisateur puisse lire l'alerte await new Promise(resolve => setTimeout(resolve, 2000)); } } try { // Vérifier si l'apprenant existe déjà const existingApprenant = await utils.client.apprenants.getByEmail.query({ email: formData.email }); let apprenantId: number; if (existingApprenant) { apprenantId = existingApprenant.id; } else { // Créer l'apprenant await createApprenantMutation.mutateAsync(formData); // Récupérer l'apprenant nouvellement créé const newApprenant = await utils.client.apprenants.getByEmail.query({ email: formData.email }); if (!newApprenant) { throw new Error("Erreur lors de la création de l'apprenant"); } apprenantId = newApprenant.id; } // Inscrire l'apprenant à la sequence await inscriptionMutation.mutateAsync({ apprenantId, sequenceId: selectedSequenceId, }); } catch (error: any) { console.error("Erreur lors de l'inscription:", error); } }; // Calculer les places disponibles pour chaque sequence const sessionsAvecPlaces = useMemo(() => { if (!sequences) return []; return sequences.map(sequence => { const now = new Date(); const dateBlocage = new Date(sequence.dateBlocage); const isBloquee = sequence.statut === 'bloquee' || now >= dateBlocage; const placesRestantes = Math.max(0, sequence.capaciteMax - (sequence.nbInscrits || 0)); return { ...sequence, isBloquee, placesRestantes, }; }); }, [sequences]); const formatDate = (dateValue: Date | string) => { if (!dateValue) return "N/A"; try { let localDate: Date; // Si c'est déjà un objet Date JavaScript, l'utiliser directement if (dateValue instanceof Date) { localDate = dateValue; } else { // Parser la date MySQL sans conversion UTC const dateStr = String(dateValue); const match = dateStr.match(/(\d{4})-(\d{2})-(\d{2})[T ]?(\d{2}):(\d{2})/); if (!match) { console.warn('Format de date non reconnu:', dateValue); return "N/A"; } const [, year, month, day, hours, minutes] = match; // Créer une Date en heure locale localDate = new Date( parseInt(year), parseInt(month) - 1, parseInt(day), parseInt(hours), parseInt(minutes) ); } return format(localDate, "EEEE d MMMM yyyy 'à' HH:mm", { locale: fr }); } catch (error) { console.error('Erreur de formatage de date:', error, dateValue); return "N/A"; } }; if (loadingFormation || loadingSessions) { return (
); } if (!formation || !formation.actif) { return (
Formation introuvable

Cette formation n'existe pas ou n'est plus disponible.

); } if (inscriptionSuccess) { return (
Inscription réussie !

{inscriptionStatut === "confirmee" ? "Votre inscription à la formation a été confirmée." : "Vous avez été ajouté à la liste d'attente. Nous vous contacterons si une place se libère."}

Prochaines étapes :

  • Vous recevrez un email de confirmation avec tous les détails
  • Une invitation Outlook sera envoyée pour bloquer votre agenda
  • Un email de rappel vous sera envoyé 7 jours avant la formation

Pour toute question, veuillez contacter le service RH.

); } return (
{/* Header */}
{APP_TITLE}

{formation.nom}

{formation.description && (

{formation.description}

)}
{/* Sessions disponibles */} Sessions disponibles Sélectionnez une sequence pour vous inscrire (capacité : 12 participants maximum) {sessionsAvecPlaces && sessionsAvecPlaces.length > 0 ? (
{sessionsAvecPlaces.map((sequence) => (
!sequence.isBloquee && setSelectedSequenceId(sequence.id)} >

{sequence.nom}

{sequence.dates.map((date: any) => (
Date {date.ordre}: {formatDate(date.dateDebut)}
))}
{sequence.lieu}
Public cible : {sequence.publicCible === "directeur" ? "Directeurs" : sequence.publicCible === "chef_service" ? "Chefs de service" : "Autre"}
{sequence.placesRestantes} places disponibles
{sequence.isBloquee ? ( Fermée ) : sequence.placesRestantes === 0 ? ( Complète ) : ( Disponible )}
))}
) : (
Aucune sequence disponible pour le moment.
)}
{/* Formulaire d'inscription */} {selectedSequenceId && ( Vos informations Complétez le formulaire pour finaliser votre inscription
setFormData({ ...formData, nom: e.target.value })} placeholder="Dupont" required />
setFormData({ ...formData, prenom: e.target.value })} placeholder="Jean" required />
setFormData({ ...formData, email: e.target.value })} placeholder="jean.dupont@example.com" required />
setFormData({ ...formData, codeEtablissement: e.target.value })} placeholder="ETB001" required />

Important : Les inscriptions seront bloquées 15 jours avant le début de la sequence. Après cette date, aucune modification ne sera possible.

)}
); }