first commit
This commit is contained in:
140
.github/copilot-instructions.md
vendored
Normal file
140
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
# Grademaxxing - AI Coding Agent Instructions
|
||||
|
||||
## Project Architecture
|
||||
|
||||
**Fullstack SPA Architecture:**
|
||||
- **Frontend:** React Router 7 SPA (client-side only, no SSR) built with Vite + TypeScript + Tailwind CSS v4
|
||||
- **Backend:** FastAPI (Python) serves both API (`/api/*`) and built React SPA (`build/client/`)
|
||||
- **Database:** MongoDB with Motor async driver
|
||||
- **Deployment:** Docker Compose with 3 services (mongodb, frontend-build, backend)
|
||||
|
||||
**Critical Architectural Decision:** Backend serves the compiled React SPA from `build/client/` directory. All non-`/api/*` routes fall through to `index.html` for client-side routing. Frontend must be built before backend serves it.
|
||||
|
||||
## Essential Workflows
|
||||
|
||||
**Development:**
|
||||
```bash
|
||||
# Docker (recommended) - starts MongoDB + builds frontend + runs backend
|
||||
docker-compose up --build
|
||||
|
||||
# Local development (3 terminals)
|
||||
# Terminal 1: cd backend && uvicorn main:app --reload
|
||||
# Terminal 2: pnpm dev
|
||||
# Terminal 3: mongod --dbpath ./data
|
||||
```
|
||||
|
||||
**Build & Deploy:**
|
||||
```bash
|
||||
pnpm build # Builds React SPA to build/client/
|
||||
# Backend automatically serves from build/client/ when running
|
||||
```
|
||||
|
||||
**Quick start scripts:** `start.bat` (Windows) or `./start.sh` (Linux/Mac) wrap `docker-compose up --build`
|
||||
|
||||
## Data Model & Business Logic
|
||||
|
||||
**Flexible Grading System (Not German-specific):**
|
||||
- Each `Subject` has custom `grading_categories` (e.g., "Written" 60%, "Oral" 40%)
|
||||
- Categories must sum to 100% weight (validated in [backend/models.py](backend/models.py#L74-L80))
|
||||
- Each `Grade` has `grade/max_grade` (e.g., 85/100) and `weight_in_category` for multiple tests
|
||||
- Supports any grading scale (percentage, German 1-6, US letters, etc.)
|
||||
|
||||
**Grade Calculation Pipeline ([app/utils/gradeCalculations.ts](app/utils/gradeCalculations.ts)):**
|
||||
1. Normalize each grade to percentage: `(grade / max_grade) * 100`
|
||||
2. Calculate category average: weighted sum of grades in category
|
||||
3. Calculate subject average: weighted sum of category averages
|
||||
4. Calculate overall GPA: simple average of all subject averages
|
||||
|
||||
**Report Periods:** Filter grades by date range for semester/quarter calculations (see [calculateReportCard](app/utils/gradeCalculations.ts#L80-L110))
|
||||
|
||||
## Code Conventions
|
||||
|
||||
**Backend (FastAPI + MongoDB):**
|
||||
- All routes require auth via `Depends(get_current_user)` from [backend/routes/auth_routes.py](backend/routes/auth_routes.py)
|
||||
- MongoDB ObjectId handling: Convert to string for JSON responses, convert to `ObjectId()` for queries
|
||||
- Model pattern: `{Entity}Base` → `{Entity}Create/Update` → `{Entity}InDB` → `{Entity}Response` (see [backend/models.py](backend/models.py))
|
||||
- Route structure: Each entity gets its own router in `backend/routes/`, imported in [main.py](backend/main.py#L40-L43)
|
||||
- Always use `await` with Motor async operations: `await db.collection.find().to_list()`
|
||||
|
||||
**Frontend (React Router 7 SPA):**
|
||||
- Route definitions in [app/routes.ts](app/routes.ts), files in `app/routes/` (use `.tsx` extension)
|
||||
- All authenticated pages wrap children in `<ProtectedRoute>` component ([app/components/ProtectedRoute.tsx](app/components/ProtectedRoute.tsx))
|
||||
- API client singleton: `import { api } from "~/api/client"` - handles auth headers automatically
|
||||
- Auth token stored in localStorage (`access_token`), set on login/register, removed on logout
|
||||
- Component pattern: Small reusable components in `app/components/`, import as `~/components/{Name}`
|
||||
- Tailwind CSS only - no CSS modules or styled-components
|
||||
|
||||
**TypeScript Type Safety:**
|
||||
- Backend types defined in [backend/models.py](backend/models.py) (Pydantic)
|
||||
- Frontend types in [app/types/api.ts](app/types/api.ts) - **manually sync with backend models**
|
||||
- Use `_id` (alias) for MongoDB ObjectId in responses, map to `id` field in TypeScript when needed
|
||||
|
||||
## Authentication & Authorization
|
||||
|
||||
**Flow:**
|
||||
1. Login/register → Backend returns JWT + user data ([backend/routes/auth_routes.py](backend/routes/auth_routes.py))
|
||||
2. Frontend stores token in localStorage ([app/api/client.ts](app/api/client.ts#L56-L58))
|
||||
3. All protected API calls include `Authorization: Bearer {token}` header
|
||||
4. Backend validates token with `get_current_user` dependency
|
||||
5. Frontend uses `ProtectedRoute` wrapper to redirect unauthenticated users
|
||||
|
||||
**User Isolation:** All database queries filter by `user_id` (from token) - users only see their own data
|
||||
|
||||
## Common Patterns
|
||||
|
||||
**Adding a New Entity:**
|
||||
1. Define Pydantic models in [backend/models.py](backend/models.py): `{Entity}Base`, `{Entity}Create`, `{Entity}InDB`, `{Entity}Response`
|
||||
2. Create router in `backend/routes/{entity}_routes.py` with CRUD endpoints
|
||||
3. Import and include router in [backend/main.py](backend/main.py)
|
||||
4. Add TypeScript types to [app/types/api.ts](app/types/api.ts)
|
||||
5. Add API client methods to [app/api/client.ts](app/api/client.ts)
|
||||
6. Create routes/components in `app/`
|
||||
|
||||
**Adding a New Route:**
|
||||
1. Create `app/routes/{name}.tsx` with default export component
|
||||
2. Add route to [app/routes.ts](app/routes.ts) config array
|
||||
3. Wrap in `<ProtectedRoute>` if auth required
|
||||
|
||||
**MongoDB Query Pattern:**
|
||||
```python
|
||||
db = get_database()
|
||||
cursor = db.collection.find({"user_id": current_user.id})
|
||||
items = await cursor.to_list(length=None)
|
||||
for item in items:
|
||||
item["_id"] = str(item["_id"]) # Convert ObjectId to string
|
||||
return [ResponseModel(**item) for item in items]
|
||||
```
|
||||
|
||||
## Critical Files
|
||||
|
||||
- [backend/main.py](backend/main.py) - FastAPI app initialization, SPA serving, route inclusion
|
||||
- [app/api/client.ts](app/api/client.ts) - Centralized API client with auth
|
||||
- [app/utils/gradeCalculations.ts](app/utils/gradeCalculations.ts) - Core business logic for grade calculations
|
||||
- [backend/models.py](backend/models.py) - Pydantic models with validation (category weights must sum to 100%)
|
||||
- [docker-compose.yml](docker-compose.yml) - Multi-stage build: frontend-build → backend serves result
|
||||
|
||||
## Known Issues & Gotchas
|
||||
|
||||
- Frontend build must complete before backend serves (`frontend-build` container in Docker)
|
||||
- MongoDB ObjectId serialization: Always convert to string for JSON responses
|
||||
- CORS: Backend allows origins from `CORS_ORIGINS` env var (comma-separated)
|
||||
- Category weights validation: Must sum to 100% (enforced in Pydantic model)
|
||||
- Date handling: Python uses `datetime.utcnow()`, frontend converts ISO strings
|
||||
- No SSR: React Router in SPA mode only, backend serves static build
|
||||
|
||||
## Testing
|
||||
|
||||
**Manual testing:** See [DEVELOPMENT.md](DEVELOPMENT.md#L244-L254) checklist (register → create period → add subject → add grades → view dashboard)
|
||||
|
||||
**API Testing:** FastAPI auto-docs at `http://localhost:8000/docs` (Swagger UI)
|
||||
|
||||
## Environment Setup
|
||||
|
||||
**Required Environment Variables:**
|
||||
- `MONGODB_URL` - MongoDB connection string (default: `mongodb://localhost:27017`)
|
||||
- `SECRET_KEY` - JWT signing key (generate with `python -c "import secrets; print(secrets.token_urlsafe(64))"`)
|
||||
- `DATABASE_NAME` - MongoDB database name (default: `grademaxxing`)
|
||||
- `CORS_ORIGINS` - Comma-separated allowed origins (default: `http://localhost:5173,http://localhost:8000`)
|
||||
- `ACCESS_TOKEN_EXPIRE_MINUTES` - JWT expiration (default: 10080 = 7 days)
|
||||
|
||||
Frontend uses `VITE_API_URL` for API base URL (defaults to `http://localhost:8000/api`)
|
||||
Reference in New Issue
Block a user