feat: add weight and height features
This commit is contained in:
+18
@@ -377,6 +377,24 @@
|
|||||||
<div class="date-display">Define your daily macros goal</div>
|
<div class="date-display">Define your daily macros goal</div>
|
||||||
|
|
||||||
<form id="settingsForm" class="settings-form">
|
<form id="settingsForm" class="settings-form">
|
||||||
|
<!-- new - start -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="bodyWeight">Body Weight (kg)</label>
|
||||||
|
<input type="text" id="bodyWeight" placeholder="Body Weight" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="height">Height (cm)</label>
|
||||||
|
<input type="number" id="height" placeholder="Height" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="bmi">Body Mass Index (BMI)</label>
|
||||||
|
<input type="number" id="bmi" placeholder="BMI" readonly>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="bmiResult">Body Mass Index (BMI) Result</label>
|
||||||
|
<input type="text" id="bmiResult" placeholder="BMI Result" readonly>
|
||||||
|
</div>
|
||||||
|
<!-- new - end -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="caloriesGoal">Daily calorie goal</label>
|
<label for="caloriesGoal">Daily calorie goal</label>
|
||||||
<input type="number" id="caloriesGoal" placeholder="Calories" required>
|
<input type="number" id="caloriesGoal" placeholder="Calories" required>
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import { Client, Account, Databases, Query, ID } from 'appwrite';
|
import { Client, Account, Databases, Query, ID } from 'appwrite';
|
||||||
import { DailyTotalCalories, FoodStorage, UserSettings, SharedDay } from './types';
|
import { FoodStorage, UserSettings } from './types';
|
||||||
|
|
||||||
// Initialize Appwrite client
|
// Initialize Appwrite client
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
|
|||||||
+60
-3
@@ -9,7 +9,7 @@ import { appState } from "./state";
|
|||||||
import { getNutritionInfo, NutritionInfo, clearNutritionCache, getCacheStats } from './claudeService';
|
import { getNutritionInfo, NutritionInfo, clearNutritionCache, getCacheStats } from './claudeService';
|
||||||
|
|
||||||
// PWA Service Worker Registration
|
// PWA Service Worker Registration
|
||||||
import { registerSW } from 'virtual:pwa-register'
|
import { registerSW } from 'virtual:pwa-register';
|
||||||
|
|
||||||
const updateSW = registerSW({
|
const updateSW = registerSW({
|
||||||
onNeedRefresh() {
|
onNeedRefresh() {
|
||||||
@@ -160,11 +160,41 @@ async function handleBulkDelete(idsToDelete: string[]): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupBMICalculator() {
|
||||||
|
const bodyWeight = getInputById('bodyWeight').value;
|
||||||
|
const height = getInputById('height').value;
|
||||||
|
|
||||||
|
if (!bodyWeight || !height || parseFloat(height) === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bmi = (parseFloat(bodyWeight) / ((parseFloat(height) / 100) ** 2)).toFixed(1);
|
||||||
|
getInputById('bmi').value = bmi;
|
||||||
|
const bmiResult = getInputById('bmiResult');
|
||||||
|
if (bmiResult) {
|
||||||
|
// BMI ranges
|
||||||
|
const bmiValue = parseFloat(bmi);
|
||||||
|
if (bmiValue < 18.5) {
|
||||||
|
bmiResult.value = 'Underweight';
|
||||||
|
} else if (bmiValue >= 18.5 && bmiValue < 24.9) {
|
||||||
|
bmiResult.value = 'Normal weight';
|
||||||
|
} else if (bmiValue >= 25 && bmiValue < 29.9) {
|
||||||
|
bmiResult.value = 'Overweight';
|
||||||
|
} else {
|
||||||
|
bmiResult.value = 'Obesity';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSaveSettings(e: SubmitEvent) {
|
async function handleSaveSettings(e: SubmitEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
showLoading();
|
showLoading();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const bodyWeight = getInputById('bodyWeight').value;
|
||||||
|
const height = getInputById('height').value;
|
||||||
|
const bmi = getInputById('bmi').value;
|
||||||
|
const bmiResult = getInputById('bmiResult');
|
||||||
const caloriesGoal = getInputById('caloriesGoal').value;
|
const caloriesGoal = getInputById('caloriesGoal').value;
|
||||||
const proteinGoal = getInputById('proteinGoal').value;
|
const proteinGoal = getInputById('proteinGoal').value;
|
||||||
const fatGoal = getInputById('fatGoal').value;
|
const fatGoal = getInputById('fatGoal').value;
|
||||||
@@ -188,6 +218,10 @@ async function handleSaveSettings(e: SubmitEvent) {
|
|||||||
fatGoal: parseInt(fatGoal),
|
fatGoal: parseInt(fatGoal),
|
||||||
carboGoal: parseInt(carboGoal),
|
carboGoal: parseInt(carboGoal),
|
||||||
fiberGoal: parseInt(fiberGoal),
|
fiberGoal: parseInt(fiberGoal),
|
||||||
|
bodyWeight: bodyWeight ? parseFloat(bodyWeight) : undefined,
|
||||||
|
height: height ? parseFloat(height) : undefined,
|
||||||
|
bmi: bmi ? parseFloat(bmi) : undefined,
|
||||||
|
bmiResult: bmiResult ? bmiResult.value : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
hideLoading();
|
hideLoading();
|
||||||
@@ -289,6 +323,10 @@ async function toggleSettingsView() {
|
|||||||
getInputById('fatGoal').value = settings[index].fatGoal;
|
getInputById('fatGoal').value = settings[index].fatGoal;
|
||||||
getInputById('carboGoal').value = settings[index].carboGoal;
|
getInputById('carboGoal').value = settings[index].carboGoal;
|
||||||
getInputById('fiberGoal').value = settings[index].fiberGoal;
|
getInputById('fiberGoal').value = settings[index].fiberGoal;
|
||||||
|
getInputById('bodyWeight').value = settings[index].bodyWeight ?? 0;
|
||||||
|
getInputById('height').value = settings[index].height ?? 0;
|
||||||
|
getInputById('bmi').value = settings[index].bmi ?? 0;
|
||||||
|
getInputById('bmiResult').value = settings[index].bmiResult ?? '';
|
||||||
} else {
|
} else {
|
||||||
getInputById('caloriesGoal').value = '';
|
getInputById('caloriesGoal').value = '';
|
||||||
getInputById('proteinGoal').value = '';
|
getInputById('proteinGoal').value = '';
|
||||||
@@ -503,6 +541,14 @@ const setupEventListeners = () => {
|
|||||||
// Settings form
|
// Settings form
|
||||||
document.getElementById('settingsForm')?.addEventListener('submit', handleSaveSettings);
|
document.getElementById('settingsForm')?.addEventListener('submit', handleSaveSettings);
|
||||||
|
|
||||||
|
// BMI calculator - recalculate on input change
|
||||||
|
getInputById('bodyWeight').addEventListener('change', () => {
|
||||||
|
setupBMICalculator();
|
||||||
|
});
|
||||||
|
getInputById('height').addEventListener('change', () => {
|
||||||
|
setupBMICalculator();
|
||||||
|
});
|
||||||
|
|
||||||
// Clear cache button
|
// Clear cache button
|
||||||
getButtonById('clearCacheBtn')?.addEventListener('click', handleClearCache);
|
getButtonById('clearCacheBtn')?.addEventListener('click', handleClearCache);
|
||||||
|
|
||||||
@@ -1324,6 +1370,7 @@ async function loadFoodEntries(date: Date) {
|
|||||||
const hasGoals = Array.isArray(settings) && settings.length > 0;
|
const hasGoals = Array.isArray(settings) && settings.length > 0;
|
||||||
if (hasGoals) {
|
if (hasGoals) {
|
||||||
const index = settings.length - 1;
|
const index = settings.length - 1;
|
||||||
|
const bodyWeight = parseInt(settings[index].bodyWeight);
|
||||||
getDivById('caloriesGoalText').classList.add('hidden');
|
getDivById('caloriesGoalText').classList.add('hidden');
|
||||||
if (parseInt(settings[index].caloriesGoal) > 0) {
|
if (parseInt(settings[index].caloriesGoal) > 0) {
|
||||||
getDivById('caloriesGoalText').textContent = `of ${settings[index].caloriesGoal}`;
|
getDivById('caloriesGoalText').textContent = `of ${settings[index].caloriesGoal}`;
|
||||||
@@ -1332,7 +1379,12 @@ async function loadFoodEntries(date: Date) {
|
|||||||
|
|
||||||
getDivById('proteinGoalText').classList.add('hidden');
|
getDivById('proteinGoalText').classList.add('hidden');
|
||||||
if (parseInt(settings[index].proteinGoal) > 0) {
|
if (parseInt(settings[index].proteinGoal) > 0) {
|
||||||
getDivById('proteinGoalText').textContent = `of ${settings[index].proteinGoal}`;
|
let percent = '';
|
||||||
|
if (bodyWeight > 0) {
|
||||||
|
const proteinPerKg = Math.round((parseInt(settings[index].proteinGoal) / bodyWeight) * 10) / 10;
|
||||||
|
percent = ` (${proteinPerKg} g/kg)`;
|
||||||
|
}
|
||||||
|
getDivById('proteinGoalText').textContent = `of ${settings[index].proteinGoal}${percent}`;
|
||||||
getDivById('proteinGoalText').classList.remove('hidden');
|
getDivById('proteinGoalText').classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1344,7 +1396,12 @@ async function loadFoodEntries(date: Date) {
|
|||||||
|
|
||||||
getDivById('carboGoalText').classList.add('hidden');
|
getDivById('carboGoalText').classList.add('hidden');
|
||||||
if (parseInt(settings[index].carboGoal) > 0) {
|
if (parseInt(settings[index].carboGoal) > 0) {
|
||||||
getDivById('carboGoalText').textContent = `of ${settings[index].carboGoal}`;
|
let percent = '';
|
||||||
|
if (bodyWeight > 0) {
|
||||||
|
const carboPerKg = Math.round((parseInt(settings[index].carboGoal) / bodyWeight) * 10) / 10;
|
||||||
|
percent = ` (${carboPerKg} g/kg)`;
|
||||||
|
}
|
||||||
|
getDivById('carboGoalText').textContent = `of ${settings[index].carboGoal}${percent}`;
|
||||||
getDivById('carboGoalText').classList.remove('hidden');
|
getDivById('carboGoalText').classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export type UserSettings = {
|
|||||||
fatGoal: number;
|
fatGoal: number;
|
||||||
carboGoal: number;
|
carboGoal: number;
|
||||||
fiberGoal: number;
|
fiberGoal: number;
|
||||||
|
bodyWeight?: number;
|
||||||
|
height?: number;
|
||||||
|
bmi?: number;
|
||||||
|
bmiResult?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DailyTotalCalories = {
|
export type DailyTotalCalories = {
|
||||||
|
|||||||
Reference in New Issue
Block a user