Checkpoint: Ajout d'un encart dédié pour afficher les signatures des formateurs sur la page d'émargement numérique

Modifications apportées :
- Backend : Ajout de la fonction getSignaturesFormateursBySequence dans presenceDb.ts
- Backend : Ajout de la procédure tRPC presences.listSignaturesFormateurs dans routers.ts
- Frontend : Ajout de l'encart visuel des signatures formateurs dans FormateurEmargement.tsx
- L'encart affiche les signatures avec date, période (matin/après-midi), heure et image de signature
- Rafraîchissement automatique toutes les 30 secondes
- Design distinct avec bordure bleue pour différencier des signatures apprenants
- Déployé sur recette (108.143.67.56) et production (98.71.216.234)
This commit is contained in:
Manus
2026-02-09 11:04:09 -05:00
parent a40e0bf6cd
commit 37924ad4fd
4 changed files with 120 additions and 1 deletions

View File

@@ -69,6 +69,12 @@ export default function FormateurEmargement() {
{ enabled: !!selectedSequenceId }
);
// Récupérer les signatures formateurs de la séquence
const { data: signaturesFormateurs, refetch: refetchSignaturesFormateurs } = trpc.presences.listSignaturesFormateurs.useQuery(
{ sequenceId: selectedSequenceId || 0 },
{ enabled: !!selectedSequenceId }
);
// Rafraîchissement automatique toutes les 30 secondes
useEffect(() => {
if (!selectedSequenceId) return;
@@ -76,12 +82,13 @@ export default function FormateurEmargement() {
const interval = setInterval(() => {
refetchInscrits();
refetchPresences();
refetchSignaturesFormateurs();
setLastRefreshTime(new Date());
setSecondsSinceRefresh(0);
}, 30000); // 30 secondes
return () => clearInterval(interval);
}, [selectedSequenceId, refetchInscrits, refetchPresences]);
}, [selectedSequenceId, refetchInscrits, refetchPresences, refetchSignaturesFormateurs]);
// Compteur pour "Mis à jour il y a X secondes"
useEffect(() => {
@@ -262,6 +269,7 @@ export default function FormateurEmargement() {
onClick={() => {
refetchInscrits();
refetchPresences();
refetchSignaturesFormateurs();
setLastRefreshTime(new Date());
setSecondsSinceRefresh(0);
}}
@@ -485,6 +493,67 @@ export default function FormateurEmargement() {
</Card>
)}
{/* Encart des signatures formateurs */}
{selectedSequenceId && signaturesFormateurs && signaturesFormateurs.length > 0 && (
<Card className="border-2 border-blue-200 bg-blue-50/30">
<CardHeader>
<CardTitle className="text-blue-700">Signatures Formateurs</CardTitle>
<CardDescription>
Signatures tactiles des formateurs pour cette séquence
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{signaturesFormateurs.map((signature: any) => (
<div
key={signature.presenceId}
className="border border-blue-200 rounded-lg p-4 bg-white space-y-3"
>
<div className="flex items-center justify-between">
<div>
<p className="font-medium text-blue-900">
{new Date(signature.dateDebut).toLocaleDateString("fr-FR", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
})}
</p>
<p className="text-sm text-muted-foreground">
{signature.periode === "matin" ? "🌅 Matin" : "🌆 Après-midi"}
</p>
</div>
<Badge variant="outline" className="bg-blue-100 text-blue-700 border-blue-300">
{new Date(signature.heurePresence).toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
})}
</Badge>
</div>
{signature.signatureUrl && (
<div className="flex flex-col gap-2">
<p className="text-xs text-muted-foreground">Signature tactile :</p>
<div className="border-2 border-blue-200 rounded-lg p-2 bg-white cursor-pointer hover:border-blue-400 transition-colors"
onClick={() => {
setSelectedSignature(signature.signatureUrl!);
setShowSignatureDialog(true);
}}
>
<img
src={signature.signatureUrl}
alt="Signature formateur"
className="w-full h-24 object-contain"
/>
</div>
</div>
)}
</div>
))}
</div>
</CardContent>
</Card>
)}
{/* Dialog QR Code Apprenants */}
<Dialog open={showQRDialog} onOpenChange={setShowQRDialog}>
<DialogContent className="max-w-md">