pomodor / index.html
Vinaymsgbox's picture
Add 2 files
ae09d0d verified
<!DOCTYPE html>
<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>