Skip to content

Commit

Permalink
Merge branch 'main' into gitar_useProjectReadModel_true
Browse files Browse the repository at this point in the history
  • Loading branch information
Tymek authored Sep 23, 2024
2 parents 9168bd3 + 5dd0fb9 commit 65efbcf
Show file tree
Hide file tree
Showing 16 changed files with 248 additions and 664 deletions.
3 changes: 2 additions & 1 deletion frontend/src/component/personalDashboard/WelcomeDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ReactComponent as UnleashLogoWhite } from 'assets/img/logoWithWhiteText
import { ThemeMode } from '../common/ThemeMode/ThemeMode';
import onboardingConcepts from 'assets/img/onboardingConcepts.png';
import { ScreenReaderOnly } from '../common/ScreenReaderOnly/ScreenReaderOnly';
import { formatAssetPath } from 'utils/formatPath';

const StyledDialog = styled(Dialog)(({ theme }) => ({
'& .MuiDialog-paper': {
Expand Down Expand Up @@ -69,7 +70,7 @@ export const WelcomeDialog: FC<IWelcomeDialogProps> = ({ open, onClose }) => {
</Typography>{' '}
in order to work effectively with Unleash
</Box>
<StyledImg src={onboardingConcepts} />
<StyledImg src={formatAssetPath(onboardingConcepts)} />
<ScreenReaderOnly>
<h2>Environments</h2>
<p>
Expand Down
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,21 @@ export const ProjectFeatureToggles = ({

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

const isOnboarded =
onboardingUIEnabled && project.onboardingStatus.status === 'onboarded';
const isNotOnboarded =
onboardingUIEnabled && project.onboardingStatus.status !== 'onboarded';
const hasFeaturesOrOnboarded =
(total !== undefined && total > 0) || isOnboarded;
const [onboardingFlow, setOnboardingFlow] = useLocalStorageState<
'visible' | 'closed'
>(`onboarding-flow:v1-${projectId}`, 'visible');

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

const columns = useMemo(
() => [
Expand Down Expand Up @@ -406,16 +416,17 @@ export const ProjectFeatureToggles = ({
return (
<Container>
<ConditionallyRender
condition={isNotOnboarded}
condition={isOnboarding}
show={
<ProjectOnboarding
projectId={projectId}
setConnectSdkOpen={setConnectSdkOpen}
setOnboardingFlow={setOnboardingFlow}
/>
}
/>
<ConditionallyRender
condition={hasFeaturesOrOnboarded}
condition={showFeaturesTable}
show={
<PageContent
disableLoading
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>
);
};
Loading

0 comments on commit 65efbcf

Please sign in to comment.