Actualiser assets/js/app.js
CI / PHP Syntax Check (push) Has been cancelled
CI / JavaScript Lint (push) Has been cancelled
CI / Docker Build Test (push) Has been cancelled
CI / Validate Translation Files (push) Has been cancelled
CI / Auto-merge develop → main (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / PHP Syntax Check (push) Has been cancelled
CI / JavaScript Lint (push) Has been cancelled
CI / Docker Build Test (push) Has been cancelled
CI / Validate Translation Files (push) Has been cancelled
CI / Auto-merge develop → main (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
This commit is contained in:
+223
-15
@@ -1384,6 +1384,140 @@ async function _loadConfigPage() {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
container.innerHTML = `<p class="settings-hint">Erreur de chargement.</p>`;
|
container.innerHTML = `<p class="settings-hint">Erreur de chargement.</p>`;
|
||||||
}
|
}
|
||||||
|
await _loadSubcategoryConfigSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _loadSubcategoryConfigSection() {
|
||||||
|
const catSelect = document.getElementById('subcat-config-category');
|
||||||
|
if (!catSelect) return;
|
||||||
|
if (!catSelect.dataset.populated) {
|
||||||
|
catSelect.innerHTML = Object.entries(CATEGORY_LABELS).map(([key, label]) => `<option value="${key}">${label}</option>`).join('');
|
||||||
|
catSelect.dataset.populated = 'true';
|
||||||
|
}
|
||||||
|
await renderSubcategoryConfigForCategory(catSelect.value || Object.keys(CATEGORY_LABELS)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderSubcategoryConfigForCategory(category) {
|
||||||
|
const catSelect = document.getElementById('subcat-config-category');
|
||||||
|
if (catSelect) catSelect.value = category;
|
||||||
|
const requiredCheckbox = document.getElementById('subcat-config-required');
|
||||||
|
if (requiredCheckbox) requiredCheckbox.checked = REQUIRED_SUBCATEGORY_CATEGORIES.includes(category);
|
||||||
|
|
||||||
|
const container = document.getElementById('subcat-list-container');
|
||||||
|
if (!container) return;
|
||||||
|
const list = SUBCATEGORIES_BY_CATEGORY[category] || [];
|
||||||
|
if (list.length === 0) {
|
||||||
|
container.innerHTML = `<p class="settings-hint">Aucune sous-catégorie pour cette catégorie.</p>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.innerHTML = list.map(sc => `
|
||||||
|
<div class="loc-row" style="display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid var(--border,#e2e8f0)">
|
||||||
|
<input type="text" class="form-input" style="flex:1" value="${escapeHtml(sc.label)}" id="subcat-label-${sc.id}">
|
||||||
|
<button class="btn btn-small btn-primary" onclick="updateSubcategoryRow(${sc.id})" title="Enregistrer">💾</button>
|
||||||
|
<button class="btn btn-small btn-secondary" onclick="removeSubcategoryRow(${sc.id})" title="Supprimer">🗑️</button>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSubcatConfigCategoryChange() {
|
||||||
|
const cat = document.getElementById('subcat-config-category')?.value || '';
|
||||||
|
await renderSubcategoryConfigForCategory(cat);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleSubcategoryRequired() {
|
||||||
|
const category = document.getElementById('subcat-config-category')?.value || '';
|
||||||
|
const checked = document.getElementById('subcat-config-required')?.checked;
|
||||||
|
let updated = REQUIRED_SUBCATEGORY_CATEGORIES.slice();
|
||||||
|
if (checked && !updated.includes(category)) updated.push(category);
|
||||||
|
if (!checked) updated = updated.filter(c => c !== category);
|
||||||
|
|
||||||
|
showLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await api('app_settings_save', {}, 'POST', { settings: { subcategory_required_categories: updated } });
|
||||||
|
showLoading(false);
|
||||||
|
if (result.success) {
|
||||||
|
REQUIRED_SUBCATEGORY_CATEGORIES = updated;
|
||||||
|
showToast('Préférence enregistrée', 'success');
|
||||||
|
} else {
|
||||||
|
showToast('Erreur lors de l\'enregistrement', 'error');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showLoading(false);
|
||||||
|
showToast('Erreur lors de l\'enregistrement', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addSubcategoryRow() {
|
||||||
|
const category = document.getElementById('subcat-config-category')?.value || '';
|
||||||
|
const labelInput = document.getElementById('new-subcat-label');
|
||||||
|
const label = labelInput.value.trim();
|
||||||
|
if (!category) { showToast('Choisis une catégorie', 'warning'); return; }
|
||||||
|
if (!label) { showToast('Indique un nom pour la nouvelle sous-catégorie', 'warning'); return; }
|
||||||
|
|
||||||
|
showLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await api('subcategories_add', {}, 'POST', { category, label });
|
||||||
|
showLoading(false);
|
||||||
|
if (result.success) {
|
||||||
|
showToast(`Sous-catégorie "${label}" ajoutée`, 'success');
|
||||||
|
labelInput.value = '';
|
||||||
|
if (!SUBCATEGORIES_BY_CATEGORY[category]) SUBCATEGORIES_BY_CATEGORY[category] = [];
|
||||||
|
SUBCATEGORIES_BY_CATEGORY[category].push({ id: result.id, category, key: result.key, label });
|
||||||
|
SUBCATEGORY_LABEL_LOOKUP[category + '::' + result.key] = label;
|
||||||
|
renderSubcategoryConfigForCategory(category);
|
||||||
|
} else {
|
||||||
|
showToast(result.error || 'Erreur lors de l\'ajout', 'error');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showLoading(false);
|
||||||
|
showToast('Erreur lors de l\'ajout', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateSubcategoryRow(id) {
|
||||||
|
const label = document.getElementById(`subcat-label-${id}`)?.value.trim();
|
||||||
|
if (!label) { showToast('Le nom ne peut pas être vide', 'warning'); return; }
|
||||||
|
|
||||||
|
showLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await api('subcategories_update', {}, 'POST', { id, label });
|
||||||
|
showLoading(false);
|
||||||
|
if (result.success) {
|
||||||
|
showToast('Sous-catégorie mise à jour', 'success');
|
||||||
|
for (const cat in SUBCATEGORIES_BY_CATEGORY) {
|
||||||
|
const row = SUBCATEGORIES_BY_CATEGORY[cat].find(sc => sc.id === id);
|
||||||
|
if (row) { row.label = label; SUBCATEGORY_LABEL_LOOKUP[cat + '::' + row.key] = label; break; }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast(result.error || 'Erreur', 'error');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showLoading(false);
|
||||||
|
showToast('Erreur', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeSubcategoryRow(id) {
|
||||||
|
if (!confirm('Supprimer cette sous-catégorie ?')) return;
|
||||||
|
|
||||||
|
showLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await api('subcategories_remove', {}, 'POST', { id });
|
||||||
|
showLoading(false);
|
||||||
|
if (result.success) {
|
||||||
|
showToast('Sous-catégorie supprimée', 'success');
|
||||||
|
for (const cat in SUBCATEGORIES_BY_CATEGORY) {
|
||||||
|
SUBCATEGORIES_BY_CATEGORY[cat] = SUBCATEGORIES_BY_CATEGORY[cat].filter(sc => sc.id !== id);
|
||||||
|
}
|
||||||
|
const category = document.getElementById('subcat-config-category')?.value || '';
|
||||||
|
renderSubcategoryConfigForCategory(category);
|
||||||
|
} else {
|
||||||
|
showToast(result.error || 'Impossible de supprimer', 'error');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showLoading(false);
|
||||||
|
showToast('Erreur lors de la suppression', 'error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderLocationsList(locations) {
|
function renderLocationsList(locations) {
|
||||||
@@ -1817,10 +1951,57 @@ const CATEGORY_LABELS = {
|
|||||||
'altro': `📦 ${t('categories.altro')}`
|
'altro': `📦 ${t('categories.altro')}`
|
||||||
};
|
};
|
||||||
|
|
||||||
const SUBCATEGORY_LABELS = {
|
// Sous-catégories chargées dynamiquement depuis la base (table subcategories), gérables via Config
|
||||||
'vin': '🍷 Vin', 'biere': '🍺 Bière', 'spiritueux': '🥃 Spiritueux',
|
let SUBCATEGORIES_BY_CATEGORY = {}; // { category: [{id, category, key, label}, ...] }
|
||||||
'soda': '🥤 Soda', 'jus': '🧃 Jus', 'eau': '💧 Eau', 'autre': '📦 Autre'
|
let SUBCATEGORY_LABEL_LOOKUP = {}; // { "category::key": label } — évite les collisions entre catégories
|
||||||
};
|
let REQUIRED_SUBCATEGORY_CATEGORIES = ['bevande']; // valeur par défaut, écrasée par syncSettingsFromDB()
|
||||||
|
|
||||||
|
async function loadCustomSubcategories() {
|
||||||
|
try {
|
||||||
|
const result = await api('subcategories_list', {}, 'GET');
|
||||||
|
if (result.success && Array.isArray(result.subcategories)) {
|
||||||
|
SUBCATEGORIES_BY_CATEGORY = {};
|
||||||
|
SUBCATEGORY_LABEL_LOOKUP = {};
|
||||||
|
result.subcategories.forEach(sc => {
|
||||||
|
if (!SUBCATEGORIES_BY_CATEGORY[sc.category]) SUBCATEGORIES_BY_CATEGORY[sc.category] = [];
|
||||||
|
SUBCATEGORIES_BY_CATEGORY[sc.category].push(sc);
|
||||||
|
SUBCATEGORY_LABEL_LOOKUP[sc.category + '::' + sc.key] = sc.label;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[EverShelf] Could not load subcategories:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubcategoryOptionsHtml(category, selectedValue = '') {
|
||||||
|
const list = SUBCATEGORIES_BY_CATEGORY[category];
|
||||||
|
if (!list || list.length === 0) return '<option value="">-- Aucune --</option>';
|
||||||
|
const opts = list.map(sc =>
|
||||||
|
`<option value="${sc.key}" ${sc.key === selectedValue ? 'selected' : ''}>${escapeHtml(sc.label)}</option>`
|
||||||
|
).join('');
|
||||||
|
return '<option value="">-- Aucune --</option>' + opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSubcategoryField(selectId, groupId, category, selectedValue = '') {
|
||||||
|
const select = document.getElementById(selectId);
|
||||||
|
const group = document.getElementById(groupId);
|
||||||
|
if (!select) return;
|
||||||
|
const list = SUBCATEGORIES_BY_CATEGORY[category];
|
||||||
|
select.innerHTML = getSubcategoryOptionsHtml(category, selectedValue);
|
||||||
|
if (group) {
|
||||||
|
group.style.display = (list && list.length > 0) ? 'block' : 'none';
|
||||||
|
const mark = group.querySelector('.subcategory-required-mark');
|
||||||
|
if (mark) {
|
||||||
|
mark.style.color = '#e74c3c';
|
||||||
|
mark.style.display = REQUIRED_SUBCATEGORY_CATEGORIES.includes(category) ? 'inline' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEditActionCategoryChange() {
|
||||||
|
const cat = document.getElementById('edit-action-category')?.value || '';
|
||||||
|
updateSubcategoryField('edit-action-subcategory', 'edit-action-subcategory-group', cat);
|
||||||
|
}
|
||||||
|
|
||||||
// Detect best unit/quantity from Open Food Facts quantity_info string
|
// Detect best unit/quantity from Open Food Facts quantity_info string
|
||||||
// Returns the actual package weight/volume as default (e.g. 700g → unit:'g', quantity:700)
|
// Returns the actual package weight/volume as default (e.g. 700g → unit:'g', quantity:700)
|
||||||
@@ -2789,6 +2970,7 @@ async function syncSettingsFromDB() {
|
|||||||
const srv = res.settings;
|
const srv = res.settings;
|
||||||
|
|
||||||
if (srv.review_confirmed) _reviewConfirmedCache = srv.review_confirmed;
|
if (srv.review_confirmed) _reviewConfirmedCache = srv.review_confirmed;
|
||||||
|
if (Array.isArray(srv.subcategory_required_categories)) REQUIRED_SUBCATEGORY_CATEGORIES = srv.subcategory_required_categories;
|
||||||
|
|
||||||
// meal_plan is stored in SQLite app_settings so all devices stay in sync
|
// meal_plan is stored in SQLite app_settings so all devices stay in sync
|
||||||
if (srv.meal_plan) {
|
if (srv.meal_plan) {
|
||||||
@@ -6676,8 +6858,8 @@ function renderInventoryItem(item) {
|
|||||||
const catIcon = CATEGORY_ICONS[catKey] || '📦';
|
const catIcon = CATEGORY_ICONS[catKey] || '📦';
|
||||||
const catLabel = t('categories.' + catKey) || catKey;
|
const catLabel = t('categories.' + catKey) || catKey;
|
||||||
const catBadge = `<span class="inv-badge badge-category" data-cat="${catKey}" data-itemname="${escapeHtml(item.name)}">${catIcon} ${catLabel}</span>`;
|
const catBadge = `<span class="inv-badge badge-category" data-cat="${catKey}" data-itemname="${escapeHtml(item.name)}">${catIcon} ${catLabel}</span>`;
|
||||||
const subBadge = (item.subcategory && SUBCATEGORY_LABELS[item.subcategory])
|
const subBadge = (item.subcategory && SUBCATEGORY_LABEL_LOOKUP[catKey + '::' + item.subcategory])
|
||||||
? `<span class="inv-badge badge-subcategory">${SUBCATEGORY_LABELS[item.subcategory]}</span>`
|
? `<span class="inv-badge badge-subcategory">${SUBCATEGORY_LABEL_LOOKUP[catKey + '::' + item.subcategory]}</span>`
|
||||||
: '';
|
: '';
|
||||||
const locInfo = LOCATIONS[item.location] || { icon: '📦', label: item.location };
|
const locInfo = LOCATIONS[item.location] || { icon: '📦', label: item.location };
|
||||||
const days = daysUntilExpiry(item.expiry_date);
|
const days = daysUntilExpiry(item.expiry_date);
|
||||||
@@ -6786,7 +6968,7 @@ function filterInventory() {
|
|||||||
// Match by inferred category: "biscotti" → queryCat="snack" → all snack items
|
// Match by inferred category: "biscotti" → queryCat="snack" → all snack items
|
||||||
if (queryCat !== 'altro' && itemCat === queryCat) return true;
|
if (queryCat !== 'altro' && itemCat === queryCat) return true;
|
||||||
if (i.subcategory && i.subcategory.toLowerCase().includes(q)) return true;
|
if (i.subcategory && i.subcategory.toLowerCase().includes(q)) return true;
|
||||||
if (i.subcategory && (SUBCATEGORY_LABELS[i.subcategory] || '').toLowerCase().includes(q)) return true;
|
if (i.subcategory && (SUBCATEGORY_LABEL_LOOKUP[itemCat + '::' + i.subcategory] || '').toLowerCase().includes(q)) return true;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
renderInventory(filtered);
|
renderInventory(filtered);
|
||||||
@@ -8397,6 +8579,7 @@ function startManualEntry(barcode = '') {
|
|||||||
document.getElementById('pf-name').value = '';
|
document.getElementById('pf-name').value = '';
|
||||||
document.getElementById('pf-brand').value = '';
|
document.getElementById('pf-brand').value = '';
|
||||||
document.getElementById('pf-category').value = '';
|
document.getElementById('pf-category').value = '';
|
||||||
|
updateSubcategoryField('pf-subcategory', 'pf-subcategory-group', '');
|
||||||
document.getElementById('pf-unit').value = 'pz';
|
document.getElementById('pf-unit').value = 'pz';
|
||||||
document.getElementById('pf-defqty').value = '1';
|
document.getElementById('pf-defqty').value = '1';
|
||||||
document.getElementById('pf-notes').value = '';
|
document.getElementById('pf-notes').value = '';
|
||||||
@@ -8508,7 +8691,10 @@ function onCategoryChange(fromAutoDetect = false) {
|
|||||||
const cat = document.getElementById('pf-category').value;
|
const cat = document.getElementById('pf-category').value;
|
||||||
const unitSelect = document.getElementById('pf-unit');
|
const unitSelect = document.getElementById('pf-unit');
|
||||||
const qtyInput = document.getElementById('pf-defqty');
|
const qtyInput = document.getElementById('pf-defqty');
|
||||||
|
|
||||||
|
// Met à jour les options/visibilité de la sous-catégorie pour la nouvelle catégorie
|
||||||
|
updateSubcategoryField('pf-subcategory', 'pf-subcategory-group', cat);
|
||||||
|
|
||||||
// If user manually changed category via dropdown, don't auto-fill qty/unit
|
// If user manually changed category via dropdown, don't auto-fill qty/unit
|
||||||
if (!fromAutoDetect) {
|
if (!fromAutoDetect) {
|
||||||
// Mark qty as "set" so future auto-detects won't overwrite either
|
// Mark qty as "set" so future auto-detects won't overwrite either
|
||||||
@@ -8668,8 +8854,8 @@ async function submitProduct(e) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const pfCategory = document.getElementById('pf-category').value;
|
const pfCategory = document.getElementById('pf-category').value;
|
||||||
const pfSubcategory = document.getElementById('pf-subcategory')?.value || '';
|
const pfSubcategory = document.getElementById('pf-subcategory')?.value || '';
|
||||||
if (pfCategory === 'bevande' && !pfSubcategory) {
|
if (REQUIRED_SUBCATEGORY_CATEGORIES.includes(pfCategory) && !pfSubcategory) {
|
||||||
showToast('Merci de préciser le type de boisson', 'error');
|
showToast('Merci de préciser la sous-catégorie', 'error');
|
||||||
document.getElementById('pf-subcategory').focus();
|
document.getElementById('pf-subcategory').focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -8805,9 +8991,14 @@ function showProductAction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Always build the edit form, but only show it auto-opened for unknown products
|
// Always build the edit form, but only show it auto-opened for unknown products
|
||||||
const categoryOptions = Object.entries(CATEGORY_LABELS).map(([key, label]) =>
|
const currentCatForEdit = mapToLocalCategory(currentProduct.category, currentProduct.name, currentProduct.brand);
|
||||||
`<option value="${key}" ${mapToLocalCategory(currentProduct.category, currentProduct.name, currentProduct.brand) === key ? 'selected' : ''}>${label}</option>`
|
const categoryOptions = Object.entries(CATEGORY_LABELS).map(([key, label]) =>
|
||||||
|
`<option value="${key}" ${currentCatForEdit === key ? 'selected' : ''}>${label}</option>`
|
||||||
).join('');
|
).join('');
|
||||||
|
const editSubcategoryOptions = getSubcategoryOptionsHtml(currentCatForEdit, currentProduct.subcategory || '');
|
||||||
|
const editSubcategoryList = SUBCATEGORIES_BY_CATEGORY[currentCatForEdit];
|
||||||
|
const editSubcategoryVisible = (editSubcategoryList && editSubcategoryList.length > 0) ? 'block' : 'none';
|
||||||
|
const editSubcategoryRequired = REQUIRED_SUBCATEGORY_CATEGORIES.includes(currentCatForEdit);
|
||||||
|
|
||||||
editInfoEl.innerHTML = `
|
editInfoEl.innerHTML = `
|
||||||
<div class="edit-unknown-card ${isUnknown ? 'highlight' : ''}">
|
<div class="edit-unknown-card ${isUnknown ? 'highlight' : ''}">
|
||||||
@@ -8824,11 +9015,17 @@ function showProductAction() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>${t('product.category_label')}</label>
|
<label>${t('product.category_label')}</label>
|
||||||
<select id="edit-action-category" class="form-input">
|
<select id="edit-action-category" class="form-input" onchange="onEditActionCategoryChange()">
|
||||||
<option value="">${t('form.select_placeholder')}</option>
|
<option value="">${t('form.select_placeholder')}</option>
|
||||||
${categoryOptions}
|
${categoryOptions}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" id="edit-action-subcategory-group" style="display:${editSubcategoryVisible}">
|
||||||
|
<label>📂 Sous-catégorie <span class="subcategory-required-mark" style="display:${editSubcategoryRequired ? 'inline' : 'none'};color:#e74c3c">*</span></label>
|
||||||
|
<select id="edit-action-subcategory" class="form-input">
|
||||||
|
${editSubcategoryOptions}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>${t('product.notes_label')}</label>
|
<label>${t('product.notes_label')}</label>
|
||||||
<textarea id="edit-action-notes" class="form-input" rows="2" placeholder="${escapeHtml(t('product.notes_placeholder') || '')}">${escapeHtml(currentProduct.notes || '')}</textarea>
|
<textarea id="edit-action-notes" class="form-input" rows="2" placeholder="${escapeHtml(t('product.notes_placeholder') || '')}">${escapeHtml(currentProduct.notes || '')}</textarea>
|
||||||
@@ -9399,8 +9596,14 @@ async function saveEditedProductInfo() {
|
|||||||
}
|
}
|
||||||
const brand = (document.getElementById('edit-action-brand')?.value || '').trim();
|
const brand = (document.getElementById('edit-action-brand')?.value || '').trim();
|
||||||
const category = document.getElementById('edit-action-category')?.value || '';
|
const category = document.getElementById('edit-action-category')?.value || '';
|
||||||
|
const subcategory = document.getElementById('edit-action-subcategory')?.value || '';
|
||||||
|
const effectiveCategory = category || currentProduct.category || '';
|
||||||
|
if (REQUIRED_SUBCATEGORY_CATEGORIES.includes(effectiveCategory) && !subcategory) {
|
||||||
|
showToast('Merci de préciser la sous-catégorie', 'error');
|
||||||
|
document.getElementById('edit-action-subcategory')?.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const notes = (document.getElementById('edit-action-notes')?.value || '').trim();
|
const notes = (document.getElementById('edit-action-notes')?.value || '').trim();
|
||||||
|
|
||||||
showLoading(true);
|
showLoading(true);
|
||||||
try {
|
try {
|
||||||
const result = await api('product_save', {}, 'POST', {
|
const result = await api('product_save', {}, 'POST', {
|
||||||
@@ -9408,7 +9611,8 @@ async function saveEditedProductInfo() {
|
|||||||
barcode: currentProduct.barcode || null,
|
barcode: currentProduct.barcode || null,
|
||||||
name: name,
|
name: name,
|
||||||
brand: brand,
|
brand: brand,
|
||||||
category: category || currentProduct.category || '',
|
category: effectiveCategory,
|
||||||
|
subcategory: subcategory || null,
|
||||||
image_url: currentProduct.image_url || '',
|
image_url: currentProduct.image_url || '',
|
||||||
unit: currentProduct.unit || 'pz',
|
unit: currentProduct.unit || 'pz',
|
||||||
default_quantity: currentProduct.default_quantity || 1,
|
default_quantity: currentProduct.default_quantity || 1,
|
||||||
@@ -9421,6 +9625,7 @@ async function saveEditedProductInfo() {
|
|||||||
currentProduct.brand = brand;
|
currentProduct.brand = brand;
|
||||||
currentProduct.notes = notes;
|
currentProduct.notes = notes;
|
||||||
if (category) currentProduct.category = category;
|
if (category) currentProduct.category = category;
|
||||||
|
currentProduct.subcategory = subcategory || null;
|
||||||
showToast(t('toast.product_updated'), 'success');
|
showToast(t('toast.product_updated'), 'success');
|
||||||
// Refresh the action page with updated data
|
// Refresh the action page with updated data
|
||||||
showProductAction();
|
showProductAction();
|
||||||
@@ -19653,6 +19858,9 @@ async function _initApp() {
|
|||||||
|
|
||||||
// Load custom locations (merges into LOCATIONS before any UI renders)
|
// Load custom locations (merges into LOCATIONS before any UI renders)
|
||||||
await loadCustomLocations();
|
await loadCustomLocations();
|
||||||
|
|
||||||
|
// Load custom subcategories (merges into SUBCATEGORIES_BY_CATEGORY before any UI renders)
|
||||||
|
await loadCustomSubcategories();
|
||||||
|
|
||||||
// Check for setup wizard resume (after language change)
|
// Check for setup wizard resume (after language change)
|
||||||
const resumeStep = localStorage.getItem('evershelf_setup_step');
|
const resumeStep = localStorage.getItem('evershelf_setup_step');
|
||||||
|
|||||||
Reference in New Issue
Block a user