mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-19 01:17:18 +02:00
feat: projects using this application (#6355)

This commit is contained in:
parent
c049374a25
commit
9cd324bd7c
@ -1,5 +1,5 @@
|
|||||||
/* eslint react/no-multi-comp:off */
|
/* eslint react/no-multi-comp:off */
|
||||||
import React, { lazy, useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Avatar,
|
Avatar,
|
||||||
@ -31,6 +31,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
|||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
import { ApplicationEdit } from './ApplicationEdit/ApplicationEdit';
|
import { ApplicationEdit } from './ApplicationEdit/ApplicationEdit';
|
||||||
|
import ApplicationOverview from './ApplicationOverview';
|
||||||
|
|
||||||
type Tab = {
|
type Tab = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -68,8 +69,6 @@ const StyledTab = styled(Tab)(({ theme }) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const ApplicationOverview = lazy(() => import('./ApplicationOverview'));
|
|
||||||
|
|
||||||
export const Application = () => {
|
export const Application = () => {
|
||||||
const useOldApplicationScreen = !useUiFlag('sdkReporting');
|
const useOldApplicationScreen = !useUiFlag('sdkReporting');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
227
frontend/src/component/application/ApplicationChart.tsx
Normal file
227
frontend/src/component/application/ApplicationChart.tsx
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
import { Box, Divider, styled, Typography, useTheme } from '@mui/material';
|
||||||
|
import { ArcherContainer, ArcherElement } from 'react-archer';
|
||||||
|
import { ConditionallyRender } from '../common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { FC, useLayoutEffect, useRef, useState } from 'react';
|
||||||
|
import { ApplicationOverviewSchema } from '../../openapi';
|
||||||
|
import { useRequiredPathParam } from '../../hooks/useRequiredPathParam';
|
||||||
|
import { WarningAmberRounded } from '@mui/icons-material';
|
||||||
|
|
||||||
|
const StyledTable = styled('table')(({ theme }) => ({
|
||||||
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledCell = styled('td')(({ theme }) => ({
|
||||||
|
verticalAlign: 'top',
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyleApplicationContainer = styled(Box)(({ theme }) => ({
|
||||||
|
marginBottom: theme.spacing(18),
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledApplicationBox = styled(Box)<{
|
||||||
|
mode: 'success' | 'warning';
|
||||||
|
}>(({ theme, mode }) => ({
|
||||||
|
borderRadius: theme.shape.borderRadiusMedium,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: theme.palette[mode].border,
|
||||||
|
backgroundColor: theme.palette[mode].light,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: theme.spacing(1.5, 3, 2, 3),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledEnvironmentBox = styled(Box)<{
|
||||||
|
mode: 'success' | 'warning';
|
||||||
|
}>(({ theme, mode }) => ({
|
||||||
|
borderRadius: theme.shape.borderRadiusMedium,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor:
|
||||||
|
theme.palette[mode === 'success' ? 'secondary' : 'warning'].border,
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette[mode === 'success' ? 'secondary' : 'warning'].light,
|
||||||
|
display: 'inline-block',
|
||||||
|
padding: theme.spacing(1.5, 1.5, 1.5, 1.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDivider = styled(Divider)(({ theme }) => ({
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
width: '100%',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledEnvironmentsContainer = styled(Box)({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'start',
|
||||||
|
gap: '20px',
|
||||||
|
});
|
||||||
|
|
||||||
|
const EnvironmentHeader = styled(Typography)(({ theme }) => ({
|
||||||
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledStatus = styled(Typography)<{
|
||||||
|
mode: 'success' | 'warning';
|
||||||
|
}>(({ theme, mode }) => ({
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
color: theme.palette[mode].dark,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const useElementWidth = () => {
|
||||||
|
const elementRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [width, setWidth] = useState('100%');
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
setWidth(`${elementRef.current?.scrollWidth}px`);
|
||||||
|
}, [elementRef, setWidth]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
elementRef,
|
||||||
|
width,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const SuccessStatus = () => (
|
||||||
|
<StyledStatus mode='success'>
|
||||||
|
<WarningAmberRounded
|
||||||
|
sx={(theme) => ({
|
||||||
|
color: theme.palette.success.main,
|
||||||
|
})}
|
||||||
|
/>{' '}
|
||||||
|
Everything looks good!
|
||||||
|
</StyledStatus>
|
||||||
|
);
|
||||||
|
|
||||||
|
const WarningStatus: FC = ({ children }) => (
|
||||||
|
<StyledStatus mode='warning'>
|
||||||
|
<WarningAmberRounded
|
||||||
|
sx={(theme) => ({
|
||||||
|
color: theme.palette.warning.main,
|
||||||
|
})}
|
||||||
|
/>{' '}
|
||||||
|
{children}
|
||||||
|
</StyledStatus>
|
||||||
|
);
|
||||||
|
|
||||||
|
interface IApplicationChartProps {
|
||||||
|
data: ApplicationOverviewSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ApplicationChart = ({ data }: IApplicationChartProps) => {
|
||||||
|
const applicationName = useRequiredPathParam('name');
|
||||||
|
const { elementRef, width } = useElementWidth();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const mode: 'success' | 'warning' =
|
||||||
|
data.issues.length === 0 ? 'success' : 'warning';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ width }}>
|
||||||
|
<ArcherContainer
|
||||||
|
strokeColor={theme.palette.secondary.border}
|
||||||
|
endMarker={false}
|
||||||
|
>
|
||||||
|
<StyleApplicationContainer>
|
||||||
|
<ArcherElement
|
||||||
|
id='application'
|
||||||
|
relations={data.environments.map((environment) => ({
|
||||||
|
targetId: environment.name,
|
||||||
|
targetAnchor: 'top',
|
||||||
|
sourceAnchor: 'bottom',
|
||||||
|
style: {
|
||||||
|
strokeColor:
|
||||||
|
mode === 'success'
|
||||||
|
? theme.palette.secondary.border
|
||||||
|
: theme.palette.warning.border,
|
||||||
|
},
|
||||||
|
}))}
|
||||||
|
>
|
||||||
|
<StyledApplicationBox mode={mode}>
|
||||||
|
<Typography
|
||||||
|
sx={(theme) => ({
|
||||||
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
|
})}
|
||||||
|
color='text.secondary'
|
||||||
|
>
|
||||||
|
Application
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={(theme) => ({
|
||||||
|
fontSize: theme.fontSizes.bodySize,
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{applicationName}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<StyledDivider />
|
||||||
|
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={mode === 'success'}
|
||||||
|
show={<SuccessStatus />}
|
||||||
|
elseShow={
|
||||||
|
<WarningStatus>
|
||||||
|
{data.issues.length} issues detected
|
||||||
|
</WarningStatus>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</StyledApplicationBox>
|
||||||
|
</ArcherElement>
|
||||||
|
</StyleApplicationContainer>
|
||||||
|
|
||||||
|
<StyledEnvironmentsContainer ref={elementRef}>
|
||||||
|
{data.environments.map((environment) => (
|
||||||
|
<ArcherElement
|
||||||
|
id={environment.name}
|
||||||
|
key={environment.name}
|
||||||
|
>
|
||||||
|
<StyledEnvironmentBox
|
||||||
|
mode={mode}
|
||||||
|
key={environment.name}
|
||||||
|
>
|
||||||
|
<EnvironmentHeader>
|
||||||
|
{environment.name} environment
|
||||||
|
</EnvironmentHeader>
|
||||||
|
|
||||||
|
<StyledTable>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<StyledCell>Instances:</StyledCell>
|
||||||
|
<StyledCell>
|
||||||
|
{environment.instanceCount}
|
||||||
|
</StyledCell>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<StyledCell>SDK:</StyledCell>
|
||||||
|
<StyledCell>
|
||||||
|
{environment.sdks.map((sdk) => (
|
||||||
|
<div key={sdk}>{sdk}</div>
|
||||||
|
))}
|
||||||
|
</StyledCell>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<StyledCell>Last seen:</StyledCell>
|
||||||
|
<StyledCell>
|
||||||
|
{environment.lastSeen}
|
||||||
|
</StyledCell>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</StyledTable>
|
||||||
|
</StyledEnvironmentBox>
|
||||||
|
</ArcherElement>
|
||||||
|
))}
|
||||||
|
</StyledEnvironmentsContainer>
|
||||||
|
</ArcherContainer>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -6,7 +6,7 @@ import { ApplicationOverviewIssuesSchema } from 'openapi';
|
|||||||
const WarningContainer = styled(Box)(({ theme }) => ({
|
const WarningContainer = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
paddingBottom: theme.spacing(8),
|
alignSelf: 'stretch',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const WarningHeader = styled(Box)(({ theme }) => ({
|
const WarningHeader = styled(Box)(({ theme }) => ({
|
||||||
|
@ -1,73 +1,13 @@
|
|||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import {
|
import { Alert, Box, Divider, styled } from '@mui/material';
|
||||||
Alert,
|
|
||||||
Box,
|
|
||||||
Divider,
|
|
||||||
styled,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
} from '@mui/material';
|
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { ArcherContainer, ArcherElement } from 'react-archer';
|
|
||||||
import { FC, useLayoutEffect, useRef, useState } from 'react';
|
|
||||||
import { useApplicationOverview } from 'hooks/api/getters/useApplicationOverview/useApplicationOverview';
|
import { useApplicationOverview } from 'hooks/api/getters/useApplicationOverview/useApplicationOverview';
|
||||||
import { WarningAmberRounded } from '@mui/icons-material';
|
|
||||||
import { ApplicationIssues } from './ApplicationIssues/ApplicationIssues';
|
import { ApplicationIssues } from './ApplicationIssues/ApplicationIssues';
|
||||||
|
import { ApplicationChart } from './ApplicationChart';
|
||||||
const StyledTable = styled('table')(({ theme }) => ({
|
import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined';
|
||||||
fontSize: theme.fontSizes.smallerBody,
|
import { Badge } from '../common/Badge/Badge';
|
||||||
marginTop: theme.spacing(2),
|
import { useNavigate } from 'react-router-dom';
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledCell = styled('td')(({ theme }) => ({
|
|
||||||
verticalAlign: 'top',
|
|
||||||
paddingLeft: 0,
|
|
||||||
paddingRight: theme.spacing(1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyleApplicationContainer = styled(Box)(({ theme }) => ({
|
|
||||||
marginBottom: theme.spacing(18),
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledApplicationBox = styled(Box)<{
|
|
||||||
mode: 'success' | 'warning';
|
|
||||||
}>(({ theme, mode }) => ({
|
|
||||||
borderRadius: theme.shape.borderRadiusMedium,
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor: theme.palette[mode].border,
|
|
||||||
backgroundColor: theme.palette[mode].light,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: theme.spacing(1.5, 3, 2, 3),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledStatus = styled(Typography)<{
|
|
||||||
mode: 'success' | 'warning';
|
|
||||||
}>(({ theme, mode }) => ({
|
|
||||||
gap: theme.spacing(1),
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
color: theme.palette[mode].dark,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledEnvironmentBox = styled(Box)<{
|
|
||||||
mode: 'success' | 'warning';
|
|
||||||
}>(({ theme, mode }) => ({
|
|
||||||
borderRadius: theme.shape.borderRadiusMedium,
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor:
|
|
||||||
theme.palette[mode === 'success' ? 'secondary' : 'warning'].border,
|
|
||||||
backgroundColor:
|
|
||||||
theme.palette[mode === 'success' ? 'secondary' : 'warning'].light,
|
|
||||||
display: 'inline-block',
|
|
||||||
padding: theme.spacing(1.5, 1.5, 1.5, 1.5),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledDivider = styled(Divider)(({ theme }) => ({
|
const StyledDivider = styled(Divider)(({ theme }) => ({
|
||||||
marginTop: theme.spacing(2),
|
marginTop: theme.spacing(2),
|
||||||
@ -75,203 +15,54 @@ const StyledDivider = styled(Divider)(({ theme }) => ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledEnvironmentsContainer = styled(Box)({
|
const ApplicationContainer = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'start',
|
padding: theme.spacing(1),
|
||||||
gap: '20px',
|
flexDirection: 'column',
|
||||||
});
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing(2),
|
||||||
const EnvironmentHeader = styled(Typography)(({ theme }) => ({
|
alignSelf: 'stretch',
|
||||||
fontSize: theme.fontSizes.smallerBody,
|
|
||||||
fontWeight: theme.fontWeight.bold,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const SuccessStatus = () => (
|
const ProjectContainer = styled(Box)(({ theme }) => ({
|
||||||
<StyledStatus mode='success'>
|
display: 'flex',
|
||||||
<WarningAmberRounded
|
alignItems: 'center',
|
||||||
sx={(theme) => ({
|
gap: theme.spacing(2),
|
||||||
color: theme.palette.success.main,
|
alignSelf: 'stretch',
|
||||||
})}
|
}));
|
||||||
/>{' '}
|
|
||||||
Everything looks good!
|
|
||||||
</StyledStatus>
|
|
||||||
);
|
|
||||||
|
|
||||||
const WarningStatus: FC = ({ children }) => (
|
const ApplicationOverview = () => {
|
||||||
<StyledStatus mode='warning'>
|
|
||||||
<WarningAmberRounded
|
|
||||||
sx={(theme) => ({
|
|
||||||
color: theme.palette.warning.main,
|
|
||||||
})}
|
|
||||||
/>{' '}
|
|
||||||
{children}
|
|
||||||
</StyledStatus>
|
|
||||||
);
|
|
||||||
|
|
||||||
const useElementWidth = () => {
|
|
||||||
const elementRef = useRef<HTMLDivElement>(null);
|
|
||||||
const [width, setWidth] = useState('100%');
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
setWidth(`${elementRef.current?.scrollWidth}px`);
|
|
||||||
}, [elementRef, setWidth]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
elementRef,
|
|
||||||
width,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ApplicationOverview = () => {
|
|
||||||
usePageTitle('Applications - Overview');
|
usePageTitle('Applications - Overview');
|
||||||
const applicationName = useRequiredPathParam('name');
|
const applicationName = useRequiredPathParam('name');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const theme = useTheme();
|
|
||||||
const { data, loading } = useApplicationOverview(applicationName);
|
const { data, loading } = useApplicationOverview(applicationName);
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
window.navigateToInstances = (environment: string) => {
|
|
||||||
navigate(
|
|
||||||
`/applications/${applicationName}/instances?environment=${environment}`,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { elementRef, width } = useElementWidth();
|
|
||||||
|
|
||||||
const mode: 'success' | 'warning' =
|
|
||||||
data.issues.length === 0 ? 'success' : 'warning';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={!loading && data.environments.length === 0}
|
condition={!loading && data.environments.length === 0}
|
||||||
show={<Alert severity='warning'>No data available.</Alert>}
|
show={<Alert severity='warning'>No data available.</Alert>}
|
||||||
elseShow={
|
elseShow={
|
||||||
<>
|
<ApplicationContainer>
|
||||||
|
<ProjectContainer>
|
||||||
|
Projects using this application
|
||||||
|
{data.projects.map((project) => (
|
||||||
|
<Badge
|
||||||
|
sx={{ cursor: 'pointer' }}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
navigate(`/projects/${project}`);
|
||||||
|
}}
|
||||||
|
color='secondary'
|
||||||
|
icon={<TopicOutlinedIcon />}
|
||||||
|
>
|
||||||
|
{project}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</ProjectContainer>
|
||||||
|
<StyledDivider />
|
||||||
<ApplicationIssues issues={data.issues} />
|
<ApplicationIssues issues={data.issues} />
|
||||||
<Box sx={{ width }}>
|
<ApplicationChart data={data} />
|
||||||
<ArcherContainer
|
</ApplicationContainer>
|
||||||
strokeColor={theme.palette.secondary.border}
|
|
||||||
endMarker={false}
|
|
||||||
>
|
|
||||||
<StyleApplicationContainer>
|
|
||||||
<ArcherElement
|
|
||||||
id='application'
|
|
||||||
relations={data.environments.map(
|
|
||||||
(environment) => ({
|
|
||||||
targetId: environment.name,
|
|
||||||
targetAnchor: 'top',
|
|
||||||
sourceAnchor: 'bottom',
|
|
||||||
style: {
|
|
||||||
strokeColor:
|
|
||||||
mode === 'success'
|
|
||||||
? theme.palette
|
|
||||||
.secondary.border
|
|
||||||
: theme.palette.warning
|
|
||||||
.border,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<StyledApplicationBox mode={mode}>
|
|
||||||
<Typography
|
|
||||||
sx={(theme) => ({
|
|
||||||
fontSize:
|
|
||||||
theme.fontSizes.smallerBody,
|
|
||||||
})}
|
|
||||||
color='text.secondary'
|
|
||||||
>
|
|
||||||
Application
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
sx={(theme) => ({
|
|
||||||
fontSize:
|
|
||||||
theme.fontSizes.bodySize,
|
|
||||||
fontWeight:
|
|
||||||
theme.fontWeight.bold,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{applicationName}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<StyledDivider />
|
|
||||||
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={mode === 'success'}
|
|
||||||
show={<SuccessStatus />}
|
|
||||||
elseShow={
|
|
||||||
<WarningStatus>
|
|
||||||
{data.issues.length} issues
|
|
||||||
detected
|
|
||||||
</WarningStatus>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</StyledApplicationBox>
|
|
||||||
</ArcherElement>
|
|
||||||
</StyleApplicationContainer>
|
|
||||||
|
|
||||||
<StyledEnvironmentsContainer ref={elementRef}>
|
|
||||||
{data.environments.map((environment) => (
|
|
||||||
<ArcherElement
|
|
||||||
id={environment.name}
|
|
||||||
key={environment.name}
|
|
||||||
>
|
|
||||||
<StyledEnvironmentBox
|
|
||||||
mode={mode}
|
|
||||||
key={environment.name}
|
|
||||||
>
|
|
||||||
<EnvironmentHeader>
|
|
||||||
{environment.name} environment
|
|
||||||
</EnvironmentHeader>
|
|
||||||
|
|
||||||
<StyledTable>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<StyledCell>
|
|
||||||
Instances:
|
|
||||||
</StyledCell>
|
|
||||||
<StyledCell>
|
|
||||||
{
|
|
||||||
environment.instanceCount
|
|
||||||
}
|
|
||||||
</StyledCell>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<StyledCell>
|
|
||||||
SDK:
|
|
||||||
</StyledCell>
|
|
||||||
<StyledCell>
|
|
||||||
{environment.sdks.map(
|
|
||||||
(sdk) => (
|
|
||||||
<div
|
|
||||||
key={
|
|
||||||
sdk
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{sdk}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</StyledCell>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<StyledCell>
|
|
||||||
Last seen:
|
|
||||||
</StyledCell>
|
|
||||||
<StyledCell>
|
|
||||||
{
|
|
||||||
environment.lastSeen
|
|
||||||
}
|
|
||||||
</StyledCell>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</StyledTable>
|
|
||||||
</StyledEnvironmentBox>
|
|
||||||
</ArcherElement>
|
|
||||||
))}
|
|
||||||
</StyledEnvironmentsContainer>
|
|
||||||
</ArcherContainer>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user