Files
vip-coordinator/CLAUDE.md
kyle 868f7efc23
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
Major Enhancement: NestJS Migration + CASL Authorization + Error Handling
Complete rewrite from Express to NestJS with enterprise-grade features:

## Backend Improvements
- Migrated from Express to NestJS 11.0.1 with TypeScript
- Implemented Prisma ORM 7.3.0 for type-safe database access
- Added CASL authorization system replacing role-based guards
- Created global exception filters with structured logging
- Implemented Auth0 JWT authentication with Passport.js
- Added vehicle management with conflict detection
- Enhanced event scheduling with driver/vehicle assignment
- Comprehensive error handling and logging

## Frontend Improvements
- Upgraded to React 19.2.0 with Vite 7.2.4
- Implemented CASL-based permission system
- Added AbilityContext for declarative permissions
- Created ErrorHandler utility for consistent error messages
- Enhanced API client with request/response logging
- Added War Room (Command Center) dashboard
- Created VIP Schedule view with complete itineraries
- Implemented Vehicle Management UI
- Added mock data generators for testing (288 events across 20 VIPs)

## New Features
- Vehicle fleet management (types, capacity, status tracking)
- Complete 3-day Jamboree schedule generation
- Individual VIP schedule pages with PDF export (planned)
- Real-time War Room dashboard with auto-refresh
- Permission-based navigation filtering
- First user auto-approval as administrator

## Documentation
- Created CASL_AUTHORIZATION.md (comprehensive guide)
- Created ERROR_HANDLING.md (error handling patterns)
- Updated CLAUDE.md with new architecture
- Added migration guides and best practices

## Technical Debt Resolved
- Removed custom authentication in favor of Auth0
- Replaced role checks with CASL abilities
- Standardized error responses across API
- Implemented proper TypeScript typing
- Added comprehensive logging

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-31 08:50:25 +01:00

1178 lines
29 KiB
Markdown

# VIP Coordinator - Claude AI Context
**Last Updated:** 2026-01-25
**Status:** Active Development - Starting Fresh Build
## Project Overview
VIP Coordinator is a full-stack web application for managing VIP transportation logistics and event coordination. It handles VIP profiles, driver assignments, event scheduling with conflict detection, flight tracking, and user management with role-based access control.
**⚠️ CURRENT STATUS: FRESH BUILD IN PROGRESS**
- Starting from scratch with modern 2026 best practices
- No production data - database can be reset at any time
- Feel free to make breaking changes without asking
- **Focus:** Getting features working correctly > preserving legacy code
---
## Technology Stack (2026 Best Practices)
### Backend
- **Framework:** NestJS 10.x (TypeScript)
- **Database ORM:** Prisma 5.x
- **Database:** PostgreSQL 15+
- **Caching:** Redis 7 (optional, for driver location tracking)
- **Authentication:** Auth0 + Passport.js (JWT strategy)
- **Validation:** class-validator + class-transformer
- **Runtime:** Node.js 20+
### Frontend
- **Framework:** React 18.2.0 (stable)
- **Build Tool:** Vite 5.x
- **UI Components:** Shadcn UI (modern, lightweight)
- **Styling:** Tailwind CSS 3.4+
- **Data Fetching:** TanStack Query v5 (formerly React Query)
- **Routing:** React Router 6.x
- **Forms:** React Hook Form + Zod
- **Auth:** Auth0 React SDK (@auth0/auth0-react)
### Infrastructure
- **Development:** Docker + Docker Compose
- **Production Target:** Digital Ocean (App Platform or Droplet)
- **Services:** PostgreSQL, Redis (optional), Backend API, Frontend SPA
### Why These Choices?
**NestJS over Express:**
- Built-in structure prevents spaghetti code
- Dependency injection, decorators, guards
- Excellent TypeScript support
- Scales well for complex applications
**Prisma over raw SQL:**
- Type-safe database queries
- Automatic migrations
- Prevents SQL injection by default
- Auto-completion in IDE
**Shadcn UI over Material-UI:**
- Modern design (2024-2026 standard)
- Lightweight (copy components into project)
- Full control over styling
- Better performance
**TanStack Query:**
- Essential for server state management
- Automatic caching and refetching
- Optimistic updates
- Industry standard for React
**Auth0:**
- Reliable hosted authentication
- No self-hosting required
- Social login support
- Excellent documentation
- Free tier for development
---
## Project Structure
```
vip-coordinator/
├── backend/ # NestJS Backend (port 3000)
│ ├── prisma/
│ │ ├── schema.prisma # Database schema (source of truth)
│ │ ├── migrations/ # Auto-generated migrations
│ │ └── seed.ts # Sample data for development
│ ├── src/
│ │ ├── main.ts # App entry point
│ │ ├── app.module.ts # Root module
│ │ ├── auth/ # Auth0 + JWT authentication
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── jwt.strategy.ts
│ │ │ ├── guards/ # @Roles(), @Public() guards
│ │ │ └── decorators/ # @CurrentUser() decorator
│ │ ├── users/ # User management + approval
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.ts
│ │ │ ├── users.controller.ts
│ │ │ └── dto/
│ │ ├── vips/ # VIP profile management
│ │ ├── drivers/ # Driver resource management
│ │ ├── events/ # Schedule + conflict detection
│ │ ├── flights/ # Flight tracking API
│ │ ├── prisma/ # Prisma service
│ │ │ ├── prisma.module.ts
│ │ │ └── prisma.service.ts
│ │ └── common/ # Shared utilities
│ ├── package.json
│ └── .env
├── frontend/ # React Frontend (port 5173)
│ ├── src/
│ │ ├── main.tsx # App entry point
│ │ ├── App.tsx # Root component + routing
│ │ ├── components/
│ │ │ ├── ui/ # Shadcn UI components
│ │ │ ├── auth/ # Login, ProtectedRoute
│ │ │ ├── vips/ # VIP forms, lists
│ │ │ ├── drivers/ # Driver components
│ │ │ └── events/ # Schedule components
│ │ ├── pages/
│ │ │ ├── Dashboard.tsx
│ │ │ ├── VIPList.tsx
│ │ │ ├── VIPDetails.tsx
│ │ │ ├── DriverList.tsx
│ │ │ ├── SchedulePage.tsx
│ │ │ ├── FlightsPage.tsx
│ │ │ └── AdminUsersPage.tsx
│ │ ├── lib/
│ │ │ ├── api.ts # Axios client
│ │ │ └── queryClient.ts # TanStack Query config
│ │ ├── hooks/
│ │ │ ├── useAuth.ts # Auth0 wrapper
│ │ │ └── usePermissions.ts
│ │ └── types/ # TypeScript interfaces
│ ├── package.json
│ └── .env
├── docker-compose.yml # Development environment
├── docker-compose.prod.yml # Production build
└── CLAUDE.md # This file (source of truth)
```
---
## Database Schema (Prisma)
### Core Models
```prisma
model User {
id String @id @default(uuid())
auth0Id String @unique // Auth0 sub claim
email String @unique
name String?
picture String?
role Role @default(COORDINATOR)
isApproved Boolean @default(false)
driver Driver? // Optional linked driver
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime? // Soft delete
}
model VIP {
id String @id @default(uuid())
name String
organization String?
department Department
arrivalMode ArrivalMode
expectedArrival DateTime? // For self-driving
airportPickup Boolean @default(false)
venueTransport Boolean @default(false)
notes String?
flights Flight[]
events ScheduleEvent[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
}
model Driver {
id String @id @default(uuid())
name String
phone String
department Department?
userId String? @unique
user User? @relation(fields: [userId], references: [id])
events ScheduleEvent[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
}
model ScheduleEvent {
id String @id @default(uuid())
vipId String
vip VIP @relation(fields: [vipId], references: [id])
title String
location String?
startTime DateTime
endTime DateTime
description String?
type EventType @default(TRANSPORT)
status EventStatus @default(SCHEDULED)
driverId String?
driver Driver? @relation(fields: [driverId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
}
model Flight {
id String @id @default(uuid())
vipId String
vip VIP @relation(fields: [vipId], references: [id])
flightNumber String
flightDate DateTime
segment Int @default(1)
departureAirport String // IATA code (e.g., "JFK")
arrivalAirport String // IATA code (e.g., "LAX")
scheduledDeparture DateTime?
scheduledArrival DateTime?
actualDeparture DateTime?
actualArrival DateTime?
status String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum Role {
ADMINISTRATOR
COORDINATOR
DRIVER
}
enum Department {
OFFICE_OF_DEVELOPMENT
ADMIN
}
enum ArrivalMode {
FLIGHT
SELF_DRIVING
}
enum EventType {
TRANSPORT
MEETING
EVENT
MEAL
ACCOMMODATION
}
enum EventStatus {
SCHEDULED
IN_PROGRESS
COMPLETED
CANCELLED
}
```
### Relationships
- User ↔ Driver: One-to-one (optional)
- VIP ↔ Flight: One-to-many
- VIP ↔ ScheduleEvent: One-to-many
- Driver ↔ ScheduleEvent: One-to-many
### Soft Delete Pattern
All main entities have `deletedAt` field. Always filter:
```typescript
where: { deletedAt: null, ...otherConditions }
```
---
## User Roles & Permissions
| Feature | Administrator | Coordinator | Driver |
|---------|--------------|-------------|--------|
| User Management | ✅ Full CRUD | ❌ None | ❌ None |
| VIP Management | ✅ Full CRUD | ✅ Full CRUD | 👁️ View Only |
| Driver Management | ✅ Full CRUD | ✅ Full CRUD | 👁️ View Only |
| Event Scheduling | ✅ Full CRUD | ✅ Full CRUD | ⚠️ View + Update Status |
| Flight Tracking | ✅ Full Access | ✅ Full Access | ❌ None |
### First User Bootstrap
- **First user to register becomes Administrator automatically**
- `isApproved: true` by default for first user
- Subsequent users require admin approval
- No manual database editing needed
---
## Development Workflow
### Prerequisites
- Node.js 20+ and npm 10+
- Docker Desktop
- Auth0 Account (free tier)
- Git
### Initial Setup
**1. Clone and Install**
```bash
git clone <repository>
cd vip-coordinator
# Backend
cd backend
npm install
# Frontend
cd ../frontend
npm install
```
**2. Configure Auth0**
- Create Auth0 account at https://auth0.com
- Create new Application (Single Page Application)
- Create new API
- Note: `Domain`, `Client ID`, `Audience`
- Configure callback URLs:
- Allowed Callback URLs: `http://localhost:5173/callback`
- Allowed Logout URLs: `http://localhost:5173`
- Allowed Web Origins: `http://localhost:5173`
**3. Environment Variables**
**Backend `.env`:**
```env
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/vip_coordinator"
REDIS_URL="redis://localhost:6379"
AUTH0_DOMAIN="your-tenant.us.auth0.com"
AUTH0_AUDIENCE="https://your-api-identifier"
AVIATIONSTACK_API_KEY="your-api-key" # Optional for flight tracking
NODE_ENV=development
PORT=3000
```
**Frontend `.env`:**
```env
VITE_API_URL=http://localhost:3000/api/v1
VITE_AUTH0_DOMAIN=your-tenant.us.auth0.com
VITE_AUTH0_CLIENT_ID=your-client-id
VITE_AUTH0_AUDIENCE=https://your-api-identifier
```
**4. Database Setup**
```bash
cd backend
# Generate Prisma Client
npx prisma generate
# Run migrations
npx prisma migrate dev --name init
# Seed sample data (optional)
npx prisma db seed
```
**5. Run Development Servers**
**Option A: Docker (Recommended)**
```bash
# From project root
docker-compose up -d
# View logs
docker-compose logs -f backend
docker-compose logs -f frontend
```
**Option B: Local**
```bash
# Terminal 1: PostgreSQL + Redis
docker-compose up -d postgres redis
# Terminal 2: Backend
cd backend
npm run start:dev
# Terminal 3: Frontend
cd frontend
npm run dev
```
**6. Access Application**
- Frontend: http://localhost:5173
- Backend API: http://localhost:3000/api/v1
- Prisma Studio: `npx prisma studio` (database GUI)
### Common Development Tasks
**Database Migrations**
```bash
# Create new migration after schema changes
npx prisma migrate dev --name describe_your_changes
# Reset database (SAFE IN DEV - no important data)
npx prisma migrate reset
# View database in GUI
npx prisma studio
```
**Adding a New Feature Module**
**Backend (NestJS):**
```bash
cd backend
nest g resource <name> --no-spec # Generates module, service, controller
```
1. Update `prisma/schema.prisma` with new models
2. Run `npx prisma migrate dev`
3. Create DTOs in `src/<name>/dto/`
4. Add guards: `@Roles('ADMINISTRATOR', 'COORDINATOR')`
5. Implement service methods using Prisma
**Frontend (React):**
1. Create page in `src/pages/<Name>Page.tsx`
2. Add route in `App.tsx`
3. Create API hooks using TanStack Query
4. Add navigation link
5. Implement role-based rendering
**Code Generation**
```bash
# Backend
nest g module <name>
nest g service <name>
nest g controller <name>
# Prisma
npx prisma generate # After schema changes
npx prisma migrate dev # Create migration
```
---
## Key Patterns & Best Practices
### Backend (NestJS) Patterns
**1. Controllers**
```typescript
@Controller('vips')
@UseGuards(JwtAuthGuard, RolesGuard)
export class VipsController {
constructor(private vipsService: VipsService) {}
@Get()
@Roles('ADMINISTRATOR', 'COORDINATOR', 'DRIVER')
async findAll(@CurrentUser() user: User) {
return this.vipsService.findAll();
}
@Post()
@Roles('ADMINISTRATOR', 'COORDINATOR')
async create(@Body() createVipDto: CreateVipDto, @CurrentUser() user: User) {
return this.vipsService.create(createVipDto);
}
}
```
**2. Services (Prisma)**
```typescript
@Injectable()
export class VipsService {
constructor(private prisma: PrismaService) {}
async findAll() {
return this.prisma.vip.findMany({
where: { deletedAt: null },
include: { flights: true, events: true },
orderBy: { createdAt: 'desc' },
});
}
async create(data: CreateVipDto) {
return this.prisma.vip.create({
data: {
...data,
// Prisma handles timestamps automatically
},
});
}
async softDelete(id: string) {
return this.prisma.vip.update({
where: { id },
data: { deletedAt: new Date() },
});
}
}
```
**3. DTOs with Validation**
```typescript
import { IsString, IsEnum, IsOptional, IsBoolean } from 'class-validator';
export class CreateVipDto {
@IsString()
name: string;
@IsEnum(Department)
department: Department;
@IsEnum(ArrivalMode)
arrivalMode: ArrivalMode;
@IsOptional()
@IsBoolean()
airportPickup?: boolean;
}
```
**4. Custom Decorators**
```typescript
// Get current user from request
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext): User => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
```
**5. Guards**
```typescript
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.get<Role[]>('roles', context.getHandler());
if (!requiredRoles) return true;
const { user } = context.switchToHttp().getRequest();
return requiredRoles.includes(user.role);
}
}
```
### Frontend (React) Patterns
**1. Protected Routes**
```typescript
function App() {
return (
<Auth0Provider domain={...} clientId={...} redirectUri={...}>
<QueryClientProvider client={queryClient}>
<Routes>
<Route path="/login" element={<Login />} />
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/vips" element={<VIPList />} />
</Route>
</Routes>
</QueryClientProvider>
</Auth0Provider>
);
}
```
**2. Data Fetching with TanStack Query**
```typescript
// Hook
function useVIPs() {
return useQuery({
queryKey: ['vips'],
queryFn: async () => {
const { data } = await api.get('/vips');
return data;
},
});
}
// Component
function VIPList() {
const { data: vips, isLoading, error } = useVIPs();
if (isLoading) return <Spinner />;
if (error) return <ErrorAlert error={error} />;
return <VIPTable vips={vips} />;
}
```
**3. Mutations**
```typescript
function useCreateVIP() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (vip: CreateVIPDto) => {
const { data } = await api.post('/vips', vip);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['vips'] });
toast.success('VIP created successfully');
},
onError: (error) => {
toast.error(error.message);
},
});
}
```
**4. Forms with React Hook Form + Zod**
```typescript
const vipSchema = z.object({
name: z.string().min(1, 'Name required'),
department: z.enum(['OFFICE_OF_DEVELOPMENT', 'ADMIN']),
arrivalMode: z.enum(['FLIGHT', 'SELF_DRIVING']),
});
type VIPFormData = z.infer<typeof vipSchema>;
function VIPForm() {
const { register, handleSubmit, formState: { errors } } = useForm<VIPFormData>({
resolver: zodResolver(vipSchema),
});
const createVIP = useCreateVIP();
const onSubmit = (data: VIPFormData) => {
createVIP.mutate(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input {...register('name')} error={errors.name?.message} />
<Button type="submit" loading={createVIP.isPending}>Create VIP</Button>
</form>
);
}
```
**5. Role-Based Rendering**
```typescript
function usePermissions() {
const { user } = useAuth0();
return {
canManageUsers: user?.role === 'ADMINISTRATOR',
canEditVIPs: ['ADMINISTRATOR', 'COORDINATOR'].includes(user?.role),
isDriver: user?.role === 'DRIVER',
};
}
// Usage
function VIPList() {
const { canEditVIPs } = usePermissions();
return (
<div>
{canEditVIPs && <Button>Add VIP</Button>}
<VIPTable />
</div>
);
}
```
### Error Handling
**Backend:**
```typescript
// Built-in HTTP exceptions
throw new NotFoundException(`VIP with ID ${id} not found`);
throw new BadRequestException('Invalid flight number format');
throw new UnauthorizedException('User not approved');
throw new ForbiddenException('Insufficient permissions');
// Custom exception filter (optional)
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
message: exception.message,
timestamp: new Date().toISOString(),
});
}
}
```
**Frontend:**
```typescript
// Global error boundary
<ErrorBoundary fallback={<ErrorPage />}>
<App />
</ErrorBoundary>
// Query error handling
const { data, error } = useQuery({
queryKey: ['vips'],
queryFn: fetchVIPs,
onError: (error) => {
console.error('[VIP] Failed to fetch:', error);
toast.error('Failed to load VIPs. Please try again.');
},
});
```
### Logging
**Backend (NestJS Logger):**
```typescript
@Injectable()
export class VipsService {
private logger = new Logger(VipsService.name);
async create(data: CreateVipDto) {
this.logger.log(`Creating VIP: ${data.name}`);
try {
const vip = await this.prisma.vip.create({ data });
this.logger.log(`VIP created: ${vip.id}`);
return vip;
} catch (error) {
this.logger.error(`Failed to create VIP: ${error.message}`, error.stack);
throw error;
}
}
}
```
**Frontend:**
```typescript
// Prefix all logs with feature area
console.log('[AUTH] User logged in:', user);
console.error('[API] Request failed:', error);
console.warn('[SCHEDULE] Conflict detected:', conflict);
console.debug('[FLIGHT] Status updated:', flight);
```
---
## Working with Claude AI
### What Claude CANNOT Do
**⚠️ Browser Testing Limitations:**
- Cannot see your browser window or UI
- Cannot view browser console directly
- Cannot see network requests in DevTools
- Cannot click buttons or interact with the app
### How to Help Claude Debug
**✅ Good Feedback Examples:**
1. **Console Errors:**
```
I clicked Login and got this error:
```
[AUTH] Failed to authenticate
POST http://localhost:3000/api/v1/auth/login 401
Unauthorized: Invalid credentials
```
```
2. **Network Errors:**
```
When I try to create a VIP, the request fails:
Request: POST /api/v1/vips
Status: 500
Response: { "message": "Prisma validation error" }
```
3. **UI Issues:**
```
The VIP list page shows a blank screen.
Console shows:
TypeError: Cannot read property 'map' of undefined
at VIPList.tsx:45
```
**❌ Unhelpful Feedback:**
- "It doesn't work"
- "There's an error"
- "The button is broken"
### Effective Workflow
1. **Claude makes changes** → Writes code
2. **You run the app** → Test locally
3. **You share feedback** → Console logs, screenshots, error messages
4. **Claude debugs** → Fixes based on your feedback
5. **Repeat** until working
### Tips for Success
- Run `docker-compose logs -f backend` to watch backend logs
- Keep browser DevTools open (F12)
- Share full error messages (not just summaries)
- Take screenshots when UI looks wrong
- Copy/paste network request/response bodies
---
## Deployment (Digital Ocean)
### Option 1: App Platform (Easiest)
**Pros:**
- Managed PostgreSQL database
- Auto-scaling
- Zero-downtime deployments
- SSL certificates automatic
**Setup:**
1. Push code to GitHub
2. Create DO App Platform app
3. Connect GitHub repository
4. Configure components:
- **Backend:** Node.js (port 3000)
- **Frontend:** Static Site (Vite build)
- **Database:** Managed PostgreSQL
5. Set environment variables in DO dashboard
6. Deploy
**Cost:** ~$12/month (basic tier)
### Option 2: Droplet + Docker (More Control)
**Pros:**
- Full control
- Cheaper for small apps
- Run everything on one server
**Setup:**
```bash
# On Digital Ocean Droplet (Ubuntu 22.04)
apt update && apt upgrade -y
apt install docker.io docker-compose -y
# Clone repository
git clone <your-repo>
cd vip-coordinator
# Set environment variables
cp .env.example .env
nano .env # Edit with production values
# Run with Docker Compose
docker-compose -f docker-compose.prod.yml up -d
# Set up nginx reverse proxy
apt install nginx certbot -y
# Configure nginx for SSL
```
**Cost:** ~$6/month (basic droplet)
### Environment Variables (Production)
**Backend:**
```env
DATABASE_URL="postgresql://user:pass@db-host:5432/vip_coordinator"
AUTH0_DOMAIN="your-tenant.auth0.com"
AUTH0_AUDIENCE="https://api.yourapp.com"
NODE_ENV=production
PORT=3000
```
**Frontend:**
```env
VITE_API_URL=https://api.yourapp.com/api/v1
VITE_AUTH0_DOMAIN=your-tenant.auth0.com
VITE_AUTH0_CLIENT_ID=your-production-client-id
VITE_AUTH0_AUDIENCE=https://api.yourapp.com
```
### Auth0 Production Setup
1. Create **Production** Auth0 application (separate from dev)
2. Update callback URLs to production domain
3. Configure custom domain (optional)
4. Enable social logins if needed
### Database Backups
**Managed Database (DO):**
- Daily automatic backups included
- Point-in-time recovery
**Self-hosted:**
```bash
# Backup script
pg_dump -U postgres vip_coordinator > backup_$(date +%Y%m%d).sql
# Cron job (daily at 2 AM)
0 2 * * * /usr/bin/pg_dump -U postgres vip_coordinator > /backups/backup_$(date +\%Y\%m\%d).sql
```
---
## Testing Strategy
### Backend Testing
**Unit Tests (Services):**
```typescript
describe('VipsService', () => {
it('should create a VIP', async () => {
const vip = await service.create({ name: 'John Doe', ... });
expect(vip.name).toBe('John Doe');
});
});
```
**Integration Tests (Controllers):**
```typescript
describe('VipsController', () => {
it('GET /vips should return all VIPs', async () => {
const response = await request(app.getHttpServer())
.get('/api/v1/vips')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(response.body).toBeInstanceOf(Array);
});
});
```
### Frontend Testing
**Component Tests (Vitest + Testing Library):**
```typescript
test('VIPList renders VIPs', async () => {
render(<VIPList />);
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
});
```
**E2E Tests (Playwright) - Recommended:**
```typescript
test('admin can create VIP', async ({ page }) => {
await page.goto('http://localhost:5173/login');
await page.click('text=Login');
// ... auth flow
await page.goto('/vips');
await page.click('text=Add VIP');
await page.fill('[name=name]', 'Jane Smith');
await page.click('text=Submit');
await expect(page.getByText('VIP created')).toBeVisible();
});
```
### Running Tests
```bash
# Backend
cd backend
npm test
npm run test:watch
npm run test:cov
# Frontend
cd frontend
npm test
npm run test:ui
# E2E
npm run test:e2e
```
---
## Security Best Practices
### Backend Security
1. **Always validate input:**
```typescript
@Post()
async create(@Body() dto: CreateVipDto) { // DTO with class-validator
return this.service.create(dto);
}
```
2. **Use guards everywhere:**
```typescript
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('ADMINISTRATOR')
```
3. **Soft deletes (preserve data):**
```typescript
async delete(id: string) {
return this.prisma.vip.update({
where: { id },
data: { deletedAt: new Date() },
});
}
```
4. **Prisma prevents SQL injection automatically**
### Frontend Security
1. **Never store tokens in localStorage** (Auth0 handles this)
2. **Always use HTTPS in production**
3. **Sanitize user input before display**
4. **Check permissions on backend, not just frontend**
### Auth0 Security
- Enable MFA for admin accounts
- Use Auth0 Actions to add custom claims
- Rotate client secrets regularly
- Monitor Auth0 logs for suspicious activity
---
## Common Issues & Solutions
### Issue: "Cannot connect to database"
**Solution:**
```bash
# Check PostgreSQL is running
docker ps | grep postgres
# Check connection string in .env
echo $DATABASE_URL
# Test connection
docker-compose exec backend npx prisma db pull
```
### Issue: "Auth0 redirect loop"
**Solution:**
- Verify `AUTH0_DOMAIN` and `AUTH0_CLIENT_ID` match Auth0 dashboard
- Check callback URLs in Auth0 settings
- Clear browser cache and cookies
### Issue: "Prisma Client not generated"
**Solution:**
```bash
cd backend
npx prisma generate
npm run build
```
### Issue: "Port already in use"
**Solution:**
```bash
# Windows
netstat -ano | findstr ":3000"
taskkill /PID <process_id> /F
# Mac/Linux
lsof -ti:3000 | xargs kill -9
```
### Issue: "First user can't login (not approved)"
**Solution:**
First user is auto-approved. If this fails:
```sql
-- Manually approve first user
UPDATE "User" SET "isApproved" = true WHERE email = 'admin@example.com';
```
Or check `backend/src/auth/auth.service.ts` first-user logic.
---
## Development Mode Permissions
Claude AI has full permission to:
-**Nuke and rebuild** the database without asking
-**Make breaking changes** to the codebase
-**Delete/rewrite entire files** if it improves the code
-**Install new packages** to solve problems properly
-**Start fresh** if something is fundamentally broken
-**Change architecture** if current approach is flawed
**Focus:** Getting it working correctly > preserving broken code
No production data exists. Feel free to reset everything.
---
## Quick Reference
### Useful Commands
```bash
# Development
npm run start:dev # Backend dev server
npm run dev # Frontend dev server
docker-compose up -d # Start all services
# Database
npx prisma migrate dev # Create migration
npx prisma studio # Database GUI
npx prisma migrate reset # Reset database
# Production
docker-compose -f docker-compose.prod.yml up -d
npm run build # Build frontend
npm run start:prod # Production server
# Testing
npm test # Run tests
npm run test:watch # Watch mode
npm run test:e2e # E2E tests
```
### API Endpoints
All endpoints prefixed with `/api/v1`:
```
POST /auth/login # Login with Auth0
GET /auth/profile # Get current user
GET /users # List users (admin only)
PATCH /users/:id/approve # Approve user (admin only)
GET /vips # List VIPs
POST /vips # Create VIP
GET /vips/:id # Get VIP details
PATCH /vips/:id # Update VIP
DELETE /vips/:id # Soft delete VIP
GET /drivers # List drivers
POST /drivers # Create driver
GET /drivers/:id # Get driver + schedule
PATCH /drivers/:id # Update driver
DELETE /drivers/:id # Soft delete driver
GET /events # List events
POST /events # Create event (with conflict check)
GET /events/:id # Get event
PATCH /events/:id # Update event
PATCH /events/:id/status # Update status (driver can do this)
DELETE /events/:id # Cancel event
GET /flights/:number # Get flight status
POST /flights/batch # Batch flight lookup
```
### Key Files
- **Backend Entry:** [backend/src/main.ts](backend/src/main.ts)
- **Frontend Entry:** [frontend/src/main.tsx](frontend/src/main.tsx)
- **Database Schema:** [backend/prisma/schema.prisma](backend/prisma/schema.prisma)
- **Auth Guard:** [backend/src/auth/guards/jwt-auth.guard.ts](backend/src/auth/guards/jwt-auth.guard.ts)
- **API Client:** [frontend/src/lib/api.ts](frontend/src/lib/api.ts)
- **Protected Routes:** [frontend/src/components/auth/ProtectedRoute.tsx](frontend/src/components/auth/ProtectedRoute.tsx)
---
**This document is the SOURCE OF TRUTH for the VIP Coordinator project.**
When code conflicts with this document, update the code to match this spec (not vice versa).