feat: Enhance Governance Controller and Proposal Handling
- Improve proposal search to include description field: This allows for more comprehensive search results. - Fix redirect after voting: The redirect now correctly handles the success message. - Handle potential invalid timestamps in ballots: The code now gracefully handles ballots with invalid timestamps, preventing crashes and using the current time as a fallback. - Add local time formatting function: This provides a way to display dates and times in the user's local timezone. - Update database path: This simplifies the database setup. - Improve proposal vote handling: Addresses issues with vote submission and timestamping. - Add client-side pagination and filtering to proposal details: Improves user experience for viewing large vote lists.
This commit is contained in:
@@ -329,7 +329,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Pagination Controls -->
|
||||
{% if votes | length > 10 %}
|
||||
<div class="d-flex justify-content-between align-items-center p-3 border-top">
|
||||
@@ -362,7 +362,8 @@
|
||||
</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>
|
||||
Showing <span id="startRow">1</span>-<span id="endRow">10</span> of <span
|
||||
id="totalRows">{{ votes | length }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -379,19 +380,19 @@
|
||||
if (window.location.search.includes('vote_success=true')) {
|
||||
const newUrl = window.location.pathname;
|
||||
window.history.replaceState({}, document.title, newUrl);
|
||||
|
||||
|
||||
// Auto-hide the success alert after 5 seconds
|
||||
const successAlert = document.querySelector('.alert-success');
|
||||
if (successAlert) {
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
successAlert.classList.remove('show');
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
successAlert.remove();
|
||||
}, 500);
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Vote filtering using data-filter attributes
|
||||
const filterButtons = document.querySelectorAll('[data-filter]');
|
||||
const voteRows = document.querySelectorAll('.vote-row');
|
||||
@@ -399,11 +400,11 @@
|
||||
|
||||
// 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');
|
||||
|
||||
|
||||
// Reset to first page and update pagination
|
||||
currentPage = 1;
|
||||
updatePagination();
|
||||
@@ -412,26 +413,26 @@
|
||||
|
||||
// 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');
|
||||
@@ -441,24 +442,24 @@
|
||||
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) {
|
||||
@@ -468,76 +469,76 @@
|
||||
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) {
|
||||
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) {
|
||||
@@ -547,25 +548,25 @@
|
||||
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;
|
||||
@@ -573,10 +574,10 @@
|
||||
totalRowsElement.textContent = totalRows;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Event listeners for pagination
|
||||
if (prevPageBtn) {
|
||||
prevPageBtn.addEventListener('click', function(e) {
|
||||
prevPageBtn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
@@ -584,20 +585,20 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (nextPageBtn) {
|
||||
nextPageBtn.addEventListener('click', function(e) {
|
||||
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) {
|
||||
@@ -607,25 +608,25 @@
|
||||
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() {
|
||||
rowsPerPageSelect.addEventListener('change', function () {
|
||||
rowsPerPage = parseInt(this.value);
|
||||
currentPage = 1; // Reset to first page
|
||||
updatePagination();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Initialize pagination
|
||||
if (paginationControls) {
|
||||
updatePagination();
|
||||
|
Reference in New Issue
Block a user