Skip to content

Latest commit

 

History

History
717 lines (597 loc) · 17.5 KB

File metadata and controls

717 lines (597 loc) · 17.5 KB

Section 11: Web Application Development

🌐 Building Responsive Blockchain Web Interfaces

Welcome to Section 11! This section focuses on creating modern, responsive web applications for blockchain systems. You'll learn how to build beautiful, functional web interfaces that make blockchain technology accessible to users through intuitive design and real-time data display.

What You'll Learn in This Section

  • Modern web development with React and Go
  • Responsive design principles and implementation
  • Real-time blockchain data display and updates
  • User authentication and session management
  • WebSocket integration for live updates
  • Progressive Web App (PWA) features

Section Overview

This section teaches you how to transform your blockchain backend into a user-friendly web application. You'll build a complete web interface that allows users to interact with the blockchain, view transactions, manage wallets, and monitor network status in real-time.


🎨 Modern Web Development Stack

Frontend Technologies

React.js

  • Component-based architecture for reusable UI elements
  • State management with hooks and context
  • Virtual DOM for efficient rendering
  • JSX for declarative UI development

CSS Frameworks

  • Tailwind CSS for utility-first styling
  • Responsive design principles
  • Modern CSS features (Grid, Flexbox, Custom Properties)
  • Dark/Light theme support

Real-time Communication

  • WebSocket connections for live updates
  • Server-Sent Events (SSE) for one-way communication
  • RESTful API integration
  • GraphQL for efficient data fetching

Backend Integration

Go API Server

  • Gorilla Mux for routing
  • CORS configuration for cross-origin requests
  • JWT authentication for secure sessions
  • WebSocket support for real-time communication

🏗️ Web Application Architecture

Component Structure

src/
├── components/
│   ├── Layout/
│   │   ├── Header.jsx
│   │   ├── Sidebar.jsx
│   │   └── Footer.jsx
│   ├── Blockchain/
│   │   ├── BlockExplorer.jsx
│   │   ├── TransactionList.jsx
│   │   └── WalletManager.jsx
│   ├── Dashboard/
│   │   ├── NetworkStatus.jsx
│   │   ├── Statistics.jsx
│   │   └── Charts.jsx
│   └── Common/
│       ├── Button.jsx
│       ├── Modal.jsx
│       └── Loading.jsx
├── pages/
│   ├── Home.jsx
│   ├── Blocks.jsx
│   ├── Transactions.jsx
│   ├── Wallets.jsx
│   └── Network.jsx
├── hooks/
│   ├── useBlockchain.js
│   ├── useWebSocket.js
│   └── useAuth.js
├── services/
│   ├── api.js
│   ├── websocket.js
│   └── auth.js
└── utils/
    ├── constants.js
    ├── helpers.js
    └── validators.js

State Management

// Context for global state
const BlockchainContext = createContext();

// Custom hooks for state management
const useBlockchain = () => {
  const [blocks, setBlocks] = useState([]);
  const [transactions, setTransactions] = useState([]);
  const [wallets, setWallets] = useState([]);
  const [networkStatus, setNetworkStatus] = useState({});
  
  return {
    blocks,
    transactions,
    wallets,
    networkStatus,
    setBlocks,
    setTransactions,
    setWallets,
    setNetworkStatus
  };
};

🎯 Core Web Components

1. Block Explorer Component

import React, { useState, useEffect } from 'react';
import { useBlockchain } from '../hooks/useBlockchain';

const BlockExplorer = () => {
  const { blocks, setBlocks } = useBlockchain();
  const [loading, setLoading] = useState(true);
  const [selectedBlock, setSelectedBlock] = useState(null);

  useEffect(() => {
    fetchBlocks();
  }, []);

  const fetchBlocks = async () => {
    try {
      const response = await fetch('/api/v1/blocks');
      const data = await response.json();
      setBlocks(data);
    } catch (error) {
      console.error('Error fetching blocks:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="block-explorer">
      <h2 className="text-2xl font-bold mb-4">Block Explorer</h2>
      
      {loading ? (
        <div className="loading-spinner">Loading blocks...</div>
      ) : (
        <div className="blocks-grid">
          {blocks.map((block) => (
            <BlockCard
              key={block.hash}
              block={block}
              onClick={() => setSelectedBlock(block)}
            />
          ))}
        </div>
      )}
      
      {selectedBlock && (
        <BlockModal
          block={selectedBlock}
          onClose={() => setSelectedBlock(null)}
        />
      )}
    </div>
  );
};

const BlockCard = ({ block, onClick }) => (
  <div 
    className="block-card cursor-pointer hover:shadow-lg transition-shadow"
    onClick={onClick}
  >
    <div className="block-header">
      <span className="block-number">#{block.index}</span>
      <span className="block-hash">{block.hash.substring(0, 16)}...</span>
    </div>
    <div className="block-details">
      <p>Transactions: {block.transactions.length}</p>
      <p>Timestamp: {new Date(block.timestamp).toLocaleString()}</p>
    </div>
  </div>
);

2. Transaction List Component

const TransactionList = () => {
  const { transactions, setTransactions } = useBlockchain();
  const [filter, setFilter] = useState('all');
  const [searchTerm, setSearchTerm] = useState('');

  const filteredTransactions = transactions.filter(tx => {
    const matchesFilter = filter === 'all' || tx.type === filter;
    const matchesSearch = tx.id.includes(searchTerm) || 
                         tx.sender.includes(searchTerm) || 
                         tx.recipient.includes(searchTerm);
    return matchesFilter && matchesSearch;
  });

  return (
    <div className="transaction-list">
      <div className="filters">
        <input
          type="text"
          placeholder="Search transactions..."
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
          className="search-input"
        />
        <select
          value={filter}
          onChange={(e) => setFilter(e.target.value)}
          className="filter-select"
        >
          <option value="all">All Transactions</option>
          <option value="BANK">Bank Transactions</option>
          <option value="MESSAGE">Message Transactions</option>
          <option value="COINBASE">Coinbase Transactions</option>
        </select>
      </div>
      
      <div className="transactions-table">
        <table>
          <thead>
            <tr>
              <th>ID</th>
              <th>Type</th>
              <th>Sender</th>
              <th>Recipient</th>
              <th>Amount</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>
            {filteredTransactions.map((tx) => (
              <TransactionRow key={tx.id} transaction={tx} />
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
};

3. Wallet Manager Component

const WalletManager = () => {
  const { wallets, setWallets } = useBlockchain();
  const [showCreateWallet, setShowCreateWallet] = useState(false);
  const [newWalletName, setNewWalletName] = useState('');

  const createWallet = async () => {
    try {
      const response = await fetch('/api/v1/wallets', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ name: newWalletName }),
      });
      
      const wallet = await response.json();
      setWallets([...wallets, wallet]);
      setShowCreateWallet(false);
      setNewWalletName('');
    } catch (error) {
      console.error('Error creating wallet:', error);
    }
  };

  return (
    <div className="wallet-manager">
      <div className="wallet-header">
        <h2 className="text-2xl font-bold">Wallet Manager</h2>
        <button
          onClick={() => setShowCreateWallet(true)}
          className="create-wallet-btn"
        >
          Create New Wallet
        </button>
      </div>
      
      <div className="wallets-grid">
        {wallets.map((wallet) => (
          <WalletCard key={wallet.address} wallet={wallet} />
        ))}
      </div>
      
      {showCreateWallet && (
        <CreateWalletModal
          walletName={newWalletName}
          setWalletName={setNewWalletName}
          onCreate={createWallet}
          onClose={() => setShowCreateWallet(false)}
        />
      )}
    </div>
  );
};

🔄 Real-time Updates with WebSocket

WebSocket Integration

// WebSocket service
class WebSocketService {
  constructor() {
    this.ws = null;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;
  }

  connect() {
    this.ws = new WebSocket('ws://localhost:8080/ws');
    
    this.ws.onopen = () => {
      console.log('WebSocket connected');
      this.reconnectAttempts = 0;
    };
    
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.handleMessage(data);
    };
    
    this.ws.onclose = () => {
      console.log('WebSocket disconnected');
      this.reconnect();
    };
    
    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error);
    };
  }

  handleMessage(data) {
    switch (data.type) {
      case 'new_block':
        this.updateBlocks(data.block);
        break;
      case 'new_transaction':
        this.updateTransactions(data.transaction);
        break;
      case 'network_status':
        this.updateNetworkStatus(data.status);
        break;
      default:
        console.log('Unknown message type:', data.type);
    }
  }

  reconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      setTimeout(() => {
        console.log(`Reconnecting... Attempt ${this.reconnectAttempts}`);
        this.connect();
      }, 1000 * this.reconnectAttempts);
    }
  }

  send(message) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    }
  }
}

Real-time Hook

// Custom hook for real-time updates
const useWebSocket = () => {
  const { setBlocks, setTransactions, setNetworkStatus } = useBlockchain();
  const [wsService] = useState(() => new WebSocketService());

  useEffect(() => {
    wsService.connect();
    
    // Override message handlers
    wsService.updateBlocks = (block) => {
      setBlocks(prev => [block, ...prev]);
    };
    
    wsService.updateTransactions = (transaction) => {
      setTransactions(prev => [transaction, ...prev]);
    };
    
    wsService.updateNetworkStatus = (status) => {
      setNetworkStatus(status);
    };

    return () => {
      if (wsService.ws) {
        wsService.ws.close();
      }
    };
  }, []);

  return wsService;
};

🔐 Authentication & Security

Authentication Context

const AuthContext = createContext();

const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    checkAuthStatus();
  }, []);

  const checkAuthStatus = async () => {
    const token = localStorage.getItem('authToken');
    if (token) {
      try {
        const response = await fetch('/api/v1/auth/verify', {
          headers: {
            'Authorization': `Bearer ${token}`
          }
        });
        
        if (response.ok) {
          const userData = await response.json();
          setUser(userData);
        } else {
          localStorage.removeItem('authToken');
        }
      } catch (error) {
        console.error('Auth check failed:', error);
        localStorage.removeItem('authToken');
      }
    }
    setLoading(false);
  };

  const login = async (credentials) => {
    try {
      const response = await fetch('/api/v1/auth/login', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(credentials),
      });
      
      if (response.ok) {
        const { token, user } = await response.json();
        localStorage.setItem('authToken', token);
        setUser(user);
        return { success: true };
      } else {
        return { success: false, error: 'Invalid credentials' };
      }
    } catch (error) {
      return { success: false, error: 'Login failed' };
    }
  };

  const logout = () => {
    localStorage.removeItem('authToken');
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout, loading }}>
      {children}
    </AuthContext.Provider>
  );
};

Protected Routes

const ProtectedRoute = ({ children }) => {
  const { user, loading } = useContext(AuthContext);

  if (loading) {
    return <div className="loading">Loading...</div>;
  }

  if (!user) {
    return <Navigate to="/login" replace />;
  }

  return children;
};

// Route configuration
const App = () => {
  return (
    <AuthProvider>
      <Router>
        <Routes>
          <Route path="/login" element={<LoginPage />} />
          <Route path="/" element={
            <ProtectedRoute>
              <Dashboard />
            </ProtectedRoute>
          } />
          <Route path="/blocks" element={
            <ProtectedRoute>
              <BlockExplorer />
            </ProtectedRoute>
          } />
          <Route path="/transactions" element={
            <ProtectedRoute>
              <TransactionList />
            </ProtectedRoute>
          } />
          <Route path="/wallets" element={
            <ProtectedRoute>
              <WalletManager />
            </ProtectedRoute>
          } />
        </Routes>
      </Router>
    </AuthProvider>
  );
};

📱 Responsive Design

Mobile-First Approach

/* Base styles for mobile */
.block-card {
  padding: 1rem;
  margin-bottom: 1rem;
  border-radius: 0.5rem;
  background: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.blocks-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

/* Tablet styles */
@media (min-width: 768px) {
  .blocks-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Desktop styles */
@media (min-width: 1024px) {
  .blocks-grid {
    grid-template-columns: repeat(3, 1fr);
  }
  
  .block-card {
    padding: 1.5rem;
  }
}

/* Large desktop styles */
@media (min-width: 1280px) {
  .blocks-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

Flexible Layout Components

const Layout = ({ children }) => {
  const [sidebarOpen, setSidebarOpen] = useState(false);

  return (
    <div className="layout">
      <Header onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
      
      <div className="layout-content">
        <Sidebar isOpen={sidebarOpen} onClose={() => setSidebarOpen(false)} />
        
        <main className="main-content">
          {children}
        </main>
      </div>
    </div>
  );
};

const Header = ({ onMenuClick }) => (
  <header className="header">
    <button 
      className="menu-button md:hidden"
      onClick={onMenuClick}
    >
      <MenuIcon />
    </button>
    
    <h1 className="header-title">Blockchain Explorer</h1>
    
    <div className="header-actions">
      <NetworkStatus />
      <UserMenu />
    </div>
  </header>
);

🎯 Section Summary

In this section, you've learned:

Modern Web Development: React.js with Go backend integration ✅ Responsive Design: Mobile-first, responsive layouts ✅ Real-time Updates: WebSocket integration for live data ✅ Authentication: JWT-based user authentication ✅ Component Architecture: Reusable, maintainable components ✅ State Management: Context API and custom hooks

Key Concepts Mastered

  1. React Development: Component-based UI development
  2. Real-time Communication: WebSocket and live updates
  3. Responsive Design: Mobile-first, adaptive layouts
  4. Authentication: Secure user sessions and protected routes
  5. API Integration: RESTful API consumption and error handling
  6. State Management: Global state and local component state

Next Steps

  1. Complete the hands-on exercises below
  2. Take the quiz to test your understanding
  3. Move on to Section 12: Mobile App Development

🛠️ Hands-On Exercises

Exercise 1: Basic React Setup

Set up a React application with:

  1. Create React App or Vite setup
  2. Tailwind CSS configuration
  3. Basic component structure
  4. Routing with React Router

Exercise 2: Block Explorer Component

Create a block explorer that:

  1. Displays blocks in a responsive grid
  2. Shows block details in a modal
  3. Implements search and filtering
  4. Handles loading and error states

Exercise 3: Real-time Updates

Implement real-time features:

  1. WebSocket connection setup
  2. Live block updates
  3. Real-time transaction feed
  4. Network status monitoring

Exercise 4: Authentication System

Build authentication:

  1. Login/logout functionality
  2. Protected routes
  3. JWT token management
  4. User profile management

Exercise 5: Responsive Dashboard

Create a responsive dashboard:

  1. Mobile-first design
  2. Adaptive layouts
  3. Touch-friendly interactions
  4. Performance optimization

📝 Quiz

Ready to test your knowledge? Take the Section 11 Quiz to verify your understanding of web application development.


Excellent work! You've built a responsive web interface for your blockchain. You're ready to create mobile applications in Section 12! 🚀