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: onboarding can be now closed #8215

Merged
merged 2 commits into from
Sep 23, 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
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,18 @@ import { useDefaultColumnVisibility } from './hooks/useDefaultColumnVisibility';
import { TableEmptyState } from './TableEmptyState/TableEmptyState';
import { useRowActions } from './hooks/useRowActions';
import { useSelectedData } from './hooks/useSelectedData';
import { FeatureOverviewCell } from '../../../common/Table/cells/FeatureOverviewCell/FeatureOverviewCell';
import { FeatureOverviewCell } from 'component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell';
import {
useProjectFeatureSearch,
useProjectFeatureSearchActions,
} from './useProjectFeatureSearch';
import { AvatarCell } from './AvatarCell';
import { ProjectOnboarding } from './ProjectOnboarding/ProjectOnboarding';
import { useUiFlag } from 'hooks/useUiFlag';
import { styled } from '@mui/material';
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
import { ConnectSdkDialog } from '../../../onboarding/ConnectSdkDialog';
import { ProjectOnboarding } from './ProjectOnboarding/ProjectOnboarding';
import { useLocalStorageState } from 'hooks/useLocalStorageState';

interface IPaginatedProjectFeatureTogglesProps {
environments: string[];
Expand Down Expand Up @@ -114,12 +115,19 @@ export const ProjectFeatureToggles = ({

const isPlaceholder = Boolean(initialLoad || (loading && total));

const [onboardingFlow, setOnboardingFlow] = useLocalStorageState<
'visible' | 'closed'
>(`onboarding-flow:v1-${projectId}`, 'visible');

const notOnboarding =
!onboardingUIEnabled ||
(onboardingUIEnabled &&
project.onboardingStatus.status === 'onboarded');
project.onboardingStatus.status === 'onboarded') ||
onboardingFlow === 'closed';
const isOnboarding =
onboardingUIEnabled && project.onboardingStatus.status !== 'onboarded';
onboardingUIEnabled &&
project.onboardingStatus.status !== 'onboarded' &&
onboardingFlow === 'visible';
const showFeaturesTable =
(total !== undefined && total > 0) || notOnboarding;

Expand Down Expand Up @@ -413,6 +421,7 @@ export const ProjectFeatureToggles = ({
<ProjectOnboarding
projectId={projectId}
setConnectSdkOpen={setConnectSdkOpen}
setOnboardingFlow={setOnboardingFlow}
/>
}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { render } from 'utils/testRenderer';
import { Route, Routes } from 'react-router-dom';
import { testServerRoute, testServerSetup } from 'utils/testServer';
import { WelcomeToProject } from './WelcomeToProject';
import { ProjectOnboarding } from './ProjectOnboarding';
import { screen } from '@testing-library/react';

const server = testServerSetup();
Expand All @@ -18,9 +18,10 @@ test('Project can start onboarding', async () => {
<Route
path={'/projects/:projectId'}
element={
<WelcomeToProject
<ProjectOnboarding
projectId={projectId}
setConnectSdkOpen={() => {}}
setOnboardingFlow={() => {}}
/>
}
/>
Expand All @@ -45,9 +46,10 @@ test('Project can connect SDK', async () => {
<Route
path={'/projects/:projectId'}
element={
<WelcomeToProject
<ProjectOnboarding
projectId={projectId}
setConnectSdkOpen={() => {}}
setOnboardingFlow={() => {}}
/>
}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,204 @@
import { styled } from '@mui/material';
import { WelcomeToProject } from './WelcomeToProject';
import { IconButton, styled, Tooltip, Typography } from '@mui/material';
import Add from '@mui/icons-material/Add';
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
import { FlagCreationButton } from '../ProjectFeatureTogglesHeader/ProjectFeatureTogglesHeader';
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
import { SdkExample } from './SdkExample';
import CloseIcon from '@mui/icons-material/Close';

interface IProjectOnboardingProps {
projectId: string;
setConnectSdkOpen: (open: boolean) => void;
setOnboardingFlow: (status: 'visible' | 'closed') => void;
}

interface ICreateFlagProps {
projectId: string;
}

const Container = styled('div')(({ theme }) => ({
display: 'flex',
width: '100%',
flexDirection: 'column',
backgroundColor: theme.palette.background.paper,
flexBasis: '70%',
borderRadius: theme.shape.borderRadiusLarge,
}));

const TitleBox = styled('div')(({ theme }) => ({
padding: theme.spacing(2, 7, 2, 7),
borderBottom: '1px solid',
borderColor: theme.palette.divider,
minHeight: '80px',
}));

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 NeutralCircleContainer = styled('span')(({ theme }) => ({
width: '28px',
height: '28px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: theme.palette.neutral.border,
borderRadius: '50%',
}));

const MainCircleContainer = styled(NeutralCircleContainer)(({ theme }) => ({
backgroundColor: theme.palette.primary.main,
color: theme.palette.background.paper,
}));

const ExistingFlagContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(3),
height: '100%',
}));

const SuccessContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',

fontSize: theme.spacing(1.75),
fontWeight: 'bold',
backgroundColor: theme.palette.success.light,
borderRadius: theme.shape.borderRadiusLarge,
padding: theme.spacing(2, 2, 2, 2),
}));

const TitleRow = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}));

export const ProjectOnboarding = ({
projectId,
setConnectSdkOpen,
setOnboardingFlow,
}: IProjectOnboardingProps) => {
const { project } = useProjectOverview(projectId);
const isFirstFlagCreated =
project.onboardingStatus.status === 'first-flag-created';

const closeOnboardingFlow = () => {
setOnboardingFlow('closed');
};

return (
<Container>
<WelcomeToProject
projectId={projectId}
setConnectSdkOpen={setConnectSdkOpen}
/>
<SdkExample />
<TitleBox>
<TitleRow>
<Typography fontWeight='bold'>
Welcome to your project
</Typography>
<Tooltip title='Close' arrow>
<IconButton onClick={closeOnboardingFlow} size='small'>
<CloseIcon />
</IconButton>
</Tooltip>
</TitleRow>
<Typography variant='body2'>
Complete the steps below to start working with this project
</Typography>
</TitleBox>
<Actions>
<ActionBox>
{project.onboardingStatus.status ===
'first-flag-created' ? (
<ExistingFlag />
) : (
<CreateFlag projectId={projectId} />
)}
</ActionBox>
<ActionBox>
<TitleContainer>
<NeutralCircleContainer>2</NeutralCircleContainer>
Connect an SDK
</TitleContainer>
<Typography>
Your project is not yet connected to any SDK. To start
using your feature flag, connect an SDK to the project.
</Typography>
<ResponsiveButton
onClick={() => {
setConnectSdkOpen(true);
}}
maxWidth='200px'
projectId={projectId}
Icon={Add}
disabled={!isFirstFlagCreated}
permission={CREATE_FEATURE}
>
Connect SDK
</ResponsiveButton>
</ActionBox>
<ActionBox>
<SdkExample />
</ActionBox>
</Actions>
</Container>
);
};

const CreateFlag = ({ projectId }: ICreateFlagProps) => {
const { refetch } = useProjectOverview(projectId);
return (
<>
<TitleContainer>
<NeutralCircleContainer>1</NeutralCircleContainer>
Create a feature flag
</TitleContainer>
<Typography>
<div>The project currently holds no feature flags.</div>
<div>Create one to get started.</div>
</Typography>
<FlagCreationButton
text='Create flag'
skipNavigationOnComplete={true}
onSuccess={refetch}
/>
</>
);
};

const ExistingFlag = () => {
return (
<ExistingFlagContainer>
<TitleContainer>
<MainCircleContainer>✓</MainCircleContainer>
Create a feature flag
</TitleContainer>
<SuccessContainer>
<Typography fontWeight='bold' variant='body2'>
Congratulations! You have created your first flag
</Typography>
<Typography variant='body2'>
Click into the flag below to customize the flag further
</Typography>
</SuccessContainer>
</ExistingFlagContainer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,13 @@ import Select from 'component/common/select';
import { useState } from 'react';
import { allSdks } from '../../../../onboarding/sharedTypes';

const Container = styled('div')(({ theme }) => ({
const TitleContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.palette.background.paper,
flexBasis: '30%',
borderRadius: theme.shape.borderRadiusLarge,
}));

const TitleBox = styled('div')(({ theme }) => ({
padding: theme.spacing(2, 7, 2, 7),
borderBottom: '1px solid',
borderColor: theme.palette.divider,
minHeight: '80px',
flexDirection: 'row',
gap: theme.spacing(2),
alignItems: 'center',
display: 'flex',
}));

const ContentBox = styled('div')(({ theme }) => ({
padding: theme.spacing(3, 2, 6, 8),
display: 'flex',
gap: theme.spacing(3),
flexDirection: 'column',
fontSize: theme.spacing(1.75),
fontWeight: 'bold',
}));

const StyledLink = styled(Link)({
Expand All @@ -45,27 +30,22 @@ export const SdkExample = () => {
setSelectedSdk(event.target.value);
};
return (
<Container>
<TitleBox>
<Typography fontWeight='bold'>View SDK Example</Typography>
</TitleBox>

<ContentBox>
<Typography>
See an example implementation of your preferred SDK.
</Typography>
<Select
id='sdk-select'
name='sdk'
options={sdkOptions}
value={selectedSdk}
onChange={onChange}
style={{
width: '60%',
}}
/>
<StyledLink to={``}>Go to example</StyledLink>
</ContentBox>
</Container>
<>
<TitleContainer>View SDK Example</TitleContainer>
<Typography>
Choose your preferred SDK to view an example
</Typography>
<Select
id='sdk-select'
name='sdk'
options={sdkOptions}
value={selectedSdk}
onChange={onChange}
style={{
width: '60%',
}}
/>
<StyledLink to={``}>Go to example</StyledLink>
</>
);
};
Loading