Skip to content

Instantly share code, notes, and snippets.

@mirzap
Created April 1, 2025 09:00
Show Gist options
  • Select an option

  • Save mirzap/ec6602ef1d6f4170406491eda22d43b0 to your computer and use it in GitHub Desktop.

Select an option

Save mirzap/ec6602ef1d6f4170406491eda22d43b0 to your computer and use it in GitHub Desktop.

Revisions

  1. mirzap created this gist Apr 1, 2025.
    267 changes: 267 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,267 @@
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <title>Paginated Events Table</title>
    <style>
    body {
    font-family: Arial, sans-serif;
    }

    #controls {
    width: 90%;
    max-width: 1000px;
    margin: 20px auto;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    }

    #controls > * {
    flex: 1 1 22%;
    }

    #table-container {
    width: 90%;
    max-width: 1000px;
    margin: auto;
    overflow-x: auto;
    }

    table {
    width: 100%;
    border-collapse: collapse;
    }

    th, td {
    padding: 8px;
    border: 1px solid #ddd;
    text-align: left;
    font-size: 14px;
    }

    #pagination {
    display: flex;
    justify-content: center;
    gap: 10px;
    margin: 20px;
    }

    #pagination button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    }
    </style>
    </head>
    <body>
    <div id="controls">
    <input type="text" id="searchInput" placeholder="Search by name, email or message" />

    <select id="sortSelect">
    <option value="id">Sort by ID</option>
    <option value="cc">Sort by Country Code</option>
    <option value="plan">Sort by Plan</option>
    <option value="size">Sort by Size</option>
    </select>

    <select id="planFilter">
    <option value="">All Plans</option>
    <option value="free">Free</option>
    <option value="pro">Pro</option>
    <option value="enterprise">Enterprise</option>
    </select>

    <select id="sizeFilter">
    <option value="">All Sizes</option>
    <option value="s">S</option>
    <option value="m">M</option>
    <option value="l">L</option>
    <option value="xl">XL</option>
    </select>

    <button onclick="applyFiltersAndSort()">Apply</button>
    </div>

    <div id="table-container">
    <table>
    <thead>
    <tr>
    <th>ID</th>
    <th>Name</th>
    <th>Email</th>
    <th>Message</th>
    <th>Country</th>
    <th>Plan</th>
    <th>Size</th>
    </tr>
    </thead>
    <tbody id="table-body"></tbody>
    </table>
    </div>

    <div id="pagination">
    <button id="firstPageBtn" onclick="firstPage()">First</button>
    <button id="prevPageBtn" onclick="prevPage()">Previous</button>
    <span id="page-info"></span>
    <button id="nextPageBtn" onclick="nextPage()">Next</button>
    <button id="lastPageBtn" onclick="lastPage()">Last</button>
    </div>

    <script>
    const events = [];
    let currentEntries = events;
    let currentPage = 0;
    const pageSize = 100;

    function add_events(input) {
    const lines = input.split('\n');
    for (let line of lines) {
    if (line.trim()) {
    try {
    events.push(JSON.parse(line.trim()));
    } catch (e) {
    console.warn('Skipping malformed line:', line);
    }
    }
    }
    currentEntries = events;
    renderTable();
    }

    function renderTable() {
    const tbody = document.getElementById('table-body');
    tbody.innerHTML = '';

    const totalPages = Math.ceil(currentEntries.length / pageSize);
    const start = currentPage * pageSize;
    const end = Math.min(currentEntries.length, start + pageSize);
    document.getElementById('page-info').textContent = `Page ${currentPage + 1} of ${totalPages}`;

    document.getElementById('firstPageBtn').disabled = currentPage === 0;
    document.getElementById('prevPageBtn').disabled = currentPage === 0;
    document.getElementById('nextPageBtn').disabled = currentPage >= totalPages - 1;
    document.getElementById('lastPageBtn').disabled = currentPage >= totalPages - 1;

    for (let i = start; i < end; i++) {
    const e = currentEntries[i];
    const row = document.createElement('tr');
    row.innerHTML = `
    <td>${e.data.id}</td>
    <td>${e.data.name}</td>
    <td>${e.data.email}</td>
    <td>${e.data.message}</td>
    <td>${e.data.cc}</td>
    <td>${e.data.plan}</td>
    <td>${e.data.size}</td>
    `;
    tbody.appendChild(row);
    }
    }

    function nextPage() {
    const totalPages = Math.ceil(currentEntries.length / pageSize);
    if (currentPage < totalPages - 1) {
    currentPage++;
    renderTable();
    }
    }

    function prevPage() {
    if (currentPage > 0) {
    currentPage--;
    renderTable();
    }
    }

    function firstPage() {
    currentPage = 0;
    renderTable();
    }

    function lastPage() {
    const totalPages = Math.ceil(currentEntries.length / pageSize);
    currentPage = totalPages - 1;
    renderTable();
    }

    function sortEntries(sort_by = 'id', ascending = true) {
    const planOrder = ['free', 'pro', 'enterprise'];
    const sizeOrder = ['s', 'm', 'l', 'xl'];

    currentEntries = [...currentEntries].sort((a, b) => {
    let compare = 0;
    if (sort_by === 'id') compare = a.data.id - b.data.id;
    else if (sort_by === 'cc') compare = a.data.cc.localeCompare(b.data.cc);
    else if (sort_by === 'size') compare = sizeOrder.indexOf(a.data.size || 's') - sizeOrder.indexOf(b.data.size || 's');
    else if (sort_by === 'plan') compare = planOrder.indexOf(a.data.plan) - planOrder.indexOf(b.data.plan);
    return ascending ? compare : -compare;
    });
    }

    function filterEntries({ type, plan, size }) {
    currentEntries = events.filter(el => {
    return (!type || el.type === type) &&
    (!plan || el.data.plan === plan) &&
    (!size || el.data.size === size);
    });
    }

    function search(query) {
    const q = query.toLowerCase();
    currentEntries = currentEntries.filter(event => {
    return (event.data.message || '').toLowerCase().includes(q) ||
    (event.data.name || '').toLowerCase().includes(q) ||
    (event.data.email || '').toLowerCase().includes(q);
    });
    }

    function applyFiltersAndSort() {
    const sortBy = document.getElementById('sortSelect').value;
    const plan = document.getElementById('planFilter').value;
    const size = document.getElementById('sizeFilter').value;
    const searchQuery = document.getElementById('searchInput').value;

    currentEntries = [...events];
    filterEntries({ plan, size });
    sortEntries(sortBy);
    if (searchQuery) {
    search(searchQuery);
    }
    currentPage = 0;
    renderTable();
    }

    const simulatedInputLines = [];
    for (let i = 0; i < 1000000; i++) {
    simulatedInputLines.push(JSON.stringify({
    type: "question",
    data: {
    cc: "fr",
    email: `user${i + 1}@example.com`,
    id: i + 1,
    message: `Message number ${i + 1}`,
    name: `User #${i + 1}`,
    plan: ['free', 'pro', 'enterprise'][i % 3],
    size: ['s', 'm', 'l', 'xl'][i % 4]
    }
    }));
    }

    function addInChunks(lines, chunkSize = 5000) {
    let index = 0;
    function processChunk() {
    const chunk = lines.slice(index, index + chunkSize);
    add_events(chunk.join('\n'));
    index += chunkSize;
    if (index < lines.length) {
    setTimeout(processChunk, 0);
    }
    }
    processChunk();
    }

    addInChunks(simulatedInputLines);
    </script>
    </body>
    </html>