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

11 KiB

AGENTS.md — Coffee Project Frontend

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

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 <layer> <ComponentName>  # 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

@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

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 (
		<div className={styles.root} data-testid="ComponentName">
			ComponentName
		</div>
	)
}

Props Interface Template (ComponentName.d.ts)

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

bun run gc <layer> <ComponentName>
# 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 NamesgetUserById not getData
  4. Early Returns — reduce nesting with guard clauses
  5. No Magic Values — use constants: const MAX_ITEMS = 10

TypeScript

// ✅ 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

// ✅ Functional components with explicit return type
export const Button: FC<IButtonProps> = ({ 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 <Name>, 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:

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.

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 TypeScriptstrict: true in tsconfig

Quick Reference

Task Command / Location
Add dependency bun add <package>
Add dev dependency bun add -d <package>
Create component bun run gc <layer> <Name>
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 <package> 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 scriptsbun 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 movingbun run gc feature <Name> 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.