openreplay/frontend/app/hooks/useForm.ts
Delirium c144add4bd
Backend logs UI (#2635)
* backend integrations ui start

* some more ui things

* moving around some integration code

* add dynatrace

* add datadog, useform hook and other things to update backend logging integration forms

* change api queries

* backend logging modals

* tracker: fix some types

* remove deprecated integrations, update types

* some ui fixes and improvements

* update notifications on success/error

* ui: debugging log output, autoclose fix

* ui: some stuff for logs base64str

* ui: improve log formatting,  change datadog data format

* some improvs for logging irm

* ui: fixup for sentry
2024-10-29 15:15:28 +01:00

110 lines
2.7 KiB
TypeScript

import React, { useEffect, useState } from 'react';
interface ValidationRule {
custom?: (
value: string | boolean | number
) => string | undefined;
length?: [min: number, max: number];
format?: [regexPattern: string, errorMsg: string];
required?: boolean;
}
type FormValue = string | boolean | number;
function useForm<T extends { [K in keyof T]: FormValue }>(
initialValues: T,
validationRules?: Partial<Record<keyof T, ValidationRule>>
) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
const [hasErrors, setHasErrors] = useState(false);
useEffect(() => {
setValues(initialValues);
}, [initialValues]);
useEffect(() => {
const hasErrors = Object.values(errors).some((error) => !!error);
setHasErrors(hasErrors);
}, [errors])
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target as unknown as { name: keyof T; value: FormValue };
setValues((prevValues) => ({
...prevValues,
[name]: value,
}));
if (validationRules?.[name]) {
validateField(name, value);
}
};
const validateField = (
fieldName: keyof T,
value: string | boolean | number
) => {
const rules = validationRules![fieldName];
let error = '';
if (typeof value !== 'string') return;
if (rules) {
if (rules.required) {
if (!value) {
error = 'Required';
}
}
if (rules.length) {
const [min, max] = rules.length;
if (value.length < min || value.length > max) {
error = `Must be between ${min} and ${max} characters`;
}
}
if (!error && rules.format) {
const [pattern, errorMsg] = rules.format;
const regex = new RegExp(pattern);
if (!regex.test(value)) {
error = errorMsg || 'Invalid format';
}
}
if (!error && typeof rules.custom === 'function') {
const customError = rules.custom(value);
if (customError) {
error = customError;
}
}
}
setErrors((prevErrors) => ({
...prevErrors,
[fieldName]: error,
}));
return Boolean(error);
};
const checkErrors = () => {
const errSignals: boolean[] = [];
Object.keys(values).forEach((key) => {
// @ts-ignore
errSignals.push(validateField(key, values[key]));
});
return errSignals.some((signal) => signal);
}
const resetForm = () => {
setValues(initialValues);
setErrors({});
};
return {
values,
errors,
handleChange,
resetForm,
hasErrors,
checkErrors,
};
}
export default useForm;