Skip to content
josh11226704 edited this page Oct 9, 2025 · 1 revision

📚 Wiki - Documentation WhatNext

Application web de suggestion de destinations de voyage avec estimation des prix


📑 Table des Matières

  1. Vue d'ensemble
  2. Architecture
  3. Structure du projet
  4. Backend (index.js)
  5. Frontend
  6. API
  7. Données
  8. Déploiement
  9. Améliorations possibles

Vue d'ensemble

Concept

Application qui suggère des destinations de voyage aléatoires avec prix estimés (hôtel, repas, transport).

Stack technique

  • Backend : Node.js + Express.js 5.1.0
  • Frontend : HTML5, CSS3, JavaScript Vanilla
  • Données : JSON
  • Modules : ES Modules (import/export)

Fonctionnalités

✅ Génération aléatoire de destinations
✅ Affichage des prix estimés
✅ Interface responsive
✅ Images haute qualité (Unsplash)
✅ API REST


Architecture

Pattern 3-tiers

┌─────────────────────────────────────┐
│     FRONTEND (HTML/CSS/JS)          │
│  - Affichage                        │
│  - Interactions utilisateur         │
└─────────────────────────────────────┘
            ↕ HTTP/JSON
┌─────────────────────────────────────┐
│     BACKEND (Express)               │
│  - API REST                         │
│  - Serveur de fichiers statiques    │
└─────────────────────────────────────┘
            ↕ File I/O
┌─────────────────────────────────────┐
│     DONNÉES (JSON)                  │
│  - destinations.json                │
└─────────────────────────────────────┘

Flux de données

  1. Chargement : Le navigateur charge index.html, styles.css, destination.js
  2. Fetch : destination.js récupère les données via GET /api/destinations
  3. Sélection : JavaScript sélectionne une destination aléatoire
  4. Affichage : Mise à jour du DOM avec les infos
  5. Refresh : Nouvelle sélection sans requête serveur

Structure du projet

WhatNext/
├── index.js                    # Serveur Express
├── package.json                # Dépendances npm
├── README.md                   # Guide utilisateur
├── wiki.md                     # Documentation technique
│
├── data/
│   └── destinations.json       # Base de données
│
└── public/                     # Fichiers statiques
    ├── pages/
    │   └── index.html          # Page principale
    ├── scripts/
    │   └── destination.js      # Logique client
    └── styles/
        └── styles.css          # Styles

Backend (index.js)

Configuration

// ES Modules (package.json: "type": "module")
import express from "express";
import path from "path";
import { fileURLToPath } from "url";

// Recréer __dirname (non disponible en ES Modules)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

Middleware

Middleware Rôle
express.static("public") Sert les fichiers statiques (HTML, CSS, JS, images)
express.json() Parse les requêtes JSON
Logger custom Log toutes les requêtes avec timestamp

Routes

Méthode Route Description Réponse
GET / Page d'accueil index.html
GET /api/destinations Liste des destinations JSON array
* Autre 404 Message d'erreur

Gestion des erreurs

// 404 - Route introuvable
app.use((req, res) => {
    res.status(404).send("404 Not Found");
});

// 500 - Erreur serveur
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send("500 Internal Error");
});

Configuration du port

const PORT = process.env.PORT || 8080;

Port par défaut : 8080 (modifiable via variable d'environnement)


Frontend

index.html

Structure simple avec :

  • <h1> : Titre "What Next"
  • .destination-card : Carte avec image, infos et prix
  • #refresh-btn : Bouton "New destination"
  • <footer> : Copyright

Les données par défaut sont remplacées par JavaScript au chargement.

destination.js

3 fonctions principales

1. fetchDestinations()

async function fetchDestinations() {
    try {
        const response = await fetch("/api/destinations");
        if (!response.ok) throw new Error("Failed to fetch");
        return await response.json();
    } catch (error) {
        console.error(error);
        return [];
    }
}
  • Récupère les destinations depuis l'API
  • Gestion d'erreurs avec try/catch
  • Retourne [] si échec

2. getRandomDestination(destinations)

function getRandomDestination(destinations) {
    const randomIndex = Math.floor(Math.random() * destinations.length);
    return destinations[randomIndex];
}
  • Sélection aléatoire via Math.random()
  • Pure function (pas d'effets de bord)

3. updateDestinationCard(destination)

function updateDestinationCard(destination) {
    // Sélectionne les éléments DOM
    // Met à jour : image, titre, description, prix
    imageDiv.style.backgroundImage = `url(${destination.image})`;
    title.textContent = `${destination.ville}, ${destination.pays}`;
    // ...
}
  • Manipulation du DOM via querySelector
  • Utilise textContent pour la sécurité (évite XSS)

Initialisation

document.addEventListener("DOMContentLoaded", async () => {
    const destinations = await fetchDestinations();
    if (destinations.length === 0) return;
    
    // Affichage initial
    updateDestinationCard(getRandomDestination(destinations));
    
    // Event listener sur le bouton
    refreshBtn.addEventListener("click", () => {
        updateDestinationCard(getRandomDestination(destinations));
    });
});

Optimisation : Les données sont récupérées 1 seule fois, puis réutilisées en mémoire.

styles.css

Variables CSS

:root {
    --primary-color: #3498db;
    --primary-color-dark: #2980b9;
    --text-color: #333;
    --background-color: #f5f5f5;
    --card-color: #fff;
    --border-radius: 6px;
    --transition-speed: 0.3s;
}

Layout Flexbox

.container {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

main {
    flex: 1; /* Footer toujours en bas */
}

Responsive

/* Desktop par défaut */
.container { max-width: 800px; }

/* Tablette */
@media (max-width: 768px) { /* ... */ }

/* Mobile */
@media (max-width: 480px) { /* ... */ }

API

GET /api/destinations

Description : Retourne toutes les destinations en JSON

Réponse :

[
  {
    "pays": "France",
    "ville": "Paris",
    "description": "La ville lumière...",
    "prix": {
      "hotel": "150-300€/nuit",
      "repas": "15-30€/repas",
      "transport": "1.90€/ticket"
    },
    "image": "https://images.unsplash.com/..."
  }
]

Test :

curl http://localhost:8080/api/destinations

Données

Structure de destinations.json

[
  {
    "pays": "String",
    "ville": "String",
    "description": "String",
    "prix": {
      "hotel": "String",
      "repas": "String",
      "transport": "String"
    },
    "image": "URL"
  }
]

Destinations disponibles

🇫🇷 Paris, France
🇯🇵 Tokyo, Japon
🇮🇹 Rome, Italie
🇹🇭 Bangkok, Thaïlande
🇺🇸 New York, États-Unis
... et plus

Ajouter une destination

Suivez le format JSON et ajoutez votre entrée dans le tableau. Utilisez des images Unsplash :

https://images.unsplash.com/photo-{id}?q=80&w=1000

Déploiement

Installation locale

git clone https://github.com/Joshua31400/WhatNext.git
cd WhatNext
npm install
npm start

Ouvrir : http://localhost:8080

Scripts npm

npm start       # Production (node index.js)
npm run dev     # Développement avec nodemon

Variable d'environnement

# Windows CMD
set PORT=3000 && npm start

# Windows PowerShell
$env:PORT=3000; npm start

# Linux/Mac
PORT=3000 npm start

Déploiement cloud

Heroku

heroku create whatnext-app
git push heroku main

Créer un Procfile :

web: node index.js

Render

  1. Connecter le repo GitHub
  2. Build : npm install
  3. Start : npm start

VPS

git clone <repo>
cd WhatNext
npm install
npm install -g pm2
pm2 start index.js --name whatnext
pm2 startup
pm2 save

Améliorations possibles

Backend

  • 🔹 Base de données : MongoDB ou PostgreSQL au lieu de JSON
  • 🔹 API CRUD : POST/PUT/DELETE pour gérer les destinations
  • 🔹 Authentification : JWT pour sécuriser les endpoints
  • 🔹 Compression : Module compression pour optimiser

Frontend

  • 🔹 Framework : Migrer vers React/Vue pour une meilleure gestion d'état
  • 🔹 Animations : Transitions CSS ou Framer Motion
  • 🔹 Mode sombre : prefers-color-scheme CSS
  • 🔹 Favoris : LocalStorage pour sauvegarder les préférences
  • 🔹 Filtres : Par budget, continent, saison

Fonctionnalités

  • 🔹 Météo : Intégration API OpenWeatherMap
  • 🔹 Carte : Leaflet ou Google Maps
  • 🔹 Comparaison : Comparer plusieurs destinations
  • 🔹 Éviter répétitions : Ne pas afficher 2× la même destination consécutive

Performance

  • 🔹 CDN : Cloudinary pour les images
  • 🔹 Cache : Headers de cache pour les ressources statiques
  • 🔹 Lazy loading : Charger les images à la demande

Tests

// Jest / Vitest
describe('getRandomDestination', () => {
    test('retourne une destination valide', () => {
        const destinations = [{ ville: 'Paris' }];
        const result = getRandomDestination(destinations);
        expect(destinations).toContain(result);
    });
});

Bonnes pratiques implémentées

Séparation des responsabilités (HTML/CSS/JS)
ES Modules pour un code moderne
Async/await pour la lisibilité
Gestion d'erreurs complète (try/catch + middleware)
Variables CSS pour la maintenabilité
Responsive design
Fonctions pures (ex: getRandomDestination)
Guard clauses (return early)
Const par défaut (immutabilité)


Ressources


👨‍💻 Développeurs : Joshua BUDGEN & Pedro MARTINS
📅 Dernière mise à jour : Octobre 2025
📜 Licence : MIT