mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-18 00:19:49 +01:00
parent
21573d36de
commit
8a7bf865d3
@ -66,6 +66,44 @@ type Props = {
|
|||||||
admins: PersonalDashboardSchemaAdminsItem[];
|
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 }) => {
|
export const ContentGridNoProjects: React.FC<Props> = ({ owners, admins }) => {
|
||||||
return (
|
return (
|
||||||
<ContentGridContainer>
|
<ContentGridContainer>
|
||||||
@ -98,40 +136,7 @@ export const ContentGridNoProjects: React.FC<Props> = ({ owners, admins }) => {
|
|||||||
<NeutralCircleContainer>1</NeutralCircleContainer>
|
<NeutralCircleContainer>1</NeutralCircleContainer>
|
||||||
Contact Unleash admin
|
Contact Unleash admin
|
||||||
</TitleContainer>
|
</TitleContainer>
|
||||||
<BoxMainContent>
|
<AdminListRendered admins={admins} />
|
||||||
{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>
|
|
||||||
</GridContent>
|
</GridContent>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem gridArea='box2'>
|
<GridItem gridArea='box2'>
|
||||||
|
@ -14,10 +14,11 @@ import { ProjectSetupComplete } from './ProjectSetupComplete';
|
|||||||
import { ConnectSDK, CreateFlag, ExistingFlag } from './ConnectSDK';
|
import { ConnectSDK, CreateFlag, ExistingFlag } from './ConnectSDK';
|
||||||
import { LatestProjectEvents } from './LatestProjectEvents';
|
import { LatestProjectEvents } from './LatestProjectEvents';
|
||||||
import { RoleAndOwnerInfo } from './RoleAndOwnerInfo';
|
import { RoleAndOwnerInfo } from './RoleAndOwnerInfo';
|
||||||
import { useEffect, useRef, type FC } from 'react';
|
import { forwardRef, useEffect, useRef, type FC } from 'react';
|
||||||
import { StyledCardTitle } from './PersonalDashboard';
|
import { StyledCardTitle } from './PersonalDashboard';
|
||||||
import type {
|
import type {
|
||||||
PersonalDashboardProjectDetailsSchema,
|
PersonalDashboardProjectDetailsSchema,
|
||||||
|
PersonalDashboardSchemaAdminsItem,
|
||||||
PersonalDashboardSchemaProjectsItem,
|
PersonalDashboardSchemaProjectsItem,
|
||||||
} from '../../openapi';
|
} from '../../openapi';
|
||||||
import {
|
import {
|
||||||
@ -29,6 +30,7 @@ import {
|
|||||||
GridItem,
|
GridItem,
|
||||||
SpacedGridItem,
|
SpacedGridItem,
|
||||||
} from './Grid';
|
} from './Grid';
|
||||||
|
import { ContactAdmins, DataError } from './ProjectDetailsError';
|
||||||
|
|
||||||
const ActiveProjectDetails: FC<{
|
const ActiveProjectDetails: FC<{
|
||||||
project: PersonalDashboardSchemaProjectsItem;
|
project: PersonalDashboardSchemaProjectsItem;
|
||||||
@ -108,98 +110,141 @@ const ProjectListItem: FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MyProjects: FC<{
|
export const MyProjects = forwardRef<
|
||||||
projects: PersonalDashboardSchemaProjectsItem[];
|
HTMLDivElement,
|
||||||
personalDashboardProjectDetails?: PersonalDashboardProjectDetailsSchema;
|
{
|
||||||
activeProject: string;
|
projects: PersonalDashboardSchemaProjectsItem[];
|
||||||
setActiveProject: (project: string) => void;
|
personalDashboardProjectDetails?: PersonalDashboardProjectDetailsSchema;
|
||||||
}> = ({
|
activeProject: string;
|
||||||
projects,
|
setActiveProject: (project: string) => void;
|
||||||
personalDashboardProjectDetails,
|
admins: PersonalDashboardSchemaAdminsItem[];
|
||||||
setActiveProject,
|
}
|
||||||
activeProject,
|
>(
|
||||||
}) => {
|
(
|
||||||
const activeProjectStage =
|
{
|
||||||
personalDashboardProjectDetails?.onboardingStatus.status ?? 'loading';
|
projects,
|
||||||
const setupIncomplete =
|
personalDashboardProjectDetails,
|
||||||
activeProjectStage === 'onboarding-started' ||
|
setActiveProject,
|
||||||
activeProjectStage === 'first-flag-created';
|
activeProject,
|
||||||
|
admins,
|
||||||
|
},
|
||||||
|
ref,
|
||||||
|
) => {
|
||||||
|
const activeProjectStage =
|
||||||
|
personalDashboardProjectDetails?.onboardingStatus.status ??
|
||||||
|
'loading';
|
||||||
|
const setupIncomplete =
|
||||||
|
activeProjectStage === 'onboarding-started' ||
|
||||||
|
activeProjectStage === 'first-flag-created';
|
||||||
|
|
||||||
return (
|
const error = personalDashboardProjectDetails === undefined;
|
||||||
<ContentGridContainer>
|
|
||||||
<ProjectGrid>
|
const box1Content = () => {
|
||||||
<GridItem gridArea='title'>
|
if (error) {
|
||||||
<Typography variant='h3'>My projects</Typography>
|
return <DataError project={activeProject} />;
|
||||||
</GridItem>
|
}
|
||||||
<GridItem
|
|
||||||
gridArea='onboarding'
|
if (
|
||||||
sx={{
|
activeProjectStage === 'onboarded' &&
|
||||||
display: 'flex',
|
personalDashboardProjectDetails
|
||||||
justifyContent: 'flex-end',
|
) {
|
||||||
}}
|
return (
|
||||||
>
|
<ProjectSetupComplete
|
||||||
{setupIncomplete ? (
|
project={activeProject}
|
||||||
<Badge color='warning'>Setup incomplete</Badge>
|
insights={personalDashboardProjectDetails.insights}
|
||||||
) : null}
|
/>
|
||||||
</GridItem>
|
);
|
||||||
<SpacedGridItem gridArea='projects'>
|
} else if (
|
||||||
<List
|
activeProjectStage === 'onboarding-started' ||
|
||||||
disablePadding={true}
|
activeProjectStage === 'loading'
|
||||||
sx={{ maxHeight: '400px', overflow: 'auto' }}
|
) {
|
||||||
|
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>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem
|
||||||
|
gridArea='onboarding'
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{projects.map((project) => {
|
{setupIncomplete ? (
|
||||||
return (
|
<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) => (
|
||||||
<ProjectListItem
|
<ProjectListItem
|
||||||
key={project.id}
|
key={project.id}
|
||||||
project={project}
|
project={project}
|
||||||
selected={project.id === activeProject}
|
selected={project.id === activeProject}
|
||||||
onClick={() => setActiveProject(project.id)}
|
onClick={() => setActiveProject(project.id)}
|
||||||
/>
|
/>
|
||||||
);
|
))}
|
||||||
})}
|
</List>
|
||||||
</List>
|
</SpacedGridItem>
|
||||||
</SpacedGridItem>
|
<SpacedGridItem gridArea='box1'>
|
||||||
<SpacedGridItem gridArea='box1'>
|
{box1Content()}
|
||||||
{activeProjectStage === 'onboarded' &&
|
</SpacedGridItem>
|
||||||
personalDashboardProjectDetails ? (
|
<SpacedGridItem gridArea='box2'>
|
||||||
<ProjectSetupComplete
|
{box2Content()}
|
||||||
project={activeProject}
|
</SpacedGridItem>
|
||||||
insights={personalDashboardProjectDetails.insights}
|
<EmptyGridItem />
|
||||||
/>
|
<GridItem gridArea='owners'>
|
||||||
) : null}
|
<RoleAndOwnerInfo
|
||||||
{activeProjectStage === 'onboarding-started' ||
|
roles={
|
||||||
activeProjectStage === 'loading' ? (
|
personalDashboardProjectDetails?.roles.map(
|
||||||
<CreateFlag project={activeProject} />
|
(role) => role.name,
|
||||||
) : null}
|
) ?? []
|
||||||
{activeProjectStage === 'first-flag-created' ? (
|
}
|
||||||
<ExistingFlag project={activeProject} />
|
owners={
|
||||||
) : null}
|
personalDashboardProjectDetails?.owners ?? [
|
||||||
</SpacedGridItem>
|
{ ownerType: 'user', name: '?' },
|
||||||
<SpacedGridItem gridArea='box2'>
|
]
|
||||||
{activeProjectStage === 'onboarded' &&
|
|
||||||
personalDashboardProjectDetails ? (
|
|
||||||
<LatestProjectEvents
|
|
||||||
latestEvents={
|
|
||||||
personalDashboardProjectDetails.latestEvents
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : null}
|
</GridItem>
|
||||||
{setupIncomplete || activeProjectStage === 'loading' ? (
|
</ProjectGrid>
|
||||||
<ConnectSDK project={activeProject} />
|
</ContentGridContainer>
|
||||||
) : null}
|
);
|
||||||
</SpacedGridItem>
|
},
|
||||||
<EmptyGridItem />
|
);
|
||||||
<GridItem gridArea='owners'>
|
|
||||||
{personalDashboardProjectDetails ? (
|
|
||||||
<RoleAndOwnerInfo
|
|
||||||
roles={personalDashboardProjectDetails.roles.map(
|
|
||||||
(role) => role.name,
|
|
||||||
)}
|
|
||||||
owners={personalDashboardProjectDetails.owners}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</GridItem>
|
|
||||||
</ProjectGrid>
|
|
||||||
</ContentGridContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
@ -55,7 +55,6 @@ export const StyledCardTitle = styled('div')<{ lines?: number }>(
|
|||||||
wordBreak: 'break-word',
|
wordBreak: 'break-word',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const FlagListItem: FC<{
|
const FlagListItem: FC<{
|
||||||
flag: { name: string; project: string; type: string };
|
flag: { name: string; project: string; type: string };
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
@ -167,7 +166,6 @@ const useDashboardState = (
|
|||||||
setActiveProject,
|
setActiveProject,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PersonalDashboard = () => {
|
export const PersonalDashboard = () => {
|
||||||
const { user } = useAuthUser();
|
const { user } = useAuthUser();
|
||||||
|
|
||||||
@ -188,8 +186,11 @@ export const PersonalDashboard = () => {
|
|||||||
'open' | 'closed'
|
'open' | 'closed'
|
||||||
>('welcome-dialog:v1', 'open');
|
>('welcome-dialog:v1', 'open');
|
||||||
|
|
||||||
const { personalDashboardProjectDetails, loading: loadingDetails } =
|
const {
|
||||||
usePersonalDashboardProjectDetails(activeProject);
|
personalDashboardProjectDetails,
|
||||||
|
loading: loadingDetails,
|
||||||
|
error: detailsError,
|
||||||
|
} = usePersonalDashboardProjectDetails(activeProject);
|
||||||
|
|
||||||
const activeProjectStage =
|
const activeProjectStage =
|
||||||
personalDashboardProjectDetails?.onboardingStatus.status ?? 'loading';
|
personalDashboardProjectDetails?.onboardingStatus.status ?? 'loading';
|
||||||
@ -199,10 +200,12 @@ export const PersonalDashboard = () => {
|
|||||||
|
|
||||||
const noProjects = projects.length === 0;
|
const noProjects = projects.length === 0;
|
||||||
|
|
||||||
const projectStageRef = useLoading(activeProjectStage === 'loading');
|
const projectStageRef = useLoading(
|
||||||
|
!detailsError && activeProjectStage === 'loading',
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={projectStageRef}>
|
<div>
|
||||||
<Typography component='h2' variant='h2'>
|
<Typography component='h2' variant='h2'>
|
||||||
Welcome {name}
|
Welcome {name}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -235,6 +238,8 @@ export const PersonalDashboard = () => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<MyProjects
|
<MyProjects
|
||||||
|
admins={personalDashboard?.admins ?? []}
|
||||||
|
ref={projectStageRef}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
activeProject={activeProject || ''}
|
activeProject={activeProject || ''}
|
||||||
setActiveProject={setActiveProject}
|
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