Skip to content

Commit 6b9bba5

Browse files
committed
Add customizable Pomodoro timer settings and sound
Introduces user-configurable work and break durations for the Pomodoro timer, accessible via a settings panel. Adds a notification sound when a session ends and updates timer logic to use the custom durations.
1 parent d9efd73 commit 6b9bba5

1 file changed

Lines changed: 85 additions & 13 deletions

File tree

app/dashboard/todo/page.tsx

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@ export default function TodoPage() {
4444
const [pomodoroActive, setPomodoroActive] = useState(false)
4545
const [pomodoroMode, setPomodoroMode] = useState<'work' | 'break'>('work')
4646
const [pomodoroSessions, setPomodoroSessions] = useState(0)
47+
48+
// Custom Settings State
49+
const [workDuration, setWorkDuration] = useState(25)
50+
const [breakDuration, setBreakDuration] = useState(5)
51+
const [showSettings, setShowSettings] = useState(false)
52+
53+
// Sound Effect
54+
const playNotificationSound = useCallback(() => {
55+
try {
56+
const audio = new Audio('https://assets.mixkit.co/active_storage/sfx/2869/2869-preview.mp3')
57+
audio.volume = 0.5
58+
audio.play().catch(e => console.log('Audio play failed:', e))
59+
} catch (e) {
60+
console.error('Audio setup failed', e)
61+
}
62+
}, [])
4763

4864
const loadTasks = useCallback(async () => {
4965
const user = getCurrentUser()
@@ -99,27 +115,27 @@ export default function TodoPage() {
99115
} else if (pomodoroTime === 0) {
100116
// Timer finished
101117
setPomodoroActive(false)
118+
playNotificationSound()
102119

103120
if (pomodoroMode === 'work') {
104121
// Work session completed
105122
setPomodoroSessions((s) => s + 1)
106-
// Notification sound/alert could be added here
107123
alert('Work session completed! Time for a break.')
108124
// Switch to break mode
109125
setPomodoroMode('break')
110-
setPomodoroTime(5 * 60) // 5 minute break
126+
setPomodoroTime(breakDuration * 60)
111127
} else {
112128
// Break completed
113129
alert('Break is over! Ready for another session?')
114130
setPomodoroMode('work')
115-
setPomodoroTime(25 * 60) // 25 minute work
131+
setPomodoroTime(workDuration * 60)
116132
}
117133
}
118134

119135
return () => {
120136
if (interval) clearInterval(interval)
121137
}
122-
}, [pomodoroActive, pomodoroTime, pomodoroMode])
138+
}, [pomodoroActive, pomodoroTime, pomodoroMode, workDuration, breakDuration, playNotificationSound])
123139

124140
const startPomodoro = () => {
125141
setPomodoroActive(true)
@@ -132,9 +148,21 @@ export default function TodoPage() {
132148
const resetPomodoro = () => {
133149
setPomodoroActive(false)
134150
if (pomodoroMode === 'work') {
135-
setPomodoroTime(25 * 60)
151+
setPomodoroTime(workDuration * 60)
136152
} else {
137-
setPomodoroTime(5 * 60)
153+
setPomodoroTime(breakDuration * 60)
154+
}
155+
}
156+
157+
const handleSettingsSave = () => {
158+
setShowSettings(false)
159+
// Update current timer if it matches the current mode
160+
if (!pomodoroActive) {
161+
if (pomodoroMode === 'work') {
162+
setPomodoroTime(workDuration * 60)
163+
} else {
164+
setPomodoroTime(breakDuration * 60)
165+
}
138166
}
139167
}
140168

@@ -255,15 +283,59 @@ export default function TodoPage() {
255283
<div className="flex items-center gap-2 mb-6">
256284
<Timer className="h-5 w-5 text-primary" />
257285
<h2 className="text-lg font-semibold">Pomodoro Timer</h2>
258-
<span className={`ml-auto px-3 py-1 rounded-full text-xs font-medium ${
259-
pomodoroMode === 'work'
260-
? 'bg-primary/10 text-primary'
261-
: 'bg-green-500/10 text-green-600'
262-
}`}>
263-
{pomodoroMode === 'work' ? 'Focus' : 'Break'}
264-
</span>
286+
<div className="ml-auto flex items-center gap-2">
287+
<Button
288+
variant="ghost"
289+
size="icon"
290+
className="h-8 w-8"
291+
onClick={() => setShowSettings(!showSettings)}
292+
title="Timer Settings"
293+
>
294+
<RotateCcw className="h-4 w-4" />
295+
</Button>
296+
<span className={`px-3 py-1 rounded-full text-xs font-medium ${
297+
pomodoroMode === 'work'
298+
? 'bg-primary/10 text-primary'
299+
: 'bg-green-500/10 text-green-600'
300+
}`}>
301+
{pomodoroMode === 'work' ? 'Focus' : 'Break'}
302+
</span>
303+
</div>
265304
</div>
266305

306+
{showSettings && (
307+
<div className="mb-6 p-4 border border-border bg-secondary/50 rounded-lg animate-fade-in-down">
308+
<h3 className="text-sm font-medium mb-3">Timer Settings (minutes)</h3>
309+
<div className="grid grid-cols-2 gap-4">
310+
<div className="space-y-2">
311+
<label className="text-xs text-muted-foreground">Work Duration</label>
312+
<Input
313+
type="number"
314+
min="1"
315+
max="60"
316+
value={workDuration}
317+
onChange={(e) => setWorkDuration(parseInt(e.target.value) || 25)}
318+
className="bg-background"
319+
/>
320+
</div>
321+
<div className="space-y-2">
322+
<label className="text-xs text-muted-foreground">Break Duration</label>
323+
<Input
324+
type="number"
325+
min="1"
326+
max="30"
327+
value={breakDuration}
328+
onChange={(e) => setBreakDuration(parseInt(e.target.value) || 5)}
329+
className="bg-background"
330+
/>
331+
</div>
332+
</div>
333+
<div className="mt-4 flex justify-end">
334+
<Button size="sm" onClick={handleSettingsSave}>Save Settings</Button>
335+
</div>
336+
</div>
337+
)}
338+
267339
<div className="grid md:grid-cols-[1fr_auto] gap-8 items-center">
268340
{/* Timer Display */}
269341
<div className="text-center space-y-6">

0 commit comments

Comments
 (0)