<style>

*, *::before, *::after { box-sizing: border-box; }

h2   { margin-bottom: .4rem; font-size: 1.4rem; color: #1a1a2e; letter-spacing: -.3px; }
p.subtitle { color: #888; margin-bottom: 1.5rem; font-size: 13px; }

/* ── Stats ── */
.stats { display: flex; gap: 12px; margin-bottom: 1.4rem; flex-wrap: wrap; }
.stat  { background: #fff; border: 1px solid #ece8f5; border-radius: 10px; padding: 12px 20px; box-shadow: 0 1px 4px rgba(156,72,147,.07); }
.stat-label { font-size: 11px; color: #999; text-transform: uppercase; letter-spacing: .5px; }
.stat-value { font-size: 22px; font-weight: 700; color: #1a1a2e; }

/* ── Toolbar ── */
.toolbar-row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 1rem;
    flex-wrap: wrap;
}
.toolbar-left { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.toolbar-right { display: flex; align-items: flex-end; gap: 10px; flex-shrink: 0; }

.fg { display: flex; flex-direction: column; gap: 4px; }
.fg label { font-size: 11px; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: .4px; }
.fg input, .fg select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 7px; font-size: 13px; background: #fff; transition: border-color .15s; }
.fg input:focus, .fg select:focus { outline: none; border-color: #9C4893; }

.btn { padding: 8px 16px; border: 1px solid #ddd; border-radius: 7px; cursor: pointer; background: #fff; font-size: 13px; height: 36px; align-self: flex-end; transition: all .15s; font-weight: 500; }
.btn:hover { border-color: #9C4893; color: #9C4893; }
.btn.primary { background: #9C4893; color: #fff; border-color: #9C4893; }
.btn.primary:hover { background: #7a3572; }

/* ── Generate DN Button ── */
.btn-dn-main {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 20px;
    background: #27ae60;
    color: #fff;
    border: none;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    transition: background .15s;
    white-space: nowrap;
    height: 38px;
    box-shadow: 0 2px 8px rgba(39,174,96,.25);
}
.btn-dn-main:hover { background: #219150; }
.btn-dn-main:disabled { background: #aaa; box-shadow: none; cursor: not-allowed; }
.btn-dn-main .dn-badge {
    background: rgba(255,255,255,.3);
    border-radius: 99px;
    min-width: 20px;
    height: 20px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    padding: 0 6px;
    font-weight: 700;
}

/* ── Table ── */
.table-wrap { border-radius: 10px; overflow: hidden; border: 1px solid #ece8f5; box-shadow: 0 2px 12px rgba(156,72,147,.07); }
table  { width: 100%; border-collapse: collapse; background: #fff; }
thead th { background: #9C4893; padding: 11px 14px; text-align: left; font-size: 12px; color: #fff; font-weight: 600; letter-spacing: .4px; text-transform: uppercase; }
tbody td { padding: 10px 14px; border-top: 1px solid #f4f0f9; font-size: 13px; color: #333; vertical-align: middle; }
tbody tr:hover { background: #fdf8fd; }
tbody tr.stripe { background: #faf7fd; }
tbody tr.stripe:hover { background: #f4eef9; }

.no-data { padding: 2.5rem; text-align: center; color: #bbb; font-size: 14px; }

/* Placeholder state */
.placeholder-state {
    padding: 3.5rem 2rem;
    text-align: center;
}
.placeholder-state .ph-icon {
    font-size: 48px;
    margin-bottom: 14px;
    display: block;
    opacity: .55;
}
.placeholder-state .ph-title {
    font-size: 16px;
    font-weight: 600;
    color: #888;
    margin-bottom: 6px;
}
.placeholder-state .ph-sub {
    font-size: 13px;
    color: #bbb;
}

/* ── Badges ── */
.badge { padding: 3px 10px; border-radius: 99px; font-size: 11px; font-weight: 600; }
.badge-pending   { background: #FFF3CD; color: #856404; }
.badge-partial   { background: #FFE5B4; color: #7a4a00; }
.badge-allocated { background: #D4EDDA; color: #155724; }
.badge-shipped   { background: #E2E3E5; color: #383d41; }

.stock-ok   { color: #27ae60; font-weight: 700; }
.stock-low  { color: #e67e22; font-weight: 700; }
.stock-zero { color: #c0392b; font-weight: 700; }

/* Alloc bar */
.alloc-bar-wrap { width: 70px; height: 5px; background: #eee; border-radius: 3px; margin-top: 3px; display:inline-block; vertical-align: middle; }
.alloc-bar { height: 100%; border-radius: 3px; background: #9C4893; }

/* ── Action buttons ── */
.btn-action { padding: 4px 11px; border: none; border-radius: 5px; cursor: pointer; font-size: 11px; font-weight: 600; letter-spacing: .2px; transition: all .15s; white-space: nowrap; }
.btn-allocate { background: #5B4FCF; color: #fff; }
.btn-allocate:hover { background: #4a3eb8; }
.btn-edit-alloc { background: #e67e22; color: #fff; margin-left: 4px; }
.btn-edit-alloc:hover { background: #ca6f1e; }

/* ── Pagination ── */
.pag-wrap { display: flex; justify-content: space-between; align-items: center; padding: 14px 0; flex-wrap: wrap; gap: 10px; }
.pag { display: flex; gap: 5px; flex-wrap: wrap; }
.pag a, .pag span { padding: 6px 11px; border: 1px solid #dee2e6; border-radius: 5px; font-size: 13px; cursor: pointer; background: #fff; color: #495057; text-decoration: none; transition: all .15s; }
.pag a:hover { background: #9C4893; color: #fff; border-color: #9C4893; }
.pag .active  { background: #9C4893; color: #fff; border-color: #9C4893; font-weight: 600; }
.pag .disabled { opacity: .4; pointer-events: none; }

/* ── Modal shared ── */
.modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 9999; align-items: center; justify-content: center; }
.modal-overlay.open { display: flex; }
.modal-box { background: #fff; border-radius: 12px; width: 580px; max-width: 96vw; box-shadow: 0 12px 40px rgba(0,0,0,.22); overflow: hidden; }
.modal-header { padding: 16px 20px; display: flex; justify-content: space-between; align-items: center; background: #27ae60; }
.modal-header h3 { margin: 0; font-size: 15px; color: #fff; font-weight: 700; }
.modal-header .close-btn { background: none; border: none; color: #fff; font-size: 20px; cursor: pointer; opacity: .8; line-height: 1; }
.modal-header .close-btn:hover { opacity: 1; }
.modal-body   { padding: 20px; }
.modal-footer { padding: 14px 20px; border-top: 1px solid #f0f0f0; display: flex; justify-content: flex-end; gap: 10px; }
.modal-footer button { padding: 9px 22px; border-radius: 7px; font-size: 14px; font-weight: 600; cursor: pointer; border: none; transition: background .15s; }
.btn-cancel  { background: #f0f0f0; color: #555; }
.btn-cancel:hover { background: #e0e0e0; }
.btn-confirm-green { background: #27ae60; color: #fff; display: flex; align-items: center; gap: 7px; }
.btn-confirm-green:hover { background: #219150; }
.btn-confirm-green:disabled { background: #ccc; cursor: not-allowed; }

/* Info banner */
.info-banner { background: #eafaf1; border: 1px solid #a9dfbf; border-radius: 8px; padding: 10px 14px; margin-bottom: 14px; font-size: 12.5px; color: #1e8449; display: flex; gap: 8px; align-items: flex-start; line-height: 1.5; }
.info-banner .icon { font-size: 15px; flex-shrink: 0; margin-top: 1px; }
.info-banner.warn-banner { background: #fef9e7; border-color: #f9ca3f; color: #856404; }

/* Info grid */
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 20px; background: #f9f4f9; border-radius: 8px; padding: 14px; border: 1px solid #e8d8e8; margin-bottom: 16px; }
.info-grid .ii { display: flex; flex-direction: column; gap: 2px; }
.info-grid .il { font-size: 11px; color: #888; text-transform: uppercase; font-weight: 600; }
.info-grid .iv { font-size: 14px; font-weight: 600; color: #333; }

/* DN lines preview */
.dn-preview-wrap { background: #f7fdf9; border: 1px solid #a9dfbf; border-radius: 8px; overflow: hidden; margin-bottom: 16px; max-height: 320px; overflow-y: auto; }
.dn-preview-title { font-size: 12px; font-weight: 700; color: #1e8449; text-transform: uppercase; letter-spacing: .4px; padding: 8px 10px 0; }
.dn-preview { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.dn-preview th { background: #eafaf1; padding: 7px 10px; text-align: left; font-size: 11px; text-transform: uppercase; color: #1e8449; font-weight: 700; border-bottom: 2px solid #a9dfbf; position: sticky; top: 0; }
.dn-preview td { padding: 7px 10px; border-bottom: 1px solid #f0faf4; }
.dn-preview tr:last-child td { border-bottom: none; }
.dn-preview .qty-cell { font-weight: 700; color: #27ae60; }

/* Ship method */
.dn-ship-section { margin-bottom: 4px; }
.dn-ship-section label { display: block; font-size: 12px; font-weight: 600; color: #555; margin-bottom: 6px; text-transform: uppercase; }
.dn-ship-section select { width: 100%; padding: 9px 12px; border: 2px solid #ccc; border-radius: 7px; font-size: 14px; transition: border-color .15s; }
.dn-ship-section select:focus { outline: none; border-color: #27ae60; }

/* Spinner */
.spinner { width: 14px; height: 14px; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; display: none; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Toast */
#toast { position: fixed; bottom: 24px; right: 24px; padding: 12px 22px; border-radius: 8px; font-size: 14px; font-weight: 500; color: #fff; z-index: 99999; opacity: 0; pointer-events: none; transition: opacity .3s; box-shadow: 0 4px 16px rgba(0,0,0,.2); }
#toast.show    { opacity: 1; }
#toast.success { background: #27ae60; }
#toast.error   { background: #c0392b; }

/* Allocate modal (purple) */
.modal-header.purple { background: #9C4893; }
.btn-confirm-purple { background: #9C4893; color: #fff; display: flex; align-items: center; gap: 7px; }
.btn-confirm-purple:hover { background: #7a3572; }
.btn-confirm-purple:disabled { background: #ccc; cursor: not-allowed; }
.alloc-preview { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.alloc-preview th { background: #f0eaf5; padding: 7px 10px; text-align: left; font-size: 11px; text-transform: uppercase; color: #9C4893; font-weight: 700; border-bottom: 2px solid #e0d0e0; }
.alloc-preview td { padding: 7px 10px; border-bottom: 1px solid #f4f0f9; }
.alloc-preview tr:last-child td { border-bottom: none; }
.alloc-preview .will-get  { color: #27ae60; font-weight: 700; }
.alloc-preview .wont-get  { color: #ccc; }
.alloc-preview .this-line { background: #fdf4fc; }
.qty-section { margin-bottom: 4px; }
.qty-section label { display: block; font-size: 12px; font-weight: 600; color: #555; margin-bottom: 6px; text-transform: uppercase; }
.qty-section input { width: 100%; padding: 10px 14px; border: 2px solid #ccc; border-radius: 7px; font-size: 16px; font-weight: 600; transition: border-color .15s; }
.qty-section input:focus { outline: none; border-color: #9C4893; }
.qty-hint  { margin-top: 6px; font-size: 12px; color: #888; }
.qty-error { color: #c0392b; font-size: 12px; margin-top: 5px; display: none; }
.info-banner.purple-banner { background: #f9f4fb; border-color: #dbbee0; color: #6a2d80; }

/* Edit allocation modal (orange) */
.modal-header.orange { background: #e67e22; }
.btn-confirm-orange { background: #e67e22; color: #fff; display: flex; align-items: center; gap: 7px; }
.btn-confirm-orange:hover { background: #ca6f1e; }
.btn-confirm-orange:disabled { background: #ccc; cursor: not-allowed; }
.info-banner.orange-banner { background: #fef6ec; border-color: #f5cba7; color: #784212; }
.qty-section input.orange-focus:focus { border-color: #e67e22; }

/* Filter active indicator */
.fg input.filter-active,
.fg select.filter-active {
    border-color: #9C4893;
    background: #fdf8fd;
}

/* DN loading overlay inside modal */
.dn-loading { text-align: center; padding: 2rem; color: #888; font-size: 14px; }
</style>

<h2>Ready Orders</h2>
<p class="subtitle">Manufactured items — allocate stock sequentially by part &amp; date, then generate a delivery note for all allocated lines.</p>

<!-- Stats -->
<div class="stats">
    <div class="stat"><div class="stat-label">Total Lines</div><div class="stat-value" id="stat-total">0</div></div>
    <div class="stat"><div class="stat-label">Total Qty Demanded</div><div class="stat-value" id="stat-qty">0</div></div>
    <div class="stat"><div class="stat-label">Showing</div><div class="stat-value" id="stat-range" style="font-size:15px;padding-top:5px">—</div></div>
    <div class="stat"><div class="stat-label">Pending Allocation</div><div class="stat-value" id="stat-pending" style="color:#856404">0</div></div>
    <div class="stat"><div class="stat-label">Fully Allocated</div><div class="stat-value" id="stat-allocated" style="color:#27ae60">0</div></div>
</div>

<!-- Toolbar row: filters left, Generate DN button right -->
<div class="toolbar-row">
    <div class="toolbar-left">
        <div class="fg"><label>Ref Sales Code</label><input type="text" id="f-ref"  placeholder="Search ref…"      oninput="debounceLoad()"></div>
        <div class="fg"><label>Customer Code</label><input  type="text" id="f-code" placeholder="Search customer…" oninput="debounceLoad()"></div>
        <div class="fg"><label>Part Number</label><input    type="text" id="f-part" placeholder="Search part…"     oninput="debounceLoad()"></div>
        <div class="fg"><label>Date From</label><input type="date" id="f-date-from" onchange="loadOrders()"></div>
        <div class="fg"><label>Date To</label><input   type="date" id="f-date-to"   onchange="loadOrders()"></div>
        <div class="fg">
            <label>Status</label>
            <select id="f-status" onchange="loadOrders()">
                <option value="">All</option>
                <option value="pending">Pending</option>
                <option value="allocated">Allocated</option>
                <option value="partial">Partial</option>
            </select>
        </div>
        <div class="fg">
            <label>Per page</label>
            <select id="per-page" onchange="loadOrders()">
                <option value="100" selected>100</option>
                <option value="150">150</option>
                <option value="200">200</option>
            </select>
        </div>
        <button class="btn" onclick="resetFilters()">↺ Reset</button>
        <button class="btn primary" onclick="loadOrders()">Search</button>
    </div>

    <!-- Generate Delivery Note — top right -->
    <div class="toolbar-right">
        <button class="btn-dn-main" id="btn-bulk-dn" onclick="openDnModal()" disabled>
            🚚 Generate Delivery Note
            <span class="dn-badge" id="dn-alloc-badge">0</span>
        </button>
    </div>
</div>

<!-- Table -->
<div class="table-wrap">
<table>
    <thead>
        <tr>
            <th>#</th>
            <th>Part Number</th>
            <th>Ref Sales Code</th>
            <th>Customer</th>
            <th>Req Date</th>
            <th>Req Qty</th>
            <th>Allocated</th>
            <th>Remaining</th>
            <th>Stock</th>
            <th>Status</th>
            <th>Actions</th>
        </tr>
    </thead>
    <tbody id="orders-body">
        <tr><td colspan="11" class="no-data">
            <div class="placeholder-state">
                <span class="ph-icon">🔍</span>
                <div class="ph-title">Please select a filter to show data</div>
                <div class="ph-sub">Use the filters above to search by ref, customer, part number, date range, or status.</div>
            </div>
        </td></tr>
    </tbody>
</table>
</div>

<!-- Pagination -->
<div class="pag-wrap" id="pag-wrap" style="display:none">
    <div style="font-size:13px;color:#666">Page <strong id="pag-info">1 of 1</strong></div>
    <div class="pag" id="pag-controls"></div>
</div>

<!-- ══ ALLOCATE STOCK MODAL ══ -->
<div class="modal-overlay" id="alloc-modal-overlay">
    <div class="modal-box">
        <div class="modal-header purple">
            <h3>📦 Allocate Stock</h3>
            <button class="close-btn" onclick="closeAllocModal()">✕</button>
        </div>
        <div class="modal-body">
            <div class="info-banner purple-banner">
                <span class="icon">ℹ️</span>
                <span>Stock fills the oldest demand lines first. Any surplus automatically cascades to later lines for the same part.</span>
            </div>
            <div class="info-grid" id="alloc-info-grid"></div>

            <div id="alloc-cascade-wrap" style="display:none;margin-bottom:16px">
                <div style="font-size:12px;font-weight:700;color:#9C4893;text-transform:uppercase;letter-spacing:.4px;margin-bottom:6px">Sequential Allocation Preview</div>
                <table class="alloc-preview">
                    <thead><tr><th>Line</th><th>Ref</th><th>Req Date</th><th>Needs</th><th>Will Allocate</th></tr></thead>
                    <tbody id="alloc-cascade-body"></tbody>
                </table>
            </div>

            <div class="qty-section">
                <label>Quantity to Allocate</label>
                <input type="number" id="alloc-qty-input" min="1" placeholder="Enter qty…">
                <div class="qty-hint">Available stock: <strong id="alloc-stock">—</strong> &nbsp;|&nbsp; This line needs: <strong id="alloc-need">—</strong></div>
                <div class="qty-error" id="alloc-qty-error">Enter a valid quantity (1 – <span id="alloc-max-lbl">—</span>).</div>
            </div>
        </div>
        <div class="modal-footer">
            <button class="btn-cancel" onclick="closeAllocModal()">Cancel</button>
            <button class="btn-confirm-purple" id="confirm-alloc-btn" onclick="confirmAllocate()">
                <span class="spinner" id="alloc-spinner"></span>Allocate
            </button>
        </div>
    </div>
</div>

<!-- ══ EDIT ALLOCATION MODAL ══ -->
<div class="modal-overlay" id="edit-alloc-modal-overlay">
    <div class="modal-box">
        <div class="modal-header orange">
            <h3>✏️ Edit Allocation</h3>
            <button class="close-btn" onclick="closeEditAllocModal()">✕</button>
        </div>
        <div class="modal-body">
            <div class="info-banner orange-banner">
                <span class="icon">⚠️</span>
                <span>Changing the allocation will adjust inventory stock accordingly. Setting to <strong>0</strong> will fully un-allocate this line.</span>
            </div>
            <div class="info-grid" id="edit-alloc-info-grid"></div>

            <div class="qty-section">
                <label>New Allocated Quantity</label>
                <input type="number" id="edit-alloc-qty-input" min="0" placeholder="Enter new qty…" class="orange-focus">
                <div class="qty-hint">Currently allocated: <strong id="edit-alloc-current">—</strong> &nbsp;|&nbsp; Max (stock + current): <strong id="edit-alloc-max">—</strong></div>
                <div class="qty-error" id="edit-alloc-qty-error">Enter a valid quantity (0 – <span id="edit-alloc-max-lbl">—</span>).</div>
            </div>
        </div>
        <div class="modal-footer">
            <button class="btn-cancel" onclick="closeEditAllocModal()">Cancel</button>
            <button class="btn-confirm-orange" id="confirm-edit-alloc-btn" onclick="confirmEditAllocate()">
                <span class="spinner" id="edit-alloc-spinner"></span>Save Changes
            </button>
        </div>
    </div>
</div>

<!-- ══ DELIVERY NOTE MODAL ══ -->
<div class="modal-overlay" id="dn-modal-overlay">
    <div class="modal-box">
        <div class="modal-header">
            <h3>🚚 Generate Delivery Note</h3>
            <button class="close-btn" onclick="closeDnModal()">✕</button>
        </div>
        <div class="modal-body" id="dn-modal-body">
            <div class="dn-loading">⏳ Loading all allocated lines…</div>
        </div>
        <div class="modal-footer">
            <button class="btn-cancel" onclick="closeDnModal()">Cancel</button>
            <button class="btn-confirm-green" id="confirm-dn-btn" onclick="confirmDeliveryNote()" disabled>
                <span class="spinner" id="dn-spinner"></span>Generate DN
            </button>
        </div>
    </div>
</div>

<div id="toast"></div>

<script>
var FILTER_KEY = 'readyorders_filters';

var currentPage  = 1;
var debTimer     = null;
var activeRow    = null;
var editActiveRow= null;
var allRowsCache = [];
var dnAllLines   = []; // All allocated lines across ALL pages for current filter

// ── Filter persistence ──────────────────────────────────────────────────────

function saveFilters() {
    var state = {
        ref      : document.getElementById('f-ref').value,
        code     : document.getElementById('f-code').value,
        part     : document.getElementById('f-part').value,
        dateFrom : document.getElementById('f-date-from').value,
        dateTo   : document.getElementById('f-date-to').value,
        status   : document.getElementById('f-status').value,
        perPage  : document.getElementById('per-page').value,
        page     : currentPage
    };
    try { sessionStorage.setItem(FILTER_KEY, JSON.stringify(state)); } catch(e) {}
    highlightActiveFilters();
}

function restoreFilters() {
    try {
        var raw = sessionStorage.getItem(FILTER_KEY);
        if (!raw) return false;
        var state = JSON.parse(raw);
        if (state.ref      !== undefined) document.getElementById('f-ref').value       = state.ref;
        if (state.code     !== undefined) document.getElementById('f-code').value      = state.code;
        if (state.part     !== undefined) document.getElementById('f-part').value      = state.part;
        if (state.dateFrom !== undefined) document.getElementById('f-date-from').value = state.dateFrom;
        if (state.dateTo   !== undefined) document.getElementById('f-date-to').value   = state.dateTo;
        if (state.status   !== undefined) document.getElementById('f-status').value    = state.status;
        if (state.perPage  !== undefined) document.getElementById('per-page').value    = state.perPage;
        if (state.page     !== undefined) currentPage = parseInt(state.page) || 1;
        highlightActiveFilters();
        return hasFilters();
    } catch(e) { return false; }
}

function clearSavedFilters() {
    try { sessionStorage.removeItem(FILTER_KEY); } catch(e) {}
}

function highlightActiveFilters() {
    ['f-ref','f-code','f-part','f-date-from','f-date-to','f-status'].forEach(function(id) {
        var el = document.getElementById(id);
        if (!el) return;
        el.classList.toggle('filter-active', !!(el.value && el.value !== ''));
    });
}

function hasFilters() {
    return document.getElementById('f-ref').value.trim()       !== '' ||
           document.getElementById('f-code').value.trim()      !== '' ||
           document.getElementById('f-part').value.trim()      !== '' ||
           document.getElementById('f-date-from').value        !== '' ||
           document.getElementById('f-date-to').value          !== '' ||
           document.getElementById('f-status').value           !== '';
}

function getParams(overrides) {
    var base = {
        current_page : currentPage,
        per_page     : document.getElementById('per-page').value,
        ref          : document.getElementById('f-ref').value.trim(),
        code         : document.getElementById('f-code').value.trim(),
        part         : document.getElementById('f-part').value.trim(),
        date_from    : document.getElementById('f-date-from').value,
        date_to      : document.getElementById('f-date-to').value,
        status_filter: document.getElementById('f-status').value,
    };
    return Object.assign(base, overrides || {});
}

function debounceLoad() {
    saveFilters();
    clearTimeout(debTimer);
    debTimer = setTimeout(function(){ currentPage = 1; loadOrders(false); }, 400);
}

// ── Placeholder ─────────────────────────────────────────────────────────────

function showPlaceholder() {
    var tbody = document.getElementById('orders-body');
    tbody.innerHTML =
        '<tr><td colspan="11" class="no-data">' +
        '<div class="placeholder-state">' +
            '<span class="ph-icon">🔍</span>' +
            '<div class="ph-title">Please select a filter to show data</div>' +
            '<div class="ph-sub">Use the filters above to search by ref, customer, part number, date range, or status.</div>' +
        '</div>' +
        '</td></tr>';
    document.getElementById('pag-wrap').style.display = 'none';
    document.getElementById('stat-total').textContent     = '0';
    document.getElementById('stat-qty').textContent       = '0';
    document.getElementById('stat-range').textContent     = '—';
    document.getElementById('stat-pending').textContent   = '0';
    document.getElementById('stat-allocated').textContent = '0';
    document.getElementById('dn-alloc-badge').textContent = '0';
    document.getElementById('btn-bulk-dn').disabled = true;
    allRowsCache = [];
    dnAllLines   = [];
}

// ── Load orders (paged view) ─────────────────────────────────────────────────

function loadOrders(resetPage) {
    if (resetPage !== false) currentPage = 1;
    saveFilters();

    if (!hasFilters()) { showPlaceholder(); return; }

    var tbody = document.getElementById('orders-body');
    tbody.innerHTML = '<tr><td colspan="11" class="no-data">Loading…</td></tr>';

    $.ajax({
        url      : site_url + '/modules/readyorders/index.php?page=ajax_readyorders_list&action=get_ready_orders',
        method   : 'GET',
        data     : getParams(),
        dataType : 'json',
        success  : function(res) {
            if (!res.success) {
                tbody.innerHTML = '<tr><td colspan="11" class="no-data" style="color:red">' + (res.message || 'Error') + '</td></tr>';
                return;
            }
            allRowsCache = res.data || [];
            renderRows(allRowsCache);
            updateStats(allRowsCache, res.pagination, res.summary);
            renderPagination(res.pagination);
            updateDnBadge(res.summary);
        },
        error: function(xhr, status, error) {
            tbody.innerHTML = '<tr><td colspan="11" class="no-data" style="color:red">Network error: ' + error + '</td></tr>';
        }
    });
}

// ── Render rows ──────────────────────────────────────────────────────────────

function renderRows(rows) {
    var tbody = document.getElementById('orders-body');
    if (!rows.length) {
        tbody.innerHTML = '<tr><td colspan="11" class="no-data">No demand lines found matching your filters.</td></tr>';
        return;
    }

    var offset   = (currentPage - 1) * parseInt(document.getElementById('per-page').value);
    var lastPart = null;
    var stripeIdx= -1;
    var html     = '';

    rows.forEach(function(r, i) {
        var partChanged = (r.part_number !== lastPart);
        if (partChanged) { lastPart = r.part_number; stripeIdx++; }

        var stripeClass = (stripeIdx % 2 === 1) ? 'stripe' : '';
        var allocated   = Number(r.allocated_qty || 0);
        var reqQty      = Number(r.req_qty || 0);
        var remaining   = reqQty - allocated;
        var stock       = Number(r.quantity_in_stock || 0);
        var pct         = reqQty > 0 ? Math.min(100, Math.round(allocated / reqQty * 100)) : 0;
        var stockCls    = stock <= 0 ? 'stock-zero' : (stock < reqQty ? 'stock-low' : 'stock-ok');
        var encoded     = encodeURIComponent(JSON.stringify(r));

        var actions = '';
        // Allocate button: only when stock available and still needs more
        if (r.is_shipped != 1 && stock > 0 && allocated < reqQty) {
            actions += '<button class="btn-action btn-allocate" onclick="openAllocModal(\'' + encoded + '\')" title="Allocate stock">Allocate</button>';
        }
        // Edit button: whenever there is any allocated qty (not yet shipped)
        if (r.is_shipped != 1 && allocated > 0) {
            actions += '<button class="btn-action btn-edit-alloc" onclick="openEditAllocModal(\'' + encoded + '\')" title="Edit allocation">Edit</button>';
        }
        if (!actions) actions = '<span style="color:#ddd;font-size:11px">—</span>';

        var allocCell = allocated + ' <div class="alloc-bar-wrap"><div class="alloc-bar" style="width:' + pct + '%"></div></div>';

        html += '<tr class="' + stripeClass + '" id="row-' + r.id + '">' +
            '<td style="color:#ccc;font-size:11px">'   + (offset + i + 1) + '</td>' +
            '<td style="font-weight:700;color:#1a1a2e">' + esc(r.part_number   || '—') + '</td>' +
            '<td>' + esc(r.RefSalesCode    || '—') + '</td>' +
            '<td>' + esc(r.customer_code   || '—') + '</td>' +
            '<td>' + esc(r.req_date        || '—') + '</td>' +
            '<td style="font-weight:600">'  + reqQty + '</td>' +
            '<td>' + allocCell + '</td>' +
            '<td style="color:' + (remaining > 0 ? '#c0392b' : '#27ae60') + ';font-weight:700">' + remaining + '</td>' +
            '<td class="' + stockCls + '">' + stock + '</td>' +
            '<td>' + statusBadge(r) + '</td>' +
            '<td style="white-space:nowrap">' + actions + '</td>' +
        '</tr>';
    });

    tbody.innerHTML = html;
}

function statusBadge(r) {
    var allocated = Number(r.allocated_qty || 0);
    var reqQty    = Number(r.req_qty || 0);
    if (r.is_shipped == 1) return '<span class="badge badge-shipped">Shipped</span>';
    if (allocated >= reqQty && reqQty > 0) return '<span class="badge badge-allocated">Allocated ✓</span>';
    if (allocated > 0) return '<span class="badge badge-partial">Partial</span>';
    return '<span class="badge badge-pending">Pending</span>';
}

function updateStats(rows, pag, summary) {
    document.getElementById('stat-total').textContent = pag ? pag.total_records : rows.length;
    document.getElementById('stat-qty').textContent   = rows.reduce(function(s,r){ return s + Number(r.req_qty||0); }, 0);
    if (pag) document.getElementById('stat-range').textContent = pag.showing_from + ' – ' + pag.showing_to;
    if (summary) {
        document.getElementById('stat-pending').textContent   = summary.pending   || 0;
        document.getElementById('stat-allocated').textContent = summary.allocated || 0;
    }
}

function updateDnBadge(summary) {
    var allocCount = summary ? (parseInt(summary.allocated || 0) + parseInt(summary.partial || 0)) : 0;
    document.getElementById('dn-alloc-badge').textContent = allocCount;
    document.getElementById('btn-bulk-dn').disabled = allocCount === 0;
}

function renderPagination(pag) {
    if (!pag || pag.total_pages <= 1) { document.getElementById('pag-wrap').style.display = 'none'; return; }
    document.getElementById('pag-wrap').style.display = 'flex';
    document.getElementById('pag-info').textContent = pag.current_page + ' of ' + pag.total_pages;
    var tp = pag.total_pages, cp = pag.current_page, html = '';
    html += cp > 1 ? '<a onclick="goTo(1)">«</a><a onclick="goTo('+(cp-1)+')">‹</a>' : '<span class="disabled">«</span><span class="disabled">‹</span>';
    var s = Math.max(1,cp-2), e = Math.min(tp,cp+2);
    if (s > 1) html += '<span>…</span>';
    for (var i = s; i <= e; i++) html += i===cp ? '<span class="active">'+i+'</span>' : '<a onclick="goTo('+i+')">'+i+'</a>';
    if (e < tp) html += '<span>…</span>';
    html += cp < tp ? '<a onclick="goTo('+(cp+1)+')">›</a><a onclick="goTo('+tp+')">»</a>' : '<span class="disabled">›</span><span class="disabled">»</span>';
    document.getElementById('pag-controls').innerHTML = html;
}

function goTo(p) { currentPage = p; saveFilters(); loadOrders(false); }

function resetFilters() {
    ['f-ref','f-code','f-part','f-date-from','f-date-to'].forEach(function(id){ document.getElementById(id).value=''; });
    document.getElementById('f-status').value  = '';
    document.getElementById('per-page').value  = '100';
    currentPage = 1;
    clearSavedFilters();
    highlightActiveFilters();
    showPlaceholder();
}

// ── Allocate Stock Modal ─────────────────────────────────────────────────────

function openAllocModal(encoded) {
    activeRow = JSON.parse(decodeURIComponent(encoded));
    var r     = activeRow;
    var stock = Number(r.quantity_in_stock || 0);
    var need  = Number(r.req_qty || 0) - Number(r.allocated_qty || 0);

    document.getElementById('alloc-info-grid').innerHTML =
        ii('Part Number',       r.part_number    || '—') +
        ii('Ref Sales Code',    r.RefSalesCode   || '—') +
        ii('Customer',          r.customer_code  || '—') +
        ii('Req Date',          r.req_date       || '—') +
        ii('Already Allocated', Number(r.allocated_qty||0)) +
        ii('Still Needed', need, need > stock ? 'color:#c0392b' : 'color:#27ae60');

    document.getElementById('alloc-stock').textContent   = stock;
    document.getElementById('alloc-need').textContent    = need;
    document.getElementById('alloc-max-lbl').textContent = stock;

    var siblings = r._siblings || [];
    document.getElementById('alloc-cascade-wrap').style.display = siblings.length > 1 ? 'block' : 'none';

    var input = document.getElementById('alloc-qty-input');
    input.max   = stock;
    input.value = Math.min(stock, need > 0 ? need : stock);
    document.getElementById('alloc-qty-error').style.display = 'none';
    document.getElementById('alloc-spinner').style.display   = 'none';
    document.getElementById('confirm-alloc-btn').disabled    = false;

    input.oninput = function() { renderCascadePreview(siblings, r.id); };
    renderCascadePreview(siblings, r.id);

    document.getElementById('alloc-modal-overlay').classList.add('open');
    input.focus();
}

function renderCascadePreview(siblings, currentId) {
    var inputVal  = parseInt(document.getElementById('alloc-qty-input').value, 10);
    var budget    = isNaN(inputVal) ? 0 : inputVal;
    var remaining = budget;
    var tbody     = document.getElementById('alloc-cascade-body');

    tbody.innerHTML = siblings.map(function(s, idx) {
        var need    = Number(s.remaining_need || 0);
        var willGet = Math.min(remaining, need);
        remaining  -= willGet;
        var isCur   = String(s.id) === String(currentId);
        return '<tr' + (isCur ? ' class="this-line"' : '') + '>' +
            '<td>' + (idx+1) + (isCur ? ' ★' : '') + '</td>' +
            '<td>' + esc(s.RefSalesCode||'—') + '</td>' +
            '<td>' + esc(s.req_date||'—') + '</td>' +
            '<td>' + need + '</td>' +
            (willGet > 0 ? '<td class="will-get">' + willGet + '</td>'
                         : '<td class="wont-get">0</td>') +
        '</tr>';
    }).join('');
}

function closeAllocModal() {
    document.getElementById('alloc-modal-overlay').classList.remove('open');
    activeRow = null;
}

function confirmAllocate() {
    if (!activeRow) return;
    var input = document.getElementById('alloc-qty-input');
    var qty   = parseInt(input.value, 10);
    var stock = Number(activeRow.quantity_in_stock || 0);
    var errEl = document.getElementById('alloc-qty-error');

    if (isNaN(qty) || qty < 1 || qty > stock) { errEl.style.display = 'block'; input.focus(); return; }
    errEl.style.display = 'none';

    var btn = document.getElementById('confirm-alloc-btn');
    btn.disabled = true;
    document.getElementById('alloc-spinner').style.display = 'inline-block';

    var fd = new FormData();
    fd.append('planning_id',  activeRow.id);
    fd.append('part_id',      activeRow.part_id || '');
    fd.append('qty_to_alloc', qty);

    $.ajax({
        url: site_url + '/modules/readyorders/index.php?page=ajax_readyorders_list&action=allocate_stock',
        method: 'POST', data: fd, processData: false, contentType: false, dataType: 'json',
        success: function(res) {
            btn.disabled = false;
            document.getElementById('alloc-spinner').style.display = 'none';
            if (res.success) { closeAllocModal(); showToast(res.message || 'Allocated!', 'success'); loadOrders(false); }
            else showToast(res.message || 'Allocation failed.', 'error');
        },
        error: function(xhr, status, error) {
            btn.disabled = false;
            document.getElementById('alloc-spinner').style.display = 'none';
            showToast('Network error: ' + error, 'error');
        }
    });
}

// ── Edit Allocation Modal ────────────────────────────────────────────────────

function openEditAllocModal(encoded) {
    editActiveRow = JSON.parse(decodeURIComponent(encoded));
    var r         = editActiveRow;
    var current   = Number(r.allocated_qty || 0);
    var stock     = Number(r.quantity_in_stock || 0);
    var reqQty    = Number(r.req_qty || 0);
    // Max they can set: current allocation + whatever is still in stock
    var maxAlloc  = current + stock;

    document.getElementById('edit-alloc-info-grid').innerHTML =
        ii('Part Number',        r.part_number   || '—') +
        ii('Ref Sales Code',     r.RefSalesCode  || '—') +
        ii('Customer',           r.customer_code || '—') +
        ii('Req Date',           r.req_date      || '—') +
        ii('Requested Qty',      reqQty) +
        ii('Current Allocation', current, 'color:#e67e22');

    document.getElementById('edit-alloc-current').textContent  = current;
    document.getElementById('edit-alloc-max').textContent      = maxAlloc;
    document.getElementById('edit-alloc-max-lbl').textContent  = maxAlloc;

    var input = document.getElementById('edit-alloc-qty-input');
    input.max   = maxAlloc;
    input.min   = 0;
    input.value = current;

    document.getElementById('edit-alloc-qty-error').style.display   = 'none';
    document.getElementById('edit-alloc-spinner').style.display     = 'none';
    document.getElementById('confirm-edit-alloc-btn').disabled      = false;

    document.getElementById('edit-alloc-modal-overlay').classList.add('open');
    input.focus();
    input.select();
}

function closeEditAllocModal() {
    document.getElementById('edit-alloc-modal-overlay').classList.remove('open');
    editActiveRow = null;
}

function confirmEditAllocate() {
    if (!editActiveRow) return;
    var input    = document.getElementById('edit-alloc-qty-input');
    var newQty   = parseFloat(input.value);
    var current  = Number(editActiveRow.allocated_qty || 0);
    var stock    = Number(editActiveRow.quantity_in_stock || 0);
    var maxAlloc = current + stock;
    var errEl    = document.getElementById('edit-alloc-qty-error');

    if (isNaN(newQty) || newQty < 0 || newQty > maxAlloc) {
        errEl.style.display = 'block'; input.focus(); return;
    }
    errEl.style.display = 'none';

    var btn = document.getElementById('confirm-edit-alloc-btn');
    btn.disabled = true;
    document.getElementById('edit-alloc-spinner').style.display = 'inline-block';

    var fd = new FormData();
    fd.append('planning_id',   editActiveRow.id);
    fd.append('part_id',       editActiveRow.part_id || '');
    fd.append('new_alloc_qty', newQty);

    $.ajax({
        url: site_url + '/modules/readyorders/index.php?page=ajax_readyorders_list&action=edit_allocation',
        method: 'POST', data: fd, processData: false, contentType: false, dataType: 'json',
        success: function(res) {
            btn.disabled = false;
            document.getElementById('edit-alloc-spinner').style.display = 'none';
            if (res.success) { closeEditAllocModal(); showToast(res.message || 'Allocation updated!', 'success'); loadOrders(false); }
            else showToast(res.message || 'Update failed.', 'error');
        },
        error: function(xhr, status, error) {
            btn.disabled = false;
            document.getElementById('edit-alloc-spinner').style.display = 'none';
            showToast('Network error: ' + error, 'error');
        }
    });
}

// ── Delivery Note Modal ──────────────────────────────────────────────────────

function openDnModal() {
    if (!hasFilters()) { showToast('Please apply a filter first.', 'error'); return; }

    // Show modal with loading state immediately
    document.getElementById('dn-modal-body').innerHTML = '<div class="dn-loading">⏳ Loading all allocated lines across all pages…</div>';
    document.getElementById('confirm-dn-btn').disabled = true;
    document.getElementById('dn-modal-overlay').classList.add('open');
    dnAllLines = [];

    // Fetch ALL allocated lines (no pagination limit) for the current filter
    $.ajax({
        url: site_url + '/modules/readyorders/index.php?page=ajax_readyorders_list&action=get_ready_orders',
        method: 'GET',
        data: getParams({ current_page: 1, per_page: 9999, status_filter: '' }), // all, no status filter
        dataType: 'json',
        success: function(res) {
            if (!res.success) {
                document.getElementById('dn-modal-body').innerHTML = '<div class="dn-loading" style="color:red">' + (res.message || 'Error loading lines') + '</div>';
                return;
            }

            // Only keep lines that have allocated qty (and aren't shipped)
            dnAllLines = (res.data || []).filter(function(r) {
                return Number(r.allocated_qty || 0) > 0 && r.is_shipped != 1;
            });

            if (!dnAllLines.length) {
                document.getElementById('dn-modal-body').innerHTML =
                    '<div class="info-banner warn-banner"><span class="icon">⚠️</span><span>No allocated lines found for the current filter. Allocate stock to lines first.</span></div>';
                return;
            }

            renderDnModal(dnAllLines);
            document.getElementById('confirm-dn-btn').disabled = false;
        },
        error: function(xhr, status, error) {
            document.getElementById('dn-modal-body').innerHTML = '<div class="dn-loading" style="color:red">Network error: ' + error + '</div>';
        }
    });
}

function renderDnModal(lines) {
    var totalAlloc = lines.reduce(function(s,r){ return s + Number(r.allocated_qty||0); }, 0);
    var totalReq   = lines.reduce(function(s,r){ return s + Number(r.req_qty||0); }, 0);
    var parts      = [...new Set(lines.map(function(r){ return r.part_number || '—'; }))];
    var customers  = [...new Set(lines.map(function(r){ return r.customer_code || '—'; }))];

    var infoGrid = ii('Lines Included', lines.length) +
        ii('Total to Ship',   totalAlloc, 'color:#27ae60;font-size:16px') +
        ii('Parts Covered',   parts.length > 4 ? parts.length + ' parts' : parts.join(', ')) +
        ii('Customer(s)',     customers.join(', ')) +
        ii('Total Demanded',  totalReq) +
        ii('Delivery Date',   new Date().toLocaleDateString('en-GB'));

    var previewRows = lines.map(function(s, idx) {
        return '<tr>' +
            '<td>' + (idx+1) + '</td>' +
            '<td style="font-weight:600">' + esc(s.part_number ||'—') + '</td>' +
            '<td>' + esc(s.RefSalesCode  ||'—') + '</td>' +
            '<td>' + esc(s.req_date      ||'—') + '</td>' +
            '<td>' + Number(s.req_qty||0) + '</td>' +
            '<td class="qty-cell">' + Number(s.allocated_qty||0) + '</td>' +
        '</tr>';
    }).join('');

    document.getElementById('dn-modal-body').innerHTML =
        '<div class="info-banner">' +
            '<span class="icon">📋</span>' +
            '<span>One delivery note will be created covering all <strong>' + lines.length + '</strong> allocated line(s) across all pages of the current filter.</span>' +
        '</div>' +
        '<div class="info-grid">' + infoGrid + '</div>' +
        '<div class="dn-preview-wrap">' +
            '<div class="dn-preview-title">Lines included in this Delivery Note</div>' +
            '<table class="dn-preview">' +
                '<thead><tr><th>#</th><th>Part</th><th>Ref</th><th>Req Date</th><th>Req Qty</th><th>Ship Qty</th></tr></thead>' +
                '<tbody>' + previewRows + '</tbody>' +
            '</table>' +
        '</div>' +
        '<div class="dn-ship-section hide">' +
            '<label>Ship Method</label>' +
            '<select id="dn-ship-id">' +
                '<option value="1">Standard</option>' +
                '<option value="2">Express</option>' +
                '<option value="3">Collection</option>' +
            '</select>' +
        '</div>';

    document.getElementById('dn-spinner').style.display = 'none';
}

function closeDnModal() {
    document.getElementById('dn-modal-overlay').classList.remove('open');
    dnAllLines = [];
}

function confirmDeliveryNote() {
    if (!dnAllLines.length) return;

    var btn = document.getElementById('confirm-dn-btn');
    btn.disabled = true;
    document.getElementById('dn-spinner').style.display = 'inline-block';

    var shipEl = document.getElementById('dn-ship-id');
    var shipId = shipEl ? shipEl.value : '1';
    var firstRow = dnAllLines[0];

    var fd = new FormData();
    dnAllLines.forEach(function(r) { fd.append('planning_ids[]', r.id); });
    fd.append('ship_id',     shipId);
    fd.append('customer_id', parseInt(firstRow.customer_id || firstRow.customer_reff || 0));

    $.ajax({
        url: site_url + '/modules/readyorders/index.php?page=ajax_readyorders_list&action=create_delivery_note',
        method: 'POST', data: fd, processData: false, contentType: false, dataType: 'json',
        success: function(res) {
            btn.disabled = false;
            document.getElementById('dn-spinner').style.display = 'none';
            if (res.success) {
                closeDnModal();
                showToast(res.message || 'Delivery Note created!', 'success');
                loadOrders(false);
            } else {
                showToast(res.message || 'Failed to create Delivery Note.', 'error');
            }
        },
        error: function(xhr, status, error) {
            btn.disabled = false;
            document.getElementById('dn-spinner').style.display = 'none';
            showToast('Network error: ' + error, 'error');
        }
    });
}

// ── Utilities ────────────────────────────────────────────────────────────────

function ii(label, val, style) {
    return '<div class="ii"><span class="il">' + label + '</span>' +
           '<span class="iv"' + (style ? ' style="' + style + '"' : '') + '>' + val + '</span></div>';
}

function esc(s) {
    return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}

function showToast(msg, type) {
    var t = document.getElementById('toast');
    t.textContent = msg;
    t.className = 'show ' + (type || 'success');
    setTimeout(function(){ t.className = ''; }, 3800);
}

/* Close modals on overlay click or Escape */
['alloc-modal-overlay','edit-alloc-modal-overlay','dn-modal-overlay'].forEach(function(id) {
    document.getElementById(id).addEventListener('click', function(e) {
        if (e.target === this) { closeAllocModal(); closeEditAllocModal(); closeDnModal(); }
    });
});
document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') { closeAllocModal(); closeEditAllocModal(); closeDnModal(); }
});

(function init() {
    var hadFilters = restoreFilters();
    if (hadFilters) { loadOrders(false); } else { showPlaceholder(); }
    setInterval(function() { if (hasFilters()) loadOrders(false); }, 5 * 60 * 1000);
})();
</script>