Загрузка данных
shkinderdv@ovdi-nfcoff-087 MINGW64 ~/moex-terminal-front (TRADERADAR-14718-E)
$ git commit -am "TRADERADAR-14718: replace icons with the letter E"
> moex_terminal_frontend@1.0.0 staged-check
> bash ./scripts/diff-check.sh --staged
--- Eslint check result: ---
C:\Users\ShkinderDV\moex-terminal-front\src\components\ExpendRowButton\index.tsx
2:1 error 'rc-table' should be listed in the project's dependencies. Run 'npm i -S rc-table' to add it import/no-extraneous-dependencies
23:5 error Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all react/jsx-no-useless-fragment
C:\Users\ShkinderDV\moex-terminal-front\src\uikit\Multiselect\index.tsx
73:11 warning Unexpected console statement no-console
C:\Users\ShkinderDV\moex-terminal-front\src\widgets\Fixings\index.tsx
141:35 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
142:42 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
C:\Users\ShkinderDV\moex-terminal-front\src\widgets\TradingResult\components\Repo\repo-table\component\CustomGroupCell\customGroupCell.tsx
14:1 warning `@uikit/Icon` import should occur before import of `@utils/decimalNumberFormatter` import/order
52:25 error Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “CustomGroupCell” and pass data as props. If you want to allow component creation in props, set allowAsProps option to true react/no-unstable-nested-components
C:\Users\ShkinderDV\moex-terminal-front\src\widgets\TradingResult\components\Repo\repo-table\utils\groups.tsx
12:51 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
36:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
48:15 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
48:38 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
59:56 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
63:33 error Unsafe arithmetic operation on optional chaining. It can result in NaN no-unsafe-optional-chaining
63:77 error Unsafe arithmetic operation on optional chaining. It can result in NaN no-unsafe-optional-chaining
68:12 error Unsafe usage of optional chaining. If it short-circuits with 'undefined' the evaluation will throw TypeError no-unsafe-optional-chaining
94:3 error iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations no-restricted-syntax
✖ 16 problems (14 errors, 2 warnings)
0 errors and 1 warning potentially fixable with the `--fix` option.
import cn from 'classnames';
import { RenderExpandIconProps } from 'rc-table/lib/interface';
import React from 'react';
import { Icon } from '@uikit/Icon';
import styles from './styles.module.scss';
/** Используется только в индикативных котировках. Провести анализ на общий табличный компонент */
export const ExpandRowButton = <T extends Record<string, unknown>>({
onExpand,
record,
expanded,
}: RenderExpandIconProps<T>) =>
record?.children ? (
<div
className={cn(styles.expandIconWrapper, expanded && styles.rotated)}
onClick={(e: React.MouseEvent<HTMLElement>) => onExpand(record, e)}
>
<Icon variant="chevron-down" />
</div>
) : (
<></>
);
import { Dropdown, Form, MenuProps } from 'antd';
import locale from 'antd/es/date-picker/locale/ru_RU';
import dayjs from 'dayjs';
import 'dayjs/locale/ru';
import React, { FC, useState } from 'react';
import { DatePickerLight } from '@components/DatePicker';
import { RoundedClose } from '@components/Icons/RoundedClose';
import { Table, useTableController } from '@components/Table';
import { TextRegular } from '@components/TextMad';
import { Footnote } from '@components/TextUI';
import WidgetContentWrapper from '@components/WidgetContentWrapper';
import WidgetHeader from '@components/WidgetHeader';
import features from '@features';
import { useSettingsComponent } from '@hooks/table/useSettingsComponent';
import { Button } from '@uikit/Button';
import { Icon } from '@uikit/Icon';
import { FixingsValuesChart } from '@widgets/Fixings/FixingsValuesChart';
import { FixingsValuesTable } from '@widgets/Fixings/FixingsValuesTable';
import { FixingData, useFixingsFacade } from '@widgets/Fixings/useFixingsFacade';
import { VirtualTableColumnType } from 'types/TableColumns';
import { WidgetContentBasicProps } from 'types/Widgets';
import './fixings.scss';
dayjs.locale('ru');
export interface FixingsProps {
state: {
firstColumns: VirtualTableColumnType<FixingData>[];
};
}
type WidgetFixingsProps = WidgetContentBasicProps<FixingsProps>;
export const Fixings: FC<WidgetFixingsProps> = function (props: WidgetFixingsProps): React.JSX.Element {
const {
selectedFixing,
filteredSelectedFixing,
selectedRow,
fixingsData,
fromDate,
toDate,
onCloseButtonClick,
onChangeDate,
columns,
} = useFixingsFacade();
const [controller, requester] = useTableController();
const [activeOption, setActiveOption] = useState('table');
const { settingsComponent, setSettingsComponent } = useSettingsComponent();
const dropdownItems = [
{
label: (
<div className="fixing-button">
<div className="icon">
<Icon variant="file-up" />
</div>
Экспортировать в Excel
</div>
),
key: '1',
},
] as MenuProps;
const settingsItems = [
{
key: '1',
label: settingsComponent,
},
];
const { widgetId } = props;
return (
<>
<WidgetHeader
showWidgetSetting
items={settingsItems}
titleExtra={selectedRow && <Footnote style={{ display: 'flex' }}>{selectedRow.ticker}</Footnote>}
handlerSaveAsExcel={features.hideSaveAsExcel ? undefined : controller?.saveAsExcel}
{...props}
/>
<WidgetContentWrapper
{...props}
scrollable={false}
>
{selectedRow && (
<div className="header">
<div className="header-btn-row">
<Button
variant={activeOption === 'table' ? 'filled-primary' : 'outlined-secondary'}
onClick={() => setActiveOption('table')}
text="Таблица"
/>
<Button
variant={activeOption === 'chart' ? 'filled-primary' : 'outlined-secondary'}
onClick={() => setActiveOption('chart')}
text="График"
/>
</div>
{selectedRow && (
<div className="header-daterange">
<Form.Item label="Значения с">
<DatePickerLight
className="header-daterange-datepicker"
locale={locale}
format="DD-MM-YYYY"
onChange={(date) => onChangeDate(date, true)}
defaultValue={dayjs(fromDate)}
/>
</Form.Item>
<Form.Item label="до">
<DatePickerLight
className="header-daterange-datepicker"
locale={locale}
format="DD-MM-YYYY"
onChange={(date) => onChangeDate(date, false)}
defaultValue={dayjs(toDate)}
/>
</Form.Item>
</div>
)}
<div
onClick={onCloseButtonClick}
className="icon"
>
<RoundedClose />
</div>
</div>
)}
{!selectedRow && (
<div className="fixing">
<Table
widgetId={widgetId}
columns={columns as any[]}
dataSource={fixingsData as any[]}
setSettingsComponent={setSettingsComponent}
setController={requester}
/>
</div>
)}
{true && (
<Dropdown
menu={dropdownItems}
trigger={['contextMenu']}
>
<div>
<FixingsValuesTable
fixingSelectedValue={filteredSelectedFixing || selectedFixing}
widgetId={widgetId}
/>
</div>
</Dropdown>
)}
{selectedRow && activeOption === 'chart' && (
<>
<TextRegular className="fixing-title">{selectedRow.caption}</TextRegular>
<FixingsValuesChart fixingSelectedValue={filteredSelectedFixing || selectedFixing} />
</>
)}
</WidgetContentWrapper>
</>
);
};
import { Dropdown } from 'antd';
import React, { FC, useState } from 'react';
import { CountIcon } from '@components/Table/components/CountIcon';
import { decimalNumbersFormatter } from '@utils/decimalNumberFormatter';
import { onFilterHandlerT } from '../../../types/group';
import { CustomGroupCellMenu } from './customGroupCellMenu';
import styles from './styles.module.scss';
import { Icon } from '@uikit/Icon';
import './styles-global.module.scss';
type CustomGroupCellType = {
isShowChildrenCount: boolean;
childrenCount: number;
value: string;
valueVariants?: string[];
index?: number;
onFilterHandler?: onFilterHandlerT;
groupKey: string;
groupValue: string;
};
export const CustomGroupCell: FC<CustomGroupCellType> = ({
valueVariants,
onFilterHandler,
value,
isShowChildrenCount,
childrenCount,
groupKey,
groupValue,
}) => {
const [menuOpen, setMenuOpen] = useState(false);
const withDropdown = !!(valueVariants && onFilterHandler);
const onSelectHandler: onFilterHandlerT = (...params) => {
onFilterHandler?.(...params);
setMenuOpen(false);
};
if (withDropdown) {
return (
<Dropdown
open={menuOpen}
onOpenChange={(open) => {
setMenuOpen(open);
}}
dropdownRender={() => (
<CustomGroupCellMenu
variants={valueVariants}
onSelectHandler={onSelectHandler}
selectedItemKey={value}
groupKey={groupKey}
groupValue={groupValue}
/>
)}
trigger={['click']}
>
<div className={styles.filteredGroupCellWrapper}>
{decimalNumbersFormatter(value)}
{isShowChildrenCount && <CountIcon count={childrenCount} />}
<Icon
variant="chevron-down"
style={{ cursor: 'pointer' }}
/>
</div>
</Dropdown>
);
}
return (
<div className="groupCellWrapper">
{decimalNumbersFormatter(value)}
{isShowChildrenCount && <CountIcon count={childrenCount} />}
</div>
);
};
import React from 'react';
import { Icon } from '@uikit/Icon';
import { BUTTON_MORE_ROW_KEY } from '@widgets/TradingResult/components/Repo/constants';
import { LoadMoreButton } from '@widgets/TradingResult/components/Repo/repo-table/component/loadMoreButton';
import { DataInfoByGroup } from '@widgets/TradingResult/components/Repo/repo-table/logic/table/useLoadGroupSubRowsData';
import { GroupFiltersStruct } from '@widgets/TradingResult/components/Repo/types/group';
import { Group } from 'types/MonitorRepo';
import repoTableStyles from '../styles/repoTable.module.scss';
export const createInitialGroupFilters = (record: any): GroupFiltersStruct => {
const initialGroupFilters = Object.keys(record.rowData)?.reduce((acc, dataKey) => {
const dataValue = record.rowData[dataKey];
const isObject = typeof dataValue === 'object' && !Array.isArray(dataValue);
if (isObject) {
const groupValue = record.key;
const filterName = dataKey;
const filterValue = record.rowData?.[filterName]?.value;
return {
...acc,
[groupValue]: {
...acc?.[groupValue],
[filterName]: filterValue,
},
};
}
return acc;
}, {} as GroupFiltersStruct);
return initialGroupFilters as GroupFiltersStruct;
};
export const ExpandIcon = ({ onExpand, record }: any) =>
record.children && (
<div
className={repoTableStyles.expandIconWrapper}
onClick={(e: React.MouseEvent<HTMLElement>) => onExpand(record, e)}
>
<Icon variant="chevron-down" />
</div>
);
export const unifyGroupAndChildrenRows = (
groupSubRowsData: Record<string, DataInfoByGroup>,
groupsData: any[] | Record<string, any>[],
buttonMargin: number,
loadMore: (groupValue: string) => Promise<void>,
) => {
// ищем значения групп по которым существуют данные подстрок
const groupValues = Object.keys(groupSubRowsData);
if (groupValues.length === 0) {
return groupsData;
}
groupValues?.forEach((groupValue) => {
const searchedGroup = groupsData?.find((groupData: any) => groupData.key === groupValue);
if (searchedGroup) {
const isEmptyChildrenRows = groupSubRowsData?.[groupValue]?.data?.length === 0;
const loadedRowsNumber = (groupSubRowsData?.[groupValue]?.page + 1) * groupSubRowsData?.[groupValue]?.size;
const itemsCount = groupSubRowsData?.[groupValue]?.data?.length;
const totalValues = groupSubRowsData?.[groupValue]?.total || 0;
// подгруженные данные репо по группировке заносим в общую структуру групп как children
searchedGroup.children = [
...groupSubRowsData[groupValue]?.data,
{
key: `${BUTTON_MORE_ROW_KEY}${groupValue}`,
forceToSort: true,
secId: (
<LoadMoreButton
styles={{ marginLeft: buttonMargin }}
onClick={() => loadMore(groupValue)}
loaded={!isEmptyChildrenRows ? Math.min(loadedRowsNumber, totalValues, itemsCount) : 0}
shouldDisplayButton={itemsCount === loadedRowsNumber}
total={!isEmptyChildrenRows ? totalValues || loadedRowsNumber : 0}
/>
),
},
];
}
});
return [...(groupsData || [])];
};
/** Обновляем только пересчитываемые значения */
export const refreshRowAggValues = (oldGroup: Group, newGroup: Group): Group => {
const valuesToNeedRefresh = ['aggPrice', 'aggQuantity', 'aggAccint', 'aggValue', 'aggRepoRate', 'aggRepoValue'];
let refreshRow = oldGroup;
for (const key of Object.keys(refreshRow)) {
const typedKey = key as keyof Group;
if (valuesToNeedRefresh.includes(typedKey)) {
const refreshedValue = newGroup[typedKey];
refreshRow = { ...refreshRow, [typedKey]: refreshedValue };
}
}
return refreshRow;
};
export type KeysForGroupingVariants = 'SECID' | 'CURRENCY' | 'REPORATE' | 'REPOTERM' | 'BOARDID';
export type GroupFiltersStruct = {
[groupValue: string]: {
[filterName: string]: string; // активное значение фильтра
};
};
export type onFilterHandlerT = (groupValue: string, filterName: string, filterValue: string) => void;