1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-09 00:18:00 +01:00

feat: connection status bubble (#8099)

Fully working cycle


![image](https://github.com/user-attachments/assets/cf20788b-29fa-4489-91e7-7db9f592da9b)
This commit is contained in:
Jaanus Sellin 2024-09-05 13:56:25 +03:00 committed by GitHub
parent 397b44bfd3
commit 01fb748c01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 169 additions and 30 deletions

View File

@ -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' : '',
}));

View File

@ -9,14 +9,11 @@ import {
import { GenerateApiKey } from './GenerateApiKey'; import { GenerateApiKey } from './GenerateApiKey';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { SelectSdk } from './SelectSdk'; import { SelectSdk } from './SelectSdk';
import { import { GenerateApiKeyConcepts, SelectSdkConcepts } from './UnleashConcepts';
ConceptsDefinitionsWrapper,
GenrateApiKeyConcepts,
SelectSdkConcepts,
} from './UnleashConcepts';
import { TestSdkConnection } from './TestSdkConnection'; import { TestSdkConnection } from './TestSdkConnection';
import type { Sdk } from './sharedTypes'; import type { Sdk } from './sharedTypes';
import { ConnectionInformation } from './ConnectionInformation';
interface IConnectSDKDialogProps { interface IConnectSDKDialogProps {
open: boolean; open: boolean;
@ -180,10 +177,15 @@ export const ConnectSdkDialog = ({
<SelectSdkConcepts /> <SelectSdkConcepts />
) : null} ) : null}
{isLargeScreen && isGenerateApiKeyStage ? ( {isLargeScreen && isGenerateApiKeyStage ? (
<GenrateApiKeyConcepts /> <GenerateApiKeyConcepts />
) : null} ) : null}
{isLargeScreen && isTestConnectionStage ? ( {isLargeScreen && isTestConnectionStage ? (
<ConceptsDefinitionsWrapper /> <ConnectionInformation
projectId={project}
sdk={sdk.name}
environment={environment}
onConnection={onClose}
/>
) : null} ) : null}
</Box> </Box>
</StyledDialog> </StyledDialog>

View File

@ -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 (
<Container>
<Title>Connection information</Title>
<SdkInfo>
<Info>
<Typography fontWeight='bold' variant='body2'>
Environment
</Typography>
<Typography variant='body2'>{environment}</Typography>
</Info>
<Info>
<Typography fontWeight='bold' variant='body2'>
SDK
</Typography>
<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>
</Container>
);
};

View File

@ -40,7 +40,7 @@ const ConceptSummary = styled('div')(({ theme }) => ({
marginBottom: theme.spacing(2), marginBottom: theme.spacing(2),
})); }));
export const GenrateApiKeyConcepts = () => ( export const GenerateApiKeyConcepts = () => (
<ConceptsDefinitionsWrapper> <ConceptsDefinitionsWrapper>
<ConceptItem> <ConceptItem>
<StyledProjectIcon /> <StyledProjectIcon />

View File

@ -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' : '',
}));

View File

@ -9,7 +9,7 @@ import {
} from '@mui/material'; } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StyledFileDropZone } from './StyledFileDropZone'; import { StyledFileDropZone } from './StyledFileDropZone';
import { PulsingAvatar } from '../PulsingAvatar'; import { PulsingAvatar } from 'component/common/PulsingAvatar/PulsingAvatar';
import ArrowUpward from '@mui/icons-material/ArrowUpward'; import ArrowUpward from '@mui/icons-material/ArrowUpward';
import { ImportExplanation } from './ImportExplanation'; import { ImportExplanation } from './ImportExplanation';
import { type FC, type ReactNode, useState } from 'react'; import { type FC, type ReactNode, useState } from 'react';

View File

@ -8,7 +8,7 @@ import { ActionsContainer } from '../ActionsContainer';
import Check from '@mui/icons-material/Check'; import Check from '@mui/icons-material/Check';
import ErrorIcon from '@mui/icons-material/Error'; import ErrorIcon from '@mui/icons-material/Error';
import Pending from '@mui/icons-material/Pending'; 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 { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Box } from '@mui/system'; import { Box } from '@mui/system';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';