diff --git a/assets/js/app.js b/assets/js/app.js
index dbd71e3..795f5fa 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -1398,6 +1398,7 @@ async function _loadConfigPage() {
await _loadCategoriesConfigSection();
await _loadSubcategoryConfigSection();
await _loadRecipeTagsConfigSection();
+ await _loadCustomUnitsConfigSection();
}
async function _loadRecipeTagsConfigSection() {
@@ -1507,6 +1508,134 @@ async function removeRecipeTagConfig(key) {
}
}
+async function _loadCustomUnitsConfigSection() {
+ const container = document.getElementById('custom-units-list-container');
+ if (!container) return;
+ container.innerHTML = `
Chargement…
`;
+ try {
+ const result = await api('custom_units_list', {}, 'GET');
+ if (!result.success) {
+ container.innerHTML = `Erreur de chargement.
`;
+ return;
+ }
+ CUSTOM_UNITS = result.units;
+ renderCustomUnitsConfigList(result.units);
+ } catch (e) {
+ container.innerHTML = `Erreur de chargement.
`;
+ }
+}
+
+function renderCustomUnitsConfigList(units) {
+ const container = document.getElementById('custom-units-list-container');
+ if (!container) return;
+ if (!units || units.length === 0) {
+ container.innerHTML = `Aucune unité personnalisée.
`;
+ return;
+ }
+ const baseOptionsHtml = (selected) => ['g', 'ml', 'pz'].map(b => ``).join('');
+ container.innerHTML = units.map(u => `
+
+
+ ${escapeHtml(u.key)}
+
+
+
+
+
+
+ `).join('');
+}
+
+async function addCustomUnitConfig() {
+ const iconInput = document.getElementById('new-unit-icon');
+ const keyInput = document.getElementById('new-unit-key');
+ const labelInput = document.getElementById('new-unit-label');
+ const baseInput = document.getElementById('new-unit-base');
+ const factorInput = document.getElementById('new-unit-factor');
+ const icon = iconInput.value.trim() || '📏';
+ const key = keyInput.value.trim();
+ const label = labelInput.value.trim();
+ const base_unit = baseInput.value;
+ const factor = parseFloat(factorInput.value);
+
+ if (!key || !label) {
+ showToast('Indique une clé et un nom pour la nouvelle unité', 'warning');
+ return;
+ }
+ if (!factor || factor <= 0) {
+ showToast('Indique un facteur de conversion valide', 'warning');
+ return;
+ }
+
+ showLoading(true);
+ try {
+ const result = await api('custom_units_add', {}, 'POST', { key, label, icon, base_unit, factor });
+ showLoading(false);
+ if (result.success) {
+ showToast(`Unité "${label}" ajoutée`, 'success');
+ keyInput.value = '';
+ labelInput.value = '';
+ factorInput.value = '';
+ _loadCustomUnitsConfigSection();
+ } else {
+ showToast(result.error || 'Erreur lors de l\'ajout', 'error');
+ }
+ } catch (e) {
+ showLoading(false);
+ showToast('Erreur lors de l\'ajout', 'error');
+ }
+}
+
+async function updateCustomUnitConfig(key) {
+ const icon = document.getElementById(`cunit-icon-${key}`).value.trim() || '📏';
+ const label = document.getElementById(`cunit-label-${key}`).value.trim();
+ const base_unit = document.getElementById(`cunit-base-${key}`).value;
+ const factor = parseFloat(document.getElementById(`cunit-factor-${key}`).value);
+
+ if (!label) {
+ showToast('Le nom ne peut pas être vide', 'warning');
+ return;
+ }
+ if (!factor || factor <= 0) {
+ showToast('Facteur de conversion invalide', 'warning');
+ return;
+ }
+
+ showLoading(true);
+ try {
+ const result = await api('custom_units_update', {}, 'POST', { key, label, icon, base_unit, factor });
+ showLoading(false);
+ if (result.success) {
+ showToast('Unité mise à jour', 'success');
+ _loadCustomUnitsConfigSection();
+ } else {
+ showToast(result.error || 'Erreur', 'error');
+ }
+ } catch (e) {
+ showLoading(false);
+ showToast('Erreur', 'error');
+ }
+}
+
+async function removeCustomUnitConfig(key) {
+ if (!confirm(`Supprimer cette unité ?`)) return;
+
+ showLoading(true);
+ try {
+ const result = await api('custom_units_remove', {}, 'POST', { key });
+ showLoading(false);
+ if (result.success) {
+ showToast('Unité supprimée', 'success');
+ _loadCustomUnitsConfigSection();
+ } else {
+ showToast(result.error || 'Impossible de supprimer', 'error');
+ }
+ } catch (e) {
+ showLoading(false);
+ showToast('Erreur lors de la suppression', 'error');
+ }
+}
+
async function _loadCategoriesConfigSection() {
const container = document.getElementById('categories-list-container');
if (!container) return;
@@ -15164,6 +15293,18 @@ async function loadRecipeTags() {
}
}
+let CUSTOM_UNITS = [];
+async function loadCustomUnits() {
+ try {
+ const result = await api('custom_units_list', {}, 'GET');
+ if (result.success && Array.isArray(result.units)) {
+ CUSTOM_UNITS = result.units;
+ }
+ } catch (e) {
+ console.warn('[EverShelf] Could not load custom units:', e);
+ }
+}
+
let _recipeLibraryCache = [];
async function loadRecipeLibrary() {
@@ -20508,6 +20649,7 @@ async function _initApp() {
// Load recipe tags (used by the "Mes recettes" form and filter bar)
await loadRecipeTags();
+ await loadCustomUnits();
// Check for setup wizard resume (after language change)
const resumeStep = localStorage.getItem('evershelf_setup_step');