mirror of
https://github.com/documenso/documenso.git
synced 2025-11-12 15:53:02 +10:00
chore: add more field types (#1141)
Adds a number of new field types and capabilities to existing fields. A massive change with far too many moving pieces to document in a single commit.
This commit is contained in:
82
packages/lib/advanced-fields-validation/validate-checkbox.ts
Normal file
82
packages/lib/advanced-fields-validation/validate-checkbox.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { checkboxValidationSigns } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
|
||||
|
||||
interface CheckboxFieldMeta {
|
||||
readOnly?: boolean;
|
||||
required?: boolean;
|
||||
validationRule?: string;
|
||||
validationLength?: number;
|
||||
}
|
||||
|
||||
export const validateCheckboxField = (
|
||||
values: string[],
|
||||
fieldMeta: CheckboxFieldMeta,
|
||||
isSigningPage: boolean = false,
|
||||
): string[] => {
|
||||
const errors = [];
|
||||
|
||||
const { readOnly, required, validationRule, validationLength } = fieldMeta;
|
||||
|
||||
if (readOnly && required) {
|
||||
errors.push('A field cannot be both read-only and required');
|
||||
}
|
||||
|
||||
if (values.length === 0) {
|
||||
errors.push('At least one option must be added');
|
||||
}
|
||||
|
||||
if (readOnly && values.length === 0) {
|
||||
errors.push('A read-only field must have at least one value');
|
||||
}
|
||||
|
||||
if (isSigningPage && required && values.length === 0) {
|
||||
errors.push('Selecting an option is required');
|
||||
}
|
||||
|
||||
if (validationRule && !validationLength) {
|
||||
errors.push('You need to specify the number of options for validation');
|
||||
}
|
||||
|
||||
if (validationLength && !validationRule) {
|
||||
errors.push('You need to specify the validation rule');
|
||||
}
|
||||
|
||||
if (validationRule && validationLength) {
|
||||
const validation = checkboxValidationSigns.find((sign) => sign.label === validationRule);
|
||||
|
||||
if (validation) {
|
||||
let lengthCondition = false;
|
||||
|
||||
switch (validation.value) {
|
||||
case '=':
|
||||
lengthCondition = isSigningPage
|
||||
? values.length !== validationLength
|
||||
: values.length < validationLength;
|
||||
break;
|
||||
case '>=':
|
||||
lengthCondition = values.length < validationLength;
|
||||
break;
|
||||
case '<=':
|
||||
lengthCondition = isSigningPage
|
||||
? values.length > validationLength
|
||||
: values.length < validationLength;
|
||||
break;
|
||||
}
|
||||
|
||||
if (lengthCondition) {
|
||||
let errorMessage;
|
||||
if (isSigningPage) {
|
||||
errorMessage = `You need to ${validationRule.toLowerCase()} ${validationLength} options`;
|
||||
} else {
|
||||
errorMessage =
|
||||
validation.value === '<='
|
||||
? `You need to select at least ${validationLength} options`
|
||||
: `You need to add at least ${validationLength} options`;
|
||||
}
|
||||
|
||||
errors.push(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
54
packages/lib/advanced-fields-validation/validate-dropdown.ts
Normal file
54
packages/lib/advanced-fields-validation/validate-dropdown.ts
Normal file
@ -0,0 +1,54 @@
|
||||
interface DropdownFieldMeta {
|
||||
readOnly?: boolean;
|
||||
required?: boolean;
|
||||
values?: { value: string }[];
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
export const validateDropdownField = (
|
||||
value: string | undefined,
|
||||
fieldMeta: DropdownFieldMeta,
|
||||
isSigningPage: boolean = false,
|
||||
): string[] => {
|
||||
const errors = [];
|
||||
|
||||
const { readOnly, required, values, defaultValue } = fieldMeta;
|
||||
|
||||
if (readOnly && required) {
|
||||
errors.push('A field cannot be both read-only and required');
|
||||
}
|
||||
|
||||
if (readOnly && (!values || values.length === 0)) {
|
||||
errors.push('A read-only field must have at least one value');
|
||||
}
|
||||
|
||||
if (isSigningPage && required && !value) {
|
||||
errors.push('Choosing an option is required');
|
||||
}
|
||||
|
||||
if (values && values.length === 0) {
|
||||
errors.push('Select field must have at least one option');
|
||||
}
|
||||
|
||||
if (values && values.length === 0 && defaultValue) {
|
||||
errors.push('Default value must be one of the available options');
|
||||
}
|
||||
|
||||
if (value && values && !values.find((item) => item.value === value)) {
|
||||
errors.push('Selected value must be one of the available options');
|
||||
}
|
||||
|
||||
if (values && defaultValue && !values.find((item) => item.value === defaultValue)) {
|
||||
errors.push('Default value must be one of the available options');
|
||||
}
|
||||
|
||||
if (values && values.some((item) => item.value.length < 1)) {
|
||||
errors.push('Option value cannot be empty');
|
||||
}
|
||||
|
||||
if (values && new Set(values.map((item) => item.value)).size !== values.length) {
|
||||
errors.push('Duplicate values are not allowed');
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
67
packages/lib/advanced-fields-validation/validate-number.ts
Normal file
67
packages/lib/advanced-fields-validation/validate-number.ts
Normal file
@ -0,0 +1,67 @@
|
||||
// import { numberFormatValues } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
|
||||
|
||||
interface NumberFieldMeta {
|
||||
minValue?: number;
|
||||
maxValue?: number;
|
||||
readOnly?: boolean;
|
||||
required?: boolean;
|
||||
numberFormat?: string;
|
||||
}
|
||||
|
||||
export const validateNumberField = (
|
||||
value: string,
|
||||
fieldMeta?: NumberFieldMeta,
|
||||
isSigningPage: boolean = false,
|
||||
): string[] => {
|
||||
const errors = [];
|
||||
|
||||
const { minValue, maxValue, readOnly, required, numberFormat } = fieldMeta || {};
|
||||
|
||||
const formatRegex: { [key: string]: RegExp } = {
|
||||
'123,456,789.00': /^(?:\d{1,3}(?:,\d{3})*|\d+)(?:\.\d{1,2})?$/,
|
||||
'123.456.789,00': /^(?:\d{1,3}(?:\.\d{3})*|\d+)(?:,\d{1,2})?$/,
|
||||
'123456,789.00': /^(?:\d+)(?:,\d{1,3}(?:\.\d{1,2})?)?$/,
|
||||
};
|
||||
|
||||
const isValidFormat = numberFormat ? formatRegex[numberFormat].test(value) : true;
|
||||
|
||||
if (!isValidFormat) {
|
||||
errors.push(`Value ${value} does not match the number format - ${numberFormat}`);
|
||||
}
|
||||
|
||||
const numberValue = parseFloat(value);
|
||||
|
||||
if (isSigningPage && required && !value) {
|
||||
errors.push('Value is required');
|
||||
}
|
||||
|
||||
if (!/^[0-9,.]+$/.test(value.trim())) {
|
||||
errors.push(`Value is not a valid number`);
|
||||
}
|
||||
|
||||
if (minValue !== undefined && minValue > 0 && numberValue < minValue) {
|
||||
errors.push(`Value ${value} is less than the minimum value of ${minValue}`);
|
||||
}
|
||||
|
||||
if (maxValue !== undefined && maxValue > 0 && numberValue > maxValue) {
|
||||
errors.push(`Value ${value} is greater than the maximum value of ${maxValue}`);
|
||||
}
|
||||
|
||||
if (minValue !== undefined && maxValue !== undefined && minValue > maxValue) {
|
||||
errors.push('Minimum value cannot be greater than maximum value');
|
||||
}
|
||||
|
||||
if (maxValue !== undefined && minValue !== undefined && maxValue < minValue) {
|
||||
errors.push('Maximum value cannot be less than minimum value');
|
||||
}
|
||||
|
||||
if (readOnly && numberValue < 1) {
|
||||
errors.push('A read-only field must have a value greater than 0');
|
||||
}
|
||||
|
||||
if (readOnly && required) {
|
||||
errors.push('A field cannot be both read-only and required');
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
41
packages/lib/advanced-fields-validation/validate-radio.ts
Normal file
41
packages/lib/advanced-fields-validation/validate-radio.ts
Normal file
@ -0,0 +1,41 @@
|
||||
interface RadioFieldMeta {
|
||||
readOnly?: boolean;
|
||||
required?: boolean;
|
||||
values?: { checked: boolean; value: string }[];
|
||||
}
|
||||
|
||||
export const validateRadioField = (
|
||||
value: string | undefined,
|
||||
fieldMeta: RadioFieldMeta,
|
||||
isSigningPage: boolean = false,
|
||||
): string[] => {
|
||||
const errors = [];
|
||||
|
||||
const { readOnly, required, values } = fieldMeta;
|
||||
|
||||
if (readOnly && required) {
|
||||
errors.push('A field cannot be both read-only and required');
|
||||
}
|
||||
|
||||
if (readOnly && (!values || values.length === 0)) {
|
||||
errors.push('A read-only field must have at least one value');
|
||||
}
|
||||
|
||||
if (isSigningPage && required && !value) {
|
||||
errors.push('Choosing an option is required');
|
||||
}
|
||||
|
||||
if (values) {
|
||||
const checkedRadioFieldValues = values.filter((option) => option.checked);
|
||||
|
||||
if (values.length === 0) {
|
||||
errors.push('Radio field must have at least one option');
|
||||
}
|
||||
|
||||
if (checkedRadioFieldValues.length > 1) {
|
||||
errors.push('There cannot be more than one checked option');
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
33
packages/lib/advanced-fields-validation/validate-text.ts
Normal file
33
packages/lib/advanced-fields-validation/validate-text.ts
Normal file
@ -0,0 +1,33 @@
|
||||
interface TextFieldMeta {
|
||||
characterLimit?: number;
|
||||
readOnly?: boolean;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export const validateTextField = (
|
||||
value: string,
|
||||
fieldMeta: TextFieldMeta,
|
||||
isSigningPage: boolean = false,
|
||||
): string[] => {
|
||||
const errors = [];
|
||||
|
||||
const { characterLimit, readOnly, required } = fieldMeta;
|
||||
|
||||
if (required && !value && isSigningPage) {
|
||||
errors.push('Value is required');
|
||||
}
|
||||
|
||||
if (characterLimit !== undefined && characterLimit > 0 && value.length > characterLimit) {
|
||||
errors.push(`Value length (${value.length}) exceeds the character limit (${characterLimit})`);
|
||||
}
|
||||
|
||||
if (readOnly && value.length < 1) {
|
||||
errors.push('A read-only field must have text');
|
||||
}
|
||||
|
||||
if (readOnly && required) {
|
||||
errors.push('A field cannot be both read-only and required');
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
Reference in New Issue
Block a user