diff --git a/.gitignore b/.gitignore index b947077..614b797 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules/ dist/ +.env +senha.txt +.vscode/ diff --git a/auth.css b/auth.css new file mode 100644 index 0000000..4513f3a --- /dev/null +++ b/auth.css @@ -0,0 +1,227 @@ +/* Authentication Header Styles */ +.auth-header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 15px 0; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + position: sticky; + top: 0; + z-index: 1000; +} + +.auth-container { + max-width: 1200px; + margin: 0 auto; + padding: 0 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo h2 { + margin: 0; + font-size: 1.5rem; + font-weight: 600; +} + +.auth-section { + display: flex; + align-items: center; +} + +.auth-form { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border-radius: 10px; + padding: 25px; + min-width: 300px; + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.auth-form h3 { + margin: 0 0 20px 0; + text-align: center; + font-size: 1.3rem; + font-weight: 500; +} + +.auth-form .form-group { + margin-bottom: 15px; +} + +.auth-form input { + width: 100%; + padding: 12px; + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 6px; + background: rgba(255, 255, 255, 0.1); + color: white; + font-size: 1rem; + backdrop-filter: blur(10px); + box-sizing: border-box; +} + +.auth-form input::placeholder { + color: rgba(255, 255, 255, 0.7); +} + +.auth-form input:focus { + outline: none; + border-color: rgba(255, 255, 255, 0.6); + background: rgba(255, 255, 255, 0.15); +} + +.auth-btn { + width: 100%; + padding: 12px; + background: rgba(255, 255, 255, 0.2); + color: white; + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + font-weight: 600; + transition: all 0.3s ease; + backdrop-filter: blur(10px); +} + +.auth-btn:hover { + background: rgba(255, 255, 255, 0.3); + transform: translateY(-1px); +} + +.auth-switch { + text-align: center; + margin: 15px 0 0 0; + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.8); +} + +.auth-switch a { + color: white; + text-decoration: none; + font-weight: 600; +} + +.auth-switch a:hover { + text-decoration: underline; +} + +.user-info { + display: flex; + align-items: center; + gap: 15px; +} + +.user-details { + display: flex; + align-items: center; + gap: 15px; +} + +.user-details span { + font-weight: 500; + font-size: 1rem; +} + +.logout-btn { + padding: 8px 16px; + background: rgba(255, 255, 255, 0.2); + color: white; + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 6px; + cursor: pointer; + font-size: 0.9rem; + transition: all 0.3s ease; + backdrop-filter: blur(10px); +} + +.logout-btn:hover { + background: rgba(255, 255, 255, 0.3); +} + +/* Loading Overlay */ +.loading-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + z-index: 9999; + backdrop-filter: blur(5px); +} + +.loading-spinner { + width: 50px; + height: 50px; + border: 4px solid rgba(255, 255, 255, 0.3); + border-top: 4px solid white; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 20px; +} + +.loading-overlay p { + color: white; + font-size: 1.1rem; + font-weight: 500; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Utility Classes */ +.hidden { + display: none !important; +} + +.display-none { + display: none; +} + +.display-block { + display: block; +} + +/* Update main content spacing */ +#app-content { + padding-top: 20px; +} + +/* Mobile Responsive for Auth */ +@media (max-width: 768px) { + .auth-container { + flex-direction: column; + gap: 15px; + padding: 0 15px; + } + + .logo { + order: 1; + } + + .auth-section, + .user-info { + order: 2; + width: 100%; + justify-content: center; + } + + .auth-form { + min-width: 280px; + padding: 20px; + } + + .user-details { + flex-direction: column; + gap: 10px; + text-align: center; + } +} diff --git a/index.html b/index.html index 65b8caa..513d454 100644 --- a/index.html +++ b/index.html @@ -5,115 +5,184 @@ Daily Calorie Tracker + -
-

🍎 Daily Calorie Tracker

- -
- -
-
0
-
Calories Today
-
- -
-
-
0
-
Protein (g)
+ +
+
+ -
-
0
-
Fat (g)
-
-
-
0
-
Carbs (g)
-
-
-
0
-
Fiber (g)
-
-
- -
-

Add Food

-
-
- - + + +
+ +
+

Login

+
+
+ +
+
+ +
+ +

+ Don't have an account? + Register here +

+
-
- - + + + -
-
-
-
Food Log
-
- - - - - - - - - - - - - - - - - - -
IDTimeFoodGramsCaloriesProtein (g)Fat (g)Carbs (g)Fiber (g)Action
-
-
- -
-
- -
Month Year
- -
-
- -
-
+ + + +
+
+

🍎 Daily Calorie Tracker

+ +
+ +
+
0
+
Calories Today
+
+ +
+
+
0
+
Protein (g)
+
+
+
0
+
Fat (g)
+
+
+
0
+
Carbs (g)
+
+
+
0
+
Fiber (g)
+
+
+ +
+

Add Food

+
+
+ + +
+
+ + +
+ +
+ +
+ +
+
Food Log
+
+ + + + + + + + + + + + + + + + + + +
IDTimeFoodGramsCaloriesProtein (g)Fat (g)Carbs (g)Fiber (g)Action
+
+
+ +
+
+ +
Month Year
+ +
+
+ +
+
+
+
+ diff --git a/src/appwrite.ts b/src/appwrite.ts new file mode 100644 index 0000000..5725e58 --- /dev/null +++ b/src/appwrite.ts @@ -0,0 +1,152 @@ +import { Client, Account, Databases, Query } from 'appwrite'; +import { FoodStorage } from './types'; + +// Initialize Appwrite client +const client = new Client(); + +client + .setEndpoint('https://cloud.appwrite.io/v1') // Your Appwrite endpoint + .setProject("684ac3bf000ee8950f5a"); // Replace with your project ID + +// Initialize services +export const account = new Account(client); +export const databases = new Databases(client); + +// Configuration constants +export const DATABASE_ID = '684b38db000fac8c781c'; // Replace with your database ID +export const FOOD_ENTRIES_COLLECTION_ID = '684b38e300336fc605dc'; // Replace with your collection ID + +// Auth helper functions +export class AppwriteAuth { + // Register new user + static async register(email: string, password: string, name: string) { + try { + const response = await account.create('unique()', email, password, name); + console.debug('User registered:', response); + return response; + } catch (error) { + console.error('Registration error:', error); + throw error; + } + } + + // Login user + static async login(email: string, password: string) { + try { + const session = await account.createEmailPasswordSession(email, password); + console.debug('User logged in:', session); + return session; + } catch (error) { + console.error('Login error:', error); + throw error; + } + } + + // Logout user + static async logout() { + try { + await account.deleteSession('current'); + console.debug('User logged out'); + } catch (error) { + console.error('Logout error:', error); + throw error; + } + } + + // Get current user + static async getCurrentUser() { + try { + const user = await account.get(); + return user; + } catch (error) { + console.error('Get user error:', error); + return null; + } + } + + // Check if user is logged in + static async isLoggedIn() { + try { + await account.get(); + return true; + } catch { + return false; + } + } +} + +// Database helper functions +export class AppwriteDB { + // Save food entry + static async saveFoodEntry(entryData: FoodStorage) { + try { + const response = await databases.createDocument( + DATABASE_ID, + FOOD_ENTRIES_COLLECTION_ID, + 'unique()', + { + ...entryData, + userId: (await account.get()).$id, + createdAt: new Date().toISOString() + } + ); + console.log('Food entry saved:', response); + return response; + } catch (error) { + console.error('Save food entry error:', error); + throw error; + } + } + + // Get user's food entries for a specific date + static async getFoodEntries(date: Date) { + try { + const user = await account.get(); + + const response = await databases.listDocuments( + DATABASE_ID, + FOOD_ENTRIES_COLLECTION_ID, + [ + Query.equal('userId', user.$id), + Query.equal('date', date.toISOString().split('T')[0]), + ] + ); + console.debug('Food entries retrieved:', response); + return response.documents; + } catch (error) { + console.error('Get food entries error:', error); + throw error; + } + } + + // Delete food entry + static async deleteFoodEntry(documentId: string) { + try { + await databases.deleteDocument( + DATABASE_ID, + FOOD_ENTRIES_COLLECTION_ID, + documentId + ); + console.debug('Food entry deleted:', documentId); + } catch (error) { + console.error('Delete food entry error:', error); + throw error; + } + } + + // Get all food entries for a user (for calendar view) + static async getAllUserEntries() { + try { + const user = await account.get(); + const response = await databases.listDocuments( + DATABASE_ID, + FOOD_ENTRIES_COLLECTION_ID, + [Query.equal('userId', user.$id)] + ); + return response.documents; + } catch (error) { + console.error('Get all entries error:', error); + throw error; + } + } +} diff --git a/src/index.ts b/src/index.ts index 43228e7..fb5ff35 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,21 +1,173 @@ import { foodDatabase } from './foodDatabase.js'; import { getButtonById, getButtonListByClassName, getDivById, getInputById, showFoodPreview } from './HtmlUtil.ts'; import { FoodItem, FoodStorage } from './types.js'; +import { AppwriteAuth, AppwriteDB } from './appwrite.js'; // App state -let selectedDate = new Date().toISOString().split('T')[0]; +let selectedDate = new Date(); let currentViewDate = new Date(); -// Initialize the app -const init = () => { +// Authentication state +let currentUser: any | null = null; + +// DOM Elements +const authSection = document.getElementById('auth-section'); +const userInfo = document.getElementById('user-info'); +const appContent = document.getElementById('app-content'); +const loadingOverlay = getDivById('loading-overlay'); +const loginForm = document.getElementById('login-form'); +const registerForm = document.getElementById('register-form'); + +// Initialize when page loads +document.addEventListener('DOMContentLoaded', async function() { + await initializeAuth(); populateFoodSelect(); updateCurrentDate(); - loadDayData(selectedDate); - renderCalendar(); - addEvents(); + setupEventListeners(); +}); + +// Initialize application +async function initializeAuth() { + showLoading(); + + try { + // Check if user is already logged in + const isLoggedIn = await AppwriteAuth.isLoggedIn(); + + if (isLoggedIn) { + currentUser = await AppwriteAuth.getCurrentUser(); + showMainApp(); + selectDate(selectedDate); + } else { + showAuthForms(); + } + } catch (error) { + console.error('Initialize app error:', error); + showAuthForms(); + } finally { + hideLoading(); + } } -const getFoodProportion = (grams: number, foodData: FoodItem): FoodItem => { +// Toggle between login and register forms +function toggleAuthForms() { + loginForm?.classList.toggle('hidden'); + registerForm?.classList.toggle('hidden'); + + // Clear form data + const logForm = document.getElementById('loginForm') as HTMLFormElement; + logForm.reset(); + + const resetForm = document.getElementById('registerForm') as HTMLFormElement; + resetForm.reset(); +} + +// Handle user registration +async function handleRegister(e: SubmitEvent) { + e.preventDefault(); + showLoading(); + + const name = getInputById('registerName').value; + const email = getInputById('registerEmail').value; + const password = getInputById('registerPassword').value; + + try { + await AppwriteAuth.register(email, password, name); + + // Auto login after registration + await AppwriteAuth.login(email, password); + currentUser = await AppwriteAuth.getCurrentUser(); + + showMainApp(); + selectDate(selectedDate); + + alert('Registration successful! Welcome to Food Tracker.'); + } catch (error) { + console.error('Registration error:', error); + alert('Registration failed: ' + error.message); + } finally { + hideLoading(); + } +} + +// Handle user login +async function handleLogin(e: SubmitEvent) { + e.preventDefault(); + showLoading(); + + const email = getInputById('loginEmail').value; + const password = getInputById('loginPassword').value; + + try { + await AppwriteAuth.login(email, password); + currentUser = await AppwriteAuth.getCurrentUser(); + + showMainApp(); + selectDate(selectedDate); + + } catch (error) { + console.error('Login error:', error); + alert('Login failed: ' + error.message); + } finally { + hideLoading(); + } +} + +async function handleLogout() { + showLoading(); + + try { + await AppwriteAuth.logout(); + currentUser = null; + showAuthForms(); + clearAppData(); + } catch (error) { + console.error('Logout error:', error); + alert('Logout failed: ' + error.message); + } finally { + hideLoading(); + } +} + +// Show authentication forms +function showAuthForms() { + authSection?.classList.remove('hidden'); + userInfo?.classList.add('hidden'); + appContent?.classList.add('hidden'); +} + +function showMainApp() { + authSection?.classList.add('hidden'); + userInfo?.classList.remove('hidden'); + appContent?.classList.remove('hidden'); + + // Update user info display + if (currentUser) { + getDivById('userName').textContent = `Welcome, ${currentUser.name}!`; + } + + // Update date display + updateCurrentDate(); +} + +// Show/hide loading overlay +function showLoading() { + loadingOverlay.classList.remove('hidden'); +} + +function hideLoading() { + loadingOverlay.classList.add('hidden'); +} + +// Clear application data +function clearAppData() { + getDivById('foodTableBody').innerHTML = ''; + getDivById('caloriesCounter').textContent = '0'; + getInputById('foodSelect').value = ''; + getInputById('gramAmount').value = '100'; +} + +const getFoodData = (grams: number, foodData: FoodItem): FoodItem => { const multiplier = grams / 100; // Database values are per 100g return { ...foodData, @@ -52,7 +204,7 @@ const previewCalories = () => { } const foodData: FoodItem = getFoodItemByName(foodSelect.value); - const proportion = getFoodProportion(grams, foodData); + const proportion = getFoodData(grams, foodData); getDivById('calorieValuePreview').textContent = proportion.info.calories.toString(); getDivById('proteinValuePreview').textContent = (Math.round(proportion.info.protein * 10) / 10).toString (); @@ -63,7 +215,7 @@ const previewCalories = () => { showFoodPreview(true); } -const addEvents = () => { +const setupEventListeners = () => { getInputById('foodSelect').addEventListener('change', () => { previewCalories(); }); @@ -83,6 +235,25 @@ const addEvents = () => { getButtonById('previous-month-btn').addEventListener('click', () => { previousMonth(); }); + + // Form switching + document.getElementById('showRegister')?.addEventListener('click', (e) => { + e.preventDefault(); + toggleAuthForms(); + }); + + document.getElementById('showLogin')?.addEventListener('click', (e) => { + e.preventDefault(); + toggleAuthForms(); + }); + + // Auth forms + document.getElementById('loginForm')?.addEventListener('submit', handleLogin); + document.getElementById('registerForm')?.addEventListener('submit', handleRegister); + document.getElementById('logoutBtn')?.addEventListener('click', handleLogout); + + // Food table delete buttons (event delegation) + // document.getElementById('foodTableBody')?.addEventListener('click', handleDeleteFood); } const addDeleteEvents = () => { @@ -92,9 +263,8 @@ const addDeleteEvents = () => { const target = e.target as HTMLElement; const row = target.closest('tr'); const entryId = row?.querySelector('.hidden-column')?.textContent; - const entryIdNumber = entryId ? parseInt(entryId) : 0; - if (entryIdNumber) { - deleteEntry(selectedDate, entryIdNumber); + if (entryId && row) { + handleDeleteFood(entryId, row); } }); }); @@ -126,7 +296,12 @@ const updateCurrentDate = () => { } // Add food to the log -const addFood = () => { +const addFood = async () => { + if (!currentUser) { + alert('Please log in to add food entries.'); + return; + } + const foodSelect = getInputById('foodSelect'); const gramAmount = getInputById('gramAmount'); @@ -135,120 +310,157 @@ const addFood = () => { return; } - const grams: number = parseFloat(gramAmount.value); - const foodData: FoodItem = getFoodItemByName(foodSelect.value); - - // Calculate nutrition for the amount - const proportion: FoodItem = getFoodProportion(grams, foodData); - const entry: FoodStorage = { - id: Date.now(), - name: foodSelect.value, - grams: grams, - calories: proportion.info.calories, - protein: proportion.info.protein, - fat: proportion.info.fat, - carbs: proportion.info.carbs, - fiber: proportion.info.fiber, - time: new Date().toLocaleTimeString('en-US', { - hour12: false, - hour: '2-digit', - minute: '2-digit' - }) - }; + showLoading(); - // Save to localStorage - const dayData = getDayData(selectedDate); - dayData.push(entry); - saveDayData(selectedDate, dayData); + try { + const grams: number = parseFloat(gramAmount.value); + const foodData: FoodItem = getFoodItemByName(foodSelect.value); - // Update display - loadDayData(selectedDate); - renderCalendar(); + // Calculate nutrition for the amount + const proportion: FoodItem = getFoodData(grams, foodData); + const entry: FoodStorage = { + name: foodSelect.value, + grams: grams, + calories: proportion.info.calories, + protein: proportion.info.protein, + fat: proportion.info.fat, + carbs: proportion.info.carbs, + fiber: proportion.info.fiber, + date: selectedDate.toISOString().split('T')[0], + time: new Date().toLocaleTimeString('en-US', { + hour12: false, + hour: '2-digit', + minute: '2-digit' + }) + }; - // Reset form - foodSelect.value = ''; - gramAmount.value = '100'; - showFoodPreview(false); -} + // Save to Appwrite + const savedEntry = await AppwriteDB.saveFoodEntry(entry); -// Get day data from localStorage -const getDayData = (date: string): FoodStorage[] => { - const key = `calories_${date}`; - const saved = localStorage.getItem(key); - if (!saved) { - return []; - } - return JSON.parse(saved); -} + // Add to HTML table with Appwrite document ID + addFoodToTable(entry, savedEntry.$id); -// Save day data to localStorage -const saveDayData = (date: string, data: FoodStorage[]) => { - const key = `calories_${date}`; - localStorage.setItem(key, JSON.stringify(data)); -} + // Update totals + updateTotalCalories(); -// Load and display day data -const loadDayData = (date: string) => { - 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; - }); - - getDivById('caloriesCounter').textContent = totalCalories.toString(); - getDivById('proteinValue').textContent = (Math.round(totalProtein * 10) / 10).toString (); - getDivById('fatValue').textContent = (Math.round(totalFat * 10) / 10).toString (); - getDivById('carboValue').textContent = (Math.round(totalCarbs * 10) / 10).toString (); - getDivById('fiberValue').textContent = (Math.round(totalFiber * 10) / 10).toString (); - - // Update table - const tbody = document.getElementById('foodTableBody'); - if (!tbody) { - throw Error('Unable to create row tables!'); - } - tbody.innerHTML = ''; - - dayData.forEach(entry => { - const row = document.createElement('tr'); - row.innerHTML = ` - ${entry.id} - ${entry.time} - ${entry.name} - ${entry.grams} - ${entry.calories} - ${entry.protein} - ${entry.fat} - ${entry.carbs} - ${entry.fiber} - - `; - tbody.appendChild(row); - }); - addDeleteEvents(); -} - -// Delete an entry -const deleteEntry = (date: string, entryId: number) => { - if (confirm('Are you sure you want to delete this entry?')) { - let dayData: FoodStorage[] = getDayData(date); - dayData = dayData.filter(entry => entry.id !== entryId); - saveDayData(date, dayData); - loadDayData(selectedDate); + // Update display renderCalendar(); + + // Reset form + foodSelect.value = ''; + gramAmount.value = '100'; + showFoodPreview(false); + } catch (error) { + console.error('Add food error:', error); + alert('Failed to add food entry: ' + error.message); + } finally { + hideLoading(); } } +// Load food entries from Appwrite +async function loadFoodEntries(date: Date) { + if (!currentUser) return; + + showLoading(); + + try { + const entries = await AppwriteDB.getFoodEntries(date); + + // Clear current table + getDivById('foodTableBody').innerHTML = ''; + + // Add each entry to table + entries.forEach(entry => { + const foodData: FoodStorage = { + id: entry.$id, + name: entry.name, + grams: entry.grams, + calories: entry.calories, + protein: entry.protein, + fat: entry.fat, + carbs: entry.carbs, + fiber: entry.fiber, + time: entry.time, + date: entry.date, + }; + + addFoodToTable(foodData, entry.$id); + }); + + // Setup Delete events + addDeleteEvents(); + + // Update total calories + updateTotalCalories(); + + } catch (error) { + console.error('Load food entries error:', error); + alert('Failed to load food entries: ' + error.message); + } finally { + hideLoading(); + } +} + +// Handle food deletion +async function handleDeleteFood(documentId: string, row: HTMLTableRowElement) { + if (!documentId) return; + + if (!confirm('Are you sure you want to delete this food entry?')) return; + + showLoading(); + + try { + // Delete from Appwrite + await AppwriteDB.deleteFoodEntry(documentId); + + // Remove from table + row.remove(); + + // Update totals + updateTotalCalories(); + + } catch (error) { + console.error('Delete food error:', error); + alert('Failed to delete food entry: ' + error.message); + } finally { + hideLoading(); + } +} + +// Add food to table (existing function - update to use document ID) +function addFoodToTable(foodData: FoodStorage, documentId: string) { + const tableBody = document.getElementById('foodTableBody'); + const row = document.createElement('tr'); + + row.innerHTML = ` + ${documentId} + ${foodData.time || new Date().toLocaleTimeString()} + ${foodData.name} + ${foodData.grams} + ${foodData.calories} + ${foodData.protein} + ${foodData.fat} + ${foodData.carbs} + ${foodData.fiber} + + `; + + tableBody?.appendChild(row); +} + +// Update total calories +function updateTotalCalories() { + const caloriesCells = document.querySelectorAll('#foodTableBody td:nth-child(5)'); // 5th column is calories + let total = 0; + + caloriesCells.forEach((cell: Element) => { + total += parseInt(cell.innerHTML) || 0; + }); + + getDivById('caloriesCounter').textContent = total.toString(); +} + // Calendar functions const previousMonth = () => { currentViewDate.setMonth(currentViewDate.getMonth() - 1); @@ -260,10 +472,10 @@ const nextMonth = () => { renderCalendar(); } -const selectDate = (date: string) => { +const selectDate = async (date: Date) => { selectedDate = date; updateCurrentDate(); - loadDayData(selectedDate); + await loadFoodEntries(date); renderCalendar(); } @@ -343,7 +555,7 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont if (isOtherMonth) { dayElement.classList.add('other-month'); } else { - dayElement.onclick = () => selectDate(dateString); + dayElement.onclick = () => selectDate(dayDate); } // Check if this is today @@ -353,15 +565,15 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont } // Check if this is selected date - if (!isOtherMonth && dateString === selectedDate) { + if (!isOtherMonth && dateString === selectedDate.toISOString().split('T')[0]) { dayElement.classList.add('selected'); } // Check if this day has data - const dayData = getDayData(dateString); + const dayData = []; // FIXME get from AppWrite if (dayData.length > 0) { dayElement.classList.add('has-data'); - const totalCalories = dayData.reduce((sum, entry) => sum + entry.calories, 0); + const totalCalories = 0; // dayData.reduce((sum, entry) => sum + entry.calories, 0); dayElement.innerHTML = `
${day}
@@ -374,5 +586,4 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont return dayElement; } -// Initialize when page loads -document.addEventListener('DOMContentLoaded', init); + diff --git a/src/types.ts b/src/types.ts index a5b91e0..56cbdd9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,7 +4,7 @@ export type FoodItem = { } export type FoodStorage = { - id: number; + id?: string; name: string; grams: number; calories: number; @@ -13,4 +13,5 @@ export type FoodStorage = { carbs: number; fiber: number; time: string; + date: string; };