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
JavaScript
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;