mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-15 01:16:22 +02:00
feat: display new completed dialog (#8255)
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.  --------- Co-authored-by: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com>
This commit is contained in:
parent
c7427f4b91
commit
aca0de414e
@ -7,22 +7,21 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { GenerateApiKey } from './GenerateApiKey';
|
import { GenerateApiKey } from './GenerateApiKey';
|
||||||
import { lazy, Suspense, useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { SelectSdk } from './SelectSdk';
|
import { SelectSdk } from './SelectSdk';
|
||||||
import { GenerateApiKeyConcepts, SelectSdkConcepts } from './UnleashConcepts';
|
import { GenerateApiKeyConcepts, SelectSdkConcepts } from './UnleashConcepts';
|
||||||
|
|
||||||
const TestSdkConnection = lazy(() => import('./TestSdkConnection'));
|
|
||||||
|
|
||||||
import type { Sdk } from './sharedTypes';
|
import type { Sdk } from './sharedTypes';
|
||||||
import { ConnectionInformation } from './ConnectionInformation';
|
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 {
|
interface IConnectSDKDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
project: string;
|
project: string;
|
||||||
environments: string[];
|
environments: string[];
|
||||||
feature: string;
|
feature?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectSdk = styled('main')(({ theme }) => ({
|
const ConnectSdk = styled('main')(({ theme }) => ({
|
||||||
@ -68,7 +67,7 @@ export const ConnectSdkDialog = ({
|
|||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
environments,
|
environments,
|
||||||
project,
|
project: projectId,
|
||||||
feature,
|
feature,
|
||||||
}: IConnectSDKDialogProps) => {
|
}: IConnectSDKDialogProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -77,6 +76,9 @@ 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, {
|
||||||
|
refreshInterval: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
const isSelectSdkStage = stage === 'select-sdk';
|
const isSelectSdkStage = stage === 'select-sdk';
|
||||||
const isGenerateApiKeyStage =
|
const isGenerateApiKeyStage =
|
||||||
@ -84,6 +86,8 @@ export const ConnectSdkDialog = ({
|
|||||||
const isTestConnectionStage =
|
const isTestConnectionStage =
|
||||||
stage === 'test-connection' && sdk && environment && apiKey;
|
stage === 'test-connection' && sdk && environment && apiKey;
|
||||||
|
|
||||||
|
const onboarded = project.onboardingStatus.status === 'onboarded';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (environments.length > 0) {
|
if (environments.length > 0) {
|
||||||
setEnvironment(environments[0]);
|
setEnvironment(environments[0]);
|
||||||
@ -106,23 +110,21 @@ export const ConnectSdkDialog = ({
|
|||||||
<GenerateApiKey
|
<GenerateApiKey
|
||||||
environments={environments}
|
environments={environments}
|
||||||
environment={environment}
|
environment={environment}
|
||||||
project={project}
|
project={projectId}
|
||||||
sdkType={sdk.type}
|
sdkType={sdk.type}
|
||||||
onEnvSelect={setEnvironment}
|
onEnvSelect={setEnvironment}
|
||||||
onApiKey={setApiKey}
|
onApiKey={setApiKey}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{isTestConnectionStage ? (
|
{isTestConnectionStage ? (
|
||||||
<Suspense fallback={<Loader />}>
|
<SdkConnection
|
||||||
<TestSdkConnection
|
apiKey={apiKey}
|
||||||
sdk={sdk}
|
sdk={sdk}
|
||||||
apiKey={apiKey}
|
feature={feature}
|
||||||
feature={feature}
|
onSdkChange={() => {
|
||||||
onSdkChange={() => {
|
setStage('select-sdk');
|
||||||
setStage('select-sdk');
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{stage === 'generate-api-key' ? (
|
{stage === 'generate-api-key' ? (
|
||||||
@ -152,22 +154,25 @@ export const ConnectSdkDialog = ({
|
|||||||
{isTestConnectionStage ? (
|
{isTestConnectionStage ? (
|
||||||
<Navigation>
|
<Navigation>
|
||||||
<NextStepSectionSpacedContainer>
|
<NextStepSectionSpacedContainer>
|
||||||
<Button
|
{!onboarded ? (
|
||||||
variant='text'
|
<Button
|
||||||
color='inherit'
|
variant='text'
|
||||||
onClick={() => {
|
color='inherit'
|
||||||
setStage('generate-api-key');
|
onClick={() => {
|
||||||
}}
|
setStage('generate-api-key');
|
||||||
>
|
}}
|
||||||
Back
|
>
|
||||||
</Button>
|
Back
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant='contained'
|
variant='contained'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Finish
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
</NextStepSectionSpacedContainer>
|
</NextStepSectionSpacedContainer>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
@ -182,10 +187,9 @@ export const ConnectSdkDialog = ({
|
|||||||
) : null}
|
) : null}
|
||||||
{isLargeScreen && isTestConnectionStage ? (
|
{isLargeScreen && isTestConnectionStage ? (
|
||||||
<ConnectionInformation
|
<ConnectionInformation
|
||||||
projectId={project}
|
projectId={projectId}
|
||||||
sdk={sdk.name}
|
sdk={sdk.name}
|
||||||
environment={environment}
|
environment={environment}
|
||||||
onConnection={onClose}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import { styled, Typography, useTheme } from '@mui/material';
|
import { styled, Typography, useTheme } from '@mui/material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import { WhitePulsingAvatar } from 'component/common/PulsingAvatar/PulsingAvatar';
|
import { WhitePulsingAvatar } from 'component/common/PulsingAvatar/PulsingAvatar';
|
||||||
import Pending from '@mui/icons-material/Pending';
|
import Pending from '@mui/icons-material/Pending';
|
||||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||||
import { useEffect } from 'react';
|
import Check from '@mui/icons-material/Check';
|
||||||
|
|
||||||
interface IConnectionInformationProps {
|
interface IConnectionInformationProps {
|
||||||
onConnection: () => void;
|
|
||||||
projectId: string;
|
projectId: string;
|
||||||
sdk: string;
|
sdk: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
@ -50,8 +48,16 @@ export const ConnectionStatus = styled('div')(({ theme }) => ({
|
|||||||
fontSize: theme.fontSizes.smallBody,
|
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 = ({
|
export const ConnectionInformation = ({
|
||||||
onConnection,
|
|
||||||
projectId,
|
projectId,
|
||||||
sdk,
|
sdk,
|
||||||
environment,
|
environment,
|
||||||
@ -63,12 +69,6 @@ export const ConnectionInformation = ({
|
|||||||
|
|
||||||
const onboarded = project.onboardingStatus.status === 'onboarded';
|
const onboarded = project.onboardingStatus.status === 'onboarded';
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (onboarded) {
|
|
||||||
onConnection();
|
|
||||||
}
|
|
||||||
}, [onboarded]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Title>Connection information</Title>
|
<Title>Connection information</Title>
|
||||||
@ -86,28 +86,38 @@ export const ConnectionInformation = ({
|
|||||||
<Typography variant='body2'>{sdk}</Typography>
|
<Typography variant='body2'>{sdk}</Typography>
|
||||||
</Info>
|
</Info>
|
||||||
</SdkInfo>
|
</SdkInfo>
|
||||||
<ConnectionStatus>
|
{onboarded ? (
|
||||||
<Typography fontWeight='bold' variant='body2'>
|
<ConnectionStatus>
|
||||||
Connection status
|
<Typography fontWeight='bold' variant='body2'>
|
||||||
</Typography>
|
Connection status
|
||||||
<Typography sx={{ mb: theme.spacing(4) }} variant='body2'>
|
</Typography>
|
||||||
Waiting for SDK data...
|
<Typography sx={{ mb: theme.spacing(4) }} variant='body2'>
|
||||||
</Typography>
|
Connected
|
||||||
<ConditionallyRender
|
</Typography>
|
||||||
condition={true}
|
<StyledCheck />
|
||||||
show={
|
<Typography sx={{ mb: theme.spacing(4) }} variant='body2'>
|
||||||
<WhitePulsingAvatar
|
We received metrics from your application!
|
||||||
sx={{
|
</Typography>
|
||||||
width: 80,
|
</ConnectionStatus>
|
||||||
height: 80,
|
) : (
|
||||||
}}
|
<ConnectionStatus>
|
||||||
active={true}
|
<Typography fontWeight='bold' variant='body2'>
|
||||||
>
|
Connection status
|
||||||
<Pending fontSize='large' />
|
</Typography>
|
||||||
</WhitePulsingAvatar>
|
<Typography sx={{ mb: theme.spacing(4) }} variant='body2'>
|
||||||
}
|
Waiting for SDK data...
|
||||||
/>
|
</Typography>
|
||||||
</ConnectionStatus>
|
<WhitePulsingAvatar
|
||||||
|
sx={{
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
}}
|
||||||
|
active={true}
|
||||||
|
>
|
||||||
|
<Pending fontSize='large' />
|
||||||
|
</WhitePulsingAvatar>
|
||||||
|
</ConnectionStatus>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
150
frontend/src/component/onboarding/SdkConnected.tsx
Normal file
150
frontend/src/component/onboarding/SdkConnected.tsx
Normal file
@ -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;
|
34
frontend/src/component/onboarding/SdkConnection.tsx
Normal file
34
frontend/src/component/onboarding/SdkConnection.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Suspense } from 'react';
|
||||||
|
import Loader from '../common/Loader/Loader';
|
||||||
|
import TestSdkConnection from './TestSdkConnection';
|
||||||
|
import type { Sdk } from './sharedTypes';
|
||||||
|
import { SdkConnected } from './SdkConnected';
|
||||||
|
|
||||||
|
interface ISdkConnectionProps {
|
||||||
|
sdk: Sdk;
|
||||||
|
apiKey: string;
|
||||||
|
feature?: string;
|
||||||
|
onSdkChange: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SdkConnection = ({
|
||||||
|
sdk,
|
||||||
|
apiKey,
|
||||||
|
feature,
|
||||||
|
onSdkChange,
|
||||||
|
}: ISdkConnectionProps) => {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<Loader />}>
|
||||||
|
{feature ? (
|
||||||
|
<TestSdkConnection
|
||||||
|
sdk={sdk}
|
||||||
|
apiKey={apiKey}
|
||||||
|
feature={feature}
|
||||||
|
onSdkChange={onSdkChange}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<SdkConnected sdk={sdk} />
|
||||||
|
)}
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
};
|
@ -534,17 +534,19 @@ export const ProjectFeatureToggles = ({
|
|||||||
</PageContent>
|
</PageContent>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{'feature' in project.onboardingStatus ? (
|
<ConnectSdkDialog
|
||||||
<ConnectSdkDialog
|
open={connectSdkOpen}
|
||||||
open={connectSdkOpen}
|
onClose={() => {
|
||||||
onClose={() => {
|
setConnectSdkOpen(false);
|
||||||
setConnectSdkOpen(false);
|
}}
|
||||||
}}
|
project={projectId}
|
||||||
project={projectId}
|
environments={environments}
|
||||||
environments={environments}
|
feature={
|
||||||
feature={project.onboardingStatus.feature}
|
'feature' in project.onboardingStatus
|
||||||
/>
|
? project.onboardingStatus.feature
|
||||||
) : null}
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
<BatchSelectionActionsBar count={selectedData.length}>
|
<BatchSelectionActionsBar count={selectedData.length}>
|
||||||
<ProjectFeaturesBatchActions
|
<ProjectFeaturesBatchActions
|
||||||
selectedIds={Object.keys(rowSelection)}
|
selectedIds={Object.keys(rowSelection)}
|
||||||
|
Loading…
Reference in New Issue
Block a user