# 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-var(--purple-lavender) 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-var(--purple-ink) text-white">
  <h1 className="text-var(--orange-light)">Accent Text</h1>
  <p className="text-var(--purple-lavender)">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-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>
  Page Title
</h1>
<p className="text-base text-var(--purple-lavender)" 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-background rounded-2xl border-2 border-var(--neutral-800)">
  {/* Content */}
</Card>
```

**Buttons:**

```jsx
// Primary
<Button className="bg-var(--purple-lavender) text-white hover:bg-var(--purple-ink) rounded-full px-6 py-3">
  Primary Action
</Button>

// Secondary
<Button variant="outline" className="border-2 border-var(--neutral-800) text-var(--purple-lavender) hover:bg-var(--lavender-300) 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-var(--neutral-800) focus:border-var(--purple-lavender)" />
<Textarea className="rounded-xl border-2 border-var(--neutral-800) min-h-[120px]" />
<Select>
  <SelectTrigger className="rounded-xl border-2 border-var(--neutral-800)">
    <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-var(--purple-lavender)" />;
```

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

**Backend API**

**Auth**
- POST `/api/auth/register`
- GET `/api/auth/verify-email`
- POST `/api/auth/resend-verification-email`
- POST `/api/auth/login`
- POST `/api/auth/forgot-password`
- POST `/api/auth/reset-password`
- GET `/api/auth/me`
- GET `/api/auth/permissions`

**Users**
- PUT `/api/users/change-password`
- GET `/api/users/profile`
- PUT `/api/users/profile`

**Members**
- GET `/api/members/directory` (defined twice in code)
- GET `/api/members/directory/{user_id}`
- GET `/api/members/profile`
- PUT `/api/members/profile`
- POST `/api/members/profile/upload-photo`
- DELETE `/api/members/profile/delete-photo`
- GET `/api/members/calendar/events`
- GET `/api/members/gallery`
- GET `/api/members/event-activity`

**Events (public/member)**
- GET `/api/events`
- GET `/api/events/{event_id}`
- GET `/api/events/{event_id}/gallery`
- POST `/api/events/{event_id}/rsvp`
- GET `/api/events/{event_id}/download.ics`

**Calendars**
- GET `/api/calendars/subscribe.ics`
- GET `/api/calendars/all-events.ics`

**Newsletters (public)**
- GET `/api/newsletters`
- GET `/api/newsletters/years`

**Financials (public)**
- GET `/api/financials`

**Bylaws (public)**
- GET `/api/bylaws/current`
- GET `/api/bylaws/history`

**Config/Diagnostics**
- GET `/api/config`
- GET `/api/config/limits`
- GET `/api/diagnostics/cors`

**Invitations**
- GET `/api/invitations/verify/{token}`
- POST `/api/invitations/accept`

**Subscriptions**
- GET `/api/subscriptions/plans`
- POST `/api/subscriptions/checkout`

**Donations**
- POST `/api/donations/checkout`

**Contact**
- POST `/api/contact`

**Admin – Calendar**
- POST `/api/admin/calendar/sync/{event_id}`
- DELETE `/api/admin/calendar/unsync/{event_id}`

**Admin – Event Gallery**
- POST `/api/admin/events/{event_id}/gallery`
- DELETE `/api/admin/event-gallery/{image_id}`
- PUT `/api/admin/event-gallery/{image_id}`

**Admin – Events**
- POST `/api/admin/events`
- PUT `/api/admin/events/{event_id}`
- GET `/api/admin/events/{event_id}`
- GET `/api/admin/events/{event_id}/rsvps`
- PUT `/api/admin/events/{event_id}/attendance`
- GET `/api/admin/events`
- DELETE `/api/admin/events/{event_id}`

**Admin – Storage**
- GET `/api/admin/storage/usage`
- GET `/api/admin/storage/breakdown`

**Admin – Users & Invitations**
- GET `/api/admin/users`
- GET `/api/admin/users/invitations`
- GET `/api/admin/users/export`
- GET `/api/admin/users/{user_id}`
- PUT `/api/admin/users/{user_id}`
- PUT `/api/admin/users/{user_id}/validate`
- PUT `/api/admin/users/{user_id}/status`
- POST `/api/admin/users/{user_id}/reject`
- POST `/api/admin/users/{user_id}/activate-payment`
- PUT `/api/admin/users/{user_id}/reset-password`
- PUT `/api/admin/users/{user_id}/role`
- POST `/api/admin/users/{user_id}/resend-verification`
- POST `/api/admin/users/{user_id}/upload-photo`
- DELETE `/api/admin/users/{user_id}/delete-photo`
- POST `/api/admin/users/create`
- POST `/api/admin/users/invite`
- POST `/api/admin/users/invitations/{invitation_id}/resend`
- DELETE `/api/admin/users/invitations/{invitation_id}`
- POST `/api/admin/users/import`
- GET `/api/admin/users/import-jobs`
- GET `/api/admin/users/import-jobs/{job_id}`

**Admin – Imports**
- POST `/api/admin/import/upload-csv`
- GET `/api/admin/import/{job_id}/preview`
- POST `/api/admin/import/{job_id}/execute`
- POST `/api/admin/import/{job_id}/rollback`
- GET `/api/admin/import/{job_id}/status`
- GET `/api/admin/import/{job_id}/errors/download`

**Admin – Subscriptions**
- GET `/api/admin/subscriptions/plans`
- GET `/api/admin/subscriptions/plans/{plan_id}`
- POST `/api/admin/subscriptions/plans`
- PUT `/api/admin/subscriptions/plans/{plan_id}`
- DELETE `/api/admin/subscriptions/plans/{plan_id}`
- GET `/api/admin/subscriptions`
- GET `/api/admin/subscriptions/stats`
- PUT `/api/admin/subscriptions/{subscription_id}`
- POST `/api/admin/subscriptions/{subscription_id}/cancel`
- GET `/api/admin/subscriptions/export`

**Admin – Donations**
- GET `/api/admin/donations`
- GET `/api/admin/donations/stats`
- GET `/api/admin/donations/export`

**Admin – Newsletters**
- POST `/api/admin/newsletters`
- PUT `/api/admin/newsletters/{newsletter_id}`
- DELETE `/api/admin/newsletters/{newsletter_id}`

**Admin – Financials**
- POST `/api/admin/financials`
- PUT `/api/admin/financials/{report_id}`
- DELETE `/api/admin/financials/{report_id}`

**Admin – Bylaws**
- POST `/api/admin/bylaws`
- PUT `/api/admin/bylaws/{bylaws_id}`
- DELETE `/api/admin/bylaws/{bylaws_id}`

**Admin – Roles**
- GET `/api/admin/roles`
- GET `/api/admin/roles/assignable`
- POST `/api/admin/roles`
- GET `/api/admin/roles/{role_id}`
- PUT `/api/admin/roles/{role_id}`
- DELETE `/api/admin/roles/{role_id}`
- GET `/api/admin/roles/{role_id}/permissions`
- PUT `/api/admin/roles/{role_id}/permissions`

**Admin – Permissions**
- GET `/api/admin/permissions`
- GET `/api/admin/permissions/modules`
- GET `/api/admin/permissions/roles/{role}`
- PUT `/api/admin/permissions/roles/{role}`
- POST `/api/admin/permissions/seed`

**Admin – Stripe Settings**
- GET `/api/admin/settings/stripe/status`
- POST `/api/admin/settings/stripe/test-connection`
- PUT `/api/admin/settings/stripe`

**Webhooks**
- POST `/api/webhooks/stripe`