Files
main_frontend/CLAUDE.md
T
2026-02-27 23:34:17 +03:00

7.0 KiB

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

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:

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:

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.