@fovestta2/web-react 1.2.1 → 1.2.2

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.
@@ -42,6 +42,10 @@ export interface FormSection {
42
42
  fields: FormColumn[];
43
43
  isRepeatable?: boolean;
44
44
  sectionKey?: string;
45
+ /** Section-specific columns per row. Overrides the form-level config. */
46
+ maxColsPerRow?: number;
47
+ /** Custom CSS class for the section wrapper */
48
+ className?: string;
45
49
  }
46
50
  export interface FormConfig {
47
51
  sections: FormSection[];
@@ -52,13 +56,18 @@ export interface FormConfig {
52
56
  onCancel: () => void;
53
57
  cancelLabel?: string;
54
58
  formTitle?: string;
59
+ /** Default columns per row for all sections */
55
60
  maxColsPerRow?: number;
56
61
  disableSubmit?: boolean;
57
62
  disableCancel?: boolean;
58
63
  hideSubmit?: boolean;
59
64
  hideCancel?: boolean;
65
+ /** Custom CSS class for the form element */
66
+ className?: string;
60
67
  }
61
68
  export interface AddUpdateFormProps {
62
69
  config: FormConfig;
70
+ /** When true, wraps the form inside a centered modal overlay */
71
+ asModal?: boolean;
63
72
  }
64
73
  export declare const AddUpdateForm: React.FC<AddUpdateFormProps>;
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
+ // import React, { useState, useEffect } from 'react';
3
+ // import { ValidationSchema } from '@fovestta2/validation-engine';
2
4
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
5
  if (k2 === undefined) k2 = k;
4
6
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -24,6 +26,308 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
26
  };
25
27
  Object.defineProperty(exports, "__esModule", { value: true });
26
28
  exports.AddUpdateForm = void 0;
29
+ // // Import all Fv controls
30
+ // import { FvEntryField } from './FvEntryField';
31
+ // import { FvDropdown } from './FvDropdown';
32
+ // import { FvNumberField } from './FvNumberField';
33
+ // import { FvDateField } from './FvDateField';
34
+ // import { FvMonthYearField } from './FvMonthYearField';
35
+ // import { FvFileSelector } from './FvFileSelector';
36
+ // import { FvImageSelector } from './FvImageSelector';
37
+ // import { FvRichTextEditor } from './FvRichTextEditor';
38
+ // import { FvNameCode } from './FvNameCode';
39
+ // import { FvPhoneField } from './FvPhoneField';
40
+ // import { FvUanField } from './FvUanField';
41
+ // import { FvPfField } from './FvPfField';
42
+ // import { FvEsiField } from './FvEsiField';
43
+ // import { FvIfscField } from './FvIfscField';
44
+ // import { FvMicrField } from './FvMicrField';
45
+ // import { FvIbanField } from './FvIbanField';
46
+ // import { FvEmailField } from './FvEmailField';
47
+ // import { FvPasswordField } from './FvPasswordField';
48
+ // import { FvToggle } from './FvToggle';
49
+ // import { FvCheckbox } from './FvCheckbox';
50
+ // import { FvRadioGroup } from './FvRadioGroup';
51
+ // import { FvServicePeriod } from './FvServicePeriod';
52
+ // import { FvDocumentField } from './FvDocumentField';
53
+ // import { FvTimeField } from './FvTimeField';
54
+ // export type FieldType = 'text' | 'email' | 'number' | 'select' | 'name-code' | 'checkbox' | 'textarea' | 'date' | 'password' | 'radio' | 'file' | 'month-year' | 'phone' | 'uan' | 'pf' | 'esi' | 'ifsc' | 'micr' | 'iban' | 'service-period' | 'scan' | 'document' | 'time' | 'toggle';
55
+ // export type ValidationType = 'required' | 'email' | 'minLength' | 'maxLength' | 'pattern' | 'min' | 'max' | 'custom' | 'duplicate' | 'passwordComplexity';
56
+ // export interface FieldValidation {
57
+ // type: ValidationType;
58
+ // value?: any;
59
+ // message?: string;
60
+ // errorKey?: string;
61
+ // params?: any;
62
+ // }
63
+ // export interface FormColumn {
64
+ // name: string;
65
+ // label: string;
66
+ // type: FieldType;
67
+ // placeholder?: string;
68
+ // required?: boolean;
69
+ // options?: { label: string; value: any }[];
70
+ // validations?: FieldValidation[];
71
+ // value?: any;
72
+ // disabled?: boolean | ((formData: any) => boolean);
73
+ // className?: string;
74
+ // hidden?: boolean;
75
+ // hint?: string;
76
+ // colSpan?: number;
77
+ // onChange?: (value: any, formData: any) => void;
78
+ // accept?: string;
79
+ // filePreview?: boolean;
80
+ // showTimePicker?: boolean;
81
+ // allowAlphabetsOnly?: boolean;
82
+ // maxLength?: number;
83
+ // layout?: 'vertical' | 'horizontal';
84
+ // servicePeriodConfig?: { startField: string; endField: string };
85
+ // }
86
+ // export interface FormSection {
87
+ // title?: string;
88
+ // fields: FormColumn[];
89
+ // isRepeatable?: boolean;
90
+ // sectionKey?: string;
91
+ // }
92
+ // export interface FormConfig {
93
+ // sections: FormSection[];
94
+ // submitLabel?: string;
95
+ // resetLabel?: string;
96
+ // onSubmit: (data: any) => void;
97
+ // onReset?: () => void;
98
+ // onCancel: () => void;
99
+ // cancelLabel?: string;
100
+ // formTitle?: string;
101
+ // maxColsPerRow?: number;
102
+ // disableSubmit?: boolean;
103
+ // disableCancel?: boolean;
104
+ // hideSubmit?: boolean;
105
+ // hideCancel?: boolean;
106
+ // }
107
+ // export interface AddUpdateFormProps {
108
+ // config: FormConfig;
109
+ // }
110
+ // export const AddUpdateForm: React.FC<AddUpdateFormProps> = ({ config }) => {
111
+ // const [formData, setFormData] = useState<Record<string, any>>({});
112
+ // useEffect(() => {
113
+ // // Initialize form structure
114
+ // const initialData: Record<string, any> = {};
115
+ // config.sections.forEach((section) => {
116
+ // if (section.isRepeatable && section.sectionKey) {
117
+ // // Init as array with one empty object
118
+ // const groupObj: Record<string, any> = {};
119
+ // section.fields.forEach((col) => {
120
+ // groupObj[col.name] = col.value || '';
121
+ // });
122
+ // initialData[section.sectionKey] = [groupObj];
123
+ // } else {
124
+ // section.fields.forEach((col) => {
125
+ // initialData[col.name] = col.value || '';
126
+ // });
127
+ // }
128
+ // });
129
+ // setFormData(initialData);
130
+ // }, [config]);
131
+ // const handleFieldChange = (name: string, value: any, sectionKey?: string, index?: number) => {
132
+ // setFormData((prev) => {
133
+ // const copy = { ...prev };
134
+ // if (sectionKey && index !== undefined) {
135
+ // copy[sectionKey] = [...(copy[sectionKey] || [])];
136
+ // copy[sectionKey][index] = { ...copy[sectionKey][index], [name]: value };
137
+ // } else {
138
+ // copy[name] = value;
139
+ // }
140
+ // return copy;
141
+ // });
142
+ // // We do a delayed trigger to on change to simulate reactive form passing state
143
+ // setTimeout(() => {
144
+ // const col = findColumn(name, sectionKey);
145
+ // if (col && col.onChange) {
146
+ // // Needs recent state
147
+ // setFormData((latestState) => {
148
+ // col.onChange!(value, latestState);
149
+ // return latestState;
150
+ // });
151
+ // }
152
+ // }, 0);
153
+ // };
154
+ // const findColumn = (name: string, sectionKey?: string): FormColumn | undefined => {
155
+ // for (const section of config.sections) {
156
+ // if (sectionKey && section.sectionKey !== sectionKey) continue;
157
+ // const col = section.fields.find(c => c.name === name);
158
+ // if (col) return col;
159
+ // }
160
+ // return undefined;
161
+ // };
162
+ // const addSectionItem = (sectionKey: string, fields: FormColumn[]) => {
163
+ // setFormData((prev) => {
164
+ // const copy = { ...prev };
165
+ // const groupObj: Record<string, any> = {};
166
+ // fields.forEach((col) => { groupObj[col.name] = col.value || ''; });
167
+ // copy[sectionKey] = [...(copy[sectionKey] || []), groupObj];
168
+ // return copy;
169
+ // });
170
+ // };
171
+ // const removeSectionItem = (sectionKey: string, index: number) => {
172
+ // setFormData((prev) => {
173
+ // const copy = { ...prev };
174
+ // if (copy[sectionKey] && copy[sectionKey].length > 1) {
175
+ // const arr = [...copy[sectionKey]];
176
+ // arr.splice(index, 1);
177
+ // copy[sectionKey] = arr;
178
+ // }
179
+ // return copy;
180
+ // });
181
+ // };
182
+ // const handleSubmit = (e: React.FormEvent) => {
183
+ // e.preventDefault();
184
+ // config.onSubmit(formData);
185
+ // };
186
+ // const getSchema = (column: FormColumn): ValidationSchema => {
187
+ // const rules: any[] = [];
188
+ // const errorPriority: string[] = [];
189
+ // if (column.validations) {
190
+ // column.validations.forEach((v) => {
191
+ // if (v.type === 'required') { rules.push({ name: 'required', params: { enabled: true }, errorKey: 'ERR_REQUIRED', message: v.message }); errorPriority.push('required'); }
192
+ // if (v.type === 'email') { rules.push({ name: 'regex', params: { pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$' }, errorKey: 'ERR_REGEX_MISMATCH', message: v.message }); errorPriority.push('regex'); }
193
+ // if (v.type === 'minLength') { rules.push({ name: 'minLength', params: { value: v.value }, errorKey: 'ERR_MIN_LENGTH', message: v.message }); errorPriority.push('minLength'); }
194
+ // if (v.type === 'maxLength') { rules.push({ name: 'maxLength', params: { value: v.value }, errorKey: 'ERR_MAX_LENGTH', message: v.message }); errorPriority.push('maxLength'); }
195
+ // if (v.type === 'pattern') { rules.push({ name: 'regex', params: { pattern: v.value }, errorKey: 'ERR_REGEX_MISMATCH', message: v.message }); errorPriority.push('regex'); }
196
+ // });
197
+ // }
198
+ // return { controlType: 'EntryField' as any, rules, errorPriority };
199
+ // };
200
+ // const isFieldDisabled = (column: FormColumn): boolean => {
201
+ // if (typeof column.disabled === 'function') {
202
+ // return column.disabled(formData);
203
+ // }
204
+ // return column.disabled || false;
205
+ // };
206
+ // const renderField = (column: FormColumn, value: any, sectionKey?: string, index?: number) => {
207
+ // const disabled = isFieldDisabled(column);
208
+ // const schema = getSchema(column);
209
+ // const onChange = (val: any) => handleFieldChange(column.name, val, sectionKey, index);
210
+ // if (column.hidden) return null;
211
+ // switch (column.type) {
212
+ // case 'text':
213
+ // return <FvEntryField label={column.label} placeholder={column.placeholder} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
214
+ // case 'email':
215
+ // return <FvEmailField label={column.label} placeholder={column.placeholder} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
216
+ // case 'password':
217
+ // return <FvPasswordField label={column.label} placeholder={column.placeholder} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
218
+ // case 'number':
219
+ // return <FvNumberField label={column.label} placeholder={column.placeholder} value={value || null} schema={schema} disabled={disabled} onChange={onChange} />;
220
+ // case 'select':
221
+ // return <FvDropdown label={column.label} placeholder={column.placeholder} options={column.options || []} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
222
+ // case 'checkbox':
223
+ // return <FvCheckbox label={column.label} value={value || false} disabled={disabled} onChange={onChange} />;
224
+ // case 'toggle':
225
+ // return <FvToggle label={column.label} value={value || false} disabled={disabled} onChange={onChange} />;
226
+ // case 'radio':
227
+ // return <FvRadioGroup label={column.label} options={column.options || []} value={value || ''} disabled={disabled} layout={column.layout} onChange={onChange} />;
228
+ // case 'date':
229
+ // return <FvDateField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
230
+ // case 'month-year':
231
+ // return <FvMonthYearField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
232
+ // case 'textarea':
233
+ // return <FvRichTextEditor label={column.label} placeholder={column.placeholder} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
234
+ // case 'file':
235
+ // if (column.accept?.startsWith('image') || column.filePreview) {
236
+ // return <FvImageSelector label={column.label} placeholder={column.placeholder} value={value || null} schema={schema} disabled={disabled} onChange={onChange} />;
237
+ // }
238
+ // return <FvFileSelector label={column.label} placeholder={column.placeholder} accept={column.accept} value={value || null} schema={schema} disabled={disabled} onChange={onChange} />;
239
+ // case 'name-code':
240
+ // return <FvNameCode label={column.label} placeholder={column.placeholder} options={column.options?.map(o => ({ code: o.value, name: o.label, value: o.value })) || []} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
241
+ // case 'phone':
242
+ // return <FvPhoneField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
243
+ // case 'uan':
244
+ // return <FvUanField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
245
+ // case 'pf':
246
+ // return <FvPfField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
247
+ // case 'esi':
248
+ // return <FvEsiField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
249
+ // case 'ifsc':
250
+ // return <FvIfscField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
251
+ // case 'micr':
252
+ // return <FvMicrField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
253
+ // case 'iban':
254
+ // return <FvIbanField label={column.label} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
255
+ // case 'document':
256
+ // return <FvDocumentField label={column.label} placeholder={column.placeholder} accept={column.accept} value={value || null} schema={schema} disabled={disabled} onChange={onChange} />;
257
+ // case 'time':
258
+ // return <FvTimeField label={column.label} placeholder={column.placeholder} value={value || ''} schema={schema} disabled={disabled} onChange={onChange} />;
259
+ // case 'service-period':
260
+ // // Not perfectly mapped due to pure group dependency, simple implementation shown for continuity
261
+ // return <FvEntryField label={column.label} value={value || ''} disabled={true} onChange={onChange} />;
262
+ // default:
263
+ // return null; // unsupported
264
+ // }
265
+ // };
266
+ // return (
267
+ // <div style={{ background: '#f5f5f5', padding: '15px 20px', borderRadius: '8px', maxWidth: '100%', boxSizing: 'border-box' }}>
268
+ // {config.formTitle && (
269
+ // <div style={{ marginBottom: '15px' }}>
270
+ // <h2 style={{ margin: 0, fontSize: '20px', fontWeight: 700, color: '#303030' }}>{config.formTitle}</h2>
271
+ // </div>
272
+ // )}
273
+ // <form onSubmit={handleSubmit} style={{ background: '#fff', padding: '15px', borderRadius: '6px', boxShadow: '0 1px 3px rgba(0,0,0,0.1)' }}>
274
+ // {config.sections.map((section, sIdx) => (
275
+ // <div key={sIdx} style={{ marginBottom: '20px' }}>
276
+ // {section.title && <h3 style={{ fontSize: '16px', fontWeight: 700, color: '#303030', marginBottom: '16px', textTransform: 'uppercase' }}>{section.title}</h3>}
277
+ // {section.isRepeatable && section.sectionKey ? (
278
+ // (formData[section.sectionKey] || []).map((group: any, idx: number) => (
279
+ // <div key={idx} style={{ position: 'relative', border: '1px solid #eee', borderRadius: '8px', padding: '20px 80px 20px 20px', marginBottom: '15px', background: '#fafafa' }}>
280
+ // <div style={{ position: 'absolute', top: '15px', right: '15px', display: 'flex', gap: '8px' }}>
281
+ // <button type="button" onClick={() => addSectionItem(section.sectionKey!, section.fields)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: '20px', color: '#2ecc71' }}>⊕</button>
282
+ // {(formData[section.sectionKey!] || []).length > 1 && (
283
+ // <button type="button" onClick={() => removeSectionItem(section.sectionKey!, idx)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: '20px', color: '#e74c3c' }}>⊗</button>
284
+ // )}
285
+ // </div>
286
+ // <div style={{ display: 'grid', gridTemplateColumns: `repeat(auto-fit, minmax(200px, 1fr))`, gap: '16px' }}>
287
+ // {section.fields.map((col, cIdx) => (
288
+ // <div key={cIdx} style={{ gridColumn: `span ${col.colSpan || 1}` }}>
289
+ // {renderField(col, group[col.name], section.sectionKey, idx)}
290
+ // </div>
291
+ // ))}
292
+ // </div>
293
+ // </div>
294
+ // ))
295
+ // ) : (
296
+ // <div style={{ display: 'grid', gridTemplateColumns: `repeat(auto-fit, minmax(200px, 1fr))`, gap: '16px' }}>
297
+ // {section.fields.map((col, cIdx) => (
298
+ // <div key={cIdx} style={{ gridColumn: `span ${col.colSpan || 1}` }}>
299
+ // {renderField(col, formData[col.name])}
300
+ // </div>
301
+ // ))}
302
+ // </div>
303
+ // )}
304
+ // </div>
305
+ // ))}
306
+ // <div style={{ display: 'flex', justifyContent: 'space-between', gap: '12px', marginTop: '20px' }}>
307
+ // {!config.hideSubmit && (
308
+ // <button
309
+ // type="submit"
310
+ // disabled={config.disableSubmit}
311
+ // style={{ padding: '8px 24px', background: config.disableSubmit ? '#bdc3c7' : '#006aff', color: '#fff', border: 'none', borderRadius: '7px', fontWeight: 600, cursor: config.disableSubmit ? 'not-allowed' : 'pointer' }}
312
+ // >
313
+ // {config.submitLabel || 'Save'}
314
+ // </button>
315
+ // )}
316
+ // {!config.hideCancel && (
317
+ // <button
318
+ // type="button"
319
+ // disabled={config.disableCancel}
320
+ // onClick={config.onCancel}
321
+ // style={{ padding: '8px 24px', background: '#303030', color: '#fff', border: 'none', borderRadius: '7px', fontWeight: 600, cursor: config.disableCancel ? 'not-allowed' : 'pointer' }}
322
+ // >
323
+ // {config.cancelLabel || 'Cancel'}
324
+ // </button>
325
+ // )}
326
+ // </div>
327
+ // </form>
328
+ // </div>
329
+ // );
330
+ // };
27
331
  const react_1 = __importStar(require("react"));
28
332
  // Import all Fv controls
29
333
  const FvEntryField_1 = require("./FvEntryField");
@@ -49,24 +353,90 @@ const FvCheckbox_1 = require("./FvCheckbox");
49
353
  const FvRadioGroup_1 = require("./FvRadioGroup");
50
354
  const FvDocumentField_1 = require("./FvDocumentField");
51
355
  const FvTimeField_1 = require("./FvTimeField");
52
- const AddUpdateForm = ({ config }) => {
356
+ // ─── Modal overlay styles ────────────────────────────────────────────────────
357
+ const overlayStyle = {
358
+ position: 'fixed',
359
+ inset: 0,
360
+ background: 'rgba(0, 0, 0, 0.4)',
361
+ display: 'flex',
362
+ alignItems: 'center',
363
+ justifyContent: 'center',
364
+ zIndex: 1000,
365
+ };
366
+ const modalBoxStyle = {
367
+ background: '#ffffff',
368
+ borderRadius: '12px',
369
+ boxShadow: '0 4px 20px rgba(0,0,0,0.15)',
370
+ width: '100%',
371
+ maxWidth: '540px',
372
+ backgroundColor: '#fff',
373
+ position: 'relative',
374
+ padding: '30px 24px',
375
+ };
376
+ const closeBtnStyle = {
377
+ position: 'absolute',
378
+ top: '16px',
379
+ right: '16px',
380
+ background: 'none',
381
+ border: 'none',
382
+ cursor: 'pointer',
383
+ fontSize: '20px',
384
+ color: '#666',
385
+ padding: '4px',
386
+ };
387
+ const modalTitleStyle = {
388
+ margin: '0 0 24px 0',
389
+ fontSize: '22px',
390
+ fontWeight: 700,
391
+ color: '#1a1a1a',
392
+ };
393
+ const getSubmitButtonStyle = (disabled) => ({
394
+ width: '100%',
395
+ padding: '12px',
396
+ background: disabled ? '#8cc1f7' : '#005bb5',
397
+ color: '#ffffff',
398
+ border: 'none',
399
+ borderRadius: '8px',
400
+ fontWeight: 600,
401
+ fontSize: '16px',
402
+ cursor: disabled ? 'not-allowed' : 'pointer',
403
+ marginTop: '24px',
404
+ });
405
+ // ── Standalone (non-modal) wrapper styles ───────────────────────────────────
406
+ const standaloneWrapStyle = {
407
+ background: '#ffffff',
408
+ borderRadius: '8px',
409
+ boxShadow: '0 1px 4px rgba(0,0,0,0.08)',
410
+ padding: '24px',
411
+ };
412
+ // ── Section title ────────────────────────────────────────────────────────────
413
+ const sectionTitleStyle = {
414
+ fontSize: '14px',
415
+ fontWeight: 600,
416
+ color: '#555',
417
+ marginBottom: '16px',
418
+ };
419
+ // ── Helpers ──────────────────────────────────────────────────────────────────
420
+ const buildGridStyle = (maxCols) => ({
421
+ display: 'grid',
422
+ gridTemplateColumns: `repeat(${maxCols}, 1fr)`,
423
+ gap: '16px',
424
+ });
425
+ // ── Component ────────────────────────────────────────────────────────────────
426
+ const AddUpdateForm = ({ config, asModal = false, }) => {
427
+ var _a;
53
428
  const [formData, setFormData] = (0, react_1.useState)({});
429
+ const maxCols = (_a = config.maxColsPerRow) !== null && _a !== void 0 ? _a : 3;
54
430
  (0, react_1.useEffect)(() => {
55
- // Initialize form structure
56
431
  const initialData = {};
57
432
  config.sections.forEach((section) => {
58
433
  if (section.isRepeatable && section.sectionKey) {
59
- // Init as array with one empty object
60
434
  const groupObj = {};
61
- section.fields.forEach((col) => {
62
- groupObj[col.name] = col.value || '';
63
- });
435
+ section.fields.forEach((col) => { var _a; groupObj[col.name] = (_a = col.value) !== null && _a !== void 0 ? _a : ''; });
64
436
  initialData[section.sectionKey] = [groupObj];
65
437
  }
66
438
  else {
67
- section.fields.forEach((col) => {
68
- initialData[col.name] = col.value || '';
69
- });
439
+ section.fields.forEach((col) => { var _a; initialData[col.name] = (_a = col.value) !== null && _a !== void 0 ? _a : ''; });
70
440
  }
71
441
  });
72
442
  setFormData(initialData);
@@ -83,11 +453,9 @@ const AddUpdateForm = ({ config }) => {
83
453
  }
84
454
  return copy;
85
455
  });
86
- // We do a delayed trigger to on change to simulate reactive form passing state
87
456
  setTimeout(() => {
88
457
  const col = findColumn(name, sectionKey);
89
- if (col && col.onChange) {
90
- // Needs recent state
458
+ if (col === null || col === void 0 ? void 0 : col.onChange) {
91
459
  setFormData((latestState) => {
92
460
  col.onChange(value, latestState);
93
461
  return latestState;
@@ -99,7 +467,7 @@ const AddUpdateForm = ({ config }) => {
99
467
  for (const section of config.sections) {
100
468
  if (sectionKey && section.sectionKey !== sectionKey)
101
469
  continue;
102
- const col = section.fields.find(c => c.name === name);
470
+ const col = section.fields.find((c) => c.name === name);
103
471
  if (col)
104
472
  return col;
105
473
  }
@@ -109,15 +477,16 @@ const AddUpdateForm = ({ config }) => {
109
477
  setFormData((prev) => {
110
478
  const copy = { ...prev };
111
479
  const groupObj = {};
112
- fields.forEach((col) => { groupObj[col.name] = col.value || ''; });
480
+ fields.forEach((col) => { var _a; groupObj[col.name] = (_a = col.value) !== null && _a !== void 0 ? _a : ''; });
113
481
  copy[sectionKey] = [...(copy[sectionKey] || []), groupObj];
114
482
  return copy;
115
483
  });
116
484
  };
117
485
  const removeSectionItem = (sectionKey, index) => {
118
486
  setFormData((prev) => {
487
+ var _a;
119
488
  const copy = { ...prev };
120
- if (copy[sectionKey] && copy[sectionKey].length > 1) {
489
+ if (((_a = copy[sectionKey]) === null || _a === void 0 ? void 0 : _a.length) > 1) {
121
490
  const arr = [...copy[sectionKey]];
122
491
  arr.splice(index, 1);
123
492
  copy[sectionKey] = arr;
@@ -158,87 +527,67 @@ const AddUpdateForm = ({ config }) => {
158
527
  }
159
528
  return { controlType: 'EntryField', rules, errorPriority };
160
529
  };
161
- const isFieldDisabled = (column) => {
162
- if (typeof column.disabled === 'function') {
163
- return column.disabled(formData);
164
- }
165
- return column.disabled || false;
166
- };
530
+ const isFieldDisabled = (column) => typeof column.disabled === 'function' ? column.disabled(formData) : column.disabled || false;
167
531
  const renderField = (column, value, sectionKey, index) => {
168
532
  var _a, _b;
533
+ if (column.hidden)
534
+ return null;
169
535
  const disabled = isFieldDisabled(column);
170
536
  const schema = getSchema(column);
171
537
  const onChange = (val) => handleFieldChange(column.name, val, sectionKey, index);
172
- if (column.hidden)
173
- return null;
174
538
  switch (column.type) {
175
- case 'text':
176
- return react_1.default.createElement(FvEntryField_1.FvEntryField, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
177
- case 'email':
178
- return react_1.default.createElement(FvEmailField_1.FvEmailField, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
179
- case 'password':
180
- return react_1.default.createElement(FvPasswordField_1.FvPasswordField, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
181
- case 'number':
182
- return react_1.default.createElement(FvNumberField_1.FvNumberField, { label: column.label, placeholder: column.placeholder, value: value || null, schema: schema, disabled: disabled, onChange: onChange });
183
- case 'select':
184
- return react_1.default.createElement(FvDropdown_1.FvDropdown, { label: column.label, placeholder: column.placeholder, options: column.options || [], value: value || '', schema: schema, disabled: disabled, onChange: onChange });
185
- case 'checkbox':
186
- return react_1.default.createElement(FvCheckbox_1.FvCheckbox, { label: column.label, value: value || false, disabled: disabled, onChange: onChange });
187
- case 'toggle':
188
- return react_1.default.createElement(FvToggle_1.FvToggle, { label: column.label, value: value || false, disabled: disabled, onChange: onChange });
189
- case 'radio':
190
- return react_1.default.createElement(FvRadioGroup_1.FvRadioGroup, { label: column.label, options: column.options || [], value: value || '', disabled: disabled, layout: column.layout, onChange: onChange });
191
- case 'date':
192
- return react_1.default.createElement(FvDateField_1.FvDateField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
193
- case 'month-year':
194
- return react_1.default.createElement(FvMonthYearField_1.FvMonthYearField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
195
- case 'textarea':
196
- return react_1.default.createElement(FvRichTextEditor_1.FvRichTextEditor, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
539
+ case 'text': return react_1.default.createElement(FvEntryField_1.FvEntryField, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
540
+ case 'email': return react_1.default.createElement(FvEmailField_1.FvEmailField, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
541
+ case 'password': return react_1.default.createElement(FvPasswordField_1.FvPasswordField, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
542
+ case 'number': return react_1.default.createElement(FvNumberField_1.FvNumberField, { label: column.label, placeholder: column.placeholder, value: value !== null && value !== void 0 ? value : null, schema: schema, disabled: disabled, onChange: onChange });
543
+ case 'select': return react_1.default.createElement(FvDropdown_1.FvDropdown, { label: column.label, placeholder: column.placeholder, options: column.options || [], value: value || '', schema: schema, disabled: disabled, onChange: onChange });
544
+ case 'checkbox': return react_1.default.createElement(FvCheckbox_1.FvCheckbox, { label: column.label, value: value || false, disabled: disabled, onChange: onChange });
545
+ case 'toggle': return react_1.default.createElement(FvToggle_1.FvToggle, { label: column.label, value: value || false, disabled: disabled, onChange: onChange });
546
+ case 'radio': return react_1.default.createElement(FvRadioGroup_1.FvRadioGroup, { label: column.label, options: column.options || [], value: value || '', disabled: disabled, layout: column.layout, onChange: onChange });
547
+ case 'date': return react_1.default.createElement(FvDateField_1.FvDateField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
548
+ case 'month-year': return react_1.default.createElement(FvMonthYearField_1.FvMonthYearField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
549
+ case 'textarea': return react_1.default.createElement(FvRichTextEditor_1.FvRichTextEditor, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
197
550
  case 'file':
198
551
  if (((_a = column.accept) === null || _a === void 0 ? void 0 : _a.startsWith('image')) || column.filePreview) {
199
- return react_1.default.createElement(FvImageSelector_1.FvImageSelector, { label: column.label, placeholder: column.placeholder, value: value || null, schema: schema, disabled: disabled, onChange: onChange });
552
+ return react_1.default.createElement(FvImageSelector_1.FvImageSelector, { label: column.label, placeholder: column.placeholder, value: value !== null && value !== void 0 ? value : null, schema: schema, disabled: disabled, onChange: onChange });
200
553
  }
201
- return react_1.default.createElement(FvFileSelector_1.FvFileSelector, { label: column.label, placeholder: column.placeholder, accept: column.accept, value: value || null, schema: schema, disabled: disabled, onChange: onChange });
202
- case 'name-code':
203
- return react_1.default.createElement(FvNameCode_1.FvNameCode, { label: column.label, placeholder: column.placeholder, options: ((_b = column.options) === null || _b === void 0 ? void 0 : _b.map(o => ({ code: o.value, name: o.label, value: o.value }))) || [], value: value || '', schema: schema, disabled: disabled, onChange: onChange });
204
- case 'phone':
205
- return react_1.default.createElement(FvPhoneField_1.FvPhoneField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
206
- case 'uan':
207
- return react_1.default.createElement(FvUanField_1.FvUanField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
208
- case 'pf':
209
- return react_1.default.createElement(FvPfField_1.FvPfField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
210
- case 'esi':
211
- return react_1.default.createElement(FvEsiField_1.FvEsiField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
212
- case 'ifsc':
213
- return react_1.default.createElement(FvIfscField_1.FvIfscField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
214
- case 'micr':
215
- return react_1.default.createElement(FvMicrField_1.FvMicrField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
216
- case 'iban':
217
- return react_1.default.createElement(FvIbanField_1.FvIbanField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
218
- case 'document':
219
- return react_1.default.createElement(FvDocumentField_1.FvDocumentField, { label: column.label, placeholder: column.placeholder, accept: column.accept, value: value || null, schema: schema, disabled: disabled, onChange: onChange });
220
- case 'time':
221
- return react_1.default.createElement(FvTimeField_1.FvTimeField, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
222
- case 'service-period':
223
- // Not perfectly mapped due to pure group dependency, simple implementation shown for continuity
224
- return react_1.default.createElement(FvEntryField_1.FvEntryField, { label: column.label, value: value || '', disabled: true, onChange: onChange });
225
- default:
226
- return null; // unsupported
554
+ return react_1.default.createElement(FvFileSelector_1.FvFileSelector, { label: column.label, placeholder: column.placeholder, accept: column.accept, value: value !== null && value !== void 0 ? value : null, schema: schema, disabled: disabled, onChange: onChange });
555
+ case 'name-code': return react_1.default.createElement(FvNameCode_1.FvNameCode, { label: column.label, placeholder: column.placeholder, options: ((_b = column.options) === null || _b === void 0 ? void 0 : _b.map((o) => ({ code: o.value, name: o.label, value: o.value }))) || [], value: value || '', schema: schema, disabled: disabled, onChange: onChange });
556
+ case 'phone': return react_1.default.createElement(FvPhoneField_1.FvPhoneField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
557
+ case 'uan': return react_1.default.createElement(FvUanField_1.FvUanField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
558
+ case 'pf': return react_1.default.createElement(FvPfField_1.FvPfField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
559
+ case 'esi': return react_1.default.createElement(FvEsiField_1.FvEsiField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
560
+ case 'ifsc': return react_1.default.createElement(FvIfscField_1.FvIfscField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
561
+ case 'micr': return react_1.default.createElement(FvMicrField_1.FvMicrField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
562
+ case 'iban': return react_1.default.createElement(FvIbanField_1.FvIbanField, { label: column.label, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
563
+ case 'document': return react_1.default.createElement(FvDocumentField_1.FvDocumentField, { label: column.label, placeholder: column.placeholder, accept: column.accept, value: value !== null && value !== void 0 ? value : null, schema: schema, disabled: disabled, onChange: onChange });
564
+ case 'time': return react_1.default.createElement(FvTimeField_1.FvTimeField, { label: column.label, placeholder: column.placeholder, value: value || '', schema: schema, disabled: disabled, onChange: onChange });
565
+ case 'service-period': return react_1.default.createElement(FvEntryField_1.FvEntryField, { label: column.label, value: value || '', disabled: true, onChange: onChange });
566
+ default: return null;
227
567
  }
228
568
  };
229
- return (react_1.default.createElement("div", { style: { background: '#f5f5f5', padding: '15px 20px', borderRadius: '8px', maxWidth: '100%', boxSizing: 'border-box' } },
230
- config.formTitle && (react_1.default.createElement("div", { style: { marginBottom: '15px' } },
231
- react_1.default.createElement("h2", { style: { margin: 0, fontSize: '20px', fontWeight: 700, color: '#303030' } }, config.formTitle))),
232
- react_1.default.createElement("form", { onSubmit: handleSubmit, style: { background: '#fff', padding: '15px', borderRadius: '6px', boxShadow: '0 1px 3px rgba(0,0,0,0.1)' } },
233
- config.sections.map((section, sIdx) => (react_1.default.createElement("div", { key: sIdx, style: { marginBottom: '20px' } },
234
- section.title && react_1.default.createElement("h3", { style: { fontSize: '16px', fontWeight: 700, color: '#303030', marginBottom: '16px', textTransform: 'uppercase' } }, section.title),
235
- section.isRepeatable && section.sectionKey ? ((formData[section.sectionKey] || []).map((group, idx) => (react_1.default.createElement("div", { key: idx, style: { position: 'relative', border: '1px solid #eee', borderRadius: '8px', padding: '20px 80px 20px 20px', marginBottom: '15px', background: '#fafafa' } },
236
- react_1.default.createElement("div", { style: { position: 'absolute', top: '15px', right: '15px', display: 'flex', gap: '8px' } },
237
- react_1.default.createElement("button", { type: "button", onClick: () => addSectionItem(section.sectionKey, section.fields), style: { background: 'none', border: 'none', cursor: 'pointer', fontSize: '20px', color: '#2ecc71' } }, "\u2295"),
238
- (formData[section.sectionKey] || []).length > 1 && (react_1.default.createElement("button", { type: "button", onClick: () => removeSectionItem(section.sectionKey, idx), style: { background: 'none', border: 'none', cursor: 'pointer', fontSize: '20px', color: '#e74c3c' } }, "\u2297"))),
239
- react_1.default.createElement("div", { style: { display: 'grid', gridTemplateColumns: `repeat(auto-fit, minmax(200px, 1fr))`, gap: '16px' } }, section.fields.map((col, cIdx) => (react_1.default.createElement("div", { key: cIdx, style: { gridColumn: `span ${col.colSpan || 1}` } }, renderField(col, group[col.name], section.sectionKey, idx))))))))) : (react_1.default.createElement("div", { style: { display: 'grid', gridTemplateColumns: `repeat(auto-fit, minmax(200px, 1fr))`, gap: '16px' } }, section.fields.map((col, cIdx) => (react_1.default.createElement("div", { key: cIdx, style: { gridColumn: `span ${col.colSpan || 1}` } }, renderField(col, formData[col.name]))))))))),
240
- react_1.default.createElement("div", { style: { display: 'flex', justifyContent: 'space-between', gap: '12px', marginTop: '20px' } },
241
- !config.hideSubmit && (react_1.default.createElement("button", { type: "submit", disabled: config.disableSubmit, style: { padding: '8px 24px', background: config.disableSubmit ? '#bdc3c7' : '#006aff', color: '#fff', border: 'none', borderRadius: '7px', fontWeight: 600, cursor: config.disableSubmit ? 'not-allowed' : 'pointer' } }, config.submitLabel || 'Save')),
242
- !config.hideCancel && (react_1.default.createElement("button", { type: "button", disabled: config.disableCancel, onClick: config.onCancel, style: { padding: '8px 24px', background: '#303030', color: '#fff', border: 'none', borderRadius: '7px', fontWeight: 600, cursor: config.disableCancel ? 'not-allowed' : 'pointer' } }, config.cancelLabel || 'Cancel'))))));
569
+ const formBody = (react_1.default.createElement("form", { onSubmit: handleSubmit, className: `fv-add-update-form ${config.className || ''}`.trim() },
570
+ config.sections.map((section, sIdx) => {
571
+ var _a;
572
+ const sectionMaxCols = (_a = section.maxColsPerRow) !== null && _a !== void 0 ? _a : maxCols;
573
+ return (react_1.default.createElement("div", { key: sIdx, style: { marginBottom: '16px' }, className: `fv-form-section ${section.className || ''}`.trim() },
574
+ section.title && react_1.default.createElement("div", { style: sectionTitleStyle, className: "fv-form-section-title" }, section.title),
575
+ section.isRepeatable && section.sectionKey ? ((formData[section.sectionKey] || []).map((group, idx) => (react_1.default.createElement("div", { key: idx, style: { position: 'relative', border: '1px solid #eee', borderRadius: '8px', padding: '12px', marginBottom: '12px', background: '#fafafa' }, className: "fv-form-repeatable-item" },
576
+ react_1.default.createElement("div", { style: { position: 'absolute', top: '8px', right: '8px', display: 'flex', gap: '4px' }, className: "fv-form-repeatable-actions" },
577
+ react_1.default.createElement("button", { type: "button", onClick: () => addSectionItem(section.sectionKey, section.fields), style: { background: 'none', border: 'none', cursor: 'pointer', fontSize: '18px', color: '#2ecc71' }, className: "fv-form-add-btn" }, "\u2295"),
578
+ (formData[section.sectionKey] || []).length > 1 && (react_1.default.createElement("button", { type: "button", onClick: () => removeSectionItem(section.sectionKey, idx), style: { background: 'none', border: 'none', cursor: 'pointer', fontSize: '18px', color: '#e74c3c' }, className: "fv-form-remove-btn" }, "\u2297"))),
579
+ react_1.default.createElement("div", { style: buildGridStyle(sectionMaxCols), className: "fv-form-grid" }, section.fields.map((col, cIdx) => (react_1.default.createElement("div", { key: cIdx, style: { gridColumn: `span ${col.colSpan || 1}` }, className: `fv-form-field-wrapper ${col.className || ''}`.trim() }, renderField(col, group[col.name], section.sectionKey, idx))))))))) : (react_1.default.createElement("div", { style: buildGridStyle(sectionMaxCols), className: "fv-form-grid" }, section.fields.map((col, cIdx) => (react_1.default.createElement("div", { key: cIdx, style: { gridColumn: `span ${col.colSpan || 1}` }, className: `fv-form-field-wrapper ${col.className || ''}`.trim() }, renderField(col, formData[col.name]))))))));
580
+ }),
581
+ react_1.default.createElement("div", { style: { display: 'flex', gap: '12px', marginTop: '8px' }, className: "fv-form-footer" }, !config.hideSubmit && (react_1.default.createElement("button", { type: "submit", disabled: config.disableSubmit, style: getSubmitButtonStyle(!!config.disableSubmit), className: "fv-form-submit-btn" }, config.submitLabel || 'Save')))));
582
+ if (asModal) {
583
+ return (react_1.default.createElement("div", { style: overlayStyle, className: "fv-form-overlay" },
584
+ react_1.default.createElement("div", { style: modalBoxStyle, className: "fv-modal-container" },
585
+ react_1.default.createElement("button", { type: "button", style: closeBtnStyle, onClick: config.onCancel, className: "fv-modal-close-btn" }, "\u2715"),
586
+ config.formTitle && react_1.default.createElement("h2", { style: modalTitleStyle, className: "fv-modal-title" }, config.formTitle),
587
+ react_1.default.createElement("div", { className: "fv-modal-body" }, formBody))));
588
+ }
589
+ return (react_1.default.createElement("div", { style: { background: '#f5f5f5', padding: '16px', borderRadius: '8px' }, className: "fv-standalone-form-container" },
590
+ config.formTitle && react_1.default.createElement("h2", { style: { margin: '0 0 16px', fontSize: '20px', fontWeight: 700 }, className: "fv-form-title" }, config.formTitle),
591
+ react_1.default.createElement("div", { style: standaloneWrapStyle, className: "fv-form-body-wrapper" }, formBody)));
243
592
  };
244
593
  exports.AddUpdateForm = AddUpdateForm;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fovestta2/web-react",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -25,7 +25,7 @@
25
25
  "react-dom": ">=18.0.0"
26
26
  },
27
27
  "dependencies": {
28
- "@fovestta2/validation-engine": "^1.2.1"
28
+ "@fovestta2/validation-engine": "^1.2.2"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/react": "^18.2.0",
@@ -35,5 +35,5 @@
35
35
  "files": [
36
36
  "dist"
37
37
  ],
38
- "gitHead": "061e434bf565b7a96cb0f7a43ee1d64d723a920a"
38
+ "gitHead": "4a124b94929ae898505ed77eae348300e2f507b1"
39
39
  }