Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>FlowPomo | Focus Timer</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script> | |
| <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js"></script> | |
| <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-firestore.js"></script> | |
| <script src="https://js.stripe.com/v3/"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <style> | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| } | |
| .pulse-animation { | |
| animation: pulse 2s infinite; | |
| } | |
| .glassmorphism { | |
| background: rgba(255, 255, 255, 0.25); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.18); | |
| box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.1); | |
| } | |
| .neumorphism { | |
| border-radius: 16px; | |
| background: #f0f0f0; | |
| box-shadow: 8px 8px 16px #d9d9d9, -8px -8px 16px #ffffff; | |
| } | |
| .timer-circle { | |
| width: 300px; | |
| height: 300px; | |
| border-radius: 50%; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .timer-progress { | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 50%; | |
| background: conic-gradient(#7c3aed var(--progress), #e9d5ff var(--progress)); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .tab-active { | |
| border-bottom: 3px solid #7c3aed; | |
| color: #7c3aed; | |
| font-weight: 600; | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.5s ease-in; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| .gradient-bg { | |
| background: linear-gradient(135deg, #e0f2fe 0%, #fae8ff 50%, #f0fdf4 100%); | |
| } | |
| </style> | |
| </head> | |
| <body class="font-['Inter'] min-h-screen gradient-bg"> | |
| <!-- Firebase Config --> | |
| <script> | |
| const firebaseConfig = { | |
| apiKey: "AIzaSyDEXAMPLEEXAMPLEEXAMPLE", | |
| authDomain: "flowpomo-example.firebaseapp.com", | |
| projectId: "flowpomo-example", | |
| storageBucket: "flowpomo-example.appspot.com", | |
| messagingSenderId: "123456789012", | |
| appId: "1:123456789012:web:abcdef1234567890" | |
| }; | |
| firebase.initializeApp(firebaseConfig); | |
| const auth = firebase.auth(); | |
| const db = firebase.firestore(); | |
| const stripe = Stripe('pk_test_51EXAMPLEEXAMPLEEXAMPLE'); | |
| </script> | |
| <!-- Auth Modal --> | |
| <div id="authModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="glassmorphism rounded-xl p-8 w-full max-w-md"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800">Welcome to FlowPomo</h2> | |
| <button onclick="closeAuthModal()" class="text-gray-500 hover:text-gray-700"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <div id="authTabs" class="flex border-b mb-6"> | |
| <button onclick="switchAuthTab('login')" class="flex-1 py-2 font-medium text-center tab-active" id="loginTab">Login</button> | |
| <button onclick="switchAuthTab('signup')" class="flex-1 py-2 font-medium text-center text-gray-500" id="signupTab">Sign Up</button> | |
| </div> | |
| <div id="loginForm"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-700 mb-2">Email</label> | |
| <input type="email" id="loginEmail" class="w-full px-4 py-2 rounded-lg border focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-gray-700 mb-2">Password</label> | |
| <input type="password" id="loginPassword" class="w-full px-4 py-2 rounded-lg border focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
| </div> | |
| <button onclick="loginWithEmail()" class="w-full bg-purple-600 text-white py-2 rounded-lg hover:bg-purple-700 transition mb-4">Login</button> | |
| <div class="text-center mb-4 text-gray-500">or</div> | |
| <button onclick="loginWithGoogle()" class="w-full flex items-center justify-center gap-2 bg-white text-gray-700 py-2 rounded-lg border hover:bg-gray-50 transition"> | |
| <img src="https://upload.wikimedia.org/wikipedia/commons/5/53/Google_%22G%22_Logo.svg" alt="Google" class="h-5"> | |
| Continue with Google | |
| </button> | |
| </div> | |
| <div id="signupForm" class="hidden"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-700 mb-2">Email</label> | |
| <input type="email" id="signupEmail" class="w-full px-4 py-2 rounded-lg border focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-700 mb-2">Password</label> | |
| <input type="password" id="signupPassword" class="w-full px-4 py-2 rounded-lg border focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-gray-700 mb-2">Confirm Password</label> | |
| <input type="password" id="signupConfirmPassword" class="w-full px-4 py-2 rounded-lg border focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
| </div> | |
| <button onclick="signUpWithEmail()" class="w-full bg-purple-600 text-white py-2 rounded-lg hover:bg-purple-700 transition">Create Account</button> | |
| </div> | |
| <div id="authMessage" class="mt-4 text-center text-sm hidden"></div> | |
| </div> | |
| </div> | |
| <!-- Premium Modal --> | |
| <div id="premiumModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="glassmorphism rounded-xl p-8 w-full max-w-md"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800">Upgrade to Premium</h2> | |
| <button onclick="closePremiumModal()" class="text-gray-500 hover:text-gray-700"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="mb-6"> | |
| <div class="neumorphism p-6 rounded-xl mb-4"> | |
| <h3 class="text-xl font-semibold text-purple-700 mb-2">Pro Plan</h3> | |
| <p class="text-gray-600 mb-4">$4.99/month or $49.99/year (save 15%)</p> | |
| <ul class="space-y-2 text-gray-700"> | |
| <li class="flex items-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500 mr-2" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" /> | |
| </svg> | |
| Premium soundscapes | |
| </li> | |
| <li class="flex items-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500 mr-2" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" /> | |
| </svg> | |
| Advanced analytics | |
| </li> | |
| <li class="flex items-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500 mr-2" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" /> | |
| </svg> | |
| Task templates & scheduling | |
| </li> | |
| <li class="flex items-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500 mr-2" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" /> | |
| </svg> | |
| Daily challenges & badges | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| <button onclick="subscribePremium()" class="w-full bg-purple-600 text-white py-3 rounded-lg hover:bg-purple-700 transition font-medium mb-4"> | |
| Upgrade Now | |
| </button> | |
| <p class="text-center text-sm text-gray-500">7-day free trial. Cancel anytime.</p> | |
| </div> | |
| </div> | |
| <!-- Soundscapes Modal --> | |
| <div id="soundModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="glassmorphism rounded-xl p-6 w-full max-w-md"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800">Soundscapes</h2> | |
| <button onclick="closeSoundModal()" class="text-gray-500 hover:text-gray-700"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="grid grid-cols-2 gap-4 mb-6"> | |
| <div class="neumorphism p-4 rounded-xl cursor-pointer hover:bg-purple-50 transition"> | |
| <div class="flex items-center"> | |
| <div class="bg-blue-100 p-2 rounded-lg mr-3"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">Rain</h3> | |
| <p class="text-xs text-gray-500">Free</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="neumorphism p-4 rounded-xl cursor-pointer hover:bg-purple-50 transition"> | |
| <div class="flex items-center"> | |
| <div class="bg-green-100 p-2 rounded-lg mr-3"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">Forest</h3> | |
| <p class="text-xs text-gray-500">Premium</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="neumorphism p-4 rounded-xl cursor-pointer hover:bg-purple-50 transition"> | |
| <div class="flex items-center"> | |
| <div class="bg-yellow-100 p-2 rounded-lg mr-3"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-yellow-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">Café</h3> | |
| <p class="text-xs text-gray-500">Premium</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="neumorphism p-4 rounded-xl cursor-pointer hover:bg-purple-50 transition"> | |
| <div class="flex items-center"> | |
| <div class="bg-purple-100 p-2 rounded-lg mr-3"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-purple-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">White Noise</h3> | |
| <p class="text-xs text-gray-500">Free</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex-1 mr-2"> | |
| <input type="range" min="0" max="100" value="50" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| </div> | |
| <div class="w-10 text-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" clip-rule="evenodd" /> | |
| </svg> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Settings Modal --> | |
| <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="glassmorphism rounded-xl p-6 w-full max-w-md"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-semibold text-gray-800">Settings</h2> | |
| <button onclick="closeSettingsModal()" class="text-gray-500 hover:text-gray-700"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <h3 class="font-medium text-gray-700 mb-2">Timer Settings</h3> | |
| <div class="space-y-3"> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-gray-600">Focus Duration</label> | |
| <select id="focusDuration" class="px-3 py-1 border rounded-lg"> | |
| <option value="15">15 min</option> | |
| <option value="25" selected>25 min</option> | |
| <option value="30">30 min</option> | |
| <option value="45">45 min</option> | |
| <option value="60">60 min</option> | |
| </select> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-gray-600">Short Break</label> | |
| <select id="shortBreakDuration" class="px-3 py-1 border rounded-lg"> | |
| <option value="3">3 min</option> | |
| <option value="5" selected>5 min</option> | |
| <option value="10">10 min</option> | |
| </select> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-gray-600">Long Break</label> | |
| <select id="longBreakDuration" class="px-3 py-1 border rounded-lg"> | |
| <option value="15">15 min</option> | |
| <option value="20">20 min</option> | |
| <option value="25">25 min</option> | |
| <option value="30" selected>30 min</option> | |
| </select> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-gray-600">Long Break Interval</label> | |
| <select id="longBreakInterval" class="px-3 py-1 border rounded-lg"> | |
| <option value="2">2</option> | |
| <option value="3">3</option> | |
| <option value="4" selected>4</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="pt-4 border-t"> | |
| <h3 class="font-medium text-gray-700 mb-2">Notifications</h3> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-gray-600">Timer Alerts</label> | |
| <label class="relative inline-flex items-center cursor-pointer"> | |
| <input type="checkbox" checked class="sr-only peer"> | |
| <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-purple-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-purple-600"></div> | |
| </label> | |
| </div> | |
| <div class="flex items-center justify-between mt-2"> | |
| <label class="text-gray-600">Task Reminders</label> | |
| <label class="relative inline-flex items-center cursor-pointer"> | |
| <input type="checkbox" class="sr-only peer"> | |
| <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-purple-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-purple-600"></div> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="pt-4 border-t"> | |
| <h3 class="font-medium text-gray-700 mb-2">Appearance</h3> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-gray-600">Dark Mode</label> | |
| <label class="relative inline-flex items-center cursor-pointer"> | |
| <input type="checkbox" class="sr-only peer"> | |
| <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-purple-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-purple-600"></div> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| <button onclick="saveSettings()" class="w-full mt-6 bg-purple-600 text-white py-2 rounded-lg hover:bg-purple-700 transition">Save Settings</button> | |
| </div> | |
| </div> | |
| <!-- Main App Container --> | |
| <div class="container mx-auto px-4 py-6"> | |
| <!-- Header --> | |
| <header class="flex justify-between items-center mb-8"> | |
| <div class="flex items-center"> | |
| <div class="w-10 h-10 rounded-full bg-purple-600 flex items-center justify-center mr-3"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| </div> | |
| <h1 class="text-2xl font-bold text-gray-800">FlowPomo</h1> | |
| </div> | |
| <div class="flex items-center space-x-3"> | |
| <button id="soundBtn" onclick="openSoundModal()" class="p-2 rounded-full hover:bg-purple-100 transition"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" /> | |
| </svg> | |
| </button> | |
| <button onclick="openSettingsModal()" class="p-2 rounded-full hover:bg-purple-100 transition"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> | |
| </svg> | |
| </button> | |
| <button id="authBtn" onclick="openAuthModal()" class="px-4 py-2 rounded-lg bg-purple-600 text-white hover:bg-purple-700 transition"> | |
| Sign In | |
| </button> | |
| <div id="userAvatar" class="hidden"> | |
| <img id="userPhoto" class="w-10 h-10 rounded-full cursor-pointer" src="" alt="User"> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
| <!-- Timer Section --> | |
| <div class="lg:col-span-2"> | |
| <div class="glassmorphism rounded-2xl p-8"> | |
| <!-- Timer Tabs --> | |
| <div class="flex justify-center mb-8"> | |
| <div class="flex space-x-1 bg-purple-50 p-1 rounded-xl"> | |
| <button id="pomoTab" onclick="switchTimerTab('pomo')" class="px-4 py-2 rounded-lg bg-purple-600 text-white">Pomodoro</button> | |
| <button id="shortBreakTab" onclick="switchTimerTab('shortBreak')" class="px-4 py-2 rounded-lg text-purple-600 hover:bg-purple-100 transition">Short Break</button> | |
| <button id="longBreakTab" onclick="switchTimerTab('longBreak')" class="px-4 py-2 rounded-lg text-purple-600 hover:bg-purple-100 transition">Long Break</button> | |
| </div> | |
| </div> | |
| <!-- Timer Display --> | |
| <div class="flex flex-col items-center"> | |
| <div class="timer-circle mb-8"> | |
| <div id="timerProgress" class="timer-progress" style="--progress: 0%"> | |
| <div class="absolute inset-4 bg-white rounded-full flex items-center justify-center"> | |
| <div id="timerDisplay" class="text-5xl font-bold text-gray-800">25:00</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex space-x-4"> | |
| <button id="startBtn" onclick="startTimer()" class="px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition font-medium flex items-center pulse-animation"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| Start | |
| </button> | |
| <button id="pauseBtn" onclick="pauseTimer()" class="px-6 py-3 bg-white text-gray-700 border rounded-lg hover:bg-gray-50 transition font-medium hidden"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 9v6m4-6v6m7-3a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| Pause | |
| </button> | |
| <button id="resetBtn" onclick="resetTimer()" class="px-6 py-3 bg-white text-gray-700 border rounded-lg hover:bg-gray-50 transition font-medium hidden"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> | |
| </svg> | |
| Reset | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Task Input --> | |
| <div class="mt-8"> | |
| <h3 class="text-lg font-medium text-gray-700 mb-3">Current Task</h3> | |
| <div class="flex"> | |
| <input id="taskInput" type="text" placeholder="What are you working on?" class="flex-1 px-4 py-2 rounded-l-lg border focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
| <button onclick="addTask()" class="px-4 py-2 bg-purple-600 text-white rounded-r-lg hover:bg-purple-700 transition"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <div id="currentTask" class="mt-3 flex items-center justify-between bg-white p-3 rounded-lg border hidden"> | |
| <div class="flex items-center"> | |
| <div class="w-4 h-4 rounded-full border-2 border-purple-500 mr-3"></div> | |
| <span id="taskText" class="text-gray-700">Complete project presentation</span> | |
| </div> | |
| <button onclick="completeTask()" class="text-purple-600 hover:text-purple-800"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Tasks List --> | |
| <div class="mt-6 glassmorphism rounded-2xl p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-800">Tasks</h2> | |
| <button id="premiumTasksBtn" onclick="openPremiumModal()" class="text-xs bg-gradient-to-r from-purple-500 to-pink-500 text-white px-3 py-1 rounded-full flex items-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v13m8-8v8m-4-5v5M4 16v6M4 11v5m0 0v5m0-5v5m0 0v5" /> | |
| </svg> | |
| PRO | |
| </button> | |
| </div> | |
| <div id="tasksList" class="space-y-2"> | |
| <div class="flex items-center justify-between bg-white p-3 rounded-lg border"> | |
| <div class="flex items-center"> | |
| <div class="w-4 h-4 rounded-full border-2 border-gray-300 mr-3"></div> | |
| <span class="text-gray-700">Write blog post</span> | |
| </div> | |
| <button class="text-gray-400 hover:text-gray-600"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="flex items-center justify-between bg-white p-3 rounded-lg border"> | |
| <div class="flex items-center"> | |
| <div class="w-4 h-4 rounded-full border-2 border-gray-300 mr-3"></div> | |
| <span class="text-gray-700">Review code</span> | |
| </div> | |
| <button class="text-gray-400 hover:text-gray-600"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| <div id="premiumTasks" class="mt-4 hidden"> | |
| <div class="flex items-center justify-between bg-white p-3 rounded-lg border border-purple-300"> | |
| <div class="flex items-center"> | |
| <div class="w-4 h-4 rounded-full border-2 border-purple-500 mr-3"></div> | |
| <span class="text-gray-700">Weekly planning (template)</span> | |
| </div> | |
| <div class="flex items-center"> | |
| <span class="text-xs bg-purple-100 text-purple-800 px-2 py-1 rounded mr-2">Template</span> | |
| <button class="text-gray-400 hover:text-gray-600"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Stats & Challenges Section --> | |
| <div> | |
| <!-- Stats Card --> | |
| <div class="glassmorphism rounded-2xl p-6 mb-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-800">Today's Stats</h2> | |
| <button onclick="exportStats()" class="text-sm text-purple-600 hover:text-purple-800 flex items-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /> | |
| </svg> | |
| Export | |
| </button> | |
| </div> | |
| <div class="grid grid-cols-2 gap-4 mb-4"> | |
| <div class="bg-white p-3 rounded-lg text-center"> | |
| <div class="text-2xl font-bold text-purple-600">3</div> | |
| <div class="text-xs text-gray-500">Pomodoros</div> | |
| </div> | |
| <div class="bg-white p-3 rounded-lg text-center"> | |
| <div class="text-2xl font-bold text-blue-600">75</div> | |
| <div class="text-xs text-gray-500">Minutes</div> | |
| </div> | |
| </div> | |
| <div class="h-40"> | |
| <canvas id="dailyChart"></canvas> | |
| </div> | |
| </div> | |
| <!-- Challenges Card --> | |
| <div class="glassmorphism rounded-2xl p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-800">Daily Challenge</h2> | |
| <span class="text-xs bg-purple-100 text-purple-800 px-2 py-1 rounded">Day 3/7</span> | |
| </div> | |
| <div class="mb-4"> | |
| <div class="flex justify-between text-sm text-gray-600 mb-1"> | |
| <span>Complete 4 pomodoros</span> | |
| <span>3/4</span> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2"> | |
| <div class="bg-purple-600 h-2 rounded-full" style="width: 75%"></div> | |
| </div> | |
| </div> | |
| <div class="space-y-3"> | |
| <div class="flex items-center"> | |
| <div class="w-8 h-8 rounded-full bg-purple-100 flex items-center justify-center mr-3"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">Focus Master</h3> | |
| <p class="text-xs text-gray-500">Complete 7 days of challenges</p> | |
| </div> | |
| </div> | |
| <div class="flex items-center opacity-50"> | |
| <div class="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center mr-3"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">Early Bird</h3> | |
| <p class="text-xs text-gray-500">Complete 5 morning sessions</p> | |
| </div> | |
| </div> | |
| </div> | |
| <button id="premiumChallengesBtn" onclick="openPremiumModal()" class="w-full mt-6 bg-gradient-to-r from-purple-500 to-pink-500 text-white py-2 rounded-lg font-medium flex items-center justify-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v13m8-8v8m-4-5v5M4 16v6M4 11v5m0 0v5m0-5v5m0 0v5" /> | |
| </svg> | |
| Unlock All Challenges | |
| </button> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <script> | |
| // Timer Variables | |
| let timer; | |
| let isRunning = false; | |
| let currentMode = 'pomo'; // 'pomo', 'shortBreak', 'longBreak' | |
| let secondsLeft = 25 * 60; // 25 minutes in seconds | |
| let pomodoroCount = 0; | |
| // Timer Settings | |
| let settings = { | |
| focusDuration: 25, | |
| shortBreakDuration: 5, | |
| longBreakDuration: 30, | |
| longBreakInterval: 4, | |
| autoStartBreaks: true, | |
| autoStartPomodoros: false | |
| }; | |
| // DOM Elements | |
| const timerDisplay = document.getElementById('timerDisplay'); | |
| const timerProgress = document.getElementById('timerProgress'); | |
| const startBtn = document.getElementById('startBtn'); | |
| const pauseBtn = document.getElementById('pauseBtn'); | |
| const resetBtn = document.getElementById('resetBtn'); | |
| const pomoTab = document.getElementById('pomoTab'); | |
| const shortBreakTab = document.getElementById('shortBreakTab'); | |
| const longBreakTab = document.getElementById('longBreakTab'); | |
| // Auth Elements | |
| const authBtn = document.getElementById('authBtn'); | |
| const userAvatar = document.getElementById('userAvatar'); | |
| const userPhoto = document.getElementById('userPhoto'); | |
| // Initialize the timer display | |
| updateTimerDisplay(); | |
| // Timer Functions | |
| function startTimer() { | |
| if (!isRunning) { | |
| isRunning = true; | |
| startBtn.classList.add('hidden'); | |
| pauseBtn.classList.remove('hidden'); | |
| resetBtn.classList.remove('hidden'); | |
| timer = setInterval(() => { | |
| secondsLeft--; | |
| updateTimerDisplay(); | |
| if (secondsLeft <= 0) { | |
| clearInterval(timer); | |
| timerComplete(); | |
| } | |
| }, 1000); | |
| } | |
| } | |
| function pauseTimer() { | |
| if (isRunning) { | |
| isRunning = false; | |
| clearInterval(timer); | |
| startBtn.classList.remove('hidden'); | |
| pauseBtn.classList.add('hidden'); | |
| } | |
| } | |
| function resetTimer() { | |
| pauseTimer(); | |
| isRunning = false; | |
| resetBtn.classList.add('hidden'); | |
| startBtn.classList.remove('hidden'); | |
| switch (currentMode) { | |
| case 'pomo': | |
| secondsLeft = settings.focusDuration * 60; | |
| break; | |
| case 'shortBreak': | |
| secondsLeft = settings.shortBreakDuration * 60; | |
| break; | |
| case 'longBreak': | |
| secondsLeft = settings.longBreakDuration * 60; | |
| break; | |
| } | |
| updateTimerDisplay(); | |
| } | |
| function timerComplete() { | |
| isRunning = false; | |
| // Play sound | |
| const audio = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-alarm-digital-clock-beep-989.mp3'); | |
| audio.play(); | |
| // Update stats | |
| if (currentMode === 'pomo') { | |
| pomodoroCount++; | |
| // Save to database if logged in | |
| } | |
| // Switch mode automatically | |
| if (currentMode === 'pomo') { | |
| if (pomodoroCount % settings.longBreakInterval === 0) { | |
| switchTimerTab('longBreak'); | |
| } else { | |
| switchTimerTab('shortBreak'); | |
| } | |
| if (settings.autoStartBreaks) { | |
| startTimer(); | |
| } | |
| } else { | |
| switchTimerTab('pomo'); | |
| if (settings.autoStartPomodoros) { | |
| startTimer(); | |
| } | |
| } | |
| } | |
| function updateTimerDisplay() { | |
| const minutes = Math.floor(secondsLeft / 60); | |
| const seconds = secondsLeft % 60; | |
| timerDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; | |
| // Update progress circle | |
| let totalSeconds; | |
| switch (currentMode) { | |
| case 'pomo': | |
| totalSeconds = settings.focusDuration * 60; | |
| break; | |
| case 'shortBreak': | |
| totalSeconds = settings.shortBreakDuration * 60; | |
| break; | |
| case 'longBreak': | |
| totalSeconds = settings.longBreakDuration * 60; | |
| break; | |
| } | |
| const progress = ((totalSeconds - secondsLeft) / totalSeconds) * 100; | |
| timerProgress.style.setProperty('--progress', `${progress}%`); | |
| } | |
| function switchTimerTab(mode) { | |
| currentMode = mode; | |
| // Reset timer if switching modes while not running | |
| if (!isRunning) { | |
| resetTimer(); | |
| } | |
| // Update UI | |
| pomoTab.classList.remove('bg-purple-600', 'text-white'); | |
| pomoTab.classList.add('text-purple-600', 'hover:bg-purple-100'); | |
| shortBreakTab.classList.remove('bg-purple-600', 'text-white'); | |
| shortBreakTab.classList.add('text-purple-600', 'hover:bg-purple-100'); | |
| longBreakTab.classList.remove('bg-purple-600', 'text-white'); | |
| longBreakTab.classList.add('text-purple-600', 'hover:bg-purple-100'); | |
| switch (mode) { | |
| case 'pomo': | |
| pomoTab.classList.add('bg-purple-600', 'text-white'); | |
| pomoTab.classList.remove('text-purple-600', 'hover:bg-purple-100'); | |
| secondsLeft = settings.focusDuration * 60; | |
| break; | |
| case 'shortBreak': | |
| shortBreakTab.classList.add('bg-purple-600', 'text-white'); | |
| shortBreakTab.classList.remove('text-purple-600', 'hover:bg-purple-100'); | |
| secondsLeft = settings.shortBreakDuration * 60; | |
| break; | |
| case 'longBreak': | |
| longBreakTab.classList.add('bg-purple-600', 'text-white'); | |
| longBreakTab.classList.remove('text-purple-600', 'hover:bg-purple-100'); | |
| secondsLeft = settings.longBreakDuration * 60; | |
| break; | |
| } | |
| updateTimerDisplay(); | |
| } | |
| // Task Functions | |
| function addTask() { | |
| const taskInput = document.getElementById('taskInput'); | |
| const taskText = taskInput.value.trim(); | |
| if (taskText) { | |
| document.getElementById('taskText').textContent = taskText; | |
| document.getElementById('currentTask').classList.remove('hidden'); | |
| taskInput.value = ''; | |
| // Add to tasks list | |
| const tasksList = document.getElementById('tasksList'); | |
| const taskElement = document.createElement('div'); | |
| taskElement.className = 'flex items-center justify-between bg-white p-3 rounded-lg border'; | |
| taskElement.innerHTML = ` | |
| <div class="flex items-center"> | |
| <div class="w-4 h-4 rounded-full border-2 border-gray-300 mr-3"></div> | |
| <span class="text-gray-700">${taskText}</span> | |
| </div> | |
| <button class="text-gray-400 hover:text-gray-600"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> | |
| </svg> | |
| </button> | |
| `; | |
| tasksList.prepend(taskElement); | |
| } | |
| } | |
| function completeTask() { | |
| document.getElementById('currentTask').classList.add('hidden'); | |
| } | |
| // Auth Functions | |
| function openAuthModal() { | |
| document.getElementById('authModal').classList.remove('hidden'); | |
| } | |
| function closeAuthModal() { | |
| document.getElementById('authModal').classList.add('hidden'); | |
| document.getElementById('authMessage').classList.add('hidden'); | |
| } | |
| function switchAuthTab(tab) { | |
| if (tab === 'login') { | |
| document.getElementById('loginTab').classList.add('tab-active'); | |
| document.getElementById('loginTab').classList.remove('text-gray-500'); | |
| document.getElementById('signupTab').classList.remove('tab-active'); | |
| document.getElementById('signupTab').classList.add('text-gray-500'); | |
| document.getElementById('loginForm').classList.remove('hidden'); | |
| document.getElementById('signupForm').classList.add('hidden'); | |
| } else { | |
| document.getElementById('signupTab').classList.add('tab-active'); | |
| document.getElementById('signupTab').classList.remove('text-gray-500'); | |
| document.getElementById('loginTab').classList.remove('tab-active'); | |
| document.getElementById('loginTab').classList.add('text-gray-500'); | |
| document.getElementById('signupForm').classList.remove('hidden'); | |
| document.getElementById('loginForm').classList.add('hidden'); | |
| } | |
| } | |
| function loginWithEmail() { | |
| const email = document.getElementById('loginEmail').value; | |
| const password = document.getElementById('loginPassword').value; | |
| auth.signInWithEmailAndPassword(email, password) | |
| .then((userCredential) => { | |
| updateAuthUI(userCredential.user); | |
| closeAuthModal(); | |
| }) | |
| .catch((error) => { | |
| showAuthMessage(error.message); | |
| }); | |
| } | |
| function signUpWithEmail() { | |
| const email = document.getElementById('signupEmail').value; | |
| const password = document.getElementById('signupPassword').value; | |
| const confirmPassword = document.getElementById('signupConfirmPassword').value; | |
| if (password !== confirmPassword) { | |
| showAuthMessage("Passwords don't match"); | |
| return; | |
| } | |
| auth.createUserWithEmailAndPassword(email, password) | |
| .then((userCredential) => { | |
| updateAuthUI(userCredential.user); | |
| closeAuthModal(); | |
| }) | |
| .catch((error) => { | |
| showAuthMessage(error.message); | |
| }); | |
| } | |
| function loginWithGoogle() { | |
| const provider = new firebase.auth.GoogleAuthProvider(); | |
| auth.signInWithPopup(provider) | |
| .then((result) => { | |
| updateAuthUI(result.user); | |
| closeAuthModal(); | |
| }) | |
| .catch((error) => { | |
| showAuthMessage(error.message); | |
| }); | |
| } | |
| function showAuthMessage(message) { | |
| const authMessage = document.getElementById('authMessage'); | |
| authMessage.textContent = message; | |
| authMessage.classList.remove('hidden'); | |
| } | |
| function updateAuthUI(user) { | |
| if (user) { | |
| authBtn.classList.add('hidden'); | |
| userAvatar.classList.remove('hidden'); | |
| userPhoto.src = user.photoURL || 'https://ui-avatars.com/api/?name=' + encodeURIComponent(user.displayName || user.email); | |
| // Load user settings from Firestore | |
| db.collection('users').doc(user.uid).get() | |
| .then((doc) => { | |
| if (doc.exists) { | |
| const userSettings = doc.data().settings; | |
| if (userSettings) { | |
| settings = {...settings, ...userSettings}; | |
| applySettings(); | |
| } | |
| } | |
| }); | |
| } else { | |
| authBtn.classList.remove('hidden'); | |
| userAvatar.classList.add('hidden'); | |
| } | |
| } | |
| // Check auth state on load | |
| auth.onAuthStateChanged(updateAuthUI); | |
| // Modal Functions | |
| function openPremiumModal() { | |
| document.getElementById('premiumModal').classList.remove('hidden'); | |
| } | |
| function closePremiumModal() { | |
| document.getElementById('premiumModal').classList.add('hidden'); | |
| } | |
| function openSoundModal() { | |
| document.getElementById('soundModal').classList.remove('hidden'); | |
| } | |
| function closeSoundModal() { | |
| document.getElementById('soundModal').classList.add('hidden'); | |
| } | |
| function openSettingsModal() { | |
| // Load current settings into form | |
| document.getElementById('focusDuration').value = settings.focusDuration; | |
| document.getElementById('shortBreakDuration').value = settings.shortBreakDuration; | |
| document.getElementById('longBreakDuration').value = settings.longBreakDuration; | |
| document.getElementById('longBreakInterval').value = settings.longBreakInterval; | |
| document.getElementById('settingsModal').classList.remove('hidden'); | |
| } | |
| function closeSettingsModal() { | |
| document.getElementById('settingsModal').classList.add('hidden'); | |
| } | |
| function saveSettings() { | |
| // Get values from form | |
| settings.focusDuration = parseInt(document.getElementById('focusDuration').value); | |
| settings.shortBreakDuration = parseInt(document.getElementById('shortBreakDuration').value); | |
| settings.longBreakDuration = parseInt(document.getElementById('longBreakDuration').value); | |
| settings.longBreakInterval = parseInt(document.getElementById('longBreakInterval').value); | |
| // Apply changes | |
| applySettings(); | |
| // Save to Firestore if logged in | |
| const user = auth.currentUser; | |
| if (user) { | |
| db.collection('users').doc(user.uid).set({ | |
| settings: settings | |
| }, { merge: true }); | |
| } | |
| closeSettingsModal(); | |
| } | |
| function applySettings() { | |
| // Update timer if not running | |
| if (!isRunning) { | |
| switchTimerTab(currentMode); | |
| } | |
| } | |
| function subscribePremium() { | |
| const user = auth.currentUser; | |
| if (!user) { | |
| showAuthMessage("Please sign in to subscribe"); | |
| closePremiumModal(); | |
| openAuthModal(); | |
| return; | |
| } | |
| // In a real app, you would redirect to Stripe checkout | |
| // This is just a simulation | |
| setTimeout(() => { | |
| alert('Premium subscription successful!'); | |
| closePremiumModal(); | |
| // Show premium features | |
| document.getElementById('premiumTasks').classList.remove('hidden'); | |
| document.getElementById('premiumTasksBtn').classList.add('hidden'); | |
| document.getElementById('premiumChallengesBtn').classList.add('hidden'); | |
| }, 1000); | |
| } | |
| function exportStats() { | |
| // In a real app, this would generate a CSV file | |
| alert('Exporting stats to CSV...'); | |
| } | |
| // Initialize Charts | |
| function initCharts() { | |
| // Daily Stats Chart | |
| const dailyCtx = document.getElementById('dailyChart').getContext('2d'); | |
| new Chart(dailyCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], | |
| datasets: [{ | |
| label: 'Pomodoros', | |
| data: [3, 5, 2, 4, 6, 1, 0], | |
| backgroundColor: '#7c3aed', | |
| borderRadius: 4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| grid: { | |
| display: false | |
| }, | |
| ticks: { | |
| stepSize: 2 | |
| } | |
| }, | |
| x: { | |
| grid: { | |
| display: false | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Initialize the app | |
| window.onload = function() { | |
| initCharts(); | |
| // Check for premium features (simulated) | |
| const isPremium = false; // In a real app, this would check the user's subscription | |
| if (isPremium) { | |
| document.getElementById('premiumTasks').classList.remove('hidden'); | |
| document.getElementById('premiumTasksBtn').classList.add('hidden'); | |
| document.getElementById('premiumChallengesBtn').classList.add('hidden'); | |
| } | |
| }; | |
| </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=Vinaymsgbox/pomodor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |