if you dont know what is fasting what are the great health benefits just watch this youtube video and thank me later.
copy paste your openai key to the its place search openai it will popup
change the prompt as you want like give your name age weight..etc search prompt it will popup
everything saved on browser localstorage

this is just simple html page save it somewhere as index.html and bookmark it to your browser or as default home ๐
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>90-Day Fasting Tracker - Dark Mode</title> <!-- Tailwind CSS CDN --> <script src="https://cdn.tailwindcss.com"></script> <!-- Marked.js CDN for Markdown support --> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <style> .day-box { transition: transform 0.1s; position: relative; } .day-box:active { transform: scale(0.95); } /* Container for the stats in the day grid */ .stats-container { position: absolute; bottom: 1px; left: 1px; right: 1px; display: flex; justify-content: center; align-items: center; gap: 4px; font-size: 20px; flex-wrap: wrap; } </style> </head> <body class="bg-gray-900 text-white min-h-screen"> <div class="flex"> <!-- Left Sidebar --> <div id="sidebar" class="w-[220px] border-r border-gray-700 p-4 min-h-screen flex flex-col justify-between"> <div> <h2 class="text-2xl font-bold mb-4">Day Details</h2> <div id="sidebarContent"> <p>Select a day.</p> </div> <!-- Global Save button will be injected here when a day is selected --> <div id="globalSaveContainer"></div> </div> <!-- Controls at the bottom of the left sidebar --> <div id="sidebarControls" class="mt-4"> <a href="#" id="resetLink" class="text-blue-400 hover:underline">Reset Tracker</a> <div class="mt-4 text-xs"> <a href="#" id="exportLink" class="text-blue-400 hover:underline">Export Data</a> | <a href="#" id="importLink" class="text-blue-400 hover:underline">Import Data</a> <div id="exportArea" class="mt-2" style="display: none;"> <textarea id="exportTextarea" class="w-full bg-gray-800 border border-gray-600 rounded p-2 text-xs" rows="4" readonly></textarea> </div> <div id="importArea" class="mt-2" style="display: none;"> <textarea id="importTextarea" class="w-full bg-gray-800 border border-gray-600 rounded p-2 text-xs" rows="4" placeholder="Paste JSON data here"></textarea> <button id="importButton" class="w-full mt-2 px-3 py-1 bg-blue-600 rounded hover:bg-green-700 text-xs">Import</button> </div> </div> </div> </div> <!-- Main Content --> <div class="flex-1 p-4"> <div class="mb-6 flex"> <h1 class="text-3xl font-bold">March to May - 90-Day Fasting Tracker</h1> <!-- Overall Statistics --> <div class="flex-1 shadow rounded p-4 text-right"> <p class="text-xl">Fasting Days: <span id="totalFasting">0</span> / 90</p> <p class="text-lg">Percentage: <span id="percentage">0</span>%</p> </div> </div> <!-- 90-Day Grid --> <div id="grid" class="grid grid-cols-10 gap-2"> <!-- Day boxes are generated here --> </div> </div> <!-- Right Sidebar - Chat UI --> <div id="chatSidebar" class="w-[400px] border-l border-gray-700 p-4 min-h-max flex flex-col"> <div class="flex justify-between items-center mb-4"> <h2 class="text-2xl font-bold">AI Chat</h2> <button id="newChatButton" class="px-2 py-1 bg-blue-600 rounded hover:bg-blue-700 text-xs">+</button> </div> <div id="chatPagination" class="flex justify-between mb-2"> <button id="prevChat" class="px-2 py-1 bg-gray-700 rounded hover:bg-gray-600 text-xs">Prev Chat</button> <button id="nextChat" class="px-2 py-1 bg-gray-700 rounded hover:bg-gray-600 text-xs">Next Chat</button> </div> <!-- The important change: we add a max height so the chat gets a scrollbar --> <div id="chatContainer" class="flex-1 overflow-y-auto mb-4 bg-gray-800 p-2 rounded max-h-[770px]" ></div> <textarea id="chatInput" placeholder="Type your message and press Enter" class="w-full bg-gray-700 border border-gray-600 rounded p-2 text-md" rows="3"></textarea> </div> </div> <script> // ===================== Fasting Tracker Code ===================== const totalDays = 90; // Set the start date as March 1, 2023 const startDate = new Date("2023-03-01"); const endDate = new Date(startDate); endDate.setDate(startDate.getDate() + totalDays - 1); // Determine today's date and its index relative to startDate const today = new Date(); let currentDayIndex = Math.floor((today - startDate) / (1000 * 60 * 60 * 24)); if (currentDayIndex < 0 || currentDayIndex >= totalDays) { currentDayIndex = null; } // Update header to show the dynamic date range document.querySelector("h1.text-3xl").textContent = startDate.toLocaleDateString('en-US', { month: 'long', day: 'numeric' }) + " to " + endDate.toLocaleDateString('en-US', { month: 'long', day: 'numeric' }) + " - 90-Day Fasting Tracker"; const gridElement = document.getElementById("grid"); const totalFastingElement = document.getElementById("totalFasting"); const percentageElement = document.getElementById("percentage"); const sidebarContent = document.getElementById("sidebarContent"); const globalSaveContainer = document.getElementById("globalSaveContainer"); // Global variable for selected day index (0-based) let selectedDay = null; // Data structure for each day: // { fasting: boolean, notes: [ { icon: string, text: string } ], weight: string, keton: string, steps: string } let fastingData = JSON.parse(localStorage.getItem("fastingData")) || Array.from({ length: totalDays }, () => ({ fasting: false, notes: [], weight: "", keton: "", steps: "" })); // Save data to localStorage function saveData() { localStorage.setItem("fastingData", JSON.stringify(fastingData)); } // Update overall statistics function updateStats() { const fastingCount = fastingData.filter(day => day.fasting).length; totalFastingElement.textContent = fastingCount; percentageElement.textContent = ((fastingCount / totalDays) * 100).toFixed(0); } // Render the 90-day grid function renderGrid() { gridElement.innerHTML = ""; fastingData.forEach((dayData, index) => { const box = document.createElement("div"); box.className = `day-box cursor-pointer border rounded flex flex-col items-center justify-center h-[86px] relative transition-all ${dayData.fasting ? "bg-blue-600 border-green-800" : "bg-gray-700 border-gray-600 hover:bg-gray-600"}`; // Day number at top left const dayNum = document.createElement("span"); dayNum.className = "absolute top-1 left-1 text-xs"; dayNum.textContent = index + 1; box.appendChild(dayNum); // Calculate the actual date for this day and display it at the bottom right const currentDate = new Date(startDate); currentDate.setDate(startDate.getDate() + index); const dateSpan = document.createElement("span"); dateSpan.className = "absolute top-1 right-1 text-[9px]"; dateSpan.textContent = currentDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); box.appendChild(dateSpan); // If this day is today, add a "Today" label if (currentDayIndex !== null && index === currentDayIndex) { const todayLabel = document.createElement("span"); todayLabel.className = "absolute top-1 right-1 text-xs text-green-400"; todayLabel.textContent = "Today"; box.appendChild(todayLabel); } // Stats container at bottom center (shows note icon, weight, keton, and steps) const statsContainer = document.createElement("div"); statsContainer.className = "stats-container"; dayData.notes.forEach(note => { if (note.icon) { const noteIcon = document.createElement("span"); noteIcon.textContent = note.icon; statsContainer.appendChild(noteIcon); } }); if (dayData.weight) { const weightSpan = document.createElement("span"); weightSpan.innerHTML = 'โ ' + dayData.weight; statsContainer.appendChild(weightSpan); } if (dayData.keton) { const ketonSpan = document.createElement("span"); ketonSpan.innerHTML = '๐งช ' + dayData.keton; statsContainer.appendChild(ketonSpan); } if (dayData.steps) { const stepsSpan = document.createElement("span"); stepsSpan.innerHTML = '' + dayData.steps; statsContainer.appendChild(stepsSpan); } if (statsContainer.children.length > 0) { box.appendChild(statsContainer); } // Highlight if selected if (selectedDay === index) { box.classList.add("ring", "ring-yellow-500", "ring-2"); } // Click event to select a day box.addEventListener("click", () => { selectedDay = index; renderSidebar(); renderGrid(); }); gridElement.appendChild(box); }); } // Render the sidebar with day details (stats and notes) function renderSidebar() { if (selectedDay === null) { sidebarContent.innerHTML = "<p>Select a day from the grid.</p>"; globalSaveContainer.innerHTML = ""; return; } const dayData = fastingData[selectedDay]; sidebarContent.innerHTML = ""; // Header const header = document.createElement("h3"); header.className = "text-xl font-semibold mb-2"; header.textContent = `Day ${selectedDay + 1} Details`; sidebarContent.appendChild(header); // Display the corresponding date for the selected day const currentDate = new Date(startDate); currentDate.setDate(startDate.getDate() + selectedDay); const dateInfo = document.createElement("p"); dateInfo.className = "text-sm text-gray-400"; dateInfo.textContent = "Date: " + currentDate.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }); sidebarContent.appendChild(dateInfo); // If the selected day is today, add a note indicating so if (currentDayIndex !== null && selectedDay === currentDayIndex) { const todayNote = document.createElement("p"); todayNote.className = "text-sm text-green-400"; todayNote.textContent = "This is today!"; sidebarContent.appendChild(todayNote); } // Fasting toggle const fastingDiv = document.createElement("div"); fastingDiv.className = "mb-4"; const fastingLabel = document.createElement("span"); fastingLabel.className = "mr-2"; fastingLabel.textContent = "Fasting:"; const fastingStatus = document.createElement("span"); fastingStatus.className = "font-bold mr-4"; fastingStatus.textContent = dayData.fasting ? "Yes" : "No"; const toggleFasting = document.createElement("button"); toggleFasting.className = "px-3 py-1 bg-blue-600 rounded hover:bg-blue-700 text-xs"; toggleFasting.textContent = "Toggle"; toggleFasting.addEventListener("click", () => { dayData.fasting = !dayData.fasting; saveData(); updateStats(); renderGrid(); renderSidebar(); }); fastingDiv.appendChild(fastingLabel); fastingDiv.appendChild(fastingStatus); fastingDiv.appendChild(toggleFasting); sidebarContent.appendChild(fastingDiv); // Daily Stats section const statsDiv = document.createElement("div"); statsDiv.className = "mb-4"; const statsHeader = document.createElement("h4"); statsHeader.className = "text-lg font-semibold mb-2"; statsHeader.textContent = "Daily Stats:"; statsDiv.appendChild(statsHeader); // Weight input const weightLabel = document.createElement("label"); weightLabel.className = "block mb-1"; weightLabel.textContent = "Weight:"; statsDiv.appendChild(weightLabel); const weightInput = document.createElement("input"); weightInput.type = "number"; weightInput.value = dayData.weight; weightInput.placeholder = "Enter weight"; weightInput.className = "w-full bg-gray-700 border border-gray-600 rounded p-1 mb-2 text-xs"; statsDiv.appendChild(weightInput); // Keton input const ketonLabel = document.createElement("label"); ketonLabel.className = "block mb-1"; ketonLabel.textContent = "Keton Value:"; statsDiv.appendChild(ketonLabel); const ketonInput = document.createElement("input"); ketonInput.type = "number"; ketonInput.value = dayData.keton; ketonInput.placeholder = "Enter keton value"; ketonInput.className = "w-full bg-gray-700 border border-gray-600 rounded p-1 mb-2 text-xs"; statsDiv.appendChild(ketonInput); // Step Count input const stepsLabel = document.createElement("label"); stepsLabel.className = "block mb-1"; stepsLabel.textContent = "Step Count:"; statsDiv.appendChild(stepsLabel); const stepsInput = document.createElement("input"); stepsInput.type = "number"; stepsInput.value = dayData.steps; stepsInput.placeholder = "Enter step count"; stepsInput.className = "w-full bg-gray-700 border border-gray-600 rounded p-1 mb-2 text-xs"; statsDiv.appendChild(stepsInput); sidebarContent.appendChild(statsDiv); // Notes section const notesHeader = document.createElement("h4"); notesHeader.className = "text-lg font-semibold mb-2"; notesHeader.textContent = "Notes:"; sidebarContent.appendChild(notesHeader); const notesList = document.createElement("div"); notesList.className = "space-y-2 mb-4"; if (dayData.notes.length === 0) { const noNotes = document.createElement("p"); noNotes.className = "text-sm text-gray-400"; noNotes.textContent = "No notes yet."; notesList.appendChild(noNotes); } else { dayData.notes.forEach((note, idx) => { const noteDiv = document.createElement("div"); noteDiv.className = "flex items-center justify-between bg-gray-800 p-2 rounded"; const noteContent = document.createElement("div"); noteContent.className = "flex items-center space-x-2"; if (note.icon) { const iconSpan = document.createElement("span"); iconSpan.textContent = note.icon; noteContent.appendChild(iconSpan); } let textElem = document.createElement("span"); textElem.className = "note-text"; textElem.dataset.index = idx; textElem.textContent = note.text; noteContent.appendChild(textElem); noteDiv.appendChild(noteContent); const btnContainer = document.createElement("div"); btnContainer.className = "flex items-center space-x-2 text-xs"; const editBtn = document.createElement("button"); editBtn.className = "text-blue-400 hover:text-blue-600"; editBtn.textContent = "Edit"; editBtn.addEventListener("click", () => { if (textElem.tagName.toLowerCase() === "input") { const spanElem = document.createElement("span"); spanElem.className = "note-text"; spanElem.dataset.index = idx; spanElem.textContent = dayData.notes[idx].text; noteContent.replaceChild(spanElem, textElem); textElem = spanElem; editBtn.textContent = "Edit"; } else { const inputField = document.createElement("input"); inputField.type = "text"; inputField.value = note.text; inputField.className = "note-edit-input text-xs bg-gray-700 border border-gray-600 rounded p-1 text-white"; inputField.dataset.index = idx; noteContent.replaceChild(inputField, textElem); textElem = inputField; editBtn.textContent = "Cancel"; } }); btnContainer.appendChild(editBtn); const deleteBtn = document.createElement("button"); deleteBtn.className = "text-red-400 hover:text-red-600"; deleteBtn.textContent = "Delete"; deleteBtn.addEventListener("click", () => { dayData.notes.splice(idx, 1); saveData(); renderSidebar(); renderGrid(); }); btnContainer.appendChild(deleteBtn); noteDiv.appendChild(btnContainer); notesList.appendChild(noteDiv); }); } sidebarContent.appendChild(notesList); const toggleNoteFormButton = document.createElement("button"); toggleNoteFormButton.className = "w-full px-3 py-2 bg-blue-600 rounded hover:bg-green-700 mb-2 text-xs"; toggleNoteFormButton.textContent = "Add Note +"; sidebarContent.appendChild(toggleNoteFormButton); const noteFormDiv = document.createElement("div"); noteFormDiv.style.display = "none"; noteFormDiv.className = "space-y-2 mb-4"; const newNoteIconInput = document.createElement("input"); newNoteIconInput.type = "text"; newNoteIconInput.placeholder = "Enter icon (emoji)"; newNoteIconInput.className = "w-full bg-gray-700 border border-gray-600 rounded p-1 mb-2 text-xs"; noteFormDiv.appendChild(newNoteIconInput); const newNoteTextarea = document.createElement("textarea"); newNoteTextarea.placeholder = "Enter your note here..."; newNoteTextarea.className = "w-full bg-gray-700 border border-gray-600 rounded p-2 text-xs"; noteFormDiv.appendChild(newNoteTextarea); sidebarContent.appendChild(noteFormDiv); toggleNoteFormButton.addEventListener("click", () => { if (noteFormDiv.style.display === "none" || noteFormDiv.style.display === "") { noteFormDiv.style.display = "block"; toggleNoteFormButton.textContent = "Hide Note Form"; } else { noteFormDiv.style.display = "none"; toggleNoteFormButton.textContent = "Add Note +"; } }); globalSaveContainer.innerHTML = ""; const globalSaveBtn = document.createElement("button"); globalSaveBtn.className = "w-full px-3 py-2 bg-blue-600 rounded hover:bg-blue-700 mt-4 text-xs"; globalSaveBtn.textContent = "Save Changes"; globalSaveBtn.addEventListener("click", () => { dayData.weight = weightInput.value; dayData.keton = ketonInput.value; dayData.steps = stepsInput.value; const editInputs = sidebarContent.querySelectorAll(".note-edit-input"); editInputs.forEach(input => { const idx = input.dataset.index; if (input.value.trim() !== "") { dayData.notes[idx].text = input.value.trim(); } }); if (noteFormDiv.style.display === "block") { const newNoteText = newNoteTextarea.value.trim(); const newNoteIcon = newNoteIconInput.value.trim(); if (newNoteText !== "") { dayData.notes.push({ icon: newNoteIcon, text: newNoteText }); } newNoteTextarea.value = ""; newNoteIconInput.value = ""; } saveData(); updateStats(); renderSidebar(); renderGrid(); }); globalSaveContainer.appendChild(globalSaveBtn); } document.getElementById("resetLink").addEventListener("click", (e) => { e.preventDefault(); if (confirm("Are you sure you want to reset your tracker?")) { fastingData = Array.from({ length: totalDays }, () => ({ fasting: false, notes: [], weight: "", keton: "", steps: "" })); selectedDay = null; saveData(); updateStats(); renderGrid(); renderSidebar(); } }); document.getElementById("exportLink").addEventListener("click", (e) => { e.preventDefault(); const exportArea = document.getElementById("exportArea"); const exportTextarea = document.getElementById("exportTextarea"); exportTextarea.value = JSON.stringify(fastingData, null, 2); exportArea.style.display = exportArea.style.display === "none" ? "block" : "none"; }); document.getElementById("importLink").addEventListener("click", (e) => { e.preventDefault(); const importArea = document.getElementById("importArea"); importArea.style.display = importArea.style.display === "none" ? "block" : "none"; }); document.getElementById("importButton").addEventListener("click", () => { const importTextarea = document.getElementById("importTextarea"); try { const importedData = JSON.parse(importTextarea.value); if (Array.isArray(importedData)) { fastingData = importedData; saveData(); renderGrid(); renderSidebar(); alert("Data imported successfully!"); } else { alert("Invalid data format."); } } catch (error) { alert("Error parsing JSON."); } }); updateStats(); renderGrid(); // ===================== Chat UI and OpenAI API Integration ===================== function getDate() { return new Date().toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }); } function getDateTime() { const now = new Date(); return now.toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); } const openaiApiKey = 'YOUR_OPENAI_KEY'; let chatSessions = JSON.parse(localStorage.getItem("chatSessions")) || []; if (chatSessions.length === 0) { chatSessions.push({ id: Date.now(), messages: [] }); } let currentChatIndex = chatSessions.length - 1; const chatContainer = document.getElementById("chatContainer"); const chatInput = document.getElementById("chatInput"); const newChatButton = document.getElementById("newChatButton"); const prevChatButton = document.getElementById("prevChat"); const nextChatButton = document.getElementById("nextChat"); function saveChats() { localStorage.setItem("chatSessions", JSON.stringify(chatSessions)); } function renderChat() { chatContainer.innerHTML = ""; const currentChat = chatSessions[currentChatIndex]; currentChat.messages.forEach(msg => { const msgDiv = document.createElement("div"); if (msg.sender === "ai") { msgDiv.className = "bg-gray-600 p-2 rounded mb-2"; msgDiv.innerHTML = marked.parse(msg.text); } else { msgDiv.className = "bg-blue-600 p-2 rounded mb-2 text-right"; msgDiv.textContent = msg.text; } chatContainer.appendChild(msgDiv); }); chatContainer.scrollTop = chatContainer.scrollHeight; } async function sendMessage(message) { chatSessions[currentChatIndex].messages.push({ sender: "user", text: message }); renderChat(); saveChats(); // Build the system prompt to include ALL days data const systemMessage = { role: "system", content: "my name is sinan. this is my 90 day tracking for fasting and walking step data starting from " + startDate.toLocaleDateString('en-US') + ". today is " + getDateTime() + ". all days data: " + JSON.stringify(fastingData) + ". please be personal with me and always give me very very positive messages." }; // Prepare the conversation array const conversation = [{ role: "system", content: systemMessage.content }]; chatSessions[currentChatIndex].messages.forEach(msg => { conversation.push({ role: msg.sender === "user" ? "user" : "assistant", content: msg.text }); }); try { const response = await fetch("https://api.openai.com/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + openaiApiKey }, body: JSON.stringify({ model: "gpt-4o-mini", messages: conversation, temperature: 0.7 }) }); const data = await response.json(); if (!data.choices || data.choices.length === 0) { console.error("Unexpected API response:", data); return; } const aiMessage = data.choices[0].message.content.trim(); chatSessions[currentChatIndex].messages.push({ sender: "ai", text: aiMessage }); renderChat(); saveChats(); } catch (error) { console.error("Error calling OpenAI API:", error); } } chatInput.addEventListener("keydown", (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); const message = chatInput.value.trim(); if (message !== "") { chatInput.value = ""; sendMessage(message); } } }); newChatButton.addEventListener("click", () => { chatSessions.push({ id: Date.now(), messages: [] }); currentChatIndex = chatSessions.length - 1; renderChat(); saveChats(); }); prevChatButton.addEventListener("click", () => { if (currentChatIndex > 0) { currentChatIndex--; renderChat(); } }); nextChatButton.addEventListener("click", () => { if (currentChatIndex < chatSessions.length - 1) { currentChatIndex++; renderChat(); } }); renderChat(); </script> </body> </html>