update filter functionality
This commit is contained in:
parent
464c62d1e5
commit
9431a0502a
10 changed files with 2362 additions and 220 deletions
388
frontend/src/stores/pagination.js
Normal file
388
frontend/src/stores/pagination.js
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const usePaginationStore = defineStore("pagination", {
|
||||
state: () => ({
|
||||
// Store pagination state by table/component name
|
||||
tablePagination: {
|
||||
clients: {
|
||||
first: 0, // Starting index for current page
|
||||
page: 0, // Current page number (0-based)
|
||||
rows: 10, // Items per page
|
||||
totalRecords: 0, // Total number of records available
|
||||
sortField: null, // Current sort field
|
||||
sortOrder: null, // Sort direction (1 for asc, -1 for desc)
|
||||
},
|
||||
jobs: {
|
||||
first: 0,
|
||||
page: 0,
|
||||
rows: 10,
|
||||
totalRecords: 0,
|
||||
sortField: null,
|
||||
sortOrder: null,
|
||||
},
|
||||
timesheets: {
|
||||
first: 0,
|
||||
page: 0,
|
||||
rows: 10,
|
||||
totalRecords: 0,
|
||||
sortField: null,
|
||||
sortOrder: null,
|
||||
},
|
||||
warranties: {
|
||||
first: 0,
|
||||
page: 0,
|
||||
rows: 10,
|
||||
totalRecords: 0,
|
||||
sortField: null,
|
||||
sortOrder: null,
|
||||
},
|
||||
routes: {
|
||||
first: 0,
|
||||
page: 0,
|
||||
rows: 10,
|
||||
totalRecords: 0,
|
||||
sortField: null,
|
||||
sortOrder: null,
|
||||
},
|
||||
},
|
||||
|
||||
// Page data cache: tableName -> { pageKey -> { data, timestamp, filterHash } }
|
||||
pageCache: {},
|
||||
|
||||
// Cache configuration
|
||||
cacheTimeout: 5 * 60 * 1000, // 5 minutes in milliseconds
|
||||
maxCacheSize: 50, // Maximum number of cached pages per table
|
||||
}),
|
||||
|
||||
getters: {
|
||||
// Get pagination state for a specific table
|
||||
getTablePagination: (state) => (tableName) => {
|
||||
return (
|
||||
state.tablePagination[tableName] || {
|
||||
first: 0,
|
||||
page: 0,
|
||||
rows: 10,
|
||||
totalRecords: 0,
|
||||
sortField: null,
|
||||
sortOrder: null,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Calculate total pages for a table
|
||||
getTotalPages: (state) => (tableName) => {
|
||||
const pagination = state.tablePagination[tableName];
|
||||
if (!pagination || pagination.totalRecords === 0) return 0;
|
||||
return Math.ceil(pagination.totalRecords / pagination.rows);
|
||||
},
|
||||
|
||||
// Check if there's a next page
|
||||
hasNextPage: (state) => (tableName) => {
|
||||
const pagination = state.tablePagination[tableName];
|
||||
if (!pagination) return false;
|
||||
return pagination.page + 1 < Math.ceil(pagination.totalRecords / pagination.rows);
|
||||
},
|
||||
|
||||
// Check if there's a previous page
|
||||
hasPreviousPage: (state) => (tableName) => {
|
||||
const pagination = state.tablePagination[tableName];
|
||||
if (!pagination) return false;
|
||||
return pagination.page > 0;
|
||||
},
|
||||
|
||||
// Get current page info for display
|
||||
getPageInfo: (state) => (tableName) => {
|
||||
const pagination = state.tablePagination[tableName];
|
||||
if (!pagination) return { start: 0, end: 0, total: 0 };
|
||||
|
||||
const start = pagination.first + 1;
|
||||
const end = Math.min(pagination.first + pagination.rows, pagination.totalRecords);
|
||||
const total = pagination.totalRecords;
|
||||
|
||||
return { start, end, total };
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
// Initialize pagination for a table if it doesn't exist
|
||||
initializeTablePagination(tableName, options = {}) {
|
||||
if (!this.tablePagination[tableName]) {
|
||||
this.tablePagination[tableName] = {
|
||||
first: 0,
|
||||
page: 0,
|
||||
rows: options.rows || 10,
|
||||
totalRecords: options.totalRecords || 0,
|
||||
sortField: options.sortField || null,
|
||||
sortOrder: options.sortOrder || null,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// Update pagination state for a table
|
||||
updateTablePagination(tableName, paginationData) {
|
||||
if (!this.tablePagination[tableName]) {
|
||||
this.initializeTablePagination(tableName);
|
||||
}
|
||||
|
||||
// Update provided fields
|
||||
Object.keys(paginationData).forEach((key) => {
|
||||
if (paginationData[key] !== undefined) {
|
||||
this.tablePagination[tableName][key] = paginationData[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure consistency between page and first
|
||||
if (paginationData.page !== undefined) {
|
||||
this.tablePagination[tableName].first =
|
||||
paginationData.page * this.tablePagination[tableName].rows;
|
||||
} else if (paginationData.first !== undefined) {
|
||||
this.tablePagination[tableName].page = Math.floor(
|
||||
paginationData.first / this.tablePagination[tableName].rows,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// Set current page
|
||||
setPage(tableName, page) {
|
||||
this.updateTablePagination(tableName, {
|
||||
page: page,
|
||||
first: page * this.getTablePagination(tableName).rows,
|
||||
});
|
||||
},
|
||||
|
||||
// Set rows per page
|
||||
setRowsPerPage(tableName, rows) {
|
||||
const currentPagination = this.getTablePagination(tableName);
|
||||
const newPage = Math.floor(currentPagination.first / rows);
|
||||
|
||||
this.updateTablePagination(tableName, {
|
||||
rows: rows,
|
||||
page: newPage,
|
||||
first: newPage * rows,
|
||||
});
|
||||
},
|
||||
|
||||
// Set total records (usually after API response)
|
||||
setTotalRecords(tableName, totalRecords) {
|
||||
this.updateTablePagination(tableName, {
|
||||
totalRecords: totalRecords,
|
||||
});
|
||||
|
||||
// Ensure current page is valid
|
||||
const currentPagination = this.getTablePagination(tableName);
|
||||
const maxPages = Math.ceil(totalRecords / currentPagination.rows);
|
||||
if (currentPagination.page >= maxPages && maxPages > 0) {
|
||||
this.setPage(tableName, maxPages - 1);
|
||||
}
|
||||
},
|
||||
|
||||
// Set sort information
|
||||
setSorting(tableName, sortField, sortOrder) {
|
||||
this.updateTablePagination(tableName, {
|
||||
sortField: sortField,
|
||||
sortOrder: sortOrder,
|
||||
});
|
||||
},
|
||||
|
||||
// Go to next page
|
||||
nextPage(tableName) {
|
||||
const pagination = this.getTablePagination(tableName);
|
||||
if (this.hasNextPage(tableName)) {
|
||||
this.setPage(tableName, pagination.page + 1);
|
||||
}
|
||||
},
|
||||
|
||||
// Go to previous page
|
||||
previousPage(tableName) {
|
||||
const pagination = this.getTablePagination(tableName);
|
||||
if (this.hasPreviousPage(tableName)) {
|
||||
this.setPage(tableName, pagination.page - 1);
|
||||
}
|
||||
},
|
||||
|
||||
// Go to first page
|
||||
firstPage(tableName) {
|
||||
this.setPage(tableName, 0);
|
||||
},
|
||||
|
||||
// Go to last page
|
||||
lastPage(tableName) {
|
||||
const totalPages = this.getTotalPages(tableName);
|
||||
if (totalPages > 0) {
|
||||
this.setPage(tableName, totalPages - 1);
|
||||
}
|
||||
},
|
||||
|
||||
// Reset pagination to first page (useful when filters change)
|
||||
resetToFirstPage(tableName) {
|
||||
this.updateTablePagination(tableName, {
|
||||
page: 0,
|
||||
first: 0,
|
||||
});
|
||||
},
|
||||
|
||||
// Clear all pagination data for a table
|
||||
clearTablePagination(tableName) {
|
||||
if (this.tablePagination[tableName]) {
|
||||
this.tablePagination[tableName] = {
|
||||
first: 0,
|
||||
page: 0,
|
||||
rows: 10,
|
||||
totalRecords: 0,
|
||||
sortField: null,
|
||||
sortOrder: null,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// Get formatted pagination parameters for API calls
|
||||
getPaginationParams(tableName) {
|
||||
const pagination = this.getTablePagination(tableName);
|
||||
return {
|
||||
page: pagination.page,
|
||||
pageSize: pagination.rows,
|
||||
offset: pagination.first,
|
||||
limit: pagination.rows,
|
||||
sortField: pagination.sortField,
|
||||
sortOrder: pagination.sortOrder,
|
||||
};
|
||||
},
|
||||
|
||||
// Handle PrimeVue lazy pagination event
|
||||
handleLazyLoad(tableName, event) {
|
||||
console.log("Pagination lazy load event:", event);
|
||||
|
||||
const page = Math.floor(event.first / event.rows);
|
||||
|
||||
this.updateTablePagination(tableName, {
|
||||
first: event.first,
|
||||
page: page,
|
||||
rows: event.rows,
|
||||
sortField: event.sortField,
|
||||
sortOrder: event.sortOrder,
|
||||
});
|
||||
|
||||
return this.getPaginationParams(tableName);
|
||||
},
|
||||
|
||||
// Cache management methods
|
||||
generateCacheKey(page, pageSize, sortField, sortOrder, filters) {
|
||||
const filterHash = this.hashFilters(filters);
|
||||
return `${page}-${pageSize}-${sortField || "none"}-${sortOrder || 0}-${filterHash}`;
|
||||
},
|
||||
|
||||
hashFilters(filters) {
|
||||
if (!filters || Object.keys(filters).length === 0) return "no-filters";
|
||||
|
||||
const sortedKeys = Object.keys(filters).sort();
|
||||
const filterString = sortedKeys
|
||||
.map((key) => {
|
||||
const filter = filters[key];
|
||||
const value = filter?.value || "";
|
||||
const matchMode = filter?.matchMode || "";
|
||||
return `${key}:${value}:${matchMode}`;
|
||||
})
|
||||
.join("|");
|
||||
|
||||
// Simple hash function
|
||||
let hash = 0;
|
||||
for (let i = 0; i < filterString.length; i++) {
|
||||
const char = filterString.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return Math.abs(hash).toString(36);
|
||||
},
|
||||
|
||||
getCachedPage(tableName, page, pageSize, sortField, sortOrder, filters) {
|
||||
if (!this.pageCache[tableName]) return null;
|
||||
|
||||
const cacheKey = this.generateCacheKey(page, pageSize, sortField, sortOrder, filters);
|
||||
const cachedEntry = this.pageCache[tableName][cacheKey];
|
||||
|
||||
if (!cachedEntry) return null;
|
||||
|
||||
// Check if cache entry is still valid (not expired)
|
||||
const now = Date.now();
|
||||
if (now - cachedEntry.timestamp > this.cacheTimeout) {
|
||||
// Cache expired, remove it
|
||||
delete this.pageCache[tableName][cacheKey];
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(`Cache HIT for ${tableName} page ${page + 1}`);
|
||||
return cachedEntry.data;
|
||||
},
|
||||
|
||||
setCachedPage(tableName, page, pageSize, sortField, sortOrder, filters, data) {
|
||||
if (!this.pageCache[tableName]) {
|
||||
this.pageCache[tableName] = {};
|
||||
}
|
||||
|
||||
const cacheKey = this.generateCacheKey(page, pageSize, sortField, sortOrder, filters);
|
||||
|
||||
// Clean up old cache entries if we're at the limit
|
||||
const cacheKeys = Object.keys(this.pageCache[tableName]);
|
||||
if (cacheKeys.length >= this.maxCacheSize) {
|
||||
// Remove oldest entries (simple FIFO approach)
|
||||
const sortedEntries = cacheKeys
|
||||
.map((key) => ({ key, timestamp: this.pageCache[tableName][key].timestamp }))
|
||||
.sort((a, b) => a.timestamp - b.timestamp);
|
||||
|
||||
// Remove oldest 25% of entries
|
||||
const toRemove = Math.floor(this.maxCacheSize * 0.25);
|
||||
for (let i = 0; i < toRemove; i++) {
|
||||
delete this.pageCache[tableName][sortedEntries[i].key];
|
||||
}
|
||||
}
|
||||
|
||||
this.pageCache[tableName][cacheKey] = {
|
||||
data: JSON.parse(JSON.stringify(data)), // Deep clone to prevent mutations
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
console.log(
|
||||
`Cache SET for ${tableName} page ${page + 1}, total cached pages: ${Object.keys(this.pageCache[tableName]).length}`,
|
||||
);
|
||||
},
|
||||
|
||||
clearTableCache(tableName) {
|
||||
if (this.pageCache[tableName]) {
|
||||
delete this.pageCache[tableName];
|
||||
console.log(`Cache cleared for ${tableName}`);
|
||||
}
|
||||
},
|
||||
|
||||
clearExpiredCache() {
|
||||
const now = Date.now();
|
||||
Object.keys(this.pageCache).forEach((tableName) => {
|
||||
Object.keys(this.pageCache[tableName]).forEach((cacheKey) => {
|
||||
if (now - this.pageCache[tableName][cacheKey].timestamp > this.cacheTimeout) {
|
||||
delete this.pageCache[tableName][cacheKey];
|
||||
}
|
||||
});
|
||||
|
||||
// If no cache entries left for this table, remove the table key
|
||||
if (Object.keys(this.pageCache[tableName]).length === 0) {
|
||||
delete this.pageCache[tableName];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getCacheStats(tableName) {
|
||||
if (!this.pageCache[tableName]) {
|
||||
return { totalPages: 0, totalSize: 0 };
|
||||
}
|
||||
|
||||
const pages = Object.keys(this.pageCache[tableName]);
|
||||
const totalSize = pages.reduce((size, key) => {
|
||||
return size + JSON.stringify(this.pageCache[tableName][key]).length;
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
totalPages: pages.length,
|
||||
totalSize: Math.round(totalSize / 1024) + " KB",
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue