Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create page for when you have no projects #8285

Merged
merged 7 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do this transformation in three different places now, so I thought it would be about time to break it out into a shared component.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import type { ProjectSchemaOwners } from 'openapi';
import type { UserAvatar } from '../UserAvatar/UserAvatar';
import { AvatarGroup } from '../AvatarGroup/AvatarGroup';

type Props = {
users: ProjectSchemaOwners;
avatarLimit?: number;
AvatarComponent?: typeof UserAvatar;
className?: string;
};
export const AvatarGroupFromOwners: React.FC<Props> = ({ users, ...props }) => {
const { uiConfig } = useUiConfig();

const mapOwners = (owner: ProjectSchemaOwners[number]) => {
if (owner.ownerType === 'user') {
return {
name: owner.name,
imageUrl: owner.imageUrl || undefined,
email: owner.email || undefined,
};
}
if (owner.ownerType === 'group') {
return {
name: owner.name,
};
}
return {
name: 'System',
imageUrl: `${uiConfig.unleashUrl}/logo-unleash.png`,
};
};
const mappedOwners = users.map(mapOwners);
return <AvatarGroup users={mappedOwners} {...props} />;
};
136 changes: 136 additions & 0 deletions frontend/src/component/personalDashboard/ContentGridNoProjects.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { Grid, Typography, styled } from '@mui/material';
import { AvatarGroupFromOwners } from 'component/common/AvatarGroupFromOwners/AvatarGroupFromOwners';
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
import type { ProjectSchemaOwners } from 'openapi';
import { Link } from 'react-router-dom';

const ContentGrid = styled(Grid)(({ theme }) => ({
backgroundColor: theme.palette.background.paper,
borderRadius: `${theme.shape.borderRadiusLarge}px`,
}));

const SpacedGridItem = styled(Grid)(({ theme }) => ({
padding: theme.spacing(4),
border: `0.5px solid ${theme.palette.divider}`,
}));

const TitleContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'row',
gap: theme.spacing(2),
alignItems: 'center',
fontSize: theme.spacing(1.75),
fontWeight: 'bold',
}));

const NeutralCircleContainer = styled('span')(({ theme }) => ({
width: '28px',
height: '28px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: theme.palette.neutral.border,
borderRadius: '50%',
}));

const GridContent = styled('div')(({ theme }) => ({
flexBasis: '50%',
padding: theme.spacing(4, 2),
display: 'flex',
gap: theme.spacing(3),
flexDirection: 'column',
}));

const BoxMainContent = styled('article')(({ theme }) => ({
display: 'flex',
flexFlow: 'column',
gap: theme.spacing(2),
}));

const AdminList = styled('ul')(({ theme }) => ({
padding: 0,
'li + li': {
marginTop: theme.spacing(2),
},
}));

const AdminListItem = styled('li')(({ theme }) => ({
display: 'flex',
flexFlow: 'row',
gap: theme.spacing(2),
}));

type Props = {
owners: ProjectSchemaOwners;
admins: { name: string; imageUrl?: string }[];
};

export const ContentGridNoProjects: React.FC<Props> = ({ owners, admins }) => {
return (
<ContentGrid container columns={{ lg: 12, md: 1 }}>
<SpacedGridItem item lg={4} md={1}>
<Typography variant='h3'>My projects</Typography>
</SpacedGridItem>
<SpacedGridItem item lg={8} md={1}>
<Typography>Potential next steps</Typography>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
<GridContent>
<Typography>
You don't currently have access to any projects in the
system.
</Typography>
<Typography>
To get started, you can{' '}
<Link to='/projects?create=true'>
create your own project
</Link>
. Alternatively, you can review the available projects
in the system and ask the owner for access.
</Typography>
</GridContent>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
<GridContent>
<TitleContainer>
<NeutralCircleContainer>1</NeutralCircleContainer>
Contact Unleash admin
</TitleContainer>
<BoxMainContent>
<p>
Your Unleash administrator
{admins.length > 1 ? 's are' : ' is'}:
</p>
<AdminList>
{admins.map((admin) => (
<AdminListItem>
<UserAvatar
sx={{
margin: 0,
}}
user={admin}
/>
<Typography>{admin.name}</Typography>
</AdminListItem>
))}
</AdminList>
</BoxMainContent>
</GridContent>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
<GridContent>
<TitleContainer>
<NeutralCircleContainer>2</NeutralCircleContainer>
Ask a project owner to add you to their project
</TitleContainer>
<BoxMainContent>
<p>Project owners in Unleash:</p>
<AvatarGroupFromOwners users={owners} avatarLimit={9} />
</BoxMainContent>
</GridContent>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1} />
<SpacedGridItem item lg={8} md={1} />
</ContentGrid>
);
};
175 changes: 96 additions & 79 deletions frontend/src/component/personalDashboard/PersonalDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
import type { PersonalDashboardSchema } from '../../openapi';
import { FlagExposure } from 'component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FlagExposure';
import { RoleAndOwnerInfo } from './RoleAndOwnerInfo';
import { ContentGridNoProjects } from './ContentGridNoProjects';

const ScreenExplanation = styled(Typography)(({ theme }) => ({
marginTop: theme.spacing(1),
Expand Down Expand Up @@ -203,6 +204,8 @@ export const PersonalDashboard = () => {
'seen' | 'not_seen'
>('welcome-dialog:v1', 'not_seen');

const noProjects = projects.length === 0;

return (
<div>
<Typography component='h2' variant='h2'>
Expand All @@ -213,88 +216,102 @@ export const PersonalDashboard = () => {
most of Unleash
</ScreenExplanation>
<StyledHeaderTitle>Your resources</StyledHeaderTitle>
<ContentGrid container columns={{ lg: 12, md: 1 }}>
<SpacedGridItem item lg={4} md={1}>
<Typography variant='h3'>My projects</Typography>
</SpacedGridItem>
<SpacedGridItem
item
lg={8}
md={1}
sx={{ display: 'flex', justifyContent: 'flex-end' }}
>
<Badge color='warning'>Setup incomplete</Badge>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
<List
disablePadding={true}
sx={{ maxHeight: '400px', overflow: 'auto' }}
{noProjects ? (
<ContentGridNoProjects
owners={[{ ownerType: 'system' }]}
admins={[
{ name: 'admin' },
{
name: 'Christopher Tompkins',
imageUrl:
'https://avatars.githubusercontent.com/u/1010371?v=4',
},
Comment on lines +219 to +228
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire bit is largely just wrapping the old content in a ternary. we'll probably sort this out with the union type down the road, but this makes it work right now.

]}
/>
) : (
<ContentGrid container columns={{ lg: 12, md: 1 }}>
<SpacedGridItem item lg={4} md={1}>
<Typography variant='h3'>My projects</Typography>
</SpacedGridItem>
<SpacedGridItem
item
lg={8}
md={1}
sx={{ display: 'flex', justifyContent: 'flex-end' }}
>
{projects.map((project) => {
return (
<ListItem
key={project.name}
disablePadding={true}
sx={{ mb: 1 }}
>
<ListItemButton
sx={projectStyle}
selected={
project.name === activeProject
}
onClick={() =>
setActiveProject(project.name)
}
<Badge color='warning'>Setup incomplete</Badge>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
<List
disablePadding={true}
sx={{ maxHeight: '400px', overflow: 'auto' }}
>
{projects.map((project) => {
return (
<ListItem
key={project.name}
disablePadding={true}
sx={{ mb: 1 }}
>
<ProjectBox>
<ProjectIcon color='primary' />
<StyledCardTitle>
{project.name}
</StyledCardTitle>
<IconButton
component={Link}
href={`projects/${project.name}`}
size='small'
sx={{ ml: 'auto' }}
>
<LinkIcon
titleAccess={`projects/${project.name}`}
<ListItemButton
sx={projectStyle}
selected={
project.name === activeProject
}
onClick={() =>
setActiveProject(project.name)
}
>
<ProjectBox>
<ProjectIcon color='primary' />
<StyledCardTitle>
{project.name}
</StyledCardTitle>
<IconButton
component={Link}
href={`projects/${project.name}`}
size='small'
sx={{ ml: 'auto' }}
>
<LinkIcon
titleAccess={`projects/${project.name}`}
/>
</IconButton>
</ProjectBox>
{project.name === activeProject ? (
<ActiveProjectDetails
project={project}
/>
</IconButton>
</ProjectBox>
{project.name === activeProject ? (
<ActiveProjectDetails
project={project}
/>
) : null}
</ListItemButton>
</ListItem>
);
})}
</List>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
{onboardingCompleted ? (
<ProjectSetupComplete project={activeProject} />
) : activeProject ? (
<CreateFlag project={activeProject} />
) : null}
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
{activeProject ? (
<ConnectSDK project={activeProject} />
) : null}
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1} />
<SpacedGridItem item lg={8} md={1}>
{activeProject ? (
<RoleAndOwnerInfo
roles={['owner', 'custom']}
owners={[{ ownerType: 'system' }]}
/>
) : null}
</SpacedGridItem>
</ContentGrid>
) : null}
</ListItemButton>
</ListItem>
);
})}
</List>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
{onboardingCompleted ? (
<ProjectSetupComplete project={activeProject} />
) : activeProject ? (
<CreateFlag project={activeProject} />
) : null}
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
{activeProject ? (
<ConnectSDK project={activeProject} />
) : null}
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1} />
<SpacedGridItem item lg={8} md={1}>
{activeProject ? (
<RoleAndOwnerInfo
roles={['owner', 'custom']}
owners={[{ ownerType: 'system' }]}
/>
) : null}
</SpacedGridItem>
</ContentGrid>
)}
<ContentGrid container columns={{ lg: 12, md: 1 }} sx={{ mt: 2 }}>
<SpacedGridItem item lg={4} md={1}>
<Typography variant='h3'>My feature flags</Typography>
Expand Down
Loading
Loading