UNPKG

@akson/cortex-landing-hooks

Version:

React hooks for landing pages - device detection, API calls, form submission, analytics, and performance

181 lines (178 loc) 5.4 kB
import { useApiCall } from "./chunk-ZHPFIMOF.mjs"; // src/forms/useFormSubmission.ts import { useCallback, useEffect, useRef, useState } from "react"; function useFormSubmission(options) { const { endpoint, method = "POST", onSuccess, onError, debounceMs = 1e3, validateBeforeSubmit, transformData = (data) => data, autosave = false, autosaveDebounceMs = 2e3, headers = {}, trackChanges = true } = options; const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); const [validationErrors, setValidationErrors] = useState([]); const [lastSaved, setLastSaved] = useState(null); const [isDirty, setIsDirty] = useState(false); const debounceTimer = useRef(void 0); const autosaveTimer = useRef(void 0); const lastSubmittedData = useRef(null); const apiCall = useApiCall({ url: endpoint, method, headers, onSuccess: (data) => { setSuccess(true); setLastSaved(/* @__PURE__ */ new Date()); if (trackChanges) { setIsDirty(false); } if (onSuccess) onSuccess(data); }, onError: (err) => { setError(err); if (onError) onError(err); } }); const submit = useCallback( async (data) => { setError(null); setValidationErrors([]); setSuccess(false); if (validateBeforeSubmit) { const validationResult = validateBeforeSubmit(data); const isValid = typeof validationResult === "boolean" ? validationResult : validationResult.isValid; const errors = typeof validationResult === "boolean" ? [] : validationResult.errors; if (!isValid) { setValidationErrors(errors); return; } } if (trackChanges && JSON.stringify(data) !== JSON.stringify(lastSubmittedData.current)) { setIsDirty(true); } setIsSubmitting(true); try { const transformedData = transformData(data); await apiCall.execute(transformedData); lastSubmittedData.current = data; } finally { setIsSubmitting(false); } }, [validateBeforeSubmit, transformData, apiCall, trackChanges] ); const submitDebounced = useCallback( (data) => { if (debounceTimer.current) clearTimeout(debounceTimer.current); debounceTimer.current = setTimeout(() => { submit(data); }, debounceMs); }, [submit, debounceMs] ); useEffect(() => { if (!autosave || !isDirty) return; if (autosaveTimer.current) clearTimeout(autosaveTimer.current); autosaveTimer.current = setTimeout(() => { if (lastSubmittedData.current) { submit(lastSubmittedData.current); } }, autosaveDebounceMs); return () => { if (autosaveTimer.current) clearTimeout(autosaveTimer.current); }; }, [autosave, isDirty, autosaveDebounceMs, submit]); const clearErrors = useCallback(() => { setError(null); setValidationErrors([]); }, []); const reset = useCallback(() => { setIsSubmitting(false); setError(null); setSuccess(false); setValidationErrors([]); setLastSaved(null); setIsDirty(false); lastSubmittedData.current = null; if (debounceTimer.current) clearTimeout(debounceTimer.current); if (autosaveTimer.current) clearTimeout(autosaveTimer.current); }, []); useEffect(() => { return () => { if (debounceTimer.current) clearTimeout(debounceTimer.current); if (autosaveTimer.current) clearTimeout(autosaveTimer.current); }; }, []); return { submit, submitDebounced, isSubmitting, error, success, validationErrors, clearErrors, reset, lastSaved, isDirty, setDirty: setIsDirty }; } function createFormValidator(schema) { return (data) => { const errors = []; for (const [fieldName, validation] of Object.entries(schema)) { const value = data[fieldName]; const fieldValidation = validation; if (fieldValidation.required && (!value || typeof value === "string" && !value.trim())) { errors.push(`${fieldName} is required`); continue; } if (!value || typeof value === "string" && !value.trim()) { continue; } if (typeof value === "string") { if (fieldValidation.minLength && value.length < fieldValidation.minLength) { errors.push(`${fieldName} must be at least ${fieldValidation.minLength} characters`); } if (fieldValidation.maxLength && value.length > fieldValidation.maxLength) { errors.push(`${fieldName} must be no more than ${fieldValidation.maxLength} characters`); } if (fieldValidation.pattern && !fieldValidation.pattern.test(value)) { errors.push(`${fieldName} format is invalid`); } } if (fieldValidation.customValidator) { const customError = fieldValidation.customValidator(value); if (customError) { errors.push(customError); } } } return { isValid: errors.length === 0, errors }; }; } var ValidationPatterns = { email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, phone: /^\+?[\d\s\-\(\)]+$/, url: /^https?:\/\/.+\..+/, number: /^\d+$/, decimal: /^\d+\.?\d*$/ }; export { useFormSubmission, createFormValidator, ValidationPatterns };