88 < link rel ="icon " href ="./icons/icon-192.png " />
99 < style >
1010 : root {--bg : # f7fafc ;--card : # fff ;--ink : # 0f172a ;--muted : # 64748b ;--ring : # 6366f1 ;--border : # e2e8f0 ;--chip : # eef2ff }
11- * {box-sizing : border-box} body {margin : 0 ;font-family : system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;background : linear-gradient (# fafafa, # fff )}
12- header {display : flex;gap : 12px ;align-items : center;justify-content : space-between;padding : 16px 20px }
13- h1 {font-size : 20px ;margin : 0 ;color : var (--ink )}
14- .btn {border : 1px solid var (--border );background : # fff ;border-radius : 12px ;padding : 8px 12px ;cursor : pointer}
11+ * {box-sizing : border-box}
12+ body {margin : 0 ;font-family : system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;background : linear-gradient (# fafafa, # fff )}
13+ header {display : flex;gap : 12px ;align-items : center;justify-content : flex-end;padding : 8px 12px }
14+ .btn {border : 1px solid var (--border );background : # fff ;border-radius : 10px ;padding : 6px 10px ;cursor : pointer;font-size : 12px }
15+ .btn .icon {display : inline-grid;place-items : center;padding : 6px ;width : 30px ;height : 30px }
1516 .btn : focus {outline : 2px solid var (--ring )}
16- . btn . icon { display : inline-grid; place-items : center; padding : 8 px ; width : 36 px ; height : 36 px }
17- .wrap {max-width : 1200px ;margin : 0 auto;padding : 0 16 px 24 px }
18- .tabs {display : flex;gap : 8 px ;border-bottom : 1px solid var (--border );margin : 0 16 px }
19- .tab {padding : 10 px 12 px ;border : 1px solid var (--border );border-bottom : none;border-radius : 12 px 12 px 0 0 ;background : # fff ;cursor : pointer}
17+
18+ .wrap {max-width : 1200px ;margin : 0 auto;padding : 0 12 px 16 px }
19+ .tabs {display : flex;gap : 6 px ;border-bottom : 1px solid var (--border );margin : 0 8 px }
20+ .tab {padding : 8 px 10 px ;border : 1px solid var (--border );border-bottom : none;border-radius : 10 px 10 px 0 0 ;background : # fff ;cursor : pointer; font-size : 13 px }
2021 .tab [aria-selected = "true" ]{background : var (--chip )}
21- .panel {$1 overflow: auto }
22- . toolbar { display : flex; gap : 8 px ; align-items : center; justify-content : space-between; margin-bottom : 12 px }
23- .range {display : grid; grid-template-columns : 1 fr 1 fr ; gap : 12 px }
22+ .panel {border : 1 px solid var ( --border ); border-radius : 14 px ; padding : 12 px ; background : # fff ; margin : 0 8 px ; overflow : auto}
23+
24+ .toolbar {display : flex; gap : 8 px ; align-items : center; justify-content : space-between; margin-bottom : 8 px }
2425 .grid {display : grid;grid-template-columns : repeat (7 , 220px );gap : 10px }
25- .calendar-shell {border : 1px solid var (--border );border-radius : 16px ;padding : 12px ;width : fit-content;}
26- .day {min-height : 160px ;border : 1px solid var (--border );border-radius : 16px ;padding : 10px ;background : # fff ;display : flex;flex-direction : column}
26+ .calendar-shell {display : inline-block;border : 1px solid var (--border );border-radius : 14px ;padding : 10px ;background : # fff }
27+
28+ .day {min-height : 170px ;border : 1px solid var (--border );border-radius : 12px ;padding : 8px ;background : # fff ;display : flex;flex-direction : column}
2729 .day .today {outline : 2px solid var (--ring )}
2830 .daytop {display : flex;align-items : baseline;justify-content : space-between}
2931 .muted {color : var (--muted )}
30- .list {margin-top : 8 px ;display : flex;flex-direction : column;gap : 6px ;overflow : auto}
31- .entry {display : flex;gap : 8px ;align-items : flex-start;border : 1px solid var (--border );border-radius : 12 px ;padding : 8 px }
32+ .list {margin-top : 6 px ;display : flex;flex-direction : column;gap : 6px ;overflow : auto}
33+ .entry {display : flex;gap : 8px ;align-items : flex-start;border : 1px solid var (--border );border-radius : 10 px ;padding : 6 px }
3234 .dot {width : 10px ;height : 10px ;border-radius : 999px ;flex : 0 0 10px ;border : 1px solid # 0002 }
3335 .grow {flex : 1 }
3436 .small {font-size : 12px }
37+
3538 .projects {display : grid;grid-template-columns : repeat (auto-fit, minmax (240px , 1fr ));gap : 10px }
3639 label {font-size : 12px ;color : var (--muted );display : block;margin-bottom : 4px }
3740 input , select {width : 100% ;padding : 8px 10px ;border : 1px solid var (--border );border-radius : 10px }
3841 .row {display : flex;gap : 10px ;align-items : end}
3942 .row > * {flex : 1 }
4043 .tag {display : inline-flex;gap : 8px ;align-items : center}
41- .chip {background : var (--chip );border-radius : 999px ;padding : 2px 8px }
4244 .right {margin-left : auto}
4345 .footer {display : flex;gap : 8px ;justify-content : flex-end}
4446 .hidden {display : none}
4547 </ style >
4648</ head >
4749< body >
4850 < header >
49- < div style ="display:flex; gap:8px; margin-left:auto ">
50- < button class ="btn small " id ="btnExport " title ="Export JSON "> ⬇︎</ button >
51- < label class ="btn small " for ="importFile " title ="Import JSON "> ⬆︎</ label >
52- < input id ="importFile " type ="file " accept ="application/json " class ="hidden " />
53- </ div >
51+ < button class ="btn icon " id ="btnExport " title ="Export JSON "> ⬇︎</ button >
52+ < label class ="btn icon " for ="importFile " title ="Import JSON "> ⬆︎</ label >
53+ < input id ="importFile " type ="file " accept ="application/json " class ="hidden " />
5454 </ header >
5555
5656 < div class ="wrap ">
6464 < section id ="panelCal " class ="panel " role ="tabpanel " aria-labelledby ="tabCal ">
6565 < div class ="toolbar ">
6666 < div style ="display:flex;gap:8px;align-items:center ">
67- < button class ="btn icon " id ="prev "> ◀</ button >
68- < button class ="btn icon " id ="next "> ▶</ button >
67+ < button class ="btn icon " id ="prev " title =" Previous week " > ◀</ button >
68+ < button class ="btn icon " id ="next " title =" Next week " > ▶</ button >
6969 < button class ="btn " id ="btnToday "> This ISO Week</ button >
70- < span class ="muted " id ="rangeText "> </ span >
70+ < span class ="muted small " id ="rangeText "> </ span >
7171 </ div >
72- < button class ="btn " id ="addEntryOpen "> Add Entry</ button >
7372 </ div >
7473 < div class ="calendar-shell ">
7574 < div class ="grid " id ="calGrid "> </ div >
114113 </ section >
115114 </ div >
116115
117- <!-- Add/Edit Entry Modal (very small vanilla modal) -->
116+ <!-- Add/Edit Entry Modal -->
118117 < dialog id ="entryDialog " style ="border:none;border-radius:16px;padding:16px;max-width:420px;width:90vw ">
119118 < h3 style ="margin:0 0 8px "> Add Time Entry</ h3 >
120119 < div class ="row ">
@@ -145,7 +144,7 @@ <h3 style="margin:0 0 8px">Add Time Entry</h3>
145144
146145 < script >
147146 // ===== Data Types & Storage =====
148- const STORAGE_KEY = 'smart-calendar-vanilla-v1 ' ;
147+ const STORAGE_KEY = 'smart-calendar-vanilla-v2 ' ;
149148 /** @type {{projects: {id:string,name:string,color:string}[], entries: {id:string,date:string,projectId:string,hours:number,note?:string}[]} } */
150149 let db = loadDB ( ) ;
151150
@@ -190,14 +189,14 @@ <h3 style="margin:0 0 8px">Add Time Entry</h3>
190189 function parseYMD ( s ) { const [ y , m , da ] = s . split ( '-' ) . map ( Number ) ; return new Date ( Date . UTC ( y , m - 1 , da ) ) ; }
191190 function humanDate ( d ) { return d . toLocaleDateString ( undefined , { weekday :'short' , month :'short' , day :'numeric' } ) ; }
192191
193- // ===== State & Elements =====
192+ // ===== Elements =====
194193 let anchor = startOfISOWeek ( new Date ( ) ) ;
195194 const calGrid = document . getElementById ( 'calGrid' ) ;
196195 const rangeText = document . getElementById ( 'rangeText' ) ;
197196 const btnPrev = document . getElementById ( 'prev' ) ;
198197 const btnNext = document . getElementById ( 'next' ) ;
199198 const btnToday = document . getElementById ( 'btnToday' ) ;
200- const addEntryOpen = document . getElementById ( 'addEntryOpen' ) ;
199+ const addEntryOpen = document . getElementById ( 'addEntryOpen' ) ; // optional (not shown now)
201200 const entryDialog = document . getElementById ( 'entryDialog' ) ;
202201 const dlgDate = document . getElementById ( 'dlgDate' ) ;
203202 const dlgHours = document . getElementById ( 'dlgHours' ) ;
@@ -222,11 +221,12 @@ <h3 style="margin:0 0 8px">Add Time Entry</h3>
222221 t . setAttribute ( 'aria-selected' , sel ?'true' :'false' ) ;
223222 }
224223 }
224+
225225 tabCal . onclick = ( ) => showPanel ( 'cal' ) ;
226226 tabTotals . onclick = ( ) => { showPanel ( 'totals' ) ; renderTotals ( ) ; } ;
227227 tabProj . onclick = ( ) => { showPanel ( 'proj' ) ; renderProjects ( ) ; } ;
228228
229- // ===== Calendar Rendering =====
229+ // ===== Calendar =====
230230 function entriesByDate ( ) {
231231 const m = { } ;
232232 for ( const e of db . entries ) { ( m [ e . date ] ||= [ ] ) . push ( e ) ; }
@@ -241,8 +241,9 @@ <h3 style="margin:0 0 8px">Add Time Entry</h3>
241241 }
242242
243243 function renderCalendar ( ) {
244+ if ( ! calGrid ) return ;
244245 const days = twoWeek ( ) ;
245- rangeText . textContent = humanDate ( days [ 0 ] ) + ' – ' + humanDate ( days [ 13 ] ) ;
246+ if ( rangeText ) rangeText . textContent = humanDate ( days [ 0 ] ) + ' – ' + humanDate ( days [ 13 ] ) ;
246247 const map = entriesByDate ( ) ;
247248 const pmap = projectById ( ) ;
248249 const todayYMD = fmtYMD ( new Date ( ) ) ;
@@ -258,7 +259,7 @@ <h3 style="margin:0 0 8px">Add Time Entry</h3>
258259 <div class="daytop"><div><strong>${ humanDate ( d ) } </strong></div><div class="muted small">${ total > 0 ? total + 'h' :'' } </div></div>
259260 <div class="list"></div>
260261 <div style="margin-top:auto">
261- <button class="btn" data-add="${ ymd } ">Add Entry </button>
262+ <button class="btn small " data-add="${ ymd } ">Add</button>
262263 </div>
263264 ` ;
264265 const listEl = dayEl . querySelector ( '.list' ) ;
@@ -311,13 +312,11 @@ <h3 style="margin:0 0 8px">Add Time Entry</h3>
311312 saveDB ( ) ; entryDialog . close ( ) ; renderCalendar ( ) ;
312313 } ;
313314
314- addEntryOpen . onclick = ( ) => openDialog ( fmtYMD ( new Date ( ) ) ) ;
315- btnPrev . onclick = ( ) => { anchor = addDays ( startOfISOWeek ( anchor ) , - 7 ) ; renderCalendar ( ) ; } ; renderCalendar ( ) ; } ;
316- btnNext . onclick = ( ) => { anchor = addDays ( startOfISOWeek ( anchor ) , 7 ) ; renderCalendar ( ) ; } ; renderCalendar ( ) ; } ;
315+ // Toolbar actions (1-week shift)
316+ btnPrev . onclick = ( ) => { anchor = addDays ( startOfISOWeek ( anchor ) , - 7 ) ; renderCalendar ( ) ; } ;
317+ btnNext . onclick = ( ) => { anchor = addDays ( startOfISOWeek ( anchor ) , 7 ) ; renderCalendar ( ) ; } ;
317318 btnToday . onclick = ( ) => { anchor = startOfISOWeek ( new Date ( ) ) ; renderCalendar ( ) ; } ;
318319
319- function delEntry ( id ) { db . entries = db . entries . filter ( e => e . id !== id ) ; saveDB ( ) ; }
320-
321320 // ===== Totals =====
322321 const totStart = document . getElementById ( 'totStart' ) ;
323322 const totEnd = document . getElementById ( 'totEnd' ) ;
0 commit comments