mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: empty state application list improvements (#6579)
This commit is contained in:
		
							parent
							
								
									45634689f8
								
							
						
					
					
						commit
						59ee0b3bbe
					
				| @ -1,201 +0,0 @@ | ||||
| import { useMemo } from 'react'; | ||||
| import { Avatar, CircularProgress, Icon, Link } from '@mui/material'; | ||||
| import Warning from '@mui/icons-material/Warning'; | ||||
| import { styles as themeStyles } from 'component/common'; | ||||
| import { PageContent } from 'component/common/PageContent/PageContent'; | ||||
| import { PageHeader } from 'component/common/PageHeader/PageHeader'; | ||||
| import useApplications from 'hooks/api/getters/useApplications/useApplications'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| import { Search } from 'component/common/Search/Search'; | ||||
| import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; | ||||
| import { | ||||
|     SortableTableHeader, | ||||
|     Table, | ||||
|     TableBody, | ||||
|     TableCell, | ||||
|     TableRow, | ||||
| } from 'component/common/Table'; | ||||
| import { useGlobalFilter, useSortBy, useTable } from 'react-table'; | ||||
| import { sortTypes } from 'utils/sortTypes'; | ||||
| import { IconCell } from 'component/common/Table/cells/IconCell/IconCell'; | ||||
| import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; | ||||
| import { ApplicationUsageCell } from './ApplicationUsageCell/ApplicationUsageCell'; | ||||
| import { ApplicationSchema } from 'openapi'; | ||||
| 
 | ||||
| export const ApplicationList = () => { | ||||
|     const { applications: data, loading } = useApplications(); | ||||
| 
 | ||||
|     const renderNoApplications = () => ( | ||||
|         <> | ||||
|             <section style={{ textAlign: 'center' }}> | ||||
|                 <Warning titleAccess='Warning' /> <br /> | ||||
|                 <br /> | ||||
|                 Oh snap, it does not seem like you have connected any | ||||
|                 applications. To connect your application to Unleash you will | ||||
|                 require a Client SDK. | ||||
|                 <br /> | ||||
|                 <br /> | ||||
|                 You can read more about how to use Unleash in your application | ||||
|                 in the{' '} | ||||
|                 <Link href='https://docs.getunleash.io/docs/sdks/'> | ||||
|                     documentation. | ||||
|                 </Link> | ||||
|             </section> | ||||
|         </> | ||||
|     ); | ||||
| 
 | ||||
|     const initialState = useMemo( | ||||
|         () => ({ | ||||
|             sortBy: [{ id: 'name', desc: false }], | ||||
|             hiddenColumns: ['description', 'sortOrder'], | ||||
|         }), | ||||
|         [], | ||||
|     ); | ||||
| 
 | ||||
|     const columns = useMemo( | ||||
|         () => [ | ||||
|             { | ||||
|                 id: 'Icon', | ||||
|                 Cell: ({ | ||||
|                     row: { | ||||
|                         original: { icon }, | ||||
|                     }, | ||||
|                 }: any) => ( | ||||
|                     <IconCell | ||||
|                         icon={ | ||||
|                             <Avatar> | ||||
|                                 <Icon>{icon || 'apps'}</Icon> | ||||
|                             </Avatar> | ||||
|                         } | ||||
|                     /> | ||||
|                 ), | ||||
|                 disableGlobalFilter: true, | ||||
|             }, | ||||
|             { | ||||
|                 Header: 'Name', | ||||
|                 accessor: 'appName', | ||||
|                 width: '50%', | ||||
|                 Cell: ({ | ||||
|                     row: { | ||||
|                         original: { appName, description }, | ||||
|                     }, | ||||
|                 }: any) => ( | ||||
|                     <LinkCell | ||||
|                         title={appName} | ||||
|                         to={`/applications/${appName}`} | ||||
|                         subtitle={description} | ||||
|                     /> | ||||
|                 ), | ||||
|                 sortType: 'alphanumeric', | ||||
|             }, | ||||
|             { | ||||
|                 Header: 'Project(environment)', | ||||
|                 accessor: 'usage', | ||||
|                 width: '50%', | ||||
|                 Cell: ({ | ||||
|                     row: { original }, | ||||
|                 }: { | ||||
|                     row: { original: ApplicationSchema }; | ||||
|                 }) => <ApplicationUsageCell usage={original.usage} />, | ||||
|                 sortType: 'alphanumeric', | ||||
|             }, | ||||
|             { | ||||
|                 accessor: 'description', | ||||
|                 disableSortBy: true, | ||||
|             }, | ||||
|             { | ||||
|                 accessor: 'sortOrder', | ||||
|                 disableGlobalFilter: true, | ||||
|                 sortType: 'number', | ||||
|             }, | ||||
|         ], | ||||
|         [], | ||||
|     ); | ||||
| 
 | ||||
|     const { | ||||
|         getTableProps, | ||||
|         getTableBodyProps, | ||||
|         headerGroups, | ||||
|         rows, | ||||
|         prepareRow, | ||||
|         state: { globalFilter }, | ||||
|         setGlobalFilter, | ||||
|     } = useTable( | ||||
|         { | ||||
|             columns: columns as any[], // TODO: fix after `react-table` v8 update
 | ||||
|             data, | ||||
|             initialState, | ||||
|             sortTypes, | ||||
|             autoResetGlobalFilter: false, | ||||
|             autoResetSortBy: false, | ||||
|             disableSortRemove: true, | ||||
|         }, | ||||
|         useGlobalFilter, | ||||
|         useSortBy, | ||||
|     ); | ||||
| 
 | ||||
|     if (!data) { | ||||
|         return <CircularProgress variant='indeterminate' />; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             <PageContent | ||||
|                 header={ | ||||
|                     <PageHeader | ||||
|                         title={`Applications (${rows.length})`} | ||||
|                         actions={ | ||||
|                             <Search | ||||
|                                 initialValue={globalFilter} | ||||
|                                 onChange={setGlobalFilter} | ||||
|                             /> | ||||
|                         } | ||||
|                     /> | ||||
|                 } | ||||
|             > | ||||
|                 <div className={themeStyles.fullwidth}> | ||||
|                     <ConditionallyRender | ||||
|                         condition={data.length > 0} | ||||
|                         show={ | ||||
|                             <SearchHighlightProvider value={globalFilter}> | ||||
|                                 <Table {...getTableProps()}> | ||||
|                                     <SortableTableHeader | ||||
|                                         headerGroups={headerGroups} | ||||
|                                     /> | ||||
|                                     <TableBody {...getTableBodyProps()}> | ||||
|                                         {rows.map((row) => { | ||||
|                                             prepareRow(row); | ||||
|                                             return ( | ||||
|                                                 <TableRow | ||||
|                                                     hover | ||||
|                                                     {...row.getRowProps()} | ||||
|                                                 > | ||||
|                                                     {row.cells.map((cell) => ( | ||||
|                                                         <TableCell | ||||
|                                                             {...cell.getCellProps()} | ||||
|                                                         > | ||||
|                                                             {cell.render( | ||||
|                                                                 'Cell', | ||||
|                                                             )} | ||||
|                                                         </TableCell> | ||||
|                                                     ))} | ||||
|                                                 </TableRow> | ||||
|                                             ); | ||||
|                                         })} | ||||
|                                     </TableBody> | ||||
|                                 </Table> | ||||
|                             </SearchHighlightProvider> | ||||
|                         } | ||||
|                         elseShow={ | ||||
|                             <ConditionallyRender | ||||
|                                 condition={loading} | ||||
|                                 show={<div>...loading</div>} | ||||
|                                 elseShow={renderNoApplications()} | ||||
|                             /> | ||||
|                         } | ||||
|                     /> | ||||
|                 </div> | ||||
|             </PageContent> | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
| @ -28,9 +28,9 @@ test('Display applications list', async () => { | ||||
|     ); | ||||
| }); | ||||
| 
 | ||||
| test('Display no applications', async () => { | ||||
| test('Display no applications connected', async () => { | ||||
|     setupApi([]); | ||||
|     render(<PaginatedApplicationList />); | ||||
| 
 | ||||
|     await screen.findByText('Warning'); | ||||
|     await screen.findByText(/To connect your application to Unleash/); | ||||
| }); | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| import { useMemo } from 'react'; | ||||
| import { Avatar, Icon, Link } from '@mui/material'; | ||||
| import Warning from '@mui/icons-material/Warning'; | ||||
| import { Avatar, Icon, Link, styled } from '@mui/material'; | ||||
| import { styles as themeStyles } from 'component/common'; | ||||
| import { PageContent } from 'component/common/PageContent/PageContent'; | ||||
| import { PageHeader } from 'component/common/PageHeader/PageHeader'; | ||||
| @ -26,23 +25,34 @@ import { withTableState } from 'utils/withTableState'; | ||||
| import useLoading from 'hooks/useLoading'; | ||||
| import mapValues from 'lodash.mapvalues'; | ||||
| 
 | ||||
| const renderNoApplications = () => ( | ||||
|     <> | ||||
|         <section style={{ textAlign: 'center' }}> | ||||
|             <Warning titleAccess='Warning' /> <br /> | ||||
|             <br /> | ||||
|             Oh snap, it does not seem like you have connected any applications. | ||||
|             To connect your application to Unleash you will require a Client | ||||
|             SDK. | ||||
|             <br /> | ||||
| const InfoMessage = styled('div')(({ theme }) => ({ | ||||
|     textAlign: 'center', | ||||
|     padding: theme.spacing(9, 0, 9, 0), | ||||
|     minHeight: '150px', | ||||
| })); | ||||
| 
 | ||||
| const renderNoResults = (query: string | null | undefined) => { | ||||
|     if (typeof query === 'string' && query.length > 0) { | ||||
|         return renderNoMatchingSearch(query); | ||||
|     } | ||||
|     return ( | ||||
|         <InfoMessage> | ||||
|             You don't have have any connected applications. To connect your | ||||
|             application to Unleash you will require a{' '} | ||||
|             <Link href='https://docs.getunleash.io/docs/sdks/'>Client SDK</Link> | ||||
|             . | ||||
|             <br /> | ||||
|             You can read more about how to use Unleash in your application in | ||||
|             the{' '} | ||||
|             <Link href='https://docs.getunleash.io/docs/sdks/'> | ||||
|                 documentation. | ||||
|             </Link> | ||||
|         </section> | ||||
|     </> | ||||
|         </InfoMessage> | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| const renderNoMatchingSearch = (query: string) => ( | ||||
|     <InfoMessage>No application found matching "{query}"</InfoMessage> | ||||
| ); | ||||
| 
 | ||||
| const columnHelper = createColumnHelper<ApplicationSchema>(); | ||||
| @ -125,8 +135,6 @@ export const PaginatedApplicationList = () => { | ||||
|         }), | ||||
|     ); | ||||
| 
 | ||||
|     const rows = table.getRowModel().rows; | ||||
| 
 | ||||
|     const { offset, limit, query, sortBy, sortOrder, ...filterState } = | ||||
|         tableState; | ||||
| 
 | ||||
| @ -165,7 +173,7 @@ export const PaginatedApplicationList = () => { | ||||
|                                 </div> | ||||
|                             </SearchHighlightProvider> | ||||
|                         } | ||||
|                         elseShow={renderNoApplications()} | ||||
|                         elseShow={renderNoResults(query)} | ||||
|                     /> | ||||
|                 </div> | ||||
|             </PageContent> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user