1
1
import { EmptySimple } from '@/components/Empty' ;
2
- import { Text , Button , Center , Box , Group , ActionIcon , Stack , Tooltip , ScrollArea } from '@mantine/core' ;
2
+ import { Text , Button , Center , Box , Group , ActionIcon , Stack , Tooltip , ScrollArea , Loader , Flex } from '@mantine/core' ;
3
3
import { IconChevronRight , IconExternalLink , IconPlus } from '@tabler/icons-react' ;
4
- import { useEffect , type FC , useCallback } from 'react' ;
4
+ import { useEffect , type FC , useCallback , useMemo } from 'react' ;
5
5
import { useNavigate } from 'react-router-dom' ;
6
6
import { useDocumentTitle } from '@mantine/hooks' ;
7
7
import { useGetStreamMetadata } from '@/hooks/useGetStreamMetadata' ;
@@ -13,34 +13,58 @@ import CreateStreamModal from './CreateStreamModal';
13
13
import { useAppStore , appStoreReducers } from '@/layouts/MainLayout/providers/AppProvider' ;
14
14
import { getStreamsSepcificAccess } from '@/components/Navbar/rolesHandler' ;
15
15
import _ from 'lodash' ;
16
+ import { heights } from '@/components/Mantine/sizing' ;
17
+ import { PRIMARY_HEADER_HEIGHT } from '@/constants/theme' ;
16
18
17
19
const { changeStream, toggleCreateStreamModal } = appStoreReducers ;
18
20
19
- const EmptyStreamsView : FC = ( ) => {
21
+ type NoStreamsViewProps = {
22
+ hasCreateStreamAccess : boolean ;
23
+ openCreateStreamModal : ( ) => void ;
24
+ } ;
25
+
26
+ const NoStreamsView : FC < NoStreamsViewProps > = ( {
27
+ hasCreateStreamAccess,
28
+ openCreateStreamModal,
29
+ } : {
30
+ hasCreateStreamAccess : boolean ;
31
+ openCreateStreamModal : ( ) => void ;
32
+ } ) => {
20
33
const classes = homeStyles ;
21
- const { messageStyle, btnStyle, noDataViewContainer } = classes ;
34
+ const { messageStyle, btnStyle, noDataViewContainer, createStreamButton } = classes ;
22
35
return (
23
36
< Center className = { noDataViewContainer } >
24
37
< EmptySimple height = { 70 } width = { 100 } />
25
38
< Text className = { messageStyle } > No Stream found on this account</ Text >
26
- < Button
27
- target = "_blank"
28
- component = "a"
29
- href = "https://www.parseable.io/docs/category/log-ingestion"
30
- className = { btnStyle }
31
- leftSection = { < IconExternalLink size = "0.9rem" /> } >
32
- Documentation
33
- </ Button >
39
+ < Flex gap = "md" >
40
+ < Button
41
+ target = "_blank"
42
+ component = "a"
43
+ href = "https://www.parseable.io/docs/category/log-ingestion"
44
+ className = { btnStyle }
45
+ leftSection = { < IconExternalLink size = "0.9rem" /> } >
46
+ Documentation
47
+ </ Button >
48
+ { hasCreateStreamAccess && (
49
+ < Button
50
+ style = { { marginTop : '1rem' } }
51
+ className = { createStreamButton }
52
+ onClick = { openCreateStreamModal }
53
+ leftSection = { < IconPlus stroke = { 2 } size = { '1rem' } /> } >
54
+ Create Stream
55
+ </ Button >
56
+ ) }
57
+ </ Flex >
34
58
</ Center >
35
59
) ;
36
60
} ;
37
61
38
62
const Home : FC = ( ) => {
39
63
useDocumentTitle ( 'Parseable | Streams' ) ;
40
64
const classes = homeStyles ;
41
- const { container, createStreamButton } = classes ;
65
+ const { container, createStreamButton, noDataViewContainer } = classes ;
42
66
const navigate = useNavigate ( ) ;
43
- const { getStreamMetadata, metaData } = useGetStreamMetadata ( ) ;
67
+ const { getStreamMetadata, metaData, isLoading , error } = useGetStreamMetadata ( ) ;
44
68
const [ userSpecificStreams , setAppStore ] = useAppStore ( ( store ) => store . userSpecificStreams ) ;
45
69
const [ userRoles ] = useAppStore ( ( store ) => store . userRoles ) ;
46
70
const [ userAccessMap ] = useAppStore ( ( store ) => store . userAccessMap ) ;
@@ -60,50 +84,72 @@ const Home: FC = () => {
60
84
setAppStore ( ( store ) => toggleCreateStreamModal ( store ) ) ;
61
85
} , [ ] ) ;
62
86
87
+ const hasCreateStreamAccess = useMemo ( ( ) => userAccessMap ?. hasCreateStreamAccess , [ userAccessMap ] ) ;
88
+
89
+ const shouldDisplayEmptyPlaceholder = displayEmptyPlaceholder || isLoading || error ;
90
+
63
91
return (
64
92
< >
65
- < Stack
66
- style = { {
67
- margin : '1rem' ,
68
- alignItems : 'center' ,
69
- justifyContent : 'space-between' ,
70
- flexDirection : 'row' ,
71
- } } >
72
- < Text style = { { fontSize : '0.8rem' } } fw = { 500 } >
73
- All Streams
74
- </ Text >
75
- < Box >
76
- { userAccessMap . hasCreateStreamAccess && (
77
- < Button
78
- variant = "outline"
79
- className = { createStreamButton }
80
- onClick = { openCreateStreamModal }
81
- leftSection = { < IconPlus stroke = { 2 } size = { '1rem' } /> } >
82
- Create Stream
83
- </ Button >
84
- ) }
85
- </ Box >
86
- </ Stack >
87
- < ScrollArea >
88
- < Box className = { container } style = { { display : 'flex' , flex : 1 , paddingBottom : '3rem' } } >
93
+ { ! shouldDisplayEmptyPlaceholder && (
94
+ < Stack
95
+ style = { {
96
+ padding : '1rem' ,
97
+ alignItems : 'center' ,
98
+ justifyContent : 'space-between' ,
99
+ flexDirection : 'row' ,
100
+ borderBottom : '1px solid var(--mantine-color-gray-3)' ,
101
+ } } >
102
+ < Text style = { { fontSize : '0.8rem' } } fw = { 500 } >
103
+ All Streams ({ metaData && Object . keys ( metaData ) . length } )
104
+ </ Text >
105
+ < Box >
106
+ { hasCreateStreamAccess && (
107
+ < Button
108
+ variant = "outline"
109
+ className = { createStreamButton }
110
+ onClick = { openCreateStreamModal }
111
+ leftSection = { < IconPlus stroke = { 2 } size = { '1rem' } /> } >
112
+ Create Stream
113
+ </ Button >
114
+ ) }
115
+ </ Box >
116
+ </ Stack >
117
+ ) }
118
+ < ScrollArea style = { { display : 'flex' , justifyContent : 'center' , alignItems : 'center' } } >
119
+ < Box
120
+ className = { container }
121
+ style = { {
122
+ display : 'flex' ,
123
+ paddingTop : shouldDisplayEmptyPlaceholder ? '0rem' : '1rem' ,
124
+ paddingBottom : shouldDisplayEmptyPlaceholder ? '0rem' : '3rem' ,
125
+ height : shouldDisplayEmptyPlaceholder ? `calc(${ heights . screen } - ${ PRIMARY_HEADER_HEIGHT } px)` : 'auto' ,
126
+ } } >
89
127
< CreateStreamModal />
90
- { displayEmptyPlaceholder ? (
91
- < EmptyStreamsView />
128
+ { isLoading ? (
129
+ < Center className = { noDataViewContainer } >
130
+ < Loader type = "parseable" />
131
+ </ Center >
92
132
) : (
93
- < Group style = { { marginRight : '1rem' , marginLeft : '1rem' , gap : '1rem' } } >
94
- { Object . entries ( metaData || { } ) . map ( ( [ stream , data ] ) => {
95
- const hasSettingsAccess = _ . includes ( getStreamsSepcificAccess ( userRoles , stream ) , 'StreamSettings' ) ;
96
- return (
97
- < StreamInfo
98
- key = { stream }
99
- stream = { stream }
100
- data = { data }
101
- navigateToStream = { navigateToStream }
102
- hasSettingsAccess = { hasSettingsAccess }
103
- />
104
- ) ;
105
- } ) }
106
- </ Group >
133
+ < >
134
+ { displayEmptyPlaceholder || error ? (
135
+ < NoStreamsView
136
+ hasCreateStreamAccess = { hasCreateStreamAccess }
137
+ openCreateStreamModal = { openCreateStreamModal }
138
+ />
139
+ ) : (
140
+ < Group style = { { margin : '0 1rem' , gap : '1rem' } } >
141
+ { Object . entries ( metaData || { } ) . map ( ( [ stream , data ] ) => (
142
+ < StreamInfo
143
+ key = { stream }
144
+ stream = { stream }
145
+ data = { data }
146
+ navigateToStream = { navigateToStream }
147
+ hasSettingsAccess = { _ . includes ( getStreamsSepcificAccess ( userRoles , stream ) , 'StreamSettings' ) }
148
+ />
149
+ ) ) }
150
+ </ Group >
151
+ ) }
152
+ </ >
107
153
) }
108
154
</ Box >
109
155
</ ScrollArea >
0 commit comments