# Présentation - Titre Professionnel CDA Niveau 6

---

## Page de garde

Bonjour à tous, je m'appelle **Guns Bruno** et aujourd'hui je me présente à vous dans le cadre du titre professionnel de niveau 6 **Concepteur Développeur d'Application**, aboutissement de ma formation professionnelle au sein de **SEM Numérica**.

---

## Page 1 : Introduction

Durant cette présentation, je vais au travers d'expériences vécues pendant ces 9 mois vous démontrer ma maîtrise par rapport aux **11 compétences requises** à l'obtention du titre professionnel auquel je me présente.

Au cours de cette présentation, vous verrez sur chaque diapositive en haut à droite, à quelle compétence, par le biais d'un numéro, je fais référence lors de mon discours.

### Liste des compétences

1. Installer et configurer son environnement de travail en fonction du projet
2. Développer des interfaces utilisateur
3. Développer des composants métier
4. Contribuer à la gestion d'un projet informatique
5. Analyser les besoins et maquetter une application
6. Définir l'architecture logicielle d'une application
7. Concevoir et mettre en place une base de données relationnelle
8. Développer des composants d'accès aux données SQL et NoSQL
9. Préparer et exécuter les plans de tests d'une application
10. Préparer et documenter le déploiement d'une application
11. Contribuer à la mise en production dans une démarche DevOps

---

## Page 2 : Présentation du projet FailDaily

### C'est quoi FailDaily ?

**FailDaily** est un réseau social mobile qui permet de partager vos petites galères du quotidien de manière drôle et bienveillante.

#### L'idée simple

Vous avez raté votre train ? Renversé votre café sur votre chemise ? Oublié l'anniversaire d'un proche ? Au lieu de rester frustré, vous pouvez partager ce moment sur FailDaily !

#### Comment ça marche ?

1. **Vous publiez** votre "fail" du jour (avec ou sans photo)
   - Vous choisissez si vous voulez le partager de façon **anonyme** ou **visible**
   - Vous sélectionnez une catégorie (Travail, Transports, Amour, Technologie, etc.)

2. **D'autres utilisateurs** réagissent avec 4 types de réactions :
   - 💪 **Courage** : Pour montrer du soutien
   - 😂 **Rire** : Quand c'est vraiment drôle
   - ❤️ **Empathie** : Pour compatir
   - 🤝 **Support** : Pour encourager

3. **Vous gagnez des points** et débloquez des badges en fonction de votre activité
   - Partager des fails
   - Réagir aux fails des autres
   - Commenter et encourager

4. **Vous commentez** pour encourager les autres ou raconter vos propres anecdotes similaires

#### Pourquoi c'est cool ?

- **Anonymat optionnel** : Vous décidez si vous voulez être visible ou anonyme sur chaque fail
- **Bienveillant** : Pas de likes/dislikes négatifs, seulement 4 réactions positives
- **Gamification** : Système de points et badges pour motiver l'entraide
- **Catégories variées** : Travail, Transports, Amour, Technologie, Vie quotidienne, Entraide...

#### En bref

FailDaily, c'est un réseau social où on partage nos moments les moins glorieux avec bienveillance, où la communauté vous comprend et vous soutient plutôt que de juger ! 😊

**Type d'application :** Progressive Web App (PWA) mobile  
**Public cible :** Tout public (13 ans minimum avec accord parental)  
**Ambiance :** Légère, drôle, bienveillante

---

# Présentation FailDaily - Oral CDA

---

## 📋 Phase 1 : CONCEPTION DU PROJET

---

### Slide 1 : Analyser les besoins 📝
**Compétence 5**

#### Ce que j'ai fait en simple :
Avant de coder quoi que ce soit, j'ai d'abord réfléchi à ce que les utilisateurs voulaient vraiment. J'ai listé toutes les fonctionnalités importantes :
- Pouvoir partager un moment embarrassant
- Choisir si on veut rester anonyme ou pas
- Recevoir du soutien des autres (pas des moqueries !)
- Gagner des récompenses pour rester motivé

#### Exemple concret - Ma liste de fonctionnalités :
```markdown
FONCTIONNALITÉS PRINCIPALES :
✓ Inscription avec email + vérification
✓ Publier un "fail" (texte + photo optionnelle)
✓ Choisir : anonyme OU visible
✓ 4 types de réactions positives (courage, rire, empathie, support)
✓ Système de points et badges
✓ Commentaires d'encouragement uniquement
✓ Modération pour protéger la communauté
```

#### Ce qu'on voit dans l'app :
*[Image : Page d'accueil avec liste de fails et bouton "Partager mon fail"]*

---

### Slide 2 : Créer les maquettes 🎨
**Compétence 5**

#### Ce que j'ai fait en simple :
Avant de développer, j'ai dessiné toutes les pages de l'application pour savoir exactement à quoi elle devait ressembler. Comme un architecte dessine une maison avant de la construire.

#### Exemple concret - Structure d'une page :
```
PAGE "PUBLIER UN FAIL"
┌─────────────────────────┐
│  [< Retour]    Publier  │  ← En-tête
├─────────────────────────┤
│ 📝 Titre du fail        │
│ [___________________]   │
│                         │
│ 📖 Raconte ce qui s'est │
│    passé...             │
│ [___________________]   │
│ [___________________]   │
│                         │
│ 📂 Catégorie            │
│ [ Travail ▼ ]           │
│                         │
│ 📸 Ajouter une photo    │
│ [+ Photo optionnelle]   │
│                         │
│ 👻 Mode anonyme         │
│ [ ○ Oui  ● Non ]        │
│                         │
│ [  PUBLIER MON FAIL  ]  │ ← Bouton
└─────────────────────────┘
```

#### Ce qu'on voit dans l'app :
*[Image : Formulaire de publication d'un fail avec tous les champs]*

---

### Slide 3 : Définir l'architecture technique 🏗️
**Compétence 6**

#### Ce que j'ai fait en simple :
J'ai décidé comment les différentes parties de l'application allaient communiquer entre elles. C'est comme organiser les pièces d'une maison : la cuisine, le salon, les chambres... chacune a son rôle.

#### Exemple concret - Les 3 grandes parties :
```
┌──────────────┐
│  FRONTEND    │  ← Ce que l'utilisateur voit et touche
│  (Angular +  │     (l'écran du téléphone)
│   Ionic)     │
└──────┬───────┘
       │ 📱 Envoie des demandes
       ↓
┌──────────────┐
│   BACKEND    │  ← Le cerveau de l'application
│  (Node.js +  │     (gère les demandes, vérifie les droits)
│   Express)   │
└──────┬───────┘
       │ 💾 Stocke/récupère les données
       ↓
┌──────────────┐
│  BASE DE     │  ← La mémoire de l'application
│  DONNÉES     │     (garde tous les fails, utilisateurs, etc.)
│  (MySQL)     │
└──────────────┘
```

#### Ce qu'on voit dans l'app :
*[Image : Schéma simple avec téléphone → serveur → base de données]*

---

### Slide 4 : Concevoir la base de données 🗄️
**Compétence 7**

#### Ce que j'ai fait en simple :
J'ai créé des "boîtes" pour ranger toutes les informations de l'application. Chaque type d'information a sa propre boîte (on appelle ça des "tables").

#### Exemple concret - Table "fails" :
```sql
-- Table qui stocke tous les fails partagés
CREATE TABLE fails (
  id              -- Numéro unique du fail
  user_id         -- Qui l'a posté
  title           -- Le titre (ex: "Raté mon train")
  description     -- L'histoire complète
  category        -- Catégorie (travail, amour, etc.)
  image_url       -- Photo (si l'utilisateur en a mis une)
  is_anonyme      -- Anonyme ? (oui/non)
  created_at      -- Date de publication
)

-- Exemple de fail stocké :
id: "abc123"
user_id: "user456"
title: "Café renversé sur mon patron"
description: "J'ai renversé mon café sur la chemise de mon chef..."
category: "travail"
is_anonyme: false (visible)
created_at: "2025-11-15 09:30:00"
```

#### Ce qu'on voit dans l'app :
*[Image : Carte d'un fail affichée avec titre, description, catégorie]*

---

## 🛠️ Phase 2 : MISE EN PLACE

---

### Slide 5 : Installer l'environnement de travail 💻
**Compétence 1**

#### Ce que j'ai fait en simple :
J'ai installé tous les outils nécessaires sur mon ordinateur, comme un cuisinier qui prépare ses casseroles et ses couteaux avant de cuisiner.

#### Exemple concret - Mes outils installés :
```bash
# Outils installés :

✓ Node.js (v20)          → Pour faire fonctionner le backend
✓ MySQL (v8)             → Pour la base de données
✓ Visual Studio Code     → Mon éditeur de code
✓ Git                    → Pour sauvegarder mon travail
✓ Docker                 → Pour emballer l'application

# Commande pour vérifier les versions :
node --version    # v20.10.0
npm --version     # 10.2.3
mysql --version   # 8.0.35
```

#### Ce qu'on voit dans l'app :
*[Image : Capture d'écran de VS Code avec le projet ouvert]*

---

### Slide 6 : Gérer le projet avec Git 📦
**Compétence 4**

#### Ce que j'ai fait en simple :
J'ai utilisé Git pour sauvegarder chaque étape de mon travail. C'est comme avoir plusieurs "points de sauvegarde" dans un jeu vidéo. Si je fais une erreur, je peux revenir en arrière.

#### Exemple concret - Mes sauvegardes :
```bash
# Historique de mes sauvegardes (commits) :

✅ "Ajout système d'authentification"
✅ "Création formulaire publication fail"
✅ "Ajout système de réactions"
✅ "Correction bug anonymat"
✅ "Ajout système de badges"

# Exemple de sauvegarde :
git add .
git commit -m "✅ FIX: Cache admin pour éviter surcharge serveur"
git push origin main
```

#### Ce qu'on voit dans l'app :
*[Image : Historique Git avec les commits dans GitHub]*

---

## 💻 Phase 3 : DÉVELOPPEMENT

---

### Slide 7 : Développer l'accès aux données 🔌
**Compétence 8**

#### Ce que j'ai fait en simple :
J'ai créé des "portes d'entrée" pour que le backend puisse parler avec la base de données. Chaque action (récupérer des fails, ajouter un commentaire) a sa propre porte.

#### Exemple concret - Récupérer les fails :
```javascript
// Fonction pour récupérer tous les fails depuis la base
async getFails() {
  // Je demande à la base : "Donne-moi tous les fails"
  const query = `
    SELECT id, title, description, category, created_at
    FROM fails
    WHERE status = 'approved'
    ORDER BY created_at DESC
    LIMIT 50
  `;
  
  // J'exécute la demande et je récupère le résultat
  const fails = await database.execute(query);
  
  // Je renvoie la liste des fails
  return fails;
}
```

#### Ce qu'on voit dans l'app :
*[Image : Liste de fails qui s'affichent sur la page d'accueil]*

---

### Slide 8 : Développer la logique métier ⚙️
**Compétence 3**

#### Ce que j'ai fait en simple :
J'ai codé les "règles du jeu" de l'application. Par exemple : quand quelqu'un réagit à un fail, l'auteur gagne des points. Ou : un utilisateur ne peut pas réagir deux fois au même fail.

#### Exemple concret - Système de points :
```javascript
// Fonction : donner des points quand on reçoit une réaction
async givePointsForReaction(fail, reactionType) {
  
  // Règle : chaque réaction donne des points différents
  const points = {
    'courage': 5,   // Réaction courage = 5 points
    'laugh': 3,     // Réaction rire = 3 points
    'empathy': 2,   // Réaction empathie = 2 points
    'support': 3    // Réaction support = 3 points
  };
  
  // Je récupère l'auteur du fail
  const author = fail.user_id;
  
  // Je lui ajoute les points correspondants
  const pointsToAdd = points[reactionType];
  await addPoints(author, pointsToAdd);
  
  // Je vérifie s'il débloque un badge
  await checkBadges(author);
}
```

#### Ce qu'on voit dans l'app :
*[Image : Notification "Vous avez gagné 5 points !" après une réaction]*

---

### Slide 9 : Développer l'interface utilisateur 🎨
**Compétence 2**

#### Ce que j'ai fait en simple :
J'ai créé tout ce que l'utilisateur voit et touche : les boutons, les formulaires, les couleurs, les animations. C'est la "vitrine" de l'application.

#### Exemple concret - Carte d'un fail :
```html
<!-- Code qui affiche une carte de fail -->
<div class="fail-card">
  <!-- En-tête avec avatar et nom -->
  <div class="header">
    <img src="avatar.png" />
    <span>{{ userName }}</span>
    <span class="category">{{ category }}</span>
  </div>
  
  <!-- Contenu du fail -->
  <h3>{{ title }}</h3>
  <p>{{ description }}</p>
  <img *ngIf="imageUrl" [src]="imageUrl" />
  
  <!-- Boutons de réactions -->
  <div class="reactions">
    <button (click)="react('courage')">💪 Courage</button>
    <button (click)="react('laugh')">😂 Rire</button>
    <button (click)="react('empathy')">❤️ Empathie</button>
    <button (click)="react('support')">🤝 Support</button>
  </div>
</div>
```

#### Ce qu'on voit dans l'app :
*[Image : Carte de fail avec boutons de réactions bien visibles]*

---

## ✅ Phase 4 : VALIDATION & DÉPLOIEMENT

---

### Slide 10 : Tester l'application 🧪
**Compétence 9**

#### Ce que j'ai fait en simple :
J'ai créé des tests automatiques pour vérifier que tout fonctionne bien. Mais pas juste "ça marche ou pas" ! Je teste aussi toute la logique de protection des mineurs : refus des moins de 13 ans, validation parentale pour les 13-17 ans, inscription directe pour les adultes.

#### Exemple concret - Test complet du système d'inscription :
```javascript
// TEST 1 : Refuser un enfant de moins de 13 ans (protection COPPA)
test('Refuser inscription si moins de 13 ans', async () => {
  // Je simule une tentative d'inscription d'un enfant de 10 ans
  const childUser = {
    email: 'enfant@example.com',      // Email de l'enfant
    password: 'MotDePasse123',        // Son mot de passe
    displayName: 'Enfant10ans',       // Son pseudo
    birthdate: '2015-01-01'           // Date de naissance (10 ans)
  };
  
  // J'envoie la demande d'inscription
  const response = await register(childUser);
  
  // Je vérifie que l'inscription est REFUSÉE
  expect(response.success).toBe(false);                    // Échec attendu
  expect(response.message).toContain('13 ans minimum');    // Message clair
  expect(response.code).toBe('AGE_RESTRICTION');           // Code d'erreur
  
  // ✓ Test réussi : enfant protégé !
});

// TEST 2 : Créer un compte mineur en attente de validation parentale
test('Créer compte mineur avec validation parentale', async () => {
  // Je simule une inscription d'un adolescent de 15 ans
  const teenUser = {
    email: 'ado@example.com',         // Email de l'ado
    password: 'MotDePasse123',        // Son mot de passe
    displayName: 'Ado15ans',          // Son pseudo
    birthdate: '2010-01-01',          // Date de naissance (15 ans)
    parentEmail: 'parent@example.com' // Email du parent (requis)
  };
  
  // J'envoie la demande d'inscription
  const response = await register(teenUser);
  
  // Je vérifie que le compte est créé MAIS en attente
  expect(response.success).toBe(true);                          // Création OK
  expect(response.user.accountStatus).toBe('pending_parental'); // Statut : en attente
  expect(response.message).toContain('email envoyé au parent'); // Confirmation
  
  // Je vérifie qu'un email a bien été envoyé au parent
  const emailSent = await checkEmailSent('parent@example.com');
  expect(emailSent).toBe(true);                                 // Email envoyé
  
  // ✓ Test réussi : ado protégé, parent informé !
});

// TEST 3 : Créer un compte adulte directement actif
test('Créer compte adulte sans validation', async () => {
  // Je simule une inscription d'un adulte de 25 ans
  const adultUser = {
    email: 'adulte@example.com',      // Email de l'adulte
    password: 'MotDePasse123',        // Son mot de passe
    displayName: 'Adulte25ans',       // Son pseudo
    birthdate: '2000-01-01'           // Date de naissance (25 ans)
  };
  
  // J'envoie la demande d'inscription
  const response = await register(adultUser);
  
  // Je vérifie que le compte est créé ET actif immédiatement
  expect(response.success).toBe(true);              // Création OK
  expect(response.user.accountStatus).toBe('active'); // Statut : actif
  expect(response.user.email).toBe('adulte@example.com'); // Email correct
  
  // Je vérifie que l'utilisateur peut se connecter tout de suite
  const loginResponse = await login(adultUser.email, adultUser.password);
  expect(loginResponse.success).toBe(true);         // Connexion OK
  
  // ✓ Test réussi : adulte opérationnel !
});

// Résultat final : ✓ 127 tests passés sur 127
// Dont 45 tests sur la protection des mineurs
```

#### Ce qu'on voit dans l'app :
*[Image : Rapport de tests avec 3 sections - Refus -13 ans ✓, Validation parentale ✓, Compte adulte ✓]*

---

### Slide 10bis : Zoom sur la validation parentale 👨‍👩‍👧
**Compétences 3 + 9**

#### Ce que j'ai fait en simple :
Quand un mineur entre 13 et 17 ans s'inscrit, son compte est créé mais bloqué. On envoie un email au parent avec un lien spécial contenant un "code secret" (token). Le parent clique, vérifie les infos, et peut approuver ou refuser. C'est sécurisé : le lien expire après 7 jours et ne peut être utilisé qu'une seule fois.

#### Exemple concret - Le processus de validation :
```javascript
// ÉTAPE 1 : Créer le token et envoyer l'email au parent
async sendParentalConsentEmail(userId, parentEmail) {
  // Je génère un code secret unique (token)
  const token = generateSecureToken();  // Ex: "a3f9k2m8x5p1"
  
  // Je stocke le token en base avec une date d'expiration
  await db.query(`
    INSERT INTO parental_consent_tokens 
    (token, user_id, parent_email, expires_at) 
    VALUES (?, ?, ?, DATE_ADD(NOW(), INTERVAL 7 DAY))
  `, [token, userId, parentEmail]);  // Expire dans 7 jours
  
  // Je crée le lien de validation pour le parent
  const validationLink = `https://faildaily.com/parental-consent?token=${token}`;
  
  // J'envoie l'email au parent avec le lien
  await sendEmail({
    to: parentEmail,                           // Email du parent
    subject: 'Validation inscription FailDaily',
    body: `Votre enfant souhaite s'inscrire. Cliquez ici : ${validationLink}`
  });
}

// ÉTAPE 2 : Le parent clique sur le lien et arrive sur cette page
async validateParentalConsent(token) {
  // Je vérifie que le token existe et n'est pas expiré
  const consent = await db.query(`
    SELECT user_id, expires_at, parent_email 
    FROM parental_consent_tokens 
    WHERE token = ? AND used_at IS NULL
  `, [token]);  // used_at = NULL signifie "pas encore utilisé"
  
  // Si le token n'existe pas ou est expiré
  if (!consent || new Date() > consent.expires_at) {
    return { error: 'Lien invalide ou expiré' };  // Sécurité
  }
  
  // Je récupère les infos de l'ado pour les afficher au parent
  const teen = await db.query(`
    SELECT display_name, email, birthdate 
    FROM users 
    WHERE id = ?
  `, [consent.user_id]);
  
  // Je renvoie les infos pour affichage
  return {
    teenName: teen.display_name,      // "Ado15ans"
    teenEmail: teen.email,            // "ado@example.com"
    parentEmail: consent.parent_email // "parent@example.com"
  };
}

// ÉTAPE 3 : Le parent clique sur "Approuver"
async approveParentalConsent(token) {
  // Je vérifie à nouveau le token (sécurité)
  const consent = await db.query(`
    SELECT user_id FROM parental_consent_tokens 
    WHERE token = ? AND used_at IS NULL
  `, [token]);
  
  if (!consent) {
    return { error: 'Token invalide' };  // Pas de token = pas d'activation
  }
  
  // J'active le compte du mineur
  await db.query(`
    UPDATE users 
    SET account_status = 'active'     -- Compte débloqué !
    WHERE id = ?
  `, [consent.user_id]);
  
  // Je marque le token comme "utilisé" pour éviter la réutilisation
  await db.query(`
    UPDATE parental_consent_tokens 
    SET used_at = NOW()               -- Date d'utilisation
    WHERE token = ?
  `, [token]);
  
  // J'envoie un email de confirmation à l'ado
  await sendEmail({
    to: teen.email,
    subject: 'Compte activé !',
    body: 'Ton parent a validé, tu peux te connecter !'
  });
  
  return { success: true };           // ✓ Validation terminée
}

// ÉTAPE 4 (optionnelle) : Le parent clique sur "Refuser"
async rejectParentalConsent(token) {
  // Même vérification du token
  const consent = await db.query(`
    SELECT user_id FROM parental_consent_tokens 
    WHERE token = ? AND used_at IS NULL
  `, [token]);
  
  // Je marque le compte comme "refusé"
  await db.query(`
    UPDATE users 
    SET account_status = 'rejected'   -- Compte bloqué définitivement
    WHERE id = ?
  `, [consent.user_id]);
  
  // Je marque le token comme utilisé
  await db.query(`
    UPDATE parental_consent_tokens 
    SET used_at = NOW() 
    WHERE token = ?
  `, [token]);
  
  return { success: true, message: 'Inscription refusée' };
}
```

#### Ce qu'on voit dans l'app :
*[Image : Email reçu par le parent avec boutons "Approuver" et "Refuser" + Page de validation]*

---

### Slide 11 : Documenter le déploiement 📚
**Compétence 10**

#### Ce que j'ai fait en simple :
J'ai écrit un guide complet qui explique comment installer et faire fonctionner l'application sur un serveur. Comme une notice de montage IKEA, mais pour une application.

#### Exemple concret - Instructions de déploiement :
```markdown
# GUIDE DE DÉPLOIEMENT

## Étape 1 : Préparer le serveur
- Installer Docker
- Créer un dossier pour l'application

## Étape 2 : Copier les fichiers
docker-compose.yml
.env (fichier de configuration)

## Étape 3 : Configurer les variables
MYSQL_PASSWORD=motdepasse_securise
JWT_SECRET=cle_secrete_tokens
API_URL=https://api.faildaily.com

## Étape 4 : Lancer l'application
docker-compose up -d

## Vérification
✓ Backend accessible : https://api.faildaily.com/health
✓ Frontend accessible : https://faildaily.com
```

#### Ce qu'on voit dans l'app :
*[Image : Documentation README.md bien structurée]*

---

### Slide 12 : Mettre en production (DevOps) 🚀
**Compétence 11**

#### Ce que j'ai fait en simple :
J'ai automatisé le déploiement pour que l'application se mette à jour toute seule quand je fais des modifications. C'est comme une usine automatique : je pousse un bouton, et hop, la nouvelle version est en ligne !

#### Exemple concret - Déploiement automatique :
```yaml
# Fichier docker-compose.yml
# Définit comment lancer l'application

services:
  # Base de données
  database:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: faildaily
      MYSQL_PASSWORD: ${DB_PASSWORD}
  
  # Backend (API)
  backend:
    build: ./backend-api
    ports:
      - "3000:3000"
    depends_on:
      - database
  
  # Frontend (Interface)
  frontend:
    build: ./frontend
    ports:
      - "80:80"

# Commande magique :
# docker-compose up -d
# → Tout démarre automatiquement !
```

#### Ce qu'on voit dans l'app :
*[Image : Application accessible sur téléphone via https://faildaily.com]*

---

## 🎯 CONCLUSION

### Ce que j'ai appris :

✅ **Concevoir** avant de coder (besoins, architecture, BDD)  
✅ **Organiser** mon travail (Git, méthodologie)  
✅ **Développer** les 3 couches (données, logique, interface)  
✅ **Tester** pour garantir la qualité  
✅ **Déployer** en production de façon professionnelle  

### Résultat final :
Une application complète, testée et déployée, utilisable par de vraies personnes !

---

**Merci de votre attention ! 😊**

*Questions ?*
