<link href="<?= ROOT_URL ?>/src/css/diary.css" rel="stylesheet">
<script src="<?= ROOT_URL ?>/src/js/moment.js"></script>
<script src="<?= ROOT_URL ?>/src/js/diary.js"></script>

<style>
.surveyor-diary-toolbar {
    gap: .5rem;
}
.surveyor-diary-calendar {
    min-height: 500px;
}
.surveyor-review-list {
    max-height: 240px;
    overflow: auto;
}
.surveyor-review-note {
    color: #596674;
    max-width: 520px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.surveyor-review-actions {
    align-items: center;
    display: flex;
    flex-wrap: nowrap;
    gap: .35rem;
}
.surveyor-review-actions .btn {
    align-items: center;
    display: inline-flex;
    height: 30px;
    justify-content: center;
    margin: 0 !important;
    padding: 0 .55rem;
    white-space: nowrap;
}
.review-contact-grid {
    display: grid;
    gap: .35rem .75rem;
    grid-template-columns: 120px minmax(0, 1fr);
}
.review-contact-grid dt {
    color: #66717d;
    font-weight: 700;
    margin: 0;
}
.review-contact-grid dd {
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
}
.surveyor-diary-list {
    max-height: 520px;
    overflow: auto;
}
.surveyor-diary-notes {
    color: #6b7580;
    display: block;
    max-width: 420px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.surveyor-list-table {
    min-width: 1280px;
    table-layout: fixed;
}
.surveyor-list-table th,
.surveyor-list-table td {
    vertical-align: middle;
}
.surveyor-list-table th {
    white-space: nowrap;
}
.surveyor-list-client {
    max-width: 360px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.surveyor-list-subtext {
    color: #6b7580;
    display: block;
    font-size: .8rem;
    line-height: 1.25;
}
.surveyor-list-date {
    white-space: nowrap;
}
.surveyor-list-date small {
    color: #596674;
}
.surveyor-due-chips,
.surveyor-list-actions {
    align-items: center;
    display: flex;
    flex-wrap: nowrap;
    gap: .35rem;
}
.surveyor-due-chip {
    border: 1px solid #dfe5eb;
    border-radius: 6px;
    font-size: .78rem;
    line-height: 1;
    padding: .28rem .4rem;
    white-space: nowrap;
}
.surveyor-list-actions .btn {
    align-items: center;
    display: inline-flex;
    gap: .3rem;
    height: 30px;
    justify-content: center;
    margin: 0 !important;
    padding: 0 .55rem;
    white-space: nowrap;
}
.surveyor-job-summary {
    border: 1px solid #e2e7ec;
    border-radius: 8px;
    overflow: hidden;
}
.surveyor-job-summary-header {
    background: #f4f7fa;
    border-bottom: 1px solid #e2e7ec;
    padding: 1rem 1.25rem;
}
.surveyor-job-summary-body {
    padding: 1.25rem;
}
.surveyor-job-client {
    font-size: 1.35rem;
    line-height: 1.25;
    margin: 0;
}
.surveyor-job-meta {
    color: #5d6873;
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: .5rem;
}
.surveyor-job-section {
    border: 1px solid #edf0f2;
    border-radius: 8px;
    padding: .9rem 1rem;
    height: 100%;
}
.surveyor-job-section-title {
    color: #5d6873;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: 0;
    margin-bottom: .4rem;
    text-transform: uppercase;
}
.surveyor-job-address,
.surveyor-job-notes {
    white-space: pre-wrap;
}
.surveyor-job-empty {
    color: #7a8591;
    font-style: italic;
}
.surveyor-job-due-grid {
    display: grid;
    gap: .75rem;
    grid-template-columns: repeat(4, minmax(0, 1fr));
}
.surveyor-job-due-tile {
    background: #f8fafc;
    border: 1px solid #e2e7ec;
    border-radius: 8px;
    padding: .75rem;
}
.surveyor-job-due-number {
    display: block;
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1;
}
.surveyor-job-ui-estimate {
    background: #f8fafc;
    border: 1px solid #e2e7ec;
    border-radius: 8px;
    color: #34485c;
    padding: .85rem 1rem;
}
.surveyor-job-footer-map {
    margin-right: auto;
}
.surveyor-diary-calendar .fc-center h2,
.surveyor-diary-calendar .fc-toolbar h2 {
    white-space: nowrap;
    line-height: 1.2;
}
.surveyor-diary-calendar .fc-day-header,
.surveyor-diary-calendar .fc-widget-header {
    background: var(--primary-color, #294766) !important;
    color: #fff !important;
    font-weight: 700;
    white-space: nowrap;
}
.surveyor-diary-calendar .fc-day-header a,
.surveyor-diary-calendar .fc-widget-header a,
.surveyor-diary-calendar .fc-col-header-cell a {
    color: #fff !important;
}
.surveyor-diary-calendar .fc-axis,
.surveyor-diary-calendar .fc-timegrid-axis,
.surveyor-diary-calendar .fc-timegrid-slot-label,
.surveyor-diary-calendar .fc-timegrid-axis-cushion,
.surveyor-diary-calendar .fc-timegrid-slot-label-cushion {
    background: #f2f4f6 !important;
    color: #4f5963 !important;
}
.surveyor-diary-calendar .fc-bg td,
.surveyor-diary-calendar .fc-slats td,
.surveyor-diary-calendar .fc-content-skeleton td,
.surveyor-diary-calendar .fc-axis {
    border-color: #d0d4d8 !important;
}
.surveyor-diary-calendar .fc-time-grid .fc-day,
.surveyor-diary-calendar .fc-time-grid .fc-bg td,
.surveyor-diary-calendar .fc-time-grid .fc-content-skeleton td,
.surveyor-diary-calendar .fc-day-grid .fc-day {
    border-left: 1px solid #d0d4d8 !important;
    border-right: 1px solid #d0d4d8 !important;
}
.surveyor-diary-calendar .fc-time-grid .fc-slats tr {
    border-top: 1px solid #e1e4e7;
}
.surveyor-diary-calendar .fc-time-grid .fc-slats tr:nth-child(even) td {
    border-top-style: dotted !important;
    border-top-color: #edf0f2 !important;
}
.surveyor-diary-calendar .fc-time-grid .fc-slats td {
    height: 44px;
}
.surveyor-diary-calendar .fc-time-grid-container {
    border: 1px solid #d0d4d8;
}
.surveyor-diary-calendar .fc-day-header {
    border: 1px solid #d0d4d8 !important;
    background: var(--primary-color, #294766) !important;
    color: #fff !important;
}
.surveyor-diary-calendar .fc-view > table,
.surveyor-diary-calendar .fc-view .fc-head,
.surveyor-diary-calendar .fc-view .fc-body {
    border-collapse: collapse !important;
}
.surveyor-diary-calendar .fc-time-grid .fc-bg table,
.surveyor-diary-calendar .fc-time-grid .fc-content-skeleton table,
.surveyor-diary-calendar .fc-day-grid .fc-bg table,
.surveyor-diary-calendar .fc-day-grid .fc-content-skeleton table {
    border-collapse: collapse !important;
}
.surveyor-diary-calendar .fc-time-grid .fc-bg td,
.surveyor-diary-calendar .fc-time-grid .fc-content-skeleton td {
    box-shadow: inset 1px 0 0 #d0d4d8, inset -1px 0 0 #d0d4d8;
}
.surveyor-diary-calendar .fc-month-view .fc-bg td,
.surveyor-diary-calendar .fc-month-view .fc-content-skeleton td,
.surveyor-diary-calendar .fc-month-view .fc-day-top {
    border: 1px solid #d0d4d8 !important;
    box-shadow: inset 0 0 0 1px #e4e7ea;
}
.surveyor-diary-calendar .fc-month-view .fc-row {
    border-top: 1px solid #d0d4d8 !important;
}
.surveyor-diary-calendar .fc-month-view .fc-day-number {
    padding: 6px;
}
.surveyor-diary-calendar .fc-time-grid-event {
    margin-left: 3px;
    margin-right: 3px;
}
.surveyor-diary-calendar .fc-event {
    border-width: 2px;
    box-shadow: 0 1px 3px rgba(0,0,0,.22);
}
.surveyor-diary-calendar .surveyor-colour-0 { background-color: #294766 !important; border-color: #294766 !important; }
.surveyor-diary-calendar .surveyor-colour-1 { background-color: #2f6f64 !important; border-color: #2f6f64 !important; }
.surveyor-diary-calendar .surveyor-colour-2 { background-color: #7a4f2b !important; border-color: #7a4f2b !important; }
.surveyor-diary-calendar .surveyor-colour-3 { background-color: #6d4c7d !important; border-color: #6d4c7d !important; }
.surveyor-diary-calendar .surveyor-colour-4 { background-color: #7a3947 !important; border-color: #7a3947 !important; }
.surveyor-diary-calendar .surveyor-colour-5 { background-color: #4f6330 !important; border-color: #4f6330 !important; }
.surveyor-diary-calendar .fc-theme-standard td,
.surveyor-diary-calendar .fc-theme-standard th,
.surveyor-diary-calendar .fc-scrollgrid,
.surveyor-diary-calendar .fc-scrollgrid td,
.surveyor-diary-calendar .fc-scrollgrid th {
    border-color: #d0d4d8 !important;
}
.surveyor-diary-calendar .fc-timegrid-col,
.surveyor-diary-calendar .fc-col-header-cell {
    border-left: 1px solid #d0d4d8 !important;
    border-right: 1px solid #d0d4d8 !important;
    box-shadow: inset 1px 0 0 #d0d4d8, inset -1px 0 0 #d0d4d8;
}
.surveyor-diary-calendar .fc-col-header-cell {
    background: var(--primary-color, #294766) !important;
    color: #fff !important;
}
.surveyor-diary-calendar .fc-timegrid-slot {
    border-top: 1px solid #e1e4e7 !important;
    height: 44px !important;
}
.surveyor-diary-calendar .fc-timegrid-slot-minor {
    border-top: 1px dotted #edf0f2 !important;
}
.surveyor-diary-calendar .fc-daygrid-day {
    border: 1px solid #d0d4d8 !important;
    box-shadow: inset 0 0 0 1px #e4e7ea;
}
.surveyor-diary-calendar .fc-daygrid-day-frame {
    border: 1px solid #e4e7ea;
    min-height: 96px;
}
.surveyor-diary-calendar .fc-daygrid-day-number {
    padding: 6px;
}
@media (max-width: 767px) {
    .surveyor-diary-calendar {
        min-height: 420px;
    }
    .surveyor-job-due-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    .surveyor-job-footer-map {
        width: 100%;
    }
}
</style>

<div class="card mb-4" id="surveyorReviewCard" style="display:none">
    <div class="card-header d-flex flex-wrap align-items-center justify-content-between surveyor-diary-toolbar">
        <h5 class="mb-0">Report Review Notes <span id="surveyorReviewCount" class="badge bg-warning text-dark ms-1">0</span></h5>
        <button class="btn btn-sm btn-outline-light" type="button" onclick="loadSurveyorReviewNotes()">
            <i class="fad fa-refresh"></i> Refresh
        </button>
    </div>
    <div class="card-body p-3">
        <div class="surveyor-review-list table-responsive">
            <table class="table table-hover table-sm align-middle mb-0">
                <thead class="table-dark">
                    <tr>
                        <th>Client / Report</th>
                        <th>Item</th>
                        <th>Note</th>
                        <th>Status</th>
                        <th class="text-end">Actions</th>
                    </tr>
                </thead>
                <tbody id="surveyorReviewTableBody"></tbody>
            </table>
        </div>
    </div>
</div>

<div class="card mb-4">
    <div class="card-header d-flex flex-wrap align-items-center justify-content-between surveyor-diary-toolbar">
        <h5 class="mb-0">My Jobs</h5>
        <div class="d-flex flex-wrap surveyor-diary-toolbar">
            <button id="surveyorDiaryRangeDay" class="btn btn-sm btn-outline-light" type="button" onclick="setSurveyorDiaryRange('day')">Day</button>
            <button id="surveyorDiaryRangeWeek" class="btn btn-sm btn-light" type="button" onclick="setSurveyorDiaryRange('week')">Week</button>
            <button id="surveyorDiaryRangeMonth" class="btn btn-sm btn-outline-light" type="button" onclick="setSurveyorDiaryRange('month')">Month</button>
            <button class="btn btn-sm btn-outline-light" type="button" onclick="toggleSurveyorDiaryMode()">
                <i class="fad fa-table"></i> List / Calendar
            </button>
        </div>
    </div>
    <div class="card-body">
        <div class="row align-items-end gy-2 mb-3">
            <div class="col-12 col-md-6">
                <label for="surveyorStatusFilter" class="form-label">Status</label>
                <select id="surveyorStatusFilter" class="form-select" onchange="applySurveyorDiaryFilters()">
                    <option value="">All current jobs</option>
                    <option value="Not Started">Not Started</option>
                    <option value="In Progress">In Progress</option>
                    <option value="Completed">Completed</option>
                </select>
            </div>
            <div class="col-12 col-md-6 text-md-end">
                <button class="btn btn-outline-secondary btn-sm" type="button" onclick="loadAssignments()">
                    <i class="fad fa-refresh"></i> Refresh
                </button>
            </div>
        </div>

        <div id="surveyorDiaryCalendarWrap" class="surveyor-diary-calendar">
            <div id="surveyorDiaryCalendar"></div>
        </div>

        <div id="surveyorDiaryListWrap" class="surveyor-diary-list table-responsive mt-3" style="display:none">
            <table class="table table-hover table-sm align-middle surveyor-list-table">
                <colgroup>
                    <col style="width: 360px">
                    <col style="width: 190px">
                    <col style="width: 360px">
                    <col style="width: 120px">
                    <col style="width: 250px">
                </colgroup>
                <thead class="table-dark">
                    <tr>
                        <th>Client / Site</th>
                        <th>Date / Time</th>
                        <th>Items Due</th>
                        <th>Status</th>
                        <th>Actions</th>
                    </tr>
                </thead>
                <tbody id="assignmentTableBody"></tbody>
            </table>
        </div>
    </div>
</div>

<div class="modal fade" id="surveyorJobModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog modal-lg modal-dialog-scrollable">
        <div class="modal-content">
            <div class="modal-header">
                <a href="javascript:void(0)" class="sa">Job Details</a>
            </div>
            <div class="modal-body">
                <div class="surveyor-job-summary">
                    <div class="surveyor-job-summary-header">
                        <div class="d-flex flex-wrap align-items-start justify-content-between gap-2">
                            <div>
                                <h5 id="surveyorJobTitle" class="surveyor-job-client"></h5>
                                <div id="surveyorJobWhen" class="surveyor-job-meta"></div>
                            </div>
                            <span id="surveyorJobStatus" class="badge"></span>
                        </div>
                    </div>
                    <div class="surveyor-job-summary-body">
                        <div class="row gy-3">
                            <div class="col-12 col-lg-7">
                                <div class="surveyor-job-section">
                                    <div class="surveyor-job-section-title"><i class="fad fa-location-dot me-1"></i> Site Address</div>
                                    <div id="surveyorJobSite" class="surveyor-job-address"></div>
                                </div>
                            </div>
                            <div class="col-12 col-lg-5">
                                <div class="surveyor-job-section">
                                    <div class="surveyor-job-section-title"><i class="fad fa-note-sticky me-1"></i> Notes</div>
                                    <div id="surveyorJobNotes" class="surveyor-job-notes"></div>
                                </div>
                            </div>
                            <div class="col-12">
                                <div id="surveyorJobDue" class="surveyor-job-due-grid"></div>
                            </div>
                            <div class="col-12">
                                <div id="surveyorJobUiEstimate" class="surveyor-job-ui-estimate"></div>
                            </div>
                        </div>
                    </div>
                </div>
                <input type="hidden" id="surveyorJobAssignmentId">
            </div>
            <div class="modal-footer d-flex flex-wrap gap-2">
                <div id="surveyorJobMap" class="surveyor-job-footer-map"></div>
                <button type="button" class="btn btn-outline-secondary" data-mdb-dismiss="modal">Close</button>
                <button type="button" class="btn btn-info" onclick="openSurveyorMessageModalFromJob()">Message Admin</button>
                <button type="button" class="btn btn-success" id="surveyorStartButton" onclick="markStartedFromModal()">Notify Started</button>
                <button type="button" class="btn btn-danger" id="surveyorCompleteButton" onclick="markCompletedFromModal()">Notify Finished</button>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="surveyorMapModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog modal-xl modal-dialog-scrollable">
        <div class="modal-content">
            <div class="modal-header">
                <a href="javascript:void(0)" class="sa">Job Map</a>
            </div>
            <div class="modal-body">
                <input type="hidden" id="surveyorMapDestination">
                <div class="row gy-2 mb-3">
                    <div class="col-12 col-lg-8">
                        <label for="surveyorMapOrigin" class="form-label">From</label>
                        <input type="text" id="surveyorMapOrigin" class="form-control" placeholder="Optional start point">
                    </div>
                    <div class="col-12 col-lg-4 d-flex align-items-end gap-2">
                        <button type="button" class="btn btn-outline-info" onclick="useGpsForSurveyorJobMap()">GPS</button>
                        <button type="button" class="btn btn-info" onclick="renderSurveyorJobMapRoute()">Route</button>
                        <button type="button" class="btn btn-outline-secondary" onclick="renderSurveyorJobMapLocation()">Location</button>
                        <button type="button" class="btn btn-outline-info" onclick="showSurveyorPhoneNav()">Phone</button>
                        <a id="surveyorMapExternalLink" class="btn btn-secondary" target="_blank" href="#">Open</a>
                    </div>
                </div>
                <div class="ratio ratio-16x9">
                    <iframe id="surveyorMapFrame" src="" style="border:0;" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
                </div>
                <div id="surveyorMapRouteSummary" class="alert alert-light border mt-3 mb-0" style="display:none"></div>
                <div id="surveyorPhoneNavPanel" class="alert alert-light border mt-3 mb-0" style="display:none">
                    <div class="row gy-3 align-items-center">
                        <div class="col-12 col-md-auto text-center">
                            <img id="surveyorPhoneQr" src="" alt="Route QR code" width="150" height="150">
                        </div>
                        <div class="col">
                            <div class="mb-2">Scan the QR code or open the route in your preferred phone satnav.</div>
                            <div class="d-flex flex-wrap gap-2">
                                <a id="surveyorPhoneGoogle" class="btn btn-sm btn-info" target="_blank" href="#">Google Maps</a>
                                <a id="surveyorPhoneApple" class="btn btn-sm btn-outline-secondary" target="_blank" href="#">Apple Maps</a>
                                <a id="surveyorPhoneWaze" class="btn btn-sm btn-outline-secondary" target="_blank" href="#">Waze</a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="surveyorMessageModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Send Message to Admin</h5>
            </div>
            <div class="modal-body">
                <form id="surveyorMessageForm">
                    <input type="hidden" id="surveyorAssignmentId" name="assignment_id">
                    <div class="mb-3">
                        <label for="surveyorMessageContent" class="form-label">Message</label>
                        <textarea id="surveyorMessageContent" name="message" class="form-control" rows="4" required></textarea>
                    </div>
                    <div class="mb-3">
                        <label for="surveyorMessageStatus" class="form-label">Priority</label>
                        <select id="surveyorMessageStatus" name="status" class="form-select">
                            <option value="normal">Normal</option>
                            <option value="urgent">Urgent</option>
                        </select>
                    </div>
                    <button type="submit" class="btn btn-primary">Send Message</button>
                </form>
            </div>
        </div>
    </div>
</div>

<script>
let surveyorJobs = [];
let surveyorReviewNotes = [];
let surveyorCalendar = null;
let surveyorCalendarVisible = true;
let surveyorDiaryStarted = false;
let surveyorDiaryRange = 'week';
let surveyorAssignmentsLoading = false;
let surveyorAssignmentsPendingReload = false;

function startSurveyorDiary() {
    if (surveyorDiaryStarted) return;
    const wrap = document.getElementById('surveyorDiaryCalendarWrap');
    if (!wrap || wrap.offsetWidth < 500 || wrap.offsetHeight < 100) {
        setTimeout(startSurveyorDiary, 100);
        return;
    }
    surveyorDiaryStarted = true;
    initialiseSurveyorCalendar();
    loadSurveyorReviewNotes();
    loadAssignments();
    setInterval(loadSurveyorReviewNotes, 30000);
    setInterval(loadAssignments, 30000);
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', startSurveyorDiary);
} else {
    setTimeout(startSurveyorDiary, 0);
}

function initialiseSurveyorCalendar() {
    const calendarEl = document.getElementById('surveyorDiaryCalendar');
    if (!calendarEl) return;
    if (typeof FullCalendar === 'undefined') {
        calendarEl.innerHTML = '<div class="alert alert-warning">Calendar library could not be loaded.</div>';
        return;
    }
    surveyorCalendar = new FullCalendar.Calendar(calendarEl, {
        height: 500,
        themeSystem: 'bootstrap',
        scrollTime: currentSurveyorCalendarScrollTime(),
        header: {
            left: 'prev,next today',
            center: 'title',
            right: ''
        },
        defaultView: 'timeGridWeek',
        displayEventTime: true,
        slotEventOverlap: true,
        eventOverlap: true,
        eventClick: function(info) {
            openSurveyorJob(info.event.extendedProps.jobId);
        },
        viewRender: function() {
            if (surveyorDiaryStarted) {
                loadAssignments();
            }
        }
    });
    surveyorCalendar.render();
    scrollSurveyorCalendarToCurrentTime();
    setSurveyorDiaryRangeButtonState();
    setTimeout(function() {
        if (!surveyorCalendar) return;
        surveyorCalendar.changeView('timeGridWeek');
        surveyorCalendar.updateSize();
        scrollSurveyorCalendarToCurrentTime();
        setSurveyorDiaryRangeButtonState();
    }, 250);
    setTimeout(function() {
        if (!surveyorCalendar) return;
        surveyorCalendar.updateSize();
    }, 600);
    if (typeof ResizeObserver !== 'undefined') {
        const wrap = document.getElementById('surveyorDiaryCalendarWrap');
        const observer = new ResizeObserver(function() {
            if (surveyorCalendar && surveyorCalendarVisible) surveyorCalendar.updateSize();
        });
        observer.observe(wrap);
    }
}

function setSurveyorDiaryRange(range) {
    surveyorDiaryRange = range;
    setSurveyorDiaryRangeButtonState();
    if (!surveyorCalendar) return;
    const view = range === 'day' ? 'timeGridDay' : (range === 'month' ? 'dayGridMonth' : 'timeGridWeek');
    surveyorCalendar.changeView(view);
    scrollSurveyorCalendarToCurrentTime();
    loadAssignments();
}

function setSurveyorDiaryRangeButtonState() {
    const buttons = {
        day: document.getElementById('surveyorDiaryRangeDay'),
        week: document.getElementById('surveyorDiaryRangeWeek'),
        month: document.getElementById('surveyorDiaryRangeMonth')
    };
    Object.keys(buttons).forEach(key => {
        if (!buttons[key]) return;
        buttons[key].classList.remove('btn-light', 'btn-outline-light', 'btn-secondary', 'btn-outline-secondary');
        buttons[key].classList.add(key === surveyorDiaryRange ? 'btn-light' : 'btn-outline-light');
    });
}

function toggleSurveyorDiaryMode() {
    surveyorCalendarVisible = !surveyorCalendarVisible;
    document.getElementById('surveyorDiaryCalendarWrap').style.display = surveyorCalendarVisible ? '' : 'none';
    document.getElementById('surveyorDiaryListWrap').style.display = surveyorCalendarVisible ? 'none' : '';
    if (surveyorCalendarVisible && surveyorCalendar) {
        surveyorCalendar.render();
        surveyorCalendar.updateSize();
        scrollSurveyorCalendarToCurrentTime();
    }
}

function currentSurveyorCalendarScrollTime() {
    const now = new Date();
    const minutes = Math.max(0, (now.getHours() * 60) + now.getMinutes() - 60);
    const hours = Math.floor(minutes / 60);
    const mins = minutes % 60;
    return String(hours).padStart(2, '0') + ':' + String(mins).padStart(2, '0') + ':00';
}

function scrollSurveyorCalendarToCurrentTime() {
    if (!surveyorCalendar || surveyorDiaryRange === 'month' || !surveyorCalendarVisible) return;
    setTimeout(function() {
        if (typeof surveyorCalendar.scrollToTime === 'function') {
            surveyorCalendar.scrollToTime(currentSurveyorCalendarScrollTime());
        }
    }, 80);
}

function loadAssignments() {
    if (surveyorAssignmentsLoading) {
        surveyorAssignmentsPendingReload = true;
        return;
    }
    surveyorAssignmentsLoading = true;
    surveyorAssignmentsPendingReload = false;
    fetch(ROOT_URL + '/includes/surveyor/admin_get_assignments.php?' + surveyorDiaryQueryString())
        .then(response => response.json())
        .then(data => {
            surveyorJobs = data.assignments || [];
            applySurveyorDiaryFilters();
        })
        .finally(() => {
            surveyorAssignmentsLoading = false;
            if (surveyorAssignmentsPendingReload) {
                loadAssignments();
            }
        });
}

function surveyorDiaryQueryString() {
    const params = new URLSearchParams({ tenant: TENANT_URL });
    const range = surveyorDiaryVisibleRange();
    params.set('start', range.start);
    params.set('end', range.end);
    return params.toString();
}

function surveyorDiaryVisibleRange() {
    if (surveyorCalendar && surveyorCalendar.view) {
        const view = surveyorCalendar.view;
        const start = view.intervalStart || view.activeStart || view.start;
        const end = view.intervalEnd || view.activeEnd || view.end;
        if (start && end) {
            return {
                start: surveyorDateLikeToTimestamp(start),
                end: Math.max(surveyorDateLikeToTimestamp(start), surveyorDateLikeToTimestamp(end) - 1)
            };
        }
    }
    const now = new Date();
    const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const end = new Date(start);
    if (surveyorDiaryRange === 'day') {
        end.setDate(start.getDate() + 1);
    } else if (surveyorDiaryRange === 'month') {
        start.setDate(1);
        end.setMonth(start.getMonth() + 1, 1);
    } else {
        const day = start.getDay() || 7;
        start.setDate(start.getDate() - day + 1);
        end.setDate(start.getDate() + 7);
    }
    return {
        start: Math.floor(start.getTime() / 1000),
        end: Math.floor(end.getTime() / 1000) - 1
    };
}

function surveyorDateLikeToTimestamp(value) {
    if (typeof value.unix === 'function') return value.unix();
    return Math.floor(new Date(value).getTime() / 1000);
}

function applySurveyorDiaryFilters() {
    const status = document.getElementById('surveyorStatusFilter').value;
    const jobs = status ? surveyorJobs.filter(job => job.status === status) : surveyorJobs.slice();
    renderSurveyorCalendar(jobs);
    renderSurveyorList(jobs);
}

function renderSurveyorCalendar(jobs) {
    if (!surveyorCalendar) return;
    const addEvents = function() {
        surveyorCalendar.removeAllEvents();
        jobs.forEach(job => {
            const start = Number(job.start_time || job.appointment_date);
            const end = Number(job.end_time || 0);
            surveyorCalendar.addEvent({
                title: job.clientname,
                start: new Date(start * 1000),
                end: end ? new Date(end * 1000) : null,
                allDay: !job.start_time,
                className: surveyorStatusClass(job.status) + ' ' + surveyorColourClass(job.surveyor_id),
                extendedProps: { jobId: job.id }
            });
        });
    };
    if (typeof surveyorCalendar.batchRendering === 'function') {
        surveyorCalendar.batchRendering(addEvents);
    } else {
        addEvents();
    }
}

function surveyorColourClass(surveyorId) {
    const id = Number(surveyorId || 0);
    return 'surveyor-colour-' + Math.abs(id % 6);
}

function renderSurveyorList(jobs) {
    const body = document.getElementById('assignmentTableBody');
    const sorted = jobs.slice().sort((a, b) => Number(a.start_time || a.appointment_date) - Number(b.start_time || b.appointment_date));
    body.innerHTML = sorted.map(job => `
            <tr>
                <td class="surveyor-list-client">
                    ${escapeHtml(job.clientname || '')}
                    ${job.locname ? `<span class="surveyor-list-subtext">${escapeHtml(job.locname)}</span>` : ''}
                    ${job.notes ? `<span class="surveyor-diary-notes">${escapeHtml(job.notes)}</span>` : ''}
                </td>
                <td class="surveyor-list-date">${formatSurveyorJobDate(job)}</td>
                <td>${surveyorItemsDueCompact(job)}</td>
                <td><span class="badge ${surveyorStatusBadge(job.status)}">${job.status}</span></td>
                <td>
                    <div class="surveyor-list-actions">
                        <button class="btn btn-secondary btn-sm" title="View job" onclick="openSurveyorJob(${job.id})"><i class="fad fa-eye"></i> View</button>
                        ${job.status === 'Not Started' ? `<button class="btn btn-success btn-sm" title="Notify started" onclick="markStarted(${job.id})"><i class="fad fa-play"></i> Start</button>` : ''}
                        ${job.status === 'In Progress' ? `<button class="btn btn-danger btn-sm" title="Notify finished" onclick="markCompleted(${job.id})"><i class="fad fa-check"></i> Finish</button>` : ''}
                        ${job.map_query ? `<button class="btn btn-outline-secondary btn-sm" title="Show map" onclick="openSurveyorJobMapModal(${job.id})"><i class="fad fa-map-location-dot"></i> Map</button>` : ''}
                        <a class="btn btn-secondary btn-sm" title="Open client assets" href="index.php?t=includes/master&p=client.php&clientnumber=${job.clientnumber}&view=asset"><i class="fad fa-clipboard-list"></i> Assets</a>
                    </div>
                </td>
            </tr>
        `).join('');
}

function loadSurveyorReviewNotes() {
    fetch(ROOT_URL + '/includes/reports_outstanding/report_review_actions.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `action=list_surveyor&tenant=${encodeURIComponent(TENANT_URL)}`
    })
    .then(response => response.json())
    .then(data => {
        if (!data || data.ok !== true) {
            renderSurveyorReviewNotes([]);
            return;
        }
        surveyorReviewNotes = data.notes || [];
        renderSurveyorReviewNotes(surveyorReviewNotes);
    })
    .catch(() => renderSurveyorReviewNotes([]));
}

function renderSurveyorReviewNotes(notes) {
    const card = document.getElementById('surveyorReviewCard');
    const body = document.getElementById('surveyorReviewTableBody');
    const count = document.getElementById('surveyorReviewCount');
    if (!card || !body || !count) return;

    count.textContent = String(notes.length);
    card.style.display = notes.length ? '' : 'none';
    body.innerHTML = '';
    notes.forEach(note => {
        const location = [note.locname, note.subloc].filter(Boolean).join(' ');
        const itemText = [
            note.item_name || '',
            note.plantnumber ? 'Plant ' + note.plantnumber : '',
            note.serialnumber ? 'Serial ' + note.serialnumber : ''
        ].filter(Boolean).join(' / ');
        body.innerHTML += `
            <tr>
                <td>
                    ${escapeHtml(note.clientname || '')}
                    <span class="surveyor-list-subtext">Report ${escapeHtml(note.repnumber || '')}${note.insdate ? ' / ' + dateInputValue(note.insdate).split('-').reverse().join('-') : ''}</span>
                </td>
                <td>
                    ${escapeHtml(itemText || 'Item ' + note.itemnumber)}
                    ${location ? `<span class="surveyor-list-subtext">${escapeHtml(location)}</span>` : ''}
                </td>
                <td><div class="surveyor-review-note">${escapeHtml(note.admin_note || '')}</div></td>
                <td><span class="badge ${note.status === 'responded' ? 'bg-info text-white' : 'bg-warning text-dark'}">${note.status === 'responded' ? 'Responded' : 'Pending'}</span></td>
                <td class="text-end">
                    <div class="surveyor-review-actions justify-content-end">
                        <button class="btn btn-secondary btn-sm" type="button" onclick="openSurveyorReviewNote(${Number(note.repitem)})"><i class="fad fa-eye"></i> View</button>
                        <button class="btn btn-success btn-sm" type="button" onclick="completeSurveyorReviewNote(${Number(note.repitem)})"><i class="fad fa-check"></i> Complete</button>
                    </div>
                </td>
            </tr>`;
    });
}

function openSurveyorReviewNote(repitem) {
    const note = surveyorReviewNotes.find(row => Number(row.repitem) === Number(repitem));
    if (!note) return;
    const location = [note.locname, note.subloc].filter(Boolean).join(' ');
    const html = `
        <dl class="review-contact-grid mb-3">
            <dt>Client</dt><dd>${escapeHtml(note.clientname || '')}</dd>
            <dt>Report</dt><dd>${escapeHtml(note.repnumber || '')}</dd>
            <dt>Inspection</dt><dd>${note.insdate ? dateInputValue(note.insdate).split('-').reverse().join('-') : ''}</dd>
            <dt>Item</dt><dd>${escapeHtml(note.item_name || '')}</dd>
            <dt>Location</dt><dd>${escapeHtml(location || '')}</dd>
            <dt>Plant</dt><dd>${escapeHtml(note.plantnumber || '')}</dd>
            <dt>Serial</dt><dd>${escapeHtml(note.serialnumber || '')}</dd>
        </dl>
        <div class="alert alert-light border"><strong>Admin note</strong><br>${escapeHtml(note.admin_note || '').replace(/\n/g, '<br>')}</div>
        <div class="mb-3">
            <label for="surveyorReviewResponse" class="form-label">Response</label>
            <textarea id="surveyorReviewResponse" class="form-control" rows="4">${escapeHtml(note.surveyor_response || '')}</textarea>
        </div>
        <div class="d-flex flex-wrap gap-2">
            <button type="button" class="btn btn-info" onclick="respondSurveyorReviewNote(${Number(repitem)})"><i class="fad fa-reply me-1"></i>Respond</button>
            <button type="button" class="btn btn-success" onclick="completeSurveyorReviewNote(${Number(repitem)})"><i class="fad fa-check me-1"></i>Mark Complete</button>
        </div>`;
    loadModalContent(html, 'surveyorReviewNoteBox', 'modal-lg', 1060, 'true', 'Report Review Note', 'close');
}

function surveyorReviewAction(action, repitem, extra) {
    const params = new URLSearchParams(Object.assign({
        action: action,
        repitem: String(repitem),
        tenant: TENANT_URL
    }, extra || {}));
    return fetch(ROOT_URL + '/includes/reports_outstanding/report_review_actions.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: params
    }).then(response => response.json());
}

function respondSurveyorReviewNote(repitem) {
    const response = (document.getElementById('surveyorReviewResponse')?.value || '').trim();
    if (!response) {
        showResponse('warning', 'Please add a response before sending.');
        return;
    }
    surveyorReviewAction('respond', repitem, {response: response}).then(data => {
        if (!data || data.ok !== true) {
            showResponse('danger', data && data.message ? data.message : 'Unable to save response.');
            return;
        }
        showResponse('success', 'Response sent.');
        killModal('surveyorReviewNoteBox');
        loadSurveyorReviewNotes();
    }).catch(() => showResponse('danger', 'Unable to save response.'));
}

function completeSurveyorReviewNote(repitem) {
    surveyorReviewAction('complete', repitem).then(data => {
        if (!data || data.ok !== true) {
            showResponse('danger', data && data.message ? data.message : 'Unable to complete action.');
            return;
        }
        showResponse('success', 'Action marked complete.');
        killModal('surveyorReviewNoteBox');
        loadSurveyorReviewNotes();
    }).catch(() => showResponse('danger', 'Unable to complete action.'));
}

function openSurveyorJob(jobId) {
    const job = surveyorJobs.find(row => Number(row.id) === Number(jobId));
    if (!job) return;
    document.getElementById('surveyorJobAssignmentId').value = job.id;
    document.getElementById('surveyorJobTitle').textContent = job.clientname || '';
    document.getElementById('surveyorJobWhen').innerHTML = formatSurveyorJobDate(job);
    document.getElementById('surveyorJobStatus').className = 'badge ' + surveyorStatusBadge(job.status);
    document.getElementById('surveyorJobStatus').textContent = job.status || '';
    document.getElementById('surveyorJobSite').innerHTML = formatSurveyorJobAddress(job);
    document.getElementById('surveyorJobNotes').innerHTML = job.notes
        ? escapeHtml(job.notes)
        : '<span class="surveyor-job-empty">No notes added.</span>';
    document.getElementById('surveyorJobDue').innerHTML = surveyorItemsDue(job);
    document.getElementById('surveyorJobUiEstimate').innerHTML = formatSurveyorUiEstimate(job);
    document.getElementById('surveyorJobMap').innerHTML = job.map_query
        ? `<button type="button" class="btn btn-outline-info" onclick="openSurveyorJobMapModal(${job.id})"><i class="fad fa-map-location-dot me-1"></i> Open Map</button>`
        : '';
    document.getElementById('surveyorStartButton').style.display = job.status === 'Not Started' ? '' : 'none';
    document.getElementById('surveyorCompleteButton').style.display = job.status === 'In Progress' ? '' : 'none';
    new mdb.Modal(document.getElementById('surveyorJobModal')).show();
}

function openSurveyorJobMapModal(jobId) {
    const job = surveyorJobs.find(row => Number(row.id) === Number(jobId));
    if (!job || !job.map_query) return;
    document.getElementById('surveyorMapDestination').value = job.map_query;
    document.getElementById('surveyorMapOrigin').value = '';
    hideSurveyorPhoneNav();
    renderSurveyorJobMapLocation();
    new mdb.Modal(document.getElementById('surveyorMapModal')).show();
}

function renderSurveyorJobMapLocation() {
    const destination = document.getElementById('surveyorMapDestination').value;
    const frame = document.getElementById('surveyorMapFrame');
    frame.removeAttribute('srcdoc');
    frame.src = 'https://maps.google.com/maps?q=' + encodeURIComponent(destination) + '&z=14&output=embed';
    document.getElementById('surveyorMapExternalLink').href = 'https://www.google.com/maps/search/?api=1&query=' + encodeURIComponent(destination);
    setSurveyorRouteSummary('');
    hideSurveyorPhoneNav();
}

function renderSurveyorJobMapRoute() {
    const origin = document.getElementById('surveyorMapOrigin').value.trim();
    const destination = document.getElementById('surveyorMapDestination').value;
    if (!origin) {
        renderSurveyorJobMapLocation();
        return;
    }
    document.getElementById('surveyorMapExternalLink').href = 'https://www.google.com/maps/dir/?api=1&origin=' + encodeURIComponent(origin) + '&destination=' + encodeURIComponent(destination);
    hideSurveyorPhoneNav();
    calculateSurveyorRouteMetrics(origin, destination);
}

function useGpsForSurveyorJobMap() {
    if (!navigator.geolocation) {
        setSurveyorRouteSummary('GPS is not available in this browser.');
        return;
    }
    setSurveyorRouteSummary('Finding your location...');
    navigator.geolocation.getCurrentPosition(function(position) {
        document.getElementById('surveyorMapOrigin').value = position.coords.latitude.toFixed(6) + ',' + position.coords.longitude.toFixed(6);
        renderSurveyorJobMapRoute();
    }, function(error) {
        setSurveyorRouteSummary('GPS location could not be used: ' + error.message);
    }, { enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 });
}

function setSurveyorRouteSummary(message) {
    const target = document.getElementById('surveyorMapRouteSummary');
    if (!target) return;
    target.style.display = message ? '' : 'none';
    target.textContent = message || '';
}

function hideSurveyorPhoneNav() {
    const panel = document.getElementById('surveyorPhoneNavPanel');
    if (panel) panel.style.display = 'none';
}

function showSurveyorPhoneNav() {
    const destination = document.getElementById('surveyorMapDestination').value;
    const origin = document.getElementById('surveyorMapOrigin').value.trim();
    const googleUrl = origin
        ? 'https://www.google.com/maps/dir/?api=1&origin=' + encodeURIComponent(origin) + '&destination=' + encodeURIComponent(destination) + '&travelmode=driving'
        : 'https://www.google.com/maps/search/?api=1&query=' + encodeURIComponent(destination);
    const appleUrl = origin
        ? 'https://maps.apple.com/?saddr=' + encodeURIComponent(origin) + '&daddr=' + encodeURIComponent(destination) + '&dirflg=d'
        : 'https://maps.apple.com/?daddr=' + encodeURIComponent(destination) + '&dirflg=d';
    const wazeUrl = 'https://waze.com/ul?q=' + encodeURIComponent(destination) + '&navigate=yes';
    document.getElementById('surveyorPhoneGoogle').href = googleUrl;
    document.getElementById('surveyorPhoneApple').href = appleUrl;
    document.getElementById('surveyorPhoneWaze').href = wazeUrl;
    document.getElementById('surveyorPhoneQr').src = 'https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=' + encodeURIComponent(googleUrl);
    document.getElementById('surveyorPhoneNavPanel').style.display = '';
}

function parseSurveyorLatLng(value) {
    const match = String(value || '').trim().match(/^(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)$/);
    if (!match) return null;
    return { lat: parseFloat(match[1]), lon: parseFloat(match[2]) };
}

function extractSurveyorUkPostcode(value) {
    const match = String(value || '').toUpperCase().match(/\b[A-Z]{1,2}\d[A-Z\d]?\s*\d[A-Z]{2}\b/);
    return match ? match[0].replace(/\s+/, ' ') : '';
}

function geocodeSurveyorMapQuery(value) {
    const coords = parseSurveyorLatLng(value);
    if (coords) return Promise.resolve(coords);

    const postcode = extractSurveyorUkPostcode(value);
    if (postcode) {
        return fetch('https://api.postcodes.io/postcodes/' + encodeURIComponent(postcode))
            .then(response => response.ok ? response.json() : Promise.reject())
            .then(data => {
                if (data && data.result) return { lat: data.result.latitude, lon: data.result.longitude };
                return Promise.reject();
            })
            .catch(() => geocodeSurveyorMapQueryWithNominatim(value));
    }

    return geocodeSurveyorMapQueryWithNominatim(value);
}

function geocodeSurveyorMapQueryWithNominatim(value) {
    return fetch('https://nominatim.openstreetmap.org/search?format=json&limit=1&q=' + encodeURIComponent(value))
        .then(response => response.ok ? response.json() : Promise.reject())
        .then(data => {
            if (data && data[0]) return { lat: parseFloat(data[0].lat), lon: parseFloat(data[0].lon) };
            return Promise.reject();
        });
}

function calculateSurveyorRouteMetrics(origin, destination) {
    setSurveyorRouteSummary('Calculating distance and travel time...');
    Promise.all([geocodeSurveyorMapQuery(origin), geocodeSurveyorMapQuery(destination)])
        .then(points => {
            const from = points[0];
            const to = points[1];
            const url = 'https://router.project-osrm.org/route/v1/driving/' + from.lon + ',' + from.lat + ';' + to.lon + ',' + to.lat + '?overview=full&geometries=geojson';
            return fetch(url)
                .then(response => response.ok ? response.json() : Promise.reject())
                .then(data => {
                    if (!data.routes || !data.routes[0]) return Promise.reject();
                    const route = data.routes[0];
                    renderSurveyorRoutePreviewMap(from, to, route.geometry && route.geometry.coordinates ? route.geometry.coordinates : []);
                    setSurveyorRouteSummary('Approx driving distance: ' + formatSurveyorDistance(route.distance) + '. Approx travel time: ' + formatSurveyorDuration(route.duration) + '. Use Open for the full live Google route if needed.');
                });
        })
        .catch(() => {
            setSurveyorRouteSummary('Route estimate is unavailable. Use Open for live Google Maps distance and traffic.');
        });
}

function renderSurveyorRoutePreviewMap(from, to, coordinates) {
    const frame = document.getElementById('surveyorMapFrame');
    if (!frame) return;
    const route = (coordinates || []).map(point => [point[1], point[0]]);
    if (!route.length) route.push([from.lat, from.lon], [to.lat, to.lon]);
    const html = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<style>html,body,#map{height:100%;margin:0}.leaflet-container{font:12px Arial,sans-serif}</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"><\/script>
<script>
const route = ${JSON.stringify(route)};
const from = ${JSON.stringify([from.lat, from.lon])};
const to = ${JSON.stringify([to.lat, to.lon])};
const map = L.map('map', { scrollWheelZoom: false });
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '&copy; OpenStreetMap' }).addTo(map);
const line = L.polyline(route, { color: '#24445f', weight: 5, opacity: 0.9 }).addTo(map);
L.circleMarker(from, { radius: 7, color: '#24445f', fillColor: '#ffffff', fillOpacity: 1, weight: 3 }).addTo(map).bindTooltip('From');
L.marker(to).addTo(map).bindTooltip('Job location');
map.fitBounds(line.getBounds().pad(0.18));
<\/script>
</body>
</html>`;
    frame.removeAttribute('src');
    frame.srcdoc = html;
}

function formatSurveyorDistance(metres) {
    const miles = metres / 1609.344;
    return miles >= 10 ? miles.toFixed(0) + ' miles' : miles.toFixed(1) + ' miles';
}

function formatSurveyorDuration(seconds) {
    const mins = Math.round(seconds / 60);
    if (mins < 60) return mins + ' mins';
    const hours = Math.floor(mins / 60);
    const rem = mins % 60;
    return hours + 'h' + (rem ? ' ' + rem + 'm' : '');
}

function openSurveyorMessageModal(assignmentId) {
    document.getElementById('surveyorAssignmentId').value = assignmentId;
    new mdb.Modal(document.getElementById('surveyorMessageModal')).show();
}

function openSurveyorMessageModalFromJob() {
    openSurveyorMessageModal(document.getElementById('surveyorJobAssignmentId').value);
}

document.getElementById('surveyorMessageForm').addEventListener('submit', function(e) {
    e.preventDefault();
    const formData = new FormData(this);
    fetch(ROOT_URL + '/includes/surveyor/send_message_surveyor.php?tenant=' + TENANT_URL, {
        method: 'POST',
        body: formData
    })
    .then(response => response.text())
    .then(data => {
        if (data === 'success') {
            showResponse('success', 'Message sent successfully.');
            document.getElementById('surveyorMessageContent').value = '';
            killModal('surveyorMessageModal');
        } else {
            showResponse('danger', 'Failed to send message.');
        }
    });
});

function markStartedFromModal() {
    markStarted(document.getElementById('surveyorJobAssignmentId').value);
}

function markCompletedFromModal() {
    markCompleted(document.getElementById('surveyorJobAssignmentId').value);
}

function markStarted(assignmentId) {
    updateSurveyorAssignmentStatus(assignmentId, 'started', 'Assignment marked as started.');
}

function markCompleted(assignmentId) {
    updateSurveyorAssignmentStatus(assignmentId, 'completed', 'Assignment marked as completed.');
}

function updateSurveyorAssignmentStatus(assignmentId, action, message) {
    fetch(ROOT_URL + '/includes/surveyor/admin_mark_assignments.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `assignment_id=${assignmentId}&action=${action}&tenant=${TENANT_URL}`
    })
    .then(response => response.text())
    .then(data => {
        if (data === 'success') {
            showResponse('success', message);
            fetch(ROOT_URL + '/includes/surveyor/send_admin_notification.php', {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: `assignment_id=${assignmentId}&action=${action}&tenant=${TENANT_URL}`
            });
            killModal('surveyorJobModal');
            loadAssignments();
        } else {
            showResponse('danger', 'Error updating assignment.');
        }
    });
}

function surveyorStatusBadge(status) {
    if (status === 'Completed') return 'bg-success';
    if (status === 'In Progress') return 'bg-warning';
    return 'bg-danger';
}

function surveyorStatusClass(status) {
    if (status === 'Completed') return 'success-color-dark';
    if (status === 'In Progress') return 'warning-color-dark';
    return 'danger-color-dark';
}

function surveyorItemsDue(job) {
    return `
        <div class="surveyor-job-due-tile">
            <span class="surveyor-job-due-number text-danger">${Number(job.overdue || 0)}</span>
            <small>Overdue</small>
        </div>
        <div class="surveyor-job-due-tile">
            <span class="surveyor-job-due-number text-warning">${Number(job.due_today || 0)}</span>
            <small>Today</small>
        </div>
        <div class="surveyor-job-due-tile">
            <span class="surveyor-job-due-number text-primary">${Number(job.due_this_week || 0)}</span>
            <small>This week</small>
        </div>
        <div class="surveyor-job-due-tile">
            <span class="surveyor-job-due-number text-info">${Number(job.due_this_month || 0)}</span>
            <small>This month</small>
        </div>`;
}

function surveyorItemsDueCompact(job) {
    return `<div class="surveyor-due-chips">
        <span class="surveyor-due-chip text-danger">Overdue ${Number(job.overdue || 0)}</span>
        <span class="surveyor-due-chip text-warning">Today ${Number(job.due_today || 0)}</span>
        <span class="surveyor-due-chip text-primary">Week ${Number(job.due_this_week || 0)}</span>
        <span class="surveyor-due-chip text-info">Month ${Number(job.due_this_month || 0)}</span>
    </div>`;
}

function formatSurveyorUiEstimate(job) {
    const units = Number(job.ui_units_due || 0);
    const missing = Number(job.ui_missing_due || 0);
    const parts = [`<i class="fad fa-stopwatch me-1"></i> Estimated inspection time: <strong>${formatSurveyorUiUnits(units)} UI</strong> (${formatSurveyorMinutes(units * 15)}).`];
    if (missing > 0) parts.push(`${missing} due/overdue item${missing === 1 ? '' : 's'} do not have a UI value.`);
    return parts.join(' ');
}

function formatSurveyorUiUnits(units) {
    return Number(units || 0).toFixed(2).replace(/\.?0+$/, '');
}

function formatSurveyorMinutes(minutes) {
    minutes = Math.round(Number(minutes || 0));
    if (minutes < 60) return minutes + ' mins';
    const hours = Math.floor(minutes / 60);
    const rem = minutes % 60;
    return hours + 'h' + (rem ? ' ' + rem + 'm' : '');
}

function formatSurveyorJobDate(job) {
    const date = dateInputValue(job.appointment_date).split('-').reverse().join('-');
    const start = timeInputValue(job.start_time);
    const end = timeInputValue(job.end_time);
    return `<span><i class="fad fa-calendar-day me-1"></i>${date}</span>${start ? `<span><i class="fad fa-clock me-1"></i>${start}${end ? ' - ' + end : ''}</span>` : ''}`;
}

function formatSurveyorJobAddress(job) {
    const parts = [];
    if (job.locname) parts.push(`<strong>${escapeHtml(job.locname)}</strong>`);
    const address = job.site_address || job.map_query || '';
    if (address) parts.push(escapeHtml(address));
    if (!parts.length) return '<span class="surveyor-job-empty">No site address available.</span>';
    return parts.join('<br>');
}

function dateInputValue(timestamp) {
    if (!timestamp) return '';
    const date = new Date(Number(timestamp) * 1000);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    return `${year}-${month}-${day}`;
}

function timeInputValue(timestamp) {
    if (!timestamp) return '';
    const date = new Date(Number(timestamp) * 1000);
    return date.toTimeString().slice(0, 5);
}

function escapeHtml(value) {
    return String(value || '').replace(/[&<>"']/g, function(chr) {
        return ({'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;'}[chr]);
    });
}
</script>
