/** * Calendar Initialization and Event Handlers * FullCalendar.js configuration for TV planning */ document.addEventListener('DOMContentLoaded', function() { const calendarEl = document.getElementById('calendar'); if (!calendarEl) { console.error('Calendar element not found'); return; } // Initialize FullCalendar with resource columns const calendar = new FullCalendar.Calendar(calendarEl, { schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source', initialView: 'resourceTimeGridDay', headerToolbar: { left: 'prevDay,prev,next,nextDay today', center: 'title', right: 'resourceTimeGridDay,resourceTimeGridWeek,resourceTimelineDay' }, customButtons: { prevDay: { text: '◀', hint: 'Vorige dag', click: function() { calendar.getDate().setDate(calendar.getDate().getDate() - 1); calendar.gotoDate(calendar.getDate()); } }, nextDay: { text: '▶', hint: 'Volgende dag', click: function() { calendar.getDate().setDate(calendar.getDate().getDate() + 1); calendar.gotoDate(calendar.getDate()); } } }, slotDuration: '00:15:00', slotLabelInterval: '00:15:00', snapDuration: '00:01:00', slotMinTime: '00:00:00', slotMaxTime: '24:00:00', height: 'auto', locale: 'nl', allDaySlot: false, nowIndicator: true, resourceAreaHeaderContent: 'Zenders', resourceAreaWidth: '120px', resourceOrder: 'title', // Resources (channels) - only SBS9 and NET5 resources: [ { id: 'SBS9', title: 'SBS9', eventBackgroundColor: '#3498db', eventBorderColor: '#2980b9' }, { id: 'NET5', title: 'NET5', eventBackgroundColor: '#e74c3c', eventBorderColor: '#c0392b' } ], // Load events from API (including background blocks) events: function(info, successCallback, failureCallback) { const startDate = info.startStr.split('T')[0]; const endDate = info.endStr.split('T')[0]; // Load both transmissions and blocks Promise.all([ fetch('api/get_transmissions.php?' + new URLSearchParams({ start: startDate, end: endDate })).then(r => r.json()), fetch('api/get_daily_blocks.php?' + new URLSearchParams({ date: startDate })).then(r => r.json()) ]) .then(([transmissions, blocksData]) => { // Combine transmissions with block background events const allEvents = [ ...transmissions, ...(blocksData.backgroundEvents || []) ]; successCallback(allEvents); // Update block info display updateBlockInfo(blocksData.blocks || []); }) .catch(error => { console.error('Error loading events:', error); failureCallback(error); }); }, // Event rendering - optimized for resource columns eventContent: function(arg) { const seriesCode = arg.event.extendedProps.series_code; const title = arg.event.title; const duration = arg.event.extendedProps.duration; // Skip rendering for background events (blocks) if (arg.event.display === 'background') { return true; // Use default rendering } // For timeline view if (arg.view.type.includes('timeline')) { return { html: `
Zender: ${channelName}
Datum: ${event.start.toLocaleDateString('nl-NL')}
Starttijd: ${event.start.toLocaleTimeString('nl-NL', {hour: '2-digit', minute: '2-digit'})}
Duur: ${props.duration}
Series Code: ${props.series_code || '-'}
Template: ${props.template}
API Status: ${props.api_status}
`; // Set delete button document.getElementById('deleteEventBtn').onclick = function() { if (confirm('Weet je zeker dat je deze uitzending wilt verwijderen?')) { deleteEvent(event.id); modal.hide(); } }; // Set sync button document.getElementById('syncEventBtn').onclick = function() { syncEvent(event.id); }; modal.show(); }, // Date click (show block time editor) dateClick: function(info) { if (info.resource) { showBlockTimeEditor(info.dateStr.split('T')[0], info.resource.id); } } }); calendar.render(); // Make external events draggable initDraggableCommercials(); // Store calendar instance globally window.tvCalendar = calendar; }); /** * Initialize draggable infomercials from sidebar */ function initDraggableCommercials() { const commercialItems = document.querySelectorAll('.infomercial-item'); commercialItems.forEach(item => { new FullCalendar.Draggable(item, { eventData: function(eventEl) { return { title: eventEl.dataset.title, duration: eventEl.dataset.duration, backgroundColor: eventEl.dataset.color, borderColor: eventEl.dataset.color, extendedProps: { infomercial_id: eventEl.dataset.commercialId, series_code: eventEl.dataset.seriesCode } }; } }); }); } /** * Show block time editor */ function showBlockTimeEditor(date, channel) { const modal = new bootstrap.Modal(document.getElementById('blockTimeModal')); document.getElementById('blockDate').value = date; document.getElementById('blockChannel').value = channel; // Load current block time fetch(`api/get_block_time.php?date=${date}&channel=${channel}`) .then(response => response.json()) .then(data => { if (data.start_time) { document.getElementById('blockStartTime').value = data.start_time; } }); modal.show(); } /** * Save block time */ function saveBlockTime() { const date = document.getElementById('blockDate').value; const channel = document.getElementById('blockChannel').value; const startTime = document.getElementById('blockStartTime').value; const recalculate = document.getElementById('recalculateTransmissions').checked; fetch('api/update_block_time.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ date: date, channel: channel, start_time: startTime, recalculate: recalculate }) }) .then(response => response.json()) .then(data => { if (data.success) { showAlert('success', 'Blok starttijd bijgewerkt'); window.tvCalendar.refetchEvents(); bootstrap.Modal.getInstance(document.getElementById('blockTimeModal')).hide(); } else { showAlert('error', data.error || 'Fout bij opslaan'); } }) .catch(error => { console.error('Error:', error); showAlert('error', 'Netwerkfout bij opslaan'); }); } /** * Delete event */ function deleteEvent(eventId) { fetch('api/delete_transmission.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: eventId }) }) .then(response => response.json()) .then(data => { if (data.success) { showAlert('success', 'Uitzending verwijderd'); window.tvCalendar.refetchEvents(); } else { showAlert('error', data.error || 'Fout bij verwijderen'); } }) .catch(error => { console.error('Error:', error); showAlert('error', 'Netwerkfout bij verwijderen'); }); } /** * Sync event to Talpa API */ function syncEvent(eventId) { showAlert('info', 'Synchroniseren...'); // This would call your existing sync functionality fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `sync_item=1&sync_id=${eventId}` }) .then(() => { showAlert('success', 'Gesynchroniseerd met Talpa'); window.tvCalendar.refetchEvents(); }) .catch(error => { console.error('Error:', error); showAlert('error', 'Fout bij synchroniseren'); }); } /** * Show alert message */ function showAlert(type, message) { const alertContainer = document.getElementById('alertContainer'); if (!alertContainer) return; const alertClass = { 'success': 'alert-success', 'error': 'alert-danger', 'warning': 'alert-warning', 'info': 'alert-info' }[type] || 'alert-info'; const alert = document.createElement('div'); alert.className = `alert ${alertClass} alert-dismissible fade show`; alert.innerHTML = ` ${message} `; alertContainer.appendChild(alert); // Auto-dismiss after 5 seconds setTimeout(() => { alert.remove(); }, 5000); } /** * Filter infomercials in sidebar */ function filterCommercials(searchTerm) { const items = document.querySelectorAll('.infomercial-item'); const term = searchTerm.toLowerCase(); items.forEach(item => { const title = item.dataset.title.toLowerCase(); const seriesCode = (item.dataset.seriesCode || '').toLowerCase(); if (title.includes(term) || seriesCode.includes(term)) { item.style.display = 'block'; } else { item.style.display = 'none'; } }); } /** * Update block info display */ function updateBlockInfo(blocks) { const container = document.getElementById('blockInfoContainer'); if (!container) return; if (blocks.length === 0) { container.innerHTML = 'Geen actieve blokken voor deze dag
'; return; } // Group blocks by channel const blocksByChannel = {}; blocks.forEach(block => { if (!blocksByChannel[block.channel]) { blocksByChannel[block.channel] = []; } blocksByChannel[block.channel].push(block); }); let html = '