@@ -3,6 +3,7 @@ let lastUpdateAfter = null;
3
3
let autoRefreshTimer = null ;
4
4
let currentRequest = null ;
5
5
let SCOPE_COLORS = { } ;
6
+ let CLOSURES = new Set ( ) ;
6
7
7
8
function buildScopeColorMap ( days ) {
8
9
const ids = [ ...new Set ( days . flatMap ( d => d . scopes . map ( s => s . id ) ) ) ] ;
@@ -31,6 +32,21 @@ function isSameRequest(a, b) {
31
32
return [ ...a . scopeIds ] . sort ( ) . join ( ',' ) === [ ...b . scopeIds ] . sort ( ) . join ( ',' ) ;
32
33
}
33
34
35
+ async function fetchClosures ( { scopeIds, dateFrom, dateUntil, fullReload = true } ) {
36
+ const res = await fetch ( `closureData/?${ new URLSearchParams ( {
37
+ scopeIds : scopeIds . join ( ',' ) ,
38
+ dateFrom,
39
+ dateUntil
40
+ } ) } `) ;
41
+ if ( ! res . ok ) throw new Error ( 'Fehler beim Laden der Closures' ) ;
42
+ const { data } = await res . json ( ) ;
43
+ const set = new Set ( ) ;
44
+ for ( const it of ( data ?. items || [ ] ) ) {
45
+ set . add ( `${ it . date } |${ it . scopeId } ` ) ;
46
+ }
47
+ CLOSURES = set ;
48
+ }
49
+
34
50
document . addEventListener ( 'DOMContentLoaded' , ( ) => {
35
51
const form = document . getElementById ( 'overall-calendar-form' ) ;
36
52
const btnRefresh = document . getElementById ( 'refresh-calendar' ) ;
@@ -56,7 +72,11 @@ document.addEventListener('DOMContentLoaded', () => {
56
72
if ( ! currentRequest ) return ;
57
73
const { scopeIds, dateFrom, dateUntil} = currentRequest ;
58
74
try {
59
- await loadCalendar ( { scopeIds, dateFrom, dateUntil, fullReload : false } ) ;
75
+ await Promise . all ( [
76
+ fetchCalendar ( { scopeIds, dateFrom, dateUntil, fullReload : false } ) ,
77
+ fetchClosures ( { scopeIds, dateFrom, dateUntil } )
78
+ ] ) ;
79
+ renderMultiDayCalendar ( calendarCache ) ;
60
80
} catch ( e ) {
61
81
alert ( 'Fehler beim Aktualisieren: ' + e . message ) ;
62
82
}
@@ -101,7 +121,7 @@ document.addEventListener('DOMContentLoaded', () => {
101
121
}
102
122
} ) ;
103
123
104
- async function loadCalendar ( { scopeIds, dateFrom, dateUntil, fullReload = false } ) {
124
+ async function fetchCalendar ( { scopeIds, dateFrom, dateUntil, fullReload = false } ) {
105
125
const incremental = ! fullReload && isSameRequest ( { scopeIds, dateFrom, dateUntil} , currentRequest ) ;
106
126
const paramsObj = { scopeIds : scopeIds . join ( ',' ) , dateFrom, dateUntil} ;
107
127
if ( incremental && lastUpdateAfter ) {
@@ -120,7 +140,6 @@ async function loadCalendar({scopeIds, dateFrom, dateUntil, fullReload = false})
120
140
calendarCache = json . data . days ;
121
141
}
122
142
123
- renderMultiDayCalendar ( calendarCache ) ;
124
143
currentRequest = { scopeIds, dateFrom, dateUntil} ;
125
144
lastUpdateAfter = serverTs ;
126
145
}
@@ -156,7 +175,11 @@ async function handleSubmit(event) {
156
175
}
157
176
158
177
try {
159
- await loadCalendar ( { scopeIds, dateFrom, dateUntil, fullReload : true } ) ;
178
+ await Promise . all ( [
179
+ fetchCalendar ( { scopeIds, dateFrom, dateUntil, fullReload : true } ) ,
180
+ fetchClosures ( { scopeIds, dateFrom, dateUntil } )
181
+ ] ) ;
182
+ renderMultiDayCalendar ( calendarCache ) ;
160
183
startAutoRefresh ( ) ;
161
184
} catch ( e ) {
162
185
alert ( e . message ) ;
@@ -176,19 +199,21 @@ function startAutoRefresh() {
176
199
177
200
async function fetchIncrementalUpdate ( ) {
178
201
if ( ! currentRequest || ! lastUpdateAfter ) return ;
179
- const { scopeIds, dateFrom, dateUntil} = currentRequest ;
180
- const params = new URLSearchParams ( {
202
+ const { scopeIds, dateFrom, dateUntil } = currentRequest ;
203
+
204
+ const res = await fetch ( `overallcalendarData/?${ new URLSearchParams ( {
181
205
scopeIds : scopeIds . join ( ',' ) ,
182
206
dateFrom,
183
207
dateUntil,
184
208
updateAfter : lastUpdateAfter
185
- } ) ;
186
- const res = await fetch ( `overallcalendarData/?${ params } ` ) ;
187
- if ( ! res . ok ) return ;
209
+ } ) } `) ;
210
+ if ( res . ok ) {
211
+ lastUpdateAfter = toMysql ( res . headers . get ( 'Last-Modified' ) || new Date ( ) ) ;
212
+ const json = await res . json ( ) ;
213
+ mergeDelta ( json . data . days ) ;
214
+ }
188
215
189
- lastUpdateAfter = toMysql ( res . headers . get ( 'Last-Modified' ) || new Date ( ) ) ;
190
- const json = await res . json ( ) ;
191
- mergeDelta ( json . data . days ) ;
216
+ await fetchClosures ( { scopeIds, dateFrom, dateUntil } ) ;
192
217
renderMultiDayCalendar ( calendarCache ) ;
193
218
}
194
219
@@ -238,6 +263,8 @@ function renderMultiDayCalendar(days) {
238
263
return ;
239
264
}
240
265
266
+ const ymdLocalFromUnix = ( ts ) => new Date ( ts * 1000 ) . toLocaleDateString ( 'sv-SE' ) ;
267
+
241
268
SCOPE_COLORS = buildScopeColorMap ( days ) ;
242
269
const allTimes = [ ...new Set (
243
270
days . flatMap ( day =>
@@ -303,13 +330,17 @@ function renderMultiDayCalendar(days) {
303
330
304
331
colCursor = 2 ;
305
332
days . forEach ( ( day , dayIdx ) => {
333
+ const dateIsoForDay = new Date ( day . date * 1000 ) . toLocaleDateString ( 'sv-SE' ) ;
306
334
day . scopes . forEach ( ( scope , scopeIdx ) => {
307
335
const head = addCell ( {
308
336
text : scope . shortName || scope . name || `Scope ${ scope . id } ` ,
309
337
className : 'overall-calendar-head overall-calendar-scope-header overall-calendar-stick-top' ,
310
338
row : 2 , col : colCursor , colSpan : scope . maxSeats
311
339
} ) ;
312
340
head . style . background = SCOPE_COLORS [ scope . id ] ;
341
+ if ( isScopeClosed ( dateIsoForDay , scope . id ) ) {
342
+ head . classList . add ( 'is-closed' ) ;
343
+ }
313
344
colCursor += scope . maxSeats ;
314
345
if ( scopeIdx < day . scopes . length - 1 ) {
315
346
addCell ( {
@@ -342,10 +373,10 @@ function renderMultiDayCalendar(days) {
342
373
343
374
let col = 2 ;
344
375
days . forEach ( ( day , dayIdx ) => {
345
- const dateKey = new Date ( day . date * 1000 ) . toISOString ( ) . slice ( 0 , 10 ) ;
376
+ const dateKey = ymdLocalFromUnix ( day . date ) ;
346
377
day . scopes . forEach ( ( scope , scopeIdx ) => {
347
378
const timeObj = scope . times . find ( t => t . name === time ) || { seats : [ ] } ;
348
-
379
+ const closed = isScopeClosed ( dateKey , scope . id ) ;
349
380
for ( let seatIdx = 0 ; seatIdx < scope . maxSeats ; seatIdx ++ ) {
350
381
if ( occupied . has ( `${ gridRow } -${ col } ` ) ) { col ++ ; continue ; }
351
382
@@ -368,7 +399,7 @@ function renderMultiDayCalendar(days) {
368
399
for ( let i = 0 ; i < span ; i ++ ) occupied . add ( `${ gridRow + i } -${ col } ` ) ;
369
400
} else if ( status !== 'skip' ) {
370
401
addCell ( {
371
- className : `overall-calendar-seat overall-calendar-${ status } ` ,
402
+ className : `overall-calendar-seat overall-calendar-${ status } ${ closed ? ' overall-calendar-closed' : '' } ` ,
372
403
row : gridRow ,
373
404
col,
374
405
id : cellId ,
@@ -391,3 +422,7 @@ function togglePageScroll(disable) {
391
422
document . documentElement . classList . toggle ( 'no-page-scroll' , disable ) ;
392
423
document . body . classList . toggle ( 'no-page-scroll' , disable ) ;
393
424
}
425
+
426
+ function isScopeClosed ( dateIso , scopeId ) {
427
+ return CLOSURES . has ( `${ dateIso } |${ scopeId } ` ) ;
428
+ }
0 commit comments