rev 4
This commit is contained in:
@@ -1,336 +1,15 @@
|
||||
# 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.
|
||||
Primary workflow guidance lives in `../AGENTS.md`.
|
||||
|
||||
## Project Overview
|
||||
Use `./CLAUDE.md` as the service-specific source of truth for:
|
||||
|
||||
Next.js 16 application using **Feature-Sliced Design (FSD)** architecture, powered by **Bun** runtime and package manager.
|
||||
- frontend commands
|
||||
- FSD architecture and boundaries
|
||||
- frontend conventions and gotchas
|
||||
|
||||
---
|
||||
OpenCode/Codex notes:
|
||||
|
||||
## 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.
|
||||
- Keep `../AGENTS.md` as the workflow and delegation source of truth.
|
||||
- Treat `CLAUDE.md` as architecture, commands, and conventions only.
|
||||
- Do not rely on `.claude/` directory contents.
|
||||
|
||||
Reference in New Issue
Block a user