127 lines
7.0 KiB
Markdown
127 lines
7.0 KiB
Markdown
# 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.
|