334 lines
11 KiB
Markdown
334 lines
11 KiB
Markdown
# 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
|
|
|
|
```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 <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
|
|
|
|
```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 (
|
|
<div className={styles.root} data-testid="ComponentName">
|
|
ComponentName
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
### 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 <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 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<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:
|
|
|
|
```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 <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 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 <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.
|