Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Multi-Functional Dashboard</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| /* Custom CSS for features not covered by Tailwind */ | |
| .calendar-day:hover { | |
| background-color: #e2e8f0; | |
| cursor: pointer; | |
| } | |
| .calendar-day.selected { | |
| background-color: #3b82f6; | |
| color: white; | |
| } | |
| .task-completed { | |
| text-decoration: line-through; | |
| color: #9ca3af; | |
| } | |
| #calculator { | |
| grid-template-columns: repeat(4, 1fr); | |
| } | |
| .calculator-btn { | |
| transition: all 0.2s; | |
| } | |
| .calculator-btn:hover { | |
| transform: scale(1.05); | |
| } | |
| .transaction-income { | |
| border-left: 4px solid #10b981; | |
| } | |
| .transaction-expense { | |
| border-left: 4px solid #ef4444; | |
| } | |
| .tab-content { | |
| display: none; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100"> | |
| <!-- Auth Modal --> | |
| <div id="authModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> | |
| <div class="bg-white rounded-lg p-6 w-full max-w-md"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-2xl font-bold" id="authModalTitle">Login</h2> | |
| <button onclick="closeAuthModal()" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div id="authTabs" class="flex border-b mb-4"> | |
| <button class="px-4 py-2 font-medium text-blue-500 border-b-2 border-blue-500" onclick="switchAuthTab('login')">Login</button> | |
| <button class="px-4 py-2 font-medium text-gray-500" onclick="switchAuthTab('signup')">Sign Up</button> | |
| </div> | |
| <form id="loginForm" class="space-y-4"> | |
| <div> | |
| <label for="loginEmail" class="block text-sm font-medium text-gray-700">Email</label> | |
| <input type="email" id="loginEmail" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <div> | |
| <label for="loginPassword" class="block text-sm font-medium text-gray-700">Password</label> | |
| <input type="password" id="loginPassword" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <button type="button" onclick="loginUser()" class="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 transition">Login</button> | |
| </form> | |
| <form id="signupForm" class="space-y-4 hidden"> | |
| <div> | |
| <label for="signupName" class="block text-sm font-medium text-gray-700">Name</label> | |
| <input type="text" id="signupName" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <div> | |
| <label for="signupEmail" class="block text-sm font-medium text-gray-700">Email</label> | |
| <input type="email" id="signupEmail" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <div> | |
| <label for="signupPassword" class="block text-sm font-medium text-gray-700">Password</label> | |
| <input type="password" id="signupPassword" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <div> | |
| <label for="signupConfirmPassword" class="block text-sm font-medium text-gray-700">Confirm Password</label> | |
| <input type="password" id="signupConfirmPassword" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <button type="button" onclick="registerUser()" class="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 transition">Sign Up</button> | |
| </form> | |
| </div> | |
| </div> | |
| <!-- Main App Container --> | |
| <div class="container mx-auto p-4" id="appContainer" style="display: none;"> | |
| <!-- Header --> | |
| <header class="flex justify-between items-center mb-8"> | |
| <h1 class="text-3xl font-bold text-blue-600">My Dashboard</h1> | |
| <div class="flex items-center space-x-4"> | |
| <span id="userGreeting" class="font-medium"></span> | |
| <button onclick="logoutUser()" class="bg-red-500 text-white px-4 py-2 rounded-md hover:bg-red-600 transition">Logout</button> | |
| </div> | |
| </header> | |
| <!-- Navigation Tabs --> | |
| <div class="flex border-b mb-6"> | |
| <button class="px-4 py-2 font-medium text-blue-500 border-b-2 border-blue-500" onclick="switchTab('dashboard')">Dashboard</button> | |
| <button class="px-4 py-2 font-medium text-gray-500" onclick="switchTab('calendar')">Calendar</button> | |
| <button class="px-4 py-2 font-medium text-gray-500" onclick="switchTab('todo')">To-Do</button> | |
| <button class="px-4 py-2 font-medium text-gray-500" onclick="switchTab('notes')">Notes</button> | |
| <button class="px-4 py-2 font-medium text-gray-500" onclick="switchTab('music')">Music</button> | |
| <button class="px-4 py-2 font-medium text-gray-500" onclick="switchTab('transactions')">Transactions</button> | |
| <button class="px-4 py-2 font-medium text-gray-500" onclick="switchTab('calculator')">Calculator</button> | |
| </div> | |
| <!-- Dashboard Tab --> | |
| <div id="dashboard" class="tab-content active"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| <!-- Calendar Widget --> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <h2 class="text-xl font-bold mb-4">Today's Date</h2> | |
| <div id="dashboardCalendar" class="text-center"> | |
| <div class="text-2xl font-bold" id="dashboardDate"></div> | |
| <div class="text-gray-500" id="dashboardDay"></div> | |
| </div> | |
| </div> | |
| <!-- Todo Summary --> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <h2 class="text-xl font-bold mb-4">Tasks Summary</h2> | |
| <div id="todoSummary"> | |
| <p>Loading tasks...</p> | |
| </div> | |
| </div> | |
| <!-- Recent Transactions --> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <h2 class="text-xl font-bold mb-4">Recent Transactions</h2> | |
| <div id="transactionSummary"> | |
| <p>Loading transactions...</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Calendar Tab --> | |
| <div id="calendar" class="tab-content"> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-bold">Calendar</h2> | |
| <div class="flex space-x-2"> | |
| <button onclick="prevMonth()" class="bg-gray-200 p-2 rounded-md hover:bg-gray-300"> | |
| <i class="fas fa-chevron-left"></i> | |
| </button> | |
| <button onclick="nextMonth()" class="bg-gray-200 p-2 rounded-md hover:bg-gray-300"> | |
| <i class="fas fa-chevron-right"></i> | |
| </button> | |
| <button onclick="goToToday()" class="bg-blue-500 text-white px-3 py-1 rounded-md hover:bg-blue-600"> | |
| Today | |
| </button> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <h3 class="text-lg font-semibold" id="currentMonthYear"></h3> | |
| </div> | |
| <div class="grid grid-cols-7 gap-1"> | |
| <div class="text-center font-medium py-2">Sun</div> | |
| <div class="text-center font-medium py-2">Mon</div> | |
| <div class="text-center font-medium py-2">Tue</div> | |
| <div class="text-center font-medium py-2">Wed</div> | |
| <div class="text-center font-medium py-2">Thu</div> | |
| <div class="text-center font-medium py-2">Fri</div> | |
| <div class="text-center font-medium py-2">Sat</div> | |
| </div> | |
| <div class="grid grid-cols-7 gap-1" id="calendarDays"></div> | |
| </div> | |
| </div> | |
| <!-- Todo Tab --> | |
| <div id="todo" class="tab-content"> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <h2 class="text-xl font-bold mb-4">To-Do List</h2> | |
| <div class="flex mb-4"> | |
| <input type="text" id="newTaskInput" placeholder="Add a new task..." class="flex-grow p-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <button onclick="addTask()" class="bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600 transition">Add</button> | |
| </div> | |
| <div class="space-y-2" id="taskList"> | |
| <!-- Tasks will be loaded here --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Notes Tab --> | |
| <div id="notes" class="tab-content"> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <h2 class="text-xl font-bold mb-4">Notes</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6" id="notesList"> | |
| <!-- Notes will be loaded here --> | |
| </div> | |
| <div class="mt-4"> | |
| <h3 class="text-lg font-semibold mb-2">Add New Note</h3> | |
| <input type="text" id="noteTitle" placeholder="Note title" class="w-full p-2 border rounded-md mb-2 focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <textarea id="noteContent" placeholder="Note content" class="w-full p-2 border rounded-md h-32 focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea> | |
| <button onclick="saveNote()" class="mt-2 bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 transition">Save Note</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Music Tab --> | |
| <div id="music" class="tab-content"> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <h2 class="text-xl font-bold mb-4">Music Collection</h2> | |
| <div class="flex mb-4"> | |
| <input type="text" id="songSearch" placeholder="Search songs..." class="flex-grow p-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <button onclick="searchSongs()" class="bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600 transition">Search</button> | |
| </div> | |
| <div class="mb-4"> | |
| <button onclick="showAddSongForm()" class="bg-green-500 text-white px-4 py-2 rounded-md hover:bg-green-600 transition">Add New Song</button> | |
| </div> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full bg-white"> | |
| <thead> | |
| <tr> | |
| <th class="py-2 px-4 border-b">Title</th> | |
| <th class="py-2 px-4 border-b">Artist</th> | |
| <th class="py-2 px-4 border-b">Album</th> | |
| <th class="py-2 px-4 border-b">Year</th> | |
| <th class="py-2 px-4 border-b">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="songsTable"> | |
| <!-- Songs will be loaded here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Add Song Form (Hidden by default) --> | |
| <div id="addSongForm" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-40 hidden"> | |
| <div class="bg-white rounded-lg p-6 w-full max-w-md"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-bold">Add New Song</h2> | |
| <button onclick="hideAddSongForm()" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="songTitle" class="block text-sm font-medium text-gray-700">Title</label> | |
| <input type="text" id="songTitle" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <div> | |
| <label for="songArtist" class="block text-sm font-medium text-gray-700">Artist</label> | |
| <input type="text" id="songArtist" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <div> | |
| <label for="songAlbum" class="block text-sm font-medium text-gray-700">Album</label> | |
| <input type="text" id="songAlbum" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <div> | |
| <label for="songYear" class="block text-sm font-medium text-gray-700">Year</label> | |
| <input type="number" id="songYear" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <div> | |
| <label for="songGenre" class="block text-sm font-medium text-gray-700">Genre</label> | |
| <input type="text" id="songGenre" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2 border"> | |
| </div> | |
| <button onclick="addNewSong()" class="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 transition">Save Song</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Transactions Tab --> | |
| <div id="transactions" class="tab-content"> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <h2 class="text-xl font-bold mb-4">Daily Transactions</h2> | |
| <div class="mb-6"> | |
| <div class="flex mb-4"> | |
| <select id="transactionType" class="p-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <option value="income">Income</option> | |
| <option value="expense">Expense</option> | |
| </select> | |
| <input type="number" id="transactionAmount" placeholder="Amount" class="p-2 border-t border-b focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <select id="transactionCategory" class="p-2 border-t border-b focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <option value="salary">Salary</option> | |
| <option value="freelance">Freelance</option> | |
| <option value="investment">Investment</option> | |
| <option value="food">Food</option> | |
| <option value="transport">Transport</option> | |
| <option value="entertainment">Entertainment</option> | |
| <option value="shopping">Shopping</option> | |
| <option value="other">Other</option> | |
| </select> | |
| <input type="text" id="transactionDescription" placeholder="Description" class="p-2 border-t border-b focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <button onclick="addTransaction()" class="bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600 transition">Add</button> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <h3 class="text-lg font-semibold">Transaction History</h3> | |
| <div class="flex justify-between items-center mb-2"> | |
| <div> | |
| <label for="transactionMonth" class="mr-2">Month:</label> | |
| <select id="transactionMonth" class="p-1 border rounded"> | |
| <option value="01">January</option> | |
| <option value="02">February</option> | |
| <option value="03">March</option> | |
| <option value="04">April</option> | |
| <option value="05">May</option> | |
| <option value="06">June</option> | |
| <option value="07">July</option> | |
| <option value="08">August</option> | |
| <option value="09">September</option> | |
| <option value="10">October</option> | |
| <option value="11">November</option> | |
| <option value="12">December</option> | |
| </select> | |
| </div> | |
| <button onclick="loadTransactions()" class="bg-blue-500 text-white px-3 py-1 rounded-md hover:bg-blue-600">Filter</button> | |
| </div> | |
| </div> | |
| <div class="space-y-2" id="transactionsList"> | |
| <!-- Transactions will be loaded here --> | |
| </div> | |
| <div class="mt-4 p-4 bg-gray-100 rounded-md"> | |
| <h3 class="text-lg font-semibold mb-2">Summary</h3> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div> | |
| <p class="text-green-600 font-medium">Total Income: <span id="totalIncome">₹0</span></p> | |
| </div> | |
| <div> | |
| <p class="text-red-600 font-medium">Total Expenses: <span id="totalExpenses">₹0</span></p> | |
| </div> | |
| <div class="col-span-2"> | |
| <p class="font-bold">Balance: <span id="balance">₹0</span></p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Calculator Tab --> | |
| <div id="calculator" class="tab-content"> | |
| <div class="bg-white rounded-lg shadow p-4 max-w-md mx-auto"> | |
| <h2 class="text-xl font-bold mb-4 text-center">Calculator</h2> | |
| <div class="mb-4"> | |
| <input type="text" id="calculatorDisplay" class="w-full p-4 text-right text-2xl border rounded-md bg-gray-50" readonly> | |
| </div> | |
| <div class="grid grid-cols-4 gap-2" id="calculatorButtons"> | |
| <button onclick="appendToDisplay('7')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">7</button> | |
| <button onclick="appendToDisplay('8')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">8</button> | |
| <button onclick="appendToDisplay('9')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">9</button> | |
| <button onclick="clearDisplay()" class="calculator-btn bg-red-500 text-white p-4 rounded-md hover:bg-red-600">C</button> | |
| <button onclick="appendToDisplay('4')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">4</button> | |
| <button onclick="appendToDisplay('5')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">5</button> | |
| <button onclick="appendToDisplay('6')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">6</button> | |
| <button onclick="appendToDisplay('+')" class="calculator-btn bg-blue-500 text-white p-4 rounded-md hover:bg-blue-600">+</button> | |
| <button onclick="appendToDisplay('1')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">1</button> | |
| <button onclick="appendToDisplay('2')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">2</button> | |
| <button onclick="appendToDisplay('3')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">3</button> | |
| <button onclick="appendToDisplay('-')" class="calculator-btn bg-blue-500 text-white p-4 rounded-md hover:bg-blue-600">-</button> | |
| <button onclick="appendToDisplay('0')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">0</button> | |
| <button onclick="appendToDisplay('.')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">.</button> | |
| <button onclick="calculate()" class="calculator-btn bg-green-500 text-white p-4 rounded-md hover:bg-green-600">=</button> | |
| <button onclick="appendToDisplay('*')" class="calculator-btn bg-blue-500 text-white p-4 rounded-md hover:bg-blue-600">×</button> | |
| <button onclick="appendToDisplay('/')" class="calculator-btn bg-blue-500 text-white p-4 rounded-md hover:bg-blue-600">÷</button> | |
| <button onclick="backspace()" class="calculator-btn bg-gray-300 p-4 rounded-md hover:bg-gray-400">⌫</button> | |
| <button onclick="appendToDisplay('(')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">(</button> | |
| <button onclick="appendToDisplay(')')" class="calculator-btn bg-gray-200 p-4 rounded-md hover:bg-gray-300">)</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Global variables | |
| let currentUser = null; | |
| let currentDate = new Date(); | |
| let currentMonth = currentDate.getMonth(); | |
| let currentYear = currentDate.getFullYear(); | |
| // Initialize the app when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', function() { | |
| checkAuthStatus(); | |
| updateDashboardDate(); | |
| generateCalendar(currentMonth, currentYear); | |
| // Show auth modal if not logged in | |
| if (!currentUser) { | |
| document.getElementById('authModal').classList.remove('hidden'); | |
| } | |
| }); | |
| // Authentication functions | |
| function checkAuthStatus() { | |
| // In a real app, this would check localStorage or cookies for auth token | |
| // For demo purposes, we'll just check if currentUser is set | |
| if (currentUser) { | |
| document.getElementById('appContainer').style.display = 'block'; | |
| document.getElementById('authModal').classList.add('hidden'); | |
| document.getElementById('userGreeting').textContent = `Hello, ${currentUser.name}`; | |
| loadInitialData(); | |
| } else { | |
| document.getElementById('appContainer').style.display = 'none'; | |
| } | |
| } | |
| function switchAuthTab(tab) { | |
| if (tab === 'login') { | |
| document.getElementById('loginForm').classList.remove('hidden'); | |
| document.getElementById('signupForm').classList.add('hidden'); | |
| document.querySelector('#authTabs button:first-child').classList.add('text-blue-500', 'border-blue-500'); | |
| document.querySelector('#authTabs button:first-child').classList.remove('text-gray-500'); | |
| document.querySelector('#authTabs button:last-child').classList.add('text-gray-500'); | |
| document.querySelector('#authTabs button:last-child').classList.remove('text-blue-500', 'border-blue-500'); | |
| document.getElementById('authModalTitle').textContent = 'Login'; | |
| } else { | |
| document.getElementById('loginForm').classList.add('hidden'); | |
| document.getElementById('signupForm').classList.remove('hidden'); | |
| document.querySelector('#authTabs button:first-child').classList.add('text-gray-500'); | |
| document.querySelector('#authTabs button:first-child').classList.remove('text-blue-500', 'border-blue-500'); | |
| document.querySelector('#authTabs button:last-child').classList.add('text-blue-500', 'border-blue-500'); | |
| document.querySelector('#authTabs button:last-child').classList.remove('text-gray-500'); | |
| document.getElementById('authModalTitle').textContent = 'Sign Up'; | |
| } | |
| } | |
| function closeAuthModal() { | |
| document.getElementById('authModal').classList.add('hidden'); | |
| } | |
| function loginUser() { | |
| const email = document.getElementById('loginEmail').value; | |
| const password = document.getElementById('loginPassword').value; | |
| if (!email || !password) { | |
| alert('Please enter both email and password'); | |
| return; | |
| } | |
| // In a real app, this would make an API call to your PHP backend | |
| // For demo purposes, we'll simulate a successful login | |
| simulateApiCall('login', {email, password}) | |
| .then(response => { | |
| if (response.success) { | |
| currentUser = response.user; | |
| checkAuthStatus(); | |
| closeAuthModal(); | |
| } else { | |
| alert(response.message || 'Login failed'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Login error:', error); | |
| alert('An error occurred during login'); | |
| }); | |
| } | |
| function registerUser() { | |
| const name = document.getElementById('signupName').value; | |
| const email = document.getElementById('signupEmail').value; | |
| const password = document.getElementById('signupPassword').value; | |
| const confirmPassword = document.getElementById('signupConfirmPassword').value; | |
| if (!name || !email || !password || !confirmPassword) { | |
| alert('Please fill in all fields'); | |
| return; | |
| } | |
| if (password !== confirmPassword) { | |
| alert('Passwords do not match'); | |
| return; | |
| } | |
| // In a real app, this would make an API call to your PHP backend | |
| simulateApiCall('register', {name, email, password}) | |
| .then(response => { | |
| if (response.success) { | |
| alert('Registration successful! Please login.'); | |
| switchAuthTab('login'); | |
| // Clear the form | |
| document.getElementById('signupName').value = ''; | |
| document.getElementById('signupEmail').value = ''; | |
| document.getElementById('signupPassword').value = ''; | |
| document.getElementById('signupConfirmPassword').value = ''; | |
| } else { | |
| alert(response.message || 'Registration failed'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Registration error:', error); | |
| alert('An error occurred during registration'); | |
| }); | |
| } | |
| function logoutUser() { | |
| // In a real app, this would also clear the auth token from storage | |
| currentUser = null; | |
| checkAuthStatus(); | |
| document.getElementById('authModal').classList.remove('hidden'); | |
| } | |
| // Tab navigation | |
| function switchTab(tabId) { | |
| // Hide all tab contents | |
| document.querySelectorAll('.tab-content').forEach(tab => { | |
| tab.classList.remove('active'); | |
| }); | |
| // Show the selected tab | |
| document.getElementById(tabId).classList.add('active'); | |
| // Update tab buttons styling | |
| const tabButtons = document.querySelectorAll('.flex.border-b button'); | |
| tabButtons.forEach(button => { | |
| if (button.textContent.toLowerCase().includes(tabId)) { | |
| button.classList.add('text-blue-500', 'border-b-2', 'border-blue-500'); | |
| button.classList.remove('text-gray-500'); | |
| } else { | |
| button.classList.add('text-gray-500'); | |
| button.classList.remove('text-blue-500', 'border-b-2', 'border-blue-500'); | |
| } | |
| }); | |
| // Load data for the tab if needed | |
| if (tabId === 'todo') { | |
| loadTasks(); | |
| } else if (tabId === 'notes') { | |
| loadNotes(); | |
| } else if (tabId === 'music') { | |
| loadSongs(); | |
| } else if (tabId === 'transactions') { | |
| loadTransactions(); | |
| } | |
| } | |
| // Dashboard functions | |
| function updateDashboardDate() { | |
| const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; | |
| const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; | |
| const today = new Date(); | |
| document.getElementById('dashboardDate').textContent = today.getDate(); | |
| document.getElementById('dashboardDay').textContent = `${days[today.getDay()]}, ${months[today.getMonth()]} ${today.getFullYear()}`; | |
| } | |
| function loadInitialData() { | |
| loadTasks(); | |
| loadNotes(); | |
| loadSongs(); | |
| loadTransactions(); | |
| // Update dashboard summaries | |
| updateTodoSummary(); | |
| updateTransactionSummary(); | |
| } | |
| // Calendar functions | |
| function generateCalendar(month, year) { | |
| const calendarDays = document.getElementById('calendarDays'); | |
| calendarDays.innerHTML = ''; | |
| const firstDay = new Date(year, month, 1); | |
| const lastDay = new Date(year, month + 1, 0); | |
| const daysInMonth = lastDay.getDate(); | |
| const startingDay = firstDay.getDay(); | |
| // Update month/year display | |
| const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; | |
| document.getElementById('currentMonthYear').textContent = `${months[month]} ${year}`; | |
| // Add empty cells for days before the first day of the month | |
| for (let i = 0; i < startingDay; i++) { | |
| const emptyDay = document.createElement('div'); | |
| emptyDay.className = 'p-2 text-center text-gray-400'; | |
| calendarDays.appendChild(emptyDay); | |
| } | |
| // Add cells for each day of the month | |
| for (let day = 1; day <= daysInMonth; day++) { | |
| const dayElement = document.createElement('div'); | |
| dayElement.className = 'p-2 text-center calendar-day rounded-md'; | |
| dayElement.textContent = day; | |
| // Highlight today | |
| const today = new Date(); | |
| if (day === today.getDate() && month === today.getMonth() && year === today.getFullYear()) { | |
| dayElement.classList.add('bg-blue-100', 'font-bold'); | |
| } | |
| // Add click event | |
| dayElement.addEventListener('click', function() { | |
| document.querySelectorAll('.calendar-day').forEach(el => el.classList.remove('selected')); | |
| this.classList.add('selected'); | |
| // You could load events for this day here | |
| }); | |
| calendarDays.appendChild(dayElement); | |
| } | |
| } | |
| function prevMonth() { | |
| currentMonth--; | |
| if (currentMonth < 0) { | |
| currentMonth = 11; | |
| currentYear--; | |
| } | |
| generateCalendar(currentMonth, currentYear); | |
| } | |
| function nextMonth() { | |
| currentMonth++; | |
| if (currentMonth > 11) { | |
| currentMonth = 0; | |
| currentYear++; | |
| } | |
| generateCalendar(currentMonth, currentYear); | |
| } | |
| function goToToday() { | |
| const today = new Date(); | |
| currentMonth = today.getMonth(); | |
| currentYear = today.getFullYear(); | |
| generateCalendar(currentMonth, currentYear); | |
| } | |
| // Todo functions | |
| function loadTasks() { | |
| // In a real app, this would fetch tasks from your PHP backend | |
| simulateApiCall('getTasks', {userId: currentUser?.id}) | |
| .then(response => { | |
| const taskList = document.getElementById('taskList'); | |
| taskList.innerHTML = ''; | |
| if (response.tasks && response.tasks.length > 0) { | |
| response.tasks.forEach(task => { | |
| const taskElement = document.createElement('div'); | |
| taskElement.className = 'flex items-center p-2 border rounded-md'; | |
| if (task.completed) { | |
| taskElement.classList.add('bg-gray-100'); | |
| } | |
| taskElement.innerHTML = ` | |
| <input type="checkbox" ${task.completed ? 'checked' : ''} | |
| onchange="toggleTaskCompletion(${task.id}, this)" | |
| class="mr-2 h-5 w-5"> | |
| <span class="flex-grow ${task.completed ? 'task-completed' : ''}">${task.title}</span> | |
| <button onclick="deleteTask(${task.id}, this.parentNode)" class="text-red-500 hover:text-red-700"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| `; | |
| taskList.appendChild(taskElement); | |
| }); | |
| } else { | |
| taskList.innerHTML = '<p class="text-gray-500">No tasks found. Add a task to get started!</p>'; | |
| } | |
| // Update dashboard summary | |
| updateTodoSummary(); | |
| }) | |
| .catch(error => { | |
| console.error('Error loading tasks:', error); | |
| document.getElementById('taskList').innerHTML = '<p class="text-red-500">Error loading tasks. Please try again.</p>'; | |
| }); | |
| } | |
| function addTask() { | |
| const taskInput = document.getElementById('newTaskInput'); | |
| const taskTitle = taskInput.value.trim(); | |
| if (!taskTitle) { | |
| alert('Please enter a task'); | |
| return; | |
| } | |
| // In a real app, this would send the task to your PHP backend | |
| simulateApiCall('addTask', {userId: currentUser?.id, title: taskTitle}) | |
| .then(response => { | |
| if (response.success) { | |
| taskInput.value = ''; | |
| loadTasks(); | |
| } else { | |
| alert(response.message || 'Failed to add task'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error adding task:', error); | |
| alert('An error occurred while adding the task'); | |
| }); | |
| } | |
| function toggleTaskCompletion(taskId, checkbox) { | |
| const completed = checkbox.checked; | |
| // In a real app, this would update the task status via your PHP backend | |
| simulateApiCall('updateTask', {taskId, completed}) | |
| .then(response => { | |
| if (!response.success) { | |
| // Revert the checkbox if the update failed | |
| checkbox.checked = !completed; | |
| alert(response.message || 'Failed to update task'); | |
| } else { | |
| const taskText = checkbox.nextElementSibling; | |
| if (completed) { | |
| taskText.classList.add('task-completed'); | |
| checkbox.parentNode.classList.add('bg-gray-100'); | |
| } else { | |
| taskText.classList.remove('task-completed'); | |
| checkbox.parentNode.classList.remove('bg-gray-100'); | |
| } | |
| // Update dashboard summary | |
| updateTodoSummary(); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error updating task:', error); | |
| checkbox.checked = !completed; | |
| alert('An error occurred while updating the task'); | |
| }); | |
| } | |
| function deleteTask(taskId, taskElement) { | |
| if (!confirm('Are you sure you want to delete this task?')) { | |
| return; | |
| } | |
| // In a real app, this would delete the task via your PHP backend | |
| simulateApiCall('deleteTask', {taskId}) | |
| .then(response => { | |
| if (response.success) { | |
| taskElement.remove(); | |
| // Check if there are no tasks left | |
| const taskList = document.getElementById('taskList'); | |
| if (taskList.children.length === 0) { | |
| taskList.innerHTML = '<p class="text-gray-500">No tasks found. Add a task to get started!</p>'; | |
| } | |
| // Update dashboard summary | |
| updateTodoSummary(); | |
| } else { | |
| alert(response.message || 'Failed to delete task'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error deleting task:', error); | |
| alert('An error occurred while deleting the task'); | |
| }); | |
| } | |
| function updateTodoSummary() { | |
| // In a real app, this would fetch task stats from your PHP backend | |
| simulateApiCall('getTaskStats', {userId: currentUser?.id}) | |
| .then(response => { | |
| const summaryElement = document.getElementById('todoSummary'); | |
| if (response.totalTasks === 0) { | |
| summaryElement.innerHTML = '<p>You have no tasks yet.</p>'; | |
| } else { | |
| const completionPercentage = Math.round((response.completedTasks / response.totalTasks) * 100); | |
| summaryElement.innerHTML = ` | |
| <div class="mb-2"> | |
| <p>Total tasks: ${response.totalTasks}</p> | |
| <p>Completed: ${response.completedTasks}</p> | |
| <p>Pending: ${response.totalTasks - response.completedTasks}</p> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> | |
| <div class="bg-blue-600 h-2.5 rounded-full" style="width: ${completionPercentage}%"></div> | |
| </div> | |
| <p class="mt-1 text-sm text-gray-600">${completionPercentage}% completed</p> | |
| `; | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error loading task summary:', error); | |
| document.getElementById('todoSummary').innerHTML = '<p class="text-red-500">Error loading task summary</p>'; | |
| }); | |
| } | |
| // Notes functions | |
| function loadNotes() { | |
| // In a real app, this would fetch notes from your PHP backend | |
| simulateApiCall('getNotes', {userId: currentUser?.id}) | |
| .then(response => { | |
| const notesList = document.getElementById('notesList'); | |
| notesList.innerHTML = ''; | |
| if (response.notes && response.notes.length > 0) { | |
| response.notes.forEach(note => { | |
| const noteElement = document.createElement('div'); | |
| noteElement.className = 'border rounded-md p-4 bg-white shadow-sm'; | |
| noteElement.innerHTML = ` | |
| <h4 class="font-bold mb-2">${note.title}</h4> | |
| <p class="text-gray-700 mb-3">${note.content.substring(0, 100)}${note.content.length > 100 ? '...' : ''}</p> | |
| <div class="flex justify-between text-sm text-gray-500"> | |
| <span>${new Date(note.created_at).toLocaleDateString()}</span> | |
| <div> | |
| <button onclick="editNote(${note.id})" class="text-blue-500 hover:text-blue-700 mr-2"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| <button onclick="deleteNote(${note.id})" class="text-red-500 hover:text-red-700"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| notesList.appendChild(noteElement); | |
| }); | |
| } else { | |
| notesList.innerHTML = '<p class="text-gray-500 col-span-3">No notes found. Create your first note!</p>'; | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error loading notes:', error); | |
| document.getElementById('notesList').innerHTML = '<p class="text-red-500 col-span-3">Error loading notes. Please try again.</p>'; | |
| }); | |
| } | |
| function saveNote() { | |
| const title = document.getElementById('noteTitle').value.trim(); | |
| const content = document.getElementById('noteContent').value.trim(); | |
| if (!title || !content) { | |
| alert('Please enter both title and content'); | |
| return; | |
| } | |
| // In a real app, this would save the note via your PHP backend | |
| simulateApiCall('saveNote', {userId: currentUser?.id, title, content}) | |
| .then(response => { | |
| if (response.success) { | |
| // Clear the form | |
| document.getElementById('noteTitle').value = ''; | |
| document.getElementById('noteContent').value = ''; | |
| // Reload notes | |
| loadNotes(); | |
| } else { | |
| alert(response.message || 'Failed to save note'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error saving note:', error); | |
| alert('An error occurred while saving the note'); | |
| }); | |
| } | |
| function editNote(noteId) { | |
| // In a real app, this would fetch the note details from your PHP backend | |
| simulateApiCall('getNote', {noteId}) | |
| .then(response => { | |
| if (response.note) { | |
| document.getElementById('noteTitle').value = response.note.title; | |
| document.getElementById('noteContent').value = response.note.content; | |
| // Change the save button to update | |
| const saveButton = document.querySelector('#notes button'); | |
| saveButton.textContent = 'Update Note'; | |
| saveButton.onclick = function() { | |
| updateNote(noteId); | |
| }; | |
| // Scroll to the form | |
| document.getElementById('noteTitle').scrollIntoView({behavior: 'smooth'}); | |
| } else { | |
| alert('Note not found'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error fetching note:', error); | |
| alert('An error occurred while fetching the note'); | |
| }); | |
| } | |
| function updateNote(noteId) { | |
| const title = document.getElementById('noteTitle').value.trim(); | |
| const content = document.getElementById('noteContent').value.trim(); | |
| if (!title || !content) { | |
| alert('Please enter both title and content'); | |
| return; | |
| } | |
| // In a real app, this would update the note via your PHP backend | |
| simulateApiCall('updateNote', {noteId, title, content}) | |
| .then(response => { | |
| if (response.success) { | |
| // Clear the form | |
| document.getElementById('noteTitle').value = ''; | |
| document.getElementById('noteContent').value = ''; | |
| // Change the button back to save | |
| const saveButton = document.querySelector('#notes button'); | |
| saveButton.textContent = 'Save Note'; | |
| saveButton.onclick = saveNote; | |
| // Reload notes | |
| loadNotes(); | |
| } else { | |
| alert(response.message || 'Failed to update note'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error updating note:', error); | |
| alert('An error occurred while updating the note'); | |
| }); | |
| } | |
| function deleteNote(noteId) { | |
| if (!confirm('Are you sure you want to delete this note?')) { | |
| return; | |
| } | |
| // In a real app, this would delete the note via your PHP backend | |
| simulateApiCall('deleteNote', {noteId}) | |
| .then(response => { | |
| if (response.success) { | |
| loadNotes(); | |
| } else { | |
| alert(response.message || 'Failed to delete note'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error deleting note:', error); | |
| alert('An error occurred while deleting the note'); | |
| }); | |
| } | |
| // Music functions | |
| function loadSongs() { | |
| // In a real app, this would fetch songs from your PHP backend | |
| simulateApiCall('getSongs', {userId: currentUser?.id}) | |
| .then(response => { | |
| const songsTable = document.getElementById('songsTable'); | |
| songsTable.innerHTML = ''; | |
| if (response.songs && response.songs.length > 0) { | |
| response.songs.forEach(song => { | |
| const songRow = document.createElement('tr'); | |
| songRow.className = 'hover:bg-gray-50'; | |
| songRow.innerHTML = ` | |
| <td class="py-2 px-4 border-b">${song.title}</td> | |
| <td class="py-2 px-4 border-b">${song.artist}</td> | |
| <td class="py-2 px-4 border-b">${song.album}</td> | |
| <td class="py-2 px-4 border-b">${song.year}</td> | |
| <td class="py-2 px-4 border-b"> | |
| <button onclick="showSongDetails(${song.id})" class="text-blue-500 hover:text-blue-700 mr-2"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button onclick="deleteSong(${song.id})" class="text-red-500 hover:text-red-700"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </td> | |
| `; | |
| songsTable.appendChild(songRow); | |
| }); | |
| } else { | |
| songsTable.innerHTML = ` | |
| <tr> | |
| <td colspan="5" class="py-4 text-center text-gray-500">No songs found. Add your first song!</td> | |
| </tr> | |
| `; | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error loading songs:', error); | |
| document.getElementById('songsTable').innerHTML = ` | |
| <tr> | |
| <td colspan="5" class="py-4 text-center text-red-500">Error loading songs. Please try again.</td> | |
| </tr> | |
| `; | |
| }); | |
| } | |
| function showAddSongForm() { | |
| document.getElementById('addSongForm').classList.remove('hidden'); | |
| } | |
| function hideAddSongForm() { | |
| document.getElementById('addSongForm').classList.add('hidden'); | |
| // Clear the form | |
| document.getElementById('songTitle').value = ''; | |
| document.getElementById('songArtist').value = ''; | |
| document.getElementById('songAlbum').value = ''; | |
| document.getElementById('songYear').value = ''; | |
| document.getElementById('songGenre').value = ''; | |
| } | |
| function addNewSong() { | |
| const title = document.getElementById('songTitle').value.trim(); | |
| const artist = document.getElementById('songArtist').value.trim(); | |
| const album = document.getElementById('songAlbum').value.trim(); | |
| const year = document.getElementById('songYear').value.trim(); | |
| const genre = document.getElementById('songGenre').value.trim(); | |
| if (!title || !artist) { | |
| alert('Please at least enter title and artist'); | |
| return; | |
| } | |
| // In a real app, this would save the song via your PHP backend | |
| simulateApiCall('addSong', {userId: currentUser?.id, title, artist, album, year, genre}) | |
| .then(response => { | |
| if (response.success) { | |
| hideAddSongForm(); | |
| loadSongs(); | |
| } else { | |
| alert(response.message || 'Failed to add song'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error adding song:', error); | |
| alert('An error occurred while adding the song'); | |
| }); | |
| } | |
| function showSongDetails(songId) { | |
| // In a real app, this would fetch song details from your PHP backend | |
| simulateApiCall('getSongDetails', {songId}) | |
| .then(response => { | |
| if (response.song) { | |
| const song = response.song; | |
| alert(`Song Details:\n\nTitle: ${song.title}\nArtist: ${song.artist}\nAlbum: ${song.album}\nYear: ${song.year}\nGenre: ${song.genre}`); | |
| } else { | |
| alert('Song not found'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error fetching song details:', error); | |
| alert('An error occurred while fetching song details'); | |
| }); | |
| } | |
| function deleteSong(songId) { | |
| if (!confirm('Are you sure you want to delete this song?')) { | |
| return; | |
| } | |
| // In a real app, this would delete the song via your PHP backend | |
| simulateApiCall('deleteSong', {songId}) | |
| .then(response => { | |
| if (response.success) { | |
| loadSongs(); | |
| } else { | |
| alert(response.message || 'Failed to delete song'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error deleting song:', error); | |
| alert('An error occurred while deleting the song'); | |
| }); | |
| } | |
| function searchSongs() { | |
| const searchTerm = document.getElementById('songSearch').value.trim(); | |
| if (!searchTerm) { | |
| loadSongs(); | |
| return; | |
| } | |
| // In a real app, this would search songs via your PHP backend | |
| simulateApiCall('searchSongs', {userId: currentUser?.id, searchTerm}) | |
| .then(response => { | |
| const songsTable = document.getElementById('songsTable'); | |
| songsTable.innerHTML = ''; | |
| if (response.songs && response.songs.length > 0) { | |
| response.songs.forEach(song => { | |
| const songRow = document.createElement('tr'); | |
| songRow.className = 'hover:bg-gray-50'; | |
| songRow.innerHTML = ` | |
| <td class="py-2 px-4 border-b">${song.title}</td> | |
| <td class="py-2 px-4 border-b">${song.artist}</td> | |
| <td class="py-2 px-4 border-b">${song.album}</td> | |
| <td class="py-2 px-4 border-b">${song.year}</td> | |
| <td class="py-2 px-4 border-b"> | |
| <button onclick="showSongDetails(${song.id})" class="text-blue-500 hover:text-blue-700 mr-2"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button onclick="deleteSong(${song.id})" class="text-red-500 hover:text-red-700"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </td> | |
| `; | |
| songsTable.appendChild(songRow); | |
| }); | |
| } else { | |
| songsTable.innerHTML = ` | |
| <tr> | |
| <td colspan="5" class="py-4 text-center text-gray-500">No songs found matching "${searchTerm}"</td> | |
| </tr> | |
| `; | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error searching songs:', error); | |
| document.getElementById('songsTable').innerHTML = ` | |
| <tr> | |
| <td colspan="5" class="py-4 text-center text-red-500">Error searching songs. Please try again.</td> | |
| </tr> | |
| `; | |
| }); | |
| } | |
| // Transaction functions | |
| function loadTransactions() { | |
| const month = document.getElementById('transactionMonth').value; | |
| // In a real app, this would fetch transactions from your PHP backend | |
| simulateApiCall('getTransactions', {userId: currentUser?.id, month}) | |
| .then(response => { | |
| const transactionsList = document.getElementById('transactionsList'); | |
| transactionsList.innerHTML = ''; | |
| if (response.transactions && response.transactions.length > 0) { | |
| response.transactions.forEach(transaction => { | |
| const transactionElement = document.createElement('div'); | |
| transactionElement.className = `p-3 bg-white rounded-md shadow-sm ${transaction.type === 'income' ? 'transaction-income' : 'transaction-expense'}`; | |
| transactionElement.innerHTML = ` | |
| <div class="flex justify-between items-center"> | |
| <div> | |
| <p class="font-medium">${transaction.description}</p> | |
| <p class="text-sm text-gray-500">${transaction.category} • ${new Date(transaction.date).toLocaleDateString()}</p> | |
| </div> | |
| <div class="text-right"> | |
| <p class="${transaction.type === 'income' ? 'text-green-600' : 'text-red-600'} font-medium"> | |
| ${transaction.type === 'income' ? '+' : '-'}₹${transaction.amount.toFixed(2)} | |
| </p> | |
| <button onclick="deleteTransaction(${transaction.id}, this.parentNode.parentNode)" class="text-red-500 text-sm hover:text-red-700"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| transactionsList.appendChild(transactionElement); | |
| }); | |
| } else { | |
| transactionsList.innerHTML = '<p class="text-gray-500">No transactions found for this month.</p>'; | |
| } | |
| // Update summary | |
| updateTransactionSummary(response.summary); | |
| }) | |
| .catch(error => { | |
| console.error('Error loading transactions:', error); | |
| document.getElementById('transactionsList').innerHTML = '<p class="text-red-500">Error loading transactions. Please try again.</p>'; | |
| }); | |
| } | |
| function addTransaction() { | |
| const type = document.getElementById('transactionType').value; | |
| const amount = parseFloat(document.getElementById('transactionAmount').value); | |
| const category = document.getElementById('transactionCategory').value; | |
| const description = document.getElementById('transactionDescription').value.trim(); | |
| if (!amount || isNaN(amount) || amount <= 0) { | |
| alert('Please enter a valid amount'); | |
| return; | |
| } | |
| if (!description) { | |
| alert('Please enter a description'); | |
| return; | |
| } | |
| // In a real app, this would save the transaction via your PHP backend | |
| simulateApiCall('addTransaction', {userId: currentUser?.id, type, amount, category, description}) | |
| .then(response => { | |
| if (response.success) { | |
| // Clear the form | |
| document.getElementById('transactionAmount').value = ''; | |
| document.getElementById('transactionDescription').value = ''; | |
| // Reload transactions | |
| loadTransactions(); | |
| // Update dashboard summary | |
| updateTransactionSummary(); | |
| } else { | |
| alert(response.message || 'Failed to add transaction'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error adding transaction:', error); | |
| alert('An error occurred while adding the transaction'); | |
| }); | |
| } | |
| function deleteTransaction(transactionId, transactionElement) { | |
| if (!confirm('Are you sure you want to delete this transaction?')) { | |
| return; | |
| } | |
| // In a real app, this would delete the transaction via your PHP backend | |
| simulateApiCall('deleteTransaction', {transactionId}) | |
| .then(response => { | |
| if (response.success) { | |
| transactionElement.remove(); | |
| // Check if there are no transactions left | |
| const transactionsList = document.getElementById('transactionsList'); | |
| if (transactionsList.children.length === 0) { | |
| transactionsList.innerHTML = '<p class="text-gray-500">No transactions found for this month.</p>'; | |
| } | |
| // Update summary | |
| updateTransactionSummary(); | |
| } else { | |
| alert(response.message || 'Failed to delete transaction'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error deleting transaction:', error); | |
| alert('An error occurred while deleting the transaction'); | |
| }); | |
| } | |
| function updateTransactionSummary(summary) { | |
| // If summary is not provided, fetch it from the server | |
| if (!summary) { | |
| const month = document.getElementById('transactionMonth').value; | |
| // In a real app, this would fetch transaction stats from your PHP backend | |
| simulateApiCall('getTransactionSummary', {userId: currentUser?.id, month}) | |
| .then(response => { | |
| displayTransactionSummary(response); | |
| }) | |
| .catch(error => { | |
| console.error('Error loading transaction summary:', error); | |
| document.getElementById('transactionSummary').innerHTML = '<p class="text-red-500">Error loading summary</p>'; | |
| }); | |
| } else { | |
| displayTransactionSummary(summary); | |
| } | |
| } | |
| function displayTransactionSummary(summary) { | |
| const summaryElement = document.getElementById('transactionSummary'); | |
| if (summary.totalTransactions === 0) { | |
| summaryElement.innerHTML = '<p>No transactions this month.</p>'; | |
| } else { | |
| const balance = summary.totalIncome - summary.totalExpenses; | |
| const balanceClass = balance >= 0 ? 'text-green-600' : 'text-red-600'; | |
| summaryElement.innerHTML = ` | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div class="bg-green-50 p-3 rounded-md"> | |
| <p class="text-sm text-green-600">Income</p> | |
| <p class="text-xl font-bold text-green-600">₹${summary.totalIncome.toFixed(2)}</p> | |
| </div> | |
| <div class="bg-red-50 p-3 rounded-md"> | |
| <p class="text-sm text-red-600">Expenses</p> | |
| <p class="text-xl font-bold text-red-600">₹${summary.totalExpenses.toFixed(2)}</p> | |
| </div> | |
| </div> | |
| <div class="mt-4 p-3 bg-blue-50 rounded-md"> | |
| <p class="text-sm text-blue-600">Balance</p> | |
| <p class="text-xl font-bold ${balanceClass}">₹${Math.abs(balance).toFixed(2)} ${balance >= 0 ? '(Surplus)' : '(Deficit)'}</p> | |
| </div> | |
| `; | |
| // Update the summary in the transactions tab | |
| document.getElementById('totalIncome').textContent = `₹${summary.totalIncome.toFixed(2)}`; | |
| document.getElementById('totalExpenses').textContent = `₹${summary.totalExpenses.toFixed(2)}`; | |
| const tabBalance = summary.totalIncome - summary.totalExpenses; | |
| const tabBalanceClass = tabBalance >= 0 ? 'text-green-600' : 'text-red-600'; | |
| document.getElementById('balance').className = tabBalanceClass; | |
| document.getElementById('balance').textContent = `₹${Math.abs(tabBalance).toFixed(2)}`; | |
| } | |
| } | |
| // Calculator functions | |
| function appendToDisplay(value) { | |
| const display = document.getElementById('calculatorDisplay'); | |
| display.value += value; | |
| } | |
| function clearDisplay() { | |
| document.getElementById('calculatorDisplay').value = ''; | |
| } | |
| function backspace() { | |
| const display = document.getElementById('calculatorDisplay'); | |
| display.value = display.value.slice(0, -1); | |
| } | |
| function calculate() { | |
| const display = document.getElementById('calculatorDisplay'); | |
| try { | |
| // Replace × with * and ÷ with / for evaluation | |
| const expression = display.value.replace(/×/g, '*').replace(/÷/g, '/'); | |
| display.value = eval(expression); | |
| } catch (error) { | |
| display.value = 'Error'; | |
| } | |
| } | |
| // Helper function to simulate API calls (in a real app, these would be actual API calls to your PHP backend) | |
| function simulateApiCall(endpoint, data) { | |
| console.log(`Simulating API call to ${endpoint}`, data); | |
| // Return a promise to simulate async API call | |
| return new Promise((resolve) => { | |
| setTimeout(() => { | |
| // Simulate different responses based on endpoint | |
| switch(endpoint) { | |
| case 'login': | |
| if (data.email === 'user@example.com' && data.password === 'password') { | |
| resolve({ | |
| success: true, | |
| user: { | |
| id: 1, | |
| name: 'Demo User', | |
| email: data.email | |
| } | |
| }); | |
| } else { | |
| resolve({ | |
| success: false, | |
| message: 'Invalid email or password' | |
| }); | |
| } | |
| break; | |
| case 'register': | |
| resolve({ | |
| success: true, | |
| message: 'Registration successful' | |
| }); | |
| break; | |
| case 'getTasks': | |
| resolve({ | |
| tasks: [ | |
| { id: 1, title: 'Complete project proposal', completed: false }, | |
| { id: 2, title: 'Buy groceries', completed: true }, | |
| { id: 3, title: 'Call mom', completed: false } | |
| ] | |
| }); | |
| break; | |
| case 'addTask': | |
| resolve({ | |
| success: true, | |
| taskId: Math.floor(Math.random() * 1000) + 4 | |
| }); | |
| break; | |
| case 'updateTask': | |
| resolve({ success: true }); | |
| break; | |
| case 'deleteTask': | |
| resolve({ success: true }); | |
| break; | |
| case 'getTaskStats': | |
| resolve({ | |
| totalTasks: 3, | |
| completedTasks: 1 | |
| }); | |
| break; | |
| case 'getNotes': | |
| resolve({ | |
| notes: [ | |
| { id: 1, title: 'Meeting Notes', content: 'Discussed project timeline and deliverables.', created_at: '2023-05-15' }, | |
| { id: 2, title: 'Shopping List', content: 'Milk, Eggs, Bread, Fruits', created_at: '2023-05-10' } | |
| ] | |
| }); | |
| break; | |
| case 'saveNote': | |
| case 'updateNote': | |
| resolve({ success: true }); | |
| break; | |
| case 'getNote': | |
| resolve({ | |
| note: { | |
| id: 1, | |
| title: 'Meeting Notes', | |
| content: 'Discussed project timeline and deliverables.', | |
| created_at: '2023-05-15' | |
| } | |
| }); | |
| break; | |
| case 'deleteNote': | |
| resolve({ success: true }); | |
| break; | |
| case 'getSongs': | |
| resolve({ | |
| songs: [ | |
| { id: 1, title: 'Bohemian Rhapsody', artist: 'Queen', album: 'A Night at the Opera', year: 1975 }, | |
| { id: 2, title: 'Hotel California', artist: 'Eagles', album: 'Hotel California', year: 1976 } | |
| ] | |
| }); | |
| break; | |
| case 'addSong': | |
| resolve({ success: true }); | |
| break; | |
| case 'getSongDetails': | |
| resolve({ | |
| song: { | |
| id: 1, | |
| title: 'Bohemian Rhapsody', | |
| artist: 'Queen', | |
| album: 'A Night at the Opera', | |
| year: 1975, | |
| genre: 'Rock' | |
| } | |
| }); | |
| break; | |
| case 'deleteSong': | |
| resolve({ success: true }); | |
| break; | |
| case 'searchSongs': | |
| if (data.searchTerm.toLowerCase() === 'queen') { | |
| resolve({ | |
| songs: [ | |
| { id: 1, title: 'Bohemian Rhapsody', artist: 'Queen', album: 'A Night at the Opera', year: 1975 } | |
| ] | |
| }); | |
| } else { | |
| resolve({ songs: [] }); | |
| } | |
| break; | |
| case 'getTransactions': | |
| const transactions = [ | |
| { id: 1, type: 'income', amount: 50000, category: 'salary', description: 'Monthly Salary', date: '2023-05-01' }, | |
| { id: 2, type: 'expense', amount: 1500, category: 'food', description: 'Grocery Shopping', date: '2023-05-05' }, | |
| { id: 3, type: 'expense', amount: 2000, category: 'transport', description: 'Fuel', date: '2023-05-10' }, | |
| { id: 4, type: 'income', amount: 8000, category: 'freelance', description: 'Website Project', date: '2023-05-15' } | |
| ]; | |
| resolve({ | |
| transactions: transactions, | |
| summary: { | |
| totalTransactions: transactions.length, | |
| totalIncome: 58000, | |
| totalExpenses: 3500 | |
| } | |
| }); | |
| break; | |
| case 'addTransaction': | |
| resolve({ success: true }); | |
| break; | |
| case 'deleteTransaction': | |
| resolve({ success: true }); | |
| break; | |
| case 'getTransactionSummary': | |
| resolve({ | |
| totalTransactions: 4, | |
| totalIncome: 58000, | |
| totalExpenses: 3500 | |
| }); | |
| break; | |
| default: | |
| resolve({ success: false, message: 'Unknown endpoint' }); | |
| } | |
| }, 500); // Simulate network delay | |
| }); | |
| } | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nextzendev2/nextzendailylife" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |