Edit product from search card, fix Ayutinskiy bread kcal

Product editing:
- "Ред." button on expanded product card
- showEditProductModal: edit name, KBJU, all micronutrients
- Updates product in DB directly, refreshes search

Bread fix:
- "Хлеб Тостовый пшеничный Аютинский 280г" was 268 in Magnit data
- Package says 243 kcal/100g — corrected in seed DB
- Seed version 7

Users can now fix any wrong KBJU directly from the product card
without re-importing or editing the seed DB manually.
This commit is contained in:
4aiman 2026-05-29 22:01:52 +03:00
commit 3789747729
2 changed files with 76 additions and 1 deletions

Binary file not shown.

View file

@ -1290,7 +1290,10 @@ async function renderProducts(container) {
</div>
${microChips.length ? `<div class="nutrient-strip" style="padding:4px 0">${microChips.map(c => c.html).join("")}</div>` : ""}
${p.composition ? `<div class="prod-composition">${esc(p.composition)}</div>` : ""}
<button class="btn-primary prod-add-btn" data-pid="${p.id}">Добавить в дневник</button>
<div style="display:flex;gap:8px;margin-top:8px">
<button class="btn-primary prod-add-btn" data-pid="${p.id}" style="flex:1">Добавить в дневник</button>
<button class="btn-secondary prod-edit-btn" data-pid="${p.id}" style="flex:0">Ред.</button>
</div>
</div>`;
}
@ -1309,6 +1312,13 @@ async function renderProducts(container) {
showAddFoodFromProduct(parseInt(btn.dataset.pid));
};
});
el.querySelectorAll(".prod-edit-btn").forEach(btn => {
btn.onclick = async e => {
e.stopPropagation();
const p = await DB.getProduct(parseInt(btn.dataset.pid));
if (p) showEditProductModal(p, () => doSearch(false));
};
});
}
function showAddFoodFromProduct(productId) {
@ -1403,6 +1413,71 @@ function showAddProductModal(onSaved) {
});
}
function showEditProductModal(p, onSaved) {
const microHtml = MICRO_FIELDS.map(f => {
let val = p[f.key] ?? "";
if (val && f.mult) val = Math.round(val / f.mult * 1000) / 1000;
return `<div class="form-group micro-field">
<label>${f.label}</label>
<input id="ep-${f.key}" type="number" inputmode="decimal" step="any" value="${val}" placeholder="—" />
</div>`;
}).join("");
showModal(`
<h3>Редактирование продукта</h3>
<div class="form-group">
<label>Название</label>
<input id="ep-name" type="text" value="${esc(p.name)}" />
</div>
<div style="display:flex;gap:8px">
<div class="form-group" style="flex:1">
<label>ккал/100г</label>
<input id="ep-kcal" type="number" inputmode="decimal" value="${p.kcal_per_100g ?? ""}" />
</div>
<div class="form-group" style="flex:1">
<label>Б</label>
<input id="ep-protein" type="number" inputmode="decimal" step="0.1" value="${p.protein_per_100g ?? ""}" />
</div>
<div class="form-group" style="flex:1">
<label>Ж</label>
<input id="ep-fat" type="number" inputmode="decimal" step="0.1" value="${p.fat_per_100g ?? ""}" />
</div>
<div class="form-group" style="flex:1">
<label>У</label>
<input id="ep-carbs" type="number" inputmode="decimal" step="0.1" value="${p.carbs_per_100g ?? ""}" />
</div>
</div>
<p style="color:var(--muted);font-size:12px;margin:8px 0 4px">Нутриенты на 100г:</p>
<div class="micro-grid">${microHtml}</div>`,
async overlay => {
const name = overlay.querySelector("#ep-name").value.trim();
if (!name) return false;
const sets = ["name=?1", "kcal_per_100g=?2", "protein_per_100g=?3", "fat_per_100g=?4", "carbs_per_100g=?5"];
const vals = [
name,
parseFloat(overlay.querySelector("#ep-kcal").value) || null,
parseFloat(overlay.querySelector("#ep-protein").value) || null,
parseFloat(overlay.querySelector("#ep-fat").value) || null,
parseFloat(overlay.querySelector("#ep-carbs").value) || null,
];
let idx = 6;
for (const f of MICRO_FIELDS) {
let val = parseFloat(overlay.querySelector(`#ep-${f.key}`).value) || null;
if (val && f.mult) val *= f.mult;
sets.push(`${f.key}=?${idx}`);
vals.push(val);
idx++;
}
vals.push(p.id);
await window.__TAURI__.core.invoke("plugin:sql|execute", {
db: "sqlite:baldav.db",
query: `UPDATE products SET ${sets.join(", ")} WHERE id=?${idx}`,
values: vals,
});
if (onSaved) onSaved();
});
}
// ---------------------------------------------------------------------------
// RECIPES screen
// ---------------------------------------------------------------------------