REACT Lesson 31 – Mini Project - Dashboard | Dataplexa
PROJECT

Mini Project – Dashboard

Build a complete business analytics dashboard with stats cards, interactive charts, and data filtering using React hooks and components.

A dashboard combines everything you've learned into one cohesive interface. The DataFlow team needs a main analytics screen where executives can track revenue, user growth, and order trends in real-time. Real dashboards like Notion's analytics or Linear's insights page follow the same patterns. Multiple components working together. State shared between sections. Data flowing from parent to child components.

Dashboard Architecture

Start by mapping out the component structure. A dashboard isn't one giant component — it's several focused pieces that communicate through props and shared state.
1
App Component (Main Container)
2
StatsBar Component (KPI Cards)
3
ChartSection Component (Revenue Graph)
4
DataTable Component (Transaction List)
Each component handles one responsibility. StatsBar shows the numbers. ChartSection visualizes trends. DataTable lists individual records. Clean separation means easier debugging and testing.

Building the Stats Cards

Start with the most visual part — those KPI cards at the top. Four metrics that executives check every morning: revenue, active users, orders, and growth percentage.
// StatsBar shows key business metrics
function StatsBar({ stats }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '16px', marginBottom: '24px' }}>
      {stats.map(stat => (
        <div key={stat.id} style={{ 
          padding: '20px', 
          background: '#fff', 
          borderRadius: '12px', 
          border: '2px solid #e2e8f0' 
        }}>
          <h3 style={{ margin: '0 0 8px', fontSize: '14px', color: '#64748b' }}>{stat.label}</h3>
          <p style={{ margin: 0, fontSize: '24px', fontWeight: '700', color: stat.color }}>{stat.value}</p>
        </div>
      ))}
    </div>
  );
}
DataFlow Dashboard

What just happened?

The map() function creates one card per stat object. Each card gets its own background color and styling. React's key prop helps track which cards changed. Try changing the stat values — React updates only the text, not the entire card.

Interactive Chart Component

Charts need state for hover effects and data filtering. Start with a simple bar chart that responds to user interaction. Real chart libraries like Chart.js or Recharts work the same way — data in, visual components out.
// ChartSection renders revenue bars with hover effects
function ChartSection() {
  const [hoveredBar, setHoveredBar] = useState(null);
  
  const monthlyData = [
    { month: 'Jan', revenue: 65000 },
    { month: 'Feb', revenue: 82000 },
    { month: 'Mar', revenue: 78000 },
    { month: 'Apr', revenue: 94000 },
    { month: 'May', revenue: 87000 }
  ];

  const maxRevenue = Math.max(...monthlyData.map(d => d.revenue));

  return (
    <div style={{ background: '#fff', padding: '24px', borderRadius: '12px', border: '2px solid #e2e8f0', marginBottom: '24px' }}>
      <h2 style={{ margin: '0 0 20px', color: '#0f172a' }}>Monthly Revenue</h2>
      <div style={{ display: 'flex', alignItems: 'end', gap: '12px', height: '200px' }}>
        {monthlyData.map(item => (
          <div key={item.month} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flex: 1 }}>
            <div 
              style={{ 
                width: '40px', 
                height: `${(item.revenue / maxRevenue) * 150}px`,
                background: hoveredBar === item.month ? '#16a34a' : '#047857',
                borderRadius: '6px',
                marginBottom: '8px',
                cursor: 'pointer',
                transition: 'all 0.2s'
              }}
              onMouseEnter={() => setHoveredBar(item.month)}
              onMouseLeave={() => setHoveredBar(null)}
            />
            <span style={{ fontSize: '12px', color: '#64748b' }}>{item.month}</span>
          </div>
        ))}
      </div>
    </div>
  );
}
DataFlow Dashboard

What just happened?

Mouse events trigger state changes. onMouseEnter sets the hovered bar, changing its color. onMouseLeave clears the hover state. The height calculation creates proportional bars based on revenue values. Try hovering over different months.

Data Table with Filtering

Tables show detailed records. Users need to search, sort, and filter this data. Start with basic functionality — add advanced features later when the foundation works.
// DataTable shows transaction history with search
function DataTable() {
  const [searchTerm, setSearchTerm] = useState('');
  
  const transactions = [
    { id: 1, customer: 'Acme Corp', amount: 2450, date: '2024-01-15', status: 'Paid' },
    { id: 2, customer: 'TechStart Inc', amount: 1200, date: '2024-01-14', status: 'Pending' },
    { id: 3, customer: 'Global Systems', amount: 3200, date: '2024-01-13', status: 'Paid' },
    { id: 4, customer: 'StartUp Labs', amount: 850, date: '2024-01-12', status: 'Failed' }
  ];

  const filteredTransactions = transactions.filter(t => 
    t.customer.toLowerCase().includes(searchTerm.toLowerCase())
  );

  return (
    <div style={{ background: '#fff', padding: '24px', borderRadius: '12px', border: '2px solid #e2e8f0' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
        <h2 style={{ margin: 0, color: '#0f172a' }}>Recent Transactions</h2>
        <input 
          type="text" 
          placeholder="Search customers..."
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
          style={{ 
            padding: '8px 12px', 
            border: '2px solid #e2e8f0', 
            borderRadius: '6px',
            fontSize: '14px'
          }}
        />
      </div>
      <div style={{ overflowX: 'auto' }}>
        <table style={{ width: '100%', borderCollapse: 'collapse' }}>
          <thead>
            <tr style={{ background: '#0f172a', color: '#fff' }}>
              <th style={{ padding: '12px 16px', textAlign: 'left', fontSize: '14px' }}>Customer</th>
              <th style={{ padding: '12px 16px', textAlign: 'left', fontSize: '14px' }}>Amount</th>
              <th style={{ padding: '12px 16px', textAlign: 'left', fontSize: '14px' }}>Date</th>
              <th style={{ padding: '12px 16px', textAlign: 'left', fontSize: '14px' }}>Status</th>
            </tr>
          </thead>
          <tbody>
            {filteredTransactions.map(transaction => (
              <tr key={transaction.id} style={{ borderBottom: '1px solid #e2e8f0' }}>
                <td style={{ padding: '12px 16px' }}>{transaction.customer}</td>
                <td style={{ padding: '12px 16px', fontWeight: '600' }}>${transaction.amount}</td>
                <td style={{ padding: '12px 16px', color: '#64748b' }}>{transaction.date}</td>
                <td style={{ padding: '12px 16px' }}>
                  <span style={{
                    padding: '4px 8px',
                    borderRadius: '4px',
                    fontSize: '12px',
                    fontWeight: '600',
                    background: transaction.status === 'Paid' ? '#dcfce7' : transaction.status === 'Pending' ? '#fef3c7' : '#fee2e2',
                    color: transaction.status === 'Paid' ? '#166534' : transaction.status === 'Pending' ? '#92400e' : '#991b1b'
                  }}>
                    {transaction.status}
                  </span>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}
DataFlow Dashboard

What just happened?

The search input controls which rows display. filter() creates a new array with matching transactions. Status badges use conditional styling for different colors. Type "Acme" in the search box — only matching customers appear.

Complete Dashboard Integration

Now combine all pieces into one cohesive dashboard. The parent component manages shared state and passes data down to children. This pattern scales to complex applications.
// Complete DataFlow Dashboard
function Dashboard() {
  const [dateRange, setDateRange] = useState('30d');
  
  const stats = [
    { id: 1, label: 'Revenue', value: '$847,230', color: '#047857' },
    { id: 2, label: 'Active Users', value: '12,847', color: '#1d4ed8' },
    { id: 3, label: 'Orders', value: '1,429', color: '#7c3aed' },
    { id: 4, label: 'Growth', value: '+23.4%', color: '#f97316' }
  ];

  return (
    <div style={{ padding: '24px', background: '#f8fafc', minHeight: '100vh' }}>
      <div style={{ 
        display: 'flex', 
        justifyContent: 'space-between', 
        alignItems: 'center', 
        marginBottom: '24px' 
      }}>
        <h1 style={{ margin: 0, color: '#0f172a', fontSize: '28px' }}>DataFlow Dashboard</h1>
        <select 
          value={dateRange} 
          onChange={(e) => setDateRange(e.target.value)}
          style={{ 
            padding: '8px 12px', 
            border: '2px solid #e2e8f0', 
            borderRadius: '6px',
            fontSize: '14px' 
          }}
        >
          <option value="7d">Last 7 days</option>
          <option value="30d">Last 30 days</option>
          <option value="90d">Last 90 days</option>
        </select>
      </div>
      
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '16px', marginBottom: '24px' }}>
        {stats.map(stat => (
          <div key={stat.id} style={{ 
            padding: '20px', 
            background: '#fff', 
            borderRadius: '12px', 
            border: '2px solid #e2e8f0' 
          }}>
            <h3 style={{ margin: '0 0 8px', fontSize: '14px', color: '#64748b' }}>{stat.label}</h3>
            <p style={{ margin: 0, fontSize: '24px', fontWeight: '700', color: stat.color }}>{stat.value}</p>
          </div>
        ))}
      </div>

      <div style={{ 
        background: '#fff', 
        padding: '24px', 
        borderRadius: '12px', 
        border: '2px solid #e2e8f0',
        textAlign: 'center',
        color: '#64748b'
      }}>
        <h2 style={{ color: '#0f172a', marginBottom: '12px' }}>Revenue Trends ({dateRange})</h2>
        <p>Chart component would render here based on selected date range</p>
      </div>
    </div>
  );
}
DataFlow Dashboard

What just happened?

The dashboard combines layout, state management, and component composition. The date range selector controls what data displays across all sections. CSS Grid creates responsive layouts that work on any screen size. Change the dropdown — notice how the chart title updates instantly.

Dashboard Best Practices

Production dashboards need performance optimizations and error handling. Here are patterns that prevent common issues:

Component Organization

Create one file per component. Dashboard.jsx, StatsBar.jsx, ChartSection.jsx. Import them into the main app. Easier debugging when each component has its own file.

Performance Optimization

Use React.memo() on expensive components that re-render frequently. Wrap chart calculations in useMemo(). Only recalculate when data actually changes, not on every render.

Error Boundaries

Wrap dashboard sections in error boundaries. If one chart crashes, the rest keep working. Users see a friendly message instead of a blank screen. Critical for production applications.

Test individual components first. Get one stats card working before building four. Add interactivity after the static version renders correctly. Dashboard complexity grows fast — take it step by step.

Real dashboards fetch data from APIs. The component structure stays the same — you replace hard-coded arrays with useEffect calls that update state when data loads. Companies like Stripe and Linear built their dashboards this exact way. Start with static components. Add state for interactivity. Connect to real data last. This approach prevents bugs and makes development predictable.

Quiz

1. The DataFlow team needs to build a dashboard with stats cards, charts, and data tables. What's the best approach for organizing these features?


2. In the DataFlow transaction table, users can search for customers by typing in a text input. How does the search functionality work?


3. The DataFlow chart component shows revenue bars that change color when users hover over them. How does this interaction work?


Up Next: Mini Project – Ecommerce UI

Build a complete shopping interface with product grids, shopping cart, and checkout flow using advanced React patterns.