This commit is contained in:
2025-09-11 00:02:38 +02:00
parent e78ea9da35
commit 902c8c52a9
6 changed files with 122 additions and 89 deletions

View File

@@ -5,7 +5,6 @@ let currentPage = 1;
let lastPageGlobal = 1;
const countPerPage = 12;
let currentSearchTerm = '';
const MAX_PORTIONS = 14;
let CURRENT_CLIENT_LIST = null; // liste filtrée paginée côté client
let ALL_RECIPES_CACHE = null; // toutes les recettes (toutes pages)
@@ -19,10 +18,19 @@ const labelMaps = { ingredients: {}, tags: {} };
// idRecette -> portions (0..3)
const ownedMap = new Map();
const MAX_PER_RECIPE = null; // null = illimité par recette
const MAX_TOTAL = null; // null = illimité au total
const clampQty = (n) => {
const v = Number(n) || 0;
if (v < 0) return 0;
// si bornes définies, on clamp ; sinon on laisse passer
if (Number.isFinite(MAX_PER_RECIPE)) return Math.min(MAX_PER_RECIPE, v);
return v;
};
/***********************
* UTILS
***********************/
const clampQty = n => Math.max(0, Math.min(3, Number(n) || 0));
const truncate = (s, m) => (s && s.length > m ? s.slice(0, m) + '…' : (s ?? ''));
const normalize = (str) => (str ?? '').toString().normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().trim();
const splitTags = (str) => (str || '').split(/[,•]/).map(t => t.trim()).filter(Boolean).filter(t => t !== '•');
@@ -232,7 +240,12 @@ function buildRecipeCardList(recipe) {
}
const current = clampQty(ownedMap.get(String(recipe.id)) || 0);
if (current === 0 && !canAddPortion()) { alert(`Limite atteinte (${MAX_PORTIONS}).`); return; }
if (current === 0 && !canAddPortion(recipe.id)) {
// message seulement s'il y a vraiment une limite
if (Number.isFinite(MAX_TOTAL)) alert(`Limite atteinte (${MAX_TOTAL}).`);
else if (Number.isFinite(MAX_PER_RECIPE)) alert(`Limite par recette atteinte (${MAX_PER_RECIPE}).`);
return;
};
const selectedNow = current > 0;
if (!selectedNow) await saveRecipe(recipe.id); else await clearRecipe(recipe.id);
applySelectionUIById(recipe.id);
@@ -273,8 +286,15 @@ function buildRecipeCardOwned(recipe) {
// plus/moins existants
card.querySelector('.btn-minus')?.addEventListener('click', (e) => { e.stopPropagation(); removeRecette(recipe.id); updateOwnedCountUI(); if (getTotalPortionsFromMap() === 0) closeOwnedIfOpen(); });
card.querySelector('.btn-plus')?.addEventListener('click', (e) => { e.stopPropagation(); if (!canAddPortion()) { alert(`Limite atteinte (${MAX_PORTIONS}).`); return; } saveRecipe(recipe.id); updateOwnedCountUI(); });
card.querySelector('.btn-plus')?.addEventListener('click', (e) => {
e.stopPropagation();
if (!canAddPortion(recipe.id)) {
if (Number.isFinite(MAX_TOTAL)) alert(`Limite atteinte (${MAX_TOTAL}).`);
else if (Number.isFinite(MAX_PER_RECIPE)) alert(`Limite par recette atteinte (${MAX_PER_RECIPE}).`);
return;
}
saveRecipe(recipe.id); updateOwnedCountUI();
});
// 🟢 Ctrl+clic = ouvrir le PDF (avec fallback GetRecipesDetails)
card.addEventListener('click', async (e) => {
if (!e.ctrlKey) return;
@@ -743,12 +763,28 @@ function wireFooterButtons() {
***********************/
function getTotalPortionsFromMap() { let total = 0; ownedMap.forEach(v => { total += (Number(v) || 0); }); return total; }
function updateOwnedCountUI() {
const el = document.getElementById('ownedCount'); if (!el) return;
const total = getTotalPortionsFromMap(); el.textContent = `(${total}/${MAX_PORTIONS})`;
el.style.color = (total >= MAX_PORTIONS) ? '#c62828' : '';
const el = document.getElementById('ownedCount'); if (!el) return;
const total = getTotalPortionsFromMap();
if (Number.isFinite(MAX_TOTAL)) {
el.textContent = `(${total}/${MAX_TOTAL})`;
el.style.color = (total >= MAX_TOTAL) ? '#c62828' : '';
} else {
// affichage sans borne
el.textContent = `(${total})`;
el.style.color = '';
}
}
function canAddPortion(forRecipeId) {
// limite globale
if (Number.isFinite(MAX_TOTAL) && getTotalPortionsFromMap() >= MAX_TOTAL) return false;
// limite par recette
if (Number.isFinite(MAX_PER_RECIPE)) {
const curr = Number(ownedMap.get(String(forRecipeId)) || 0);
return curr < MAX_PER_RECIPE;
}
// illimité
return true;
}
function canAddPortion() { return getTotalPortionsFromMap() < MAX_PORTIONS; }
/***********************
* Ingredients meta attach (for page)
***********************/