88 body {
99 font-family : Arial, sans-serif;
1010 display : flex;
11- justify-content : center ;
11+ flex-direction : column ;
1212 align-items : center;
13- height : 100vh ;
14- margin : 0 ;
13+ padding : 2rem ;
1514 background-color : # f0f0f0 ;
1615 }
1716 .container {
2019 padding : 2rem ;
2120 border-radius : 10px ;
2221 box-shadow : 0 4px 6px rgba (0 , 0 , 0 , 0.1 );
22+ margin-bottom : 2rem ;
2323 }
2424 # timer {
2525 font-size : 4rem ;
2626 margin-bottom : 1rem ;
2727 }
28- button , select {
28+ button , select , input {
2929 font-size : 1rem ;
3030 padding : 0.5rem 1rem ;
3131 margin : 0.5rem ;
4141 # startBtn : hover {
4242 background-color : # 45a049 ;
4343 }
44- # resetBtn {
44+ # resetBtn , # endBtn {
4545 background-color : # f44336 ;
4646 color : white;
4747 }
48- # resetBtn : hover {
48+ # resetBtn : hover , # endBtn : hover {
4949 background-color : # da190b ;
5050 }
5151 select {
5555 select : hover {
5656 background-color : # 2980b9 ;
5757 }
58+ # goalInput {
59+ width : 100% ;
60+ padding : 0.5rem ;
61+ margin-bottom : 1rem ;
62+ border : 1px solid # ccc ;
63+ border-radius : 5px ;
64+ }
65+ # sessionLog {
66+ width : 100% ;
67+ max-width : 800px ;
68+ margin-top : 2rem ;
69+ overflow-x : auto;
70+ }
71+ # sessionLog table {
72+ width : 100% ;
73+ border-collapse : collapse;
74+ }
75+ # sessionLog th , # sessionLog td {
76+ border : 1px solid # ddd ;
77+ padding : 8px ;
78+ text-align : left;
79+ }
80+ # sessionLog th {
81+ background-color : # f2f2f2 ;
82+ }
83+ # jsonOutput {
84+ width : 100% ;
85+ height : 200px ;
86+ margin-top : 2rem ;
87+ font-family : monospace;
88+ }
5889 </ style >
5990</ head >
6091< body >
6192 < div class ="container ">
6293 < h1 > Pomodoro Timer</ h1 >
94+ < input type ="text " id ="goalInput " placeholder ="Enter your goal for this session " required >
6395 < div id ="timer "> 25:00</ div >
6496 < select id ="durationSelect ">
6597 < option value ="5 "> 5 minutes</ option >
@@ -74,18 +106,81 @@ <h1>Pomodoro Timer</h1>
74106 < br >
75107 < button id ="startBtn "> Start</ button >
76108 < button id ="resetBtn "> Reset</ button >
109+ < button id ="endBtn "> End Session</ button >
110+ </ div >
111+
112+ < div id ="sessionLog ">
113+ < h2 > Session Log</ h2 >
114+ < table >
115+ < thead >
116+ < tr >
117+ < th > Goal</ th >
118+ < th > Start Time</ th >
119+ < th > End Time</ th >
120+ < th > Duration</ th >
121+ < th > Pauses</ th >
122+ </ tr >
123+ </ thead >
124+ < tbody id ="sessionLogBody "> </ tbody >
125+ </ table >
77126 </ div >
78127
128+ < textarea id ="jsonOutput " readonly > </ textarea >
129+
79130 < script >
80131 const timerDisplay = document . getElementById ( 'timer' ) ;
81132 const startBtn = document . getElementById ( 'startBtn' ) ;
82133 const resetBtn = document . getElementById ( 'resetBtn' ) ;
134+ const endBtn = document . getElementById ( 'endBtn' ) ;
83135 const durationSelect = document . getElementById ( 'durationSelect' ) ;
136+ const goalInput = document . getElementById ( 'goalInput' ) ;
137+ const sessionLogBody = document . getElementById ( 'sessionLogBody' ) ;
138+ const jsonOutput = document . getElementById ( 'jsonOutput' ) ;
84139
85140 let startTime ;
86141 let timeLeft = 25 * 60 ; // Default to 25 minutes in seconds
87142 let isRunning = false ;
88143 let duration = 25 * 60 ; // Default duration in seconds
144+ let currentSession = null ;
145+ let sessions = [ ] ;
146+
147+ // Check if localStorage is available
148+ function isLocalStorageAvailable ( ) {
149+ try {
150+ localStorage . setItem ( 'test' , 'test' ) ;
151+ localStorage . removeItem ( 'test' ) ;
152+ return true ;
153+ } catch ( e ) {
154+ return false ;
155+ }
156+ }
157+
158+ // Load sessions from storage
159+ function loadSessions ( ) {
160+ if ( isLocalStorageAvailable ( ) ) {
161+ const storedSessions = localStorage . getItem ( 'pomodoroSessions' ) ;
162+ if ( storedSessions ) {
163+ sessions = JSON . parse ( storedSessions ) ;
164+ }
165+ } else {
166+ console . warn ( 'localStorage is not available. Session data will not persist.' ) ;
167+ }
168+ updateSessionLog ( ) ;
169+ }
170+
171+ loadSessions ( ) ;
172+
173+ function formatDate ( date ) {
174+ return new Date ( date ) . toLocaleString ( 'en-US' , {
175+ year : 'numeric' ,
176+ month : '2-digit' ,
177+ day : '2-digit' ,
178+ hour : '2-digit' ,
179+ minute : '2-digit' ,
180+ second : '2-digit' ,
181+ hour12 : true
182+ } ) . replace ( ',' , '' ) ;
183+ }
89184
90185 function updateDisplay ( ) {
91186 const minutes = Math . floor ( timeLeft / 60 ) ;
@@ -95,14 +190,34 @@ <h1>Pomodoro Timer</h1>
95190
96191 function startTimer ( ) {
97192 if ( ! isRunning ) {
193+ if ( ! goalInput . value ) {
194+ alert ( 'Please enter a goal for this session.' ) ;
195+ return ;
196+ }
98197 isRunning = true ;
99198 startBtn . textContent = 'Pause' ;
100199 durationSelect . disabled = true ;
200+ goalInput . disabled = true ;
201+ if ( ! currentSession ) {
202+ currentSession = {
203+ goal : goalInput . value ,
204+ startTime : new Date ( ) . toISOString ( ) ,
205+ endTime : null ,
206+ duration : 0 ,
207+ pauses : [ ]
208+ } ;
209+ } else {
210+ currentSession . pauses [ currentSession . pauses . length - 1 ] . resumeTime = new Date ( ) . toISOString ( ) ;
211+ }
101212 startTime = Date . now ( ) - ( ( duration - timeLeft ) * 1000 ) ;
102213 requestAnimationFrame ( updateTimer ) ;
103214 } else {
104215 isRunning = false ;
105216 startBtn . textContent = 'Resume' ;
217+ currentSession . pauses . push ( {
218+ pauseTime : new Date ( ) . toISOString ( ) ,
219+ resumeTime : null
220+ } ) ;
106221 }
107222 }
108223
@@ -116,6 +231,8 @@ <h1>Pomodoro Timer</h1>
116231 isRunning = false ;
117232 startBtn . textContent = 'Start' ;
118233 durationSelect . disabled = false ;
234+ goalInput . disabled = false ;
235+ endSession ( ) ;
119236 alert ( 'Pomodoro session complete!' ) ;
120237 } else {
121238 requestAnimationFrame ( updateTimer ) ;
@@ -132,6 +249,8 @@ <h1>Pomodoro Timer</h1>
132249 updateDisplay ( ) ;
133250 startBtn . textContent = 'Start' ;
134251 durationSelect . disabled = false ;
252+ goalInput . disabled = false ;
253+ currentSession = null ;
135254 }
136255
137256 function changeDuration ( ) {
@@ -142,10 +261,64 @@ <h1>Pomodoro Timer</h1>
142261 }
143262 }
144263
264+ function endSession ( ) {
265+ if ( currentSession ) {
266+ currentSession . endTime = new Date ( ) . toISOString ( ) ;
267+ currentSession . duration = Math . round ( ( new Date ( currentSession . endTime ) - new Date ( currentSession . startTime ) ) / 1000 ) ;
268+ sessions . unshift ( currentSession ) ;
269+ updateSessionLog ( ) ;
270+ saveSessions ( ) ;
271+ resetTimer ( ) ;
272+ }
273+ }
274+
275+ function updateSessionLog ( ) {
276+ sessionLogBody . innerHTML = '' ;
277+ sessions . forEach ( session => {
278+ const row = document . createElement ( 'tr' ) ;
279+ row . innerHTML = `
280+ <td>${ session . goal } </td>
281+ <td>${ formatDate ( session . startTime ) } </td>
282+ <td>${ session . endTime ? formatDate ( session . endTime ) : 'In progress' } </td>
283+ <td>${ formatDuration ( session . duration ) } </td>
284+ <td>${ formatPauses ( session . pauses ) } </td>
285+ ` ;
286+ sessionLogBody . appendChild ( row ) ;
287+ } ) ;
288+ jsonOutput . value = JSON . stringify ( sessions , null , 2 ) ;
289+ }
290+
291+ function formatDuration ( seconds ) {
292+ const minutes = Math . floor ( seconds / 60 ) ;
293+ const remainingSeconds = seconds % 60 ;
294+ return `${ minutes } m ${ remainingSeconds } s` ;
295+ }
296+
297+ function formatPauses ( pauses ) {
298+ return pauses . map ( pause =>
299+ `${ formatDate ( pause . pauseTime ) } - ${ pause . resumeTime ? formatDate ( pause . resumeTime ) : 'Not resumed' } `
300+ ) . join ( '<br>' ) ;
301+ }
302+
303+ function saveSessions ( ) {
304+ if ( isLocalStorageAvailable ( ) ) {
305+ localStorage . setItem ( 'pomodoroSessions' , JSON . stringify ( sessions ) ) ;
306+ }
307+ }
308+
145309 startBtn . addEventListener ( 'click' , startTimer ) ;
146310 resetBtn . addEventListener ( 'click' , resetTimer ) ;
311+ endBtn . addEventListener ( 'click' , endSession ) ;
147312 durationSelect . addEventListener ( 'change' , changeDuration ) ;
148313
314+ // Keyboard shortcut
315+ document . addEventListener ( 'keydown' , ( e ) => {
316+ if ( e . code === 'Space' && document . activeElement !== goalInput ) {
317+ e . preventDefault ( ) ;
318+ startTimer ( ) ;
319+ }
320+ } ) ;
321+
149322 updateDisplay ( ) ;
150323 </ script >
151324</ body >
0 commit comments