2026-10-20 07:18:57 +02:00
/* Case-local time grid; the shared planner remains responsible for loading. */
( ( ) => {
'use strict' ;
window . renderCaseWeekCalendar = function ( host , anchor , items ) {
const esc = value => String ( value ? ? '' ) . replace ( /[&<>"']/g , c => ( { '&' : '&' , '<' : '<' , '>' : '>' , '"' : '"' , "'" : ''' } [ c ] ) ) ;
const key = date => ` ${ date . getFullYear ( ) } - ${ String ( date . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) } - ${ String ( date . getDate ( ) ) . padStart ( 2 , '0' ) } ` ;
const time = date => date . toLocaleTimeString ( 'da-DK' , { hour : '2-digit' , minute : '2-digit' } ) ;
const monday = new Date ( anchor ) ;
monday . setHours ( 0 , 0 , 0 , 0 ) ;
const days = [ ] ;
let firstHour = 7 , lastHour = 17 ;
for ( let index = 0 ; index < 7 ; index ++ ) {
const date = new Date ( monday ) ; date . setDate ( date . getDate ( ) + index ) ;
const next = new Date ( date ) ; next . setDate ( next . getDate ( ) + 1 ) ;
const segments = items . filter ( item => new Date ( item . starts _at ) < next && new Date ( item . ends _at ) > date ) . map ( item => {
const start = new Date ( Math . max ( new Date ( item . starts _at ) . getTime ( ) , date . getTime ( ) ) ) ;
const end = new Date ( Math . min ( new Date ( item . ends _at ) . getTime ( ) , next . getTime ( ) ) ) ;
const from = start . getHours ( ) * 60 + start . getMinutes ( ) ;
const to = end . getTime ( ) === next . getTime ( ) ? 1440 : end . getHours ( ) * 60 + end . getMinutes ( ) ;
firstHour = Math . min ( firstHour , Math . floor ( from / 60 ) ) ;
lastHour = Math . max ( lastHour , Math . ceil ( to / 60 ) ) ;
return { item , start , end , from , to } ;
} ) . sort ( ( a , b ) => a . from - b . from ) ;
if ( index < 5 || segments . length ) days . push ( { date , segments } ) ;
}
const height = ( lastHour - firstHour ) * 96 ;
host . classList . add ( 'v5-time-grid' ) ;
host . style . setProperty ( '--week-days' , days . length ) ;
host . dataset . firstHour = String ( firstHour ) ;
host . dataset . lastHour = String ( lastHour ) ;
host . innerHTML = '<div class="v5-time-corner">Kl.</div>' + days . map ( ( { date } ) => ` <div class="v5-time-heading ${ key ( date ) === key ( new Date ( ) ) ? 'is-today' : '' } "> ${ esc ( date . toLocaleDateString ( 'da-DK' , { weekday : 'short' , day : 'numeric' , month : 'short' } ))}</div> ` ) . join ( '' ) +
` <div class="v5-time-labels" style="height: ${ height } px"> ${ Array . from ( { length : lastHour - firstHour + 1 } ,(_,i)=> ` < span style = "top:${i*96}px" > $ { String ( firstHour + i ) . padStart ( 2 , '0' ) } : 00 < / s p a n > ` ) . j o i n ( ' ' ) } < / d i v > ` +
days . map ( ( { date , segments } ) => {
const lanes = [ ] ;
for ( const segment of segments ) {
let lane = lanes . findIndex ( end => end <= segment . from ) ;
if ( lane < 0 ) lane = lanes . length ;
lanes [ lane ] = segment . to ; segment . lane = lane ;
}
return ` <div class="v5-time-day" data-date=" ${ key ( date ) } " style="height: ${ height } px"> ${ segments . map ( ( { item , start , end , from , to , lane } ) => {
const canResize = ! item . locked && new Date ( item . ends _at ) . getTime ( ) === end . getTime ( ) ;
2026-11-03 01:16:12 +01:00
return ` <div class="case-planner-calendar-item v5-time-event" ${ ! item . locked ? ` draggable="true" data-move-id=" ${ Number ( item . id ) } " data-version=" ${ Number ( item . version ) } " data-start=" ${ esc ( item . starts _at ) } " data-end=" ${ esc ( item . ends _at ) } " data-segment-start=" ${ esc ( start . toISOString ( ) ) } " title="Træk aftalen til en anden dag eller tid" ` : '' } style="top: ${ ( from - firstHour * 60 ) * 1.6 } px;height: ${ ( to - from ) * 1.6 } px;left:calc( ${ lane * 100 / lanes . length } % + 2px);width:calc( ${ 100 / lanes . length } % - 4px)"><strong> ${ time ( start ) } – ${ time ( end ) } </strong><div> ${ esc ( item . user _name || 'Ukendt' ) } </div> ${ item . note ? ` <div> ${ esc ( item . note ) } </div> ` : '' } ${ item . conflict _status && item . conflict _status !== 'none' ? '<span class="badge text-bg-warning">Overlap</span>' : '' } ${ canResize ? ` <button type="button" class="case-slot-resize" data-allocation-id=" ${ Number ( item . id ) } " data-version=" ${ Number ( item . version ) } " data-start=" ${ esc ( item . starts _at ) } " data-end=" ${ esc ( item . ends _at ) } " data-pixels-per-step="24" aria-label="Ændr varighed: træk eller brug piletaster" title="Træk for at ændre varighed">━</button> ` : '' } </div> ` ;
2026-10-20 07:18:57 +02:00
} ) . join ( '' ) } < / d i v > ` ;
} ) . join ( '' ) ;
const friday = days [ days . length - 1 ] . date ;
document . getElementById ( 'casePlannerWeekLabel' ) . textContent = ` ${ monday . toLocaleDateString ( 'da-DK' , { day : 'numeric' , month : 'short' } )} – ${ friday . toLocaleDateString ( 'da-DK' , { day : 'numeric' , month : 'short' , year : 'numeric' } )} ` ;
if ( ! host . dataset . timeGridBound ) {
host . dataset . timeGridBound = 'true' ;
host . addEventListener ( 'click' , event => {
if ( event . target . closest ( '.v5-time-event' ) ) return ;
const day = event . target . closest ( '.v5-time-day' ) ;
if ( ! day ) return ;
const minutes = Math . max ( 0 , Math . min ( 1425 , Number ( host . dataset . firstHour ) * 60 + Math . floor ( ( event . clientY - day . getBoundingClientRect ( ) . top ) / 24 ) * 15 ) ) ;
window . openEmbeddedPlannerSlot ( day . dataset . date ) ;
document . getElementById ( 'casePlannerSlotTime' ) . value = ` ${ String ( Math . floor ( minutes / 60 ) ) . padStart ( 2 , '0' ) } : ${ String ( minutes % 60 ) . padStart ( 2 , '0' ) } ` ;
} ) ;
}
} ;
} ) ( ) ;