Module de Suivi : Évaluation des Statuts en Temps Réel

Retour à l'organigramme
Architecture Frontend

Pourquoi calculer le statut côté client ?

  • Le problème d'une base de données : Si on stockait le statut "En retard" dans SQLite, il faudrait un script de fond (Cron Job) tournant chaque minute pour mettre à jour la base dès qu'une date limite est franchie.
  • La solution React : La base de données ne fournit que des faits immuables (la date de rendu requise et l'existence ou non d'une soumission).
  • Évaluation à la volée : C'est le navigateur de l'étudiant qui, au moment précis d'afficher la page, compare ces données avec son horloge actuelle. L'état est toujours garanti 100% exact et à jour.
/* Les données "brutes" fournies par l'API (Backend) */
const donneeServeur = {
    id: 42,
    nom: "SAE 301 - Développement",
    date_rendu: "2023-12-01T23:59:00", // Le fait (Date limite)
    rendu_id: null                     // Le fait (Travail non rendu)
};

// Le Frontend prend ces données et calcule le contexte 
// en fonction de la variable d'environnement (new Date()).
React / frontend/src/App.jsx

L'algorithme de détermination

  • Fonction Pure : determinerStatut(sae) prend un objet en entrée et renvoie toujours le même résultat pour les mêmes conditions temporelles.
  • Règle 1 (Priorité absolue) : Si un identifiant de rendu existe, la SAE est "Terminée" (Vert), peu importe les dates.
  • Règle 2 (Tolérance) : Si aucune date limite n'est spécifiée par le professeur, elle reste perpétuellement "En cours".
  • Règle 3 (Échéance) : On convertit la chaîne ISO de la BDD en un objet Date() JavaScript pour le comparer mathématiquement (<) avec l'instant T. Si le délai est dépassé, c'est "En retard" (Rouge).
/* Algorithme d'évaluation des priorités */
const determinerStatut = (sae) => {
    // 1. Validation de la soumission (Priorité Haute)
    if (sae.rendu_id || (selectedRendu && selectedSae?.id === sae.id)) {
        return { texte: 'Terminée', couleur: 'success' };
    }
    
    // 2. Gestion des SAE ouvertes sans deadline
    if (!sae.date_rendu) {
        return { texte: 'En cours', couleur: 'primary' };
    }
    
    // 3. Comparaison temporelle en temps réel
    const isRetard = new Date(sae.date_rendu) < new Date();
    
    if (isRetard) {
        return { texte: 'En retard', couleur: 'danger' };
    }
    
    // 4. Par défaut (Date future, non rendu)
    return { texte: 'En cours', couleur: 'primary' };
};
React / frontend/src/App.jsx

Connexion avec le système de filtre

  • State Array : Les cases à cocher de l'interface ajoutent ou retirent des mots-clés dans un tableau filtresStatut (ex: ['En cours', 'En retard']).
  • Interception : Dans la fonction de préparation de la grille (getSaesTriees), chaque SAE est passée dans la moulinette de determinerStatut.
  • Inclusion : Si le texte du statut renvoyé est inclus dans les préférences de l'utilisateur, la SAE est affichée. Sinon, elle disparaît instantanément.
/* L'état React (Ce qui est coché par l'utilisateur) */
const [filtresStatut, setFiltresStatut] = useState(['En cours', 'En retard', 'Terminée']);

/* Application du filtre dans le pipeline de rendu */
const getSaesTriees = (listeASorter) => {
    return [...listeASorter].filter(sae => {
       // Les enseignants et admins voient tout
       if (role !== 'etudiant') return true;
       
       // On calcule le statut exact à cet instant
       const statutCalcule = determinerStatut(sae).texte;
       
       // On vérifie si ce statut fait partie des filtres actifs
       return filtresStatut.includes(statutCalcule);
    })
    // ... enchaînement avec .sort() pour les dates
};
React / frontend/src/App.jsx

Rendu Visuel et Badges

  • Déstructuration : La fonction de calcul renvoie un objet contenant le texte exact à afficher et la couleur de la classe CSS.
  • Template Literals : L'attribut className de la balise HTML est construit dynamiquement (ex: badge-danger, badge-success) pour lier la logique JavaScript à la feuille de style (App.css).
{/* À l'intérieur du composant Map qui génère la grille */}
{getSaesTriees(saes).map(sae => {
  // Appel de l'algorithme une seule fois par carte
  const statut = determinerStatut(sae);
  
  return (
    
{sae.classe_cible} {/* Injection dynamique de la classe et du label */} {statut.texte}

{sae.nom}

{/* ... */}
); })}