Module 4: Build Sprint 3
Understanding Your Third Ticket
Learn how to approach your third ticket in the Labs project and understand the development workflow.
Third Ticket Details
View your third ticket details and requirements on GitHub:
Third Ticket DocumentationApproaching Your Third Feature
Learn how to implement a React client application that integrates with your backend API.
Implementation Checklist
- Review and select 3 pages from the provided wireframes
- Set up React project structure and routing
- Implement authentication flow with JWT
- Create reusable components for common UI elements
- Integrate with backend API endpoints
- Implement responsive design
- Add error handling and loading states
React Project Structure
// Example project structure src/ ├── components/ │ ├── common/ │ │ ├── Button.jsx │ │ ├── Card.jsx │ │ └── Navbar.jsx │ ├── auth/ │ │ ├── LoginForm.jsx │ │ └── ProtectedRoute.jsx │ └── pages/ │ ├── Home.jsx │ ├── Dashboard.jsx │ └── AssignmentView.jsx ├── services/ │ ├── api.js │ └── auth.js ├── context/ │ └── AuthContext.jsx └── utils/ └── helpers.js
Authentication Context Example
// Example of authentication context setup import { createContext, useContext, useState, useEffect } from 'react'; import api from '../services/api'; const AuthContext = createContext(); export function AuthProvider({ children }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const login = async (credentials) => { try { const response = await api.post('/api/auth/login', credentials); const { token } = response.data; localStorage.setItem('token', token); // Decode token and set user const decodedUser = decodeToken(token); setUser(decodedUser); return { success: true }; } catch (error) { return { success: false, message: error.response?.data?.message || 'Login failed' }; } }; const logout = () => { localStorage.removeItem('token'); setUser(null); }; // Check if user is authenticated on initial load useEffect(() => { const checkAuth = async () => { const token = localStorage.getItem('token'); if (token) { try { // Verify token is still valid const response = await api.get('/api/auth/me'); setUser(response.data); } catch (error) { localStorage.removeItem('token'); } } setLoading(false); }; checkAuth(); }, []); const value = { user, loading, login, logout, isAuthenticated: !!user }; return ( <AuthContext.Provider value={value}> {children} </AuthContext.Provider> ); } export function useAuth() { return useContext(AuthContext); }
API Integration
Learn how to integrate your React frontend with your Spring Boot backend API.
API Service Example
// Example of API service setup import axios from 'axios'; const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8080/api'; const api = axios.create({ baseURL: API_URL, headers: { 'Content-Type': 'application/json' } }); // Add request interceptor to include auth token api.interceptors.request.use( (config) => { const token = localStorage.getItem('token'); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); // Add response interceptor to handle common errors api.interceptors.response.use( (response) => response, (error) => { if (error.response && error.response.status === 401) { // Unauthorized - token expired or invalid localStorage.removeItem('token'); window.location.href = '/login'; } return Promise.reject(error); } ); export default api;
React Component Example
// Example of a component that fetches and displays data import { useState, useEffect } from 'react'; import api from '../services/api'; import { useAuth } from '../context/AuthContext'; function Dashboard() { const [assignments, setAssignments] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const { user } = useAuth(); useEffect(() => { const fetchAssignments = async () => { try { const response = await api.get('/assignments'); setAssignments(response.data); setLoading(false); } catch (err) { setError('Failed to load assignments'); setLoading(false); } }; fetchAssignments(); }, []); if (loading) return <div>Loading assignments...</div>; if (error) return <div>{error}</div>; return ( <div className="dashboard"> <h1>Welcome, {user.name}!</h1> <h2>Your Assignments</h2> <div className="assignment-list"> {assignments.length === 0 ? ( <p>No assignments found.</p> ) : ( assignments.map(assignment => ( <div key={assignment.id} className="assignment-card"> <h3>{assignment.title}</h3> <p>{assignment.description}</p> <span className="status">{assignment.status}</span> <button onClick={() => navigate(`/assignments/${assignment.id}`)}> View Details </button> </div> )) )} </div> </div> ); }