# React Frontend Implementation Plan for Sim-Search ## Overview This document outlines the plan for implementing a React frontend for the sim-search project, replacing the current Gradio interface with a modern, responsive, and feature-rich user interface. The frontend will communicate with the new FastAPI backend to provide a seamless user experience. ## Architecture ### Core Components 1. **Next.js Framework** - Server-side rendering for improved SEO - API routes for backend proxying if needed - Static site generation for performance 2. **Component Library** - Modular React components - Reusable UI elements - Styling with Tailwind CSS 3. **State Management** - React Query for server state - Context API for application state - Form state management 4. **Authentication** - JWT token management - Protected routes - User profile management ## Directory Structure ``` sim-search-ui/ ├── src/ │ ├── components/ │ │ ├── layout/ │ │ │ ├── Header.jsx # Application header │ │ │ ├── Sidebar.jsx # Sidebar menu │ │ │ └── Layout.jsx # Main layout wrapper │ │ ├── search/ │ │ │ ├── SearchForm.jsx # Search input form │ │ │ ├── SearchResults.jsx # Results display │ │ │ ├── ResultItem.jsx # Individual result │ │ │ └── EngineSelector.jsx # Search engine selector │ │ ├── report/ │ │ │ ├── ReportGenerator.jsx # Report generation form │ │ │ ├── ReportViewer.jsx # Report display │ │ │ ├── ReportsList.jsx # Reports list/management │ │ │ └── ReportOptions.jsx # Report generation options │ │ ├── common/ │ │ │ ├── Button.jsx # Reusable button component │ │ │ ├── Card.jsx # Card container component │ │ │ ├── Loading.jsx # Loading indicator │ │ │ └── Modal.jsx # Modal dialog │ │ └── auth/ │ │ ├── LoginForm.jsx # User login form │ │ └── RegisterForm.jsx # User registration form │ ├── hooks/ │ │ ├── useAuth.js # Authentication hook │ │ ├── useSearch.js # Search execution hook │ │ └── useReport.js # Report management hook │ ├── context/ │ │ ├── AuthContext.jsx # Authentication context │ │ └── SearchContext.jsx # Search state context │ ├── services/ │ │ ├── api.js # API client service │ │ ├── auth.js # Authentication service │ │ ├── search.js # Search service │ │ └── report.js # Report service │ ├── utils/ │ │ ├── formatting.js # Text/data formatting utilities │ │ └── validation.js # Form validation utilities │ ├── styles/ │ │ ├── globals.css # Global styles │ │ └── theme.js # Theme configuration │ └── pages/ │ ├── _app.jsx # App component │ ├── index.jsx # Home page │ ├── search.jsx # Search page │ ├── reports/ │ │ ├── index.jsx # Reports list page │ │ ├── [id].jsx # Individual report page │ │ └── new.jsx # New report page │ └── auth/ │ ├── login.jsx # Login page │ └── register.jsx # Registration page ├── public/ │ ├── logo.svg # Application logo │ └── favicon.ico # Favicon ├── tailwind.config.js # Tailwind configuration ├── next.config.js # Next.js configuration └── package.json # Dependencies ``` ## Key Pages and Features ### Home Page - Overview of the system - Quick access to search and reports - Feature highlights and documentation ### Search Page - Comprehensive search form - Multiple search engine selection - Advanced search options - Results display with filtering and sorting - Options to generate reports from results ### Report Generation Page - Detail level selection - Query type selection - Model selection - Advanced options - Progress tracking ### Reports Management Page - List of generated reports - Filtering and sorting options - Download in different formats - Delete and manage reports ### Authentication Pages - Login page - Registration page - User profile management ## Component Design ### Search Components #### SearchForm Component ```jsx const SearchForm = ({ onSearchComplete }) => { const [query, setQuery] = useState(''); const [selectedEngines, setSelectedEngines] = useState([]); const [numResults, setNumResults] = useState(10); const [useReranker, setUseReranker] = useState(true); const { engines, loading, error, loadEngines, search } = useSearch(); // Load available search engines on component mount useEffect(() => { loadEngines(); }, []); // Handle search submission const handleSubmit = async (e) => { e.preventDefault(); const searchParams = { query: query.trim(), search_engines: selectedEngines.length > 0 ? selectedEngines : undefined, num_results: numResults, use_reranker: useReranker, }; const results = await search(searchParams); if (results && onSearchComplete) { onSearchComplete(results); } }; return ( // Form UI with input fields, engine selection, and options ); }; ``` #### SearchResults Component ```jsx const SearchResults = ({ results, query, onGenerateReport }) => { const [selectedResults, setSelectedResults] = useState([]); const [sortBy, setSortBy] = useState('relevance'); // Toggle a result's selection const toggleResultSelection = (resultId) => { setSelectedResults(prev => ( prev.includes(resultId) ? prev.filter(id => id !== resultId) : [...prev, resultId] )); }; // Handle generate report button click const handleGenerateReport = () => { // Filter results to only include selected ones if any are selected const resultsToUse = selectedResults.length > 0 ? results.filter((result, index) => selectedResults.includes(index)) : results; if (onGenerateReport) { onGenerateReport(resultsToUse, query); } }; return ( // Results UI with sorting, filtering, and item selection ); }; ``` ### Report Components #### ReportGenerator Component ```jsx const ReportGenerator = ({ query, searchResults, searchId }) => { const [detailLevel, setDetailLevel] = useState('standard'); const [queryType, setQueryType] = useState('auto-detect'); const [customModel, setCustomModel] = useState(''); const [initialResults, setInitialResults] = useState(10); const [finalResults, setFinalResults] = useState(7); const { loading, error, createReport } = useReport(); // Generate the report const handleGenerateReport = async () => { const reportParams = { query, search_id: searchId, search_results: !searchId ? searchResults : undefined, detail_level: detailLevel, query_type: queryType, custom_model: customModel || undefined, initial_results: initialResults, final_results: finalResults }; await createReport(reportParams); }; return ( // Report generation form with options ); }; ``` #### ReportViewer Component ```jsx const ReportViewer = ({ report, onDownload }) => { const [selectedFormat, setSelectedFormat] = useState('markdown'); const { download, loading } = useReport(); const handleDownload = async () => { if (onDownload) { onDownload(report.id, selectedFormat); } else { await download(report.id, selectedFormat); } }; return ( // Report content display with markdown rendering and download options ); }; ``` ## API Integration Services ### API Client Service ```javascript import axios from 'axios'; // Create an axios instance with default config const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000', headers: { 'Content-Type': 'application/json', }, }); // Add a request interceptor to include auth token in requests api.interceptors.request.use( (config) => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); // Add a response interceptor to handle common errors api.interceptors.response.use( (response) => response, (error) => { // Handle 401 Unauthorized - redirect to login if (error.response && error.response.status === 401) { localStorage.removeItem('token'); window.location.href = '/auth/login'; } return Promise.reject(error); } ); export default api; ``` ### Search Service ```javascript import api from './api'; export const executeSearch = async (searchParams) => { try { const response = await api.post('/api/search/execute', searchParams); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.response?.data?.detail || 'Failed to execute search' }; } }; export const getAvailableEngines = async () => { try { const response = await api.get('/api/search/engines'); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.response?.data?.detail || 'Failed to get search engines' }; } }; ``` ### Report Service ```javascript import api from './api'; export const generateReport = async (reportParams) => { try { const response = await api.post('/api/report/generate', reportParams); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.response?.data?.detail || 'Failed to generate report' }; } }; export const getReportsList = async (skip = 0, limit = 100) => { try { const response = await api.get(`/api/report/list?skip=${skip}&limit=${limit}`); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.response?.data?.detail || 'Failed to get reports list' }; } }; ``` ## Custom Hooks ### Authentication Hook ```javascript import { useState, useEffect, useContext, createContext } from 'react'; import { getCurrentUser, isAuthenticated } from '../services/auth'; // Create auth context const AuthContext = createContext(null); // Auth provider component export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { // Check if user is authenticated and fetch user data const fetchUser = async () => { if (isAuthenticated()) { try { setLoading(true); const result = await getCurrentUser(); if (result.success) { setUser(result.data); } else { setError(result.error); } } catch (err) { setError('Failed to fetch user data'); } finally { setLoading(false); } } else { setLoading(false); } }; fetchUser(); }, []); // Return provider with auth context return ( {children} ); }; // Custom hook to use auth context export const useAuth = () => { const context = useContext(AuthContext); if (context === null) { throw new Error('useAuth must be used within an AuthProvider'); } return context; }; ``` ### Search Hook ```javascript import { useState } from 'react'; import { executeSearch, getAvailableEngines } from '../services/search'; export const useSearch = () => { const [results, setResults] = useState([]); const [engines, setEngines] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // Load available search engines const loadEngines = async () => { try { setLoading(true); const result = await getAvailableEngines(); if (result.success) { setEngines(result.data); } else { setError(result.error); } } catch (err) { setError('Failed to load search engines'); } finally { setLoading(false); } }; // Execute a search const search = async (searchParams) => { try { setLoading(true); setError(null); const result = await executeSearch(searchParams); if (result.success) { setResults(result.data.results); return result.data; } else { setError(result.error); return null; } } catch (err) { setError('Failed to execute search'); return null; } finally { setLoading(false); } }; return { results, engines, loading, error, search, loadEngines, }; }; ``` ## Implementation Phases ### Phase 1: Project Setup & Core Components (Week 1) - Set up Next.js project - Configure Tailwind CSS - Implement common UI components - Create layout components ### Phase 2: Authentication & API Integration (Week 1-2) - Implement authentication components - Create API service layer - Implement custom hooks - Set up protected routes ### Phase 3: Search Functionality (Week 2) - Implement search form - Create search results display - Add filtering and sorting - Implement search engine selection ### Phase 4: Report Generation & Management (Week 2-3) - Implement report generation form - Create report viewer with markdown rendering - Add report management interface - Implement download functionality ### Phase 5: Testing & Refinement (Week 3) - Write component tests - Perform cross-browser testing - Add responsive design improvements - Optimize performance ### Phase 6: Deployment & Documentation (Week 3-4) - Set up deployment configuration - Create user documentation - Add inline help and tooltips - Perform final testing ## Dependencies ```json { "dependencies": { "next": "^13.5.4", "react": "^18.2.0", "react-dom": "^18.2.0", "axios": "^1.5.1", "react-markdown": "^9.0.0", "react-query": "^3.39.3", "tailwindcss": "^3.3.3", "postcss": "^8.4.31", "autoprefixer": "^10.4.16", "jose": "^4.14.6" }, "devDependencies": { "eslint": "^8.51.0", "eslint-config-next": "^13.5.4", "typescript": "^5.2.2", "@types/react": "^18.2.28", "@types/node": "^20.8.6", "jest": "^29.7.0", "@testing-library/react": "^14.0.0", "@testing-library/jest-dom": "^6.1.4" } } ``` ## Accessibility Considerations The React frontend will be built with accessibility in mind: 1. **Semantic HTML**: Use proper HTML elements for their intended purpose 2. **ARIA Attributes**: Add ARIA attributes where necessary 3. **Keyboard Navigation**: Ensure all interactive elements are keyboard accessible 4. **Focus Management**: Properly manage focus, especially in modals and dialogs 5. **Color Contrast**: Ensure sufficient color contrast for text and UI elements 6. **Screen Reader Support**: Test with screen readers to ensure compatibility ## Performance Optimization To ensure optimal performance: 1. **Code Splitting**: Use Next.js code splitting to reduce initial bundle size 2. **Lazy Loading**: Implement lazy loading for components not needed immediately 3. **Memoization**: Use React.memo and useMemo to prevent unnecessary re-renders 4. **Image Optimization**: Use Next.js image optimization for faster loading 5. **API Response Caching**: Cache API responses with React Query 6. **Bundle Analysis**: Regularly analyze bundle size to identify improvements ## Conclusion This implementation plan provides a structured approach to creating a modern React frontend for the sim-search project. By following this plan, we will create a user-friendly, accessible, and feature-rich interface that leverages the power of the new FastAPI backend. The component-based architecture ensures reusability and maintainability, while the use of modern React patterns and hooks simplifies state management and side effects. The integration with the FastAPI backend is handled through a clean service layer, making it easy to adapt to changes in the API. With this implementation, users will have a much improved experience compared to the current Gradio interface, with better search capabilities, more advanced report generation options, and a more intuitive interface for managing their research.