feat(web): add property comparison page with side-by-side view

Build a complete property comparison feature at /compare:
- Zustand store with localStorage persistence for selected listings (2-5)
- Side-by-side comparison table (price, area, price/m², amenities, location, etc.)
- Summary statistics banner (price range, area range, price/m² range)
- "Add to Compare" button on property cards and detail pages
- Floating comparison bar for quick access when listings are selected
- Bilingual i18n support (Vietnamese + English)
- 18 unit tests for store logic and comparison stats computation
- Mobile-responsive layout with horizontal scroll on comparison table

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-10 23:55:50 +07:00
parent 55a01c5738
commit 37fab515b7
13 changed files with 1092 additions and 0 deletions

View File

@@ -215,5 +215,46 @@
"10to20b": "10 - 20 billion",
"over20b": "Over 20 billion"
}
},
"compare": {
"title": "Compare properties",
"subtitle": "Comparing {count} properties",
"emptyState": "Select at least 2 properties to compare. Go back to search to select.",
"goToSearch": "Go to search",
"addMore": "Add more",
"clearAll": "Clear all",
"compareNow": "Compare now",
"needMore": "Need more",
"selected": "{count}/{max} selected",
"removeItem": "Remove",
"addToCompare": "Compare",
"removeFromCompare": "Remove from compare",
"added": "Added",
"loadError": "Unable to load data. Please try again.",
"retry": "Retry",
"property": "Property",
"noImage": "No image",
"remove": "Remove",
"price": "Price",
"transactionType": "Transaction",
"sale": "Sale",
"rent": "Rent",
"propertyType": "Property type",
"area": "Area",
"pricePerM2": "Price/m²",
"bedrooms": "Bedrooms",
"bathrooms": "Bathrooms",
"rooms": "rooms",
"direction": "Direction",
"floors": "Floors",
"yearBuilt": "Year built",
"legalStatus": "Legal status",
"location": "Location",
"amenities": "Amenities",
"projectName": "Project",
"priceRange": "Price range",
"areaRange": "Area range",
"pricePerM2Range": "Price/m² range",
"average": "Average"
}
}