# MenuLoader - Système modulaire pour menus dynamiques

## Vue d'ensemble

`MenuLoader` est un système modulaire et réutilisable pour charger des menus dynamiquement depuis une API. Il permet de découpler la logique métier de l'affichage et supporte différents templates HTML selon vos besoins.

## Structure du projet

```text
dynamic-menu-lightspeed/
├── src/
│   └── menu-loader.js          # source lisible (développement)
├── dist/
│   └── menu-loader.min.js      # version minifiée et obfusquée (production)
├── examples/
│   └── menu-loader-examples.js # exemples d'usage
├── package.json
└── obfuscator.config.json
```

## Build

```bash
npm install
npm run build
```

Le build produit `dist/menu-loader.min.js` à partir de `src/menu-loader.js` via minification (terser) puis obfuscation (javascript-obfuscator). L'API publique (`MenuLoader`, `create`, `init`, `formatPrice`, etc.) est préservée.

## CDN (recommandé en production)

Une fois le package publié sur npm sous `@xzeppetella/menu-loader`, il est disponible automatiquement via jsDelivr et unpkg.

### jsDelivr

```html
<script src="https://cdn.jsdelivr.net/npm/@xzeppetella/menu-loader@1.1.0/dist/menu-loader.min.js"></script>
```

### unpkg

```html
<script src="https://unpkg.com/@xzeppetella/menu-loader@1.1.0/dist/menu-loader.min.js"></script>
```

### Version flottante (dernière 1.x)

```html
<script src="https://cdn.jsdelivr.net/npm/@xzeppetella/menu-loader@1/dist/menu-loader.min.js"></script>
```

En production, préférez une version figée (`@1.1.0`) plutôt que `@latest`.

## Publication sur npm

### Prérequis

1. Compte sur [npmjs.com](https://www.npmjs.com/)
2. Connexion CLI : `npm login`
3. Vérifier la disponibilité du nom : `npm view @xzeppetella/menu-loader` (doit retourner une erreur 404 si libre)
4. Le package est scopé sous ton compte npm (`@xzeppetella`)

### Publier

```bash
npm install
npm run build
npm publish --access public
```

Le script `prepublishOnly` relance automatiquement le build avant publication.

### Mettre à jour une version

```bash
npm version patch   # 1.0.0 → 1.0.1
npm publish --access public
```

Puis mettre à jour l'URL CDN dans vos pages avec la nouvelle version.

### Vérifier le contenu du package avant publication

```bash
npm pack --dry-run
```

Seuls les fichiers listés dans `files` (dans `package.json`) seront publiés : `dist/`, `src/`, `LICENSE`, `README.md`.

## ✅ Avantages du système

- **🚫 Zéro duplication de code** entre les pages
- **🎨 Templates personnalisables** pour différents layouts
- **🌐 Support multilingue** avec fallback automatique
- **⚡ Facile à utiliser** : 1 ligne de configuration par page
- **🔧 Extensible** : Templates sur mesure pour vos besoins
- **📱 Responsive** : Différents templates pour mobile/desktop

## 🚀 Usage basique

### 1. Inclure le script

**Production via CDN** (recommandé) :

```html
<script src="https://cdn.jsdelivr.net/npm/@xzeppetella/menu-loader@1.1.0/dist/menu-loader.min.js"></script>
```

**Production en local** (fichier copié sur le serveur) :

```html
<script src="dist/menu-loader.min.js"></script>
```

**Développement** (source lisible) :

```html
<script src="src/menu-loader.js"></script>
```

### 2. Configuration avec templates personnalisés

```javascript
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/get-menu/177957674942466/177957674955376',
  locale: 'fr',
  containerSelector: '.menu-list',
  
  // Définir vos propres templates directement
  itemTemplate: function(item) {
    const productName = this.getLocalizedProductName(item);
    const productPrice = this.formatPrice(item.productPrice || '0.00');
    const description = this.getLocalizedDescription(item);

    return `
      <div class="menu-item">
        <h4>${productName}</h4>
        <p>${description}</p>
        <span class="price">${productPrice}€</span>
      </div>
    `;
  },

  categoryTemplate: function(group, itemsHTML) {
    return `
      <div class="menu-category">
        <h3>${group.name}</h3>
        <div class="items">${itemsHTML}</div>
      </div>
    `;
  }
}).init();
```

## 📋 Configuration complète

```javascript
const config = {
  // 🔗 API
  apiUrl: 'http://localhost:3000/api/menu',
  
  // 🌍 Langue (fr, en, etc.)
  locale: 'fr',
  
  // 🎯 Sélecteur CSS du conteneur
  containerSelector: '.menu-list',
  
  // 💬 Messages personnalisés
  messages: {
    loading: 'Chargement du menu...',
    error: 'Impossible de charger le menu',
    errorDescription: 'Veuillez réessayer plus tard. Erreur: {error}'
  },
  
  // 🎨 Templates personnalisés (optionnel)
  itemTemplate: function(item) { /* ... */ },
  categoryTemplate: function(group, itemsHTML) { /* ... */ },
  loadingTemplate: function() { /* ... */ },
  errorTemplate: function(error) { /* ... */ }
};
```

## 🎨 Templates personnalisés

### Template d'item simple

```javascript
itemTemplate: function(item) {
  const name = item.productName || '';
  const price = this.formatPrice(item.productPrice || '0.00');
  const description = this.getLocalizedDescription(item);

  return `
    <div class="simple-item">
      <h4>${name} - ${price}€</h4>
      <p>${description}</p>
    </div>
  `;
}
```

### Template de catégorie en grid

```javascript
categoryTemplate: function(group, itemsHTML) {
  return `
    <div class="grid-category">
      <h2>${group.name}</h2>
      <div class="items-grid">
        ${itemsHTML}
      </div>
    </div>
  `;
}
```

### Template avec images

```javascript
itemTemplate: function(item) {
  const name = item.productName || '';
  const price = this.formatPrice(item.productPrice || '0.00');
  const description = this.getLocalizedDescription(item);
  const image = item.itemRichData?.squareImageUrl || '/default.jpg';

  return `
    <div class="card-item">
      <img src="${image}" alt="${name}">
      <div class="card-content">
        <h4>${name}</h4>
        <p>${description}</p>
        <span class="price">${price}€</span>
      </div>
    </div>
  `;
}
```

## 🌐 Gestion multilingue

Le système recherche automatiquement les descriptions dans la langue demandée :

```javascript
// Exemple de données API
{
  "productName": "Tzatziki",
  "productPrice": "8.00",
  "itemRichData": {
    "texts": [
      {
        "locale": "fr",
        "description": "Le traditionnel tzatziki fait maison"
      },
      {
        "locale": "en", 
        "description": "Traditional homemade tzatziki"
      }
    ]
  }
}
```

- `locale: 'fr'` → Affiche la description française
- `locale: 'en'` → Affiche la description anglaise
- Si la locale n'existe pas → Utilise la première traduction disponible

## 📱 Exemples d'usage par contexte

### Page standard (style Khora)

```javascript
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'fr',
  containerSelector: '.menu-list'
}).init();
```

### Layout simple

```javascript
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'en',
  containerSelector: '.simple-menu',
  itemTemplate: function(item) {
    return `<div>${item.productName} - ${this.formatPrice(item.productPrice)}€</div>`;
  }
}).init();
```

### Menu mobile avec accordéon

```javascript
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'fr',
  containerSelector: '.mobile-menu',
  categoryTemplate: function(group, itemsHTML) {
    return `
      <div class="mobile-category">
        <button class="category-toggle" onclick="toggleCategory(this)">
          ${group.name} <span class="arrow">▼</span>
        </button>
        <div class="category-content collapsed">
          ${itemsHTML}
        </div>
      </div>
    `;
  }
}).init();
```

### Menu avec recherche et filtres

```javascript
// Voir examples/menu-loader-examples.js pour un exemple complet
```

## 🛠 Méthodes utilitaires disponibles

Dans vos templates, vous avez accès à ces méthodes :

```javascript
// Formatage du prix
this.formatPrice('8.50') // → "8.50"

// Nom affiché : friendlyDisplayName localisé si présent, sinon productName
this.getLocalizedProductName(item) // → "Burger - ENG" ou "Burger"

// Récupération de la description localisée
this.getLocalizedDescription(item) // → Description dans la bonne langue

// Nettoyage du HTML
this.stripHTML('<p>Description</p>') // → "Description"
```

## 🔧 API avancée

### Contrôle manuel

```javascript
const menu = MenuLoader.create(config);

// Charger le menu manuellement
await menu.loadMenu();

// Afficher seulement l'animation de chargement
menu.showLoading();

// Afficher une erreur
menu.showError(new Error('Problème de connexion'));

// Afficher du HTML personnalisé
menu.showMenu('<div>Menu personnalisé</div>');
```

### Récupération des données sans affichage

```javascript
const menu = MenuLoader.create(config);
const menuData = await menu.fetchMenu();
console.log(menuData);
```

## 🎯 Cas d'usage réels

### 1. Site multilingue

```javascript
// Page française
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'fr',
  containerSelector: '.menu-list'
}).init();

// Page anglaise  
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'en',
  containerSelector: '.menu-list'
}).init();
```

### 2. Différents layouts sur le même site

```javascript
// Menu principal (page principale)
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'fr',
  containerSelector: '.main-menu'
}).init();

// Menu simplifié (footer)
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'fr',
  containerSelector: '.footer-menu',
  itemTemplate: function(item) {
    return `<a href="#${item.productName}">${item.productName}</a>`;
  }
}).init();
```

### 3. Menu avec données en cache

```javascript
// Ajouter un système de cache simple
const cachedMenuLoader = MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'fr',
  containerSelector: '.menu-list'
});

// Override de fetchMenu pour ajouter un cache
const originalFetch = cachedMenuLoader.fetchMenu;
cachedMenuLoader.fetchMenu = async function() {
  const cached = localStorage.getItem('menu-cache');
  if (cached) {
    const data = JSON.parse(cached);
    if (Date.now() - data.timestamp < 300000) { // 5 minutes
      return data.menu;
    }
  }
  
  const menu = await originalFetch.call(this);
  localStorage.setItem('menu-cache', JSON.stringify({
    menu,
    timestamp: Date.now()
  }));
  
  return menu;
};

cachedMenuLoader.init();
```

## 🎨 Découplage parfait : Templates dans les pages

L'approche recommandée est de définir vos templates directement dans chaque page HTML. Cela montre clairement le découplage entre :

- **Logique métier** (dans `src/menu-loader.js`) : Récupération API, gestion locale, etc.
- **Présentation** (dans chaque page) : Templates HTML spécifiques au layout

### Exemple concret

```javascript
// Page restaurant élégante
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'fr',
  containerSelector: '.elegant-menu',
  
  itemTemplate: function(item) {
    return `
      <div class="elegant-dish">
        <h4 class="dish-name">${item.productName}</h4>
        <p class="dish-description">${this.getLocalizedDescription(item)}</p>
        <span class="dish-price">${this.formatPrice(item.productPrice)}€</span>
      </div>
    `;
  }
}).init();

// Page mobile compacte
MenuLoader.create({
  apiUrl: 'http://localhost:3000/api/menu',
  locale: 'fr',
  containerSelector: '.compact-menu',
  
  itemTemplate: function(item) {
    return `
      <div class="compact-item">
        ${item.productName} - ${this.formatPrice(item.productPrice)}€
      </div>
    `;
  }
}).init();
```

## 📄 Exemples

Consultez `examples/menu-loader-examples.js` pour des exemples complets :

1. **Layout basique** - Liste simple
2. **Layout cartes** - Grid avec images
3. **Layout mobile** - Accordéon interactif
4. **Menu avec recherche et filtres**

## ✨ Conclusion

Le système `MenuLoader` vous permet de :

1. **Éliminer la duplication** de code entre vos pages
2. **Découpler parfaitement** la logique de la présentation
3. **Personnaliser facilement** chaque layout dans sa page
4. **Gérer multilingue** sans effort
5. **Maintenir facilement** : 1 fichier central + templates spécifiques

**Avant** : 200+ lignes de code dupliqué par page  
**Après** : Templates personnalisés par page + 1 logique partagée

**Avantage clé** : Chaque page définit son propre style sans affecter les autres !
