Frontend improvements #2
79
src/App.jsx
79
src/App.jsx
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Routes, Route, Link, useLocation, Navigate } from 'react-router-dom';
|
import { Routes, Route, Link, useLocation, Navigate } from 'react-router-dom';
|
||||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, BarChart, Bar, PieChart, Pie, Cell, Area, AreaChart, ScatterChart, Scatter, ComposedChart, ReferenceLine } from 'recharts';
|
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, BarChart, Bar, PieChart, Pie, Cell, Area, AreaChart, ScatterChart, Scatter, ComposedChart, ReferenceLine } from 'recharts';
|
||||||
import { TrendingUp, TrendingDown, Home, DollarSign, Calendar, MapPin, Activity, AlertCircle, Star, RefreshCw, Wifi, WifiOff, Bell, Target, Filter, Search, ArrowUp, ArrowDown, Zap, Clock, Eye } from 'lucide-react';
|
import { TrendingUp, TrendingDown, Home, DollarSign, Calendar, MapPin, Activity, AlertCircle, Star, RefreshCw, Wifi, WifiOff, Bell, Target, Filter, Search, ArrowUp, ArrowDown, Zap, Clock, Eye, LayoutGrid, List } from 'lucide-react';
|
||||||
|
|
||||||
const ApartmentDashboard = () => {
|
const ApartmentDashboard = () => {
|
||||||
// Router
|
// Router
|
||||||
@ -21,6 +21,7 @@ const ApartmentDashboard = () => {
|
|||||||
const [bedroomFilter, setBedroomFilter] = useState('all');
|
const [bedroomFilter, setBedroomFilter] = useState('all');
|
||||||
const [sortBy, setSortBy] = useState('alphabetical');
|
const [sortBy, setSortBy] = useState('alphabetical');
|
||||||
const [watchedUnits, setWatchedUnits] = useState(new Set());
|
const [watchedUnits, setWatchedUnits] = useState(new Set());
|
||||||
|
const [unitsViewMode, setUnitsViewMode] = useState('grid');
|
||||||
|
|
||||||
// API Data state - using your working endpoints
|
// API Data state - using your working endpoints
|
||||||
const [dailySummary, setDailySummary] = useState(null);
|
const [dailySummary, setDailySummary] = useState(null);
|
||||||
@ -690,10 +691,27 @@ const ApartmentDashboard = () => {
|
|||||||
<option value="daysAvailable">Days Available</option>
|
<option value="daysAvailable">Days Available</option>
|
||||||
<option value="pricePerSqft">Price per Sqft</option>
|
<option value="pricePerSqft">Price per Sqft</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div className="flex items-center border rounded-md overflow-hidden ml-auto">
|
||||||
|
<button
|
||||||
|
onClick={() => setUnitsViewMode('grid')}
|
||||||
|
className={`p-2 ${unitsViewMode === 'grid' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700 hover:bg-gray-100'}`}
|
||||||
|
title="Grid view"
|
||||||
|
>
|
||||||
|
<LayoutGrid className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setUnitsViewMode('list')}
|
||||||
|
className={`p-2 ${unitsViewMode === 'list' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700 hover:bg-gray-100'}`}
|
||||||
|
title="List view"
|
||||||
|
>
|
||||||
|
<List className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Unit Cards */}
|
{/* Unit Cards - Grid View */}
|
||||||
|
{unitsViewMode === 'grid' && (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||||
{filteredUnits.map((unit) => (
|
{filteredUnits.map((unit) => (
|
||||||
<div key={unit.unitCode} className={`bg-white rounded-lg shadow-md overflow-hidden ${watchedUnits.has(unit.unitCode) ? 'ring-2 ring-blue-400' : ''}`}>
|
<div key={unit.unitCode} className={`bg-white rounded-lg shadow-md overflow-hidden ${watchedUnits.has(unit.unitCode) ? 'ring-2 ring-blue-400' : ''}`}>
|
||||||
@ -748,6 +766,63 @@ const ApartmentDashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Unit Cards - List View */}
|
||||||
|
{unitsViewMode === 'list' && (
|
||||||
|
<div className="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
|
<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-center py-3 px-4 font-semibold text-gray-700">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{filteredUnits.map((unit, index) => (
|
||||||
|
<tr
|
||||||
|
key={unit.unitCode}
|
||||||
|
className={`border-b ${index % 2 === 0 ? 'bg-white' : 'bg-gray-50'} ${watchedUnits.has(unit.unitCode) ? 'ring-2 ring-inset ring-blue-400' : ''} hover:bg-blue-50`}
|
||||||
|
>
|
||||||
|
<td className="py-3 px-4 font-bold">{unit.unitCode}</td>
|
||||||
|
<td className="py-3 px-4 text-gray-600">{unit.planName}</td>
|
||||||
|
<td className="py-3 px-4 text-center">{unit.bedCount}/{unit.bathCount}</td>
|
||||||
|
<td className="py-3 px-4 text-center">{unit.area} sqft</td>
|
||||||
|
<td className="py-3 px-4 text-right font-bold text-green-600">{formatPrice(unit.currentPrice)}</td>
|
||||||
|
<td className="py-3 px-4 text-right text-gray-500">${((unit.currentPrice || 0) / (unit.area || 1)).toFixed(2)}</td>
|
||||||
|
<td className="py-3 px-4 text-center">{unit.daysAvailable || 0}</td>
|
||||||
|
<td className="py-3 px-4">
|
||||||
|
<div className="flex justify-center space-x-2">
|
||||||
|
<button
|
||||||
|
onClick={() => openUnitDetail(unit)}
|
||||||
|
className="bg-blue-600 text-white py-1 px-3 rounded-md hover:bg-blue-700 transition-colors text-sm"
|
||||||
|
>
|
||||||
|
Details
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => toggleWatchUnit(unit.unitCode)}
|
||||||
|
className={`p-1 rounded-md transition-colors ${
|
||||||
|
watchedUnits.has(unit.unitCode)
|
||||||
|
? 'bg-yellow-500 text-white hover:bg-yellow-600'
|
||||||
|
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Eye className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{filteredUnits.length === 0 && (
|
{filteredUnits.length === 0 && (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
|
|||||||
Reference in New Issue
Block a user