Improve chart Y-axis scaling for better data visualization
- Add getAxisDomain helper function for calculating nice axis bounds - Update Overview charts: Price Trend, Unit Availability, Avg Price by Plan - Update Analytics charts: Monthly Trends, Seasonality, Price per Sqft - Axes now auto-scale with 10% padding and round to nice intervals
This commit is contained in:
47
src/App.jsx
47
src/App.jsx
@ -157,6 +157,39 @@ const ApartmentDashboard = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate nice axis bounds that round to reasonable values
|
||||
const getAxisDomain = (data, dataKey, padding = 0.1) => {
|
||||
if (!data || data.length === 0) return [0, 100];
|
||||
|
||||
const values = data.map(d => d[dataKey]).filter(v => v != null && !isNaN(v));
|
||||
if (values.length === 0) return [0, 100];
|
||||
|
||||
const min = Math.min(...values);
|
||||
const max = Math.max(...values);
|
||||
const range = max - min;
|
||||
|
||||
// Add padding to the range
|
||||
const paddedMin = min - range * padding;
|
||||
const paddedMax = max + range * padding;
|
||||
|
||||
// Determine a nice interval based on the range
|
||||
const getNiceInterval = (range) => {
|
||||
if (range <= 10) return 1;
|
||||
if (range <= 50) return 5;
|
||||
if (range <= 100) return 10;
|
||||
if (range <= 500) return 50;
|
||||
if (range <= 1000) return 100;
|
||||
if (range <= 5000) return 500;
|
||||
return 1000;
|
||||
};
|
||||
|
||||
const interval = getNiceInterval(range);
|
||||
const niceMin = Math.floor(paddedMin / interval) * interval;
|
||||
const niceMax = Math.ceil(paddedMax / interval) * interval;
|
||||
|
||||
return [Math.max(0, niceMin), niceMax];
|
||||
};
|
||||
|
||||
// Process price history based on time range
|
||||
const processPriceHistory = (priceHistory, timeRange) => {
|
||||
if (!priceHistory || priceHistory.length === 0) return [];
|
||||
@ -515,7 +548,7 @@ const ApartmentDashboard = () => {
|
||||
dataKey="date"
|
||||
tickFormatter={(date) => formatDate(date)}
|
||||
/>
|
||||
<YAxis tickFormatter={(value) => formatPrice(value)} />
|
||||
<YAxis tickFormatter={(value) => formatPrice(value)} domain={getAxisDomain(priceHistory.filter(d => d.avgPrice !== null), 'avgPrice')} />
|
||||
<Tooltip
|
||||
formatter={(value, name) => [formatPrice(value), 'Avg Price']}
|
||||
labelFormatter={(date) => formatDate(date)}
|
||||
@ -535,7 +568,7 @@ const ApartmentDashboard = () => {
|
||||
dataKey="date"
|
||||
tickFormatter={(date) => formatDate(date)}
|
||||
/>
|
||||
<YAxis />
|
||||
<YAxis domain={getAxisDomain(priceHistory, 'unitCount')} />
|
||||
<Tooltip
|
||||
formatter={(value, name) => [value, 'Available Units']}
|
||||
labelFormatter={(date) => formatDate(date)}
|
||||
@ -578,7 +611,7 @@ const ApartmentDashboard = () => {
|
||||
<BarChart data={planStats}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" angle={-45} textAnchor="end" height={80} />
|
||||
<YAxis tickFormatter={(value) => formatPrice(value)} />
|
||||
<YAxis tickFormatter={(value) => formatPrice(value)} domain={getAxisDomain(planStats, 'avgPrice')} />
|
||||
<Tooltip formatter={(value, name) => [formatPrice(value), 'Avg Price']} />
|
||||
<Bar dataKey="avgPrice" fill="#8B0000" />
|
||||
</BarChart>
|
||||
@ -792,8 +825,8 @@ const ApartmentDashboard = () => {
|
||||
<ComposedChart data={analyticsData.monthlyTrends}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="month" tickFormatter={(m) => m ? m.substring(2) : ''} />
|
||||
<YAxis yAxisId="left" tickFormatter={(v) => `$${(v/1000).toFixed(1)}k`} />
|
||||
<YAxis yAxisId="right" orientation="right" />
|
||||
<YAxis yAxisId="left" tickFormatter={(v) => `$${(v/1000).toFixed(1)}k`} domain={getAxisDomain(analyticsData.monthlyTrends, 'avgPrice')} />
|
||||
<YAxis yAxisId="right" orientation="right" domain={getAxisDomain(analyticsData.monthlyTrends, 'unitCount')} />
|
||||
<Tooltip
|
||||
formatter={(value, name) => {
|
||||
if (name === 'avgPrice') return [formatPrice(value), 'Avg Price'];
|
||||
@ -821,7 +854,7 @@ const ApartmentDashboard = () => {
|
||||
}))}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="monthName" />
|
||||
<YAxis tickFormatter={(v) => `$${(v/1000).toFixed(1)}k`} domain={['dataMin - 100', 'dataMax + 100']} />
|
||||
<YAxis tickFormatter={(v) => `$${(v/1000).toFixed(1)}k`} domain={getAxisDomain(analyticsData.seasonality, 'avgPrice')} />
|
||||
<Tooltip formatter={(value) => [formatPrice(value), 'Avg Price']} />
|
||||
<Bar dataKey="avgPrice" fill="#8B0000" />
|
||||
</BarChart>
|
||||
@ -839,7 +872,7 @@ const ApartmentDashboard = () => {
|
||||
<BarChart data={analyticsData.pricePerSqftByBed}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="bedrooms" tickFormatter={(v) => `${v} BR`} />
|
||||
<YAxis tickFormatter={(v) => `$${v.toFixed(2)}`} />
|
||||
<YAxis tickFormatter={(v) => `$${v.toFixed(2)}`} domain={getAxisDomain(analyticsData.pricePerSqftByBed, 'avgPricePerSqft')} />
|
||||
<Tooltip
|
||||
formatter={(value, name) => {
|
||||
if (name === 'avgPricePerSqft') return [`$${value.toFixed(2)}/sqft`, 'Price/Sqft'];
|
||||
|
||||
Reference in New Issue
Block a user