@moamc/rn-cli 1.0.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/LICENSE +21 -0
- package/README.md +154 -0
- package/generators/api.js +155 -0
- package/generators/feature.js +155 -0
- package/generators/hook.js +115 -0
- package/generators/screen.js +351 -0
- package/index.js +100 -0
- package/package.json +43 -0
- package/templates/apiTemplate.js +217 -0
- package/templates/constantsTemplate.js +33 -0
- package/templates/constantsTemplate.js.backup +18 -0
- package/templates/fieldGeneratorTemplate.js +165 -0
- package/templates/helpersTemplate.js +10 -0
- package/templates/hookTemplate.js +132 -0
- package/templates/hookTemplate.js.backup +113 -0
- package/templates/queryTemplate.js +23 -0
- package/templates/screenTemplate.js +165 -0
- package/templates/screenTemplate.js.backup +147 -0
- package/templates/stylesTemplate.js +52 -0
- package/templates/stylesTemplate.js.backup +88 -0
- package/templates/validationTemplate.js +122 -0
- package/templates/validationTemplate.js.backup +37 -0
- package/utils/fileUtils.js +116 -0
- package/utils/navigationUtils.js +74 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
const generateApiServiceTemplate = (apiConfig) => {
|
|
2
|
+
const { serviceName, methods } = apiConfig;
|
|
3
|
+
|
|
4
|
+
const methodsCode = methods.map(method => {
|
|
5
|
+
const { name, endpoint, type, hasAuth = true } = method;
|
|
6
|
+
const callType = type === 'GET' ? 'makeAuthenticatedGetCall' : 'makeAuthenticatedPostCall';
|
|
7
|
+
|
|
8
|
+
if (type === 'GET') {
|
|
9
|
+
return `function ${name}(data) {
|
|
10
|
+
\tconst response = coreAPI.${callType}('${endpoint}', ${hasAuth});
|
|
11
|
+
\treturn handleResponse(response);
|
|
12
|
+
}`;
|
|
13
|
+
} else {
|
|
14
|
+
return `function ${name}(data) {
|
|
15
|
+
\tconst response = coreAPI.${callType}('${endpoint}', data, ${hasAuth});
|
|
16
|
+
\treturn handleResponse(response);
|
|
17
|
+
}`;
|
|
18
|
+
}
|
|
19
|
+
}).join('\n\n');
|
|
20
|
+
|
|
21
|
+
const exports = methods.map(m => m.name).join(',\n\t');
|
|
22
|
+
|
|
23
|
+
return `import { coreAPI } from '../common/baseConfig';
|
|
24
|
+
import handleResponse from '../handler';
|
|
25
|
+
|
|
26
|
+
${methodsCode}
|
|
27
|
+
|
|
28
|
+
export const ${serviceName}Service = {
|
|
29
|
+
\t${exports}
|
|
30
|
+
};
|
|
31
|
+
`;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const generateQueryOptionsTemplate = (apiConfig) => {
|
|
35
|
+
const { serviceName, methods } = apiConfig;
|
|
36
|
+
|
|
37
|
+
const queryMethods = methods.map(method => {
|
|
38
|
+
const { name, queryKey, hasParams = true } = method;
|
|
39
|
+
|
|
40
|
+
return `\t${name}: (data) => (queryOptions({
|
|
41
|
+
\t\tqueryKey: ['${queryKey}', data],
|
|
42
|
+
\t\tqueryFn: () => ${serviceName}Service.${name}(data),
|
|
43
|
+
\t\t...baseQueryOptions,
|
|
44
|
+
\t})),`;
|
|
45
|
+
}).join('\n\n');
|
|
46
|
+
|
|
47
|
+
return `import { queryOptions } from '@tanstack/react-query';
|
|
48
|
+
import { ${serviceName}Service } from '../services/${serviceName}/${serviceName}';
|
|
49
|
+
|
|
50
|
+
const baseQueryOptions = {
|
|
51
|
+
\trefetchOnWindowFocus: false,
|
|
52
|
+
\trefetchOnReconnect: false,
|
|
53
|
+
\trefetchInterval: false,
|
|
54
|
+
\tstaleTime: 5 * 60 * 1000,
|
|
55
|
+
\tgcTime: 10 * 60 * 1000,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const ${serviceName}QueryOptions = {
|
|
59
|
+
${queryMethods}
|
|
60
|
+
};
|
|
61
|
+
`;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const generateApiIntegratedHookTemplate = (screenName, apiConfig, formFields = []) => {
|
|
65
|
+
const { serviceName, methods, primaryMethod, hasPostMethod } = apiConfig;
|
|
66
|
+
const getMethod = methods.find(m => m.type === 'GET');
|
|
67
|
+
const postMethod = methods.find(m => m.type === 'POST');
|
|
68
|
+
|
|
69
|
+
const formFieldsInit = formFields.map(f => `\t\t${f.name}: '',`).join('\n');
|
|
70
|
+
|
|
71
|
+
if (hasPostMethod) {
|
|
72
|
+
return `import { useState, useEffect } from 'react';
|
|
73
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
74
|
+
import { ${serviceName}QueryOptions } from '../../../../queries/${serviceName}Query';
|
|
75
|
+
import { ${serviceName}Service } from '../../../../services/${serviceName}/${serviceName}';
|
|
76
|
+
import { showToastNotification } from '../../../../helpers/common';
|
|
77
|
+
import { validate${screenName} } from '../validation';
|
|
78
|
+
|
|
79
|
+
export const use${screenName} = () => {
|
|
80
|
+
\tconst queryClient = useQueryClient();
|
|
81
|
+
\tconst [formData, setFormData] = useState({
|
|
82
|
+
${formFieldsInit}
|
|
83
|
+
\t});
|
|
84
|
+
\tconst [errors, setErrors] = useState({});
|
|
85
|
+
\tconst [touched, setTouched] = useState({});
|
|
86
|
+
|
|
87
|
+
\t// Fetch data using React Query${getMethod ? `
|
|
88
|
+
\tconst { data, isLoading, error, refetch } = useQuery(
|
|
89
|
+
\t\t${serviceName}QueryOptions.${getMethod.name}(formData)
|
|
90
|
+
\t);` : ''}
|
|
91
|
+
|
|
92
|
+
\t// Submit mutation
|
|
93
|
+
\tconst submitMutation = useMutation({
|
|
94
|
+
\t\tmutationFn: (data) => ${serviceName}Service.${postMethod.name}(data),
|
|
95
|
+
\t\tonSuccess: (response) => {
|
|
96
|
+
\t\t\tshowToastNotification('Success!');
|
|
97
|
+
\t\t\t// Invalidate and refetch${getMethod ? `
|
|
98
|
+
\t\t\tqueryClient.invalidateQueries({ queryKey: ['${getMethod.queryKey}'] });` : ''}
|
|
99
|
+
\t\t},
|
|
100
|
+
\t\tonError: (error) => {
|
|
101
|
+
\t\t\tshowToastNotification(error?.message || 'Something went wrong');
|
|
102
|
+
\t\t},
|
|
103
|
+
\t});${getMethod ? `
|
|
104
|
+
|
|
105
|
+
\tuseEffect(() => {
|
|
106
|
+
\t\tif (error) {
|
|
107
|
+
\t\t\tshowToastNotification(error?.message || 'Something went wrong');
|
|
108
|
+
\t\t}
|
|
109
|
+
\t}, [error]);` : ''}
|
|
110
|
+
|
|
111
|
+
\tconst handleChange = (value, field) => {
|
|
112
|
+
\t\tconst updatedFormData = { ...formData, [field]: value };
|
|
113
|
+
\t\tsetFormData(updatedFormData);
|
|
114
|
+
\t\tsetTouched(prev => ({ ...prev, [field]: true }));
|
|
115
|
+
\t\t
|
|
116
|
+
\t\t// Re-validate on change
|
|
117
|
+
\t\tconst validationErrors = validate${screenName}(updatedFormData);
|
|
118
|
+
\t\tsetErrors(validationErrors);
|
|
119
|
+
\t};
|
|
120
|
+
|
|
121
|
+
\tconst handleSubmit = async () => {
|
|
122
|
+
\t\tconst validationErrors = validate${screenName}(formData);
|
|
123
|
+
\t\tif (Object.keys(validationErrors).length > 0) {
|
|
124
|
+
\t\t\tsetErrors(validationErrors);
|
|
125
|
+
\t\t\tsetTouched(Object.keys(formData).reduce((acc, key) => ({ ...acc, [key]: true }), {}));
|
|
126
|
+
\t\t\treturn;
|
|
127
|
+
\t\t}
|
|
128
|
+
|
|
129
|
+
\t\tsubmitMutation.mutate(formData);
|
|
130
|
+
\t};
|
|
131
|
+
|
|
132
|
+
\treturn {
|
|
133
|
+
\t\tformData,${getMethod ? `
|
|
134
|
+
\t\tdata,` : ''}
|
|
135
|
+
\t\terrors,
|
|
136
|
+
\t\ttouched,
|
|
137
|
+
\t\tloading: ${getMethod ? 'isLoading || ' : ''}submitMutation.isPending,
|
|
138
|
+
\t\thandleChange,
|
|
139
|
+
\t\thandleSubmit,${getMethod ? `
|
|
140
|
+
\t\trefetch,` : ''}
|
|
141
|
+
\t};
|
|
142
|
+
};
|
|
143
|
+
`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const method = methods.find(m => m.name === primaryMethod) || methods[0];
|
|
147
|
+
|
|
148
|
+
return `import { useState, useEffect } from 'react';
|
|
149
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
150
|
+
import { ${serviceName}QueryOptions } from '../../../../queries/${serviceName}Query';
|
|
151
|
+
import { showToastNotification } from '../../../../helpers/common';
|
|
152
|
+
import { validate${screenName} } from '../validation';
|
|
153
|
+
|
|
154
|
+
export const use${screenName} = () => {
|
|
155
|
+
\tconst queryClient = useQueryClient();
|
|
156
|
+
\tconst [formData, setFormData] = useState({
|
|
157
|
+
${formFieldsInit}
|
|
158
|
+
\t});
|
|
159
|
+
\tconst [errors, setErrors] = useState({});
|
|
160
|
+
\tconst [touched, setTouched] = useState({});
|
|
161
|
+
|
|
162
|
+
\t// Fetch data using React Query
|
|
163
|
+
\tconst { data, isLoading, error, refetch } = useQuery(
|
|
164
|
+
\t\t${serviceName}QueryOptions.${method.name}(formData)
|
|
165
|
+
\t);
|
|
166
|
+
|
|
167
|
+
\tuseEffect(() => {
|
|
168
|
+
\t\tif (error) {
|
|
169
|
+
\t\t\tshowToastNotification(error?.message || 'Something went wrong');
|
|
170
|
+
\t\t}
|
|
171
|
+
\t}, [error]);
|
|
172
|
+
|
|
173
|
+
\tconst handleChange = (value, field) => {
|
|
174
|
+
\t\tconst updatedFormData = { ...formData, [field]: value };
|
|
175
|
+
\t\tsetFormData(updatedFormData);
|
|
176
|
+
\t\tsetTouched(prev => ({ ...prev, [field]: true }));
|
|
177
|
+
\t\t
|
|
178
|
+
\t\t// Re-validate on change
|
|
179
|
+
\t\tconst validationErrors = validate${screenName}(updatedFormData);
|
|
180
|
+
\t\tsetErrors(validationErrors);
|
|
181
|
+
\t};
|
|
182
|
+
|
|
183
|
+
\tconst handleSubmit = async () => {
|
|
184
|
+
\t\tconst validationErrors = validate${screenName}(formData);
|
|
185
|
+
\t\tif (Object.keys(validationErrors).length > 0) {
|
|
186
|
+
\t\t\tsetErrors(validationErrors);
|
|
187
|
+
\t\t\tsetTouched(Object.keys(formData).reduce((acc, key) => ({ ...acc, [key]: true }), {}));
|
|
188
|
+
\t\t\treturn;
|
|
189
|
+
\t\t}
|
|
190
|
+
|
|
191
|
+
\t\ttry {
|
|
192
|
+
\t\t\tawait refetch();
|
|
193
|
+
\t\t\tshowToastNotification('Success!');
|
|
194
|
+
\t\t} catch (error) {
|
|
195
|
+
\t\t\tshowToastNotification(error?.message || 'Something went wrong');
|
|
196
|
+
\t\t}
|
|
197
|
+
\t};
|
|
198
|
+
|
|
199
|
+
\treturn {
|
|
200
|
+
\t\tformData,
|
|
201
|
+
\t\tdata,
|
|
202
|
+
\t\terrors,
|
|
203
|
+
\t\ttouched,
|
|
204
|
+
\t\tloading: isLoading,
|
|
205
|
+
\t\thandleChange,
|
|
206
|
+
\t\thandleSubmit,
|
|
207
|
+
\t\trefetch,
|
|
208
|
+
\t};
|
|
209
|
+
};
|
|
210
|
+
`;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
module.exports = {
|
|
214
|
+
generateApiServiceTemplate,
|
|
215
|
+
generateQueryOptionsTemplate,
|
|
216
|
+
generateApiIntegratedHookTemplate,
|
|
217
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const generateConstantsTemplate = (formFields = []) => {
|
|
2
|
+
const dropdownFields = formFields.filter(f => f.type === 'dropdown' || f.type === 'radio');
|
|
3
|
+
|
|
4
|
+
if (dropdownFields.length === 0) {
|
|
5
|
+
return `// Add your constants here
|
|
6
|
+
export const DEFAULT_OPTION = [
|
|
7
|
+
\t{ label: 'Yes', value: 'yes' },
|
|
8
|
+
\t{ label: 'No', value: 'no' },
|
|
9
|
+
];
|
|
10
|
+
`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const constants = dropdownFields.map(field => {
|
|
14
|
+
const constantName = field.name.toUpperCase() + '_OPTIONS';
|
|
15
|
+
return `export const ${constantName} = [
|
|
16
|
+
\t{ label: 'Option 1', value: '1' },
|
|
17
|
+
\t{ label: 'Option 2', value: '2' },
|
|
18
|
+
\t// Add more options as needed
|
|
19
|
+
];`;
|
|
20
|
+
}).join('\n\n');
|
|
21
|
+
|
|
22
|
+
return `// Dropdown and Radio button options
|
|
23
|
+
${constants}
|
|
24
|
+
|
|
25
|
+
// Common options
|
|
26
|
+
export const DEFAULT_OPTION = [
|
|
27
|
+
\t{ label: 'Yes', value: 'yes' },
|
|
28
|
+
\t{ label: 'No', value: 'no' },
|
|
29
|
+
];
|
|
30
|
+
`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = { generateConstantsTemplate };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const generateConstantsTemplate = (formFields = []) => {
|
|
2
|
+
const dropdownFields = formFields?.filter(f => f.type === 'dropdown') || [];
|
|
3
|
+
|
|
4
|
+
if (dropdownFields.length === 0) {
|
|
5
|
+
return `// Add your constants here
|
|
6
|
+
export const EXAMPLE_CONSTANT = 'value';
|
|
7
|
+
`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const constants = dropdownFields.map(field => {
|
|
11
|
+
const options = field.options?.map(opt => `\t{ label: '${opt}', value: '${opt.toUpperCase().replace(/\s+/g, '_')}' },`).join('\n') || '';
|
|
12
|
+
return `export const ${field.name.toUpperCase()}_OPTIONS = [\n${options}\n];`;
|
|
13
|
+
}).join('\n\n');
|
|
14
|
+
|
|
15
|
+
return `// Dropdown options\n${constants}\n`;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = { generateConstantsTemplate };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Field type configurations
|
|
2
|
+
const FIELD_TYPES = {
|
|
3
|
+
TEXT: 'text',
|
|
4
|
+
EMAIL: 'email',
|
|
5
|
+
NUMBER: 'number',
|
|
6
|
+
DATE: 'date',
|
|
7
|
+
DROPDOWN: 'dropdown',
|
|
8
|
+
RADIO: 'radio',
|
|
9
|
+
CHECKBOX: 'checkbox',
|
|
10
|
+
FILE: 'file'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const FIELD_WIDTHS = {
|
|
14
|
+
FULL: 'full',
|
|
15
|
+
HALF: 'half'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const generateFormField = (field) => {
|
|
19
|
+
const { name, type, label, width = 'full' } = field;
|
|
20
|
+
|
|
21
|
+
const containerStyle = width === 'half' ? 'styles.headWidth' : '{ width: \'100%\' }';
|
|
22
|
+
|
|
23
|
+
switch (type) {
|
|
24
|
+
case FIELD_TYPES.DATE:
|
|
25
|
+
return generateDateField(name, label, containerStyle);
|
|
26
|
+
|
|
27
|
+
case FIELD_TYPES.DROPDOWN:
|
|
28
|
+
return generateDropdownField(name, label, containerStyle);
|
|
29
|
+
|
|
30
|
+
case FIELD_TYPES.RADIO:
|
|
31
|
+
return generateRadioField(name, label, containerStyle);
|
|
32
|
+
|
|
33
|
+
case FIELD_TYPES.CHECKBOX:
|
|
34
|
+
return generateCheckboxField(name, label);
|
|
35
|
+
|
|
36
|
+
case FIELD_TYPES.FILE:
|
|
37
|
+
return generateFileField(name, label, containerStyle);
|
|
38
|
+
|
|
39
|
+
case FIELD_TYPES.TEXT:
|
|
40
|
+
case FIELD_TYPES.EMAIL:
|
|
41
|
+
case FIELD_TYPES.NUMBER:
|
|
42
|
+
default:
|
|
43
|
+
return generateTextInputField(name, label, type, containerStyle);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const generateTextInputField = (name, label, type, containerStyle) => {
|
|
48
|
+
const keyboardType = type === 'number' ? 'numeric' : type === 'email' ? 'email-address' : 'default';
|
|
49
|
+
const autoCapitalize = type === 'email' ? 'none' : 'sentences';
|
|
50
|
+
|
|
51
|
+
return `\t\t\t\t\t<View style={${containerStyle}}>
|
|
52
|
+
\t\t\t\t\t\t<FloatingTextInput
|
|
53
|
+
\t\t\t\t\t\t\tlabel="${label}"
|
|
54
|
+
\t\t\t\t\t\t\tvalue={formData?.${name} || ''}
|
|
55
|
+
\t\t\t\t\t\t\tonChangeText={(e) => handleChange(e, '${name}')}
|
|
56
|
+
\t\t\t\t\t\t\ttextInputStyles={styles.floatContentStyle}
|
|
57
|
+
\t\t\t\t\t\t\tinputContainerStyle={styles.floatStyle}
|
|
58
|
+
\t\t\t\t\t\t\tlabelStyles={styles.floatLabelStyle}
|
|
59
|
+
\t\t\t\t\t\t\terror={touched?.${name} && errors?.${name}}
|
|
60
|
+
\t\t\t\t\t\t\tkeyboardType="${keyboardType}"${type === 'email' ? `\n\t\t\t\t\t\t\totherTextInputProps={{ autoCapitalize: '${autoCapitalize}' }}` : ''}
|
|
61
|
+
\t\t\t\t\t\t/>
|
|
62
|
+
\t\t\t\t\t</View>`;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const generateDateField = (name, label, containerStyle) => {
|
|
66
|
+
return `\t\t\t\t\t<TouchableRipple
|
|
67
|
+
\t\t\t\t\t\tpointerEvents="box-only"
|
|
68
|
+
\t\t\t\t\t\tstyle={${containerStyle}}
|
|
69
|
+
\t\t\t\t\t\tonPress={() => handleCalendar('${name}')}
|
|
70
|
+
\t\t\t\t\t>
|
|
71
|
+
\t\t\t\t\t\t<FloatingTextInput
|
|
72
|
+
\t\t\t\t\t\t\tlabel="${label}"
|
|
73
|
+
\t\t\t\t\t\t\tvalue={formikState?.values?.${name} || ''}
|
|
74
|
+
\t\t\t\t\t\t\ttextInputStyles={customStyle.floatContentStyle}
|
|
75
|
+
\t\t\t\t\t\t\tinputContainerStyle={customStyle.floatStyle}
|
|
76
|
+
\t\t\t\t\t\t\tlabelStyle={customStyle.floatLabelStyle}
|
|
77
|
+
\t\t\t\t\t\t\terror={(touched == '${name}' || touched == null) && formikState.errors.${name}}
|
|
78
|
+
\t\t\t\t\t\t\teditable={false}
|
|
79
|
+
\t\t\t\t\t\t\ttrailingIconName={!((touched == '${name}' || touched == null) && formikState.errors.${name}) && 'calendar'}
|
|
80
|
+
\t\t\t\t\t\t\ticonColor={colorConstant?.amcBlue}
|
|
81
|
+
\t\t\t\t\t\t\totherTextInputProps={{ selection: { start: 0 } }}
|
|
82
|
+
\t\t\t\t\t\t/>
|
|
83
|
+
\t\t\t\t\t</TouchableRipple>`;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const generateDropdownField = (name, label, containerStyle) => {
|
|
87
|
+
return `\t\t\t\t\t<TouchableRipple
|
|
88
|
+
\t\t\t\t\t\tpointerEvents="box-only"
|
|
89
|
+
\t\t\t\t\t\tstyle={${containerStyle}}
|
|
90
|
+
\t\t\t\t\t\tonPress={() => openBottomSheet('${name}')}
|
|
91
|
+
\t\t\t\t\t>
|
|
92
|
+
\t\t\t\t\t\t<FloatingTextInput
|
|
93
|
+
\t\t\t\t\t\t\tlabel="${label}"
|
|
94
|
+
\t\t\t\t\t\t\tvalue={formData?.${name}?.label || ''}
|
|
95
|
+
\t\t\t\t\t\t\ttextInputStyles={styles.floatContentStyle}
|
|
96
|
+
\t\t\t\t\t\t\tinputContainerStyle={styles.floatStyle}
|
|
97
|
+
\t\t\t\t\t\t\tlabelStyles={styles.floatLabelStyle}
|
|
98
|
+
\t\t\t\t\t\t\teditable={false}
|
|
99
|
+
\t\t\t\t\t\t\ttrailingIconName="menu-down"
|
|
100
|
+
\t\t\t\t\t\t\ticonColor={colorConstant?.amcBlack}
|
|
101
|
+
\t\t\t\t\t\t\totherTextInputProps={{ selection: { start: 0 } }}
|
|
102
|
+
\t\t\t\t\t\t/>
|
|
103
|
+
\t\t\t\t\t</TouchableRipple>`;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const generateRadioField = (name, label, containerStyle) => {
|
|
107
|
+
return `\t\t\t\t\t<View style={${containerStyle}}>
|
|
108
|
+
\t\t\t\t\t\t<Text style={customStyle?.optionText}>${label}</Text>
|
|
109
|
+
\t\t\t\t\t\t<RadioForm
|
|
110
|
+
\t\t\t\t\t\t\thelperTextStyle={{ height: 0 }}
|
|
111
|
+
\t\t\t\t\t\t\toption={${name.toUpperCase()}_OPTIONS}
|
|
112
|
+
\t\t\t\t\t\t\tvalue={formikState?.values?.${name}}
|
|
113
|
+
\t\t\t\t\t\t\ttextStyle={customStyle?.optionInnerText}
|
|
114
|
+
\t\t\t\t\t\t\tradioInputContainer={customStyle.optionContainer}
|
|
115
|
+
\t\t\t\t\t\t\tactiveRadioContainer={customStyle.activeOptionContainer}
|
|
116
|
+
\t\t\t\t\t\t\tactiveRadioText={customStyle.activeOptionText}
|
|
117
|
+
\t\t\t\t\t\t\tonChange={(e) => handleChange(e, '${name}')}
|
|
118
|
+
\t\t\t\t\t\t/>
|
|
119
|
+
\t\t\t\t\t</View>`;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const generateCheckboxField = (name, label) => {
|
|
123
|
+
return `\t\t\t\t\t<View style={customStyle.lastHeadContainer}>
|
|
124
|
+
\t\t\t\t\t\t<View style={customStyle.checkBoxContainer}>
|
|
125
|
+
\t\t\t\t\t\t\t<CheckBox
|
|
126
|
+
\t\t\t\t\t\t\t\tsetcheckBoxBtn={() => handleChange(!formikState?.values?.${name}, '${name}')}
|
|
127
|
+
\t\t\t\t\t\t\t\tlightBackground={false}
|
|
128
|
+
\t\t\t\t\t\t\t\tlabelStyle={customStyle.StartToday}
|
|
129
|
+
\t\t\t\t\t\t\t\tposition={'leading'}
|
|
130
|
+
\t\t\t\t\t\t\t\tlabel={''}
|
|
131
|
+
\t\t\t\t\t\t\t\tgreyColor={true}
|
|
132
|
+
\t\t\t\t\t\t\t\tcheckBoxBtn={formikState?.values?.${name}}
|
|
133
|
+
\t\t\t\t\t\t\t/>
|
|
134
|
+
\t\t\t\t\t\t</View>
|
|
135
|
+
\t\t\t\t\t\t<Text style={customStyle.bottomContainertTextLower}>${label}</Text>
|
|
136
|
+
\t\t\t\t\t</View>`;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const generateFileField = (name, label, containerStyle) => {
|
|
140
|
+
return `\t\t\t\t\t<TouchableRipple
|
|
141
|
+
\t\t\t\t\t\tpointerEvents="box-only"
|
|
142
|
+
\t\t\t\t\t\tstyle={${containerStyle}}
|
|
143
|
+
\t\t\t\t\t\tonPress={() => handleDocumentPicker('${name}')}
|
|
144
|
+
\t\t\t\t\t>
|
|
145
|
+
\t\t\t\t\t\t<FloatingTextInput
|
|
146
|
+
\t\t\t\t\t\t\tlabel="${label}"
|
|
147
|
+
\t\t\t\t\t\t\tvalue={formikState?.values?.${name}?.name || formikState?.values?.${name} && 'File selected' || 'Choose'}
|
|
148
|
+
\t\t\t\t\t\t\ttextInputStyles={customStyle.floatContentStyle}
|
|
149
|
+
\t\t\t\t\t\t\tlabelStyles={customStyle.labelStyles}
|
|
150
|
+
\t\t\t\t\t\t\teditable={false}
|
|
151
|
+
\t\t\t\t\t\t\tnumberOfLines={1}
|
|
152
|
+
\t\t\t\t\t\t\terror={(touched == '${name}' || touched == null) && formikState.errors.${name}}
|
|
153
|
+
\t\t\t\t\t\t\ttrailingIconName="attachment"
|
|
154
|
+
\t\t\t\t\t\t\ticonColor={colorConstant?.amcBlack}
|
|
155
|
+
\t\t\t\t\t\t\totherTextInputProps={{ selection: { start: 0 } }}
|
|
156
|
+
\t\t\t\t\t\t\tdisabled={true}
|
|
157
|
+
\t\t\t\t\t\t/>
|
|
158
|
+
\t\t\t\t\t</TouchableRipple>`;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
module.exports = {
|
|
162
|
+
generateFormField,
|
|
163
|
+
FIELD_TYPES,
|
|
164
|
+
FIELD_WIDTHS
|
|
165
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const generateInitialValues = (formFields = []) => {
|
|
2
|
+
if (!formFields || formFields.length === 0) {
|
|
3
|
+
return '\t\tloader: false,';
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const fields = formFields.map(field => {
|
|
7
|
+
let defaultValue;
|
|
8
|
+
switch (field.type) {
|
|
9
|
+
case 'checkbox':
|
|
10
|
+
defaultValue = 'false';
|
|
11
|
+
break;
|
|
12
|
+
case 'dropdown':
|
|
13
|
+
case 'radio':
|
|
14
|
+
defaultValue = 'null';
|
|
15
|
+
break;
|
|
16
|
+
case 'file':
|
|
17
|
+
defaultValue = 'null';
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
defaultValue = "''";
|
|
21
|
+
}
|
|
22
|
+
return `\t\t${field.name}: ${defaultValue},`;
|
|
23
|
+
}).join('\n');
|
|
24
|
+
|
|
25
|
+
const hasDropdown = formFields.some(f => f.type === 'dropdown');
|
|
26
|
+
const hasDate = formFields.some(f => f.type === 'date');
|
|
27
|
+
|
|
28
|
+
let additionalFields = '';
|
|
29
|
+
if (hasDropdown) {
|
|
30
|
+
additionalFields += '\n\t\topenBottomNote: false,\n\t\tdropDownType: \'\',';
|
|
31
|
+
}
|
|
32
|
+
if (hasDate) {
|
|
33
|
+
additionalFields += '\n\t\topenCalendar: false,\n\t\tcalendarField: \'\',';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return `\t\tloader: false,\n${fields}${additionalFields}`;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const generateHookTemplate = (hookName, hasForm = false, formFields = [], eventName = null) => {
|
|
40
|
+
if (hasForm) {
|
|
41
|
+
const hasDropdown = formFields.some(f => f.type === 'dropdown');
|
|
42
|
+
const hasDate = formFields.some(f => f.type === 'date');
|
|
43
|
+
|
|
44
|
+
return `import { useState } from 'react';
|
|
45
|
+
import { showToastNotification } from '../../../../helpers/common';${hasDate ? '\nimport moment from \'moment\';' : ''}
|
|
46
|
+
import { validate${hookName} } from '../validation';
|
|
47
|
+
|
|
48
|
+
export const use${hookName} = () => {
|
|
49
|
+
\tconst [formData, setFormData] = useState({
|
|
50
|
+
${formFields.map(f => `\t\t${f.name}: '',`).join('\n')}
|
|
51
|
+
\t});
|
|
52
|
+
\tconst [errors, setErrors] = useState({});
|
|
53
|
+
\tconst [touched, setTouched] = useState({});
|
|
54
|
+
\tconst [loading, setLoading] = useState(false);${hasDropdown ? '\n\tconst [bottomSheetType, setBottomSheetType] = useState(null);' : ''}${hasDate ? '\n\tconst [openCalendar, setOpenCalendar] = useState(false);' : ''}
|
|
55
|
+
|
|
56
|
+
\tconst handleChange = (value, field) => {
|
|
57
|
+
\t\tconst updatedFormData = { ...formData, [field]: value };
|
|
58
|
+
\t\tsetFormData(updatedFormData);
|
|
59
|
+
\t\tsetTouched(prev => ({ ...prev, [field]: true }));
|
|
60
|
+
\t\t
|
|
61
|
+
\t\t// Re-validate on change
|
|
62
|
+
\t\tconst validationErrors = validate${hookName}(updatedFormData);
|
|
63
|
+
\t\tsetErrors(validationErrors);
|
|
64
|
+
\t};
|
|
65
|
+
|
|
66
|
+
\tconst handleSubmit = async () => {
|
|
67
|
+
\t\tconst validationErrors = validate${hookName}(formData);
|
|
68
|
+
\t\tif (Object.keys(validationErrors).length > 0) {
|
|
69
|
+
\t\t\tsetErrors(validationErrors);
|
|
70
|
+
\t\t\tsetTouched(Object.keys(formData).reduce((acc, key) => ({ ...acc, [key]: true }), {}));
|
|
71
|
+
\t\t\t// Validation errors are shown inline below each field
|
|
72
|
+
\t\t\treturn;
|
|
73
|
+
\t\t}
|
|
74
|
+
|
|
75
|
+
\t\tsetLoading(true);
|
|
76
|
+
\t\ttry {
|
|
77
|
+
\t\t\t// Add your API call here
|
|
78
|
+
\t\t\tshowToastNotification('Success!');
|
|
79
|
+
\t\t} catch (error) {
|
|
80
|
+
\t\t\tshowToastNotification(error?.message || 'Something went wrong');
|
|
81
|
+
\t\t} finally {
|
|
82
|
+
\t\t\tsetLoading(false);
|
|
83
|
+
\t\t}
|
|
84
|
+
\t};${hasDropdown ? `
|
|
85
|
+
|
|
86
|
+
\tconst openBottomSheet = (type) => {
|
|
87
|
+
\t\tsetBottomSheetType(type);
|
|
88
|
+
\t};` : ''}
|
|
89
|
+
|
|
90
|
+
\treturn {
|
|
91
|
+
\t\tformData,
|
|
92
|
+
\t\terrors,
|
|
93
|
+
\t\ttouched,
|
|
94
|
+
\t\tloading,
|
|
95
|
+
\t\thandleChange,
|
|
96
|
+
\t\thandleSubmit,${hasDropdown ? '\n\t\topenBottomSheet,\n\t\tbottomSheetType,' : ''}${hasDate ? '\n\t\topenCalendar,\n\t\tsetOpenCalendar,' : ''}
|
|
97
|
+
\t};
|
|
98
|
+
};
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return `import { useState, useEffect } from 'react';
|
|
103
|
+
import { useQuery } from '@tanstack/react-query';
|
|
104
|
+
import { showToastNotification } from '../../../../helpers/common';
|
|
105
|
+
|
|
106
|
+
export const use${hookName} = ({ navigation }) => {
|
|
107
|
+
\tconst [loading, setLoading] = useState(false);
|
|
108
|
+
|
|
109
|
+
\tconst { data, isLoading, error } = useQuery({
|
|
110
|
+
\t\tqueryKey: ['${hookName.toLowerCase()}'],
|
|
111
|
+
\t\tqueryFn: async () => {
|
|
112
|
+
\t\t\t// Add your API call here
|
|
113
|
+
\t\t\treturn null;
|
|
114
|
+
\t\t},
|
|
115
|
+
\t\tenabled: true,
|
|
116
|
+
\t});
|
|
117
|
+
|
|
118
|
+
\tuseEffect(() => {
|
|
119
|
+
\t\tif (error) {
|
|
120
|
+
\t\t\tshowToastNotification(error?.message || 'Something went wrong');
|
|
121
|
+
\t\t}
|
|
122
|
+
\t}, [error]);
|
|
123
|
+
|
|
124
|
+
\treturn {
|
|
125
|
+
\t\tloading: isLoading,
|
|
126
|
+
\t\tdata,
|
|
127
|
+
\t};
|
|
128
|
+
};
|
|
129
|
+
`;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
module.exports = { generateHookTemplate };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const generateInitialValues = (formFields = []) => {
|
|
2
|
+
if (!formFields || formFields.length === 0) {
|
|
3
|
+
return '\t\tloader: false,';
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const fields = formFields.map(field => {
|
|
7
|
+
const defaultValue = field.type === 'checkbox' ? 'false' : field.type === 'dropdown' ? 'null' : "''";
|
|
8
|
+
return `\t\t${field.name}: ${defaultValue},`;
|
|
9
|
+
}).join('\n');
|
|
10
|
+
|
|
11
|
+
return `\t\tloader: false,\n${fields}`;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const generateHookTemplate = (hookName, hasForm = false, formFields = []) => {
|
|
15
|
+
if (hasForm) {
|
|
16
|
+
const initialValues = generateInitialValues(formFields);
|
|
17
|
+
|
|
18
|
+
return `import { useState } from 'react';
|
|
19
|
+
import { useFormik } from 'formik';
|
|
20
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
21
|
+
import { showToastNotification } from '../../../helpers/common';
|
|
22
|
+
import { validationSchema } from './validation';
|
|
23
|
+
|
|
24
|
+
export const use${hookName} = ({ navigation, params, user }) => {
|
|
25
|
+
\tconst queryClient = useQueryClient();
|
|
26
|
+
\tconst [touched, setTouched] = useState([null]);
|
|
27
|
+
|
|
28
|
+
\tconst initialValues = {
|
|
29
|
+
${initialValues}
|
|
30
|
+
\t};
|
|
31
|
+
|
|
32
|
+
\tconst onSubmit = async (values, formikAction) => {
|
|
33
|
+
\t\tformikState.setFieldValue('loader', true);
|
|
34
|
+
|
|
35
|
+
\t\ttry {
|
|
36
|
+
\t\t\t// Add your API call here
|
|
37
|
+
\t\t\t// const response = await queryClient.fetchQuery(...);
|
|
38
|
+
\t\t\t
|
|
39
|
+
\t\t\tshowToastNotification('Success!');
|
|
40
|
+
\t\t\tnavigation.goBack();
|
|
41
|
+
\t\t} catch (error) {
|
|
42
|
+
\t\t\tformikState.setFieldValue('loader', false);
|
|
43
|
+
\t\t\tconst errorMessage = error?.message || 'Something went wrong';
|
|
44
|
+
\t\t\tshowToastNotification(errorMessage);
|
|
45
|
+
\t\t}
|
|
46
|
+
\t};
|
|
47
|
+
|
|
48
|
+
\tconst formikState = useFormik({
|
|
49
|
+
\t\tinitialValues,
|
|
50
|
+
\t\tenableReinitialize: true,
|
|
51
|
+
\t\tvalidationSchema,
|
|
52
|
+
\t\tonSubmit,
|
|
53
|
+
\t});
|
|
54
|
+
|
|
55
|
+
\tconst handleChange = (data, type) => {
|
|
56
|
+
\t\tformikState.setFieldValue(type, data);
|
|
57
|
+
\t\tlet touchedType = touched;
|
|
58
|
+
\t\tif (touchedType.length === 1 && touchedType[0] === null) {
|
|
59
|
+
\t\t\ttouchedType = [];
|
|
60
|
+
\t\t}
|
|
61
|
+
\t\tif (!touchedType.includes(type)) {
|
|
62
|
+
\t\t\tsetTouched([...touchedType, type]);
|
|
63
|
+
\t\t}
|
|
64
|
+
\t};
|
|
65
|
+
|
|
66
|
+
\tconst handleSubmitValue = () => {
|
|
67
|
+
\t\tsetTouched([null]);
|
|
68
|
+
\t\tformikState.handleSubmit();
|
|
69
|
+
\t};
|
|
70
|
+
|
|
71
|
+
\treturn {
|
|
72
|
+
\t\tformikState,
|
|
73
|
+
\t\ttouched,
|
|
74
|
+
\t\thandleChange,
|
|
75
|
+
\t\thandleSubmitValue,
|
|
76
|
+
\t};
|
|
77
|
+
};
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return `import { useState, useEffect } from 'react';
|
|
82
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
83
|
+
import { showToastNotification } from '../../../helpers/common';
|
|
84
|
+
|
|
85
|
+
export const use${hookName} = ({ navigation, params, user }) => {
|
|
86
|
+
\tconst queryClient = useQueryClient();
|
|
87
|
+
\tconst [loading, setLoading] = useState(false);
|
|
88
|
+
|
|
89
|
+
\tconst { data, isLoading, error } = useQuery({
|
|
90
|
+
\t\tqueryKey: ['${hookName.toLowerCase()}'],
|
|
91
|
+
\t\tqueryFn: async () => {
|
|
92
|
+
\t\t\t// Add your API call here
|
|
93
|
+
\t\t\t// return await queryClient.fetchQuery(...);
|
|
94
|
+
\t\t\treturn null;
|
|
95
|
+
\t\t},
|
|
96
|
+
\t\tenabled: true,
|
|
97
|
+
\t});
|
|
98
|
+
|
|
99
|
+
\tuseEffect(() => {
|
|
100
|
+
\t\tif (error) {
|
|
101
|
+
\t\t\tshowToastNotification(error?.message || 'Something went wrong');
|
|
102
|
+
\t\t}
|
|
103
|
+
\t}, [error]);
|
|
104
|
+
|
|
105
|
+
\treturn {
|
|
106
|
+
\t\tloading: isLoading,
|
|
107
|
+
\t\tdata,
|
|
108
|
+
\t};
|
|
109
|
+
};
|
|
110
|
+
`;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
module.exports = { generateHookTemplate };
|