From cf95a3c01984d2bf17167bf76da72877cd033ba7 Mon Sep 17 00:00:00 2001 From: Kenil_KB Date: Thu, 30 Oct 2025 12:27:26 +0530 Subject: [PATCH] Ui updated --- ...ons_Only_Paginated.postman_collection.json | 154 ++++++++++++++++++ public/js/transactions.js | 68 +++++++- public/transactions.html | 16 +- src/routes/api.js | 93 ++++++++--- 4 files changed, 301 insertions(+), 30 deletions(-) create mode 100644 Transactions_Only_Paginated.postman_collection.json diff --git a/Transactions_Only_Paginated.postman_collection.json b/Transactions_Only_Paginated.postman_collection.json new file mode 100644 index 0000000..74b087f --- /dev/null +++ b/Transactions_Only_Paginated.postman_collection.json @@ -0,0 +1,154 @@ +{ + "info": { + "name": "Dubai DLD - Transactions Only (Paginated)", + "_postman_id": "c0a5b3e6-6b0f-4b9e-9f77-transactions-only-v2", + "description": "Only /api/transactions/recent examples, covering filters, legacy limit, and server-side pagination.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "variable": [ + { "key": "baseUrl", "value": "http://localhost:3000" } + ], + "item": [ + { + "name": "Recent - No filters (all rows, no LIMIT)", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { "raw": "{{baseUrl}}/api/transactions/recent", "host": ["{{baseUrl}}"], "path": ["api","transactions","recent"] } + } + }, + { + "name": "Legacy: limit only (top N without OFFSET)", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { + "raw": "{{baseUrl}}/api/transactions/recent?limit=7", + "host": ["{{baseUrl}}"], + "path": ["api","transactions","recent"], + "query": [ {"key":"limit","value":"7"} ] + } + } + }, + { + "name": "Pagination: page 1, size 30", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { + "raw": "{{baseUrl}}/api/transactions/recent?page=1&page_size=30", + "host": ["{{baseUrl}}"], + "path": ["api","transactions","recent"], + "query": [ {"key":"page","value":"1"}, {"key":"page_size","value":"30"} ] + } + } + }, + { + "name": "Pagination: page 2, size 30", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { + "raw": "{{baseUrl}}/api/transactions/recent?page=2&page_size=30", + "host": ["{{baseUrl}}"], + "path": ["api","transactions","recent"], + "query": [ {"key":"page","value":"2"}, {"key":"page_size","value":"30"} ] + } + } + }, + { + "name": "Filter: area_name + pagination", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { + "raw": "{{baseUrl}}/api/transactions/recent?area_name=business%20bay&page=1&page_size=50", + "host": ["{{baseUrl}}"], + "path": ["api","transactions","recent"], + "query": [ + {"key":"area_name","value":"business bay"}, + {"key":"page","value":"1"}, + {"key":"page_size","value":"50"} + ] + } + } + }, + { + "name": "Filter: property_type + beds + pagination", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { + "raw": "{{baseUrl}}/api/transactions/recent?property_type=apartment&beds=2&page=1&page_size=30", + "host": ["{{baseUrl}}"], + "path": ["api","transactions","recent"], + "query": [ + {"key":"property_type","value":"apartment"}, + {"key":"beds","value":"2"}, + {"key":"page","value":"1"}, + {"key":"page_size","value":"30"} + ] + } + } + }, + { + "name": "Filter: size range + pagination", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { + "raw": "{{baseUrl}}/api/transactions/recent?size_min=1000&size_max=5000&page=1&page_size=30", + "host": ["{{baseUrl}}"], + "path": ["api","transactions","recent"], + "query": [ + {"key":"size_min","value":"1000"}, + {"key":"size_max","value":"5000"}, + {"key":"page","value":"1"}, + {"key":"page_size","value":"30"} + ] + } + } + }, + { + "name": "Filter: project + pagination", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { + "raw": "{{baseUrl}}/api/transactions/recent?project=29%20boulevard&page=1&page_size=30", + "host": ["{{baseUrl}}"], + "path": ["api","transactions","recent"], + "query": [ + {"key":"project","value":"29 boulevard"}, + {"key":"page","value":"1"}, + {"key":"page_size","value":"30"} + ] + } + } + }, + { + "name": "Combined filters + pagination", + "request": { + "method": "GET", + "header": [ { "key": "Accept", "value": "application/json" } ], + "url": { + "raw": "{{baseUrl}}/api/transactions/recent?area_name=business%20bay&property_type=apartment&beds=2&project=29%20boulevard&size_min=800&size_max=1600&page=1&page_size=50", + "host": ["{{baseUrl}}"], + "path": ["api","transactions","recent"], + "query": [ + {"key":"area_name","value":"business bay"}, + {"key":"property_type","value":"apartment"}, + {"key":"beds","value":"2"}, + {"key":"project","value":"29 boulevard"}, + {"key":"size_min","value":"800"}, + {"key":"size_max","value":"1600"}, + {"key":"page","value":"1"}, + {"key":"page_size","value":"50"} + ] + } + } + } + ] +} + + diff --git a/public/js/transactions.js b/public/js/transactions.js index bfd8582..c8b5fee 100644 --- a/public/js/transactions.js +++ b/public/js/transactions.js @@ -128,7 +128,10 @@ function formatCurrency(amount) { return 'AED ' + parseFloat(amount).toLocaleString('en-US', { maximumFractionDigits: 0 }); } -async function searchTransactions() { +let currentPage = 1; +let lastPaging = { total: 0, page: null, page_size: 30, total_pages: 1, has_next: false, has_prev: false }; + +async function searchTransactions(pageOverride) { const formData = new FormData(document.getElementById('filtersForm')); const params = new URLSearchParams(); @@ -138,7 +141,8 @@ async function searchTransactions() { const size_max = formData.get('size_max'); const beds = formData.get('beds'); const project = formData.get('project'); - const limit = formData.get('limit') || '30'; + const pageSize = formData.get('page_size') || '30'; + const page = pageOverride != null ? pageOverride : (formData.get('page') || currentPage || 1); if (area_name) params.append('area_name', area_name); if (property_type && property_type !== 'all') params.append('property_type', property_type); @@ -146,7 +150,9 @@ async function searchTransactions() { if (size_max && !isNaN(parseFloat(size_max))) params.append('size_max', size_max); if (beds && beds !== 'all') params.append('beds', beds); if (project && project !== 'all') params.append('project', project); - params.append('limit', limit); + // pagination params + if (page) params.append('page', page); + if (pageSize) params.append('page_size', pageSize); showLoading(true); hideError(); @@ -156,6 +162,15 @@ async function searchTransactions() { const response = await fetch(`${API_BASE}/transactions/recent?${params.toString()}`); const data = await response.json(); if (data.success) { + currentPage = data.data.page || 1; + lastPaging = { + total: data.data.total ?? data.data.count, + page: data.data.page, + page_size: data.data.page_size ?? parseInt(pageSize,10), + total_pages: data.data.total_pages ?? 1, + has_next: !!data.data.has_next, + has_prev: !!data.data.has_prev + }; displayResults(data.data); } else { showError(data.message || 'Failed to fetch transactions'); @@ -168,10 +183,24 @@ async function searchTransactions() { } function displayResults(data) { - const { transactions, count } = data; + const { transactions, count, total, page, page_size, total_pages, has_next, has_prev } = data; const tbody = document.getElementById('transactionsBody'); const resultsCount = document.getElementById('resultsCount'); - resultsCount.textContent = `Found ${count} transaction${count !== 1 ? 's' : ''}`; + const showing = count; + const grandTotal = total ?? count; + resultsCount.textContent = `Showing ${showing} of ${grandTotal} transactions`; + const pageInfo = document.getElementById('pageInfo'); + if (pageInfo) { + if (page && total_pages) { + pageInfo.textContent = `Page ${page} of ${total_pages}`; + } else { + pageInfo.textContent = ''; + } + } + const prevBtn = document.getElementById('prevPage'); + const nextBtn = document.getElementById('nextPage'); + if (prevBtn) prevBtn.disabled = !(has_prev); + if (nextBtn) nextBtn.disabled = !(has_next); if (transactions.length === 0) { const colCount = document.querySelectorAll('#transactionsTable thead th').length; @@ -300,13 +329,40 @@ document.addEventListener('DOMContentLoaded', () => { document.getElementById('filtersForm').addEventListener('submit', async (e) => { e.preventDefault(); - await searchTransactions(); + currentPage = 1; + const pageInput = document.getElementById('page'); + if (pageInput) pageInput.value = '1'; + await searchTransactions(1); }); const resetBtn = document.getElementById('resetBtn'); if (resetBtn) { resetBtn.addEventListener('click', resetFilters); } + + // pagination buttons + const prevBtn = document.getElementById('prevPage'); + const nextBtn = document.getElementById('nextPage'); + if (prevBtn) { + prevBtn.addEventListener('click', async () => { + if (lastPaging.has_prev && currentPage > 1) { + currentPage -= 1; + const pageInput2 = document.getElementById('page'); + if (pageInput2) pageInput2.value = String(currentPage); + await searchTransactions(currentPage); + } + }); + } + if (nextBtn) { + nextBtn.addEventListener('click', async () => { + if (lastPaging.has_next) { + currentPage += 1; + const pageInput3 = document.getElementById('page'); + if (pageInput3) pageInput3.value = String(currentPage); + await searchTransactions(currentPage); + } + }); + } }); diff --git a/public/transactions.html b/public/transactions.html index 2c74431..4d15e72 100644 --- a/public/transactions.html +++ b/public/transactions.html @@ -411,8 +411,15 @@
- - + + +
@@ -452,6 +459,11 @@