+9
-24
@@ -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">
|
||||
@@ -183,30 +188,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="food-table-section">
|
||||
<div class="table-title">Food Log</div>
|
||||
<div class="table-wrapper">
|
||||
<table class="food-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="hidden-column">ID</th>
|
||||
<th>Time</th>
|
||||
<th>Food</th>
|
||||
<th>Grams</th>
|
||||
<th>Calories</th>
|
||||
<th>Protein (g)</th>
|
||||
<th>Fat (g)</th>
|
||||
<th>Carbs (g)</th>
|
||||
<th>Fiber (g)</th>
|
||||
<th>Action</th>
|
||||
<th class="hidden-column">Alkaline</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="foodTableBody">
|
||||
<!-- Food entries will be added here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="section-title">Food Log</div>
|
||||
|
||||
<div class="food-cards-container" id="foodCardsContainer">
|
||||
<!-- Food cards will be added here -->
|
||||
</div>
|
||||
|
||||
<div class="calendar-section">
|
||||
|
||||
Generated
+8
-8
@@ -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": {
|
||||
|
||||
+2
-2
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
VITE_APPWRITE_ENDPOINT=here
|
||||
VITE_APPWRITE_DBID=here
|
||||
VITE_APPWRITE_FOODENTRIESID=here
|
||||
VITE_APPWRITE_USERSETTINGSID=here
|
||||
@@ -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 {
|
||||
|
||||
+42
-12
@@ -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,22 @@ 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 } },
|
||||
{ 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 } },
|
||||
@@ -89,5 +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: '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 } },
|
||||
];
|
||||
|
||||
+268
-52
@@ -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;
|
||||
@@ -281,13 +285,31 @@ 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('foodTableBody').innerHTML = '';
|
||||
getDivById('caloriesCounter').textContent = '0';
|
||||
getInputById('foodSearchInput').value = '';
|
||||
getInputById('gramAmount').value = '100';
|
||||
getDivById('foodCardsContainer').innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">🍽️</div>
|
||||
<div>No food items logged yet.</div>
|
||||
<div style="margin-top: 8px; font-size: 14px; opacity: 0.7;">Add your first item to get started!</div>
|
||||
</div>
|
||||
`;
|
||||
selectedFood = null;
|
||||
totalCaloriesToday = 0;
|
||||
totalGramsToday = 0;
|
||||
totalAlkalineToday = 0;
|
||||
getButtonById('cancel-food-btn').style.display = 'none';
|
||||
}
|
||||
|
||||
const getFoodData = (grams: number, foodData: FoodItem): FoodItem => {
|
||||
@@ -309,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];
|
||||
}
|
||||
@@ -353,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', () => {
|
||||
@@ -375,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);
|
||||
@@ -473,12 +503,13 @@ function displaySearchResults(results: FoodItem[]) {
|
||||
searchResults.innerHTML = '<div class="no-results">No foods found. Try a different search term.</div>';
|
||||
} 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) => `
|
||||
@@ -531,23 +562,101 @@ 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');
|
||||
currentHighlightIndex = -1;
|
||||
}
|
||||
|
||||
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, row);
|
||||
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) {
|
||||
setFoodToEdit(foodId);
|
||||
}
|
||||
}
|
||||
|
||||
const addDeleteEvents = () => {
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -562,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) {
|
||||
@@ -572,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;
|
||||
}
|
||||
|
||||
@@ -611,7 +770,7 @@ const addFood = async () => {
|
||||
addFoodToTable(entry, savedEntry.$id);
|
||||
|
||||
// Update totals
|
||||
updateTotalCalories();
|
||||
updateTotalCalories([savedEntry]);
|
||||
|
||||
// Update total macros
|
||||
const proteinDiv: HTMLElement = getDivById('proteinValue');
|
||||
@@ -655,7 +814,7 @@ 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
|
||||
entries.forEach(entry => {
|
||||
@@ -676,11 +835,21 @@ async function loadFoodEntries(date: Date) {
|
||||
addFoodToTable(foodData, entry.$id);
|
||||
});
|
||||
|
||||
if (entries.length === 0) {
|
||||
getDivById('foodCardsContainer').innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">🍽️</div>
|
||||
<div>No food items logged yet.</div>
|
||||
<div style="margin-top: 8px; font-size: 14px; opacity: 0.7;">Add your first item to get started!</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Setup Delete events
|
||||
addDeleteEvents();
|
||||
|
||||
// Update total calories
|
||||
updateTotalCalories();
|
||||
updateTotalCalories(entries);
|
||||
|
||||
// Update counters
|
||||
let totalCalories = 0;
|
||||
@@ -751,7 +920,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({
|
||||
@@ -772,7 +941,6 @@ async function handleDeleteFood(documentId: string, row: HTMLTableRowElement) {
|
||||
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');
|
||||
@@ -783,55 +951,87 @@ async function handleDeleteFood(documentId: string, row: HTMLTableRowElement) {
|
||||
|
||||
// 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');
|
||||
// New Food card
|
||||
const container = getDivById('foodCardsContainer');
|
||||
const card = document.createElement('div');
|
||||
card.className = 'food-card';
|
||||
card.setAttribute('data-id', documentId);
|
||||
|
||||
row.innerHTML = `
|
||||
<td class="hidden-column">${documentId}</td>
|
||||
<td>${foodData.time || new Date().toLocaleTimeString()}</td>
|
||||
<td>${foodData.name}</td>
|
||||
<td>${foodData.grams}</td>
|
||||
<td>${foodData.calories}</td>
|
||||
<td>${foodData.protein}</td>
|
||||
<td>${foodData.fat}</td>
|
||||
<td>${foodData.carbs}</td>
|
||||
<td>${foodData.fiber}</td>
|
||||
<td><button class="delete-btn delete-food-entry">Delete</button></td>
|
||||
<td class="hidden-column">${foodData.alkaline}</td>
|
||||
if (container.innerHTML.includes('empty-state')) {
|
||||
container.innerHTML = '';
|
||||
}
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="card-header card-header-toggle">
|
||||
<div class="card-main-info">
|
||||
<div class="food-name-time">
|
||||
<div class="food-name">${foodData.name}</div>
|
||||
<div class="food-time">${foodData.time || new Date().toLocaleTimeString()} • ${foodData.grams}g</div>
|
||||
</div>
|
||||
<div class="calories-display">${foodData.calories} cal</div>
|
||||
</div>
|
||||
<div class="expand-icon">▼</div>
|
||||
</div>
|
||||
<div class="card-details">
|
||||
<div class="details-content">
|
||||
<div class="nutrition-grid">
|
||||
<div class="nutrition-item">
|
||||
<div class="nutrition-label">Protein</div>
|
||||
<div class="nutrition-value">${foodData.protein}g</div>
|
||||
</div>
|
||||
<div class="nutrition-item">
|
||||
<div class="nutrition-label">Fat</div>
|
||||
<div class="nutrition-value">${foodData.fat}g</div>
|
||||
</div>
|
||||
<div class="nutrition-item">
|
||||
<div class="nutrition-label">Carbs</div>
|
||||
<div class="nutrition-value">${foodData.carbs}g</div>
|
||||
</div>
|
||||
<div class="nutrition-item">
|
||||
<div class="nutrition-label">Fiber</div>
|
||||
<div class="nutrition-value">${foodData.fiber}g</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-edit" data-food-id="${documentId}">Edit</button>
|
||||
<button class="btn btn-delete" data-food-id="${documentId}">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
tableBody?.appendChild(row);
|
||||
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;
|
||||
function updateTotalCalories(entries: Models.DefaultDocument[]) {
|
||||
let total = totalCaloriesToday;
|
||||
|
||||
caloriesCells.forEach((cell: Element) => {
|
||||
total += parseInt(cell.innerHTML) || 0;
|
||||
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;
|
||||
@@ -858,6 +1058,9 @@ const nextMonth = () => {
|
||||
|
||||
const selectDate = async (date: Date) => {
|
||||
selectedDate = date;
|
||||
totalCaloriesToday = 0;
|
||||
totalGramsToday = 0;
|
||||
totalAlkalineToday = 0;
|
||||
updateCurrentDate();
|
||||
await loadFoodEntries(date);
|
||||
renderCalendar();
|
||||
@@ -970,4 +1173,17 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont
|
||||
return dayElement;
|
||||
}
|
||||
|
||||
function toggleCard(id: string) {
|
||||
const card = document.querySelector(`[data-id="${id}"]`);
|
||||
card?.classList.toggle('expanded');
|
||||
}
|
||||
|
||||
function deleteFood(id: string) {
|
||||
handleDeleteFood(id);
|
||||
}
|
||||
|
||||
function editFood(id: string) {
|
||||
alert(`Edit functionality for food item ${id} would be implemented here`);
|
||||
}
|
||||
|
||||
(window as any).selectFood = selectFood;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -167,11 +167,204 @@ 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;
|
||||
}
|
||||
|
||||
.card-header .food-name {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.card-details {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.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 {
|
||||
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;
|
||||
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 {
|
||||
@@ -209,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;
|
||||
}
|
||||
@@ -274,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;
|
||||
@@ -341,6 +552,7 @@ label {
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
@@ -522,4 +734,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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user