@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,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
|