# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

See also the monorepo-level `../CLAUDE.md` for full architecture overview and backend docs.

## Commands

```bash
bun dev                          # Dev server (localhost:3000)
bun run build                    # Production build
bun run lint                     # ESLint + Prettier (concurrent)
bunx tsc --noEmit                # Type-check without emitting
bun run gc <layer> <Name>        # Generate FSD component (e.g. bun run gc shared Button)
bun run gicons                   # Convert raw SVGs → React icon components
bun run gen:api-types            # Regenerate API types from OpenAPI schema (backend must be running)
```

## Architecture

Next.js 16 App Router with Feature-Sliced Design. Strict unidirectional imports: `pages → widgets → features → entities → shared`.

- **App directory** is at `app/` (project root), not `src/app/`. The `src/app/` layer holds global styles and providers.
- **`app/template.tsx`** wraps all routes with Header (conditionally hidden on auth routes).
- **All components are `"use client"`** unless explicitly marked otherwise.

## API & Data Layer

- **`fetchClient`** (`openapi-fetch`) — typed HTTP client with JWT middleware that reads `access_token` from cookies. Defined in `src/shared/api/index.ts`.
- **`api`** (`openapi-react-query`) — wraps `fetchClient` for use with TanStack Query hooks in components. Import as `import api from "@shared/api"`.
- **Generated types** live in `src/shared/api/__generated__/openapi.types.ts` — never edit manually.
- **Server actions** in `src/shared/api/server.ts` — used for server-side API calls (ping, token verification).

## Styling

- **SCSS Modules** (`.module.scss`) for all component styles.
- **SCSS partials auto-injected** via `next.config.mjs` using `@use`: `_variables.scss`, `_breakpoints.scss`, `_typography.scss`, `_mixins.scss`. No need to import them manually in `.module.scss` files.
- **Radix UI Themes** wraps the app (`accentColor="iris"`, `grayColor="slate"`). Some components use Radix primitives directly (e.g., Dropdown uses `@radix-ui/react-dropdown-menu`, not Radix Themes).
- **Class composition**: `import cs from "classnames"`.
- **Design tokens** defined as CSS custom properties in `src/shared/styles/global.scss`, mirrored as SCSS vars in `_variables.scss`.

## State Management

- **Server state**: TanStack React Query (primary for all API data).
- **Client state**: Redux Toolkit with two slices: `appState` and `user` (in `src/shared/store/`).
- **Provider hierarchy** (in `src/shared/context/AppProviders.tsx`): Redux → QueryClient → UserSync → Radix Theme.

## Component Convention

Generate new components with `bun run gc <layer> <Name>` — never create component files manually. Each component folder contains:

- `index.ts` — public re-export only
- `ComponentName.tsx` — implementation
- `ComponentName.module.scss` — scoped styles
- `ComponentName.d.ts` — props interface (`IComponentNameProps`)

## Code Style

- **Prettier**: tabs (width 2), no semicolons, double quotes, sorted imports.
- **Imports**: use path aliases (`@shared/*`, `@entities/*`, etc.), never relative paths across layers.
- **Forms**: `react-hook-form` for form state management.
- **Icons**: Lucide React for standard icons. Custom icons: place SVG in `src/shared/assets/raw-icons/`, run `bun run gicons`, import from `@shared/ui/Icons/IconName`.

## Features Layer — Module-Aware Structure

Features are **grouped by domain module**, not placed flat at the top level. Each module folder has a barrel `index.ts`:

```
src/features/
├── profile/              # Profile domain
│   ├── index.ts          # Barrel: re-exports all features in module
│   ├── AvatarUpload/
│   ├── EditProfileForm/
│   └── LogoutButton/
└── project/              # Project domain
    ├── index.ts
    ├── CreateProjectModal/
    ├── DeleteProjectModal/
    ├── EditProjectModal/
    └── RenameProjectModal/
```

Import via module barrel: `import { AvatarUpload, EditProfileForm } from "@features/profile"`.

When adding a new feature, place it inside the relevant domain module folder (create one if needed).

## File Uploads

Use the shared `uploadFile` utility for any file upload — do not inline FormData logic in components:

```ts
import { uploadFile } from "@shared/api/uploadFile"

const result = await uploadFile(file, "avatars")
// result.file_url, result.file_path
```

The utility handles FormData construction, Content-Type override, and auth middleware automatically.

## Date Formatting

Use `date-fns` with Russian locale for all date formatting — never use `moment.js` or inline `Date` logic:

```ts
import { formatDate, formatRelativeTime } from "@shared/lib/dates"

formatDate(user.date_joined) // "21.02.2026"
formatDate(date, "dd MMM yyyy") // "21 февр. 2026"
formatRelativeTime(project.updated_at) // "2 дня назад"
```

Utilities live in `src/shared/lib/dates.ts`. Add new date helpers there, not in components.

## Localization

All user-facing UI text **must be in Russian** — labels, headings, buttons, placeholders, tooltips, aria-labels, error messages, breadcrumbs. The brand name "Coffee Project" / "Cofee Project" stays in English.

## Gotchas

- The `pages/` directory in the project root must exist (even if empty) — removing it causes Next.js build errors.
- Dropdown component's `asChild` trigger applies both the Dropdown's trigger class AND the child's class. Avoid `all: unset` on Dropdown triggers — it strips child flex/display styles.
- SCSS auto-import uses `@use` (not `@import`), so variables/mixins are namespaced (e.g., `variables.$color`). The files are injected as `additionalData` in `next.config.mjs`.
- **openapi-fetch + multipart**: `fetchClient` defaults to `Content-Type: application/json`. For file uploads, you must override the header and body serializer. Use the shared `uploadFile()` utility instead of doing this manually.
- **StaticLoader** is exported from `@shared/ui/Loader` (file is `Loader.tsx`), not from `@shared/ui/Loader/StaticLoader` — there is no subdirectory.
- **`lint:es` / `lint:prettier` scripts** are referenced by `bun run lint` but not defined in `package.json`. Linting is currently broken — use `bunx tsc --noEmit` for type checking.
- **`next/image` remote hosts**: External image hostnames must be listed in `next.config.mjs` `images.remotePatterns`. MinIO (`localhost:9000`) is already configured. If you add another storage backend, add its hostname there too.
- **Stale OpenAPI types**: Always run `bun run gen:api-types` before implementing against the API if the backend has changed. Stale types cause silent 404s at runtime.
- **Never use raw `fetch`/`useEffect` for API calls** — always use `api.useQuery()`/`api.useMutation()` from `@shared/api` (TanStack Query + openapi-fetch wrapper). For polling, use the `refetchInterval` option. Raw `fetch` bypasses typed routes, auth middleware, and query caching.

Always use Context7 MCP when I need library/API documentation, code generation, setup or configuration steps without me having to explicitly ask.

## Testing Standards

- All E2E tests use Playwright with TypeScript
- Test files live in tests/e2e/
- Use `getByRole` as primary locator strategy
- Every PR must include error-state tests, not just happy paths
