# AGENTS.md — Coffee Project Frontend Primary Codex reference: [`../.codex/services/frontend.md`](/Users/daniilrakityansky/Documents/Work/Cofee/.codex/services/frontend.md). If this file conflicts with the `.codex` guide, prefer the `.codex` guide. `CLAUDE.md` remains for Claude-specific tooling. ## Project Overview Next.js 16 application using **Feature-Sliced Design (FSD)** architecture, powered by **Bun** runtime and package manager. --- ## Tech Stack | Category | Technology | | ------------- | ------------------------------------------ | | Runtime | Bun 1.3.5 | | Framework | Next.js 16.1.1 (App Router) | | Language | TypeScript 5.9 | | UI Library | React 19 | | Styling | SCSS Modules, normalize.css | | State/Fetch | TanStack React Query 5, Axios, Xior | | Animation | Framer Motion | | Utilities | Lodash, date-fns, classnames, usehooks-ts | | Icons | Lucide React, SVGR (custom icons) | | Notifications | React Toastify | | File Upload | React Dropzone | | Linting | ESLint 9, Prettier, Stylelint | | Testing | Jest, Testing Library | --- ## Commands ```bash bun dev # Start dev server bun run build # Production build bun run start # Start production server bun run lint # Run ESLint + Prettier bun run gc # Generate FSD component bun run gicons # Convert SVGs to React components ``` --- ## Project Structure (FSD) ``` app/ # Next.js App Router entry pages/ # Keep empty (Next.js requires it) public/ # Static assets src/ ├── app/ # App layer: global styles, providers ├── pages/ # Page compositions ├── widgets/ # Large UI blocks (header, sidebar) ├── features/ # User interactions (auth, search) ├── entities/ # Business entities (user, product) └── shared/ # Reusable: UI kit, utils, API, assets ├── ui/ # Button, Input, Icons... ├── api/ # API clients ├── lib/ # Utilities └── assets/ # Images, raw-icons ``` ### Path Aliases ```ts @app/* → ./src/app/* @pages/* → ./src/pages/* @widgets/* → ./src/widgets/* @features/* → ./src/features/* @entities/* → ./src/entities/* @shared/* → ./src/shared/* @/* → ./src/* ``` --- ## Component Structure Each component follows **flat FSD structure** (simplified for MVP): ``` ComponentName/ ├── index.ts # Public API (re-export) ├── ComponentName.tsx # Component + imports ├── ComponentName.module.scss # Styles ├── ComponentName.d.ts # Props interface └── useComponentApi.ts # Optional: hooks/API if needed ``` > **Note:** Old nested structure (`ui/`, `model/`, `api/` folders) has been deprecated. > The backup of the old generator is at `.scripts/create-fsd-component.ts.bak` ### Component Template ```tsx import type { IComponentNameProps } from "./ComponentName.d" import type { JSX } from "react" import { FunctionComponent } from "react" import styles from "./ComponentName.module.scss" export const ComponentName: FunctionComponent< IComponentNameProps > = (): JSX.Element => { return (
ComponentName
) } ``` ### Props Interface Template (ComponentName.d.ts) ```typescript export interface IComponentNameProps { className?: string } ``` ### Generate Component Use one of these commands to generate new project-wide standardized component, don't create new component file by file by yourself ```bash bun run gc # Examples: bun run gc shared Button bun run gc feature AuthForm bun run gc entity UserCard bun run gc page HomePage bun run gc widget Sidebar ``` --- ## Best Practices ### Code Style 1. **Small Functions** — max 20-30 lines; extract helpers 2. **Single Responsibility** — one function = one purpose 3. **Descriptive Names** — `getUserById` not `getData` 4. **Early Returns** — reduce nesting with guard clauses 5. **No Magic Values** — use constants: `const MAX_ITEMS = 10` ### TypeScript ```ts // ✅ Use interfaces for props interface IButtonProps { variant: "primary" | "secondary" disabled?: boolean onClick: () => void } // ✅ Prefer explicit types over `any` // ✅ Use `type` for unions/intersections, `interface` for objects ``` ### React Patterns ```tsx // ✅ Functional components with explicit return type export const Button: FC = ({ variant, onClick }): JSX.Element => { ... } // ✅ Destructure props // ✅ Use data-testid for testing // ✅ Colocate styles with components (CSS Modules) ``` ### FSD Rules 1. **Import Direction** — only downward: `features → entities → shared` 2. **Public API** — export only through `index.ts` 3. **No Cross-Slice Imports** — features cannot import from other features 4. **Shared is Agnostic** — no business logic in shared layer 5. **Features are module-aware** — group features by domain inside module folders (see below) ### When to Split Files Split into separate files **only when**: - Hook/API is reused by multiple components - File exceeds ~200 lines - Props interface is shared across 3+ components ### File Naming | Type | Convention | Example | | --------- | ----------------- | ---------------------- | | Component | PascalCase | `UserCard.tsx` | | Module | PascalCase.module | `UserCard.module.scss` | | Types | PascalCase.d | `UserCard.d.ts` | | Hook | camelCase (use-) | `useAuth.ts` | | Utility | camelCase | `formatDate.ts` | | Constant | UPPER_SNAKE_CASE | `API_ENDPOINTS.ts` | ### Performance 1. Use `React.memo` for expensive renders 2. Use `useMemo` / `useCallback` for derived data and callbacks 3. Lazy load pages/heavy components with `next/dynamic` 4. Prefer server components where possible (Next.js App Router) ### Testing - Place tests next to components: `ComponentName.test.tsx` - Use `data-testid` attributes for queries - Test behavior, not implementation --- ## Features Layer — Module-Aware Structure Features **must be grouped by domain module**. Never place feature folders flat at the top of `src/features/`. ``` src/features/ ├── profile/ # Profile domain module │ ├── index.ts # Barrel export for all features in this module │ ├── AvatarUpload/ │ ├── EditProfileForm/ │ └── LogoutButton/ └── project/ # Project domain module ├── index.ts ├── CreateProjectModal/ └── ... ``` **Rules:** - Each module folder has an `index.ts` barrel that re-exports all its features - Import via the module barrel: `import { AvatarUpload } from "@features/profile"` - When creating a new feature, place it inside the relevant domain folder - After running `bun run gc feature `, move the generated folder into the correct module - Create a new module folder + barrel if the domain doesn't exist yet --- ## Shared Utilities Reusable operations should live in `src/shared/` — **do not inline shared logic inside feature components**. ### File Upload Use `uploadFile()` from `@shared/api/uploadFile` for any file upload: ```ts import { uploadFile } from "@shared/api/uploadFile" const result = await uploadFile(file, "avatars") // result.file_url — URL of the uploaded file // result.file_path — storage path ``` This handles FormData construction, Content-Type header override, and JWT auth automatically. ### Date Formatting Use `date-fns` with Russian locale via shared utilities in `src/shared/lib/dates.ts`. **Never use `moment.js` or inline `Date` formatting in components.** ```ts import { formatDate, formatRelativeTime } from "@shared/lib/dates" formatDate(user.date_joined) // "21.02.2026" (default: "dd.MM.yyyy") formatDate(date, "dd MMM yyyy") // "21 февр. 2026" formatRelativeTime(project.updated_at) // "2 дня назад" ``` Add new date helpers to `src/shared/lib/dates.ts`, not to individual components. ### API Client - **In React components**: use `api.useQuery()` / `api.useMutation()` from `@shared/api` - **Outside React** (utilities, event handlers): use `fetchClient` from `@shared/api` - **File uploads**: use `uploadFile()` from `@shared/api/uploadFile` --- ## Icons Workflow 1. Place raw SVG in `src/shared/assets/raw-icons/` 2. Run `bun run gicons` 3. Import from `@shared/ui/Icons/IconName` --- ## Notes - **Keep `pages/` folder** in root — removing causes Next.js build errors - **Bun only** — use `bun` commands, not npm/yarn - **SCSS Modules** — all styles are scoped via `.module.scss` - **Strict TypeScript** — `strict: true` in tsconfig --- ## Quick Reference | Task | Command / Location | | ------------------ | ------------------------------- | | Add dependency | `bun add ` | | Add dev dependency | `bun add -d ` | | Create component | `bun run gc ` | | Global styles | `src/app/styles/global.scss` | | API client | Use Axios/Xior with React Query | | State management | TanStack Query (server state) | ## Localization All user-facing UI text **must be in Russian**. This includes: labels, headings, buttons, placeholders, tooltips, aria-labels, error messages, breadcrumbs, and any other text visible to the user. The only exception is the brand name "Coffee Project" / "Cofee Project" — it stays in English. ## Implementation sentiments Write less complicated code, simple but readable code Less overhead - better Write all components with html semantics in mind To import classNames lib use `import cs from 'classnames'` Always install packages using `bun install ` To test is project have no errors use `bunx tsc --noEmit` --- ## Common Mistakes to Avoid 1. **Flat features folder** — never place feature component folders directly in `src/features/`. Always group them inside a domain module folder (`profile/`, `project/`, etc.). 2. **Inlining reusable logic** — if an operation (file upload, date formatting, etc.) could be used by multiple features, extract it to `src/shared/`. Features should be thin wrappers around shared utilities. 3. **Wrong StaticLoader import** — it lives at `@shared/ui/Loader`, not `@shared/ui/Loader/StaticLoader`. There is no subdirectory. 4. **multipart/form-data with fetchClient** — the default `fetchClient` sets `Content-Type: application/json`. For file uploads you must override headers and body serializer. Use the shared `uploadFile()` utility instead. 5. **Broken lint scripts** — `bun run lint` calls `lint:es` and `lint:prettier` which are not defined in `package.json`. Use `bunx tsc --noEmit` for type checking until lint is fixed. 6. **Generator output needs moving** — `bun run gc feature ` creates the folder flat in `src/features/`. You must manually move it into the correct domain module folder afterward. 7. **Raw `fetch` / `useEffect` for API calls** — never use plain `fetch` or `useEffect`-based polling for API requests. Always use `api.useQuery()` / `api.useMutation()` from `@shared/api` which wraps TanStack Query + openapi-fetch. For polling, use `refetchInterval`. Raw `fetch` bypasses typed routes, auth middleware, and query caching.