Frontend improvements #2

Merged
stephen merged 6 commits from frontend-improvements into main 2026-01-21 12:04:13 -07:00
Showing only changes of commit b3f607b0fc - Show all commits

View File

@ -19,7 +19,8 @@ const ApartmentDashboard = () => {
// Filters
const [priceFilter, setPriceFilter] = useState({ min: 0, max: 10000 });
const [bedroomFilter, setBedroomFilter] = useState('all');
const [sortBy, setSortBy] = useState('alphabetical');
const [sortBy, setSortBy] = useState('unitCode');
const [sortDirection, setSortDirection] = useState('asc');
const [watchedUnits, setWatchedUnits] = useState(new Set());
const [unitsViewMode, setUnitsViewMode] = useState('list');
@ -371,19 +372,31 @@ const ApartmentDashboard = () => {
const insights = calculateInsights();
// Sort handler for column headers
const handleSort = (field) => {
if (sortBy === field) {
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
} else {
setSortBy(field);
setSortDirection('asc');
}
};
// Filter function for units
const filteredUnits = availableUnits.filter(unit => {
const matchesPrice = (unit.currentPrice || 0) >= priceFilter.min && (unit.currentPrice || 0) <= priceFilter.max;
const matchesBedroom = bedroomFilter === 'all' || (unit.bedCount != null && unit.bedCount.toString() === bedroomFilter);
return matchesPrice && matchesBedroom;
}).sort((a, b) => {
const dir = sortDirection === 'asc' ? 1 : -1;
switch (sortBy) {
case 'alphabetical': return (a.unitCode || '').localeCompare(b.unitCode || '');
case 'price': return (a.currentPrice || 0) - (b.currentPrice || 0);
case 'priceDesc': return (b.currentPrice || 0) - (a.currentPrice || 0);
case 'size': return (b.area || 0) - (a.area || 0);
case 'daysAvailable': return (b.daysAvailable || 0) - (a.daysAvailable || 0);
case 'pricePerSqft': return ((a.currentPrice || 0) / (a.area || 1)) - ((b.currentPrice || 0) / (b.area || 1));
case 'unitCode': return dir * (a.unitCode || '').localeCompare(b.unitCode || '');
case 'planName': return dir * (a.planName || '').localeCompare(b.planName || '');
case 'bedCount': return dir * ((a.bedCount || 0) - (b.bedCount || 0));
case 'area': return dir * ((a.area || 0) - (b.area || 0));
case 'price': return dir * ((a.currentPrice || 0) - (b.currentPrice || 0));
case 'pricePerSqft': return dir * (((a.currentPrice || 0) / (a.area || 1)) - ((b.currentPrice || 0) / (b.area || 1)));
case 'daysAvailable': return dir * ((a.daysAvailable || 0) - (b.daysAvailable || 0));
default: return 0;
}
});
@ -680,16 +693,24 @@ const ApartmentDashboard = () => {
<option value="2">2 BR</option>
</select>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
value={`${sortBy}-${sortDirection}`}
onChange={(e) => {
const [field, dir] = e.target.value.split('-');
setSortBy(field);
setSortDirection(dir);
}}
className="border rounded-md px-3 py-2"
>
<option value="alphabetical">Alphabetical</option>
<option value="price">Price: Low to High</option>
<option value="priceDesc">Price: High to Low</option>
<option value="size">Size: Large to Small</option>
<option value="daysAvailable">Days Available</option>
<option value="pricePerSqft">Price per Sqft</option>
<option value="unitCode-asc">Unit: A to Z</option>
<option value="unitCode-desc">Unit: Z to A</option>
<option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option>
<option value="area-asc">Size: Small to Large</option>
<option value="area-desc">Size: Large to Small</option>
<option value="daysAvailable-asc">Days: Low to High</option>
<option value="daysAvailable-desc">Days: High to Low</option>
<option value="pricePerSqft-asc">$/sqft: Low to High</option>
<option value="pricePerSqft-desc">$/sqft: High to Low</option>
</select>
<div className="flex items-center border rounded-md overflow-hidden ml-auto">
<button
@ -774,13 +795,69 @@ const ApartmentDashboard = () => {
<table className="w-full">
<thead className="bg-gray-50 border-b">
<tr>
<th className="text-left py-3 px-4 font-semibold text-gray-700">Unit</th>
<th className="text-left py-3 px-4 font-semibold text-gray-700">Plan</th>
<th className="text-center py-3 px-4 font-semibold text-gray-700">Bed/Bath</th>
<th className="text-center py-3 px-4 font-semibold text-gray-700">Area</th>
<th className="text-right py-3 px-4 font-semibold text-gray-700">Price</th>
<th className="text-right py-3 px-4 font-semibold text-gray-700">$/sqft</th>
<th className="text-center py-3 px-4 font-semibold text-gray-700">Days</th>
<th
className="text-left py-3 px-4 font-semibold text-gray-700 cursor-pointer hover:bg-gray-100 select-none"
onClick={() => handleSort('unitCode')}
>
<span className="flex items-center">
Unit
{sortBy === 'unitCode' && (sortDirection === 'asc' ? <ArrowUp className="w-4 h-4 ml-1" /> : <ArrowDown className="w-4 h-4 ml-1" />)}
</span>
</th>
<th
className="text-left py-3 px-4 font-semibold text-gray-700 cursor-pointer hover:bg-gray-100 select-none"
onClick={() => handleSort('planName')}
>
<span className="flex items-center">
Plan
{sortBy === 'planName' && (sortDirection === 'asc' ? <ArrowUp className="w-4 h-4 ml-1" /> : <ArrowDown className="w-4 h-4 ml-1" />)}
</span>
</th>
<th
className="text-center py-3 px-4 font-semibold text-gray-700 cursor-pointer hover:bg-gray-100 select-none"
onClick={() => handleSort('bedCount')}
>
<span className="flex items-center justify-center">
Bed/Bath
{sortBy === 'bedCount' && (sortDirection === 'asc' ? <ArrowUp className="w-4 h-4 ml-1" /> : <ArrowDown className="w-4 h-4 ml-1" />)}
</span>
</th>
<th
className="text-center py-3 px-4 font-semibold text-gray-700 cursor-pointer hover:bg-gray-100 select-none"
onClick={() => handleSort('area')}
>
<span className="flex items-center justify-center">
Area
{sortBy === 'area' && (sortDirection === 'asc' ? <ArrowUp className="w-4 h-4 ml-1" /> : <ArrowDown className="w-4 h-4 ml-1" />)}
</span>
</th>
<th
className="text-right py-3 px-4 font-semibold text-gray-700 cursor-pointer hover:bg-gray-100 select-none"
onClick={() => handleSort('price')}
>
<span className="flex items-center justify-end">
Price
{sortBy === 'price' && (sortDirection === 'asc' ? <ArrowUp className="w-4 h-4 ml-1" /> : <ArrowDown className="w-4 h-4 ml-1" />)}
</span>
</th>
<th
className="text-right py-3 px-4 font-semibold text-gray-700 cursor-pointer hover:bg-gray-100 select-none"
onClick={() => handleSort('pricePerSqft')}
>
<span className="flex items-center justify-end">
$/sqft
{sortBy === 'pricePerSqft' && (sortDirection === 'asc' ? <ArrowUp className="w-4 h-4 ml-1" /> : <ArrowDown className="w-4 h-4 ml-1" />)}
</span>
</th>
<th
className="text-center py-3 px-4 font-semibold text-gray-700 cursor-pointer hover:bg-gray-100 select-none"
onClick={() => handleSort('daysAvailable')}
>
<span className="flex items-center justify-center">
Days
{sortBy === 'daysAvailable' && (sortDirection === 'asc' ? <ArrowUp className="w-4 h-4 ml-1" /> : <ArrowDown className="w-4 h-4 ml-1" />)}
</span>
</th>
<th className="text-center py-3 px-4 font-semibold text-gray-700">Actions</th>
</tr>
</thead>