BROOKO icon
BROOKO UK NETWORK
Where code meets creativity & adventure
File viewer

calendar.php

Type
php
Size
52.87 KB
Modified
15 May
calendar.php 52.87 KB
<?php
require_once __DIR__ . '/../bootstrap.php';

requireAuth();

if (!hasPermission('calendar.view')) {
    http_response_code(403);
    die('Access denied');
}

$pageTitle = 'Calendar';
$canEdit = hasPermission('calendar.edit');

// Page-specific assets
$extraHead = ''
    . '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.css">' . "\n"
    . '<link rel="stylesheet" href="' . e(app_asset_url('css/calendar-new.css')) . '?v=' . APP_VERSION . '">' . "\n";

// Fetch categories
$categories = [];
try {
    $stmt = $pdo->query("SELECT * FROM calendar_categories ORDER BY sort_order ASC, name ASC");
    $categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
    error_log("Calendar categories error: " . $e->getMessage());
}

// Fetch active vans
$vans = [];
try {
    $stmt = $pdo->query("SELECT id, plate_full, plate_short, make, model, tag_color FROM vans WHERE is_active=1 ORDER BY plate_full ASC");
    $vans = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
    error_log("Vans error: " . $e->getMessage());
}

// Optional: focus a specific vehicle from map (calendar?van_id=123)
$focusVanId = (isset($_GET['van_id']) && $_GET['van_id'] !== '') ? (int)$_GET['van_id'] : 0;
$focusVan = null;
if ($focusVanId > 0) {
    try {
        $stmt = $pdo->prepare("SELECT id, plate_full, plate_short, make, model FROM vans WHERE id = ?");
        $stmt->execute([$focusVanId]);
        $focusVan = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    } catch (Throwable $e) {
        $focusVan = null;
    }
}

// Fetch active users for drivers/porters
$drivers = [];
$porters = [];
try {
    $stmt = $pdo->query("SELECT id, display_name, role, secondary_group FROM users WHERE is_active = 1 ORDER BY display_name ASC");
    $allUsers = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    foreach ($allUsers as $user) {
        $role = strtolower($user['role'] ?? '');
        $secondaryGroup = strtolower($user['secondary_group'] ?? '');
        
        if (strpos($role, 'driver') !== false || $secondaryGroup === 'driver') {
            $drivers[] = $user;
        }
        
        if (strpos($role, 'porter') !== false || $secondaryGroup === 'porter') {
            $porters[] = $user;
        }
    }
} catch (Throwable $e) {
    error_log("Users fetch error: " . $e->getMessage());
}

// CSRF token
if (!isset($_SESSION['calendar_csrf_token'])) {
    $_SESSION['calendar_csrf_token'] = bin2hex(random_bytes(32));
}
$csrfToken = $_SESSION['calendar_csrf_token'];

include __DIR__ . '/../partials/header.php';
?>

<div class="content-header">
    <div>
        <h1 class="content-title">📅 Calendar</h1>
        <p class="content-subtitle">Schedule and manage events</p>
    </div>
    <div style="display: flex; gap: 10px;">
        <button class="btn btn-secondary" onclick="openFilterModal()">
            🔍 Filter
        </button>
        <?php if ($canEdit): ?>
        <button class="btn btn-primary" onclick="openAddEventModal()">
            ➕ Add Event
        </button>
        <?php endif; ?>
    </div>
</div>

<?php if (!empty($focusVan)): ?>
<div class="alert alert-warning" style="display:flex;align-items:center;justify-content:space-between;gap:12px;">
    <div>
        Showing events for vehicle <strong><?= htmlspecialchars($focusVan['plate_full'] ?? '') ?></strong>
        <?php if (!empty($focusVan['make']) || !empty($focusVan['model'])): ?>
            <span class="text-muted">— <?= htmlspecialchars(trim(($focusVan['make'] ?? '') . ' ' . ($focusVan['model'] ?? ''))) ?></span>
        <?php endif; ?>
    </div>
    <a class="btn btn-secondary btn-xs" href="<?= e(app_url('calendar')) ?>" style="white-space:nowrap;">Clear</a>
</div>
<?php endif; ?>

<!-- Calendar Card -->
<div class="calendar-container">
    <div id="calendar"></div>
</div>

<!-- Filter Modal -->
<div class="modal-overlay" id="filterModal" style="display: none;">
    <div class="modal" style="max-width: 400px;">
        <div class="modal-header">
            <h3>Filter Events</h3>
            <button class="modal-close" onclick="closeFilterModal()">×</button>
        </div>
        
        <div class="modal-body" style="padding: 20px;">
            <div class="form-group">
                <label style="display: flex; align-items: center; padding: 10px; cursor: pointer; border-radius: 6px; transition: background 0.2s;" onmouseover="this.style.background='var(--bg-hover)'" onmouseout="this.style.background='transparent'">
                    <input type="radio" name="filterCategory" value="" checked onchange="applyFilter()" style="margin-right: 10px;">
                    <span class="pill-dot" style="background: var(--accent); margin-right: 10px;"></span>
                    <span style="font-weight: 600;">All Events</span>
                </label>
                
                <?php foreach ($categories as $cat): ?>
                <label style="display: flex; align-items: center; padding: 10px; cursor: pointer; border-radius: 6px; transition: background 0.2s;" onmouseover="this.style.background='var(--bg-hover)'" onmouseout="this.style.background='transparent'">
                    <input type="radio" name="filterCategory" value="<?= (int)$cat['id'] ?>" onchange="applyFilter()" style="margin-right: 10px;">
                    <span class="pill-dot" style="background: <?= htmlspecialchars($cat['color']) ?>; margin-right: 10px;"></span>
                    <span style="font-weight: 600;"><?= htmlspecialchars($cat['name']) ?></span>
                </label>
                <?php endforeach; ?>
            </div>
        </div>
        
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" onclick="closeFilterModal()">Close</button>
        </div>
    </div>
</div>

<!-- Event Modal -->
<div class="modal-overlay" id="eventModal">
    <div class="modal">
        <div class="modal-header">
            <h3 id="modalTitle">Add Event</h3>
            <button class="modal-close" onclick="closeModal()">×</button>
        </div>
        
        <div class="modal-body">
            <form id="eventForm">
                <input type="hidden" id="eventId" name="id">
                
                <!-- Category Selection -->
                <div class="info-row">
                    <label>Event Type *</span>
                    <select id="categoryId" name="category_id" class="form-input" required onchange="switchEventType()">
                        <option value="">Select type...</option>
                        <?php foreach ($categories as $cat): ?>
                        <option value="<?= (int)$cat['id'] ?>" data-name="<?= htmlspecialchars($cat['name']) ?>">
                            <?= htmlspecialchars($cat['name']) ?>
                        </option>
                        <?php endforeach; ?>
                    </select>
                </div>
                
                <!-- JOBS Fields -->
                <div id="runsFields" class="event-fields" style="display: none;">
                    <div class="info-row">
                        <label>Job Number</span>
                        <input type="text" id="runNumber" name="run_number" class="form-input" placeholder="e.g., J123">
                    </div>
                    
                    <div class="info-row">
                        <label>Vehicle *</span>
                        <select id="runVehicle" name="van_id" class="form-input">
                            <option value="">Select vehicle...</option>
                            <?php foreach ($vans as $van): ?>
                            <option value="<?= (int)$van['id'] ?>" 
                                    data-color="<?= htmlspecialchars($van['tag_color'] ?? '#ff8c1a') ?>"
                                    data-plate-short="<?= htmlspecialchars($van['plate_short'] ?? '') ?>"
                                    data-plate-full="<?= htmlspecialchars($van['plate_full']) ?>">
                                <?= htmlspecialchars($van['plate_full']) ?>
                                <?php if ($van['make'] && $van['model']): ?>
                                - <?= htmlspecialchars($van['make']) ?> <?= htmlspecialchars($van['model']) ?>
                                <?php endif; ?>
                            </option>
                            <?php endforeach; ?>
                            <option value="temp">Temporary Vehicle</option>
                        </select>
                    </div>
                    
                    <div id="tempVehicleField" class="form-group" style="display: none;">
                        <label>Temp Vehicle Plate *</span>
                        <input type="text" id="tempVehiclePlate" name="van_plate_temp" class="form-input" placeholder="ABC123">
                    </div>
                    
                    <div class="info-row">
                        <label>Driver *</span>
                        <select id="runDriver" name="driver_user_id" class="form-input">
                            <option value="">Select driver...</option>
                            <?php foreach ($drivers as $driver): ?>
                            <option value="<?= (int)$driver['id'] ?>"><?= htmlspecialchars($driver['display_name']) ?></option>
                            <?php endforeach; ?>
                            <option value="other">Other (External)</option>
                        </select>
                    </div>
                    
                    <div id="otherDriverField" class="form-group" style="display: none;">
                        <label>Driver Name *</span>
                        <input type="text" id="otherDriverName" name="driver_name_other" class="form-input" placeholder="External driver name">
                    </div>
                    
                    <div class="info-row">
                        <label>Porter</span>
                        <select id="runPorter" name="porter_user_id" class="form-input">
                            <option value="">No porter</option>
                            <?php foreach ($porters as $porter): ?>
                            <option value="<?= (int)$porter['id'] ?>"><?= htmlspecialchars($porter['display_name']) ?></option>
                            <?php endforeach; ?>
                            <option value="other">Other (External)</option>
                        </select>
                    </div>
                    
                    <div id="otherPorterField" class="form-group" style="display: none;">
                        <label>Porter Name</span>
                        <input type="text" id="otherPorterName" name="porter_name_other" class="form-input" placeholder="External porter name">
                    </div>
                </div>
                
                <!-- MEETINGS Fields -->
                <div id="meetingsFields" class="event-fields" style="display: none;">
                    <div class="form-group">
                        <label>Meeting Title *</label>
                        <input type="text" id="meetingTitle" name="title" class="form-input" placeholder="e.g., Client Consultation, Team Review">
                    </div>
                    
                    <div class="form-group">
                        <label>Meeting With</label>
                        <input type="text" id="meetingWith" name="meeting_with" class="form-input" placeholder="e.g., John Smith, ABC Company">
                        <small class="form-help">Who is this meeting with? (client, external party, etc.)</small>
                    </div>
                    
                    <div class="form-group">
                        <label>Organized By</label>
                        <select id="meetingBy" name="meeting_by_user_id" class="form-input">
                            <option value="">Select staff member...</option>
                            <?php foreach ($allUsers as $user): ?>
                            <option value="<?= (int)$user['id'] ?>"><?= htmlspecialchars($user['display_name']) ?></option>
                            <?php endforeach; ?>
                        </select>
                        <small class="form-help">Which staff member is organizing this meeting?</small>
                    </div>
                    
                    <div class="form-group">
                        <label>Location</label>
                        <input type="text" id="meetingLocation" name="location" class="form-input" placeholder="e.g., Office, Zoom, Client Site">
                    </div>
                    <div class="form-group" id="meetingStatusGroup" style="display: none;">
                        <label>Attendance Status</label>
                        <select id="meetingStatus" name="meeting_status" class="form-input">
                            <option value="pending">Pending</option>
                            <option value="attended">Attended</option>
                            <option value="no_show">No Show</option>
                            <option value="cancelled">Cancelled</option>
                        </select>
                        <small class="form-help">Mark attendance after the meeting takes place</small>
                    </div>
                </div>
                
                <!-- ANNUAL LEAVE Fields -->
<div id="holidaysFields" class="event-fields" style="display: none;">
                    <div class="form-group">
                        <label>Leave Name *</label>
                        <input type="text" id="holidayTitle" name="title" class="form-input" placeholder="e.g., Christmas Break, Summer Holiday">
                    </div>
                    
                    <div class="form-group">
                        <label>Staff Members *</label>
                        <div style="display: flex; gap: 8px; margin-bottom: 8px;">
                            <select id="staffSelector" class="form-input" style="flex: 1;">
                                <option value="">Select staff member...</option>
                                <?php foreach ($allUsers as $user): ?>
                                <option value="<?= (int)$user['id'] ?>" data-name="<?= htmlspecialchars($user['display_name']) ?>">
                                    <?= htmlspecialchars($user['display_name']) ?>
                                </option>
                                <?php endforeach; ?>
                            </select>
                            <button type="button" class="btn btn-primary" onclick="addStaffMember()" style="white-space: nowrap;">+ Add</button>
                        </div>
                        
                        <!-- Selected Staff Display Box -->
                        <div id="selectedStaffBox" style="border: 1px solid var(--border); border-radius: 6px; padding: 10px; min-height: 60px; background: var(--bg-panel); margin-top: 8px;">
                            <div id="selectedStaffList" style="display: flex; flex-wrap: wrap; gap: 6px;">
                                <span style="color: var(--text-muted); font-size: 0.9rem;" id="noStaffMessage">No staff selected</span>
                            </div>
                        </div>
                        <small class="form-help">Select staff members who will be on leave</small>
                        <input type="hidden" id="selectedStaffIds" name="staff_ids" value="">
                    </div>
                    
                    <div class="form-group">
                        <label>Start Date *</label>
                        <input type="date" id="holidayStartDate" name="start_date" class="form-input">
                    </div>
                    
                    <div class="form-group">
                        <label>End Date</label>
                        <input type="date" id="holidayEndDate" name="end_date" class="form-input">
                        <small class="form-help">Leave empty for single-day leave</small>
                    </div>
                </div>
                
                <!-- GENERAL Fields (Maintenance, Appointment, Personal, Other) -->
                <div id="generalFields" class="event-fields" style="display: none;">
                    <div class="info-row">
                        <label>Title *</span>
                        <input type="text" id="generalTitle" name="title" class="form-input" placeholder="Event title">
                    </div>
                    
                    <div class="info-row">
                        <label>Location</span>
                        <input type="text" id="generalLocation" name="location" class="form-input" placeholder="Event location">
                    </div>
                </div>
                
                <!-- Common Fields (Date & Time) -->
                <div id="commonFields" style="display: none;">
                    <div id="commonDateTimeFields">
<div class="info-row">
                        <label>Date *</span>
                        <input type="date" id="eventDate" name="date" class="form-input" required>
                    </div>
                    
                    <div class="info-row">
                        <label class="checkbox-label">
                            <input type="checkbox" id="allDay" name="all_day" value="1" onchange="toggleTimeFields()">
                            <span>All-day event</span>
                        </span>
                    </div>
                    
                    <div id="timeFields">
                        <div class="form-row" style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
                            <div class="form-group">
                                <label>Start Time</label>
                                <div style="display: flex; gap: 8px; align-items: center;">
                                    <select id="startHour" class="form-input" style="flex: 1;">
                                        <?php for ($h = 0; $h < 24; $h++): ?>
                                        <option value="<?= sprintf('%02d', $h) ?>"<?= $h == 8 ? ' selected' : '' ?>><?= sprintf('%02d', $h) ?></option>
                                        <?php endfor; ?>
                                    </select>
                                    <span style="font-weight: 700;">:</span>
                                    <select id="startMinute" class="form-input" style="flex: 1;">
                                        <option value="00" selected>00</option>
                                        <option value="15">15</option>
                                        <option value="30">30</option>
                                        <option value="45">45</option>
                                    </select>
                                </div>
                            </div>
                            
                            <div class="form-group">
                                <label>End Time</label>
                                <div style="display: flex; gap: 8px; align-items: center;">
                                    <select id="endHour" class="form-input" style="flex: 1;">
                                        <option value="">--</option>
                                        <?php for ($h = 0; $h < 24; $h++): ?>
                                        <option value="<?= sprintf('%02d', $h) ?>"><?= sprintf('%02d', $h) ?></option>
                                        <?php endfor; ?>
                                    </select>
                                    <span style="font-weight: 700;">:</span>
                                    <select id="endMinute" class="form-input" style="flex: 1;">
                                        <option value="00" selected>00</option>
                                        <option value="15">15</option>
                                        <option value="30">30</option>
                                        <option value="45">45</option>
                                    </select>
                                </div>
                            </div>
                        </div>
                    </div>
</div>
                    
                    <div class="form-group">
                        <label>Details/Notes</label>
                        <textarea id="eventNotes" name="notes" class="form-input" rows="3" placeholder="Details/Notes..."></textarea>
                    </div>
                </div>
                
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" onclick="closeModal()">Cancel</button>
                    <button type="submit" class="btn btn-primary">Save Event</button>
                </div>
            </form>
        </div>
    </div>
</div>


<script src="<?= e(app_asset_url('js/modal.js')) ?>?v=<?= APP_VERSION ?>"></script>
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.js"></script>
<script>
const CSRF_TOKEN = '<?= $csrfToken ?>';
let calendar = null;
let currentFilter = '';
const INITIAL_VAN_FILTER = <?= (int)($focusVan['id'] ?? 0) ?>;
let currentVanFilter = INITIAL_VAN_FILTER ? String(INITIAL_VAN_FILTER) : '';


document.addEventListener('DOMContentLoaded', function() {
    initCalendar();
});

function initCalendar() {
    const calendarEl = document.getElementById('calendar');

    // Match the app's responsive breakpoints (sidebar collapses around Bootstrap lg)
    const isMobile = window.matchMedia('(max-width: 991.98px)').matches;

    // Remove legacy compact mode (no longer supported)
    try {
        localStorage.removeItem('wp_calendar_compact');
        localStorage.removeItem('wp_calendar_view_noncompact');
    } catch (e) {}

    // Mobile: force Day view only
    const savedView = localStorage.getItem('wp_calendar_view') || '';
    const initialView = isMobile ? 'timeGridDay' : (savedView || 'dayGridMonth');

    function nudgeNowIndicator() {
        // FullCalendar sometimes mis-positions the now-indicator after view changes/resizes.
        // A size update + small delay fixes the majority of cases.
        try { calendar.updateSize(); } catch (e) {}
        setTimeout(() => { try { calendar.updateSize(); } catch (e) {} }, 160);

        // In day view, keep the scroll around "now" (helps the now-indicator appear correctly).
        if (calendar && calendar.view && String(calendar.view.type) === 'timeGridDay') {
            const d = new Date();
            const hh = String(d.getHours()).padStart(2, '0');
            const mm = String(d.getMinutes()).padStart(2, '0');
            const ss = String(d.getSeconds()).padStart(2, '0');
            const t = `${hh}:${mm}:${ss}`;
            try { calendar.scrollToTime(t); } catch (e) {}
        }
    }

    function applyToolbarForWidth() {
        const mobileNow = window.matchMedia('(max-width: 991.98px)').matches;
        calendar.setOption('headerToolbar', {
            left: mobileNow ? 'prev,next' : 'prev,next today',
            center: 'title',
            right: mobileNow ? '' : 'dayGridMonth,timeGridWeek,timeGridDay'
        });

        // Mobile is day-only
        if (mobileNow && calendar.view && calendar.view.type !== 'timeGridDay') {
            try { calendar.changeView('timeGridDay'); } catch (e) {}
        }
    }

    calendar = new FullCalendar.Calendar(calendarEl, {
        initialView: initialView,
        headerToolbar: {
            left: isMobile ? 'prev,next' : 'prev,next today',
            center: 'title',
            // Mobile is day-only, and we hide the view switcher completely
            right: isMobile ? '' : 'dayGridMonth,timeGridWeek,timeGridDay'
        },
        height: 'auto',
        expandRows: true,
        firstDay: 1, // Monday as start of week

        // Explicitly stick to local time (prevents odd now-indicator offsets)
        timeZone: 'local',

        // Time grid settings
        slotMinTime: '06:00:00',
        slotMaxTime: '22:00:00',
        scrollTime: '06:00:00',
        slotDuration: '01:00:00',
        allDaySlot: true,
        nowIndicator: true,
        scrollTimeReset: false,

        events: {
            url: '/api/calendar',
            method: 'GET',
            extraParams: function() {
                return {
                    action: 'list',
                    category: currentFilter || '',
                    van_id: currentVanFilter || ''
                };
            },
            failure: function(error) {
                console.error('Failed to load events:', error);
            }
        },
        eventClick: function(info) {
            viewEvent(info.event.id);
        },
        dateClick: function(info) {
            <?php if ($canEdit): ?>
            openAddEventModal(info.dateStr);
            <?php endif; ?>
        },

        datesSet: function(arg) {
            const mobileNow = window.matchMedia('(max-width: 991.98px)').matches;

            // Persist view choice on desktop only
            if (!mobileNow && arg && arg.view && arg.view.type) {
                localStorage.setItem('wp_calendar_view', arg.view.type);
            }

            // Force day-only on mobile
            if (mobileNow && arg && arg.view && arg.view.type !== 'timeGridDay') {
                try { calendar.changeView('timeGridDay'); } catch (e) {}
                return;
            }

            nudgeNowIndicator();
        },

        viewDidMount: function() {
            nudgeNowIndicator();
        },

        windowResize: function() {
            // Keep toolbar + view in sync with responsive layout
            try { applyToolbarForWidth(); } catch (e) {}

            // FullCalendar sometimes needs a nudge after resize
            setTimeout(() => { try { calendar.updateSize(); } catch (e) {} }, 120);
        }
    });

    // IMPORTANT: without render() the calendar stays blank
    calendar.render();

    // Ensure correct toolbar after first render (helps when CSS breakpoints differ)
    try { applyToolbarForWidth(); } catch (e) {}
}


function openAddEventModal(date = null) {
    document.getElementById('modalTitle').textContent = 'Add Event';
    document.getElementById('eventForm').reset();
    document.getElementById('eventId').value = '';
    
    // Set default date to today if not provided
    const defaultDate = date || new Date().toISOString().substring(0, 10);
    document.getElementById('eventDate').value = defaultDate;
    document.getElementById('holidayStartDate').value = defaultDate;
    
    // Set default time to 08:00
    document.getElementById('startHour').value = '08';
    document.getElementById('startMinute').value = '00';
    document.getElementById('endHour').value = '';
    document.getElementById('endMinute').value = '00';
    
    hideAllEventFields();
    
    const dtBlock = document.getElementById('commonDateTimeFields');
    if (dtBlock) dtBlock.style.display = 'block';

    document.getElementById('eventModal').classList.add('active');
}

function closeModal() {
    document.getElementById('eventModal').classList.remove('active');
    document.getElementById('eventForm').reset();
    hideAllEventFields();
    clearSelectedStaff();
}

function hideAllEventFields() {
    document.querySelectorAll('.event-fields').forEach(el => el.style.display = 'none');
    document.getElementById('commonFields').style.display = 'none';
}

function isJobType(name) {
    const n = (name || '').trim();
    return ['Jobs','Job','Runs','Run'].includes(n);
}

function isMeetingType(name) {
    const n = (name || '').trim();
    return ['Meetings','Meeting'].includes(n);
}

function switchEventType(skipReset = false) {
    const select = document.getElementById('categoryId');
    const categoryName = select.options[select.selectedIndex]?.getAttribute('data-name') || '';
    
    hideAllEventFields();
    
    
    const dt = document.getElementById('commonDateTimeFields');
    if (dt) dt.style.display = 'block';
// Reset all inputs only when creating new events, not when editing
    if (!skipReset) {
        document.querySelectorAll('.event-fields input, .event-fields textarea, .event-fields select').forEach(input => {
            if (input.type !== 'checkbox') input.value = '';
        });
    }
    
    // Show appropriate fields
    if (isJobType(categoryName)) {
        document.getElementById('runsFields').style.display = 'block';
        document.getElementById('commonFields').style.display = 'block';
        if (!skipReset) document.getElementById('allDay').checked = true;
        toggleTimeFields();
    } else if (isMeetingType(categoryName)) {
        document.getElementById('meetingsFields').style.display = 'block';
        document.getElementById('commonFields').style.display = 'block';
        if (!skipReset) document.getElementById('allDay').checked = false;
        toggleTimeFields();
    } else if (categoryName === 'Annual Leave') {
        document.getElementById('holidaysFields').style.display = 'block';
        document.getElementById('commonFields').style.display = 'block';
        const dt = document.getElementById('commonDateTimeFields');
        if (dt) dt.style.display = 'none';
    } else if (categoryName) {
        document.getElementById('generalFields').style.display = 'block';
        document.getElementById('commonFields').style.display = 'block';
        if (!skipReset) document.getElementById('allDay').checked = false;
        toggleTimeFields();
    }
}

function toggleTimeFields() {
    const allDay = document.getElementById('allDay').checked;
    document.getElementById('timeFields').style.display = allDay ? 'none' : 'block';
}

// Vehicle selection handler
document.getElementById('runVehicle')?.addEventListener('change', function() {
    const tempField = document.getElementById('tempVehicleField');
    tempField.style.display = this.value === 'temp' ? 'block' : 'none';
});

// Driver selection handler
document.getElementById('runDriver')?.addEventListener('change', function() {
    const otherField = document.getElementById('otherDriverField');
    otherField.style.display = this.value === 'other' ? 'block' : 'none';
});

// Porter selection handler
document.getElementById('runPorter')?.addEventListener('change', function() {
    const otherField = document.getElementById('otherPorterField');
    otherField.style.display = this.value === 'other' ? 'block' : 'none';
});

// Form submission
document.getElementById('eventForm').addEventListener('submit', async function(e) {
    e.preventDefault();
    
    const formData = new FormData(this);
    const categoryName = document.getElementById('categoryId').options[document.getElementById('categoryId').selectedIndex]?.getAttribute('data-name') || '';
    
    // Get event ID for update vs create
    const eventId = document.getElementById('eventId').value;
    if (eventId) {
        formData.set('id', eventId);
    }
    
    // Build proper start/end based on event type
    if (categoryName === 'Annual Leave') {
        const startDate = document.getElementById('holidayStartDate').value;
        const endDate = document.getElementById('holidayEndDate').value || startDate;
        formData.set('start', startDate);
        formData.set('end', endDate);
        formData.set('all_day', '1');
        
        // Use annual leave title
        const title = document.getElementById('holidayTitle').value;
        formData.set('title', title);
        
        // Details/Notes
        const notes = (document.getElementById('eventNotes')?.value || '').trim();
        if (notes) formData.set('notes', notes);
    } else {
        const date = document.getElementById('eventDate').value;
        const allDay = document.getElementById('allDay').checked;
        
        if (allDay) {
            formData.set('start', date);
            formData.set('end', date);
            formData.set('all_day', '1');
        } else {
            const startHour = document.getElementById('startHour').value || '08';
            const startMinute = document.getElementById('startMinute').value || '00';
            const startTime = `${startHour}:${startMinute}`;
            
            formData.set('start', `${date}T${startTime}:00`);
            
            const endHour = document.getElementById('endHour').value;
            if (endHour) {
                const endMinute = document.getElementById('endMinute').value || '00';
                const endTime = `${endHour}:${endMinute}`;
                formData.set('end', `${date}T${endTime}:00`);
            }
            formData.set('all_day', '0');
        }
        
        // Set title based on event type
        if (isJobType(categoryName)) {
                        const vehicleSelect = document.getElementById('runVehicle');
            const selectedOption = vehicleSelect.options[vehicleSelect.selectedIndex];
            const plateShort = selectedOption?.getAttribute('data-plate-short') || '';
            // Format (Option B): "FV1 - FTT"
            const jobNum = (document.getElementById('runNumber').value || '').trim();
            formData.set('run_number', jobNum);

            // Vehicle label (supports temp vehicles)
            let vehicleLabel = plateShort;
            if (vehicleSelect.value === 'temp') {
                vehicleLabel = (document.getElementById('tempVehiclePlate').value || '').trim();
            }

            const title = (vehicleLabel && jobNum) ? `${vehicleLabel} - ${jobNum}` : (vehicleLabel || jobNum || 'Job');
            formData.set('title', title);
            formData.set('is_run_event', '1');
            
            // Get notes from eventNotes if it exists
            const notesField = document.getElementById('eventNotes');
            if (notesField && notesField.value) {
                formData.set('notes', notesField.value);
            }
        } else if (isMeetingType(categoryName)) {
            formData.set('title', document.getElementById('meetingTitle').value);
            
            // Meeting-specific fields
            const meetingWith = document.getElementById('meetingWith').value;
            if (meetingWith) formData.set('meeting_with', meetingWith);
            
            const meetingBy = document.getElementById('meetingBy').value;
            if (meetingBy) formData.set('meeting_by_user_id', meetingBy);
            
            const meetingStatus = document.getElementById('meetingStatus').value;
            if (meetingStatus) formData.set('meeting_status', meetingStatus);
        } else {
            formData.set('title', document.getElementById('generalTitle').value);
        }
    }
    // Details/Notes (applies to all event types)
    const notesField = document.getElementById('eventNotes');
    const notesVal = (notesField && notesField.value) ? notesField.value.trim() : '';
    if (notesVal) formData.set('notes', notesVal);

    formData.set('action', 'save');
    
    // Debug: log what we're sending
    console.log('Submitting event:', {
        id: formData.get('id'),
        action: formData.get('action'),
        title: formData.get('title'),
        start: formData.get('start'),
        all_day: formData.get('all_day')
    });
    
    try {
        const response = await fetch('/api/calendar', {
            method: 'POST',
            headers: { 'X-CSRF-Token': CSRF_TOKEN },
            body: formData
        });
        
        const result = await response.json();
        
        console.log('Server response:', result);
        
        if (result.success) {
            calendar.refetchEvents();
            closeModal();
        } else {
            alert('Error: ' + (result.error || 'Failed to save event'));
        }
    } catch (error) {
        console.error('Save error:', error);
        alert('Failed to save event');
    }
});

// Filter Modal Functions
function openFilterModal() {
    document.getElementById('filterModal').style.display = 'flex';
}

function closeFilterModal() {
    document.getElementById('filterModal').style.display = 'none';
}

function applyFilter() {
    const selected = document.querySelector('input[name="filterCategory"]:checked');
    currentFilter = selected ? selected.value : '';
    calendar.refetchEvents();
}

function filterByCategory(categoryId) {
    // Legacy function - keeping for compatibility
    currentFilter = categoryId;
    calendar.refetchEvents();
}

// View Event Functions
let currentViewEventId = null;
let currentViewModal = null;

async function viewEvent(eventId) {
    currentViewEventId = eventId;
    
    // Create modal with loading state
    const canEdit = <?= $canEdit ? 'true' : 'false' ?>;
    const buttons = [
        { text: 'Close', class: 'btn-secondary', onClick: (e, m) => m.close() }
    ];
    
    if (canEdit) {
        buttons.push(
            { text: 'Delete', class: 'btn-danger', onClick: () => deleteEventFromModal() },
            { text: 'Edit', class: 'btn-primary', onClick: () => editEventFromModal() }
        );
    }
    
    currentViewModal = new Modal({
        title: 'Event Details',
        content: '<p style="text-align: center; padding: 20px;">Loading...</p>',
        size: 'medium',
        buttons: buttons,
        onClose: () => {
            currentViewEventId = null;
            currentViewModal = null;
        }
    });
    
    currentViewModal.open();
    currentViewModal.setLoading(true);
    
    try {
        const response = await fetch(`/api/calendar?action=get&id=${eventId}`);
        const result = await response.json();
        
        if (result.success && result.event) {
            const contentHtml = buildEventDetailsHtml(result.event);
            currentViewModal.updateContent(contentHtml);
            currentViewModal.setLoading(false);
        } else {
            currentViewModal.updateContent('<p style="color: var(--text-danger);">Error loading event details</p>');
            currentViewModal.setLoading(false);
        }
    } catch (error) {
        console.error('Error fetching event:', error);
        currentViewModal.updateContent('<p style="color: var(--text-danger);">Failed to load event details</p>');
        currentViewModal.setLoading(false);
    }
}

function buildEventDetailsHtml(event) {
    const categoryName = event.category_name || '';
    let html = '<div style="line-height: 2;">';
    
    // Title
    html += `<div class="info-row">
        <span class="info-label">Event</span>
        <div class="info-value" style="font-size: 1.1rem; font-weight: 600;">${escapeHtml(event.title)}</div>
    </div>`;
    
    // Category
    if (categoryName) {
        html += `<div class="info-row">
            <span class="info-label">Type</span>
            <div class="info-value">${escapeHtml(categoryName)}</div>
        </div>`;
    }
    
    // Date & Time
    const startDate = new Date(event.start_at || event.start);
    const endDate = (event.end_at || event.end) ? new Date(event.end_at || event.end) : null;
    
    if (event.all_day) {
        if (endDate && endDate.toDateString() !== startDate.toDateString()) {
            html += `<div class="info-row">
                <span class="info-label">Dates</span>
                <div class="info-value">${formatDate(startDate)} - ${formatDate(endDate)}</div>
            </div>`;
        } else {
            html += `<div class="info-row">
                <span class="info-label">Date</span>
                <div class="info-value">${formatDate(startDate)}${(isJobType(categoryName) || event.is_run_event) ? '' : ' (All-day)'}</div>
            </div>`;
        }
    } else {
        html += `<div class="info-row">
            <span class="info-label">Date & Time</span>
            <div class="info-value">${formatDate(startDate)} at ${formatTime(startDate)}${endDate ? ' - ' + formatTime(endDate) : ''}</div>
        </div>`;
    }
    
    // Jobs-specific fields
    if (isJobType(categoryName) || event.is_run_event) {
        if (event.run_number) {
            html += `<div class="info-row">
                <span class="info-label">Job Number</span>
                <div class="info-value">${escapeHtml(event.run_number)}</div>
            </div>`;
        }
        
        if (event.van_plate_full || event.van_plate_temp) {
            html += `<div class="info-row">
                <span class="info-label">Vehicle</span>
                <div class="info-value">${escapeHtml(event.van_plate_full || event.van_plate_temp)}</div>
            </div>`;
        }
        
        if (event.driver_name || event.driver_name_other) {
            html += `<div class="info-row">
                <span class="info-label">Driver</span>
                <div class="info-value">${escapeHtml(event.driver_name || event.driver_name_other)}</div>
            </div>`;
        }
        
        if (event.porter_name || event.porter_name_other) {
            html += `<div class="info-row">
                <span class="info-label">Porter</span>
                <div class="info-value">${escapeHtml(event.porter_name || event.porter_name_other)}</div>
            </div>`;
        }
    }
    
    // Meetings-specific fields
    if (categoryName === 'Meetings') {
        if (event.meeting_with) {
            html += `<div class="info-row">
                <span class="info-label">Meeting With</span>
                <div class="info-value">${escapeHtml(event.meeting_with)}</div>
            </div>`;
        }
        
        if (event.meeting_by_name) {
            html += `<div class="info-row">
                <span class="info-label">Organized By</span>
                <div class="info-value">${escapeHtml(event.meeting_by_name)}</div>
            </div>`;
        }
        
        if (event.meeting_status) {
            const statusLabels = {
                'pending': '⏳ Pending',
                'attended': '✅ Attended',
                'no_show': '❌ No Show',
                'cancelled': '🚫 Cancelled'
            };
            const statusLabel = statusLabels[event.meeting_status] || event.meeting_status;
            
            html += `<div class="info-row">
                <span class="info-label">Status</span>
                <div class="info-value">${statusLabel}</div>
            </div>`;
        }
    }
    
    // Annual Leave-specific fields
    if (categoryName === 'Annual Leave' && event.staff_members && event.staff_members.length > 0) {
        html += `<div class="info-row">
            <span class="info-label">Staff Members</span>
            <div class="info-value">`;
        
        event.staff_members.forEach((staff, index) => {
            if (index > 0) html += ', ';
            html += escapeHtml(staff.display_name);
        });
        
        html += `</div>
        </div>`;
    }
    
    // Location
    if (event.location) {
        html += `<div class="info-row">
            <span class="info-label">Location</span>
            <div class="info-value">${escapeHtml(event.location)}</div>
        </div>`;
    }
    
    // Description
    if (event.description) {
        html += `<div class="info-row">
            <span class="info-label">Details/Notes</span>
            <div class="info-value" style="white-space: pre-wrap;">${escapeHtml(event.description)}</div>
        </div>`;
    }
    
    html += '</div>';
    return html;
}

function deleteEventFromModal() {
    if (!currentViewEventId) return;
    
    showConfirm(
        'Delete Event',
        'Are you sure you want to delete this event? This action cannot be undone.',
        async () => {
            try {
                const response = await fetch('/api/calendar', {
                    method: 'POST',
                    headers: { 
                        'Content-Type': 'application/json',
                        'X-CSRF-Token': CSRF_TOKEN 
                    },
                    body: JSON.stringify({
                        action: 'delete',
                        id: currentViewEventId
                    })
                });
                
                const result = await response.json();
                
                if (result.success) {
                    if (currentViewModal) currentViewModal.close();
                    calendar.refetchEvents();
                    showAlert('Success', 'Event deleted successfully');
                } else {
                    showAlert('Error', result.error || 'Failed to delete event');
                }
            } catch (error) {
                console.error('Delete error:', error);
                showAlert('Error', 'Failed to delete event');
            }
        }
    );
}

async function editEventFromModal() {
    if (!currentViewEventId) return;
    
    console.log('Editing event ID:', currentViewEventId);
    
    try {
        const response = await fetch(`/api/calendar?action=get&id=${currentViewEventId}`);
        const result = await response.json();
        
        console.log('Loaded event data:', result);
        
        if (result.success && result.event) {
            console.log('Loading into form - Category:', result.event.category_name);
            loadEventIntoForm(result.event);
            if (currentViewModal) currentViewModal.close();
            document.getElementById('eventModal').classList.add('active');
        } else {
            console.error('Failed to load event:', result.error);
            showAlert('Error', 'Failed to load event for editing');
        }
    } catch (error) {
        console.error('Error loading event:', error);
        showAlert('Error', 'Failed to load event');
    }
}

function loadEventIntoForm(event) {
    // Set modal title
    document.getElementById('modalTitle').textContent = 'Edit Event';
    
    // Set event ID
    document.getElementById('eventId').value = event.id;
    
    // Set category
    const categorySelect = document.getElementById('categoryId');
    categorySelect.value = event.category_id || '';
    switchEventType(true); // Pass true to skip resetting fields
    
    const categoryName = event.category_name || '';
    
    // Set common fields
    if (categoryName !== 'Annual Leave') {
        document.getElementById('eventDate').value = event.start?.substring(0, 10) || '';
        document.getElementById('allDay').checked = event.all_day == 1;
        toggleTimeFields();
        
        if (!event.all_day && event.start_at) {
            const startTime = event.start_at.substring(11, 16); // HH:MM
            const [startHour, startMinute] = startTime.split(':');
            document.getElementById('startHour').value = startHour;
            document.getElementById('startMinute').value = startMinute;
            
            if (event.end_at) {
                const endTime = event.end_at.substring(11, 16); // HH:MM
                const [endHour, endMinute] = endTime.split(':');
                document.getElementById('endHour').value = endHour;
                document.getElementById('endMinute').value = endMinute;
            }
        }
        
        if (document.getElementById('eventNotes')) {
            document.getElementById('eventNotes').value = event.description || '';
        }
    }
    
    // Set type-specific fields
    if (isJobType(categoryName)) {
        if (event.run_number) {
            document.getElementById('runNumber').value = event.run_number;
        }
        
        if (event.van_id) {
            document.getElementById('runVehicle').value = event.van_id;
        } else if (event.van_plate_temp) {
            document.getElementById('runVehicle').value = 'temp';
            document.getElementById('tempVehicleField').style.display = 'block';
            document.getElementById('tempVehiclePlate').value = event.van_plate_temp;
        }
        
        if (event.driver_user_id) {
            document.getElementById('runDriver').value = event.driver_user_id;
        } else if (event.driver_name_other) {
            document.getElementById('runDriver').value = 'other';
            document.getElementById('otherDriverField').style.display = 'block';
            document.getElementById('otherDriverName').value = event.driver_name_other;
        }
        
        if (event.porter_user_id) {
            document.getElementById('runPorter').value = event.porter_user_id;
        } else if (event.porter_name_other) {
            document.getElementById('runPorter').value = 'other';
            document.getElementById('otherPorterField').style.display = 'block';
            document.getElementById('otherPorterName').value = event.porter_name_other;
        }
    } else if (isMeetingType(categoryName)) {
        document.getElementById('meetingTitle').value = event.title || '';
        if (event.location) document.getElementById('meetingLocation').value = event.location;
        if (event.meeting_with) document.getElementById('meetingWith').value = event.meeting_with;
        if (event.meeting_by_user_id) document.getElementById('meetingBy').value = event.meeting_by_user_id;
        
        
        // Show attendance status for existing meetings
        const statusGroup = document.getElementById('meetingStatusGroup');
        if (event.id) {
            statusGroup.style.display = 'block';
            if (event.meeting_status) {
                document.getElementById('meetingStatus').value = event.meeting_status;
            }
        } else {
            statusGroup.style.display = 'none';
        }
    } else if (categoryName === 'Annual Leave') {
        document.getElementById('holidayTitle').value = event.title || '';
        if (event.start_at || event.start) {
            const startDate = event.start_at || event.start;
            document.getElementById('holidayStartDate').value = startDate.substring(0, 10);
        }
        if (event.end_at || event.end) {
            const endDateStr = event.end_at || event.end;
            const startDateStr = event.start_at || event.start;
            if (endDateStr !== startDateStr) {
                const endDate = new Date(endDateStr);
                endDate.setDate(endDate.getDate() - 1); // Adjust for exclusive end
                document.getElementById('holidayEndDate').value = endDate.toISOString().substring(0, 10);
            }
        }
        // Load staff members
        selectedStaff = [];
        if (event.staff_members && event.staff_members.length > 0) {
            event.staff_members.forEach(staff => {
                selectedStaff.push({
                    id: parseInt(staff.id),
                    name: staff.display_name
                });
            });
        }
        updateSelectedStaffDisplay(); // Always update display, even if empty
    } else {
        document.getElementById('generalTitle').value = event.title || '';
        if (event.location) document.getElementById('generalLocation').value = event.location;
        }
    // Details/Notes
    const notesEl = document.getElementById('eventNotes');
    if (notesEl) notesEl.value = event.description || event.notes || '';
}

function formatDate(date) {
    return date.toLocaleDateString('en-GB', { 
        weekday: 'short', 
        year: 'numeric', 
        month: 'short', 
        day: 'numeric' 
    });
}

function formatTime(date) {
    return date.toLocaleTimeString('en-GB', { 
        hour: '2-digit', 
        minute: '2-digit',
        hour12: false
    });
}

// Staff selector functions for Annual Leave
let selectedStaff = [];

function addStaffMember() {
    const selector = document.getElementById('staffSelector');
    const selectedOption = selector.options[selector.selectedIndex];
    
    if (!selectedOption || !selectedOption.value) {
        return;
    }
    
    const staffId = parseInt(selectedOption.value);
    const staffName = selectedOption.getAttribute('data-name');
    
    // Check if already added
    if (selectedStaff.find(s => s.id === staffId)) {
        alert('This staff member is already added');
        return;
    }
    
    // Add to array
    selectedStaff.push({ id: staffId, name: staffName });
    
    // Update display
    updateSelectedStaffDisplay();
    
    // Reset selector
    selector.selectedIndex = 0;
}

function removeStaffMember(staffId) {
    selectedStaff = selectedStaff.filter(s => s.id !== staffId);
    updateSelectedStaffDisplay();
}

function updateSelectedStaffDisplay() {
    const listContainer = document.getElementById('selectedStaffList');
    const noMessage = document.getElementById('noStaffMessage');
    const hiddenInput = document.getElementById('selectedStaffIds');
    
    if (selectedStaff.length === 0) {
        listContainer.innerHTML = '<span style="color: var(--text-muted); font-size: 0.9rem;" id="noStaffMessage">No staff selected</span>';
        hiddenInput.value = '';
        return;
    }
    
    // Build staff badges
    let html = '';
    selectedStaff.forEach(staff => {
        html += `
            <div style="display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; background: var(--accent-soft); border: 1px solid var(--accent); border-radius: 6px; font-size: 0.9rem;">
                <span style="font-weight: 600;">${escapeHtml(staff.name)}</span>
                <button type="button" onclick="removeStaffMember(${staff.id})" style="background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 0; font-size: 1.1rem; line-height: 1;" title="Remove">×</button>
            </div>
        `;
    });
    
    listContainer.innerHTML = html;
    
    // Update hidden input with comma-separated IDs
    hiddenInput.value = selectedStaff.map(s => s.id).join(',');
}

function clearSelectedStaff() {
    selectedStaff = [];
    updateSelectedStaffDisplay();
}

function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}
</script>

<?php include __DIR__ . '/../partials/footer.php'; ?>