Tous les guides
IntermédiaireÀ votre rythmeGuide gratuitSection 20/95

Mini-projet — Application web complète

Pratiquez les fondations du développement : programmation, web, API, données et usages de l’IA. Des sections et exercices pour construire vos premiers projets.

💡

Contenu gratuit, accessible sans compte. Les outils et modèles évoluent : consultez le dossier actuel pour vérifier leurs versions. Modèles et sources

Section 11.1.20 : Mini-projet — Application web complète

🎯 Objectif pédagogique

Synthétiser toutes les compétences de la Semaine 1 en créant une application web fonctionnelle de bout en bout : HTML structuré, CSS stylisé, JavaScript interactif, appel API, et déploiement. C'est le moment de preuve que Marc est prêt pour la Semaine 2.


Le projet : "Job Tracker" — Tableau de bord de candidatures

Marc est en reconversion. Il a besoin de suivre ses candidatures. Plutôt que d'utiliser un fichier Excel (son réflexe d'ex-analyste), il va construire son propre outil — en appliquant tout ce qu'il a appris cette semaine.

Architecture du projet

job-tracker/
├── index.html          ← Structure (HTML)
├── style.css           ← Design (CSS)
├── app.js              ← Logique (JavaScript)
├── README.md           ← Documentation
├── .gitignore          ← Fichiers à ignorer
└── .env                ← Clé API (si IA intégrée)

Étape 1 : HTML — Structure de la page

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Job Tracker — Marc Dupont</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header>
    <h1>🎯 Job Tracker</h1>
    <p>Suivi de candidatures — Bootcamp Semaine 1</p>
  </header>

  <main>
    <section id="add-job">
      <h2>Ajouter une candidature</h2>
      <form id="job-form">
        <input type="text" id="company" placeholder="Entreprise" required>
        <input type="text" id="position" placeholder="Poste" required>
        <select id="status">
          <option value="applied">📨 Candidature envoyée</option>
          <option value="interview">🎤 Entretien planifié</option>
          <option value="offer">🎉 Offre reçue</option>
          <option value="rejected">❌ Refusée</option>
        </select>
        <input type="date" id="date">
        <button type="submit">Ajouter</button>
      </form>
    </section>

    <section id="stats">
      <div class="stat-card" id="total-count">
        <span class="stat-number">0</span>
        <span class="stat-label">Total</span>
      </div>
      <div class="stat-card" id="interview-count">
        <span class="stat-number">0</span>
        <span class="stat-label">Entretiens</span>
      </div>
      <div class="stat-card" id="offer-count">
        <span class="stat-number">0</span>
        <span class="stat-label">Offres</span>
      </div>
    </section>

    <section id="jobs-list">
      <h2>Candidatures</h2>
      <div id="jobs-container"></div>
    </section>
  </main>

  <footer>
    <p>Construit par Marc Dupont — Semaine 1 du Bootcamp Tech & IA</p>
  </footer>

  <script src="app.js"></script>
</body>
</html>

Étape 2 : CSS — Design professionnel

* \{ margin: 0; padding: 0; box-sizing: border-box; \}

body \{
  font-family: 'Inter', -apple-system, sans-serif;
  background: #F5F3EF;
  color: #333;
  line-height: 1.6;
\}

header \{
  background: linear-gradient(135deg, #0891B2, #06b6d4);
  color: white;
  padding: 2rem;
  text-align: center;
\}

main \{ max-width: 900px; margin: 2rem auto; padding: 0 1rem; \}

/* Formulaire */
#job-form \{
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  background: white;
  padding: 1.5rem;
  border-radius: 12px;
  border: 1px solid #E2DFD8;
  margin-bottom: 2rem;
\}

#job-form input, #job-form select \{
  flex: 1 1 200px;
  padding: 0.75rem;
  border: 1px solid #E2DFD8;
  border-radius: 8px;
  font-size: 0.95rem;
\}

#job-form button \{
  padding: 0.75rem 2rem;
  background: #0891B2;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 1rem;
  font-weight: 600;
\}

#job-form button:hover \{ background: #0e7490; \}

/* Stats */
#stats \{
  display: flex;
  gap: 1rem;
  margin-bottom: 2rem;
\}

.stat-card \{
  flex: 1;
  background: white;
  padding: 1.5rem;
  border-radius: 12px;
  text-align: center;
  border: 1px solid #E2DFD8;
\}

.stat-number \{
  display: block;
  font-size: 2rem;
  font-weight: 700;
  color: #0891B2;
\}

.stat-label \{ color: #666; font-size: 0.85rem; \}

/* Job cards */
.job-card \{
  background: white;
  padding: 1.25rem;
  border-radius: 8px;
  border: 1px solid #E2DFD8;
  margin-bottom: 0.75rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
\}

.job-card .company \{ font-weight: 600; \}
.job-card .position \{ color: #666; \}

.badge \{
  padding: 0.25rem 0.75rem;
  border-radius: 20px;
  font-size: 0.8rem;
  font-weight: 500;
\}

.badge-applied \{ background: #dbeafe; color: #1d4ed8; \}
.badge-interview \{ background: #fef3c7; color: #92400e; \}
.badge-offer \{ background: #d1fae5; color: #065f46; \}
.badge-rejected \{ background: #fee2e2; color: #991b1b; \}

.delete-btn \{
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1.2rem;
  opacity: 0.5;
\}

.delete-btn:hover \{ opacity: 1; \}

@media (max-width: 640px) \{
  #stats \{ flex-direction: column; \}
  #job-form \{ flex-direction: column; \}
\}

footer \{
  text-align: center;
  padding: 2rem;
  color: #999;
  margin-top: 3rem;
\}

Étape 3 : JavaScript — Logique et interactivité

// État de l'application
let jobs = JSON.parse(localStorage.getItem("jobs")) || [];

// Éléments DOM
const form = document.querySelector("#job-form");
const container = document.querySelector("#jobs-container");

// Soumission du formulaire
form.addEventListener("submit", (e) => \{
  e.preventDefault();
  
  const job = \{
    id: Date.now(),
    company: document.querySelector("#company").value.trim(),
    position: document.querySelector("#position").value.trim(),
    status: document.querySelector("#status").value,
    date: document.querySelector("#date").value || new Date().toISOString().split("T")[0]
  \};
  
  jobs.push(job);
  save();
  render();
  form.reset();
\});

// Sauvegarder dans localStorage
function save() \{
  localStorage.setItem("jobs", JSON.stringify(jobs));
\}

// Supprimer une candidature
function deleteJob(id) \{
  jobs = jobs.filter(j => j.id !== id);
  save();
  render();
\}

// Afficher les stats
function updateStats() \{
  document.querySelector("#total-count .stat-number").textContent = jobs.length;
  document.querySelector("#interview-count .stat-number").textContent = 
    jobs.filter(j => j.status === "interview").length;
  document.querySelector("#offer-count .stat-number").textContent = 
    jobs.filter(j => j.status === "offer").length;
\}

// Rendu des cartes
function render() \{
  const statusLabels = \{
    applied: "📨 Envoyée",
    interview: "🎤 Entretien",
    offer: "🎉 Offre",
    rejected: "❌ Refusée"
  \};
  
  container.innerHTML = jobs.map(job => \`
    <div class="job-card">
      <div>
        <span class="company">\$\{job.company\}</span>
        <span class="position"> — \$\{job.position\}</span>
        <small style="color:#999; margin-left:0.5rem">\$\{job.date\}</small>
      </div>
      <div style="display:flex; align-items:center; gap:0.5rem">
        <span class="badge badge-\$\{job.status\}">\$\{statusLabels[job.status]\}</span>
        <button class="delete-btn" onclick="deleteJob(\$\{job.id\})">🗑️</button>
      </div>
    </div>
  \`).join("");
  
  updateStats();
\}

// Rendu initial
render();

Étape 4 : Git + Déploiement

# Initialiser et committer
cd job-tracker
git init
echo "node_modules/\n.env" > .gitignore
git add .
git commit -m "feat: Job Tracker — projet Semaine 1"

# Pousser sur GitHub
git remote add origin https://github.com/votre-username/job-tracker.git
git push -u origin main

# Déployer sur Vercel
# → vercel.com → Import projet → Deploy
Loading diagram…

Fonctionnalités bonus (optionnel)

  1. →Filtrage par statut (ajouter des boutons radio)
  2. →Export CSV des candidatures
  3. →Mode sombre avec toggle
  4. →IA : "Analysez mes candidatures" via l'API OpenAI/Groq

🏋️ Récapitulatif Semaine 1

CompétenceSectionStatut
Architecture ordinateur11.1.1✅
Internet & HTTP11.1.2✅
Terminal11.1.3✅
Environnement dev (VS Code)11.1.4✅
Git11.1.5✅
GitHub11.1.6✅
Figma11.1.7✅
HTML11.1.8-11.1.9✅
CSS11.1.10-11.1.11✅
JavaScript11.1.12-11.1.14✅
Python11.1.15-11.1.16✅
APIs11.1.17-11.1.18✅
Déploiement11.1.19✅
Mini-projet11.1.20✅

Marc referme son laptop avec un sourire. Il a un portfolio en ligne, un Job Tracker fonctionnel, et les bases pour la suite. En une semaine, il est passé de "je ne sais pas ce qu'est un terminal" à "j'ai déployé une application web". La Semaine 2 s'attaque aux données — pandas, SQL, et visualisation. L'analyste financier en lui a hâte.

Vérification rapide

Quel est l'avantage d'utiliser localStorage dans le Job Tracker ?

Vérification rapide

Pourquoi utiliser JSON.stringify() et JSON.parse() avec localStorage ?

Vérification rapide

L'application Job Tracker utilise toutes les technologies SAUF :

Votre progression

Cette progression est déclarative et conservée dans votre navigateur. L’attestation finale ne constitue pas un diplôme ni une évaluation indépendante.

Voir ma progression et mon attestation

Apprendre l’IA, du premier prompt aux agents

15 guides gratuitsQuiz, schémas et ateliersSans compte

Un cas pratique IA dans votre boîte mail

Méthodes, ressources et nouveaux exercices. Gratuit, avec désinscription à tout moment.