175 lines
5.7 KiB
JavaScript
175 lines
5.7 KiB
JavaScript
// ---------- ÉTAT ----------
|
|
let HISTORY_CACHE = []; // [{ date, rawDate, items: [...] }]
|
|
let HISTORY_SEARCH = "";
|
|
|
|
// Normalisation basique pour la recherche
|
|
const ACCENT_RX = /[\u0300-\u036f]/g;
|
|
const norm = s => (s || "").toString().toLowerCase()
|
|
.normalize("NFD").replace(ACCENT_RX, "").trim();
|
|
|
|
// Palette de couleurs (plus large possible pour différencier)
|
|
const USER_COLORS = [
|
|
"#C0392B", // rouge foncé
|
|
"#2980B9", // bleu foncé
|
|
"#27AE60", // vert foncé
|
|
"#D35400", // orange foncé
|
|
"#8E44AD", // violet foncé
|
|
"#2C3E50", // gris/bleu très foncé
|
|
"#7D3C98", // violet profond
|
|
"#1ABC9C" // turquoise foncé
|
|
];
|
|
// Cache pour assigner une couleur unique par user
|
|
const userColorMap = new Map();
|
|
let userColorIndex = 0;
|
|
|
|
function getUserColor(username) {
|
|
if (!username) return "#999";
|
|
if (!userColorMap.has(username)) {
|
|
// Assigne une couleur en cycle
|
|
userColorMap.set(username, USER_COLORS[userColorIndex % USER_COLORS.length]);
|
|
userColorIndex++;
|
|
}
|
|
return userColorMap.get(username);
|
|
}
|
|
|
|
function buildUserBadges(usersArr) {
|
|
const wrap = document.createElement("div");
|
|
wrap.className = "label-container";
|
|
(usersArr || []).forEach(u => {
|
|
const span = document.createElement("span");
|
|
span.className = "label-badge user-badge";
|
|
span.textContent = u;
|
|
|
|
// Couleur dynamique par utilisateur
|
|
span.style.backgroundColor = getUserColor(u);
|
|
span.style.color = "#fff"; // texte blanc pour lisibilité
|
|
|
|
wrap.appendChild(span);
|
|
});
|
|
return wrap;
|
|
}
|
|
|
|
const truncate = (s, m) => (s && s.length > m ? s.slice(0, m) + "…" : (s ?? ""));
|
|
|
|
function buildRecipeCardHistory(rec) {
|
|
const card = document.createElement("div");
|
|
card.className = "card";
|
|
card.id = `hist-${rec.id}`;
|
|
|
|
const name = rec.name || `Recette ${rec.id}`;
|
|
const imgSrc = rec.image || "/images/recipe-placeholder.png"; // mets un vrai fichier ici
|
|
|
|
card.innerHTML = `
|
|
<div class="image-container">
|
|
<img src="${imgSrc}" alt="${truncate(name, 10)}"
|
|
onerror="this.onerror=null;this.src='/images/recipe-placeholder.png'">
|
|
<div class="badge-portions">${rec.portions} portion${rec.portions > 1 ? 's' : ''}</div>
|
|
</div>
|
|
<div class="card-content"><h3>${name}</h3></div>
|
|
`;
|
|
|
|
// badges utilisateurs (JSON camelCase => "users")
|
|
const users = Array.isArray(rec.users) ? rec.users : Array.isArray(rec.Users) ? rec.Users : [];
|
|
if (users.length) {
|
|
card.querySelector(".image-container")?.appendChild(buildUserBadges(users));
|
|
}
|
|
return card;
|
|
}
|
|
|
|
|
|
function buildDateSection(dateStr, items) {
|
|
const sec = document.createElement("section");
|
|
sec.className = "history-section";
|
|
|
|
const h = document.createElement("h3");
|
|
h.className = "history-date";
|
|
h.textContent = dateStr;
|
|
sec.appendChild(h);
|
|
|
|
const grid = document.createElement("div");
|
|
grid.className = "hf-grid";
|
|
if (Array.isArray(items) && items.length) {
|
|
items.forEach(r => grid.appendChild(buildRecipeCardHistory(r)));
|
|
} else {
|
|
grid.innerHTML = `<p class="placeholder">Aucune recette pour cette date.</p>`;
|
|
}
|
|
sec.appendChild(grid);
|
|
return sec;
|
|
}
|
|
|
|
// Applique la recherche sur le cache et rend
|
|
function renderHistoryFiltered() {
|
|
const container = document.getElementById("historyContainer");
|
|
if (!container) return;
|
|
|
|
const q = norm(HISTORY_SEARCH);
|
|
const groups = [];
|
|
|
|
for (const g of (HISTORY_CACHE || [])) {
|
|
if (!q) {
|
|
groups.push(g);
|
|
continue;
|
|
}
|
|
const filteredItems = (g.items || []).filter(it => norm(it.name).includes(q));
|
|
if (filteredItems.length) {
|
|
groups.push({ date: g.date, rawDate: g.rawDate, items: filteredItems });
|
|
}
|
|
}
|
|
|
|
container.innerHTML = "";
|
|
if (!groups.length) {
|
|
container.innerHTML = `<p class="placeholder">Aucun résultat pour “${HISTORY_SEARCH}”.</p>`;
|
|
return;
|
|
}
|
|
groups.forEach(group => container.appendChild(buildDateSection(group.date, group.items)));
|
|
}
|
|
|
|
// ---------- CHARGEMENT ----------
|
|
async function loadHistory(scope = "mine") {
|
|
const container = document.getElementById("historyContainer");
|
|
if (!container) return;
|
|
container.innerHTML = "Chargement…";
|
|
try {
|
|
const isUserImportant = (scope !== "all");
|
|
const res = await fetch(`/HelloFresh/GetHistory?isUserImportant=${isUserImportant ? "true" : "false"}&search=${encodeURIComponent(HISTORY_SEARCH)}`, { credentials: "same-origin" });
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
const data = await res.json(); // [{ date, rawDate, items }]
|
|
|
|
HISTORY_CACHE = Array.isArray(data) ? data : [];
|
|
renderHistoryFiltered(); // premier rendu (avec éventuel terme déjà saisi)
|
|
} catch (e) {
|
|
console.error("loadHistory error:", e);
|
|
container.innerHTML = `<p class="placeholder">Erreur de chargement.</p>`;
|
|
}
|
|
}
|
|
|
|
// ---------- INIT ----------
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
const scopeSel = document.getElementById("historyScope");
|
|
const search = document.getElementById("historySearch");
|
|
|
|
// charge au démarrage
|
|
loadHistory(scopeSel?.value || "mine");
|
|
|
|
// changement de portée
|
|
scopeSel?.addEventListener("change", () => loadHistory(scopeSel.value || "mine"));
|
|
|
|
// recherche (debounce)
|
|
let timer;
|
|
search?.addEventListener("input", (e) => {
|
|
HISTORY_SEARCH = e.target.value || "";
|
|
clearTimeout(timer);
|
|
timer = setTimeout(renderHistoryFiltered, 200);
|
|
});
|
|
|
|
// Entrée = rendu immédiat
|
|
search?.addEventListener("keydown", (e) => {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
clearTimeout(timer);
|
|
renderHistoryFiltered();
|
|
}
|
|
});
|
|
});
|
|
|