diff --git a/package-lock.json b/package-lock.json index c930b03c..72d30bd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@loadable/component": "^5.15.3", "@nilfoundation/react-components": "^0.8.3", - "@nilfoundation/ui-kit": "^2.1.3", + "@nilfoundation/ui-kit": "^2.2.3", "@reduxjs/toolkit": "^1.8.5", "@sentry/react": "^7.21.1", "@sentry/tracing": "^7.21.1", @@ -2575,9 +2575,9 @@ } }, "node_modules/@nilfoundation/ui-kit": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nilfoundation/ui-kit/-/ui-kit-2.1.3.tgz", - "integrity": "sha512-M3dgvejsehjOcXCVBWTJsGRf6+vfDdSS6HzeyrOY8NdM2tMkF16DDCOYg83DTvv81LfQP9+wEu7H42fm5n52cQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@nilfoundation/ui-kit/-/ui-kit-2.2.3.tgz", + "integrity": "sha512-UybcSWiwuGc//K1944CQW74lZvHBtpoSTT7RVVZqG+rUyaZZF1RUmV0CmphZpdg0KgOGOyZHYXGoxbADw7yZHw==", "dependencies": { "copy-to-clipboard": "^3.3.3", "inline-style-expand-shorthand": "^1.6.0", @@ -2587,6 +2587,7 @@ "@uiw/codemirror-themes": ">=4.21.0 < 5", "@uiw/react-codemirror": ">=4.21.0 < 5", "baseui": ">=13.0.0 < 14", + "lightweight-charts": ">=4.0.0 < 5", "react": ">=18.1.0 < 19", "react-dom": ">=18.1.0 < 19", "styletron-react": ">=6.1.0 < 7", diff --git a/package.json b/package.json index ff16cb27..e5850713 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "dependencies": { "@loadable/component": "^5.15.3", "@nilfoundation/react-components": "^0.8.3", - "@nilfoundation/ui-kit": "^2.1.3", + "@nilfoundation/ui-kit": "^2.2.3", "@reduxjs/toolkit": "^1.8.5", "@sentry/react": "^7.21.1", "@sentry/tracing": "^7.21.1", diff --git a/src/components/common/ChartTemplate/ChartBaseProps.ts b/src/components/common/ChartTemplate/ChartBaseProps.ts deleted file mode 100644 index 2d0156ca..00000000 --- a/src/components/common/ChartTemplate/ChartBaseProps.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @file Type declaration. - * @copyright Yury Korotovskikh - */ - -import type { DateUnit } from '@/enums'; - -/** - * Base chart props. - */ -export type ChartBaseProps = { - dataRange: DateUnit; - displayVolumes: boolean; - height: number; -}; diff --git a/src/components/common/ChartTemplate/ChartTemplate.module.scss b/src/components/common/ChartTemplate/ChartTemplate.module.scss deleted file mode 100644 index f253dd63..00000000 --- a/src/components/common/ChartTemplate/ChartTemplate.module.scss +++ /dev/null @@ -1,16 +0,0 @@ -.chart { - position: relative; - margin-top: 3rem; -} - -.spinner { - position: absolute; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - left: 0; - top: 0; - z-index: 2; -} diff --git a/src/components/common/ChartTemplate/ChartTemplate.tsx b/src/components/common/ChartTemplate/ChartTemplate.tsx deleted file mode 100644 index 00a84720..00000000 --- a/src/components/common/ChartTemplate/ChartTemplate.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @file React component. - * @copyright Yury Korotovskikh - */ - -import type { ReactElement } from 'react'; -import { useMemo, useRef } from 'react'; -import { Spinner } from '@nilfoundation/react-components'; -import type { - CandlestickData, - CandlestickStyleOptions, - ChartOptions, - DeepPartial, - HistogramData, - LineData, - LineStyleOptions, - SeriesOptionsCommon, - UTCTimestamp, - WhitespaceData, -} from 'lightweight-charts'; -import { useChart, useRenderChartData } from '@/hooks'; -import { formatUTCTimestamp } from '@/utils'; -import { getDateFormatBasedOnDateUnit } from '@/enums'; -import { ChartLegend } from '../ChartLegend'; -import type { ChartBaseProps } from './ChartBaseProps'; -import styles from './ChartTemplate.module.scss'; - -/** - * Props. - */ -type ChartTemplateProps = ChartBaseProps & { - chartName: string; - loadingData: boolean; - seriesType: T; - seriesData: T extends 'Line' ? LineData[] : CandlestickData[]; - seriesOptions?: DeepPartial< - (T extends 'Line' ? LineStyleOptions : CandlestickStyleOptions) & SeriesOptionsCommon - >; - chartOptions?: DeepPartial; - volumesData?: Array; -}; - -/** - * Chart template. - * - * @param {ChartTemplateProps} props Props. - * @returns React component. - */ -export const ChartTemplate = ({ - chartName, - loadingData, - seriesType, - seriesData, - seriesOptions, - chartOptions, - volumesData, - dataRange, - displayVolumes, - height, -}: ChartTemplateProps): ReactElement => { - const ref = useRef(null); - const options = useMemo( - (): DeepPartial => ({ - ...chartOptions, - localization: { - timeFormatter: (t: UTCTimestamp) => - formatUTCTimestamp(t, getDateFormatBasedOnDateUnit(dataRange)), - }, - timeScale: { - tickMarkFormatter: (t: UTCTimestamp) => - formatUTCTimestamp(t, getDateFormatBasedOnDateUnit(dataRange)), - fixRightEdge: true, - fixLeftEdge: true, - }, - rightPriceScale: { - scaleMargins: { - top: 0.2, - bottom: displayVolumes ? 0.3 : 0.2, - }, - }, - leftPriceScale: { - visible: displayVolumes, - }, - crosshair: { - mode: 0, - }, - }), - [chartOptions, dataRange, displayVolumes], - ); - - const { chart } = useChart({ ref, options }); - - const { currentChartData } = useRenderChartData({ - seriesType, - seriesData, - chart, - options: seriesOptions, - visibleRange: dataRange, - volumes: volumesData, - }); - - return ( -
- - {loadingData && seriesData.length === 0 && ( -
- -
- )} -
- ); -}; diff --git a/src/components/common/ChartTemplate/index.ts b/src/components/common/ChartTemplate/index.ts deleted file mode 100644 index d361a117..00000000 --- a/src/components/common/ChartTemplate/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @file Index. - * @copyright Yury Korotovskikh - */ - -export * from './ChartTemplate'; -export * from './ChartBaseProps'; diff --git a/src/components/common/StatementCharts/ProofCostChart.tsx b/src/components/common/StatementCharts/ProofCostChart.tsx deleted file mode 100644 index 99dc226a..00000000 --- a/src/components/common/StatementCharts/ProofCostChart.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @file React component. - * @copyright Yury Korotovskikh - */ - -import type { ReactElement } from 'react'; -import { useMemo } from 'react'; -import type { LineWidth } from 'lightweight-charts'; -import { useGetStatementDashboardData } from '@/hooks'; -import { siteMoneyTickerAbbreviation } from '@/constants'; -import colors from '@/styles/export.module.scss'; -import { ChartTemplate } from '../ChartTemplate'; -import type { ChartBaseProps } from '../ChartTemplate'; - -/** - * Props. - */ -type ProofCostChartProps = ChartBaseProps; - -/** - * Proof cost chart. - * - * @param {ProofCostChartProps} props Props. - * @returns React component. - */ -export const ProofCostChart = (props: ProofCostChartProps): ReactElement => { - const seriesOptions = useMemo( - () => ({ - upColor: colors.successColor, - downColor: colors.dangerColor, - priceLineWidth: 2 as LineWidth, - }), - [], - ); - const { - chartData: { candlestickChartData, volumesData }, - loadingData: isLoadingChartData, - } = useGetStatementDashboardData(props.displayVolumes, props.dataRange); - - return ( - - ); -}; diff --git a/src/components/common/StatementCharts/index.ts b/src/components/common/StatementCharts/index.ts deleted file mode 100644 index ba95aebc..00000000 --- a/src/components/common/StatementCharts/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @file Index. - * @copyright Yury Korotovskikh - */ - -export * from './ProofCostChart'; -export * from './ProofGenTimeChart'; diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 6407f16c..2ce9ace0 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -13,7 +13,4 @@ export * from './ObjectAsPlainTextViewer'; export * from './GALocationTracker'; export * from './ProgressBar'; export * from './RouterLink'; -export * from './ChartTemplate'; -export * from './ChartLegend'; -export * from './StatementCharts'; export * from './FullScreenLoader'; diff --git a/src/components/common/ChartLegend/ChartLegend.module.scss b/src/features/dashboard/components/ChartLegends/ChartLegend.module.scss similarity index 86% rename from src/components/common/ChartLegend/ChartLegend.module.scss rename to src/features/dashboard/components/ChartLegends/ChartLegend.module.scss index d966dc64..1f2ced8c 100644 --- a/src/components/common/ChartLegend/ChartLegend.module.scss +++ b/src/features/dashboard/components/ChartLegends/ChartLegend.module.scss @@ -1,4 +1,4 @@ -@import "../../../styles/constants.scss"; +@import '../../../styles/constants.scss'; $height: 3rem; diff --git a/src/components/common/ChartLegend/ChartLegend.tsx b/src/features/dashboard/components/ChartLegends/ChartLegend.tsx similarity index 100% rename from src/components/common/ChartLegend/ChartLegend.tsx rename to src/features/dashboard/components/ChartLegends/ChartLegend.tsx diff --git a/src/components/common/ChartLegend/index.ts b/src/features/dashboard/components/ChartLegends/index.ts similarity index 100% rename from src/components/common/ChartLegend/index.ts rename to src/features/dashboard/components/ChartLegends/index.ts diff --git a/src/features/dashboard/components/Charts/ProofCostChart.tsx b/src/features/dashboard/components/Charts/ProofCostChart.tsx new file mode 100644 index 00000000..11c3817d --- /dev/null +++ b/src/features/dashboard/components/Charts/ProofCostChart.tsx @@ -0,0 +1,44 @@ +/** + * @file React component. + * @copyright Yury Korotovskikh + */ + +import type { ReactElement } from 'react'; +import { useState } from 'react'; +import { CandlestickSeries, Chart } from '@nilfoundation/ui-kit'; +import type { CandlestickData, CandlestickSeriesPartialOptions } from 'lightweight-charts'; +import { useGetStatementDashboardData } from '../../hooks/useGetStatementDashboardData'; + +/** + * Proof cost chart. + * + * @returns React component. + */ +export const ProofCostChart = (): ReactElement => { + const [legendData, setLegendData] = useState(null); + const { + chartData: { candlestickChartData, volumesData }, + loadingData: isLoadingChartData, + } = useGetStatementDashboardData(props.displayVolumes, props.dataRange); + + return ( + + + + ); +}; + +/** + * Series default options. + */ +const seriesDefaultOptions: CandlestickSeriesPartialOptions = { + priceFormat: { + type: 'price', + precision: 4, + minMove: 0.0001, + }, +}; diff --git a/src/components/common/StatementCharts/ProofGenTimeChart.tsx b/src/features/dashboard/components/Charts/ProofGenTimeChart.tsx similarity index 100% rename from src/components/common/StatementCharts/ProofGenTimeChart.tsx rename to src/features/dashboard/components/Charts/ProofGenTimeChart.tsx diff --git a/src/features/dashboard/components/Dashboard/ChartTypeSelect.tsx b/src/features/dashboard/components/Dashboard/ChartTypeSelect.tsx new file mode 100644 index 00000000..235a6165 --- /dev/null +++ b/src/features/dashboard/components/Dashboard/ChartTypeSelect.tsx @@ -0,0 +1,51 @@ +/** + * @file React component. + * @copyright Yury Korotovskikh + */ + +import type { ReactElement } from 'react'; +import { Nav } from '@nilfoundation/react-components'; +import { ChartType } from '@/enums'; +import { useBreakpoint } from '@/features/shared'; +import { BREAKPOINT } from '@/styles/Breakpoint'; + +/** + * Props. + */ +type ChartTypeSelectProps = { + chartType: ChartType; + onSelectChartType: (chartType: ChartType) => void; + disabled: boolean; +}; + +/** + * Chart type selector. + * + * @param {ChartTypeSelectProps} props Props. + * @returns React component. + */ +export const ChartTypeSelect = ({ + chartType, + onSelectChartType, + disabled, +}: ChartTypeSelectProps): ReactElement => { + const bp = useBreakpoint(); + + return ( + + ); +}; diff --git a/src/features/dashboard/components/Dashboard/CopyToClipboardNavItem.tsx b/src/features/dashboard/components/Dashboard/CopyToClipboardNavItem.tsx new file mode 100644 index 00000000..fe4fb630 --- /dev/null +++ b/src/features/dashboard/components/Dashboard/CopyToClipboardNavItem.tsx @@ -0,0 +1,93 @@ +/** + * @file React component. + * @copyright Yury Korotovskikh + */ + +import type { ReactElement } from 'react'; +import { useState, useMemo } from 'react'; +import { Icon, Nav } from '@nilfoundation/react-components'; +import loadable from '@loadable/component'; +import { useParams } from 'react-router-dom'; +import { StatefulTooltip, TOOLTIP_KIND } from '@nilfoundation/ui-kit'; +import { ACCESSIBILITY_TYPE } from 'baseui/popover'; +import type { DateUnit, ChartType } from '@/enums'; +import { RouterSearchParam, RouterParam } from '@/enums'; +import { Path } from '@/features/routing'; + +const CopyToClipboard = loadable.lib(() => import('react-copy-to-clipboard')); + +/** + * Props. + */ +type CopyToClipboardNavItemProps = { + disabled: boolean; + chartType: ChartType; + chartDataRange: DateUnit; + displayVolumes: boolean; +}; + +/** + * Copy to clipboard nav item. + * + * @param {CopyToClipboardNavItemProps} props Props. + * @returns React component. + */ +export const CopyToClipboardNavItem = ({ + disabled, + chartType, + chartDataRange, + displayVolumes, +}: CopyToClipboardNavItemProps): ReactElement => { + const statementName = useParams()[RouterParam.statementName]; + const [copied, setCopied] = useState(false); + + const shareChartText = useMemo(() => { + let baseUrl = `${window.location.protocol}//${window.location.hostname}`; + + if (import.meta.env.DEV) { + baseUrl += `:${window.location.port}`; + } + + const path = `${Path.charts}/#/${statementName}/${chartType}`; + + let urlSearchParams = `?${RouterSearchParam.chartDataRange}=${chartDataRange}`; + + if (displayVolumes) { + urlSearchParams += `&${RouterSearchParam.chartDisplayVolumes}=true`; + } + + const src = baseUrl + path + urlSearchParams; + + return ``; + }, [chartType, statementName, chartDataRange, displayVolumes]); + + return ( + }> + {({ default: Lib }) => { + return ( + setCopied(true)} + > + setCopied(false)} + onBlur={() => setCopied(false)} + kind={copied ? TOOLTIP_KIND.SUCCESS : TOOLTIP_KIND.DEFAULT} + > + + + + + + ); + }} + + ); +}; diff --git a/src/features/dashboard/components/Dashboard/Dashboard.tsx b/src/features/dashboard/components/Dashboard/Dashboard.tsx index e69de29b..178e87d5 100644 --- a/src/features/dashboard/components/Dashboard/Dashboard.tsx +++ b/src/features/dashboard/components/Dashboard/Dashboard.tsx @@ -0,0 +1,94 @@ +/** + * @file React component. + * @copyright Yury Korotovskikh + */ + +import type { ReactElement } from 'react'; +import { useState } from 'react'; +import { match } from 'ts-pattern'; +import { Card } from '@nilfoundation/ui-kit'; +import { ChartType, DateUnit } from '@/enums'; +import { selectCurrentStatement, useAppSelector } from '@/redux'; +import { useLocalStorage } from '@/hooks'; +import { StatementInfoPanel } from '@/features/dashboard'; +import { useWindowDimensions } from '@/features/shared'; +import { ChartTypeSelect } from './ChartTypeSelect'; +import { DataRangeSelect } from './DataRangeSelect'; +import { DashboardToolbar } from './DashboardToolbar'; +import { CopyToClipboardNavItem } from './CopyToClipboardNavItem'; +import { getCardOverrides } from './overrides'; +import { ProofCostChart } from '../Charts/ProofCostChart'; + +/** + * Statement dashboard. + * + * @returns React component. + */ +const StatementDashboard = (): ReactElement => { + const currentStatement = useAppSelector(selectCurrentStatement); + const [chartType, setChartType] = useState(ChartType.proofCostChart); + + const [dataRange, setDataRange] = useLocalStorage( + 'statementDashboardDataRange', + DateUnit.hour, + ); + + const [displayVolumes, setDisplayVolumes] = useLocalStorage( + 'statementDashboardDisplayVolumes', + false, + ); + + return ( + +
+ +
+ + + + +
+ +
+
+ ); +}; + +/** + * Renders chart depending on its type. + * + * @param {{chartType: ChartType}} props Props. + * @returns Chart. + */ +const ChartFactory = ({ chartType, ...rest }: { chartType: ChartType }) => { + return match(chartType) + .with(ChartType.proofCostChart, () => ) + .with(ChartType.proofGetTimeChart, () => ) + .otherwise(() => <>); +}; + +export default StatementDashboard; diff --git a/src/features/dashboard/components/Dashboard/DashboardToolbar.tsx b/src/features/dashboard/components/Dashboard/DashboardToolbar.tsx new file mode 100644 index 00000000..6f44ea52 --- /dev/null +++ b/src/features/dashboard/components/Dashboard/DashboardToolbar.tsx @@ -0,0 +1,62 @@ +/** + * @file React component. + * @copyright Yury Korotovskikh + */ + +import type { ReactElement, ReactNode } from 'react'; +import { Icon, Nav } from '@nilfoundation/react-components'; + +/** + * Props. + */ +type DashboardToolbarProps = { + children?: ReactNode; + disabled: boolean; + isFullscreen: boolean; + setFullScreen: (x: boolean) => void; + displayVolumes: boolean; + setDisplayVolumes: (d: boolean) => void; +}; + +/** + * Dashboard toolbar. + * + * @param {DashboardToolbarProps} props Props. + * @returns React component. + */ +export const DashboardToolbar = ({ + disabled, + isFullscreen, + setFullScreen, + displayVolumes, + setDisplayVolumes, + children, +}: DashboardToolbarProps): ReactElement => { + return ( + + ); +}; diff --git a/src/features/dashboard/components/Dashboard/DataRangeSelect.tsx b/src/features/dashboard/components/Dashboard/DataRangeSelect.tsx new file mode 100644 index 00000000..a575c3c1 --- /dev/null +++ b/src/features/dashboard/components/Dashboard/DataRangeSelect.tsx @@ -0,0 +1,44 @@ +/** + * @file React component. + * @copyright Yury Korotovskikh + */ + +import type { ReactElement } from 'react'; +import { Nav } from '@nilfoundation/react-components'; +import { DateUnit } from '@/enums'; + +/** + * Props. + */ +type DataRangeSelectProps = { + disabled: boolean; + dataRange: DateUnit; + setDataRange: (r: DateUnit) => void; +}; + +/** + * Data range select. + * + * @param {DataRangeSelectProps} props Props. + * @returns React component. + */ +export const DataRangeSelect = ({ + disabled, + dataRange, + setDataRange, +}: DataRangeSelectProps): ReactElement => { + return ( + + ); +}; diff --git a/src/features/dashboard/components/Dashboard/overrides.ts b/src/features/dashboard/components/Dashboard/overrides.ts new file mode 100644 index 00000000..ccb1b4b1 --- /dev/null +++ b/src/features/dashboard/components/Dashboard/overrides.ts @@ -0,0 +1,18 @@ +/** + * @file Overrides for StatementsList component. + */ + +import { PRIMITIVE_COLORS } from '@nilfoundation/ui-kit'; +import type { CardOverrides } from 'baseui/card'; + +/** + * @returns Overrides for card. + */ +export const getCardOverrides = (): CardOverrides => ({ + Root: { + style: { + maxWidth: '100%', + backgroundColor: PRIMITIVE_COLORS.gray900, + }, + }, +}); diff --git a/src/features/dashboard/components/Dashboard/styles.ts b/src/features/dashboard/components/Dashboard/styles.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/features/dashboard/hooks/useGetStatementDashboardData.ts b/src/features/dashboard/hooks/useGetStatementDashboardData.ts new file mode 100644 index 00000000..18b0f132 --- /dev/null +++ b/src/features/dashboard/hooks/useGetStatementDashboardData.ts @@ -0,0 +1,152 @@ +/** + * @file React hook. + * @copyright Yury Korotovskikh + */ + +import { useMemo } from 'react'; +import { useSelector } from 'react-redux'; +import { dequal as deepEqual } from 'dequal'; +import sum from 'lodash/sum'; +import type { + CandlestickData, + HistogramData, + LineData, + UTCTimestamp, + WhitespaceData, +} from 'lightweight-charts'; +import { useAppSelector, selectSortedChartData } from '@/redux'; +import { getUTCTimestamp } from '@/utils'; +import { DateUnit } from '@/enums'; +import type { Proposal, Request } from '@/models'; +import { PRIMITIVE_COLORS } from '@nilfoundation/ui-kit'; + +/** + * Hook return type. + */ +type UseGetStatementDashboardDataReturnType = { + chartData: { + candlestickChartData: CandlestickData[]; + proofGenTimeData: LineData[]; + volumesData?: Array; + }; + loadingData: boolean; +}; + +/** + * Get data to draw statement chart. + * + * @param [withVolumes] Should calculate volumes data. + * @param [dataRange] Data range. + * @returns Data to draw statement chart. + */ +export const useGetStatementDashboardData = ( + withVolumes = false, + dataRange = DateUnit.day, +): UseGetStatementDashboardDataReturnType => { + const loadingData = useAppSelector(s => s.statementsState.isLoading || s.chartsState.isLoading); + const proposals = useSelector(selectSortedChartData, deepEqual); + const grouppedOrders = useMemo(() => { + return reduceOrdersByDate(proposals, dataRange); + }, [proposals, dataRange]); + + const chartData = useMemo( + () => ({ + candlestickChartData: getCandlestickData(grouppedOrders), + proofGenTimeData: getProofGenTimeData(grouppedOrders), + volumesData: withVolumes ? getVolumesData(grouppedOrders) : undefined, + }), + [grouppedOrders, withVolumes], + ); + + return { chartData, loadingData }; +}; + +/** + * Takes orders array and returns dict, where keys are dates, and values are arrays of orders. + * + * @param proposals Proposals. + * @param dataRange Data range. + * @returns Orders, grouped by date. + */ +const reduceOrdersByDate = (proposals: T[], dataRange: DateUnit) => { + return proposals.reduce((previousValue: Record, currentValue: T) => { + const date = getUTCTimestamp(currentValue.updatedOn!, dataRange); + + if (!previousValue[date]) previousValue[date] = []; + + previousValue[date].push(currentValue); + + return previousValue; + }, {}); +}; + +/** + * Creates candleStick data {@link CandlestickData} array from orders, groupped by date. + * + * @param ordersGrouppedByDate Orders array. + * @returns Array of candleStick data. + */ +const getCandlestickData = ( + ordersGrouppedByDate: Record, +): CandlestickData[] => { + const keys = Object.keys(ordersGrouppedByDate); + + return keys.map((x, index) => { + const ordersCosts = ordersGrouppedByDate[x].map(x => x.cost); + + const high = Math.max(...ordersCosts); + const low = Math.min(...ordersCosts); + const open = index === 0 ? ordersCosts[0] : ordersGrouppedByDate[keys[index - 1]].at(-1)!.cost; + const close = ordersCosts[ordersCosts.length - 1]; + + return { + time: Number(x) as UTCTimestamp, + high, + low, + open, + close, + }; + }); +}; + +/** + * Creates line data {@link LineData} array from orders, groupped by date. + * + * @param ordersGrouppedByDate Orders array. + * @returns Array of line data. + */ +const getProofGenTimeData = ( + ordersGrouppedByDate: Record, +): LineData[] => { + return Object.keys(ordersGrouppedByDate).map(x => { + const ordersEvalTime = ordersGrouppedByDate[x].map(x => x.generation_time); + const averageEvalTime = sum(ordersEvalTime) / ordersEvalTime.length; + + return { time: Number(x) as UTCTimestamp, value: averageEvalTime }; + }); +}; + +/** + * Generates volume data. + * + * @param ordersGrouppedByDate Orders array. + * @returns Volume data. + */ +const getVolumesData = ( + ordersGrouppedByDate: Record, +): Array => { + const keys = Object.keys(ordersGrouppedByDate); + + return keys.map((x, index) => { + const costs = ordersGrouppedByDate[x].map(x => x.cost); + + const open = index === 0 ? costs[0] : ordersGrouppedByDate[keys[index - 1]].at(-1)!.cost; + const close = costs[costs.length - 1]; + + return { + time: Number(x) as UTCTimestamp, + value: ordersGrouppedByDate[x].length, + color: open < close ? PRIMITIVE_COLORS.blue100 : PRIMITIVE_COLORS.blue200, + }; + }); +}; diff --git a/src/features/dashboard/index.tsx b/src/features/dashboard/index.tsx index 838ed054..c3c9712a 100644 --- a/src/features/dashboard/index.tsx +++ b/src/features/dashboard/index.tsx @@ -6,11 +6,8 @@ import loadable from '@loadable/component'; import { Spinner } from '@nilfoundation/ui-kit'; -const StatementInfoPanel = loadable( - () => import('./components/StatementInfoPanel/StatementInfoPanel'), - { - fallback: , - }, -); +const Dashboard = loadable(() => import('./components/Dashboard/Dashboard'), { + fallback: , +}); -export { StatementInfoPanel }; +export { Dashboard }; diff --git a/src/features/dashboard/utils/getLastBar.ts b/src/features/dashboard/utils/getLastBar.ts new file mode 100644 index 00000000..fe43a2c5 --- /dev/null +++ b/src/features/dashboard/utils/getLastBar.ts @@ -0,0 +1,18 @@ +/** + * @file Utility function. + * @copyright Yury Korotovskikh + */ + +import type { SeriesType } from '@nilfoundation/ui-kit'; +import type { ISeriesApi } from 'lightweight-charts'; +import { MismatchDirection } from 'lightweight-charts'; + +/** + * Get last series data. + * + * @param series Series. + * @returns Last series bar. + */ +export const getLastBar = (series: ISeriesApi) => { + return series.dataByIndex(Infinity, MismatchDirection.NearestLeft) ?? undefined; +};