analytics



This content originally appeared on DEV Community and was authored by Nebula

import { useState, useEffect, useCallback, useMemo } from "react";
import { Search, Filter, X, RefreshCw, AlertCircle, TrendingUp, TrendingDown, Calendar, DollarSign, PieChart, BarChart3, Download, Eye, EyeOff } from "lucide-react";
import styled from 'styled-components';
import { motion, AnimatePresence } from 'framer-motion';
import { LineChart, Line, AreaChart, Area, BarChart, Bar, PieChart as RechartsPieChart, Cell, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';

// Design tokens (from your original file)
const colors = {
  primary: '#667eea',
  primaryDark: '#5a67d8',
  secondary: '#764ba2',
  success: '#48bb78',
  error: '#ff6b6b',
  warning: '#f6ad55',
  text: '#1a202c',
  textSecondary: '#4a5568',
  textMuted: '#a0aec0',
  background: 'rgba(255, 255, 255, 0.95)',
  border: 'rgba(102, 126, 234, 0.2)',
  borderHover: '#667eea',
  glass: 'rgba(255, 255, 255, 0.95)',
  glassLight: 'rgba(255, 255, 255, 0.8)',
  shadow: 'rgba(0, 0, 0, 0.1)',
  shadowHover: 'rgba(102, 126, 234, 0.1)'
};

const gradients = {
  primary: `linear-gradient(135deg, ${colors.primary}, ${colors.secondary})`,
  success: `linear-gradient(135deg, ${colors.success}, #38b2ac)`,
  error: `linear-gradient(135deg, ${colors.error}, #ee5a52)`,
  warning: `linear-gradient(135deg, ${colors.warning}, #ff8c00)`
};

const spacing = {
  xs: '4px',
  sm: '8px',
  md: '12px',
  lg: '16px',
  xl: '20px',
  xxl: '24px',
  xxxl: '32px'
};

// Styled Components
const Container = styled(motion.div)`
  padding: ${spacing.xxxl};
  max-width: 1400px;
  margin: 0 auto;
  min-height: calc(100vh - 120px);

  h1 {
    font-size: 36px;
    font-weight: 800;
    color: ${colors.text};
    margin-bottom: ${spacing.sm};
    background: ${gradients.primary};
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    text-align: center;
  }

  .subtitle {
    text-align: center;
    color: ${colors.textMuted};
    margin-bottom: ${spacing.xxxl};
    font-size: 16px;
  }

  @media (max-width: 768px) {
    padding: ${spacing.xl};

    h1 {
      font-size: 28px;
      margin-bottom: ${spacing.xs};
    }
  }
`;

const StatsGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: ${spacing.xl};
  margin-bottom: ${spacing.xxxl};

  @media (max-width: 768px) {
    grid-template-columns: 1fr;
    gap: ${spacing.lg};
  }
`;

const StatCard = styled(motion.div)`
  background: ${colors.glass};
  backdrop-filter: blur(20px);
  border-radius: 20px;
  padding: ${spacing.xxl};
  box-shadow: 0 8px 32px ${colors.shadow};
  border: 1px solid rgba(255, 255, 255, 0.2);
  position: relative;
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: ${props => props.color || gradients.primary};
  }

  .stat-header {
    display: flex;
    align-items: center;
    gap: ${spacing.md};
    margin-bottom: ${spacing.lg};

    svg {
      color: ${props => props.iconColor || colors.primary};
    }

    h3 {
      margin: 0;
      font-size: 14px;
      font-weight: 600;
      color: ${colors.textSecondary};
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
  }

  .stat-value {
    font-size: 32px;
    font-weight: 800;
    color: ${colors.text};
    margin-bottom: ${spacing.sm};
  }

  .stat-change {
    display: flex;
    align-items: center;
    gap: ${spacing.xs};
    font-size: 14px;
    font-weight: 600;

    &.positive {
      color: ${colors.success};
    }

    &.negative {
      color: ${colors.error};
    }
  }
`;

const FilterSection = styled(motion.div)`
  background: ${colors.glass};
  backdrop-filter: blur(20px);
  border-radius: 20px;
  padding: ${spacing.xxl};
  margin-bottom: ${spacing.xxxl};
  box-shadow: 0 8px 32px ${colors.shadow};
  border: 1px solid rgba(255, 255, 255, 0.2);
  position: relative;
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: ${gradients.primary};
  }

  .filter-header {
    display: flex;
    align-items: center;
    gap: ${spacing.md};
    margin-bottom: ${spacing.xl};

    h3 {
      margin: 0;
      font-size: 18px;
      font-weight: 600;
      color: ${colors.text};
    }
  }
`;

const FiltersGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: ${spacing.lg};
  margin-bottom: ${spacing.xl};

  @media (max-width: 768px) {
    grid-template-columns: 1fr;
  }
`;

const FilterSelect = styled.select`
  padding: ${spacing.lg} ${spacing.xl};
  border: 2px solid ${colors.border};
  border-radius: 12px;
  font-size: 16px;
  font-weight: 500;
  color: ${colors.text};
  background: ${colors.glassLight};
  backdrop-filter: blur(10px);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  cursor: pointer;

  &:focus {
    outline: none;
    border-color: ${colors.borderHover};
    box-shadow: 0 0 0 4px ${colors.shadowHover};
    background: ${colors.background};
  }
`;

const DateInput = styled.input`
  padding: ${spacing.lg} ${spacing.xl};
  border: 2px solid ${colors.border};
  border-radius: 12px;
  font-size: 16px;
  font-weight: 500;
  color: ${colors.text};
  background: ${colors.glassLight};
  backdrop-filter: blur(10px);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

  &:focus {
    outline: none;
    border-color: ${colors.borderHover};
    box-shadow: 0 0 0 4px ${colors.shadowHover};
    background: ${colors.background};
  }
`;

const AmountRangeContainer = styled.div`
  display: flex;
  gap: ${spacing.md};
  align-items: center;

  input {
    flex: 1;
    padding: ${spacing.lg};
    border: 2px solid ${colors.border};
    border-radius: 12px;
    font-size: 16px;
    font-weight: 500;
    color: ${colors.text};
    background: ${colors.glassLight};
    backdrop-filter: blur(10px);
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

    &:focus {
      outline: none;
      border-color: ${colors.borderHover};
      box-shadow: 0 0 0 4px ${colors.shadowHover};
      background: ${colors.background};
    }
  }

  span {
    color: ${colors.textMuted};
    font-weight: 600;
  }
`;

const ButtonGroup = styled.div`
  display: flex;
  gap: ${spacing.md};
  justify-content: flex-end;

  @media (max-width: 768px) {
    justify-content: stretch;
  }
`;

const Button = styled(motion.button)`
  display: flex;
  align-items: center;
  gap: ${spacing.sm};
  padding: ${spacing.md} ${spacing.xl};
  border-radius: 12px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border: 2px solid transparent;

  ${props => props.variant === 'primary' ? `
    background: ${gradients.primary};
    color: white;
    box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);

    &:hover:not(:disabled) {
      transform: translateY(-2px);
      box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
    }
  ` : `
    background: transparent;
    color: ${colors.textSecondary};
    border-color: #e2e8f0;

    &:hover:not(:disabled) {
      background: #f7fafc;
      border-color: #cbd5e0;
    }
  `}

  &:active {
    transform: translateY(0);
  }

  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }

  @media (max-width: 768px) {
    flex: 1;
    justify-content: center;
  }
`;

const ChartsGrid = styled.div`
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: ${spacing.xl};
  margin-bottom: ${spacing.xxxl};

  @media (max-width: 1024px) {
    grid-template-columns: 1fr;
  }
`;

const ChartContainer = styled(motion.div)`
  background: ${colors.glass};
  backdrop-filter: blur(20px);
  border-radius: 20px;
  padding: ${spacing.xxl};
  box-shadow: 0 8px 32px ${colors.shadow};
  border: 1px solid rgba(255, 255, 255, 0.2);
  position: relative;
  overflow: hidden;
  grid-column: ${props => props.fullWidth ? 'span 2' : 'span 1'};

  @media (max-width: 1024px) {
    grid-column: span 1;
  }

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: ${gradients.primary};
  }

  .chart-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: ${spacing.xl};

    h3 {
      margin: 0;
      font-size: 18px;
      font-weight: 600;
      color: ${colors.text};
    }

    .chart-controls {
      display: flex;
      gap: ${spacing.sm};
    }
  }

  .chart-wrapper {
    height: 300px;
  }
`;

const ToggleButton = styled(motion.button)`
  padding: ${spacing.sm} ${spacing.md};
  border: 2px solid ${colors.border};
  border-radius: 8px;
  background: ${props => props.active ? gradients.primary : 'transparent'};
  color: ${props => props.active ? 'white' : colors.textSecondary};
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;

  &:hover {
    border-color: ${colors.borderHover};
  }
`;

const LoadingSpinner = styled(motion.div)`
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 64px;
  color: ${colors.primary};

  svg {
    animation: spin 1s linear infinite;
  }

  @keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
  }
`;

// Mock data generator
const generateMockTransactions = (count = 100) => {
  const types = ['DEPOSIT', 'WITHDRAW', 'TRANSFER', 'LOAN'];
  const statuses = ['SUCCESS', 'PENDING', 'FAILED'];
  const merchants = ['Amazon', 'Starbucks', 'Uber', 'Netflix', 'Spotify', 'Apple', 'Google', 'Microsoft', 'Target', 'Walmart'];
  const categories = ['Food & Dining', 'Shopping', 'Transportation', 'Entertainment', 'Bills & Utilities', 'Healthcare', 'Travel', 'Education'];

  return Array.from({ length: count }, (_, index) => {
    const type = types[Math.floor(Math.random() * types.length)];
    const amount = type === 'DEPOSIT' 
      ? Math.floor(Math.random() * 5000) + 100
      : -(Math.floor(Math.random() * 1000) + 10);

    return {
      id: `txn_${index + 1}`,
      type,
      amount,
      status: statuses[Math.floor(Math.random() * statuses.length)],
      date: new Date(Date.now() - Math.floor(Math.random() * 90) * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
      merchant: merchants[Math.floor(Math.random() * merchants.length)],
      category: categories[Math.floor(Math.random() * categories.length)],
      description: `Transaction with ${merchants[Math.floor(Math.random() * merchants.length)]}`
    };
  });
};

// Custom tooltip component
const CustomTooltip = ({ active, payload, label }) => {
  if (active && payload && payload.length) {
    return (
      <div style={{
        background: colors.glass,
        backdropFilter: 'blur(20px)',
        border: `1px solid ${colors.border}`,
        borderRadius: '12px',
        padding: spacing.md,
        boxShadow: `0 8px 32px ${colors.shadow}`
      }}>
        <p style={{ margin: 0, color: colors.text, fontWeight: '600' }}>{label}</p>
        {payload.map((entry, index) => (
          <p key={index} style={{ margin: '4px 0', color: entry.color, fontSize: '14px' }}>
            {`${entry.name}: $${Math.abs(entry.value).toLocaleString()}`}
          </p>
        ))}
      </div>
    );
  }
  return null;
};

const BankingAnalyticsDashboard = () => {
  const [transactions, setTransactions] = useState([]);
  const [loading, setLoading] = useState(true);
  const [filters, setFilters] = useState({
    type: 'ALL',
    status: 'ALL',
    category: 'ALL',
    dateFrom: '',
    dateTo: '',
    amountMin: '',
    amountMax: ''
  });
  const [chartView, setChartView] = useState({
    timeChart: 'line',
    showBalance: true
  });

  // Simulate API call
  useEffect(() => {
    const fetchTransactions = async () => {
      setLoading(true);
      // Simulate API delay
      await new Promise(resolve => setTimeout(resolve, 1000));
      setTransactions(generateMockTransactions(150));
      setLoading(false);
    };

    fetchTransactions();
  }, []);

  // Filter transactions
  const filteredTransactions = useMemo(() => {
    return transactions.filter(transaction => {
      if (filters.type !== 'ALL' && transaction.type !== filters.type) return false;
      if (filters.status !== 'ALL' && transaction.status !== filters.status) return false;
      if (filters.category !== 'ALL' && transaction.category !== filters.category) return false;
      if (filters.dateFrom && transaction.date < filters.dateFrom) return false;
      if (filters.dateTo && transaction.date > filters.dateTo) return false;
      if (filters.amountMin && Math.abs(transaction.amount) < parseFloat(filters.amountMin)) return false;
      if (filters.amountMax && Math.abs(transaction.amount) > parseFloat(filters.amountMax)) return false;
      return true;
    });
  }, [transactions, filters]);

  // Calculate statistics
  const stats = useMemo(() => {
    const totalIncome = filteredTransactions
      .filter(t => t.amount > 0)
      .reduce((sum, t) => sum + t.amount, 0);

    const totalExpenses = filteredTransactions
      .filter(t => t.amount < 0)
      .reduce((sum, t) => sum + Math.abs(t.amount), 0);

    const netBalance = totalIncome - totalExpenses;
    const avgTransaction = filteredTransactions.length > 0 
      ? filteredTransactions.reduce((sum, t) => sum + Math.abs(t.amount), 0) / filteredTransactions.length 
      : 0;

    // Calculate month-over-month changes (mock data)
    const incomeChange = Math.random() * 20 - 10; // -10% to +10%
    const expenseChange = Math.random() * 20 - 10;
    const balanceChange = Math.random() * 30 - 15;

    return {
      totalIncome,
      totalExpenses,
      netBalance,
      avgTransaction,
      transactionCount: filteredTransactions.length,
      incomeChange,
      expenseChange,
      balanceChange
    };
  }, [filteredTransactions]);

  // Prepare chart data
  const timeSeriesData = useMemo(() => {
    const dataMap = {};
    let runningBalance = 0;

    filteredTransactions
      .sort((a, b) => new Date(a.date) - new Date(b.date))
      .forEach(transaction => {
        const date = transaction.date;
        if (!dataMap[date]) {
          dataMap[date] = { date, income: 0, expenses: 0, balance: runningBalance };
        }

        if (transaction.amount > 0) {
          dataMap[date].income += transaction.amount;
        } else {
          dataMap[date].expenses += Math.abs(transaction.amount);
        }

        runningBalance += transaction.amount;
        dataMap[date].balance = runningBalance;
      });

    return Object.values(dataMap);
  }, [filteredTransactions]);

  const categoryData = useMemo(() => {
    const categoryMap = {};
    filteredTransactions.forEach(transaction => {
      if (transaction.amount < 0) { // Only expenses
        const category = transaction.category;
        categoryMap[category] = (categoryMap[category] || 0) + Math.abs(transaction.amount);
      }
    });

    return Object.entries(categoryMap).map(([name, value]) => ({ name, value }));
  }, [filteredTransactions]);

  const typeData = useMemo(() => {
    const typeMap = {};
    filteredTransactions.forEach(transaction => {
      const type = transaction.type;
      typeMap[type] = (typeMap[type] || 0) + Math.abs(transaction.amount);
    });

    return Object.entries(typeMap).map(([name, value]) => ({ name, value }));
  }, [filteredTransactions]);

  const handleFilterChange = (key, value) => {
    setFilters(prev => ({ ...prev, [key]: value }));
  };

  const resetFilters = () => {
    setFilters({
      type: 'ALL',
      status: 'ALL',
      category: 'ALL',
      dateFrom: '',
      dateTo: '',
      amountMin: '',
      amountMax: ''
    });
  };

  const refreshData = () => {
    setTransactions(generateMockTransactions(150));
  };

  const exportData = () => {
    // Mock export functionality
    const dataStr = JSON.stringify(filteredTransactions, null, 2);
    const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);

    const exportFileDefaultName = 'transaction-analytics.json';

    const linkElement = document.createElement('a');
    linkElement.setAttribute('href', dataUri);
    linkElement.setAttribute('download', exportFileDefaultName);
    linkElement.click();
  };

  const pieColors = [colors.primary, colors.secondary, colors.success, colors.warning, colors.error, '#9f7aea', '#38b2ac', '#ed8936'];

  if (loading) {
    return (
      <Container>
        <LoadingSpinner
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
        >
          <RefreshCw size={32} />
          <span style={{ marginLeft: spacing.md, fontSize: '18px', fontWeight: '600' }}>
            Loading Analytics...
          </span>
        </LoadingSpinner>
      </Container>
    );
  }

  return (
    <Container
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.6 }}
    >
      <h1>Transaction Analytics</h1>
      <p className="subtitle">Comprehensive insights into your financial activities</p>

      {/* Statistics Cards */}
      <StatsGrid>
        <StatCard
          color={gradients.success}
          iconColor={colors.success}
          initial={{ opacity: 0, y: 30 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.1 }}
        >
          <div className="stat-header">
            <TrendingUp size={20} />
            <h3>Total Income</h3>
          </div>
          <div className="stat-value">${stats.totalIncome.toLocaleString()}</div>
          <div className={`stat-change ${stats.incomeChange >= 0 ? 'positive' : 'negative'}`}>
            {stats.incomeChange >= 0 ? <TrendingUp size={16} /> : <TrendingDown size={16} />}
            {Math.abs(stats.incomeChange).toFixed(1)}% from last month
          </div>
        </StatCard>

        <StatCard
          color={gradients.error}
          iconColor={colors.error}
          initial={{ opacity: 0, y: 30 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.2 }}
        >
          <div className="stat-header">
            <TrendingDown size={20} />
            <h3>Total Expenses</h3>
          </div>
          <div className="stat-value">${stats.totalExpenses.toLocaleString()}</div>
          <div className={`stat-change ${stats.expenseChange <= 0 ? 'positive' : 'negative'}`}>
            {stats.expenseChange <= 0 ? <TrendingDown size={16} /> : <TrendingUp size={16} />}
            {Math.abs(stats.expenseChange).toFixed(1)}% from last month
          </div>
        </StatCard>

        <StatCard
          color={stats.netBalance >= 0 ? gradients.success : gradients.error}
          iconColor={stats.netBalance >= 0 ? colors.success : colors.error}
          initial={{ opacity: 0, y: 30 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.3 }}
        >
          <div className="stat-header">
            <DollarSign size={20} />
            <h3>Net Balance</h3>
          </div>
          <div className="stat-value">${stats.netBalance.toLocaleString()}</div>
          <div className={`stat-change ${stats.balanceChange >= 0 ? 'positive' : 'negative'}`}>
            {stats.balanceChange >= 0 ? <TrendingUp size={16} /> : <TrendingDown size={16} />}
            {Math.abs(stats.balanceChange).toFixed(1)}% from last month
          </div>
        </StatCard>

        <StatCard
          color={gradients.primary}
          iconColor={colors.primary}
          initial={{ opacity: 0, y: 30 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.4 }}
        >
          <div className="stat-header">
            <BarChart3 size={20} />
            <h3>Avg Transaction</h3>
          </div>
          <div className="stat-value">${stats.avgTransaction.toLocaleString()}</div>
          <div className="stat-change positive">
            <Calendar size={16} />
            {stats.transactionCount} transactions
          </div>
        </StatCard>
      </StatsGrid>

      {/* Filters */}
      <FilterSection
        initial={{ opacity: 0, y: 30 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ delay: 0.5 }}
      >
        <div className="filter-header">
          <Filter size={20} />
          <h3>Filters & Controls</h3>
        </div>

        <FiltersGrid>
          <FilterSelect 
            value={filters.type} 
            onChange={(e) => handleFilterChange('type', e.target.value)}
          >
            <option value="ALL">All Types</option>
            <option value="DEPOSIT">Deposits</option>
            <option value="WITHDRAW">Withdrawals</option>
            <option value="TRANSFER">Transfers</option>
            <option value="LOAN">Loans</option>
          </FilterSelect>

          <FilterSelect 
            value={filters.status} 
            onChange={(e) => handleFilterChange('status', e.target.value)}
          >
            <option value="ALL">All Status</option>
            <option value="SUCCESS">Success</option>
            <option value="PENDING">Pending</option>
            <option value="FAILED">Failed</option>
          </FilterSelect>

          <FilterSelect 
            value={filters.category} 
            onChange={(e) => handleFilterChange('category', e.target.value)}
          >
            <option value="ALL">All Categories</option>
            <option value="Food & Dining">Food & Dining</option>
            <option value="Shopping">Shopping</option>
            <option value="Transportation">Transportation</option>
            <option value="Entertainment">Entertainment</option>
            <option value="Bills & Utilities">Bills & Utilities</option>
            <option value="Healthcare">Healthcare</option>
            <option value="Travel">Travel</option>
            <option value="Education">Education</option>
          </FilterSelect>

          <DateInput
            type="date"
            placeholder="From Date"
            value={filters.dateFrom}
            onChange={(e) => handleFilterChange('dateFrom', e.target.value)}
          />

          <DateInput
            type="date"
            placeholder="To Date"
            value={filters.dateTo}
            onChange={(e) => handleFilterChange('dateTo', e.target.value)}
          />

          <AmountRangeContainer>
            <input
              type="number"
              placeholder="Min Amount"
              value={filters.amountMin}
              onChange={(e) => handleFilterChange('amountMin', e.target.value)}
            />
            <span>-</span>
            <input
              type="number"
              placeholder="Max Amount"
              value={filters.amountMax}
              onChange={(e) => handleFilterChange('amountMax', e.target.value)}
            />
          </AmountRangeContainer>
        </FiltersGrid>

        <ButtonGroup>
          <Button onClick={resetFilters}>
            <X size={16} />
            Clear Filters
          </Button>
          <Button onClick={refreshData}>
            <RefreshCw size={16} />
            Refresh Data
          </Button>
          <Button variant="primary" onClick={exportData}>
            <Download size={16} />
            Export Data
          </Button>
        </ButtonGroup>
      </FilterSection>

      {/* Charts */}
      <ChartsGrid>
        {/* Time Series Chart */}
        <ChartContainer
          fullWidth
          initial={{ opacity: 0, y: 30 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.6 }}
        >
          <div className="chart-header">
            <h3>Financial Flow Over Time</h3>
            <div className="chart-controls">
              <ToggleButton
                active={chartView.timeChart === 'line'}
                onClick={() => setChartView(prev => ({...prev, timeChart: 'line'}))}
              >
                Line
              </ToggleButton>
              <ToggleButton
                active={chartView.timeChart === 'area'}
                onClick={() => setChartView(prev => ({...prev, timeChart: 'area'}))}
              >
                Area
              </ToggleButton>
              <ToggleButton
                active={chartView.showBalance}
                onClick={() => setChartView(prev => ({...prev, showBalance: !prev.showBalance}))}
              >
                {chartView.showBalance ? <Eye size={14} /> : <EyeOff size={14} />}
                Balance
              </ToggleButton>
            </div>
          </div>
          <div className="chart-wrapper">
            <ResponsiveContainer width="100%" height="100%">
              {chartView.timeChart === 'line' ? (
                <LineChart data={timeSeriesData}>
                  <CartesianGrid strokeDasharray="3 3" stroke={colors.border} />
                  <XAxis dataKey="date" stroke={colors.textMuted} />
                  <YAxis stroke={colors.textMuted} />
                  <Tooltip content={<CustomTooltip />} />
                  <Legend />
                  <Line 
                    type="monotone" 
                    dataKey="income" 
                    stroke={colors.success} 
                    strokeWidth={3}
                    dot={{ fill: colors.success, strokeWidth: 2, r: 4 }}
                    name="Income"
                  />
                  <Line 
                    type="monotone" 
                    dataKey="expenses" 
                    stroke={colors.error} 
                    strokeWidth={3}
                    dot={{ fill: colors.error, strokeWidth: 2, r: 4 }}
                    name="Expenses"
                  />
                  {chartView.showBalance && (
                    <Line 
                      type="monotone" 
                      dataKey="balance" 
                      stroke={colors.primary} 
                      strokeWidth={3}
                      dot={{ fill: colors.primary, strokeWidth: 2, r: 4 }}
                      name="Running Balance"
                    />
                  )}
                </LineChart>
              ) : (
                <AreaChart data={timeSeriesData}>
                  <CartesianGrid strokeDasharray="3 3" stroke={colors.border} />
                  <XAxis dataKey="date" stroke={colors.textMuted} />
                  <YAxis stroke={colors.textMuted} />
                  <Tooltip content={<CustomTooltip />} />
                  <Legend />
                  <Area 
                    type="monotone" 
                    dataKey="income" 
                    stackId="1"
                    stroke={colors.success} 
                    fill={colors.success}
                    fillOpacity={0.6}
                    name="Income"
                  />
                  <Area 
                    type="monotone" 
                    dataKey="expenses" 
                    stackId="2"
                    stroke={colors.error} 
                    fill={colors.error}
                    fillOpacity={0.6}
                    name="Expenses"
                  />
                  {chartView.showBalance && (
                    <Area 
                      type="monotone" 
                      dataKey="balance" 
                      stroke={colors.primary} 
                      fill={colors.primary}
                      fillOpacity={0.3}
                      name="Running Balance"
                    />
                  )}
                </AreaChart>
              )}
            </ResponsiveContainer>
          </div>
        </ChartContainer>

        {/* Category Breakdown */}
        <ChartContainer
          initial={{ opacity: 0, y: 30 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.7 }}
        >
          <div className="chart-header">
            <h3>Expenses by Category</h3>
          </div>
          <div className="chart-wrapper">
            <ResponsiveContainer width="100%" height="100%">
              <RechartsPieChart>
                <Pie
                  data={categoryData}
                  cx="50%"
                  cy="50%"
                  innerRadius={60}
                  outerRadius={100}
                  paddingAngle={5}
                  dataKey="value"
                >
                  {categoryData.map((entry, index) => (
                    <Cell key={`cell-${index}`} fill={pieColors[index % pieColors.length]} />
                  ))}
                </Pie>
                <Tooltip content={<CustomTooltip />} />
                <Legend />
              </RechartsPieChart>
            </ResponsiveContainer>
          </div>
        </ChartContainer>

        {/* Transaction Types */}
        <ChartContainer
          initial={{ opacity: 0, y: 30 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.8 }}
        >
          <div className="chart-header">
            <h3>Transaction Types</h3>
          </div>
          <div className="chart-wrapper">
            <ResponsiveContainer width="100%" height="100%">
              <BarChart data={typeData}>
                <CartesianGrid strokeDasharray="3 3" stroke={colors.border} />
                <XAxis dataKey="name" stroke={colors.textMuted} />
                <YAxis stroke={colors.textMuted} />
                <Tooltip content={<CustomTooltip />} />
                <Bar dataKey="value" fill={colors.primary} radius={[8, 8, 0, 0]} />
              </BarChart>
            </ResponsiveContainer>
          </div>
        </ChartContainer>
      </ChartsGrid>

      {/* Recent Transactions Table */}
      <ChartContainer
        initial={{ opacity: 0, y: 30 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ delay: 0.9 }}
      >
        <div className="chart-header">
          <h3>Recent Transactions</h3>
          <div className="chart-controls">
            <span style={{ fontSize: '14px', color: colors.textMuted }}>
              Showing {Math.min(10, filteredTransactions.length)} of {filteredTransactions.length} transactions
            </span>
          </div>
        </div>

        <div style={{ overflowX: 'auto' }}>
          <table style={{ width: '100%', borderCollapse: 'separate', borderSpacing: 0 }}>
            <thead>
              <tr style={{ background: gradients.primary }}>
                <th style={{ padding: spacing.lg, color: 'white', fontWeight: '600', fontSize: '14px', textAlign: 'left' }}>Date</th>
                <th style={{ padding: spacing.lg, color: 'white', fontWeight: '600', fontSize: '14px', textAlign: 'left' }}>Description</th>
                <th style={{ padding: spacing.lg, color: 'white', fontWeight: '600', fontSize: '14px', textAlign: 'left' }}>Category</th>
                <th style={{ padding: spacing.lg, color: 'white', fontWeight: '600', fontSize: '14px', textAlign: 'left' }}>Type</th>
                <th style={{ padding: spacing.lg, color: 'white', fontWeight: '600', fontSize: '14px', textAlign: 'left' }}>Status</th>
                <th style={{ padding: spacing.lg, color: 'white', fontWeight: '600', fontSize: '14px', textAlign: 'right' }}>Amount</th>
              </tr>
            </thead>
            <tbody>
              {filteredTransactions.slice(0, 10).map((transaction, index) => (
                <tr 
                  key={transaction.id}
                  style={{ 
                    background: index % 2 === 0 ? 'rgba(248, 250, 252, 0.5)' : 'transparent',
                    borderBottom: '1px solid rgba(226, 232, 240, 0.5)'
                  }}
                >
                  <td style={{ padding: spacing.lg, color: colors.textSecondary, fontWeight: '500' }}>
                    {new Date(transaction.date).toLocaleDateString()}
                  </td>
                  <td style={{ padding: spacing.lg, color: colors.text, fontWeight: '600' }}>
                    {transaction.description}
                  </td>
                  <td style={{ padding: spacing.lg, color: colors.textSecondary }}>
                    {transaction.category}
                  </td>
                  <td style={{ padding: spacing.lg }}>
                    <TypeBadge type={transaction.type}>
                      {transaction.type}
                    </TypeBadge>
                  </td>
                  <td style={{ padding: spacing.lg }}>
                    <StatusBadge status={transaction.status}>
                      {transaction.status}
                    </StatusBadge>
                  </td>
                  <td style={{ 
                    padding: spacing.lg, 
                    textAlign: 'right', 
                    fontWeight: '700',
                    color: transaction.amount > 0 ? colors.success : colors.error
                  }}>
                    {transaction.amount > 0 ? '+' : ''}${transaction.amount.toLocaleString()}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </ChartContainer>
    </Container>
  );
};

// Styled badge components
const StatusBadge = styled.span`
  padding: 6px ${spacing.md};
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;

  ${props => props.status === 'SUCCESS' && `
    background: linear-gradient(135deg, rgba(72, 187, 120, 0.2), rgba(56, 178, 172, 0.2));
    color: ${colors.success};
    border: 1px solid rgba(72, 187, 120, 0.3);
  `}

  ${props => props.status === 'PENDING' && `
    background: linear-gradient(135deg, rgba(255, 193, 7, 0.2), rgba(255, 152, 0, 0.2));
    color: ${colors.warning};
    border: 1px solid rgba(255, 193, 7, 0.3);
  `}

  ${props => props.status === 'FAILED' && `
    background: linear-gradient(135deg, rgba(255, 107, 107, 0.2), rgba(238, 90, 82, 0.2));
    color: ${colors.error};
    border: 1px solid rgba(255, 107, 107, 0.3);
  `}
`;

const TypeBadge = styled.span`
  padding: 6px ${spacing.md};
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;

  ${props => props.type === 'DEPOSIT' && `
    background: linear-gradient(135deg, rgba(72, 187, 120, 0.2), rgba(56, 178, 172, 0.2));
    color: ${colors.success};
    border: 1px solid rgba(72, 187, 120, 0.3);
  `}

  ${props => props.type === 'WITHDRAW' && `
    background: linear-gradient(135deg, rgba(255, 107, 107, 0.2), rgba(238, 90, 82, 0.2));
    color: ${colors.error};
    border: 1px solid rgba(255, 107, 107, 0.3);
  `}

  ${props => props.type === 'TRANSFER' && `
    background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
    color: ${colors.primary};
    border: 1px solid rgba(102, 126, 234, 0.3);
  `}

  ${props => props.type === 'LOAN' && `
    background: linear-gradient(135deg, rgba(255, 193, 7, 0.2), rgba(255, 152, 0, 0.2));
    color: ${colors.warning};
    border: 1px solid rgba(255, 193, 7, 0.3);
  `}
`;

export default BankingAnalyticsDashboard


This content originally appeared on DEV Community and was authored by Nebula