feat: add calories in the calendar
This commit is contained in:
@@ -164,6 +164,7 @@
|
|||||||
<!-- Hidden inputs for state -->
|
<!-- Hidden inputs for state -->
|
||||||
<input type="hidden" id="foodIdToUpdate">
|
<input type="hidden" id="foodIdToUpdate">
|
||||||
<input type="hidden" id="foodTimeToUpdate">
|
<input type="hidden" id="foodTimeToUpdate">
|
||||||
|
<input type="hidden" id="foodCaloriesToUpdate">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ VITE_APPWRITE_ENDPOINT=here
|
|||||||
VITE_APPWRITE_DBID=here
|
VITE_APPWRITE_DBID=here
|
||||||
VITE_APPWRITE_FOODENTRIESID=here
|
VITE_APPWRITE_FOODENTRIESID=here
|
||||||
VITE_APPWRITE_USERSETTINGSID=here
|
VITE_APPWRITE_USERSETTINGSID=here
|
||||||
|
VITE_APPWRITE_MONTLYCALORIESID=here
|
||||||
+68
-1
@@ -1,5 +1,5 @@
|
|||||||
import { Client, Account, Databases, Query } from 'appwrite';
|
import { Client, Account, Databases, Query } from 'appwrite';
|
||||||
import { FoodStorage, UserSettings } from './types';
|
import { DailyTotalCalories, FoodStorage, UserSettings } from './types';
|
||||||
|
|
||||||
// Initialize Appwrite client
|
// Initialize Appwrite client
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
@@ -16,6 +16,7 @@ export const databases = new Databases(client);
|
|||||||
export const DATABASE_ID = import.meta.env.VITE_APPWRITE_DBID
|
export const DATABASE_ID = import.meta.env.VITE_APPWRITE_DBID
|
||||||
export const FOOD_ENTRIES_COLLECTION_ID = import.meta.env.VITE_APPWRITE_FOODENTRIESID
|
export const FOOD_ENTRIES_COLLECTION_ID = import.meta.env.VITE_APPWRITE_FOODENTRIESID
|
||||||
export const USER_SETTINGS_COLLECTION_ID = import.meta.env.VITE_APPWRITE_USERSETTINGSID
|
export const USER_SETTINGS_COLLECTION_ID = import.meta.env.VITE_APPWRITE_USERSETTINGSID
|
||||||
|
export const MONTHLY_CALORIES_COLLECTION_ID = import.meta.env.VITE_APPWRITE_MONTLYCALORIESID
|
||||||
|
|
||||||
// Auth helper functions
|
// Auth helper functions
|
||||||
export class AppwriteAuth {
|
export class AppwriteAuth {
|
||||||
@@ -160,6 +161,72 @@ export class AppwriteDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all calories for a particular date
|
||||||
|
static async getMonthlyCalories(date: Date) {
|
||||||
|
try {
|
||||||
|
const user = await account.get();
|
||||||
|
const localDateTime = new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString();
|
||||||
|
|
||||||
|
const response = await databases.listDocuments(
|
||||||
|
DATABASE_ID,
|
||||||
|
MONTHLY_CALORIES_COLLECTION_ID,
|
||||||
|
[
|
||||||
|
Query.equal('userId', user.$id),
|
||||||
|
Query.startsWith('date', localDateTime.substring(0, 7)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
console.debug('Food entries retrieved:', response);
|
||||||
|
return response.documents;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Get all calories for month error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async createMonthlyCaloryForDay(date: Date, totalCalories: number) {
|
||||||
|
try {
|
||||||
|
const localDateTime = new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString();
|
||||||
|
|
||||||
|
const response = await databases.createDocument(
|
||||||
|
DATABASE_ID,
|
||||||
|
MONTHLY_CALORIES_COLLECTION_ID ,
|
||||||
|
'unique()',
|
||||||
|
{
|
||||||
|
userId: (await account.get()).$id,
|
||||||
|
date: localDateTime.substring(0, 10),
|
||||||
|
totalCalories: totalCalories
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.debug('User settings saved:', response);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Save user settings error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateMonthlyCaloryForDay(id: string, date: Date, totalCalories: number) {
|
||||||
|
try {
|
||||||
|
const localDateTime = new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString();
|
||||||
|
|
||||||
|
const response = await databases.updateDocument(
|
||||||
|
DATABASE_ID,
|
||||||
|
MONTHLY_CALORIES_COLLECTION_ID ,
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
userId: (await account.get()).$id,
|
||||||
|
date: localDateTime.substring(0, 10),
|
||||||
|
totalCalories: totalCalories
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.debug('User settings saved:', response);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Save user settings error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get user's settings
|
// get user's settings
|
||||||
static async getUserSettings() {
|
static async getUserSettings() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
+108
-19
@@ -1,6 +1,6 @@
|
|||||||
import { foodDatabase } from './foodDatabase.js';
|
import { foodDatabase } from './foodDatabase.js';
|
||||||
import { getButtonById, getButtonListByClassName, getDivById, getInputById, showFoodPreview } from './HtmlUtil.ts';
|
import { getButtonById, getButtonListByClassName, getDivById, getInputById, showFoodPreview } from './HtmlUtil.ts';
|
||||||
import { FoodItem, FoodStorage } from './types.js';
|
import { DailyTotalCalories, FoodItem, FoodStorage } from './types.js';
|
||||||
import { AppwriteAuth, AppwriteDB } from './appwrite.js';
|
import { AppwriteAuth, AppwriteDB } from './appwrite.js';
|
||||||
import swal from 'sweetalert';
|
import swal from 'sweetalert';
|
||||||
import { Models } from 'appwrite';
|
import { Models } from 'appwrite';
|
||||||
@@ -12,6 +12,7 @@ let searchTimeout: number | null = null;
|
|||||||
let selectedFood: FoodItem | null = null; // review here
|
let selectedFood: FoodItem | null = null; // review here
|
||||||
let currentHighlightIndex: number = -1;
|
let currentHighlightIndex: number = -1;
|
||||||
let currentResults: FoodItem[] = [];
|
let currentResults: FoodItem[] = [];
|
||||||
|
let calendarMonthlyCalories: DailyTotalCalories[] = [];
|
||||||
|
|
||||||
// Authentication state
|
// Authentication state
|
||||||
let currentUser: any | null = null;
|
let currentUser: any | null = null;
|
||||||
@@ -45,7 +46,7 @@ async function initializeAuth() {
|
|||||||
|
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
currentUser = await AppwriteAuth.getCurrentUser();
|
currentUser = await AppwriteAuth.getCurrentUser();
|
||||||
showMainApp();
|
await showMainApp();
|
||||||
selectDate(selectedDate);
|
selectDate(selectedDate);
|
||||||
} else {
|
} else {
|
||||||
showAuthForms();
|
showAuthForms();
|
||||||
@@ -87,7 +88,7 @@ async function handleRegister(e: SubmitEvent) {
|
|||||||
await AppwriteAuth.login(email, password);
|
await AppwriteAuth.login(email, password);
|
||||||
currentUser = await AppwriteAuth.getCurrentUser();
|
currentUser = await AppwriteAuth.getCurrentUser();
|
||||||
|
|
||||||
showMainApp();
|
await showMainApp();
|
||||||
selectDate(selectedDate);
|
selectDate(selectedDate);
|
||||||
|
|
||||||
swal('Registration successful! Welcome to Food Tracker.');
|
swal('Registration successful! Welcome to Food Tracker.');
|
||||||
@@ -111,7 +112,7 @@ async function handleLogin(e: SubmitEvent) {
|
|||||||
await AppwriteAuth.login(email, password);
|
await AppwriteAuth.login(email, password);
|
||||||
currentUser = await AppwriteAuth.getCurrentUser();
|
currentUser = await AppwriteAuth.getCurrentUser();
|
||||||
|
|
||||||
showMainApp();
|
await showMainApp();
|
||||||
selectDate(selectedDate);
|
selectDate(selectedDate);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -300,7 +301,7 @@ function showAuthForms() {
|
|||||||
appContent?.classList.add('hidden');
|
appContent?.classList.add('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMainApp() {
|
async function showMainApp() {
|
||||||
authSection?.classList.add('hidden');
|
authSection?.classList.add('hidden');
|
||||||
userInfo?.classList.remove('hidden');
|
userInfo?.classList.remove('hidden');
|
||||||
appContent?.classList.remove('hidden');
|
appContent?.classList.remove('hidden');
|
||||||
@@ -312,6 +313,14 @@ function showMainApp() {
|
|||||||
|
|
||||||
// Update date display
|
// Update date display
|
||||||
updateCurrentDate();
|
updateCurrentDate();
|
||||||
|
|
||||||
|
// Get entries with total
|
||||||
|
const entries = await AppwriteDB.getMonthlyCalories(selectedDate);
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
const calories = parseInt(entry.totalCalories);
|
||||||
|
const day = parseInt(entry.date.substring(8));
|
||||||
|
updateDocumentIdForDay(entry.$id, calories, day);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show/hide loading overlay
|
// Show/hide loading overlay
|
||||||
@@ -645,6 +654,7 @@ async function setFoodToEdit(foodId: string) {
|
|||||||
getButtonById('add-food-btn').innerHTML = 'Save Food';
|
getButtonById('add-food-btn').innerHTML = 'Save Food';
|
||||||
getInputById('foodIdToUpdate').value = foodId;
|
getInputById('foodIdToUpdate').value = foodId;
|
||||||
getInputById('foodTimeToUpdate').value = foodToEdit[0].time;
|
getInputById('foodTimeToUpdate').value = foodToEdit[0].time;
|
||||||
|
getInputById('foodCaloriesToUpdate').value = foodToEdit[0].calories;
|
||||||
|
|
||||||
hideLoading();
|
hideLoading();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -772,6 +782,19 @@ const updateFood = async () => {
|
|||||||
|
|
||||||
// Save to Appwrite
|
// Save to Appwrite
|
||||||
await AppwriteDB.updateFoodEntry(foodId, entry);
|
await AppwriteDB.updateFoodEntry(foodId, entry);
|
||||||
|
|
||||||
|
// Save or update monthly total calories
|
||||||
|
const monthlyDocumentId = getDocumentIdForToday();
|
||||||
|
if (!monthlyDocumentId?.documentId) {
|
||||||
|
const monthlyCreated = await AppwriteDB.createMonthlyCaloryForDay(selectedDate, entry.calories);
|
||||||
|
updateDocumentIdForToday(monthlyCreated.$id, monthlyCreated.totalCalories);
|
||||||
|
} else {
|
||||||
|
const gramsToRemove = parseInt(getInputById('foodCaloriesToUpdate').value);
|
||||||
|
const totalCalories = monthlyDocumentId?.totalCalories + entry.calories - gramsToRemove;
|
||||||
|
const monthlyUpdated = await AppwriteDB.updateMonthlyCaloryForDay(monthlyDocumentId?.documentId, selectedDate, totalCalories);
|
||||||
|
updateDocumentIdForToday(monthlyUpdated.$id, monthlyUpdated.totalCalories);
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
clearEditing();
|
clearEditing();
|
||||||
selectDate(selectedDate);
|
selectDate(selectedDate);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -782,6 +805,43 @@ const updateFood = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getDocumentIdForToday = (): DailyTotalCalories | null => {
|
||||||
|
const today = selectedDate.getDate();
|
||||||
|
const todayRecord = calendarMonthlyCalories.filter(x => x.day === today);
|
||||||
|
if (todayRecord.length > 0) {
|
||||||
|
return todayRecord[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDocumentIdForDay = (day: number): number => {
|
||||||
|
const todayRecord = calendarMonthlyCalories.filter(x => x.day === day);
|
||||||
|
return todayRecord.length > 0 ? todayRecord[0].totalCalories : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateDocumentIdForToday = (id: string, totalCalories: number): void => {
|
||||||
|
const today = selectedDate.getDate();
|
||||||
|
updateDocumentIdForDay(id, totalCalories, today);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateDocumentIdForDay = (id: string, totalCalories: number, day: number): void => {
|
||||||
|
const record = calendarMonthlyCalories.filter(x => x.day === day);
|
||||||
|
if (record.length === 0) {
|
||||||
|
calendarMonthlyCalories.push({
|
||||||
|
documentId: id,
|
||||||
|
day: day,
|
||||||
|
totalCalories: totalCalories
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
calendarMonthlyCalories.forEach((record) => {
|
||||||
|
if (record.day === day) {
|
||||||
|
record.documentId = id;
|
||||||
|
record.totalCalories = totalCalories;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add food to the log
|
// Add food to the log
|
||||||
const addFood = async () => {
|
const addFood = async () => {
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
@@ -827,6 +887,19 @@ const addFood = async () => {
|
|||||||
// Save to Appwrite
|
// Save to Appwrite
|
||||||
const savedEntry = await AppwriteDB.saveFoodEntry(entry);
|
const savedEntry = await AppwriteDB.saveFoodEntry(entry);
|
||||||
|
|
||||||
|
// Save or update monthly total calories
|
||||||
|
const monthlyDocumentId = getDocumentIdForToday();
|
||||||
|
if (!monthlyDocumentId?.documentId) {
|
||||||
|
// create
|
||||||
|
const monthlyCreated = await AppwriteDB.createMonthlyCaloryForDay(selectedDate, entry.calories);
|
||||||
|
updateDocumentIdForToday(monthlyCreated.$id, monthlyCreated.totalCalories);
|
||||||
|
} else {
|
||||||
|
// update
|
||||||
|
const totalCalories = monthlyDocumentId?.totalCalories + entry.calories;
|
||||||
|
const monthlyCreated = await AppwriteDB.updateMonthlyCaloryForDay(monthlyDocumentId?.documentId, selectedDate, totalCalories);
|
||||||
|
updateDocumentIdForToday(monthlyCreated.$id, monthlyCreated.totalCalories);
|
||||||
|
}
|
||||||
|
|
||||||
// Add to HTML table with Appwrite document ID
|
// Add to HTML table with Appwrite document ID
|
||||||
addFoodToTable(entry, savedEntry.$id);
|
addFoodToTable(entry, savedEntry.$id);
|
||||||
|
|
||||||
@@ -849,14 +922,13 @@ const addFood = async () => {
|
|||||||
carboDiv.textContent = (Math.round(totalCarbs * 10) / 10).toString();
|
carboDiv.textContent = (Math.round(totalCarbs * 10) / 10).toString();
|
||||||
fiberDiv.textContent = (Math.round(totalFiber * 10) / 10).toString();
|
fiberDiv.textContent = (Math.round(totalFiber * 10) / 10).toString();
|
||||||
|
|
||||||
// Update alkaline level
|
|
||||||
const div = getDivById('alkaline-level');
|
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
selectedFood = null;
|
selectedFood = null;
|
||||||
getInputById('foodSearchInput').value = '';
|
getInputById('foodSearchInput').value = '';
|
||||||
gramAmount.value = '100';
|
gramAmount.value = '100';
|
||||||
showFoodPreview(false);
|
showFoodPreview(false);
|
||||||
|
await delay(1);
|
||||||
|
renderCalendar();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Add food error:', error);
|
console.error('Add food error:', error);
|
||||||
swal('Oh no!', 'Failed to add food entry: ' + error.message, 'error');
|
swal('Oh no!', 'Failed to add food entry: ' + error.message, 'error');
|
||||||
@@ -980,6 +1052,8 @@ async function loadFoodEntries(date: Date) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const delay = (seconds: number) => new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
||||||
|
|
||||||
// Handle food deletion
|
// Handle food deletion
|
||||||
async function handleDeleteFood(documentId: string) {
|
async function handleDeleteFood(documentId: string) {
|
||||||
if (!documentId) return;
|
if (!documentId) return;
|
||||||
@@ -1001,6 +1075,24 @@ async function handleDeleteFood(documentId: string) {
|
|||||||
// Delete from Appwrite
|
// Delete from Appwrite
|
||||||
await AppwriteDB.deleteFoodEntry(documentId);
|
await AppwriteDB.deleteFoodEntry(documentId);
|
||||||
|
|
||||||
|
// Delete from monthly total
|
||||||
|
const monthlyDocumentId = getDocumentIdForToday();
|
||||||
|
if (monthlyDocumentId?.documentId) {
|
||||||
|
// find calories from document
|
||||||
|
const targetDiv = document.querySelector(`.food-card[data-id="${documentId}"]`);
|
||||||
|
let existingToDelete = 0;
|
||||||
|
if (targetDiv) {
|
||||||
|
const divCalories = targetDiv.querySelectorAll('.calories-display');
|
||||||
|
if (divCalories && divCalories.length > 0) {
|
||||||
|
existingToDelete = parseInt(divCalories[0].innerHTML.replace(' cal', ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const totalCalories = monthlyDocumentId?.totalCalories - existingToDelete;
|
||||||
|
const monthlyCreated = await AppwriteDB.updateMonthlyCaloryForDay(monthlyDocumentId?.documentId, selectedDate, totalCalories);
|
||||||
|
updateDocumentIdForToday(monthlyCreated.$id, monthlyCreated.totalCalories);
|
||||||
|
await delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
selectDate(selectedDate);
|
selectDate(selectedDate);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Delete food error:', error);
|
console.error('Delete food error:', error);
|
||||||
@@ -1172,13 +1264,14 @@ const renderCalendar = async () => {
|
|||||||
|
|
||||||
for (let i = startingDayOfWeek - 1; i >= 0; i--) {
|
for (let i = startingDayOfWeek - 1; i >= 0; i--) {
|
||||||
const day = daysInPrevMonth - i;
|
const day = daysInPrevMonth - i;
|
||||||
const dayElement = createDayElement(day, true, year, month - 1);
|
const dayElement = createDayElement(day, true, year, month - 1, 0);
|
||||||
grid.appendChild(dayElement);
|
grid.appendChild(dayElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add days of current month
|
// Add days of current month
|
||||||
for (let day = 1; day <= daysInMonth; day++) {
|
for (let day = 1; day <= daysInMonth; day++) {
|
||||||
const dayElement = createDayElement(day, false, year, month);
|
const dayCalories = getDocumentIdForDay(day);
|
||||||
|
const dayElement = createDayElement(day, false, year, month, dayCalories);
|
||||||
grid.appendChild(dayElement);
|
grid.appendChild(dayElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1187,12 +1280,12 @@ const renderCalendar = async () => {
|
|||||||
const remainingCells = 42 - totalCells;
|
const remainingCells = 42 - totalCells;
|
||||||
|
|
||||||
for (let day = 1; day <= remainingCells; day++) {
|
for (let day = 1; day <= remainingCells; day++) {
|
||||||
const dayElement = createDayElement(day, true, year, month + 1);
|
const dayElement = createDayElement(day, true, year, month + 1, 0);
|
||||||
grid.appendChild(dayElement);
|
grid.appendChild(dayElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDayElement(day: number, isOtherMonth: boolean, year: number, month: number) {
|
function createDayElement(day: number, isOtherMonth: boolean, year: number, month: number, calories: number) {
|
||||||
const dayElement = document.createElement('div') as HTMLElement;
|
const dayElement = document.createElement('div') as HTMLElement;
|
||||||
dayElement.className = 'calendar-day';
|
dayElement.className = 'calendar-day';
|
||||||
|
|
||||||
@@ -1217,14 +1310,10 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if this day has data
|
// Check if this day has data
|
||||||
const dayData = []; // FIXME get from AppWrite
|
if (calories > 0) {
|
||||||
if (dayData.length > 0) {
|
|
||||||
dayElement.classList.add('has-data');
|
|
||||||
const totalCalories = 0; // dayData.reduce((sum, entry) => sum + entry.calories, 0);
|
|
||||||
|
|
||||||
dayElement.innerHTML = `
|
dayElement.innerHTML = `
|
||||||
<div>${day}</div>
|
${day}
|
||||||
<div class="day-calories">${totalCalories} cal</div>
|
<small class="muted">${calories}</small>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
dayElement.textContent = day.toString();
|
dayElement.textContent = day.toString();
|
||||||
|
|||||||
@@ -27,3 +27,9 @@ export type UserSettings = {
|
|||||||
carboGoal: number;
|
carboGoal: number;
|
||||||
fiberGoal: number;
|
fiberGoal: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DailyTotalCalories = {
|
||||||
|
documentId: string;
|
||||||
|
day: number;
|
||||||
|
totalCalories: number;
|
||||||
|
};
|
||||||
|
|||||||
@@ -685,6 +685,10 @@ label {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.container {
|
.container {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
- [x] Allow search without special characters
|
- [x] Allow search without special characters
|
||||||
- [ ] Get the calories from the last 7 days and display in the calendar
|
- [x] Get the calories from the last 7 days and display in the calendar
|
||||||
- [x] Add food type: Alkaline or Acid
|
- [x] Add food type: Alkaline or Acid
|
||||||
- [x] Display the percentage of Alkaline daily
|
- [x] Display the percentage of Alkaline daily
|
||||||
- [ ] Remove the table and create a list-like component
|
- [x] Remove the table and create a list-like component
|
||||||
- [x] Add logo
|
- [x] Add logo
|
||||||
- [ ] Group food added by time and consider a meal
|
- [ ] Group food added by time and consider a meal
|
||||||
|
|||||||
Reference in New Issue
Block a user