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 = `
-
`;
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 {