Donation Tracking\ Validation Rejection\ Subscription Data Export\ Admin Dashboard Logo\ Admin Navbar Reorganization
913 lines
21 KiB
Markdown
913 lines
21 KiB
Markdown
# LOAF Membership Platform - Frontend
|
|
|
|
React 19-based frontend application for the LOAF (LGBT Organization and Friends) membership management platform.
|
|
|
|
## Table of Contents
|
|
|
|
- [Setup & Installation](#setup--installation)
|
|
- [Architecture & Code Structure](#architecture--code-structure)
|
|
- [Design System](#design-system)
|
|
- [Deployment Guide](#deployment-guide)
|
|
- [Troubleshooting](#troubleshooting)
|
|
|
|
---
|
|
|
|
## Setup & Installation
|
|
|
|
### Prerequisites
|
|
|
|
- **Node.js**: 18.0 or higher
|
|
- **Yarn**: 1.22+ (or npm 8+)
|
|
- **Backend API**: Running on http://localhost:8000
|
|
|
|
### 1. Install Dependencies
|
|
|
|
```bash
|
|
cd frontend
|
|
|
|
# Using Yarn (recommended)
|
|
yarn install
|
|
|
|
# Or using npm
|
|
npm install
|
|
```
|
|
|
|
**Key Dependencies:**
|
|
- `react@19.0.0` - UI library
|
|
- `react-router-dom@7.5.1` - Routing
|
|
- `axios@1.8.4` - HTTP client
|
|
- `react-hook-form@7.56.2` - Form handling
|
|
- `zod@3.24.4` - Schema validation
|
|
- `@radix-ui/*` - UI components (45+ components)
|
|
- `tailwindcss@3.4.17` - CSS framework
|
|
- `lucide-react@0.507.0` - Icons
|
|
- `sonner@1.7.4` - Toast notifications
|
|
|
|
### 2. Environment Configuration
|
|
|
|
Create `.env` file in the frontend directory:
|
|
|
|
```bash
|
|
# Backend API URL
|
|
REACT_APP_BACKEND_URL=http://localhost:8000
|
|
|
|
# Optional: Analytics, Sentry, etc.
|
|
# REACT_APP_SENTRY_DSN=your-sentry-dsn
|
|
# REACT_APP_GA_TRACKING_ID=UA-XXXXXXXXX-X
|
|
```
|
|
|
|
**Important:**
|
|
- All environment variables must start with `REACT_APP_`
|
|
- Restart development server after changing `.env`
|
|
|
|
### 3. Start Development Server
|
|
|
|
```bash
|
|
# Start development server
|
|
yarn start
|
|
|
|
# Or with npm
|
|
npm start
|
|
```
|
|
|
|
**Development server will be available at:**
|
|
- Frontend: http://localhost:3000
|
|
- Auto-reloads on file changes
|
|
|
|
### 4. Build for Production
|
|
|
|
```bash
|
|
# Create production build
|
|
yarn build
|
|
|
|
# Or with npm
|
|
npm build
|
|
```
|
|
|
|
Build output will be in `/build` directory.
|
|
|
|
### 5. Run Tests
|
|
|
|
```bash
|
|
# Run tests in watch mode
|
|
yarn test
|
|
|
|
# Run tests with coverage
|
|
yarn test --coverage
|
|
|
|
# Or with npm
|
|
npm test
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture & Code Structure
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
frontend/
|
|
├── public/ # Static assets
|
|
│ ├── loaf-logo.png # LOAF logo for admin sidebar
|
|
│ ├── hero-loaf.png # Landing page hero image
|
|
│ └── index.html # HTML template
|
|
├── src/
|
|
│ ├── pages/ # Page components (20+ pages)
|
|
│ │ ├── Landing.js # Public landing page
|
|
│ │ ├── Register.js # 4-step registration (388 lines)
|
|
│ │ ├── Login.js # Authentication
|
|
│ │ ├── Dashboard.js # Member dashboard
|
|
│ │ ├── Profile.js # User profile with photo upload
|
|
│ │ ├── Events.js # Event listing
|
|
│ │ ├── EventDetails.js # Event details + RSVP
|
|
│ │ └── admin/ # Admin pages
|
|
│ │ ├── AdminDashboard.js
|
|
│ │ ├── AdminUsers.js
|
|
│ │ ├── AdminValidations.js # Approve/reject workflow
|
|
│ │ ├── AdminEvents.js
|
|
│ │ ├── AdminSubscriptions.js # With CSV export
|
|
│ │ └── AdminDonations.js # Donation tracking
|
|
│ ├── components/ # Reusable components
|
|
│ │ ├── Navbar.js # Main navigation
|
|
│ │ ├── AdminSidebar.js # Admin sidebar with logo
|
|
│ │ ├── RejectionDialog.js # Rejection workflow dialog
|
|
│ │ └── ui/ # 45+ Radix UI components
|
|
│ │ ├── button.js
|
|
│ │ ├── dialog.js
|
|
│ │ ├── input.js
|
|
│ │ ├── select.js
|
|
│ │ └── ... (40+ more)
|
|
│ ├── context/
|
|
│ │ └── AuthContext.js # Global auth state
|
|
│ ├── hooks/
|
|
│ │ └── use-toast.js # Toast notification hook
|
|
│ ├── utils/
|
|
│ │ └── api.js # Axios instance with JWT interceptor
|
|
│ ├── App.js # Main routing setup
|
|
│ ├── index.js # React entry point
|
|
│ └── index.css # Global styles + Tailwind
|
|
├── craco.config.js # Craco configuration
|
|
├── tailwind.config.js # Tailwind CSS configuration
|
|
├── package.json # Dependencies
|
|
└── .env # Environment variables (not in git)
|
|
```
|
|
|
|
### Core Technologies
|
|
|
|
| Technology | Purpose | Version |
|
|
|------------|---------|---------|
|
|
| React | UI library | 19.0.0 |
|
|
| React Router | Client-side routing | 7.5.1 |
|
|
| Axios | HTTP requests | 1.8.4 |
|
|
| React Hook Form | Form handling | 7.56.2 |
|
|
| Zod | Schema validation | 3.24.4 |
|
|
| Tailwind CSS | CSS framework | 3.4.17 |
|
|
| Radix UI | Component library | Latest |
|
|
| Lucide React | Icons | 0.507.0 |
|
|
| Sonner | Toast notifications | 1.7.4 |
|
|
|
|
### Key Features
|
|
|
|
#### Authentication System
|
|
|
|
**Global Auth Context** (`src/context/AuthContext.js`):
|
|
- JWT token storage in localStorage
|
|
- Automatic token injection via Axios interceptor
|
|
- User state management
|
|
- Protected route wrapper
|
|
|
|
**Usage:**
|
|
```jsx
|
|
import { useAuth } from '../context/AuthContext';
|
|
|
|
function MyComponent() {
|
|
const { user, login, logout, isAuthenticated } = useAuth();
|
|
|
|
// user contains: id, email, first_name, last_name, role, status
|
|
}
|
|
```
|
|
|
|
#### Protected Routes
|
|
|
|
**PrivateRoute Wrapper:**
|
|
```jsx
|
|
<Route path="/dashboard" element={
|
|
<PrivateRoute>
|
|
<Dashboard />
|
|
</PrivateRoute>
|
|
} />
|
|
|
|
// Admin-only route
|
|
<Route path="/admin" element={
|
|
<PrivateRoute adminOnly>
|
|
<AdminDashboard />
|
|
</PrivateRoute>
|
|
} />
|
|
```
|
|
|
|
#### API Integration
|
|
|
|
**Axios Instance** (`src/utils/api.js`):
|
|
- Automatic JWT token injection
|
|
- Request/response interceptors
|
|
- Error handling
|
|
- Base URL configuration
|
|
|
|
**Usage:**
|
|
```jsx
|
|
import api from '../utils/api';
|
|
|
|
// GET request
|
|
const response = await api.get('/members/profile');
|
|
|
|
// POST request with data
|
|
const response = await api.post('/admin/users/123/reject', {
|
|
reason: 'Incomplete application'
|
|
});
|
|
|
|
// File upload
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
const response = await api.post('/members/profile/upload-photo', formData, {
|
|
headers: { 'Content-Type': 'multipart/form-data' }
|
|
});
|
|
```
|
|
|
|
#### Form Handling
|
|
|
|
**React Hook Form + Zod Pattern:**
|
|
```jsx
|
|
import { useForm } from 'react-hook-form';
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
import { z } from 'zod';
|
|
|
|
const schema = z.object({
|
|
email: z.string().email('Invalid email address'),
|
|
password: z.string().min(8, 'Password must be at least 8 characters')
|
|
});
|
|
|
|
function LoginForm() {
|
|
const { register, handleSubmit, formState: { errors } } = useForm({
|
|
resolver: zodResolver(schema)
|
|
});
|
|
|
|
const onSubmit = async (data) => {
|
|
try {
|
|
await api.post('/auth/login', data);
|
|
toast.success('Login successful!');
|
|
} catch (error) {
|
|
toast.error(error.response?.data?.detail || 'Login failed');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit(onSubmit)}>
|
|
<Input {...register('email')} />
|
|
{errors.email && <span>{errors.email.message}</span>}
|
|
{/* ... */}
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Toast Notifications
|
|
|
|
**Using Sonner:**
|
|
```jsx
|
|
import { toast } from 'sonner';
|
|
|
|
// Success
|
|
toast.success('Profile updated successfully!');
|
|
|
|
// Error
|
|
toast.error('Failed to upload photo');
|
|
|
|
// Custom
|
|
toast('Processing...', {
|
|
description: 'Please wait while we process your request'
|
|
});
|
|
```
|
|
|
|
### Page Components
|
|
|
|
#### Public Pages
|
|
|
|
**Landing.js**
|
|
- Hero section with LOAF branding
|
|
- Feature highlights
|
|
- Call-to-action buttons
|
|
|
|
**Register.js** (388 lines)
|
|
- 4-step registration wizard:
|
|
1. Basic Info (email, password, name)
|
|
2. Personal Details (phone, address, DOB)
|
|
3. Partner Information (optional)
|
|
4. Lead Sources & Referral
|
|
- Form validation with Zod
|
|
- Progress indicator
|
|
- Email verification trigger
|
|
|
|
**Login.js**
|
|
- Email/password authentication
|
|
- JWT token storage
|
|
- Remember me functionality
|
|
- Redirect to dashboard on success
|
|
|
|
#### Member Pages
|
|
|
|
**Dashboard.js**
|
|
- Welcome message with user name
|
|
- Upcoming events preview
|
|
- Membership status card
|
|
- Quick action buttons
|
|
|
|
**Profile.js**
|
|
- Profile photo upload with Avatar component
|
|
- File validation (image types, 50MB max)
|
|
- Personal information editing
|
|
- Partner information
|
|
- Save changes with API update
|
|
|
|
**Events.js**
|
|
- Event listing with filters
|
|
- Search functionality
|
|
- Upcoming/past events toggle
|
|
- Event cards with cover images
|
|
|
|
**EventDetails.js**
|
|
- Full event information
|
|
- RSVP form (Yes/No/Maybe)
|
|
- Attendance confirmation
|
|
- Google Maps integration (optional)
|
|
|
|
#### Admin Pages
|
|
|
|
**AdminDashboard.js**
|
|
- Statistics overview
|
|
- Pending validations count
|
|
- Recent activity feed
|
|
- Quick links to management pages
|
|
|
|
**AdminUsers.js**
|
|
- User listing with search/filters
|
|
- Status badges
|
|
- Bulk operations
|
|
- CSV export
|
|
|
|
**AdminValidations.js**
|
|
- Pending user applications
|
|
- Approve button
|
|
- **Reject button with RejectionDialog**
|
|
- Validation workflow management
|
|
|
|
**AdminSubscriptions.js**
|
|
- Subscription listing
|
|
- Status filters (active, cancelled, expired)
|
|
- **CSV export dropdown** (Export All / Export Current View)
|
|
- Edit subscription dialog
|
|
|
|
**AdminDonations.js** (400+ lines)
|
|
- 4 stats cards: Total, Member, Public, Total Amount
|
|
- Filters: type, status, date range, search
|
|
- Responsive table with mobile cards
|
|
- **CSV export functionality**
|
|
|
|
**AdminEvents.js**
|
|
- Event management interface
|
|
- Create/edit events
|
|
- Publish/unpublish toggle
|
|
- Cover image upload
|
|
- RSVP tracking
|
|
- Attendance marking
|
|
|
|
### Component Library (Radix UI)
|
|
|
|
**45+ UI Components** in `src/components/ui/`:
|
|
|
|
**Form Components:**
|
|
- Button, Input, Textarea, Checkbox, Radio
|
|
- Select, Label, Form
|
|
|
|
**Layout Components:**
|
|
- Card, Dialog, Sheet (Drawer), Popover
|
|
- Dropdown Menu, Tabs, Accordion
|
|
|
|
**Feedback Components:**
|
|
- Alert, Badge, Toast (Sonner)
|
|
- Progress, Skeleton, Spinner
|
|
|
|
**Navigation:**
|
|
- Navigation Menu, Breadcrumb, Pagination
|
|
|
|
**Usage Example:**
|
|
```jsx
|
|
import { Button } from './components/ui/button';
|
|
import { Dialog, DialogContent, DialogTitle } from './components/ui/dialog';
|
|
import { Select, SelectTrigger, SelectContent, SelectItem } from './components/ui/select';
|
|
|
|
<Button className="bg-[#664fa3] text-white">
|
|
Click me
|
|
</Button>
|
|
```
|
|
|
|
### Admin Sidebar Features
|
|
|
|
**Logo Integration:**
|
|
- LOAF logo in header
|
|
- Shows logo + "Admin" text when expanded
|
|
- Logo only when collapsed
|
|
- Smooth transition animations
|
|
|
|
**Navigation Groups:**
|
|
- **Dashboard** (standalone)
|
|
- **MEMBERSHIP** - Staff, Members, Validations
|
|
- **FINANCIALS** - Plans, Subscriptions, Donations
|
|
- **EVENTS & MEDIA** - Events, Gallery
|
|
- **DOCUMENTATION** - Newsletters, Financial Reports, Bylaws
|
|
- **Permissions** (Superadmin only)
|
|
|
|
---
|
|
|
|
## Design System
|
|
|
|
### Color Palette
|
|
|
|
**LOAF Brand Colors:**
|
|
```css
|
|
--primary: #422268 /* Deep Purple - Primary brand */
|
|
--secondary: #664fa3 /* Light Purple - Secondary elements */
|
|
--accent: #ff9e77 /* Coral - Accents and highlights */
|
|
--muted: #ddd8eb /* Light Purple Gray - Borders */
|
|
--background: #f9f5ff /* Very Light Purple - Backgrounds */
|
|
--foreground: #422268 /* Text color */
|
|
```
|
|
|
|
**Usage in Tailwind:**
|
|
```jsx
|
|
<div className="bg-[#422268] text-white">
|
|
<h1 className="text-[#ff9e77]">Accent Text</h1>
|
|
<p className="text-[#664fa3]">Secondary Text</p>
|
|
</div>
|
|
```
|
|
|
|
### Typography
|
|
|
|
**Font Families:**
|
|
- **Headings**: 'Inter', sans-serif
|
|
- **Body**: 'Nunito Sans', sans-serif
|
|
- **Code**: 'Fira Code', monospace (if needed)
|
|
|
|
**Font Sizes:**
|
|
```css
|
|
text-xs → 0.75rem (12px)
|
|
text-sm → 0.875rem (14px)
|
|
text-base → 1rem (16px)
|
|
text-lg → 1.125rem (18px)
|
|
text-xl → 1.25rem (20px)
|
|
text-2xl → 1.5rem (24px)
|
|
text-3xl → 1.875rem (30px)
|
|
```
|
|
|
|
**Usage:**
|
|
```jsx
|
|
<h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
|
|
Page Title
|
|
</h1>
|
|
<p className="text-base text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
|
Body text
|
|
</p>
|
|
```
|
|
|
|
### Spacing System
|
|
|
|
**Tailwind Spacing Scale:**
|
|
```css
|
|
p-2 → 0.5rem (8px)
|
|
p-4 → 1rem (16px)
|
|
p-6 → 1.5rem (24px)
|
|
p-8 → 2rem (32px)
|
|
|
|
gap-2, gap-4, gap-6, gap-8 (same scale for flex/grid gaps)
|
|
```
|
|
|
|
### Component Styling Patterns
|
|
|
|
**Cards:**
|
|
```jsx
|
|
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]">
|
|
{/* Content */}
|
|
</Card>
|
|
```
|
|
|
|
**Buttons:**
|
|
```jsx
|
|
// Primary
|
|
<Button className="bg-[#664fa3] text-white hover:bg-[#422268] rounded-full px-6 py-3">
|
|
Primary Action
|
|
</Button>
|
|
|
|
// Secondary
|
|
<Button variant="outline" className="border-2 border-[#ddd8eb] text-[#664fa3] hover:bg-[#f1eef9] rounded-full">
|
|
Secondary Action
|
|
</Button>
|
|
|
|
// Destructive
|
|
<Button className="bg-red-600 text-white hover:bg-red-700 rounded-full">
|
|
Delete
|
|
</Button>
|
|
```
|
|
|
|
**Form Inputs:**
|
|
```jsx
|
|
<Input className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" />
|
|
<Textarea className="rounded-xl border-2 border-[#ddd8eb] min-h-[120px]" />
|
|
<Select>
|
|
<SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]">
|
|
<SelectValue placeholder="Select..." />
|
|
</SelectTrigger>
|
|
</Select>
|
|
```
|
|
|
|
**Badges:**
|
|
```jsx
|
|
// Status badges
|
|
<Badge className="bg-green-100 text-green-800">Active</Badge>
|
|
<Badge className="bg-yellow-100 text-yellow-800">Pending</Badge>
|
|
<Badge className="bg-red-100 text-red-800">Rejected</Badge>
|
|
```
|
|
|
|
### Icons (Lucide React)
|
|
|
|
**Common Icons:**
|
|
```jsx
|
|
import {
|
|
User, Mail, Phone, MapPin, // User info
|
|
Calendar, Clock, CheckCircle, // Status
|
|
Edit, Trash2, X, Check, // Actions
|
|
Search, Filter, Download, // Utilities
|
|
Heart, DollarSign, CreditCard, // Financial
|
|
AlertTriangle, Info, HelpCircle // Alerts
|
|
} from 'lucide-react';
|
|
|
|
<User className="h-5 w-5 text-[#664fa3]" />
|
|
```
|
|
|
|
### Responsive Design
|
|
|
|
**Breakpoints:**
|
|
```css
|
|
sm: 640px → @media (min-width: 640px)
|
|
md: 768px → @media (min-width: 768px)
|
|
lg: 1024px → @media (min-width: 1024px)
|
|
xl: 1280px → @media (min-width: 1280px)
|
|
2xl: 1536px → @media (min-width: 1536px)
|
|
```
|
|
|
|
**Mobile-First Approach:**
|
|
```jsx
|
|
<div className="flex flex-col md:flex-row gap-4">
|
|
{/* Stacks vertically on mobile, horizontal on tablet+ */}
|
|
</div>
|
|
|
|
<div className="hidden md:block">
|
|
{/* Only shows on tablet+ */}
|
|
</div>
|
|
|
|
<div className="md:hidden">
|
|
{/* Only shows on mobile */}
|
|
</div>
|
|
```
|
|
|
|
### Animations
|
|
|
|
**Tailwind Transitions:**
|
|
```jsx
|
|
<Button className="transition-all duration-200 hover:scale-105">
|
|
Hover me
|
|
</Button>
|
|
|
|
<div className="animate-fade-in">
|
|
{/* Fades in on mount */}
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Deployment Guide
|
|
|
|
### Production Build
|
|
|
|
#### 1. Environment Variables
|
|
|
|
Create `.env.production`:
|
|
```bash
|
|
REACT_APP_BACKEND_URL=https://api.loaf.org
|
|
REACT_APP_SENTRY_DSN=your-production-sentry-dsn
|
|
```
|
|
|
|
#### 2. Build Application
|
|
|
|
```bash
|
|
# Install dependencies
|
|
yarn install
|
|
|
|
# Create production build
|
|
yarn build
|
|
```
|
|
|
|
Build output will be in `/build` directory.
|
|
|
|
#### 3. Deployment Options
|
|
|
|
### Option A: Netlify (Recommended)
|
|
|
|
**Via Netlify CLI:**
|
|
```bash
|
|
# Install Netlify CLI
|
|
npm install -g netlify-cli
|
|
|
|
# Login
|
|
netlify login
|
|
|
|
# Deploy
|
|
netlify deploy --prod --dir=build
|
|
```
|
|
|
|
**Via Git Integration:**
|
|
1. Push code to GitHub/GitLab
|
|
2. Connect repository in Netlify dashboard
|
|
3. Configure:
|
|
- Build command: `yarn build`
|
|
- Publish directory: `build`
|
|
- Environment variables (from .env.production)
|
|
4. Deploy automatically on push
|
|
|
|
**Configure Redirects** (`public/_redirects`):
|
|
```
|
|
/* /index.html 200
|
|
```
|
|
This enables client-side routing.
|
|
|
|
### Option B: Vercel
|
|
|
|
```bash
|
|
# Install Vercel CLI
|
|
npm install -g vercel
|
|
|
|
# Deploy
|
|
vercel --prod
|
|
```
|
|
|
|
**Configure** (`vercel.json`):
|
|
```json
|
|
{
|
|
"rewrites": [
|
|
{ "source": "/(.*)", "destination": "/index.html" }
|
|
],
|
|
"env": {
|
|
"REACT_APP_BACKEND_URL": "https://api.loaf.org"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Option C: Traditional Web Server (Nginx)
|
|
|
|
**1. Copy build files to server:**
|
|
```bash
|
|
scp -r build/* user@server:/var/www/loaf-frontend/
|
|
```
|
|
|
|
**2. Configure Nginx** (`/etc/nginx/sites-available/loaf-frontend`):
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name app.loaf.org;
|
|
root /var/www/loaf-frontend;
|
|
index index.html;
|
|
|
|
location / {
|
|
try_files $uri /index.html;
|
|
}
|
|
|
|
# Cache static assets
|
|
location /static/ {
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
# Gzip compression
|
|
gzip on;
|
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
|
|
}
|
|
```
|
|
|
|
**3. Enable site and restart:**
|
|
```bash
|
|
sudo ln -s /etc/nginx/sites-available/loaf-frontend /etc/nginx/sites-enabled/
|
|
sudo nginx -t
|
|
sudo systemctl restart nginx
|
|
```
|
|
|
|
**4. SSL Certificate:**
|
|
```bash
|
|
sudo certbot --nginx -d app.loaf.org
|
|
```
|
|
|
|
### Option D: AWS S3 + CloudFront
|
|
|
|
**1. Create S3 bucket:**
|
|
```bash
|
|
aws s3 mb s3://loaf-frontend
|
|
aws s3 sync build/ s3://loaf-frontend --delete
|
|
```
|
|
|
|
**2. Configure bucket for static hosting:**
|
|
```bash
|
|
aws s3 website s3://loaf-frontend --index-document index.html --error-document index.html
|
|
```
|
|
|
|
**3. Create CloudFront distribution** pointing to S3 bucket with custom error pages (all errors → /index.html).
|
|
|
|
### Performance Optimization
|
|
|
|
**1. Code Splitting:**
|
|
```jsx
|
|
// Lazy load routes
|
|
import { lazy, Suspense } from 'react';
|
|
|
|
const AdminDonations = lazy(() => import('./pages/admin/AdminDonations'));
|
|
|
|
<Route path="/admin/donations" element={
|
|
<Suspense fallback={<div>Loading...</div>}>
|
|
<AdminDonations />
|
|
</Suspense>
|
|
} />
|
|
```
|
|
|
|
**2. Image Optimization:**
|
|
- Use WebP format when possible
|
|
- Compress images before upload
|
|
- Use lazy loading: `loading="lazy"`
|
|
|
|
**3. Bundle Analysis:**
|
|
```bash
|
|
# Install analyzer
|
|
yarn add --dev webpack-bundle-analyzer
|
|
|
|
# Analyze bundle
|
|
yarn build
|
|
npx webpack-bundle-analyzer build/static/js/*.js
|
|
```
|
|
|
|
### CI/CD Pipeline
|
|
|
|
**GitHub Actions** (`.github/workflows/deploy.yml`):
|
|
```yaml
|
|
name: Deploy Frontend
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: actions/setup-node@v3
|
|
with:
|
|
node-version: '18'
|
|
- run: yarn install
|
|
- run: yarn build
|
|
env:
|
|
REACT_APP_BACKEND_URL: ${{ secrets.BACKEND_URL }}
|
|
- uses: netlify/actions/cli@master
|
|
with:
|
|
args: deploy --dir=build --prod
|
|
env:
|
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_TOKEN }}
|
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
#### 1. Module Not Found Errors
|
|
|
|
**Error:** `Module not found: Can't resolve 'package-name'`
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Clear node_modules and reinstall
|
|
rm -rf node_modules yarn.lock
|
|
yarn install
|
|
|
|
# Or with npm
|
|
rm -rf node_modules package-lock.json
|
|
npm install
|
|
```
|
|
|
|
#### 2. CORS Errors
|
|
|
|
**Error:** `Access to XMLHttpRequest blocked by CORS policy`
|
|
|
|
**Solution:**
|
|
- Backend must include frontend URL in CORS_ORIGINS
|
|
- Check REACT_APP_BACKEND_URL is correct
|
|
- Backend: `CORS_ORIGINS=http://localhost:3000,https://app.loaf.org`
|
|
|
|
#### 3. 401 Unauthorized
|
|
|
|
**Error:** API returns 401 after some time
|
|
|
|
**Solution:**
|
|
- JWT token expired (default 30 minutes)
|
|
- User needs to log in again
|
|
- Check token is being sent in Authorization header
|
|
|
|
#### 4. Environment Variables Not Working
|
|
|
|
**Error:** `process.env.REACT_APP_BACKEND_URL is undefined`
|
|
|
|
**Solution:**
|
|
- Ensure variable name starts with `REACT_APP_`
|
|
- Restart development server after changing .env
|
|
- Don't commit .env to git (use .env.example)
|
|
|
|
#### 5. Build Fails
|
|
|
|
**Error:** `npm run build` fails with memory error
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Increase Node memory limit
|
|
NODE_OPTIONS=--max_old_space_size=4096 yarn build
|
|
```
|
|
|
|
#### 6. Routing Not Working in Production
|
|
|
|
**Error:** Refresh on /dashboard returns 404
|
|
|
|
**Solution:**
|
|
- Configure server to redirect all routes to index.html
|
|
- Netlify: Add `_redirects` file
|
|
- Nginx: Use `try_files $uri /index.html`
|
|
- Vercel: Add vercel.json with rewrites
|
|
|
|
#### 7. Images Not Loading
|
|
|
|
**Error:** Profile photos return 404
|
|
|
|
**Solution:**
|
|
- Check R2_PUBLIC_URL is correct in backend .env
|
|
- Verify Cloudflare R2 bucket is public
|
|
- Check CORS settings in R2 bucket
|
|
|
|
### Debug Mode
|
|
|
|
```jsx
|
|
// Add to any component for debugging
|
|
console.log('Component rendered', { user, props });
|
|
|
|
// Check API responses
|
|
api.interceptors.response.use(
|
|
response => {
|
|
console.log('API Response:', response);
|
|
return response;
|
|
},
|
|
error => {
|
|
console.error('API Error:', error.response);
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
```
|
|
|
|
### Getting Help
|
|
|
|
- **Project Context**: See `CLAUDE.md` and `PRD.md`
|
|
- **API Docs**: http://localhost:8000/docs (backend Swagger)
|
|
- **React Docs**: https://react.dev/
|
|
- **Tailwind CSS**: https://tailwindcss.com/docs
|
|
- **Radix UI**: https://www.radix-ui.com/primitives
|
|
|
|
---
|
|
|
|
## Additional Resources
|
|
|
|
- **React Documentation**: https://react.dev/
|
|
- **React Router**: https://reactrouter.com/
|
|
- **Tailwind CSS**: https://tailwindcss.com/
|
|
- **Radix UI**: https://www.radix-ui.com/
|
|
- **React Hook Form**: https://react-hook-form.com/
|
|
- **Zod**: https://zod.dev/
|
|
- **Lucide Icons**: https://lucide.dev/
|
|
|
|
---
|
|
|
|
**Last Updated**: December 18, 2024
|
|
**Version**: 1.0.0
|
|
**Maintainer**: LOAF Development Team
|