From 5b62fd989f1d9d8a2d6d76adadbe760ba5671f2a Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Tue, 2 Sep 2025 18:21:59 -0300 Subject: [PATCH 1/4] feat: improve food list --- index.html | 3 + src/.env.example | 4 + src/index.ts | 78 ++++++++++++++++- style.css | 214 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 src/.env.example diff --git a/index.html b/index.html index 47c9c53..5dafd76 100644 --- a/index.html +++ b/index.html @@ -207,6 +207,9 @@ +
+ +
diff --git a/src/.env.example b/src/.env.example new file mode 100644 index 0000000..0493e4a --- /dev/null +++ b/src/.env.example @@ -0,0 +1,4 @@ +VITE_APPWRITE_ENDPOINT=here +VITE_APPWRITE_DBID=here +VITE_APPWRITE_FOODENTRIESID=here +VITE_APPWRITE_USERSETTINGSID=here \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 9a1efc6..a677558 100644 --- a/src/index.ts +++ b/src/index.ts @@ -545,7 +545,7 @@ const addDeleteEvents = () => { const row = target.closest('tr'); const entryId = row?.querySelector('.hidden-column')?.textContent; if (entryId && row) { - handleDeleteFood(entryId, row); + handleDeleteFood(entryId); } }); }); @@ -656,6 +656,7 @@ async function loadFoodEntries(date: Date) { // Clear current table getDivById('foodTableBody').innerHTML = ''; + getDivById('foodCardsContainer').innerHTML = ''; // Add each entry to table entries.forEach(entry => { @@ -676,6 +677,16 @@ async function loadFoodEntries(date: Date) { addFoodToTable(foodData, entry.$id); }); + if (entries.length === 0) { + getDivById('foodCardsContainer').innerHTML = ` +
+
🍽️
+
No food items logged yet.
+
Add your first item to get started!
+
+ `; + } + // Setup Delete events addDeleteEvents(); @@ -751,7 +762,7 @@ async function loadFoodEntries(date: Date) { } // Handle food deletion -async function handleDeleteFood(documentId: string, row: HTMLTableRowElement) { +async function handleDeleteFood(documentId: string) { if (!documentId) return; const willDelete = await swal({ @@ -804,6 +815,53 @@ function addFoodToTable(foodData: FoodStorage, documentId: string) { // Setup Delete events addDeleteEvents(); + + // New Food card + const container = document.getElementById('foodCardsContainer'); + const card = document.createElement('div'); + card.className = 'food-card'; + card.setAttribute('data-id', documentId); + + card.innerHTML = ` +
+
+
+
${foodData.name}
+
${foodData.time || new Date().toLocaleTimeString()} • ${foodData.grams}g
+
+
${foodData.calories} cal
+
+
+
+
+
+
+
+
Protein
+
${foodData.protein}g
+
+
+
Fat
+
${foodData.fat}g
+
+
+
Carbs
+
${foodData.carbs}g
+
+
+
Fiber
+
${foodData.fiber}g
+
+
+
+ + +
+
+
+ `; + + container?.appendChild(card); } // Update total calories @@ -970,4 +1028,20 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont return dayElement; } +function toggleCard(id) { + const card = document.querySelector(`[data-id="${id}"]`); + card.classList.toggle('expanded'); +} + +function deleteFood(id) { + handleDeleteFood(id); +} + +function editFood(id) { + alert(`Edit functionality for food item ${id} would be implemented here`); +} + (window as any).selectFood = selectFood; +(window as any).toggleCard = toggleCard; +(window as any).editFood = editFood; +(window as any).deleteFood = deleteFood; diff --git a/style.css b/style.css index 0c69910..19af16c 100644 --- a/style.css +++ b/style.css @@ -167,11 +167,200 @@ h1 { background-color: #f8f9ff; } +.food-log-section { + max-width: 800px; + margin: 0 auto; +} -.food-name { +.section-title { + font-size: 24px; font-weight: 600; color: #333; - margin-bottom: 2px; + margin-bottom: 20px; + text-align: center; +} + +.food-cards-container { + display: flex; + flex-direction: column; + gap: 12px; +} + +.food-card { + background: white; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; + transition: all 0.3s ease; +} + +.food-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.card-header { + padding: 16px; + cursor: pointer; + display: flex; + justify-content: between; + align-items: center; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + position: relative; +} + +.card-header:hover { + background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%); +} + +.card-main-info { + flex: 1; + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; +} + +.food-name-time { + flex: 1; +} + +.food-name { + font-size: 16px; + font-weight: 600; + color: #333; + margin-bottom: 4px; +} + +.food-time { + font-size: 13px; + opacity: 0.9; +} + +.calories-display { + font-size: 18px; + font-weight: 700; + color: #fff; + text-align: right; +} + +.expand-icon { + margin-left: 12px; + font-size: 14px; + transition: transform 0.3s ease; + opacity: 0.8; +} + +.food-card.expanded .expand-icon { + transform: rotate(180deg); +} + +.card-details { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease; + background: #fafafa; +} + +.food-card.expanded .card-details { + max-height: 300px; +} + +.details-content { + padding: 20px; +} + +.nutrition-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 16px; + margin-bottom: 20px; +} + +.nutrition-item { + text-align: center; + padding: 12px; + background: white; + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.nutrition-label { + font-size: 12px; + color: #666; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 4px; +} + +.nutrition-value { + font-size: 16px; + font-weight: 600; + color: #333; +} + +.card-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + padding-top: 16px; + border-top: 1px solid #e0e0e0; +} + +.btn { + padding: 8px 16px; + border: none; + border-radius: 6px; + font-size: 14px; + cursor: pointer; + transition: all 0.2s ease; +} + +.btn-delete { + background: #ff4757; + color: white; +} + +.btn-delete:hover { + background: #ff3838; +} + +.btn-edit { + background: #5352ed; + color: white; +} + +.btn-edit:hover { + background: #4834d4; +} + +.empty-state { + text-align: center; + padding: 60px 20px; + color: #666; +} + +.empty-state-icon { + font-size: 48px; + margin-bottom: 16px; + opacity: 0.5; +} + +.add-food-btn { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border: none; + padding: 12px 24px; + border-radius: 8px; + font-size: 16px; + cursor: pointer; + margin-bottom: 20px; + transition: all 0.2s ease; +} + +.add-food-btn:hover { + background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%); + transform: translateY(-1px); } .food-details { @@ -522,4 +711,25 @@ label { h1 { font-size: 2rem; } + + .card-header { + padding: 14px; + } + + .food-name { + font-size: 15px; + } + + .calories-display { + font-size: 16px; + } + + .nutrition-grid { + grid-template-columns: repeat(2, 1fr); + gap: 12px; + } + + .details-content { + padding: 16px; + } } From 71564b1c8072788d3e0f9236d7c4f919a3c18bcb Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Wed, 3 Sep 2025 21:03:46 -0300 Subject: [PATCH 2/4] feat: replace the food table by a better ui --- index.html | 31 ++------- package-lock.json | 16 ++--- package.json | 4 +- src/index.ts | 169 +++++++++++++++++++++++++++++----------------- style.css | 27 +++++--- 5 files changed, 138 insertions(+), 109 deletions(-) diff --git a/index.html b/index.html index 5dafd76..197923d 100644 --- a/index.html +++ b/index.html @@ -183,33 +183,10 @@
-
-
Food Log
-
- - - - - - - - - - - - - - - - - - - -
IDTimeFoodGramsCaloriesProtein (g)Fat (g)Carbs (g)Fiber (g)ActionAlkaline
-
-
- -
+
Food Log
+ +
+
diff --git a/package-lock.json b/package-lock.json index abf198f..b3d4055 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,12 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "appwrite": "^18.2.0", + "appwrite": "^19.0.0", "sweetalert": "^2.1.2" }, "devDependencies": { "typescript": "^5.9.2", - "vite": "^7.1.3" + "vite": "^7.1.4" } }, "node_modules/@esbuild/aix-ppc64": { @@ -747,9 +747,9 @@ "license": "MIT" }, "node_modules/appwrite": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-18.2.0.tgz", - "integrity": "sha512-g7pQpsxqR7+amEIaQLXMN4XzdQKenTHnGdA4s7UUJdZufhlHdJby8895h8z893+S0XipeHZhi0wpxYA2An95Rg==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-19.0.0.tgz", + "integrity": "sha512-guamzST6Fvn/lvuBMLJGzJGL+N+o313j53qyfKM1ZHfcDd1ADnSONgm9DZihRFYBaWtU7s1bwkphkBJWSUwurQ==", "license": "BSD-3-Clause" }, "node_modules/es6-object-assign": { @@ -999,9 +999,9 @@ } }, "node_modules/vite": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", - "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.4.tgz", + "integrity": "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index a870824..4503707 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,10 @@ "description": "", "devDependencies": { "typescript": "^5.9.2", - "vite": "^7.1.3" + "vite": "^7.1.4" }, "dependencies": { - "appwrite": "^18.2.0", + "appwrite": "^19.0.0", "sweetalert": "^2.1.2" } } diff --git a/src/index.ts b/src/index.ts index a677558..af5cdaa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import { getButtonById, getButtonListByClassName, getDivById, getInputById, show import { FoodItem, FoodStorage } from './types.js'; import { AppwriteAuth, AppwriteDB } from './appwrite.js'; import swal from 'sweetalert'; +import { Models } from 'appwrite'; // App state let selectedDate = new Date(); @@ -11,6 +12,9 @@ let searchTimeout: number | null = null; let selectedFood: FoodItem | null = null; // review here let currentHighlightIndex: number = -1; let currentResults: FoodItem[] = []; +let totalCaloriesToday: number = 0; +let totalGramsToday: number = 0; +let totalAlkalineToday: number = 0; // Authentication state let currentUser: any | null = null; @@ -283,11 +287,20 @@ function hideLoading() { // Clear application data function clearAppData() { - getDivById('foodTableBody').innerHTML = ''; getDivById('caloriesCounter').textContent = '0'; getInputById('foodSearchInput').value = ''; getInputById('gramAmount').value = '100'; + getDivById('foodCardsContainer').innerHTML = ` +
+
🍽️
+
No food items logged yet.
+
Add your first item to get started!
+
+ `; selectedFood = null; + totalCaloriesToday = 0; + totalGramsToday = 0; + totalAlkalineToday = 0; } const getFoodData = (grams: number, foodData: FoodItem): FoodItem => { @@ -537,17 +550,65 @@ function hideSearchResults() { currentHighlightIndex = -1; } +function toggleCardHandler(e: Event) { + e.preventDefault(); + e.stopPropagation(); + + const card = e.currentTarget as HTMLElement; + card.classList.toggle('expanded'); + + const cardDetails = card.nextElementSibling as HTMLElement; + if (cardDetails && cardDetails.classList.contains('card-details')) { + cardDetails.classList.toggle('expanded'); + } +} + +function deleteCardHandler(e: Event) { + e.preventDefault(); + e.stopPropagation(); + + const card = e.currentTarget as HTMLElement; + const foodId = card.getAttribute('data-food-id'); + + if (foodId) { + handleDeleteFood(foodId); + } +} + +function editCardHandler(e: Event) { + e.preventDefault(); + e.stopPropagation(); + + const card = e.currentTarget as HTMLElement; + const foodId = card.getAttribute('data-food-id'); + + if (foodId) { + // FIXME: Implement edit functionality + // handleEditFood(foodId); + window.alert('Edit functionality is not implemented yet.'); + } +} + const addDeleteEvents = () => { - const elements = getButtonListByClassName('delete-food-entry'); - Array.from(elements).forEach((el: HTMLButtonElement) => { - el.addEventListener('click', (e) => { - const target = e.target as HTMLElement; - const row = target.closest('tr'); - const entryId = row?.querySelector('.hidden-column')?.textContent; - if (entryId && row) { - handleDeleteFood(entryId); - } - }); + // Setup toggle show cards events + const cards = getButtonListByClassName('card-header-toggle'); + Array.from(cards).forEach((card: HTMLElement) => { + card.removeEventListener('click', toggleCardHandler); + card.addEventListener('click', toggleCardHandler); + }); + + // Setup delete card events + const deleteCards = getButtonListByClassName('btn-delete'); + Array.from(deleteCards).forEach((card: HTMLElement) => { + card.removeEventListener('click', deleteCardHandler); + card.addEventListener('click', deleteCardHandler); + }); + + // Setup edit card events + const editCards = getButtonListByClassName('btn-edit'); + Array.from(editCards).forEach((card: HTMLElement) => { + card.removeEventListener('click', editCardHandler); + card.addEventListener('click', editCardHandler); }); } @@ -611,7 +672,7 @@ const addFood = async () => { addFoodToTable(entry, savedEntry.$id); // Update totals - updateTotalCalories(); + updateTotalCalories([savedEntry]); // Update total macros const proteinDiv: HTMLElement = getDivById('proteinValue'); @@ -655,7 +716,6 @@ async function loadFoodEntries(date: Date) { const entries = await AppwriteDB.getFoodEntries(date); // Clear current table - getDivById('foodTableBody').innerHTML = ''; getDivById('foodCardsContainer').innerHTML = ''; // Add each entry to table @@ -691,7 +751,7 @@ async function loadFoodEntries(date: Date) { addDeleteEvents(); // Update total calories - updateTotalCalories(); + updateTotalCalories(entries); // Update counters let totalCalories = 0; @@ -783,7 +843,6 @@ async function handleDeleteFood(documentId: string) { await AppwriteDB.deleteFoodEntry(documentId); selectDate(selectedDate); - } catch (error) { console.error('Delete food error:', error); swal('Oh no!', 'Failed to delete food entry: ' + error.message, 'error'); @@ -794,36 +853,18 @@ async function handleDeleteFood(documentId: string) { // Add food to table (existing function - update to use document ID) function addFoodToTable(foodData: FoodStorage, documentId: string) { - const tableBody = document.getElementById('foodTableBody'); - const row = document.createElement('tr'); - - row.innerHTML = ` - ${documentId} - ${foodData.time || new Date().toLocaleTimeString()} - ${foodData.name} - ${foodData.grams} - ${foodData.calories} - ${foodData.protein} - ${foodData.fat} - ${foodData.carbs} - ${foodData.fiber} - - ${foodData.alkaline} - `; - - tableBody?.appendChild(row); - - // Setup Delete events - addDeleteEvents(); - // New Food card - const container = document.getElementById('foodCardsContainer'); + const container = getDivById('foodCardsContainer'); const card = document.createElement('div'); card.className = 'food-card'; card.setAttribute('data-id', documentId); + if (container.innerHTML.includes('empty-state')) { + container.innerHTML = ''; + } + card.innerHTML = ` -
+
${foodData.name}
@@ -854,42 +895,45 @@ function addFoodToTable(foodData: FoodStorage, documentId: string) {
- - + +
`; container?.appendChild(card); + + // Setup Delete events + addDeleteEvents(); } // Update total calories -function updateTotalCalories() { - const caloriesCells = document.querySelectorAll('#foodTableBody td:nth-child(5)'); // 5th column is calories - let total = 0; - - caloriesCells.forEach((cell: Element) => { - total += parseInt(cell.innerHTML) || 0; +function updateTotalCalories(entries: Models.DefaultDocument[]) { + let total = totalCaloriesToday; + + entries.forEach((entry: Models.DefaultDocument) => { + total += entry.calories || 0; }); - + getDivById('caloriesCounter').textContent = total.toString(); + totalCaloriesToday = total; + + // FIX ME: alkaline count // Update alkaline level - const gramsCell = document.querySelectorAll('#foodTableBody td:nth-child(4)'); // 4th is grams - let totalGrams = 0; + let totalGrams = totalGramsToday; let indexMap: {index: number, grams: number}[] = []; - gramsCell.forEach((cell: Element, key: number) => { - totalGrams += parseInt(cell.innerHTML) || 0; + entries.forEach((entry: Models.DefaultDocument, key: number) => { + totalGrams += entry.grams || 0; indexMap.push({ index: key, - grams: parseInt(cell.innerHTML) || 0 + grams: entry.grams || 0 }); }); - const alkalineCell = document.querySelectorAll('#foodTableBody td:nth-child(11)'); // 11th is alkaline - let totalAlkaline = 0; - alkalineCell.forEach((cell: Element, key: number) => { - if (cell.innerHTML === 'true') { + let totalAlkaline = totalAlkalineToday; + entries.forEach((entry: Models.DefaultDocument, key: number) => { + if (entry.alkaline) { for (let ob of indexMap) { if (ob.index === key) { totalAlkaline += ob.grams; @@ -916,6 +960,9 @@ const nextMonth = () => { const selectDate = async (date: Date) => { selectedDate = date; + totalCaloriesToday = 0; + totalGramsToday = 0; + totalAlkalineToday = 0; updateCurrentDate(); await loadFoodEntries(date); renderCalendar(); @@ -1028,16 +1075,16 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont return dayElement; } -function toggleCard(id) { +function toggleCard(id: string) { const card = document.querySelector(`[data-id="${id}"]`); - card.classList.toggle('expanded'); + card?.classList.toggle('expanded'); } -function deleteFood(id) { +function deleteFood(id: string) { handleDeleteFood(id); } -function editFood(id) { +function editFood(id: string) { alert(`Edit functionality for food item ${id} would be implemented here`); } diff --git a/style.css b/style.css index 19af16c..ccbe813 100644 --- a/style.css +++ b/style.css @@ -232,6 +232,10 @@ h1 { margin-bottom: 4px; } +.card-header .food-name { + color: #fff; +} + .food-time { font-size: 13px; opacity: 0.9; @@ -251,19 +255,20 @@ h1 { opacity: 0.8; } -.food-card.expanded .expand-icon { - transform: rotate(180deg); -} - .card-details { - max-height: 0; - overflow: hidden; - transition: max-height 0.3s ease; - background: #fafafa; + display: none; } -.food-card.expanded .card-details { - max-height: 300px; +.card-details.expanded { + display: block; +} + +.card-header-toggle .expand-icon { + transition: transform 0.3s ease; +} + +.card-header-toggle.expanded .expand-icon { + transform: rotate(180deg); } .details-content { @@ -288,7 +293,6 @@ h1 { .nutrition-label { font-size: 12px; color: #666; - text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; } @@ -530,6 +534,7 @@ label { border-radius: 6px; box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1); overflow: hidden; + margin-top: 30px; } .calendar-header { From 07eba9b64a39a5639b932af3016142a0bdd3bd77 Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Thu, 4 Sep 2025 11:40:53 -0300 Subject: [PATCH 3/4] feat: add more food and food type along with new icons --- src/foodDatabase.ts | 52 +++++++++++++++++++++++++++++++++++---------- src/index.ts | 13 ++++++------ src/types.ts | 2 +- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/foodDatabase.ts b/src/foodDatabase.ts index 9652aba..6156a0d 100644 --- a/src/foodDatabase.ts +++ b/src/foodDatabase.ts @@ -15,19 +15,29 @@ export const foodDatabase: FoodItem[] = [ { name: 'Semente de Abóbora', info: { calories: 446, protein: 18.55, fat: 19.4, carbs: 53.75, fiber: 6, category: 'fats', alkaline: true } }, // Proteínas - Proteins - { name: 'Atum Sólido, natural', info: { calories: 135, protein: 28.3, fat: 2.7, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Atum, conserva em óleo', info: { calories: 166, protein: 26.2, fat: 6, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, { name: 'Filé de Tilápia, cozido', info: { calories: 128, protein: 26.1, fat: 2.6, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, - { name: 'Ovo inteiro, cozido', info: { calories: 146, protein: 13.3, fat: 9.5, carbs: 0.6, fiber: 0, category: 'proteins', alkaline: false } }, - { name: 'Ovo clara, cozido', info: { calories: 59, protein: 13.4, fat: 0.1, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Ovo, de galinha, inteiro, cozido', info: { calories: 146, protein: 13.3, fat: 9.5, carbs: 0.6, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Ovo, de galinha, gema, cozida', info: { calories: 353, protein: 15.9, fat: 30.8, carbs: 1.6, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Ovo, de galinha, clara, cozida', info: { calories: 59, protein: 13.4, fat: 0.1, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, { name: 'PTS, crua', info: { calories: 288, protein: 52, fat: 0, carbs: 20, fiber: 14, category: 'proteins', alkaline: false } }, - { name: 'Patinho moído, grelhado', info: { calories: 219, protein: 35.9, fat: 7.3, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Carne, bovina, patinho, sem gordura, grelhado', info: { calories: 219, protein: 35.9, fat: 7.3, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, { name: 'Soja em Pó Growth', info: { calories: 400, protein: 86.7, fat: 5, carbs: 3.3, fiber: 0, category: 'proteins', alkaline: false } }, { name: 'Sobrecoxa s/pele, cozida', info: { calories: 245, protein: 24.9, fat: 15.4, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, - { name: 'Tofu', info: { calories: 65, protein: 6.5, fat: 4, carbs: 2, fiber: 0.75, category: 'proteins', alkaline: false } }, + { name: 'Soja, queijo (tofu)', info: { calories: 64, protein: 6.6, fat: 4, carbs: 2.1, fiber: 0.8, category: 'proteins', alkaline: false } }, { name: 'Feijão preto, cozido', info: { calories: 77, protein: 4.5, fat: 0.5, carbs: 14, fiber: 8.4, category: 'proteins', alkaline: false } }, { name: 'Lentilha, cozida', info: { calories: 93, protein: 6.3, fat: 0.5, carbs: 16.3, fiber: 7.9, category: 'proteins', alkaline: false } }, { name: 'Frango, peito, sem pele, cozido', info: { calories: 163, protein: 31.5, fat: 3.2, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, { name: 'Frango, peito, sem pele, grelhado', info: { calories: 159, protein: 32, fat: 2.5, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Carne, bovina, acém, moído, cozido', info: { calories: 212, protein: 26.7, fat: 10.9, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Carne, bovina, contra-filé, sem gordura, grelhado', info: { calories: 194, protein: 35.9, fat: 4.5, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Carne, bovina, costela, assada', info: { calories: 373, protein: 28.8, fat: 27.7, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Carne, bovina, cupim, assado', info: { calories: 330, protein: 28.6, fat: 23, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Carne, bovina, fígado, grelhado', info: { calories: 225, protein: 29.9, fat: 9, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Carne, bovina, picanha, sem gordura, grelhada', info: { calories: 238, protein: 31.9, fat: 11.3, carbs: 0, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Apresuntado', info: { calories: 129, protein: 13.5, fat: 6.7, carbs: 2.9, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Presunto, com capa de gordura', info: { calories: 128, protein: 14.4, fat: 6.8, carbs: 1.4, fiber: 0, category: 'proteins', alkaline: false } }, + { name: 'Presunto, sem capa de gordura', info: { calories: 94, protein: 14.3, fat: 2.7, carbs: 2.1, fiber: 0, category: 'proteins', alkaline: false } }, // Carboidratos, amiláceos - Carbs { name: 'Grão de Bico, cozido', info: { calories: 180, protein: 9.5, fat: 3, carbs: 30, fiber: 8.6, category: 'carbs (high)', alkaline: false } }, @@ -41,16 +51,16 @@ export const foodDatabase: FoodItem[] = [ { name: 'Batata inglesa, cozida', info: { calories: 52, protein: 1.2, fat: 0, carbs: 11.9, fiber: 1.3, category: 'carbs (high)', alkaline: true } }, { name: 'Batata inglesa, crua', info: { calories: 64, protein: 1.8, fat: 0, carbs: 14.7, fiber: 1.2, category: 'carbs (high)', alkaline: true } }, { name: 'Mandioca, cozida', info: { calories: 125, protein: 0.6, fat: 0.3, carbs: 30.1, fiber: 1.6, category: 'carbs (high)', alkaline: false } }, - { name: 'Pão Francês', info: { calories: 300, protein: 8, fat: 3.1, carbs: 58.6, fiber: 2.3, category: 'carbs (high)', alkaline: false } }, + { name: 'Pão, trigo, francês', info: { calories: 300, protein: 8, fat: 3.1, carbs: 58.6, fiber: 2.3, category: 'carbs (high)', alkaline: false } }, { name: 'Polenta, cozida', info: { calories: 192, protein: 4.8, fat: 1.2, carbs: 40, fiber: 3.8, category: 'carbs (high)', alkaline: false } }, - { name: 'Arroz branco, cozido', info: { calories: 128, protein: 2.5, fat: 0.2, carbs: 28.1, fiber: 1.6, category: 'carbs (high)', alkaline: false } }, - { name: 'Arroz integral, cozido', info: { calories: 124, protein: 2.6, fat: 1.0, carbs: 25.8, fiber: 2.7, category: 'carbs (high)', alkaline: false } }, + { name: 'Arroz, tipo 1 (branco), cozido', info: { calories: 128, protein: 2.5, fat: 0.2, carbs: 28.1, fiber: 1.6, category: 'carbs (high)', alkaline: false } }, + { name: 'Arroz, integral, cozido', info: { calories: 124, protein: 2.6, fat: 1.0, carbs: 25.8, fiber: 2.7, category: 'carbs (high)', alkaline: false } }, { name: 'Aveia', info: { calories: 394, protein: 13.9, fat: 8.5, carbs: 66.6, fiber: 9.1, category: 'carbs (high)', alkaline: false } }, { name: 'Ervilha seca, cozida', info: { calories: 88, protein: 7.5, fat: 0.5, carbs: 14.2, fiber: 9.7, category: 'carbs (high)', alkaline: true } }, { name: 'Ervilha congelada, cozida', info: { calories: 57, protein: 5, fat: 1, carbs: 7, fiber: 8, category: 'carbs (high)', alkaline: true } }, - { name: 'Pão integral de forma', info: { calories: 253, protein: 9.4, fat: 3.7, carbs: 49.9, fiber: 6.9, category: 'carbs (high)', alkaline: false } }, - { name: 'Pipoca c/óleo', info: { calories: 448, protein: 9.9, fat: 15.9, carbs: 70.3, fiber: 14.3, category: 'carbs (high)', alkaline: false } }, - { name: 'Pipoca s/óleo', info: { calories: 178, protein: 3.3, fat: 7.2, carbs: 25, fiber: 4.3, category: 'carbs (high)', alkaline: false } }, + { name: 'Pão, trigo, forma, integral', info: { calories: 253, protein: 9.4, fat: 3.7, carbs: 49.9, fiber: 6.9, category: 'carbs (high)', alkaline: false } }, + { name: 'Pipoca, com óleo de soja, sem sal', info: { calories: 448, protein: 9.9, fat: 15.9, carbs: 70.3, fiber: 14.3, category: 'carbs (high)', alkaline: false } }, + { name: 'Pipoca, sem óleo, sem sal', info: { calories: 178, protein: 3.3, fat: 7.2, carbs: 25, fiber: 4.3, category: 'carbs (high)', alkaline: false } }, { name: 'Quinoa, cozida', info: { calories: 158, protein: 5.55, fat: 2.5, carbs: 29, fiber: 2.5, category: 'carbs (high)', alkaline: true } }, { name: '70% Nibs Garoto', info: { calories: 544, protein: 8.4, fat: 40, carbs: 34, fiber: 0, category: 'carbs (high)', alkaline: false } }, { name: 'Barra de Proteína Vegana Growth', info: { calories: 400, protein: 25, fat: 22, carbs: 25, fiber: 8, category: 'carbs (high)', alkaline: false } }, @@ -59,6 +69,9 @@ export const foodDatabase: FoodItem[] = [ { name: 'Mel Silvestre', info: { calories: 230, protein: 0, fat: 0, carbs: 65, fiber: 0, category: 'carbs (high)', alkaline: false } }, { name: 'Melado', info: { calories: 297, protein: 0, fat: 0, carbs: 76.6, fiber: 0, category: 'carbs (high)', alkaline: false } }, { name: 'Tâmara desidratada', info: { calories: 230, protein: 0.3, fat: 33, carbs: 63.3, fiber: 7, category: 'carbs (high)', alkaline: false } }, + { name: 'Pão, de queijo, assado', info: { calories: 363, protein: 5.1, fat: 24.6, carbs: 34.2, fiber: 0.6, category: 'carbs (high)', alkaline: false } }, + { name: 'Cuscuz, de milho , cozido com sal', info: { calories: 113, protein: 2.2, fat: 0.7, carbs: 25.3, fiber: 2.1, category: 'carbs (high)', alkaline: false } }, + { name: 'Paçoca, amendoim', info: { calories: 487, protein: 16, fat: 26.1, carbs: 52.4, fiber: 7.3, category: 'carbs (high)', alkaline: false } }, // Folhas e verduras - Leaves { name: 'Agrião', info: { calories: 17, protein: 2.7, fat: 0.2, carbs: 2.3, fiber: 2.1, category: 'leaves', alkaline: true } }, @@ -75,6 +88,21 @@ 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 } }, + + // Dairy + { name: 'Iogurte, natural, desnatado', info: { calories: 41, protein: 3.8, fat: 0.3, carbs: 5.8, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Queijo, mozarela, mussarela, muçarela', info: { calories: 330, protein: 22.6, fat: 25.2, carbs: 3, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Queijo, parmesão', info: { calories: 453, protein: 35.6, fat: 33.5, carbs: 1.7, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Queijo, prato', info: { calories: 360, protein: 22.7, fat: 29.1, carbs: 1.9, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Queijo, requeijão, cremoso', info: { calories: 257, protein: 9.6, fat: 23.4, carbs: 2.4, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Requeijão light (Tirol)', info: { calories: 177, protein: 11, fat: 13.3, carbs: 3.3, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Queijo, ricota', info: { calories: 140, protein: 12.6, fat: 8.1, carbs: 3.8, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Creme de leite', info: { calories: 221, protein: 1.5, fat: 22.5, carbs: 4.6, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Leite, condensado', info: { calories: 313, protein: 7.7, fat: 6.7, carbs: 57, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Leite, de vaca, desnatado, pó', info: { calories: 362, protein: 34.7, fat: 0.9, carbs: 53, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Leite, de vaca, desnatado, UTH (Tirol)', info: { calories: 30.5, protein: 3.15, fat: 0, carbs: 4.5, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Leite, de vaca, integral, UTH (Tirol)', info: { calories: 57.5, protein: 3.15, fat: 3, carbs: 4.5, fiber: 0, category: 'dairy', alkaline: true } }, + { name: 'Leite, de vaca, integral, pó', info: { calories: 496, protein: 26, fat: 26.4, carbs: 38.4, fiber: 0, category: 'dairy', alkaline: true } }, // Low carb { name: 'Abóbrinha, italinaa, cozida', info: { calories: 15, protein: 1.1, fat: 0.2, carbs: 3.0, fiber: 1.6, category: 'carbs (low)', alkaline: true } }, @@ -90,4 +118,6 @@ export const foodDatabase: FoodItem[] = [ { 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 } }, ]; diff --git a/src/index.ts b/src/index.ts index af5cdaa..5af75ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -486,12 +486,13 @@ function displaySearchResults(results: FoodItem[]) { searchResults.innerHTML = '
No foods found. Try a different search term.
'; } else { const icons = { - 'fats': '🥑', - 'proteins': '🫘', - 'carbs (high)': '🍞', - 'leaves': '🥬', - 'fruits': '🍊', - 'carbs (low)': '🍍' + 'fats': '🥑 🍳 🍟', + 'proteins': '🫘 🥩 🥚', + 'carbs (high)': '🍞 🥔 🍠', + 'leaves': '🥬 🥗 🌿', + 'fruits': '🍊 🍇 🍎', + 'carbs (low)': '🥦 🍅 🍓', + 'dairy': '🧀 🧈 🥛' }; searchResults.innerHTML = results.map((food, index) => ` diff --git a/src/types.ts b/src/types.ts index 0722cf0..1fe51b6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -type FoodCategory = 'fats' | 'proteins' | 'carbs (high)' | 'leaves' | 'fruits' | 'carbs (low)'; +type FoodCategory = 'fats' | 'proteins' | 'carbs (high)' | 'leaves' | 'fruits' | 'carbs (low)' | 'dairy'; export type FoodItem = { name: string; From 261528f329dfdd698c6aa24874190ea0c273f0b7 Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Thu, 4 Sep 2025 12:51:43 -0300 Subject: [PATCH 4/4] feat: add editing capability --- index.html | 5 ++ src/appwrite.ts | 20 ++++++++ src/foodDatabase.ts | 6 +-- src/index.ts | 112 ++++++++++++++++++++++++++++++++++++++++---- style.css | 20 +++++++- 5 files changed, 150 insertions(+), 13 deletions(-) diff --git a/index.html b/index.html index 197923d..f3d4dd3 100644 --- a/index.html +++ b/index.html @@ -149,6 +149,10 @@
+ + + +
@@ -156,6 +160,7 @@
+