feat: add editing capability
This commit is contained in:
@@ -149,6 +149,10 @@
|
||||
<div class="search-results" id="searchResults">
|
||||
<!-- Results will be populated here -->
|
||||
</div>
|
||||
|
||||
<!-- Hidden inputs for state -->
|
||||
<input type="hidden" id="foodIdToUpdate">
|
||||
<input type="hidden" id="foodTimeToUpdate">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -156,6 +160,7 @@
|
||||
<input type="number" id="gramAmount" min="1" step="1" value="100">
|
||||
</div>
|
||||
<button class="add-btn" id="add-food-btn">Add Food</button>
|
||||
<button class="cancel-btn" id="cancel-food-btn">Cancel</button>
|
||||
</div>
|
||||
<div class="display-none" id="food-preview">
|
||||
<div class="nutrition-summary">
|
||||
|
||||
@@ -98,6 +98,26 @@ export class AppwriteDB {
|
||||
}
|
||||
}
|
||||
|
||||
// Upadte food entry
|
||||
static async updateFoodEntry(documentId: string, entryData: Partial<FoodStorage>) {
|
||||
try {
|
||||
const response = await databases.updateDocument(
|
||||
DATABASE_ID,
|
||||
FOOD_ENTRIES_COLLECTION_ID,
|
||||
documentId,
|
||||
{
|
||||
...entryData,
|
||||
userId: (await account.get()).$id,
|
||||
}
|
||||
);
|
||||
console.debug('Food entry updated:', response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Update food entry error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Save food entry
|
||||
static async saveUserSettings(userSettings: UserSettings) {
|
||||
try {
|
||||
|
||||
+3
-3
@@ -88,6 +88,7 @@ export const foodDatabase: FoodItem[] = [
|
||||
{ name: 'Melão', info: { calories: 29, protein: 0.7, fat: 0, carbs: 7.5, fiber: 0.3, category: 'fruits', alkaline: true } },
|
||||
{ name: 'Uva', info: { calories: 53, protein: 0.7, fat: 0.2, carbs: 13.6, fiber: 0.9, category: 'fruits', alkaline: true } },
|
||||
{ name: 'Goiaba, branca, com casca, crua', info: { calories: 52, protein: 0.9, fat: 0.5, carbs: 12.4, fiber: 6.3, category: 'fruits', alkaline: true } },
|
||||
{ name: 'Morango, cru', info: { calories: 30, protein: 0.9, fat: 0.3, carbs: 6.8, fiber: 1.7, category: 'fruits', alkaline: true } },
|
||||
|
||||
// Dairy
|
||||
{ name: 'Iogurte, natural, desnatado', info: { calories: 41, protein: 3.8, fat: 0.3, carbs: 5.8, fiber: 0, category: 'dairy', alkaline: true } },
|
||||
@@ -117,7 +118,6 @@ export const foodDatabase: FoodItem[] = [
|
||||
{ name: 'Couve flor, cozida', info: { calories: 19, protein: 1.2, fat: 0.3, carbs: 3.9, fiber: 2.1, category: 'carbs (low)', alkaline: true } },
|
||||
{ name: 'Pepino', info: { calories: 10, protein: 0.9, fat: 0, carbs: 2, fiber: 1.1, category: 'carbs (low)', alkaline: true } },
|
||||
{ name: 'Chuchu, cozido', info: { calories: 19, protein: 0.4, fat: 0, carbs: 4.9, fiber: 1.0, category: 'carbs (low)', alkaline: true } },
|
||||
{ name: 'Cogumelo Paris, in natura', info: { calories: 17, protein: 4.9, fat: 0, carbs: 1, fiber: 1.7, category: 'proteins', alkaline: true } },
|
||||
{ name: 'Milho, verde, enlatado, drenado', info: { calories: 98, protein: 3.2, fat: 2.4, carbs: 17.1, fiber: 4.6, category: 'proteins', alkaline: true } },
|
||||
{ name: 'Morango, cru', info: { calories: 30, protein: 0.9, fat: 0.3, carbs: 6.8, fiber: 1.7, category: 'proteins', alkaline: true } },
|
||||
{ name: 'Cogumelo Paris, in natura', info: { calories: 17, protein: 4.9, fat: 0, carbs: 1, fiber: 1.7, category: 'carbs (low)', alkaline: true } },
|
||||
{ name: 'Milho, verde, enlatado, drenado', info: { calories: 98, protein: 3.2, fat: 2.4, carbs: 17.1, fiber: 4.6, category: 'carbs (low)', alkaline: true } },
|
||||
];
|
||||
|
||||
+102
-8
@@ -285,6 +285,14 @@ function hideLoading() {
|
||||
loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
function clearEditing() {
|
||||
searchInput.value = '';
|
||||
getInputById('gramAmount').value = '100';
|
||||
showFoodPreview(false);
|
||||
getButtonById('add-food-btn').innerHTML = 'Add Food';
|
||||
selectedFood = null;
|
||||
}
|
||||
|
||||
// Clear application data
|
||||
function clearAppData() {
|
||||
getDivById('caloriesCounter').textContent = '0';
|
||||
@@ -301,6 +309,7 @@ function clearAppData() {
|
||||
totalCaloriesToday = 0;
|
||||
totalGramsToday = 0;
|
||||
totalAlkalineToday = 0;
|
||||
getButtonById('cancel-food-btn').style.display = 'none';
|
||||
}
|
||||
|
||||
const getFoodData = (grams: number, foodData: FoodItem): FoodItem => {
|
||||
@@ -322,7 +331,7 @@ const getFoodData = (grams: number, foodData: FoodItem): FoodItem => {
|
||||
const getFoodItemByName = (foodName: string): FoodItem => {
|
||||
const foodDataSearch: FoodItem[] = foodDatabase.filter(f => f.name === foodName);
|
||||
if (foodDataSearch.length === 0) {
|
||||
throw new Error(`Food not found for name ${name}`);
|
||||
throw new Error(`Food not found for name ${foodName}`);
|
||||
}
|
||||
return foodDataSearch[0];
|
||||
}
|
||||
@@ -366,7 +375,11 @@ const setupEventListeners = () => {
|
||||
});
|
||||
|
||||
getButtonById('add-food-btn').addEventListener('click', () => {
|
||||
if (getButtonById('add-food-btn').innerHTML === 'Add Food') {
|
||||
addFood();
|
||||
} else {
|
||||
updateFood();
|
||||
}
|
||||
});
|
||||
|
||||
getButtonById('next-month-btn').addEventListener('click', () => {
|
||||
@@ -388,6 +401,10 @@ const setupEventListeners = () => {
|
||||
toggleAuthForms();
|
||||
});
|
||||
|
||||
getButtonById('cancel-food-btn').addEventListener('click', () => {
|
||||
clearEditing();
|
||||
});
|
||||
|
||||
// Auth forms
|
||||
document.getElementById('loginForm')?.addEventListener('submit', handleLogin);
|
||||
document.getElementById('registerForm')?.addEventListener('submit', handleRegister);
|
||||
@@ -545,6 +562,38 @@ function selectFood(food: FoodItem) {
|
||||
previewCalories(food);
|
||||
}
|
||||
|
||||
async function setFoodToEdit(foodId: string) {
|
||||
showLoading();
|
||||
|
||||
try {
|
||||
// get food item
|
||||
const entries = await AppwriteDB.getFoodEntries(selectedDate);
|
||||
const foodToEdit = entries.filter(entry => entry.$id === foodId);
|
||||
if (foodToEdit.length === 0) {
|
||||
swal('Hey!', 'Unable to get food entry from server', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// set the selected food name and quantity
|
||||
const foodData: FoodItem = getFoodItemByName(foodToEdit[0].name);
|
||||
selectedFood = foodData;
|
||||
searchInput.value = foodToEdit[0].name;
|
||||
getInputById('gramAmount').value = foodToEdit[0].grams;
|
||||
previewCalories(foodData)
|
||||
|
||||
getButtonById('add-food-btn').innerHTML = 'Save Food';
|
||||
getInputById('foodIdToUpdate').value = foodId;
|
||||
getInputById('foodTimeToUpdate').value = foodToEdit[0].time;
|
||||
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Set food to edit error:', error);
|
||||
swal('Oh no!', 'Failed to load food entry: ' + error.message, 'error');
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// Hide search results
|
||||
function hideSearchResults() {
|
||||
searchResults.classList.remove('show');
|
||||
@@ -584,9 +633,7 @@ function editCardHandler(e: Event) {
|
||||
const foodId = card.getAttribute('data-food-id');
|
||||
|
||||
if (foodId) {
|
||||
// FIXME: Implement edit functionality
|
||||
// handleEditFood(foodId);
|
||||
window.alert('Edit functionality is not implemented yet.');
|
||||
setFoodToEdit(foodId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,6 +671,56 @@ const updateCurrentDate = () => {
|
||||
});
|
||||
}
|
||||
|
||||
const updateFood = async () => {
|
||||
if (!currentUser) {
|
||||
swal('Hey!', 'Please log in to add food entries.', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
const gramAmount = getInputById('gramAmount');
|
||||
|
||||
if (!selectedFood || !gramAmount.value) {
|
||||
swal('Hey!', 'Please select a food item and enter the amount', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading();
|
||||
|
||||
try {
|
||||
const foodId = getInputById('foodIdToUpdate').value;
|
||||
const grams: number = parseFloat(gramAmount.value);
|
||||
const foodData: FoodItem = getFoodItemByName(selectedFood.name);
|
||||
|
||||
// Current date, with timezone
|
||||
const date = new Date(selectedDate.getTime() - (selectedDate.getTimezoneOffset() * 60000)).toISOString();
|
||||
|
||||
// Calculate nutrition for the amount
|
||||
const proportion: FoodItem = getFoodData(grams, foodData);
|
||||
const entry: FoodStorage = {
|
||||
name: selectedFood.name,
|
||||
grams: grams,
|
||||
calories: proportion.info.calories,
|
||||
protein: proportion.info.protein,
|
||||
fat: proportion.info.fat,
|
||||
carbs: proportion.info.carbs,
|
||||
fiber: proportion.info.fiber,
|
||||
date: date.split('T')[0],
|
||||
alkaline: selectedFood.info.alkaline,
|
||||
time: getInputById('foodTimeToUpdate').value,
|
||||
};
|
||||
|
||||
// Save to Appwrite
|
||||
await AppwriteDB.updateFoodEntry(foodId, entry);
|
||||
clearEditing();
|
||||
selectDate(selectedDate);
|
||||
} catch (error) {
|
||||
console.error('Update food error:', error);
|
||||
swal('Oh no!', 'Failed to update food entry: ' + error.message, 'error');
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// Add food to the log
|
||||
const addFood = async () => {
|
||||
if (!currentUser) {
|
||||
@@ -634,7 +731,7 @@ const addFood = async () => {
|
||||
const gramAmount = getInputById('gramAmount');
|
||||
|
||||
if (!selectedFood || !gramAmount.value) {
|
||||
swal('Hey!', 'Please select a food item and enter amount', 'error');
|
||||
swal('Hey!', 'Please select a food item and enter the amount', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1090,6 +1187,3 @@ function editFood(id: string) {
|
||||
}
|
||||
|
||||
(window as any).selectFood = selectFood;
|
||||
(window as any).toggleCard = toggleCard;
|
||||
(window as any).editFood = editFood;
|
||||
(window as any).deleteFood = deleteFood;
|
||||
|
||||
@@ -402,7 +402,7 @@ h1 {
|
||||
|
||||
.add-food-form {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr auto;
|
||||
grid-template-columns: 2fr 1fr 1fr auto;
|
||||
gap: 15px;
|
||||
align-items: end;
|
||||
}
|
||||
@@ -467,6 +467,24 @@ label {
|
||||
box-shadow: 0 5px 15px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
padding: 17px 25px;
|
||||
background: linear-gradient(135deg, #757575, #616161);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.cancel-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.food-table-section {
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
|
||||
Reference in New Issue
Block a user