@famgia/omnify-typescript 0.0.67 → 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,259 @@
|
|
|
1
|
+
# Omnify React Form Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to build forms using Omnify generated schemas with **Ant Design** and **TanStack Query**.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Form } from 'antd';
|
|
9
|
+
import { useFormMutation } from '@/omnify/hooks';
|
|
10
|
+
import { type CustomerCreate } from '@/omnify/schemas';
|
|
11
|
+
import { CustomerForm } from '@/features/customers';
|
|
12
|
+
import { api } from '@/lib/api';
|
|
13
|
+
|
|
14
|
+
function CreateCustomerPage() {
|
|
15
|
+
const [form] = Form.useForm<CustomerCreate>();
|
|
16
|
+
|
|
17
|
+
const mutation = useFormMutation<CustomerCreate>({
|
|
18
|
+
form,
|
|
19
|
+
mutationFn: (data) => api.post('/customers', data),
|
|
20
|
+
invalidateKeys: [['customers']],
|
|
21
|
+
successMessage: '顧客を登録しました',
|
|
22
|
+
onSuccess: () => form.resetFields(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<CustomerForm
|
|
27
|
+
form={form}
|
|
28
|
+
onSubmit={mutation.mutate}
|
|
29
|
+
loading={mutation.isPending}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## useFormMutation Hook
|
|
36
|
+
|
|
37
|
+
Location: `resources/ts/omnify/hooks/use-form-mutation.ts`
|
|
38
|
+
|
|
39
|
+
### Usage with Types
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { useFormMutation } from '@/omnify/hooks';
|
|
43
|
+
import { type CustomerCreate } from '@/omnify/schemas';
|
|
44
|
+
|
|
45
|
+
// With type parameter for type safety
|
|
46
|
+
const mutation = useFormMutation<CustomerCreate>({
|
|
47
|
+
form,
|
|
48
|
+
mutationFn: (data) => api.post('/customers', data),
|
|
49
|
+
invalidateKeys: [['customers']],
|
|
50
|
+
successMessage: '保存しました',
|
|
51
|
+
onSuccess: () => form.resetFields(),
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Options
|
|
56
|
+
|
|
57
|
+
| Option | Type | Description |
|
|
58
|
+
|--------|------|-------------|
|
|
59
|
+
| `form` | `FormInstance<T>` | Ant Design form instance (required) |
|
|
60
|
+
| `mutationFn` | `(data: T) => Promise` | API call function (required) |
|
|
61
|
+
| `invalidateKeys` | `string[][]` | Query keys to invalidate on success |
|
|
62
|
+
| `successMessage` | `string` | Toast message on success |
|
|
63
|
+
| `onSuccess` | `(data) => void` | Callback after success |
|
|
64
|
+
| `onError` | `(error) => void` | Callback after error |
|
|
65
|
+
|
|
66
|
+
### Laravel Error Handling
|
|
67
|
+
|
|
68
|
+
The hook automatically handles Laravel validation errors (422):
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"message": "The given data was invalid.",
|
|
73
|
+
"errors": {
|
|
74
|
+
"email": ["The email has already been taken."]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Japanese Form Components
|
|
80
|
+
|
|
81
|
+
Omnify provides specialized components for Japanese data input.
|
|
82
|
+
|
|
83
|
+
### JapaneseNameField
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { JapaneseNameField } from '@/omnify/components';
|
|
87
|
+
|
|
88
|
+
<JapaneseNameField
|
|
89
|
+
schemas={customerSchemas}
|
|
90
|
+
i18n={customerI18n}
|
|
91
|
+
prefix="name" // name_lastname, name_firstname, etc.
|
|
92
|
+
required // Show required asterisk
|
|
93
|
+
showKana={true} // Show kana fields (default)
|
|
94
|
+
/>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Fields:** `{prefix}_lastname`, `{prefix}_firstname`, `{prefix}_kana_lastname`, `{prefix}_kana_firstname`
|
|
98
|
+
|
|
99
|
+
### JapaneseAddressField
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { JapaneseAddressField } from '@/omnify/components';
|
|
103
|
+
|
|
104
|
+
<JapaneseAddressField
|
|
105
|
+
form={form}
|
|
106
|
+
schemas={customerSchemas}
|
|
107
|
+
i18n={customerI18n}
|
|
108
|
+
prefix="address"
|
|
109
|
+
enablePostalLookup={true} // Postal code → address lookup
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Fields:** `{prefix}_postal_code`, `{prefix}_prefecture`, `{prefix}_address1`, `{prefix}_address2`, `{prefix}_address3`
|
|
114
|
+
|
|
115
|
+
**Options:**
|
|
116
|
+
- `enablePostalLookup` - Enable postal code search
|
|
117
|
+
- `autoSearch` - Auto-search when 7 digits entered
|
|
118
|
+
- `usePrefectureId` - Use numeric ID instead of string enum
|
|
119
|
+
|
|
120
|
+
### JapaneseBankField
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { JapaneseBankField } from '@/omnify/components';
|
|
124
|
+
|
|
125
|
+
<JapaneseBankField
|
|
126
|
+
schemas={customerSchemas}
|
|
127
|
+
i18n={customerI18n}
|
|
128
|
+
prefix="bank"
|
|
129
|
+
/>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Fields:** `{prefix}_bank_code`, `{prefix}_branch_code`, `{prefix}_account_type`, `{prefix}_account_number`, `{prefix}_account_holder`
|
|
133
|
+
|
|
134
|
+
Account type options auto-loaded from `BankAccountType` enum with i18n.
|
|
135
|
+
|
|
136
|
+
## Complete Form Example
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import { Form, Input, Button, Card, Space, Divider } from 'antd';
|
|
140
|
+
import type { FormInstance } from 'antd';
|
|
141
|
+
import { zodRule, setZodLocale } from '@/omnify/lib';
|
|
142
|
+
import { JapaneseNameField, JapaneseAddressField, JapaneseBankField } from '@/omnify/components';
|
|
143
|
+
import {
|
|
144
|
+
type Customer, type CustomerCreate,
|
|
145
|
+
customerSchemas, customerI18n,
|
|
146
|
+
getCustomerFieldLabel, getCustomerFieldPlaceholder,
|
|
147
|
+
} from '@/omnify/schemas';
|
|
148
|
+
|
|
149
|
+
const LOCALE = 'ja';
|
|
150
|
+
|
|
151
|
+
interface CustomerFormProps {
|
|
152
|
+
form: FormInstance<CustomerCreate>;
|
|
153
|
+
initialValues?: Partial<Customer>;
|
|
154
|
+
onSubmit: (values: CustomerCreate) => void;
|
|
155
|
+
loading?: boolean;
|
|
156
|
+
isEdit?: boolean;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function CustomerForm({ form, initialValues, onSubmit, loading, isEdit }: CustomerFormProps) {
|
|
160
|
+
setZodLocale(LOCALE);
|
|
161
|
+
|
|
162
|
+
const label = (key: string) => getCustomerFieldLabel(key, LOCALE);
|
|
163
|
+
const placeholder = (key: string) => getCustomerFieldPlaceholder(key, LOCALE);
|
|
164
|
+
const rule = (key: keyof typeof customerSchemas) => zodRule(customerSchemas[key], label(key));
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<Card>
|
|
168
|
+
<Form form={form} layout="horizontal" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}
|
|
169
|
+
initialValues={initialValues} onFinish={onSubmit} style={{ maxWidth: 800 }}>
|
|
170
|
+
|
|
171
|
+
<Divider orientation="left">氏名</Divider>
|
|
172
|
+
<JapaneseNameField schemas={customerSchemas} i18n={customerI18n} prefix="name" required />
|
|
173
|
+
|
|
174
|
+
<Divider orientation="left">連絡先</Divider>
|
|
175
|
+
<Form.Item name="phone" label={label('phone')} rules={[rule('phone')]}>
|
|
176
|
+
<Input placeholder={placeholder('phone')} />
|
|
177
|
+
</Form.Item>
|
|
178
|
+
<Form.Item name="email" label={label('email')} rules={[rule('email')]}>
|
|
179
|
+
<Input type="email" placeholder={placeholder('email')} />
|
|
180
|
+
</Form.Item>
|
|
181
|
+
|
|
182
|
+
<Divider orientation="left">住所</Divider>
|
|
183
|
+
<JapaneseAddressField form={form} schemas={customerSchemas} i18n={customerI18n}
|
|
184
|
+
prefix="address" enablePostalLookup={true} />
|
|
185
|
+
|
|
186
|
+
<Divider orientation="left">銀行口座</Divider>
|
|
187
|
+
<JapaneseBankField schemas={customerSchemas} i18n={customerI18n} prefix="bank" />
|
|
188
|
+
|
|
189
|
+
<Form.Item wrapperCol={{ offset: 6, span: 18 }}>
|
|
190
|
+
<Space>
|
|
191
|
+
<Button type="primary" htmlType="submit" loading={loading}>
|
|
192
|
+
{isEdit ? '更新' : '登録'}
|
|
193
|
+
</Button>
|
|
194
|
+
</Space>
|
|
195
|
+
</Form.Item>
|
|
196
|
+
</Form>
|
|
197
|
+
</Card>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Zod Validation with i18n
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { zodRule, setZodLocale } from '@/omnify/lib';
|
|
206
|
+
|
|
207
|
+
// Set locale once at component level
|
|
208
|
+
setZodLocale('ja');
|
|
209
|
+
|
|
210
|
+
// Use zodRule for field validation
|
|
211
|
+
<Form.Item name="email" label={label('email')} rules={[zodRule(customerSchemas.email, label('email'))]}>
|
|
212
|
+
<Input />
|
|
213
|
+
</Form.Item>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Kana Validation Rules
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { kanaString, KATAKANA_PATTERN } from '@/omnify/lib/rules';
|
|
220
|
+
|
|
221
|
+
// Full-width katakana (default)
|
|
222
|
+
const kanaSchema = kanaString();
|
|
223
|
+
|
|
224
|
+
// Custom options
|
|
225
|
+
const kanaSchema = kanaString({
|
|
226
|
+
fullWidthKatakana: true,
|
|
227
|
+
hiragana: true,
|
|
228
|
+
allowNumbers: true,
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## File Structure
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
resources/ts/omnify/
|
|
236
|
+
├── components/
|
|
237
|
+
│ ├── JapaneseNameField.tsx
|
|
238
|
+
│ ├── JapaneseAddressField.tsx
|
|
239
|
+
│ └── JapaneseBankField.tsx
|
|
240
|
+
├── enum/
|
|
241
|
+
│ └── plugin/
|
|
242
|
+
│ ├── Prefecture.ts
|
|
243
|
+
│ └── BankAccountType.ts
|
|
244
|
+
├── hooks/
|
|
245
|
+
│ └── use-form-mutation.ts
|
|
246
|
+
├── lib/
|
|
247
|
+
│ ├── form-validation.ts
|
|
248
|
+
│ ├── zod-i18n.ts
|
|
249
|
+
│ └── rules/kana.ts
|
|
250
|
+
└── schemas/
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Tips
|
|
254
|
+
|
|
255
|
+
1. **Use type generics** - `useFormMutation<CustomerCreate>` for type safety
|
|
256
|
+
2. **Use `setZodLocale`** - Call once for localized validation messages
|
|
257
|
+
3. **Use Japanese* components** - Built-in i18n, validation, postal lookup
|
|
258
|
+
4. **Set `invalidateKeys`** - Auto-refresh lists after mutations
|
|
259
|
+
5. **Don't pass `form` to Name/Bank components** - Only Address needs it for postal lookup
|
|
@@ -145,6 +145,59 @@ export function isPostStatus(value: unknown): value is PostStatus {
|
|
|
145
145
|
}
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
+
## Display Names and Placeholders
|
|
149
|
+
|
|
150
|
+
Omnify generates multi-locale display names and placeholders for form inputs.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import {
|
|
154
|
+
UserDisplayName,
|
|
155
|
+
UserPropertyDisplayNames,
|
|
156
|
+
UserPropertyPlaceholders,
|
|
157
|
+
getUserDisplayName,
|
|
158
|
+
getUserPropertyDisplayName,
|
|
159
|
+
getUserPropertyPlaceholder,
|
|
160
|
+
} from './types/models/User';
|
|
161
|
+
|
|
162
|
+
// Get model display name
|
|
163
|
+
getUserDisplayName('ja'); // "ユーザー"
|
|
164
|
+
getUserDisplayName('en'); // "User"
|
|
165
|
+
|
|
166
|
+
// Get property display name
|
|
167
|
+
getUserPropertyDisplayName('email', 'ja'); // "メールアドレス"
|
|
168
|
+
getUserPropertyDisplayName('email', 'en'); // "Email"
|
|
169
|
+
|
|
170
|
+
// Get property placeholder
|
|
171
|
+
getUserPropertyPlaceholder('email', 'ja'); // "メールアドレスを入力"
|
|
172
|
+
getUserPropertyPlaceholder('email', 'en'); // "Enter your email"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Using Placeholders in Forms
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { Form, Input } from 'antd';
|
|
179
|
+
import {
|
|
180
|
+
getUserRules,
|
|
181
|
+
getUserPropertyDisplayName,
|
|
182
|
+
getUserPropertyPlaceholder,
|
|
183
|
+
} from './types/models/User';
|
|
184
|
+
|
|
185
|
+
function UserForm({ locale = 'ja' }) {
|
|
186
|
+
const rules = getUserRules(locale);
|
|
187
|
+
return (
|
|
188
|
+
<Form>
|
|
189
|
+
<Form.Item
|
|
190
|
+
name="email"
|
|
191
|
+
label={getUserPropertyDisplayName('email', locale)}
|
|
192
|
+
rules={rules.email}
|
|
193
|
+
>
|
|
194
|
+
<Input placeholder={getUserPropertyPlaceholder('email', locale)} />
|
|
195
|
+
</Form.Item>
|
|
196
|
+
</Form>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
148
201
|
## Validation Rules (Ant Design)
|
|
149
202
|
|
|
150
203
|
Omnify generates Ant Design compatible validation rules in `rules/` directory.
|