@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.
Files changed (43) hide show
  1. package/ai-guides/react-form-guide.md +259 -0
  2. package/ai-guides/typescript-guide.md +53 -0
  3. package/dist/{chunk-4L77AHAC.js → chunk-6I4O23X6.js} +521 -66
  4. package/dist/chunk-6I4O23X6.js.map +1 -0
  5. package/dist/index.cjs +761 -65
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +138 -2
  8. package/dist/index.d.ts +138 -2
  9. package/dist/index.js +227 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/plugin.cjs +624 -75
  12. package/dist/plugin.cjs.map +1 -1
  13. package/dist/plugin.d.cts +6 -0
  14. package/dist/plugin.d.ts +6 -0
  15. package/dist/plugin.js +96 -11
  16. package/dist/plugin.js.map +1 -1
  17. package/package.json +4 -3
  18. package/scripts/postinstall.js +29 -40
  19. package/stubs/JapaneseAddressField.tsx.stub +289 -0
  20. package/stubs/JapaneseBankField.tsx.stub +212 -0
  21. package/stubs/JapaneseNameField.tsx.stub +194 -0
  22. package/stubs/ai-guides/checklists/react.md.stub +108 -0
  23. package/stubs/ai-guides/cursor/react-design.mdc.stub +289 -0
  24. package/stubs/ai-guides/cursor/react-form.mdc.stub +277 -0
  25. package/stubs/ai-guides/cursor/react-services.mdc.stub +304 -0
  26. package/stubs/ai-guides/cursor/react.mdc.stub +305 -0
  27. package/stubs/ai-guides/react/README.md.stub +221 -0
  28. package/stubs/ai-guides/react/antd-guide.md.stub +294 -0
  29. package/stubs/ai-guides/react/checklist.md.stub +108 -0
  30. package/stubs/ai-guides/react/datetime-guide.md.stub +137 -0
  31. package/stubs/ai-guides/react/design-philosophy.md.stub +363 -0
  32. package/stubs/ai-guides/react/i18n-guide.md.stub +211 -0
  33. package/stubs/ai-guides/react/laravel-integration.md.stub +181 -0
  34. package/stubs/ai-guides/react/service-pattern.md.stub +180 -0
  35. package/stubs/ai-guides/react/tanstack-query.md.stub +339 -0
  36. package/stubs/ai-guides/react/types-guide.md.stub +524 -0
  37. package/stubs/components-index.ts.stub +13 -0
  38. package/stubs/form-validation.ts.stub +106 -0
  39. package/stubs/rules/index.ts.stub +48 -0
  40. package/stubs/rules/kana.ts.stub +291 -0
  41. package/stubs/use-form-mutation.ts.stub +117 -0
  42. package/stubs/zod-i18n.ts.stub +32 -0
  43. package/dist/chunk-4L77AHAC.js.map +0 -1
@@ -0,0 +1,212 @@
1
+ /**
2
+ * JapaneseBankField - Japanese bank account input component
3
+ */
4
+ import { Form, Input, Select, Row, Col } from 'antd';
5
+ import type { FormInstance } from 'antd';
6
+ import type { RuleObject } from 'antd/es/form';
7
+ import { zodRule } from '../lib/form-validation.js';
8
+ import { getZodLocale } from '../lib/zod-i18n.js';
9
+ import { BankAccountType, BankAccountTypeValues, getBankAccountTypeLabel } from '../enum/plugin/BankAccountType.js';
10
+
11
+ interface I18nConfig {
12
+ fields: Record<string, { label?: Record<string, string>; placeholder?: Record<string, string> }>;
13
+ }
14
+
15
+ interface SelectOption {
16
+ value: string | number;
17
+ label: string;
18
+ }
19
+
20
+ interface JapaneseBankFieldProps {
21
+ form?: FormInstance;
22
+ schemas: Record<string, unknown>;
23
+ i18n: I18nConfig;
24
+ prefix?: string;
25
+ accountTypeOptions?: SelectOption[];
26
+ }
27
+
28
+ function getLabel(i18n: I18nConfig, field: string, locale: string): string {
29
+ return i18n.fields[field]?.label?.[locale] ?? i18n.fields[field]?.label?.['en'] ?? field;
30
+ }
31
+
32
+ function getPlaceholder(i18n: I18nConfig, field: string, locale: string): string {
33
+ return i18n.fields[field]?.placeholder?.[locale] ?? i18n.fields[field]?.placeholder?.['en'] ?? '';
34
+ }
35
+
36
+ // Build account type options from enum
37
+ function buildAccountTypeOptions(locale: string): SelectOption[] {
38
+ return BankAccountTypeValues.map(value => ({
39
+ value,
40
+ label: getBankAccountTypeLabel(value, locale),
41
+ }));
42
+ }
43
+
44
+ export function JapaneseBankField({
45
+ schemas,
46
+ i18n,
47
+ prefix = 'bank',
48
+ accountTypeOptions,
49
+ }: JapaneseBankFieldProps) {
50
+ const locale = getZodLocale();
51
+ const options = accountTypeOptions ?? buildAccountTypeOptions(locale);
52
+
53
+ const bankCodeField = `${prefix}_bank_code`;
54
+ const bankNameField = `${prefix}_bank_name`;
55
+ const branchCodeField = `${prefix}_branch_code`;
56
+ const branchNameField = `${prefix}_branch_name`;
57
+ const accountTypeField = `${prefix}_account_type`;
58
+ const accountNumberField = `${prefix}_account_number`;
59
+ const accountHolderField = `${prefix}_account_holder`;
60
+
61
+ const getRule = (field: string): RuleObject[] => {
62
+ const schema = schemas[field];
63
+ if (!schema) return [];
64
+ return [zodRule(schema as any, getLabel(i18n, field, locale))];
65
+ };
66
+
67
+ // Check if a field is required by examining its Zod schema
68
+ const isRequired = (field: string): boolean => {
69
+ const schema = schemas[field];
70
+ if (!schema) return false;
71
+ const schemaDesc = (schema as any)?._def?.typeName;
72
+ const inner = (schema as any)?._def?.innerType;
73
+ if (schemaDesc === 'ZodOptional' || schemaDesc === 'ZodNullable') return false;
74
+ if (inner?._def?.typeName === 'ZodOptional' || inner?._def?.typeName === 'ZodNullable') return false;
75
+ return true;
76
+ };
77
+
78
+ const bankRequired = isRequired(bankCodeField) || isRequired(bankNameField);
79
+ const branchRequired = isRequired(branchCodeField) || isRequired(branchNameField);
80
+
81
+ // Short labels
82
+ const codeShortLabel = locale === 'ja' ? 'コード' : 'Code';
83
+ const nameShortLabel = locale === 'ja' ? '名称' : 'Name';
84
+
85
+ // Compound label from i18n
86
+ const compoundLabel = i18n.fields[prefix]?.label?.[locale]
87
+ ?? i18n.fields[prefix]?.label?.['en']
88
+ ?? getLabel(i18n, bankCodeField, locale);
89
+
90
+ const bankLabel = locale === 'ja' ? '銀行' : 'Bank';
91
+ const branchLabel = locale === 'ja' ? '支店' : 'Branch';
92
+
93
+ const prefixStyle = {
94
+ color: 'rgba(0, 0, 0, 0.88)',
95
+ fontWeight: 500,
96
+ borderRight: '1px solid #d9d9d9',
97
+ paddingRight: 8,
98
+ marginRight: 4,
99
+ };
100
+
101
+ return (
102
+ <>
103
+ {/* 銀行コード・銀行名 */}
104
+ <Form.Item
105
+ label={bankLabel}
106
+ required={bankRequired}
107
+ style={{ marginBottom: 0 }}
108
+ >
109
+ <Row gutter={8}>
110
+ <Col span={8}>
111
+ <Form.Item
112
+ name={bankCodeField}
113
+ rules={getRule(bankCodeField)}
114
+ style={{ marginBottom: 16 }}
115
+ >
116
+ <Input
117
+ prefix={<span style={prefixStyle}>{codeShortLabel}</span>}
118
+ placeholder={getPlaceholder(i18n, bankCodeField, locale)}
119
+ maxLength={4}
120
+ />
121
+ </Form.Item>
122
+ </Col>
123
+ <Col span={16}>
124
+ <Form.Item
125
+ name={bankNameField}
126
+ rules={getRule(bankNameField)}
127
+ style={{ marginBottom: 16 }}
128
+ >
129
+ <Input
130
+ prefix={<span style={prefixStyle}>{nameShortLabel}</span>}
131
+ placeholder={getPlaceholder(i18n, bankNameField, locale)}
132
+ />
133
+ </Form.Item>
134
+ </Col>
135
+ </Row>
136
+ </Form.Item>
137
+
138
+ {/* 支店コード・支店名 */}
139
+ <Form.Item
140
+ label={branchLabel}
141
+ required={branchRequired}
142
+ style={{ marginBottom: 0 }}
143
+ >
144
+ <Row gutter={8}>
145
+ <Col span={8}>
146
+ <Form.Item
147
+ name={branchCodeField}
148
+ rules={getRule(branchCodeField)}
149
+ style={{ marginBottom: 16 }}
150
+ >
151
+ <Input
152
+ prefix={<span style={prefixStyle}>{codeShortLabel}</span>}
153
+ placeholder={getPlaceholder(i18n, branchCodeField, locale)}
154
+ maxLength={3}
155
+ />
156
+ </Form.Item>
157
+ </Col>
158
+ <Col span={16}>
159
+ <Form.Item
160
+ name={branchNameField}
161
+ rules={getRule(branchNameField)}
162
+ style={{ marginBottom: 16 }}
163
+ >
164
+ <Input
165
+ prefix={<span style={prefixStyle}>{nameShortLabel}</span>}
166
+ placeholder={getPlaceholder(i18n, branchNameField, locale)}
167
+ />
168
+ </Form.Item>
169
+ </Col>
170
+ </Row>
171
+ </Form.Item>
172
+
173
+ {/* 口座種別 */}
174
+ <Form.Item
175
+ name={accountTypeField}
176
+ label={getLabel(i18n, accountTypeField, locale)}
177
+ rules={getRule(accountTypeField)}
178
+ required={isRequired(accountTypeField)}
179
+ >
180
+ <Select
181
+ placeholder={getPlaceholder(i18n, accountTypeField, locale)}
182
+ options={options}
183
+ style={{ width: 150 }}
184
+ />
185
+ </Form.Item>
186
+
187
+ {/* 口座番号 */}
188
+ <Form.Item
189
+ name={accountNumberField}
190
+ label={getLabel(i18n, accountNumberField, locale)}
191
+ rules={getRule(accountNumberField)}
192
+ required={isRequired(accountNumberField)}
193
+ >
194
+ <Input
195
+ placeholder={getPlaceholder(i18n, accountNumberField, locale)}
196
+ maxLength={7}
197
+ style={{ width: 150 }}
198
+ />
199
+ </Form.Item>
200
+
201
+ {/* 口座名義 */}
202
+ <Form.Item
203
+ name={accountHolderField}
204
+ label={getLabel(i18n, accountHolderField, locale)}
205
+ rules={getRule(accountHolderField)}
206
+ required={isRequired(accountHolderField)}
207
+ >
208
+ <Input placeholder={getPlaceholder(i18n, accountHolderField, locale)} />
209
+ </Form.Item>
210
+ </>
211
+ );
212
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * JapaneseNameField - Japanese name input component
3
+ * Handles lastname + firstname with optional kana fields
4
+ */
5
+ import { Form, Input, Row, Col } from 'antd';
6
+ import type { FormInstance } from 'antd';
7
+ import type { RuleObject } from 'antd/es/form';
8
+ import { zodRule } from '../lib/form-validation';
9
+ import { getZodLocale } from '../lib/zod-i18n';
10
+
11
+ interface I18nConfig {
12
+ fields: Record<string, { label?: Record<string, string>; placeholder?: Record<string, string> }>;
13
+ }
14
+
15
+ interface JapaneseNameFieldProps {
16
+ form?: FormInstance;
17
+ schemas: Record<string, unknown>;
18
+ i18n: I18nConfig;
19
+ prefix?: string;
20
+ required?: boolean;
21
+ showKana?: boolean;
22
+ label?: string;
23
+ kanaLabel?: string;
24
+ }
25
+
26
+ function getLabel(i18n: I18nConfig, field: string, locale: string): string {
27
+ return i18n.fields[field]?.label?.[locale] ?? i18n.fields[field]?.label?.['en'] ?? field;
28
+ }
29
+
30
+ function getCompoundLabel(i18n: I18nConfig, prefix: string, locale: string): string | undefined {
31
+ // Try to get compound-level label (e.g., 'name' -> '氏名')
32
+ return i18n.fields[prefix]?.label?.[locale] ?? i18n.fields[prefix]?.label?.['en'];
33
+ }
34
+
35
+ function getPlaceholder(i18n: I18nConfig, field: string, locale: string): string {
36
+ return i18n.fields[field]?.placeholder?.[locale] ?? i18n.fields[field]?.placeholder?.['en'] ?? '';
37
+ }
38
+
39
+ export function JapaneseNameField({
40
+ schemas,
41
+ i18n,
42
+ prefix = 'name',
43
+ required = false,
44
+ showKana = true,
45
+ label,
46
+ kanaLabel,
47
+ }: JapaneseNameFieldProps) {
48
+ const locale = getZodLocale();
49
+
50
+ const lastnameField = `${prefix}_lastname`;
51
+ const firstnameField = `${prefix}_firstname`;
52
+ const kanaLastnameField = `${prefix}_kana_lastname`;
53
+ const kanaFirstnameField = `${prefix}_kana_firstname`;
54
+
55
+ const getRule = (field: string): RuleObject[] => {
56
+ const schema = schemas[field];
57
+ if (!schema) return [];
58
+ return [zodRule(schema as any, getLabel(i18n, field, locale))];
59
+ };
60
+
61
+ // Check if a field is required by examining its Zod schema
62
+ const isFieldRequired = (field: string): boolean => {
63
+ const schema = schemas[field];
64
+ if (!schema) return false;
65
+ const schemaDesc = (schema as any)?._def?.typeName;
66
+ const inner = (schema as any)?._def?.innerType;
67
+ if (schemaDesc === 'ZodOptional' || schemaDesc === 'ZodNullable') return false;
68
+ if (inner?._def?.typeName === 'ZodOptional' || inner?._def?.typeName === 'ZodNullable') return false;
69
+ return true;
70
+ };
71
+
72
+ const nameRequired = isFieldRequired(lastnameField) || isFieldRequired(firstnameField) || required;
73
+ const kanaRequired = isFieldRequired(kanaLastnameField) || isFieldRequired(kanaFirstnameField);
74
+
75
+ // Try compound label first, then fallback to first field's label
76
+ const nameLabel = label ?? getCompoundLabel(i18n, prefix, locale) ?? getLabel(i18n, lastnameField, locale);
77
+ const nameKanaLabel = kanaLabel ?? `${getCompoundLabel(i18n, prefix, locale) ?? getLabel(i18n, kanaLastnameField, locale)}(カナ)`;
78
+
79
+ // Get short field labels (姓, 名, etc.)
80
+ const lastnameShortLabel = locale === 'ja' ? '姓' : 'Last';
81
+ const firstnameShortLabel = locale === 'ja' ? '名' : 'First';
82
+
83
+ return (
84
+ <>
85
+ <Form.Item
86
+ label={nameLabel}
87
+ required={nameRequired}
88
+ style={{ marginBottom: 0 }}
89
+ >
90
+ <Row gutter={8}>
91
+ <Col span={12}>
92
+ <Form.Item
93
+ name={lastnameField}
94
+ rules={getRule(lastnameField)}
95
+ style={{ marginBottom: 16 }}
96
+ >
97
+ <Input
98
+ prefix={
99
+ <span style={{
100
+ color: 'rgba(0, 0, 0, 0.88)',
101
+ fontWeight: 500,
102
+ borderRight: '1px solid #d9d9d9',
103
+ paddingRight: 8,
104
+ marginRight: 4,
105
+ }}>
106
+ {lastnameShortLabel}
107
+ </span>
108
+ }
109
+ placeholder={getPlaceholder(i18n, lastnameField, locale)}
110
+ />
111
+ </Form.Item>
112
+ </Col>
113
+ <Col span={12}>
114
+ <Form.Item
115
+ name={firstnameField}
116
+ rules={getRule(firstnameField)}
117
+ style={{ marginBottom: 16 }}
118
+ >
119
+ <Input
120
+ prefix={
121
+ <span style={{
122
+ color: 'rgba(0, 0, 0, 0.88)',
123
+ fontWeight: 500,
124
+ borderRight: '1px solid #d9d9d9',
125
+ paddingRight: 8,
126
+ marginRight: 4,
127
+ }}>
128
+ {firstnameShortLabel}
129
+ </span>
130
+ }
131
+ placeholder={getPlaceholder(i18n, firstnameField, locale)}
132
+ />
133
+ </Form.Item>
134
+ </Col>
135
+ </Row>
136
+ </Form.Item>
137
+
138
+ {showKana && (
139
+ <Form.Item
140
+ label={nameKanaLabel}
141
+ required={kanaRequired}
142
+ style={{ marginBottom: 0 }}
143
+ >
144
+ <Row gutter={8}>
145
+ <Col span={12}>
146
+ <Form.Item
147
+ name={kanaLastnameField}
148
+ rules={getRule(kanaLastnameField)}
149
+ style={{ marginBottom: 16 }}
150
+ >
151
+ <Input
152
+ prefix={
153
+ <span style={{
154
+ color: 'rgba(0, 0, 0, 0.88)',
155
+ fontWeight: 500,
156
+ borderRight: '1px solid #d9d9d9',
157
+ paddingRight: 8,
158
+ marginRight: 4,
159
+ }}>
160
+ {lastnameShortLabel}
161
+ </span>
162
+ }
163
+ placeholder={getPlaceholder(i18n, kanaLastnameField, locale)}
164
+ />
165
+ </Form.Item>
166
+ </Col>
167
+ <Col span={12}>
168
+ <Form.Item
169
+ name={kanaFirstnameField}
170
+ rules={getRule(kanaFirstnameField)}
171
+ style={{ marginBottom: 16 }}
172
+ >
173
+ <Input
174
+ prefix={
175
+ <span style={{
176
+ color: 'rgba(0, 0, 0, 0.88)',
177
+ fontWeight: 500,
178
+ borderRight: '1px solid #d9d9d9',
179
+ paddingRight: 8,
180
+ marginRight: 4,
181
+ }}>
182
+ {firstnameShortLabel}
183
+ </span>
184
+ }
185
+ placeholder={getPlaceholder(i18n, kanaFirstnameField, locale)}
186
+ />
187
+ </Form.Item>
188
+ </Col>
189
+ </Row>
190
+ </Form.Item>
191
+ )}
192
+ </>
193
+ );
194
+ }
@@ -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