Files
membership-fe/README.md
Koncept Kit 8c0d9a2a18 - Profile Picture\
Donation Tracking\
Validation Rejection\
Subscription Data Export\
Admin Dashboard Logo\
Admin Navbar Reorganization
2025-12-18 17:04:50 +07:00

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