11import block from 'bem-cn-lite' ;
22import { useCallback , useEffect , useMemo , useState } from 'react' ;
33import { useDispatch } from 'react-redux' ;
4- import { escapeRegExp } from 'lodash/fp' ;
54
65import DataTable from '@gravity-ui/react-data-table' ;
7- import { Select , TableColumnSetup } from '@gravity-ui/uikit' ;
8- import { TableColumnSetupItem } from '@gravity-ui/uikit/build/esm/components/Table/hoc/withTableSettings/withTableSettings' ;
96
10- import type { EPathType } from '../../../../types/api/schema' ;
7+ import { useAutofetcher , useTypedSelector , useSetting } from '../../../../utils/hooks' ;
8+ import { DEFAULT_TABLE_SETTINGS , PARTITIONS_HIDDEN_COLUMNS_KEY } from '../../../../utils/constants' ;
119
12- import { useAutofetcher , useTypedSelector } from '../../../../utils/hooks' ;
13- import { DEFAULT_TABLE_SETTINGS , PARTITIONS_SELECTED_COLUMNS_KEY } from '../../../../utils/constants' ;
14-
15- import { getSettingValue , setSettingValue } from '../../../../store/reducers/settings' ;
10+ import { getNodesList , selectNodesMap } from '../../../../store/reducers/nodesList' ;
11+ import { setSelectedConsumer } from '../../../../store/reducers/consumer' ;
12+ import {
13+ cleanTopicData ,
14+ getTopic ,
15+ selectConsumersNames ,
16+ setDataWasNotLoaded as setTopicDataWasNotLoaded ,
17+ } from '../../../../store/reducers/topic' ;
1618import {
17- getConsumer ,
18- selectPreparedPartitionsData ,
19- setDataWasNotLoaded ,
20- setSelectedConsumer ,
21- } from '../../../../store/reducers/consumer' ;
19+ getPartitions ,
20+ setDataWasNotLoaded as setPartitionsDataWasNotLoaded ,
21+ } from '../../../../store/reducers/partitions/partitions' ;
2222
2323import { TableSkeleton } from '../../../../components/TableSkeleton/TableSkeleton' ;
24- import { Search } from '../../../../components/Search' ;
2524import { ResponseError } from '../../../../components/Errors/ResponseError' ;
2625
27- import { isCdcStreamEntityType } from '../../utils/schema' ;
28-
29- import type { IPreparedPartitionDataWithHosts } from './utils/types' ;
30- import {
31- PARTITIONS_COLUMNS_IDS ,
32- PARTITIONS_COLUMNS_TITILES ,
33- PARTITIONS_DEFAULT_SELECTED_COLUMNS ,
34- } from './utils/constants' ;
35-
36- import { columns as partitionsColumns } from './columns' ;
26+ import type { PreparedPartitionDataWithHosts } from './utils/types' ;
27+ import { addHostToPartitions } from './utils' ;
28+ import { PartitionsControls } from './PartitionsControls/PartitionsControls' ;
29+ import { useGetPartitionsColumns } from './utils/useGetPartitionsColumns' ;
3730
3831import i18n from './i18n' ;
3932
@@ -43,215 +36,140 @@ export const b = block('ydb-diagnostics-partitions');
4336
4437interface PartitionsProps {
4538 path ?: string ;
46- type ?: EPathType ;
47- nodes ?: Record < number , string > ;
48- consumers ?: string [ ] ;
4939}
5040
51- export const Partitions = ( { path, type, nodes, consumers} : PartitionsProps ) => {
52- const isCdcStream = isCdcStreamEntityType ( type ) ;
53-
41+ export const Partitions = ( { path} : PartitionsProps ) => {
5442 const dispatch = useDispatch ( ) ;
5543
56- const [ generalSearchValue , setGeneralSearchValue ] = useState ( '' ) ;
57- const [ partitionIdSearchValue , setPartitionIdSearchValue ] = useState ( '' ) ;
58-
44+ // Manual path control to ensure that topic state will be reset before data fetch
45+ // so no request with wrong params will be sent
5946 const [ componentCurrentPath , setComponentCurrentPath ] = useState ( path ) ;
6047
61- const { autorefresh} = useTypedSelector ( ( state ) => state . schema ) ;
62- const { loading, wasLoaded, error, selectedConsumer} = useTypedSelector (
63- ( state ) => state . consumer ,
48+ const [ partitionsToRender , setPartitionsToRender ] = useState < PreparedPartitionDataWithHosts [ ] > (
49+ [ ] ,
6450 ) ;
6551
66- const partitions = useTypedSelector ( ( state ) => selectPreparedPartitionsData ( state ) ) ;
67-
68- const savedSelectedColumns : string = useTypedSelector ( ( state ) =>
69- getSettingValue ( state , PARTITIONS_SELECTED_COLUMNS_KEY ) ,
52+ const consumers = useTypedSelector ( selectConsumersNames ) ;
53+ const nodesMap = useTypedSelector ( selectNodesMap ) ;
54+ const { autorefresh} = useTypedSelector ( ( state ) => state . schema ) ;
55+ const { selectedConsumer} = useTypedSelector ( ( state ) => state . consumer ) ;
56+ const {
57+ loading : partitionsLoading ,
58+ wasLoaded : partitionsWasLoaded ,
59+ error : partitionsError ,
60+ partitions : rawPartitions ,
61+ } = useTypedSelector ( ( state ) => state . partitions ) ;
62+ const {
63+ loading : topicLoading ,
64+ wasLoaded : topicWasLoaded ,
65+ error : topicError ,
66+ } = useTypedSelector ( ( state ) => state . topic ) ;
67+ const {
68+ loading : nodesLoading ,
69+ wasLoaded : nodesWasLoaded ,
70+ error : nodesError ,
71+ } = useTypedSelector ( ( state ) => state . nodesList ) ;
72+
73+ const [ hiddenColumns , setHiddenColumns ] = useSetting < string [ ] > (
74+ PARTITIONS_HIDDEN_COLUMNS_KEY ,
75+ [ ] ,
7076 ) ;
7177
78+ const [ columns , columnsIdsForSelector ] = useGetPartitionsColumns ( selectedConsumer ) ;
79+
7280 useEffect ( ( ) => {
73- // Manual path control to ensure it updates with other values so no request with wrong params will be sent
81+ dispatch ( cleanTopicData ( ) ) ;
82+ dispatch ( setTopicDataWasNotLoaded ( ) ) ;
83+
84+ dispatch ( getNodesList ( ) ) ;
85+ dispatch ( getTopic ( path ) ) ;
86+
7487 setComponentCurrentPath ( path ) ;
7588 } , [ dispatch , path ] ) ;
7689
77- const fetchConsumerData = useCallback (
90+ const partitionsWithHosts = useMemo ( ( ) => {
91+ return addHostToPartitions ( rawPartitions , nodesMap ) ;
92+ } , [ rawPartitions , nodesMap ] ) ;
93+
94+ const fetchData = useCallback (
7895 ( isBackground : boolean ) => {
7996 if ( ! isBackground ) {
80- dispatch ( setDataWasNotLoaded ( ) ) ;
97+ dispatch ( setPartitionsDataWasNotLoaded ( ) ) ;
8198 }
82-
83- if ( selectedConsumer && consumers && consumers . includes ( selectedConsumer ) ) {
84- dispatch ( getConsumer ( componentCurrentPath , selectedConsumer ) ) ;
99+ if ( topicWasLoaded && componentCurrentPath ) {
100+ dispatch ( getPartitions ( componentCurrentPath , selectedConsumer ) ) ;
85101 }
86102 } ,
87- [ dispatch , selectedConsumer , componentCurrentPath , consumers ] ,
103+ [ dispatch , selectedConsumer , componentCurrentPath , topicWasLoaded ] ,
88104 ) ;
89105
90- useAutofetcher ( fetchConsumerData , [ fetchConsumerData ] , autorefresh ) ;
91-
92- const consumersToSelect = useMemo (
93- ( ) =>
94- consumers
95- ? consumers . map ( ( consumer ) => ( {
96- value : consumer ,
97- content : consumer ,
98- } ) )
99- : undefined ,
100- [ consumers ] ,
101- ) ;
106+ useAutofetcher ( fetchData , [ fetchData ] , autorefresh ) ;
102107
108+ // Wrong consumer could be passed in search query
109+ // Reset consumer if it doesn't exist for current topic
103110 useEffect ( ( ) => {
104- const shouldUpdateSelectedConsumer =
105- ! selectedConsumer || ( consumers && ! consumers . includes ( selectedConsumer ) ) ;
111+ const isTopicWithoutConsumers = topicWasLoaded && ! consumers ;
112+ const wrongSelectedConsumer =
113+ selectedConsumer && consumers && ! consumers . includes ( selectedConsumer ) ;
106114
107- if ( consumersToSelect && consumersToSelect . length && shouldUpdateSelectedConsumer ) {
108- dispatch ( setSelectedConsumer ( consumersToSelect [ 0 ] . value ) ) ;
115+ if ( isTopicWithoutConsumers || wrongSelectedConsumer ) {
116+ dispatch ( setSelectedConsumer ( ) ) ;
109117 }
110- } , [ dispatch , consumersToSelect , selectedConsumer , consumers ] ) ;
111-
112- const selectedColumns : string [ ] = useMemo (
113- ( ) =>
114- savedSelectedColumns
115- ? JSON . parse ( savedSelectedColumns )
116- : PARTITIONS_DEFAULT_SELECTED_COLUMNS ,
117- [ savedSelectedColumns ] ,
118- ) ;
119-
120- const columnsToSelect = useMemo ( ( ) => {
121- return Object . values ( PARTITIONS_COLUMNS_IDS ) . map ( ( id ) => {
122- return {
123- title : PARTITIONS_COLUMNS_TITILES [ id ] ,
124- selected : Boolean ( selectedColumns ?. includes ( id ) ) ,
125- id : id ,
126- required : id === PARTITIONS_COLUMNS_IDS . PARTITION_ID ,
127- } ;
128- } ) ;
129- } , [ selectedColumns ] ) ;
118+ } , [ dispatch , topicWasLoaded , selectedConsumer , consumers ] ) ;
130119
131120 const columnsToShow = useMemo ( ( ) => {
132- return partitionsColumns . filter ( ( column ) => selectedColumns ?. includes ( column . name ) ) ;
133- } , [ selectedColumns ] ) ;
134-
135- const partitionsWithHosts : IPreparedPartitionDataWithHosts [ ] | undefined = useMemo ( ( ) => {
136- return partitions ?. map ( ( partition ) => {
137- const partitionHost =
138- partition . partitionNodeId && nodes ? nodes [ partition . partitionNodeId ] : undefined ;
139-
140- const connectionHost =
141- partition . connectionNodeId && nodes ? nodes [ partition . connectionNodeId ] : undefined ;
142-
143- return {
144- ...partition ,
145- partitionHost,
146- connectionHost,
147- } ;
148- } ) ;
149- } , [ partitions , nodes ] ) ;
150-
151- const dataToRender = useMemo ( ( ) => {
152- if ( ! partitionsWithHosts ) {
153- return [ ] ;
154- }
155-
156- const partitionIdRe = new RegExp ( escapeRegExp ( partitionIdSearchValue ) , 'i' ) ;
157- const generalRe = new RegExp ( escapeRegExp ( generalSearchValue ) , 'i' ) ;
158-
159- return partitionsWithHosts . filter ( ( partition ) => {
160- const {
161- partitionId,
162- readerName = '' ,
163- readSessionId = '' ,
164- partitionNodeId,
165- connectionNodeId,
166- partitionHost = '' ,
167- connectionHost = '' ,
168- } = partition ;
121+ return columns . filter ( ( column ) => ! hiddenColumns . includes ( column . name ) ) ;
122+ } , [ columns , hiddenColumns ] ) ;
169123
170- const isPartitionIdMatch = partitionIdRe . test ( partitionId ) ;
171- const isOtherValuesMatch =
172- generalRe . test ( readerName ) ||
173- generalRe . test ( readSessionId ) ||
174- generalRe . test ( String ( partitionNodeId ) ) ||
175- generalRe . test ( String ( connectionNodeId ) ) ||
176- generalRe . test ( partitionHost ) ||
177- generalRe . test ( connectionHost ) ;
178-
179- return isPartitionIdMatch && isOtherValuesMatch ;
180- } ) ;
181- } , [ partitionIdSearchValue , generalSearchValue , partitionsWithHosts ] ) ;
182-
183- const hadleTableColumnsSetupChange = ( value : TableColumnSetupItem [ ] ) => {
184- const columns = value . filter ( ( el ) => el . selected ) . map ( ( el ) => el . id ) ;
185- dispatch ( setSettingValue ( PARTITIONS_SELECTED_COLUMNS_KEY , JSON . stringify ( columns ) ) ) ;
124+ const hadleTableColumnsSetupChange = ( newHiddenColumns : string [ ] ) => {
125+ setHiddenColumns ( newHiddenColumns ) ;
186126 } ;
187127
188- const handleConsumerSelectChange = ( value : string [ ] ) => {
189- dispatch ( setSelectedConsumer ( value [ 0 ] ) ) ;
128+ const handleSelectedConsumerChange = ( value ? : string ) => {
129+ dispatch ( setSelectedConsumer ( value ) ) ;
190130 } ;
191131
192- const handlePartitionIdSearchChange = ( value : string ) => {
193- setPartitionIdSearchValue ( value ) ;
194- } ;
132+ const loading =
133+ ( topicLoading && ! topicWasLoaded ) ||
134+ ( nodesLoading && ! nodesWasLoaded ) ||
135+ ( partitionsLoading && ! partitionsWasLoaded ) ;
195136
196- const handleGeneralSearchChange = ( value : string ) => {
197- setGeneralSearchValue ( value ) ;
198- } ;
137+ const error = nodesError || topicError || partitionsError ;
199138
200- if ( error ) {
201- return < ResponseError error = { error } /> ;
202- }
139+ const getContent = ( ) => {
140+ if ( loading ) {
141+ return < TableSkeleton className = { b ( 'loader' ) } /> ;
142+ }
143+ if ( error ) {
144+ return < ResponseError error = { error } /> ;
145+ }
203146
204- if ( ! consumersToSelect || ! consumersToSelect . length ) {
205- return < div > { i18n ( `noConsumersMessage.${ isCdcStream ? 'stream' : 'topic' } ` ) } </ div > ;
206- }
147+ return (
148+ < DataTable
149+ theme = "yandex-cloud"
150+ data = { partitionsToRender }
151+ columns = { columnsToShow }
152+ settings = { DEFAULT_TABLE_SETTINGS }
153+ emptyDataMessage = { i18n ( 'table.emptyDataMessage' ) }
154+ />
155+ ) ;
156+ } ;
207157
208158 return (
209159 < div className = { b ( ) } >
210- < div className = { b ( 'controls' ) } >
211- < Select
212- className = { b ( 'consumer-select' ) }
213- placeholder = { i18n ( 'controls.consumerSelector.placeholder' ) }
214- label = { i18n ( 'controls.consumerSelector' ) }
215- options = { consumersToSelect }
216- value = { [ selectedConsumer || '' ] }
217- onUpdate = { handleConsumerSelectChange }
218- filterable = { consumers && consumers . length > 5 }
219- />
220- < Search
221- onChange = { handlePartitionIdSearchChange }
222- placeholder = { i18n ( 'controls.partitionSearch' ) }
223- className = { b ( 'search' , { partition : true } ) }
224- value = { partitionIdSearchValue }
225- />
226- < Search
227- onChange = { handleGeneralSearchChange }
228- placeholder = { i18n ( 'controls.generalSearch' ) }
229- className = { b ( 'search' , { general : true } ) }
230- value = { generalSearchValue }
231- />
232- < TableColumnSetup
233- key = "TableColumnSetup"
234- popupWidth = "242px"
235- items = { columnsToSelect }
236- showStatus
237- onUpdate = { hadleTableColumnsSetupChange }
238- className = { b ( 'table-settings' ) }
239- />
240- </ div >
160+ < PartitionsControls
161+ consumers = { consumers }
162+ selectedConsumer = { selectedConsumer }
163+ onSelectedConsumerChange = { handleSelectedConsumerChange }
164+ selectDisabled = { Boolean ( error ) || loading }
165+ partitions = { partitionsWithHosts }
166+ onSearchChange = { setPartitionsToRender }
167+ hiddenColumns = { hiddenColumns }
168+ onHiddenColumnsChange = { hadleTableColumnsSetupChange }
169+ initialColumnsIds = { columnsIdsForSelector }
170+ />
241171 < div className = { b ( 'table-wrapper' ) } >
242- < div className = { b ( 'table-content' ) } >
243- { loading && ! wasLoaded ? (
244- < TableSkeleton className = { b ( 'loader' ) } />
245- ) : (
246- < DataTable
247- theme = "yandex-cloud"
248- data = { dataToRender }
249- columns = { columnsToShow }
250- settings = { DEFAULT_TABLE_SETTINGS }
251- emptyDataMessage = { i18n ( 'table.emptyDataMessage' ) }
252- />
253- ) }
254- </ div >
172+ < div className = { b ( 'table-content' ) } > { getContent ( ) } </ div >
255173 </ div >
256174 </ div >
257175 ) ;
0 commit comments