rbin-task-flow 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/coding_standards.mdc +129 -45
- package/package.json +1 -1
|
@@ -21,12 +21,42 @@ src/
|
|
|
21
21
|
└── shared/ # Global reusable code
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
### Allowed folders in shared/ and features/ (Front web & Mobile only)
|
|
25
|
+
|
|
26
|
+
In **front web** (Next.js, React, or other React-based web) or **mobile** (Expo, React Native) projects, only the following folder names may exist directly under `shared/` or under each feature in `features/`:
|
|
27
|
+
|
|
28
|
+
| Folder | Use |
|
|
29
|
+
|---------------|------------------------|
|
|
30
|
+
| `pages/` | Web only — page components |
|
|
31
|
+
| `screens/` | Mobile only — screen components |
|
|
32
|
+
| `components/` | UI components |
|
|
33
|
+
| `constants/` | Constants |
|
|
34
|
+
| `utils/` | Utilities |
|
|
35
|
+
| `validations/` | Validation helpers (e.g. `.validation.ts` for Zod) |
|
|
36
|
+
| `hooks/` | Custom hooks |
|
|
37
|
+
| `providers/` | Context providers |
|
|
38
|
+
| `styles/` | Styles |
|
|
39
|
+
| `types/` | TypeScript types |
|
|
40
|
+
| `libs/` | Library wrappers |
|
|
41
|
+
| `services/` | React Query / API wrappers |
|
|
42
|
+
| `use-cases/` | Pure API / business logic |
|
|
43
|
+
| `schemas/` | Zod (or similar) schemas |
|
|
44
|
+
|
|
45
|
+
**No other folder names** are allowed under `shared/` or under `features/[feature-name]/`. Use only `pages/` for web and `screens/` for mobile; do not mix both in the same project type.
|
|
46
|
+
|
|
47
|
+
### No subfolders in shared/ (Front web & Mobile only)
|
|
48
|
+
|
|
49
|
+
In **front web** (Next.js, React) or **mobile** (Expo, React Native) projects, each folder under `shared/` must be **flat**: no subfolders. Files assume the responsibility through clear naming.
|
|
50
|
+
|
|
51
|
+
- **shared/components/**: One file per component, directly in `shared/components/`. No nested folders (e.g. no `form/`, `inputs/`). Name files so the purpose is clear (e.g. `button.tsx`, `input-text.tsx`, `input-container.tsx`, `input-error.tsx`, `input-label.tsx`).
|
|
52
|
+
- **shared/hooks/**, **shared/utils/**, **shared/validations/**, **shared/constants/**, etc.: Same rule — only files, no subfolders. Use the file name to carry the responsibility. Validation helpers (e.g. `required-email.validation.ts`) live in `shared/validations/`.
|
|
53
|
+
|
|
24
54
|
---
|
|
25
55
|
|
|
26
56
|
## Tech Stack
|
|
27
57
|
|
|
28
|
-
### Front-end (Next.js)
|
|
29
|
-
- **Framework**: Next.js 15+ with App Router
|
|
58
|
+
### Front-end (Next.js / React)
|
|
59
|
+
- **Framework**: Next.js 15+ with App Router, or React (Vite, CRA, or similar). These rules apply to any React-based web project.
|
|
30
60
|
- **Language**: TypeScript (strict mode)
|
|
31
61
|
- **Styling**: Tailwind CSS v4 + `clsx` + `tailwind-merge` via `cn()` helper
|
|
32
62
|
- **UI Components**: shadcn/ui + `lucide-react` icons
|
|
@@ -37,7 +67,7 @@ src/
|
|
|
37
67
|
- **E2E Testing**: Playwright and/or Cypress
|
|
38
68
|
|
|
39
69
|
### Mobile (Expo / React Native)
|
|
40
|
-
- **Framework**: Expo with Expo Router
|
|
70
|
+
- **Framework**: Expo with Expo Router, or React Native (bare or managed). These rules apply to both Expo and React Native.
|
|
41
71
|
- **Language**: TypeScript (strict mode)
|
|
42
72
|
- **Styling**: NativeWind + `clsx` + `tailwind-merge` via `cn()` helper
|
|
43
73
|
- **Data Fetching**: `@tanstack/react-query`
|
|
@@ -61,7 +91,7 @@ src/
|
|
|
61
91
|
|
|
62
92
|
The `app/` directory contains ONLY route definitions. Each file is a thin wrapper that imports and renders the feature page/screen.
|
|
63
93
|
|
|
64
|
-
**Front-end (Next.js App Router):**
|
|
94
|
+
**Front-end (Next.js App Router or React):**
|
|
65
95
|
```typescript
|
|
66
96
|
// src/app/(private)/dashboard/page.tsx
|
|
67
97
|
import { DashboardPage } from '@/features/dashboard/pages/dashboard.page'
|
|
@@ -71,7 +101,7 @@ export default function Dashboard() {
|
|
|
71
101
|
}
|
|
72
102
|
```
|
|
73
103
|
|
|
74
|
-
**Mobile (Expo Router):**
|
|
104
|
+
**Mobile (Expo Router or React Native):**
|
|
75
105
|
```typescript
|
|
76
106
|
// src/app/(private)/dashboard.tsx
|
|
77
107
|
import { DashboardScreen } from '@/features/dashboard/screens/dashboard.screen'
|
|
@@ -249,40 +279,84 @@ export function DashboardPage() {
|
|
|
249
279
|
}
|
|
250
280
|
```
|
|
251
281
|
|
|
252
|
-
###
|
|
282
|
+
### Page-specific component naming (Front web & Mobile)
|
|
253
283
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
-
|
|
284
|
+
When a component is specific to a page (lives under `features/[feature]/components/`), it must use the **page prefix** in the file name and in the function name.
|
|
285
|
+
|
|
286
|
+
**File names (kebab-case):**
|
|
287
|
+
- Page: `[page-name].page.tsx` → e.g. `admin-question.page.tsx`
|
|
288
|
+
- Page-specific components: prefix = page name (no `.page`) → `admin-question-card.tsx`, `admin-question-card-item.tsx`
|
|
289
|
+
- A **child component** of another component stays in the same `components/` folder (same level as parent) and **inherits the parent prefix**: `admin-question-card-item.tsx` (child of `admin-question-card.tsx`). No subfolders.
|
|
290
|
+
|
|
291
|
+
**Function/component names (PascalCase):**
|
|
292
|
+
- Route in `app/`: one word from route + page name → `AdminQuestion` (export default)
|
|
293
|
+
- Page in features: page name + `Page` → `AdminQuestionPage`
|
|
294
|
+
- Components: PascalCase of the file name → `AdminQuestionCard`, `AdminQuestionCardItem`
|
|
295
|
+
|
|
296
|
+
| Location | File | Export name |
|
|
297
|
+
|----------|------|-------------|
|
|
298
|
+
| `app/(private)/admin/question/page.tsx` | (route file) | `AdminQuestion` (default) |
|
|
299
|
+
| `features/admin/question/pages/admin-question.page.tsx` | `admin-question.page.tsx` | `AdminQuestionPage` |
|
|
300
|
+
| `features/admin/question/components/admin-question-card.tsx` | `admin-question-card.tsx` | `AdminQuestionCard` |
|
|
301
|
+
| `features/admin/question/components/admin-question-card-item.tsx` | `admin-question-card-item.tsx` | `AdminQuestionCardItem` |
|
|
258
302
|
|
|
259
303
|
```typescript
|
|
260
|
-
// src/
|
|
261
|
-
import {
|
|
262
|
-
import { Skeleton } from '@/shared/components/skeleton'
|
|
304
|
+
// src/app/(private)/admin/question/page.tsx
|
|
305
|
+
import { AdminQuestionPage } from '@/features/admin/question/pages/admin-question.page'
|
|
263
306
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
isLoading: boolean
|
|
267
|
-
isError: boolean
|
|
307
|
+
export default function AdminQuestion() {
|
|
308
|
+
return <AdminQuestionPage />
|
|
268
309
|
}
|
|
310
|
+
```
|
|
269
311
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
312
|
+
```typescript
|
|
313
|
+
// src/features/admin/question/pages/admin-question.page.tsx
|
|
314
|
+
'use client'
|
|
315
|
+
|
|
316
|
+
import { AdminQuestionCard } from '@/features/admin/question/components/admin-question-card'
|
|
317
|
+
|
|
318
|
+
export function AdminQuestionPage() {
|
|
319
|
+
return (
|
|
320
|
+
<section>
|
|
321
|
+
<AdminQuestionCard />
|
|
322
|
+
</section>
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
```
|
|
273
326
|
|
|
327
|
+
```typescript
|
|
328
|
+
// src/features/admin/question/components/admin-question-card.tsx
|
|
329
|
+
import { AdminQuestionCardItem } from '@/features/admin/question/components/admin-question-card-item'
|
|
330
|
+
|
|
331
|
+
export function AdminQuestionCard() {
|
|
274
332
|
return (
|
|
275
|
-
<
|
|
276
|
-
<
|
|
277
|
-
</
|
|
333
|
+
<ul>
|
|
334
|
+
<AdminQuestionCardItem />
|
|
335
|
+
</ul>
|
|
278
336
|
)
|
|
279
337
|
}
|
|
280
338
|
```
|
|
281
339
|
|
|
340
|
+
```typescript
|
|
341
|
+
// src/features/admin/question/components/admin-question-card-item.tsx
|
|
342
|
+
export function AdminQuestionCardItem() {
|
|
343
|
+
return <li>...</li>
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Components — Single Responsibility
|
|
348
|
+
|
|
349
|
+
Each component has a single responsibility. Components:
|
|
350
|
+
- Receive data via props (no direct service calls unless truly necessary)
|
|
351
|
+
- Follow the **page prefix** naming above when they are page-specific (inside a feature)
|
|
352
|
+
- Stay small and focused; child components stay in the same `components/` folder with the inherited prefix in the name
|
|
353
|
+
|
|
282
354
|
---
|
|
283
355
|
|
|
284
356
|
## shared/ — Global Reusable Code
|
|
285
357
|
|
|
358
|
+
In front web (Next.js, React) and mobile (Expo, React Native), `shared/` is flat per folder (no subfolders). One file per concern; naming carries the responsibility.
|
|
359
|
+
|
|
286
360
|
```
|
|
287
361
|
shared/
|
|
288
362
|
├── components/
|
|
@@ -291,14 +365,11 @@ shared/
|
|
|
291
365
|
│ ├── data-handler.tsx
|
|
292
366
|
│ ├── skeleton.tsx
|
|
293
367
|
│ ├── dialog.tsx
|
|
294
|
-
│
|
|
295
|
-
│
|
|
296
|
-
│
|
|
297
|
-
│
|
|
298
|
-
│
|
|
299
|
-
│ ├── input-container.tsx
|
|
300
|
-
│ ├── input-error.tsx
|
|
301
|
-
│ └── input-label.tsx
|
|
368
|
+
│ ├── input-text.tsx
|
|
369
|
+
│ ├── input-select.tsx
|
|
370
|
+
│ ├── input-container.tsx
|
|
371
|
+
│ ├── input-error.tsx
|
|
372
|
+
│ └── input-label.tsx
|
|
302
373
|
├── hooks/
|
|
303
374
|
│ ├── dialog.hook.tsx
|
|
304
375
|
│ └── drawer.hook.tsx
|
|
@@ -312,11 +383,11 @@ shared/
|
|
|
312
383
|
│ └── shared.ui.type.ts
|
|
313
384
|
├── constants/
|
|
314
385
|
│ └── server-routes.constants.ts
|
|
386
|
+
├── validations/
|
|
387
|
+
│ ├── required-email.validation.ts
|
|
388
|
+
│ ├── required-string.validation.ts
|
|
389
|
+
│ └── required-phone.validation.ts
|
|
315
390
|
└── utils/
|
|
316
|
-
├── validation/
|
|
317
|
-
│ ├── required-email.validation.ts
|
|
318
|
-
│ ├── required-string.validation.ts
|
|
319
|
-
│ └── required-phone.validation.ts
|
|
320
391
|
└── error.util.ts
|
|
321
392
|
```
|
|
322
393
|
|
|
@@ -516,8 +587,8 @@ export type ServiceInput<TData = unknown> = {
|
|
|
516
587
|
```typescript
|
|
517
588
|
// src/features/auth/schemas/login.schema.ts
|
|
518
589
|
import { z } from 'zod'
|
|
519
|
-
import { requiredEmail } from '@/shared/
|
|
520
|
-
import { requiredString } from '@/shared/
|
|
590
|
+
import { requiredEmail } from '@/shared/validations/required-email.validation'
|
|
591
|
+
import { requiredString } from '@/shared/validations/required-string.validation'
|
|
521
592
|
|
|
522
593
|
export const loginSchema = z.object({
|
|
523
594
|
email: requiredEmail(),
|
|
@@ -529,7 +600,7 @@ export type LoginSchema = z.infer<typeof loginSchema>
|
|
|
529
600
|
|
|
530
601
|
**Reusable validators in shared/:**
|
|
531
602
|
```typescript
|
|
532
|
-
// src/shared/
|
|
603
|
+
// src/shared/validations/required-string.validation.ts
|
|
533
604
|
import { z } from 'zod'
|
|
534
605
|
|
|
535
606
|
export const requiredString = ({ field, min = 1 }: { field: string; min?: number }) =>
|
|
@@ -547,7 +618,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
|
|
|
547
618
|
import { useForm } from 'react-hook-form'
|
|
548
619
|
import { LoginSchema, loginSchema } from '@/features/auth/schemas/login.schema'
|
|
549
620
|
import { SessionCreateService } from '@/features/auth/services/session-create.service'
|
|
550
|
-
import { InputText } from '@/shared/components/
|
|
621
|
+
import { InputText } from '@/shared/components/input-text'
|
|
551
622
|
import { Button } from '@/shared/components/button'
|
|
552
623
|
|
|
553
624
|
export function LoginForm() {
|
|
@@ -572,7 +643,7 @@ export function LoginForm() {
|
|
|
572
643
|
|
|
573
644
|
**Input uses `Controller` from RHF and is generic:**
|
|
574
645
|
```typescript
|
|
575
|
-
// src/shared/components/
|
|
646
|
+
// src/shared/components/input-text.tsx
|
|
576
647
|
import { Control, Controller, FieldValues, Path, PathValue } from 'react-hook-form'
|
|
577
648
|
import { cn } from '@/shared/libs/tw-merge'
|
|
578
649
|
|
|
@@ -616,13 +687,24 @@ export function InputText<T extends FieldValues>({
|
|
|
616
687
|
|
|
617
688
|
## File Naming Conventions
|
|
618
689
|
|
|
619
|
-
|
|
690
|
+
### Rule for front (web & mobile): outside `app/` only
|
|
691
|
+
|
|
692
|
+
Every file outside the `app/` folder must follow:
|
|
693
|
+
|
|
694
|
+
- **Format**: lowercase, words separated by `-`, then `.tipo.ext` (suffix by role).
|
|
695
|
+
- **Examples**: `pagina-exemplo.page.tsx`, `login.schema.ts`, `session-create.use-case.ts`, `dashboard-active.type.ts`.
|
|
696
|
+
|
|
697
|
+
**Components are the exception**: do **not** use a `.component` suffix. Use only kebab-case + extension, e.g. `pagina-exemplo.tsx`, `dashboard-revenue-card.tsx`, `input-text.tsx`.
|
|
698
|
+
|
|
699
|
+
**Another exception**: files inside any `styles/` folder do not need to follow this pattern (e.g. CSS/SCSS modules may use their own convention).
|
|
700
|
+
|
|
701
|
+
### Suffix by role
|
|
620
702
|
|
|
621
703
|
| Role | Suffix | Example |
|
|
622
704
|
|------|--------|---------|
|
|
623
|
-
| Page component | `.page.tsx` | `
|
|
705
|
+
| Page component | `.page.tsx` | `pagina-exemplo.page.tsx` |
|
|
624
706
|
| Screen component (mobile) | `.screen.tsx` | `login.screen.tsx` |
|
|
625
|
-
| UI component | `.tsx` | `dashboard-revenue-card.tsx` |
|
|
707
|
+
| UI component | (no suffix) `.tsx` | `dashboard-revenue-card.tsx`, `input-text.tsx` |
|
|
626
708
|
| React Query service | `.service.tsx` | `session-create.service.tsx` |
|
|
627
709
|
| API call (pure) | `.use-case.ts` | `session-create.use-case.ts` |
|
|
628
710
|
| Zod schema | `.schema.ts` | `login.schema.ts` |
|
|
@@ -630,7 +712,7 @@ Use kebab-case for all filenames. Suffix reflects the file's role:
|
|
|
630
712
|
| API response type | `.api.type.ts` | `session.api.type.ts` |
|
|
631
713
|
| Domain type | `.type.ts` | `dashboard-active.type.ts` |
|
|
632
714
|
| Utility | `.util.ts` | `error.util.ts` |
|
|
633
|
-
| Validation helper | `.validation.ts` | `required-email.validation.ts` |
|
|
715
|
+
| Validation helper | `.validation.ts` | `required-email.validation.ts` (in `validations/`) |
|
|
634
716
|
| Constants | `.constants.ts` | `server-routes.constants.ts` |
|
|
635
717
|
|
|
636
718
|
---
|
|
@@ -684,7 +766,7 @@ export const useAuth = () => useContext(AuthContext)
|
|
|
684
766
|
|
|
685
767
|
## Testing
|
|
686
768
|
|
|
687
|
-
### Front-end (Next.js)
|
|
769
|
+
### Front-end (Next.js / React)
|
|
688
770
|
- **E2E**: Playwright and/or Cypress
|
|
689
771
|
- Tests live in `e2e/` or `cypress/` at project root
|
|
690
772
|
- Test files: `[feature].spec.ts` (Playwright) or `[feature].cy.ts` (Cypress)
|
|
@@ -710,3 +792,5 @@ export const useAuth = () => useContext(AuthContext)
|
|
|
710
792
|
8. **Type everything**: No `any`. All props, params, and return values must be typed.
|
|
711
793
|
9. **Shared is truly shared**: Only put in `shared/` what is used by 2+ features.
|
|
712
794
|
10. **Naming reflects role**: File name + suffix must make the file's purpose immediately obvious.
|
|
795
|
+
11. **No barrel files for re-exports**: Do not use `index.ts` (or `index.tsx`) only to re-export other modules. Import directly from the source file (e.g. `from '@/shared/components/button'` not `from '@/shared/components'`). An `index.ts` is only allowed when it contains real logic or composition, not mere re-exports.
|
|
796
|
+
12. **Page-specific components use page prefix**: In features, the page file is `[page-name].page.tsx`; components specific to that page use the same prefix in file and function name (e.g. `admin-question-card.tsx` → `AdminQuestionCard`). Child components stay in the same `components/` folder and inherit the parent prefix (e.g. `admin-question-card-item.tsx` → `AdminQuestionCardItem`). App route exports `AdminQuestion`, feature page exports `AdminQuestionPage`.
|