UNPKG

pagamio-frontend-commons-lib

Version:

Pagamio library for Frontend reusable components like the form engine and table container

149 lines (148 loc) 7.68 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { useForm } from 'react-hook-form'; import { useEffect, useRef, useState } from 'react'; import { Button } from '../../components'; import { FieldWrapper } from '../../form-engine'; const DrawerContent = ({ fields, onSubmit, isOpen, initialValues, submitButtonText = 'Submit', cancelButtonText = 'Cancel', showForm = true, showDrawerButtons = true, handleCloseDrawer, onFieldUpdate, children, updateFieldOptions, updateFieldLabel, setFieldHidden, addField, updateFieldValidation, processInitialFieldUpdates, processingDependencyRef, pendingUpdatesRef, }) => { const { control, handleSubmit, watch, setValue, clearErrors, formState: { errors, isSubmitting }, reset, getValues, } = useForm({ mode: 'onBlur', defaultValues: initialValues }); const [pendingUpdateCount, setPendingUpdateCount] = useState(0); const [submitError, setSubmitError] = useState(null); const fieldChangeRef = useRef(new Set()); const initialLoadPerformedRef = useRef(false); const password = watch('password'); // Process initial field updates once when drawer opens useEffect(() => { if (isOpen && !initialLoadPerformedRef.current && processInitialFieldUpdates) { const initializeFields = async () => { await processInitialFieldUpdates(getValues); initialLoadPerformedRef.current = true; }; initializeFields(); } // Reset initialization flag when drawer closes if (!isOpen) { initialLoadPerformedRef.current = false; fieldChangeRef.current.clear(); } }, [isOpen, processInitialFieldUpdates]); const handleFormSubmit = async (data) => { setSubmitError(null); try { const visibleFieldsData = Object.fromEntries(Object.entries(data).filter(([key]) => !fields.find((field) => field.name === key && field.isHidden))); await onSubmit(visibleFieldsData); } catch (error) { setSubmitError(error instanceof Error ? error.message : 'An error occurred'); } }; useEffect(() => { // Only reset the form when the drawer is closed if (!isOpen) { reset(); } }, [isOpen, reset]); // Helper function to process dependency validations const processDependencyValidation = (field) => { if (!field.validation?.dependency) { return field.validation; } const dependencies = Array.isArray(field.validation.dependency) ? field.validation.dependency : [field.validation.dependency]; let finalValidation = { ...field.validation }; delete finalValidation.dependency; // Apply each dependency validation if its condition is met for (const dep of dependencies) { const dependencyValue = watch(dep.field); if (dep.condition(dependencyValue)) { finalValidation = { ...finalValidation, ...dep.validationToApply }; } } return { ...finalValidation, validate: field.name === 'confirmPassword' ? (value) => value === password || 'Passwords do not match' : finalValidation.validate, }; }; // Helper function to apply a single update result const applyUpdateResult = (result) => { if (!result) return; const { field, options, value, label, hideField, fieldInnitialData } = result; // Apply each possible update type if needed if (options !== undefined && updateFieldOptions) { updateFieldOptions(field, options); } if (value !== undefined && setValue) { setValue(field, value); } if (label !== undefined && updateFieldLabel) { updateFieldLabel(field, label); } if (hideField !== undefined && setFieldHidden) { setFieldHidden(field, hideField); if (hideField && setValue) { setValue(field, undefined); } } if (fieldInnitialData && addField) { addField(fieldInnitialData); } if (clearErrors) { clearErrors(field); } }; // Helper function to process updates for a single field const processFieldUpdates = async (fieldName, value) => { if (!onFieldUpdate?.[fieldName]) return; const updates = onFieldUpdate[fieldName]; for (const update of updates) { try { const result = await update.action(value, false); applyUpdateResult(result); } catch (error) { console.error(`Error processing update for ${fieldName}:`, error); } } }; // Main function with reduced complexity const processPendingUpdates = async () => { // Early return if conditions aren't met if (!pendingUpdatesRef || !processingDependencyRef || processingDependencyRef.current) { return; } processingDependencyRef.current = true; try { const pendingUpdates = pendingUpdatesRef.current; if (pendingUpdates.size === 0) return; // Process all pending updates const updatePromises = Array.from(pendingUpdates.entries()).map(([fieldName, value]) => processFieldUpdates(fieldName, value)); await Promise.all(updatePromises); pendingUpdatesRef.current.clear(); } finally { processingDependencyRef.current = false; } }; // Process pending updates when needed useEffect(() => { if (pendingUpdatesRef?.current?.size && pendingUpdatesRef?.current?.size > 0) { processPendingUpdates().then(); } }, [pendingUpdateCount]); return (_jsxs(_Fragment, { children: [showForm && (_jsxs("form", { className: "grid grid-cols-12 gap-2 align-middle mb-6 border-gray-400 px-6 py-5", onSubmit: handleSubmit(handleFormSubmit), children: [fields?.map((field) => (_jsx(FieldWrapper, { field: { ...field, validation: processDependencyValidation(field), }, control: control, errors: errors, layout: "vertical", onFieldUpdate: onFieldUpdate, updateFieldOptions: updateFieldOptions, updateFieldLabel: updateFieldLabel, setFieldHidden: setFieldHidden, addField: addField, setValue: setValue, clearErrors: clearErrors, getValues: getValues, pendingUpdatesRef: pendingUpdatesRef, notifyPendingUpdate: () => setPendingUpdateCount((prev) => prev + 1), processingDependencyRef: processingDependencyRef, fieldChangeRef: fieldChangeRef }, field.name))), isSubmitting && _jsx("span", { children: "Submitting..." }), submitError && _jsx("p", { className: "text-red-500", children: submitError })] })), children, showDrawerButtons && (_jsxs("div", { className: "flex border-t bg-white", style: { height: '50px', position: 'fixed', bottom: 0, zIndex: 100, width: '420px', }, children: [_jsx(Button, { variant: "outline-primary", className: "w-full flex-1 h-full rounded-none", disabled: isSubmitting, onClick: handleCloseDrawer, children: cancelButtonText }), _jsx(Button, { variant: "primary", className: "flex-1 h-full rounded-none", disabled: isSubmitting, onClick: handleSubmit(handleFormSubmit), children: submitButtonText })] }))] })); }; export default DrawerContent;