From 916fd6a3f6076f359be7cf792cdee09dd2971864 Mon Sep 17 00:00:00 2001
From: Morgan Wattiez <morgan@zoemp.be>
Date: Sun, 25 May 2025 02:47:58 +0200
Subject: [PATCH] filter by age without researching again

---
 public/index.html | 246 +++++++++++++++++++++++++---------------------
 1 file changed, 132 insertions(+), 114 deletions(-)

diff --git a/public/index.html b/public/index.html
index 42b46e7..7e0376a 100644
--- a/public/index.html
+++ b/public/index.html
@@ -123,13 +123,23 @@
     badge.textContent = val + '+';
     badge.style.background = getAgeColor(val);
   }
-  async function search() {
+  let lastQuery = '';
+  let lastResults = [];
+
+  async function search(force = false) {
     const query = document.getElementById('q').value.trim();
     if (!query) return;
     const maxAge = parseInt(document.getElementById('maxAge').value, 10);
     window.history.replaceState({}, '', '?q=' + encodeURIComponent(query) + (maxAge < 21 ? `&maxAge=${maxAge}` : ''));
     const filmsDiv = document.getElementById('films');
     filmsDiv.innerHTML = '<p class="loader">Searching...</p>';
+
+    // Si on a déjà cherché ce terme, et pas force, on ne refait pas le fetch
+    if (!force && lastQuery === query && lastResults.length) {
+      renderFilms(filterFilmsByMaxAge(lastResults, maxAge));
+      return;
+    }
+
     try {
       const base = window.location.origin;
       const response = await fetch(`${base}/search?q=${encodeURIComponent(query)}`);
@@ -138,16 +148,8 @@
         return;
       }
       let films = await response.json();
-      if (!Array.isArray(films) || !films.length) {
-        filmsDiv.innerHTML = '<p class="no-results">No results. Try another query.</p>';
-        return;
-      }
-      // Filtres sources inutiles (pas de description et/ou pas d'âge)
       films = films.map(film => {
         film.results = film.results.filter(r => {
-          // On considère valide si :
-          //   - summary ou parentsNeedToKnow ou details.summary >= 8 chars
-          //   - ET un âge existe (age, normalizedMarks, marks, details.ageLegal, etc)
           let hasDescription =
             (r.summary && r.summary.length >= 8) ||
             (r.parentsNeedToKnow && r.parentsNeedToKnow.length >= 8) ||
@@ -159,118 +161,133 @@
         return film;
       }).filter(film => film.results.length > 0);
 
-      if (isFinite(maxAge)) {
-        films = films.filter(film => {
-          const uniqueResults = [];
-          const seenSources = new Set();
-          film.results.forEach(r => {
-            if (!seenSources.has(r.source)) {
-              uniqueResults.push(r);
-              seenSources.add(r.source);
-            }
-          });
-          const ages = getAllAges(uniqueResults);
-          if (ages.length === 0) return true;
-          return Math.max(...ages) <= maxAge;
-        });
-      }
-      if (!films.length) {
-        filmsDiv.innerHTML = '<p class="no-results">No results for this max age.</p>';
-        return;
-      }
-      let html = `<table class="results-table">
-        <thead>
-          <tr>
-            <th>Title</th>
-            <th>Year</th>
-            <th>Information sources</th>
-          </tr>
-        </thead>
-        <tbody>`;
-      films.forEach(film => {
-        const allImgs = film.results.map(r => r.img).filter(Boolean);
-        const mainImg = allImgs.length ? allImgs[0] : null;
-        html += `<tr>
-          <td style="vertical-align:top;">
-            <div style="text-align:center;">
-              <div style="font-weight:bold;margin-bottom:0.7em;">${film.title || 'Unknown title'}</div>
-              ${mainImg ? `<img src="${mainImg}" alt="Poster for ${film.title}" style="display:block;margin:auto;max-width:100px;max-height:150px;border-radius:5px;box-shadow:0 2px 8px #0003;margin-bottom:10px;">` : ''}
-            </div>
-          </td>
-          <td class="year" style="vertical-align:top;">${film.year || 'N/A'}</td>
-          <td>`;
-        const uniqueResults = [];
-        const seenSources = new Set();
-        film.results.forEach(r => {
-          if (!seenSources.has(r.source)) {
-            uniqueResults.push(r);
-            seenSources.add(r.source);
-          }
-        });
-        uniqueResults.forEach(r => {
-          html += `<div class="source-block">`;
-          html += `<p class="source-name">${r.source.charAt(0).toUpperCase() + r.source.slice(1)}</p>`;
-          if (r.link) {
-            html += `<p><a href="${r.link}" target="_blank">View details</a></p>`;
-          }
-          if (r.source === 'commonsense') {
-            html += `<p><b>Recommended age:</b> ${ageBadge(r.age)}</p>`;
-            html += `<p><b>Summary (CSM):</b> ${shortSummary(r.summary || r.parentsNeedToKnow)}</p>`;
-            if (r.details && r.details.length) {
-              html += `<p><b>Details (CSM):</b></p><ul>`;
-              r.details.forEach(d => {
-                html += `<li>${d.type}: ${d.score}/5 - ${shortSummary(d.description, 80)}</li>`;
-              });
-              html += `</ul>`;
-            }
-          }
-          else if (r.source === 'cinecheck') {
-            let numericAges = Array.isArray(r.normalizedMarks) && r.normalizedMarks.length
-              ? r.normalizedMarks
-              : (r.marks || []).map(x => {
-                  let n = parseInt((x + '').replace(/\d+/, ''), 10);
-                  return isNaN(n) ? null : n;
-                }).filter(n => n !== null && !isNaN(n));
-            let minAge = numericAges.length ? Math.min(...numericAges) : '';
-            html += `<p><b>Age(s) (Cinecheck):</b> ${minAge ? ageBadge(minAge) : ''}${r.marks && r.marks.length ? r.marks.join(', ') : '-'}</p>`;
-            html += `<p><b>Summary (Cinecheck):</b> ${shortSummary(r.summary)}</p>`;
-            if (r.details && r.details.length) {
-              html += `<p><b>Pictograms (Cinecheck):</b> ${r.details.map(d => d.type).join(', ') || '-'}</p>`;
-            }
-          }
-          else if (r.source === 'filmages') {
-            html += `<p><b>Original title (Filmages):</b> ${r.details.titleOriginalPage || r.details.titleOriginal || '-'}</p>`;
-            html += `<p><b>Legal age (Filmages):</b> ${ageBadge(r.details.ageLegal)}</p>`;
-            html += `<p><b>Suggested age (Filmages):</b> ${ageBadge(r.details.ageSuggested)}</p>`;
-            html += `<p><b>Summary (Filmages):</b> ${shortSummary(r.details.summary)}</p>`;
-            html += `<p><b>Synthesis (Filmages):</b> ${shortSummary(r.details.synthesis, 100)}</p>`;
-            if (r.details.indications && r.details.indications.length) {
-              html += `<p><b>Indications:</b> ${shortSummary(r.details.indications.join(', '), 100)}</p>`;
-            }
-            if (r.details.counterIndications && r.details.counterIndications.length) {
-              html += `<p><b>Contra-indications:</b> ${shortSummary(r.details.counterIndications.join(', '), 100)}</p>`;
-            }
-          }
-          else {
-            html += `<p><b>Summary:</b> ${shortSummary(r.summary)}</p>`;
-            html += `<p><b>Age:</b> ${ageBadge(r.age)}</p>`;
-          }
-          html += `</div>`;
-        });
-        html += `</td></tr>`;
-      });
-      html += '</tbody></table>';
-      filmsDiv.innerHTML = html;
+      lastQuery = query;
+      lastResults = films;
+      renderFilms(filterFilmsByMaxAge(films, maxAge));
     } catch (error) {
       console.error('Search function error:', error);
       filmsDiv.innerHTML = `<p class="no-results">Search failed. Check the console.</p>`;
     }
   }
 
+  function filterFilmsByMaxAge(films, maxAge) {
+    if (!isFinite(maxAge)) return films;
+    return films.filter(film => {
+      const uniqueResults = [];
+      const seenSources = new Set();
+      film.results.forEach(r => {
+        if (!seenSources.has(r.source)) {
+          uniqueResults.push(r);
+          seenSources.add(r.source);
+        }
+      });
+      const ages = getAllAges(uniqueResults);
+      if (ages.length === 0) return true;
+      return Math.max(...ages) <= maxAge;
+    });
+  }
+
+  // Extraction de la partie rendering pour pouvoir la réutiliser
+  function renderFilms(films) {
+    const filmsDiv = document.getElementById('films');
+    if (!films.length) {
+      filmsDiv.innerHTML = '<p class="no-results">No results for this max age.</p>';
+      return;
+    }
+    let html = `<table class="results-table">
+      <thead>
+        <tr>
+          <th>Title</th>
+          <th>Year</th>
+          <th>Information sources</th>
+        </tr>
+      </thead>
+      <tbody>`;
+    films.forEach(film => {
+      const allImgs = film.results.map(r => r.img).filter(Boolean);
+      const mainImg = allImgs.length ? allImgs[0] : null;
+      html += `<tr>
+        <td style="vertical-align:top;">
+          <div style="text-align:center;">
+            <div style="font-weight:bold;margin-bottom:0.7em;">${film.title || 'Unknown title'}</div>
+            ${mainImg ? `<img src="${mainImg}" alt="Poster for ${film.title}" style="display:block;margin:auto;max-width:100px;max-height:150px;border-radius:5px;box-shadow:0 2px 8px #0003;margin-bottom:10px;">` : ''}
+          </div>
+        </td>
+        <td class="year" style="vertical-align:top;">${film.year || 'N/A'}</td>
+        <td>`;
+      const uniqueResults = [];
+      const seenSources = new Set();
+      film.results.forEach(r => {
+        if (!seenSources.has(r.source)) {
+          uniqueResults.push(r);
+          seenSources.add(r.source);
+        }
+      });
+      uniqueResults.forEach(r => {
+        html += `<div class="source-block">`;
+        html += `<p class="source-name">${r.source.charAt(0).toUpperCase() + r.source.slice(1)}</p>`;
+        if (r.link) {
+          html += `<p><a href="${r.link}" target="_blank">View details</a></p>`;
+        }
+        if (r.source === 'commonsense') {
+          html += `<p><b>Recommended age:</b> ${ageBadge(r.age)}</p>`;
+          html += `<p><b>Summary (CSM):</b> ${shortSummary(r.summary || r.parentsNeedToKnow)}</p>`;
+          if (r.details && r.details.length) {
+            html += `<p><b>Details (CSM):</b></p><ul>`;
+            r.details.forEach(d => {
+              html += `<li>${d.type}: ${d.score}/5 - ${shortSummary(d.description, 80)}</li>`;
+            });
+            html += `</ul>`;
+          }
+        }
+        else if (r.source === 'cinecheck') {
+          let numericAges = Array.isArray(r.normalizedMarks) && r.normalizedMarks.length
+            ? r.normalizedMarks
+            : (r.marks || []).map(x => {
+                let n = parseInt((x + '').replace(/\d+/, ''), 10);
+                return isNaN(n) ? null : n;
+              }).filter(n => n !== null && !isNaN(n));
+          let minAge = numericAges.length ? Math.min(...numericAges) : '';
+          html += `<p><b>Age(s) (Cinecheck):</b> ${minAge ? ageBadge(minAge) : ''}${r.marks && r.marks.length ? r.marks.join(', ') : '-'}</p>`;
+          html += `<p><b>Summary (Cinecheck):</b> ${shortSummary(r.summary)}</p>`;
+          if (r.details && r.details.length) {
+            html += `<p><b>Pictograms (Cinecheck):</b> ${r.details.map(d => d.type).join(', ') || '-'}</p>`;
+          }
+        }
+        else if (r.source === 'filmages') {
+          html += `<p><b>Original title (Filmages):</b> ${r.details.titleOriginalPage || r.details.titleOriginal || '-'}</p>`;
+          html += `<p><b>Legal age (Filmages):</b> ${ageBadge(r.details.ageLegal)}</p>`;
+          html += `<p><b>Suggested age (Filmages):</b> ${ageBadge(r.details.ageSuggested)}</p>`;
+          html += `<p><b>Summary (Filmages):</b> ${shortSummary(r.details.summary)}</p>`;
+          html += `<p><b>Synthesis (Filmages):</b> ${shortSummary(r.details.synthesis, 100)}</p>`;
+          if (r.details.indications && r.details.indications.length) {
+            html += `<p><b>Indications:</b> ${shortSummary(r.details.indications.join(', '), 100)}</p>`;
+          }
+          if (r.details.counterIndications && r.details.counterIndications.length) {
+            html += `<p><b>Contra-indications:</b> ${shortSummary(r.details.counterIndications.join(', '), 100)}</p>`;
+          }
+        }
+        else {
+          html += `<p><b>Summary:</b> ${shortSummary(r.summary)}</p>`;
+          html += `<p><b>Age:</b> ${ageBadge(r.age)}</p>`;
+        }
+        html += `</div>`;
+      });
+      html += `</td></tr>`;
+    });
+    html += '</tbody></table>';
+    filmsDiv.innerHTML = html;
+  }
+
   document.getElementById('maxAge').addEventListener('input', function() {
     updateMaxAgeDisplay();
-    search();
+    // Pas de fetch, juste filtre en front
+    const query = document.getElementById('q').value.trim();
+    if (!query || !lastResults.length) return;
+    const maxAge = parseInt(document.getElementById('maxAge').value, 10);
+    renderFilms(filterFilmsByMaxAge(lastResults, maxAge));
   });
+
   window.addEventListener('DOMContentLoaded', () => {
     const params = new URLSearchParams(window.location.search);
     const q = params.get('q');
@@ -281,10 +298,11 @@
     }
     if (q) {
       document.getElementById('q').value = q;
-      search();
+      search(true); // force fetch initiale
     }
   });
-  document.getElementById('q').addEventListener('keydown', e => { if (e.key === 'Enter') search(); });
+  document.getElementById('q').addEventListener('keydown', e => { if (e.key === 'Enter') search(true); });
+
 </script>
 </body>
 </html>