540 lines
23 KiB
JavaScript
540 lines
23 KiB
JavaScript
// Properties page logic for unified rent/sale endpoint
|
|
|
|
const API_BASE = '/api';
|
|
|
|
// Combined area dropdown values (from both transactions and rents)
|
|
const areas = [
|
|
'abu hail', 'al athbah', 'al aweer first', 'al aweer second', 'al bada', 'al baraha',
|
|
'al barari', 'al barsha first', 'al barsha second', 'al barsha third', 'al barshaa south first',
|
|
'al barshaa south second', 'al barshaa south third', 'al buteen', 'al dhagaya', 'al eyas',
|
|
'al furjan', 'al garhoud', 'al goze first', 'al goze fourth', 'al goze industrial first',
|
|
'al goze industrial fourth', 'al goze industrial second', 'al goze industrial third', 'al goze third',
|
|
'al hamriya', 'al hebiah fifth', 'al hebiah first', 'al hebiah fourth', 'al hebiah sixth',
|
|
'al hebiah third', 'al hudaiba', 'al jadaf', 'al jafliya', 'al karama', 'al khabeesi',
|
|
'al khail heights', 'al khairan first', 'al khawaneej first', 'al khawaneej second', 'al kheeran',
|
|
'al kifaf', 'al lusaily', 'al mamzer', 'al manara', 'al mararr', 'al merkadh',
|
|
'al mizhar first', 'al mizhar fourth', 'al mizhar second', 'al mizhar third', 'al murqabat',
|
|
'al muteena', 'al nahda first', 'al nahda second', 'al qusais', 'al qusais industrial fifth',
|
|
'al qusais industrial first', 'al qusais industrial fourth', 'al qusais industrial third', 'al raffa',
|
|
'al ras', 'al rashidiya', 'al rega', 'al rowaiyah third', 'al saffa first', 'al saffa second',
|
|
'al safouh first', 'al satwa', 'al suq al kabeer', 'al thanyah fifth', 'al thanyah third',
|
|
'al ttay', 'al twar fifth', 'al twar first', 'al twar fourth', 'al twar second', 'al twar third',
|
|
'al waha', 'al waheda', 'al warqa first', 'al warqa fourth', 'al warqa second', 'al warqa third',
|
|
'al warsan second', 'al warsan third', 'al wasl', 'al yelayiss 1', 'al yelayiss 2', 'al yelayiss 5',
|
|
'al yufrah 1', 'arabian ranches i', 'arabian ranches ii', 'arabian ranches iii',
|
|
'arabian ranches polo club', 'arjan', 'barsha heights', 'bluewaters', 'bukadra', 'burj khalifa',
|
|
'business bay', 'business park', 'cherrywoods', 'city of arabia', 'city walk', 'damac hills',
|
|
'discovery gardens', 'dmcc-ez2', 'down town jabal ali', 'dubai creek harbour', 'dubai design district',
|
|
'dubai golf city', 'dubai harbour', 'dubai healthcare city - phase 1', 'dubai healthcare city - phase 2',
|
|
'dubai hills', 'dubai industrial city', 'dubai international airport', 'dubai investment park first',
|
|
'dubai investment park second', 'dubai land residence complex', 'dubai lifestyle city', 'dubai marina',
|
|
'dubai maritime city', 'dubai production city', 'dubai science park', 'dubai south', 'dubai sports city',
|
|
'dubai studio city', 'dubai water canal', 'dubai water front', 'emaar south', 'emirate living',
|
|
'eyal nasser', 'falcon city of wonders', 'ghadeer al tair', 'ghadeer barashy', 'grand hills dubai',
|
|
'grand views', 'hadaeq sheikh mohammed bin rashid', 'hessyan second', 'hor al anz', 'hor al anz east',
|
|
'horizon', 'international city ph 1', 'international city ph 2 & 3', 'island 2', 'jabal ali first',
|
|
'jabal ali industrial first', 'jabal ali industrial second', 'jabel ali hills', 'jaddaf waterfront',
|
|
'jumeira bay', 'jumeirah beach residence', 'jumeirah first', 'jumeirah golf', 'jumeirah heights',
|
|
'jumeirah islands', 'jumeirah lakes towers', 'jumeirah living', 'jumeirah park', 'jumeirah second',
|
|
'jumeirah third', 'jumeirah village circle', 'jumeirah village triangle', 'la mer', 'lehbab first',
|
|
'lehbab second', 'living legends', 'liwan', 'liwan 2', 'madinat al mataar', 'madinat dubai almelaheyah',
|
|
'madinat hind 3', 'madinat hind 4', 'madinat latifa', 'majan', 'mankhool', 'margham', 'marsa dubai',
|
|
'mbr district 1', 'mbr district 7', "me'aisem first", "me'aisem second", 'medyan race course villas',
|
|
'mena jabal ali', 'meydan avenue', 'meydan one', 'millennium', 'mina rashid', 'mira', 'mirdif',
|
|
'motor city', 'mudon', 'muhaisanah first', 'muhaisanah fourth', 'muhaisanah second', 'muhaisanah third',
|
|
'mushrif', 'nad al hamar', 'nad al sheba gardens', 'nad al shiba first', 'nad al shiba fourth',
|
|
'nad al shiba second', 'nad al shiba third', 'nad shamma', 'nadd hessa', 'naif', 'nazwah', 'oud metha',
|
|
'palm deira', 'palm jabal ali', 'palm jumeirah', 'palmarosa', 'pearl jumeira', 'polo townhouses igo',
|
|
'port saeed', 'ras al khor', 'ras al khor industrial first', 'ras al khor industrial second',
|
|
'ras al khor industrial third', 'rega al buteen', 'remraam', 'rukan', 'saih shuaib 1', 'saih shuaib 2',
|
|
'saih shuaib 3', 'saih shuaib 4', 'sama al jadaf', 'serena', 'silicon oasis', 'sobha heartland',
|
|
'sufouh gardens', 'sustainable city', 'tecom site a', 'tecom site d', 'the beach', 'the field',
|
|
'the greens', 'the lakes', 'the valley', 'the villa', 'the world', 'tilal al ghaf', 'town square',
|
|
'trade center first', 'trade center second', 'um al sheif', 'um hurair first', 'um hurair second',
|
|
'um ramool', 'um suqaim first', 'um suqaim second', 'um suqaim third', 'umm addamin', 'villanova',
|
|
'wadi al amardi', 'wadi al safa 2', 'wadi al safa 3', 'wadi al safa 4', 'wadi al safa 5',
|
|
'wadi al safa 6', 'wadi al safa 7', 'warsan first', 'warsan fourth', 'yaraah', 'zaabeel first', 'zaabeel second'
|
|
];
|
|
|
|
// Property types for rent
|
|
const rentPropertyTypes = [
|
|
{ value: 'all', label: 'All Types' },
|
|
{ value: 'unit', label: 'Unit' },
|
|
{ value: 'villa', label: 'Villa' },
|
|
{ value: 'virtual unit', label: 'Virtual Unit' },
|
|
{ value: 'land', label: 'Land' },
|
|
{ value: 'building', label: 'Building' }
|
|
];
|
|
|
|
// Property types for sale
|
|
const salePropertyTypes = [
|
|
{ value: 'all', label: 'All Types' },
|
|
{ value: 'building', label: 'Building' },
|
|
{ value: 'land', label: 'Land' },
|
|
{ value: 'unit', label: 'Unit' }
|
|
];
|
|
|
|
// Combined projects list (simplified - you may want to load dynamically)
|
|
const projects = [
|
|
'014 tower', '08 life residences', '1 residences', '10 oxford by iman', '105 residences by kamdar',
|
|
'11 hills park', '15 cascade', '15 northside', '161 jumeirah lane', '17 icon bay',
|
|
'171 garden heights', '1wood residence', '2020 marquis', '23 marina',
|
|
'29 boulevard', '310 riverside crescent', '311 boulevard by bam eskan',
|
|
'empire estates', 'empire heights', 'starz tower by danube', 'azizi feirouz i',
|
|
'candace acacia hotel apartments', 'burj al nujoom', 'azizi riviera 35'
|
|
// Add more projects as needed
|
|
];
|
|
|
|
let currentType = 'rent';
|
|
let currentPage = 1;
|
|
let lastPaging = { total: 0, page: null, page_size: 30, total_pages: 1, has_next: false, has_prev: false };
|
|
|
|
function populateAreas() {
|
|
const areaSelect = document.getElementById('area_name');
|
|
areas.forEach(area => {
|
|
const option = document.createElement('option');
|
|
option.value = area;
|
|
option.textContent = area.charAt(0).toUpperCase() + area.slice(1).replace(/\b\w/g, l => l.toUpperCase());
|
|
areaSelect.appendChild(option);
|
|
});
|
|
}
|
|
|
|
function loadProjects() {
|
|
const projectSelect = document.getElementById('project');
|
|
const sortedProjects = projects.slice().sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
|
|
sortedProjects.forEach(project => {
|
|
const option = document.createElement('option');
|
|
option.value = project;
|
|
option.textContent = project.length > 70 ? project.substring(0, 70) + '...' : project;
|
|
projectSelect.appendChild(option);
|
|
});
|
|
}
|
|
|
|
function updatePropertyTypes() {
|
|
const propertyTypeSelect = document.getElementById('property_type');
|
|
propertyTypeSelect.innerHTML = '';
|
|
|
|
const types = currentType === 'rent' ? rentPropertyTypes : salePropertyTypes;
|
|
types.forEach(type => {
|
|
const option = document.createElement('option');
|
|
option.value = type.value;
|
|
option.textContent = type.label;
|
|
propertyTypeSelect.appendChild(option);
|
|
});
|
|
}
|
|
|
|
function updateTypeDependentFields() {
|
|
const typeInput = document.getElementById('type');
|
|
typeInput.value = currentType;
|
|
|
|
// Show/hide rooms vs beds
|
|
const roomsGroup = document.getElementById('roomsGroup');
|
|
const bedsGroup = document.getElementById('bedsGroup');
|
|
|
|
if (currentType === 'rent') {
|
|
roomsGroup.style.display = 'flex';
|
|
bedsGroup.style.display = 'none';
|
|
document.getElementById('beds').value = 'all';
|
|
} else {
|
|
roomsGroup.style.display = 'none';
|
|
bedsGroup.style.display = 'flex';
|
|
document.getElementById('rooms').value = 'all';
|
|
}
|
|
|
|
updatePropertyTypes();
|
|
updateTableHeaders();
|
|
}
|
|
|
|
function updateTableHeaders() {
|
|
const tableHead = document.getElementById('tableHead');
|
|
|
|
if (currentType === 'rent') {
|
|
tableHead.innerHTML = `
|
|
<tr>
|
|
<th>rent_id</th>
|
|
<th>registration_date</th>
|
|
<th>start_date</th>
|
|
<th>end_date</th>
|
|
<th>version_en</th>
|
|
<th>area_en</th>
|
|
<th>contract_amount</th>
|
|
<th>annual_amount</th>
|
|
<th>is_free_hold_en</th>
|
|
<th>actual_area</th>
|
|
<th>prop_type_en</th>
|
|
<th>prop_sub_type_en</th>
|
|
<th>rooms</th>
|
|
<th>usage_en</th>
|
|
<th>nearest_metro_en</th>
|
|
<th>nearest_mall_en</th>
|
|
<th>nearest_landmark_en</th>
|
|
<th>parking</th>
|
|
<th>total_properties</th>
|
|
<th>master_project_en</th>
|
|
<th>project_en</th>
|
|
<th>created_at</th>
|
|
<th>updated_at</th>
|
|
</tr>
|
|
`;
|
|
} else {
|
|
tableHead.innerHTML = `
|
|
<tr>
|
|
<th>transaction_id</th>
|
|
<th>transaction_number</th>
|
|
<th>instance_date</th>
|
|
<th>group_en</th>
|
|
<th>procedure_en</th>
|
|
<th>is_offplan_en</th>
|
|
<th>is_free_hold_en</th>
|
|
<th>usage_en</th>
|
|
<th>area_en</th>
|
|
<th>prop_type_en</th>
|
|
<th>prop_sb_type_en</th>
|
|
<th>trans_value</th>
|
|
<th>procedure_area</th>
|
|
<th>actual_area</th>
|
|
<th>rooms_en</th>
|
|
<th>parking</th>
|
|
<th>nearest_metro_en</th>
|
|
<th>nearest_mall_en</th>
|
|
<th>nearest_landmark_en</th>
|
|
<th>total_buyer</th>
|
|
<th>total_seller</th>
|
|
<th>master_project_en</th>
|
|
<th>project_en</th>
|
|
<th>created_at</th>
|
|
<th>updated_at</th>
|
|
</tr>
|
|
`;
|
|
}
|
|
}
|
|
|
|
function showLoading(show) {
|
|
document.getElementById('loading').style.display = show ? 'block' : 'none';
|
|
}
|
|
|
|
function showError(message) {
|
|
const errorDiv = document.getElementById('error');
|
|
errorDiv.textContent = message;
|
|
errorDiv.style.display = 'block';
|
|
}
|
|
|
|
function hideError() {
|
|
document.getElementById('error').style.display = 'none';
|
|
}
|
|
|
|
function showResults() {
|
|
document.getElementById('resultsSection').style.display = 'block';
|
|
}
|
|
|
|
function hideResults() {
|
|
document.getElementById('resultsSection').style.display = 'none';
|
|
}
|
|
|
|
function formatDate(dateString) {
|
|
if (!dateString) return 'N/A';
|
|
const date = new Date(dateString);
|
|
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
|
}
|
|
|
|
function formatNumber(num, decimals = 0) {
|
|
if (num === null || num === undefined) return 'N/A';
|
|
return parseFloat(num).toLocaleString('en-US', { maximumFractionDigits: decimals });
|
|
}
|
|
|
|
function formatCurrency(amount) {
|
|
if (!amount) return 'N/A';
|
|
return 'AED ' + parseFloat(amount).toLocaleString('en-US', { maximumFractionDigits: 0 });
|
|
}
|
|
|
|
async function searchProperties(pageOverride) {
|
|
const formData = new FormData(document.getElementById('filtersForm'));
|
|
const params = new URLSearchParams();
|
|
|
|
const type = formData.get('type') || currentType;
|
|
const area_name = formData.get('area_name');
|
|
const property_type = formData.get('property_type');
|
|
const size_min = formData.get('size_min');
|
|
const size_max = formData.get('size_max');
|
|
const rooms = formData.get('rooms');
|
|
const beds = formData.get('beds');
|
|
const project = formData.get('project');
|
|
const pageSize = formData.get('page_size') || '30';
|
|
const page = pageOverride != null ? pageOverride : (formData.get('page') || currentPage || 1);
|
|
|
|
params.append('type', type);
|
|
if (area_name) params.append('area_name', area_name);
|
|
if (property_type && property_type !== 'all') params.append('property_type', property_type);
|
|
if (size_min && !isNaN(parseFloat(size_min))) params.append('size_min', size_min);
|
|
if (size_max && !isNaN(parseFloat(size_max))) params.append('size_max', size_max);
|
|
if (type === 'rent' && rooms && rooms !== 'all') {
|
|
params.append('rooms', rooms);
|
|
} else if (type === 'sale' && beds && beds !== 'all') {
|
|
params.append('beds', beds);
|
|
}
|
|
if (project && project !== 'all') params.append('project', project);
|
|
if (page) params.append('page', page);
|
|
if (pageSize) params.append('page_size', pageSize);
|
|
|
|
showLoading(true);
|
|
hideError();
|
|
hideResults();
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE}/properties/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 properties');
|
|
}
|
|
} catch (error) {
|
|
showError('Network error: ' + error.message);
|
|
} finally {
|
|
showLoading(false);
|
|
}
|
|
}
|
|
|
|
function displayResults(data) {
|
|
const dataKey = currentType === 'rent' ? 'rents' : 'transactions';
|
|
const results = data[dataKey] || [];
|
|
const { count, total, page, page_size, total_pages, has_next, has_prev } = data;
|
|
const tbody = document.getElementById('propertiesBody');
|
|
const resultsCount = document.getElementById('resultsCount');
|
|
const showing = count;
|
|
const grandTotal = total ?? count;
|
|
|
|
resultsCount.textContent = `Showing ${showing} of ${grandTotal} ${currentType === 'rent' ? 'rents' : '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 (results.length === 0) {
|
|
const colCount = document.querySelectorAll('#propertiesTable thead th').length;
|
|
tbody.innerHTML = (
|
|
'<tr>' +
|
|
`<td colspan="${colCount}" class="no-results">` +
|
|
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">' +
|
|
'<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>' +
|
|
'</svg>' +
|
|
`<div>No ${currentType === 'rent' ? 'rents' : 'transactions'} found matching your filters</div>` +
|
|
'</td>' +
|
|
'</tr>'
|
|
);
|
|
showResults();
|
|
return;
|
|
}
|
|
|
|
if (currentType === 'rent') {
|
|
tbody.innerHTML = results.map(r => (
|
|
'<tr>' +
|
|
`<td>${r.rent_id ?? 'N/A'}</td>` +
|
|
`<td>${formatDate(r.registration_date)}</td>` +
|
|
`<td>${formatDate(r.start_date)}</td>` +
|
|
`<td>${formatDate(r.end_date)}</td>` +
|
|
`<td>${r.version_en ?? 'N/A'}</td>` +
|
|
`<td>${r.area_en ?? 'N/A'}</td>` +
|
|
`<td>${formatCurrency(r.contract_amount)}</td>` +
|
|
`<td>${formatCurrency(r.annual_amount)}</td>` +
|
|
`<td>${r.is_free_hold_en ?? 'N/A'}</td>` +
|
|
`<td>${formatNumber(r.actual_area)}</td>` +
|
|
`<td>${r.prop_type_en ?? 'N/A'}</td>` +
|
|
`<td>${r.prop_sub_type_en ?? 'N/A'}</td>` +
|
|
`<td>${r.rooms != null ? formatNumber(r.rooms, 1) : 'N/A'}</td>` +
|
|
`<td>${r.usage_en ?? 'N/A'}</td>` +
|
|
`<td>${r.nearest_metro_en ?? 'N/A'}</td>` +
|
|
`<td>${r.nearest_mall_en ?? 'N/A'}</td>` +
|
|
`<td>${r.nearest_landmark_en ?? 'N/A'}</td>` +
|
|
`<td>${r.parking != null ? formatNumber(r.parking, 1) : 'N/A'}</td>` +
|
|
`<td>${r.total_properties ?? '0'}</td>` +
|
|
`<td>${r.master_project_en ?? 'N/A'}</td>` +
|
|
`<td>${r.project_en ?? 'N/A'}</td>` +
|
|
`<td>${formatDate(r.created_at)}</td>` +
|
|
`<td>${formatDate(r.updated_at)}</td>` +
|
|
'</tr>'
|
|
)).join('');
|
|
} else {
|
|
tbody.innerHTML = results.map(t => (
|
|
'<tr>' +
|
|
`<td>${t.transaction_id ?? 'N/A'}</td>` +
|
|
`<td>${t.transaction_number ?? 'N/A'}</td>` +
|
|
`<td>${formatDate(t.instance_date)}</td>` +
|
|
`<td>${t.group_en ?? 'N/A'}</td>` +
|
|
`<td>${t.procedure_en ?? 'N/A'}</td>` +
|
|
`<td>${t.is_offplan_en ?? 'N/A'}</td>` +
|
|
`<td>${t.is_free_hold_en ?? 'N/A'}</td>` +
|
|
`<td>${t.usage_en ?? 'N/A'}</td>` +
|
|
`<td>${t.area_en ?? 'N/A'}</td>` +
|
|
`<td>${t.prop_type_en ?? 'N/A'}</td>` +
|
|
`<td>${t.prop_sb_type_en ?? 'N/A'}</td>` +
|
|
`<td>${formatCurrency(t.trans_value)}</td>` +
|
|
`<td>${t.procedure_area != null ? formatNumber(t.procedure_area) : 'N/A'}</td>` +
|
|
`<td>${t.actual_area != null ? formatNumber(t.actual_area) : 'N/A'}</td>` +
|
|
`<td>${t.rooms_en ?? 'N/A'}</td>` +
|
|
`<td>${t.parking ?? 'N/A'}</td>` +
|
|
`<td>${t.nearest_metro_en ?? 'N/A'}</td>` +
|
|
`<td>${t.nearest_mall_en ?? 'N/A'}</td>` +
|
|
`<td>${t.nearest_landmark_en ?? 'N/A'}</td>` +
|
|
`<td>${t.total_buyer ?? '0'}</td>` +
|
|
`<td>${t.total_seller ?? '0'}</td>` +
|
|
`<td>${t.master_project_en ?? 'N/A'}</td>` +
|
|
`<td>${t.project_en ?? 'N/A'}</td>` +
|
|
`<td>${formatDate(t.created_at)}</td>` +
|
|
`<td>${formatDate(t.updated_at)}</td>` +
|
|
'</tr>'
|
|
)).join('');
|
|
}
|
|
|
|
showResults();
|
|
}
|
|
|
|
function resetFilters() {
|
|
document.getElementById('filtersForm').reset();
|
|
document.getElementById('size_min').value = '';
|
|
document.getElementById('size_max').value = '';
|
|
document.getElementById('sizeValue').textContent = 'All Sizes';
|
|
document.getElementById('type_rent').checked = true;
|
|
currentType = 'rent';
|
|
updateTypeDependentFields();
|
|
hideResults();
|
|
hideError();
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
populateAreas();
|
|
loadProjects();
|
|
updateTypeDependentFields();
|
|
|
|
// Type selector radio buttons
|
|
document.querySelectorAll('input[name="type"]').forEach(radio => {
|
|
radio.addEventListener('change', (e) => {
|
|
currentType = e.target.value;
|
|
updateTypeDependentFields();
|
|
hideResults();
|
|
hideError();
|
|
});
|
|
});
|
|
|
|
// Size slider setup
|
|
const sizeMinHidden = document.getElementById('size_min');
|
|
const sizeMaxHidden = document.getElementById('size_max');
|
|
const sizeMinRange = document.getElementById('size_min_range');
|
|
const sizeMaxRange = document.getElementById('size_max_range');
|
|
const sizeFill = document.getElementById('sizeFill');
|
|
const sizeValue = document.getElementById('sizeValue');
|
|
const MIN_VAL = parseFloat(sizeMinRange.min);
|
|
const MAX_VAL = parseFloat(sizeMinRange.max);
|
|
const STEP = parseFloat(sizeMinRange.step) || 100;
|
|
|
|
function updateSizeLabel() {
|
|
const min = parseFloat(sizeMinRange.value);
|
|
const max = parseFloat(sizeMaxRange.value);
|
|
const hasMin = !isNaN(min);
|
|
const hasMax = !isNaN(max);
|
|
if (!hasMin && !hasMax) {
|
|
sizeValue.textContent = 'All Sizes';
|
|
} else if (hasMin && hasMax) {
|
|
sizeValue.textContent = `${min.toLocaleString('en-US')} - ${max.toLocaleString('en-US')} sq.ft`;
|
|
} else if (hasMin) {
|
|
sizeValue.textContent = `≥ ${min.toLocaleString('en-US')} sq.ft`;
|
|
} else {
|
|
sizeValue.textContent = `≤ ${max.toLocaleString('en-US')} sq.ft`;
|
|
}
|
|
sizeMinHidden.value = (min > MIN_VAL) ? min : '';
|
|
sizeMaxHidden.value = (max < MAX_VAL) ? max : '';
|
|
const left = ((Math.max(MIN_VAL, Math.min(min, max)) - MIN_VAL) / (MAX_VAL - MIN_VAL)) * 100;
|
|
const right = ((Math.max(MIN_VAL, Math.max(min, max)) - MIN_VAL) / (MAX_VAL - MIN_VAL)) * 100;
|
|
sizeFill.style.left = `${left}%`;
|
|
sizeFill.style.width = `${Math.max(0, right - left)}%`;
|
|
}
|
|
|
|
function clampRanges() {
|
|
if (parseFloat(sizeMinRange.value) > parseFloat(sizeMaxRange.value) - STEP) {
|
|
sizeMinRange.value = (parseFloat(sizeMaxRange.value) - STEP).toString();
|
|
}
|
|
if (parseFloat(sizeMaxRange.value) < parseFloat(sizeMinRange.value) + STEP) {
|
|
sizeMaxRange.value = (parseFloat(sizeMinRange.value) + STEP).toString();
|
|
}
|
|
}
|
|
|
|
sizeMinRange.addEventListener('input', () => { clampRanges(); updateSizeLabel(); });
|
|
sizeMaxRange.addEventListener('input', () => { clampRanges(); updateSizeLabel(); });
|
|
|
|
function bringMinToFront() {
|
|
sizeMinRange.style.zIndex = '6';
|
|
sizeMaxRange.style.zIndex = '5';
|
|
}
|
|
function bringMaxToFront() {
|
|
sizeMinRange.style.zIndex = '5';
|
|
sizeMaxRange.style.zIndex = '6';
|
|
}
|
|
['mousedown','pointerdown','touchstart'].forEach(evt => {
|
|
sizeMinRange.addEventListener(evt, bringMinToFront, { passive: true });
|
|
sizeMaxRange.addEventListener(evt, bringMaxToFront, { passive: true });
|
|
});
|
|
|
|
sizeMinRange.value = MIN_VAL;
|
|
sizeMaxRange.value = MAX_VAL;
|
|
updateSizeLabel();
|
|
|
|
// Form submission
|
|
document.getElementById('filtersForm').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
currentPage = 1;
|
|
const pageInput = document.getElementById('page');
|
|
if (pageInput) pageInput.value = '1';
|
|
await searchProperties(1);
|
|
});
|
|
|
|
// Reset button
|
|
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 searchProperties(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 searchProperties(currentPage);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|