WIP: development_backend #4
@@ -275,7 +275,7 @@
 | 
			
		||||
                                        <th class="text-end pe-3">Date</th>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                </thead>
 | 
			
		||||
                                <tbody>
 | 
			
		||||
                                <tbody id="votesTableBody">
 | 
			
		||||
                                    {% if votes | length == 0 %}
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td colspan="4" class="text-center py-4">
 | 
			
		||||
@@ -329,6 +329,43 @@
 | 
			
		||||
                                </tbody>
 | 
			
		||||
                            </table>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        
 | 
			
		||||
                        <!-- Pagination Controls -->
 | 
			
		||||
                        {% if votes | length > 10 %}
 | 
			
		||||
                        <div class="d-flex justify-content-between align-items-center p-3 border-top">
 | 
			
		||||
                            <div class="d-flex align-items-center">
 | 
			
		||||
                                <label class="me-2 text-muted small">Rows per page:</label>
 | 
			
		||||
                                <select id="rowsPerPage" class="form-select form-select-sm" style="width: auto;">
 | 
			
		||||
                                    <option value="10">10</option>
 | 
			
		||||
                                    <option value="25">25</option>
 | 
			
		||||
                                    <option value="50">50</option>
 | 
			
		||||
                                    <option value="100">100</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div>
 | 
			
		||||
                                <nav aria-label="Votes pagination">
 | 
			
		||||
                                    <ul class="pagination pagination-sm mb-0" id="paginationControls">
 | 
			
		||||
                                        <li class="page-item disabled" id="prevPage">
 | 
			
		||||
                                            <a class="page-link" href="#" aria-label="Previous">
 | 
			
		||||
                                                <span aria-hidden="true">«</span>
 | 
			
		||||
                                            </a>
 | 
			
		||||
                                        </li>
 | 
			
		||||
                                        <li class="page-item active"><a class="page-link" href="#">1</a></li>
 | 
			
		||||
                                        <li class="page-item"><a class="page-link" href="#">2</a></li>
 | 
			
		||||
                                        <li class="page-item"><a class="page-link" href="#">3</a></li>
 | 
			
		||||
                                        <li class="page-item" id="nextPage">
 | 
			
		||||
                                            <a class="page-link" href="#" aria-label="Next">
 | 
			
		||||
                                                <span aria-hidden="true">»</span>
 | 
			
		||||
                                            </a>
 | 
			
		||||
                                        </li>
 | 
			
		||||
                                    </ul>
 | 
			
		||||
                                </nav>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="text-muted small" id="paginationInfo">
 | 
			
		||||
                                Showing <span id="startRow">1</span>-<span id="endRow">10</span> of <span id="totalRows">{{ votes | length }}</span>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
@@ -362,41 +399,237 @@
 | 
			
		||||
 | 
			
		||||
            // Filter votes by type
 | 
			
		||||
            filterButtons.forEach(button => {
 | 
			
		||||
                button.addEventListener('click', function () {
 | 
			
		||||
                button.addEventListener('click', function() {
 | 
			
		||||
                    // Update active button
 | 
			
		||||
                    filterButtons.forEach(btn => btn.classList.remove('active'));
 | 
			
		||||
                    this.classList.add('active');
 | 
			
		||||
 | 
			
		||||
                    const filterType = this.getAttribute('data-filter');
 | 
			
		||||
 | 
			
		||||
                    voteRows.forEach(row => {
 | 
			
		||||
                        if (filterType === 'all') {
 | 
			
		||||
                            row.style.display = '';
 | 
			
		||||
                        } else {
 | 
			
		||||
                            const voteType = row.getAttribute('data-vote-type');
 | 
			
		||||
                            row.style.display = (voteType === filterType) ? '' : 'none';
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    
 | 
			
		||||
                    // Reset to first page and update pagination
 | 
			
		||||
                    currentPage = 1;
 | 
			
		||||
                    updatePagination();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Search functionality
 | 
			
		||||
            if (searchInput) {
 | 
			
		||||
                searchInput.addEventListener('input', function () {
 | 
			
		||||
                searchInput.addEventListener('input', function() {
 | 
			
		||||
                    const searchTerm = this.value.toLowerCase();
 | 
			
		||||
 | 
			
		||||
                    
 | 
			
		||||
                    voteRows.forEach(row => {
 | 
			
		||||
                        const voterName = row.querySelector('td:first-child').textContent.toLowerCase();
 | 
			
		||||
                        const comment = row.querySelector('td:nth-child(3)').textContent.toLowerCase();
 | 
			
		||||
 | 
			
		||||
                        
 | 
			
		||||
                        if (voterName.includes(searchTerm) || comment.includes(searchTerm)) {
 | 
			
		||||
                            row.style.display = '';
 | 
			
		||||
                        } else {
 | 
			
		||||
                            row.style.display = 'none';
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    
 | 
			
		||||
                    // Reset pagination after search
 | 
			
		||||
                    currentPage = 1;
 | 
			
		||||
                    updatePagination();
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Pagination functionality
 | 
			
		||||
            const rowsPerPageSelect = document.getElementById('rowsPerPage');
 | 
			
		||||
            const paginationControls = document.getElementById('paginationControls');
 | 
			
		||||
            const votesTableBody = document.getElementById('votesTableBody');
 | 
			
		||||
            const startRowElement = document.getElementById('startRow');
 | 
			
		||||
            const endRowElement = document.getElementById('endRow');
 | 
			
		||||
            const totalRowsElement = document.getElementById('totalRows');
 | 
			
		||||
            const prevPageBtn = document.getElementById('prevPage');
 | 
			
		||||
            const nextPageBtn = document.getElementById('nextPage');
 | 
			
		||||
            
 | 
			
		||||
            let currentPage = 1;
 | 
			
		||||
            let rowsPerPage = rowsPerPageSelect ? parseInt(rowsPerPageSelect.value) : 10;
 | 
			
		||||
            
 | 
			
		||||
            // Function to update pagination display
 | 
			
		||||
            function updatePagination() {
 | 
			
		||||
                if (!paginationControls) return;
 | 
			
		||||
                
 | 
			
		||||
                // Get all rows that match the current filter
 | 
			
		||||
                const currentFilter = document.querySelector('[data-filter].active');
 | 
			
		||||
                const filterType = currentFilter ? currentFilter.getAttribute('data-filter') : 'all';
 | 
			
		||||
                
 | 
			
		||||
                // Get rows that match the current filter and search term
 | 
			
		||||
                let filteredRows = Array.from(voteRows);
 | 
			
		||||
                if (filterType !== 'all') {
 | 
			
		||||
                    filteredRows = filteredRows.filter(row => row.getAttribute('data-vote-type') === filterType);
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Apply search filter if there's a search term
 | 
			
		||||
                const searchTerm = searchInput ? searchInput.value.toLowerCase() : '';
 | 
			
		||||
                if (searchTerm) {
 | 
			
		||||
                    filteredRows = filteredRows.filter(row => {
 | 
			
		||||
                        const voterName = row.querySelector('td:first-child').textContent.toLowerCase();
 | 
			
		||||
                        const comment = row.querySelector('td:nth-child(3)').textContent.toLowerCase();
 | 
			
		||||
                        return voterName.includes(searchTerm) || comment.includes(searchTerm);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                const totalRows = filteredRows.length;
 | 
			
		||||
                
 | 
			
		||||
                // Calculate total pages
 | 
			
		||||
                const totalPages = Math.max(1, Math.ceil(totalRows / rowsPerPage));
 | 
			
		||||
                
 | 
			
		||||
                // Ensure current page is valid
 | 
			
		||||
                if (currentPage > totalPages) {
 | 
			
		||||
                    currentPage = totalPages;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Update pagination controls
 | 
			
		||||
                if (paginationControls) {
 | 
			
		||||
                    // Clear existing page links (except prev/next)
 | 
			
		||||
                    const pageLinks = paginationControls.querySelectorAll('li:not(#prevPage):not(#nextPage)');
 | 
			
		||||
                    pageLinks.forEach(link => link.remove());
 | 
			
		||||
                    
 | 
			
		||||
                    // Add new page links
 | 
			
		||||
                    const maxVisiblePages = 5;
 | 
			
		||||
                    let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
 | 
			
		||||
                    let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
 | 
			
		||||
                    
 | 
			
		||||
                    // Adjust if we're near the end
 | 
			
		||||
                    if (endPage - startPage + 1 < maxVisiblePages && startPage > 1) {
 | 
			
		||||
                        startPage = Math.max(1, endPage - maxVisiblePages + 1);
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    // Insert page links before the next button
 | 
			
		||||
                    const nextPageElement = document.getElementById('nextPage');
 | 
			
		||||
                    for (let i = startPage; i <= endPage; i++) {
 | 
			
		||||
                        const li = document.createElement('li');
 | 
			
		||||
                        li.className = `page-item ${i === currentPage ? 'active' : ''}`;
 | 
			
		||||
                        
 | 
			
		||||
                        const a = document.createElement('a');
 | 
			
		||||
                        a.className = 'page-link';
 | 
			
		||||
                        a.href = '#';
 | 
			
		||||
                        a.textContent = i;
 | 
			
		||||
                        a.addEventListener('click', function(e) {
 | 
			
		||||
                            e.preventDefault();
 | 
			
		||||
                            currentPage = i;
 | 
			
		||||
                            updatePagination();
 | 
			
		||||
                        });
 | 
			
		||||
                        
 | 
			
		||||
                        li.appendChild(a);
 | 
			
		||||
                        paginationControls.insertBefore(li, nextPageElement);
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    // Update prev/next buttons
 | 
			
		||||
                    prevPageBtn.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
 | 
			
		||||
                    nextPageBtn.className = `page-item ${currentPage === totalPages ? 'disabled' : ''}`;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Show current page
 | 
			
		||||
                showCurrentPage();
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Function to show current page
 | 
			
		||||
            function showCurrentPage() {
 | 
			
		||||
                if (!votesTableBody) return;
 | 
			
		||||
                
 | 
			
		||||
                // Get all rows that match the current filter
 | 
			
		||||
                const currentFilter = document.querySelector('[data-filter].active');
 | 
			
		||||
                const filterType = currentFilter ? currentFilter.getAttribute('data-filter') : 'all';
 | 
			
		||||
                
 | 
			
		||||
                // Get rows that match the current filter and search term
 | 
			
		||||
                let filteredRows = Array.from(voteRows);
 | 
			
		||||
                if (filterType !== 'all') {
 | 
			
		||||
                    filteredRows = filteredRows.filter(row => row.getAttribute('data-vote-type') === filterType);
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Apply search filter if there's a search term
 | 
			
		||||
                const searchTerm = searchInput ? searchInput.value.toLowerCase() : '';
 | 
			
		||||
                if (searchTerm) {
 | 
			
		||||
                    filteredRows = filteredRows.filter(row => {
 | 
			
		||||
                        const voterName = row.querySelector('td:first-child').textContent.toLowerCase();
 | 
			
		||||
                        const comment = row.querySelector('td:nth-child(3)').textContent.toLowerCase();
 | 
			
		||||
                        return voterName.includes(searchTerm) || comment.includes(searchTerm);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Hide all rows first
 | 
			
		||||
                voteRows.forEach(row => row.style.display = 'none');
 | 
			
		||||
                
 | 
			
		||||
                // Calculate pagination
 | 
			
		||||
                const totalRows = filteredRows.length;
 | 
			
		||||
                const totalPages = Math.max(1, Math.ceil(totalRows / rowsPerPage));
 | 
			
		||||
                
 | 
			
		||||
                // Ensure current page is valid
 | 
			
		||||
                if (currentPage > totalPages) {
 | 
			
		||||
                    currentPage = totalPages;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Show only rows for current page
 | 
			
		||||
                const start = (currentPage - 1) * rowsPerPage;
 | 
			
		||||
                const end = start + rowsPerPage;
 | 
			
		||||
                
 | 
			
		||||
                filteredRows.slice(start, end).forEach(row => row.style.display = '');
 | 
			
		||||
                
 | 
			
		||||
                // Update pagination info
 | 
			
		||||
                if (startRowElement && endRowElement && totalRowsElement) {
 | 
			
		||||
                    startRowElement.textContent = totalRows > 0 ? start + 1 : 0;
 | 
			
		||||
                    endRowElement.textContent = Math.min(end, totalRows);
 | 
			
		||||
                    totalRowsElement.textContent = totalRows;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Event listeners for pagination
 | 
			
		||||
            if (prevPageBtn) {
 | 
			
		||||
                prevPageBtn.addEventListener('click', function(e) {
 | 
			
		||||
                    e.preventDefault();
 | 
			
		||||
                    if (currentPage > 1) {
 | 
			
		||||
                        currentPage--;
 | 
			
		||||
                        updatePagination();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            if (nextPageBtn) {
 | 
			
		||||
                nextPageBtn.addEventListener('click', function(e) {
 | 
			
		||||
                    e.preventDefault();
 | 
			
		||||
                    // Get all rows that match the current filter
 | 
			
		||||
                    const currentFilter = document.querySelector('[data-filter].active');
 | 
			
		||||
                    const filterType = currentFilter ? currentFilter.getAttribute('data-filter') : 'all';
 | 
			
		||||
                    
 | 
			
		||||
                    // Get rows that match the current filter and search term
 | 
			
		||||
                    let filteredRows = Array.from(voteRows);
 | 
			
		||||
                    if (filterType !== 'all') {
 | 
			
		||||
                        filteredRows = filteredRows.filter(row => row.getAttribute('data-vote-type') === filterType);
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    // Apply search filter if there's a search term
 | 
			
		||||
                    const searchTerm = searchInput ? searchInput.value.toLowerCase() : '';
 | 
			
		||||
                    if (searchTerm) {
 | 
			
		||||
                        filteredRows = filteredRows.filter(row => {
 | 
			
		||||
                            const voterName = row.querySelector('td:first-child').textContent.toLowerCase();
 | 
			
		||||
                            const comment = row.querySelector('td:nth-child(3)').textContent.toLowerCase();
 | 
			
		||||
                            return voterName.includes(searchTerm) || comment.includes(searchTerm);
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    const totalRows = filteredRows.length;
 | 
			
		||||
                    const totalPages = Math.max(1, Math.ceil(totalRows / rowsPerPage));
 | 
			
		||||
                    
 | 
			
		||||
                    if (currentPage < totalPages) {
 | 
			
		||||
                        currentPage++;
 | 
			
		||||
                        updatePagination();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            if (rowsPerPageSelect) {
 | 
			
		||||
                rowsPerPageSelect.addEventListener('change', function() {
 | 
			
		||||
                    rowsPerPage = parseInt(this.value);
 | 
			
		||||
                    currentPage = 1; // Reset to first page
 | 
			
		||||
                    updatePagination();
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Initialize pagination
 | 
			
		||||
            if (paginationControls) {
 | 
			
		||||
                updatePagination();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Initialize tooltips for all elements with title attributes
 | 
			
		||||
            const tooltipElements = document.querySelectorAll('[title]');
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user