Загрузка данных


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;