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