new features
This commit is contained in:
@@ -17,7 +17,7 @@ Next.js 16 application using **Feature-Sliced Design (FSD)** architecture, power
|
||||
| Styling | SCSS Modules, normalize.css |
|
||||
| State/Fetch | TanStack React Query 5, Axios, Xior |
|
||||
| Animation | Framer Motion |
|
||||
| Utilities | Lodash, Moment.js, classnames, usehooks-ts |
|
||||
| Utilities | Lodash, date-fns, classnames, usehooks-ts |
|
||||
| Icons | Lucide React, SVGR (custom icons) |
|
||||
| Notifications | React Toastify |
|
||||
| File Upload | React Dropzone |
|
||||
@@ -174,10 +174,12 @@ export const Button: FC<IButtonProps> = ({ variant, onClick }): JSX.Element => {
|
||||
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
|
||||
@@ -208,6 +210,72 @@ Split into separate files **only when**:
|
||||
|
||||
---
|
||||
|
||||
## 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/`
|
||||
@@ -236,6 +304,10 @@ Split into separate files **only when**:
|
||||
| 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
|
||||
@@ -245,3 +317,17 @@ 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.
|
||||
|
||||
Reference in New Issue
Block a user