mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-18 01:18:23 +02:00
* refactor: update test deps * refactor: remove unused ts-expect-error annotations * refactor: add missing arg and return types * refactor: the loading prop is optional * refactor: add missing arg and return types * reafactor: fix value arg type * refactor: fix missing array type * refactor: the parameters field is an array * refactor: use undefined instead of null in state * refactor: add missing params type * refactor: add missing children prop * refactor: add missing array type * refactor: add missing React imports * refactor: use correct IProjectEnvironment type * refactor: type errors as unknown * refactor: the index prop is required * refactor: fix date prop type * refactor: fix tooltip placement prop type * refactor: fix environments state type * refactor: add missing arg types * refactor: add guard for undefined field * refactor: fix ChangePassword prop types * refactor: fix MUI import paths * refactor: add missing arg type * refactor: fix showDialog prop type * refactor: remove unused openUpdateDialog prop * refactor: add missing non-null assertion * refactor: remove unused types prop * refactor: stricten API error handler types * refactor: add missing undefined check * refactor: add missing IProject id field * refactor: fix ConditionallyRender condition prop types * refactor: remove unused args * refactor: add AddVariant prop types * refactor: add types to UIContext * refactor: fix event arg type * refactor: add missing default impressionData field * refactor: fix handleDeleteEnvironment prop args * refactor: fix IFeatureMetrics field requirements * refactor: add missing element types to ConditionallyRender * refactor: remove unused ProjectAccess projectId prop * refactor: add missing undefined check * refactor: fix getCreateTogglePath arg type * refactor: add missing IStrategyPayload import * refactor: remove unused user arg * refactor: add missing event arg type * refactor: add missing style object types * refactor: improve userApiErrors prop type * refactor: the Dialogue onClose prop is optional * refactor: fix the AddonEvents setEventValue prop type
216 lines
8.0 KiB
TypeScript
216 lines
8.0 KiB
TypeScript
import { useContext } from 'react';
|
|
import { Link, useParams } from 'react-router-dom';
|
|
import {
|
|
Grid,
|
|
List,
|
|
ListItem,
|
|
ListItemAvatar,
|
|
ListItemText,
|
|
Typography,
|
|
} from '@material-ui/core';
|
|
import {
|
|
Extension,
|
|
FlagRounded,
|
|
Report,
|
|
SvgIconComponent,
|
|
Timeline,
|
|
} from '@material-ui/icons';
|
|
import {
|
|
CREATE_FEATURE,
|
|
CREATE_STRATEGY,
|
|
} from '../../providers/AccessProvider/permissions';
|
|
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
|
|
import { getTogglePath } from '../../../utils/route-path-helpers';
|
|
import useApplication from '../../../hooks/api/getters/useApplication/useApplication';
|
|
import AccessContext from '../../../contexts/AccessContext';
|
|
import { formatDateYMDHMS } from '../../../utils/format-date';
|
|
import { useLocationSettings } from '../../../hooks/useLocationSettings';
|
|
|
|
export const ApplicationView = () => {
|
|
const { hasAccess } = useContext(AccessContext);
|
|
const { name } = useParams<{ name: string }>();
|
|
const { application } = useApplication(name);
|
|
const { locationSettings } = useLocationSettings();
|
|
const { instances, strategies, seenToggles } = application;
|
|
|
|
const notFoundListItem = ({
|
|
createUrl,
|
|
name,
|
|
permission,
|
|
}: {
|
|
createUrl: string;
|
|
name: string;
|
|
permission: string;
|
|
}) => (
|
|
<ConditionallyRender
|
|
key={`not_found_conditional_${name}`}
|
|
condition={hasAccess(permission)}
|
|
show={
|
|
<ListItem key={`not_found_${name}`}>
|
|
<ListItemAvatar>
|
|
<Report style={{ color: 'red' }} />
|
|
</ListItemAvatar>
|
|
<ListItemText
|
|
primary={<Link to={`${createUrl}`}>{name}</Link>}
|
|
secondary={'Missing, want to create?'}
|
|
/>
|
|
</ListItem>
|
|
}
|
|
elseShow={
|
|
<ListItem key={`not_found_${name}`}>
|
|
<ListItemAvatar>
|
|
<Report />
|
|
</ListItemAvatar>
|
|
<ListItemText
|
|
primary={name}
|
|
secondary={`Could not find feature toggle with name ${name}`}
|
|
/>
|
|
</ListItem>
|
|
}
|
|
/>
|
|
);
|
|
|
|
const foundListItem = ({
|
|
viewUrl,
|
|
name,
|
|
description,
|
|
Icon,
|
|
i,
|
|
}: {
|
|
viewUrl: string;
|
|
name: string;
|
|
description: string;
|
|
Icon: SvgIconComponent;
|
|
i: number;
|
|
}) => (
|
|
<ListItem key={`found_${name}-${i}`}>
|
|
<ListItemAvatar>
|
|
<Icon />
|
|
</ListItemAvatar>
|
|
<ListItemText
|
|
primary={
|
|
<Link
|
|
to={`${viewUrl}/${name}`}
|
|
style={{ wordBreak: 'break-all' }}
|
|
>
|
|
{name}
|
|
</Link>
|
|
}
|
|
secondary={description}
|
|
/>
|
|
</ListItem>
|
|
);
|
|
return (
|
|
<Grid container style={{ margin: 0 }}>
|
|
<Grid item xl={6} md={6} xs={12}>
|
|
<Typography variant="subtitle1" style={{ padding: '1rem 0' }}>
|
|
Toggles
|
|
</Typography>
|
|
<hr />
|
|
<List>
|
|
{seenToggles.map(
|
|
({ name, description, notFound, project }, i) => (
|
|
<ConditionallyRender
|
|
key={`toggle_conditional_${name}`}
|
|
condition={notFound}
|
|
show={notFoundListItem({
|
|
createUrl: `/projects/default/create-toggle?name=${name}`,
|
|
name,
|
|
permission: CREATE_FEATURE,
|
|
})}
|
|
elseShow={foundListItem({
|
|
viewUrl: getTogglePath(project, name),
|
|
name,
|
|
description,
|
|
Icon: FlagRounded,
|
|
i,
|
|
})}
|
|
/>
|
|
)
|
|
)}
|
|
</List>
|
|
</Grid>
|
|
<Grid item xl={6} md={6} xs={12}>
|
|
<Typography variant="subtitle1" style={{ padding: '1rem 0' }}>
|
|
Implemented strategies
|
|
</Typography>
|
|
<hr />
|
|
<List>
|
|
{strategies.map(
|
|
({ name, description, notFound }, i: number) => (
|
|
<ConditionallyRender
|
|
key={`strategies_conditional_${name}`}
|
|
condition={notFound}
|
|
show={notFoundListItem({
|
|
createUrl: '/strategies/create',
|
|
name,
|
|
permission: CREATE_STRATEGY,
|
|
})}
|
|
elseShow={foundListItem({
|
|
viewUrl: '/strategies/view',
|
|
name,
|
|
Icon: Extension,
|
|
description,
|
|
i,
|
|
})}
|
|
/>
|
|
)
|
|
)}
|
|
</List>
|
|
</Grid>
|
|
<Grid item xl={12} md={12}>
|
|
<Typography variant="subtitle1" style={{ padding: '1rem 0' }}>
|
|
{instances.length} Instances registered
|
|
</Typography>
|
|
<hr />
|
|
<List>
|
|
{instances.map(
|
|
({
|
|
instanceId,
|
|
clientIp,
|
|
lastSeen,
|
|
sdkVersion,
|
|
}: {
|
|
instanceId: string;
|
|
clientIp: string;
|
|
lastSeen: string;
|
|
sdkVersion: string;
|
|
}) => (
|
|
<ListItem key={`${instanceId}`}>
|
|
<ListItemAvatar>
|
|
<Timeline />
|
|
</ListItemAvatar>
|
|
<ListItemText
|
|
primary={
|
|
<ConditionallyRender
|
|
key={`${instanceId}_conditional`}
|
|
condition={Boolean(sdkVersion)}
|
|
show={
|
|
<span>
|
|
{instanceId} {sdkVersion}
|
|
</span>
|
|
}
|
|
elseShow={<span>{instanceId}</span>}
|
|
/>
|
|
}
|
|
secondary={
|
|
<span>
|
|
{clientIp} last seen at{' '}
|
|
<small>
|
|
{formatDateYMDHMS(
|
|
lastSeen,
|
|
locationSettings.locale
|
|
)}
|
|
</small>
|
|
</span>
|
|
}
|
|
/>
|
|
</ListItem>
|
|
)
|
|
)}
|
|
</List>
|
|
</Grid>
|
|
</Grid>
|
|
);
|
|
};
|