From 147078d72fc46cfa881cf2cd7931c6821942173d Mon Sep 17 00:00:00 2001 From: kyle Date: Sat, 31 Jan 2026 17:38:34 +0100 Subject: [PATCH] chore: Remove Claude AI development files from repository Removed files only needed for Claude AI development workflow: - CLAUDE.md - AI context documentation (not needed to run app) - .claude/settings.local.json - Claude Code CLI settings Added to .gitignore: - .claude/ - Claude Code CLI configuration directory - CLAUDE.md - AI context file These files are kept locally for development but excluded from repository. Application does not require these files to function. Co-Authored-By: Claude Sonnet 4.5 --- .claude/settings.local.json | 42 -- .gitignore | 4 + CLAUDE.md | 1177 ----------------------------------- 3 files changed, 4 insertions(+), 1219 deletions(-) delete mode 100644 .claude/settings.local.json delete mode 100644 CLAUDE.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index d5158c6..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(npx prisma generate:*)", - "Bash(docker-compose:*)", - "Bash(npx prisma migrate dev:*)", - "Bash(docker port:*)", - "Bash(netstat:*)", - "Bash(findstr:*)", - "Bash(npm run prisma:seed:*)", - "Bash(timeout 10 npm run start:dev:*)", - "Bash(npm run build:*)", - "Bash(npm install:*)", - "Bash(docker volume ls:*)", - "Bash(tasklist:*)", - "Bash(taskkill:*)", - "Bash(npm run start:dev:*)", - "Bash(npm run dev:*)", - "Bash(npx prisma:*)", - "Bash(git add:*)", - "Bash(git commit:*)", - "Bash(git push:*)", - "Bash(git remote set-url:*)", - "Bash(npx playwright:*)", - "Bash(npm test:*)", - "Bash(npm run test:ui:*)", - "Bash(timeout 5 tail:*)", - "Bash(npm run test:headed:*)", - "Bash(npm run test:*)", - "Bash(curl:*)", - "Bash(ls:*)", - "Bash(node -e:*)", - "Bash(node check-users.js:*)", - "Bash(timeout /t 10 /nobreak)", - "Bash(dir:*)", - "Bash(lsof:*)", - "Bash(powershell -Command:*)", - "Bash(git rm:*)", - "Bash(docker ps:*)" - ] - } -} diff --git a/.gitignore b/.gitignore index 9bc1b0c..ae3af3d 100644 --- a/.gitignore +++ b/.gitignore @@ -56,10 +56,14 @@ jspm_packages/ # IDE files .vscode/ .idea/ +.claude/ *.swp *.swo *~ +# AI context files +CLAUDE.md + # OS generated files .DS_Store .DS_Store? diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 1a68a22..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,1177 +0,0 @@ -# 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 -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 --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//dto/` -4. Add guards: `@Roles('ADMINISTRATOR', 'COORDINATOR')` -5. Implement service methods using Prisma - -**Frontend (React):** -1. Create page in `src/pages/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 -nest g service -nest g controller - -# 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('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 ( - - - - } /> - }> - } /> - } /> - - - - - ); -} -``` - -**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 ; - if (error) return ; - - return ; -} -``` - -**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; - -function VIPForm() { - const { register, handleSubmit, formState: { errors } } = useForm({ - resolver: zodResolver(vipSchema), - }); - const createVIP = useCreateVIP(); - - const onSubmit = (data: VIPFormData) => { - createVIP.mutate(data); - }; - - return ( -
- - -
- ); -} -``` - -**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 ( -
- {canEditVIPs && } - -
- ); -} -``` - -### 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 -}> - - - -// 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 -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(); - 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 /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).