mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: add a button to download user access information (#4746)
This commit is contained in:
		
							parent
							
								
									53c40372dd
								
							
						
					
					
						commit
						7843c93dc5
					
				
							
								
								
									
										17
									
								
								frontend/src/component/accessOverview/AccessOverview.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								frontend/src/component/accessOverview/AccessOverview.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import { ADMIN } from 'component/providers/AccessProvider/permissions'; | ||||
| import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard'; | ||||
| import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | ||||
| import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature'; | ||||
| import { IconButton } from '@mui/material'; | ||||
| import { Download } from '@mui/icons-material'; | ||||
| import { useAccessOverviewApi } from 'hooks/api/actions/useAccessOverviewApi/useAccessOverviewApi'; | ||||
| 
 | ||||
| export const AccessOverview = () => { | ||||
|     const { downloadCSV } = useAccessOverviewApi(); | ||||
| 
 | ||||
|     return ( | ||||
|         <IconButton onClick={downloadCSV}> | ||||
|             <Download /> | ||||
|         </IconButton> | ||||
|     ); | ||||
| }; | ||||
| @ -6,6 +6,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit | ||||
| import ConfirmUserAdded from '../ConfirmUserAdded/ConfirmUserAdded'; | ||||
| import { useUsers } from 'hooks/api/getters/useUsers/useUsers'; | ||||
| import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi'; | ||||
| import { useAccessOverviewApi } from 'hooks/api/actions/useAccessOverviewApi/useAccessOverviewApi'; | ||||
| import { IUser } from 'interfaces/user'; | ||||
| import { IRole } from 'interfaces/role'; | ||||
| import useToast from 'hooks/useToast'; | ||||
| @ -13,7 +14,7 @@ import { formatUnknownError } from 'utils/formatUnknownError'; | ||||
| import { useUsersPlan } from 'hooks/useUsersPlan'; | ||||
| import { PageContent } from 'component/common/PageContent/PageContent'; | ||||
| import { PageHeader } from 'component/common/PageHeader/PageHeader'; | ||||
| import { Button, useMediaQuery } from '@mui/material'; | ||||
| import { Button, IconButton, Tooltip, useMediaQuery } from '@mui/material'; | ||||
| import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; | ||||
| import { UserTypeCell } from './UserTypeCell/UserTypeCell'; | ||||
| import { useFlexLayout, useSortBy, useTable } from 'react-table'; | ||||
| @ -31,12 +32,15 @@ import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColum | ||||
| import { UserLimitWarning } from './UserLimitWarning/UserLimitWarning'; | ||||
| import { RoleCell } from 'component/common/Table/cells/RoleCell/RoleCell'; | ||||
| import { useSearch } from 'hooks/useSearch'; | ||||
| import { Download } from '@mui/icons-material'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag'; | ||||
| 
 | ||||
| const UsersList = () => { | ||||
|     const navigate = useNavigate(); | ||||
|     const { users, roles, refetch, loading } = useUsers(); | ||||
|     const { setToastData, setToastApiError } = useToast(); | ||||
|     const { removeUser, userLoading, userApiErrors } = useAdminUsersApi(); | ||||
|     const { downloadCSV } = useAccessOverviewApi(); | ||||
|     const [pwDialog, setPwDialog] = useState<{ open: boolean; user?: IUser }>({ | ||||
|         open: false, | ||||
|     }); | ||||
| @ -47,6 +51,8 @@ const UsersList = () => { | ||||
|     const [delUser, setDelUser] = useState<IUser>(); | ||||
|     const { planUsers, isBillingUsers } = useUsersPlan(users); | ||||
| 
 | ||||
|     const accessOverviewEnabled = useUiFlag('accessOverview'); | ||||
| 
 | ||||
|     const [searchValue, setSearchValue] = useState(''); | ||||
| 
 | ||||
|     const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); | ||||
| @ -263,6 +269,23 @@ const UsersList = () => { | ||||
|                                 onChange={setSearchValue} | ||||
|                             /> | ||||
|                             <PageHeader.Divider /> | ||||
| 
 | ||||
|                             <ConditionallyRender | ||||
|                                 condition={Boolean(accessOverviewEnabled)} | ||||
|                                 show={() => ( | ||||
|                                     <> | ||||
|                                         <Tooltip | ||||
|                                             title="Exports user access information" | ||||
|                                             arrow | ||||
|                                             describeChild | ||||
|                                         > | ||||
|                                             <IconButton onClick={downloadCSV}> | ||||
|                                                 <Download /> | ||||
|                                             </IconButton> | ||||
|                                         </Tooltip> | ||||
|                                     </> | ||||
|                                 )} | ||||
|                             /> | ||||
|                             <Button | ||||
|                                 variant="contained" | ||||
|                                 color="primary" | ||||
|  | ||||
| @ -0,0 +1,30 @@ | ||||
| import useAPI from '../useApi/useApi'; | ||||
| 
 | ||||
| export const useAccessOverviewApi = () => { | ||||
|     const { loading, makeRequest, createRequest, errors } = useAPI({ | ||||
|         propagateErrors: true, | ||||
|     }); | ||||
| 
 | ||||
|     const downloadCSV = async () => { | ||||
|         const requestId = 'downloadCSV'; | ||||
|         const req = createRequest( | ||||
|             'api/admin/access/overview', | ||||
|             { | ||||
|                 method: 'GET', | ||||
|                 responseType: 'blob', | ||||
|                 headers: { Accept: 'text/csv' }, | ||||
|             }, | ||||
|             requestId | ||||
|         ); | ||||
| 
 | ||||
|         const file = await (await makeRequest(req.caller, req.id)).blob(); | ||||
|         const url = window.URL.createObjectURL(file); | ||||
|         window.location.assign(url); | ||||
|     }; | ||||
| 
 | ||||
|     return { | ||||
|         downloadCSV, | ||||
|         errors, | ||||
|         loading, | ||||
|     }; | ||||
| }; | ||||
| @ -66,6 +66,7 @@ export type UiFlags = { | ||||
|     doraMetrics?: boolean; | ||||
|     variantTypeNumber?: boolean; | ||||
|     privateProjects?: boolean; | ||||
|     accessOverview?: boolean; | ||||
|     [key: string]: boolean | Variant | undefined; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -71,6 +71,7 @@ exports[`should create default config 1`] = ` | ||||
|       "isEnabled": [Function], | ||||
|     }, | ||||
|     "flags": { | ||||
|       "accessOverview": false, | ||||
|       "anonymiseEventLog": false, | ||||
|       "caseInsensitiveInOperators": false, | ||||
|       "customRootRolesKillSwitch": false, | ||||
| @ -108,6 +109,7 @@ exports[`should create default config 1`] = ` | ||||
|   }, | ||||
|   "flagResolver": FlagResolver { | ||||
|     "experiments": { | ||||
|       "accessOverview": false, | ||||
|       "anonymiseEventLog": false, | ||||
|       "caseInsensitiveInOperators": false, | ||||
|       "customRootRolesKillSwitch": false, | ||||
|  | ||||
| @ -29,6 +29,7 @@ export type IFlagKey = | ||||
|     | 'featureNamingPattern' | ||||
|     | 'doraMetrics' | ||||
|     | 'variantTypeNumber' | ||||
|     | 'accessOverview' | ||||
|     | 'privateProjects'; | ||||
| 
 | ||||
| export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; | ||||
| @ -137,6 +138,10 @@ const flags: IFlags = { | ||||
|         process.env.UNLEASH_EXPERIMENTAL_PRIVATE_PROJECTS, | ||||
|         false, | ||||
|     ), | ||||
|     accessOverview: parseEnvVarBoolean( | ||||
|         process.env.UNLEASH_EXPERIMENTAL_ACCESS_OVERVIEW, | ||||
|         false, | ||||
|     ), | ||||
| }; | ||||
| 
 | ||||
| export const defaultExperimentalOptions: IExperimentalOptions = { | ||||
|  | ||||
| @ -44,6 +44,7 @@ process.nextTick(async () => { | ||||
|                         doraMetrics: true, | ||||
|                         variantTypeNumber: true, | ||||
|                         privateProjects: true, | ||||
|                         accessOverview: true, | ||||
|                     }, | ||||
|                 }, | ||||
|                 authentication: { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user