@fovestta2/web-react 1.2.2 → 1.2.3
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/dist/components/AddUpdateForm.js +25 -5
- package/dist/components/FvDropdown.js +17 -16
- package/dist/components/FvEmailField.js +12 -14
- package/dist/components/FvEntryField.js +12 -17
- package/dist/components/FvMasterScreen.d.ts +78 -0
- package/dist/components/FvMasterScreen.js +317 -0
- package/dist/components/FvNumberField.js +12 -16
- package/dist/components/FvPasswordField.js +12 -16
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +2 -2
|
@@ -419,7 +419,7 @@ const sectionTitleStyle = {
|
|
|
419
419
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
420
420
|
const buildGridStyle = (maxCols) => ({
|
|
421
421
|
display: 'grid',
|
|
422
|
-
gridTemplateColumns: `repeat(${maxCols}, 1fr)`,
|
|
422
|
+
gridTemplateColumns: `repeat(${maxCols}, minmax(0, 1fr))`,
|
|
423
423
|
gap: '16px',
|
|
424
424
|
});
|
|
425
425
|
// ── Component ────────────────────────────────────────────────────────────────
|
|
@@ -512,17 +512,37 @@ const AddUpdateForm = ({ config, asModal = false, }) => {
|
|
|
512
512
|
errorPriority.push('regex');
|
|
513
513
|
}
|
|
514
514
|
if (v.type === 'minLength') {
|
|
515
|
-
rules.push({ name: 'minLength', params: { value: v.value }, errorKey: 'ERR_MIN_LENGTH', message: v.message });
|
|
515
|
+
rules.push({ name: 'minLength', params: { value: v.value }, errorKey: v.errorKey || 'ERR_MIN_LENGTH', message: v.message });
|
|
516
516
|
errorPriority.push('minLength');
|
|
517
517
|
}
|
|
518
518
|
if (v.type === 'maxLength') {
|
|
519
|
-
rules.push({ name: 'maxLength', params: { value: v.value }, errorKey: 'ERR_MAX_LENGTH', message: v.message });
|
|
519
|
+
rules.push({ name: 'maxLength', params: { value: v.value }, errorKey: v.errorKey || 'ERR_MAX_LENGTH', message: v.message });
|
|
520
520
|
errorPriority.push('maxLength');
|
|
521
521
|
}
|
|
522
522
|
if (v.type === 'pattern') {
|
|
523
|
-
rules.push({ name: 'regex', params: { pattern: v.value }, errorKey: 'ERR_REGEX_MISMATCH', message: v.message });
|
|
523
|
+
rules.push({ name: 'regex', params: { pattern: v.value }, errorKey: v.errorKey || 'ERR_REGEX_MISMATCH', message: v.message });
|
|
524
524
|
errorPriority.push('regex');
|
|
525
525
|
}
|
|
526
|
+
if (v.type === 'min') {
|
|
527
|
+
rules.push({ name: 'min', params: { value: v.value }, errorKey: v.errorKey || 'ERR_MIN_VALUE', message: v.message });
|
|
528
|
+
errorPriority.push('min');
|
|
529
|
+
}
|
|
530
|
+
if (v.type === 'max') {
|
|
531
|
+
rules.push({ name: 'max', params: { value: v.value }, errorKey: v.errorKey || 'ERR_MAX_VALUE', message: v.message });
|
|
532
|
+
errorPriority.push('max');
|
|
533
|
+
}
|
|
534
|
+
if (v.type === 'custom') {
|
|
535
|
+
rules.push({ name: 'custom', params: { validator: v.value }, errorKey: v.errorKey || 'ERR_CUSTOM_INVALID', message: v.message });
|
|
536
|
+
errorPriority.push('custom');
|
|
537
|
+
}
|
|
538
|
+
if (v.type === 'duplicate') {
|
|
539
|
+
rules.push({ name: 'duplicate', params: v.params, errorKey: v.errorKey || 'ERR_DUPLICATE', message: v.message });
|
|
540
|
+
errorPriority.push('duplicate');
|
|
541
|
+
}
|
|
542
|
+
if (v.type === 'passwordComplexity') {
|
|
543
|
+
rules.push({ name: 'passwordComplexity', params: v.params, errorKey: v.errorKey || 'ERR_PASSWORD_COMPLEXITY', message: v.message });
|
|
544
|
+
errorPriority.push('passwordComplexity');
|
|
545
|
+
}
|
|
526
546
|
});
|
|
527
547
|
}
|
|
528
548
|
return { controlType: 'EntryField', rules, errorPriority };
|
|
@@ -576,7 +596,7 @@ const AddUpdateForm = ({ config, asModal = false, }) => {
|
|
|
576
596
|
react_1.default.createElement("div", { style: { position: 'absolute', top: '8px', right: '8px', display: 'flex', gap: '4px' }, className: "fv-form-repeatable-actions" },
|
|
577
597
|
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
598
|
(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}
|
|
599
|
+
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}`, minWidth: 0 }, 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}`, minWidth: 0 }, className: `fv-form-field-wrapper ${col.className || ''}`.trim() }, renderField(col, formData[col.name]))))))));
|
|
580
600
|
}),
|
|
581
601
|
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
602
|
if (asModal) {
|
|
@@ -27,7 +27,7 @@ exports.FvDropdown = void 0;
|
|
|
27
27
|
const react_1 = __importStar(require("react"));
|
|
28
28
|
const validation_engine_1 = require("@fovestta2/validation-engine");
|
|
29
29
|
const FvDropdown = ({ label = '', placeholder = 'Select an option', options = [], value, schema, disabled = false, className = '', inputClassName = '', labelClassName = '', errorClassName = '', onChange, onBlur, onFocus, }) => {
|
|
30
|
-
const [
|
|
30
|
+
const [isTouched, setIsTouched] = (0, react_1.useState)(false);
|
|
31
31
|
const [isOpen, setIsOpen] = (0, react_1.useState)(false);
|
|
32
32
|
const [searchText, setSearchText] = (0, react_1.useState)('');
|
|
33
33
|
const [filteredOptions, setFilteredOptions] = (0, react_1.useState)([]);
|
|
@@ -35,6 +35,12 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
35
35
|
const containerRef = (0, react_1.useRef)(null);
|
|
36
36
|
const inputRef = (0, react_1.useRef)(null);
|
|
37
37
|
const listRef = (0, react_1.useRef)(null);
|
|
38
|
+
const errorMessage = (0, react_1.useMemo)(() => {
|
|
39
|
+
if (!isTouched || !schema)
|
|
40
|
+
return null;
|
|
41
|
+
const result = validation_engine_1.Validator.validate(value, schema);
|
|
42
|
+
return result.isValid ? null : (result.message || result.errorKey);
|
|
43
|
+
}, [value, schema, isTouched]);
|
|
38
44
|
// Helper type guards
|
|
39
45
|
const isGroup = (item) => {
|
|
40
46
|
return 'items' in item && Array.isArray(item.items);
|
|
@@ -89,7 +95,6 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
89
95
|
};
|
|
90
96
|
// Sync initial and prop changes
|
|
91
97
|
(0, react_1.useEffect)(() => {
|
|
92
|
-
validateValue(value);
|
|
93
98
|
const selected = findOption(value);
|
|
94
99
|
if (selected) {
|
|
95
100
|
setSearchText(selected.label);
|
|
@@ -104,6 +109,7 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
104
109
|
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
105
110
|
if (isOpen) {
|
|
106
111
|
closeDropdown();
|
|
112
|
+
setIsTouched(true);
|
|
107
113
|
if (onBlur)
|
|
108
114
|
onBlur();
|
|
109
115
|
}
|
|
@@ -114,17 +120,6 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
114
120
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
115
121
|
};
|
|
116
122
|
}, [isOpen, value, options]);
|
|
117
|
-
const validateValue = (val) => {
|
|
118
|
-
if (!schema)
|
|
119
|
-
return;
|
|
120
|
-
const result = validation_engine_1.Validator.validate(val, schema);
|
|
121
|
-
if (!result.isValid && result.errorKey) {
|
|
122
|
-
setErrorMessage(result.errorKey);
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
setErrorMessage(null);
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
123
|
const openDropdown = () => {
|
|
129
124
|
if (disabled)
|
|
130
125
|
return;
|
|
@@ -157,7 +152,6 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
157
152
|
if (!value)
|
|
158
153
|
setSearchText('');
|
|
159
154
|
}
|
|
160
|
-
validateValue(value);
|
|
161
155
|
};
|
|
162
156
|
const toggleDropdown = () => {
|
|
163
157
|
var _a;
|
|
@@ -186,6 +180,7 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
186
180
|
setSearchText(viewItem.label);
|
|
187
181
|
setIsOpen(false);
|
|
188
182
|
setHighlightedIndex(-1);
|
|
183
|
+
setIsTouched(true);
|
|
189
184
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
190
185
|
if (onBlur)
|
|
191
186
|
onBlur();
|
|
@@ -260,10 +255,13 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
260
255
|
ERR_REQUIRED: 'This field is required',
|
|
261
256
|
ERR_INVALID_VALUE: 'Invalid selection',
|
|
262
257
|
ERR_DUPLICATE: 'This selection already exists',
|
|
258
|
+
ERR_MIN_VALUE: 'Value is too small',
|
|
259
|
+
ERR_MAX_VALUE: 'Value is too large',
|
|
260
|
+
ERR_CUSTOM_INVALID: 'Invalid value',
|
|
263
261
|
};
|
|
264
262
|
return errorMessages[errorMessage] || errorMessage;
|
|
265
263
|
};
|
|
266
|
-
return (react_1.default.createElement("div", { ref: containerRef, className: `fv-dropdown-container ${className}`, style: { marginBottom: '16px', display: 'flex', flexDirection: 'column', position: 'relative' }, onKeyDown: handleKeyDown },
|
|
264
|
+
return (react_1.default.createElement("div", { ref: containerRef, className: `fv-dropdown-container ${className}`, style: { marginBottom: '16px', display: 'flex', flexDirection: 'column', position: 'relative', width: '100%' }, onKeyDown: handleKeyDown },
|
|
267
265
|
label && (react_1.default.createElement("label", { className: `fv-dropdown-label ${labelClassName}`, style: { marginBottom: '6px', fontSize: '14px', fontWeight: 500, color: disabled ? '#999' : '#333' } },
|
|
268
266
|
label,
|
|
269
267
|
isRequired() && react_1.default.createElement("span", { className: "fv-required", style: { color: '#dc3545', fontWeight: 'bold' } }, " *"))),
|
|
@@ -273,7 +271,8 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
273
271
|
borderRadius: '4px',
|
|
274
272
|
backgroundColor: disabled ? '#f5f5f5' : '#fff',
|
|
275
273
|
cursor: disabled ? 'not-allowed' : 'text',
|
|
276
|
-
position: 'relative'
|
|
274
|
+
position: 'relative',
|
|
275
|
+
width: '100%'
|
|
277
276
|
}, onClick: (e) => {
|
|
278
277
|
var _a;
|
|
279
278
|
if (disabled)
|
|
@@ -287,6 +286,8 @@ const FvDropdown = ({ label = '', placeholder = 'Select an option', options = []
|
|
|
287
286
|
} },
|
|
288
287
|
react_1.default.createElement("input", { ref: inputRef, type: "text", className: `fv-dropdown-input ${inputClassName}`, value: searchText, onChange: handleSearchChange, onFocus: openDropdown, disabled: disabled, placeholder: placeholder, style: {
|
|
289
288
|
flex: 1,
|
|
289
|
+
width: '100%',
|
|
290
|
+
minWidth: 0,
|
|
290
291
|
padding: '10px',
|
|
291
292
|
border: 'none',
|
|
292
293
|
background: 'transparent',
|
|
@@ -27,7 +27,7 @@ exports.FvEmailField = void 0;
|
|
|
27
27
|
const react_1 = __importStar(require("react"));
|
|
28
28
|
const validation_engine_1 = require("@fovestta2/validation-engine");
|
|
29
29
|
const FvEmailField = ({ label = 'Email Address', placeholder = 'e.g. john@example.com', value, schema, disabled = false, readonly = false, className = '', inputClassName = '', labelClassName = '', errorClassName = '', onChange, onBlur, onFocus, }) => {
|
|
30
|
-
const [
|
|
30
|
+
const [isTouched, setIsTouched] = (0, react_1.useState)(false);
|
|
31
31
|
const defaultSchema = schema || {
|
|
32
32
|
controlType: 'EntryField',
|
|
33
33
|
errorPriority: ['required', 'regex'],
|
|
@@ -41,23 +41,18 @@ const FvEmailField = ({ label = 'Email Address', placeholder = 'e.g. john@exampl
|
|
|
41
41
|
},
|
|
42
42
|
],
|
|
43
43
|
};
|
|
44
|
-
(0, react_1.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
setErrorMessage(null);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
44
|
+
const errorMessage = (0, react_1.useMemo)(() => {
|
|
45
|
+
if (!isTouched)
|
|
46
|
+
return null;
|
|
47
|
+
const result = validation_engine_1.Validator.validate(value, schema || defaultSchema);
|
|
48
|
+
return result.isValid ? null : (result.message || result.errorKey);
|
|
49
|
+
}, [value, schema, isTouched, defaultSchema]);
|
|
50
|
+
// Sync logic removed in favor of useMemo
|
|
56
51
|
const handleInput = (e) => {
|
|
57
52
|
onChange(e.target.value);
|
|
58
53
|
};
|
|
59
54
|
const handleBlur = (e) => {
|
|
60
|
-
|
|
55
|
+
setIsTouched(true);
|
|
61
56
|
if (onBlur)
|
|
62
57
|
onBlur();
|
|
63
58
|
};
|
|
@@ -77,6 +72,9 @@ const FvEmailField = ({ label = 'Email Address', placeholder = 'e.g. john@exampl
|
|
|
77
72
|
ERR_EMAIL_INVALID: 'Invalid email format',
|
|
78
73
|
ERR_REGEX_MISMATCH: 'Invalid email format',
|
|
79
74
|
ERR_DUPLICATE: 'Email already exists',
|
|
75
|
+
ERR_MIN_VALUE: 'Value is too small',
|
|
76
|
+
ERR_MAX_VALUE: 'Value is too large',
|
|
77
|
+
ERR_CUSTOM_INVALID: 'Invalid value',
|
|
80
78
|
};
|
|
81
79
|
return errorMessages[errorMessage] || errorMessage;
|
|
82
80
|
};
|
|
@@ -27,22 +27,14 @@ exports.FvEntryField = void 0;
|
|
|
27
27
|
const react_1 = __importStar(require("react"));
|
|
28
28
|
const validation_engine_1 = require("@fovestta2/validation-engine");
|
|
29
29
|
const FvEntryField = ({ label = '', placeholder = '', value, schema, disabled = false, readonly = false, type = 'text', allowAlphabetsOnly = false, maxLength, className = '', inputClassName = '', labelClassName = '', errorClassName = '', onChange, onBlur, onFocus, }) => {
|
|
30
|
-
const [
|
|
31
|
-
(0, react_1.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// In React this is fully controlled by state, without formControl injection
|
|
39
|
-
if (!result.isValid && result.errorKey) {
|
|
40
|
-
setErrorMessage(result.errorKey);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
setErrorMessage(null);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
30
|
+
const [isTouched, setIsTouched] = (0, react_1.useState)(false);
|
|
31
|
+
const errorMessage = (0, react_1.useMemo)(() => {
|
|
32
|
+
if (!isTouched || !schema)
|
|
33
|
+
return null;
|
|
34
|
+
const result = validation_engine_1.Validator.validate(value, schema);
|
|
35
|
+
return result.isValid ? null : (result.message || result.errorKey);
|
|
36
|
+
}, [value, schema, isTouched]);
|
|
37
|
+
// Sync logic removed in favor of useMemo
|
|
46
38
|
const handleInput = (e) => {
|
|
47
39
|
let newValue = e.target.value;
|
|
48
40
|
if (allowAlphabetsOnly) {
|
|
@@ -54,7 +46,7 @@ const FvEntryField = ({ label = '', placeholder = '', value, schema, disabled =
|
|
|
54
46
|
onChange(newValue);
|
|
55
47
|
};
|
|
56
48
|
const handleBlur = (e) => {
|
|
57
|
-
|
|
49
|
+
setIsTouched(true);
|
|
58
50
|
if (onBlur)
|
|
59
51
|
onBlur();
|
|
60
52
|
};
|
|
@@ -75,6 +67,9 @@ const FvEntryField = ({ label = '', placeholder = '', value, schema, disabled =
|
|
|
75
67
|
ERR_MAX_LENGTH: 'Value is too long',
|
|
76
68
|
ERR_REGEX_MISMATCH: 'Invalid format',
|
|
77
69
|
ERR_DUPLICATE: 'This value already exists',
|
|
70
|
+
ERR_MIN_VALUE: 'Value is too small',
|
|
71
|
+
ERR_MAX_VALUE: 'Value is too large',
|
|
72
|
+
ERR_CUSTOM_INVALID: 'Invalid value',
|
|
78
73
|
};
|
|
79
74
|
return errorMessages[errorMessage] || errorMessage;
|
|
80
75
|
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface FvMasterAction {
|
|
3
|
+
label: string;
|
|
4
|
+
onClick: () => void;
|
|
5
|
+
icon?: React.ReactNode;
|
|
6
|
+
variant?: 'primary' | 'secondary' | 'danger';
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface FvMasterHeaderConfig {
|
|
10
|
+
title?: string;
|
|
11
|
+
subtitle?: string;
|
|
12
|
+
addAction?: FvMasterAction;
|
|
13
|
+
addOptions?: FvMasterAction[];
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface FvMasterCardConfig {
|
|
17
|
+
label: string;
|
|
18
|
+
value: string | number;
|
|
19
|
+
icon?: React.ReactNode;
|
|
20
|
+
colSpan?: number;
|
|
21
|
+
color?: string;
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface FvMasterTab {
|
|
25
|
+
label: string;
|
|
26
|
+
key: string;
|
|
27
|
+
count?: number;
|
|
28
|
+
colSpan?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface FvMasterColumn {
|
|
31
|
+
header: string;
|
|
32
|
+
key: string;
|
|
33
|
+
render?: (row: any) => React.ReactNode;
|
|
34
|
+
width?: string;
|
|
35
|
+
sortable?: boolean;
|
|
36
|
+
sortKey?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface FvMasterTableActions {
|
|
39
|
+
onView?: (row: any) => void;
|
|
40
|
+
onUpdate?: (row: any) => void;
|
|
41
|
+
onDelete?: (row: any) => void;
|
|
42
|
+
className?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface FvMasterPaginationConfig {
|
|
45
|
+
totalItems: number;
|
|
46
|
+
pageSize: number;
|
|
47
|
+
currentPage: number;
|
|
48
|
+
onPageChange: (page: number) => void;
|
|
49
|
+
onPageSizeChange?: (size: number) => void;
|
|
50
|
+
pageSizeOptions?: number[];
|
|
51
|
+
}
|
|
52
|
+
export interface FvMasterConfig {
|
|
53
|
+
header?: FvMasterHeaderConfig;
|
|
54
|
+
cards?: {
|
|
55
|
+
items: FvMasterCardConfig[];
|
|
56
|
+
maxColsPerRow?: number;
|
|
57
|
+
};
|
|
58
|
+
tabs?: {
|
|
59
|
+
items: FvMasterTab[];
|
|
60
|
+
activeKey: string;
|
|
61
|
+
onTabChange: (key: string) => void;
|
|
62
|
+
};
|
|
63
|
+
filters?: {
|
|
64
|
+
onExport?: (format: 'excel' | 'csv') => void;
|
|
65
|
+
className?: string;
|
|
66
|
+
};
|
|
67
|
+
table?: {
|
|
68
|
+
columns: FvMasterColumn[];
|
|
69
|
+
data: any[];
|
|
70
|
+
actions?: FvMasterTableActions;
|
|
71
|
+
pagination?: FvMasterPaginationConfig;
|
|
72
|
+
};
|
|
73
|
+
className?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface FvMasterScreenProps {
|
|
76
|
+
config: FvMasterConfig;
|
|
77
|
+
}
|
|
78
|
+
export declare const FvMasterScreen: React.FC<FvMasterScreenProps>;
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.FvMasterScreen = void 0;
|
|
27
|
+
const react_1 = __importStar(require("react"));
|
|
28
|
+
// ─── Styles ──────────────────────────────────────────────────────────────────
|
|
29
|
+
const containerStyle = {
|
|
30
|
+
padding: '24px',
|
|
31
|
+
background: '#f8fafc',
|
|
32
|
+
minHeight: '100%',
|
|
33
|
+
fontFamily: 'Inter, system-ui, sans-serif',
|
|
34
|
+
};
|
|
35
|
+
const headerRowStyle = {
|
|
36
|
+
display: 'flex',
|
|
37
|
+
justifyContent: 'space-between',
|
|
38
|
+
alignItems: 'flex-start',
|
|
39
|
+
marginBottom: '24px',
|
|
40
|
+
};
|
|
41
|
+
const titleStyle = {
|
|
42
|
+
fontSize: '28px',
|
|
43
|
+
fontWeight: 700,
|
|
44
|
+
color: '#0f172a',
|
|
45
|
+
margin: 0,
|
|
46
|
+
};
|
|
47
|
+
const subtitleStyle = {
|
|
48
|
+
fontSize: '14px',
|
|
49
|
+
color: '#64748b',
|
|
50
|
+
marginTop: '4px',
|
|
51
|
+
};
|
|
52
|
+
const addBtnStyle = {
|
|
53
|
+
background: '#005bb5',
|
|
54
|
+
color: '#fff',
|
|
55
|
+
border: 'none',
|
|
56
|
+
padding: '10px 20px',
|
|
57
|
+
borderRadius: '8px',
|
|
58
|
+
fontWeight: 600,
|
|
59
|
+
cursor: 'pointer',
|
|
60
|
+
display: 'flex',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
gap: '8px',
|
|
63
|
+
boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
|
|
64
|
+
position: 'relative',
|
|
65
|
+
};
|
|
66
|
+
const cardGridStyle = (cols) => ({
|
|
67
|
+
display: 'grid',
|
|
68
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
69
|
+
gap: '20px',
|
|
70
|
+
marginBottom: '32px',
|
|
71
|
+
});
|
|
72
|
+
const cardStyle = {
|
|
73
|
+
background: '#fff',
|
|
74
|
+
padding: '20px',
|
|
75
|
+
borderRadius: '12px',
|
|
76
|
+
border: '1px solid #e2e8f0',
|
|
77
|
+
boxShadow: '0 1px 3px rgba(0,0,0,0.02)',
|
|
78
|
+
display: 'flex',
|
|
79
|
+
alignItems: 'center', // Align horizontal
|
|
80
|
+
gap: '16px',
|
|
81
|
+
};
|
|
82
|
+
const toolbarRowStyle = {
|
|
83
|
+
display: 'flex',
|
|
84
|
+
justifyContent: 'space-between',
|
|
85
|
+
alignItems: 'center',
|
|
86
|
+
marginBottom: '20px',
|
|
87
|
+
gap: '16px',
|
|
88
|
+
};
|
|
89
|
+
const tabBarStyle = {
|
|
90
|
+
display: 'grid',
|
|
91
|
+
gridTemplateColumns: 'repeat(12, 1fr)', // Use 12-column grid for dynamic sizing
|
|
92
|
+
gap: '12px',
|
|
93
|
+
flex: 1,
|
|
94
|
+
};
|
|
95
|
+
const pillTabStyle = (isActive, colSpan) => ({
|
|
96
|
+
padding: '8px 20px',
|
|
97
|
+
borderRadius: '9999px', // Pill shape
|
|
98
|
+
background: isActive ? '#005bb5' : '#fff',
|
|
99
|
+
color: isActive ? '#fff' : '#64748b',
|
|
100
|
+
fontWeight: 600,
|
|
101
|
+
fontSize: '14px',
|
|
102
|
+
cursor: 'pointer',
|
|
103
|
+
display: 'flex',
|
|
104
|
+
alignItems: 'center',
|
|
105
|
+
justifyContent: 'center',
|
|
106
|
+
gap: '8px',
|
|
107
|
+
boxShadow: '0 1px 3px rgba(0,0,0,0.05)',
|
|
108
|
+
transition: 'all 0.2s',
|
|
109
|
+
border: isActive ? '1px solid #005bb5' : '1px solid #e2e8f0', // Simplified
|
|
110
|
+
gridColumn: colSpan ? `span ${colSpan}` : 'span 2', // Default span if not specified
|
|
111
|
+
whiteSpace: 'nowrap',
|
|
112
|
+
overflow: 'hidden',
|
|
113
|
+
textOverflow: 'ellipsis',
|
|
114
|
+
});
|
|
115
|
+
const tableContainerStyle = {
|
|
116
|
+
background: '#fff',
|
|
117
|
+
borderRadius: '12px',
|
|
118
|
+
border: '1px solid #e2e8f0',
|
|
119
|
+
overflow: 'hidden',
|
|
120
|
+
boxShadow: '0 1px 3px rgba(0,0,0,0.05)',
|
|
121
|
+
};
|
|
122
|
+
const tableStyle = {
|
|
123
|
+
width: '100%',
|
|
124
|
+
borderCollapse: 'collapse',
|
|
125
|
+
fontSize: '14px',
|
|
126
|
+
};
|
|
127
|
+
const thStyle = {
|
|
128
|
+
background: '#f8fafc',
|
|
129
|
+
padding: '14px 16px',
|
|
130
|
+
textAlign: 'left',
|
|
131
|
+
fontWeight: 700,
|
|
132
|
+
color: '#475569',
|
|
133
|
+
borderBottom: '1px solid #e2e8f0',
|
|
134
|
+
};
|
|
135
|
+
const tdStyle = {
|
|
136
|
+
padding: '16px',
|
|
137
|
+
borderBottom: '1px solid #e2e8f0',
|
|
138
|
+
color: '#1e293b',
|
|
139
|
+
};
|
|
140
|
+
const actionIconStyle = {
|
|
141
|
+
cursor: 'pointer',
|
|
142
|
+
// padding: '6px',
|
|
143
|
+
borderRadius: '6px',
|
|
144
|
+
fontSize: '18px',
|
|
145
|
+
border: 'none',
|
|
146
|
+
background: 'none',
|
|
147
|
+
transition: 'background 0.2s',
|
|
148
|
+
display: 'inline-flex',
|
|
149
|
+
alignItems: 'center',
|
|
150
|
+
justifyContent: 'center',
|
|
151
|
+
};
|
|
152
|
+
const dropdownMenuStyle = {
|
|
153
|
+
position: 'absolute',
|
|
154
|
+
top: '100%',
|
|
155
|
+
right: 0,
|
|
156
|
+
background: '#fff',
|
|
157
|
+
borderRadius: '8px',
|
|
158
|
+
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
|
|
159
|
+
marginTop: '8px',
|
|
160
|
+
minWidth: '160px',
|
|
161
|
+
zIndex: 100,
|
|
162
|
+
border: '1px solid #e2e8f0',
|
|
163
|
+
padding: '4px',
|
|
164
|
+
};
|
|
165
|
+
const dropdownItemStyle = {
|
|
166
|
+
width: '100%',
|
|
167
|
+
padding: '10px 16px',
|
|
168
|
+
background: 'none',
|
|
169
|
+
border: 'none',
|
|
170
|
+
textAlign: 'left',
|
|
171
|
+
cursor: 'pointer',
|
|
172
|
+
fontSize: '14px',
|
|
173
|
+
borderRadius: '6px',
|
|
174
|
+
color: '#1e293b',
|
|
175
|
+
fontWeight: 500,
|
|
176
|
+
display: 'flex',
|
|
177
|
+
alignItems: 'center',
|
|
178
|
+
gap: '8px',
|
|
179
|
+
};
|
|
180
|
+
// ─── Component ───────────────────────────────────────────────────────────────
|
|
181
|
+
const FvMasterScreen = ({ config }) => {
|
|
182
|
+
var _a, _b, _c;
|
|
183
|
+
const [showAddDropdown, setShowAddDropdown] = (0, react_1.useState)(false);
|
|
184
|
+
const [sortConfig, setSortConfig] = (0, react_1.useState)(null);
|
|
185
|
+
const handleSort = (key) => {
|
|
186
|
+
let direction = 'desc'; // Default to descending as requested
|
|
187
|
+
if (sortConfig && sortConfig.key === key && sortConfig.direction === 'desc') {
|
|
188
|
+
direction = 'asc';
|
|
189
|
+
}
|
|
190
|
+
setSortConfig({ key, direction });
|
|
191
|
+
};
|
|
192
|
+
const getSortedData = () => {
|
|
193
|
+
var _a;
|
|
194
|
+
const data = [...(((_a = config.table) === null || _a === void 0 ? void 0 : _a.data) || [])];
|
|
195
|
+
if (!sortConfig)
|
|
196
|
+
return data;
|
|
197
|
+
return data.sort((a, b) => {
|
|
198
|
+
const aValue = a[sortConfig.key];
|
|
199
|
+
const bValue = b[sortConfig.key];
|
|
200
|
+
if (aValue === bValue)
|
|
201
|
+
return 0;
|
|
202
|
+
const isAsc = sortConfig.direction === 'asc';
|
|
203
|
+
// Handle numeric sorting
|
|
204
|
+
if (typeof aValue === 'number' && typeof bValue === 'number') {
|
|
205
|
+
return isAsc ? aValue - bValue : bValue - aValue;
|
|
206
|
+
}
|
|
207
|
+
// Handle string/other sorting
|
|
208
|
+
const aStr = String(aValue || '').toLowerCase();
|
|
209
|
+
const bStr = String(bValue || '').toLowerCase();
|
|
210
|
+
if (aStr < bStr)
|
|
211
|
+
return isAsc ? -1 : 1;
|
|
212
|
+
if (aStr > bStr)
|
|
213
|
+
return isAsc ? 1 : -1;
|
|
214
|
+
return 0;
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
const sortedData = getSortedData();
|
|
218
|
+
const renderPagination = () => {
|
|
219
|
+
var _a;
|
|
220
|
+
const pag = (_a = config.table) === null || _a === void 0 ? void 0 : _a.pagination;
|
|
221
|
+
if (!pag)
|
|
222
|
+
return null;
|
|
223
|
+
const totalPages = Math.ceil(pag.totalItems / pag.pageSize);
|
|
224
|
+
return (react_1.default.createElement("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px', borderTop: '1px solid #e2e8f0' }, className: "fv-master-pagination" },
|
|
225
|
+
react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: '14px', color: '#64748b' } },
|
|
226
|
+
"Results Per Page",
|
|
227
|
+
react_1.default.createElement("select", { value: pag.pageSize, onChange: (e) => { var _a; return (_a = pag.onPageSizeChange) === null || _a === void 0 ? void 0 : _a.call(pag, Number(e.target.value)); }, style: { padding: '6px 12px', borderRadius: '6px', border: '1px solid #e2e8f0', background: '#fff' } }, (pag.pageSizeOptions || [10, 20, 50, 100]).map(opt => (react_1.default.createElement("option", { key: opt, value: opt }, opt))))),
|
|
228
|
+
react_1.default.createElement("div", { style: { display: 'flex', gap: '6px' } },
|
|
229
|
+
react_1.default.createElement("button", { disabled: pag.currentPage === 1, onClick: () => pag.onPageChange(pag.currentPage - 1), style: { padding: '6px 14px', borderRadius: '8px', border: '1px solid #e2e8f0', background: '#fff', cursor: pag.currentPage === 1 ? 'not-allowed' : 'pointer', fontWeight: 600 } }, "\u2039"),
|
|
230
|
+
[...Array(totalPages)].map((_, i) => (react_1.default.createElement("button", { key: i + 1, onClick: () => pag.onPageChange(i + 1), style: {
|
|
231
|
+
padding: '6px 14px',
|
|
232
|
+
borderRadius: '8px',
|
|
233
|
+
border: '1px solid #e2e8f0',
|
|
234
|
+
background: pag.currentPage === i + 1 ? '#005bb5' : '#fff',
|
|
235
|
+
color: pag.currentPage === i + 1 ? '#fff' : '#1e293b',
|
|
236
|
+
fontWeight: 700,
|
|
237
|
+
cursor: 'pointer'
|
|
238
|
+
} }, i + 1))),
|
|
239
|
+
react_1.default.createElement("button", { disabled: pag.currentPage === totalPages, onClick: () => pag.onPageChange(pag.currentPage + 1), style: { padding: '6px 14px', borderRadius: '8px', border: '1px solid #e2e8f0', background: '#fff', cursor: pag.currentPage === totalPages ? 'not-allowed' : 'pointer', fontWeight: 600 } }, "\u203A"))));
|
|
240
|
+
};
|
|
241
|
+
return (react_1.default.createElement("div", { style: containerStyle, className: `fv-master-screen ${config.className || ''}`.trim() },
|
|
242
|
+
config.header && (react_1.default.createElement("div", { style: headerRowStyle, className: `fv-master-header ${config.header.className || ''}`.trim() },
|
|
243
|
+
react_1.default.createElement("div", null,
|
|
244
|
+
config.header.title && react_1.default.createElement("h1", { style: titleStyle }, config.header.title),
|
|
245
|
+
config.header.subtitle && react_1.default.createElement("p", { style: subtitleStyle }, config.header.subtitle)),
|
|
246
|
+
react_1.default.createElement("div", { style: { position: 'relative' } }, config.header.addOptions ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
247
|
+
react_1.default.createElement("button", { onClick: () => setShowAddDropdown(!showAddDropdown), style: addBtnStyle, className: "fv-master-add-dropdown-btn" },
|
|
248
|
+
react_1.default.createElement("span", null, ((_a = config.header.addAction) === null || _a === void 0 ? void 0 : _a.icon) || '+'),
|
|
249
|
+
((_b = config.header.addAction) === null || _b === void 0 ? void 0 : _b.label) || 'Add',
|
|
250
|
+
react_1.default.createElement("span", { style: { fontSize: '12px', marginLeft: '4px' } }, "\u25BC")),
|
|
251
|
+
showAddDropdown && (react_1.default.createElement("div", { style: dropdownMenuStyle, className: "fv-master-add-dropdown-menu" }, config.header.addOptions.map((opt, i) => (react_1.default.createElement("button", { key: i, onClick: () => { opt.onClick(); setShowAddDropdown(false); }, style: dropdownItemStyle, onMouseOver: (e) => (e.currentTarget.style.backgroundColor = '#f1f5f9'), onMouseOut: (e) => (e.currentTarget.style.backgroundColor = 'transparent') },
|
|
252
|
+
opt.icon,
|
|
253
|
+
" ",
|
|
254
|
+
opt.label))))))) : (config.header.addAction && (react_1.default.createElement("button", { onClick: config.header.addAction.onClick, style: addBtnStyle, className: `fv-master-add-btn ${config.header.addAction.className || ''}`.trim() },
|
|
255
|
+
config.header.addAction.icon || react_1.default.createElement("span", null, "+"),
|
|
256
|
+
config.header.addAction.label)))))),
|
|
257
|
+
config.cards && config.cards.items.length > 0 && (react_1.default.createElement("div", { style: cardGridStyle(config.cards.maxColsPerRow || 4), className: "fv-master-cards" }, config.cards.items.map((card, idx) => (react_1.default.createElement("div", { key: idx, style: {
|
|
258
|
+
...cardStyle,
|
|
259
|
+
gridColumn: card.colSpan ? `span ${card.colSpan}` : undefined
|
|
260
|
+
}, className: `fv-master-card ${card.className || ''}`.trim() },
|
|
261
|
+
react_1.default.createElement("div", { style: { background: card.color ? `${card.color}15` : '#f1f5f9', padding: '12px', borderRadius: '12px', color: card.color || '#64748b', display: 'flex', alignItems: 'center', justifyContent: 'center' } },
|
|
262
|
+
react_1.default.createElement("span", { style: { fontSize: '20px' } }, card.icon || '🛒')),
|
|
263
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column' } },
|
|
264
|
+
react_1.default.createElement("div", { style: { fontSize: '24px', fontWeight: 800, color: '#1e293b', lineHeight: 1.2 } }, card.value),
|
|
265
|
+
react_1.default.createElement("div", { style: { fontSize: '13px', color: '#64748b', fontWeight: 600 } }, card.label))))))),
|
|
266
|
+
react_1.default.createElement("div", { style: toolbarRowStyle, className: "fv-master-toolbar" },
|
|
267
|
+
config.tabs && (react_1.default.createElement("div", { style: tabBarStyle, className: "fv-master-tabs" }, config.tabs.items.map(tab => {
|
|
268
|
+
var _a;
|
|
269
|
+
const isActive = ((_a = config.tabs) === null || _a === void 0 ? void 0 : _a.activeKey) === tab.key;
|
|
270
|
+
return (react_1.default.createElement("button", { key: tab.key, onClick: () => { var _a; return (_a = config.tabs) === null || _a === void 0 ? void 0 : _a.onTabChange(tab.key); }, style: pillTabStyle(isActive, tab.colSpan) },
|
|
271
|
+
tab.label,
|
|
272
|
+
tab.count !== undefined && (react_1.default.createElement("span", { style: {
|
|
273
|
+
fontSize: '11px',
|
|
274
|
+
background: isActive ? 'rgba(255,255,255,0.2)' : '#e2e8f0',
|
|
275
|
+
color: isActive ? '#fff' : '#64748b',
|
|
276
|
+
padding: '1px 8px',
|
|
277
|
+
borderRadius: '10px'
|
|
278
|
+
} }, tab.count))));
|
|
279
|
+
}))),
|
|
280
|
+
((_c = config.filters) === null || _c === void 0 ? void 0 : _c.onExport) && (react_1.default.createElement("div", { style: { display: 'flex', gap: '8px' } },
|
|
281
|
+
react_1.default.createElement("button", { onClick: () => { var _a, _b; return (_b = (_a = config.filters) === null || _a === void 0 ? void 0 : _a.onExport) === null || _b === void 0 ? void 0 : _b.call(_a, 'excel'); }, style: { ...addBtnStyle, background: '#fff', color: '#1e293b', border: '1px solid #e2e8f0', padding: '8px 16px' }, className: "fv-master-export-btn" },
|
|
282
|
+
react_1.default.createElement("span", { style: { fontSize: '16px' } }, "\uD83D\uDCE5"),
|
|
283
|
+
" Export")))),
|
|
284
|
+
config.table && (react_1.default.createElement("div", { style: tableContainerStyle, className: "fv-master-table-container" },
|
|
285
|
+
react_1.default.createElement("table", { style: tableStyle },
|
|
286
|
+
react_1.default.createElement("thead", null,
|
|
287
|
+
react_1.default.createElement("tr", null,
|
|
288
|
+
config.table.columns.map(col => (react_1.default.createElement("th", { key: col.key, style: {
|
|
289
|
+
...thStyle,
|
|
290
|
+
width: col.width,
|
|
291
|
+
cursor: col.sortable !== false ? 'pointer' : 'default',
|
|
292
|
+
userSelect: 'none'
|
|
293
|
+
}, onClick: () => col.sortable !== false && handleSort(col.sortKey || col.key) },
|
|
294
|
+
react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '4px' } },
|
|
295
|
+
col.header,
|
|
296
|
+
col.sortable !== false && (react_1.default.createElement("span", { style: {
|
|
297
|
+
fontSize: '12px',
|
|
298
|
+
opacity: (sortConfig === null || sortConfig === void 0 ? void 0 : sortConfig.key) === (col.sortKey || col.key) ? 1 : 0.3,
|
|
299
|
+
color: (sortConfig === null || sortConfig === void 0 ? void 0 : sortConfig.key) === (col.sortKey || col.key) ? '#005bb5' : 'inherit'
|
|
300
|
+
} }, (sortConfig === null || sortConfig === void 0 ? void 0 : sortConfig.key) === (col.sortKey || col.key)
|
|
301
|
+
? (sortConfig.direction === 'asc' ? '↑' : '↓')
|
|
302
|
+
: '↓')))))),
|
|
303
|
+
config.table.actions && react_1.default.createElement("th", { style: { ...thStyle, textAlign: 'center' } }, "Actions"))),
|
|
304
|
+
react_1.default.createElement("tbody", null, sortedData.length > 0 ? (sortedData.map((row, rIdx) => (react_1.default.createElement("tr", { key: rIdx, style: { transition: 'background 0.2s' }, onMouseOver: (e) => (e.currentTarget.style.backgroundColor = '#f8fafc'), onMouseOut: (e) => (e.currentTarget.style.backgroundColor = 'transparent') },
|
|
305
|
+
config.table.columns.map(col => (react_1.default.createElement("td", { key: col.key, style: tdStyle }, col.render ? col.render(row) : row[col.key]))),
|
|
306
|
+
config.table.actions && (react_1.default.createElement("td", { style: { ...tdStyle, textAlign: 'right' } },
|
|
307
|
+
react_1.default.createElement("div", { style: { display: 'flex', justifyContent: 'center', gap: '12px' } },
|
|
308
|
+
config.table.actions.onView && (react_1.default.createElement("button", { onClick: () => config.table.actions.onView(row), style: { ...actionIconStyle, color: '#64748b' }, title: "View" }, "\uD83D\uDC41")),
|
|
309
|
+
config.table.actions.onUpdate && (react_1.default.createElement("button", { onClick: () => config.table.actions.onUpdate(row), style: { ...actionIconStyle, color: '#005bb5' }, title: "Edit" }, "\u270E")),
|
|
310
|
+
config.table.actions.onDelete && (react_1.default.createElement("button", { onClick: () => config.table.actions.onDelete(row), style: { ...actionIconStyle, color: '#ef4444' }, title: "Delete" }, "\uD83D\uDDD1"))))))))) : (react_1.default.createElement("tr", null,
|
|
311
|
+
react_1.default.createElement("td", { colSpan: (config.table.columns.length || 0) + (config.table.actions ? 1 : 0), style: { ...tdStyle, textAlign: 'center', padding: '64px', color: '#94a3b8' } },
|
|
312
|
+
react_1.default.createElement("div", { style: { fontSize: '48px', marginBottom: '16px', opacity: 0.5 } }, "\uD83D\uDCE6"),
|
|
313
|
+
react_1.default.createElement("div", { style: { fontSize: '16px', fontWeight: 600 } }, "No records found"),
|
|
314
|
+
react_1.default.createElement("div", { style: { fontSize: '14px', marginTop: '4px' } }, "Try adjusting your filters or search.")))))),
|
|
315
|
+
renderPagination()))));
|
|
316
|
+
};
|
|
317
|
+
exports.FvMasterScreen = FvMasterScreen;
|
|
@@ -27,27 +27,20 @@ exports.FvNumberField = void 0;
|
|
|
27
27
|
const react_1 = __importStar(require("react"));
|
|
28
28
|
const validation_engine_1 = require("@fovestta2/validation-engine");
|
|
29
29
|
const FvNumberField = ({ label = '', placeholder = '', value, schema, disabled = false, readonly = false, min, max, step = 1, className = '', inputClassName = '', labelClassName = '', errorClassName = '', onChange, onBlur, onFocus, }) => {
|
|
30
|
-
const [
|
|
31
|
-
(0, react_1.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!result.isValid && result.errorKey) {
|
|
39
|
-
setErrorMessage(result.errorKey);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
setErrorMessage(null);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
30
|
+
const [isTouched, setIsTouched] = (0, react_1.useState)(false);
|
|
31
|
+
const errorMessage = (0, react_1.useMemo)(() => {
|
|
32
|
+
if (!isTouched || !schema)
|
|
33
|
+
return null;
|
|
34
|
+
const result = validation_engine_1.Validator.validate(value, schema);
|
|
35
|
+
return result.isValid ? null : (result.message || result.errorKey);
|
|
36
|
+
}, [value, schema, isTouched]);
|
|
37
|
+
// Sync logic removed in favor of useMemo
|
|
45
38
|
const handleInput = (e) => {
|
|
46
39
|
const val = e.target.value;
|
|
47
40
|
onChange(val === '' ? '' : Number(val));
|
|
48
41
|
};
|
|
49
42
|
const handleBlur = (e) => {
|
|
50
|
-
|
|
43
|
+
setIsTouched(true);
|
|
51
44
|
if (onBlur)
|
|
52
45
|
onBlur();
|
|
53
46
|
};
|
|
@@ -72,6 +65,9 @@ const FvNumberField = ({ label = '', placeholder = '', value, schema, disabled =
|
|
|
72
65
|
ERR_NUMERIC_INVALID: 'Please enter a valid number',
|
|
73
66
|
ERR_RANGE_INVALID: 'Number is out of range',
|
|
74
67
|
ERR_DUPLICATE: 'This value already exists',
|
|
68
|
+
ERR_MIN_VALUE: 'Value is too small',
|
|
69
|
+
ERR_MAX_VALUE: 'Value is too large',
|
|
70
|
+
ERR_CUSTOM_INVALID: 'Invalid value',
|
|
75
71
|
};
|
|
76
72
|
return errorMessages[errorMessage] || errorMessage;
|
|
77
73
|
};
|
|
@@ -27,27 +27,20 @@ exports.FvPasswordField = void 0;
|
|
|
27
27
|
const react_1 = __importStar(require("react"));
|
|
28
28
|
const validation_engine_1 = require("@fovestta2/validation-engine");
|
|
29
29
|
const FvPasswordField = ({ label = 'Password', placeholder = '••••••••', value, schema, disabled = false, readonly = false, className = '', inputClassName = '', labelClassName = '', errorClassName = '', onChange, onBlur, onFocus, }) => {
|
|
30
|
-
const [
|
|
30
|
+
const [isTouched, setIsTouched] = (0, react_1.useState)(false);
|
|
31
31
|
const [showPassword, setShowPassword] = (0, react_1.useState)(false);
|
|
32
|
-
(0, react_1.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (!result.isValid && result.errorKey) {
|
|
40
|
-
setErrorMessage(result.errorKey);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
setErrorMessage(null);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
32
|
+
const errorMessage = (0, react_1.useMemo)(() => {
|
|
33
|
+
if (!isTouched || !schema)
|
|
34
|
+
return null;
|
|
35
|
+
const result = validation_engine_1.Validator.validate(value, schema);
|
|
36
|
+
return result.isValid ? null : (result.message || result.errorKey);
|
|
37
|
+
}, [value, schema, isTouched]);
|
|
38
|
+
// Sync logic removed in favor of useMemo
|
|
46
39
|
const handleInput = (e) => {
|
|
47
40
|
onChange(e.target.value);
|
|
48
41
|
};
|
|
49
42
|
const handleBlur = (e) => {
|
|
50
|
-
|
|
43
|
+
setIsTouched(true);
|
|
51
44
|
if (onBlur)
|
|
52
45
|
onBlur();
|
|
53
46
|
};
|
|
@@ -76,6 +69,9 @@ const FvPasswordField = ({ label = 'Password', placeholder = '••••••
|
|
|
76
69
|
ERR_PASSWORD_MISSING_LOWERCASE: 'Lowercase letter missing',
|
|
77
70
|
ERR_PASSWORD_MISSING_NUMBER: 'At least one number missing',
|
|
78
71
|
ERR_PASSWORD_MISSING_SPECIAL_CHAR: 'Special character missing',
|
|
72
|
+
ERR_MIN_VALUE: 'Value is too small',
|
|
73
|
+
ERR_MAX_VALUE: 'Value is too large',
|
|
74
|
+
ERR_CUSTOM_INVALID: 'Invalid value',
|
|
79
75
|
};
|
|
80
76
|
return errorMessages[errorMessage] || errorMessage;
|
|
81
77
|
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -45,3 +45,4 @@ Object.defineProperty(exports, "FvDocumentField", { enumerable: true, get: funct
|
|
|
45
45
|
__exportStar(require("./components/FvRichTextEditor"), exports);
|
|
46
46
|
__exportStar(require("./components/AddUpdateForm"), exports);
|
|
47
47
|
__exportStar(require("./components/QueryForm"), exports);
|
|
48
|
+
__exportStar(require("./components/FvMasterScreen"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fovestta2/web-react",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
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.
|
|
28
|
+
"@fovestta2/validation-engine": "^1.2.3"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/react": "^18.2.0",
|