feat: add select item and improve settings
This commit is contained in:
@@ -2,9 +2,9 @@
|
|||||||
.auth-header {
|
.auth-header {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 15px 0;
|
padding: 12px 0;
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
position: sticky;
|
border-radius: 6px;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
@@ -12,15 +12,16 @@
|
|||||||
.auth-container {
|
.auth-container {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 20px;
|
padding: 0 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
min-height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo h2 {
|
.logo h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.5rem;
|
font-size: 1.3rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
.auth-form {
|
.auth-form {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
border-radius: 10px;
|
border-radius: 6px;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
@@ -110,28 +111,28 @@
|
|||||||
.user-info {
|
.user-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-details {
|
.user-details {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-details span {
|
.user-details span {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 1rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout-btn {
|
.logout-btn {
|
||||||
padding: 8px 16px;
|
padding: 6px 12px;
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
color: white;
|
color: white;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.9rem;
|
font-size: 0.8rem;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
@@ -198,16 +199,23 @@
|
|||||||
|
|
||||||
/* Mobile Responsive for Auth */
|
/* Mobile Responsive for Auth */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
.auth-header {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-container {
|
.auth-container {
|
||||||
flex-direction: column;
|
min-height: 45px;
|
||||||
gap: 15px;
|
padding: 0 12px;
|
||||||
padding: 0 15px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
order: 1;
|
order: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo h2 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-section,
|
.auth-section,
|
||||||
.user-info {
|
.user-info {
|
||||||
order: 2;
|
order: 2;
|
||||||
@@ -222,8 +230,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-details {
|
.user-details {
|
||||||
flex-direction: column;
|
gap: 8px;
|
||||||
gap: 10px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-9
@@ -78,8 +78,6 @@
|
|||||||
<!-- Main App Content -->
|
<!-- Main App Content -->
|
||||||
<main id="app-content" class="hidden">
|
<main id="app-content" class="hidden">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>🍎 Daily Calorie Tracker</h1>
|
|
||||||
|
|
||||||
<div class="date-display" id="currentDate"></div>
|
<div class="date-display" id="currentDate"></div>
|
||||||
|
|
||||||
<div class="calories-banner">
|
<div class="calories-banner">
|
||||||
@@ -89,35 +87,50 @@
|
|||||||
|
|
||||||
<div class="nutrition-summary" id="nutritionSummary">
|
<div class="nutrition-summary" id="nutritionSummary">
|
||||||
<div class="nutrition-item">
|
<div class="nutrition-item">
|
||||||
|
<div class="nutrition-label">Protein (g)</div>
|
||||||
<div class="nutrition-value" id="proteinValue">0</div>
|
<div class="nutrition-value" id="proteinValue">0</div>
|
||||||
<div class="nutrition-label hidden" id="proteinGoalText">of 190</div>
|
<div class="nutrition-label hidden" id="proteinGoalText">of 190</div>
|
||||||
<div class="nutrition-label">Protein (g)</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="nutrition-item">
|
<div class="nutrition-item">
|
||||||
|
<div class="nutrition-label">Fat (g)</div>
|
||||||
<div class="nutrition-value" id="fatValue">0</div>
|
<div class="nutrition-value" id="fatValue">0</div>
|
||||||
<div class="nutrition-label hidden" id="fatGoalText">of 190</div>
|
<div class="nutrition-label hidden" id="fatGoalText">of 190</div>
|
||||||
<div class="nutrition-label">Fat (g)</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="nutrition-item">
|
<div class="nutrition-item">
|
||||||
|
<div class="nutrition-label">Carbs (g)</div>
|
||||||
<div class="nutrition-value" id="carboValue">0</div>
|
<div class="nutrition-value" id="carboValue">0</div>
|
||||||
<div class="nutrition-label hidden" id="carboGoalText">of 190</div>
|
<div class="nutrition-label hidden" id="carboGoalText">of 190</div>
|
||||||
<div class="nutrition-label">Carbs (g)</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="nutrition-item">
|
<div class="nutrition-item">
|
||||||
|
<div class="nutrition-label">Fiber (g)</div>
|
||||||
<div class="nutrition-value" id="fiberValue">0</div>
|
<div class="nutrition-value" id="fiberValue">0</div>
|
||||||
<div class="nutrition-label hidden" id="fiberGoalText">of 190</div>
|
<div class="nutrition-label hidden" id="fiberGoalText">of 190</div>
|
||||||
<div class="nutrition-label">Fiber (g)</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Food Search Input -->
|
||||||
|
|
||||||
|
|
||||||
<div class="add-food-section">
|
<div class="add-food-section">
|
||||||
<h2 class="add-food-title">Add Food</h2>
|
<h2 class="add-food-title">Add Food</h2>
|
||||||
<div class="add-food-form">
|
<div class="add-food-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="foodSelect">Food Item</label>
|
<label for="foodSelect">Food Item</label>
|
||||||
<select id="foodSelect">
|
<div class="food-search-container">
|
||||||
<option value="">Select a food item...</option>
|
<input
|
||||||
</select>
|
type="text"
|
||||||
|
class="food-search-input"
|
||||||
|
id="foodSearchInput"
|
||||||
|
placeholder="Search food (e.g., apple, chicken breast, rice...)"
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
<div class="search-loading" id="searchLoading"></div>
|
||||||
|
|
||||||
|
<!-- Search Results Overlay -->
|
||||||
|
<div class="search-results" id="searchResults">
|
||||||
|
<!-- Results will be populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="gramAmount">Amount (grams)</label>
|
<label for="gramAmount">Amount (grams)</label>
|
||||||
@@ -198,15 +211,19 @@
|
|||||||
|
|
||||||
<form id="settingsForm" class="settings-form">
|
<form id="settingsForm" class="settings-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<label for="proteinGoal">Protein goal</label>
|
||||||
<input type="number" id="proteinGoal" placeholder="Proteins in grams" required>
|
<input type="number" id="proteinGoal" placeholder="Proteins in grams" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<label for="proteinGoal">Fat goal</label>
|
||||||
<input type="number" id="fatGoal" placeholder="Fat in grams" required>
|
<input type="number" id="fatGoal" placeholder="Fat in grams" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<label for="proteinGoal">Carbs goal</label>
|
||||||
<input type="number" id="carboGoal" placeholder="Carbo in grams" required>
|
<input type="number" id="carboGoal" placeholder="Carbo in grams" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<label for="proteinGoal">Fiber goal</label>
|
||||||
<input type="number" id="fiberGoal" placeholder="Fiber in grams" required>
|
<input type="number" id="fiberGoal" placeholder="Fiber in grams" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="settings-btn">Save</button>
|
<button type="submit" class="settings-btn">Save</button>
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ export class AppwriteDB {
|
|||||||
{
|
{
|
||||||
...entryData,
|
...entryData,
|
||||||
userId: (await account.get()).$id,
|
userId: (await account.get()).$id,
|
||||||
createdAt: new Date().toISOString()
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.debug('Food entry saved:', response);
|
console.debug('Food entry saved:', response);
|
||||||
|
|||||||
+72
-68
@@ -1,79 +1,83 @@
|
|||||||
import { FoodItem } from "./types";
|
import { FoodItem } from "./types";
|
||||||
|
|
||||||
export const foodDatabase: FoodItem[] = [
|
export const foodDatabase: FoodItem[] = [
|
||||||
{ name: 'Frutas - Abacate', info: { calories: 96, protein: 1.2, fat: 8.4, carbs: 6, fiber: 6.3 } },
|
// Gorduras - Fats
|
||||||
{ name: 'Frutas - Abacaxi', info: { calories: 48, protein: 0.9, fat: 0.1, carbs: 12.3, fiber: 1 } },
|
{ name: 'Abacate', info: { calories: 96, protein: 1.2, fat: 8.4, carbs: 6, fiber: 6.3, category: 'fats' } },
|
||||||
{ name: 'Frutas - Banana Nanica', info: { calories: 92, protein: 1.4, fat: 0.1, carbs: 23.8, fiber: 1.9 } },
|
{ name: 'Amêndoas', info: { calories: 581, protein: 18.6, fat: 47.3, carbs: 29.5, fiber: 11.6, category: 'fats' } },
|
||||||
{ name: 'Frutas - Laranja', info: { calories: 37, protein: 1, fat: 0.1, carbs: 8.9, fiber: 0.8 } },
|
{ name: 'Amendoin natural', info: { calories: 611, protein: 26, fat: 49, carbs: 16, fiber: 8.5, category: 'fats' } },
|
||||||
{ name: 'Frutas - Maçã', info: { calories: 56, protein: 0.3, fat: 0, carbs: 15.2, fiber: 1.3 } },
|
{ name: 'Azeite de Oliva EV', info: { calories: 844, protein: 0, fat: 100, carbs: 0, fiber: 0, category: 'fats' } },
|
||||||
{ name: 'Frutas - Manga Palmer', info: { calories: 72, protein: 0.4, fat: 0.2, carbs: 19.4, fiber: 1.6 } },
|
{ name: 'Castanha de Caju', info: { calories: 570, protein: 18.5, fat: 46.3, carbs: 29.1, fiber: 3.7, category: 'fats' } },
|
||||||
{ name: 'Frutas - Mamão', info: { calories: 40, protein: 0.5, fat: 0.1, carbs: 10.4, fiber: 1 } },
|
{ name: 'Castanha do Pará', info: { calories: 643, protein: 14.5, fat: 63.5, carbs: 15.1, fiber: 7.9, category: 'fats' } },
|
||||||
{ name: 'Frutas - Melancia', info: { calories: 33, protein: 0.9, fat: 0, carbs: 8.1, fiber: 0.1 } },
|
{ name: 'Semente de Chia', info: { calories: 490, protein: 15.6, fat: 30.7, carbs: 43.8, fiber: 37.7, category: 'fats' } },
|
||||||
{ name: 'Frutas - Melão', info: { calories: 29, protein: 0.7, fat: 0, carbs: 7.5, fiber: 0.3 } },
|
{ name: 'Semente de Linhaça', info: { calories: 495, protein: 14.1, fat: 32.3, carbs: 43.3, fiber: 33.5, category: 'fats' } },
|
||||||
{ name: 'Frutas - Uva', info: { calories: 53, protein: 0.7, fat: 0.2, carbs: 13.6, fiber: 0.9 } },
|
{ name: 'Semente de Gergelim', info: { calories: 584, protein: 21.2, fat: 50.4, carbs: 21.6, fiber: 11.9, category: 'fats' } },
|
||||||
|
{ name: 'Pasta de Amendoim Growth', info: { calories: 544, protein: 27, fat: 33, carbs: 20, fiber: 8, category: 'fats' } },
|
||||||
|
|
||||||
{ name: 'Verduras - Agrião', info: { calories: 17, protein: 2.7, fat: 0.2, carbs: 2.3, fiber: 2.1 } },
|
// Proteínas - Proteins
|
||||||
{ name: 'Verduras - Espinafre', info: { calories: 16, protein: 2, fat: 0.2, carbs: 2.6, fiber: 2.1 } },
|
{ name: 'Atum Sólido, natural', info: { calories: 135, protein: 28.3, fat: 2.7, carbs: 0, fiber: 0, category: 'proteins' } },
|
||||||
{ name: 'Verduras - Rúcula', info: { calories: 13, protein: 1.8, fat: 0.1, carbs: 2.2, fiber: 1.7 } },
|
{ name: 'Filé de Tilápia, cozido', info: { calories: 128, protein: 26.1, fat: 2.6, carbs: 0, fiber: 0, category: 'proteins' } },
|
||||||
|
{ name: 'Ovo inteiro, cozido', info: { calories: 146, protein: 13.3, fat: 9.5, carbs: 0.6, fiber: 0, category: 'proteins' } },
|
||||||
|
{ name: 'Ovo clara, cozido', info: { calories: 59, protein: 13.4, fat: 0.1, carbs: 0, fiber: 0, category: 'proteins' } },
|
||||||
|
{ name: 'PTS, crua', info: { calories: 288, protein: 52, fat: 0, carbs: 20, fiber: 14, category: 'proteins' } },
|
||||||
|
{ name: 'Patinho moído, grelhado', info: { calories: 219, protein: 35.9, fat: 7.3, carbs: 0, fiber: 0, category: 'proteins' } },
|
||||||
|
{ name: 'Soja em Pó Growth', info: { calories: 400, protein: 86.7, fat: 5, carbs: 3.3, fiber: 0, category: 'proteins' } },
|
||||||
|
{ name: 'Sobrecoxa s/pele, cozida', info: { calories: 245, protein: 24.9, fat: 15.4, carbs: 0, fiber: 0, category: 'proteins' } },
|
||||||
|
{ name: 'Tofu', info: { calories: 65, protein: 6.5, fat: 4, carbs: 2, fiber: 0.75, category: 'proteins' } },
|
||||||
|
{ name: 'Feijão preto, cozido', info: { calories: 77, protein: 4.5, fat: 0.5, carbs: 14, fiber: 8.4, category: 'proteins' } },
|
||||||
|
{ name: 'Lentilha, cozida', info: { calories: 93, protein: 6.3, fat: 0.5, carbs: 16.3, fiber: 7.9, category: 'proteins' } },
|
||||||
|
{ name: 'Grão de Bico, cozido', info: { calories: 180, protein: 9.5, fat: 3, carbs: 30, fiber: 8.6, category: 'proteins' } },
|
||||||
|
|
||||||
{ name: 'Legumes - Abóbora Cabotian (crua)', info: { calories: 39, protein: 1.7, fat: 0.5, carbs: 8.4, fiber: 2.2 } },
|
// Carboidratos, amiláceos - Carbs
|
||||||
{ name: 'Legumes - Abóbora Cabotian (cozida)', info: { calories: 48, protein: 1.4, fat: 0.7, carbs: 10.8, fiber: 2.5 } },
|
{ name: 'Banana Nanica', info: { calories: 92, protein: 1.4, fat: 0.1, carbs: 23.8, fiber: 1.9, category: 'carbs (high)' } },
|
||||||
{ name: 'Legumes - Brócolis (cozido)', info: { calories: 25, protein: 2.1, fat: 0.5, carbs: 4.4, fiber: 3.4 } },
|
{ name: 'Manga Palmer', info: { calories: 72, protein: 0.4, fat: 0.2, carbs: 19.4, fiber: 1.6, category: 'carbs (high)' } },
|
||||||
{ name: 'Legumes - Cenoura (cozida)', info: { calories: 30, protein: 0.8, fat: 0.2, carbs: 6.7, fiber: 2.6 } },
|
{ name: 'Mamão', info: { calories: 40, protein: 0.5, fat: 0.1, carbs: 10.4, fiber: 1, category: 'carbs (high)' } },
|
||||||
{ name: 'Legumes - Couve flor (cozida)', info: { calories: 19, protein: 1.2, fat: 0.3, carbs: 3.9, fiber: 2.1 } },
|
{ name: 'Batata doce, assada', info: { calories: 90, protein: 2.0, fat: 0.1, carbs: 20.7, fiber: 3.3, category: 'carbs (high)' } },
|
||||||
{ name: 'Legumes - Pepino', info: { calories: 10, protein: 0.9, fat: 0, carbs: 2, fiber: 1.1 } },
|
{ name: 'Batata doce, cozida', info: { calories: 77, protein: 0.6, fat: 0.1, carbs: 18.4, fiber: 2.2, category: 'carbs (high)' } },
|
||||||
{ name: 'Legumes - Tomate', info: { calories: 15, protein: 1.1, fat: 0.2, carbs: 3.1, fiber: 1.2 } },
|
{ name: 'Batata doce, crua', info: { calories: 118, protein: 1.3, fat: 0.1, carbs: 28.2, fiber: 2.6, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Batata inglesa, assada', info: { calories: 94, protein: 2.1, fat: 0.1, carbs: 21.8, fiber: 2.1, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Batata inglesa, cozida', info: { calories: 52, protein: 1.2, fat: 0, carbs: 11.9, fiber: 1.3, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Batata inglesa, crua', info: { calories: 64, protein: 1.8, fat: 0, carbs: 14.7, fiber: 1.2, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Mandioca, cozida', info: { calories: 125, protein: 0.6, fat: 0.3, carbs: 30.1, fiber: 1.6, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Pão Francês', info: { calories: 300, protein: 8, fat: 3.1, carbs: 58.6, fiber: 2.3, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Polenta, cozida', info: { calories: 192, protein: 4.8, fat: 1.2, carbs: 40, fiber: 3.8, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Arroz integral, cozido', info: { calories: 130, protein: 2.7, fat: 0.3, carbs: 28, fiber: 0.4, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Aveia', info: { calories: 394, protein: 13.9, fat: 8.5, carbs: 66.6, fiber: 9.1, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Ervilha, cozida', info: { calories: 88, protein: 7.5, fat: 0.5, carbs: 14.2, fiber: 9.7, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Pão integral de forma', info: { calories: 253, protein: 9.4, fat: 3.7, carbs: 49.9, fiber: 6.9, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Pipoca c/óleo', info: { calories: 448, protein: 9.9, fat: 15.9, carbs: 70.3, fiber: 14.3, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Pipoca s/óleo', info: { calories: 178, protein: 3.3, fat: 7.2, carbs: 25, fiber: 4.3, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Quinoa, cozida', info: { calories: 158, protein: 5.55, fat: 2.5, carbs: 29, fiber: 2.5, category: 'carbs (high)' } },
|
||||||
|
{ name: '70% Nibs Garoto', info: { calories: 544, protein: 8.4, fat: 40, carbs: 34, fiber: 0, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Barra de Proteína Vegana Growth', info: { calories: 400, protein: 25, fat: 22, carbs: 25, fiber: 8, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Goiabada', info: { calories: 320, protein: 0, fat: 0, carbs: 80, fiber: 4.5, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Goma de Tapioca', info: { calories: 226, protein: 0, fat: 0, carbs: 57, fiber: 0, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Mel Silvestre', info: { calories: 230, protein: 0, fat: 0, carbs: 65, fiber: 0, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Melado', info: { calories: 297, protein: 0, fat: 0, carbs: 76.6, fiber: 0, category: 'carbs (high)' } },
|
||||||
|
{ name: 'Tâmara desidratada', info: { calories: 230, protein: 0.3, fat: 33, carbs: 63.3, fiber: 7, category: 'carbs (high)' } },
|
||||||
|
|
||||||
{ name: 'Algas - Spirulina', info: { calories: 290, protein: 57.5, fat: 23.9, carbs: 7.7, fiber: 0 } },
|
// Folhas e verduras - Leaves
|
||||||
|
{ name: 'Agrião', info: { calories: 17, protein: 2.7, fat: 0.2, carbs: 2.3, fiber: 2.1, category: 'leaves' } },
|
||||||
|
{ name: 'Espinafre', info: { calories: 16, protein: 2, fat: 0.2, carbs: 2.6, fiber: 2.1, category: 'leaves' } },
|
||||||
|
{ name: 'Rúcula', info: { calories: 13, protein: 1.8, fat: 0.1, carbs: 2.2, fiber: 1.7, category: 'leaves' } },
|
||||||
|
{ name: 'Spirulina', info: { calories: 290, protein: 57.5, fat: 23.9, carbs: 7.7, fiber: 0, category: 'leaves' } },
|
||||||
|
|
||||||
{ name: 'Carboidratos - Batata doce (assada)', info: { calories: 90, protein: 2.0, fat: 0.1, carbs: 20.7, fiber: 3.3 } },
|
// Frutas - Fruits
|
||||||
{ name: 'Carboidratos - Batata doce (cozida)', info: { calories: 77, protein: 0.6, fat: 0.1, carbs: 18.4, fiber: 2.2 } },
|
{ name: 'Abacaxi', info: { calories: 48, protein: 0.9, fat: 0.1, carbs: 12.3, fiber: 1, category: 'fruits' } },
|
||||||
{ name: 'Carboidratos - Batata doce (crua)', info: { calories: 118, protein: 1.3, fat: 0.1, carbs: 28.2, fiber: 2.6 } },
|
{ name: 'Laranja', info: { calories: 37, protein: 1, fat: 0.1, carbs: 8.9, fiber: 0.8, category: 'fruits' } },
|
||||||
{ name: 'Carboidratos - Batata inglesa (assada)', info: { calories: 94, protein: 2.1, fat: 0.1, carbs: 21.8, fiber: 2.1 } },
|
{ name: 'Maçã', info: { calories: 56, protein: 0.3, fat: 0, carbs: 15.2, fiber: 1.3, category: 'fruits' } },
|
||||||
{ name: 'Carboidratos - Batata inglesa (cozida)', info: { calories: 52, protein: 1.2, fat: 0, carbs: 11.9, fiber: 1.3 } },
|
{ name: 'Melancia', info: { calories: 33, protein: 0.9, fat: 0, carbs: 8.1, fiber: 0.1, category: 'fruits' } },
|
||||||
{ name: 'Carboidratos - Batata inglesa (crua)', info: { calories: 64, protein: 1.8, fat: 0, carbs: 14.7, fiber: 1.2 } },
|
{ name: 'Melão', info: { calories: 29, protein: 0.7, fat: 0, carbs: 7.5, fiber: 0.3, category: 'fruits' } },
|
||||||
{ name: 'Carboidratos - Mandioca (cozida)', info: { calories: 125, protein: 0.6, fat: 0.3, carbs: 30.1, fiber: 1.6 } },
|
{ name: 'Uva', info: { calories: 53, protein: 0.7, fat: 0.2, carbs: 13.6, fiber: 0.9, category: 'fruits' } },
|
||||||
{ name: 'Carboidratos - Pão Francês', info: { calories: 300, protein: 8, fat: 3.1, carbs: 58.6, fiber: 2.3 } },
|
|
||||||
{ name: 'Carboidrados - Polenta (cozida)', info: { calories: 192, protein: 4.8, fat: 1.2, carbs: 40, fiber: 3.8 } },
|
|
||||||
|
|
||||||
{ name: 'Proteínas - Atum Sólido (natural)', info: { calories: 135, protein: 28.3, fat: 2.7, carbs: 0, fiber: 0 } },
|
// Low carb
|
||||||
{ name: 'Proteínas - Filé de Tilápia (cozido)', info: { calories: 128, protein: 26.1, fat: 2.6, carbs: 0, fiber: 0 } },
|
{ name: 'Abóbora Cabotian, crua', info: { calories: 39, protein: 1.7, fat: 0.5, carbs: 8.4, fiber: 2.2, category: 'carbs (low)' } },
|
||||||
{ name: 'Proteínas - Ovo inteiro (cozido)', info: { calories: 146, protein: 13.3, fat: 9.5, carbs: 0.6, fiber: 0 } },
|
{ name: 'Abóbora Cabotian, cozida', info: { calories: 48, protein: 1.4, fat: 0.7, carbs: 10.8, fiber: 2.5, category: 'carbs (low)' } },
|
||||||
{ name: 'Proteínas - Ovo clara (cozido)', info: { calories: 59, protein: 13.4, fat: 0.1, carbs: 0, fiber: 0 } },
|
{ name: 'Cenoura, cozida', info: { calories: 30, protein: 0.8, fat: 0.2, carbs: 6.7, fiber: 2.6, category: 'carbs (low)' } },
|
||||||
{ name: 'Proteínas - PTS (crua)', info: { calories: 288, protein: 52, fat: 0, carbs: 20, fiber: 14 } },
|
{ name: 'Tomate', info: { calories: 15, protein: 1.1, fat: 0.2, carbs: 3.1, fiber: 1.2, category: 'carbs (low)' } },
|
||||||
{ name: 'Proteínas - Patinho moído (grelhado)', info: { calories: 219, protein: 35.9, fat: 7.3, carbs: 0, fiber: 0 } },
|
{ name: 'Brócolis, cozido', info: { calories: 25, protein: 2.1, fat: 0.5, carbs: 4.4, fiber: 3.4, category: 'carbs (low)' } },
|
||||||
{ name: 'Proteínas - Soja em Pó Growth', info: { calories: 400, protein: 86.7, fat: 5, carbs: 3.3, fiber: 0 } },
|
{ name: 'Couve flor, cozida', info: { calories: 19, protein: 1.2, fat: 0.3, carbs: 3.9, fiber: 2.1, category: 'carbs (low)' } },
|
||||||
{ name: 'Proteínas - Sobrecoxa s/pele (cozida)', info: { calories: 245, protein: 24.9, fat: 15.4, carbs: 0, fiber: 0 } },
|
{ name: 'Pepino', info: { calories: 10, protein: 0.9, fat: 0, carbs: 2, fiber: 1.1, category: 'carbs (low)' } },
|
||||||
{ name: 'Proteínas - Tofu', info: { calories: 65, protein: 6.5, fat: 4, carbs: 2, fiber: 0.75 } },
|
{ name: 'Chuchu, cozido', info: { calories: 19, protein: 0.4, fat: 0, carbs: 4.9, fiber: 1.0, category: 'carbs (low)' } },
|
||||||
|
|
||||||
{ name: 'Grãos - Arroz integral (cozido)', info: { calories: 130, protein: 2.7, fat: 0.3, carbs: 28, fiber: 0.4 } },
|
|
||||||
{ name: 'Grãos - Aveia', info: { calories: 394, protein: 13.9, fat: 8.5, carbs: 66.6, fiber: 9.1 } },
|
|
||||||
{ name: 'Grãos - Ervilha (cozida)', info: { calories: 88, protein: 7.5, fat: 0.5, carbs: 14.2, fiber: 9.7 } },
|
|
||||||
{ name: 'Grãos - Feijão preto (cozido)', info: { calories: 77, protein: 4.5, fat: 0.5, carbs: 14, fiber: 8.4 } },
|
|
||||||
{ name: 'Grãos - Lentilha (cozida)', info: { calories: 93, protein: 6.3, fat: 0.5, carbs: 16.3, fiber: 7.9 } },
|
|
||||||
{ name: 'Grãos - Grão de Bico (cozido)', info: { calories: 180, protein: 9.5, fat: 3, carbs: 30, fiber: 8.6 } },
|
|
||||||
{ name: 'Grãos - Pão integral de forma', info: { calories: 253, protein: 9.4, fat: 3.7, carbs: 49.9, fiber: 6.9 } },
|
|
||||||
{ name: 'Grãos - Pipoca c/óleo', info: { calories: 448, protein: 9.9, fat: 15.9, carbs: 70.3, fiber: 14.3 } },
|
|
||||||
{ name: 'Grãos - Pipoca s/óleo', info: { calories: 178, protein: 3.3, fat: 7.2, carbs: 25, fiber: 4.3 } },
|
|
||||||
{ name: 'Grãos - Quinoa (cozida)', info: { calories: 158, protein: 5.55, fat: 2.5, carbs: 29, fiber: 2.5 } },
|
|
||||||
|
|
||||||
{ name: 'Castanhas - Amêndoas', info: { calories: 581, protein: 18.6, fat: 47.3, carbs: 29.5, fiber: 11.6 } },
|
|
||||||
{ name: 'Castanhas - Amendoin natural', info: { calories: 611, protein: 26, fat: 49, carbs: 16, fiber: 8.5 } },
|
|
||||||
{ name: 'Castanhas - Castanha de Caju', info: { calories: 570, protein: 18.5, fat: 46.3, carbs: 29.1, fiber: 3.7 } },
|
|
||||||
{ name: 'Castanhas - Castanha do Pará', info: { calories: 643, protein: 14.5, fat: 63.5, carbs: 15.1, fiber: 7.9 } },
|
|
||||||
|
|
||||||
{ name: 'Sementes - Semente de Chia', info: { calories: 490, protein: 15.6, fat: 30.7, carbs: 43.8, fiber: 37.7 } },
|
|
||||||
{ name: 'Sementes - Semente de Linhaça', info: { calories: 495, protein: 14.1, fat: 32.3, carbs: 43.3, fiber: 33.5 } },
|
|
||||||
{ name: 'Sementes - Semente de Gergelim', info: { calories: 584, protein: 21.2, fat: 50.4, carbs: 21.6, fiber: 11.9 } },
|
|
||||||
|
|
||||||
{ name: 'Lanches - 70% Nibs Garoto', info: { calories: 544, protein: 8.4, fat: 40, carbs: 34, fiber: 0 } },
|
|
||||||
{ name: 'Lanches - Barra de Proteína Vegana Growth', info: { calories: 400, protein: 25, fat: 22, carbs: 25, fiber: 8 } },
|
|
||||||
{ name: 'Lanches - Goiabada', info: { calories: 320, protein: 0, fat: 0, carbs: 80, fiber: 4.5 } },
|
|
||||||
{ name: 'Lanches - Goma de Tapioca', info: { calories: 226, protein: 0, fat: 0, carbs: 57, fiber: 0 } },
|
|
||||||
{ name: 'Lanches - Mel Silvestre', info: { calories: 230, protein: 0, fat: 0, carbs: 65, fiber: 0 } },
|
|
||||||
{ name: 'Lanches - Melado', info: { calories: 297, protein: 0, fat: 0, carbs: 76.6, fiber: 0 } },
|
|
||||||
{ name: 'Lanches - Pasta de Amendoim Growth', info: { calories: 544, protein: 27, fat: 33, carbs: 20, fiber: 8 } },
|
|
||||||
{ name: 'Lanches - Tâmara desidratada', info: { calories: 230, protein: 0.3, fat: 33, carbs: 63.3, fiber: 7 } },
|
|
||||||
|
|
||||||
{ name: 'Óleos - Azeite de Oliva EV', info: { calories: 844, protein: 0, fat: 100, carbs: 0, fiber: 0 } }
|
|
||||||
];
|
];
|
||||||
|
|||||||
+171
-31
@@ -7,6 +7,10 @@ import swal from 'sweetalert';
|
|||||||
// App state
|
// App state
|
||||||
let selectedDate = new Date();
|
let selectedDate = new Date();
|
||||||
let currentViewDate = new Date();
|
let currentViewDate = new Date();
|
||||||
|
let searchTimeout: number | null = null;
|
||||||
|
let selectedFood: FoodItem | null = null; // review here
|
||||||
|
let currentHighlightIndex: number = -1;
|
||||||
|
let currentResults: FoodItem[] = [];
|
||||||
|
|
||||||
// Authentication state
|
// Authentication state
|
||||||
let currentUser: any | null = null;
|
let currentUser: any | null = null;
|
||||||
@@ -19,11 +23,13 @@ const settingsContent = document.getElementById('settings-content');
|
|||||||
const loadingOverlay = getDivById('loading-overlay');
|
const loadingOverlay = getDivById('loading-overlay');
|
||||||
const loginForm = document.getElementById('login-form');
|
const loginForm = document.getElementById('login-form');
|
||||||
const registerForm = document.getElementById('register-form');
|
const registerForm = document.getElementById('register-form');
|
||||||
|
const searchInput = getInputById('foodSearchInput');
|
||||||
|
const searchLoading = document.getElementById('searchLoading');
|
||||||
|
const searchResults = getDivById('searchResults');
|
||||||
|
|
||||||
// Initialize when page loads
|
// Initialize when page loads
|
||||||
document.addEventListener('DOMContentLoaded', async function() {
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
await initializeAuth();
|
await initializeAuth();
|
||||||
populateFoodSelect();
|
|
||||||
updateCurrentDate();
|
updateCurrentDate();
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
});
|
});
|
||||||
@@ -223,7 +229,7 @@ async function handleSettings() {
|
|||||||
getInputById('fiberGoal').value = '';
|
getInputById('fiberGoal').value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
getButtonById('settingsBtn').textContent = 'Back to App';
|
getButtonById('settingsBtn').textContent = 'Back';
|
||||||
|
|
||||||
appContent?.classList.add('hidden');
|
appContent?.classList.add('hidden');
|
||||||
settingsContent?.classList.remove('hidden');
|
settingsContent?.classList.remove('hidden');
|
||||||
@@ -275,8 +281,9 @@ function hideLoading() {
|
|||||||
function clearAppData() {
|
function clearAppData() {
|
||||||
getDivById('foodTableBody').innerHTML = '';
|
getDivById('foodTableBody').innerHTML = '';
|
||||||
getDivById('caloriesCounter').textContent = '0';
|
getDivById('caloriesCounter').textContent = '0';
|
||||||
getInputById('foodSelect').value = '';
|
getInputById('foodSearchInput').value = '';
|
||||||
getInputById('gramAmount').value = '100';
|
getInputById('gramAmount').value = '100';
|
||||||
|
selectedFood = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFoodData = (grams: number, foodData: FoodItem): FoodItem => {
|
const getFoodData = (grams: number, foodData: FoodItem): FoodItem => {
|
||||||
@@ -288,7 +295,8 @@ const getFoodData = (grams: number, foodData: FoodItem): FoodItem => {
|
|||||||
protein: Math.round(foodData.info.protein * multiplier * 10) / 10,
|
protein: Math.round(foodData.info.protein * multiplier * 10) / 10,
|
||||||
fat: Math.round(foodData.info.fat * multiplier * 10) / 10,
|
fat: Math.round(foodData.info.fat * multiplier * 10) / 10,
|
||||||
carbs: Math.round(foodData.info.carbs * multiplier * 10) / 10,
|
carbs: Math.round(foodData.info.carbs * multiplier * 10) / 10,
|
||||||
fiber: Math.round(foodData.info.fiber * multiplier * 10) / 10
|
fiber: Math.round(foodData.info.fiber * multiplier * 10) / 10,
|
||||||
|
category: foodData.info.category
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -301,11 +309,11 @@ const getFoodItemByName = (foodName: string): FoodItem => {
|
|||||||
return foodDataSearch[0];
|
return foodDataSearch[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewCalories = () => {
|
const previewCalories = (foodSelected?: FoodItem) => {
|
||||||
const foodSelect = getInputById('foodSelect');
|
const foodToPreview = foodSelected ?? selectedFood;
|
||||||
const gramAmount = getInputById('gramAmount');
|
const gramAmount = getInputById('gramAmount');
|
||||||
|
|
||||||
if (!foodSelect.value) {
|
if (!foodToPreview) {
|
||||||
showFoodPreview(false);
|
showFoodPreview(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -315,7 +323,7 @@ const previewCalories = () => {
|
|||||||
grams = parseFloat(gramAmount.value);
|
grams = parseFloat(gramAmount.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const foodData: FoodItem = getFoodItemByName(foodSelect.value);
|
const foodData: FoodItem = getFoodItemByName(foodToPreview.name);
|
||||||
const proportion = getFoodData(grams, foodData);
|
const proportion = getFoodData(grams, foodData);
|
||||||
|
|
||||||
getDivById('calorieValuePreview').textContent = proportion.info.calories.toString();
|
getDivById('calorieValuePreview').textContent = proportion.info.calories.toString();
|
||||||
@@ -328,8 +336,11 @@ const previewCalories = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setupEventListeners = () => {
|
const setupEventListeners = () => {
|
||||||
getInputById('foodSelect').addEventListener('change', () => {
|
getInputById('foodSearchInput').addEventListener('change', (e: Event) => {
|
||||||
previewCalories();
|
const target = e.target as HTMLInputElement;
|
||||||
|
if (target.value === ''){
|
||||||
|
showFoodPreview(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
getInputById('gramAmount').addEventListener('change', () => {
|
getInputById('gramAmount').addEventListener('change', () => {
|
||||||
@@ -365,6 +376,146 @@ const setupEventListeners = () => {
|
|||||||
document.getElementById('logoutBtn')?.addEventListener('click', handleLogout);
|
document.getElementById('logoutBtn')?.addEventListener('click', handleLogout);
|
||||||
document.getElementById('settingsBtn')?.addEventListener('click', handleSettings);
|
document.getElementById('settingsBtn')?.addEventListener('click', handleSettings);
|
||||||
document.getElementById('settingsForm')?.addEventListener('submit', handleSaveSettings);
|
document.getElementById('settingsForm')?.addEventListener('submit', handleSaveSettings);
|
||||||
|
|
||||||
|
// Search functionality
|
||||||
|
searchInput.addEventListener('input', function(e: Event) {
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
const query = target.value.trim();
|
||||||
|
|
||||||
|
if (query.length === 0) {
|
||||||
|
hideSearchResults();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show loading
|
||||||
|
searchLoading?.classList.add('show');
|
||||||
|
|
||||||
|
// Clear previous timeout
|
||||||
|
if (searchTimeout) {
|
||||||
|
clearTimeout(searchTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounce search
|
||||||
|
searchTimeout = setTimeout(() => {
|
||||||
|
performSearch(query);
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keyboard navigation
|
||||||
|
searchInput?.addEventListener('keydown', function(e) {
|
||||||
|
if (!searchResults?.classList.contains('show')) return;
|
||||||
|
|
||||||
|
switch(e.key) {
|
||||||
|
case 'ArrowDown':
|
||||||
|
e.preventDefault();
|
||||||
|
navigateResults(1);
|
||||||
|
break;
|
||||||
|
case 'ArrowUp':
|
||||||
|
e.preventDefault();
|
||||||
|
navigateResults(-1);
|
||||||
|
break;
|
||||||
|
case 'Enter':
|
||||||
|
e.preventDefault();
|
||||||
|
if (currentHighlightIndex >= 0 && currentResults[currentHighlightIndex]) {
|
||||||
|
selectFood(currentResults[currentHighlightIndex]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Escape':
|
||||||
|
hideSearchResults();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click outside to close
|
||||||
|
document.addEventListener('click', function(e: Event) {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (!searchResults.contains(target) && !searchInput.contains(target)) {
|
||||||
|
hideSearchResults();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform search
|
||||||
|
function performSearch(query: string) {
|
||||||
|
// Simulate API call - replace with your actual database search
|
||||||
|
const results: FoodItem[] = foodDatabase.filter(food =>
|
||||||
|
food.name.toLowerCase().includes(query.toLowerCase()) ||
|
||||||
|
food.info.category.toLowerCase().includes(query.toLowerCase())
|
||||||
|
).slice(0, 5); // Limit to 5 results
|
||||||
|
|
||||||
|
currentResults = results;
|
||||||
|
displaySearchResults(results);
|
||||||
|
searchLoading?.classList.remove('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display search results
|
||||||
|
function displaySearchResults(results: FoodItem[]) {
|
||||||
|
if (results.length === 0) {
|
||||||
|
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)': '🍍'
|
||||||
|
};
|
||||||
|
|
||||||
|
searchResults.innerHTML = results.map((food, index) => `
|
||||||
|
<div class="search-result-item" data-index="${index}" onclick="selectFood(${JSON.stringify(food).replace(/"/g, '"')})">
|
||||||
|
<div class="food-info">
|
||||||
|
<div class="food-name">${icons[food.info.category]} ${food.name}</div>
|
||||||
|
<div class="food-details">${food.info.category} • 100 g</div>
|
||||||
|
</div>
|
||||||
|
<div class="food-calories">${food.info.calories} cal</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
searchResults.classList.add('show');
|
||||||
|
currentHighlightIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate results with keyboard
|
||||||
|
function navigateResults(direction: number) {
|
||||||
|
const items = searchResults.querySelectorAll('.search-result-item');
|
||||||
|
if (items.length === 0) return;
|
||||||
|
|
||||||
|
// Remove current highlight
|
||||||
|
if (currentHighlightIndex >= 0) {
|
||||||
|
items[currentHighlightIndex].classList.remove('highlighted');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate new index
|
||||||
|
currentHighlightIndex += direction;
|
||||||
|
if (currentHighlightIndex < 0) currentHighlightIndex = items.length - 1;
|
||||||
|
if (currentHighlightIndex >= items.length) currentHighlightIndex = 0;
|
||||||
|
|
||||||
|
// Add new highlight
|
||||||
|
items[currentHighlightIndex].classList.add('highlighted');
|
||||||
|
items[currentHighlightIndex].scrollIntoView({ block: 'nearest' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select food
|
||||||
|
function selectFood(food: FoodItem) {
|
||||||
|
selectedFood = food;
|
||||||
|
|
||||||
|
// Clear search and hide results
|
||||||
|
searchInput.value = food.name;
|
||||||
|
hideSearchResults();
|
||||||
|
|
||||||
|
// Focus on quantity input
|
||||||
|
getInputById('gramAmount').focus();
|
||||||
|
|
||||||
|
// Update calories
|
||||||
|
previewCalories(food);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide search results
|
||||||
|
function hideSearchResults() {
|
||||||
|
searchResults.classList.remove('show');
|
||||||
|
currentHighlightIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addDeleteEvents = () => {
|
const addDeleteEvents = () => {
|
||||||
@@ -381,20 +532,6 @@ const addDeleteEvents = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate food select dropdown
|
|
||||||
const populateFoodSelect = () => {
|
|
||||||
const select = getInputById('foodSelect');
|
|
||||||
select.innerHTML = '<option value="">Select a food item...</option>';
|
|
||||||
|
|
||||||
const sortedFoods = foodDatabase.sort((f1, f2) => f1.name.localeCompare(f2.name));
|
|
||||||
sortedFoods.forEach((food: FoodItem) => {
|
|
||||||
const option = document.createElement('option') as HTMLOptionElement;
|
|
||||||
option.value = food.name;
|
|
||||||
option.textContent = food.name;
|
|
||||||
select.appendChild(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update current date display
|
// Update current date display
|
||||||
const updateCurrentDate = () => {
|
const updateCurrentDate = () => {
|
||||||
const date = new Date(selectedDate);
|
const date = new Date(selectedDate);
|
||||||
@@ -413,10 +550,9 @@ const addFood = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const foodSelect = getInputById('foodSelect');
|
|
||||||
const gramAmount = getInputById('gramAmount');
|
const gramAmount = getInputById('gramAmount');
|
||||||
|
|
||||||
if (!foodSelect.value || !gramAmount.value) {
|
if (!selectedFood || !gramAmount.value) {
|
||||||
swal('Hey!', 'Please select a food item and enter amount', 'error');
|
swal('Hey!', 'Please select a food item and enter amount', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -425,19 +561,22 @@ const addFood = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const grams: number = parseFloat(gramAmount.value);
|
const grams: number = parseFloat(gramAmount.value);
|
||||||
const foodData: FoodItem = getFoodItemByName(foodSelect.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
|
// Calculate nutrition for the amount
|
||||||
const proportion: FoodItem = getFoodData(grams, foodData);
|
const proportion: FoodItem = getFoodData(grams, foodData);
|
||||||
const entry: FoodStorage = {
|
const entry: FoodStorage = {
|
||||||
name: foodSelect.value,
|
name: selectedFood.name,
|
||||||
grams: grams,
|
grams: grams,
|
||||||
calories: proportion.info.calories,
|
calories: proportion.info.calories,
|
||||||
protein: proportion.info.protein,
|
protein: proportion.info.protein,
|
||||||
fat: proportion.info.fat,
|
fat: proportion.info.fat,
|
||||||
carbs: proportion.info.carbs,
|
carbs: proportion.info.carbs,
|
||||||
fiber: proportion.info.fiber,
|
fiber: proportion.info.fiber,
|
||||||
date: selectedDate.toISOString().split('T')[0],
|
date: date.split('T')[0],
|
||||||
time: new Date().toLocaleTimeString('en-US', {
|
time: new Date().toLocaleTimeString('en-US', {
|
||||||
hour12: false,
|
hour12: false,
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
@@ -471,7 +610,8 @@ const addFood = async () => {
|
|||||||
fiberDiv.textContent = (Math.round(totalFiber * 10) / 10).toString();
|
fiberDiv.textContent = (Math.round(totalFiber * 10) / 10).toString();
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
foodSelect.value = '';
|
selectedFood = null;
|
||||||
|
getInputById('foodSearchInput').value = '';
|
||||||
gramAmount.value = '100';
|
gramAmount.value = '100';
|
||||||
showFoodPreview(false);
|
showFoodPreview(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -772,4 +912,4 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont
|
|||||||
return dayElement;
|
return dayElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(window as any).selectFood = selectFood;
|
||||||
|
|||||||
+3
-1
@@ -1,6 +1,8 @@
|
|||||||
|
type FoodCategory = 'fats' | 'proteins' | 'carbs (high)' | 'leaves' | 'fruits' | 'carbs (low)';
|
||||||
|
|
||||||
export type FoodItem = {
|
export type FoodItem = {
|
||||||
name: string;
|
name: string;
|
||||||
info: { calories: number, protein: number, fat: number, carbs: number, fiber: number }
|
info: { calories: number, protein: number, fat: number, carbs: number, fiber: number, category: FoodCategory }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FoodStorage = {
|
export type FoodStorage = {
|
||||||
|
|||||||
@@ -5,18 +5,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: #f5f5f5;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 20px;
|
padding: 12px;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
border-radius: 20px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
@@ -34,8 +35,8 @@ h1 {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
background: linear-gradient(135deg, #FF6B6B, #4ECDC4);
|
background: linear-gradient(135deg, #FF6B6B, #4ECDC4);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 60px 40px;
|
padding: 30px 20px;
|
||||||
border-radius: 15px;
|
border-radius: 6px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
box-shadow: 0 10px 30px rgba(255, 107, 107, 0.3);
|
box-shadow: 0 10px 30px rgba(255, 107, 107, 0.3);
|
||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
@@ -59,16 +60,131 @@ h1 {
|
|||||||
|
|
||||||
.date-display {
|
.date-display {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 1.5rem;
|
font-size: 1.1rem;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
color: #4a5568;
|
color: #4a5568;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.food-search-container {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1001; /* Higher than other elements */
|
||||||
|
}
|
||||||
|
|
||||||
|
.food-search-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: white;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.food-search-input:focus {
|
||||||
|
border-color: #667eea;
|
||||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.food-search-input::placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading indicator */
|
||||||
|
.search-loading {
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 30%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-top: 2px solid #667eea;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-loading.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: translateY(-50%) rotate(0deg); }
|
||||||
|
100% { transform: translateY(-50%) rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search Results Overlay */
|
||||||
|
.search-results {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1002;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: none;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-item {
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-item:hover,
|
||||||
|
.search-result-item.highlighted {
|
||||||
|
background-color: #f8f9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.food-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.food-details {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.food-calories {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-results {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.add-food-section {
|
.add-food-section {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
border-radius: 15px;
|
border-radius: 6px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
@@ -85,7 +201,6 @@ h1 {
|
|||||||
grid-template-columns: 2fr 1fr auto;
|
grid-template-columns: 2fr 1fr auto;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
align-items: end;
|
align-items: end;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-none {
|
.display-none {
|
||||||
@@ -108,28 +223,34 @@ label {
|
|||||||
color: #4a5568;
|
color: #4a5568;
|
||||||
}
|
}
|
||||||
|
|
||||||
select, input {
|
#gramAmount {
|
||||||
padding: 12px;
|
|
||||||
border: 2px solid #e2e8f0;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 1rem;
|
|
||||||
transition: border-color 0.3s ease;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: white;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
select:focus, input:focus {
|
#gramAmount:focus {
|
||||||
outline: none;
|
|
||||||
border-color: #667eea;
|
border-color: #667eea;
|
||||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gramAmount::placeholder {
|
||||||
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-btn {
|
.add-btn {
|
||||||
padding: 12px 25px;
|
padding: 17px 25px;
|
||||||
background: linear-gradient(135deg, #4CAF50, #45a049);
|
background: linear-gradient(135deg, #4CAF50, #45a049);
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -144,7 +265,7 @@ select:focus, input:focus {
|
|||||||
|
|
||||||
.food-table-section {
|
.food-table-section {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 15px;
|
border-radius: 6px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -194,7 +315,7 @@ select:focus, input:focus {
|
|||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 5px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
transition: background 0.3s ease;
|
transition: background 0.3s ease;
|
||||||
@@ -206,7 +327,7 @@ select:focus, input:focus {
|
|||||||
|
|
||||||
.calendar-section {
|
.calendar-section {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 15px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -225,7 +346,7 @@ select:focus, input:focus {
|
|||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
transition: background 0.3s ease;
|
transition: background 0.3s ease;
|
||||||
@@ -314,14 +435,14 @@ select:focus, input:focus {
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
border-radius: 10px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nutrition-item {
|
.nutrition-item {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user