prepare frontend for /unlimited page

This commit is contained in:
vchikalkin 2023-03-28 12:35:51 +03:00
parent c347095ded
commit 8268ba3c26
21 changed files with 156 additions and 80 deletions

View File

@ -21,8 +21,7 @@ export default function buildAction<T>(
return (
<Component
action={() =>
import(`process/${processName}/action`).then((module) => module.action(context))
}
import(`process/${processName}/action`).then((module) => module.action(context))}
status={status}
{...props}
/>

View File

@ -19,13 +19,13 @@ export default function buildOptions<T>(
return observer((props: T) => {
const [value, setValue] = useStoreValue(valueName);
const status = useStatus(elementName);
const { isValid, help } = useValidation(elementName);
const { validateStatus, help } = useValidation(elementName);
const options = useOptions(elementName);
return (
<Component
help={help}
isValid={isValid}
validateStatus={validateStatus}
options={options}
setValue={setValue}
status={status}

View File

@ -18,12 +18,12 @@ export default function buildValue<T>(
return observer((props: T) => {
const [value, setValue] = useStoreValue(valueName);
const status = useStatus(elementName);
const { isValid, help } = useValidation(elementName);
const { validateStatus, help } = useValidation(elementName);
return (
<Component
help={help}
isValid={isValid}
validateStatus={validateStatus}
setValue={setValue}
status={status}
value={value}

View File

@ -23,41 +23,57 @@ const AlertWrapper = styled(Box)`
margin: 0 0 5px 0;
`;
function getElementsErrors($calculation) {
function getElementsErrors({ $calculation, $process }) {
return Object.values($calculation.$validation).map((validation) => {
const elementErrors = validation.getErrors();
const elementTitle = validation.params.err_title;
return elementErrors.map(({ key, message }) => (
<AlertWrapper>
<Alert key={key} type="error" showIcon message={Message(elementTitle, message)} />
<Alert
key={key}
type={$process.has('Unlimited') ? 'warning' : 'error'}
showIcon
message={Message(elementTitle, message)}
/>
</AlertWrapper>
));
});
}
function getPaymentsTableErrors($tables) {
function getPaymentsTableErrors({ $tables, $process }) {
const { payments } = $tables;
const errors = payments.validation.getErrors();
const title = payments.validation.params.err_title;
return errors.map(({ key, message }) => (
<Alert key={key} type="error" showIcon message={Message(title, message)} />
<Alert
key={key}
type={$process.has('Unlimited') ? 'warning' : 'error'}
showIcon
message={Message(title, message)}
/>
));
}
function getInsuranceTableErrors($tables) {
function getInsuranceTableErrors({ $tables, $process }) {
const { insurance } = $tables;
const errors = insurance.validation.getErrors();
const title = insurance.validation.params.err_title;
return errors.map(({ key, message }) => (
<Alert key={key} type="error" showIcon message={Message(title, message)} />
<Alert
key={key}
type={$process.has('Unlimited') ? 'warning' : 'error'}
showIcon
message={Message(title, message)}
/>
));
}
const Errors = observer(() => {
const { $calculation, $tables } = useStore();
const store = useStore();
const { $calculation, $tables } = store;
const hasElementsErrors = Object.values($calculation.$validation).some(
(validation) => validation.hasErrors
@ -69,9 +85,9 @@ const Errors = observer(() => {
return <Alert type="success" showIcon message="Ошибок нет 🙂" />;
}
const elementsErrors = getElementsErrors($calculation);
const paymentsErrors = getPaymentsTableErrors($tables);
const insuranceErrors = getInsuranceTableErrors($tables);
const elementsErrors = getElementsErrors(store);
const paymentsErrors = getPaymentsTableErrors(store);
const insuranceErrors = getInsuranceTableErrors(store);
const errors = [...elementsErrors, ...paymentsErrors, ...insuranceErrors];

View File

@ -5,6 +5,7 @@ import type { ProcessContext } from '@/process/types';
import ValidationHelper from '@/stores/validation/helper';
import { reaction } from 'mobx';
import { uid } from 'radash';
import { makeDisposable } from 'tools';
import type { BaseOption } from 'ui/elements/types';
function hasInvalidValueOrOptions(value: unknown, options: Array<BaseOption<unknown>>) {
@ -16,34 +17,38 @@ function hasInvalidValueOrOptions(value: unknown, options: Array<BaseOption<unkn
}
export default function reactions({ store }: ProcessContext) {
const { $calculation, $tables } = store;
const { $calculation, $tables, $process } = store;
reaction(
() => {
const hasElementsErrors = Object.values($calculation.$validation).some(
(validation) => validation.hasErrors
);
makeDisposable(
() =>
reaction(
() => {
const hasElementsErrors = Object.values($calculation.$validation).some(
(validation) => validation.hasErrors
);
const hasPaymentsErrors = $tables.payments.validation.hasErrors;
const hasInsuranceErrors = $tables.insurance.validation.hasErrors;
const hasFingapErrors = $tables.fingap.validation.hasErrors;
const hasPaymentsErrors = $tables.payments.validation.hasErrors;
const hasInsuranceErrors = $tables.insurance.validation.hasErrors;
const hasFingapErrors = $tables.fingap.validation.hasErrors;
return hasElementsErrors || hasPaymentsErrors || hasInsuranceErrors || hasFingapErrors;
},
(hasErrors) => {
if (hasErrors) {
$calculation.$status.setStatus('btnCalculate', 'Disabled');
$calculation.$status.setStatus('btnCreateKP', 'Disabled');
$calculation.$status.setStatus('btnCreateKPMini', 'Disabled');
} else {
$calculation.$status.setStatus('btnCalculate', 'Default');
$calculation.$status.setStatus('btnCreateKP', 'Default');
$calculation.$status.setStatus('btnCreateKPMini', 'Default');
}
},
{
fireImmediately: true,
}
return hasElementsErrors || hasPaymentsErrors || hasInsuranceErrors || hasFingapErrors;
},
(hasErrors) => {
if (hasErrors) {
$calculation.$status.setStatus('btnCalculate', 'Disabled');
$calculation.$status.setStatus('btnCreateKP', 'Disabled');
$calculation.$status.setStatus('btnCreateKPMini', 'Disabled');
} else {
$calculation.$status.setStatus('btnCalculate', 'Default');
$calculation.$status.setStatus('btnCreateKP', 'Default');
$calculation.$status.setStatus('btnCreateKPMini', 'Default');
}
},
{
fireImmediately: true,
}
),
() => $process.has('Unlimited')
);
/**

View File

@ -1,3 +1,4 @@
import Validation from '../validation';
import type { ValidationParams } from '../validation/types';
import OptionsStore from './options';
import StatusStore from './statuses';
@ -6,17 +7,18 @@ import titles from '@/Components/Calculation/config/elements-titles';
import type * as Values from '@/Components/Calculation/config/map/values';
import { getValueName } from '@/Components/Calculation/config/map/values';
import type RootStore from '@/stores/root';
import Validation from '@/stores/validation';
import { observable } from 'mobx';
import type { BaseOption } from 'ui/elements/types';
export default class CalculationStore {
private root: RootStore;
public $values: ValuesStore;
public $status: StatusStore;
public $options: OptionsStore;
public $validation: Partial<Record<Values.Elements, Validation>>;
constructor(rootStore: RootStore) {
this.root = rootStore;
this.$values = new ValuesStore(rootStore);
this.$status = new StatusStore(rootStore);
this.$options = new OptionsStore(rootStore);
@ -24,10 +26,13 @@ export default class CalculationStore {
}
private createElementValidation = <E extends Values.Elements>(elementName: E) => {
this.$validation[elementName] = new Validation({
err_key: elementName,
err_title: titles[elementName],
});
this.$validation[elementName] = new Validation(
{
err_key: elementName,
err_title: titles[elementName],
},
this.root
);
};
public element = <E extends Values.Elements>(elementName: E) => ({

View File

@ -21,6 +21,8 @@ export default class StatusStore {
};
public getStatus(elementName: ElementsActions | ElementsValues) {
if (this.root.$process.has('Unlimited')) return 'Default';
return this.overrided[elementName] || this.statuses[elementName];
}

View File

@ -1,16 +1,16 @@
import { useStore } from '@/stores/hooks';
export function useValidation(elementName) {
const { $calculation } = useStore();
const { $calculation, $process } = useStore();
const hasErrors = $calculation.$validation?.[elementName]?.hasErrors;
if (hasErrors) {
return {
help: 'Некорректные данные',
isValid: false,
validateStatus: $process.has('Unlimited') ? 'warning' : 'error',
};
}
return {
isValid: true,
validateStatus: '',
};
}

View File

@ -1,7 +1,7 @@
import type { ObservableSet } from 'mobx';
import { observable } from 'mobx';
export type Process = 'ELT' | 'LoadKP';
export type Process = 'ELT' | 'LoadKP' | 'Unlimited';
export type ProcessStore = ObservableSet<Process>;
export default function createProcessStore() {

View File

@ -15,10 +15,13 @@ export default class FinGAPTable {
this.selectedKeys = new Set();
this.risks = observable<FinGAP.Risk>([]);
this.validation = new Validation({
err_key: 'ERR_FINGAP_TABLE',
err_title: 'Таблица рисков Safe Finance',
});
this.validation = new Validation(
{
err_key: 'ERR_FINGAP_TABLE',
err_title: 'Таблица рисков Safe Finance',
},
rootStore
);
makeAutoObservable(this);
this.root = rootStore;

View File

@ -21,10 +21,13 @@ export default class InsuranceTable {
insuranceTableConfig.defaultStatuses;
constructor(rootStore: RootStore) {
this.validation = new Validation({
err_key: 'ERR_INSURANCE_TABLE',
err_title: 'Таблица страхования',
});
this.validation = new Validation(
{
err_key: 'ERR_INSURANCE_TABLE',
err_title: 'Таблица страхования',
},
rootStore
);
makeAutoObservable(this);
this.root = rootStore;
@ -95,7 +98,11 @@ export default class InsuranceTable {
getOptions: <V extends Insurance.Values>(valueName: V) => this.options[key][valueName],
getStatus: (valueName: Insurance.Values) => this.statuses[key][valueName],
getStatus: (valueName: Insurance.Values) => {
if (this.root.$process.has('Unlimited')) return 'Default';
return this.statuses[key][valueName];
},
getValue: <V extends Insurance.Values>(valueName: V) => {
const rowIndex = this.values.findIndex((x) => x.key === key);

View File

@ -14,10 +14,13 @@ export default class PaymentsTable {
private overridedStatus: IObservableValue<Status | undefined>;
constructor(rootStore: RootStore) {
this.validation = new Validation({
err_key: 'ERR_PAYMENTS_TABLE',
err_title: 'Таблица платежей',
});
this.validation = new Validation(
{
err_key: 'ERR_PAYMENTS_TABLE',
err_title: 'Таблица платежей',
},
rootStore
);
this.values = observable<number>([]);
this.statuses = observable<Status>([]);
@ -50,6 +53,8 @@ export default class PaymentsTable {
};
public getStatus(index: number) {
if (this.root.$process.has('Unlimited')) return 'Default';
return this.overridedStatus.get() ?? this.statuses[index];
}

View File

@ -1,14 +1,17 @@
import type RootStore from '../root';
import type { RemoveError, ValidationConfig, ValidationError, ValidationParams } from './types';
import { makeAutoObservable } from 'mobx';
import notification from 'ui/elements/notification';
export default class Validation {
private root: RootStore;
private params: ValidationConfig;
private errors: Set<ValidationError>;
constructor(config: ValidationConfig) {
constructor(config: ValidationConfig, rootStore: RootStore) {
this.params = config;
this.errors = new Set();
this.root = rootStore;
makeAutoObservable(this);
}
@ -33,10 +36,11 @@ export default class Validation {
this.errors.add({ key, message });
if (!silent) {
notification.error({
notification.open({
description: message,
key: this.params.err_key,
message: this.params.err_title,
type: this.root.$process.has('Unlimited') ? 'warning' : 'error',
});
}

View File

@ -16,7 +16,7 @@ function Checkbox({
value,
setValue,
status,
isValid,
validateStatus,
help,
text,
...props
@ -26,7 +26,7 @@ function Checkbox({
}
return (
<FormItem hasFeedback help={help} validateStatus={isValid === false ? 'error' : ''}>
<FormItem hasFeedback help={help} validateStatus={validateStatus}>
<AntCheckbox
checked={value}
disabled={status === 'Disabled'}

View File

@ -5,13 +5,20 @@ import type { ChangeEvent, FC } from 'react';
const { Item: FormItem } = Form;
function Input({ value, setValue, status, isValid, help, ...props }: BaseElementProps<string>) {
function Input({
value,
setValue,
status,
validateStatus,
help,
...props
}: BaseElementProps<string>) {
function handleChange(event: ChangeEvent<HTMLInputElement>) {
setValue(event.target.value);
}
return (
<FormItem hasFeedback help={help} validateStatus={isValid === false ? 'error' : ''}>
<FormItem hasFeedback help={help} validateStatus={validateStatus}>
<AntInput disabled={status === 'Disabled'} onChange={handleChange} value={value} {...props} />
</FormItem>
);

View File

@ -7,7 +7,13 @@ const { Item: FormItem } = Form;
type InputNumberProps = AntInputNumberProps<number>;
function InputNumber({ setValue, status, isValid, help, ...props }: BaseElementProps<number>) {
function InputNumber({
setValue,
status,
validateStatus,
help,
...props
}: BaseElementProps<number>) {
function handleChange(value: number | null) {
if (value) {
setValue(value);
@ -17,7 +23,7 @@ function InputNumber({ setValue, status, isValid, help, ...props }: BaseElementP
}
return (
<FormItem hasFeedback help={help} validateStatus={isValid === false ? 'error' : ''}>
<FormItem hasFeedback help={help} validateStatus={validateStatus}>
<AntInputNumber
disabled={status === 'Disabled'}
onChange={handleChange}

View File

@ -19,7 +19,7 @@ function Radio({
setValue,
options,
status,
isValid,
validateStatus,
help,
spaceProps,
...props
@ -29,7 +29,7 @@ function Radio({
}
return (
<FormItem hasFeedback help={help} validateStatus={isValid === false ? 'error' : ''}>
<FormItem hasFeedback help={help} validateStatus={validateStatus}>
<AntRadio.Group
disabled={status === 'Disabled'}
onChange={handleChange}

View File

@ -9,9 +9,17 @@ type ElementProps = BaseElementProps<number | string> & {
options: BaseOption[];
};
function Segmented({ value, setValue, options, status, isValid, help, ...props }: ElementProps) {
function Segmented({
value,
setValue,
options,
status,
validateStatus,
help,
...props
}: ElementProps) {
return (
<FormItem hasFeedback help={help} validateStatus={isValid === false ? 'error' : ''}>
<FormItem hasFeedback help={help} validateStatus={validateStatus}>
<AntSegmented
disabled={status === 'Disabled'}
onChange={setValue}

View File

@ -15,7 +15,7 @@ function Select({
setValue,
options = [],
status,
isValid,
validateStatus,
help,
...props
}: BaseElementProps<string | null> & ElementProps) {
@ -31,7 +31,7 @@ function Select({
);
return (
<FormItem hasFeedback help={help} validateStatus={isValid === false ? 'error' : ''}>
<FormItem hasFeedback help={help} validateStatus={validateStatus}>
<AntSelect
disabled={status === 'Disabled'}
loading={status === 'Loading'}

View File

@ -5,9 +5,16 @@ import type { FC } from 'react';
const { Item: FormItem } = Form;
function Switch({ value, setValue, status, isValid, help, ...props }: BaseElementProps<boolean>) {
function Switch({
value,
setValue,
status,
validateStatus,
help,
...props
}: BaseElementProps<boolean>) {
return (
<FormItem hasFeedback help={help} validateStatus={isValid === false ? 'error' : ''}>
<FormItem hasFeedback help={help} validateStatus={validateStatus}>
<AntSwitch checked={value} disabled={status === 'Disabled'} onChange={setValue} {...props} />
</FormItem>
);

View File

@ -1,10 +1,12 @@
import type { ValidateStatus } from 'antd/es/form/FormItem';
export type Status = 'Default' | 'Disabled' | 'Hidden' | 'Loading';
export type BaseElementProps<Value> = {
help?: string;
isValid?: boolean;
setValue: (value: Value) => void;
status?: Status;
validateStatus?: ValidateStatus;
value: Value;
};