# 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
