{"id":2,"date":"2025-02-15T22:12:24","date_gmt":"2025-02-15T21:12:24","guid":{"rendered":"http:\/\/gabrielmillon.com\/?page_id=2"},"modified":"2026-08-22T23:57:20","modified_gmt":"2026-08-22T21:57:20","slug":"page-d-exemple","status":"publish","type":"page","link":"https:\/\/gabrielmillon.com\/index.php\/page-d-exemple\/","title":{"rendered":"Page d\u2019exemple"},"content":{"rendered":"\n<div id=\"calorie-tracker-app\">\n  <div class=\"ct-box\">\n    <h2 style=\"text-align:center; margin:0 0 15px 0; font-size:1.3rem;\">Journal de Calories<\/h2>\n\n    <!-- Objectif -->\n    <div class=\"ct-section\">\n      <div class=\"ct-flex-between\">\n        <label for=\"ct-goal-input\"><strong>Objectif quotidien :<\/strong><\/label>\n        <div>\n          <input type=\"number\" id=\"ct-goal-input\" value=\"2000\" min=\"500\" step=\"50\" style=\"width:85px; padding:6px;\"> kcal\n        <\/div>\n      <\/div>\n      <div class=\"ct-bar-bg\">\n        <div id=\"ct-bar-fill\"><\/div>\n      <\/div>\n      <div class=\"ct-flex-between\" style=\"font-size:0.85rem; color:#555;\">\n        <span>Consomm\u00e9 : <strong id=\"ct-consumed-text\">0<\/strong> kcal<\/span>\n        <span>Restant : <strong id=\"ct-remaining-text\">2000<\/strong> kcal<\/span>\n      <\/div>\n    <\/div>\n\n    <!-- Recherche API -->\n    <div class=\"ct-section\">\n      <h3 style=\"margin:0 0 10px 0; font-size:1.05rem;\">Rechercher un aliment (API)<\/h3>\n      \n      <div style=\"position:relative; margin-bottom:12px;\">\n        <label for=\"ct-search-input\" style=\"display:block; font-size:0.85rem; margin-bottom:4px;\">Nom de l&#8217;aliment \/ marque :<\/label>\n        <input type=\"text\" id=\"ct-search-input\" placeholder=\"Tapez au moins 3 lettres (ex. Lindt, Danone, Avocat)...\" autocomplete=\"off\">\n        <div id=\"ct-loading-status\" style=\"font-size:0.75rem; color:#718096; margin-top:4px; display:none;\">\ud83d\udd0d Recherche en cours&#8230;<\/div>\n        <div id=\"ct-dropdown\" class=\"ct-dropdown-menu\"><\/div>\n      <\/div>\n\n      <div style=\"display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:12px;\">\n        <div>\n          <label for=\"ct-weight-input\" style=\"display:block; font-size:0.85rem; margin-bottom:4px;\">Portion (g ou ml) :<\/label>\n          <input type=\"number\" id=\"ct-weight-input\" value=\"100\" min=\"1\">\n        <\/div>\n        <div>\n          <label for=\"ct-kcal-input\" style=\"display:block; font-size:0.85rem; margin-bottom:4px;\">kcal pour 100g :<\/label>\n          <input type=\"number\" id=\"ct-kcal-input\" placeholder=\"0\" min=\"0\">\n        <\/div>\n      <\/div>\n\n      <div style=\"background:#f0f4f8; padding:8px; border-radius:6px; text-align:center; font-size:0.95rem; margin-bottom:12px;\">\n        Total estim\u00e9 : <strong id=\"ct-calc-total\">0<\/strong> kcal\n      <\/div>\n\n      <button type=\"button\" id=\"ct-add-btn\" class=\"ct-btn-main\">Ajouter au journal<\/button>\n    <\/div>\n\n    <!-- Carnet -->\n    <div class=\"ct-section\">\n      <div class=\"ct-flex-between\" style=\"margin-bottom:8px;\">\n        <h3 style=\"margin:0; font-size:1.05rem;\">Repas enregistr\u00e9s<\/h3>\n        <button type=\"button\" id=\"ct-reset-btn\" style=\"background:none; border:none; color:#c53030; font-size:0.8rem; cursor:pointer; text-decoration:underline;\">Tout vider<\/button>\n      <\/div>\n      <ul id=\"ct-items-list\" style=\"list-style:none; padding:0; margin:0;\"><\/ul>\n    <\/div>\n  <\/div>\n<\/div>\n\n<style>\n  #calorie-tracker-app {\n    font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n    color: #2d3748;\n    max-width: 480px;\n    margin: 10px auto;\n  }\n  #calorie-tracker-app .ct-box {\n    background: #ffffff;\n    border: 1px solid #e2e8f0;\n    border-radius: 12px;\n    padding: 16px;\n    box-shadow: 0 4px 10px rgba(0,0,0,0.05);\n  }\n  #calorie-tracker-app .ct-section {\n    background: #fafbfc;\n    border: 1px solid #edf2f7;\n    border-radius: 8px;\n    padding: 14px;\n    margin-bottom: 14px;\n  }\n  #calorie-tracker-app .ct-flex-between {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n  }\n  #calorie-tracker-app input[type=\"text\"],\n  #calorie-tracker-app input[type=\"number\"] {\n    width: 100%;\n    padding: 9px;\n    border: 1px solid #cbd5e0;\n    border-radius: 6px;\n    font-size: 0.95rem;\n    box-sizing: border-box;\n    background: #fff;\n  }\n  #calorie-tracker-app .ct-bar-bg {\n    background: #e2e8f0;\n    border-radius: 8px;\n    height: 12px;\n    overflow: hidden;\n    margin: 10px 0;\n  }\n  #calorie-tracker-app #ct-bar-fill {\n    background: #319795;\n    height: 100%;\n    width: 0%;\n    transition: width 0.3s ease;\n  }\n  #calorie-tracker-app #ct-bar-fill.over {\n    background: #e53e3e;\n  }\n  #calorie-tracker-app .ct-dropdown-menu {\n    position: absolute;\n    top: 100%;\n    left: 0;\n    right: 0;\n    background: #fff;\n    border: 1px solid #cbd5e0;\n    border-radius: 0 0 6px 6px;\n    max-height: 220px;\n    overflow-y: auto;\n    z-index: 9999;\n    display: none;\n    box-shadow: 0 4px 10px rgba(0,0,0,0.15);\n  }\n  #calorie-tracker-app .ct-dropdown-item {\n    padding: 10px 12px;\n    cursor: pointer;\n    font-size: 0.88rem;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    border-bottom: 1px solid #edf2f7;\n  }\n  #calorie-tracker-app .ct-dropdown-item:hover {\n    background: #ebf8ff;\n  }\n  #calorie-tracker-app .ct-brand-tag {\n    display: block;\n    font-size: 0.75rem;\n    color: #718096;\n  }\n  #calorie-tracker-app .ct-btn-main {\n    width: 100%;\n    background: #319795;\n    color: #fff;\n    border: none;\n    padding: 11px;\n    border-radius: 6px;\n    font-size: 0.95rem;\n    font-weight: 600;\n    cursor: pointer;\n  }\n  #calorie-tracker-app .ct-btn-main:active {\n    background: #234e52;\n  }\n  #calorie-tracker-app .ct-row {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 8px 0;\n    border-bottom: 1px solid #edf2f7;\n    font-size: 0.9rem;\n  }\n  #calorie-tracker-app .ct-del-x {\n    background: #fed7d7;\n    color: #9b2c2c;\n    border: none;\n    border-radius: 4px;\n    padding: 2px 6px;\n    margin-left: 8px;\n    cursor: pointer;\n    font-weight: bold;\n  }\n<\/style>\n\n<script>\n(function() {\n  function startTracker() {\n    let appState = {\n      goal: 2000,\n      entries: []\n    };\n\n    const searchInput = document.getElementById(\"ct-search-input\");\n    const dropdown = document.getElementById(\"ct-dropdown\");\n    const loadingStatus = document.getElementById(\"ct-loading-status\");\n    const weightInput = document.getElementById(\"ct-weight-input\");\n    const kcalInput = document.getElementById(\"ct-kcal-input\");\n    const calcTotalDisplay = document.getElementById(\"ct-calc-total\");\n    const goalInput = document.getElementById(\"ct-goal-input\");\n    const itemsList = document.getElementById(\"ct-items-list\");\n    const consumedText = document.getElementById(\"ct-consumed-text\");\n    const remainingText = document.getElementById(\"ct-remaining-text\");\n    const barFill = document.getElementById(\"ct-bar-fill\");\n    const addBtn = document.getElementById(\"ct-add-btn\");\n    const resetBtn = document.getElementById(\"ct-reset-btn\");\n\n    let debounceTimer = null;\n    let currentAbortCtrl = null;\n\n    const savedData = localStorage.getItem(\"my_wp_calorie_data\");\n    if (savedData) {\n      try {\n        appState = JSON.parse(savedData);\n        if (goalInput) goalInput.value = appState.goal || 2000;\n      } catch(e) {}\n    }\n\n    function updateCalc() {\n      const w = parseFloat(weightInput.value) || 0;\n      const k = parseFloat(kcalInput.value) || 0;\n      const res = Math.round((w * k) \/ 100);\n      calcTotalDisplay.innerText = res;\n      return res;\n    }\n\n    \/\/ Appel API Open Food Facts\n    function fetchFoodData(query) {\n      if (currentAbortCtrl) {\n        currentAbortCtrl.abort();\n      }\n      currentAbortCtrl = new AbortController();\n\n      loadingStatus.style.display = \"block\";\n      loadingStatus.innerText = \"\ud83d\udd0d Recherche en cours sur l'API...\";\n\n      const url = `https:\/\/fr.openfoodfacts.org\/cgi\/search.pl?search_terms=${encodeURIComponent(query)}&search_simple=1&action=process&json=1&page_size=8&fields=product_name,product_name_fr,brands,nutriments`;\n\n      fetch(url, { signal: currentAbortCtrl.signal })\n        .then(response => {\n          if (!response.ok) throw new Error(\"Erreur r\u00e9seau\");\n          return response.json();\n        })\n        .then(data => {\n          loadingStatus.style.display = \"none\";\n          dropdown.innerHTML = \"\";\n\n          const products = (data && data.products) ? data.products : [];\n          const validProducts = products.filter(p => {\n            const n = p.nutriments || {};\n            const kcal = n['energy-kcal_100g'] || (n['energy-kcal'] ? n['energy-kcal'] : (n['energy_100g'] ? n['energy_100g'] \/ 4.184 : null));\n            return kcal && kcal > 0;\n          });\n\n          if (validProducts.length === 0) {\n            dropdown.innerHTML = `<div style=\"padding:10px; font-size:0.85rem; color:#718096; text-align:center;\">Aucun aliment avec calories trouv\u00e9 pour \u00ab ${query} \u00bb<\/div>`;\n            dropdown.style.display = \"block\";\n            return;\n          }\n\n          validProducts.forEach(product => {\n            const name = product.product_name_fr || product.product_name || \"Aliment\";\n            const brand = product.brands ? product.brands.split(',')[0].trim() : \"\";\n            const n = product.nutriments || {};\n            const kcal = Math.round(n['energy-kcal_100g'] || (n['energy-kcal'] ? n['energy-kcal'] : n['energy_100g'] \/ 4.184));\n\n            const row = document.createElement(\"div\");\n            row.className = \"ct-dropdown-item\";\n            row.innerHTML = `\n              <div style=\"overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-right:8px;\">\n                <strong>${name}<\/strong>\n                ${brand ? `<span class=\"ct-brand-tag\">${brand}<\/span>` : ''}\n              <\/div>\n              <div style=\"font-weight:700; color:#319795; white-space:nowrap;\">${kcal} kcal<\/div>\n            `;\n\n            row.addEventListener(\"click\", function () {\n              searchInput.value = name + (brand ? ` (${brand})` : '');\n              kcalInput.value = kcal;\n              dropdown.style.display = \"none\";\n              updateCalc();\n            });\n\n            dropdown.appendChild(row);\n          });\n\n          dropdown.style.display = \"block\";\n        })\n        .catch(err => {\n          if (err.name === 'AbortError') return;\n          loadingStatus.style.display = \"block\";\n          loadingStatus.innerText = \"\u26a0\ufe0f Erreur lors de la recherche API. V\u00e9rifiez votre connexion.\";\n        });\n    }\n\n    searchInput.addEventListener(\"input\", function () {\n      clearTimeout(debounceTimer);\n      const query = this.value.trim();\n\n      if (query.length < 3) {\n        dropdown.style.display = \"none\";\n        loadingStatus.style.display = \"none\";\n        return;\n      }\n\n      debounceTimer = setTimeout(() => {\n        fetchFoodData(query);\n      }, 350);\n    });\n\n    document.addEventListener(\"click\", function (e) {\n      if (!dropdown.contains(e.target) && e.target !== searchInput) {\n        dropdown.style.display = \"none\";\n      }\n    });\n\n    weightInput.addEventListener(\"input\", updateCalc);\n    kcalInput.addEventListener(\"input\", updateCalc);\n\n    goalInput.addEventListener(\"change\", function () {\n      appState.goal = parseInt(this.value, 10) || 2000;\n      saveAndRender();\n    });\n\n    addBtn.addEventListener(\"click\", function () {\n      const name = searchInput.value.trim();\n      const w = parseFloat(weightInput.value) || 0;\n      const totalKcal = updateCalc();\n\n      if (!name || totalKcal <= 0) {\n        alert(\"Veuillez s\u00e9lectionner un aliment et v\u00e9rifier les calories.\");\n        return;\n      }\n\n      appState.entries.push({\n        id: Date.now(),\n        name: name,\n        weight: w,\n        kcal: totalKcal\n      });\n\n      searchInput.value = \"\";\n      kcalInput.value = \"\";\n      calcTotalDisplay.innerText = \"0\";\n\n      saveAndRender();\n    });\n\n    resetBtn.addEventListener(\"click\", function () {\n      if (confirm(\"Effacer tout le journal du jour ?\")) {\n        appState.entries = [];\n        saveAndRender();\n      }\n    });\n\n    function saveAndRender() {\n      localStorage.setItem(\"my_wp_calorie_data\", JSON.stringify(appState));\n      \n      itemsList.innerHTML = \"\";\n      let total = 0;\n\n      appState.entries.forEach(entry => {\n        total += entry.kcal;\n        const li = document.createElement(\"li\");\n        li.className = \"ct-row\";\n        li.innerHTML = `\n          <span><strong>${entry.name}<\/strong> (${entry.weight} g)<\/span>\n          <span>\n            ${entry.kcal} kcal\n            <button class=\"ct-del-x\" data-id=\"${entry.id}\">\u00d7<\/button>\n          <\/span>\n        `;\n        itemsList.appendChild(li);\n      });\n\n      if (appState.entries.length === 0) {\n        itemsList.innerHTML = '<li style=\"text-align:center; color:#a0aec0; font-size:0.85rem; padding:6px 0;\">Aucun aliment enregistr\u00e9<\/li>';\n      }\n\n      document.querySelectorAll(\".ct-del-x\").forEach(btn => {\n        btn.addEventListener(\"click\", function () {\n          const id = parseInt(this.getAttribute(\"data-id\"), 10);\n          appState.entries = appState.entries.filter(item => item.id !== id);\n          saveAndRender();\n        });\n      });\n\n      const goal = appState.goal || 2000;\n      const remaining = goal - total;\n\n      consumedText.innerText = total;\n      remainingText.innerText = remaining;\n\n      barFill.style.width = (total > 0 ? (total \/ goal) * 100 : 0) + \"%\";\n\n      if (total > goal) {\n        barFill.classList.add(\"over\");\n      } else {\n        barFill.classList.remove(\"over\");\n      }\n    }\n\n    saveAndRender();\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\"DOMContentLoaded\", startTracker);\n  } else {\n    startTracker();\n  }\n})();\n<\/script>\n\n\n","protected":false},"excerpt":{"rendered":"<p>Journal de Calories Objectif quotidien : kcal Consomm\u00e9 : 0 kcal Restant : 2000 kcal Rechercher un aliment (API) Nom de l&#8217;aliment \/ marque : \ud83d\udd0d Recherche en cours&#8230; Portion (g ou ml) : kcal pour 100g : Total estim\u00e9 : 0 kcal Ajouter au journal Repas enregistr\u00e9s Tout vider<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"open","template":"","meta":{"footnotes":""},"class_list":["post-2","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/gabrielmillon.com\/index.php\/wp-json\/wp\/v2\/pages\/2","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gabrielmillon.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/gabrielmillon.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/gabrielmillon.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gabrielmillon.com\/index.php\/wp-json\/wp\/v2\/comments?post=2"}],"version-history":[{"count":4,"href":"https:\/\/gabrielmillon.com\/index.php\/wp-json\/wp\/v2\/pages\/2\/revisions"}],"predecessor-version":[{"id":187,"href":"https:\/\/gabrielmillon.com\/index.php\/wp-json\/wp\/v2\/pages\/2\/revisions\/187"}],"wp:attachment":[{"href":"https:\/\/gabrielmillon.com\/index.php\/wp-json\/wp\/v2\/media?parent=2"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}