Skip to content

Commit

Permalink
feat: display new completed dialog (#8255)
Browse files Browse the repository at this point in the history
1. Now the dialog will not close when SDK got connected
2. It will start to show the suggested production code. ( this will be
attached in next PR)
3. Also, it has connected indicator on the right
4. Back button is removed in this stage.


![image](https://github.com/user-attachments/assets/c7290e0f-8fa7-4382-a91d-7206e32d81ae)

---------

Co-authored-by: Tymoteusz Czech <[email protected]>
  • Loading branch information
sjaanus and Tymek authored Sep 26, 2024
1 parent c7427f4 commit aca0de4
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 72 deletions.
62 changes: 33 additions & 29 deletions frontend/src/component/onboarding/ConnectSdkDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,21 @@ import {
useTheme,
} from '@mui/material';
import { GenerateApiKey } from './GenerateApiKey';
import { lazy, Suspense, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { SelectSdk } from './SelectSdk';
import { GenerateApiKeyConcepts, SelectSdkConcepts } from './UnleashConcepts';

const TestSdkConnection = lazy(() => import('./TestSdkConnection'));

import type { Sdk } from './sharedTypes';
import { ConnectionInformation } from './ConnectionInformation';
import Loader from 'component/common/Loader/Loader';
import { SdkConnection } from './SdkConnection';
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';

interface IConnectSDKDialogProps {
open: boolean;
onClose: () => void;
project: string;
environments: string[];
feature: string;
feature?: string;
}

const ConnectSdk = styled('main')(({ theme }) => ({
Expand Down Expand Up @@ -68,7 +67,7 @@ export const ConnectSdkDialog = ({
open,
onClose,
environments,
project,
project: projectId,
feature,
}: IConnectSDKDialogProps) => {
const theme = useTheme();
Expand All @@ -77,13 +76,18 @@ export const ConnectSdkDialog = ({
const [environment, setEnvironment] = useState<string | null>(null);
const [apiKey, setApiKey] = useState<string | null>(null);
const [stage, setStage] = useState<OnboardingStage>('select-sdk');
const { project } = useProjectOverview(projectId, {
refreshInterval: 1000,
});

const isSelectSdkStage = stage === 'select-sdk';
const isGenerateApiKeyStage =
stage === 'generate-api-key' && sdk && environment;
const isTestConnectionStage =
stage === 'test-connection' && sdk && environment && apiKey;

const onboarded = project.onboardingStatus.status === 'onboarded';

useEffect(() => {
if (environments.length > 0) {
setEnvironment(environments[0]);
Expand All @@ -106,23 +110,21 @@ export const ConnectSdkDialog = ({
<GenerateApiKey
environments={environments}
environment={environment}
project={project}
project={projectId}
sdkType={sdk.type}
onEnvSelect={setEnvironment}
onApiKey={setApiKey}
/>
) : null}
{isTestConnectionStage ? (
<Suspense fallback={<Loader />}>
<TestSdkConnection
sdk={sdk}
apiKey={apiKey}
feature={feature}
onSdkChange={() => {
setStage('select-sdk');
}}
/>
</Suspense>
<SdkConnection
apiKey={apiKey}
sdk={sdk}
feature={feature}
onSdkChange={() => {
setStage('select-sdk');
}}
/>
) : null}

{stage === 'generate-api-key' ? (
Expand Down Expand Up @@ -152,22 +154,25 @@ export const ConnectSdkDialog = ({
{isTestConnectionStage ? (
<Navigation>
<NextStepSectionSpacedContainer>
<Button
variant='text'
color='inherit'
onClick={() => {
setStage('generate-api-key');
}}
>
Back
</Button>
{!onboarded ? (
<Button
variant='text'
color='inherit'
onClick={() => {
setStage('generate-api-key');
}}
>
Back
</Button>
) : null}

<Button
variant='contained'
onClick={() => {
onClose();
}}
>
Finish
Next
</Button>
</NextStepSectionSpacedContainer>
</Navigation>
Expand All @@ -182,10 +187,9 @@ export const ConnectSdkDialog = ({
) : null}
{isLargeScreen && isTestConnectionStage ? (
<ConnectionInformation
projectId={project}
projectId={projectId}
sdk={sdk.name}
environment={environment}
onConnection={onClose}
/>
) : null}
</Box>
Expand Down
74 changes: 42 additions & 32 deletions frontend/src/component/onboarding/ConnectionInformation.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { styled, Typography, useTheme } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { WhitePulsingAvatar } from 'component/common/PulsingAvatar/PulsingAvatar';
import Pending from '@mui/icons-material/Pending';
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
import { useEffect } from 'react';
import Check from '@mui/icons-material/Check';

interface IConnectionInformationProps {
onConnection: () => void;
projectId: string;
sdk: string;
environment: string;
Expand Down Expand Up @@ -50,8 +48,16 @@ export const ConnectionStatus = styled('div')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
}));

export const StyledCheck = styled(Check)(({ theme }) => ({
color: theme.palette.primary.main,
backgroundColor: theme.palette.background.paper,
borderRadius: '50%',
padding: theme.spacing(1),
width: '80px',
height: '80px',
}));

export const ConnectionInformation = ({
onConnection,
projectId,
sdk,
environment,
Expand All @@ -63,12 +69,6 @@ export const ConnectionInformation = ({

const onboarded = project.onboardingStatus.status === 'onboarded';

useEffect(() => {
if (onboarded) {
onConnection();
}
}, [onboarded]);

return (
<Container>
<Title>Connection information</Title>
Expand All @@ -86,28 +86,38 @@ export const ConnectionInformation = ({
<Typography variant='body2'>{sdk}</Typography>
</Info>
</SdkInfo>
<ConnectionStatus>
<Typography fontWeight='bold' variant='body2'>
Connection status
</Typography>
<Typography sx={{ mb: theme.spacing(4) }} variant='body2'>
Waiting for SDK data...
</Typography>
<ConditionallyRender
condition={true}
show={
<WhitePulsingAvatar
sx={{
width: 80,
height: 80,
}}
active={true}
>
<Pending fontSize='large' />
</WhitePulsingAvatar>
}
/>
</ConnectionStatus>
{onboarded ? (
<ConnectionStatus>
<Typography fontWeight='bold' variant='body2'>
Connection status
</Typography>
<Typography sx={{ mb: theme.spacing(4) }} variant='body2'>
Connected
</Typography>
<StyledCheck />
<Typography sx={{ mb: theme.spacing(4) }} variant='body2'>
We received metrics from your application!
</Typography>
</ConnectionStatus>
) : (
<ConnectionStatus>
<Typography fontWeight='bold' variant='body2'>
Connection status
</Typography>
<Typography sx={{ mb: theme.spacing(4) }} variant='body2'>
Waiting for SDK data...
</Typography>
<WhitePulsingAvatar
sx={{
width: 80,
height: 80,
}}
active={true}
>
<Pending fontSize='large' />
</WhitePulsingAvatar>
</ConnectionStatus>
)}
</Container>
);
};
150 changes: 150 additions & 0 deletions frontend/src/component/onboarding/SdkConnected.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import type { FC } from 'react';
import { Box, IconButton, styled, Tooltip, Typography } from '@mui/material';
import { SectionHeader, StepperBox } from './SharedComponents';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import type { SdkName, Sdk } from './sharedTypes';
import copy from 'copy-to-clipboard';
import useToast from 'hooks/useToast';
import CopyIcon from '@mui/icons-material/FileCopy';
import { Stepper } from './Stepper';
import { Badge } from '../common/Badge/Badge';
import { Markdown } from 'component/common/Markdown/Markdown';
import type { CodeComponent } from 'react-markdown/lib/ast-to-react';
import android from './snippets/android.md?raw';
import go from './snippets/go.md?raw';
import javascript from './snippets/javascript.md?raw';
import nodejs from './snippets/nodejs.md?raw';
import python from './snippets/python.md?raw';
import ruby from './snippets/ruby.md?raw';
import svelte from './snippets/svelte.md?raw';
import vue from './snippets/vue.md?raw';
import flutter from './snippets/flutter.md?raw';
import java from './snippets/java.md?raw';
import dotnet from './snippets/dotnet.md?raw';
import php from './snippets/php.md?raw';
import react from './snippets/react.md?raw';
import rust from './snippets/rust.md?raw';
import swift from './snippets/swift.md?raw';

const snippets: Record<SdkName, string> = {
Android: android,
Go: go,
JavaScript: javascript,
'Node.js': nodejs,
Python: python,
Ruby: ruby,
Svelte: svelte,
Vue: vue,
Flutter: flutter,
Java: java,
'.NET': dotnet,
PHP: php,
React: react,
Rust: rust,
Swift: swift,
};

const SpacedContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(5, 8, 2, 8),
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(3),
}));

const StyledCodeBlock = styled('pre')(({ theme }) => ({
backgroundColor: theme.palette.background.elevation1,
padding: theme.spacing(2),
borderRadius: theme.shape.borderRadius,
overflow: 'auto',
fontSize: theme.typography.body2.fontSize,
wordBreak: 'break-all',
whiteSpace: 'pre-wrap',
position: 'relative',
maxHeight: theme.spacing(34),
}));

const CopyToClipboard = styled(Tooltip)(({ theme }) => ({
position: 'absolute',
top: theme.spacing(1),
right: theme.spacing(1),
}));

const CopyBlock: FC<{ title: string; code: string }> = ({ title, code }) => {
const onCopyToClipboard = (data: string) => () => {
copy(data);
setToastData({
type: 'success',
title: 'Copied to clipboard',
});
};
const { setToastData } = useToast();

return (
<StyledCodeBlock>
{code}
<CopyToClipboard title={title} arrow>
<IconButton onClick={onCopyToClipboard(code)} size='small'>
<CopyIcon />
</IconButton>
</CopyToClipboard>
</StyledCodeBlock>
);
};

const ChangeSdk = styled('div')(({ theme }) => ({
display: 'inline-flex',
gap: theme.spacing(3),
padding: theme.spacing(1, 2),
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadius,
marginBottom: theme.spacing(3),
}));

const CodeRenderer: CodeComponent = ({ inline = false, children }) => {
if (!inline && typeof children?.[0] === 'string') {
return <CopyBlock code={children[0]} title='Copy code' />;
}

return <code>{children}</code>;
};

interface ISdkConnectedProps {
sdk: Sdk;
}

export const SdkConnected: FC<ISdkConnectedProps> = ({ sdk }) => {
const { uiConfig } = useUiConfig();

const clientApiUrl = `${uiConfig.unleashUrl}/api/`;
const frontendApiUrl = `${uiConfig.unleashUrl}/api/frontend/`;
const apiUrl = sdk.type === 'client' ? clientApiUrl : frontendApiUrl;

const snippet = (snippets[sdk.name] || '').replace(
'<YOUR_API_URL>',
apiUrl,
);

return (
<SpacedContainer>
<Typography variant='h2'>Connect an SDK to Unleash</Typography>
<StepperBox>
<Stepper active={2} steps={3} />
<Badge color='secondary'>3/3 - Test connection</Badge>
</StepperBox>
<Box sx={{ mt: 2 }}>
<SectionHeader>Production settings</SectionHeader>
<Typography variant='body2'>
In order to validate the connection, we changed some
settings that you might want to revert. We recommend the
following default settings.
</Typography>
<Markdown components={{ code: CodeRenderer }}>
{snippet}
</Markdown>
</Box>
</SpacedContainer>
);
};

// Use a default export for lazy-loading
export default SdkConnected;
Loading

0 comments on commit aca0de4

Please sign in to comment.