diff --git a/src/App.jsx b/src/App.jsx index c0e5cb2..ad9b88e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,133 +1,102 @@ import React, { useState, useEffect } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, BarChart, Bar, PieChart, Pie, Cell, Area, AreaChart } from 'recharts'; -import { TrendingUp, TrendingDown, Home, DollarSign, Calendar, MapPin, Activity, AlertCircle, Star } from 'lucide-react'; +import { TrendingUp, TrendingDown, Home, DollarSign, Calendar, MapPin, Activity, AlertCircle, Star, RefreshCw, Wifi, WifiOff } from 'lucide-react'; const ApartmentDashboard = () => { const [selectedUnit, setSelectedUnit] = useState(null); - const [timeRange, setTimeRange] = useState('30d'); const [viewMode, setViewMode] = useState('overview'); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [lastUpdated, setLastUpdated] = useState(null); - // Mock data based on your MongoDB structure - const [mockData] = useState({ - dailySummary: { - date: '2025-01-15', - newUnits: 3, - rentedUnits: 5, - staleUnits: 1, - netChange: -3, - turnoverRate: 8.2, - totalAvailable: 47 - }, - priceHistory: [ - { date: '2025-01-01', avgPrice: 3850, unitCount: 52 }, - { date: '2025-01-02', avgPrice: 3845, unitCount: 48 }, - { date: '2025-01-03', avgPrice: 3860, unitCount: 51 }, - { date: '2025-01-04', avgPrice: 3875, unitCount: 49 }, - { date: '2025-01-05', avgPrice: 3820, unitCount: 53 }, - { date: '2025-01-06', avgPrice: 3835, unitCount: 50 }, - { date: '2025-01-07', avgPrice: 3890, unitCount: 47 }, - { date: '2025-01-08', avgPrice: 3865, unitCount: 52 }, - { date: '2025-01-09', avgPrice: 3880, unitCount: 48 }, - { date: '2025-01-10', avgPrice: 3855, unitCount: 51 }, - { date: '2025-01-11', avgPrice: 3870, unitCount: 49 }, - { date: '2025-01-12', avgPrice: 3825, unitCount: 54 }, - { date: '2025-01-13', avgPrice: 3845, unitCount: 50 }, - { date: '2025-01-14', avgPrice: 3885, unitCount: 46 }, - { date: '2025-01-15', avgPrice: 3875, unitCount: 47 } - ], - availableUnits: [ - { - unitCode: 'W2506', - planName: 'Maroon Peak', - bedCount: 2, - bathCount: 2, - area: 1089, - currentPrice: 3750, - minPrice: 3650, - maxPrice: 3850, - daysAvailable: 12, - community: 'Country Club Towers', - priceHistory: [ - { date: '2025-01-04', price: 3850 }, - { date: '2025-01-05', price: 3825 }, - { date: '2025-01-06', price: 3800 }, - { date: '2025-01-07', price: 3775 }, - { date: '2025-01-08', price: 3750 } - ] - }, - { - unitCode: 'T1205', - planName: 'North Maroon Peak', - bedCount: 2, - bathCount: 2, - area: 1150, - currentPrice: 3950, - minPrice: 3875, - maxPrice: 3995, - daysAvailable: 8, - community: 'Country Club Towers', - priceHistory: [ - { date: '2025-01-08', price: 3995 }, - { date: '2025-01-09', price: 3975 }, - { date: '2025-01-10', price: 3950 } - ] - }, - { - unitCode: 'G0304', - planName: 'Snowmass', - bedCount: 1, - bathCount: 1, - area: 750, - currentPrice: 2850, - minPrice: 2800, - maxPrice: 2950, - daysAvailable: 22, - community: 'Country Club Gardens', - priceHistory: [ - { date: '2024-12-24', price: 2950 }, - { date: '2024-12-30', price: 2925 }, - { date: '2025-01-05', price: 2900 }, - { date: '2025-01-10', price: 2875 }, - { date: '2025-01-15', price: 2850 } - ] + // Real data state + const [dailySummary, setDailySummary] = useState(null); + const [priceHistory, setPriceHistory] = useState([]); + const [availableUnits, setAvailableUnits] = useState([]); + const [recentActivity, setRecentActivity] = useState([]); + const [planStats, setPlanStats] = useState([]); + + // API base URL + const API_BASE = '/api'; + + // Fetch data from API + const fetchData = async () => { + setLoading(true); + setError(null); + + try { + console.log('🔄 Fetching apartment data...'); + + // Fetch all endpoints in parallel + const [ + dailySummaryRes, + priceHistoryRes, + availableUnitsRes, + recentActivityRes, + planStatsRes + ] = await Promise.all([ + fetch(`${API_BASE}/daily-summary`), + fetch(`${API_BASE}/price-history`), + fetch(`${API_BASE}/available-units`), + fetch(`${API_BASE}/recent-activity`), + fetch(`${API_BASE}/plan-stats`) + ]); + + // Check if all requests were successful + if (!dailySummaryRes.ok || !priceHistoryRes.ok || !availableUnitsRes.ok || + !recentActivityRes.ok || !planStatsRes.ok) { + throw new Error('One or more API requests failed'); } - ], - recentActivity: [ - { - type: 'new', - unitCode: 'W1408', - planName: 'Maroon Peak', - price: 3825, - date: '2025-01-15', - bedCount: 2, - bathCount: 2 - }, - { - type: 'rented', - unitCode: 'T0912', - planName: 'North Maroon Peak', - price: 3950, - date: '2025-01-15', - bedCount: 2, - bathCount: 2 - }, - { - type: 'price_drop', - unitCode: 'W2506', - oldPrice: 3775, - newPrice: 3750, - date: '2025-01-15', - planName: 'Maroon Peak' - } - ], - planStats: [ - { name: 'Maroon Peak', count: 15, avgPrice: 3780, avgArea: 1089 }, - { name: 'North Maroon Peak', count: 8, avgPrice: 3920, avgArea: 1150 }, - { name: 'Snowmass', count: 12, avgPrice: 2850, avgArea: 750 }, - { name: 'Aspen', count: 7, avgPrice: 3200, avgArea: 950 }, - { name: 'Capitol Peak', count: 5, avgPrice: 4200, avgArea: 1350 } - ] - }); + + // Parse JSON responses + const [ + dailySummaryData, + priceHistoryData, + availableUnitsData, + recentActivityData, + planStatsData + ] = await Promise.all([ + dailySummaryRes.json(), + priceHistoryRes.json(), + availableUnitsRes.json(), + recentActivityRes.json(), + planStatsRes.json() + ]); + + console.log('✅ Data fetched successfully:', { + dailySummary: dailySummaryData, + priceHistory: priceHistoryData.length, + availableUnits: availableUnitsData.length, + recentActivity: recentActivityData.length, + planStats: planStatsData.length + }); + + // Update state with real data + setDailySummary(dailySummaryData); + setPriceHistory(priceHistoryData); + setAvailableUnits(availableUnitsData); + setRecentActivity(recentActivityData); + setPlanStats(planStatsData); + setLastUpdated(new Date()); + setLoading(false); + + } catch (err) { + console.error('❌ Error fetching data:', err); + setError(err.message); + setLoading(false); + } + }; + + // Initial data fetch + useEffect(() => { + fetchData(); + }, []); + + // Auto-refresh every 5 minutes + useEffect(() => { + const interval = setInterval(fetchData, 5 * 60 * 1000); + return () => clearInterval(interval); + }, []); const MetricCard = ({ title, value, subtitle, icon: Icon, trend, color = "blue" }) => (
Fetching real-time market information...
+Unable to fetch apartment data: {error}
+ +Real-time apartment market dashboard
+Real-time apartment market dashboard
+ {lastUpdated && ( +- {activity.type === 'new' && `New unit available: ${activity.unitCode}`} - {activity.type === 'rented' && `Unit rented: ${activity.unitCode}`} - {activity.type === 'price_drop' && `Price drop: ${activity.unitCode}`} -
-- {activity.planName} • {activity.bedCount}BR/{activity.bathCount}BA - {activity.type === 'price_drop' - ? ` • ${formatPrice(activity.oldPrice)} → ${formatPrice(activity.newPrice)}` - : ` • ${formatPrice(activity.price)}` - } -
+ {recentActivity.length > 0 && ( ++ {activity.type === 'new' && `New unit available: ${activity.unitCode}`} + {activity.type === 'rented' && `Unit rented: ${activity.unitCode}`} + {activity.type === 'price_drop' && `Price drop: ${activity.unitCode}`} +
++ {activity.planName} • {activity.bedCount}BR/{activity.bathCount}BA + {activity.type === 'price_drop' + ? ` • ${formatPrice(activity.oldPrice)} → ${formatPrice(activity.newPrice)}` + : activity.price ? ` • ${formatPrice(activity.price)}` : '' + } +
+{unit.planName}
- {unit.planName.toLowerCase().includes('maroon peak') && + {unit.planName && unit.planName.toLowerCase().includes('maroon peak') && unit.currentPrice < 3800 &&Available
-{unit.daysAvailable} days
+{unit.daysAvailable || 0} days
No apartment units are currently available.
+- Daily turnover rate of {mockData.dailySummary.turnoverRate}% indicates moderate market activity. -
-- Average prices have stabilized around $3,850 with minimal daily fluctuation. -
-+ Daily turnover rate of {(dailySummary.turnoverRate || dailySummary.turnover_rate || 0).toFixed(1)}% indicates { + (dailySummary.turnoverRate || dailySummary.turnover_rate || 0) > 10 ? 'high' : + (dailySummary.turnoverRate || dailySummary.turnover_rate || 0) > 5 ? 'moderate' : 'low' + } market activity. +
++ {priceHistory.length > 1 ? ( + `Average prices ${priceHistory[priceHistory.length - 1].avgPrice > priceHistory[priceHistory.length - 2].avgPrice ? 'increased' : 'decreased'} in recent days.` + ) : ( + 'Price trend data is being collected.' + )} +
+Days Available
-{selectedUnit.daysAvailable}
+{selectedUnit.daysAvailable || 0}