first commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
dist/
|
||||
@@ -0,0 +1,70 @@
|
||||
const foodDatabase = {
|
||||
// Fruits
|
||||
'Frutas - Abacate': { calories: 96, protein: 1.2, fat: 8.4, carbs: 6, fiber: 6.3 },
|
||||
'Frutas - Abacaxi': { calories: 48, protein: 0.9, fat: 0.1, carbs: 12.3, fiber: 1 },
|
||||
'Frutas - Banana Nanica': { calories: 92, protein: 1.4, fat: 0.1, carbs: 23.8, fiber: 1.9 },
|
||||
'Frutas - Laranja': { calories: 37, protein: 1, fat: 0.1, carbs: 8.9, fiber: 0.8 },
|
||||
'Frutas - Maçã': { calories: 56, protein: 0.3, fat: 0, carbs: 15.2, fiber: 1.3 },
|
||||
'Frutas - Manga Palmer': { calories: 72, protein: 0.4, fat: 0.2, carbs: 19.4, fiber: 1.6 },
|
||||
'Frutas - Mamão': { calories: 40, protein: 0.5, fat: 0.1, carbs: 10.4, fiber: 1 },
|
||||
'Frutas - Melancia': { calories: 33, protein: 0.9, fat: 0, carbs: 8.1, fiber: 0.1 },
|
||||
'Frutas - Melão': { calories: 29, protein: 0.7, fat: 0, carbs: 7.5, fiber: 0.3 },
|
||||
'Frutas - Uva': { calories: 53, protein: 0.7, fat: 0.2, carbs: 13.6, fiber: 0.9 },
|
||||
|
||||
// Vegetables
|
||||
'Vegetais - Agrião': { calories: 17, protein: 2.7, fat: 0.2, carbs: 2.3, fiber: 2.1 },
|
||||
'Vegetais - Brócolis (cozido)': { calories: 25, protein: 2.1, fat: 0.5, carbs: 4.4, fiber: 3.4 },
|
||||
'Vegetais - Cenoura (cozida)': { calories: 30, protein: 0.8, fat: 0.2, carbs: 6.7, fiber: 2.6 },
|
||||
'Vegetais - Couve flor (cozida)': { calories: 19, protein: 1.2, fat: 0.3, carbs: 3.9, fiber: 2.1 },
|
||||
'Vegetais - Ervilha (cozida)': { calories: 88, protein: 7.5, fat: 0.5, carbs: 14.2, fiber: 9.7 },
|
||||
'Vegetais - Espinafre': { calories: 16, protein: 2, fat: 0.2, carbs: 2.6, fiber: 2.1 },
|
||||
'Vegetais - Feijão preto (cozido)': { calories: 77, protein: 4.5, fat: 0.5, carbs: 14, fiber: 8.4 },
|
||||
'Vegetais - Lentilha (cozida)': { calories: 93, protein: 6.3, fat: 0.5, carbs: 16.3, fiber: 7.9 },
|
||||
'Vegetais - Mandioca (cozida)': { calories: 125, protein: 0.6, fat: 0.3, carbs: 30.1, fiber: 1.6 },
|
||||
'Vegetais - Pepino': { calories: 10, protein: 0.9, fat: 0, carbs: 2, fiber: 1.1 },
|
||||
'Vegetais - Rúcula': { calories: 13, protein: 1.8, fat: 0.1, carbs: 2.2, fiber: 1.7 },
|
||||
'Vegetais - Spirulina': { calories: 290, protein: 57.5, fat: 23.9, carbs: 7.7, fiber: 0 },
|
||||
'Vegetais - Tomate': { calories: 15, protein: 1.1, fat: 0.2, carbs: 3.1, fiber: 1.2 },
|
||||
|
||||
// Carbohydrates
|
||||
'Carboidrados - Pão Francês': { calories: 300, protein: 8, fat: 3.1, carbs: 58.6, fiber: 2.3 },
|
||||
'Carboidrados - Polenta (cozida)': { calories: 192, protein: 4.8, fat: 1.2, carbs: 40, fiber: 3.8 },
|
||||
|
||||
// Proteins
|
||||
'Proteínas - Atum Sólido (natural)': { calories: 135, protein: 28.3, fat: 2.7, carbs: 0, fiber: 0 },
|
||||
'Proteínas - Filé de Tilápia (cozido)': { calories: 128, protein: 26.1, fat: 2.6, carbs: 0, fiber: 0 },
|
||||
'Proteínas - Ovo inteiro (cozido)': { calories: 146, protein: 13.3, fat: 9.5, carbs: 0.6, fiber: 0 },
|
||||
'Proteínas - Ovo clara (cozido)': { calories: 59, protein: 13.4, fat: 0.1, carbs: 0, fiber: 0 },
|
||||
'Proteínas - PTS (crua)': { calories: 288, protein: 52, fat: 0, carbs: 20, fiber: 14 },
|
||||
'Proteínas - Patinho moído (grelhado)': { calories: 219, protein: 35.9, fat: 7.3, carbs: 0, fiber: 0 },
|
||||
'Proteínas - Soja em Pó Growth': { calories: 400, protein: 86.7, fat: 5, carbs: 3.3, fiber: 0 },
|
||||
'Proteínas - Sobrecoxa s/pele (cozida)': { calories: 245, protein: 24.9, fat: 15.4, carbs: 0, fiber: 0 },
|
||||
'Proteínas - Tofu': { calories: 65, protein: 6.5, fat: 4, carbs: 2, fiber: 0.75 },
|
||||
|
||||
// Grains
|
||||
'Grãos - Arroz integral (cozido)': { calories: 130, protein: 2.7, fat: 0.3, carbs: 28, fiber: 0.4 },
|
||||
'Grãos - Aveia': { calories: 394, protein: 13.9, fat: 8.5, carbs: 66.6, fiber: 9.1 },
|
||||
'Grãos - Grão de Bico (cozido)': { calories: 180, protein: 9.5, fat: 3, carbs: 30, fiber: 8.6 },
|
||||
'Grãos - Pão integral de forma': { calories: 253, protein: 9.4, fat: 3.7, carbs: 49.9, fiber: 6.9 },
|
||||
'Grãos - Pipoca c/óleo': { calories: 448, protein: 9.9, fat: 15.9, carbs: 70.3, fiber: 14.3 },
|
||||
'Grãos - Pipoca s/óleo': { calories: 178, protein: 3.3, fat: 7.2, carbs: 25, fiber: 4.3 },
|
||||
'Grãos - Quinoa (cozida)': { calories: 158, protein: 5.55, fat: 2.5, carbs: 29, fiber: 2.5 },
|
||||
|
||||
// Nuts & Seeds
|
||||
'Castanhas - Amêndoas': { calories: 581, protein: 18.6, fat: 47.3, carbs: 29.5, fiber: 11.6 },
|
||||
'Castanhas - Amendoin natural': { calories: 611, protein: 26, fat: 49, carbs: 16, fiber: 8.5 },
|
||||
'Castanhas - Castanha de Caju': { calories: 570, protein: 18.5, fat: 46.3, carbs: 29.1, fiber: 3.7 },
|
||||
'Castanhas - Castanha do Pará': { calories: 643, protein: 14.5, fat: 63.5, carbs: 15.1, fiber: 7.9 },
|
||||
'Sementes - Semente de Chia': { calories: 490, protein: 15.6, fat: 30.7, carbs: 43.8, fiber: 37.7 },
|
||||
'Sementes - Semente de Linhaça': { calories: 495, protein: 14.1, fat: 32.3, carbs: 43.3, fiber: 33.5 },
|
||||
'Sementes - Semente de Gergelim': { calories: 584, protein: 21.2, fat: 50.4, carbs: 21.6, fiber: 11.9 },
|
||||
|
||||
// Snacks
|
||||
'Lanches - Barra de Proteína Vegana Growth': { calories: 400, protein: 25, fat: 22, carbs: 25, fiber: 8 },
|
||||
'Lanches - Goiabada': { calories: 320, protein: 0, fat: 0, carbs: 80, fiber: 4.5 },
|
||||
'Lanches - Mel Silvestre': { calories: 230, protein: 0, fat: 0, carbs: 65, fiber: 0 },
|
||||
'Lanches - Melado': { calories: 297, protein: 0, fat: 0, carbs: 76.6, fiber: 0 },
|
||||
'Lanches - Pasta de Amendoim Growth': { calories: 544, protein: 27, fat: 33, carbs: 20, fiber: 8 },
|
||||
'Lanches - Goma de Tapioca': { calories: 226, protein: 0, fat: 0, carbs: 57, fiber: 0 },
|
||||
'Lanches - 70% Nibs Garoto': { calories: 544, protein: 8.4, fat: 40, carbs: 34, fiber: 0 }
|
||||
};
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Daily Calorie Tracker</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🍎 Daily Calorie Tracker</h1>
|
||||
|
||||
<div class="date-display" id="currentDate"></div>
|
||||
|
||||
<div class="calories-banner">
|
||||
<div class="calories-number" id="caloriesCounter">0</div>
|
||||
<div class="calories-text">Calories Today</div>
|
||||
</div>
|
||||
|
||||
<div class="nutrition-summary" id="nutritionSummary">
|
||||
<div class="nutrition-item">
|
||||
<div class="nutrition-value" id="proteinValue">0</div>
|
||||
<div class="nutrition-label">Protein (g)</div>
|
||||
</div>
|
||||
<div class="nutrition-item">
|
||||
<div class="nutrition-value" id="fatValue">0</div>
|
||||
<div class="nutrition-label">Fat (g)</div>
|
||||
</div>
|
||||
<div class="nutrition-item">
|
||||
<div class="nutrition-value" id="carboValue">0</div>
|
||||
<div class="nutrition-label">Carbs (g)</div>
|
||||
</div>
|
||||
<div class="nutrition-item">
|
||||
<div class="nutrition-value" id="fiberValue">0</div>
|
||||
<div class="nutrition-label">Fiber (g)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="add-food-section">
|
||||
<h2 class="add-food-title">Add Food</h2>
|
||||
<div class="add-food-form">
|
||||
<div class="form-group">
|
||||
<label for="foodSelect">Food Item</label>
|
||||
<select id="foodSelect">
|
||||
<option value="">Select a food item...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="gramAmount">Amount (grams)</label>
|
||||
<input type="number" id="gramAmount" min="1" step="1" value="100">
|
||||
</div>
|
||||
<button class="add-btn" onclick="addFood()">Add Food</button>
|
||||
</div>
|
||||
<div id="food-preview" class="add-food-form-item display-none">
|
||||
<p>Calories: 0 - Protein: 0 - Fat: 0 - Carbohydrates: 0</p>
|
||||
</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>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>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="foodTableBody">
|
||||
<!-- Food entries will be added here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="calendar-section">
|
||||
<div class="calendar-header">
|
||||
<button class="calendar-nav" onclick="previousMonth()">‹</button>
|
||||
<div class="calendar-title" id="calendarTitle">Month Year</div>
|
||||
<button class="calendar-nav" onclick="nextMonth()">›</button>
|
||||
</div>
|
||||
<div class="calendar-grid" id="calendarGrid">
|
||||
<!-- Calendar will be generated here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="foodDatabase.js"></script>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,322 @@
|
||||
// App state
|
||||
let selectedDate = new Date().toISOString().split('T')[0];
|
||||
let currentViewDate = new Date();
|
||||
|
||||
// Initialize the app
|
||||
function init() {
|
||||
populateFoodSelect();
|
||||
updateCurrentDate();
|
||||
loadDayData(selectedDate);
|
||||
renderCalendar();
|
||||
addEvents();
|
||||
}
|
||||
|
||||
function getFoodProportion(grams, foodData) {
|
||||
const multiplier = grams / 100; // Database values are per 100g
|
||||
return {
|
||||
calories: Math.round(foodData.calories * multiplier),
|
||||
protein: Math.round(foodData.protein * multiplier * 10) / 10,
|
||||
fat: Math.round(foodData.fat * multiplier * 10) / 10,
|
||||
carbs: Math.round(foodData.carbs * multiplier * 10) / 10,
|
||||
fiber: Math.round(foodData.fiber * multiplier * 10) / 10
|
||||
};
|
||||
}
|
||||
|
||||
function previewCalories() {
|
||||
const foodSelect = document.getElementById('foodSelect');
|
||||
const gramAmount = document.getElementById('gramAmount');
|
||||
|
||||
if (!foodSelect.value) {
|
||||
document.getElementById('food-preview').innerHTML = '';
|
||||
document.getElementById('food-preview').classList = 'add-food-form-item display-none';
|
||||
return;
|
||||
}
|
||||
|
||||
let grams = 100;
|
||||
if (gramAmount && gramAmount.value && parseFloat(gramAmount.value) > 0) {
|
||||
grams = parseFloat(gramAmount.value);
|
||||
}
|
||||
|
||||
const foodName = foodSelect.value;
|
||||
const foodData = foodDatabase[foodName];
|
||||
const proportion = getFoodProportion(grams, foodData);
|
||||
|
||||
const textArray = [];
|
||||
textArray.push(`Calories in ${grams}g: ${proportion.calories}; `);
|
||||
textArray.push(`Protein: ${proportion.protein}g; `);
|
||||
textArray.push(`Fat: ${proportion.fat}g; `);
|
||||
textArray.push(`Carbohydrates: ${proportion.carbs}g; `);
|
||||
textArray.push(`Fiber: ${proportion.fiber}g;`);
|
||||
|
||||
document.getElementById('food-preview').innerHTML = textArray.join('');
|
||||
document.getElementById('food-preview').classList = 'add-food-form-item display-block';
|
||||
}
|
||||
|
||||
function addEvents() {
|
||||
document.getElementById('foodSelect').addEventListener('change', () => {
|
||||
previewCalories();
|
||||
});
|
||||
|
||||
document.getElementById('gramAmount').addEventListener('change', () => {
|
||||
previewCalories();
|
||||
});
|
||||
}
|
||||
|
||||
// Populate food select dropdown
|
||||
function populateFoodSelect() {
|
||||
const select = document.getElementById('foodSelect');
|
||||
select.innerHTML = '<option value="">Select a food item...</option>';
|
||||
|
||||
const sortedFoods = Object.keys(foodDatabase).sort();
|
||||
sortedFoods.forEach(food => {
|
||||
const option = document.createElement('option');
|
||||
option.value = food;
|
||||
option.textContent = food.charAt(0).toUpperCase() + food.slice(1);
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
// Update current date display
|
||||
function updateCurrentDate() {
|
||||
const date = new Date(selectedDate);
|
||||
const options = {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
};
|
||||
document.getElementById('currentDate').textContent = date.toLocaleDateString('en-US', options);
|
||||
}
|
||||
|
||||
// Add food to the log
|
||||
function addFood() {
|
||||
const foodSelect = document.getElementById('foodSelect');
|
||||
const gramAmount = document.getElementById('gramAmount');
|
||||
|
||||
if (!foodSelect.value || !gramAmount.value) {
|
||||
alert('Please select a food item and enter amount');
|
||||
return;
|
||||
}
|
||||
|
||||
const foodName = foodSelect.value;
|
||||
const grams = parseFloat(gramAmount.value);
|
||||
const foodData = foodDatabase[foodName];
|
||||
|
||||
// Calculate nutrition for the amount
|
||||
const proportion = getFoodProportion(grams, foodData);
|
||||
const entry = {
|
||||
id: Date.now(),
|
||||
name: foodName,
|
||||
grams: grams,
|
||||
calories: proportion.calories,
|
||||
protein: proportion.protein,
|
||||
fat: proportion.fat,
|
||||
carbs: proportion.carbs,
|
||||
fiber: proportion.fiber,
|
||||
time: new Date().toLocaleTimeString('en-US', {
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
};
|
||||
|
||||
// Save to localStorage
|
||||
const dayData = getDayData(selectedDate);
|
||||
dayData.push(entry);
|
||||
saveDayData(selectedDate, dayData);
|
||||
|
||||
// Update display
|
||||
loadDayData(selectedDate);
|
||||
renderCalendar();
|
||||
|
||||
// Reset form
|
||||
foodSelect.value = '';
|
||||
gramAmount.value = '100';
|
||||
document.getElementById('food-preview').innerHTML = '';
|
||||
document.getElementById('food-preview').classList = 'add-food-form-item display-none';
|
||||
}
|
||||
|
||||
// Get day data from localStorage
|
||||
function getDayData(date) {
|
||||
const key = `calories_${date}`;
|
||||
return JSON.parse(localStorage.getItem(key)) || [];
|
||||
}
|
||||
|
||||
// Save day data to localStorage
|
||||
function saveDayData(date, data) {
|
||||
const key = `calories_${date}`;
|
||||
localStorage.setItem(key, JSON.stringify(data));
|
||||
}
|
||||
|
||||
// Load and display day data
|
||||
function loadDayData(date) {
|
||||
const dayData = getDayData(date);
|
||||
|
||||
// Update counters
|
||||
let totalCalories = 0;
|
||||
let totalProtein = 0;
|
||||
let totalFat = 0;
|
||||
let totalCarbs = 0;
|
||||
let totalFiber = 0;
|
||||
|
||||
dayData.forEach(entry => {
|
||||
totalCalories += entry.calories;
|
||||
totalProtein += entry.protein;
|
||||
totalFat += entry.fat;
|
||||
totalCarbs += entry.carbs;
|
||||
totalFiber += entry.fiber;
|
||||
});
|
||||
|
||||
document.getElementById('caloriesCounter').textContent = totalCalories;
|
||||
document.getElementById('proteinValue').textContent = Math.round(totalProtein * 10) / 10;
|
||||
document.getElementById('fatValue').textContent = Math.round(totalFat * 10) / 10;
|
||||
document.getElementById('carboValue').textContent = Math.round(totalCarbs * 10) / 10;
|
||||
document.getElementById('fiberValue').textContent = Math.round(totalFiber * 10) / 10;
|
||||
|
||||
// Update table
|
||||
const tbody = document.getElementById('foodTableBody');
|
||||
tbody.innerHTML = '';
|
||||
|
||||
dayData.forEach(entry => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${entry.time}</td>
|
||||
<td>${entry.name.charAt(0).toUpperCase() + entry.name.slice(1)}</td>
|
||||
<td>${entry.grams}</td>
|
||||
<td>${entry.calories}</td>
|
||||
<td>${entry.protein}</td>
|
||||
<td>${entry.fat}</td>
|
||||
<td>${entry.carbs}</td>
|
||||
<td>${entry.fiber}</td>
|
||||
<td><button class="delete-btn" onclick="deleteEntry('${date}', ${entry.id})">Delete</button></td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
// Delete an entry
|
||||
function deleteEntry(date, entryId) {
|
||||
if (confirm('Are you sure you want to delete this entry?')) {
|
||||
let dayData = getDayData(date);
|
||||
dayData = dayData.filter(entry => entry.id !== entryId);
|
||||
saveDayData(date, dayData);
|
||||
loadDayData(selectedDate);
|
||||
renderCalendar();
|
||||
}
|
||||
}
|
||||
|
||||
// Calendar functions
|
||||
function previousMonth() {
|
||||
currentViewDate.setMonth(currentViewDate.getMonth() - 1);
|
||||
renderCalendar();
|
||||
}
|
||||
|
||||
function nextMonth() {
|
||||
currentViewDate.setMonth(currentViewDate.getMonth() + 1);
|
||||
renderCalendar();
|
||||
}
|
||||
|
||||
function selectDate(date) {
|
||||
selectedDate = date;
|
||||
updateCurrentDate();
|
||||
loadDayData(selectedDate);
|
||||
renderCalendar();
|
||||
}
|
||||
|
||||
function renderCalendar() {
|
||||
const year = currentViewDate.getFullYear();
|
||||
const month = currentViewDate.getMonth();
|
||||
|
||||
// Update calendar title
|
||||
const monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
document.getElementById('calendarTitle').textContent = `${monthNames[month]} ${year}`;
|
||||
|
||||
// Clear calendar
|
||||
const grid = document.getElementById('calendarGrid');
|
||||
grid.innerHTML = '';
|
||||
|
||||
// Add day headers
|
||||
const dayHeaders = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
dayHeaders.forEach(day => {
|
||||
const header = document.createElement('div');
|
||||
header.className = 'calendar-day-header';
|
||||
header.textContent = day;
|
||||
grid.appendChild(header);
|
||||
});
|
||||
|
||||
// Get first day of month and number of days
|
||||
const firstDay = new Date(year, month, 1);
|
||||
const lastDay = new Date(year, month + 1, 0);
|
||||
const daysInMonth = lastDay.getDate();
|
||||
const startingDayOfWeek = firstDay.getDay();
|
||||
|
||||
// Add empty cells for previous month
|
||||
const prevMonth = new Date(year, month, 0);
|
||||
const daysInPrevMonth = prevMonth.getDate();
|
||||
|
||||
for (let i = startingDayOfWeek - 1; i >= 0; i--) {
|
||||
const day = daysInPrevMonth - i;
|
||||
const dayElement = createDayElement(day, true, year, month - 1);
|
||||
grid.appendChild(dayElement);
|
||||
}
|
||||
|
||||
// Add days of current month
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const dayElement = createDayElement(day, false, year, month);
|
||||
grid.appendChild(dayElement);
|
||||
}
|
||||
|
||||
// Add days from next month to fill the grid
|
||||
const totalCells = grid.children.length - 7;
|
||||
const remainingCells = 42 - totalCells;
|
||||
|
||||
for (let day = 1; day <= remainingCells; day++) {
|
||||
const dayElement = createDayElement(day, true, year, month + 1);
|
||||
grid.appendChild(dayElement);
|
||||
}
|
||||
}
|
||||
|
||||
function createDayElement(day, isOtherMonth, year, month) {
|
||||
const dayElement = document.createElement('div');
|
||||
dayElement.className = 'calendar-day';
|
||||
|
||||
const dayDate = new Date(year, month, day);
|
||||
const dateString = dayDate.toISOString().split('T')[0];
|
||||
|
||||
if (isOtherMonth) {
|
||||
dayElement.classList.add('other-month');
|
||||
} else {
|
||||
dayElement.onclick = () => selectDate(dateString);
|
||||
}
|
||||
|
||||
// Check if this is today
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
if (!isOtherMonth && dateString === today) {
|
||||
dayElement.classList.add('today');
|
||||
}
|
||||
|
||||
// Check if this is selected date
|
||||
if (!isOtherMonth && dateString === selectedDate) {
|
||||
dayElement.classList.add('selected');
|
||||
}
|
||||
|
||||
// Check if this day has data
|
||||
const dayData = getDayData(dateString);
|
||||
if (dayData.length > 0) {
|
||||
dayElement.classList.add('has-data');
|
||||
const totalCalories = dayData.reduce((sum, entry) => sum + entry.calories, 0);
|
||||
|
||||
dayElement.innerHTML = `
|
||||
<div>${day}</div>
|
||||
<div class="day-calories">${totalCalories} cal</div>
|
||||
`;
|
||||
} else {
|
||||
dayElement.textContent = day;
|
||||
}
|
||||
|
||||
return dayElement;
|
||||
}
|
||||
|
||||
// Initialize when page loads
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
Generated
+1009
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "calories-tracker",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
|
||||
padding: 40px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
font-size: 2.5rem;
|
||||
color: #4a5568;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.calories-banner {
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, #FF6B6B, #4ECDC4);
|
||||
color: white;
|
||||
padding: 60px 40px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 10px 30px rgba(255, 107, 107, 0.3);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.calories-banner:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.calories-number {
|
||||
font-size: 4rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.calories-text {
|
||||
font-size: 1.3rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.date-display {
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 30px;
|
||||
color: #4a5568;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.add-food-section {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.add-food-title {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 20px;
|
||||
color: #4a5568;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.add-food-form {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr auto;
|
||||
gap: 15px;
|
||||
align-items: end;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.add-food-form-item {
|
||||
margin-top: 15px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.display-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: 5px;
|
||||
font-weight: 600;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
select, input {
|
||||
padding: 12px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.3s ease;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
select:focus, input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
padding: 12px 25px;
|
||||
background: linear-gradient(135deg, #4CAF50, #45a049);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.add-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.food-table-section {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
.food-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 600px;
|
||||
}
|
||||
|
||||
.food-table th,
|
||||
.food-table td {
|
||||
padding: 15px 10px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.food-table th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: #4a5568;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.food-table tr:hover {
|
||||
background: #f7fafc;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #ff6b6b;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #e55656;
|
||||
}
|
||||
|
||||
.calendar-section {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.calendar-nav {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 1.1rem;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.calendar-nav:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.calendar-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 1px;
|
||||
background: #e2e8f0;
|
||||
}
|
||||
|
||||
.calendar-day-header {
|
||||
background: #f8fafc;
|
||||
padding: 15px 5px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
color: #4a5568;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
background: white;
|
||||
padding: 15px 5px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.calendar-day:hover {
|
||||
background: #f7fafc;
|
||||
}
|
||||
|
||||
.calendar-day.other-month {
|
||||
color: #cbd5e0;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.calendar-day.today {
|
||||
background: #667eea;
|
||||
/*color: white;*/
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.calendar-day.selected {
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.calendar-day.has-data {
|
||||
background: #e6fffa;
|
||||
border: 2px solid #4ECDC4;
|
||||
}
|
||||
|
||||
.calendar-day.has-data.selected {
|
||||
background: #4CAF50;
|
||||
border-color: #45a049;
|
||||
}
|
||||
|
||||
.day-calories {
|
||||
font-size: 0.7rem;
|
||||
margin-top: 2px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.nutrition-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background: #f8fafc;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.nutrition-item {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.nutrition-value {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.nutrition-label {
|
||||
font-size: 0.8rem;
|
||||
color: #718096;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
margin: 0px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.calories-number {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.add-food-form {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.food-table {
|
||||
font-size: 0.8rem;
|
||||
min-width: 650px;
|
||||
}
|
||||
|
||||
.food-table th,
|
||||
.food-table td {
|
||||
padding: 8px 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
/* Optional: Add a subtle shadow to indicate scrollability */
|
||||
.table-wrapper::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.table-wrapper::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.table-wrapper::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user