2025-12-05 16:40:33 +07:00
2025-12-17 13:34:06 -06:00
2025-12-05 16:41:33 +07:00
2025-12-05 16:40:33 +07:00
2025-12-05 16:40:33 +07:00
2025-12-05 16:40:33 +07:00
2025-12-10 17:52:47 +07:00
2025-12-05 16:40:33 +07:00
2025-12-18 17:04:50 +07:00
2025-12-05 16:40:33 +07:00
2025-12-10 17:52:47 +07:00

LOAF Membership Platform - Frontend

React 19-based frontend application for the LOAF (LGBT Organization and Friends) membership management platform.

Table of Contents


Setup & Installation

Prerequisites

1. Install Dependencies

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:

# 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

# Start development server
yarn start

# Or with npm
npm start

Development server will be available at:

4. Build for Production

# Create production build
yarn build

# Or with npm
npm build

Build output will be in /build directory.

5. Run Tests

# 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:

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:

<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:

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:

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:

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:

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:

--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:

<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:

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:

<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:

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:

<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]">
  {/* Content */}
</Card>

Buttons:

// 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:

<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:

// 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:

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:

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:

<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:

<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:

REACT_APP_BACKEND_URL=https://api.loaf.org
REACT_APP_SENTRY_DSN=your-production-sentry-dsn

2. Build Application

# Install dependencies
yarn install

# Create production build
yarn build

Build output will be in /build directory.

3. Deployment Options

Via Netlify CLI:

# 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

# Install Vercel CLI
npm install -g vercel

# Deploy
vercel --prod

Configure (vercel.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:

scp -r build/* user@server:/var/www/loaf-frontend/

2. Configure Nginx (/etc/nginx/sites-available/loaf-frontend):

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:

sudo ln -s /etc/nginx/sites-available/loaf-frontend /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

4. SSL Certificate:

sudo certbot --nginx -d app.loaf.org

Option D: AWS S3 + CloudFront

1. Create S3 bucket:

aws s3 mb s3://loaf-frontend
aws s3 sync build/ s3://loaf-frontend --delete

2. Configure bucket for static hosting:

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:

// 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:

# 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):

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:

# 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:

# 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

// 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


Additional Resources


Last Updated: December 18, 2024 Version: 1.0.0 Maintainer: LOAF Development Team

Description
No description provided
Readme 3.6 MiB
Languages
JavaScript 99.4%
CSS 0.4%
HTML 0.2%