# ============================================================
# WEZABI — WINDSURF RULES (Cascade AI Rules)
# Application mobile d'entraide scolaire — Afrique francophone
# Flutter / Firebase / Google AdMob
# Version 1.0 — Juin 2026
# ============================================================
# ─────────────────────────────────────────
# 1. IDENTITÉ DU PROJET
# ─────────────────────────────────────────
project:
name: Wezabi
description: >
Application mobile Flutter d'entraide scolaire peer-to-peer
destinée aux étudiants d'Afrique francophone. Les étudiants
posent des questions, les tuteurs répondent, la communauté vote,
et l'auteur valide la meilleure réponse.
platform: Flutter (Android + iOS)
language: Dart
backend: Firebase (Auth, Firestore, Storage, Cloud Messaging, Cloud
Functions)
monetization: Google AdMob (bannière, interstitiel, pub récompensée)
target_market: Afrique francophone (Côte d'Ivoire, Sénégal, Cameroun,
Mali)
app_language: Français uniquement (V1)
# ─────────────────────────────────────────
# 2. STACK TECHNIQUE OBLIGATOIRE
# ─────────────────────────────────────────
tech_stack:
flutter_sdk: ">=3.0.0"
state_management: provider
navigation: go_router
firebase_packages:
- firebase_core
- firebase_auth # Authentification OTP par téléphone
- cloud_firestore # Base de données temps réel
- firebase_storage # Photos d'exercices et avatars
- firebase_messaging # Notifications push FCM
- cloud_functions # Logique serveur (alertes tuteurs, points)
ui_packages:
- cached_network_image # Chargement optimisé des images
- image_picker # Sélection photo exercice et avatar
ads_packages:
- google_mobile_ads # Bannière + interstitiel + récompensée
utility_packages:
- badges # Badge rouge sur avatar (nouvelles réponses)
- flutter_local_notifications # Notifications locales
forbidden_packages:
- getx # Ne jamais utiliser GetX — utiliser Provider
uniquement
- bloc # Ne pas utiliser BLoC — utiliser Provider uniquement
- riverpod # Ne pas utiliser Riverpod — utiliser Provider
uniquement
- http # Ne pas utiliser http — Firebase gère tout le backend
# ─────────────────────────────────────────
# 3. STRUCTURE DES DOSSIERS — OBLIGATOIRE
# ─────────────────────────────────────────
folder_structure:
lib/:
screens/:
auth/:
- splash_screen.dart
- phone_screen.dart
- otp_screen.dart
- onboarding_screen.dart
home/:
- home_screen.dart
question/:
- post_question_screen.dart
- question_detail_screen.dart
ranking/:
- ranking_screen.dart
profile/:
- profile_screen.dart
notifications/:
- notif_screen.dart
search/:
- search_screen.dart
widgets/:
- question_card.dart
- answer_card.dart
- ad_banner_widget.dart
- notif_badge_widget.dart
models/:
- user_model.dart
- question_model.dart
- answer_model.dart
services/:
- auth_service.dart
- firestore_service.dart
- points_service.dart
- notification_service.dart
- ad_service.dart
theme/:
- app_theme.dart
- [Link]
assets/:
mockups/:
- "*.png" # Références visuelles des écrans
# ─────────────────────────────────────────
# 4. IDENTITÉ VISUELLE — COULEURS OFFICIELLES
# ─────────────────────────────────────────
design:
palette:
primaryColor: "#2563EB" # Bleu électrique — boutons, liens, actif
backgroundColor: "#0F172A" # Noir profond — fond de tous les écrans
surfaceColor: "#1E293B" # Gris sombre — cartes, champs de saisie
accentColor: "#F97316" # Orange vif — avatar, badge, accent
successColor: "#10B981" # Vert émeraude — validation, succès
textColor: "#F8FAFC" # Blanc cassé — texte principal
mutedColor: "#64748B" # Gris moyen — texte secondaire
errorColor: "#DC2626" # Rouge — erreurs uniquement
typography:
font_family: "SF Pro Display" # Fallback: system font
heading_weight: 800
body_weight: 400
button_weight: 700
border_radius:
card: 12.0
button: 12.0
chip: 20.0
avatar: 999.0 # Circulaire
modal: 20.0
logo:
icon: "🧠"
background: "linear-gradient(135deg, #2563EB, #1D4ED8)"
border_radius: 18.0
text: "Wezabi"
text_color_we: "#F8FAFC"
text_color_zabi: "#F97316" # "zabi" toujours en orange
nav_bar:
items:
- icon: "Icons.chat_bubble_rounded"
label: "Fil"
index: 0
- icon: "Icons.search_rounded"
label: "Chercher"
index: 1
- type: "FAB"
icon: "Icons.add_rounded"
color: "#2563EB"
index: 2
- icon: "Icons.star_rounded"
label: "Classement"
index: 3
# ─────────────────────────────────────────
# 5. STRUCTURE FIRESTORE — SCHÉMA OBLIGATOIRE
# ─────────────────────────────────────────
firestore_schema:
collection_users:
path: "users/{uid}"
fields:
- uid: String
- name: String
- phone: String
- level: String # Seconde / Première / Terminale / Licence
1/2/3
- city: String
- subjects: List<String> # Matières favorites
- points: int # Points totaux
- rank: String # Apprenti / Tuteur / Mentor / Sage
- answersGiven: int
- questionsAsked: int
- validatedAnswers: int
- fcmToken: String # Token Firebase Cloud Messaging
- createdAt: Timestamp
collection_questions:
path: "questions/{questionId}"
fields:
- text: String # Contenu de la question (max 500 chars)
- subject: String # Maths / Physique / Chimie / Français / SVT
/ Anglais / Histoire-Géo / Philosophie
- level: String
- authorId: String
- authorName: String
- imageUrl: String? # Photo d'exercice (optionnel)
- createdAt: Timestamp
- answersCount: int # Default 0
- votesCount: int # Default 0
- isPinned: bool # Default false (pub récompensée)
- pinnedUntil: Timestamp? # Expiration de l'épinglage
- hasValidatedAnswer: bool # Default false
- unreadAnswers: int # Compteur pour badge avatar auteur
subcollection_answers:
path: "questions/{questionId}/answers/{answerId}"
fields:
- text: String
- authorId: String
- authorName: String
- authorRank: String
- votes: int # Default 0
- voterIds: List<String> # UIDs ayant voté (un vote par user)
- isValidated: bool # Default false
- validatedAt: Timestamp?
- score: int # (votes × 1) + (isValidated ? 5 : 0)
- createdAt: Timestamp
# ─────────────────────────────────────────
# 6. RÈGLES MÉTIER CRITIQUES
# ─────────────────────────────────────────
business_rules:
authentication:
- Inscription et connexion UNIQUEMENT par numéro de téléphone (OTP SMS)
- Aucun champ email dans toute l'application
- Pays disponibles au sélecteur: CI (+225), SN (+221), CM (+237), ML
(+223), BF (+226)
questions:
- Longueur maximale: 500 caractères
- Photo optionnelle via image_picker → Firebase Storage
- Matières disponibles: Maths, Physique, Chimie, Français, SVT,
Anglais, Histoire-Géo, Philosophie
- Niveaux disponibles: Seconde, Première, Terminale, Licence 1, Licence
2, Licence 3
- Les questions épinglées (isPinned=true) s'affichent TOUJOURS en tête
du fil
- Badge "✅ Résolue" affiché si hasValidatedAnswer=true (bordure gauche
verte)
answers:
- Un utilisateur ne peut PAS répondre à sa propre question
- Chaque réponse soumise déclenche immédiatement +5 pts à l'auteur
- Chaque réponse soumise incrémente unreadAnswers de la question
parente
- Chaque réponse soumise envoie une notification push à l'auteur de la
question
votes:
- Un utilisateur ne peut voter QU'UNE SEULE FOIS par réponse
- Un utilisateur ne peut PAS voter pour sa propre réponse
- Vérification via voterIds (List<String>) dans Firestore
- Chaque vote = +2 pts au répondeur + notification push au répondeur
- Le score de la réponse = (votes × 1) + (isValidated ? 5 : 0)
validation:
- Le bouton "✅ Ça m'a aidé" est visible UNIQUEMENT pour l'auteur de la
question
- L'auteur ne peut PAS valider sa propre réponse ([Link] !=
[Link])
- Plusieurs réponses peuvent être validées sur une même question
- Une réponse validée ne peut PAS être dé-validée
- Validation → isValidated=true + score recalculé + +10 pts au
répondeur
- Validation → notification push au répondeur
- Validation → hasValidatedAnswer=true sur la question parente
- Après validation → bouton remplacé par label "✅ Validée" non
cliquable
points_and_ranks:
- +5 pts: réponse donnée (immédiat)
- +2 pts: vote reçu sur une réponse
- +10 pts: réponse validée par l'auteur
- Rangs automatiques mis à jour après chaque gain de points:
0-50 pts → "🌱 Apprenti"
51-200 pts → "📚 Tuteur"
201-500 pts → "🎓 Mentor"
500+ pts → "👑 Sage"
notifications:
- Notification à l'auteur de la question quand une réponse est ajoutée
- Notification au répondeur quand sa réponse reçoit un vote
- Notification au répondeur quand sa réponse est validée
- Notification aux tuteurs d'une matière quand une question est publiée
(max 50 tuteurs)
- Jamais de notification si l'utilisateur est l'auteur de l'action
# ─────────────────────────────────────────
# 7. RÈGLES PUBLICITAIRES ADMOB
# ─────────────────────────────────────────
admob_rules:
banner:
position: "En bas du fil des questions, au-dessus de la nav bar"
screens: ["home_screen"]
never_show_on: ["post_question_screen", "notif_screen",
"question_detail_screen (pendant saisie réponse)"]
interstitial:
trigger: "Toutes les 5 questions consultées (compteur dans Provider)"
screen: "question_detail_screen (à l'ouverture)"
never_on_first_launch: true
counter_reset_after_show: true
rewarded:
screen: "post_question_screen"
placement: "Carte orange avant le bouton Publier"
label: "📌 Épingle ta question 48h pour plus de réponses !"
button: "▶ Voir une pub (30s)"
reward: "isPinned=true dans Firestore pendant 48h"
effect: "Question remonte en tête du fil avec badge 📌"
general_rules:
- Jamais de pub pendant la saisie d'une réponse
- Jamais de pub sur l'écran de notifications
- Respecter les délais minimum entre interstitiels (min 30s)
- Utiliser les IDs de test AdMob en mode développement
# ─────────────────────────────────────────
# 8. CONVENTIONS DE CODE
# ─────────────────────────────────────────
coding_conventions:
dart:
- Utiliser const constructors partout où c'est possible
- Nommer les widgets en PascalCase (ex: QuestionCard, AnswerCard)
- Nommer les fichiers en snake_case (ex: question_card.dart)
- Nommer les variables en camelCase (ex: answerCount)
- Toujours typer les variables explicitement (pas de var sauf cas
évident)
- Utiliser final pour les variables immuables
- Séparer la logique métier des widgets (services/)
flutter:
- Chaque écran est un StatelessWidget ou StatefulWidget dans son propre
fichier
- Utiliser Consumer<Provider> pour accéder aux données du Provider
- Utiliser StreamBuilder pour les données Firestore en temps réel
- Toujours gérer les états: loading, error, empty, data
- Utiliser CachedNetworkImage pour toutes les images réseau
- Ne jamais hardcoder les couleurs — utiliser app_theme.dart uniquement
firebase:
- Toujours gérer les erreurs Firebase avec try/catch
- Utiliser les transactions Firestore pour les opérations atomiques
(votes, points)
- Ne jamais exposer les règles de sécurité Firestore côté client
- Utiliser Cloud Functions pour toute logique serveur sensible (points,
alertes)
performance:
- Paginer les listes avec 20 éléments par page (Firestore pagination)
- Utiliser des indexes Firestore pour les requêtes de tri
- Compresser les images avant upload (max 800px, qualité 70%)
- Lazy loading pour les images dans les listes
# ─────────────────────────────────────────
# 9. GESTION DES ERREURS — MESSAGES UTILISATEUR
# ─────────────────────────────────────────
error_messages:
auth:
invalid_phone: "Numéro de téléphone invalide. Vérifie le format."
invalid_otp: "Code incorrect. Vérifie le SMS reçu."
otp_expired: "Code expiré. Demande un nouveau code."
network_error: "Pas de connexion. Vérifie ta connexion internet."
question:
empty_text: "Écris ta question avant de publier."
too_long: "Ta question dépasse 500 caractères."
no_subject: "Sélectionne une matière."
no_level: "Sélectionne ton niveau scolaire."
upload_error: "Erreur lors de l'envoi de la photo. Réessaie."
answer:
empty_text: "Écris ta réponse avant d'envoyer."
own_question: "Tu ne peux pas répondre à ta propre question."
vote:
already_voted: "Tu as déjà voté pour cette réponse."
own_answer: "Tu ne peux pas voter pour ta propre réponse."
general:
network: "Connexion perdue. Vérifie ta connexion."
unknown: "Une erreur est survenue. Réessaie."
# ─────────────────────────────────────────
# 10. COMPORTEMENTS INTERDITS
# ─────────────────────────────────────────
forbidden:
- Ne jamais utiliser de couleurs hardcodées dans les widgets (toujours
app_theme.dart)
- Ne jamais afficher le bouton "✅ Ça m'a aidé" à un non-auteur de la
question
- Ne jamais permettre à un utilisateur de voter pour sa propre réponse
- Ne jamais permettre à un utilisateur de valider sa propre réponse
- Ne jamais afficher de publicité pendant la saisie d'une réponse
- Ne jamais utiliser un autre système d'authentification que Firebase
Phone Auth
- Ne jamais stocker le mot de passe ou des données sensibles localement
- Ne jamais afficher les scores des autres utilisateurs comme des données
personnelles
- Ne jamais supprimer une réponse validée
- Ne jamais modifier les points manuellement côté client (uniquement via
Cloud Functions)
# ─────────────────────────────────────────
# 11. RÉFÉRENCE VISUELLE
# ─────────────────────────────────────────
visual_reference:
mockups_folder: "assets/mockups/"
description: >
Avant de coder chaque écran, consulte l'image de référence
correspondante dans assets/mockups/. Ces images représentent
exactement le design attendu. Respecte scrupuleusement :
les couleurs, l'espacement, la typographie, les icônes,
la disposition des éléments et les états (loading, error, empty, data).
screens:
- [Link] → Palette couleurs + structure dossiers
- [Link] → Splash, téléphone, OTP, onboarding
- [Link] → Fil des questions + filtres
- [Link] → Poster une question
- [Link] → Réponses + validation
- [Link] → Notifications + badge avatar
- [Link] → Classement + rangs
- [Link] → Profil utilisateur
- [Link] → Monétisation AdMob
# ─────────────────────────────────────────
# 12. INSTRUCTIONS GÉNÉRALES POUR CASCADE
# ─────────────────────────────────────────
cascade_instructions:
- >
À chaque fois que tu crées un nouvel écran, consulte d'abord
l'image de référence dans assets/mockups/ avant d'écrire le code.
- >
Utilise TOUJOURS les couleurs définies dans app_theme.dart.
Ne jamais hardcoder une couleur hexadécimale dans un widget.
- >
Après chaque écran créé, vérifie que tous les états sont gérés :
état loading (CircularProgressIndicator bleu), état erreur
(message centré avec bouton retry), état vide (message encourageant),
état données (contenu normal).
- >
Les textes de l'interface sont TOUS en français.
Les commentaires dans le code peuvent être en anglais.
- >
Chaque service (auth_service, firestore_service, etc.) doit être
injecté via Provider. Ne jamais instancier un service directement
dans un widget.
- >
Toujours tester le cas où l'utilisateur n'a pas de connexion internet.
Afficher un message d'erreur approprié sans crash.
- >
Le scoring des réponses (votes × 1 + isValidated × 5) doit être
recalculé côté serveur via Cloud Functions, jamais côté client.
- >
Pour les notifications push, utiliser Firebase Cloud Messaging (FCM).
Stocker le fcmToken dans le document users/{uid} à chaque connexion.