From 01fb748c018dc6bf432c7f586a9260d27d9fb1da Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Thu, 5 Sep 2024 13:56:25 +0300 Subject: [PATCH] feat: connection status bubble (#8099) Fully working cycle ![image](https://github.com/user-attachments/assets/cf20788b-29fa-4489-91e7-7db9f592da9b) --- .../common/PulsingAvatar/PulsingAvatar.tsx | 44 +++++++ .../component/onboarding/ConnectSdkDialog.tsx | 16 +-- .../onboarding/ConnectionInformation.tsx | 113 ++++++++++++++++++ .../component/onboarding/UnleashConcepts.tsx | 2 +- .../project/Project/Import/PulsingAvatar.tsx | 20 ---- .../Import/configure/ConfigurationStage.tsx | 2 +- .../Project/Import/import/ImportStage.tsx | 2 +- 7 files changed, 169 insertions(+), 30 deletions(-) create mode 100644 frontend/src/component/common/PulsingAvatar/PulsingAvatar.tsx create mode 100644 frontend/src/component/onboarding/ConnectionInformation.tsx delete mode 100644 frontend/src/component/project/Project/Import/PulsingAvatar.tsx diff --git a/frontend/src/component/common/PulsingAvatar/PulsingAvatar.tsx b/frontend/src/component/common/PulsingAvatar/PulsingAvatar.tsx new file mode 100644 index 0000000000..0cdb21e95d --- /dev/null +++ b/frontend/src/component/common/PulsingAvatar/PulsingAvatar.tsx @@ -0,0 +1,44 @@ +import { alpha, Avatar, styled } from '@mui/material'; + +export const PulsingAvatar = styled(Avatar, { + shouldForwardProp: (prop) => prop !== 'active', +})<{ active: boolean }>(({ theme, active }) => ({ + transition: 'background-color 0.5s ease', + color: theme.palette.common.white, + backgroundColor: active + ? theme.palette.primary.main + : theme.palette.divider, + '@keyframes pulse': { + '0%': { + boxShadow: `0 0 0 0px ${alpha(theme.palette.primary.main, 0.7)}`, + }, + '100%': { + boxShadow: `0 0 0 20px ${alpha(theme.palette.primary.main, 0.0)}`, + }, + }, + animation: active ? 'pulse 2s infinite' : '', +})); + +/** + * Temporary component until we decide how all the colors will look like + * Then we can use PulsingAvatar with a color prop perhaps + * PulsingAvatar was not working nicely on purple background + */ +export const WhitePulsingAvatar = styled(Avatar, { + shouldForwardProp: (prop) => prop !== 'active', +})<{ active: boolean }>(({ theme, active }) => ({ + transition: 'background-color 0.5s ease', + color: theme.palette.primary.main, + backgroundColor: active + ? theme.palette.background.default + : theme.palette.divider, + '@keyframes pulse': { + '0%': { + boxShadow: `0 0 0 0px ${alpha(theme.palette.background.default, 0.7)}`, + }, + '100%': { + boxShadow: `0 0 0 20px ${alpha(theme.palette.background.default, 0.0)}`, + }, + }, + animation: active ? 'pulse 2s infinite' : '', +})); diff --git a/frontend/src/component/onboarding/ConnectSdkDialog.tsx b/frontend/src/component/onboarding/ConnectSdkDialog.tsx index dbee6486a8..256dd349e5 100644 --- a/frontend/src/component/onboarding/ConnectSdkDialog.tsx +++ b/frontend/src/component/onboarding/ConnectSdkDialog.tsx @@ -9,14 +9,11 @@ import { import { GenerateApiKey } from './GenerateApiKey'; import { useEffect, useState } from 'react'; import { SelectSdk } from './SelectSdk'; -import { - ConceptsDefinitionsWrapper, - GenrateApiKeyConcepts, - SelectSdkConcepts, -} from './UnleashConcepts'; +import { GenerateApiKeyConcepts, SelectSdkConcepts } from './UnleashConcepts'; import { TestSdkConnection } from './TestSdkConnection'; import type { Sdk } from './sharedTypes'; +import { ConnectionInformation } from './ConnectionInformation'; interface IConnectSDKDialogProps { open: boolean; @@ -180,10 +177,15 @@ export const ConnectSdkDialog = ({ ) : null} {isLargeScreen && isGenerateApiKeyStage ? ( - + ) : null} {isLargeScreen && isTestConnectionStage ? ( - + ) : null} diff --git a/frontend/src/component/onboarding/ConnectionInformation.tsx b/frontend/src/component/onboarding/ConnectionInformation.tsx new file mode 100644 index 0000000000..e8419c3d88 --- /dev/null +++ b/frontend/src/component/onboarding/ConnectionInformation.tsx @@ -0,0 +1,113 @@ +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'; + +interface IConnectionInformationProps { + onConnection: () => void; + projectId: string; + sdk: string; + environment: string; +} +export const Container = styled('div')(({ theme }) => ({ + backgroundColor: theme.palette.background.sidebar, + padding: theme.spacing(6, 9, 6, 9), + minWidth: '400px', + display: 'flex', + flexDirection: 'column', + color: theme.palette.primary.contrastText, +})); + +export const Title = styled(Typography)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + fontWeight: theme.typography.fontWeightBold, +})); + +export const SdkInfo = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + padding: theme.spacing(4, 0, 12, 0), + justifyContent: 'space-between', + fontSize: theme.spacing(1), +})); + +export const Info = styled('div')(({ theme }) => ({ + display: 'flex', + gap: theme.spacing(1), + flexDirection: 'column', +})); + +export const ConnectionStatus = styled('div')(({ theme }) => ({ + alignItems: 'center', + justifyContent: 'center', + display: 'flex', + gap: theme.spacing(2), + flexDirection: 'column', + fontSize: theme.fontSizes.smallBody, +})); + +export const ConnectionInformation = ({ + onConnection, + projectId, + sdk, + environment, +}: IConnectionInformationProps) => { + const theme = useTheme(); + const { project } = useProjectOverview(projectId, { + refreshInterval: 1000, + }); + + const onboarded = project.onboardingStatus.status === 'onboarded'; + + useEffect(() => { + if (onboarded) { + onConnection(); + } + }, [onboarded]); + + return ( + + Connection information + + + + Environment + + {environment} + + + + SDK + + {sdk} + + + + + Connection status + + + Waiting for SDK data... + + + + + } + /> + + + ); +}; diff --git a/frontend/src/component/onboarding/UnleashConcepts.tsx b/frontend/src/component/onboarding/UnleashConcepts.tsx index a91804b1ba..7f9a575339 100644 --- a/frontend/src/component/onboarding/UnleashConcepts.tsx +++ b/frontend/src/component/onboarding/UnleashConcepts.tsx @@ -40,7 +40,7 @@ const ConceptSummary = styled('div')(({ theme }) => ({ marginBottom: theme.spacing(2), })); -export const GenrateApiKeyConcepts = () => ( +export const GenerateApiKeyConcepts = () => ( diff --git a/frontend/src/component/project/Project/Import/PulsingAvatar.tsx b/frontend/src/component/project/Project/Import/PulsingAvatar.tsx deleted file mode 100644 index 38f7e49d9e..0000000000 --- a/frontend/src/component/project/Project/Import/PulsingAvatar.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { alpha, Avatar, styled } from '@mui/material'; - -export const PulsingAvatar = styled(Avatar, { - shouldForwardProp: (prop) => prop !== 'active', -})<{ active: boolean }>(({ theme, active }) => ({ - transition: 'background-color 0.5s ease', - color: theme.palette.common.white, - backgroundColor: active - ? theme.palette.primary.main - : theme.palette.divider, - '@keyframes pulse': { - '0%': { - boxShadow: `0 0 0 0px ${alpha(theme.palette.primary.main, 0.7)}`, - }, - '100%': { - boxShadow: `0 0 0 20px ${alpha(theme.palette.primary.main, 0.0)}`, - }, - }, - animation: active ? 'pulse 2s infinite' : '', -})); diff --git a/frontend/src/component/project/Project/Import/configure/ConfigurationStage.tsx b/frontend/src/component/project/Project/Import/configure/ConfigurationStage.tsx index 4d4dd7ba8f..b385d2f481 100644 --- a/frontend/src/component/project/Project/Import/configure/ConfigurationStage.tsx +++ b/frontend/src/component/project/Project/Import/configure/ConfigurationStage.tsx @@ -9,7 +9,7 @@ import { } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { StyledFileDropZone } from './StyledFileDropZone'; -import { PulsingAvatar } from '../PulsingAvatar'; +import { PulsingAvatar } from 'component/common/PulsingAvatar/PulsingAvatar'; import ArrowUpward from '@mui/icons-material/ArrowUpward'; import { ImportExplanation } from './ImportExplanation'; import { type FC, type ReactNode, useState } from 'react'; diff --git a/frontend/src/component/project/Project/Import/import/ImportStage.tsx b/frontend/src/component/project/Project/Import/import/ImportStage.tsx index 3eac181e51..1ea8fda834 100644 --- a/frontend/src/component/project/Project/Import/import/ImportStage.tsx +++ b/frontend/src/component/project/Project/Import/import/ImportStage.tsx @@ -8,7 +8,7 @@ import { ActionsContainer } from '../ActionsContainer'; import Check from '@mui/icons-material/Check'; import ErrorIcon from '@mui/icons-material/Error'; import Pending from '@mui/icons-material/Pending'; -import { PulsingAvatar } from '../PulsingAvatar'; +import { PulsingAvatar } from 'component/common/PulsingAvatar/PulsingAvatar'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { Box } from '@mui/system'; import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';