"""Route accueil — écran dynamique par rôle.""" from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.core.database import get_db from app.core.security import get_current_membre, is_sysadmin from app.models.affectation import Affectation from app.models.poste import Poste from app.models.groupe import Groupe from app.models.poste_module import PosteModule from app.schemas.schemas import AccueilResponse, ModuleTuile, MembrePublic router = APIRouter(prefix="/accueil", tags=["Accueil"]) # ── Définition des modules ── MODULES_CONSULTATION = [ ModuleTuile(code="C1", nom="Rapports PDF", description="Finances, PV, résolutions", icone="📄", couleur="blue"), ModuleTuile(code="C2", nom="Événements", description="Anniversaires et événements", icone="📅", couleur="green"), ModuleTuile(code="C4", nom="Littérature", description="Inventaire et prix", icone="📚", couleur="purple"), ModuleTuile(code="C5", nom="Gouvernance", description="Décisions du groupe", icone="📌", couleur="red"), ModuleTuile(code="C7", nom="Mon profil", description="Modifier, PIN, notifications", icone="👤", couleur="gray"), ModuleTuile(code="C9", nom="Postes à pourvoir", description="Postes disponibles, candidature", icone="🙋", couleur="yellow"), ModuleTuile(code="C11", nom="Principes d'utilisation", description="L'application comme outil de service", icone="🧭", couleur="indigo"), ModuleTuile(code="C12", nom="Mode papier", description="Formulaires de reprise en main", icone="🖨️", couleur="gray"), ModuleTuile(code="S1T", nom="Trésorerie", description="État des résultats, soldes, registre", icone="🏦", couleur="green"), ] MODULES_PAR_CODE = {m.code: m for m in MODULES_CONSULTATION} MODULES_PAR_CODE["S0"] = ModuleTuile(code="S0", nom="Rencontres", description="Saisies, ventes, jetons et rapports", icone="🗓️", couleur="green") MODULES_PAR_CODE["M0"] = ModuleTuile(code="M0", nom="Assemblées", description="Présences, procès-verbal et rapports", icone="🏛️", couleur="indigo") MODULES_PAR_CODE["S1"] = ModuleTuile(code="S1", nom="Collecte 7e", description="Saisir le panier", icone="💰", couleur="green") MODULES_SAISIE_TOUS = [ ModuleTuile(code="S5", nom="Dépense", description="Soumettre une dépense", icone="💸", couleur="red"), ] # Modules de saisie par poste MODULES_SAISIE = { "Trésorier(ière)": [ ModuleTuile(code="M4", nom="Donner au suivant", description="Destinataires et contributions", icone="📮", couleur="blue"), ], "Secrétaire": [ ModuleTuile(code="M1", nom="Rédiger PV", description="Procès-verbal", icone="📝", couleur="blue"), ModuleTuile(code="M2", nom="Présences", description="Réunion d'affaires", icone="✅", couleur="green"), ], "RSG": [ ModuleTuile(code="M3", nom="Rapport RSG", description="Compte-rendu district", icone="📋", couleur="blue"), ], "RSG Substitut(e)": [ ModuleTuile(code="M3", nom="Rapport RSG", description="Compte-rendu district", icone="📋", couleur="blue"), ], "Animateur(trice)": [], "Responsable littérature": [ ModuleTuile(code="S3", nom="Vente littérature", description="Enregistrer une vente", icone="📖", couleur="purple"), ModuleTuile(code="M5", nom="Gestion des inventaires", description="Littérature et jetons", icone="📦", couleur="orange"), ], "Responsable jetons": [ ModuleTuile(code="S2", nom="Remise jeton", description="Enregistrer un jeton", icone="🏅", couleur="yellow"), ModuleTuile(code="M5", nom="Gestion des inventaires", description="Littérature et jetons", icone="📦", couleur="orange"), ], } MODULES_GESTION = [ ModuleTuile(code="G2", nom="Gestion des membres", description="Membres, invitations, désactivation", icone="👥", couleur="blue"), ModuleTuile(code="G9", nom="Paramètres groupe", description="Nom, horaire, adresse", icone="🔧", couleur="gray"), ModuleTuile(code="G11", nom="Gestion des mandats", description="Affecter, terminer, rotations", icone="🎯", couleur="purple"), ModuleTuile(code="G12", nom="Gestion des postes", description="Postes, candidatures, accès", icone="📋", couleur="purple"), ModuleTuile(code="G13", nom="Journal", description="Changements enregistrés", icone="🧾", couleur="gray"), ] MODULE_SYSADMIN = ModuleTuile(code="G14", nom="Gestion des groupes", description="Groupes et démonstration", icone="🏷️", couleur="indigo") for _modules in [MODULES_SAISIE_TOUS, MODULES_GESTION, *MODULES_SAISIE.values()]: for _m in _modules: MODULES_PAR_CODE[_m.code] = _m @router.get("/", response_model=AccueilResponse) async def accueil( membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db), ): """Écran d'accueil dynamique — retourne les modules selon les postes du membre.""" if is_sysadmin(membre): modules_saisie_sysadmin = [] codes_vus = set() for module in [MODULES_PAR_CODE["S0"], MODULES_PAR_CODE["M0"], *MODULES_SAISIE_TOUS, *[m for mods in MODULES_SAISIE.values() for m in mods]]: if module.code not in codes_vus: modules_saisie_sysadmin.append(module) codes_vus.add(module.code) groupe = await db.get(Groupe, membre.groupe_id) groupe_info = None if groupe: groupe_info = { "id": str(groupe.id), "nom": groupe.nom, "jour_reunion": groupe.jour_reunion, "heure_reunion": groupe.heure_reunion.strftime("%H:%M") if groupe.heure_reunion else None, "assemblee_affaires_ordre": groupe.assemblee_affaires_ordre, "adresse_local": groupe.adresse_local, } return AccueilResponse( membre=MembrePublic.model_validate(membre), postes=["Sysadmin"], est_executif=True, modules_consultation=MODULES_CONSULTATION, modules_saisie=modules_saisie_sysadmin, modules_gestion=[MODULE_SYSADMIN, *MODULES_GESTION], groupe=groupe_info, ) # Récupérer les affectations actives result = await db.execute( select(Affectation).join(Poste).where( Affectation.membre_id == membre.id, Affectation.groupe_id == membre.groupe_id, Affectation.statut == "actif", Poste.groupe_id == membre.groupe_id, ) ) affectations = result.scalars().all() postes_noms = [] postes_categories = [] poste_ids = [] for a in affectations: poste = await db.get(Poste, a.poste_id) if poste and poste.groupe_id == membre.groupe_id: postes_noms.append(poste.nom) postes_categories.append(poste.categorie) poste_ids.append(poste.id) # Modules de consultation : toujours visibles modules_consultation = MODULES_CONSULTATION # Modules de saisie : collecte pour tous + modules par poste modules_saisie = list(MODULES_SAISIE_TOUS) codes_vus = {"S5"} result = await db.execute(select(PosteModule).where(PosteModule.poste_id.in_(poste_ids))) if poste_ids else None modules_configures = result.scalars().all() if result else [] if modules_configures: codes = sorted({pm.module_code for pm in modules_configures}) for code in codes: if code == "S1" and "executif" not in postes_categories: continue m = MODULES_PAR_CODE.get(code) if m and not code.startswith("G") and m.code not in codes_vus: modules_saisie.append(m) codes_vus.add(m.code) else: for nom_poste in postes_noms: for m in MODULES_SAISIE.get(nom_poste, []): if m.code not in codes_vus: modules_saisie.append(m) codes_vus.add(m.code) if "executif" in postes_categories and "S1" not in codes_vus: modules_saisie.insert(0, MODULES_PAR_CODE["S0"]) modules_saisie.insert(1, MODULES_PAR_CODE["M0"]) codes_vus.add("S0") codes_vus.add("M0") # Modules de gestion : seulement si exécutif if modules_configures: modules_gestion = [ MODULES_PAR_CODE[code] for code in sorted({pm.module_code for pm in modules_configures}) if code.startswith("G") and code in MODULES_PAR_CODE ] else: modules_gestion = MODULES_GESTION if "executif" in postes_categories else [] # Info groupe pour le frontend groupe = await db.get(Groupe, membre.groupe_id) groupe_info = None if groupe: groupe_info = { "id": str(groupe.id), "nom": groupe.nom, "jour_reunion": groupe.jour_reunion, "heure_reunion": groupe.heure_reunion.strftime("%H:%M") if groupe.heure_reunion else None, "assemblee_affaires_ordre": groupe.assemblee_affaires_ordre, "adresse_local": groupe.adresse_local, } return AccueilResponse( membre=MembrePublic.model_validate(membre), postes=postes_noms, est_executif="executif" in postes_categories, modules_consultation=modules_consultation, modules_saisie=modules_saisie, modules_gestion=modules_gestion, groupe=groupe_info, )