mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: after onboarding show success box with resources (#8278)
![image](https://github.com/user-attachments/assets/7e60ad54-c750-4e8a-8556-a1735a99a43e)
This commit is contained in:
parent
547e41e566
commit
86e7bbc85d
@ -19,6 +19,7 @@ import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectO
|
|||||||
interface IConnectSDKDialogProps {
|
interface IConnectSDKDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
onFinish: () => void;
|
||||||
project: string;
|
project: string;
|
||||||
environments: string[];
|
environments: string[];
|
||||||
feature?: string;
|
feature?: string;
|
||||||
@ -66,6 +67,7 @@ type OnboardingStage = 'select-sdk' | 'generate-api-key' | 'test-connection';
|
|||||||
export const ConnectSdkDialog = ({
|
export const ConnectSdkDialog = ({
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
|
onFinish,
|
||||||
environments,
|
environments,
|
||||||
project: projectId,
|
project: projectId,
|
||||||
feature,
|
feature,
|
||||||
@ -76,6 +78,7 @@ export const ConnectSdkDialog = ({
|
|||||||
const [environment, setEnvironment] = useState<string | null>(null);
|
const [environment, setEnvironment] = useState<string | null>(null);
|
||||||
const [apiKey, setApiKey] = useState<string | null>(null);
|
const [apiKey, setApiKey] = useState<string | null>(null);
|
||||||
const [stage, setStage] = useState<OnboardingStage>('select-sdk');
|
const [stage, setStage] = useState<OnboardingStage>('select-sdk');
|
||||||
|
|
||||||
const { project } = useProjectOverview(projectId, {
|
const { project } = useProjectOverview(projectId, {
|
||||||
refreshInterval: 1000,
|
refreshInterval: 1000,
|
||||||
});
|
});
|
||||||
@ -166,12 +169,7 @@ export const ConnectSdkDialog = ({
|
|||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<Button
|
<Button variant='contained' onClick={onFinish}>
|
||||||
variant='contained'
|
|
||||||
onClick={() => {
|
|
||||||
onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Next
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
</NextStepSectionSpacedContainer>
|
</NextStepSectionSpacedContainer>
|
||||||
|
@ -9,7 +9,7 @@ interface IConnectionInformationProps {
|
|||||||
sdk: string;
|
sdk: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
}
|
}
|
||||||
export const Container = styled('div')(({ theme }) => ({
|
const Container = styled('div')(({ theme }) => ({
|
||||||
backgroundColor: theme.palette.background.sidebar,
|
backgroundColor: theme.palette.background.sidebar,
|
||||||
padding: theme.spacing(6, 9, 6, 9),
|
padding: theme.spacing(6, 9, 6, 9),
|
||||||
minWidth: '400px',
|
minWidth: '400px',
|
||||||
@ -18,14 +18,14 @@ export const Container = styled('div')(({ theme }) => ({
|
|||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const Title = styled(Typography)(({ theme }) => ({
|
const Title = styled(Typography)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
fontWeight: theme.typography.fontWeightBold,
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const SdkInfo = styled('div')(({ theme }) => ({
|
const SdkInfo = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
padding: theme.spacing(4, 0, 12, 0),
|
padding: theme.spacing(4, 0, 12, 0),
|
||||||
@ -33,13 +33,13 @@ export const SdkInfo = styled('div')(({ theme }) => ({
|
|||||||
fontSize: theme.spacing(1),
|
fontSize: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const Info = styled('div')(({ theme }) => ({
|
const Info = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: theme.spacing(1),
|
gap: theme.spacing(1),
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const ConnectionStatus = styled('div')(({ theme }) => ({
|
const ConnectionStatus = styled('div')(({ theme }) => ({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -48,7 +48,7 @@ export const ConnectionStatus = styled('div')(({ theme }) => ({
|
|||||||
fontSize: theme.fontSizes.smallBody,
|
fontSize: theme.fontSizes.smallBody,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const StyledCheck = styled(Check)(({ theme }) => ({
|
const StyledCheck = styled(Check)(({ theme }) => ({
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
|
@ -36,6 +36,7 @@ test('Onboarding for SDK', async () => {
|
|||||||
open={true}
|
open={true}
|
||||||
environments={['development', 'production']}
|
environments={['development', 'production']}
|
||||||
feature='featureA'
|
feature='featureA'
|
||||||
|
onFinish={() => {}}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
153
frontend/src/component/onboarding/flow/ProjectOnboarded.tsx
Normal file
153
frontend/src/component/onboarding/flow/ProjectOnboarded.tsx
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { IconButton, styled, Tooltip, Typography } from '@mui/material';
|
||||||
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
import Check from '@mui/icons-material/Check';
|
||||||
|
import People from '@mui/icons-material/People';
|
||||||
|
import MenuBook from '@mui/icons-material/MenuBook';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
interface IProjectOnboardedProps {
|
||||||
|
projectId: string;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
flexBasis: '70%',
|
||||||
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TitleRow = styled('div')(({ theme }) => ({
|
||||||
|
padding: theme.spacing(2, 4, 2, 7),
|
||||||
|
borderBottom: '1px solid',
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
gap: theme.spacing(3),
|
||||||
|
alignItems: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Actions = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexGrow: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ActionBox = styled('div')(({ theme }) => ({
|
||||||
|
flexBasis: '50%',
|
||||||
|
padding: theme.spacing(3, 2, 6, 8),
|
||||||
|
display: 'flex',
|
||||||
|
gap: theme.spacing(3),
|
||||||
|
flexDirection: 'column',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TitleContainer = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: theme.spacing(1.75),
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Title = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledCheck = styled(Check)(({ theme }) => ({
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
color: theme.palette.background.paper,
|
||||||
|
borderRadius: '50%',
|
||||||
|
padding: theme.spacing(0.5),
|
||||||
|
width: '28px',
|
||||||
|
height: '28px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ColoredPeople = styled(People)(({ theme }) => ({
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ColoredMenuBook = styled(MenuBook)(({ theme }) => ({
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const ProjectOnboarded = ({
|
||||||
|
projectId,
|
||||||
|
onClose,
|
||||||
|
}: IProjectOnboardedProps) => {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<TitleRow>
|
||||||
|
<StyledCheck />
|
||||||
|
<Title>
|
||||||
|
<Typography fontWeight='bold'>Setup completed</Typography>
|
||||||
|
<Typography variant='body2'>Next steps</Typography>
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Tooltip title='Close' arrow sx={{ ml: 'auto' }}>
|
||||||
|
<IconButton onClick={onClose} size='small'>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</TitleRow>
|
||||||
|
<Actions>
|
||||||
|
<ActionBox>
|
||||||
|
<TitleContainer>
|
||||||
|
Expose your feature flag to users
|
||||||
|
</TitleContainer>
|
||||||
|
<Typography>
|
||||||
|
You can have fine grained control over who is exposed to
|
||||||
|
your feature flag by leveraging{' '}
|
||||||
|
<Link
|
||||||
|
className='unleash-action-button'
|
||||||
|
to={`https://docs.getunleash.io/reference/activation-strategies`}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
>
|
||||||
|
strategies
|
||||||
|
</Link>
|
||||||
|
. Visit the feature flag page to start adding strategies
|
||||||
|
to control exposure.
|
||||||
|
</Typography>
|
||||||
|
</ActionBox>
|
||||||
|
<ActionBox>
|
||||||
|
<TitleContainer>
|
||||||
|
<ColoredPeople />
|
||||||
|
Add members to your project
|
||||||
|
</TitleContainer>
|
||||||
|
<Typography>
|
||||||
|
Unleash is best when collaborating with your co-workers.{' '}
|
||||||
|
<Link
|
||||||
|
className='unleash-action-button'
|
||||||
|
to={`/projects/${projectId}/settings/access`}
|
||||||
|
>
|
||||||
|
Add your co-workers to the project
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</Typography>
|
||||||
|
</ActionBox>
|
||||||
|
<ActionBox>
|
||||||
|
<TitleContainer>
|
||||||
|
<ColoredMenuBook />
|
||||||
|
Learn about unleash
|
||||||
|
</TitleContainer>
|
||||||
|
<Typography>
|
||||||
|
Take a deep dive through our documentation,{' '}
|
||||||
|
<Link
|
||||||
|
className='unleash-action-button'
|
||||||
|
to={`https://docs.getunleash.io/unleash-academy/foundational`}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
>
|
||||||
|
starting with the foundations of Unleash
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</Typography>
|
||||||
|
</ActionBox>
|
||||||
|
</Actions>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
@ -45,6 +45,7 @@ import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectO
|
|||||||
import { ConnectSdkDialog } from '../../../onboarding/dialog/ConnectSdkDialog';
|
import { ConnectSdkDialog } from '../../../onboarding/dialog/ConnectSdkDialog';
|
||||||
import { ProjectOnboarding } from '../../../onboarding/flow/ProjectOnboarding';
|
import { ProjectOnboarding } from '../../../onboarding/flow/ProjectOnboarding';
|
||||||
import { useLocalStorageState } from 'hooks/useLocalStorageState';
|
import { useLocalStorageState } from 'hooks/useLocalStorageState';
|
||||||
|
import { ProjectOnboarded } from 'component/onboarding/flow/ProjectOnboarded';
|
||||||
|
|
||||||
interface IPaginatedProjectFeatureTogglesProps {
|
interface IPaginatedProjectFeatureTogglesProps {
|
||||||
environments: string[];
|
environments: string[];
|
||||||
@ -118,6 +119,9 @@ export const ProjectFeatureToggles = ({
|
|||||||
const [onboardingFlow, setOnboardingFlow] = useLocalStorageState<
|
const [onboardingFlow, setOnboardingFlow] = useLocalStorageState<
|
||||||
'visible' | 'closed'
|
'visible' | 'closed'
|
||||||
>(`onboarding-flow:v1-${projectId}`, 'visible');
|
>(`onboarding-flow:v1-${projectId}`, 'visible');
|
||||||
|
const [setupCompletedState, setSetupCompletedState] = useLocalStorageState<
|
||||||
|
'hide-setup' | 'show-setup'
|
||||||
|
>(`onboarding-state:v1-${projectId}`, 'hide-setup');
|
||||||
|
|
||||||
const notOnboarding =
|
const notOnboarding =
|
||||||
!onboardingUIEnabled ||
|
!onboardingUIEnabled ||
|
||||||
@ -425,6 +429,17 @@ export const ProjectFeatureToggles = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={setupCompletedState === 'show-setup'}
|
||||||
|
show={
|
||||||
|
<ProjectOnboarded
|
||||||
|
projectId={projectId}
|
||||||
|
onClose={() => {
|
||||||
|
setSetupCompletedState('hide-setup');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={showFeaturesTable}
|
condition={showFeaturesTable}
|
||||||
show={
|
show={
|
||||||
@ -539,6 +554,10 @@ export const ProjectFeatureToggles = ({
|
|||||||
onClose={() => {
|
onClose={() => {
|
||||||
setConnectSdkOpen(false);
|
setConnectSdkOpen(false);
|
||||||
}}
|
}}
|
||||||
|
onFinish={() => {
|
||||||
|
setConnectSdkOpen(false);
|
||||||
|
setSetupCompletedState('show-setup');
|
||||||
|
}}
|
||||||
project={projectId}
|
project={projectId}
|
||||||
environments={environments}
|
environments={environments}
|
||||||
feature={
|
feature={
|
||||||
|
Loading…
Reference in New Issue
Block a user