Cuts bug fixes and updates
This commit is contained in:
parent
f44cb35253
commit
ebf9ff23ab
@ -19,7 +19,8 @@ class CutsController {
|
|||||||
|
|
||||||
// For NocoDB v2 API, we need to get all records and filter in memory
|
// For NocoDB v2 API, we need to get all records and filter in memory
|
||||||
// since the where clause syntax may be different
|
// since the where clause syntax may be different
|
||||||
const response = await nocodbService.getAll(
|
// Use paginated method to get ALL records (not just the default 25 limit)
|
||||||
|
const response = await nocodbService.getAllPaginated(
|
||||||
config.nocodb.cutsSheetId
|
config.nocodb.cutsSheetId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -791,10 +791,19 @@
|
|||||||
<option value="Neighborhood">Neighborhood</option>
|
<option value="Neighborhood">Neighborhood</option>
|
||||||
<option value="District">District</option>
|
<option value="District">District</option>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="cuts-per-page" class="form-control">
|
||||||
|
<option value="5" selected>5 per page</option>
|
||||||
|
<option value="10">10 per page</option>
|
||||||
|
<option value="20">20 per page</option>
|
||||||
|
<option value="50">50 per page</option>
|
||||||
|
<option value="100">100 per page</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div id="cuts-list" class="cuts-list">
|
<div id="cuts-list" class="cuts-list">
|
||||||
<!-- Cuts will be populated here -->
|
<!-- Cuts will be populated here -->
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Pagination Controls (will be dynamically created after cuts-list) -->
|
||||||
|
<div id="cuts-pagination" class="cuts-pagination" style="display: none;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -516,3 +516,91 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Cuts Pagination Styles */
|
||||||
|
.cuts-pagination {
|
||||||
|
margin-top: var(--padding-sm);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--padding-sm) 0;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-primary);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all var(--transition-base);
|
||||||
|
min-width: 2.5rem;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover:not(.active):not(:disabled) {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn.active {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
padding: 0.5rem 0.25rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cuts list header with count information */
|
||||||
|
.cuts-list-header {
|
||||||
|
padding: var(--padding-sm);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cuts-count {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Updated cuts filters to accommodate items per page selector */
|
||||||
|
.cuts-filters {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--padding-sm);
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: var(--padding-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cuts-filters .form-control {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cuts-filters .form-control:last-child {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|||||||
@ -23,6 +23,11 @@ class AdminCutsManager {
|
|||||||
// Location markers for map display
|
// Location markers for map display
|
||||||
this.locationMarkers = null;
|
this.locationMarkers = null;
|
||||||
|
|
||||||
|
// Pagination properties
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.itemsPerPage = 5;
|
||||||
|
this.totalPages = 1;
|
||||||
|
|
||||||
// Bind event handler once to avoid issues with removing listeners
|
// Bind event handler once to avoid issues with removing listeners
|
||||||
this.boundHandleCutActionClick = this.handleCutActionClick.bind(this);
|
this.boundHandleCutActionClick = this.handleCutActionClick.bind(this);
|
||||||
}
|
}
|
||||||
@ -354,6 +359,16 @@ class AdminCutsManager {
|
|||||||
categoryFilter.addEventListener('change', () => this.filterCuts());
|
categoryFilter.addEventListener('change', () => this.filterCuts());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up items per page selector
|
||||||
|
const itemsPerPageSelect = document.getElementById('cuts-per-page');
|
||||||
|
if (itemsPerPageSelect) {
|
||||||
|
itemsPerPageSelect.addEventListener('change', (e) => {
|
||||||
|
this.itemsPerPage = parseInt(e.target.value);
|
||||||
|
this.currentPage = 1; // Reset to first page
|
||||||
|
this.renderCutsList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Add drawing toolbar button handlers
|
// Add drawing toolbar button handlers
|
||||||
const finishDrawingBtn = document.getElementById('finish-cut-btn');
|
const finishDrawingBtn = document.getElementById('finish-cut-btn');
|
||||||
if (finishDrawingBtn) {
|
if (finishDrawingBtn) {
|
||||||
@ -1036,6 +1051,12 @@ class AdminCutsManager {
|
|||||||
|
|
||||||
this.allCuts = data.list || [];
|
this.allCuts = data.list || [];
|
||||||
this.filteredCuts = [...this.allCuts];
|
this.filteredCuts = [...this.allCuts];
|
||||||
|
|
||||||
|
console.log(`Loaded ${this.allCuts.length} cuts from server`);
|
||||||
|
if (this.allCuts.length >= 100) {
|
||||||
|
console.warn('Large dataset detected. Consider implementing pagination or server-side filtering.');
|
||||||
|
}
|
||||||
|
|
||||||
this.renderCutsList();
|
this.renderCutsList();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -1056,11 +1077,31 @@ class AdminCutsManager {
|
|||||||
|
|
||||||
if (this.filteredCuts.length === 0) {
|
if (this.filteredCuts.length === 0) {
|
||||||
this.cutsList.innerHTML = '<p class="no-data">No cuts found</p>';
|
this.cutsList.innerHTML = '<p class="no-data">No cuts found</p>';
|
||||||
|
this.renderPagination(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = this.filteredCuts.map(cut => this.renderCutItem(cut)).join('');
|
// Calculate pagination
|
||||||
this.cutsList.innerHTML = html;
|
this.totalPages = Math.ceil(this.filteredCuts.length / this.itemsPerPage);
|
||||||
|
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
|
||||||
|
const endIndex = startIndex + this.itemsPerPage;
|
||||||
|
const cutsToShow = this.filteredCuts.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
// Render header with count and pagination info
|
||||||
|
const headerHtml = `
|
||||||
|
<div class="cuts-list-header">
|
||||||
|
<div class="cuts-count">
|
||||||
|
Showing ${startIndex + 1}-${Math.min(endIndex, this.filteredCuts.length)} of ${this.filteredCuts.length} cuts
|
||||||
|
${this.filteredCuts.length !== this.allCuts.length ? `(filtered from ${this.allCuts.length} total)` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const cutsHtml = cutsToShow.map(cut => this.renderCutItem(cut)).join('');
|
||||||
|
this.cutsList.innerHTML = headerHtml + cutsHtml;
|
||||||
|
|
||||||
|
// Render pagination controls
|
||||||
|
this.renderPagination(this.filteredCuts.length);
|
||||||
|
|
||||||
// Add event delegation for cut action buttons
|
// Add event delegation for cut action buttons
|
||||||
this.cutsList.addEventListener('click', this.boundHandleCutActionClick);
|
this.cutsList.addEventListener('click', this.boundHandleCutActionClick);
|
||||||
@ -1426,20 +1467,148 @@ class AdminCutsManager {
|
|||||||
let filteredCuts = this.allCuts;
|
let filteredCuts = this.allCuts;
|
||||||
|
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
filteredCuts = filteredCuts.filter(cut =>
|
filteredCuts = filteredCuts.filter(cut => {
|
||||||
cut.name.toLowerCase().includes(searchTerm) ||
|
// Handle different possible field names and null/undefined values
|
||||||
(cut.description && cut.description.toLowerCase().includes(searchTerm))
|
const cutName = cut.name || cut.Name || '';
|
||||||
);
|
const cutDescription = cut.description || cut.Description || '';
|
||||||
|
|
||||||
|
// Prioritize name matches - if name matches, return true immediately
|
||||||
|
if (cutName.toLowerCase().includes(searchTerm)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only check description if name doesn't match
|
||||||
|
return cutDescription.toLowerCase().includes(searchTerm);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort results to prioritize name matches at the top
|
||||||
|
filteredCuts.sort((a, b) => {
|
||||||
|
const nameA = a.name || a.Name || '';
|
||||||
|
const nameB = b.name || b.Name || '';
|
||||||
|
const nameAMatches = nameA.toLowerCase().includes(searchTerm);
|
||||||
|
const nameBMatches = nameB.toLowerCase().includes(searchTerm);
|
||||||
|
|
||||||
|
// If both match by name or both don't match by name, maintain original order
|
||||||
|
if (nameAMatches === nameBMatches) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prioritize name matches (true comes before false)
|
||||||
|
return nameBMatches - nameAMatches;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (categoryFilter) {
|
if (categoryFilter) {
|
||||||
filteredCuts = filteredCuts.filter(cut => cut.category === categoryFilter);
|
filteredCuts = filteredCuts.filter(cut => {
|
||||||
|
const cutCategory = cut.category || cut.Category || '';
|
||||||
|
return cutCategory === categoryFilter;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.filteredCuts = filteredCuts;
|
this.filteredCuts = filteredCuts;
|
||||||
|
|
||||||
|
// Reset to first page when filtering
|
||||||
|
this.currentPage = 1;
|
||||||
|
|
||||||
this.renderCutsList();
|
this.renderCutsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderPagination(totalItems) {
|
||||||
|
const paginationContainer = document.getElementById('cuts-pagination') || this.createPaginationContainer();
|
||||||
|
|
||||||
|
if (totalItems <= this.itemsPerPage) {
|
||||||
|
paginationContainer.innerHTML = '';
|
||||||
|
paginationContainer.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paginationContainer.style.display = 'block';
|
||||||
|
|
||||||
|
let paginationHtml = '<div class="pagination-controls">';
|
||||||
|
|
||||||
|
// Previous button
|
||||||
|
if (this.currentPage > 1) {
|
||||||
|
paginationHtml += `<button class="pagination-btn" data-page="${this.currentPage - 1}">‹ Previous</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page numbers (show max 7 pages)
|
||||||
|
const maxVisiblePages = 7;
|
||||||
|
let startPage = Math.max(1, this.currentPage - Math.floor(maxVisiblePages / 2));
|
||||||
|
let endPage = Math.min(this.totalPages, startPage + maxVisiblePages - 1);
|
||||||
|
|
||||||
|
// Adjust if we're near the end
|
||||||
|
if (endPage - startPage < maxVisiblePages - 1) {
|
||||||
|
startPage = Math.max(1, endPage - maxVisiblePages + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First page and ellipsis
|
||||||
|
if (startPage > 1) {
|
||||||
|
paginationHtml += `<button class="pagination-btn" data-page="1">1</button>`;
|
||||||
|
if (startPage > 2) {
|
||||||
|
paginationHtml += '<span class="pagination-ellipsis">...</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page numbers
|
||||||
|
for (let i = startPage; i <= endPage; i++) {
|
||||||
|
const isActive = i === this.currentPage ? 'active' : '';
|
||||||
|
paginationHtml += `<button class="pagination-btn ${isActive}" data-page="${i}">${i}</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last page and ellipsis
|
||||||
|
if (endPage < this.totalPages) {
|
||||||
|
if (endPage < this.totalPages - 1) {
|
||||||
|
paginationHtml += '<span class="pagination-ellipsis">...</span>';
|
||||||
|
}
|
||||||
|
paginationHtml += `<button class="pagination-btn" data-page="${this.totalPages}">${this.totalPages}</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next button
|
||||||
|
if (this.currentPage < this.totalPages) {
|
||||||
|
paginationHtml += `<button class="pagination-btn" data-page="${this.currentPage + 1}">Next ›</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
paginationHtml += '</div>';
|
||||||
|
|
||||||
|
paginationContainer.innerHTML = paginationHtml;
|
||||||
|
|
||||||
|
// Add click handlers for pagination
|
||||||
|
paginationContainer.addEventListener('click', (e) => {
|
||||||
|
if (e.target.classList.contains('pagination-btn') && e.target.dataset.page) {
|
||||||
|
this.goToPage(parseInt(e.target.dataset.page));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createPaginationContainer() {
|
||||||
|
let container = document.getElementById('cuts-pagination');
|
||||||
|
if (!container) {
|
||||||
|
container = document.createElement('div');
|
||||||
|
container.id = 'cuts-pagination';
|
||||||
|
container.className = 'cuts-pagination';
|
||||||
|
|
||||||
|
// Insert after cuts list
|
||||||
|
const cutsList = document.getElementById('cuts-list');
|
||||||
|
if (cutsList && cutsList.parentNode) {
|
||||||
|
cutsList.parentNode.insertBefore(container, cutsList.nextSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
goToPage(page) {
|
||||||
|
if (page < 1 || page > this.totalPages) return;
|
||||||
|
|
||||||
|
this.currentPage = page;
|
||||||
|
this.renderCutsList();
|
||||||
|
|
||||||
|
// Scroll to top of cuts list
|
||||||
|
const cutsList = document.getElementById('cuts-list');
|
||||||
|
if (cutsList) {
|
||||||
|
cutsList.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exportCuts() {
|
exportCuts() {
|
||||||
const exportData = {
|
const exportData = {
|
||||||
version: '1.0',
|
version: '1.0',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user