170 lines
4.9 KiB
Markdown
170 lines
4.9 KiB
Markdown
# Agent Guidelines for placebo.mk
|
|
|
|
## Project Overview
|
|
Macedonian satirical news site using TanStack stack + NestJS backend.
|
|
|
|
## Tech Stack
|
|
- **Frontend**: React 19 + TanStack (Query, Router) + Vite + Tailwind CSS + shadcn/ui
|
|
- **Backend**: NestJS + TypeORM + SQLite
|
|
- **CMS**: Strapi (in /cms)
|
|
|
|
## Commands
|
|
|
|
### Root Level
|
|
```bash
|
|
npm run dev:local # Start backend & frontend locally
|
|
npm run dev:docker # Start via docker-compose
|
|
npm run lint # Lint both projects
|
|
npm run lint:fix # Auto-fix lint issues
|
|
npm run type-check # Type check both projects
|
|
```
|
|
|
|
### Backend (cd backend)
|
|
```bash
|
|
npm run start:dev # Watch mode
|
|
npm run lint / lint:fix # ESLint
|
|
npm run type-check # TypeScript check
|
|
npm test # All tests
|
|
npm test app.service.spec # Single file (omit .ts)
|
|
npm test -t "should return" # By pattern
|
|
npm test:cov # Coverage
|
|
npm test:e2e # E2E tests
|
|
```
|
|
|
|
### Frontend (cd frontend)
|
|
```bash
|
|
npm run dev # Vite dev server
|
|
npm run lint / lint:fix # ESLint
|
|
npm run type-check # TypeScript check
|
|
npm test # All tests (Vitest)
|
|
npm test Header.test # Single file (omit .tsx)
|
|
npm test -t "renders" # By pattern
|
|
npm test:ui # Vitest UI
|
|
npm test:coverage # Coverage
|
|
```
|
|
|
|
## Code Style
|
|
|
|
### TypeScript
|
|
- Strict mode: `noImplicitAny`, `strictNullChecks`, `noFallthroughCasesInSwitch`
|
|
- No implicit `any` - use `unknown` when type uncertain
|
|
- Explicit return types for public methods
|
|
- Prefer `interface` over `type` for objects
|
|
- Use `readonly` for immutable properties
|
|
|
|
### Formatting (Prettier)
|
|
- Single quotes, trailing commas, 2-space indent
|
|
- No semicolons in comments
|
|
|
|
### Naming
|
|
| Element | Convention | Example |
|
|
|---------|------------|---------|
|
|
| Files | kebab-case | `user-profile.ts`, `auth.service.ts` |
|
|
| Classes | PascalCase | `UserService`, `AuthGuard` |
|
|
| Functions/Variables | camelCase | `getUser`, `isLoading` |
|
|
| Constants | UPPER_SNAKE_CASE | `MAX_RETRIES` |
|
|
| Private members | underscore prefix | `_cache`, `_validate()` |
|
|
| Interfaces | PascalCase (no I prefix) | `User`, `ApiResponse` |
|
|
|
|
### Imports
|
|
Group with blank lines: External → Internal → Relative
|
|
```typescript
|
|
// External
|
|
import { Injectable } from '@nestjs/common';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
|
|
// Internal modules
|
|
import { UserService } from '../users/user.service';
|
|
|
|
// Relative
|
|
import { AuthResponse } from './types';
|
|
```
|
|
|
|
### File Structure
|
|
```
|
|
backend/src/
|
|
modules/{feature}/
|
|
{feature}.module.ts
|
|
{feature}.controller.ts
|
|
{feature}.service.ts
|
|
{feature}.dto.ts
|
|
common/{decorators,filters,guards,interceptors,pipes}
|
|
entities.ts # All TypeORM entities
|
|
|
|
frontend/src/
|
|
components/{ui,layout,features,admin,routes}/
|
|
hooks/ # Custom React hooks
|
|
queries/ # TanStack Query hooks
|
|
lib/ # Utilities, API client
|
|
routes/ # TanStack Router routes
|
|
types/ # TypeScript types
|
|
```
|
|
|
|
### Path Aliases
|
|
- Frontend: `@/*` maps to `./src/*`
|
|
- Import UI components: `import { Button } from '@/components/ui/button'`
|
|
|
|
## Patterns
|
|
|
|
### Backend Services (NestJS)
|
|
```typescript
|
|
@Injectable()
|
|
export class AuthService {
|
|
constructor(
|
|
private userService: UserService,
|
|
private jwtService: JwtService,
|
|
) {}
|
|
|
|
async login(dto: LoginUserDto): Promise<AuthResponse> {
|
|
// Implementation
|
|
}
|
|
}
|
|
```
|
|
|
|
### Frontend Hooks (TanStack Query)
|
|
```typescript
|
|
export function useArticles(params: FindArticlesParams = {}) {
|
|
return useQuery({
|
|
queryKey: ['articles', params],
|
|
queryFn: () => api.fetchArticles(params),
|
|
});
|
|
}
|
|
|
|
export function useCreateArticle() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: api.createArticle,
|
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['articles'] }),
|
|
});
|
|
}
|
|
```
|
|
|
|
### Error Handling
|
|
- **Backend**: NestJS exceptions (`NotFoundException`, `BadRequestException`)
|
|
- **Frontend**: Error boundaries, try-catch with user-friendly messages
|
|
|
|
### API Response Format
|
|
```typescript
|
|
{ data: T, meta: { total, page, limit }, error?: { code, message } }
|
|
```
|
|
|
|
## Testing
|
|
- **Backend**: Jest (`*.spec.ts`), AAA pattern, mock dependencies
|
|
- **Frontend**: Vitest + React Testing Library (`*.test.tsx`)
|
|
- Descriptive names: `should return user data when valid ID provided`
|
|
|
|
## Git Commits
|
|
- Conventional: `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`
|
|
- Atomic, imperative mood
|
|
|
|
## UI Components
|
|
- Use shadcn/ui from `components/ui/`
|
|
- CVA for variants, clsx + tailwind-merge for classes
|
|
|
|
## Agent Instructions
|
|
- **ALWAYS** run `npm run lint` and `npm run type-check` after changes
|
|
- **NEVER** commit without explicit request
|
|
- **PREFER** editing existing files over creating new ones
|
|
- **VERIFY** tests pass before considering work complete
|
|
- **CHECK** both backend/frontend when making API changes
|