Frontend improvements #2
121
src/App.jsx
121
src/App.jsx
@ -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>
|
||||
|
||||
Reference in New Issue
Block a user