mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	
							parent
							
								
									21573d36de
								
							
						
					
					
						commit
						8a7bf865d3
					
				| @ -66,6 +66,44 @@ type Props = { | ||||
|     admins: PersonalDashboardSchemaAdminsItem[]; | ||||
| }; | ||||
| 
 | ||||
| export const AdminListRendered: React.FC<Pick<Props, 'admins'>> = ({ | ||||
|     admins, | ||||
| }) => { | ||||
|     return ( | ||||
|         <BoxMainContent> | ||||
|             {admins.length ? ( | ||||
|                 <> | ||||
|                     <p> | ||||
|                         Your Unleash administrator | ||||
|                         {admins.length > 1 ? 's are' : ' is'}: | ||||
|                     </p> | ||||
|                     <AdminList> | ||||
|                         {admins.map((admin) => { | ||||
|                             return ( | ||||
|                                 <AdminListItem key={admin.id}> | ||||
|                                     <UserAvatar | ||||
|                                         sx={{ | ||||
|                                             margin: 0, | ||||
|                                         }} | ||||
|                                         user={admin} | ||||
|                                     /> | ||||
|                                     <Typography> | ||||
|                                         {admin.name || | ||||
|                                             admin.username || | ||||
|                                             admin.email} | ||||
|                                     </Typography> | ||||
|                                 </AdminListItem> | ||||
|                             ); | ||||
|                         })} | ||||
|                     </AdminList> | ||||
|                 </> | ||||
|             ) : ( | ||||
|                 <p>You have no Unleash administrators to contact.</p> | ||||
|             )} | ||||
|         </BoxMainContent> | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| export const ContentGridNoProjects: React.FC<Props> = ({ owners, admins }) => { | ||||
|     return ( | ||||
|         <ContentGridContainer> | ||||
| @ -98,40 +136,7 @@ export const ContentGridNoProjects: React.FC<Props> = ({ owners, admins }) => { | ||||
|                             <NeutralCircleContainer>1</NeutralCircleContainer> | ||||
|                             Contact Unleash admin | ||||
|                         </TitleContainer> | ||||
|                         <BoxMainContent> | ||||
|                             {admins.length ? ( | ||||
|                                 <> | ||||
|                                     <p> | ||||
|                                         Your Unleash administrator | ||||
|                                         {admins.length > 1 ? 's are' : ' is'}: | ||||
|                                     </p> | ||||
|                                     <AdminList> | ||||
|                                         {admins.map((admin) => { | ||||
|                                             return ( | ||||
|                                                 <AdminListItem key={admin.id}> | ||||
|                                                     <UserAvatar | ||||
|                                                         sx={{ | ||||
|                                                             margin: 0, | ||||
|                                                         }} | ||||
|                                                         user={admin} | ||||
|                                                     /> | ||||
|                                                     <Typography> | ||||
|                                                         {admin.name || | ||||
|                                                             admin.username || | ||||
|                                                             admin.email} | ||||
|                                                     </Typography> | ||||
|                                                 </AdminListItem> | ||||
|                                             ); | ||||
|                                         })} | ||||
|                                     </AdminList> | ||||
|                                 </> | ||||
|                             ) : ( | ||||
|                                 <p> | ||||
|                                     You have no Unleash administrators to | ||||
|                                     contact. | ||||
|                                 </p> | ||||
|                             )} | ||||
|                         </BoxMainContent> | ||||
|                         <AdminListRendered admins={admins} /> | ||||
|                     </GridContent> | ||||
|                 </GridItem> | ||||
|                 <GridItem gridArea='box2'> | ||||
|  | ||||
| @ -14,10 +14,11 @@ import { ProjectSetupComplete } from './ProjectSetupComplete'; | ||||
| import { ConnectSDK, CreateFlag, ExistingFlag } from './ConnectSDK'; | ||||
| import { LatestProjectEvents } from './LatestProjectEvents'; | ||||
| import { RoleAndOwnerInfo } from './RoleAndOwnerInfo'; | ||||
| import { useEffect, useRef, type FC } from 'react'; | ||||
| import { forwardRef, useEffect, useRef, type FC } from 'react'; | ||||
| import { StyledCardTitle } from './PersonalDashboard'; | ||||
| import type { | ||||
|     PersonalDashboardProjectDetailsSchema, | ||||
|     PersonalDashboardSchemaAdminsItem, | ||||
|     PersonalDashboardSchemaProjectsItem, | ||||
| } from '../../openapi'; | ||||
| import { | ||||
| @ -29,6 +30,7 @@ import { | ||||
|     GridItem, | ||||
|     SpacedGridItem, | ||||
| } from './Grid'; | ||||
| import { ContactAdmins, DataError } from './ProjectDetailsError'; | ||||
| 
 | ||||
| const ActiveProjectDetails: FC<{ | ||||
|     project: PersonalDashboardSchemaProjectsItem; | ||||
| @ -108,25 +110,85 @@ const ProjectListItem: FC<{ | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| export const MyProjects: FC<{ | ||||
| export const MyProjects = forwardRef< | ||||
|     HTMLDivElement, | ||||
|     { | ||||
|         projects: PersonalDashboardSchemaProjectsItem[]; | ||||
|         personalDashboardProjectDetails?: PersonalDashboardProjectDetailsSchema; | ||||
|         activeProject: string; | ||||
|         setActiveProject: (project: string) => void; | ||||
| }> = ({ | ||||
|         admins: PersonalDashboardSchemaAdminsItem[]; | ||||
|     } | ||||
| >( | ||||
|     ( | ||||
|         { | ||||
|             projects, | ||||
|             personalDashboardProjectDetails, | ||||
|             setActiveProject, | ||||
|             activeProject, | ||||
| }) => { | ||||
|             admins, | ||||
|         }, | ||||
|         ref, | ||||
|     ) => { | ||||
|         const activeProjectStage = | ||||
|         personalDashboardProjectDetails?.onboardingStatus.status ?? 'loading'; | ||||
|             personalDashboardProjectDetails?.onboardingStatus.status ?? | ||||
|             'loading'; | ||||
|         const setupIncomplete = | ||||
|             activeProjectStage === 'onboarding-started' || | ||||
|             activeProjectStage === 'first-flag-created'; | ||||
| 
 | ||||
|         const error = personalDashboardProjectDetails === undefined; | ||||
| 
 | ||||
|         const box1Content = () => { | ||||
|             if (error) { | ||||
|                 return <DataError project={activeProject} />; | ||||
|             } | ||||
| 
 | ||||
|             if ( | ||||
|                 activeProjectStage === 'onboarded' && | ||||
|                 personalDashboardProjectDetails | ||||
|             ) { | ||||
|                 return ( | ||||
|         <ContentGridContainer> | ||||
|                     <ProjectSetupComplete | ||||
|                         project={activeProject} | ||||
|                         insights={personalDashboardProjectDetails.insights} | ||||
|                     /> | ||||
|                 ); | ||||
|             } else if ( | ||||
|                 activeProjectStage === 'onboarding-started' || | ||||
|                 activeProjectStage === 'loading' | ||||
|             ) { | ||||
|                 return <CreateFlag project={activeProject} />; | ||||
|             } else if (activeProjectStage === 'first-flag-created') { | ||||
|                 return <ExistingFlag project={activeProject} />; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         const box2Content = () => { | ||||
|             if (error) { | ||||
|                 return <ContactAdmins admins={admins} />; | ||||
|             } | ||||
| 
 | ||||
|             if ( | ||||
|                 activeProjectStage === 'onboarded' && | ||||
|                 personalDashboardProjectDetails | ||||
|             ) { | ||||
|                 return ( | ||||
|                     <LatestProjectEvents | ||||
|                         latestEvents={ | ||||
|                             personalDashboardProjectDetails.latestEvents | ||||
|                         } | ||||
|                     /> | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             if (setupIncomplete || activeProjectStage === 'loading') { | ||||
|                 return <ConnectSDK project={activeProject} />; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         return ( | ||||
|             <ContentGridContainer ref={ref}> | ||||
|                 <ProjectGrid> | ||||
|                     <GridItem gridArea='title'> | ||||
|                         <Typography variant='h3'>My projects</Typography> | ||||
| @ -141,65 +203,48 @@ export const MyProjects: FC<{ | ||||
|                         {setupIncomplete ? ( | ||||
|                             <Badge color='warning'>Setup incomplete</Badge> | ||||
|                         ) : null} | ||||
|                         {error ? ( | ||||
|                             <Badge color='error'>Setup state unknown</Badge> | ||||
|                         ) : null} | ||||
|                     </GridItem> | ||||
|                     <SpacedGridItem gridArea='projects'> | ||||
|                         <List | ||||
|                             disablePadding={true} | ||||
|                             sx={{ maxHeight: '400px', overflow: 'auto' }} | ||||
|                         > | ||||
|                         {projects.map((project) => { | ||||
|                             return ( | ||||
|                             {projects.map((project) => ( | ||||
|                                 <ProjectListItem | ||||
|                                     key={project.id} | ||||
|                                     project={project} | ||||
|                                     selected={project.id === activeProject} | ||||
|                                     onClick={() => setActiveProject(project.id)} | ||||
|                                 /> | ||||
|                             ); | ||||
|                         })} | ||||
|                             ))} | ||||
|                         </List> | ||||
|                     </SpacedGridItem> | ||||
|                     <SpacedGridItem gridArea='box1'> | ||||
|                     {activeProjectStage === 'onboarded' && | ||||
|                     personalDashboardProjectDetails ? ( | ||||
|                         <ProjectSetupComplete | ||||
|                             project={activeProject} | ||||
|                             insights={personalDashboardProjectDetails.insights} | ||||
|                         /> | ||||
|                     ) : null} | ||||
|                     {activeProjectStage === 'onboarding-started' || | ||||
|                     activeProjectStage === 'loading' ? ( | ||||
|                         <CreateFlag project={activeProject} /> | ||||
|                     ) : null} | ||||
|                     {activeProjectStage === 'first-flag-created' ? ( | ||||
|                         <ExistingFlag project={activeProject} /> | ||||
|                     ) : null} | ||||
|                         {box1Content()} | ||||
|                     </SpacedGridItem> | ||||
|                     <SpacedGridItem gridArea='box2'> | ||||
|                     {activeProjectStage === 'onboarded' && | ||||
|                     personalDashboardProjectDetails ? ( | ||||
|                         <LatestProjectEvents | ||||
|                             latestEvents={ | ||||
|                                 personalDashboardProjectDetails.latestEvents | ||||
|                             } | ||||
|                         /> | ||||
|                     ) : null} | ||||
|                     {setupIncomplete || activeProjectStage === 'loading' ? ( | ||||
|                         <ConnectSDK project={activeProject} /> | ||||
|                     ) : null} | ||||
|                         {box2Content()} | ||||
|                     </SpacedGridItem> | ||||
|                     <EmptyGridItem /> | ||||
|                     <GridItem gridArea='owners'> | ||||
|                     {personalDashboardProjectDetails ? ( | ||||
|                         <RoleAndOwnerInfo | ||||
|                             roles={personalDashboardProjectDetails.roles.map( | ||||
|                             roles={ | ||||
|                                 personalDashboardProjectDetails?.roles.map( | ||||
|                                     (role) => role.name, | ||||
|                             )} | ||||
|                             owners={personalDashboardProjectDetails.owners} | ||||
|                                 ) ?? [] | ||||
|                             } | ||||
|                             owners={ | ||||
|                                 personalDashboardProjectDetails?.owners ?? [ | ||||
|                                     { ownerType: 'user', name: '?' }, | ||||
|                                 ] | ||||
|                             } | ||||
|                         /> | ||||
|                     ) : null} | ||||
|                     </GridItem> | ||||
|                 </ProjectGrid> | ||||
|             </ContentGridContainer> | ||||
|         ); | ||||
| }; | ||||
|     }, | ||||
| ); | ||||
|  | ||||
| @ -55,7 +55,6 @@ export const StyledCardTitle = styled('div')<{ lines?: number }>( | ||||
|         wordBreak: 'break-word', | ||||
|     }), | ||||
| ); | ||||
| 
 | ||||
| const FlagListItem: FC<{ | ||||
|     flag: { name: string; project: string; type: string }; | ||||
|     selected: boolean; | ||||
| @ -167,7 +166,6 @@ const useDashboardState = ( | ||||
|         setActiveProject, | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| export const PersonalDashboard = () => { | ||||
|     const { user } = useAuthUser(); | ||||
| 
 | ||||
| @ -188,8 +186,11 @@ export const PersonalDashboard = () => { | ||||
|         'open' | 'closed' | ||||
|     >('welcome-dialog:v1', 'open'); | ||||
| 
 | ||||
|     const { personalDashboardProjectDetails, loading: loadingDetails } = | ||||
|         usePersonalDashboardProjectDetails(activeProject); | ||||
|     const { | ||||
|         personalDashboardProjectDetails, | ||||
|         loading: loadingDetails, | ||||
|         error: detailsError, | ||||
|     } = usePersonalDashboardProjectDetails(activeProject); | ||||
| 
 | ||||
|     const activeProjectStage = | ||||
|         personalDashboardProjectDetails?.onboardingStatus.status ?? 'loading'; | ||||
| @ -199,10 +200,12 @@ export const PersonalDashboard = () => { | ||||
| 
 | ||||
|     const noProjects = projects.length === 0; | ||||
| 
 | ||||
|     const projectStageRef = useLoading(activeProjectStage === 'loading'); | ||||
|     const projectStageRef = useLoading( | ||||
|         !detailsError && activeProjectStage === 'loading', | ||||
|     ); | ||||
| 
 | ||||
|     return ( | ||||
|         <div ref={projectStageRef}> | ||||
|         <div> | ||||
|             <Typography component='h2' variant='h2'> | ||||
|                 Welcome {name} | ||||
|             </Typography> | ||||
| @ -235,6 +238,8 @@ export const PersonalDashboard = () => { | ||||
|                 /> | ||||
|             ) : ( | ||||
|                 <MyProjects | ||||
|                     admins={personalDashboard?.admins ?? []} | ||||
|                     ref={projectStageRef} | ||||
|                     projects={projects} | ||||
|                     activeProject={activeProject || ''} | ||||
|                     setActiveProject={setActiveProject} | ||||
|  | ||||
| @ -0,0 +1,54 @@ | ||||
| import { styled } from '@mui/material'; | ||||
| import type { PersonalDashboardSchemaAdminsItem } from 'openapi'; | ||||
| import type { FC } from 'react'; | ||||
| import { AdminListRendered } from './ContentGridNoProjects'; | ||||
| 
 | ||||
| const TitleContainer = styled('div')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     flexDirection: 'row', | ||||
|     gap: theme.spacing(2), | ||||
|     alignItems: 'center', | ||||
|     fontSize: theme.spacing(1.75), | ||||
|     fontWeight: 'bold', | ||||
| })); | ||||
| 
 | ||||
| const ActionBox = styled('div')(({ theme }) => ({ | ||||
|     flexBasis: '50%', | ||||
|     padding: theme.spacing(4, 2), | ||||
|     display: 'flex', | ||||
|     gap: theme.spacing(3), | ||||
|     flexDirection: 'column', | ||||
| })); | ||||
| 
 | ||||
| export const DataError: FC<{ project: string }> = ({ project }) => { | ||||
|     return ( | ||||
|         <ActionBox data-loading> | ||||
|             <TitleContainer> | ||||
|                 Couldn't fetch data for project "{project}". | ||||
|             </TitleContainer> | ||||
| 
 | ||||
|             <p> | ||||
|                 The API request to get data for this project returned with an | ||||
|                 error. | ||||
|             </p> | ||||
|             <p> | ||||
|                 This may be due to an intermittent error or it may be due to | ||||
|                 issues with the project's id ("{project}"). You can try | ||||
|                 reloading to see if that helps. | ||||
|             </p> | ||||
|         </ActionBox> | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| export const ContactAdmins: FC<{ | ||||
|     admins: PersonalDashboardSchemaAdminsItem[]; | ||||
| }> = ({ admins }) => { | ||||
|     return ( | ||||
|         <ActionBox> | ||||
|             <TitleContainer> | ||||
|                 Consider contacting one of your Unleash admins for help. | ||||
|             </TitleContainer> | ||||
|             <AdminListRendered admins={admins} /> | ||||
|         </ActionBox> | ||||
|     ); | ||||
| }; | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user