more validations
This commit is contained in:
parent
3808ae3a3e
commit
a1102d5f9c
@ -38,7 +38,7 @@ const renderElements = ({ elements }) => {
|
||||
return elements.map((element, ie) => {
|
||||
const { title: elementTitle, Component, props: elementProps } = element;
|
||||
return (
|
||||
<Flex flexDirection="column" key={ie} my="5px">
|
||||
<Flex flexDirection="column" key={ie}>
|
||||
<ElementTitle>{elementTitle}</ElementTitle>
|
||||
<Component {...elementProps} />
|
||||
</Flex>
|
||||
@ -64,7 +64,7 @@ const renderBlocks = ({ blocks }) => {
|
||||
'2 0 45%',
|
||||
'2 0 45%',
|
||||
'2 0 45%',
|
||||
'3 0 30%'
|
||||
'3 0 30%',
|
||||
]}
|
||||
mx="10px"
|
||||
my="15px"
|
||||
|
||||
@ -7,7 +7,12 @@ import Radio from 'client/Elements/Radio';
|
||||
import Select from 'client/Elements/Select';
|
||||
import Switch from 'client/Elements/Switch';
|
||||
import TextArea from 'client/Elements/TextArea';
|
||||
import { validateInn } from 'client/tools/validate';
|
||||
import {
|
||||
validateInn,
|
||||
validateKpp,
|
||||
validatePhone,
|
||||
validateEmail,
|
||||
} from 'client/tools/validate';
|
||||
|
||||
export default [
|
||||
{
|
||||
@ -113,7 +118,10 @@ export default [
|
||||
title: 'КПП',
|
||||
Component: Input,
|
||||
props: {
|
||||
// TODO regular min: 9, max: 9
|
||||
validation: {
|
||||
errorMessage: 'Некорректный КПП',
|
||||
validator: validateKpp,
|
||||
},
|
||||
name: 'tbxKPP',
|
||||
valueName: 'KPP',
|
||||
},
|
||||
@ -156,7 +164,10 @@ export default [
|
||||
type: 'tel',
|
||||
name: 'tbxPhoneNumber',
|
||||
valueName: 'phoneNumber',
|
||||
pattern: undefined,
|
||||
validation: {
|
||||
errorMessage: 'Некорректный номер телефона',
|
||||
validator: validatePhone,
|
||||
},
|
||||
//TODO: mask + 7(999) 999 99 99
|
||||
},
|
||||
},
|
||||
@ -167,8 +178,11 @@ export default [
|
||||
type: 'email',
|
||||
name: 'tbxEmailAddress',
|
||||
valueName: 'emailAddress',
|
||||
pattern: undefined,
|
||||
//TODO check email valid
|
||||
//TODO email mask
|
||||
validation: {
|
||||
errorMessage: 'Некорректный E-mail',
|
||||
validator: validateEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,29 +1,28 @@
|
||||
import { Checkbox as AntCheckbox } from 'antd';
|
||||
import { Checkbox as AntCheckbox, Form } from 'antd';
|
||||
import { useStatus } from 'client/hooks/useStatus';
|
||||
import { useStoreValue } from 'client/hooks/useStoreValue';
|
||||
import { Box } from 'client/UIKit/grid';
|
||||
import { DEFAULT_DEBOUNCE_DELAY } from 'core/constants/debounce';
|
||||
import { Status } from 'core/types/statuses';
|
||||
import { observer } from 'mobx-react';
|
||||
import React from 'react';
|
||||
import { DEFAULT_DEBOUNCE_DELAY } from 'core/constants/debounce';
|
||||
|
||||
const Checkbox = ({ name, readonly, valueName, computedValue }) => {
|
||||
const { value, setCurrentValue } = useStoreValue({
|
||||
computedValue,
|
||||
valueName,
|
||||
debounceDelay: DEFAULT_DEBOUNCE_DELAY
|
||||
debounceDelay: DEFAULT_DEBOUNCE_DELAY,
|
||||
});
|
||||
const { status } = useStatus(name);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Form.Item>
|
||||
<AntCheckbox
|
||||
disabled={status === Status.Disabled}
|
||||
readonly={readonly}
|
||||
checked={value}
|
||||
onChange={e => setCurrentValue(e.target.checked)}
|
||||
/>
|
||||
</Box>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { InputNumber as AntInputNumber } from 'antd';
|
||||
import { InputNumber as AntInputNumber, Form } from 'antd';
|
||||
import { useStatus } from 'client/hooks/useStatus';
|
||||
import { useStoreValue } from 'client/hooks/useStoreValue';
|
||||
import { Status } from 'core/types/statuses';
|
||||
@ -16,31 +16,33 @@ const InputNumber = ({
|
||||
parser,
|
||||
placeholder,
|
||||
valueName,
|
||||
computedValue
|
||||
computedValue,
|
||||
}) => {
|
||||
const { value, setCurrentValue } = useStoreValue({
|
||||
computedValue,
|
||||
valueName,
|
||||
debounceDelay: TEXT_INPUT_DEBOUNCE_DELAY
|
||||
debounceDelay: TEXT_INPUT_DEBOUNCE_DELAY,
|
||||
});
|
||||
const { status } = useStatus(name);
|
||||
|
||||
return (
|
||||
<AntInputNumber
|
||||
disabled={status === Status.Disabled}
|
||||
readOnly={readonly}
|
||||
placeholder={placeholder}
|
||||
style={{
|
||||
width: '100%'
|
||||
}}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
formatter={formatter}
|
||||
parser={parser}
|
||||
onChange={value => setCurrentValue(value)}
|
||||
value={value}
|
||||
/>
|
||||
<Form.Item>
|
||||
<AntInputNumber
|
||||
disabled={status === Status.Disabled}
|
||||
readOnly={readonly}
|
||||
placeholder={placeholder}
|
||||
style={{
|
||||
width: '100%',
|
||||
}}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
formatter={formatter}
|
||||
parser={parser}
|
||||
onChange={value => setCurrentValue(value)}
|
||||
value={value}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Radio as AntRadio } from 'antd';
|
||||
import { Radio as AntRadio, Form } from 'antd';
|
||||
import { useOptions } from 'client/hooks/useOptions';
|
||||
import { useStatus } from 'client/hooks/useStatus';
|
||||
import { useStoreValue } from 'client/hooks/useStoreValue';
|
||||
@ -11,54 +11,44 @@ const Radio = ({ name, style, computedValue, valueName }) => {
|
||||
const { value, setCurrentValue } = useStoreValue({
|
||||
computedValue,
|
||||
valueName,
|
||||
debounceDelay: DEFAULT_DEBOUNCE_DELAY
|
||||
debounceDelay: DEFAULT_DEBOUNCE_DELAY,
|
||||
});
|
||||
const { status } = useStatus(name);
|
||||
const { options } = useOptions(name);
|
||||
|
||||
return (
|
||||
<AntRadio.Group
|
||||
disabled={status === Status.Disabled}
|
||||
buttonStyle={style === 'button' && 'solid'}
|
||||
value={value}
|
||||
onChange={e => {
|
||||
if (e && e.target) setCurrentValue(e.target.value);
|
||||
}}
|
||||
>
|
||||
{options.map((option, i) => {
|
||||
switch (style) {
|
||||
case 'button':
|
||||
if (option) {
|
||||
return (
|
||||
<AntRadio.Button key={i} value={option.value || ''}>
|
||||
{option.name}
|
||||
</AntRadio.Button>
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (option)
|
||||
return (
|
||||
<AntRadio
|
||||
key={i}
|
||||
value={option.value || ''}
|
||||
style={styles.radio}
|
||||
>
|
||||
{option.name}
|
||||
</AntRadio>
|
||||
);
|
||||
break;
|
||||
}
|
||||
})}
|
||||
</AntRadio.Group>
|
||||
<Form.Item>
|
||||
<AntRadio.Group
|
||||
disabled={status === Status.Disabled}
|
||||
buttonStyle={style === 'button' && 'solid'}
|
||||
value={value}
|
||||
onChange={e => {
|
||||
if (e && e.target) setCurrentValue(e.target.value);
|
||||
}}
|
||||
>
|
||||
{options.map((option, i) => {
|
||||
if (style === 'button') {
|
||||
return (
|
||||
<AntRadio.Button key={i} value={option.value || ''}>
|
||||
{option.name}
|
||||
</AntRadio.Button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<AntRadio key={i} value={option.value || ''} style={styles.radio}>
|
||||
{option.name}
|
||||
</AntRadio>
|
||||
);
|
||||
})}
|
||||
</AntRadio.Group>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = {
|
||||
radio: {
|
||||
display: 'block'
|
||||
}
|
||||
display: 'block',
|
||||
},
|
||||
};
|
||||
|
||||
export default observer(Radio);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Select as AntSelect } from 'antd';
|
||||
import { Select as AntSelect, Form } from 'antd';
|
||||
import { useStatus } from 'client/hooks/useStatus';
|
||||
import { useStoreValue } from 'client/hooks/useStoreValue';
|
||||
import { Status } from 'core/types/statuses';
|
||||
@ -11,29 +11,32 @@ const Select = ({ name, showSearch, computedValue, valueName }) => {
|
||||
const { value, setCurrentValue } = useStoreValue({
|
||||
computedValue,
|
||||
valueName,
|
||||
debounceDelay: DEFAULT_DEBOUNCE_DELAY
|
||||
debounceDelay: DEFAULT_DEBOUNCE_DELAY,
|
||||
});
|
||||
const { status } = useStatus(name);
|
||||
const { options, filter } = useOptions(name);
|
||||
|
||||
return (
|
||||
<AntSelect
|
||||
disabled={status === Status.Disabled}
|
||||
showSearch={showSearch}
|
||||
optionFilterProp="children"
|
||||
filterOption={filter}
|
||||
value={value}
|
||||
onChange={val => setCurrentValue(val)}
|
||||
>
|
||||
{options.map((option, i) => {
|
||||
if (option)
|
||||
return (
|
||||
<AntSelect.Option key={i} value={option.value}>
|
||||
{option.name}
|
||||
</AntSelect.Option>
|
||||
);
|
||||
})}
|
||||
</AntSelect>
|
||||
<Form.Item>
|
||||
<AntSelect
|
||||
disabled={status === Status.Disabled}
|
||||
showSearch={showSearch}
|
||||
optionFilterProp="children"
|
||||
filterOption={filter}
|
||||
value={value}
|
||||
onChange={val => setCurrentValue(val)}
|
||||
>
|
||||
{options.map((option, i) => {
|
||||
if (option)
|
||||
return (
|
||||
<AntSelect.Option key={i} value={option.value}>
|
||||
{option.name}
|
||||
</AntSelect.Option>
|
||||
);
|
||||
return null;
|
||||
})}
|
||||
</AntSelect>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,22 +1,21 @@
|
||||
import { Switch as AntSwitch } from 'antd';
|
||||
import { Form, Switch as AntSwitch } from 'antd';
|
||||
import { useStatus } from 'client/hooks/useStatus';
|
||||
import { useStoreValue } from 'client/hooks/useStoreValue';
|
||||
import { Box } from 'client/UIKit/grid';
|
||||
import { DEFAULT_DEBOUNCE_DELAY } from 'core/constants/debounce';
|
||||
import { Status } from 'core/types/statuses';
|
||||
import { observer } from 'mobx-react';
|
||||
import React from 'react';
|
||||
import { DEFAULT_DEBOUNCE_DELAY } from 'core/constants/debounce';
|
||||
|
||||
const Switch = ({ name, valueName, computedValue }) => {
|
||||
const { value, setCurrentValue } = useStoreValue({
|
||||
computedValue,
|
||||
valueName,
|
||||
debounceDelay: DEFAULT_DEBOUNCE_DELAY
|
||||
debounceDelay: DEFAULT_DEBOUNCE_DELAY,
|
||||
});
|
||||
const { status } = useStatus(name);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Form.Item>
|
||||
<AntSwitch
|
||||
disabled={status === Status.Disabled}
|
||||
checked={value}
|
||||
@ -24,7 +23,7 @@ const Switch = ({ name, valueName, computedValue }) => {
|
||||
setCurrentValue(checked);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -11,20 +11,18 @@ const TextArea = ({
|
||||
readonly,
|
||||
placeholder,
|
||||
valueName,
|
||||
computedValue
|
||||
computedValue,
|
||||
}) => {
|
||||
const { value, setCurrentValue } = useStoreValue({
|
||||
computedValue,
|
||||
valueName,
|
||||
debounceDelay: TEXT_INPUT_DEBOUNCE_DELAY
|
||||
debounceDelay: TEXT_INPUT_DEBOUNCE_DELAY,
|
||||
});
|
||||
const { status } = useStatus(name);
|
||||
|
||||
return (
|
||||
<AntInput.TextArea
|
||||
style={{
|
||||
height: '150px'
|
||||
}}
|
||||
autoSize={{ minRows: 5, maxRows: 8 }}
|
||||
disabled={status === Status.Disabled}
|
||||
readOnly={readonly}
|
||||
placeholder={placeholder}
|
||||
|
||||
@ -16,9 +16,10 @@ import { ICalculationStore } from 'core/types/stores';
|
||||
const CalculationStore: ICalculationStore = observable(
|
||||
assignProperties(
|
||||
{
|
||||
statuses: initialStatuses,
|
||||
validations: {},
|
||||
values: initialValues,
|
||||
options: initialOptions,
|
||||
statuses: initialStatuses,
|
||||
filters: {},
|
||||
|
||||
getValue(sourceValueName: ValuesNames) {
|
||||
@ -33,14 +34,14 @@ const CalculationStore: ICalculationStore = observable(
|
||||
},
|
||||
setStatus(elementName: ElementsNames, status: Status) {
|
||||
this.statuses[elementName] = status;
|
||||
}
|
||||
},
|
||||
},
|
||||
computedEffects
|
||||
)
|
||||
computedEffects,
|
||||
),
|
||||
);
|
||||
|
||||
autorunEffects.map(autorunEffect =>
|
||||
autorun(autorunEffect(CalculationStore, CommonStore))
|
||||
autorun(autorunEffect(CalculationStore, CommonStore)),
|
||||
);
|
||||
|
||||
reactionEffects.map(reactionEffectBuilder => {
|
||||
|
||||
@ -1,49 +1,90 @@
|
||||
export function validateInn(inn) {
|
||||
if (typeof inn === 'number') {
|
||||
inn = inn.toString();
|
||||
} else if (typeof inn !== 'string') {
|
||||
inn = '';
|
||||
import validator from 'validator';
|
||||
|
||||
const isINNIndividual = value => {
|
||||
const valueToString = value ? value.toString() : '';
|
||||
const getN = index => parseInt(valueToString[index]);
|
||||
if (valueToString.length === 12) {
|
||||
const dgt11 =
|
||||
((7 * getN(0) +
|
||||
2 * getN(1) +
|
||||
4 * getN(2) +
|
||||
10 * getN(3) +
|
||||
3 * getN(4) +
|
||||
5 * getN(5) +
|
||||
9 * getN(6) +
|
||||
4 * getN(7) +
|
||||
6 * getN(8) +
|
||||
8 * getN(9)) %
|
||||
11) %
|
||||
10;
|
||||
|
||||
const dgt12 =
|
||||
((3 * getN(0) +
|
||||
7 * getN(1) +
|
||||
2 * getN(2) +
|
||||
4 * getN(3) +
|
||||
10 * getN(4) +
|
||||
3 * getN(5) +
|
||||
5 * getN(6) +
|
||||
9 * getN(7) +
|
||||
4 * getN(8) +
|
||||
6 * getN(9) +
|
||||
8 * getN(10)) %
|
||||
11) %
|
||||
10;
|
||||
|
||||
return getN(10) === dgt11 && getN(11) === dgt12;
|
||||
}
|
||||
var checkDigit = function (inn, coefficients) {
|
||||
var n = 0;
|
||||
for (var i in coefficients) {
|
||||
n += coefficients[i] * inn[i];
|
||||
}
|
||||
return parseInt((n % 11) % 10);
|
||||
};
|
||||
switch (inn.length) {
|
||||
case 10:
|
||||
var n10 = checkDigit(inn, [2, 4, 10, 3, 5, 9, 4, 6, 8]);
|
||||
return n10 === parseInt(inn[9]);
|
||||
case 12:
|
||||
var n11 = checkDigit(inn, [7, 2, 4, 10, 3, 5, 9, 4, 6, 8]);
|
||||
var n12 = checkDigit(inn, [3, 7, 2, 4, 10, 3, 5, 9, 4, 6, 8]);
|
||||
return n11 === parseInt(inn[10]) && n12 === parseInt(inn[11]);
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isINNLegalEntity = value => {
|
||||
const valueToString = value ? value.toString() : '';
|
||||
const getN = index => parseInt(valueToString[index]);
|
||||
if (valueToString.length === 10) {
|
||||
const dgt10 =
|
||||
((2 * getN(0) +
|
||||
4 * getN(1) +
|
||||
10 * getN(2) +
|
||||
3 * getN(3) +
|
||||
5 * getN(4) +
|
||||
9 * getN(5) +
|
||||
4 * getN(6) +
|
||||
6 * getN(7) +
|
||||
8 * getN(8)) %
|
||||
11) %
|
||||
10;
|
||||
return getN(9) === dgt10;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export function validateInn(value) {
|
||||
const stringValue = value ? value.toString() : '';
|
||||
|
||||
if (stringValue.length === 10) {
|
||||
return isINNLegalEntity(stringValue);
|
||||
}
|
||||
if (stringValue.length === 12) {
|
||||
return isINNIndividual(stringValue);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function validateKpp(kpp, error) {
|
||||
var result = false;
|
||||
if (typeof kpp === 'number') {
|
||||
kpp = kpp.toString();
|
||||
} else if (typeof kpp !== 'string') {
|
||||
kpp = '';
|
||||
}
|
||||
if (!kpp.length) {
|
||||
error.code = 1;
|
||||
error.message = 'КПП пуст';
|
||||
} else if (kpp.length !== 9) {
|
||||
error.code = 2;
|
||||
error.message =
|
||||
'КПП может состоять только из 9 знаков (цифр или заглавных букв латинского алфавита от A до Z)';
|
||||
} else if (!/^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$/.test(kpp)) {
|
||||
error.code = 3;
|
||||
error.message = 'Неправильный формат КПП';
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
export function validateKpp(value) {
|
||||
const stringValue = value ? value.toString() : '';
|
||||
if (stringValue.length !== 9) return false;
|
||||
if (!stringValue.match(/\d{4}[\dA-Z][\dA-Z]\d{3}/)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validatePhone(value) {
|
||||
const stringValue = value ? value.toString() : '';
|
||||
return validator.isMobilePhone(stringValue, ['ru-RU']);
|
||||
}
|
||||
|
||||
export function validateEmail(value) {
|
||||
const stringValue = value ? value.toString() : '';
|
||||
return validator.isEmail(stringValue);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { TElements } from 'core/types/elements';
|
||||
|
||||
const initialOptions: TElements = {
|
||||
const initialOptions: TElements<any> = {
|
||||
selectChannel: [
|
||||
{
|
||||
name: 'От агента-ФЛ-сотрудника поставщика',
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
export const TEXT_INPUT_DEBOUNCE_DELAY = 800;
|
||||
export const TEXT_INPUT_DEBOUNCE_DELAY = 650;
|
||||
export const DEFAULT_DEBOUNCE_DELAY = 0;
|
||||
|
||||
@ -133,6 +133,6 @@ export type ElementsNames =
|
||||
| 'tbxOpportunityNumber'
|
||||
| 'radioInsuranceOPF';
|
||||
|
||||
export type TElements = {
|
||||
[elementName in ElementsNames]?: any;
|
||||
export type TElements<T> = {
|
||||
[elementName in ElementsNames]?: T;
|
||||
};
|
||||
|
||||
@ -3,10 +3,11 @@ import { TElements, ElementsNames } from './elements';
|
||||
import { TStatuses, Status } from './statuses';
|
||||
|
||||
export interface ICalculationStore {
|
||||
values: TValues;
|
||||
options: TElements;
|
||||
statuses: TStatuses;
|
||||
filters: any;
|
||||
validations: TElements<boolean>;
|
||||
values: TValues;
|
||||
options: TElements<any>;
|
||||
filters: TElements<any>;
|
||||
|
||||
getValue: (sourceValueName: ValuesNames) => any;
|
||||
getStatus: (elementName: ElementsNames) => Status;
|
||||
|
||||
Reference in New Issue
Block a user