feat(TEAMMSBMOB-22193): изменение логики бэк контролей платежей
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import { network } from '@msb/http';
|
||||
import { PAYMENTS_CLIENT_I18N_RU_RU } from '../../endpoints';
|
||||
|
||||
const fetchPaymentsI18n = async (): Promise<Record<string, string>> => {
|
||||
const response = await network.client.get<Record<string, string>>(PAYMENTS_CLIENT_I18N_RU_RU);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export { fetchPaymentsI18n };
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './fetchPaymentsI18n';
|
||||
export * from './queryKeys';
|
||||
@@ -0,0 +1,5 @@
|
||||
const QUERY_KEY_PAYMENTS_I18N = 'payments-i18n';
|
||||
|
||||
export { QUERY_KEY_PAYMENTS_I18N };
|
||||
|
||||
|
||||
@@ -17,3 +17,4 @@ export * from './fetchEditTemplate';
|
||||
export * from './sendPayments';
|
||||
export * from './fetchSignatories';
|
||||
export * from './fetchSignatures';
|
||||
export * from './i18n';
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
const PAYMENTS_CLIENT_I18N_RU_RU = '/ruble-payment-client/i18n/ru_Ru' as const;
|
||||
|
||||
export { PAYMENTS_CLIENT_I18N_RU_RU };
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './constants';
|
||||
|
||||
|
||||
@@ -22,3 +22,4 @@ export * from './fetchEditTemplate';
|
||||
export * from './sendPayments';
|
||||
export * from './fetchSignatories';
|
||||
export * from './fetchSignatures';
|
||||
export * from './i18n';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './usePaymentsI18n';
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useQuery } from '@msb/http';
|
||||
import { fetchPaymentsI18n, QUERY_KEY_PAYMENTS_I18N } from '../../api';
|
||||
|
||||
const usePaymentsI18n = () => {
|
||||
const { data, error, isLoading, refetch } = useQuery<Record<string, string>, Error | undefined>({
|
||||
queryKey: [QUERY_KEY_PAYMENTS_I18N],
|
||||
queryFn: fetchPaymentsI18n,
|
||||
refetchOnMount: true,
|
||||
staleTime: 0,
|
||||
});
|
||||
|
||||
return {
|
||||
paymentsI18nData: data,
|
||||
paymentsI18nError: error,
|
||||
isPaymentsI18nLoading: isLoading,
|
||||
refetchPaymentsI18n: refetch,
|
||||
};
|
||||
};
|
||||
|
||||
export { usePaymentsI18n };
|
||||
|
||||
|
||||
@@ -11,3 +11,4 @@ export * from './paymentImport';
|
||||
export * from './fileImport';
|
||||
export * from './fetchSignatores';
|
||||
export * from './fetchSignatures';
|
||||
export * from './i18n';
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
getPaymentCopyHandlers,
|
||||
getPageWithIndicatorsHandlers,
|
||||
getSuggestionPageHandlers,
|
||||
paymentsI18nHandlers,
|
||||
} from './payments-client';
|
||||
import { fileImportHandlers } from './payments-client/fileImport';
|
||||
import { paymentImportHandlers } from './payments-client/paymentImport';
|
||||
@@ -119,6 +120,7 @@ const handlers = [
|
||||
...userNotificationsHandler,
|
||||
...getPageWithIndicatorsHandlers,
|
||||
...getSuggestionPageHandlers,
|
||||
...paymentsI18nHandlers,
|
||||
...editTreasuryDealsHandlers,
|
||||
...retryTreasuryDealsHandlers,
|
||||
...fetchTreasuryDealsAccountsMnoHandlers,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { PAYMENTS_CLIENT_I18N_RU_RU } from '@msb/http';
|
||||
import { rest } from 'msw';
|
||||
import { PAYMENTS_CLIENT_I18N_MOCK } from './mocks';
|
||||
|
||||
const paymentsI18nHandler = rest.get<Record<string, string>>(PAYMENTS_CLIENT_I18N_RU_RU, (_, res, ctx) =>
|
||||
res(ctx.delay(100), ctx.json(PAYMENTS_CLIENT_I18N_MOCK))
|
||||
);
|
||||
|
||||
export { paymentsI18nHandler };
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { paymentsI18nHandler } from './i18n';
|
||||
|
||||
export * from './mocks';
|
||||
export * from './i18n';
|
||||
|
||||
const paymentsI18nHandlers = [paymentsI18nHandler];
|
||||
|
||||
export { paymentsI18nHandlers };
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
const PAYMENTS_CLIENT_I18N_MOCK: Record<string, string> = {
|
||||
'cpm.field.empty': 'Реквизит «{fieldName}» является обязательным для заполнения, не может быть пустым',
|
||||
'cpm.field.string.empty': 'Поле «{fieldName}» обязательно для заполнения',
|
||||
'cpm.field.paymentAmount.paymentAmount.positive': 'Сумма платежа не может быть нулевой',
|
||||
'cpm.field.paymentAmount.vatCalculationMethod.not.selected': 'Не выбран способ расчета НДС',
|
||||
'cpm.field.paymentInfo.paymentPurpose.existNds': 'В назначении платежа не обнаружено упоминание об НДС',
|
||||
'cpm.field.payerDetails.innKio.length': 'Длина ИНН должна быть 10 или 12 символов, КИО - 5 символов',
|
||||
'field.empty': 'Реквизит «{field}» является обязательным для заполнения и не может быть пустым.',
|
||||
'paymentAmount.vatCalculationMethod': 'Способ расчета НДС',
|
||||
'paymentInfo.paymentPurpose': 'Назначение платежа',
|
||||
'sbp.bankClient.merchantAddress': 'Адрес Точки продаж',
|
||||
};
|
||||
|
||||
export { PAYMENTS_CLIENT_I18N_MOCK };
|
||||
|
||||
|
||||
@@ -12,4 +12,5 @@ export * from './getPaymentCopy';
|
||||
export * from './paymentImport';
|
||||
export * from './fileImport';
|
||||
export * from './sendPayments';
|
||||
export * from './i18n';
|
||||
export * from './types';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './dropdownType';
|
||||
export * from './serverValidationLocalization';
|
||||
export * from './validationErrorMessages';
|
||||
export * from './fieldIdentityMapping';
|
||||
export * from './signAndSend';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ValidationBankSerials } from '@msb/shared';
|
||||
import { validationErrorMessages } from '../constants';
|
||||
import { popBackendWarningForField } from './backendControlsStore';
|
||||
import {
|
||||
PAYEE_FIELD_IDENTITY as PAYEE,
|
||||
PAYER_FIELD_IDENTITY as PAYER,
|
||||
@@ -159,6 +160,10 @@ namespace FormValidation {
|
||||
});
|
||||
}
|
||||
|
||||
export function hasAnyMessages(): boolean {
|
||||
return Object.values(_messages).some(message => message !== null && message !== undefined);
|
||||
}
|
||||
|
||||
export function attachMessages(messages: ValidationMessage.Message[]) {
|
||||
Object.keys(_messages).forEach(key => {
|
||||
_messages[key] = null;
|
||||
@@ -218,27 +223,50 @@ namespace FormValidation {
|
||||
}
|
||||
|
||||
export function validate(fieldId: string, value: string): MessageExistence {
|
||||
let result: MessageExistence;
|
||||
|
||||
switch (fieldId) {
|
||||
case TEMPLATE_NAME:
|
||||
return validateTemplateName(value);
|
||||
result = validateTemplateName(value);
|
||||
break;
|
||||
case PAYER.DOCUMENT_NUMBER:
|
||||
return validateDocumentNumber(value);
|
||||
result = validateDocumentNumber(value);
|
||||
break;
|
||||
case PAYER.WRITE_OFF_ACCOUNT:
|
||||
return validatePayerAccount(value);
|
||||
result = validatePayerAccount(value);
|
||||
break;
|
||||
case PAYEE.INN:
|
||||
return validateInnGeneric(PAYEE.INN, value);
|
||||
result = validateInnGeneric(PAYEE.INN, value);
|
||||
break;
|
||||
case PAYER.THIRD_PARTY_INN:
|
||||
return validateInnGeneric(PAYER.THIRD_PARTY_INN, value);
|
||||
result = validateInnGeneric(PAYER.THIRD_PARTY_INN, value);
|
||||
break;
|
||||
case PAYEE.ACCOUNT: {
|
||||
const values = value.split(delimiter);
|
||||
|
||||
return validatePayeeAccount(values[0], values[1]);
|
||||
result = validatePayeeAccount(values[0], values[1]);
|
||||
break;
|
||||
}
|
||||
case PAYER.TARGET:
|
||||
return validatePaymentPurpose(value);
|
||||
result = validatePaymentPurpose(value);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
result = null;
|
||||
}
|
||||
|
||||
if (result === null) {
|
||||
const backendWarning = popBackendWarningForField(fieldId);
|
||||
|
||||
if (backendWarning) {
|
||||
const warningMessage = ValidationMessage.createWarning(fieldId, backendWarning);
|
||||
|
||||
_messages[fieldId] = warningMessage;
|
||||
|
||||
return warningMessage;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function clearBudgetFields(): void {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { PaymentFormFields } from '@/features/Forms';
|
||||
|
||||
type Listener = () => void;
|
||||
|
||||
interface UnknownControl {
|
||||
level: PaymentFormFields.VALIDATION_LEVEL;
|
||||
text: string;
|
||||
}
|
||||
|
||||
let unknownControls: UnknownControl[] = [];
|
||||
const unknownListeners = new Set<Listener>();
|
||||
|
||||
let warningsByField: Record<string, string[]> = {};
|
||||
|
||||
let hasKnownFieldErrors = false;
|
||||
const knownErrorsListeners = new Set<Listener>();
|
||||
|
||||
const notifyUnknown = () => unknownListeners.forEach(listener => listener());
|
||||
const notifyKnownErrors = () => knownErrorsListeners.forEach(listener => listener());
|
||||
|
||||
const setUnknownControlTexts = (controls: UnknownControl[]): void => {
|
||||
unknownControls = controls;
|
||||
notifyUnknown();
|
||||
};
|
||||
|
||||
const clearUnknownControlTexts = (): void => {
|
||||
unknownControls = [];
|
||||
notifyUnknown();
|
||||
};
|
||||
|
||||
const getUnknownControlTexts = (): UnknownControl[] => unknownControls;
|
||||
|
||||
const subscribeUnknownControlTexts = (listener: Listener): (() => void) => {
|
||||
unknownListeners.add(listener);
|
||||
|
||||
return () => unknownListeners.delete(listener);
|
||||
};
|
||||
|
||||
const useUnknownControlTexts = (): UnknownControl[] => {
|
||||
const [value, setValue] = useState<UnknownControl[]>(() => getUnknownControlTexts());
|
||||
|
||||
useEffect(() => subscribeUnknownControlTexts(() => setValue(getUnknownControlTexts())), []);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const setBackendWarningsByField = (next: Record<string, string[]>): void => {
|
||||
warningsByField = next;
|
||||
};
|
||||
|
||||
const clearBackendWarningsByField = (): void => {
|
||||
warningsByField = {};
|
||||
};
|
||||
|
||||
const popBackendWarningForField = (fieldId: string): string | undefined => {
|
||||
const list = warningsByField[fieldId];
|
||||
|
||||
if (!list || list.length === 0) return undefined;
|
||||
|
||||
return list.shift();
|
||||
};
|
||||
|
||||
const setHasKnownFieldErrors = (value: boolean): void => {
|
||||
hasKnownFieldErrors = value;
|
||||
notifyKnownErrors();
|
||||
};
|
||||
|
||||
const getHasKnownFieldErrors = (): boolean => hasKnownFieldErrors;
|
||||
|
||||
const subscribeHasKnownFieldErrors = (listener: Listener): (() => void) => {
|
||||
knownErrorsListeners.add(listener);
|
||||
|
||||
return () => knownErrorsListeners.delete(listener);
|
||||
};
|
||||
|
||||
const useHasKnownFieldErrors = (): boolean => {
|
||||
const [value, setValue] = useState<boolean>(() => getHasKnownFieldErrors());
|
||||
|
||||
useEffect(() => subscribeHasKnownFieldErrors(() => setValue(getHasKnownFieldErrors())), []);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export type { UnknownControl };
|
||||
|
||||
export {
|
||||
setUnknownControlTexts,
|
||||
clearUnknownControlTexts,
|
||||
getUnknownControlTexts,
|
||||
subscribeUnknownControlTexts,
|
||||
useUnknownControlTexts,
|
||||
setBackendWarningsByField,
|
||||
clearBackendWarningsByField,
|
||||
popBackendWarningForField,
|
||||
setHasKnownFieldErrors,
|
||||
getHasKnownFieldErrors,
|
||||
subscribeHasKnownFieldErrors,
|
||||
useHasKnownFieldErrors,
|
||||
};
|
||||
@@ -2,3 +2,4 @@ export * from './ValidationMessage';
|
||||
export * from './FormValidation';
|
||||
export * from './utils';
|
||||
export * from './PaymentFormFields';
|
||||
export * from './backendControlsStore';
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
let paymentsI18nData: Record<string, string> | null = null;
|
||||
|
||||
const setPaymentsI18nData = (data: Record<string, string> | undefined): void => {
|
||||
paymentsI18nData = data ?? null;
|
||||
};
|
||||
|
||||
const getPaymentsI18nData = (): Record<string, string> | undefined => paymentsI18nData ?? undefined;
|
||||
|
||||
export { getPaymentsI18nData, setPaymentsI18nData };
|
||||
@@ -1,58 +1,96 @@
|
||||
import type { ValidationResultData, GroupResult, ControlResult, CheckResult } from '@msb/http';
|
||||
import { ALLOWED_SIGN_AND_SEND_STATUSES } from '../constants';
|
||||
import {
|
||||
ValidationMessage,
|
||||
payeeMapTitleIdentity,
|
||||
payerMapTitleIdentity,
|
||||
budgetPaymentMapTitleIdentity,
|
||||
serverValidationLocalization,
|
||||
mapServerFieldToUi,
|
||||
FormValidation,
|
||||
PaymentFormFields,
|
||||
} from '@/features/Forms';
|
||||
clearBackendWarningsByField,
|
||||
clearUnknownControlTexts,
|
||||
setBackendWarningsByField,
|
||||
setHasKnownFieldErrors,
|
||||
setUnknownControlTexts,
|
||||
} from './backendControlsStore';
|
||||
import { getPaymentsI18nData } from './paymentsI18nStore';
|
||||
import { ValidationMessage, mapServerFieldToUi, FormValidation, PaymentFormFields } from '@/features/Forms';
|
||||
|
||||
const isSignAndSendAllowedStatus = (status: string | null | undefined): boolean =>
|
||||
!!status && (ALLOWED_SIGN_AND_SEND_STATUSES as readonly string[]).includes(status);
|
||||
|
||||
const mapTitleIdentity = { ...payeeMapTitleIdentity, ...payerMapTitleIdentity, ...budgetPaymentMapTitleIdentity };
|
||||
|
||||
const cleanArray = (arr: Array<{ label: string; value: string | undefined }>) => arr.filter(el => !!el.value);
|
||||
|
||||
const collectValidationMessages = (vr?: ValidationResultData): ValidationMessage.Message[] => {
|
||||
if (!vr?.groupResults || !Array.isArray(vr.groupResults)) return [];
|
||||
if (!vr?.groupResults || !Array.isArray(vr.groupResults)) {
|
||||
clearBackendWarningsByField();
|
||||
clearUnknownControlTexts();
|
||||
setHasKnownFieldErrors(false);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const messages: ValidationMessage.Message[] = [];
|
||||
const warningsByFieldAndType = new Map<string, Map<string, string>>();
|
||||
const unknownByType = new Map<string, { level: PaymentFormFields.VALIDATION_LEVEL; text: string }>();
|
||||
|
||||
const LEVEL_CRITICAL = PaymentFormFields.VALIDATION_LEVEL.CRITICAL;
|
||||
const LEVEL_WARNING = PaymentFormFields.VALIDATION_LEVEL.WARNING;
|
||||
const regEx = /{([^}]+)}/g;
|
||||
const FIELD_NAME_PARAM = 'fieldName';
|
||||
const i18nData = getPaymentsI18nData();
|
||||
|
||||
vr.groupResults.forEach((group: GroupResult) => {
|
||||
(group.controlResults || []).forEach((control: ControlResult) => {
|
||||
const { level, checkResults } = control;
|
||||
const { id: controlId, name: controlName } = control as { id?: string; name?: string };
|
||||
const controlType = String(controlId || controlName || '');
|
||||
|
||||
(checkResults || []).forEach((check: CheckResult) => {
|
||||
const field = mapServerFieldToUi(check.fieldName, FormValidation.isThirdPartyEnabled());
|
||||
const { message } = check;
|
||||
let localized = serverValidationLocalization[message.message] || null;
|
||||
const { fieldName, message } = check;
|
||||
const field = mapServerFieldToUi(fieldName, FormValidation.isThirdPartyEnabled());
|
||||
const key = message?.message;
|
||||
const params = message?.params;
|
||||
let template = (key ? i18nData?.[key] : undefined) || key || null;
|
||||
|
||||
if (localized && message) {
|
||||
localized =
|
||||
localized.match(regEx)?.reduce((acc, current) => {
|
||||
if (template && message) {
|
||||
const fieldNameText = (fieldName ? i18nData?.[fieldName] : undefined) || '';
|
||||
|
||||
template =
|
||||
template.match(regEx)?.reduce((acc, current) => {
|
||||
const identity = current.replaceAll('{', '').replaceAll('}', '');
|
||||
|
||||
return acc.replaceAll(current, message.params?.[identity] || '');
|
||||
}, localized) ?? localized;
|
||||
if (identity === FIELD_NAME_PARAM) {
|
||||
return acc.replaceAll(current, fieldNameText);
|
||||
}
|
||||
|
||||
return acc.replaceAll(current, params?.[identity] || '');
|
||||
}, template) ?? template;
|
||||
}
|
||||
|
||||
if (!field || !localized) return;
|
||||
if (!template) return;
|
||||
|
||||
localized = localized.replace('{fieldName}', mapTitleIdentity[field] || '');
|
||||
if (!field) {
|
||||
const unknownKey = controlType || template;
|
||||
|
||||
if (!unknownByType.has(unknownKey)) {
|
||||
const unknownLevel = level === LEVEL_CRITICAL ? LEVEL_CRITICAL : LEVEL_WARNING;
|
||||
|
||||
unknownByType.set(unknownKey, {
|
||||
level: unknownLevel,
|
||||
text: template,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (level === LEVEL_CRITICAL) {
|
||||
messages.push(ValidationMessage.createError(field, localized));
|
||||
} else {
|
||||
messages.push(ValidationMessage.createWarning(field, localized));
|
||||
messages.push(ValidationMessage.createError(field, template));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const warningKey = controlType || template;
|
||||
const perField = warningsByFieldAndType.get(field) || new Map<string, string>();
|
||||
|
||||
if (!perField.has(warningKey)) perField.set(warningKey, template);
|
||||
|
||||
warningsByFieldAndType.set(field, perField);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -67,6 +105,20 @@ const collectValidationMessages = (vr?: ValidationResultData): ValidationMessage
|
||||
}
|
||||
});
|
||||
|
||||
setBackendWarningsByField(
|
||||
Object.fromEntries(Array.from(warningsByFieldAndType.entries()).map(([fieldId, byType]) => [fieldId, Array.from(byType.values())]))
|
||||
);
|
||||
|
||||
const hasKnownErrors = byField.size > 0 || warningsByFieldAndType.size > 0;
|
||||
|
||||
setHasKnownFieldErrors(hasKnownErrors);
|
||||
|
||||
if (unknownByType.size > 0) {
|
||||
setUnknownControlTexts(Array.from(unknownByType.values()));
|
||||
} else {
|
||||
clearUnknownControlTexts();
|
||||
}
|
||||
|
||||
return Array.from(byField.values());
|
||||
};
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import styled from '@emotion/styled';
|
||||
import styledCss from '@styled-system/css';
|
||||
|
||||
const Container = styled.div(
|
||||
styledCss({
|
||||
marginTop: '32px',
|
||||
})
|
||||
);
|
||||
|
||||
const List = styled.ul(
|
||||
styledCss({
|
||||
display: 'grid',
|
||||
gap: '8px',
|
||||
paddingLeft: '18px',
|
||||
margin: 0,
|
||||
})
|
||||
);
|
||||
|
||||
const WarningWrapper = styled.div<{ hasCritical: boolean }>`
|
||||
margin-top: ${({ hasCritical }) => (hasCritical ? '16px' : 0)};
|
||||
`;
|
||||
|
||||
export { Container, List, WarningWrapper };
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Informer } from '@fractal-ui/extended';
|
||||
import * as S from './UnknownBackendControlsSheet.styles';
|
||||
import { LOCALIZATION } from './constants/localization';
|
||||
import { PaymentFormFields } from '@/features/Forms';
|
||||
import { useHasKnownFieldErrors, useUnknownControlTexts } from '@/features/Forms/lib';
|
||||
|
||||
const UnknownBackendControlsSheet = (): ReactElement | null => {
|
||||
const controls = useUnknownControlTexts();
|
||||
const hasKnownFieldErrors = useHasKnownFieldErrors();
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const hasScrolledRef = useRef(false);
|
||||
|
||||
const { critical, warning } = useMemo(() => {
|
||||
const criticalControls = controls.filter(c => c.level === PaymentFormFields.VALIDATION_LEVEL.CRITICAL);
|
||||
const warningControls = controls.filter(c => c.level === PaymentFormFields.VALIDATION_LEVEL.WARNING);
|
||||
|
||||
return {
|
||||
critical: criticalControls.map(c => c.text),
|
||||
warning: warningControls.map(c => c.text),
|
||||
};
|
||||
}, [controls]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
if (controls.length === 0) return;
|
||||
|
||||
if (hasKnownFieldErrors) return;
|
||||
|
||||
if (hasScrolledRef.current) return;
|
||||
|
||||
hasScrolledRef.current = true;
|
||||
containerRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||
}, [controls.length, hasKnownFieldErrors]);
|
||||
|
||||
if (controls.length === 0) return null;
|
||||
|
||||
return (
|
||||
<S.Container ref={containerRef}>
|
||||
{critical.length > 0 && (
|
||||
<Informer statusIcon title={LOCALIZATION.TITLE} variant="error">
|
||||
<S.List>
|
||||
{critical.map(text => (
|
||||
<li key={`critical-${text}`}>{text}</li>
|
||||
))}
|
||||
</S.List>
|
||||
</Informer>
|
||||
)}
|
||||
{warning.length > 0 && (
|
||||
<S.WarningWrapper hasCritical={critical.length > 0}>
|
||||
<Informer statusIcon variant="warning">
|
||||
<S.List>
|
||||
{warning.map(text => (
|
||||
<li key={`warning-${text}`}>{text}</li>
|
||||
))}
|
||||
</S.List>
|
||||
</Informer>
|
||||
</S.WarningWrapper>
|
||||
)}
|
||||
</S.Container>
|
||||
);
|
||||
};
|
||||
|
||||
export { UnknownBackendControlsSheet };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
const LOCALIZATION = {
|
||||
TITLE: 'Ошибки',
|
||||
} as const;
|
||||
|
||||
export { LOCALIZATION };
|
||||
@@ -0,0 +1 @@
|
||||
export * from './UnknownBackendControlsSheet';
|
||||
@@ -7,3 +7,4 @@ export * from './DropdownIncomeType';
|
||||
export * from './DropdownOrganization';
|
||||
export * from './PayeeFormFields';
|
||||
export * from './PayerFormFields';
|
||||
export * from './UnknownBackendControlsSheet';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button, ScrollContainer } from '@fractal-ui/core';
|
||||
import { ErrorIcon, OkIcon } from '@fractal-ui/library';
|
||||
import { StatusModal, useSnackbar } from '@fractal-ui/overlays';
|
||||
import { Text } from '@fractal-ui/styling';
|
||||
import { queryClient, QUERY_KEYS_OPERATIONS_HISTORY, type RublePaymentTemplateEdit } from '@msb/http';
|
||||
import { queryClient, QUERY_KEYS_OPERATIONS_HISTORY, usePaymentsI18n } from '@msb/http';
|
||||
import {
|
||||
AUTHORITIES,
|
||||
BaseDialog,
|
||||
@@ -24,7 +24,15 @@ import { useLocation } from 'react-router-dom';
|
||||
import { LOCALIZATION } from '../constants';
|
||||
import { PaymentOrderFormSaveSign } from '../lib';
|
||||
import * as S from './PaymentOrderForm.styles';
|
||||
import { PayeeFormFields, PayerFormFields, FormValidation, PaymentFormFields, isSignAndSendAllowedStatus } from '@/features/Forms';
|
||||
import {
|
||||
PayeeFormFields,
|
||||
PayerFormFields,
|
||||
FormValidation,
|
||||
PaymentFormFields,
|
||||
isSignAndSendAllowedStatus,
|
||||
UnknownBackendControlsSheet,
|
||||
} from '@/features/Forms';
|
||||
import { setPaymentsI18nData } from '@/features/Forms/lib/paymentsI18nStore';
|
||||
import { PAYMENTS_YM_EVENTS, LOCALIZATION as SIGN_LOCALIZATION } from '@/shared/constants';
|
||||
import { PageHeader } from '@/shared/ui';
|
||||
|
||||
@@ -38,6 +46,7 @@ namespace PaymentOrderForm {
|
||||
const { hash } = useLocation();
|
||||
const { showSnackbarMessage } = useSnackbar();
|
||||
const { showModal } = useModal(BaseDialog);
|
||||
const { paymentsI18nData } = usePaymentsI18n();
|
||||
const organizations = checkOrganizationsHavePermission(userAuthorities?.data.clientAuthorities || {}, [AUTHORITIES.PAYMENT.VIEW]);
|
||||
|
||||
const [currentOrg, setCurrentOrg] = useState<string>('');
|
||||
@@ -139,6 +148,10 @@ namespace PaymentOrderForm {
|
||||
FormValidation.attachMessages([]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setPaymentsI18nData(paymentsI18nData);
|
||||
}, [paymentsI18nData]);
|
||||
|
||||
const handleSubmit = () => {};
|
||||
|
||||
return (
|
||||
@@ -180,8 +193,9 @@ namespace PaymentOrderForm {
|
||||
formValidation={FormValidation}
|
||||
orgHandleChange={orgHandleChange}
|
||||
/>
|
||||
<UnknownBackendControlsSheet />
|
||||
|
||||
</ScrollContainer>
|
||||
|
||||
<S.ButtonsWrapper>
|
||||
{canShowByAuthorities && isStatusOk && (
|
||||
<Button
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { type ReactElement, useCallback, useRef, useState } from 'react';
|
||||
import { type ReactElement, useCallback, useRef, useState, useEffect } from 'react';
|
||||
import { Input } from '@fractal-ui/composites';
|
||||
import { Button, ScrollContainer } from '@fractal-ui/core';
|
||||
import { ErrorIcon, OkIcon } from '@fractal-ui/library';
|
||||
import { useSnackbar } from '@fractal-ui/overlays';
|
||||
import { type RublePaymentTemplateInfo } from '@msb/http';
|
||||
import { type RublePaymentTemplateInfo, usePaymentsI18n } from '@msb/http';
|
||||
import { handleReachGoal, useExtendedNavigation, useHashedRedirect, YM_GOALS } from '@msb/shared';
|
||||
import { Form } from 'react-final-form';
|
||||
import { LOCALIZATION, FIELD_IDENTITY } from '../constants';
|
||||
import * as S from './TemplateForm.styles';
|
||||
import { PayeeFormFields, PayerFormFields, FormValidation, PaymentFormFields } from '@/features/Forms';
|
||||
import { PayeeFormFields, PayerFormFields, FormValidation, PaymentFormFields, UnknownBackendControlsSheet } from '@/features/Forms';
|
||||
import { setPaymentsI18nData } from '@/features/Forms/lib/paymentsI18nStore';
|
||||
import { TemplateFormFields } from '@/features/TemplateForm/lib';
|
||||
import { PATHS, PAYMENTS_YM_EVENTS } from '@/shared/constants';
|
||||
import { PageHeader } from '@/shared/ui';
|
||||
@@ -21,6 +22,7 @@ namespace TemplateForm {
|
||||
|
||||
export const Element = ({ organizationId, raw }: Props): ReactElement => {
|
||||
const { showSnackbarMessage } = useSnackbar();
|
||||
const { paymentsI18nData } = usePaymentsI18n();
|
||||
const snackbarCallback = useCallback(
|
||||
(type: TemplateFormFields.TOAST_TYPE) => {
|
||||
let message = '';
|
||||
@@ -91,6 +93,10 @@ namespace TemplateForm {
|
||||
const isAllowCreatePayment =
|
||||
formFields.templateName.length > 0 && (formFields.payerFieldsOutput?.payerDetails?.accountNumber.length || 0) > 0;
|
||||
|
||||
useEffect(() => {
|
||||
setPaymentsI18nData(paymentsI18nData);
|
||||
}, [paymentsI18nData]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
render={({ form }) => {
|
||||
@@ -121,8 +127,9 @@ namespace TemplateForm {
|
||||
formValidation={FormValidation}
|
||||
orgHandleChange={orgChangeHandler}
|
||||
/>
|
||||
<UnknownBackendControlsSheet />
|
||||
|
||||
</ScrollContainer>
|
||||
|
||||
<S.ButtonsWrapper>
|
||||
{isAllowCreatePayment && (
|
||||
<Button dataAction="Save and create payment" size="M" onClick={createPaymentHandler}>
|
||||
|
||||
Reference in New Issue
Block a user