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:
parent
397b44bfd3
commit
01fb748c01
@ -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' : '',
|
||||||
|
}));
|
@ -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>
|
||||||
|
113
frontend/src/component/onboarding/ConnectionInformation.tsx
Normal file
113
frontend/src/component/onboarding/ConnectionInformation.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
@ -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 />
|
||||||
|
@ -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' : '',
|
|
||||||
}));
|
|
@ -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';
|
||||||
|
@ -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';
|
||||||
|
Loading…
Reference in New Issue
Block a user