From 374d067cf4188d9beb890a78d00ef33f573a7e14 Mon Sep 17 00:00:00 2001 From: Dayowe Date: Wed, 12 Nov 2025 18:34:26 +0100 Subject: [PATCH] Fixes and improvements --- iframe.html | 61 ++++++++++++++++++++++++-------------------- src/js/calculator.js | 61 ++++++++++++++++++++++++-------------------- 2 files changed, 68 insertions(+), 54 deletions(-) diff --git a/iframe.html b/iframe.html index b370e17..877ee92 100644 --- a/iframe.html +++ b/iframe.html @@ -2202,7 +2202,8 @@ const CALCULATOR_CONFIG = { energyUnit: 'kcal100g', percentage: 0, isLocked: false, - chartType: null + chartType: null, + splitByMeals: false }; this.foodSources.push(treats); this.renderFoodSource(treats); @@ -2654,6 +2655,11 @@ const CALCULATOR_CONFIG = { const container = document.getElementById('foodSources'); if (!container) return; + const isChart = foodSource.chartType === 'gc' || foodSource.chartType === 'kibble'; + const energyReadonlyAttr = isChart ? 'readonly' : ''; + const energyTitle = isChart ? 'Chart-based food: kcal locked' : 'Enter energy content'; + const unitDisabledAttr = isChart ? 'disabled' : ''; + const cardHTML = `
@@ -2664,11 +2670,11 @@ const CALCULATOR_CONFIG = {
- +
- @@ -2729,7 +2735,7 @@ const CALCULATOR_CONFIG = { }); } - if (energyInput) { + if (energyInput && !energyInput.hasAttribute('readonly')) { energyInput.addEventListener('input', () => { this.updateFoodSourceData(id, 'energy', energyInput.value); // If kibble reference changed, recompute daily target @@ -2753,7 +2759,7 @@ const CALCULATOR_CONFIG = { energyInput.addEventListener('blur', () => this.validateFoodSourceEnergy(id)); } - if (energyUnitSelect) { + if (energyUnitSelect && !energyUnitSelect.hasAttribute('disabled')) { energyUnitSelect.addEventListener('change', () => { this.updateFoodSourceData(id, 'energyUnit', energyUnitSelect.value); if (id === this.kibbleRefId) { @@ -3365,7 +3371,7 @@ const CALCULATOR_CONFIG = { // Debug: log what unit is being used console.log('UpdateFoodCalculations - unit:', unit, 'unitLabel:', unitLabel); - // Determine frequency suffix for display + // Determine frequency suffix for display (will adjust per-item below) const frequencySuffix = this.showPerMeal ? '/meal' : '/day'; // Clear all food source errors first @@ -3429,6 +3435,8 @@ const CALCULATOR_CONFIG = { const kcalPerPercent = chartedPercent > 0 ? (chartedKcal / chartedPercent) : null; // Second pass: finalize amounts + let splitDailyTotal = 0; + let dailyOnlyTotal = 0; firstPass.forEach(({ fs, energyPer100g, gramsPortion }) => { let dailyGramsForThisFood = 0; let hasEnergyContent = !!(energyPer100g && energyPer100g > 0); @@ -3447,12 +3455,14 @@ const CALCULATOR_CONFIG = { } } - const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood; + const isDailyOnly = fs.splitByMeals === false; + const displayGrams = (this.showPerMeal && !isDailyOnly) ? (dailyGramsForThisFood / this.mealsPerDay) : dailyGramsForThisFood; foodBreakdowns.push({ name: fs.name, percentage: fs.percentage, dailyGrams: dailyGramsForThisFood, + isDailyOnly: isDailyOnly, displayGrams: displayGrams, dailyCups: null, displayCups: null, @@ -3463,6 +3473,7 @@ const CALCULATOR_CONFIG = { foodSource: fs }); totalDailyGrams += dailyGramsForThisFood; + if (isDailyOnly) dailyOnlyTotal += dailyGramsForThisFood; else splitDailyTotal += dailyGramsForThisFood; if (dailyGramsForThisFood > 0) hasValidFoods = true; }); @@ -3483,12 +3494,13 @@ const CALCULATOR_CONFIG = { const unitButtons = document.getElementById('unitButtons'); if (unitButtons) unitButtons.style.display = 'none'; - // If we have any food sources without energy content, still show the breakdown section - if (foodBreakdowns.length > 0) { + // If we have any foods with >0% but missing energy, show warnings only for those + const visibleBreakdownsMissing = foodBreakdowns.filter(b => b.percentage > 0); + if (visibleBreakdownsMissing.length > 0) { // Show food amounts section with warnings for missing energy content const unitLabel = unit === 'g' ? 'g' : unit === 'kg' ? 'kg' : unit === 'oz' ? 'oz' : 'lb'; - const foodAmountsHTML = foodBreakdowns.map(breakdown => { + const foodAmountsHTML = visibleBreakdownsMissing.map(breakdown => { const lockIndicator = breakdown.isLocked ? '🔒' : ''; return ` @@ -3539,31 +3551,24 @@ const CALCULATOR_CONFIG = { const unitButtons = document.getElementById('unitButtons'); if (unitButtons) unitButtons.style.display = 'flex'; - // Update per-food breakdown - if (foodBreakdownList && foodBreakdowns.length > 1) { - const breakdownHTML = foodBreakdowns.map(breakdown => { + // Update per-food breakdown (show only items with >0%) + const visibleBreakdowns = foodBreakdowns.filter(b => b.percentage > 0); + if (foodBreakdownList && visibleBreakdowns.length > 0) { + const breakdownHTML = visibleBreakdowns.map(breakdown => { let valueContent; + // Choose per-item frequency suffix: daily-only items stay /day even in per-meal view + const itemSuffix = (this.showPerMeal && !breakdown.isDailyOnly) ? '/meal' : '/day'; if (breakdown.hasEnergyContent) { if (unit === 'cups') { // For cups, use the pre-calculated cups value if available if (breakdown.displayCups !== null) { - if (breakdown.hasRange && breakdown.displayCupsMin !== breakdown.displayCupsMax) { - valueContent = `${this.formatNumber(breakdown.displayCupsMin, decimals)}-${this.formatNumber(breakdown.displayCupsMax, decimals)} ${unitLabel}${frequencySuffix}`; - } else { - valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${frequencySuffix}`; - } + valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${itemSuffix}`; } else { valueContent = `N/A`; } } else { // For other units (g, kg, oz, lb) - if (breakdown.hasRange && breakdown.displayGramsMin !== breakdown.displayGramsMax) { - const minConverted = this.convertUnits(breakdown.displayGramsMin, unit); - const maxConverted = this.convertUnits(breakdown.displayGramsMax, unit); - valueContent = `${this.formatNumber(minConverted, decimals)}-${this.formatNumber(maxConverted, decimals)} ${unitLabel}${frequencySuffix}`; - } else { - valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${frequencySuffix}`; - } + valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${itemSuffix}`; } } else { valueContent = `⚠️`; @@ -3586,7 +3591,8 @@ const CALCULATOR_CONFIG = { // Generate individual food amount breakdown // Update daily food value with correct units - const displayTotal = this.showPerMeal ? totalDailyGrams / this.mealsPerDay : totalDailyGrams; + // When per-meal view is enabled, split-only items divide by meals/day; daily-only items remain as daily totals + const displayTotal = this.showPerMeal ? (splitDailyTotal / this.mealsPerDay + dailyOnlyTotal) : (splitDailyTotal + dailyOnlyTotal); let convertedTotal; let totalDisplayText; @@ -3653,7 +3659,8 @@ const CALCULATOR_CONFIG = { dailyFoodValue.textContent = totalDisplayText; // Build HTML for individual food amounts - const foodAmountsHTML = foodBreakdowns.map(breakdown => { + const foodAmountsVisible = foodBreakdowns.filter(b => b.percentage > 0); + const foodAmountsHTML = foodAmountsVisible.map(breakdown => { const lockIndicator = breakdown.isLocked ? '🔒' : ''; if (!breakdown.hasEnergyContent) { diff --git a/src/js/calculator.js b/src/js/calculator.js index b6f6f31..a7b52f2 100644 --- a/src/js/calculator.js +++ b/src/js/calculator.js @@ -106,7 +106,8 @@ energyUnit: 'kcal100g', percentage: 0, isLocked: false, - chartType: null + chartType: null, + splitByMeals: false }; this.foodSources.push(treats); this.renderFoodSource(treats); @@ -558,6 +559,11 @@ const container = document.getElementById('foodSources'); if (!container) return; + const isChart = foodSource.chartType === 'gc' || foodSource.chartType === 'kibble'; + const energyReadonlyAttr = isChart ? 'readonly' : ''; + const energyTitle = isChart ? 'Chart-based food: kcal locked' : 'Enter energy content'; + const unitDisabledAttr = isChart ? 'disabled' : ''; + const cardHTML = `
@@ -568,11 +574,11 @@
- +
- @@ -633,7 +639,7 @@ }); } - if (energyInput) { + if (energyInput && !energyInput.hasAttribute('readonly')) { energyInput.addEventListener('input', () => { this.updateFoodSourceData(id, 'energy', energyInput.value); // If kibble reference changed, recompute daily target @@ -657,7 +663,7 @@ energyInput.addEventListener('blur', () => this.validateFoodSourceEnergy(id)); } - if (energyUnitSelect) { + if (energyUnitSelect && !energyUnitSelect.hasAttribute('disabled')) { energyUnitSelect.addEventListener('change', () => { this.updateFoodSourceData(id, 'energyUnit', energyUnitSelect.value); if (id === this.kibbleRefId) { @@ -1269,7 +1275,7 @@ // Debug: log what unit is being used console.log('UpdateFoodCalculations - unit:', unit, 'unitLabel:', unitLabel); - // Determine frequency suffix for display + // Determine frequency suffix for display (will adjust per-item below) const frequencySuffix = this.showPerMeal ? '/meal' : '/day'; // Clear all food source errors first @@ -1333,6 +1339,8 @@ const kcalPerPercent = chartedPercent > 0 ? (chartedKcal / chartedPercent) : null; // Second pass: finalize amounts + let splitDailyTotal = 0; + let dailyOnlyTotal = 0; firstPass.forEach(({ fs, energyPer100g, gramsPortion }) => { let dailyGramsForThisFood = 0; let hasEnergyContent = !!(energyPer100g && energyPer100g > 0); @@ -1351,12 +1359,14 @@ } } - const displayGrams = this.showPerMeal ? dailyGramsForThisFood / this.mealsPerDay : dailyGramsForThisFood; + const isDailyOnly = fs.splitByMeals === false; + const displayGrams = (this.showPerMeal && !isDailyOnly) ? (dailyGramsForThisFood / this.mealsPerDay) : dailyGramsForThisFood; foodBreakdowns.push({ name: fs.name, percentage: fs.percentage, dailyGrams: dailyGramsForThisFood, + isDailyOnly: isDailyOnly, displayGrams: displayGrams, dailyCups: null, displayCups: null, @@ -1367,6 +1377,7 @@ foodSource: fs }); totalDailyGrams += dailyGramsForThisFood; + if (isDailyOnly) dailyOnlyTotal += dailyGramsForThisFood; else splitDailyTotal += dailyGramsForThisFood; if (dailyGramsForThisFood > 0) hasValidFoods = true; }); @@ -1387,12 +1398,13 @@ const unitButtons = document.getElementById('unitButtons'); if (unitButtons) unitButtons.style.display = 'none'; - // If we have any food sources without energy content, still show the breakdown section - if (foodBreakdowns.length > 0) { + // If we have any foods with >0% but missing energy, show warnings only for those + const visibleBreakdownsMissing = foodBreakdowns.filter(b => b.percentage > 0); + if (visibleBreakdownsMissing.length > 0) { // Show food amounts section with warnings for missing energy content const unitLabel = unit === 'g' ? 'g' : unit === 'kg' ? 'kg' : unit === 'oz' ? 'oz' : 'lb'; - const foodAmountsHTML = foodBreakdowns.map(breakdown => { + const foodAmountsHTML = visibleBreakdownsMissing.map(breakdown => { const lockIndicator = breakdown.isLocked ? '🔒' : ''; return ` @@ -1443,31 +1455,24 @@ const unitButtons = document.getElementById('unitButtons'); if (unitButtons) unitButtons.style.display = 'flex'; - // Update per-food breakdown - if (foodBreakdownList && foodBreakdowns.length > 1) { - const breakdownHTML = foodBreakdowns.map(breakdown => { + // Update per-food breakdown (show only items with >0%) + const visibleBreakdowns = foodBreakdowns.filter(b => b.percentage > 0); + if (foodBreakdownList && visibleBreakdowns.length > 0) { + const breakdownHTML = visibleBreakdowns.map(breakdown => { let valueContent; + // Choose per-item frequency suffix: daily-only items stay /day even in per-meal view + const itemSuffix = (this.showPerMeal && !breakdown.isDailyOnly) ? '/meal' : '/day'; if (breakdown.hasEnergyContent) { if (unit === 'cups') { // For cups, use the pre-calculated cups value if available if (breakdown.displayCups !== null) { - if (breakdown.hasRange && breakdown.displayCupsMin !== breakdown.displayCupsMax) { - valueContent = `${this.formatNumber(breakdown.displayCupsMin, decimals)}-${this.formatNumber(breakdown.displayCupsMax, decimals)} ${unitLabel}${frequencySuffix}`; - } else { - valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${frequencySuffix}`; - } + valueContent = `${this.formatNumber(breakdown.displayCups, decimals)} ${unitLabel}${itemSuffix}`; } else { valueContent = `N/A`; } } else { // For other units (g, kg, oz, lb) - if (breakdown.hasRange && breakdown.displayGramsMin !== breakdown.displayGramsMax) { - const minConverted = this.convertUnits(breakdown.displayGramsMin, unit); - const maxConverted = this.convertUnits(breakdown.displayGramsMax, unit); - valueContent = `${this.formatNumber(minConverted, decimals)}-${this.formatNumber(maxConverted, decimals)} ${unitLabel}${frequencySuffix}`; - } else { - valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${frequencySuffix}`; - } + valueContent = `${this.formatNumber(this.convertUnits(breakdown.displayGrams, unit), decimals)} ${unitLabel}${itemSuffix}`; } } else { valueContent = `⚠️`; @@ -1490,7 +1495,8 @@ // Generate individual food amount breakdown // Update daily food value with correct units - const displayTotal = this.showPerMeal ? totalDailyGrams / this.mealsPerDay : totalDailyGrams; + // When per-meal view is enabled, split-only items divide by meals/day; daily-only items remain as daily totals + const displayTotal = this.showPerMeal ? (splitDailyTotal / this.mealsPerDay + dailyOnlyTotal) : (splitDailyTotal + dailyOnlyTotal); let convertedTotal; let totalDisplayText; @@ -1557,7 +1563,8 @@ dailyFoodValue.textContent = totalDisplayText; // Build HTML for individual food amounts - const foodAmountsHTML = foodBreakdowns.map(breakdown => { + const foodAmountsVisible = foodBreakdowns.filter(b => b.percentage > 0); + const foodAmountsHTML = foodAmountsVisible.map(breakdown => { const lockIndicator = breakdown.isLocked ? '🔒' : ''; if (!breakdown.hasEnergyContent) {