Pull request #2467: Release 1.10.0
Merge in MCB_FE/mcb-platform-monorepo from release-1.10.0 to master * commit 'b0e7c45c453739767b7cbbc2bca5b4a87d4f6de7': (141 commits) chore(app): 1.10.0 feat(TEAMMSBMOB-22759): изменение цвета текста feat(TEAMMSBMOB-22759): рекламная капания 5 fix(TEAMMSBMOB-21989): Исправление отображения кнопки подписать fix(TEAMMSBMOB-21989): исправление заголовка feat(TEAMMSBMOB-22193): фикс бэк контроля кпп 3-го лица feat(TEAMMSBMOB-22812): фикс модального окна об успешном создании заявки нэп bugfix(TEAMMSBMOB-10267): убрал иконку валюты из чипсов + сброс фильтров после перехода со страницы "мои продукты" feat(TEAMMSBMOB-22193): фикс кпп и Кода там.органа fix(TEAMMSBMOB-21989): Исправление привилегии fix(TEAMMSBMOB-21989): Исправление логики поиска организации feat(TEAMMSBMOB-22193): откад логики очередй бэк контролей feat(TEAMMSBMOB-21989): Добавление заголовка к запросам fix(TEAMMSBMOB-22841): removed bad logic fix(TEAMMSBMOB-22836): change expired info fix(TEAMMSBMOB-22840): added option template bugfix(TEAMMSBMOB-10264): фикс багов по форме расширеных фильтров fix(TEAMMSBMOB-22838): фикс выбора улучшенного предложения fix(TEAMMSBMOB-22842): фикс бага года в платежах fix(TEAMMSBMOB-22609): Возврат UI выпадающего меню ...
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@msb/ib-module",
|
||||
"version": "1.9.2",
|
||||
"version": "1.10.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@msb/ib-module",
|
||||
"version": "1.9.2",
|
||||
"version": "1.10.0",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"services/*"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@msb/ib-module",
|
||||
"version": "1.9.2",
|
||||
"version": "1.10.0",
|
||||
"files": [
|
||||
"msb-host",
|
||||
"msb-main-page",
|
||||
|
||||
@@ -10,7 +10,7 @@ const Weeks: React.FC = () => {
|
||||
const { weekDays } = useContext(CalendarContext);
|
||||
|
||||
return (
|
||||
<WeekContainer data-testid="calendarWeek" mx={{ XS: 'calendar.mobileHeaderPx', S: '-2px' }} px={{ XS: 'calendar.XS', S: '0' }}>
|
||||
<WeekContainer data-testid="calendarWeek" mx={{ XS: 'calendar.mobileHeaderPx', S: '20px' }} px={{ XS: 'calendar.XS', S: '0' }}>
|
||||
{weekDays.map(weekDay => (
|
||||
<Text.P3
|
||||
key={weekDay}
|
||||
|
||||
@@ -255,7 +255,6 @@ export const WeekContainer = styled.div<SpaceProps>(
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(7,1fr)',
|
||||
justifyContent: 'start',
|
||||
width: '250px',
|
||||
marginBottom: calendarM,
|
||||
userSelect: 'none',
|
||||
borderColor: 'control.secondary.grey.bg',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { Theme } from '@emotion/react';
|
||||
import { BottomSheet, PopupContainer } from '@fractal-ui/overlays';
|
||||
import { BottomSheet, Drawer, PopupContainer } from '@fractal-ui/overlays';
|
||||
import { BreakPoint, Responsive, useBreakpoints, Wrapper } from '@fractal-ui/styling';
|
||||
import type { StyledComponentProps } from '@fractal-ui/styling';
|
||||
import Dropdown from '../../dropdown';
|
||||
@@ -83,6 +83,7 @@ const withOptions = <T extends BaseWithOptionsProps>(WrappedComponent: React.For
|
||||
onBeforeClose,
|
||||
onBeforeOpen,
|
||||
notFoundButtonVariant = 'primary',
|
||||
showDrawerForBreakPoint = false,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
@@ -96,7 +97,7 @@ const withOptions = <T extends BaseWithOptionsProps>(WrappedComponent: React.For
|
||||
const [isOpened, setIsOpened] = useState<boolean>(false);
|
||||
const [searchInputValue, setSearchInputValue] = useState<number | string | undefined>();
|
||||
|
||||
const { XS } = useBreakpoints();
|
||||
const { XS, S } = useBreakpoints();
|
||||
|
||||
const isShowButtonPositionTop = Boolean(onInnerButtonClick) && innerButtonText && innerButtonPosition === 'top';
|
||||
|
||||
@@ -341,7 +342,7 @@ const withOptions = <T extends BaseWithOptionsProps>(WrappedComponent: React.For
|
||||
innerButtonIcon={innerButtonIcon}
|
||||
innerButtonPosition={innerButtonPosition}
|
||||
innerButtonText={innerButtonText}
|
||||
isEmbedded={XS}
|
||||
isEmbedded={showDrawerForBreakPoint ? S : XS}
|
||||
isInnerButtonFixed={isInnerButtonFixed}
|
||||
notFoundButtonText={notFoundButtonText}
|
||||
notFoundButtonVariant={notFoundButtonVariant}
|
||||
@@ -407,17 +408,35 @@ const withOptions = <T extends BaseWithOptionsProps>(WrappedComponent: React.For
|
||||
<WrappedComponent {...(wrappedProps as T)} ref={mergeRefs(ref, inputRef)} />
|
||||
<Responsive>
|
||||
<BreakPoint>
|
||||
<PopupContainer
|
||||
closeOnEscape
|
||||
anchorEl={containerRef}
|
||||
isOpen={isOpened}
|
||||
onAfterClose={onAfterCloseDropdown}
|
||||
onBeforeClose={onBeforeClose}
|
||||
onBeforeOpen={onBeforeOpen}
|
||||
onClose={handleClose}
|
||||
>
|
||||
{dropdown}
|
||||
</PopupContainer>
|
||||
{showDrawerForBreakPoint ? (
|
||||
<Drawer
|
||||
alwaysMaxHeight={alwaysMaxHeight}
|
||||
// bottomSheetRef={bottomSheetRef}
|
||||
// disableFooterPadding={isShowButtonPositionBottom}
|
||||
footer={footer}
|
||||
header={header || rest.label}
|
||||
isOpen={isOpened}
|
||||
unscrollableContent={unscrollableContent}
|
||||
onAfterClose={onAfterCloseDropdown}
|
||||
onBeforeClose={onBeforeClose}
|
||||
onBeforeOpen={onBeforeOpen}
|
||||
onClose={handleClose}
|
||||
>
|
||||
{dropdown}
|
||||
</Drawer>
|
||||
) : (
|
||||
<PopupContainer
|
||||
closeOnEscape
|
||||
anchorEl={containerRef}
|
||||
isOpen={isOpened}
|
||||
onAfterClose={onAfterCloseDropdown}
|
||||
onBeforeClose={onBeforeClose}
|
||||
onBeforeOpen={onBeforeOpen}
|
||||
onClose={handleClose}
|
||||
>
|
||||
{dropdown}
|
||||
</PopupContainer>
|
||||
)}
|
||||
</BreakPoint>
|
||||
<BreakPoint at="XS">
|
||||
<BottomSheet
|
||||
|
||||
@@ -160,6 +160,13 @@ export interface WithOptionsProps<T = Record<string, unknown>>
|
||||
* @default false
|
||||
*/
|
||||
hasFocusOnBottomSheetSearch?: boolean;
|
||||
/**
|
||||
* Параметр отвечающий за то, какой вид выбора опций будет у селектора в дефолтном BreakPoint.
|
||||
* Необходим, т.к. сетка брекпоинтов фрактала отличается от наших размеров.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
showDrawerForBreakPoint?: boolean;
|
||||
}
|
||||
|
||||
/** Контекст хока withOptions. */
|
||||
|
||||
@@ -8,6 +8,8 @@ export const getBusinessCardsGetPage = async (
|
||||
): Promise<BusinessCardsGetPageResponseDto> => {
|
||||
const response = await network.client.post<BusinessCardsGetPageResponseDto>(BUSINESS_CARDS_GET_PAGE_ENDPOINT, data, {
|
||||
headers: { Organizationid: organizationId },
|
||||
// На eco-test2 запрос проходит почни минуту (49.88с)
|
||||
timeout: 60_000,
|
||||
});
|
||||
|
||||
return response.data;
|
||||
|
||||
@@ -40,12 +40,14 @@ enum FEATURE_TOGGLE_NAMES {
|
||||
LOCAL_QUALIFIED_SIGN_IBMSB = 'localQualifiedSignIBMSB',
|
||||
IMPORT_PAYMENTS = 'ImportPaymentsIBMSB',
|
||||
FEA_OPERATION_DAY = 'vedOperationDayInformIBMSB',
|
||||
CURRENCY_TRANSFER_LIST = 'currencyTransferIBMSB',
|
||||
MAIN_PAGE_DYNAMIC_BANNERS = 'mainpageDinamicBannersIBMSB',
|
||||
SENTRY = 'monitoringIBMSB',
|
||||
YM_USER_ID = 'metricaSetIdIBMSB',
|
||||
STAR_RATING = 'starRatingIBMSB',
|
||||
DEPOSIT_FORM = 'depositFormIBMSB',
|
||||
DEPOSIT_ADVANCED_FILTER = 'depositFilterIBMSB',
|
||||
VED_CALLBACK_IMSB = 'vedCallbackIBMSB',
|
||||
}
|
||||
|
||||
export { FEATURE_TOGGLE_NAMES, type FeatureToggleData, type FeatureToggleItem, type FeatureToggleResponse };
|
||||
|
||||
@@ -240,6 +240,8 @@ interface OtherInfo {
|
||||
filialAbsCode?: string;
|
||||
/** Адрес филиала в АБС Ф1. */
|
||||
filialAbsAddress?: string;
|
||||
/** Наименование подразделения. */
|
||||
filialName?: string;
|
||||
}
|
||||
|
||||
interface InquiryRequestDto {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type FilterResponseDto, GET_INVESTMENT_DOCUMENT_FILTERS, network } from '@msb/http';
|
||||
import type { FilterByStatusResponseDto, FilterResponseDto } from '@msb/http';
|
||||
import { GET_INVESTMENT_DOCUMENT_FILTERS, network, POST_INVESTMENT_DOCUMENT_CATEGORIES_BY_FILTER } from '@msb/http';
|
||||
|
||||
const fetchFilters = async (): Promise<FilterResponseDto> => {
|
||||
const response = await network.client.get<FilterResponseDto>(GET_INVESTMENT_DOCUMENT_FILTERS);
|
||||
@@ -6,4 +7,10 @@ const fetchFilters = async (): Promise<FilterResponseDto> => {
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export { fetchFilters };
|
||||
const fetchFiltersByStatus = async (): Promise<FilterByStatusResponseDto> => {
|
||||
const response = await network.client.post<FilterByStatusResponseDto>(POST_INVESTMENT_DOCUMENT_CATEGORIES_BY_FILTER);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export { fetchFilters, fetchFiltersByStatus };
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const QUERY_KEY_FILTERS = 'DOCUMENTS_FILTERS';
|
||||
|
||||
export { QUERY_KEY_FILTERS };
|
||||
const QUERY_KEY_FILTERS_BY_STATUS = 'INVESTMENT_DOCUMENT_FILTERS_BY_STATUS';
|
||||
|
||||
export { QUERY_KEY_FILTERS, QUERY_KEY_FILTERS_BY_STATUS };
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
const statusFilters = [
|
||||
{ label: 'Черновик', statuses: ['CAT_NEW', 'CAT_DRAFT', 'CAT_QUOTED'], code: 'DRAFT' },
|
||||
{ label: 'Требует подтверждения', statuses: ['CAT_NEW', 'CAT_QUOTED'], code: 'QUOTED' },
|
||||
{ label: 'Закрыт досрочно', statuses: ['CAT_WITHDRAWN'], code: 'WITHDRAWN' },
|
||||
{ label: 'Закрыт', statuses: ['CAT_EXECUTED'], code: 'EXECUTED' },
|
||||
{ label: 'Нарушен', statuses: ['CAT_VIOLATED'], code: 'VIOLATED' },
|
||||
{ label: 'Отменён', statuses: ['CAT_CANCELED', 'CAT_VISA_REJECTED'], code: 'CANCELED' },
|
||||
{ label: 'Открыт', statuses: ['CAT_DONE'], code: 'ACTIVE' },
|
||||
{ label: 'В обработке', statuses: ['CAT_DELIVERED', 'CAT_WAITING_FOR_VISA', 'CAT_CONFIRMED'], code: 'CONFIRMED' },
|
||||
];
|
||||
|
||||
export { statusFilters };
|
||||
@@ -0,0 +1 @@
|
||||
export * from './constants';
|
||||
@@ -1,3 +1,5 @@
|
||||
const GET_INVESTMENT_DOCUMENT_FILTERS = '/treasury-deals-client/investment-document/filters' as const;
|
||||
|
||||
export { GET_INVESTMENT_DOCUMENT_FILTERS };
|
||||
const POST_INVESTMENT_DOCUMENT_CATEGORIES_BY_FILTER = '/treasury-deals-client/investment-document/categories-by-filter' as const;
|
||||
|
||||
export { GET_INVESTMENT_DOCUMENT_FILTERS, POST_INVESTMENT_DOCUMENT_CATEGORIES_BY_FILTER };
|
||||
|
||||
@@ -15,4 +15,13 @@ interface FilterResponseDto {
|
||||
traceId: string;
|
||||
}
|
||||
|
||||
export type { FilterResponseDto, DataFilters, ErrorResponse };
|
||||
interface FilterByStatusDto {
|
||||
code: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
interface FilterByStatusResponseDto {
|
||||
data: FilterByStatusDto[];
|
||||
}
|
||||
|
||||
export type { FilterResponseDto, DataFilters, ErrorResponse, FilterByStatusResponseDto };
|
||||
|
||||
@@ -26,7 +26,7 @@ interface TreasuryDealsCurrenciesResponseDto {
|
||||
|
||||
interface CurrenciesRequestBody {
|
||||
data: {
|
||||
clientWebDealingId?: string;
|
||||
clientWebDealingId: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './endpoints';
|
||||
export * from './model';
|
||||
export * from './api';
|
||||
export * from './constants';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from './useProducts';
|
||||
export { useFetchedFilters } from './useFilters';
|
||||
export { useFetchedFilters, useFiltersByStatus } from './useFilters';
|
||||
export { useUserInfoUnion } from './useUserInfoUnion';
|
||||
export { useProductsMainPage } from './useProductsMainPage';
|
||||
export { useValidate } from './useValidate';
|
||||
@@ -9,4 +9,4 @@ export { useWorkCalendar } from './workCalendar';
|
||||
export * from './showcase';
|
||||
export * from './calculator';
|
||||
export { useRubAccountNew } from './useRubAccountNew';
|
||||
export { useAccountById } from './useAccountById';
|
||||
export { useAccountById } from './useAccountById';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery, type FilterResponseDto } from '@msb/http';
|
||||
import { fetchFilters, QUERY_KEY_FILTERS } from '../../api';
|
||||
import type { FilterByStatusResponseDto, FilterResponseDto } from '@msb/http';
|
||||
import { useQuery, QUERY_KEY_FILTERS_BY_STATUS, fetchFilters, QUERY_KEY_FILTERS, fetchFiltersByStatus } from '@msb/http';
|
||||
|
||||
const useFetchedFilters = () => {
|
||||
const { data: fetchedFilters, isLoading: isLoadingFilters } = useQuery<FilterResponseDto, Error | undefined>({
|
||||
@@ -10,4 +10,13 @@ const useFetchedFilters = () => {
|
||||
return { fetchedFilters, isLoadingFilters };
|
||||
};
|
||||
|
||||
export { useFetchedFilters };
|
||||
const useFiltersByStatus = () => {
|
||||
const { data: filtersByStatus, isLoading: isLoadingFiltersByStatus } = useQuery<FilterByStatusResponseDto, Error | undefined>({
|
||||
queryKey: [QUERY_KEY_FILTERS_BY_STATUS],
|
||||
queryFn: fetchFiltersByStatus,
|
||||
});
|
||||
|
||||
return { filtersByStatus, isLoadingFiltersByStatus };
|
||||
};
|
||||
|
||||
export { useFetchedFilters, useFiltersByStatus };
|
||||
|
||||
@@ -3,7 +3,14 @@ type TreasuryDealsFilter = 'fastFilter' | 'organization' | 'product';
|
||||
interface AdvancedFilterState {
|
||||
amountFrom: string;
|
||||
amountTo: string;
|
||||
termFrom: string;
|
||||
termTo: string;
|
||||
dateBeginStart: string;
|
||||
dateBeginEnd: string;
|
||||
dateEndStart: string;
|
||||
dateEndEnd: string;
|
||||
currency: string[];
|
||||
status: string[];
|
||||
}
|
||||
|
||||
type TreasuryDealsFilters = Record<TreasuryDealsFilter, string[]>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import type { AdvancedFilterState, ProductsResponseDto } from '@msb/http';
|
||||
import { QUERY_KEY_FETCH_PRODUCTS_SINGLE_TYPE, useInfiniteQuery } from '@msb/http';
|
||||
import { QUERY_KEY_FETCH_PRODUCTS_SINGLE_TYPE, useInfiniteQuery, statusFilters } from '@msb/http';
|
||||
import {
|
||||
fetchProducts,
|
||||
QUERY_KEY_FETCH_PRODUCTS,
|
||||
@@ -54,6 +54,35 @@ const useProducts = (filters: TreasuryDealsFilters, advancedFilters?: AdvancedFi
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
params: {
|
||||
filter,
|
||||
paging: { offset: 0, limit: LIMIT },
|
||||
},
|
||||
};
|
||||
}, [filters, fetchedFilters]);
|
||||
|
||||
const advancedResultFilters = useMemo(() => {
|
||||
const filter: Record<string, any> = {};
|
||||
|
||||
if (filters.organization.length > 0) {
|
||||
filter.clientId = { in: filters.organization };
|
||||
}
|
||||
|
||||
if (filters.product.length > 0) {
|
||||
filter.docType = { in: filters.product };
|
||||
}
|
||||
|
||||
if (filters.fastFilter.length > 0 && fetchedFilters) {
|
||||
filters.fastFilter.forEach(code => {
|
||||
const fetched = fetchedFilters.data.find(f => f.code === code);
|
||||
|
||||
if (fetched) {
|
||||
Object.assign(filter, fetched.filter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (advancedFilters) {
|
||||
if (advancedFilters.amountFrom) {
|
||||
filter.amount = { ge: advancedFilters.amountFrom };
|
||||
@@ -63,9 +92,39 @@ const useProducts = (filters: TreasuryDealsFilters, advancedFilters?: AdvancedFi
|
||||
filter.amount = { ...filter.amount, le: advancedFilters.amountTo };
|
||||
}
|
||||
|
||||
if (advancedFilters.termFrom) {
|
||||
filter.period = { ge: Number(advancedFilters.termFrom) };
|
||||
}
|
||||
|
||||
if (advancedFilters.termTo) {
|
||||
filter.period = { ...filter.period, le: Number(advancedFilters.termTo) };
|
||||
}
|
||||
|
||||
if (advancedFilters?.currency?.length > 0) {
|
||||
filter.currency = { in: advancedFilters.currency };
|
||||
}
|
||||
|
||||
if (advancedFilters?.status?.length > 0) {
|
||||
const statuses = advancedFilters.status.flatMap(code => statusFilters.find(el => el.code === code)?.statuses || []);
|
||||
|
||||
filter.statusCategory = { in: statuses };
|
||||
}
|
||||
|
||||
if (advancedFilters?.dateBeginStart) {
|
||||
filter.dateBegin = { ge: advancedFilters.dateBeginStart };
|
||||
}
|
||||
|
||||
if (advancedFilters?.dateBeginEnd) {
|
||||
filter.dateBegin = { ...filter.dateBegin, le: advancedFilters.dateBeginEnd };
|
||||
}
|
||||
|
||||
if (advancedFilters?.dateEndStart) {
|
||||
filter.dateEnd = { ge: advancedFilters.dateEndStart };
|
||||
}
|
||||
|
||||
if (advancedFilters?.dateEndEnd) {
|
||||
filter.dateEnd = { ...filter.dateEnd, le: advancedFilters.dateEndEnd };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -74,9 +133,10 @@ const useProducts = (filters: TreasuryDealsFilters, advancedFilters?: AdvancedFi
|
||||
paging: { offset: 0, limit: LIMIT },
|
||||
},
|
||||
};
|
||||
}, [filters, fetchedFilters, advancedFilters]);
|
||||
}, [advancedFilters, fetchedFilters, filters]);
|
||||
|
||||
const filtersKey = useMemo(() => JSON.stringify({ ...filters, ...(advancedFilters ?? {}) }), [advancedFilters, filters]);
|
||||
const filtersKey = useMemo(() => JSON.stringify({ ...filters }), [filters]);
|
||||
const advancedFiltersKey = useMemo(() => JSON.stringify({ ...filters, ...(advancedFilters ?? {}) }), [advancedFilters, filters]);
|
||||
|
||||
// =========================== ALL products data fetching and parsing ========================= //
|
||||
const {
|
||||
@@ -86,13 +146,13 @@ const useProducts = (filters: TreasuryDealsFilters, advancedFilters?: AdvancedFi
|
||||
hasNextPage: hasAllProductsDataNextPage,
|
||||
fetchNextPage: allProductsDataFetchNextPage,
|
||||
} = useInfiniteQuery<ProductsResponseDto, Error | undefined>(
|
||||
[QUERY_KEY_FETCH_PRODUCTS, filtersKey],
|
||||
[QUERY_KEY_FETCH_PRODUCTS, advancedFiltersKey],
|
||||
({
|
||||
pageParam = {
|
||||
offset: 0,
|
||||
limit: LIMIT,
|
||||
},
|
||||
}) => fetchProducts({ ...resultFilters, params: { ...resultFilters.params, paging: pageParam } }),
|
||||
}) => fetchProducts({ ...advancedResultFilters, params: { ...advancedResultFilters.params, paging: pageParam } }),
|
||||
{
|
||||
cacheTime: 0,
|
||||
getNextPageParam: getNextPageParamCommon,
|
||||
|
||||
@@ -68,7 +68,7 @@ const BUSINESS_CARDS_ACCOUNTS_MOCK = {
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: '7i98i769-fjjf-9ij1-g7f6-15jf42g473gf',
|
||||
accountId: '7a98a769-faaf-9ad1-a7a6-15af42a473af',
|
||||
number: '40702810670000005678',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '1',
|
||||
@@ -77,6 +77,127 @@ const BUSINESS_CARDS_ACCOUNTS_MOCK = {
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: '8b09b87a-abbb-0be2-b8b7-26ba53b584b0',
|
||||
number: '40702810770000006789',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '2',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 1_200_000.50,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: '9c1ac98b-bccc-1cf3-c9c8-37cb64c695c1',
|
||||
number: '40702810870000007890',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '1',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 4_500_000.75,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: 'ad2bda9c-cddd-2da4-dada-48dc75d7a6d2',
|
||||
number: '40702810970000008901',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '2',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 850_000.25,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: 'be3cebad-deee-3eb5-ebeb-59ed86e8b7e3',
|
||||
number: '40702811070000009012',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '1',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 2_750_000.00,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: 'cf4dfcbe-efff-4fc6-fcfc-6afe97f9c8f4',
|
||||
number: '40702811170000000123',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '2',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 1_950_000.50,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: 'd05e0dcf-f000-50d7-0d0d-7b0fa80ad905',
|
||||
number: '40702811270000001234',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '1',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 5_200_000.75,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: 'e16f1ed0-0111-61e8-1e1e-8c10b91bea16',
|
||||
number: '40702811370000002345',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '2',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 650_000.25,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: 'f27a2fe1-1222-72f9-2f2f-9d21ca2cfb27',
|
||||
number: '40702811470000003456',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '1',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 3_850_000.00,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: '038b3af2-2333-83a0-3a3a-ae32db3d0c38',
|
||||
number: '40702811570000004567',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '2',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 1_350_000.50,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: '149c4ba3-3444-94b1-4b4b-bf43ec4e1d49',
|
||||
number: '40702811670000005678',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '1',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 6_100_000.75,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
{
|
||||
clientId: 'c060253d-5195-4ee4-8380-37f3f9b1f4db',
|
||||
accountId: '25ad5cb4-4555-a5c2-5c5c-c054fd5f2e5a',
|
||||
number: '40702811770000006789',
|
||||
type: 'Расчетный',
|
||||
typeContrAccount: '2',
|
||||
currencyCode: 'RUB',
|
||||
restAmount: 950_000.25,
|
||||
serviceBranchCode: '000',
|
||||
serviceOfficeCode: '001-001-099-009',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,17 @@ const isAccessTest = false;
|
||||
const getBusinessCardByIdHandler = rest.get<never, never, BusinessCardsGetByIdResponseDto>(
|
||||
BUSINESS_CARDS_GET_BY_ID_ENDPOINT,
|
||||
async (req, res, ctx) => {
|
||||
const clientId = req.url.searchParams.get('clientId');
|
||||
|
||||
let result = BUSINESS_CARDS_GET_BY_ID_MOCK;
|
||||
|
||||
if (!clientId) {
|
||||
// Бэк фактически возвращает без поля "data".
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return res(ctx.delay(1000), ctx.status(400), ctx.json({}));
|
||||
}
|
||||
|
||||
if (isIntegrationError || isAccessTest) {
|
||||
// Бэк фактически возвращает без поля "data".
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
@@ -20,9 +29,15 @@ const getBusinessCardByIdHandler = rest.get<never, never, BusinessCardsGetByIdRe
|
||||
if (result?.error) {
|
||||
result.error.code = isAccessTest ? '403' : '0';
|
||||
}
|
||||
|
||||
return res(
|
||||
ctx.delay(1000),
|
||||
ctx.status(403),
|
||||
ctx.json({ error: { code: '403', message: 'Ошибка' } } as BusinessCardsGetByIdResponseDto)
|
||||
);
|
||||
}
|
||||
|
||||
return res(ctx.delay(1000), ctx.status(200), ctx.json(result));
|
||||
return res(ctx.delay(1000), ctx.status(isAccessTest ? 403 : 200), ctx.json(result));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@ const BUSINESS_CARDS_GET_BY_ID_MOCK: BusinessCardsGetByIdResponseDto = {
|
||||
|
||||
const BUSINESS_CARDS_ERROR = {
|
||||
error: {
|
||||
code: '0',
|
||||
code: '403',
|
||||
description: 'description',
|
||||
logLevel: 'ERROR',
|
||||
message: 'message',
|
||||
|
||||
@@ -74,6 +74,14 @@ const FEATURE_TOGGLE_MOCK: FeatureToggleResponse = {
|
||||
featureCode: FEATURE_TOGGLE_NAMES.DEPOSIT_ADVANCED_FILTER,
|
||||
isEnabled: true,
|
||||
},
|
||||
{
|
||||
featureCode: FEATURE_TOGGLE_NAMES.VED_CALLBACK_IMSB,
|
||||
isEnabled: true,
|
||||
},
|
||||
{
|
||||
featureCode: FEATURE_TOGGLE_NAMES.CURRENCY_TRANSFER_LIST,
|
||||
isEnabled: true,
|
||||
},
|
||||
{
|
||||
featureCode: FEATURE_TOGGLE_NAMES.STAR_RATING,
|
||||
isEnabled: true,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -800,6 +800,7 @@ const GET_INQUIRY_MOCK: InquiryRequestDto = {
|
||||
filialAbsCode: '099/1009',
|
||||
filialAbsAddress: 'ул Тольятти, д. 33А',
|
||||
receptionEmailAgreement: false,
|
||||
filialName: 'Ф-Л БАНКА ГПБ (АО) "ЗАПАДНО-СИБИРСКИЙ"',
|
||||
},
|
||||
inquiryVersion: 2,
|
||||
};
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1163,7 +1163,7 @@ const STATEMENTS_REQUESTS_HISTORY_MOCK: StatementRequest[] = [
|
||||
|
||||
const STATEMENT_RELEVANCE_STATUS_MOCK = {
|
||||
data: {
|
||||
status: STATEMENT_RELEVANCE_STATUS.ACTUAL,
|
||||
status: STATEMENT_RELEVANCE_STATUS.OUTDATED,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { type FilterResponseDto, GET_INVESTMENT_DOCUMENT_FILTERS } from '@msb/http/treasury-deals-client/endpoints';
|
||||
import { type FilterResponseDto, GET_INVESTMENT_DOCUMENT_FILTERS, POST_INVESTMENT_DOCUMENT_CATEGORIES_BY_FILTER } from '@msb/http';
|
||||
import type { FilterByStatusResponseDto } from '@msb/http/treasury-deals-client/endpoints';
|
||||
import { rest } from 'msw';
|
||||
import { FILTERS_MOCK } from './mocks';
|
||||
import { FILTER_BY_STATUS_MOCK, FILTERS_MOCK } from './mocks';
|
||||
|
||||
const GET_FILTERS_HANDLER = rest.get<never, never, FilterResponseDto>(GET_INVESTMENT_DOCUMENT_FILTERS, (_, res, ctx) =>
|
||||
res(ctx.json(FILTERS_MOCK))
|
||||
);
|
||||
|
||||
export { GET_FILTERS_HANDLER };
|
||||
const GET_FILTERS_BY_STATUS_HANDLER = rest.post<never, never, FilterByStatusResponseDto>(
|
||||
POST_INVESTMENT_DOCUMENT_CATEGORIES_BY_FILTER,
|
||||
(_, res, ctx) => res(ctx.json(FILTER_BY_STATUS_MOCK))
|
||||
);
|
||||
|
||||
export { GET_FILTERS_HANDLER, GET_FILTERS_BY_STATUS_HANDLER };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_FILTERS_HANDLER } from './filters';
|
||||
import { GET_FILTERS_HANDLER, GET_FILTERS_BY_STATUS_HANDLER } from './filters';
|
||||
|
||||
const filterHandlers = [GET_FILTERS_HANDLER];
|
||||
const filterHandlers = [GET_FILTERS_HANDLER, GET_FILTERS_BY_STATUS_HANDLER];
|
||||
|
||||
export { filterHandlers };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import type { FilterResponseDto } from '@msb/http/treasury-deals-client/endpoints';
|
||||
import type { FilterByStatusResponseDto, FilterResponseDto } from '@msb/http/treasury-deals-client/endpoints';
|
||||
|
||||
const FILTERS_MOCK: FilterResponseDto = {
|
||||
data: [
|
||||
@@ -112,4 +112,53 @@ const FILTERS_MOCK: FilterResponseDto = {
|
||||
traceId: 'string',
|
||||
};
|
||||
|
||||
export { FILTERS_MOCK };
|
||||
const FILTER_BY_STATUS_MOCK: FilterByStatusResponseDto = {
|
||||
data: [
|
||||
{
|
||||
code: 'CAT_DRAFT',
|
||||
title: 'Черновик',
|
||||
},
|
||||
{
|
||||
code: 'CAT_NEW',
|
||||
title: 'Ожидание подтверждения',
|
||||
},
|
||||
{
|
||||
code: 'CAT_WAITING_FOR_VISA',
|
||||
title: 'Ожидает согласования спец. депозитария',
|
||||
},
|
||||
{
|
||||
code: 'CAT_VISA_REJECTED',
|
||||
title: 'Отклонена спец. депозитарием',
|
||||
},
|
||||
{
|
||||
code: 'CAT_DELIVERED',
|
||||
title: 'Подтверждено Клиентом',
|
||||
},
|
||||
{
|
||||
code: 'CAT_CANCELED',
|
||||
title: 'Отменена',
|
||||
},
|
||||
{
|
||||
code: 'CAT_CONFIRMED',
|
||||
title: 'Сделка подтверждена',
|
||||
},
|
||||
{
|
||||
code: 'CAT_DONE',
|
||||
title: 'Заключена',
|
||||
},
|
||||
{
|
||||
code: 'CAT_WITHDRAWN',
|
||||
title: 'Уведомление на досрочное истребование',
|
||||
},
|
||||
{
|
||||
code: 'CAT_VIOLATED',
|
||||
title: 'Нарушена',
|
||||
},
|
||||
{
|
||||
code: 'CAT_EXECUTED',
|
||||
title: 'Завершена',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export { FILTERS_MOCK, FILTER_BY_STATUS_MOCK };
|
||||
|
||||
@@ -31,14 +31,9 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'SWITCHING_TO_AB_PAYMENTS',
|
||||
'SWITCHING_TO_LK_UVED',
|
||||
'TREASURY_AGREEMENT_REQUEST_DEPOSIT_CREATE',
|
||||
'INQUIRY_REQUEST.VIEW',
|
||||
'INQUIRY.COPY_DOC',
|
||||
'TREASURY_COMMON',
|
||||
'TREASURY_GENERAL_AGREEMENTS_COMMON',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY_REQUEST.UPDATE',
|
||||
'INQUIRY_REQUEST.SIGN',
|
||||
'INQUIRY.EXPORT_DOC',
|
||||
'INQUIRY_REQUEST.CREATE_CLIENT_CLIENT',
|
||||
'CLIENT_HOME_PAGE_ACCOUNT_VIEW',
|
||||
'TREASURY_ACCOUNTS_COMMON',
|
||||
'TREASURY_DEPOSIT_DEALS_COMMON',
|
||||
@@ -56,6 +51,10 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
clientAuthorities: {
|
||||
'3fa85f64-5717-4562-b3fc-2c963f66afa2': ['ECO_PARTNER_CHECK_CLIENT.VIEW_USE_SERVICE_PAGE'],
|
||||
'64b3578f-eaa2-4367-90f2-98965b589262': [
|
||||
'INQUIRY.CLIENT.LIST',
|
||||
'PRODUCT_OFFER.CLIENT.CONSULTATION_REQUEST',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'ECO_PARTNER_CHECK_CLIENT.VIEW_MANAGE_SERVICE_PAGE',
|
||||
'CURRENCY_TRANSFER_CLIENT_SCROLLER_VIEW',
|
||||
'TAD.CLIENT.RQ_VIEW_SC',
|
||||
'CONV.CLIENT.RQ_VIEW_SC',
|
||||
@@ -76,7 +75,7 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'LKMP.ACCESS.VIEW',
|
||||
'LKMP.OFFER.VIEW',
|
||||
'LKUVED.VIEW',
|
||||
'INQUIRY_REQUEST.VIEW',
|
||||
'INQUIRY.CLIENT.VIEW',
|
||||
'LKUVED.ACCESS.VIEW',
|
||||
'OFFER_ACCEPTANCE.VIEW',
|
||||
'TREASURY_AGREEMENT_REQUEST_MNO_VIEW',
|
||||
@@ -90,7 +89,7 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'TREASURY_AGREEMENT_REQUEST_DU_MNO_VIEW',
|
||||
'TREASURY_AGREEMENT_REQUEST_DU_CONVERSION_VIEW',
|
||||
'DIGITAL_SIGNATURE_TOOL_VIEW',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY_REQUEST.CREATE_CLIENT_CLIENT',
|
||||
'VRKO_CONV',
|
||||
'VRKO_VP',
|
||||
'RRKO_PPTS',
|
||||
@@ -110,15 +109,14 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'TMG.ACCESS.VIEW',
|
||||
'TMG.OFFER.VIEW',
|
||||
'CONSTITUTION_REGISTRATION_CLIENT.VIEW_LIST',
|
||||
'ECO_PARTNER_CHECK_CLIENT.VIEW_MANAGE_SERVICE_PAGE',
|
||||
'ECO_PARTNER_CHECK_CLIENT.VIEW_USE_SERVICE_PAGE',
|
||||
'ECO_PARTNER_CHECK_CLIENT.VIEW_CONNECTION_SERVICE_PAGE',
|
||||
'ECO_PARTNER_CHECK_CLIENT.SUBSCRIPTION_SIGN',
|
||||
'SBP.CONTRACT.REQUEST.VIEW.LIST',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'CLIENT_HOME_PAGE_ACCOUNT_VIEW',
|
||||
'INQUIRY_REQUEST.SIGN',
|
||||
'INQUIRY.EXPORT_DOC',
|
||||
'INQUIRY.CLIENT.SIGN',
|
||||
'INQUIRY.CLIENT.EXPORT_DOC',
|
||||
'RUBLE_PAYMENT_ORDER.SEND',
|
||||
'RUBLE_PAYMENT_ORDER.SIGN',
|
||||
'ACCOUNT_OPENING_REQUEST.CLIENT.CONTRACT_CREATE',
|
||||
@@ -155,7 +153,7 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'LKMP.ACCESS.VIEW',
|
||||
'LKMP.OFFER.VIEW',
|
||||
'LKUVED.VIEW',
|
||||
'INQUIRY_REQUEST.VIEW',
|
||||
'INQUIRY.CLIENT.VIEW',
|
||||
'LKUVED.ACCESS.VIEW',
|
||||
'OFFER_ACCEPTANCE.VIEW',
|
||||
'TREASURY_AGREEMENT_REQUEST_MNO_VIEW',
|
||||
@@ -169,9 +167,9 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'TREASURY_AGREEMENT_REQUEST_DU_MNO_VIEW',
|
||||
'TREASURY_AGREEMENT_REQUEST_DU_CONVERSION_VIEW',
|
||||
'DIGITAL_SIGNATURE_TOOL_VIEW',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY_REQUEST.SIGN',
|
||||
'INQUIRY.EXPORT_DOC',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'INQUIRY.CLIENT.SIGN',
|
||||
'INQUIRY.CLIENT.EXPORT_DOC',
|
||||
'VRKO_CONV',
|
||||
'VRKO_VP',
|
||||
'RRKO_PPTS',
|
||||
@@ -193,7 +191,7 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'CONSTITUTION_REGISTRATION_CLIENT.VIEW_LIST',
|
||||
'ECO_PARTNER_CHECK_CLIENT.VIEW_MANAGE_SERVICE_PAGE',
|
||||
'SBP.CONTRACT.REQUEST.VIEW.LIST',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'CLIENT_HOME_PAGE_ACCOUNT_VIEW',
|
||||
'BC.CLIENT.LIST.VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
@@ -205,9 +203,9 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'LKMP.ACCESS.VIEW_LIST',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
'TREASURY_MNO_DEALS_COMMON',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY_REQUEST.SIGN',
|
||||
'INQUIRY.EXPORT_DOC',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'INQUIRY.CLIENT.SIGN',
|
||||
'INQUIRY.CLIENT.EXPORT_DOC',
|
||||
'CLIENT_HOME_PAGE_ACCOUNT_VIEW',
|
||||
'CLIENT_VIEW_ACCOUNT_DETAILS',
|
||||
'RUBLE_PAYMENT_ORDER.CREATE',
|
||||
@@ -227,7 +225,7 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'CLIENT_HOME_PAGE_ACCOUNT_VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
'TREASURY_MNO_DEALS_COMMON',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'BC.CLIENT.LIST.VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
],
|
||||
@@ -238,9 +236,9 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'CLIENT_HOME_PAGE_ACCOUNT_VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
'TREASURY_MNO_DEALS_COMMON',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY_REQUEST.SIGN',
|
||||
'INQUIRY.EXPORT_DOC',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'INQUIRY.CLIENT.SIGN',
|
||||
'INQUIRY.CLIENT.EXPORT_DOC',
|
||||
'BC.CLIENT.LIST.VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
],
|
||||
@@ -251,9 +249,9 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'CLIENT_HOME_PAGE_ACCOUNT_VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
'TREASURY_MNO_DEALS_COMMON',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY_REQUEST.SIGN',
|
||||
'INQUIRY.EXPORT_DOC',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'INQUIRY.CLIENT.SIGN',
|
||||
'INQUIRY.CLIENT.EXPORT_DOC',
|
||||
'BC.CLIENT.LIST.VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
],
|
||||
@@ -264,9 +262,9 @@ const USER_AUTHORITIES_MOCK: AuthoritiesResponseDto = {
|
||||
'CLIENT_HOME_PAGE_ACCOUNT_VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
'TREASURY_MNO_DEALS_COMMON',
|
||||
'INQUIRY_REQUEST.CREATE',
|
||||
'INQUIRY_REQUEST.SIGN',
|
||||
'INQUIRY.EXPORT_DOC',
|
||||
'INQUIRY.CLIENT.CREATE',
|
||||
'INQUIRY.CLIENT.SIGN',
|
||||
'INQUIRY.CLIENT.EXPORT_DOC',
|
||||
'BC.CLIENT.LIST.VIEW',
|
||||
'BANK_CLIENT_GET_USER_ORG',
|
||||
],
|
||||
|
||||
@@ -22,9 +22,16 @@ const LOCALIZATION = {
|
||||
BUTTON_GO: 'Перейти',
|
||||
BUTTON_STAY: 'Остаться',
|
||||
OPEN_MNO_ACTION: 'Открыть неснижаемый остаток',
|
||||
FROM: 'От',
|
||||
TO: 'До',
|
||||
SELECT_FROM_THE_LIST: 'Выберите из списка',
|
||||
FIELDS: {
|
||||
AMOUNT: 'Сумма',
|
||||
CURRENCY: 'Валюта',
|
||||
STATUS: 'Статус',
|
||||
TERM: 'Срок',
|
||||
DATE_BEGIN: 'Дата открытия',
|
||||
DATE_END: 'Дата закрытия',
|
||||
},
|
||||
BLOCKING: {
|
||||
DFM_BANNER_MESSAGE:
|
||||
|
||||
@@ -36,7 +36,7 @@ const PATHS_DEPOSITS = {
|
||||
HOME: PATHS.DEPOSITS,
|
||||
PRODUCTS: `${PATHS.DEPOSITS}/products`,
|
||||
TREASURY_DEALS: `${PATHS.DEPOSITS}/treasury-deals`,
|
||||
TREAUSRY_DEALS_UNIVERSAL: `${PATHS.DEPOSITS}/treasury-deals-universal`, //TODO: временное расположение, закроется фиче тогглом
|
||||
TREAUSRY_DEALS_UNIVERSAL: `${PATHS.DEPOSITS}/treasury-deals-universal`, // TODO: временное расположение, закроется фиче тогглом
|
||||
CONFIRMATION: `${PATHS.DEPOSITS}/confirmation`,
|
||||
CANCEL_DOCUMENT: `${PATHS.DEPOSITS}/products/cancel`,
|
||||
EARLY_REFUND: `${PATHS.DEPOSITS}/products/early-refund`,
|
||||
@@ -164,6 +164,7 @@ const EXTERNAL_LINKS = {
|
||||
REGULATIONS_CERTIFICATION_CENTER: 'https://www.gazprombank.ru/corporate/settlement_cash_services/client_bank/#tab_432725',
|
||||
CRYPTO_PRO: 'https://passport.gbo.gazprombank.ru/help#cm61',
|
||||
SALARY_PROJECTS: 'https://www.gazprombank.ru/business/salary-project/#first-step',
|
||||
PAYMENTS_ABROAD: 'https://www.gazprombank.ru/special/msb/ved-payments-1/',
|
||||
};
|
||||
|
||||
const CROSS_SALE_PATHS = {
|
||||
@@ -181,6 +182,19 @@ const CROSS_BORDER_AB_PAYMENTS = {
|
||||
AUTH: 'https://app.ab-payments.ru/auth',
|
||||
};
|
||||
|
||||
const MY_DSS_V1_LINKS = {
|
||||
RU_STORE: 'https://www.rustore.ru/catalog/app/ru.cryptopro.mydss',
|
||||
APP_GALLERY: 'https://appgallery.huawei.com/#/app/C103988925',
|
||||
CRYPTOPRO: 'https://cryptopro.ru/products/mydss',
|
||||
};
|
||||
|
||||
const MY_DSS_V2_LINKS = {
|
||||
RU_STORE: 'https://www.rustore.ru/catalog/app/ru.safetech.mydss.v2',
|
||||
APP_GALLERY: 'https://appgallery.huawei.com/app/C103534717',
|
||||
APP_STORE: 'https://apps.apple.com/ru/app/mydss-2-0/id1524467203',
|
||||
APK: 'https://cdn.gbo.gazprombank.ru/app/android/businesssign.apk',
|
||||
};
|
||||
|
||||
const IB_MODULE_VERSION = process.env.IB_MODULE_VERSION ?? '';
|
||||
|
||||
const queryStringVersion = `${IB_MODULE_VERSION ? `?version=${IB_MODULE_VERSION}` : ''}`;
|
||||
@@ -203,4 +217,6 @@ export {
|
||||
CROSS_SALE_PATHS,
|
||||
EXTERNAL_LINKS,
|
||||
queryStringVersion,
|
||||
MY_DSS_V1_LINKS,
|
||||
MY_DSS_V2_LINKS,
|
||||
};
|
||||
|
||||
@@ -8,3 +8,4 @@ export * from './useScrollLock';
|
||||
export * from './useRedirectByToggle';
|
||||
export * from './useInfiniteScroll';
|
||||
export * from './useSearchParams';
|
||||
export * from './useOrganizationWithAuthorities';
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { Authority } from '@msb/http';
|
||||
import { useAppContext } from '../context';
|
||||
import { getOrganizationWithAuthorities } from '../lib';
|
||||
|
||||
const useOrganizationWithAuthorities = (authorities: Authority[], isCheckEvery: boolean = true) => {
|
||||
const { userAuthorities } = useAppContext();
|
||||
|
||||
const clientAuthorities = useMemo(() => userAuthorities?.data.clientAuthorities || {}, [userAuthorities?.data.clientAuthorities]);
|
||||
|
||||
return useMemo(
|
||||
() => getOrganizationWithAuthorities(clientAuthorities, authorities, isCheckEvery),
|
||||
[authorities, clientAuthorities, isCheckEvery]
|
||||
);
|
||||
};
|
||||
|
||||
export { useOrganizationWithAuthorities };
|
||||
@@ -77,6 +77,18 @@ function checkIsPermissionsAvailable(
|
||||
: authorities.some(authority => organizationAuthorities.includes(authority));
|
||||
}
|
||||
|
||||
function getOrganizationWithAuthorities(clientAuthorities: ClientAuthoritiesDto, authorities: Authority[], isCheckEvery: boolean = true) {
|
||||
const organizationIds = Object.keys(clientAuthorities);
|
||||
|
||||
return organizationIds.find(organizationId => {
|
||||
if (isCheckEvery) {
|
||||
return authorities.every(authority => clientAuthorities[organizationId].includes(authority));
|
||||
}
|
||||
|
||||
return authorities.some(authority => clientAuthorities[organizationId].includes(authority));
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
checkOrganizationsHavePermission,
|
||||
checkHavePermission,
|
||||
@@ -84,4 +96,5 @@ export {
|
||||
checkHavePermissionList,
|
||||
checkHaveUserPermissionsList,
|
||||
checkIsPermissionsAvailable,
|
||||
getOrganizationWithAuthorities,
|
||||
};
|
||||
|
||||
@@ -89,4 +89,7 @@ const formatSize = (byte: number | string, locale: string = 'ru') => {
|
||||
return `${byteSize} ${SIZE_DESIGNATIONS[locale].B}`;
|
||||
};
|
||||
|
||||
export { formatSize };
|
||||
const getFormattedBalanceWithoutIcon = (money: number, maximumFractionDigits = 2, minimumFractionDigits = 2) =>
|
||||
new Intl.NumberFormat('ru-RU', { style: 'decimal', maximumFractionDigits, minimumFractionDigits }).format(money);
|
||||
|
||||
export { formatSize, getFormattedBalanceWithoutIcon };
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Не экспортировать /tests!!
|
||||
export * from './useMediaQuery';
|
||||
export * from './pluralize';
|
||||
export * from './useImageItemsLoader';
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './customRender';
|
||||
export * from './mocks';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './useYaMetrika';
|
||||
export * from './useMediaQuery';
|
||||
export * from './useRedirect';
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Утилиты для создания моков useMediaQuery
|
||||
* Используется в тестах для мокирования медиа-запросов.
|
||||
*/
|
||||
|
||||
export interface UseMediaQueryMockState {
|
||||
isMobileValue: boolean;
|
||||
isTabletValue: boolean;
|
||||
}
|
||||
|
||||
export interface UseMediaQueryMocks {
|
||||
state: UseMediaQueryMockState;
|
||||
useMediaQueryMock: jest.Mock<boolean, unknown[]>;
|
||||
}
|
||||
|
||||
const DEFAULT_MEDIA_QUERY_STATE: UseMediaQueryMockState = {
|
||||
isMobileValue: false,
|
||||
isTabletValue: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Создает мок для useMediaQuery с поддержкой mobile и tablet
|
||||
* Первый вызов возвращает isMobileValue, второй - isTabletValue.
|
||||
*/
|
||||
export function createUseMediaQueryMockWithDeviceTypes(
|
||||
initialState: UseMediaQueryMockState = DEFAULT_MEDIA_QUERY_STATE
|
||||
): UseMediaQueryMocks {
|
||||
const state: UseMediaQueryMockState = {
|
||||
isMobileValue: initialState.isMobileValue,
|
||||
isTabletValue: initialState.isTabletValue,
|
||||
};
|
||||
|
||||
const useMediaQueryMock = jest.fn(() => {
|
||||
const callCount = useMediaQueryMock.mock.calls.length;
|
||||
|
||||
if (callCount === 1) {
|
||||
return state.isMobileValue;
|
||||
}
|
||||
|
||||
return state.isTabletValue;
|
||||
});
|
||||
|
||||
return {
|
||||
state,
|
||||
useMediaQueryMock,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Утилиты для создания моков useRedirect
|
||||
* Используется в тестах для мокирования редиректов.
|
||||
*/
|
||||
|
||||
export interface UseRedirectMocks {
|
||||
redirectMock: jest.Mock;
|
||||
useRedirectMock: jest.Mock<jest.Mock, [string?]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает моки для useRedirect.
|
||||
*/
|
||||
export function createUseRedirectMock(): UseRedirectMocks {
|
||||
const redirectMock = jest.fn();
|
||||
const useRedirectMock = jest.fn((_path?: string) => redirectMock);
|
||||
|
||||
return {
|
||||
redirectMock,
|
||||
useRedirectMock,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Утилиты для создания моков useYaMetrika
|
||||
* Используется в тестах для мокирования Яндекс.Метрики.
|
||||
*/
|
||||
|
||||
export interface UseYaMetrikaMockReturn {
|
||||
handleReachGoal: jest.Mock;
|
||||
}
|
||||
|
||||
export interface UseYaMetrikaMocks {
|
||||
handleReachGoalMock: jest.Mock;
|
||||
useYaMetrikaMock: jest.Mock<UseYaMetrikaMockReturn, unknown[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает моки для useYaMetrika.
|
||||
* @returns Объект с моками handleReachGoal и useYaMetrika.
|
||||
*/
|
||||
export function createUseYaMetrikaMock(): UseYaMetrikaMocks {
|
||||
const handleReachGoalMock = jest.fn();
|
||||
const useYaMetrikaMock = jest.fn(() => ({
|
||||
handleReachGoal: handleReachGoalMock,
|
||||
}));
|
||||
|
||||
return {
|
||||
handleReachGoalMock,
|
||||
useYaMetrikaMock,
|
||||
};
|
||||
}
|
||||
@@ -3,7 +3,12 @@ import { LongRightIcon } from '@fractal-ui/library';
|
||||
import { Modal } from '@fractal-ui/overlays';
|
||||
import { Title, Text } from '@fractal-ui/styling';
|
||||
import type { MegaBannerModel, SALE_GRADIENTS } from '@msb/shared';
|
||||
import { MEDIA, TEXT_RATE_KEY, useBestRatesMainPage, useMediaQuery, type BaseModalProps } from '@msb/shared';
|
||||
import {
|
||||
MEDIA,
|
||||
// TEXT_RATE_KEY, useBestRatesMainPage,
|
||||
useMediaQuery,
|
||||
type BaseModalProps,
|
||||
} from '@msb/shared';
|
||||
import * as S from './MegaBanner.styles';
|
||||
|
||||
interface MegaBannerProps extends MegaBannerModel, BaseModalProps {
|
||||
@@ -17,14 +22,14 @@ const MegaBanner = ({
|
||||
button,
|
||||
image,
|
||||
gradient = 'sale2',
|
||||
rateText,
|
||||
dealType,
|
||||
// rateText,
|
||||
// dealType,
|
||||
onConfirm,
|
||||
onClose,
|
||||
}: MegaBannerProps) => {
|
||||
const isMobile = useMediaQuery(MEDIA.mobile);
|
||||
|
||||
const { bestRates } = useBestRatesMainPage();
|
||||
// const { bestRates } = useBestRatesMainPage();
|
||||
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
@@ -38,13 +43,13 @@ const MegaBanner = ({
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
const haveRate = dealType && rateText && bestRates && bestRates[dealType];
|
||||
// const haveRate = dealType && rateText && bestRates && bestRates[dealType];
|
||||
|
||||
const finalText = haveRate
|
||||
? rateText
|
||||
.split(new RegExp(`(${TEXT_RATE_KEY})`))
|
||||
.map(textPart => (textPart === TEXT_RATE_KEY ? <b>{bestRates[dealType]}%</b> : textPart))
|
||||
: text;
|
||||
// const finalText = haveRate
|
||||
// ? rateText
|
||||
// .split(new RegExp(`(${TEXT_RATE_KEY})`))
|
||||
// .map(textPart => (textPart === TEXT_RATE_KEY ? <b>{bestRates[dealType]}%</b> : textPart))
|
||||
// : text;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -62,11 +67,11 @@ const MegaBanner = ({
|
||||
{isMobile ? <Title.H1Bold color="text.primary">{title}</Title.H1Bold> : <Title.H2Bold color="text.primary">{title}</Title.H2Bold>}
|
||||
{isMobile ? (
|
||||
<Text.P2 color="text.primary" whiteSpace="pre-wrap">
|
||||
{finalText}
|
||||
{text}
|
||||
</Text.P2>
|
||||
) : (
|
||||
<Text.P1 color="text.primary" whiteSpace="inherit">
|
||||
{finalText}
|
||||
{text}
|
||||
</Text.P1>
|
||||
)}
|
||||
</S.Content>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable react/jsx-no-useless-fragment */
|
||||
import { useMemo } from 'react';
|
||||
import { useAppContext } from '../../context';
|
||||
import { checkHavePermission, checkHaveUserPermissions } from '../../lib';
|
||||
import { checkHavePermission, checkHaveUserPermissions, checkIsPermissionsAvailable } from '../../lib';
|
||||
import type { PermissionItem } from '../../model';
|
||||
import { AUTHORITIES_FIELD } from '../../model';
|
||||
import { NotAuthorizedError } from '../SystemResponse';
|
||||
@@ -10,21 +10,26 @@ interface PermissionsProps {
|
||||
permissionsList: PermissionItem[];
|
||||
children: React.ReactNode;
|
||||
ymNotAuthorizedGoal?: React.ReactNode;
|
||||
organizationId?: string;
|
||||
}
|
||||
|
||||
const Permissions = ({ permissionsList, children, ymNotAuthorizedGoal }: PermissionsProps) => {
|
||||
const Permissions = ({ permissionsList, children, ymNotAuthorizedGoal, organizationId }: PermissionsProps) => {
|
||||
const { userAuthorities } = useAppContext();
|
||||
|
||||
const hasPermissions = useMemo(
|
||||
() =>
|
||||
permissionsList.every(({ permissions, authoritiesField, isCheckEvery }) => {
|
||||
if (organizationId) {
|
||||
return checkIsPermissionsAvailable(organizationId, userAuthorities?.data.clientAuthorities || {}, permissions, isCheckEvery);
|
||||
}
|
||||
|
||||
if (authoritiesField === AUTHORITIES_FIELD.CLIENT_AUTHORITIES) {
|
||||
return checkHavePermission(userAuthorities?.data.clientAuthorities || {}, permissions, isCheckEvery);
|
||||
}
|
||||
|
||||
return checkHaveUserPermissions(userAuthorities?.data.authorities, permissions, isCheckEvery);
|
||||
}),
|
||||
[permissionsList, userAuthorities]
|
||||
[organizationId, permissionsList, userAuthorities]
|
||||
);
|
||||
|
||||
return hasPermissions ? (
|
||||
|
||||
@@ -11,9 +11,10 @@ const BannerContainer = styled.div<{ $background: string }>(({ $background }) =>
|
||||
|
||||
const Image = styled.img({
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
right: '-12px',
|
||||
bottom: 16,
|
||||
height: 132,
|
||||
scale: '1.5',
|
||||
});
|
||||
|
||||
const Content = styled.div({
|
||||
|
||||
@@ -56,10 +56,10 @@ const PromoBanner: FC<PromoBannerProps> = ({
|
||||
{bestRates[dealType]?.replace('.', ',')}%
|
||||
</Title.H1>
|
||||
)}
|
||||
{title && <Title.H4 color="text.primary">{title}</Title.H4>}
|
||||
<Text.P3 color="text.primary">{description}</Text.P3>
|
||||
{title && <Title.H4 color="bg.primary">{title}</Title.H4>}
|
||||
<Text.P3 color="bg.primary">{description}</Text.P3>
|
||||
</S.Title>
|
||||
<Button dataAction="read-more" size="M" variant="secondary" onClick={navigateTo}>
|
||||
<Button dataAction="read-more" size="S" variant="white" onClick={navigateTo}>
|
||||
{buttonText}
|
||||
</Button>
|
||||
</S.Content>
|
||||
|
||||
@@ -7,16 +7,18 @@ import type { DateFieldWithCalendarContextValues, Option, PERIOD_TYPE } from './
|
||||
const useDateButton = () => {
|
||||
const { onChange, options, value, isCalendarShown, setSelectedCalendarDate, setSelectedDate, handleCalendarDateSelect } =
|
||||
useDateFieldWithCalendarContext();
|
||||
const selected = useMemo(() => options.find(opt => opt.value === value?.period), [options, value?.period]);
|
||||
|
||||
const selectedOption = useMemo(() => {
|
||||
const found = options.find(opt => opt.value === value?.period);
|
||||
|
||||
return found || DEFAULT_SELECTED_OPTION;
|
||||
}, [options, value?.period]);
|
||||
|
||||
const [isDrawerOpened, setIsDrawerOpened] = useState(false);
|
||||
const [selectedOptionLabel, setSelectedOptionLabel] = useState(selected?.label);
|
||||
const [selectedOption, setSelectedOption] = useState<Option>(selected ? selected : DEFAULT_SELECTED_OPTION);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setSelectedCalendarDate(undefined);
|
||||
setSelectedDate(undefined);
|
||||
setSelectedOption(DEFAULT_SELECTED_OPTION);
|
||||
setSelectedOptionLabel('');
|
||||
onChange();
|
||||
setIsDrawerOpened(false);
|
||||
}, [onChange, setSelectedCalendarDate, setSelectedDate]);
|
||||
@@ -25,12 +27,12 @@ const useDateButton = () => {
|
||||
(option?: DateFieldWithCalendarContextValues['options'][0]) => {
|
||||
if (isCalendarShown) {
|
||||
handleCalendarDateSelect();
|
||||
setSelectedOption(DEFAULT_SELECTED_OPTION);
|
||||
setSelectedOptionLabel('');
|
||||
} else if (option) {
|
||||
setSelectedOption(option);
|
||||
setSelectedDate(option?.value as PERIOD_TYPE);
|
||||
setSelectedOptionLabel(option?.label);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (option) {
|
||||
setSelectedDate(option.value as PERIOD_TYPE);
|
||||
|
||||
const interval = getIntervalByPeriod(option?.value as PERIOD_TYPE);
|
||||
|
||||
@@ -41,13 +43,11 @@ const useDateButton = () => {
|
||||
);
|
||||
|
||||
const handleButtonClick = useCallback(() => {
|
||||
if (!isDrawerOpened) {
|
||||
setIsDrawerOpened(() => true);
|
||||
}
|
||||
}, [isDrawerOpened]);
|
||||
setIsDrawerOpened(true);
|
||||
}, []);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setIsDrawerOpened(() => false);
|
||||
setIsDrawerOpened(false);
|
||||
}, []);
|
||||
|
||||
const contextValues: WithOptionsContextProps<{ label: string; value: number | string }> = useMemo(
|
||||
@@ -66,8 +66,6 @@ const useDateButton = () => {
|
||||
reset,
|
||||
change,
|
||||
drawer: { open: handleButtonClick, close: handleClose, state: isDrawerOpened },
|
||||
selectedOptionLabel,
|
||||
selected,
|
||||
selectedOption,
|
||||
contextValues,
|
||||
};
|
||||
|
||||
@@ -30,7 +30,8 @@ const DateButton = ({
|
||||
}: Props) => {
|
||||
const { options, handleInnerButtonClick, isCalendarShown, setSelectedCalendarDate, setIsCalendarShown } =
|
||||
useDateFieldWithCalendarContext();
|
||||
const { reset, change, drawer, selectedOptionLabel, selectedOption, contextValues } = useDateButton();
|
||||
|
||||
const { reset, change, drawer, selectedOption, contextValues } = useDateButton();
|
||||
|
||||
const drawerActions: ModalButtonProps[] = useMemo(() => {
|
||||
let actions: ModalButtonProps[] = [
|
||||
@@ -50,7 +51,7 @@ const DateButton = ({
|
||||
{
|
||||
text: LOCALIZATION.SELECT_LABEL,
|
||||
shape: 'default',
|
||||
disabled: isCalendarShown ? !selectedCalendarDate : !selectedOption.value || selectedOption.value === selectedDate,
|
||||
disabled: !selectedCalendarDate,
|
||||
dataAction: 'select-drawer-calendar-date',
|
||||
onClick: () => change(),
|
||||
},
|
||||
@@ -59,21 +60,27 @@ const DateButton = ({
|
||||
}
|
||||
|
||||
return actions;
|
||||
}, [change, isCalendarShown, reset, selectedCalendarDate, selectedDate, selectedOption.value]);
|
||||
}, [change, isCalendarShown, reset, selectedCalendarDate, selectedDate]);
|
||||
|
||||
const bottomSheetRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const buttonVariant = selectedDate || drawer.state ? 'primary' : 'secondary';
|
||||
|
||||
// Лейбл вычисляется из value.period + options
|
||||
const labelToShow = selectedOption?.label;
|
||||
|
||||
return (
|
||||
<WithOptionsContext.Provider value={contextValues}>
|
||||
<Button
|
||||
isIconRight
|
||||
dataAction={name}
|
||||
flex="0 0 auto"
|
||||
icon={drawer.state || selectedDate ? CrossIcon : DownIcon}
|
||||
minWidth="fit-content"
|
||||
shape="default"
|
||||
size="S"
|
||||
variant={buttonVariant}
|
||||
width="fit-content"
|
||||
onClick={e => {
|
||||
const tagName =
|
||||
e.nativeEvent.target && 'tagName' in e.nativeEvent.target ? (e.nativeEvent.target as { tagName: string }).tagName : '';
|
||||
@@ -85,7 +92,7 @@ const DateButton = ({
|
||||
}
|
||||
}}
|
||||
>
|
||||
{selectedOptionLabel ? `${LOCALIZATION.REQUEST_DATE}: ${selectedOptionLabel}` : LOCALIZATION.REQUEST_DATE}
|
||||
{labelToShow ? labelToShow : LOCALIZATION.REQUEST_DATE}
|
||||
</Button>
|
||||
<Drawer
|
||||
hideContentPadding
|
||||
|
||||
@@ -53,6 +53,13 @@ const DateFieldWithCalendar = ({
|
||||
);
|
||||
const [options, setOptions] = useState(acceptableOptions);
|
||||
|
||||
// Если опция изменилась на пустую, то нужно задать отчистить выбранную опцию
|
||||
useEffect(() => {
|
||||
if (!other.value) {
|
||||
selectPeriod(undefined);
|
||||
}
|
||||
}, [other.value]);
|
||||
|
||||
const setupCustomPeriod = useCallback(() => {
|
||||
const { customLabel } = getSelectedDateLabel(other.value?.dateInterval || other.defaultValue?.dateInterval);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ const DateSelect = ({
|
||||
tooltipPosition,
|
||||
readOnly,
|
||||
shouldEnableWeekends = false,
|
||||
width,
|
||||
}: Props) => {
|
||||
const { setSelectedCalendarDate, options, setIsCalendarShown, isCalendarShown, handleInnerButtonClick } =
|
||||
useDateFieldWithCalendarContext();
|
||||
@@ -65,6 +66,7 @@ const DateSelect = ({
|
||||
tooltipPosition={tooltipPosition}
|
||||
value={selectedDate}
|
||||
warningText={warningText}
|
||||
width={width}
|
||||
onChange={handleSelectChange}
|
||||
onInnerButtonClick={handleInnerButtonClick}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { Select } from './ui';
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Select as FractalSelect } from '@msb/fractal-ui-composites';
|
||||
import type { BaseSelectProps } from '@msb/fractal-ui-composites';
|
||||
import { MEDIA, useMediaQuery } from '@msb/shared';
|
||||
|
||||
/**
|
||||
* Обёртка над компонентом Select фрактала, необходимая для кастомизации функциональности под наши решения.
|
||||
*
|
||||
* ShowDrawerForBreakPoint нужен для отображения Drawer в размере Tablet.
|
||||
* Отображаем разные FractalSelect, т.к. Брекпоинты фрактала отличаются от наших.
|
||||
*/
|
||||
const Select = (props: BaseSelectProps) => {
|
||||
const isTablet = useMediaQuery(MEDIA.tablet);
|
||||
|
||||
return isTablet ? <FractalSelect showDrawerForBreakPoint {...(props as any)} /> : <FractalSelect {...(props as any)} />;
|
||||
};
|
||||
|
||||
export { Select };
|
||||
@@ -0,0 +1 @@
|
||||
export { Select } from './Select';
|
||||
@@ -2,4 +2,5 @@ export { MultiSelect } from './MultiSelect';
|
||||
export { MultiSelectButton } from './MultiSelectButton';
|
||||
export { SelectButton } from './SelectButton';
|
||||
export { SingleSelectButton } from './SingleSelectButton';
|
||||
export { Select } from './Select';
|
||||
export * from './DateFieldWithCalendar';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import type { ModalButtonProps, StatusModalType } from '@fractal-ui/overlays';
|
||||
import { Text } from '@fractal-ui/styling';
|
||||
import type { BaseModalProps } from '../../context';
|
||||
@@ -32,6 +33,15 @@ interface BaseDialogProps<T = unknown> extends BaseModalProps {
|
||||
isLoading?: boolean;
|
||||
/** Признак скрытия иконки "Назад". */
|
||||
hideBackIcon?: boolean;
|
||||
/** Устанавливает ширину кнопки.
|
||||
* @default fit-content
|
||||
*/
|
||||
okButtonWidth?: CSSProperties['width'];
|
||||
/**
|
||||
* Устанавливает ширину кнопки отмены.
|
||||
* @default 300px
|
||||
*/
|
||||
cancelButtonWidth?: CSSProperties['width'];
|
||||
}
|
||||
|
||||
/** Модальное окно подтверждения действия. */
|
||||
@@ -53,6 +63,8 @@ const BaseDialog = <T,>({
|
||||
isLoading,
|
||||
hideBackIcon = false,
|
||||
okButtonShape,
|
||||
okButtonWidth = 'fit-content',
|
||||
cancelButtonWidth = 300,
|
||||
}: BaseDialogProps<T>) => {
|
||||
const handleAccept = () => (onOk ? onOk(data) : onSuccess?.());
|
||||
|
||||
@@ -68,7 +80,7 @@ const BaseDialog = <T,>({
|
||||
text: okButtonText,
|
||||
shape: okButtonShape,
|
||||
variant: 'primary',
|
||||
width: 300,
|
||||
width: okButtonWidth,
|
||||
},
|
||||
{
|
||||
dataAction: 'cancel',
|
||||
@@ -77,7 +89,7 @@ const BaseDialog = <T,>({
|
||||
size: 'L',
|
||||
text: cancelButtonText,
|
||||
variant: 'primary',
|
||||
width: 300,
|
||||
width: cancelButtonWidth,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { OrganizationBlocker } from './OrganizationBlocker';
|
||||
import { OrganizationField } from './OrganizationField';
|
||||
import { ReservedAccountField } from './ReservedAccountField';
|
||||
import { SignatoryField } from './SignatoryField';
|
||||
import { Skeletons } from './Skeletons';
|
||||
import { OPEN_ACCOUNT_GOALS } from '@/shared/constants';
|
||||
import { getCardDescription, getCardStatusProps } from '@/shared/lib';
|
||||
import { Aside } from '@/shared/ui/Aside';
|
||||
@@ -63,20 +64,22 @@ const AccountOrderForm = () => {
|
||||
<Flex column height="100%" justifyContent="space-between" pt="4">
|
||||
<Flex column gap={7}>
|
||||
<OrganizationField />
|
||||
<AccountTypeField />
|
||||
<OrganizationBlocker>
|
||||
<CurrencyField />
|
||||
<OfficeField />
|
||||
<CommissionAccountBlocker>
|
||||
<ContractField />
|
||||
<ReservedAccountField />
|
||||
<Flex alignItems="flex-start" gap={5} {...(isMobile ? { column: true } : { row: true })}>
|
||||
<CommissionAccountField />
|
||||
<SignatoryField />
|
||||
</Flex>
|
||||
<AgreementsFields />
|
||||
</CommissionAccountBlocker>
|
||||
</OrganizationBlocker>
|
||||
<Skeletons>
|
||||
<AccountTypeField />
|
||||
<OrganizationBlocker>
|
||||
<CurrencyField />
|
||||
<OfficeField />
|
||||
<CommissionAccountBlocker>
|
||||
<ContractField />
|
||||
<ReservedAccountField />
|
||||
<Flex alignItems="flex-start" gap={5} {...(isMobile ? { column: true } : { row: true })}>
|
||||
<CommissionAccountField />
|
||||
<SignatoryField />
|
||||
</Flex>
|
||||
<AgreementsFields />
|
||||
</CommissionAccountBlocker>
|
||||
</OrganizationBlocker>
|
||||
</Skeletons>
|
||||
</Flex>
|
||||
<CommissionAccountBlocker>
|
||||
<Actions />
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { Skeleton } from '@fractal-ui/core';
|
||||
import { Flex } from '@msb/shared';
|
||||
import { useOrganizationFieldValidation } from '../model';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const Skeletons = ({ children }: Props) => {
|
||||
const { isValidating } = useOrganizationFieldValidation();
|
||||
|
||||
return isValidating ? (
|
||||
<Flex column gap={7}>
|
||||
<Skeleton dataName="field1" height="40px" />
|
||||
<Skeleton dataName="field2" height="40px" />
|
||||
<Flex row gap={5}>
|
||||
<Skeleton dataName="field3" height="40px" width="100%" />
|
||||
<Skeleton dataName="field4" height="40px" width="100%" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
) : (
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
<>{children}</>
|
||||
);
|
||||
};
|
||||
|
||||
export { Skeletons };
|
||||
+13
-7
@@ -107,13 +107,19 @@ const useOpenAccountsOrdersTable = () => {
|
||||
},
|
||||
[getRowActions]
|
||||
);
|
||||
const actionHeaderProps = {
|
||||
buttonProps: {
|
||||
dataAction: 'open-card',
|
||||
},
|
||||
handler: handleClickOpenAccount,
|
||||
title: LOCALIZATION.OPEN_ACCOUNT,
|
||||
};
|
||||
const actionHeaderProps = useMemo(() => {
|
||||
if (openRequestsData.length === 0 && !isLoading && !filterIsActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
buttonProps: {
|
||||
dataAction: 'open-card',
|
||||
},
|
||||
handler: handleClickOpenAccount,
|
||||
title: LOCALIZATION.OPEN_ACCOUNT,
|
||||
};
|
||||
}, [filterIsActive, handleClickOpenAccount, isLoading, openRequestsData.length]);
|
||||
|
||||
return {
|
||||
columns,
|
||||
|
||||
@@ -27,7 +27,7 @@ const usePhoto = (props: UsePhotoProps) => {
|
||||
network.client
|
||||
.get(link)
|
||||
.then(response => {
|
||||
const contentType = response.headers['Content-Type'];
|
||||
const contentType = response.headers['content-type'];
|
||||
|
||||
if (response.status && typeof contentType === 'string' && contentType?.includes('image')) {
|
||||
setManagerPhoto(link);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
const SbpLandingContext = createContext<{ setIsSbpContractExist(value: boolean): void } | null>(null);
|
||||
|
||||
const useSbpLandingContext = () => {
|
||||
const context = useContext(SbpLandingContext);
|
||||
|
||||
if (context === null) {
|
||||
throw new Error('Use context with SbpLandingContext provider');
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export { SbpLandingContext, useSbpLandingContext };
|
||||
@@ -0,0 +1 @@
|
||||
export { SbpLandingContext, useSbpLandingContext } from './SbpLandingContext';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './localization';
|
||||
@@ -0,0 +1,6 @@
|
||||
const LOCALIZATION = {
|
||||
ACCEPT_SBP_BUTTON: 'Подключить',
|
||||
OPEN_SBP_DETAILS_BUTTON: 'Подробнее',
|
||||
};
|
||||
|
||||
export { LOCALIZATION };
|
||||
@@ -0,0 +1 @@
|
||||
export { GuideList } from './ui';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './types';
|
||||
@@ -0,0 +1,12 @@
|
||||
interface GuideListItemProps {
|
||||
title: string;
|
||||
description: string;
|
||||
index: number;
|
||||
isIndexVisible?: boolean;
|
||||
}
|
||||
|
||||
interface GuideListProps {
|
||||
items: Array<Omit<GuideListItemProps, 'index' | 'isIndexVisible'>>;
|
||||
}
|
||||
|
||||
export type { GuideListItemProps, GuideListProps };
|
||||
@@ -0,0 +1,29 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const GuideListLayout = styled.div(({ theme }) => ({
|
||||
backgroundColor: theme.colors.bg.secondary,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: '20px',
|
||||
padding: '24px',
|
||||
width: '100%',
|
||||
gap: '24px',
|
||||
}));
|
||||
|
||||
const GuideListButtonsLayout = styled.div`
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
margin-top: 16px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const ListItemsDivider = styled.div(({ theme }) => ({
|
||||
borderColor: theme.colors.control.border,
|
||||
borderBottomWidth: '1px',
|
||||
borderBottomStyle: 'solid',
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
export { GuideListLayout, GuideListButtonsLayout, ListItemsDivider };
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { Button, ButtonLink } from '@fractal-ui/core';
|
||||
import { LOCALIZATION } from '../constants';
|
||||
import type { GuideListProps } from '../model';
|
||||
import * as S from './GuideList.styles';
|
||||
import { ListItemsDivider } from './GuideList.styles';
|
||||
import { GuideListItem } from './GuideListItem';
|
||||
import { useSbpLandingContext } from '@/entities/SbpLandingContext';
|
||||
|
||||
const GuideList = ({ items }: GuideListProps) => {
|
||||
const { setIsSbpContractExist } = useSbpLandingContext();
|
||||
const onApplyClick = () => {
|
||||
setIsSbpContractExist(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<S.GuideListLayout>
|
||||
{items.map(({ description, title }, index) => (
|
||||
<>
|
||||
<GuideListItem key={title} description={description} index={index + 1} title={title} />
|
||||
{index !== items.length - 1 && <ListItemsDivider key={`${title}-divider`} />}
|
||||
</>
|
||||
))}
|
||||
<S.GuideListButtonsLayout>
|
||||
<Button dataAction="accept-sbp" onClick={onApplyClick}>
|
||||
{LOCALIZATION.ACCEPT_SBP_BUTTON}
|
||||
</Button>
|
||||
<ButtonLink dataAction="open-sbp-details">{LOCALIZATION.OPEN_SBP_DETAILS_BUTTON}</ButtonLink>
|
||||
</S.GuideListButtonsLayout>
|
||||
</S.GuideListLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export { GuideList };
|
||||
@@ -0,0 +1,16 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const GuideListItemLayout = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
min-height: 52px;
|
||||
`;
|
||||
|
||||
const GuideListItemTitle = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
export { GuideListItemLayout, GuideListItemTitle };
|
||||
@@ -0,0 +1,22 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Title, Text, useTheme } from '@fractal-ui/styling';
|
||||
import type { GuideListItemProps } from '../model';
|
||||
import * as S from './GuideListItem.styles';
|
||||
|
||||
const GuideListItem = ({ title, description, index, isIndexVisible = true }: GuideListItemProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const preparedIndex = useMemo(() => (index < 10 ? index.toString().padStart(2, '0') : index), [index]);
|
||||
|
||||
return (
|
||||
<S.GuideListItemLayout>
|
||||
<S.GuideListItemTitle>
|
||||
<Title.H4>{title}</Title.H4>
|
||||
{isIndexVisible && <Title.H4 color={theme.colors.text.accentBrand}>{preparedIndex}</Title.H4>}
|
||||
</S.GuideListItemTitle>
|
||||
<Text.P2 color={theme.colors.text.secondary}>{description}</Text.P2>
|
||||
</S.GuideListItemLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export { GuideListItem };
|
||||
@@ -0,0 +1 @@
|
||||
export { GuideList } from './GuideList';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { SBP_CONTRACT_FIELDS } from '../model';
|
||||
import { LOCALIZATION } from './localization';
|
||||
|
||||
const SBP_CONTRACT_FIELDS_TO_LABELS = {
|
||||
[SBP_CONTRACT_FIELDS.CONTRACT_TYPE]: LOCALIZATION[SBP_CONTRACT_FIELDS.CONTRACT_TYPE],
|
||||
[SBP_CONTRACT_FIELDS.INN]: LOCALIZATION[SBP_CONTRACT_FIELDS.INN],
|
||||
[SBP_CONTRACT_FIELDS.KPP]: LOCALIZATION[SBP_CONTRACT_FIELDS.KPP],
|
||||
[SBP_CONTRACT_FIELDS.OGRN]: LOCALIZATION[SBP_CONTRACT_FIELDS.OGRN],
|
||||
[SBP_CONTRACT_FIELDS.CONTRACT_NUMBER]: LOCALIZATION[SBP_CONTRACT_FIELDS.CONTRACT_NUMBER],
|
||||
[SBP_CONTRACT_FIELDS.LEGAL_ID]: LOCALIZATION[SBP_CONTRACT_FIELDS.LEGAL_ID],
|
||||
[SBP_CONTRACT_FIELDS.SIGN_DATE]: LOCALIZATION[SBP_CONTRACT_FIELDS.SIGN_DATE],
|
||||
[SBP_CONTRACT_FIELDS.START_DATE]: LOCALIZATION[SBP_CONTRACT_FIELDS.START_DATE],
|
||||
[SBP_CONTRACT_FIELDS.RETURN_ACCOUNT]: LOCALIZATION[SBP_CONTRACT_FIELDS.RETURN_ACCOUNT],
|
||||
[SBP_CONTRACT_FIELDS.COMMISSION_ACCOUNT]: LOCALIZATION[SBP_CONTRACT_FIELDS.COMMISSION_ACCOUNT],
|
||||
[SBP_CONTRACT_FIELDS.ACCOUNTS]: LOCALIZATION[SBP_CONTRACT_FIELDS.ACCOUNTS],
|
||||
};
|
||||
|
||||
export { SBP_CONTRACT_FIELDS_TO_LABELS };
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './localization';
|
||||
export * from './mocks';
|
||||
@@ -0,0 +1,23 @@
|
||||
import { SBP_CONTRACT_FIELDS } from '../model';
|
||||
|
||||
const LOCALIZATION = {
|
||||
SBP_DETAILS_BUTTON: 'Подробнее о СБП',
|
||||
ADD_SBP_ACCOUNTS_BUTTON: 'Добавить счета',
|
||||
ORG_DATA_TITLE: 'Данные организации',
|
||||
CONTRACT_DATA_TITLE: 'Данные договора',
|
||||
ACCOUNTS_DATA_TITLE: 'Счета',
|
||||
ORGANIZATION: 'Организация',
|
||||
[SBP_CONTRACT_FIELDS.CONTRACT_TYPE]: 'Вид деятельности',
|
||||
[SBP_CONTRACT_FIELDS.INN]: 'ИНН',
|
||||
[SBP_CONTRACT_FIELDS.KPP]: 'КПП',
|
||||
[SBP_CONTRACT_FIELDS.OGRN]: 'ОГРН / ОГРНИП',
|
||||
[SBP_CONTRACT_FIELDS.CONTRACT_NUMBER]: 'Договор №',
|
||||
[SBP_CONTRACT_FIELDS.LEGAL_ID]: 'LegalID в СБП',
|
||||
[SBP_CONTRACT_FIELDS.SIGN_DATE]: 'Дата подписания',
|
||||
[SBP_CONTRACT_FIELDS.START_DATE]: 'Дата начала действия',
|
||||
[SBP_CONTRACT_FIELDS.RETURN_ACCOUNT]: 'Счёт для возвратов по СБП',
|
||||
[SBP_CONTRACT_FIELDS.COMMISSION_ACCOUNT]: 'Счёт для списания комиссии по СБП',
|
||||
[SBP_CONTRACT_FIELDS.ACCOUNTS]: 'Счета для регистрации в СБП:',
|
||||
};
|
||||
|
||||
export { LOCALIZATION };
|
||||
@@ -0,0 +1,80 @@
|
||||
import { SBP_CONTRACT_FIELDS } from '../model';
|
||||
import { SBP_CONTRACT_FIELDS_TO_LABELS } from './constants';
|
||||
import type { SbpDescriptionRow } from '@/shared/ui/DescriptionList';
|
||||
|
||||
const ORGANIZATION_DATA: SbpDescriptionRow[] = [
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.CONTRACT_TYPE,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.CONTRACT_TYPE],
|
||||
value: 'Водоснабжение, водоотведение, организация',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.INN,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.INN],
|
||||
value: '234567890765',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.KPP,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.KPP],
|
||||
value: '773643002',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.OGRN,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.OGRN],
|
||||
value: '773643002',
|
||||
description: '',
|
||||
},
|
||||
];
|
||||
|
||||
const CONTRACT_DATA: SbpDescriptionRow[] = [
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.CONTRACT_NUMBER,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.CONTRACT_NUMBER],
|
||||
value: '9721007182',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.LEGAL_ID,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.LEGAL_ID],
|
||||
value: '40702.810.8.21643442469',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.SIGN_DATE,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.SIGN_DATE],
|
||||
value: '24.01.2022',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.START_DATE,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.START_DATE],
|
||||
value: '24.01.2022',
|
||||
description: '',
|
||||
},
|
||||
];
|
||||
|
||||
const ACCOUNTS_DATA: SbpDescriptionRow[] = [
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.RETURN_ACCOUNT,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.RETURN_ACCOUNT],
|
||||
value: '40702.810.8.21644442469',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.COMMISSION_ACCOUNT,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.COMMISSION_ACCOUNT],
|
||||
value: '40702.810.8.21644442469',
|
||||
description: 'Комиссия: 0.4% с транзакции, но не более 1 500 ₽',
|
||||
},
|
||||
{
|
||||
name: SBP_CONTRACT_FIELDS.ACCOUNTS,
|
||||
label: SBP_CONTRACT_FIELDS_TO_LABELS[SBP_CONTRACT_FIELDS.ACCOUNTS],
|
||||
value: '40702.810.8.21644442469; 40702.810.8.21644442469; 40702.810.8.21644442469',
|
||||
description: '',
|
||||
},
|
||||
];
|
||||
|
||||
export { ORGANIZATION_DATA, CONTRACT_DATA, ACCOUNTS_DATA };
|
||||
@@ -0,0 +1 @@
|
||||
export { SbpContracts } from './ui';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './types';
|
||||
@@ -0,0 +1,17 @@
|
||||
enum SBP_CONTRACT_FIELDS {
|
||||
ORG_SHORT_NAME = 'shortName',
|
||||
INN = 'innKio',
|
||||
OGRN = 'ogrnOgrip',
|
||||
KPP = 'kpp',
|
||||
CONTRACT_NUMBER = 'contractNumber',
|
||||
CONTRACT_TYPE = 'contractType',
|
||||
LEGAL_ID = 'legalId',
|
||||
MCC_NAME = 'mccName',
|
||||
SIGN_DATE = 'signDate',
|
||||
START_DATE = 'startDate',
|
||||
RETURN_ACCOUNT = 'returnAccount',
|
||||
COMMISSION_ACCOUNT = 'commissionAccount',
|
||||
ACCOUNTS = 'accounts',
|
||||
}
|
||||
|
||||
export { SBP_CONTRACT_FIELDS };
|
||||
@@ -0,0 +1,21 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const SbpContractsLayout = styled.div(({ theme }) => ({
|
||||
backgroundColor: theme.colors.bg.primary,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: '16px',
|
||||
padding: '0 24px',
|
||||
width: '100%',
|
||||
gap: '40px',
|
||||
}));
|
||||
|
||||
const SbpContractsButtonsLayout = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export { SbpContractsLayout, SbpContractsButtonsLayout };
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { Select } from '@fractal-ui/composites';
|
||||
import { Button } from '@fractal-ui/core';
|
||||
import { ACCOUNTS_DATA, CONTRACT_DATA, LOCALIZATION, ORGANIZATION_DATA } from '../constants';
|
||||
import * as S from './SbpContracts.styles';
|
||||
import { useOrganizationsSelect } from '@/shared/lib/hooks/useOrganizationsSelect';
|
||||
import { SbpDescriptionList } from '@/shared/ui/DescriptionList';
|
||||
|
||||
const SbpContracts = () => {
|
||||
const { onOrganizationChange, organizationsOptions, selectedOrganization } = useOrganizationsSelect();
|
||||
|
||||
return (
|
||||
<S.SbpContractsLayout>
|
||||
<Select
|
||||
name="Sbp_contracts_organizations"
|
||||
options={organizationsOptions}
|
||||
value={selectedOrganization}
|
||||
width="50%"
|
||||
onChange={onOrganizationChange}
|
||||
/>
|
||||
<SbpDescriptionList dataName="sbp-org-data" items={ORGANIZATION_DATA} title={LOCALIZATION.ORG_DATA_TITLE} />
|
||||
<SbpDescriptionList dataName="sbp-contract-data" items={CONTRACT_DATA} title={LOCALIZATION.CONTRACT_DATA_TITLE} />
|
||||
<SbpDescriptionList dataName="sbp-accounts-data" items={ACCOUNTS_DATA} title={LOCALIZATION.ACCOUNTS_DATA_TITLE} />
|
||||
<S.SbpContractsButtonsLayout>
|
||||
<Button dataAction="sbp-details" variant="blue">
|
||||
{LOCALIZATION.SBP_DETAILS_BUTTON}
|
||||
</Button>
|
||||
<Button dataAction="add-accounts">{LOCALIZATION.ADD_SBP_ACCOUNTS_BUTTON}</Button>
|
||||
</S.SbpContractsButtonsLayout>
|
||||
</S.SbpContractsLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export { SbpContracts };
|
||||
@@ -0,0 +1 @@
|
||||
export { SbpContracts } from './SbpContracts';
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Text } from '@fractal-ui/styling';
|
||||
import { Cell, CELL_TYPE } from '@fractal-ui/table';
|
||||
import { documentStatusTypeToBadgeType } from '../lib';
|
||||
import type { TransactionsHistory } from '../model';
|
||||
import { SBP_OPERATIONS_HISTORY_FIELDS, TRANSACTIONS_HISTORY_STATUS, TRANSACTIONS_HISTORY_TYPE } from '../model';
|
||||
import { LOCALIZATION } from './localization';
|
||||
|
||||
const TRANSACTION_REQUEST_TYPE_OPTIONS = {
|
||||
[TRANSACTIONS_HISTORY_TYPE.PAYMENT_BACK]: LOCALIZATION.RETURN_TEXT,
|
||||
[TRANSACTIONS_HISTORY_TYPE.PAYOUT_FROM_MERCHANT]: LOCALIZATION.TRANSFER_TEXT,
|
||||
[TRANSACTIONS_HISTORY_TYPE.INCOMING_PAYMENT]: LOCALIZATION.INCOMING_PAYMENT,
|
||||
};
|
||||
|
||||
const TRANSACTION_REQUEST_CLIENT_STATUS_LABEL = {
|
||||
[TRANSACTIONS_HISTORY_STATUS.IN_PROGRESS]: LOCALIZATION[TRANSACTIONS_HISTORY_STATUS.IN_PROGRESS],
|
||||
[TRANSACTIONS_HISTORY_STATUS.CANCELLED]: LOCALIZATION[TRANSACTIONS_HISTORY_STATUS.CANCELLED],
|
||||
[TRANSACTIONS_HISTORY_STATUS.PERFORMED]: LOCALIZATION[TRANSACTIONS_HISTORY_STATUS.PERFORMED],
|
||||
[TRANSACTIONS_HISTORY_STATUS.DRAFT]: LOCALIZATION[TRANSACTIONS_HISTORY_STATUS.DRAFT],
|
||||
};
|
||||
|
||||
const OPERATIONS_HISTORY_COLUMNS = [
|
||||
{
|
||||
id: SBP_OPERATIONS_HISTORY_FIELDS.AMOUNT,
|
||||
label: LOCALIZATION[SBP_OPERATIONS_HISTORY_FIELDS.AMOUNT],
|
||||
name: SBP_OPERATIONS_HISTORY_FIELDS.AMOUNT,
|
||||
headerType: 'string',
|
||||
Cell({ amount }: TransactionsHistory) {
|
||||
return <Cell cellName={SBP_OPERATIONS_HISTORY_FIELDS.AMOUNT} cellType={CELL_TYPE.STRING} text={amount?.value} />;
|
||||
},
|
||||
hasSort: true,
|
||||
},
|
||||
{
|
||||
id: SBP_OPERATIONS_HISTORY_FIELDS.DOC_DATE,
|
||||
label: LOCALIZATION[SBP_OPERATIONS_HISTORY_FIELDS.DOC_DATE],
|
||||
name: SBP_OPERATIONS_HISTORY_FIELDS.DOC_DATE,
|
||||
headerType: 'string',
|
||||
Cell({ transactionTime }: TransactionsHistory) {
|
||||
return (
|
||||
<Cell
|
||||
cellName={SBP_OPERATIONS_HISTORY_FIELDS.DOC_DATE}
|
||||
cellType={CELL_TYPE.STRING}
|
||||
subText={transactionTime.toDateString()}
|
||||
text={transactionTime.toDateString()}
|
||||
/>
|
||||
);
|
||||
},
|
||||
hasSort: true,
|
||||
defaultCanSort: true,
|
||||
sortDescFirst: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: SBP_OPERATIONS_HISTORY_FIELDS.MERCHANT_NAME,
|
||||
label: LOCALIZATION[SBP_OPERATIONS_HISTORY_FIELDS.MERCHANT_NAME],
|
||||
name: SBP_OPERATIONS_HISTORY_FIELDS.MERCHANT_NAME,
|
||||
headerType: 'string',
|
||||
Cell({ merchant, isB2b }: TransactionsHistory) {
|
||||
return (
|
||||
<Cell
|
||||
cellName={SBP_OPERATIONS_HISTORY_FIELDS.MERCHANT_ID}
|
||||
cellType={CELL_TYPE.STRING}
|
||||
subText={isB2b ? merchant.id : ''}
|
||||
text={merchant?.name}
|
||||
/>
|
||||
);
|
||||
},
|
||||
hasSort: false,
|
||||
},
|
||||
{
|
||||
id: SBP_OPERATIONS_HISTORY_FIELDS.DOC_TYPE,
|
||||
label: LOCALIZATION[SBP_OPERATIONS_HISTORY_FIELDS.DOC_TYPE],
|
||||
name: SBP_OPERATIONS_HISTORY_FIELDS.DOC_TYPE,
|
||||
headerType: 'string',
|
||||
Cell({ transactionType }: TransactionsHistory) {
|
||||
return (
|
||||
<Cell
|
||||
cellName={SBP_OPERATIONS_HISTORY_FIELDS.DOC_TYPE}
|
||||
cellType={CELL_TYPE.STRING}
|
||||
text={TRANSACTION_REQUEST_TYPE_OPTIONS[transactionType]}
|
||||
/>
|
||||
);
|
||||
},
|
||||
hasSort: true,
|
||||
width: 130,
|
||||
},
|
||||
{
|
||||
id: SBP_OPERATIONS_HISTORY_FIELDS.STATUS,
|
||||
label: LOCALIZATION[SBP_OPERATIONS_HISTORY_FIELDS.STATUS],
|
||||
name: SBP_OPERATIONS_HISTORY_FIELDS.STATUS,
|
||||
headerType: 'string',
|
||||
Cell({ status }: { status: TRANSACTIONS_HISTORY_STATUS }) {
|
||||
return (
|
||||
<Cell badgeType={documentStatusTypeToBadgeType(status)} cellName={SBP_OPERATIONS_HISTORY_FIELDS.STATUS} cellType={CELL_TYPE.STATUS}>
|
||||
<Text.P3Short>{TRANSACTION_REQUEST_CLIENT_STATUS_LABEL[status]}</Text.P3Short>
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
hasSort: true,
|
||||
},
|
||||
];
|
||||
|
||||
export { OPERATIONS_HISTORY_COLUMNS, TRANSACTION_REQUEST_TYPE_OPTIONS };
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './constants';
|
||||
export * from './mocks';
|
||||
export * from './localization';
|
||||
@@ -0,0 +1,38 @@
|
||||
import { SBP_OPERATIONS_HISTORY_FIELDS } from '../model';
|
||||
import { TRANSACTION_REQUEST_CLIENT_STATUS } from '@/shared/models';
|
||||
|
||||
const LOCALIZATION = {
|
||||
[TRANSACTION_REQUEST_CLIENT_STATUS.DRAFT]: 'Черновик',
|
||||
[SBP_OPERATIONS_HISTORY_FIELDS.DOC_TYPE]: 'Операция',
|
||||
[SBP_OPERATIONS_HISTORY_FIELDS.DOC_DATE]: 'Дата',
|
||||
[SBP_OPERATIONS_HISTORY_FIELDS.CONTRACTOR]: 'Контрагент',
|
||||
[SBP_OPERATIONS_HISTORY_FIELDS.CONTRACTOR_BANK]: 'Банк контрагента',
|
||||
[SBP_OPERATIONS_HISTORY_FIELDS.AMOUNT]: 'Сумма',
|
||||
[SBP_OPERATIONS_HISTORY_FIELDS.MERCHANT_ID]: 'Мерчант',
|
||||
[SBP_OPERATIONS_HISTORY_FIELDS.STATUS]: 'Статус',
|
||||
[SBP_OPERATIONS_HISTORY_FIELDS.MERCHANT_NAME]: 'Наименование мерчанта',
|
||||
PERFORMED: 'Исполнен',
|
||||
CANCELLED: 'Отменён',
|
||||
IN_PROGRESS: 'В обработке',
|
||||
EXPANDED_ROW_RECIPIENT_ACCOUNT: 'Счёт получателя',
|
||||
EXPANDED_ROW_SENDER_ACCOUNT: 'Счёт отправителя',
|
||||
EXPANDED_ROW_CONTRACTOR_NAME: 'Иия контрагента',
|
||||
EXPANDED_ROW_CONTRACTOR_ADDRESS: 'Адрес контрагента',
|
||||
EXPANDED_ROW_CONTRACTOR_BANK: 'Банк контрагента',
|
||||
EXPANDED_ROW_PAYMENT_PURPOSE: 'Назначение платежа',
|
||||
EXPANDED_ROW_TRANSACTION_DATE: 'Дата входящей операции',
|
||||
EXPANDED_ROW_TRANSACTION_AMOUNT: 'Сумма входящей операции',
|
||||
EXPANDED_ROW_TRANSACTION_COMMISSION: 'Комиссия',
|
||||
EXPANDED_ROW_TRANSACTION_TRANSACTION_ID: 'ID транзакции',
|
||||
EXPANDED_ROW_TRANSACTION_QR_ID: 'ID QR-кода',
|
||||
EXPANDED_ROW_TRANSACTION_INCOME_CODE: 'Код вида дохода',
|
||||
EXPANDED_ROW_TRANSACTION_BIC: 'БИК банка',
|
||||
EXPANDED_ROW_TRANSACTION_AGENT: 'Транзакция при участии Агента ТСП',
|
||||
RETURN_TEXT: 'Возврат',
|
||||
TRANSFER_TEXT: 'Перевод',
|
||||
INCOMING_PAYMENT: 'Входящий платёж',
|
||||
INN_TEXT: 'ИНН',
|
||||
BIC_TEXT: 'БИК',
|
||||
};
|
||||
|
||||
export { LOCALIZATION };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user