367 lines
9.9 KiB
Markdown
367 lines
9.9 KiB
Markdown
# Agent Development Guide for FitAI
|
|
|
|
This document provides essential guidelines for AI coding agents working in the FitAI codebase.
|
|
|
|
## Project Overview
|
|
|
|
FitAI is a monorepo containing a fitness AI platform with:
|
|
|
|
- **Admin App** (`apps/admin`): Next.js 16 web application for gym management
|
|
- **Mobile App** (`apps/mobile`): React Native/Expo mobile app for clients
|
|
- **Shared Packages**: Database (`@fitai/database`) and utilities (`@fitai/shared`)
|
|
|
|
**Tech Stack**: TypeScript, Next.js 16, React 19, React Native, Expo 54, Drizzle ORM, Clerk Auth, TanStack Query, Tailwind CSS
|
|
|
|
## Build, Lint, and Test Commands
|
|
|
|
### From Repository Root (`/home/echo/dev/prototype`)
|
|
|
|
```bash
|
|
# Development
|
|
npm run dev # Start both admin and mobile dev servers
|
|
npm run dev:admin # Start admin dev server (Next.js)
|
|
npm run dev:mobile # Start mobile dev server (Expo)
|
|
|
|
# Build
|
|
npm run build # Build both apps
|
|
npm run build:admin # Build admin app
|
|
npm run build:mobile # Build mobile app
|
|
|
|
# Linting
|
|
npm run lint # Lint both apps
|
|
npm run lint:admin # Lint admin app
|
|
npm run lint:mobile # Lint mobile app
|
|
|
|
# Type Checking
|
|
npm run typecheck # Check types in both apps
|
|
npm run typecheck:admin # Check types in admin
|
|
npm run typecheck:mobile # Check types in mobile
|
|
|
|
# Testing
|
|
npm run test # Run all tests
|
|
npm run test:admin # Run admin tests
|
|
npm run test:mobile # Run mobile tests
|
|
```
|
|
|
|
### Admin App (`apps/admin`)
|
|
|
|
```bash
|
|
cd apps/admin
|
|
|
|
# Development
|
|
npm run dev # Start Next.js dev server (port 3000)
|
|
|
|
# Testing
|
|
npm test # Run all tests
|
|
npx jest # Run all tests
|
|
npx jest path/to/file.test.ts # Run specific test file
|
|
npx jest --testPathPattern=drizzle # Run tests matching pattern
|
|
npx jest src/lib/database/__tests__/drizzle.test.ts # Run specific test
|
|
|
|
# Build & Lint
|
|
npm run build # Build production bundle
|
|
npm run lint # Run ESLint
|
|
npm run typecheck # Type check without emitting
|
|
```
|
|
|
|
### Mobile App (`apps/mobile`)
|
|
|
|
```bash
|
|
cd apps/mobile
|
|
|
|
# Development
|
|
npm start # Start Expo dev server
|
|
npm run android # Start on Android emulator
|
|
npm run ios # Start on iOS simulator
|
|
|
|
# Testing
|
|
npm test # Run all tests
|
|
npx jest path/to/file.test.ts # Run specific test file
|
|
npx jest --testPathPattern=component # Run tests matching pattern
|
|
|
|
# Build & Lint
|
|
npm run build # Build with Expo
|
|
npm run lint # Run ESLint
|
|
npm run typecheck # Type check
|
|
```
|
|
|
|
## Code Style Guidelines
|
|
|
|
### Import Organization
|
|
|
|
**Order**: External libraries → Internal imports, grouped logically
|
|
|
|
```typescript
|
|
// External: React, Next.js, React Native
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import React from "react";
|
|
|
|
// External: Third-party libraries
|
|
import { auth, clerkClient } from "@clerk/nextjs/server";
|
|
import bcrypt from "bcryptjs";
|
|
|
|
// Monorepo packages
|
|
import { db, sql } from "@fitai/database";
|
|
|
|
// Internal: Path aliases or relative imports
|
|
import { getDatabase } from "@/lib/database";
|
|
import type { User } from "./types";
|
|
```
|
|
|
|
**Key Rules**:
|
|
|
|
- Use `import type { ... }` for type-only imports
|
|
- Admin app uses path aliases: `@/*` → `./src/*`
|
|
- Mobile app uses relative paths or `@/*` alias
|
|
- No automatic import sorting; manual grouping by category
|
|
|
|
### Component Structure & Naming
|
|
|
|
**Components**: PascalCase function components with named exports
|
|
|
|
```typescript
|
|
// Feature components - Named export (function)
|
|
export function UserManagement() {
|
|
return <div>...</div>
|
|
}
|
|
|
|
// UI library components - Named export (forwardRef constant)
|
|
const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|
({ className, ...props }, ref) => (
|
|
<div ref={ref} className={cn("rounded-lg", className)} {...props} />
|
|
)
|
|
)
|
|
Card.displayName = "Card"
|
|
export { Card }
|
|
```
|
|
|
|
**Naming Conventions**:
|
|
|
|
- Components: `UserManagement`, `GoalProgressCard`
|
|
- Functions: `handleEditUser`, `getGreeting`
|
|
- Custom hooks: `useUser`, `useAuth`
|
|
- API routes: Named exports `GET`, `POST`, `PUT`, `DELETE`
|
|
|
|
### Type Definitions
|
|
|
|
**Interfaces** for object shapes and React props; **Types** for unions and aliases
|
|
|
|
```typescript
|
|
// Props interfaces (ComponentNameProps pattern)
|
|
interface UserManagementProps {
|
|
userId: string;
|
|
onUpdate?: () => void;
|
|
}
|
|
|
|
// Domain interfaces
|
|
interface User {
|
|
id: string;
|
|
email: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
}
|
|
|
|
// Type aliases and unions
|
|
type UserRole = "admin" | "trainer" | "client";
|
|
type Status = "active" | "inactive" | "pending";
|
|
|
|
// No I prefix or T prefix
|
|
```
|
|
|
|
### Function Patterns
|
|
|
|
**Arrow functions** for components and handlers; **function declarations** for utilities
|
|
|
|
```typescript
|
|
// Component functions
|
|
export function UserCard() {
|
|
const handleClick = () => { /* ... */ } // Arrow function for handlers
|
|
return <button onClick={handleClick}>Click</button>
|
|
}
|
|
|
|
// Exported utility functions
|
|
export async function setUserRole(userId: string, role: UserRole) {
|
|
// ... implementation
|
|
}
|
|
|
|
// Always use async/await (never .then() chains)
|
|
const fetchData = async () => {
|
|
try {
|
|
const response = await fetch(url)
|
|
const data = await response.json()
|
|
return data
|
|
} catch (error) {
|
|
console.error('Failed to fetch:', error)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
**API Routes** (Next.js):
|
|
|
|
```typescript
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const { email, password } = await request.json();
|
|
|
|
// Early returns for validation
|
|
if (!email || !password) {
|
|
return NextResponse.json(
|
|
{ error: "Email and password are required" },
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
|
|
// ... logic
|
|
return NextResponse.json({ success: true });
|
|
} catch (error) {
|
|
console.error("Login error:", error);
|
|
return NextResponse.json(
|
|
{ error: "Internal server error" },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
**React Components**:
|
|
|
|
```typescript
|
|
// Admin: Use try-catch with console.error
|
|
const handleSave = async () => {
|
|
try {
|
|
await updateUser(userId, data);
|
|
} catch (error) {
|
|
console.error("Failed to update user:", error);
|
|
// Show error to user (toast, alert, etc.)
|
|
}
|
|
};
|
|
|
|
// Mobile: Use Alert for confirmations
|
|
const handleDelete = () => {
|
|
Alert.alert("Delete Goal", "Are you sure?", [
|
|
{ text: "Cancel", style: "cancel" },
|
|
{ text: "Delete", style: "destructive", onPress: onDelete },
|
|
]);
|
|
};
|
|
```
|
|
|
|
### Comments & Documentation
|
|
|
|
Use JSDoc for public APIs; inline comments for complex logic only
|
|
|
|
```typescript
|
|
/**
|
|
* Set a user's role in Clerk public metadata
|
|
*
|
|
* @param userId - Clerk user ID
|
|
* @param role - Role to assign (admin, trainer, or client)
|
|
* @returns Updated user object
|
|
*
|
|
* @example
|
|
* await setUserRole('user_abc123', 'admin')
|
|
*/
|
|
export async function setUserRole(userId: string, role: UserRole) {
|
|
// Implementation
|
|
}
|
|
|
|
// Inline comments for clarification
|
|
// Optimistically update local state so grid reflects changes immediately
|
|
setUsers((prev) => prev.map((u) => (u.id === id ? { ...u, ...updates } : u)));
|
|
```
|
|
|
|
### File Naming Conventions
|
|
|
|
```
|
|
apps/admin/src/
|
|
├── app/
|
|
│ ├── api/*/route.ts # API routes (lowercase route.ts)
|
|
│ └── */page.tsx # Pages (lowercase page.tsx)
|
|
├── components/
|
|
│ ├── ui/button.tsx # UI primitives (kebab-case)
|
|
│ └── users/UserManagement.tsx # Feature components (PascalCase)
|
|
├── lib/
|
|
│ ├── database/index.ts # Utilities (kebab-case)
|
|
│ └── clerk-helpers.ts # Helpers (kebab-case)
|
|
|
|
apps/mobile/src/
|
|
├── app/
|
|
│ └── (tabs)/index.tsx # Routes (lowercase)
|
|
├── components/
|
|
│ └── GoalProgressCard.tsx # All components (PascalCase)
|
|
└── services/
|
|
└── fitnessGoals.ts # Services (camelCase)
|
|
```
|
|
|
|
### Styling
|
|
|
|
**Admin App**: Tailwind CSS with utility classes
|
|
|
|
```typescript
|
|
<div className="flex items-center gap-2 rounded-lg border p-4">
|
|
<Button className="bg-primary text-white hover:bg-primary/90">
|
|
Click me
|
|
</Button>
|
|
</div>
|
|
```
|
|
|
|
**Mobile App**: StyleSheet.create() at file bottom with theme system
|
|
|
|
```typescript
|
|
import { theme } from '../styles/theme'
|
|
|
|
export function MyComponent() {
|
|
return <View style={styles.container}>...</View>
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: theme.colors.background,
|
|
padding: theme.spacing.md,
|
|
},
|
|
})
|
|
```
|
|
|
|
## ESLint Rules (Admin)
|
|
|
|
- `@typescript-eslint/no-unused-vars`: error
|
|
- `@typescript-eslint/no-explicit-any`: warn (allowed but discouraged)
|
|
- `prefer-const`: error
|
|
- `no-var`: error
|
|
- Extends: `next/core-web-vitals`, `@typescript-eslint/recommended`
|
|
|
|
## TypeScript Configuration
|
|
|
|
- **Strict mode enabled**: All strict type checking options on
|
|
- **Path aliases**: `@/*` maps to `./src/*`
|
|
- **Module resolution**: `bundler` (admin), `node` (mobile)
|
|
- **Target**: ES5 (admin), ESNext (mobile)
|
|
- Always provide explicit return types for exported functions
|
|
|
|
## Testing Best Practices
|
|
|
|
- Tests in `__tests__/` directories or `*.test.ts` files
|
|
- Use `@jest-environment node` comment for Node.js API tests
|
|
- Admin: Jest + ts-jest + @testing-library/react
|
|
- Mobile: Jest + react-native preset + @testing-library/react-native
|
|
- Test file naming: `component.test.ts` or `feature.test.tsx`
|
|
- Always test error cases and edge cases
|
|
|
|
## Key Patterns
|
|
|
|
1. **State Management**: Multiple `useState` declarations grouped together
|
|
2. **Destructuring**: Props in function signature, responses inline
|
|
3. **Type Safety**: Explicit return types, const assertions for readonly arrays
|
|
4. **Database**: Factory pattern with singleton (`getDatabase()`)
|
|
5. **Forms**: React Hook Form + Zod validation
|
|
6. **Data Fetching**: TanStack Query for server state
|
|
7. **Authentication**: Clerk for both admin and mobile (different packages)
|
|
|
|
## Node & Package Manager
|
|
|
|
- **Node**: >=18.0.0
|
|
- **Package Manager**: npm (>=9.0.0)
|
|
- Use `npm install` (not yarn or pnpm)
|