funuicss 3.8.11 → 3.8.13
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/css/fun.css +4 -3
- package/package.json +1 -1
- package/ui/form/Form.js +111 -1006
- package/ui/video/Video.js +19 -2
- package/ui/vista/Vista.d.ts +19 -5
- package/ui/vista/Vista.js +210 -55
package/ui/form/Form.js
CHANGED
|
@@ -164,7 +164,7 @@ var FormCheckbox = function (_a) {
|
|
|
164
164
|
alignItems: 'center',
|
|
165
165
|
justifyContent: 'center',
|
|
166
166
|
flexShrink: 0,
|
|
167
|
-
} }, checked && (react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.
|
|
167
|
+
} }, checked && (react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org2000/svg", style: {
|
|
168
168
|
stroke: 'white',
|
|
169
169
|
strokeWidth: '2',
|
|
170
170
|
strokeLinecap: 'round',
|
|
@@ -282,7 +282,7 @@ var formatWhatsAppMessage = function (values, fields, header, footer) {
|
|
|
282
282
|
}
|
|
283
283
|
return encodeURIComponent(message);
|
|
284
284
|
};
|
|
285
|
-
// Main Form Component -
|
|
285
|
+
// Main Form Component - FIXED
|
|
286
286
|
var Form = function (props) {
|
|
287
287
|
var fieldsProp = props.fields, onSubmitProp = props.onSubmit, _a = props.defaultValues, defaultValuesProp = _a === void 0 ? {} : _a, _b = props.submitText, submitTextProp = _b === void 0 ? 'Submit' : _b, _c = props.submitBg, submitBgProp = _c === void 0 ? 'primary' : _c, submitPrefixProp = props.submitPrefix, submitSuffixProp = props.submitSuffix, _d = props.resetText, resetTextProp = _d === void 0 ? 'Reset' : _d, _e = props.showReset, showResetProp = _e === void 0 ? true : _e, _f = props.isLoading, isLoadingProp = _f === void 0 ? false : _f, _g = props.className, classNameProp = _g === void 0 ? '' : _g, _h = props.layout, layoutProp = _h === void 0 ? 'vertical' : _h, _j = props.gap, gapProp = _j === void 0 ? '1.5rem' : _j, titleProp = props.title, titleSizeProp = props.titleSize, titleColorProp = props.titleColor, descriptionProp = props.description, descriptionSizeProp = props.descriptionSize, descriptionColorProp = props.descriptionColor, whatsappContactProp = props.whatsappContact, widthProp = props.width, centeredProp = props.centered, whatsappHeaderProp = props.whatsappHeader, whatsappFooterProp = props.whatsappFooter, _k = props.fullWidth, fullWidthProp = _k === void 0 ? true : _k, _l = props.variant, variant = _l === void 0 ? '' : _l;
|
|
288
288
|
// Use component configuration with variant
|
|
@@ -349,7 +349,8 @@ var Form = function (props) {
|
|
|
349
349
|
var parsedDefaultValues = (0, react_1.useMemo)(function () { return parseJsonInput(defaultValues, {}); }, [defaultValues]);
|
|
350
350
|
// State management
|
|
351
351
|
var _m = (0, react_1.useState)({}), errors = _m[0], setErrors = _m[1];
|
|
352
|
-
var _o = (0, react_1.useState)(
|
|
352
|
+
var _o = (0, react_1.useState)({}), touched = _o[0], setTouched = _o[1];
|
|
353
|
+
var _p = (0, react_1.useState)(function () {
|
|
353
354
|
// Initialize form values from defaultValues and field values
|
|
354
355
|
var initialValues = {};
|
|
355
356
|
parsedFields.forEach(function (field) {
|
|
@@ -373,9 +374,8 @@ var Form = function (props) {
|
|
|
373
374
|
}
|
|
374
375
|
});
|
|
375
376
|
return initialValues;
|
|
376
|
-
}), formValues =
|
|
377
|
-
var
|
|
378
|
-
var _q = (0, react_1.useState)(false), hasBeenSubmitted = _q[0], setHasBeenSubmitted = _q[1];
|
|
377
|
+
}), formValues = _p[0], setFormValues = _p[1];
|
|
378
|
+
var _q = (0, react_1.useState)(false), isSubmitting = _q[0], setIsSubmitting = _q[1];
|
|
379
379
|
// Update form values when defaultValues prop changes
|
|
380
380
|
(0, react_1.useEffect)(function () {
|
|
381
381
|
if (Object.keys(parsedDefaultValues).length > 0) {
|
|
@@ -476,7 +476,6 @@ var Form = function (props) {
|
|
|
476
476
|
}
|
|
477
477
|
});
|
|
478
478
|
setErrors(newErrors);
|
|
479
|
-
setHasBeenSubmitted(true);
|
|
480
479
|
return !hasErrors;
|
|
481
480
|
}, [parsedFields, validateField, formValues]);
|
|
482
481
|
// Handle field change for checkboxes and radios
|
|
@@ -486,36 +485,96 @@ var Form = function (props) {
|
|
|
486
485
|
var _a;
|
|
487
486
|
return (__assign(__assign({}, prev), (_a = {}, _a[fieldName] = newValue, _a)));
|
|
488
487
|
});
|
|
489
|
-
//
|
|
488
|
+
// Update touched state
|
|
489
|
+
setTouched(function (prev) {
|
|
490
|
+
var _a;
|
|
491
|
+
return (__assign(__assign({}, prev), (_a = {}, _a[fieldName] = true, _a)));
|
|
492
|
+
});
|
|
493
|
+
// Validate the changed field
|
|
494
|
+
var field = parsedFields.find(function (f) { return f.name === fieldName; });
|
|
495
|
+
if (!field)
|
|
496
|
+
return;
|
|
497
|
+
var error = validateField(field, newValue);
|
|
490
498
|
setErrors(function (prev) {
|
|
491
|
-
var
|
|
492
|
-
|
|
493
|
-
|
|
499
|
+
var _a;
|
|
500
|
+
if (error) {
|
|
501
|
+
return __assign(__assign({}, prev), (_a = {}, _a[fieldName] = error, _a));
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
var newErrors = __assign({}, prev);
|
|
505
|
+
delete newErrors[fieldName];
|
|
506
|
+
return newErrors;
|
|
507
|
+
}
|
|
494
508
|
});
|
|
495
|
-
}, []);
|
|
496
|
-
// Handle input change -
|
|
509
|
+
}, [parsedFields, validateField]);
|
|
510
|
+
// FIXED: Handle input change - pass event directly to Input component
|
|
497
511
|
var handleInputEventChange = (0, react_1.useCallback)(function (e) {
|
|
498
512
|
var fieldName = e.target.name;
|
|
499
513
|
var value = e.target.value;
|
|
500
|
-
// Update form values
|
|
514
|
+
// Update form values - PASS VALUE AS-IS (preserves spaces)
|
|
501
515
|
setFormValues(function (prev) {
|
|
502
516
|
var _a;
|
|
503
517
|
return (__assign(__assign({}, prev), (_a = {}, _a[fieldName] = value, _a)));
|
|
504
518
|
});
|
|
505
|
-
//
|
|
519
|
+
// Update touched state
|
|
520
|
+
setTouched(function (prev) {
|
|
521
|
+
var _a;
|
|
522
|
+
return (__assign(__assign({}, prev), (_a = {}, _a[fieldName] = true, _a)));
|
|
523
|
+
});
|
|
524
|
+
// Validate the changed field
|
|
525
|
+
var field = parsedFields.find(function (f) { return f.name === fieldName; });
|
|
526
|
+
if (!field)
|
|
527
|
+
return;
|
|
528
|
+
var error = validateField(field, value);
|
|
506
529
|
setErrors(function (prev) {
|
|
507
|
-
var
|
|
508
|
-
|
|
509
|
-
|
|
530
|
+
var _a;
|
|
531
|
+
if (error) {
|
|
532
|
+
return __assign(__assign({}, prev), (_a = {}, _a[fieldName] = error, _a));
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
var newErrors = __assign({}, prev);
|
|
536
|
+
delete newErrors[fieldName];
|
|
537
|
+
return newErrors;
|
|
538
|
+
}
|
|
510
539
|
});
|
|
511
|
-
}, []);
|
|
540
|
+
}, [parsedFields, validateField]);
|
|
541
|
+
// FIXED: Handle blur event - pass event directly to Input component
|
|
542
|
+
var handleInputEventBlur = (0, react_1.useCallback)(function (e) {
|
|
543
|
+
var fieldName = e.target.name;
|
|
544
|
+
setTouched(function (prev) {
|
|
545
|
+
var _a;
|
|
546
|
+
return (__assign(__assign({}, prev), (_a = {}, _a[fieldName] = true, _a)));
|
|
547
|
+
});
|
|
548
|
+
// Validate the field
|
|
549
|
+
var field = parsedFields.find(function (f) { return f.name === fieldName; });
|
|
550
|
+
if (!field)
|
|
551
|
+
return;
|
|
552
|
+
var value = formValues[fieldName];
|
|
553
|
+
var error = validateField(field, value);
|
|
554
|
+
setErrors(function (prev) {
|
|
555
|
+
var _a;
|
|
556
|
+
if (error) {
|
|
557
|
+
return __assign(__assign({}, prev), (_a = {}, _a[fieldName] = error, _a));
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
var newErrors = __assign({}, prev);
|
|
561
|
+
delete newErrors[fieldName];
|
|
562
|
+
return newErrors;
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}, [parsedFields, validateField, formValues]);
|
|
512
566
|
// Handle form submission
|
|
513
567
|
var handleSubmit = (0, react_1.useCallback)(function (e) { return __awaiter(void 0, void 0, void 0, function () {
|
|
514
|
-
var isValid, firstErrorField, element, message, cleanPhone, whatsappUrl, error_1;
|
|
568
|
+
var allTouched, isValid, firstErrorField, element, message, cleanPhone, whatsappUrl, error_1;
|
|
515
569
|
return __generator(this, function (_a) {
|
|
516
570
|
switch (_a.label) {
|
|
517
571
|
case 0:
|
|
518
572
|
e.preventDefault();
|
|
573
|
+
allTouched = {};
|
|
574
|
+
parsedFields.forEach(function (field) {
|
|
575
|
+
allTouched[field.name] = true;
|
|
576
|
+
});
|
|
577
|
+
setTouched(allTouched);
|
|
519
578
|
isValid = validateForm();
|
|
520
579
|
if (!isValid) {
|
|
521
580
|
firstErrorField = Object.keys(errors)[0];
|
|
@@ -559,7 +618,7 @@ var Form = function (props) {
|
|
|
559
618
|
case 9: return [2 /*return*/];
|
|
560
619
|
}
|
|
561
620
|
});
|
|
562
|
-
}); }, [validateForm, formValues, whatsappContact, whatsappHeader, whatsappFooter, onSubmit, errors
|
|
621
|
+
}); }, [parsedFields, validateForm, formValues, whatsappContact, whatsappHeader, whatsappFooter, onSubmit, errors]);
|
|
563
622
|
// Handle form reset
|
|
564
623
|
var handleReset = (0, react_1.useCallback)(function () {
|
|
565
624
|
// Reset to initial values
|
|
@@ -585,36 +644,49 @@ var Form = function (props) {
|
|
|
585
644
|
});
|
|
586
645
|
setFormValues(initialValues);
|
|
587
646
|
setErrors({});
|
|
588
|
-
|
|
647
|
+
setTouched({});
|
|
589
648
|
}, [parsedFields, parsedDefaultValues]);
|
|
590
649
|
// Get field status for Input component
|
|
591
650
|
var getFieldStatus = (0, react_1.useCallback)(function (fieldName) {
|
|
592
651
|
var error = errors[fieldName];
|
|
593
|
-
|
|
594
|
-
if (
|
|
652
|
+
var isTouched = touched[fieldName];
|
|
653
|
+
if (error)
|
|
595
654
|
return 'danger';
|
|
655
|
+
if (isTouched && !error && formValues[fieldName] !== '') {
|
|
656
|
+
return 'success';
|
|
596
657
|
}
|
|
597
658
|
return undefined;
|
|
598
|
-
}, [errors,
|
|
659
|
+
}, [errors, touched, formValues]);
|
|
599
660
|
// Check if form is valid for submission
|
|
600
661
|
var isFormValid = (0, react_1.useMemo)(function () {
|
|
601
|
-
//
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
662
|
+
// Check if any required fields are empty
|
|
663
|
+
var hasEmptyRequiredFields = parsedFields.some(function (field) {
|
|
664
|
+
if (!field.required)
|
|
665
|
+
return false;
|
|
666
|
+
var value = formValues[field.name];
|
|
667
|
+
if (field.type === 'checkbox' && field.multiple) {
|
|
668
|
+
return !Array.isArray(value) || value.length === 0;
|
|
669
|
+
}
|
|
670
|
+
if (field.type === 'checkbox') {
|
|
671
|
+
return value === false;
|
|
672
|
+
}
|
|
673
|
+
return !value || value === '';
|
|
674
|
+
});
|
|
675
|
+
// Check if there are any validation errors
|
|
676
|
+
var hasValidationErrors = Object.keys(errors).length > 0;
|
|
677
|
+
return !hasEmptyRequiredFields && !hasValidationErrors;
|
|
678
|
+
}, [parsedFields, formValues, errors]);
|
|
608
679
|
// Submit button disabled state
|
|
609
|
-
var isSubmitDisabled = isSubmitting || isLoading;
|
|
680
|
+
var isSubmitDisabled = isSubmitting || isLoading || !isFormValid;
|
|
610
681
|
// Check if WhatsApp is configured
|
|
611
682
|
var hasWhatsApp = !!whatsappContact;
|
|
612
|
-
// Render field based on type
|
|
683
|
+
// Render field based on type - FIXED
|
|
613
684
|
var renderField = (0, react_1.useCallback)(function (field) {
|
|
614
685
|
var _a, _b, _c, _d, _e;
|
|
615
686
|
var status = getFieldStatus(field.name);
|
|
616
687
|
var error = errors[field.name];
|
|
617
|
-
var
|
|
688
|
+
var isTouched = touched[field.name];
|
|
689
|
+
var showError = error && isTouched;
|
|
618
690
|
var value = formValues[field.name];
|
|
619
691
|
// Field wrapper classes
|
|
620
692
|
var wrapperClass = "col min-w-200 field ".concat(showError ? 'field-error' : '').trim();
|
|
@@ -667,16 +739,16 @@ var Form = function (props) {
|
|
|
667
739
|
react_1.default.createElement(Flex_1.default, { direction: "column", gap: "0.5rem" }, field.options.map(function (option, index) { return (react_1.default.createElement(FormRadio, { key: "".concat(field.name, "_").concat(index), id: "".concat(inputId, "_").concat(index), label: option.label, checked: value === option.value, onChange: function () { return handleFieldChange(field.name, option.value); }, disabled: field.disabled || option.disabled || isLoading, required: field.required && index === 0, value: option.value })); }))));
|
|
668
740
|
case 'textarea':
|
|
669
741
|
return (react_1.default.createElement("div", { key: field.name, className: wrapperClass },
|
|
670
|
-
react_1.default.createElement(Input_1.default, __assign({ multiline: true, rows: ((_d = field.inputProps) === null || _d === void 0 ? void 0 : _d.rows) || 4, value: value || '', onChange: handleInputEventChange }, baseProps))));
|
|
742
|
+
react_1.default.createElement(Input_1.default, __assign({ multiline: true, rows: ((_d = field.inputProps) === null || _d === void 0 ? void 0 : _d.rows) || 4, value: value || '', onChange: handleInputEventChange, onBlur: handleInputEventBlur }, baseProps))));
|
|
671
743
|
case 'select':
|
|
672
744
|
return (react_1.default.createElement("div", { key: field.name, className: wrapperClass },
|
|
673
|
-
react_1.default.createElement(Input_1.default, __assign({ select: true, options: ((_e = field.options) === null || _e === void 0 ? void 0 : _e.map(function (opt) { return ({ text: opt.label, value: opt.value }); })) || [], value: value || '', onChange: handleInputEventChange }, baseProps))));
|
|
745
|
+
react_1.default.createElement(Input_1.default, __assign({ select: true, options: ((_e = field.options) === null || _e === void 0 ? void 0 : _e.map(function (opt) { return ({ text: opt.label, value: opt.value }); })) || [], value: value || '', onChange: handleInputEventChange, onBlur: handleInputEventBlur }, baseProps))));
|
|
674
746
|
default:
|
|
675
747
|
// text, email, number, tel, date, file, password
|
|
676
748
|
return (react_1.default.createElement("div", { key: field.name, className: wrapperClass },
|
|
677
|
-
react_1.default.createElement(Input_1.default, __assign({ type: field.type, value: value || '', onChange: handleInputEventChange }, baseProps))));
|
|
749
|
+
react_1.default.createElement(Input_1.default, __assign({ type: field.type, value: value || '', onChange: handleInputEventChange, onBlur: handleInputEventBlur }, baseProps))));
|
|
678
750
|
}
|
|
679
|
-
}, [errors,
|
|
751
|
+
}, [errors, touched, formValues, isLoading, getFieldStatus, handleFieldChange, handleInputEventChange, handleInputEventBlur, fullWidth]);
|
|
680
752
|
// Don't render if no fields are configured
|
|
681
753
|
if (parsedFields.length === 0) {
|
|
682
754
|
console.warn('Form: No fields configured. Please provide fields prop or configure via theme.');
|
|
@@ -693,970 +765,3 @@ var Form = function (props) {
|
|
|
693
765
|
react_1.default.createElement(Button_1.default, { type: "submit", text: hasWhatsApp ? "Send via WhatsApp" : submitText, bg: submitBg || "primary", raised: true, prefix: submitPrefix || hasWhatsApp ? react_1.default.createElement(pi_1.PiWhatsappLogo, null) : react_1.default.createElement(pi_1.PiPaperPlaneTilt, null), suffix: submitSuffix, disabled: isSubmitDisabled, isLoading: isSubmitting || isLoading, fullWidth: fullWidth })))));
|
|
694
766
|
};
|
|
695
767
|
exports.default = Form;
|
|
696
|
-
// 'use client';
|
|
697
|
-
// import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
698
|
-
// import Button from '../button/Button';
|
|
699
|
-
// import Input from '../input/Input';
|
|
700
|
-
// import Flex from '../flex/Flex';
|
|
701
|
-
// import Text from '../text/Text';
|
|
702
|
-
// import { PiPaperPlaneTilt, PiWarningCircle, PiWhatsappLogo } from 'react-icons/pi';
|
|
703
|
-
// import { useComponentConfiguration } from '../../utils/componentUtils';
|
|
704
|
-
// import { useVariant } from '../theme/theme';
|
|
705
|
-
// // Field types supported
|
|
706
|
-
// export type InputType =
|
|
707
|
-
// | 'text'
|
|
708
|
-
// | 'email'
|
|
709
|
-
// | 'number'
|
|
710
|
-
// | 'tel'
|
|
711
|
-
// | 'textarea'
|
|
712
|
-
// | 'password'
|
|
713
|
-
// | 'date'
|
|
714
|
-
// | 'select'
|
|
715
|
-
// | 'checkbox'
|
|
716
|
-
// | 'radio'
|
|
717
|
-
// | 'file';
|
|
718
|
-
// // Field option
|
|
719
|
-
// export interface FieldOption {
|
|
720
|
-
// label: string;
|
|
721
|
-
// value: string;
|
|
722
|
-
// disabled?: boolean;
|
|
723
|
-
// }
|
|
724
|
-
// // Simplified field configuration
|
|
725
|
-
// export interface FormField {
|
|
726
|
-
// name: string;
|
|
727
|
-
// label?: string;
|
|
728
|
-
// type: InputType;
|
|
729
|
-
// required?: boolean;
|
|
730
|
-
// placeholder?: string;
|
|
731
|
-
// options?: FieldOption[];
|
|
732
|
-
// multiple?: boolean; // For checkboxes
|
|
733
|
-
// value?: any; // Initial value
|
|
734
|
-
// disabled?: boolean;
|
|
735
|
-
// helperText?: string;
|
|
736
|
-
// // Input component props (passed directly to Input)
|
|
737
|
-
// inputProps?: {
|
|
738
|
-
// startIcon?: React.ReactNode;
|
|
739
|
-
// endIcon?: React.ReactNode;
|
|
740
|
-
// prefix?: React.ReactNode;
|
|
741
|
-
// suffix?: React.ReactNode;
|
|
742
|
-
// stringPrefix?: string;
|
|
743
|
-
// stringSuffix?: string;
|
|
744
|
-
// iconicBg?: string;
|
|
745
|
-
// funcss?: string;
|
|
746
|
-
// bg?: string;
|
|
747
|
-
// flat?: boolean;
|
|
748
|
-
// bordered?: boolean;
|
|
749
|
-
// borderless?: boolean;
|
|
750
|
-
// rounded?: boolean;
|
|
751
|
-
// leftRounded?: boolean;
|
|
752
|
-
// rightRounded?: boolean;
|
|
753
|
-
// rows?: number;
|
|
754
|
-
// noBorder?: boolean;
|
|
755
|
-
// variant?: string;
|
|
756
|
-
// // Standard HTML attributes
|
|
757
|
-
// id?: string;
|
|
758
|
-
// autocomplete?: string;
|
|
759
|
-
// pattern?: string;
|
|
760
|
-
// min?: string | number;
|
|
761
|
-
// max?: string | number;
|
|
762
|
-
// minLength?: number;
|
|
763
|
-
// maxLength?: number;
|
|
764
|
-
// step?: string | number;
|
|
765
|
-
// };
|
|
766
|
-
// }
|
|
767
|
-
// // Form props
|
|
768
|
-
// export interface FormProps {
|
|
769
|
-
// fields?: FormField[] | string; // Allow string (JSON) or array
|
|
770
|
-
// onSubmit?: (values: Record<string, any>, viaWhatsApp?: boolean) => void;
|
|
771
|
-
// defaultValues?: Record<string, any> | string; // Allow string (JSON) or object
|
|
772
|
-
// submitText?: string;
|
|
773
|
-
// submitBg?: string;
|
|
774
|
-
// submitPrefix?: React.ReactNode | string;
|
|
775
|
-
// submitSuffix?: React.ReactNode | string;
|
|
776
|
-
// resetText?: string;
|
|
777
|
-
// showReset?: boolean;
|
|
778
|
-
// isLoading?: boolean;
|
|
779
|
-
// className?: string;
|
|
780
|
-
// layout?: 'vertical' | 'horizontal';
|
|
781
|
-
// gap?: number | string;
|
|
782
|
-
// title?: string;
|
|
783
|
-
// titleSize?: string;
|
|
784
|
-
// titleColor?: string;
|
|
785
|
-
// description?: string;
|
|
786
|
-
// descriptionSize?: string;
|
|
787
|
-
// descriptionColor?: string;
|
|
788
|
-
// // WhatsApp props (simple, not object)
|
|
789
|
-
// whatsappContact?: string;
|
|
790
|
-
// whatsappHeader?: string;
|
|
791
|
-
// whatsappFooter?: string;
|
|
792
|
-
// // New props for fullWidth
|
|
793
|
-
// fullWidth?: boolean;
|
|
794
|
-
// width?: string;
|
|
795
|
-
// centered?: boolean;
|
|
796
|
-
// // Variant support
|
|
797
|
-
// variant?: string;
|
|
798
|
-
// }
|
|
799
|
-
// // Helper function to parse JSON input
|
|
800
|
-
// const parseJsonInput = <T,>(input: T | string | undefined, defaultValue: T): T => {
|
|
801
|
-
// if (input === undefined || input === null) {
|
|
802
|
-
// return defaultValue;
|
|
803
|
-
// }
|
|
804
|
-
// // If it's already the correct type, return as is
|
|
805
|
-
// if (typeof input !== 'string') {
|
|
806
|
-
// return input;
|
|
807
|
-
// }
|
|
808
|
-
// try {
|
|
809
|
-
// // Try to parse as JSON
|
|
810
|
-
// const parsed = JSON.parse(input);
|
|
811
|
-
// return parsed;
|
|
812
|
-
// } catch (error) {
|
|
813
|
-
// console.warn('Failed to parse JSON input:', input, error);
|
|
814
|
-
// // If parsing fails, try to interpret as a string that might be valid
|
|
815
|
-
// try {
|
|
816
|
-
// // Try to handle common cases like arrays or objects without quotes
|
|
817
|
-
// const trimmed = input.trim();
|
|
818
|
-
// if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
819
|
-
// return JSON.parse(trimmed);
|
|
820
|
-
// } else if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
821
|
-
// return JSON.parse(trimmed);
|
|
822
|
-
// }
|
|
823
|
-
// } catch (e) {
|
|
824
|
-
// // If still fails, return default
|
|
825
|
-
// }
|
|
826
|
-
// return defaultValue;
|
|
827
|
-
// }
|
|
828
|
-
// };
|
|
829
|
-
// // Custom Checkbox Component (unchanged)
|
|
830
|
-
// const FormCheckbox: React.FC<{
|
|
831
|
-
// label?: string;
|
|
832
|
-
// checked: boolean;
|
|
833
|
-
// onChange: (checked: boolean) => void;
|
|
834
|
-
// disabled?: boolean;
|
|
835
|
-
// required?: boolean;
|
|
836
|
-
// value?: string;
|
|
837
|
-
// id?: string;
|
|
838
|
-
// }> = ({ label, checked, onChange, disabled, required, value, id }) => {
|
|
839
|
-
// return (
|
|
840
|
-
// <label
|
|
841
|
-
// className="funui_form-checkbox"
|
|
842
|
-
// style={{
|
|
843
|
-
// display: 'flex',
|
|
844
|
-
// alignItems: 'center',
|
|
845
|
-
// gap: '0.5rem',
|
|
846
|
-
// cursor: disabled ? 'not-allowed' : 'pointer',
|
|
847
|
-
// userSelect: 'none',
|
|
848
|
-
// width: 'fit-content',
|
|
849
|
-
// padding: '0.25rem 0',
|
|
850
|
-
// }}
|
|
851
|
-
// >
|
|
852
|
-
// <input
|
|
853
|
-
// type="checkbox"
|
|
854
|
-
// id={id}
|
|
855
|
-
// checked={checked}
|
|
856
|
-
// onChange={(e) => !disabled && onChange(e.target.checked)}
|
|
857
|
-
// disabled={disabled}
|
|
858
|
-
// required={required}
|
|
859
|
-
// value={value}
|
|
860
|
-
// style={{
|
|
861
|
-
// position: 'absolute',
|
|
862
|
-
// opacity: 0,
|
|
863
|
-
// width: 0,
|
|
864
|
-
// height: 0,
|
|
865
|
-
// pointerEvents: 'none'
|
|
866
|
-
// }}
|
|
867
|
-
// />
|
|
868
|
-
// <div
|
|
869
|
-
// className="funui_form-checkbox-box"
|
|
870
|
-
// style={{
|
|
871
|
-
// width: '1.25rem',
|
|
872
|
-
// height: '1.25rem',
|
|
873
|
-
// border: checked ? '2px solid var(--primary)' : '2px solid var(--borderColor)',
|
|
874
|
-
// borderRadius: '0.25rem',
|
|
875
|
-
// backgroundColor: checked ? 'var(--primary)' : 'transparent',
|
|
876
|
-
// position: 'relative',
|
|
877
|
-
// transition: 'all 0.2s ease',
|
|
878
|
-
// display: 'flex',
|
|
879
|
-
// alignItems: 'center',
|
|
880
|
-
// justifyContent: 'center',
|
|
881
|
-
// flexShrink: 0,
|
|
882
|
-
// }}
|
|
883
|
-
// >
|
|
884
|
-
// {checked && (
|
|
885
|
-
// <svg
|
|
886
|
-
// width="14"
|
|
887
|
-
// height="14"
|
|
888
|
-
// viewBox="0 0 14 14"
|
|
889
|
-
// fill="none"
|
|
890
|
-
// xmlns="http://www.w3.org2000/svg"
|
|
891
|
-
// style={{
|
|
892
|
-
// stroke: 'white',
|
|
893
|
-
// strokeWidth: '2',
|
|
894
|
-
// strokeLinecap: 'round',
|
|
895
|
-
// strokeLinejoin: 'round',
|
|
896
|
-
// }}
|
|
897
|
-
// >
|
|
898
|
-
// <path d="M3 7L6 10L11 4" />
|
|
899
|
-
// </svg>
|
|
900
|
-
// )}
|
|
901
|
-
// </div>
|
|
902
|
-
// {label && (
|
|
903
|
-
// <span
|
|
904
|
-
// className="funui_form-checkbox-label"
|
|
905
|
-
// style={{
|
|
906
|
-
// fontSize: '0.875rem',
|
|
907
|
-
// color: disabled ? 'var(--text-muted)' : 'var(--text)',
|
|
908
|
-
// fontWeight: checked ? '500' : '400',
|
|
909
|
-
// }}
|
|
910
|
-
// >
|
|
911
|
-
// {label}{required && ' *'}
|
|
912
|
-
// </span>
|
|
913
|
-
// )}
|
|
914
|
-
// </label>
|
|
915
|
-
// );
|
|
916
|
-
// };
|
|
917
|
-
// // Custom Radio Component (unchanged)
|
|
918
|
-
// const FormRadio: React.FC<{
|
|
919
|
-
// label?: string;
|
|
920
|
-
// checked: boolean;
|
|
921
|
-
// onChange: (checked: boolean) => void;
|
|
922
|
-
// disabled?: boolean;
|
|
923
|
-
// required?: boolean;
|
|
924
|
-
// value?: string;
|
|
925
|
-
// id?: string;
|
|
926
|
-
// }> = ({ label, checked, onChange, disabled, required, value, id }) => {
|
|
927
|
-
// return (
|
|
928
|
-
// <label
|
|
929
|
-
// className="funui_form-radio"
|
|
930
|
-
// style={{
|
|
931
|
-
// display: 'flex',
|
|
932
|
-
// alignItems: 'center',
|
|
933
|
-
// gap: '0.5rem',
|
|
934
|
-
// cursor: disabled ? 'not-allowed' : 'pointer',
|
|
935
|
-
// userSelect: 'none',
|
|
936
|
-
// width: 'fit-content',
|
|
937
|
-
// padding: '0.25rem 0',
|
|
938
|
-
// }}
|
|
939
|
-
// >
|
|
940
|
-
// <input
|
|
941
|
-
// type="radio"
|
|
942
|
-
// id={id}
|
|
943
|
-
// checked={checked}
|
|
944
|
-
// onChange={(e) => !disabled && onChange(e.target.checked)}
|
|
945
|
-
// disabled={disabled}
|
|
946
|
-
// required={required}
|
|
947
|
-
// value={value}
|
|
948
|
-
// style={{
|
|
949
|
-
// position: 'absolute',
|
|
950
|
-
// opacity: 0,
|
|
951
|
-
// width: 0,
|
|
952
|
-
// height: 0,
|
|
953
|
-
// pointerEvents: 'none'
|
|
954
|
-
// }}
|
|
955
|
-
// />
|
|
956
|
-
// <div
|
|
957
|
-
// className="funui_form-radio-circle"
|
|
958
|
-
// style={{
|
|
959
|
-
// width: '1.25rem',
|
|
960
|
-
// height: '1.25rem',
|
|
961
|
-
// border: checked ? '2px solid var(--primary)' : '2px solid var(--borderColor)',
|
|
962
|
-
// borderRadius: '50%',
|
|
963
|
-
// backgroundColor: 'transparent',
|
|
964
|
-
// position: 'relative',
|
|
965
|
-
// transition: 'all 0.2s ease',
|
|
966
|
-
// display: 'flex',
|
|
967
|
-
// alignItems: 'center',
|
|
968
|
-
// justifyContent: 'center',
|
|
969
|
-
// flexShrink: 0,
|
|
970
|
-
// }}
|
|
971
|
-
// >
|
|
972
|
-
// {checked && (
|
|
973
|
-
// <div
|
|
974
|
-
// style={{
|
|
975
|
-
// width: '0.75rem',
|
|
976
|
-
// height: '0.75rem',
|
|
977
|
-
// backgroundColor: 'var(--primary)',
|
|
978
|
-
// borderRadius: '50%',
|
|
979
|
-
// }}
|
|
980
|
-
// />
|
|
981
|
-
// )}
|
|
982
|
-
// </div>
|
|
983
|
-
// {label && (
|
|
984
|
-
// <span
|
|
985
|
-
// className="funui_form-radio-label"
|
|
986
|
-
// style={{
|
|
987
|
-
// fontSize: '0.875rem',
|
|
988
|
-
// color: disabled ? 'var(--text-muted)' : 'var(--text)',
|
|
989
|
-
// fontWeight: checked ? '500' : '400',
|
|
990
|
-
// }}
|
|
991
|
-
// >
|
|
992
|
-
// {label}{required && ' *'}
|
|
993
|
-
// </span>
|
|
994
|
-
// )}
|
|
995
|
-
// </label>
|
|
996
|
-
// );
|
|
997
|
-
// };
|
|
998
|
-
// // Function to format WhatsApp message - UPDATED with proper formatting
|
|
999
|
-
// const formatWhatsAppMessage = (
|
|
1000
|
-
// values: Record<string, any>,
|
|
1001
|
-
// fields: FormField[],
|
|
1002
|
-
// header?: string,
|
|
1003
|
-
// footer?: string
|
|
1004
|
-
// ): string => {
|
|
1005
|
-
// // Build message lines
|
|
1006
|
-
// let message = '';
|
|
1007
|
-
// // Add header if provided
|
|
1008
|
-
// if (header) {
|
|
1009
|
-
// message += `${header}\n\n`;
|
|
1010
|
-
// }
|
|
1011
|
-
// // Filter out empty/null/undefined values
|
|
1012
|
-
// const nonEmptyFields = fields.filter(field => {
|
|
1013
|
-
// const value = values[field.name];
|
|
1014
|
-
// // Skip if value is undefined, null, or empty string
|
|
1015
|
-
// if (value === undefined || value === null || value === '') {
|
|
1016
|
-
// return false;
|
|
1017
|
-
// }
|
|
1018
|
-
// // Skip if array is empty
|
|
1019
|
-
// if (Array.isArray(value) && value.length === 0) {
|
|
1020
|
-
// return false;
|
|
1021
|
-
// }
|
|
1022
|
-
// // Skip if checkbox is false
|
|
1023
|
-
// if (field.type === 'checkbox' && !field.multiple && value === false) {
|
|
1024
|
-
// return false;
|
|
1025
|
-
// }
|
|
1026
|
-
// return true;
|
|
1027
|
-
// });
|
|
1028
|
-
// // Format each field
|
|
1029
|
-
// const fieldLines = nonEmptyFields.map(field => {
|
|
1030
|
-
// const value = values[field.name];
|
|
1031
|
-
// let displayValue = value;
|
|
1032
|
-
// // Format array values (for multiple checkboxes)
|
|
1033
|
-
// if (Array.isArray(value)) {
|
|
1034
|
-
// displayValue = value.join(', ');
|
|
1035
|
-
// }
|
|
1036
|
-
// // Format checkbox values
|
|
1037
|
-
// if (field.type === 'checkbox' && !field.multiple) {
|
|
1038
|
-
// displayValue = value ? 'Yes' : 'No';
|
|
1039
|
-
// }
|
|
1040
|
-
// // Format select/radio values
|
|
1041
|
-
// if ((field.type === 'select' || field.type === 'radio') && field.options) {
|
|
1042
|
-
// const option = field.options.find(opt => opt.value === value);
|
|
1043
|
-
// displayValue = option ? option.label : value;
|
|
1044
|
-
// }
|
|
1045
|
-
// // Ensure displayValue is a string and preserve spaces/newlines
|
|
1046
|
-
// displayValue = String(displayValue);
|
|
1047
|
-
// // WhatsApp formatting:
|
|
1048
|
-
// // - Field label on its own line
|
|
1049
|
-
// // - Value on next line wrapped in backticks
|
|
1050
|
-
// // - Double newline between fields for readability
|
|
1051
|
-
// return `${field.label || field.name}\n\`${displayValue}\``;
|
|
1052
|
-
// });
|
|
1053
|
-
// // Join with double newline for spacing
|
|
1054
|
-
// message += fieldLines.join('\n\n');
|
|
1055
|
-
// // Add footer if provided
|
|
1056
|
-
// if (footer) {
|
|
1057
|
-
// message += `\n\n${footer}`;
|
|
1058
|
-
// }
|
|
1059
|
-
// return encodeURIComponent(message);
|
|
1060
|
-
// };
|
|
1061
|
-
// // Main Form Component - FIXED
|
|
1062
|
-
// const Form: React.FC<FormProps> = (props) => {
|
|
1063
|
-
// const {
|
|
1064
|
-
// fields: fieldsProp,
|
|
1065
|
-
// onSubmit: onSubmitProp,
|
|
1066
|
-
// defaultValues: defaultValuesProp = {},
|
|
1067
|
-
// submitText: submitTextProp = 'Submit',
|
|
1068
|
-
// submitBg: submitBgProp = 'primary',
|
|
1069
|
-
// submitPrefix: submitPrefixProp,
|
|
1070
|
-
// submitSuffix: submitSuffixProp,
|
|
1071
|
-
// resetText: resetTextProp = 'Reset',
|
|
1072
|
-
// showReset: showResetProp = true,
|
|
1073
|
-
// isLoading: isLoadingProp = false,
|
|
1074
|
-
// className: classNameProp = '',
|
|
1075
|
-
// layout: layoutProp = 'vertical',
|
|
1076
|
-
// gap: gapProp = '1.5rem',
|
|
1077
|
-
// title: titleProp,
|
|
1078
|
-
// titleSize: titleSizeProp,
|
|
1079
|
-
// titleColor: titleColorProp,
|
|
1080
|
-
// description: descriptionProp,
|
|
1081
|
-
// descriptionSize: descriptionSizeProp,
|
|
1082
|
-
// descriptionColor: descriptionColorProp,
|
|
1083
|
-
// whatsappContact: whatsappContactProp,
|
|
1084
|
-
// width: widthProp,
|
|
1085
|
-
// centered: centeredProp,
|
|
1086
|
-
// whatsappHeader: whatsappHeaderProp,
|
|
1087
|
-
// whatsappFooter: whatsappFooterProp,
|
|
1088
|
-
// fullWidth: fullWidthProp = true,
|
|
1089
|
-
// variant = '',
|
|
1090
|
-
// } = props;
|
|
1091
|
-
// // Use component configuration with variant
|
|
1092
|
-
// const { mergeWithLocal } = useComponentConfiguration('Form', variant);
|
|
1093
|
-
// // Create local props object
|
|
1094
|
-
// const localProps = {
|
|
1095
|
-
// fields: fieldsProp,
|
|
1096
|
-
// onSubmit: onSubmitProp,
|
|
1097
|
-
// defaultValues: defaultValuesProp,
|
|
1098
|
-
// submitText: submitTextProp,
|
|
1099
|
-
// submitBg: submitBgProp,
|
|
1100
|
-
// submitPrefix: submitPrefixProp,
|
|
1101
|
-
// submitSuffix: submitSuffixProp,
|
|
1102
|
-
// resetText: resetTextProp,
|
|
1103
|
-
// showReset: showResetProp,
|
|
1104
|
-
// isLoading: isLoadingProp,
|
|
1105
|
-
// className: classNameProp,
|
|
1106
|
-
// layout: layoutProp,
|
|
1107
|
-
// gap: gapProp,
|
|
1108
|
-
// title: titleProp,
|
|
1109
|
-
// titleSize: titleSizeProp,
|
|
1110
|
-
// titleColor: titleColorProp,
|
|
1111
|
-
// description: descriptionProp,
|
|
1112
|
-
// descriptionSize: descriptionSizeProp,
|
|
1113
|
-
// descriptionColor: descriptionColorProp,
|
|
1114
|
-
// whatsappContact: whatsappContactProp,
|
|
1115
|
-
// width: widthProp,
|
|
1116
|
-
// centered: centeredProp,
|
|
1117
|
-
// whatsappHeader: whatsappHeaderProp,
|
|
1118
|
-
// whatsappFooter: whatsappFooterProp,
|
|
1119
|
-
// fullWidth: fullWidthProp,
|
|
1120
|
-
// variant,
|
|
1121
|
-
// };
|
|
1122
|
-
// // Merge with theme configuration
|
|
1123
|
-
// const { props: mergedProps } = mergeWithLocal(localProps);
|
|
1124
|
-
// // Destructure with proper priority: local props override config props
|
|
1125
|
-
// const fields = fieldsProp !== undefined ? fieldsProp : mergedProps.fields;
|
|
1126
|
-
// const onSubmit = onSubmitProp !== undefined ? onSubmitProp : mergedProps.onSubmit;
|
|
1127
|
-
// const defaultValues = defaultValuesProp !== undefined ? defaultValuesProp : mergedProps.defaultValues;
|
|
1128
|
-
// const submitText = submitTextProp !== undefined ? submitTextProp : mergedProps.submitText;
|
|
1129
|
-
// const submitBg = submitBgProp !== undefined ? submitBgProp : mergedProps.submitBg;
|
|
1130
|
-
// const submitPrefix = submitPrefixProp !== undefined ? submitPrefixProp : mergedProps.submitPrefix;
|
|
1131
|
-
// const submitSuffix = submitSuffixProp !== undefined ? submitSuffixProp : mergedProps.submitSuffix;
|
|
1132
|
-
// const resetText = resetTextProp !== undefined ? resetTextProp : mergedProps.resetText;
|
|
1133
|
-
// const showReset = showResetProp !== undefined ? showResetProp : mergedProps.showReset;
|
|
1134
|
-
// const isLoading = isLoadingProp !== undefined ? isLoadingProp : mergedProps.isLoading;
|
|
1135
|
-
// const className = classNameProp !== undefined ? classNameProp : mergedProps.className;
|
|
1136
|
-
// const layout = layoutProp !== undefined ? layoutProp : mergedProps.layout;
|
|
1137
|
-
// const gap = gapProp !== undefined ? gapProp : mergedProps.gap;
|
|
1138
|
-
// const title = titleProp !== undefined ? titleProp : mergedProps.title;
|
|
1139
|
-
// const titleSize = titleSizeProp !== undefined ? titleSizeProp : mergedProps.titleSize;
|
|
1140
|
-
// const titleColor = titleColorProp !== undefined ? titleColorProp : mergedProps.titleColor;
|
|
1141
|
-
// const description = descriptionProp !== undefined ? descriptionProp : mergedProps.description;
|
|
1142
|
-
// const descriptionSize = descriptionSizeProp !== undefined ? descriptionSizeProp : mergedProps.descriptionSize;
|
|
1143
|
-
// const descriptionColor = descriptionColorProp !== undefined ? descriptionColorProp : mergedProps.descriptionColor;
|
|
1144
|
-
// const whatsappContact = whatsappContactProp !== undefined ? whatsappContactProp : mergedProps.whatsappContact;
|
|
1145
|
-
// const width = widthProp !== undefined ? widthProp : mergedProps.width;
|
|
1146
|
-
// const centered = centeredProp !== undefined ? centeredProp : mergedProps.centered;
|
|
1147
|
-
// const whatsappHeader = whatsappHeaderProp !== undefined ? whatsappHeaderProp : mergedProps.whatsappHeader;
|
|
1148
|
-
// const whatsappFooter = whatsappFooterProp !== undefined ? whatsappFooterProp : mergedProps.whatsappFooter;
|
|
1149
|
-
// const fullWidth = fullWidthProp !== undefined ? fullWidthProp : mergedProps.fullWidth;
|
|
1150
|
-
// // Parse JSON inputs
|
|
1151
|
-
// const parsedFields = useMemo(() => parseJsonInput(fields, [] as FormField[]), [fields]);
|
|
1152
|
-
// const parsedDefaultValues = useMemo(() => parseJsonInput(defaultValues, {} as Record<string, any>), [defaultValues]);
|
|
1153
|
-
// // State management
|
|
1154
|
-
// const [errors, setErrors] = useState<Record<string, string>>({});
|
|
1155
|
-
// const [touched, setTouched] = useState<Record<string, boolean>>({});
|
|
1156
|
-
// const [formValues, setFormValues] = useState<Record<string, any>>(() => {
|
|
1157
|
-
// // Initialize form values from defaultValues and field values
|
|
1158
|
-
// const initialValues: Record<string, any> = {};
|
|
1159
|
-
// parsedFields.forEach((field: any) => {
|
|
1160
|
-
// if (field.value !== undefined) {
|
|
1161
|
-
// initialValues[field.name] = field.value;
|
|
1162
|
-
// } else if (parsedDefaultValues[field.name] !== undefined) {
|
|
1163
|
-
// initialValues[field.name] = parsedDefaultValues[field.name];
|
|
1164
|
-
// } else {
|
|
1165
|
-
// // Set default empty values
|
|
1166
|
-
// if (field.type === 'checkbox' && field.multiple) {
|
|
1167
|
-
// initialValues[field.name] = [];
|
|
1168
|
-
// } else if (field.type === 'checkbox') {
|
|
1169
|
-
// initialValues[field.name] = false;
|
|
1170
|
-
// } else {
|
|
1171
|
-
// initialValues[field.name] = '';
|
|
1172
|
-
// }
|
|
1173
|
-
// }
|
|
1174
|
-
// });
|
|
1175
|
-
// return initialValues;
|
|
1176
|
-
// });
|
|
1177
|
-
// const [isSubmitting, setIsSubmitting] = useState(false);
|
|
1178
|
-
// // Update form values when defaultValues prop changes
|
|
1179
|
-
// useEffect(() => {
|
|
1180
|
-
// if (Object.keys(parsedDefaultValues).length > 0) {
|
|
1181
|
-
// setFormValues(prev => ({
|
|
1182
|
-
// ...prev,
|
|
1183
|
-
// ...parsedDefaultValues
|
|
1184
|
-
// }));
|
|
1185
|
-
// }
|
|
1186
|
-
// }, [parsedDefaultValues]);
|
|
1187
|
-
// // Validate a single field
|
|
1188
|
-
// const validateField = useCallback((field: FormField, value: any): string | null => {
|
|
1189
|
-
// // Required validation
|
|
1190
|
-
// if (field.required) {
|
|
1191
|
-
// if (value === undefined || value === null || value === '') {
|
|
1192
|
-
// return `${field.label || field.name} is required`;
|
|
1193
|
-
// }
|
|
1194
|
-
// if (field.type === 'checkbox' && field.multiple && Array.isArray(value) && value.length === 0) {
|
|
1195
|
-
// return `${field.label || field.name} is required`;
|
|
1196
|
-
// }
|
|
1197
|
-
// if (field.type === 'checkbox' && !field.multiple && value === false) {
|
|
1198
|
-
// return `${field.label || field.name} is required`;
|
|
1199
|
-
// }
|
|
1200
|
-
// }
|
|
1201
|
-
// // Type-specific validations (only for non-empty values)
|
|
1202
|
-
// if (value && (typeof value !== 'string' || value !== '')) {
|
|
1203
|
-
// // Email validation
|
|
1204
|
-
// if (field.type === 'email' && typeof value === 'string') {
|
|
1205
|
-
// const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1206
|
-
// if (!emailRegex.test(value)) {
|
|
1207
|
-
// return 'Please enter a valid email address';
|
|
1208
|
-
// }
|
|
1209
|
-
// }
|
|
1210
|
-
// // Number validation
|
|
1211
|
-
// if (field.type === 'number') {
|
|
1212
|
-
// let numValue: number;
|
|
1213
|
-
// if (typeof value === 'string') {
|
|
1214
|
-
// numValue = parseFloat(value);
|
|
1215
|
-
// } else if (typeof value === 'number') {
|
|
1216
|
-
// numValue = value;
|
|
1217
|
-
// } else {
|
|
1218
|
-
// return 'Please enter a valid number';
|
|
1219
|
-
// }
|
|
1220
|
-
// if (isNaN(numValue)) {
|
|
1221
|
-
// return 'Please enter a valid number';
|
|
1222
|
-
// }
|
|
1223
|
-
// const min = field.inputProps?.min;
|
|
1224
|
-
// const max = field.inputProps?.max;
|
|
1225
|
-
// if (min !== undefined && numValue < parseFloat(min.toString())) {
|
|
1226
|
-
// return `Minimum value is ${min}`;
|
|
1227
|
-
// }
|
|
1228
|
-
// if (max !== undefined && numValue > parseFloat(max.toString())) {
|
|
1229
|
-
// return `Maximum value is ${max}`;
|
|
1230
|
-
// }
|
|
1231
|
-
// }
|
|
1232
|
-
// // Phone validation (basic) - only for strings
|
|
1233
|
-
// if (field.type === 'tel' && typeof value === 'string') {
|
|
1234
|
-
// const phoneRegex = /^[\+]?[1-9][\d]{0,17}$/;
|
|
1235
|
-
// if (!phoneRegex.test(value.replace(/[\s+\-()]/g, ''))) {
|
|
1236
|
-
// return 'Please enter a valid phone number';
|
|
1237
|
-
// }
|
|
1238
|
-
// }
|
|
1239
|
-
// // Length validation for strings
|
|
1240
|
-
// if (typeof value === 'string') {
|
|
1241
|
-
// const minLength = field.inputProps?.minLength;
|
|
1242
|
-
// const maxLength = field.inputProps?.maxLength;
|
|
1243
|
-
// if (minLength && value.length < minLength) {
|
|
1244
|
-
// return `Minimum ${minLength} characters required`;
|
|
1245
|
-
// }
|
|
1246
|
-
// if (maxLength && value.length > maxLength) {
|
|
1247
|
-
// return `Maximum ${maxLength} characters allowed`;
|
|
1248
|
-
// }
|
|
1249
|
-
// }
|
|
1250
|
-
// // Pattern validation if provided - only for strings
|
|
1251
|
-
// if (field.inputProps?.pattern && typeof value === 'string') {
|
|
1252
|
-
// try {
|
|
1253
|
-
// const regex = new RegExp(field.inputProps.pattern);
|
|
1254
|
-
// if (!regex.test(value)) {
|
|
1255
|
-
// return 'Invalid format';
|
|
1256
|
-
// }
|
|
1257
|
-
// } catch (error) {
|
|
1258
|
-
// console.warn('Invalid regex pattern:', field.inputProps.pattern);
|
|
1259
|
-
// }
|
|
1260
|
-
// }
|
|
1261
|
-
// }
|
|
1262
|
-
// return null;
|
|
1263
|
-
// }, []);
|
|
1264
|
-
// // Validate form
|
|
1265
|
-
// const validateForm = useCallback((): boolean => {
|
|
1266
|
-
// const newErrors: Record<string, string> = {};
|
|
1267
|
-
// let hasErrors = false;
|
|
1268
|
-
// parsedFields.forEach((field: any) => {
|
|
1269
|
-
// const value = formValues[field.name];
|
|
1270
|
-
// const error = validateField(field, value);
|
|
1271
|
-
// if (error) {
|
|
1272
|
-
// newErrors[field.name] = error;
|
|
1273
|
-
// hasErrors = true;
|
|
1274
|
-
// }
|
|
1275
|
-
// });
|
|
1276
|
-
// setErrors(newErrors);
|
|
1277
|
-
// return !hasErrors;
|
|
1278
|
-
// }, [parsedFields, validateField, formValues]);
|
|
1279
|
-
// // Handle field change for checkboxes and radios
|
|
1280
|
-
// const handleFieldChange = useCallback((fieldName: string, newValue: any) => {
|
|
1281
|
-
// // Update form values
|
|
1282
|
-
// setFormValues(prev => ({
|
|
1283
|
-
// ...prev,
|
|
1284
|
-
// [fieldName]: newValue
|
|
1285
|
-
// }));
|
|
1286
|
-
// // Update touched state
|
|
1287
|
-
// setTouched(prev => ({ ...prev, [fieldName]: true }));
|
|
1288
|
-
// // Validate the changed field
|
|
1289
|
-
// const field = parsedFields.find((f: any) => f.name === fieldName);
|
|
1290
|
-
// if (!field) return;
|
|
1291
|
-
// const error = validateField(field, newValue);
|
|
1292
|
-
// setErrors(prev => {
|
|
1293
|
-
// if (error) {
|
|
1294
|
-
// return { ...prev, [fieldName]: error };
|
|
1295
|
-
// } else {
|
|
1296
|
-
// const newErrors = { ...prev };
|
|
1297
|
-
// delete newErrors[fieldName];
|
|
1298
|
-
// return newErrors;
|
|
1299
|
-
// }
|
|
1300
|
-
// });
|
|
1301
|
-
// }, [parsedFields, validateField]);
|
|
1302
|
-
// // FIXED: Handle input change - pass event directly to Input component
|
|
1303
|
-
// const handleInputEventChange = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
|
1304
|
-
// const fieldName = e.target.name;
|
|
1305
|
-
// const value = e.target.value;
|
|
1306
|
-
// // Update form values - PASS VALUE AS-IS (preserves spaces)
|
|
1307
|
-
// setFormValues(prev => ({
|
|
1308
|
-
// ...prev,
|
|
1309
|
-
// [fieldName]: value
|
|
1310
|
-
// }));
|
|
1311
|
-
// // Update touched state
|
|
1312
|
-
// setTouched(prev => ({ ...prev, [fieldName]: true }));
|
|
1313
|
-
// // Validate the changed field
|
|
1314
|
-
// const field = parsedFields.find((f: any) => f.name === fieldName);
|
|
1315
|
-
// if (!field) return;
|
|
1316
|
-
// const error = validateField(field, value);
|
|
1317
|
-
// setErrors(prev => {
|
|
1318
|
-
// if (error) {
|
|
1319
|
-
// return { ...prev, [fieldName]: error };
|
|
1320
|
-
// } else {
|
|
1321
|
-
// const newErrors = { ...prev };
|
|
1322
|
-
// delete newErrors[fieldName];
|
|
1323
|
-
// return newErrors;
|
|
1324
|
-
// }
|
|
1325
|
-
// });
|
|
1326
|
-
// }, [parsedFields, validateField]);
|
|
1327
|
-
// // FIXED: Handle blur event - pass event directly to Input component
|
|
1328
|
-
// const handleInputEventBlur = useCallback((e: React.FocusEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
|
1329
|
-
// const fieldName = e.target.name;
|
|
1330
|
-
// setTouched(prev => ({ ...prev, [fieldName]: true }));
|
|
1331
|
-
// // Validate the field
|
|
1332
|
-
// const field = parsedFields.find((f: any) => f.name === fieldName);
|
|
1333
|
-
// if (!field) return;
|
|
1334
|
-
// const value = formValues[fieldName];
|
|
1335
|
-
// const error = validateField(field, value);
|
|
1336
|
-
// setErrors(prev => {
|
|
1337
|
-
// if (error) {
|
|
1338
|
-
// return { ...prev, [fieldName]: error };
|
|
1339
|
-
// } else {
|
|
1340
|
-
// const newErrors = { ...prev };
|
|
1341
|
-
// delete newErrors[fieldName];
|
|
1342
|
-
// return newErrors;
|
|
1343
|
-
// }
|
|
1344
|
-
// });
|
|
1345
|
-
// }, [parsedFields, validateField, formValues]);
|
|
1346
|
-
// // Handle form submission
|
|
1347
|
-
// const handleSubmit = useCallback(async (e: React.FormEvent) => {
|
|
1348
|
-
// e.preventDefault();
|
|
1349
|
-
// // Mark all fields as touched
|
|
1350
|
-
// const allTouched: Record<string, boolean> = {};
|
|
1351
|
-
// parsedFields.forEach((field: any) => {
|
|
1352
|
-
// allTouched[field.name] = true;
|
|
1353
|
-
// });
|
|
1354
|
-
// setTouched(allTouched);
|
|
1355
|
-
// // Validate form
|
|
1356
|
-
// const isValid = validateForm();
|
|
1357
|
-
// if (!isValid) {
|
|
1358
|
-
// // Scroll to first error
|
|
1359
|
-
// const firstErrorField = Object.keys(errors)[0];
|
|
1360
|
-
// if (firstErrorField) {
|
|
1361
|
-
// const element = document.getElementById(`form-field-${firstErrorField}`);
|
|
1362
|
-
// element?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
1363
|
-
// }
|
|
1364
|
-
// return;
|
|
1365
|
-
// }
|
|
1366
|
-
// // Get form data and submit
|
|
1367
|
-
// setIsSubmitting(true);
|
|
1368
|
-
// try {
|
|
1369
|
-
// if (whatsappContact) {
|
|
1370
|
-
// // Submit via WhatsApp
|
|
1371
|
-
// const message = formatWhatsAppMessage(formValues, parsedFields, whatsappHeader, whatsappFooter);
|
|
1372
|
-
// const cleanPhone = whatsappContact.replace(/[\s+\-()]/g, '');
|
|
1373
|
-
// const whatsappUrl = `https://wa.me/${cleanPhone}?text=${message}`;
|
|
1374
|
-
// window.open(whatsappUrl, '_blank');
|
|
1375
|
-
// // Still call onSubmit if provided
|
|
1376
|
-
// if (onSubmit) {
|
|
1377
|
-
// await onSubmit(formValues, true);
|
|
1378
|
-
// }
|
|
1379
|
-
// } else {
|
|
1380
|
-
// // Regular submission
|
|
1381
|
-
// if (onSubmit) {
|
|
1382
|
-
// await onSubmit(formValues, false);
|
|
1383
|
-
// }
|
|
1384
|
-
// }
|
|
1385
|
-
// } catch (error) {
|
|
1386
|
-
// console.error('Form submission error:', error);
|
|
1387
|
-
// setErrors(prev => ({
|
|
1388
|
-
// ...prev,
|
|
1389
|
-
// _form: 'There was an error submitting the form. Please try again.'
|
|
1390
|
-
// }));
|
|
1391
|
-
// } finally {
|
|
1392
|
-
// setIsSubmitting(false);
|
|
1393
|
-
// }
|
|
1394
|
-
// }, [parsedFields, validateForm, formValues, whatsappContact, whatsappHeader, whatsappFooter, onSubmit, errors]);
|
|
1395
|
-
// // Handle form reset
|
|
1396
|
-
// const handleReset = useCallback(() => {
|
|
1397
|
-
// // Reset to initial values
|
|
1398
|
-
// const initialValues: Record<string, any> = {};
|
|
1399
|
-
// parsedFields.forEach((field: any) => {
|
|
1400
|
-
// if (field.value !== undefined) {
|
|
1401
|
-
// initialValues[field.name] = field.value;
|
|
1402
|
-
// } else if (parsedDefaultValues[field.name] !== undefined) {
|
|
1403
|
-
// initialValues[field.name] = parsedDefaultValues[field.name];
|
|
1404
|
-
// } else {
|
|
1405
|
-
// if (field.type === 'checkbox' && field.multiple) {
|
|
1406
|
-
// initialValues[field.name] = [];
|
|
1407
|
-
// } else if (field.type === 'checkbox') {
|
|
1408
|
-
// initialValues[field.name] = false;
|
|
1409
|
-
// } else {
|
|
1410
|
-
// initialValues[field.name] = '';
|
|
1411
|
-
// }
|
|
1412
|
-
// }
|
|
1413
|
-
// });
|
|
1414
|
-
// setFormValues(initialValues);
|
|
1415
|
-
// setErrors({});
|
|
1416
|
-
// setTouched({});
|
|
1417
|
-
// }, [parsedFields, parsedDefaultValues]);
|
|
1418
|
-
// // Get field status for Input component
|
|
1419
|
-
// const getFieldStatus = useCallback((fieldName: string): 'success' | 'warning' | 'danger' | 'info' | undefined => {
|
|
1420
|
-
// const error = errors[fieldName];
|
|
1421
|
-
// const isTouched = touched[fieldName];
|
|
1422
|
-
// if (error) return 'danger';
|
|
1423
|
-
// if (isTouched && !error && formValues[fieldName] !== '') {
|
|
1424
|
-
// return 'success';
|
|
1425
|
-
// }
|
|
1426
|
-
// return undefined;
|
|
1427
|
-
// }, [errors, touched, formValues]);
|
|
1428
|
-
// // Check if form is valid for submission
|
|
1429
|
-
// const isFormValid = useMemo(() => {
|
|
1430
|
-
// // Check if any required fields are empty
|
|
1431
|
-
// const hasEmptyRequiredFields = parsedFields.some((field: any) => {
|
|
1432
|
-
// if (!field.required) return false;
|
|
1433
|
-
// const value = formValues[field.name];
|
|
1434
|
-
// if (field.type === 'checkbox' && field.multiple) {
|
|
1435
|
-
// return !Array.isArray(value) || value.length === 0;
|
|
1436
|
-
// }
|
|
1437
|
-
// if (field.type === 'checkbox') {
|
|
1438
|
-
// return value === false;
|
|
1439
|
-
// }
|
|
1440
|
-
// return !value || value === '';
|
|
1441
|
-
// });
|
|
1442
|
-
// // Check if there are any validation errors
|
|
1443
|
-
// const hasValidationErrors = Object.keys(errors).length > 0;
|
|
1444
|
-
// return !hasEmptyRequiredFields && !hasValidationErrors;
|
|
1445
|
-
// }, [parsedFields, formValues, errors]);
|
|
1446
|
-
// // Submit button disabled state
|
|
1447
|
-
// const isSubmitDisabled = isSubmitting || isLoading || !isFormValid;
|
|
1448
|
-
// // Check if WhatsApp is configured
|
|
1449
|
-
// const hasWhatsApp = !!whatsappContact;
|
|
1450
|
-
// // Render field based on type - FIXED
|
|
1451
|
-
// const renderField = useCallback((field: FormField) => {
|
|
1452
|
-
// const status = getFieldStatus(field.name);
|
|
1453
|
-
// const error = errors[field.name];
|
|
1454
|
-
// const isTouched = touched[field.name];
|
|
1455
|
-
// const showError = error && isTouched;
|
|
1456
|
-
// const value = formValues[field.name];
|
|
1457
|
-
// // Field wrapper classes
|
|
1458
|
-
// const wrapperClass = `col min-w-200 field ${showError ? 'field-error' : ''}`.trim();
|
|
1459
|
-
// // Helper text (error takes priority)
|
|
1460
|
-
// const helperText = showError ? error : field.helperText;
|
|
1461
|
-
// // Generate unique ID for the input
|
|
1462
|
-
// const inputId = field.inputProps?.id || `form-field-${field.name}`;
|
|
1463
|
-
// // Base props for Input component
|
|
1464
|
-
// const baseProps: any = {
|
|
1465
|
-
// id: inputId,
|
|
1466
|
-
// name: field.name, // IMPORTANT: Make sure name is passed
|
|
1467
|
-
// label: field.label,
|
|
1468
|
-
// placeholder: field.placeholder,
|
|
1469
|
-
// helperText: helperText,
|
|
1470
|
-
// disabled: field.disabled || isLoading,
|
|
1471
|
-
// fullWidth: fullWidth,
|
|
1472
|
-
// ...field.inputProps,
|
|
1473
|
-
// };
|
|
1474
|
-
// // Only add status if it's not undefined
|
|
1475
|
-
// if (status !== undefined) {
|
|
1476
|
-
// baseProps.status = status;
|
|
1477
|
-
// }
|
|
1478
|
-
// // Render based on field type
|
|
1479
|
-
// switch (field.type) {
|
|
1480
|
-
// case 'checkbox':
|
|
1481
|
-
// if (field.options?.length) {
|
|
1482
|
-
// // Multiple checkboxes (checkbox group)
|
|
1483
|
-
// const checkedValues = Array.isArray(value) ? value : [];
|
|
1484
|
-
// return (
|
|
1485
|
-
// <div key={field.name} className={wrapperClass}>
|
|
1486
|
-
// {field.label && (
|
|
1487
|
-
// <div className="form-label" style={{ marginBottom: '0.5rem' }}>
|
|
1488
|
-
// <Text text={field.label + (field.required ? ' *' : '')} size="sm" color="text" bold />
|
|
1489
|
-
// </div>
|
|
1490
|
-
// )}
|
|
1491
|
-
// <Flex direction="column" gap="0.5rem">
|
|
1492
|
-
// {field.options.map((option, index) => {
|
|
1493
|
-
// const isChecked = checkedValues.includes(option.value);
|
|
1494
|
-
// return (
|
|
1495
|
-
// <FormCheckbox
|
|
1496
|
-
// key={`${field.name}_${index}`}
|
|
1497
|
-
// id={`${inputId}_${index}`}
|
|
1498
|
-
// label={option.label}
|
|
1499
|
-
// checked={isChecked}
|
|
1500
|
-
// onChange={(checked) => {
|
|
1501
|
-
// if (checked) {
|
|
1502
|
-
// // Add value to array
|
|
1503
|
-
// const newValues = [...checkedValues, option.value];
|
|
1504
|
-
// handleFieldChange(field.name, newValues);
|
|
1505
|
-
// } else {
|
|
1506
|
-
// // Remove value from array
|
|
1507
|
-
// const newValues = checkedValues.filter(v => v !== option.value);
|
|
1508
|
-
// handleFieldChange(field.name, newValues);
|
|
1509
|
-
// }
|
|
1510
|
-
// }}
|
|
1511
|
-
// disabled={field.disabled || option.disabled || isLoading}
|
|
1512
|
-
// required={field.required && index === 0}
|
|
1513
|
-
// value={option.value}
|
|
1514
|
-
// />
|
|
1515
|
-
// );
|
|
1516
|
-
// })}
|
|
1517
|
-
// </Flex>
|
|
1518
|
-
// </div>
|
|
1519
|
-
// );
|
|
1520
|
-
// } else {
|
|
1521
|
-
// // Single checkbox (boolean)
|
|
1522
|
-
// return (
|
|
1523
|
-
// <div key={field.name} className={wrapperClass}>
|
|
1524
|
-
// <FormCheckbox
|
|
1525
|
-
// label={field.label}
|
|
1526
|
-
// checked={!!value}
|
|
1527
|
-
// onChange={(checked) => handleFieldChange(field.name, checked)}
|
|
1528
|
-
// disabled={field.disabled || isLoading}
|
|
1529
|
-
// required={field.required}
|
|
1530
|
-
// id={inputId}
|
|
1531
|
-
// />
|
|
1532
|
-
// </div>
|
|
1533
|
-
// );
|
|
1534
|
-
// }
|
|
1535
|
-
// case 'radio':
|
|
1536
|
-
// if (!field.options?.length) return null;
|
|
1537
|
-
// return (
|
|
1538
|
-
// <div key={field.name} className={wrapperClass}>
|
|
1539
|
-
// {field.label && (
|
|
1540
|
-
// <div className="form-label" style={{ marginBottom: '0.5rem' }}>
|
|
1541
|
-
// <Text text={field.label + (field.required ? ' *' : '')} size="sm" color="text" bold />
|
|
1542
|
-
// </div>
|
|
1543
|
-
// )}
|
|
1544
|
-
// <Flex direction="column" gap="0.5rem">
|
|
1545
|
-
// {field.options.map((option, index) => (
|
|
1546
|
-
// <FormRadio
|
|
1547
|
-
// key={`${field.name}_${index}`}
|
|
1548
|
-
// id={`${inputId}_${index}`}
|
|
1549
|
-
// label={option.label}
|
|
1550
|
-
// checked={value === option.value}
|
|
1551
|
-
// onChange={() => handleFieldChange(field.name, option.value)}
|
|
1552
|
-
// disabled={field.disabled || option.disabled || isLoading}
|
|
1553
|
-
// required={field.required && index === 0}
|
|
1554
|
-
// value={option.value}
|
|
1555
|
-
// />
|
|
1556
|
-
// ))}
|
|
1557
|
-
// </Flex>
|
|
1558
|
-
// </div>
|
|
1559
|
-
// );
|
|
1560
|
-
// case 'textarea':
|
|
1561
|
-
// return (
|
|
1562
|
-
// <div key={field.name} className={wrapperClass}>
|
|
1563
|
-
// <Input
|
|
1564
|
-
// multiline
|
|
1565
|
-
// rows={field.inputProps?.rows || 4}
|
|
1566
|
-
// value={value || ''}
|
|
1567
|
-
// onChange={handleInputEventChange} // FIXED: Pass event handler directly
|
|
1568
|
-
// onBlur={handleInputEventBlur} // FIXED: Pass event handler directly
|
|
1569
|
-
// {...baseProps}
|
|
1570
|
-
// />
|
|
1571
|
-
// </div>
|
|
1572
|
-
// );
|
|
1573
|
-
// case 'select':
|
|
1574
|
-
// return (
|
|
1575
|
-
// <div key={field.name} className={wrapperClass}>
|
|
1576
|
-
// <Input
|
|
1577
|
-
// select
|
|
1578
|
-
// options={field.options?.map(opt => ({ text: opt.label, value: opt.value })) || []}
|
|
1579
|
-
// value={value || ''}
|
|
1580
|
-
// onChange={handleInputEventChange} // FIXED: Pass event handler directly
|
|
1581
|
-
// onBlur={handleInputEventBlur} // FIXED: Pass event handler directly
|
|
1582
|
-
// {...baseProps}
|
|
1583
|
-
// />
|
|
1584
|
-
// </div>
|
|
1585
|
-
// );
|
|
1586
|
-
// default:
|
|
1587
|
-
// // text, email, number, tel, date, file, password
|
|
1588
|
-
// return (
|
|
1589
|
-
// <div key={field.name} className={wrapperClass}>
|
|
1590
|
-
// <Input
|
|
1591
|
-
// type={field.type}
|
|
1592
|
-
// value={value || ''}
|
|
1593
|
-
// onChange={handleInputEventChange} // FIXED: Pass event handler directly
|
|
1594
|
-
// onBlur={handleInputEventBlur} // FIXED: Pass event handler directly
|
|
1595
|
-
// {...baseProps}
|
|
1596
|
-
// />
|
|
1597
|
-
// </div>
|
|
1598
|
-
// );
|
|
1599
|
-
// }
|
|
1600
|
-
// }, [errors, touched, formValues, isLoading, getFieldStatus, handleFieldChange, handleInputEventChange, handleInputEventBlur, fullWidth]);
|
|
1601
|
-
// // Don't render if no fields are configured
|
|
1602
|
-
// if (parsedFields.length === 0) {
|
|
1603
|
-
// console.warn('Form: No fields configured. Please provide fields prop or configure via theme.');
|
|
1604
|
-
// return null;
|
|
1605
|
-
// }
|
|
1606
|
-
// return (
|
|
1607
|
-
// <div className={`form-wrapper ${centered ? 'center' : ''} ${className}`} style={{ width: "100%", maxWidth: width || "450px" }}>
|
|
1608
|
-
// {/* Title Section */}
|
|
1609
|
-
// {title && (
|
|
1610
|
-
// <div className="form-header" style={{ marginBottom: '2rem' }}>
|
|
1611
|
-
// <Text
|
|
1612
|
-
// text={title}
|
|
1613
|
-
// size={titleSize || "3xl"}
|
|
1614
|
-
// color={titleColor || ""}
|
|
1615
|
-
// block
|
|
1616
|
-
// />
|
|
1617
|
-
// {description && (
|
|
1618
|
-
// <Text article
|
|
1619
|
-
// size={descriptionSize || "sm"}
|
|
1620
|
-
// color={descriptionColor || ""}
|
|
1621
|
-
// >
|
|
1622
|
-
// <div
|
|
1623
|
-
// className="article text-sm"
|
|
1624
|
-
// dangerouslySetInnerHTML={{ __html: description }}
|
|
1625
|
-
// />
|
|
1626
|
-
// </Text>
|
|
1627
|
-
// )}
|
|
1628
|
-
// </div>
|
|
1629
|
-
// )}
|
|
1630
|
-
// <form
|
|
1631
|
-
// className="form"
|
|
1632
|
-
// onSubmit={handleSubmit}
|
|
1633
|
-
// style={{ width: '100%' }}
|
|
1634
|
-
// >
|
|
1635
|
-
// {/* Form fields wrapped in Flex */}
|
|
1636
|
-
// <Flex
|
|
1637
|
-
// direction={layout === 'horizontal' ? 'row' : 'column'}
|
|
1638
|
-
// gap={gap}
|
|
1639
|
-
// width='100%'
|
|
1640
|
-
// >
|
|
1641
|
-
// {parsedFields.map(renderField)}
|
|
1642
|
-
// </Flex>
|
|
1643
|
-
// {/* Form actions - SINGLE BUTTON */}
|
|
1644
|
-
// <Flex direction="column" gap="1rem" style={{ marginTop: '2rem', width: fullWidth ? '100%' : undefined }}>
|
|
1645
|
-
// {/* Single submit button */}
|
|
1646
|
-
// <Button
|
|
1647
|
-
// type="submit"
|
|
1648
|
-
// text={hasWhatsApp ? `Send via WhatsApp` : submitText}
|
|
1649
|
-
// bg={submitBg || "primary"}
|
|
1650
|
-
// raised
|
|
1651
|
-
// prefix={submitPrefix || hasWhatsApp ? <PiWhatsappLogo /> : <PiPaperPlaneTilt />}
|
|
1652
|
-
// suffix={submitSuffix}
|
|
1653
|
-
// disabled={isSubmitDisabled}
|
|
1654
|
-
// isLoading={isSubmitting || isLoading}
|
|
1655
|
-
// fullWidth={fullWidth}
|
|
1656
|
-
// />
|
|
1657
|
-
// </Flex>
|
|
1658
|
-
// </form>
|
|
1659
|
-
// </div>
|
|
1660
|
-
// );
|
|
1661
|
-
// };
|
|
1662
|
-
// export default Form;
|