diff --git a/frontend/src/component/onboarding/ConnectSdkDialog.tsx b/frontend/src/component/onboarding/ConnectSdkDialog.tsx index 5ebbd0a2b50d..a237f737dead 100644 --- a/frontend/src/component/onboarding/ConnectSdkDialog.tsx +++ b/frontend/src/component/onboarding/ConnectSdkDialog.tsx @@ -7,7 +7,7 @@ import { useTheme, } from '@mui/material'; import { GenerateApiKey } from './GenerateApiKey'; -import { lazy, Suspense, useEffect, useState } from 'react'; +import { lazy, useEffect, useState } from 'react'; import { SelectSdk } from './SelectSdk'; import { GenerateApiKeyConcepts, SelectSdkConcepts } from './UnleashConcepts'; @@ -15,14 +15,15 @@ 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 }) => ({ @@ -68,7 +69,7 @@ export const ConnectSdkDialog = ({ open, onClose, environments, - project, + project: projectId, feature, }: IConnectSDKDialogProps) => { const theme = useTheme(); @@ -77,6 +78,9 @@ export const ConnectSdkDialog = ({ const [environment, setEnvironment] = useState(null); const [apiKey, setApiKey] = useState(null); const [stage, setStage] = useState('select-sdk'); + const { project } = useProjectOverview(projectId, { + refreshInterval: 1000, + }); const isSelectSdkStage = stage === 'select-sdk'; const isGenerateApiKeyStage = @@ -84,6 +88,8 @@ export const ConnectSdkDialog = ({ const isTestConnectionStage = stage === 'test-connection' && sdk && environment && apiKey; + const onboarded = project.onboardingStatus.status === 'onboarded'; + useEffect(() => { if (environments.length > 0) { setEnvironment(environments[0]); @@ -106,23 +112,21 @@ export const ConnectSdkDialog = ({ ) : null} {isTestConnectionStage ? ( - }> - { - setStage('select-sdk'); - }} - /> - + { + setStage('select-sdk'); + }} + /> ) : null} {stage === 'generate-api-key' ? ( @@ -152,22 +156,25 @@ export const ConnectSdkDialog = ({ {isTestConnectionStage ? ( - + {!onboarded ? ( + + ) : null} + @@ -182,10 +189,9 @@ export const ConnectSdkDialog = ({ ) : null} {isLargeScreen && isTestConnectionStage ? ( ) : null} diff --git a/frontend/src/component/onboarding/ConnectionInformation.tsx b/frontend/src/component/onboarding/ConnectionInformation.tsx index e8419c3d8813..d405eee820c2 100644 --- a/frontend/src/component/onboarding/ConnectionInformation.tsx +++ b/frontend/src/component/onboarding/ConnectionInformation.tsx @@ -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; @@ -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, @@ -63,12 +69,6 @@ export const ConnectionInformation = ({ const onboarded = project.onboardingStatus.status === 'onboarded'; - useEffect(() => { - if (onboarded) { - onConnection(); - } - }, [onboarded]); - return ( Connection information @@ -86,28 +86,38 @@ export const ConnectionInformation = ({ {sdk} - - - Connection status - - - Waiting for SDK data... - - - - - } - /> - + {onboarded ? ( + + + Connection status + + + Connected + + + + We received metrics from your application! + + + ) : ( + + + Connection status + + + Waiting for SDK data... + + + + + + )} ); }; diff --git a/frontend/src/component/onboarding/SdkConnected.tsx b/frontend/src/component/onboarding/SdkConnected.tsx new file mode 100644 index 000000000000..91bd33b6546b --- /dev/null +++ b/frontend/src/component/onboarding/SdkConnected.tsx @@ -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 = { + 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 ( + + {code} + + + + + + + ); +}; + +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 ; + } + + return {children}; +}; + +interface ISdkConnectedProps { + sdk: Sdk; +} + +export const SdkConnected: FC = ({ 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( + '', + apiUrl, + ); + + return ( + + Connect an SDK to Unleash + + + 3/3 - Test connection + + + Production settings + + In order to validate the connection, we changed some + settings that you might want to revert. We recommend the + following default settings. + + + {snippet} + + + + ); +}; + +// Use a default export for lazy-loading +export default SdkConnected; diff --git a/frontend/src/component/onboarding/SdkConnection.tsx b/frontend/src/component/onboarding/SdkConnection.tsx new file mode 100644 index 000000000000..14e2450c3400 --- /dev/null +++ b/frontend/src/component/onboarding/SdkConnection.tsx @@ -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 ( + }> + {feature ? ( + + ) : ( + + )} + + ); +}; diff --git a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx index ae3408e9c049..8213832afbed 100644 --- a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx +++ b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx @@ -70,6 +70,8 @@ export const ProjectFeatureToggles = ({ const { project } = useProjectOverview(projectId); const [connectSdkOpen, setConnectSdkOpen] = useState(false); + console.log('connectSdkOpen', connectSdkOpen); + const { features, total, @@ -534,17 +536,19 @@ export const ProjectFeatureToggles = ({ } /> - {'feature' in project.onboardingStatus ? ( - { - setConnectSdkOpen(false); - }} - project={projectId} - environments={environments} - feature={project.onboardingStatus.feature} - /> - ) : null} + { + setConnectSdkOpen(false); + }} + project={projectId} + environments={environments} + feature={ + 'feature' in project.onboardingStatus + ? project.onboardingStatus.feature + : undefined + } + />