pomodoro/index.html

553 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Odak Sayacı - Pomodoro</title>
<style>
:root {
--primary-color: #6a11cb;
--secondary-color: #2575fc;
--background-start: #1a1a2e;
--background-end: #16213e;
--card-bg: rgba(0, 0, 0, 0.6);
--text-color: #ffffff;
--accent-color: #ffcc00;
--shadow-color: rgba(0, 0, 0, 0.3);
--border-color: rgba(255, 255, 255, 0.1);
}
body {
font-family: 'Poppins', sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, var(--background-start) 0%, var(--background-end) 100%);
color: var(--text-color);
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.container {
display: flex;
flex-wrap: wrap;
gap: 30px;
max-width: 1200px;
width: 90%;
margin: 30px 0;
justify-content: center;
align-items: flex-start;
}
.card {
background: var(--card-bg);
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 30px var(--shadow-color);
backdrop-filter: blur(15px);
border: 1px solid var(--border-color);
flex: 1;
min-width: 300px;
max-width: 500px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px var(--shadow-color);
}
h1 {
color: var(--accent-color);
margin-bottom: 30px;
font-size: 2.5em;
text-shadow: 2px 2px 5px rgba(0,0,0,0.5);
}
.timer-container {
position: relative;
width: 250px;
height: 250px;
margin-bottom: 30px;
}
.timer-svg {
transform: rotate(-90deg);
position: absolute;
top: 0;
left: 0;
}
.timer-circle {
fill: none;
stroke: var(--primary-color);
stroke-width: 15;
stroke-linecap: round;
transition: stroke-dashoffset 0.5s linear;
}
.timer-bg-circle {
stroke: rgba(255, 255, 255, 0.1);
stroke-width: 15;
}
.timer-display {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 4em;
font-weight: 600;
color: #ffffff;
text-shadow: 2px 2px 10px rgba(0,0,0,0.5);
}
.controls button {
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
color: white;
border: none;
padding: 12px 25px;
margin: 0 10px;
border-radius: 30px;
font-size: 1.1em;
cursor: pointer;
outline: none;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
transition: all 0.3s ease;
}
.controls button:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0,0,0,0.3);
opacity: 0.9;
}
.controls button:active {
transform: translateY(0);
box-shadow: 0 3px 10px rgba(0,0,0,0.2);
}
.controls button:disabled {
background: #555;
cursor: not-allowed;
opacity: 0.6;
}
.summary-card {
display: flex;
flex-direction: column;
gap: 15px;
width: 100%;
margin-top: 20px;
}
.summary-item {
background: rgba(0, 0, 0, 0.7);
padding: 15px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.05);
text-align: left;
display: flex;
justify-content: space-between;
align-items: center;
}
.summary-item span:first-child {
font-weight: 400;
color: #ffffff;
}
.summary-item span:last-child {
font-weight: 600;
color: var(--accent-color);
font-size: 1.2em;
}
.task-list-card {
width: 100%;
}
.task-input-container {
display: flex;
gap: 10px;
margin-bottom: 20px;
width: 100%;
}
.task-input-container input[type="text"] {
flex-grow: 1;
padding: 12px 15px;
border-radius: 10px;
border: 1px solid var(--border-color);
background: rgba(255, 255, 255, 0.05);
color: var(--text-color);
font-size: 1em;
outline: none;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.task-input-container input[type="text"]::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.task-input-container input[type="text"]:focus {
border-color: var(--secondary-color);
box-shadow: 0 0 0 3px rgba(37, 117, 252, 0.3);
}
.task-input-container button {
background: var(--accent-color);
color: #333;
border: none;
padding: 12px 20px;
border-radius: 10px;
font-size: 1em;
cursor: pointer;
transition: background 0.3s ease;
}
.task-input-container button:hover {
background: #e6b800;
}
.task-list {
list-style: none;
padding: 0;
width: 100%;
}
.task-item {
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(0, 0, 0, 0.7);
padding: 12px 15px;
margin-bottom: 10px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.05);
transition: background 0.3s ease;
}
.task-item.completed {
opacity: 0.7;
text-decoration: line-through;
background: rgba(255, 255, 255, 0.03);
}
.task-item span {
flex-grow: 1;
text-align: left;
margin-right: 15px;
cursor: pointer;
}
.task-item .task-actions {
display: flex;
gap: 8px;
}
.task-item .task-actions button {
background: none;
border: none;
color: var(--text-color);
cursor: pointer;
font-size: 1.1em;
padding: 5px;
border-radius: 5px;
transition: color 0.3s ease, background 0.3s ease;
}
.task-item .task-actions button.complete-btn:hover {
color: #4CAF50;
}
.task-item .task-actions button.delete-btn:hover {
color: #f44336;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
align-items: center;
}
.card {
width: 90%;
max-width: unset;
}
h1 {
font-size: 2em;
}
.timer-display {
font-size: 3.5em;
}
.controls button {
padding: 10px 20px;
font-size: 1em;
margin: 0 5px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="card timer-card">
<h1>Odak Sayacı</h1>
<div class="timer-container">
<svg class="timer-svg" width="250" height="250" viewBox="0 0 250 250">
<circle class="timer-bg-circle" cx="125" cy="125" r="110"></circle>
<circle class="timer-circle" cx="125" cy="125" r="110" id="timerCircle"></circle>
</svg>
<div class="timer-display" id="timerDisplay">25:00</div>
</div>
<div class="controls">
<button id="startBtn">Başlat</button>
<button id="pauseBtn" disabled>Duraklat</button>
<button id="resetBtn">Sıfırla</button>
</div>
<div class="summary-card">
<div class="summary-item">
<span>Tamamlanan Pomodoro:</span>
<span id="completedPomodoros">0</span>
</div>
<div class="summary-item">
<span>Günlük Toplam Çalışma:</span>
<span id="dailyTotalTime">00:00</span>
</div>
</div>
</div>
<div class="card task-list-card">
<h2>Görev Listesi</h2>
<div class="task-input-container">
<input type="text" id="taskInput" placeholder="Yeni görev ekle...">
<button id="addTaskBtn">Ekle</button>
</div>
<ul class="task-list" id="taskList">
<!-- Görevler buraya eklenecek -->
</ul>
</div>
</div>
<audio id="beepSound" src="data:audio/wav;base64,UklGRl9SAABXQVZFZm10IBAAAAABAAEARKwAAESsAAABAAgAZGF0YV8SAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"></audio>
<script>
const timerDisplay = document.getElementById('timerDisplay');
const timerCircle = document.getElementById('timerCircle');
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resetBtn = document.getElementById('resetBtn');
const completedPomodorosSpan = document.getElementById('completedPomodoros');
const dailyTotalTimeSpan = document.getElementById('dailyTotalTime');
const taskInput = document.getElementById('taskInput');
const addTaskBtn = document.getElementById('addTaskBtn');
const taskList = document.getElementById('taskList');
const beepSound = document.getElementById('beepSound');
const WORK_TIME = 25 * 60; // 25 dakika
const BREAK_TIME = 5 * 60; // 5 dakika
let timeLeft = WORK_TIME;
let timerInterval;
let isPaused = true;
let isWorking = true; // true: çalışma, false: mola
let completedPomodoros = 0;
let dailyTotalWorkSeconds = 0; // Günlük toplam çalışma süresi saniye cinsinden
const circumference = 2 * Math.PI * 110; // r=110
// Bildirim izni iste
if (Notification.permission === 'default') {
Notification.requestPermission();
}
function updateTimerDisplay() {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerDisplay.textContent = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
}
function updateCircleProgress() {
const totalTime = isWorking ? WORK_TIME : BREAK_TIME;
const progress = (timeLeft / totalTime);
const dashoffset = circumference * (1 - progress);
timerCircle.style.strokeDasharray = circumference;
timerCircle.style.strokeDashoffset = dashoffset;
timerCircle.style.stroke = isWorking ? 'var(--primary-color)' : 'var(--accent-color)';
}
function startTimer() {
if (!isPaused) return;
isPaused = false;
startBtn.disabled = true;
pauseBtn.disabled = false;
timerInterval = setInterval(() => {
timeLeft--;
updateTimerDisplay();
updateCircleProgress();
if (timeLeft <= 0) {
clearInterval(timerInterval);
beepSound.play();
showNotification();
if (isWorking) {
completedPomodoros++;
dailyTotalWorkSeconds += WORK_TIME;
saveState();
updateSummary();
isWorking = false;
timeLeft = BREAK_TIME;
alert('Çalışma süresi bitti! Şimdi mola zamanı.');
} else {
isWorking = true;
timeLeft = WORK_TIME;
alert('Mola süresi bitti! Hadi tekrar çalışmaya.');
}
updateTimerDisplay();
updateCircleProgress();
startTimer(); // Otomatik olarak bir sonraki döngüyü başlat
}
}, 1000);
}
function pauseTimer() {
clearInterval(timerInterval);
isPaused = true;
startBtn.disabled = false;
pauseBtn.disabled = true;
}
function resetTimer() {
clearInterval(timerInterval);
isPaused = true;
isWorking = true;
timeLeft = WORK_TIME;
startBtn.disabled = false;
pauseBtn.disabled = true;
updateTimerDisplay();
updateCircleProgress();
}
function showNotification() {
if (Notification.permission === 'granted') {
const title = isWorking ? 'Mola Zamanı!' : 'Çalışma Zamanı!';
const options = {
body: isWorking ? '25 dakikalık çalışma süreniz bitti. Şimdi 5 dakikalık bir mola verin.' : '5 dakikalık molanız bitti. Hadi tekrar odaklanalım!',
icon: 'https://www.flaticon.com/svg/static/icons/svg/2533/2533159.svg' // Placeholder icon
};
new Notification(title, options);
}
}
function updateSummary() {
completedPomodorosSpan.textContent = completedPomodoros;
const totalMinutes = Math.floor(dailyTotalWorkSeconds / 60);
const totalHours = Math.floor(totalMinutes / 60);
const remainingMinutes = totalMinutes % 60;
dailyTotalTimeSpan.textContent = `${String(totalHours).padStart(2, '0')}:${String(remainingMinutes).padStart(2, '0')}`;
}
// --- Local Storage İşlemleri ---
function saveState() {
localStorage.setItem('pomodoroState', JSON.stringify({
completedPomodoros,
dailyTotalWorkSeconds,
tasks: tasks // Görevleri de kaydet
}));
}
function loadState() {
const savedState = JSON.parse(localStorage.getItem('pomodoroState'));
if (savedState) {
completedPomodoros = savedState.completedPomodoros || 0;
dailyTotalWorkSeconds = savedState.dailyTotalWorkSeconds || 0;
tasks = savedState.tasks || [];
updateSummary();
renderTasks();
}
}
// --- Görev Listesi İşlemleri ---
let tasks = [];
function addTask() {
const taskText = taskInput.value.trim();
if (taskText) {
tasks.push({ id: Date.now(), text: taskText, completed: false });
taskInput.value = '';
saveState();
renderTasks();
}
}
function renderTasks() {
taskList.innerHTML = '';
tasks.forEach(task => {
const li = document.createElement('li');
li.className = `task-item ${task.completed ? 'completed' : ''}`;
li.innerHTML = `
<span>${task.text}</span>
<div class="task-actions">
<button class="complete-btn" data-id="${task.id}" title="${task.completed ? 'Tamamlanmadı olarak işaretle' : 'Tamamlandı olarak işaretle'}">
${task.completed ? '&#10003;' : '&#9744;'}
</button>
<button class="delete-btn" data-id="${task.id}" title="Görevi Sil">&#10006;</button>
</div>
`;
taskList.appendChild(li);
});
}
function handleTaskAction(event) {
const target = event.target;
const taskId = parseInt(target.dataset.id);
if (target.classList.contains('complete-btn')) {
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex > -1) {
tasks[taskIndex].completed = !tasks[taskIndex].completed;
saveState();
renderTasks();
}
} else if (target.classList.contains('delete-btn')) {
tasks = tasks.filter(task => task.id !== taskId);
saveState();
renderTasks();
}
}
// --- Event Listeners ---
startBtn.addEventListener('click', startTimer);
pauseBtn.addEventListener('click', pauseTimer);
resetBtn.addEventListener('click', resetTimer);
addTaskBtn.addEventListener('click', addTask);
taskInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
addTask();
}
});
taskList.addEventListener('click', handleTaskAction);
// --- Initial Setup ---
document.addEventListener('DOMContentLoaded', () => {
loadState();
updateTimerDisplay();
updateCircleProgress();
});
</script>
</body>
</html>