statut agit comme un verrou. Seules les SAEs marquées comme "validee" (par un administrateur) sont techniquement publiables.est_publique (0 ou 1) donne le contrôle final à l'auteur pour décider si le sujet doit apparaître sur la vitrine publique du site./* Structure de la table pour les Situations d'Apprentissage */
CREATE TABLE IF NOT EXISTS SAE (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nom TEXT NOT NULL,
auteur_id INTEGER NOT NULL,
description TEXT,
date_creation TEXT,
documents TEXT,
date_rendu TEXT,
classe_cible TEXT,
/* Verrou administratif */
statut TEXT DEFAULT 'validee',
/* Verrou enseignant (Vitrine) */
est_publique INTEGER DEFAULT 0,
afficher_rendus INTEGER DEFAULT 0,
FOREIGN KEY (auteur_id) REFERENCES Comptes(id) ON DELETE CASCADE
);
/api/sae, la route publique /api/public/sae ne requiert aucun Token JWT.statut = 'validee' ET est_publique = 1 pour prévenir toute fuite de sujets en cours de rédaction.Comptes pour enrichir l'affichage visuel sans exposer d'autres données sensibles du professeur./* Route non protégée (Pas de middleware verifierToken) */
app.get('/api/public/sae', async (req, res) => {
try {
// Extraction filtrée des SAEs autorisées
const saes = await db.all(`
SELECT SAE.*, Comptes.nom AS auteur_nom, Comptes.prenom AS auteur_prenom
FROM SAE
JOIN Comptes ON SAE.auteur_id = Comptes.id
WHERE SAE.statut = 'validee' AND SAE.est_publique = 1
`);
// ... (Logique d'attachement des rendus étudiants)
res.json(saes);
} catch (error) {
res.status(500).json({ message: "Erreur serveur" });
}
});
export const saeService = {
// ... autres méthodes
// Appel de la route publique sans injection de Token
getPublicListeSae: async () => {
const response = await fetch(`${API_BASE_URL}/public/sae`);
// Interception des erreurs HTTP (ex: 500 Internal Server Error)
if (!response.ok) {
throw new Error("Erreur de chargement des ressources publiques.");
}
// Renvoi des données JSON prêtes à l'emploi
return await response.json();
}
};
getPublicListeSae() est déclenchée.getSaesTriees() qui permet au visiteur de les classer par date (proche ou lointaine)./* 1. Déclenchement au chargement de l'application */
useEffect(() => {
setLoading(true);
if (!token) {
// Mode Visiteur : On charge uniquement la vitrine
saeService.getPublicListeSae()
.then(setSaes)
.finally(() => setLoading(false));
}
}, [token]);
/* 2. Rendu de la grille (JSX) */
{getSaesTriees(saes).map(sae => (
{sae.classe_cible}
{sae.nom}
{/* Troncation automatique de la description longue */}
{sae.description.substring(0, 100)}...
Date limite : {formatDateTime(sae.date_rendu)}
))}