Загрузка данных
import React, { FC, lazy, useContext, useState } from 'react';
import { DNDWrapper } from '@components/DNDWrapper';
import { InstrumentSearch } from '@components/InstrumentSearch';
import WidgetContentWrapper from '@components/WidgetContentWrapper';
import WidgetHeader from '@components/WidgetHeader';
import { useDropInstrument } from '@hooks/dnd';
import { WidgetEnvContext } from '@terminal/desktop/workspaces/default/components/Widget/context';
import { useWidgetHeaderName } from '@utils/useWidgetName';
import useChartComponentFacade from './hooks/useChartComponentFacade';
import type { ChartContainerProps } from './types';
const MoexChart = lazy(() => import('./components/MoexChart/MoexChart'));
export const Chart: FC<ChartContainerProps> = function (props): JSX.Element {
const indicativeData = props.widgetContentProps?.indicativeData;
const {
dropDownOpen,
setDropdownOpen,
currentInstrument,
setIsWidgetHeaderContextMenuOpen,
isWidgetHeaderContextMenuOpen,
onDropInstruments,
addInstrumentFromModal,
isOver,
} = useChartComponentFacade(props);
/* на дроп обновляем название бумаги в сторе
и делаем апдейт на бэк, чтобы сохранить изменения
при обновлении страницы */
const { dropRef } = useDropInstrument((dragData) => {
onDropInstruments(dragData.properties.issKey, true);
});
const [isOpenEmptyAction, setIsOpenEmptyAction] = useState<boolean>(false);
const openInstrumentModal = () => {
setIsOpenEmptyAction(true);
setIsWidgetHeaderContextMenuOpen(false);
};
// TODO: Зависит от сеток
const { isDraggedOver } = useContext(WidgetEnvContext);
const contractsInstrumentName = useWidgetHeaderName(currentInstrument);
const instrumentName = indicativeData
? `${indicativeData.instrumentName} ${indicativeData.settlement} - ${indicativeData.firmName}`
: contractsInstrumentName;
return (
<DNDWrapper
ref={dropRef}
isOver={isOver}
canDrop
>
<WidgetHeader
{...props}
dropdownOpen={dropDownOpen}
setDropdownOpen={setDropdownOpen}
itemsSearchIcon={[true]}
handlerSaveAsExcel={null}
addToWidgetNamePrefix={instrumentName}
setIsOpenContextMenuFromWidget={setIsWidgetHeaderContextMenuOpen}
isOpenContextMenuFromWidget={isWidgetHeaderContextMenuOpen}
openInstrumentsModal={openInstrumentModal}
/>
<WidgetContentWrapper {...props}>
<div
style={{
display: 'flex',
flexDirection: 'column',
height: '100%',
pointerEvents: isDraggedOver ? 'none' : 'inherit',
}}
>
<MoexChart
fullName={currentInstrument}
indicativeData={indicativeData}
widgetId={props.widgetId}
addInstrumentFromModal={addInstrumentFromModal}
/>
{isOpenEmptyAction && (
<InstrumentSearch
setOpen={setIsOpenEmptyAction}
isOpen={isOpenEmptyAction}
variant="single"
widgetId={props.widgetId}
// withNRD={false}
addInstruments={addInstrumentFromModal}
/>
)}
</div>
</WidgetContentWrapper>
</DNDWrapper>
);
};
import React from 'react';
import { HelpCenter } from '@modules/HelpCenter';
import { Instruction } from '@modules/Instruction';
import { HelpWidget } from '@modules/WidgetHelp/components';
import AboutInstrument from '@widgets/AboutInstrument/AboutInstrument';
import { AboutInstrumentProps } from '@widgets/AboutInstrument/types';
import { BondScreener, BondScreenerProps } from '@widgets/BondScreener';
import { Chart } from '@widgets/Chart';
import { WidgetProperties as ChartWidgetProperties } from '@widgets/Chart/properties/types';
import { Contacts, ContactsProps } from '@widgets/Contacts';
import { CorpActions } from '@widgets/CorpActions';
import { Curves } from '@widgets/Curves';
import { DepositCcpTables, DepositsCcpTablesProps } from '@widgets/DepositCcpTables';
import { DraftBrokerSpfi } from '@widgets/DraftBrokerSpfi';
import { Fixings, FixingsProps } from '@widgets/Fixings';
import { Futoi } from '@widgets/Futoi';
import { Glass } from '@widgets/Glass';
import { HHI } from '@widgets/HHI';
import { IndicativeQuotes } from '@widgets/IndicativeQuotes';
import { IssuerCard } from '@widgets/IssuerCard';
import { MacroDataWidget } from '@widgets/MacroData';
import { MarketMap } from '@widgets/MarketMap';
import { MarketMapPropsBasic } from '@widgets/MarketMap/types';
import { News } from '@widgets/News';
import { NoTradeChat } from '@widgets/NoTradeChat';
import NtbIndexes from '@widgets/ntb/Indexes/widget';
import { LogisticAuto, LogisticFreight } from '@widgets/ntb/Logistic';
import { OrdersAndDeals } from '@widgets/ntb/OrdersAndDeals';
import { NtbPositions } from '@widgets/ntb/Positions';
import { OrdersJournal } from '@widgets/OrdersJournal';
import { OrdersJournalProps } from '@widgets/OrdersJournal/types';
import { OTCTurnover } from '@widgets/OTCTurnover';
import { Quotes } from '@widgets/Quotes';
import { QuotesNTPro, QuotesNTProProps } from '@widgets/QuotesNTPro';
import { SpfiPrices } from '@widgets/SpfiPrices';
import { SwapCalculator } from '@widgets/SwapCalculator/SwapCalculator';
import { TestIFrame } from '@widgets/TestIFrame';
import TradeJournal from '@widgets/TradeJournal';
import TradeJournalDetails from '@widgets/TradeJournalDetails';
import { TWidgetProps } from '@widgets/TradeJournalDetails/types';
import { TradingResult, TradingResultProps } from '@widgets/TradingResult';
import { TurnoversProps, WidgetTurnovers } from '@widgets/Turnovers';
import { WidgetContentBasicProps, WidgetContentType } from 'types/Widgets';
import { ChoiserValue } from './types';
import type { GlassWidgetProperties } from '@widgets/Glass/properties/types';
export const WIDGETS_MAP: Record<WidgetContentType, ChoiserValue> = {
testIFrame: ({ id, properties, onWidgetContentClick }) => (
<TestIFrame
widgetId={id}
widgetContentProps={properties}
onWidgetContentClick={onWidgetContentClick}
/>
),
graphic: ({ id, properties, onWidgetContentClick }) => (
<Chart
widgetId={id}
widgetContentProps={properties as ChartWidgetProperties}
onWidgetContentClick={onWidgetContentClick}
/>
),
// TODO, удалить, как уберут на бэке
newsIss: ({ id, onWidgetContentClick }) => (
<News
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
/>
),
news: ({ id, onWidgetContentClick }) => (
<News
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
/>
),
glass: ({ id, properties, onWidgetContentClick }) => (
<Glass
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as GlassWidgetProperties}
/>
),
instruments: ({ id, properties, onWidgetContentClick }) => (
<Quotes
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as WidgetContentBasicProps}
/>
),
ntbOrdersAndDeals: ({ id, properties, onWidgetContentClick }) => (
<OrdersAndDeals
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
ntbPositions: ({ id, properties, onWidgetContentClick }) => (
<NtbPositions
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
ntbLogisticAuto: ({ id, properties, onWidgetContentClick }) => (
<LogisticAuto
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
ntbLogisticFreight: ({ id, properties, onWidgetContentClick }) => (
<LogisticFreight
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
ntbIndexes: ({ id, properties, onWidgetContentClick }) => (
<NtbIndexes
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
noTradeChat: ({ id, properties, onWidgetContentClick }) => (
<NoTradeChat
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
contacts: ({ id, properties, onWidgetContentClick }) => (
<Contacts
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as ContactsProps}
/>
),
aboutInstruments: ({ id, properties, onWidgetContentClick }) => (
<AboutInstrument
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as AboutInstrumentProps}
/>
),
fixings: ({ id, properties, onWidgetContentClick }) => (
<Fixings
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as FixingsProps}
/>
),
curves: ({ id, properties, onWidgetContentClick }) => (
<Curves
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
profitabilityAndDuration: ({ id, properties, onWidgetContentClick }) => (
<MarketMap
widgetId={id}
widgetContentProps={properties as MarketMapPropsBasic}
onWidgetContentClick={onWidgetContentClick}
/>
),
widgetTurnovers: ({ id, properties, onWidgetContentClick }) => (
<WidgetTurnovers
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as TurnoversProps}
/>
),
tradingResult: ({ id, properties, onWidgetContentClick }) => (
<TradingResult
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as TradingResultProps}
/>
),
quotesNTPro: ({ id, properties, onWidgetContentClick }) => (
<QuotesNTPro
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as QuotesNTProProps}
/>
),
instruction: ({ id, onWidgetContentClick }) => (
<Instruction
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
/>
),
macroData: ({ id, properties, onWidgetContentClick }) => ({
content: (
<MacroDataWidget
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
settings: {
minWidth: 480,
},
}),
techSupport: ({ id, properties, onWidgetContentClick }) => (
<HelpWidget
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
FUTOI: ({ id, onWidgetContentClick }) => (
<Futoi
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
/>
),
corporateActions: ({ id, onWidgetContentClick }) => (
<CorpActions
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
/>
),
HHI: ({ id, onWidgetContentClick, properties }) => (
<HHI
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as FixingsProps}
/>
),
otcTurnover: ({ id, onWidgetContentClick }) => (
<OTCTurnover
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
/>
),
screener: ({ id, onWidgetContentClick, properties }) => (
<BondScreener
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as BondScreenerProps}
/>
),
ordersJournal: ({ id, onWidgetContentClick, properties }) => (
<OrdersJournal
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as OrdersJournalProps}
/>
),
indicativeQuotes: ({ id, onWidgetContentClick, properties }) => (
<IndicativeQuotes
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as WidgetContentBasicProps}
/>
),
swapCalculator: ({ id, onWidgetContentClick }) => (
<SwapCalculator
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
/>
),
spfiPrices: ({ id, onWidgetContentClick, properties }) => (
<SpfiPrices
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
issuerCard: ({ id, onWidgetContentClick, properties }) => (
<IssuerCard
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
tradeJournal: ({ id, onWidgetContentClick, properties }) => ({
content: (
<TradeJournal
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
settings: {
minWidth: 700,
},
}),
draftBrokerSpfi: ({ id, onWidgetContentClick, properties }) => (
<DraftBrokerSpfi
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties}
/>
),
tradeJournalDetails: ({ id, onWidgetContentClick, properties }) => (
<TradeJournalDetails
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as TWidgetProps}
/>
),
depositCcpOrderTables: ({ id, onWidgetContentClick, properties }) => (
<DepositCcpTables
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as DepositsCcpTablesProps}
/>
),
depositCcpTradeTables: ({ id, onWidgetContentClick, properties }) => (
<DepositCcpTables
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as DepositsCcpTablesProps}
/>
),
depositCcpReferenceTables: ({ id, onWidgetContentClick, properties }) => (
<DepositCcpTables
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as DepositsCcpTablesProps}
/>
),
depositCcpRiskTables: ({ id, onWidgetContentClick, properties }) => (
<DepositCcpTables
widgetId={id}
onWidgetContentClick={onWidgetContentClick}
widgetContentProps={properties as DepositsCcpTablesProps}
/>
),
review: <div>222</div>,
helpCenter: <HelpCenter />,
};
import { Contract } from '@modules/contracts/types';
import { ChartIndicativeData } from '../types';
import type { Intervals, Timeframes } from 'moex-chart';
export type WidgetProperties = {
chartState: {
savedInstrument: Contract['issKey'] | null;
interval: string;
savedData?: string;
};
indicativeData?: ChartIndicativeData;
moexChartState?: {
initialInterval?: Intervals;
timeframe?: Timeframes;
savedData?: string;
};
};
import type { WidgetContentBasicProps } from 'types/Widgets';
import type { WidgetProperties } from './properties/types';
export type ChartIndicativeData = {
secId: string;
instrumentName: string;
settlement: string;
firmName: string;
key: string;
};
export type ChartContainerProps = WidgetContentBasicProps<WidgetProperties>;
import { useCallback, useEffect, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import { communicator } from '@core/comm';
import { useAppSelect } from '@hooks/useAppSelector';
import { CORPACTIONS_OPEN_EXESTED_WIDGET_EVENT, HIGHLIGHT_WIDGET_EVENT } from '@modules/widgets/shared';
import { addContentPropsToWidget, unbindWidgets } from '@store/slices/widgets';
import { getState } from '@store/store';
import { useWidgetsBind } from '@utils/hooks/useWidgetsBind';
import { DEFAULT_SYMBOL } from '../const';
import { useChartPublicContext } from './useChartPublicContext';
import type { WidgetProperties } from '../properties/types';
import type { ChartContainerProps } from '../types';
import type { Contract } from '@modules/contracts';
import type { Dispatch, SetStateAction } from 'react';
import type { Widget } from 'types/Widgets';
interface UseChartComponentFacadeReturn {
dropDownOpen: boolean;
setDropdownOpen: Dispatch<SetStateAction<boolean>>;
currentInstrument: string;
isWidgetHeaderContextMenuOpen: boolean;
setIsWidgetHeaderContextMenuOpen: Dispatch<SetStateAction<boolean>>;
onDropInstruments: (val: string, withUpdate?: boolean) => void;
addInstrumentFromModal: (instruments: Pick<Contract, 'issKey'>[]) => void;
isOver: boolean;
}
type SaveContentPropsOptions = Partial<WidgetProperties['chartState']> & {
withUpdate?: boolean;
cleanIndicativeData?: boolean;
};
export default function useChartComponentFacade(props: ChartContainerProps): UseChartComponentFacadeReturn {
const { widgetId } = props;
const dispatch = useDispatch();
const [isOver, setIsOver] = useState(false);
const [dropDownOpen, setDropdownOpen] = useState(false);
const [isWidgetHeaderContextMenuOpen, setIsWidgetHeaderContextMenuOpen] = useState(false);
const widgetProperties = useAppSelect(
(state) => state.widgets.widgets.find(({ id }) => id === widgetId)?.widgetContentProps,
) as WidgetProperties | undefined;
const initialInstrument = widgetProperties?.chartState?.savedInstrument ?? DEFAULT_SYMBOL;
// TODO временно реф из-за непредсказуемого изменения если используется useState,
// нужен рефакторинг и вернуть обратно useState
const currentInstrumentRef = useRef(initialInstrument);
const [currentInstrument, setCurrentInstrument] = useState(initialInstrument);
const { triggerRelatedWidgetsToUpdate, getMasterInstrumentFromPublicContext } = useWidgetsBind({
widgetId,
});
const triggerRelatedWidgetsToUpdateRef = useRef(triggerRelatedWidgetsToUpdate);
useEffect(() => {
triggerRelatedWidgetsToUpdateRef.current = triggerRelatedWidgetsToUpdate;
}, [triggerRelatedWidgetsToUpdate]);
const saveContentProps = useCallback(
(val: SaveContentPropsOptions): void => {
const { withUpdate, cleanIndicativeData, ...chartStateVal } = val;
const oldProps = (getState().widgets.widgets.find(({ id }) => id === widgetId) as Widget | undefined)
?.widgetContentProps;
if (!oldProps) {
return;
}
dispatch(
addContentPropsToWidget({
id: widgetId,
withoutSend: !withUpdate,
widgetContentProps: {
chartState: {
...oldProps.chartState,
...chartStateVal,
},
// при смене инструмента очищаем индикативные данные, переданные из виджета Индикативные котировки
indicativeData: cleanIndicativeData ? undefined : oldProps.indicativeData,
},
}),
);
},
[dispatch, widgetId],
);
const onInstrumentChange = useCallback(
(newVal: string, withUpdate?: boolean, unbind = true): void => {
if (!newVal) {
return;
}
setIsWidgetHeaderContextMenuOpen(false);
if (currentInstrumentRef.current === newVal) {
return;
}
currentInstrumentRef.current = newVal;
setCurrentInstrument(newVal);
if (unbind) {
dispatch(unbindWidgets({ widgetId }));
}
// при смене инструмента очищаем индикативные данные виджета график
// т.к. логика для графика индикатива построена на наличии в widgetContentProps данных indicativeData
saveContentProps({
savedInstrument: newVal,
withUpdate,
cleanIndicativeData: true,
});
triggerRelatedWidgetsToUpdateRef.current(newVal);
},
[dispatch, saveContentProps, widgetId],
);
const addInstrumentFromModal = useCallback(
(instruments: Pick<Contract, 'issKey'>[]) => {
const issKey = instruments[0]?.issKey;
if (!issKey) {
return;
}
onInstrumentChange(issKey);
},
[onInstrumentChange],
);
const onInstrumentChangeFromBind = useCallback(
(instrumentId: string) => {
onInstrumentChange(instrumentId, true, false);
},
[onInstrumentChange],
);
const onDropInstruments = useCallback(
(val: string, withUpdate?: boolean): void => {
onInstrumentChange(val, withUpdate);
},
[onInstrumentChange],
);
useChartPublicContext({
setCurrInstrument: onInstrumentChangeFromBind,
widgetId,
getMasterInstrumentFromPublicContext,
});
useEffect(() => {
triggerRelatedWidgetsToUpdateRef.current(currentInstrumentRef.current);
}, []);
useEffect(() => {
const unsubscribe = communicator.listen(
{
messageType: CORPACTIONS_OPEN_EXESTED_WIDGET_EVENT,
},
(message) => {
const issKey = (message as Record<number, string>)[widgetId];
if (issKey) {
addInstrumentFromModal([{ issKey }]);
}
},
);
const unsubscribeHighlighter = communicator.listen(
{
messageType: HIGHLIGHT_WIDGET_EVENT,
},
(message) => {
const typedMessage = message as Record<number, boolean>;
if (Object.keys(typedMessage).includes(String(widgetId))) {
setIsOver(typedMessage[widgetId]);
}
},
);
return () => {
unsubscribe();
unsubscribeHighlighter();
};
}, [addInstrumentFromModal, widgetId]);
return {
dropDownOpen,
setDropdownOpen,
currentInstrument,
isWidgetHeaderContextMenuOpen,
setIsWidgetHeaderContextMenuOpen,
onDropInstruments,
addInstrumentFromModal,
isOver,
};
}
import { useEffect, useMemo } from 'react';
import { useAppSelect } from '@hooks/useAppSelector';
import { Contract, useContracts } from '@modules/contracts';
import { filterByUniqIssKey } from '@utils/filterByUniqIssKey';
import { DEFAULT_SYMBOL } from '@widgets/Chart/const';
import { Widget } from 'types/Widgets';
interface UseChartPublicContextArg {
setCurrInstrument: (instrumentId: string) => void;
widgetId: Widget['id'];
getMasterInstrumentFromPublicContext: () => string | number | null | undefined;
}
interface UseChartPublicContextReturn {
issKey: Contract['issKey'] | undefined;
}
export function useChartPublicContext({
widgetId,
setCurrInstrument,
getMasterInstrumentFromPublicContext,
}: UseChartPublicContextArg): UseChartPublicContextReturn {
const publicContext = useAppSelect((state) => state.publicContext.publicContext);
const widget = useAppSelect((state) => state.widgets.widgets.find(({ id }) => id === widgetId));
const { contracts } = useContracts();
const instruments = useMemo(() => [...(contracts && filterByUniqIssKey(contracts, ['issKey']))], [contracts]);
const issKey = useMemo(() => {
const fieldValue = getMasterInstrumentFromPublicContext();
const instrKey = instruments.find((item) => item.issKey === fieldValue)?.issKey;
return instrKey;
// eslint-disable-next-line react-hooks/exhaustive-deps -- Посмотреть этот момент.
}, [publicContext, instruments]);
useEffect(() => {
const fieldValue = getMasterInstrumentFromPublicContext();
if (!fieldValue && widget?.master) {
setCurrInstrument(DEFAULT_SYMBOL);
return;
}
const instrKey = instruments.find((item) => item.issKey === fieldValue)?.issKey;
if (instrKey) {
setCurrInstrument(instrKey);
}
// eslint-disable-next-line react-hooks/exhaustive-deps -- Посмотреть этот момент.
}, [publicContext, instruments, widget?.externalProperties, setCurrInstrument]);
return {
issKey: issKey ?? undefined,
};
}
import isNil from 'lodash/isNil';
import { Intervals, MoexChart, Timeframes } from 'moex-chart';
import { useEffect, useRef, useState } from 'react';
import { useChangeProperties, useSelectProperties } from '@modules/widgetProperties';
import { ChartIndicativeData } from '@widgets/Chart/types';
import { WidgetProperties } from '../../../properties/types';
import { MOEX_CHART_CONFIG } from '../constants';
import { DataSourceProvider } from '../dataSourceProvide';
import type { __CompareManager__, IMoexChart } from 'moex-chart';
interface TUseMoexChartProps {
symbol: string;
indicativeData?: ChartIndicativeData;
}
export const useMoexChart = ({ symbol, indicativeData }: TUseMoexChartProps) => {
const moexChartState = useSelectProperties((wProps: Partial<WidgetProperties>) => wProps.moexChartState);
const { updateProperties } = useChangeProperties<WidgetProperties>();
const [isCompareOpen, setIsCompareOpen] = useState(false);
const [isSymbolSearchOpen, setIsSymbolSearchOpen] = useState(false);
const containerRef = useRef<HTMLDivElement | null>(null);
const chartRef = useRef<MoexChart | null>(null);
const compareManagerRef = useRef<null | __CompareManager__>(null);
const currentSymbolRef = useRef(symbol);
const timeframeRef = useRef<Timeframes | undefined>(moexChartState?.timeframe);
const savedDataRef = useRef<string | undefined>(moexChartState?.savedData);
const initialIntervalRef = useRef<Intervals | undefined>(moexChartState?.initialInterval);
const updateTimeframeRef = useRef<((tf: Timeframes) => void) | null>(null);
useEffect(() => {
if (!symbol || currentSymbolRef.current === symbol) {
return;
}
currentSymbolRef.current = symbol;
chartRef.current?.setSymbol(symbol);
}, [symbol]);
const setMainSymbol = (nextSymbol: string) => {
const normalizedSymbol = nextSymbol.trim();
if (!normalizedSymbol || currentSymbolRef.current === normalizedSymbol) {
return;
}
currentSymbolRef.current = normalizedSymbol;
chartRef.current?.setSymbol(normalizedSymbol);
};
useEffect(() => {
timeframeRef.current = moexChartState?.timeframe;
savedDataRef.current = moexChartState?.savedData;
}, [moexChartState]);
updateTimeframeRef.current = (timeframe: Timeframes) => {
if (timeframeRef.current === timeframe) {
return;
}
timeframeRef.current = timeframe;
updateProperties((state) => {
state.moexChartState = {
...state.moexChartState,
timeframe,
};
});
};
const saveSnapshot = () => {
const snapshot = chartRef.current?.getSnapshot();
if (!snapshot) {
return;
}
const savedData = JSON.stringify({
...snapshot,
charts: snapshot.charts.map((chart) => ({
...chart,
panes: chart.panes.map((pane) => ({
...pane,
indicators: pane.indicators.map((indicator) => {
const compareSeries = indicator.config?.series[0];
return {
...indicator,
dataSource: undefined, // dataSource пока не среиализуем
config:
indicator.indicatorType === undefined && indicator.config?.label && compareSeries
? {
label: indicator.config.label,
newPane: indicator.config.newPane,
series: [
{
name: compareSeries.name,
seriesOptions: {
priceScaleId: compareSeries.seriesOptions?.priceScaleId,
},
},
],
}
: undefined,
};
}),
})),
})),
});
savedDataRef.current = savedData;
updateProperties((state) => {
state.moexChartState = {
...state.moexChartState,
timeframe: timeframeRef.current || Timeframes['1m'],
savedData,
};
});
};
const applySnapshot = () => {
if (!savedDataRef.current || !chartRef.current) {
return;
}
const savedSnapshot = JSON.parse(savedDataRef.current) as IMoexChart['snapshot'];
const timeframe = timeframeRef.current || Timeframes['1m'];
chartRef.current.setSnapshot({
...savedSnapshot,
charts: savedSnapshot.charts.map((chartSnapshot) => ({
...chartSnapshot,
symbol: currentSymbolRef.current,
timeframe,
})),
});
compareManagerRef.current = chartRef.current.getCompareManager();
};
useEffect(() => {
const container = containerRef.current;
if (!container) {
return undefined;
}
const timeframe = timeframeRef.current || Timeframes['1m'];
const savedSnapshot = savedDataRef.current
? (JSON.parse(savedDataRef.current) as IMoexChart['snapshot'])
: MOEX_CHART_CONFIG.snapshot;
const dataProvider = new DataSourceProvider();
const chart = new MoexChart({
...MOEX_CHART_CONFIG,
container,
snapshot: {
...savedSnapshot,
charts: savedSnapshot.charts.map((chartSnapshot) => ({
...chartSnapshot,
symbol: currentSymbolRef.current,
timeframe,
})),
},
chartCollectionPreset: {
...MOEX_CHART_CONFIG.chartCollectionPreset,
openCompareModal: () => setIsCompareOpen(true),
openSymbolSearchModal: () => setIsSymbolSearchOpen(true),
getDataSource: dataProvider.getDataSource(indicativeData, (tf) => {
updateTimeframeRef.current?.(tf);
}),
startRealtime: (getSymbols, getTimeframe, update) =>
dataProvider.startRealtime({
getSymbols,
getTimeframe,
update,
}),
},
});
chartRef.current = chart;
compareManagerRef.current = chart.getCompareManager();
if (initialIntervalRef.current) {
chart.setSettings({ interval: initialIntervalRef.current });
updateProperties((state) => {
state.moexChartState = {
...state.moexChartState,
initialInterval: undefined,
};
});
initialIntervalRef.current = undefined;
}
const intervalId = setInterval(() => {
saveSnapshot();
}, 1000);
// явная инициализация индикативных данных в график
if (!isNil(indicativeData)) {
chartRef.current?.setSymbol(indicativeData.key);
}
return () => {
clearInterval(intervalId);
saveSnapshot();
chartRef.current = null;
compareManagerRef.current = null;
chart.destroy();
};
// eslint-disable-next-line react-hooks/exhaustive-deps -- исправим позже
}, [indicativeData]);
return {
containerRef,
isCompareOpen,
isSymbolSearchOpen,
compareManagerRef,
setIsCompareOpen,
setIsSymbolSearchOpen,
setMainSymbol,
saveSnapshot,
applySnapshot,
hasSavedSnapshot: Boolean(moexChartState?.savedData),
};
};
import React from 'react';
import { Contract } from '@modules/contracts';
import { SymbolSearchModal } from '@widgets/Chart/components/MoexChart/components/SymbolSearchModal';
import { ChartIndicativeData } from '../../types';
import { CompareModal } from './components/CompareModal';
import { useMoexChart } from './hooks';
import 'moex-chart/dist/styles.css';
type TRProps = {
fullName: string;
indicativeData?: ChartIndicativeData | undefined;
widgetId: number;
addInstrumentFromModal: (instruments: Pick<Contract, 'issKey'>[]) => void;
};
export default React.memo(({ fullName, indicativeData, widgetId, addInstrumentFromModal }: TRProps) => {
const {
containerRef,
isCompareOpen,
isSymbolSearchOpen,
compareManagerRef,
setIsCompareOpen,
setIsSymbolSearchOpen,
setMainSymbol,
} = useMoexChart({
indicativeData,
symbol: fullName,
});
const handleSymbolChange = (symbol: string): void => {
addInstrumentFromModal([{ issKey: symbol }]);
setMainSymbol(symbol);
};
return (
<div
style={{
flex: '1 1 0',
minHeight: 0,
minWidth: 0,
}}
>
<div ref={containerRef} />
{isCompareOpen && (
<CompareModal
onClose={() => setIsCompareOpen(false)}
compareManager={compareManagerRef}
widgetId={widgetId}
isOpen={isCompareOpen}
setOpen={setIsCompareOpen}
/>
)}
{isSymbolSearchOpen && (
<SymbolSearchModal
widgetId={widgetId}
isOpen={isSymbolSearchOpen}
setOpen={setIsSymbolSearchOpen}
onSymbolChange={handleSymbolChange}
/>
)}
</div>
);
});
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import { indicativeQuotesController } from '@api/controllers/indicativeQuotesController';
import api from '@api/index';
import { candleToBar } from '@utils/candleToBar';
import { ChartIndicativeData } from './types';
import { isIndicativeTicker } from './utils/isIndicativeTicker';
import { transformKeyToLowerCase } from './utils/transformKeyToLowerCase';
import type { Candle } from 'moex-chart';
dayjs.extend(utc);
export interface PeriodParams {
from: number;
to: number;
countBack: number;
firstDataRequest: boolean;
}
export interface HistoryMetadata {
noData: boolean;
}
export type HistoryCallback = (candles: Candle[], metadata: HistoryMetadata) => void;
export type SubscribeBarsCallback = (candle: Candle) => void;
interface RequestBarsArgs {
currencyPair: string;
interval: string;
periodParams: PeriodParams;
onHistoryCallback?: HistoryCallback;
ticker?: string;
indicativeData?: ChartIndicativeData;
}
interface RequestRealtimeBarsArgs {
currencyPair: string;
interval: string;
ticker?: string;
indicativeData?: ChartIndicativeData;
onRealtimeCallback?: SubscribeBarsCallback;
}
export type CustomBarsResolver = (args: {
ticker?: string;
currencyPair: string;
periodParams: PeriodParams;
interval: string;
}) => Promise<Candle[]>;
const barsResolvers = new Map<string, CustomBarsResolver>();
export const registerBarsResolver = (boardKey: string, resolver: CustomBarsResolver) => {
barsResolvers.set(boardKey, resolver);
};
const getBoardFromTicker = (ticker?: string) => ticker?.split(/[:.]/)[1];
const getBarsResolver = (ticker?: string) => barsResolvers.get(getBoardFromTicker(ticker) ?? '');
export async function requestBars({
currencyPair,
interval,
periodParams,
onHistoryCallback,
ticker,
indicativeData,
}: RequestBarsArgs): Promise<Candle[]> {
const customResolver = getBarsResolver(ticker);
if (customResolver) {
try {
const customBars = await customResolver({ ticker, currencyPair, periodParams, interval });
const toMs = periodParams.to * 1000;
const olderBars = customBars.filter((bar) => bar.time < toMs);
onHistoryCallback?.(olderBars, { noData: olderBars.length === 0 });
return olderBars;
} catch (e) {
onHistoryCallback?.([], { noData: true });
return [];
}
}
const date = new Date(periodParams.to * 1000);
const year = date.getUTCFullYear();
const month = `0${date.getUTCMonth() + 1}`.slice(-2);
const day = `0${date.getUTCDate()}`.slice(-2);
const hours = `0${date.getUTCHours()}`.slice(-2);
const minutes = `0${date.getUTCMinutes()}`.slice(-2);
const seconds = `0${date.getUTCSeconds()}`.slice(-2);
const hasIndicativeBoardInTicker = isIndicativeTicker(ticker);
// если при инициализации графика были данные indicativeData и текущий инструмент совпадает
// то отправляем запрос на индикатив
// иначе на инструменты
const isIndicativeInstrument = (indicativeData && indicativeData.key === ticker) || hasIndicativeBoardInTicker;
if (isIndicativeInstrument) {
const dateStr = `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
const lowerCaseKey = transformKeyToLowerCase(currencyPair);
if (!lowerCaseKey) {
onHistoryCallback?.([], { noData: true });
return [];
}
try {
const { data } = await indicativeQuotesController.getCandles({
count: periodParams.countBack,
key: lowerCaseKey,
date: dateStr,
interval,
});
const candles = data.indicativeCandles.reverse().map(candleToBar);
onHistoryCallback?.(candles, {
noData: candles.length === 0,
});
return candles;
} catch {
onHistoryCallback?.([], {
noData: true,
});
return [];
}
}
const dateStr = `${year}-${month}-${day}%20${hours}:${minutes}:${seconds}`;
try {
const { data } = await api.getBars({
currencyPair,
date: dateStr,
interval,
count: periodParams.countBack,
ticker,
});
const bars = data.reverse().map(candleToBar);
onHistoryCallback?.(bars, {
noData: bars.length === 0,
});
return bars;
} catch {
onHistoryCallback?.([], {
noData: true,
});
return [];
}
}
export async function requestRealtimeBars({
currencyPair,
interval,
ticker,
onRealtimeCallback,
indicativeData,
}: RequestRealtimeBarsArgs): Promise<Candle | undefined> {
if (getBarsResolver(ticker)) {
return;
}
const hasIndicativeBoardInTicker = isIndicativeTicker(ticker);
// если при инициализации графика были данные indicativeData и текущий инструмент совпадает
// то отправляем запрос на индикатив
// иначе на инструменты
const isIndicativeInstrument = (indicativeData && indicativeData.key === ticker) || hasIndicativeBoardInTicker;
if (isIndicativeInstrument) {
const lowerCaseKey = transformKeyToLowerCase(currencyPair);
if (!lowerCaseKey) {
return undefined;
}
try {
const { data } = await indicativeQuotesController.getCandles({
count: 1,
key: lowerCaseKey,
date: dayjs().utc().add(1, 'minute').format('YYYY-MM-DDTHH:mm:ss'),
interval,
});
if (data.indicativeCandles.length === 0) {
return undefined;
}
const sortedData = [...data.indicativeCandles].sort(
(first, second) => new Date(first.end).valueOf() - new Date(second.end).valueOf(),
);
const firstCandle = sortedData[0];
if (!firstCandle) {
return undefined;
}
const bar = candleToBar(firstCandle);
onRealtimeCallback?.(bar);
return bar;
} catch (error) {
console.error('error from requestRealTimeBars indicativeQuotesController: ', error);
return undefined;
}
}
try {
const { data } = await api.getBars({
currencyPair,
date: dayjs().utc().add(1, 'minute').format('YYYY-MM-DD%20HH:mm:ss'),
interval,
count: 1,
ticker,
});
if (data.length === 0) {
return undefined;
}
const sortedData = [...data].sort(
(first, second) => new Date(first.end).valueOf() - new Date(second.end).valueOf(),
);
const firstCandle = sortedData[0];
if (!firstCandle) {
return undefined;
}
const bar = candleToBar(firstCandle);
onRealtimeCallback?.(bar);
return bar;
} catch (error) {
console.error('error from requestRealTimeBars: ', error);
return undefined;
}
}
import dayjs, { type Dayjs } from 'dayjs';
import { feedbackController } from '@api/controllers/feedbackController';
import { commonDateFormat } from '@configs/standartDateFormat';
import { AboutInstrumentTradingResultType } from 'types/AboutInstrument';
import { Candle } from 'types/Candles';
import axios, {
axiosCurves,
axiosIndicative,
axiosInstanceFormalization,
axiosInstanceNews,
axiosInstanceNTBAnalytics,
axiosInstanceNTPro,
axiosInstanceSPFI,
axiosInstanceTD,
axiosInstanceTMD,
axiosInstanceTus,
uninterceptedAxiosInstance,
} from './axios';
import { abbrevationsController } from './controllers/abbrevationsController';
import { candlesController } from './controllers/candles';
import { frontExceptionController } from './controllers/frontExceptionController';
import { newsSettingsController } from './controllers/newsController';
import { noTradeChatController } from './controllers/noTradeChatController';
import { tradeChatController } from './controllers/tradeChatController';
import { widgetPropertiesController } from './controllers/widgetProperties';
import { widgetsController } from './controllers/widgets';
import { workspaceController } from './controllers/workspace';
import type { ResponseSearchSmartType } from '@components/InputSearchCommand/type/type';
import type { Contract } from '@modules/contracts';
import type { BoardType, Counterparty } from '@widgets/AboutInstrument/types';
import type { QuotesNTProTypeServer } from '@widgets/QuotesNTPro/type';
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
import type { IChatFolder, IChatMessages, ICreateChatFolder, IUpdateChatFolder } from 'types/Chats';
import type {
CurveValue,
IndicativeCurveFilters,
IndicativeCurveType,
IssCurveFilters,
IssCurveGroup,
IssCurveType,
ZeroCouponCurveLiveRequestParams,
ZeroCouponCurveLiveResponse,
} from 'types/Curves';
import type { CustomerDataType } from 'types/Customers';
import type { Firm } from 'types/Firm';
import type { FixingData } from 'types/FixingData';
import type { Instrument } from 'types/Instruments';
import type { INewsFeed, INewsItem } from 'types/News';
// import type { NSDCostType } from 'types/NSDCost';
import type { Payment } from 'types/Payment';
import type { QuotesType } from 'types/Quotes';
import type { GetBarsParams } from 'types/Requests/BarsTypes';
import type { SittingCB } from 'types/SittingCB';
import type { User, UserSettings } from 'types/User';
const api = {
getBars({ currencyPair, interval, date, count }: GetBarsParams) {
return axiosInstanceTD.get<Candle[]>(
`/api/v1/candles/list/offset?secId=${currencyPair}&date=${date}&interval=${interval}&count=${count}`,
);
},
getQuotes() {
return axios.post('api/v1/blotters/quotes', {
timestamp: -1,
keys: [
'AIX:MAIN:BAST',
'AIX:MAIN:Bond',
'AIX:MAIN:0000000000000',
'AIX:MAIN:aix123',
'AIX:AOTC:EQOTCa',
'AIX:MAIN:TickEQ1',
'AIX:MAIN:COVID19',
'AIX:MAIN:HSBK',
'AIX:MAIN:Forte_AIX',
'AIX:MAIN:Tatn_AIX',
'AIX:MAIN:170920201635',
'INT:IUSA:123987',
'INT:IUSA:HSBK_priv_IUSA',
'INT:IUSA:AAPL',
'INT:IUSA:aaa8888811122',
'INT:IUSA:aaa1',
'INT:IUSA:171107122020',
'KSE:TQBR:RAHT',
'KSE:EQBR:HSBKp',
'KSE:TQBR:KZTK',
'KSE:TQBR:KZAP',
'KSE:TQBR:HSBK',
'KSE:TQBR:hydro10',
'INT:ILSE:KAZ',
'INT:IOTC:EQOTCI',
'INT:ILSE:tick_test1201',
'INT:IOTC:test_IOTS1201',
'INT:ILSE:tick_009',
'INT:ILSE:13360812',
'INT:ILSE:Forte_LSE',
'INT:IUSA:Test666',
],
});
},
getMarketDepth(key: Contract['key']) {
return axiosInstanceTMD.post('/api/v1/blotters/marketdepths', {
key,
timestamp: -1,
});
},
workspaceController,
widgetsController,
widgetPropertiesController,
frontExceptionController,
candlesController,
newsSettingsController,
getChatCustomers(market?: string, spfiTradingPermission?: boolean) {
return axios.get<CustomerDataType[]>('/api/v1/data/cache/customers', { params: { market, spfiTradingPermission } });
},
getCounterparties() {
return axios.get<CustomerDataType[]>('/api/v1/data/customers');
},
getCounterpartiesById(id: number) {
return axios.get<Counterparty>(`/api/v1/data/customers/${id}`, {
full: true,
} as AxiosRequestConfig<{ full: boolean }>);
},
getInstrumentById(id: number): Promise<AxiosResponse<Instrument>> {
return axios.get(`/api/v1/data/instruments/${id}`);
},
getInstrumentList(): Promise<AxiosResponse<Instrument[]>> {
return axios.get('/api/v1/data/instruments?');
},
tradeChatController,
noTradeChatController,
getNewsList(dateRange: string[]) {
return axiosInstanceTD.get(`news/getList?from=${dateRange[0]}&till=${dateRange[1]}`);
},
getQuotesList() {
return axiosInstanceTD.get('quotes/');
},
getBoardByInstrumentId(id: number, floorCode: string) {
return axios.get<BoardType[]>(`/api/v1/data/boards?floorCode=${floorCode}&instrumentId=${id}`);
},
getDealsList(isTradeChat = false) {
return axios.get(`/api/v1/blotters/deals${isTradeChat ? '?isTradeChat=true' : ''}`);
},
hideTrade(talkId: number) {
return axios.put(`api/v1/blotters/deals/hide/${talkId}`);
},
getTermsDict() {
return axiosInstanceTD.get<{ dictionary: Record<string, string> }>('/api/v1/contracts/dictionary/term');
},
getBoardsDict() {
return axiosInstanceTD.get<{ dictionary: Record<string, string> }>('/api/v1/contracts/dictionary/board');
},
getInstrGroupDict() {
return axiosInstanceTD.get<{ dictionary: Record<string, string> }>('/api/v1/contracts/dictionary/instrGroupType');
},
getIssContracts() {
return axiosInstanceTD.get<Contract[]>('/api/v1/contracts');
},
getIndicativeContracts() {
return axiosIndicative.get<Contract[]>('/api/v1/contracts');
},
getNtProContracts() {
return axiosInstanceNTPro.get<Contract[]>('/api/v1/contracts');
},
getSapfirContracts() {
return axiosInstanceSPFI.get<Contract[]>('/api/v1/contracts');
},
getNtbAnalyticsContracts() {
return axiosInstanceNTBAnalytics.get<Contract[]>('/api/v1/contracts');
},
getFixingsList(): Promise<AxiosResponse<FixingData[]>> {
return axiosInstanceTD.get('/api/fixings/');
},
getFixingById(key: string): Promise<AxiosResponse<FixingData[]>> {
return axiosInstanceTD.get(`/api/fixings?code=${key}`);
},
getQuotesByKeys(keys: string[], signal?: AbortSignal) {
return axiosInstanceTD.put<QuotesType[]>('/api/v1/quotes', keys, { signal });
},
getQuotesByIndicativeKeys(keys: string[], signal?: AbortSignal) {
return axiosIndicative.put<QuotesType[]>('/api/v1/quotes', keys, { signal });
},
getQuotesListWithParams({ skip, limit }: { skip: number; limit: number }) {
return axiosInstanceTD.get<QuotesType[]>('/api/v1/quotes/list', {
params: {
skip,
limit,
},
});
},
getTurnoversByDate(date: Date) {
const year = date.getFullYear();
const month = date.getMonth() + 1 < 10 ? `0${date.getMonth() + 1}` : date.getMonth() + 1;
const day = date.getDate() < 10 ? `0${date.getDate()}` : date.getDate();
const newDate = `${year}-${month}-${day}`;
return axiosInstanceTD.get(`/api/v1/turnovers?date=${newDate}`);
},
/** Кривые */
getCurveGroups() {
return axiosCurves.get<IssCurveGroup[]>('/api/v1/data/curves/groups');
},
getCurves() {
return axiosCurves.get<IssCurveType[]>('/api/v1/data/curves');
},
getCurvesFilter: (params: { groupCurveId?: number; id?: number; currencyCode?: string } = {}) =>
axiosCurves.get<IssCurveFilters>('/api/v1/data/curvesfilter', { params }),
getCurvePoints(groupId: number, curveId: number, date: Date | string) {
const newDate = date instanceof Date ? dayjs(date).format(commonDateFormat.backendDateFormat) : date;
return axiosCurves.get<CurveValue[]>(`/api/v1/data/curvepoints/${groupId}/${curveId}/${newDate}`);
},
getZeroCouponCurveLive: (params?: ZeroCouponCurveLiveRequestParams) =>
axiosInstanceTD.post<ZeroCouponCurveLiveResponse>('/api/v1/kbd/live', params),
/** Кривые индикативные котировок */
getIndicativeCurves: () => axiosIndicative.get<IndicativeCurveType[]>('/api/v1/data/curves'),
getIndicativeCurveFilter: (params: { groupCurveId?: number; curveId?: string; currencyCode?: string } = {}) =>
axiosIndicative.get<IndicativeCurveFilters>('/api/v1/data/curvesfilter', { params }),
getIndicativeCurvePoints(key: string, date: Dayjs) {
const formattedDate = date.format(commonDateFormat.backendDateFormat);
return axiosIndicative.get<CurveValue[]>(`/api/v1/data/curvepoints/${key}/${formattedDate}`);
},
getQuoteByIssKey(key: string) {
const issKeyParts = key.split(':');
return axiosInstanceTD.get<QuotesType>(
`/api/v1/quotes?secId=${issKeyParts[2]}&boardId=${issKeyParts[1]}&tradingSystem=${issKeyParts[0]}`,
);
},
getPaymentsByIsin(isin: string) {
return axiosInstanceTD.get<Payment[]>(`/api/v1/payments?isin=${isin}`);
},
getTradingResults(security: string, date: string | Dayjs, board: string) {
return axiosInstanceTD.get<AboutInstrumentTradingResultType[]>(
`api/trading-result?mode=instrument&security=${security}&till=${date}&from=${date}&board=${board}`,
);
},
getQuotesNTPro() {
return axiosInstanceNTPro.get<QuotesNTProTypeServer[]>('/quotes');
},
getIndexesBySecId(key: string) {
const issKeyParts = key.split(':');
return axios.get(`/api/v1/data/indexes/${issKeyParts[2]}`);
},
getUserPermissions(): Promise<AxiosResponse<string[]>> {
return axios.get('/api/v1/permission/user');
},
/** Получение основной информации по пользователю */
getCurrentUserInfo(): Promise<AxiosResponse<User>> {
return axios.get('api/v1/users/current');
},
updateMenuLocked(id: number, locked: boolean): Promise<AxiosResponse<User>> {
return axios.patch('api/v1/users/settings', {
user_id: id,
is_menu_locked: locked,
});
},
updateUser(user: User): Promise<AxiosResponse<User>> {
return axios.put('api/v1/users', user);
},
getCurrentUserSettings(id: number) {
return axios.get<UserSettings>(`/api/v1/notification-users/${id}`);
},
updateCurrentUserSettings(settigs: UserSettings) {
return axios.put('api/v1/notification-users', settigs);
},
getSearchSmart(search: string, signal: AbortSignal) {
return axiosInstanceTus.get<ResponseSearchSmartType>(`/api/v1/search-smart?search=${encodeURIComponent(search)}`, {
signal,
});
},
healthcheckTd(): Promise<AxiosResponse<{ status: string }>> {
return axiosInstanceTD.get('/actuator/health-td');
},
healthcheckTb(): Promise<AxiosResponse<{ status: string }>> {
return axios.get('/actuator/health-tb');
},
healthcheckSPFI(): Promise<AxiosResponse<{ status: string }>> {
return axiosInstanceSPFI.get('/actuator/health-spfi');
},
healthcheckForm(): Promise<AxiosResponse<{ status: string }>> {
return axiosInstanceFormalization.get('/actuator/health-form');
},
connectionCheckRequest(): Promise<AxiosResponse<{ status: string }>> {
return uninterceptedAxiosInstance.get('https://iss.moex.com/iss/sitenews/');
},
getFirms(): Promise<AxiosResponse<Firm[]>> {
return axios.get('/api/v1/data/cache/firms');
},
abbrevationsController,
feedbackController,
// Отключено в рамках TRADERADAR-12666 Отключение источника НРД
// getNSDCost() {
// return axiosInstanceTD.get<NSDCostType[]>('/api/v1/data/nsdCost');
// },
getChatFolders(): Promise<AxiosResponse<IChatFolder[]>> {
return axiosInstanceFormalization.get('/api/v1/folders');
},
createChatFolder(folderData: ICreateChatFolder): Promise<AxiosResponse<IChatFolder>> {
return axiosInstanceFormalization.post('/api/v1/folders', folderData);
},
deleteChatFolder(folderId: number): Promise<AxiosResponse> {
return axiosInstanceFormalization.delete(`/api/v1/folders/${folderId}`);
},
updateChatFolder(folderData: IUpdateChatFolder): Promise<AxiosResponse<IChatFolder>> {
return axiosInstanceFormalization.patch(`/api/v1/folders/${folderData.folderId}`, folderData);
},
updateFolderPosition(folderPosition: Record<string, number>): void {
axiosInstanceFormalization.post('/api/v1/folders/positions', folderPosition);
},
sendChatMessages(data: IChatMessages): Promise<AxiosResponse> {
return axiosInstanceFormalization.post('/api/v1/chat-messages', data);
},
getNewsItemById(newsId: string): Promise<AxiosResponse<INewsItem>> {
return axiosInstanceNews.get(`/api/v1/news/${newsId}`);
},
findNewsByText(text?: string): Promise<AxiosResponse<INewsItem[]>> {
const requestUrl = `/api/v1/news/search${text ? `?search=${encodeURIComponent(text)}` : ''}`;
return axiosInstanceNews.get(requestUrl);
},
getCombinedNews(searchParams?: string): Promise<AxiosResponse<INewsItem[]>> {
const requestUrl = searchParams ? `/api/v1/news/search?${searchParams}` : '/api/v1/news/search';
return axiosInstanceNews.get(requestUrl);
},
getNewsFeeds(): Promise<AxiosResponse<INewsFeed[]>> {
return axiosInstanceNews.get('/api/v1/newsfeeds');
},
getContractsIssInfo(): Promise<AxiosResponse<{ key: string }>> {
return axiosInstanceTD.get('/api/v1/contracts/info');
},
getSittingCB() {
return axiosInstanceTD.get<SittingCB>('/api/v1/data/sittingCB');
},
};
export default api;
export type Api = typeof api;