@famgia/omnify-typescript 0.0.66 → 0.0.68
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/ai-guides/react-form-guide.md +259 -0
- package/ai-guides/typescript-guide.md +53 -0
- package/dist/{chunk-4L77AHAC.js → chunk-6I4O23X6.js} +521 -66
- package/dist/chunk-6I4O23X6.js.map +1 -0
- package/dist/index.cjs +761 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +138 -2
- package/dist/index.d.ts +138 -2
- package/dist/index.js +227 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin.cjs +624 -75
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +6 -0
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.js +96 -11
- package/dist/plugin.js.map +1 -1
- package/package.json +4 -3
- package/scripts/postinstall.js +29 -40
- package/stubs/JapaneseAddressField.tsx.stub +289 -0
- package/stubs/JapaneseBankField.tsx.stub +212 -0
- package/stubs/JapaneseNameField.tsx.stub +194 -0
- package/stubs/ai-guides/checklists/react.md.stub +108 -0
- package/stubs/ai-guides/cursor/react-design.mdc.stub +289 -0
- package/stubs/ai-guides/cursor/react-form.mdc.stub +277 -0
- package/stubs/ai-guides/cursor/react-services.mdc.stub +304 -0
- package/stubs/ai-guides/cursor/react.mdc.stub +305 -0
- package/stubs/ai-guides/react/README.md.stub +221 -0
- package/stubs/ai-guides/react/antd-guide.md.stub +294 -0
- package/stubs/ai-guides/react/checklist.md.stub +108 -0
- package/stubs/ai-guides/react/datetime-guide.md.stub +137 -0
- package/stubs/ai-guides/react/design-philosophy.md.stub +363 -0
- package/stubs/ai-guides/react/i18n-guide.md.stub +211 -0
- package/stubs/ai-guides/react/laravel-integration.md.stub +181 -0
- package/stubs/ai-guides/react/service-pattern.md.stub +180 -0
- package/stubs/ai-guides/react/tanstack-query.md.stub +339 -0
- package/stubs/ai-guides/react/types-guide.md.stub +524 -0
- package/stubs/components-index.ts.stub +13 -0
- package/stubs/form-validation.ts.stub +106 -0
- package/stubs/rules/index.ts.stub +48 -0
- package/stubs/rules/kana.ts.stub +291 -0
- package/stubs/use-form-mutation.ts.stub +117 -0
- package/stubs/zod-i18n.ts.stub +32 -0
- package/dist/chunk-4L77AHAC.js.map +0 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Frontend Architecture Guide
|
|
2
|
+
|
|
3
|
+
> **Related docs:**
|
|
4
|
+
> - [Design Philosophy](./design-philosophy.md) ⭐ **Start here** - Architecture, principles
|
|
5
|
+
> - [Types Guide](./types-guide.md) - Where to define types
|
|
6
|
+
> - [Service Pattern](./service-pattern.md) - API services
|
|
7
|
+
> - [TanStack Query](./tanstack-query.md) - Data fetching
|
|
8
|
+
> - [Ant Design](./antd-guide.md) - UI components
|
|
9
|
+
> - [i18n](./i18n-guide.md) - Multi-language
|
|
10
|
+
> - [DateTime](./datetime-guide.md) - Day.js, UTC handling
|
|
11
|
+
> - [Laravel Integration](./laravel-integration.md) - Backend integration
|
|
12
|
+
> - [Checklists](./checklist.md) - Before commit, new resource
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
See [Design Philosophy](./design-philosophy.md) for architecture diagram and principles.
|
|
17
|
+
|
|
18
|
+
**Stack**: Next.js 16 + TypeScript + Ant Design 6 + TanStack Query + Axios
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Directory Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
frontend/src/
|
|
26
|
+
├── app/ # Next.js App Router (Pages)
|
|
27
|
+
│ ├── layout.tsx # Root: Providers wrapper
|
|
28
|
+
│ ├── page.tsx # Public: Home page
|
|
29
|
+
│ │
|
|
30
|
+
│ ├── (auth)/ # Group: Auth pages (no layout)
|
|
31
|
+
│ │ ├── login/page.tsx
|
|
32
|
+
│ │ └── register/page.tsx
|
|
33
|
+
│ │
|
|
34
|
+
│ └── (dashboard)/ # Group: Protected pages
|
|
35
|
+
│ ├── layout.tsx # Shared: Sidebar + Header
|
|
36
|
+
│ ├── page.tsx # /dashboard
|
|
37
|
+
│ └── users/ # Resource: Users
|
|
38
|
+
│ ├── page.tsx # GET /users (List)
|
|
39
|
+
│ ├── new/page.tsx # POST /users (Create)
|
|
40
|
+
│ └── [id]/
|
|
41
|
+
│ ├── page.tsx # GET /users/:id (Show)
|
|
42
|
+
│ └── edit/page.tsx # PUT /users/:id (Edit)
|
|
43
|
+
│
|
|
44
|
+
├── features/ # Feature-specific components & hooks
|
|
45
|
+
│ ├── users/ # User feature
|
|
46
|
+
│ │ ├── UserTable.tsx # Only used in users feature
|
|
47
|
+
│ │ ├── UserForm.tsx
|
|
48
|
+
│ │ └── useUserFilters.ts # Feature-specific hook
|
|
49
|
+
│ └── posts/
|
|
50
|
+
│ ├── PostCard.tsx
|
|
51
|
+
│ └── PostEditor.tsx
|
|
52
|
+
│
|
|
53
|
+
├── components/ # SHARED components (2+ features)
|
|
54
|
+
│ ├── layouts/ # Layout wrappers
|
|
55
|
+
│ │ ├── DashboardLayout.tsx
|
|
56
|
+
│ │ └── AuthLayout.tsx
|
|
57
|
+
│ └── common/ # Reusable UI
|
|
58
|
+
│ ├── DataTable.tsx # Generic table
|
|
59
|
+
│ └── PageHeader.tsx
|
|
60
|
+
│
|
|
61
|
+
├── services/ # API Service Layer (ALWAYS here)
|
|
62
|
+
│ ├── auth.ts # POST /login, /logout, /register
|
|
63
|
+
│ └── users.ts # CRUD /api/users
|
|
64
|
+
│
|
|
65
|
+
├── hooks/ # SHARED hooks (2+ features)
|
|
66
|
+
│ ├── useAuth.ts # App-wide auth
|
|
67
|
+
│ └── useDebounce.ts # Utility hook
|
|
68
|
+
│
|
|
69
|
+
├── lib/ # Core Infrastructure
|
|
70
|
+
│ ├── api.ts # Axios instance + interceptors
|
|
71
|
+
│ ├── query.tsx # QueryClient provider
|
|
72
|
+
│ ├── queryKeys.ts # Query key factory
|
|
73
|
+
│ └── dayjs.ts # Day.js config + utilities
|
|
74
|
+
│
|
|
75
|
+
├── i18n/ # Internationalization
|
|
76
|
+
│ ├── config.ts # Locales config
|
|
77
|
+
│ ├── request.ts # Server-side locale detection
|
|
78
|
+
│ └── messages/ # Translation files
|
|
79
|
+
│ ├── ja.json
|
|
80
|
+
│ ├── en.json
|
|
81
|
+
│ └── vi.json
|
|
82
|
+
│
|
|
83
|
+
└── types/ # TypeScript Types
|
|
84
|
+
└── model/ # Omnify auto-generated types
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## When to Use Which Folder
|
|
90
|
+
|
|
91
|
+
### Decision Rules
|
|
92
|
+
|
|
93
|
+
| Question | Answer | Location |
|
|
94
|
+
| ------------------------------------ | ----------- | --------------------- |
|
|
95
|
+
| Component used in how many features? | 1 feature | `features/{feature}/` |
|
|
96
|
+
| Component used in how many features? | 2+ features | `components/common/` |
|
|
97
|
+
| Is it a layout wrapper? | Yes | `components/layouts/` |
|
|
98
|
+
| Is it a service (API calls)? | Yes | `services/` (ALWAYS) |
|
|
99
|
+
| Hook used in how many features? | 1 feature | `features/{feature}/` |
|
|
100
|
+
| Hook used in how many features? | 2+ features | `hooks/` |
|
|
101
|
+
|
|
102
|
+
### Flowchart
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
┌─────────────────────────────────────────────────────────┐
|
|
106
|
+
│ NEW COMPONENT │
|
|
107
|
+
└─────────────────────┬───────────────────────────────────┘
|
|
108
|
+
│
|
|
109
|
+
Used in how many features?
|
|
110
|
+
│
|
|
111
|
+
┌─────────────┴─────────────┐
|
|
112
|
+
│ │
|
|
113
|
+
1 feature 2+ features
|
|
114
|
+
│ │
|
|
115
|
+
▼ ▼
|
|
116
|
+
features/{feature}/ components/common/
|
|
117
|
+
UserTable.tsx DataTable.tsx
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
┌─────────────────────────────────────────────────────────┐
|
|
122
|
+
│ NEW HOOK │
|
|
123
|
+
└─────────────────────┬───────────────────────────────────┘
|
|
124
|
+
│
|
|
125
|
+
Used in how many features?
|
|
126
|
+
│
|
|
127
|
+
┌─────────────┴─────────────┐
|
|
128
|
+
│ │
|
|
129
|
+
1 feature 2+ features
|
|
130
|
+
│ │
|
|
131
|
+
▼ ▼
|
|
132
|
+
features/{feature}/ hooks/
|
|
133
|
+
useUserFilters.ts useDebounce.ts
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
┌─────────────────────────────────────────────────────────┐
|
|
138
|
+
│ NEW SERVICE │
|
|
139
|
+
└─────────────────────┬───────────────────────────────────┘
|
|
140
|
+
│
|
|
141
|
+
ALWAYS
|
|
142
|
+
│
|
|
143
|
+
▼
|
|
144
|
+
services/
|
|
145
|
+
users.ts
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Examples
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// ❌ WRONG: Service in features folder
|
|
152
|
+
features/users/services/users.ts
|
|
153
|
+
|
|
154
|
+
// ✅ CORRECT: Service always centralized
|
|
155
|
+
services/users.ts
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// ❌ WRONG: Feature-specific component in components/
|
|
160
|
+
components/users/UserTable.tsx // Only used in users feature
|
|
161
|
+
|
|
162
|
+
// ✅ CORRECT: Feature-specific in features/
|
|
163
|
+
features/users/UserTable.tsx
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
// ❌ WRONG: Shared component in features/
|
|
168
|
+
features/users/DataTable.tsx // Used in users AND posts
|
|
169
|
+
|
|
170
|
+
// ✅ CORRECT: Shared in components/
|
|
171
|
+
components/common/DataTable.tsx
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Naming Conventions
|
|
177
|
+
|
|
178
|
+
### Files
|
|
179
|
+
|
|
180
|
+
| Type | Pattern | Example |
|
|
181
|
+
| --------- | ------------------------ | ------------------------------- |
|
|
182
|
+
| Component | PascalCase | `UserForm.tsx`, `DataTable.tsx` |
|
|
183
|
+
| Hook | camelCase + `use` prefix | `useAuth.ts`, `useUsers.ts` |
|
|
184
|
+
| Service | camelCase | `users.ts`, `auth.ts` |
|
|
185
|
+
| Utility | camelCase | `utils.ts`, `formatters.ts` |
|
|
186
|
+
| Type | camelCase or PascalCase | `types.ts`, `User.ts` |
|
|
187
|
+
| Page | lowercase | `page.tsx`, `layout.tsx` |
|
|
188
|
+
|
|
189
|
+
### Code
|
|
190
|
+
|
|
191
|
+
| Type | Pattern | Example |
|
|
192
|
+
| -------------- | --------------------- | --------------------------- |
|
|
193
|
+
| Component | PascalCase | `function UserForm()` |
|
|
194
|
+
| Hook | camelCase + `use` | `function useAuth()` |
|
|
195
|
+
| Service object | camelCase + `Service` | `const userService = {}` |
|
|
196
|
+
| Interface | PascalCase | `interface User` |
|
|
197
|
+
| Type | PascalCase | `type UserFormData` |
|
|
198
|
+
| Constant | UPPER_SNAKE_CASE | `const API_TIMEOUT = 30000` |
|
|
199
|
+
| Function | camelCase | `function formatDate()` |
|
|
200
|
+
| Variable | camelCase | `const userData = ...` |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Types
|
|
205
|
+
|
|
206
|
+
See [Types Guide](./types-guide.md) for complete type definition rules.
|
|
207
|
+
|
|
208
|
+
**Quick reference:**
|
|
209
|
+
|
|
210
|
+
| Type | Location |
|
|
211
|
+
| ------------------ | --------------------------------------- |
|
|
212
|
+
| Model (User, Post) | `@/types/model` (Omnify auto-generated) |
|
|
213
|
+
| Input types | Service file (colocated) |
|
|
214
|
+
| Props | Component file |
|
|
215
|
+
| API Response | `lib/api.ts` |
|
|
216
|
+
|
|
217
|
+
**Omnify files:**
|
|
218
|
+
- `base/`, `rules/`, `enum/` → ❌ DO NOT EDIT
|
|
219
|
+
- `User.ts` (root level) → ✅ CAN EDIT (extension)
|
|
220
|
+
|
|
221
|
+
See also: [Omnify TypeScript Guide](../omnify/typescript-guide.md)
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Ant Design Guide
|
|
2
|
+
|
|
3
|
+
> **Related:** [README](./README.md) | [i18n](./i18n-guide.md)
|
|
4
|
+
|
|
5
|
+
## ⚠️ IMPORTANT: Use Ant Design First
|
|
6
|
+
|
|
7
|
+
**ALWAYS check if Ant Design has a component before creating your own.**
|
|
8
|
+
|
|
9
|
+
Ant Design provides 60+ components: https://ant.design/components/overview
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// ✅ DO: Use Ant Design components
|
|
13
|
+
import { Table, Form, Input, Button, Modal, Card, Descriptions } from "antd";
|
|
14
|
+
|
|
15
|
+
// ❌ DON'T: Create custom components that Ant Design already has
|
|
16
|
+
// DON'T create: CustomTable, CustomModal, CustomForm, CustomButton
|
|
17
|
+
// DON'T create: DataGrid, Popup, FormInput
|
|
18
|
+
|
|
19
|
+
// ✅ DO: Extend Ant Design if needed
|
|
20
|
+
function UserTable(props: { users: User[] }) {
|
|
21
|
+
return <Table dataSource={props.users} columns={...} />; // Wraps AntD Table
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ❌ DON'T: Build from scratch
|
|
25
|
+
function UserTable(props: { users: User[] }) {
|
|
26
|
+
return <table><tbody>{users.map(...)}</tbody></table>; // WRONG!
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## ⚠️ No New Libraries Without Permission
|
|
33
|
+
|
|
34
|
+
**DO NOT install new npm packages without explicit user approval.**
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# ❌ DON'T: Install without asking
|
|
38
|
+
npm install lodash
|
|
39
|
+
npm install moment
|
|
40
|
+
npm install react-table
|
|
41
|
+
|
|
42
|
+
# ✅ DO: Ask first
|
|
43
|
+
"Do you want to install library X for Y?"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Already installed libraries (use these):**
|
|
47
|
+
- UI: `antd`, `@ant-design/icons`
|
|
48
|
+
- HTTP: `axios`
|
|
49
|
+
- State: `@tanstack/react-query`
|
|
50
|
+
- Styling: `tailwindcss`
|
|
51
|
+
- i18n: `next-intl`
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## When to Create a Component
|
|
56
|
+
|
|
57
|
+
| Create Component | Don't Create |
|
|
58
|
+
| ------------------------------- | ----------------------------- |
|
|
59
|
+
| Used in 2+ places | Used only once |
|
|
60
|
+
| Has own state/logic (>50 lines) | Simple markup (<30 lines) |
|
|
61
|
+
| Needs unit testing | Trivial display |
|
|
62
|
+
| Complex props interface | Few inline props |
|
|
63
|
+
| **Ant Design doesn't have it** | **Ant Design already has it** |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Container vs Presentational
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// CONTAINER COMPONENT (Smart) - pages or complex components
|
|
72
|
+
// - Fetches data
|
|
73
|
+
// - Handles mutations
|
|
74
|
+
// - Contains business logic
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
// app/(dashboard)/users/page.tsx
|
|
78
|
+
"use client";
|
|
79
|
+
|
|
80
|
+
import { useState } from "react";
|
|
81
|
+
import { useQuery } from "@tanstack/react-query";
|
|
82
|
+
import { userService, UserListParams } from "@/services/users";
|
|
83
|
+
import { queryKeys } from "@/lib/queryKeys";
|
|
84
|
+
import { UserTable } from "@/components/tables/UserTable";
|
|
85
|
+
|
|
86
|
+
export default function UsersPage() {
|
|
87
|
+
const [filters, setFilters] = useState<UserListParams>({ page: 1 });
|
|
88
|
+
|
|
89
|
+
const { data, isLoading } = useQuery({
|
|
90
|
+
queryKey: queryKeys.users.list(filters),
|
|
91
|
+
queryFn: () => userService.list(filters),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<UserTable
|
|
96
|
+
users={data?.data ?? []}
|
|
97
|
+
loading={isLoading}
|
|
98
|
+
pagination={data?.meta}
|
|
99
|
+
onPageChange={(page) => setFilters({ ...filters, page })}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// PRESENTATIONAL COMPONENT (Dumb) - reusable UI
|
|
106
|
+
// - Receives data via props
|
|
107
|
+
// - No data fetching
|
|
108
|
+
// - No business logic
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
// components/tables/UserTable.tsx
|
|
112
|
+
import { Table } from "antd";
|
|
113
|
+
import type { User } from "@/types/model";
|
|
114
|
+
import type { PaginatedResponse } from "@/lib/api";
|
|
115
|
+
|
|
116
|
+
interface UserTableProps {
|
|
117
|
+
users: User[];
|
|
118
|
+
loading: boolean;
|
|
119
|
+
pagination?: PaginatedResponse<User>["meta"];
|
|
120
|
+
onPageChange: (page: number) => void;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function UserTable({ users, loading, pagination, onPageChange }: UserTableProps) {
|
|
124
|
+
return (
|
|
125
|
+
<Table
|
|
126
|
+
dataSource={users}
|
|
127
|
+
loading={loading}
|
|
128
|
+
rowKey="id"
|
|
129
|
+
pagination={{
|
|
130
|
+
current: pagination?.current_page,
|
|
131
|
+
total: pagination?.total,
|
|
132
|
+
onChange: onPageChange,
|
|
133
|
+
}}
|
|
134
|
+
columns={[
|
|
135
|
+
{ title: "ID", dataIndex: "id" },
|
|
136
|
+
{ title: "Name", dataIndex: "name" },
|
|
137
|
+
{ title: "Email", dataIndex: "email" },
|
|
138
|
+
]}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Form Pattern with Laravel Validation
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
"use client";
|
|
150
|
+
|
|
151
|
+
import { Form, Input, Button, message } from "antd";
|
|
152
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
153
|
+
import { useTranslations } from "next-intl";
|
|
154
|
+
import { getFormErrors } from "@/lib/api";
|
|
155
|
+
import { queryKeys } from "@/lib/queryKeys";
|
|
156
|
+
import { userService } from "@/services/users";
|
|
157
|
+
|
|
158
|
+
export default function UserForm() {
|
|
159
|
+
const t = useTranslations();
|
|
160
|
+
const [form] = Form.useForm();
|
|
161
|
+
const queryClient = useQueryClient();
|
|
162
|
+
|
|
163
|
+
const mutation = useMutation({
|
|
164
|
+
mutationFn: userService.create,
|
|
165
|
+
onSuccess: () => {
|
|
166
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.users.all });
|
|
167
|
+
message.success(t("messages.created"));
|
|
168
|
+
form.resetFields();
|
|
169
|
+
},
|
|
170
|
+
onError: (error) => {
|
|
171
|
+
// This maps Laravel's 422 { errors: { email: ["Already exists"] } }
|
|
172
|
+
// to Ant Design's form.setFields format
|
|
173
|
+
form.setFields(getFormErrors(error));
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<Form
|
|
179
|
+
form={form}
|
|
180
|
+
layout="vertical"
|
|
181
|
+
onFinish={(values) => mutation.mutate(values)}
|
|
182
|
+
>
|
|
183
|
+
<Form.Item
|
|
184
|
+
name="name"
|
|
185
|
+
label={t("common.name")}
|
|
186
|
+
rules={[{ required: true }]}
|
|
187
|
+
>
|
|
188
|
+
<Input />
|
|
189
|
+
</Form.Item>
|
|
190
|
+
|
|
191
|
+
<Form.Item
|
|
192
|
+
name="email"
|
|
193
|
+
label={t("auth.email")}
|
|
194
|
+
rules={[{ required: true }, { type: "email" }]}
|
|
195
|
+
>
|
|
196
|
+
<Input />
|
|
197
|
+
</Form.Item>
|
|
198
|
+
|
|
199
|
+
<Form.Item>
|
|
200
|
+
<Button
|
|
201
|
+
type="primary"
|
|
202
|
+
htmlType="submit"
|
|
203
|
+
loading={mutation.isPending}
|
|
204
|
+
>
|
|
205
|
+
{t("common.save")}
|
|
206
|
+
</Button>
|
|
207
|
+
</Form.Item>
|
|
208
|
+
</Form>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Deprecated Props (v6+)
|
|
216
|
+
|
|
217
|
+
> ⚠️ **CRITICAL**: Always use the latest prop names. Using deprecated props will show console warnings.
|
|
218
|
+
|
|
219
|
+
| Component | Deprecated | Use Instead |
|
|
220
|
+
| ------------ | -------------------------- | ----------------------- |
|
|
221
|
+
| Space | `direction` | `orientation` |
|
|
222
|
+
| Modal | `visible` | `open` |
|
|
223
|
+
| Drawer | `visible` | `open` |
|
|
224
|
+
| Dropdown | `visible` | `open` |
|
|
225
|
+
| Tooltip | `visible` | `open` |
|
|
226
|
+
| Popover | `visible` | `open` |
|
|
227
|
+
| Popconfirm | `visible` | `open` |
|
|
228
|
+
| Select | `dropdownMatchSelectWidth` | `popupMatchSelectWidth` |
|
|
229
|
+
| TreeSelect | `dropdownMatchSelectWidth` | `popupMatchSelectWidth` |
|
|
230
|
+
| Cascader | `dropdownMatchSelectWidth` | `popupMatchSelectWidth` |
|
|
231
|
+
| AutoComplete | `dropdownMatchSelectWidth` | `popupMatchSelectWidth` |
|
|
232
|
+
| Table | `filterDropdownVisible` | `filterDropdownOpen` |
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// ❌ DON'T: Use deprecated props
|
|
236
|
+
<Space direction="vertical">
|
|
237
|
+
<Modal visible={isOpen}>
|
|
238
|
+
<Dropdown visible={show}>
|
|
239
|
+
|
|
240
|
+
// ✅ DO: Use new props
|
|
241
|
+
<Space orientation="vertical">
|
|
242
|
+
<Modal open={isOpen}>
|
|
243
|
+
<Dropdown open={show}>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Anti-Patterns
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// ❌ Creating components that Ant Design already has
|
|
252
|
+
function CustomButton({ children }) { ... } // Use <Button> from antd
|
|
253
|
+
function CustomModal({ visible }) { ... } // Use <Modal> from antd
|
|
254
|
+
function CustomTable({ data }) { ... } // Use <Table> from antd
|
|
255
|
+
function DataGrid({ rows }) { ... } // Use <Table> from antd
|
|
256
|
+
|
|
257
|
+
// ❌ Installing libraries without permission
|
|
258
|
+
npm install lodash // Ask first!
|
|
259
|
+
npm install react-icons // Use @ant-design/icons
|
|
260
|
+
npm install styled-components // Use Tailwind CSS
|
|
261
|
+
|
|
262
|
+
// ❌ API call in component (bypass service layer)
|
|
263
|
+
function UserList() {
|
|
264
|
+
const { data } = useQuery({
|
|
265
|
+
queryKey: ["users"],
|
|
266
|
+
queryFn: () => axios.get("/api/users"), // WRONG: Use service
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ❌ Business logic in component
|
|
271
|
+
function UserList() {
|
|
272
|
+
const users = data?.filter(u => u.active).sort((a, b) => a.name > b.name);
|
|
273
|
+
// Move to service or utility function
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ❌ Hardcoded strings - use i18n
|
|
277
|
+
<Button>Save</Button> // WRONG
|
|
278
|
+
<Button>{t("common.save")}</Button> // CORRECT
|
|
279
|
+
|
|
280
|
+
// ❌ Multiple sources of truth
|
|
281
|
+
const [users, setUsers] = useState([]); // Local state
|
|
282
|
+
const { data } = useQuery({ ... }); // Server state
|
|
283
|
+
// Pick one: TanStack Query for server data
|
|
284
|
+
|
|
285
|
+
// ❌ Prop drilling
|
|
286
|
+
<Parent data={data}>
|
|
287
|
+
<Child data={data}>
|
|
288
|
+
<GrandChild data={data} /> // Use Context or pass minimal props
|
|
289
|
+
</Child>
|
|
290
|
+
</Parent>
|
|
291
|
+
|
|
292
|
+
// ❌ Giant components (>200 lines)
|
|
293
|
+
// Split into smaller components or extract hooks
|
|
294
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Checklists
|
|
2
|
+
|
|
3
|
+
> **Related:** [README](./README.md)
|
|
4
|
+
|
|
5
|
+
## After Writing Code
|
|
6
|
+
|
|
7
|
+
> **IMPORTANT**: Always run these commands after writing/modifying code:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 1. Type check
|
|
11
|
+
npm run typecheck
|
|
12
|
+
|
|
13
|
+
# 2. Lint check
|
|
14
|
+
npm run lint
|
|
15
|
+
|
|
16
|
+
# Or combined
|
|
17
|
+
npm run typecheck && npm run lint
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Adding New Resource
|
|
23
|
+
|
|
24
|
+
When adding a new resource (e.g., `posts`), follow these steps:
|
|
25
|
+
|
|
26
|
+
### 1. Service Layer (Always in `services/`)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Create: services/posts.ts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- [ ] Import types: `import type { Post, PostCreate, PostUpdate } from "@/types/model"`
|
|
33
|
+
- [ ] Define only `PostListParams` (Create/Update come from Omnify)
|
|
34
|
+
- [ ] Create `postService` object with CRUD methods
|
|
35
|
+
- [ ] Add JSDoc comments for each method
|
|
36
|
+
|
|
37
|
+
### 2. Query Keys
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Update: lib/queryKeys.ts
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- [ ] Add `posts` object to `queryKeys`
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
posts: {
|
|
47
|
+
all: ["posts"] as const,
|
|
48
|
+
lists: () => [...queryKeys.posts.all, "list"] as const,
|
|
49
|
+
list: (params?: PostListParams) => [...queryKeys.posts.lists(), params] as const,
|
|
50
|
+
details: () => [...queryKeys.posts.all, "detail"] as const,
|
|
51
|
+
detail: (id: number) => [...queryKeys.posts.details(), id] as const,
|
|
52
|
+
},
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Feature Components (in `features/posts/`)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Create: features/posts/
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- [ ] `PostTable.tsx` - Table component
|
|
62
|
+
- [ ] `PostForm.tsx` - Form component
|
|
63
|
+
- [ ] `usePostFilters.ts` - Feature-specific hooks (if needed)
|
|
64
|
+
|
|
65
|
+
### 4. Pages
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Create pages in app/(dashboard)/posts/
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- [ ] `page.tsx` - List page (imports from `features/posts/`)
|
|
72
|
+
- [ ] `new/page.tsx` - Create form
|
|
73
|
+
- [ ] `[id]/page.tsx` - Detail view
|
|
74
|
+
- [ ] `[id]/edit/page.tsx` - Edit form
|
|
75
|
+
|
|
76
|
+
### 5. Shared Components (only if reused)
|
|
77
|
+
|
|
78
|
+
- [ ] If component used in 2+ features → move to `components/common/`
|
|
79
|
+
|
|
80
|
+
### 6. Translations
|
|
81
|
+
|
|
82
|
+
- [ ] Add labels to `src/i18n/messages/*.json` if needed
|
|
83
|
+
|
|
84
|
+
### 7. Final Check
|
|
85
|
+
|
|
86
|
+
- [ ] Run `npm run typecheck && npm run lint`
|
|
87
|
+
- [ ] Test create, read, update, delete operations
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Adding New Language
|
|
92
|
+
|
|
93
|
+
- [ ] Create message file: `src/i18n/messages/{locale}.json`
|
|
94
|
+
- [ ] Add locale to `src/i18n/config.ts`
|
|
95
|
+
- [ ] Import Ant Design locale in `src/components/AntdThemeProvider.tsx`
|
|
96
|
+
- [ ] Test with `LocaleSwitcher` component
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Before Commit
|
|
101
|
+
|
|
102
|
+
- [ ] `npm run typecheck` passes
|
|
103
|
+
- [ ] `npm run lint` passes
|
|
104
|
+
- [ ] No console warnings about deprecated props
|
|
105
|
+
- [ ] No hardcoded strings (use i18n)
|
|
106
|
+
- [ ] Forms handle loading state (`isPending`)
|
|
107
|
+
- [ ] Forms handle validation errors (`getFormErrors`)
|
|
108
|
+
- [ ] Mutations invalidate related queries
|