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

feat: review your draft sidebar (#2305)

* refactor playground status chip component

* fix: update change request sidebar

* refactor: status badge cleanup

* fix: prettier formatting
This commit is contained in:
Tymoteusz Czech 2022-11-02 16:05:27 +01:00 committed by GitHub
parent 2f1f9cecc2
commit 0a855604af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 107 additions and 676 deletions

View File

@ -4,18 +4,18 @@ import { ChangeRequestFeatureToggleChange } from '../ChangeRequestOverview/Chang
import { objectId } from 'utils/objectId';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ToggleStatusChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ToggleStatusChange';
import type { IChangeRequestResponse } from 'hooks/api/getters/useChangeRequestDraft/useChangeRequestDraft';
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
import { formatUnknownError } from 'utils/formatUnknownError';
import useToast from 'hooks/useToast';
import type { IChangeRequest } from '../changeRequest.types';
interface IChangeRequest {
changeRequest: IChangeRequestResponse;
interface IChangeRequestProps {
changeRequest: IChangeRequest;
onRefetch?: () => void;
onNavigate?: () => void;
}
export const ChangeRequest: VFC<IChangeRequest> = ({
export const ChangeRequest: VFC<IChangeRequestProps> = ({
changeRequest,
onRefetch,
onNavigate,
@ -41,7 +41,6 @@ export const ChangeRequest: VFC<IChangeRequest> = ({
return (
<Box>
Changes
{changeRequest.features?.map(featureToggleChange => (
<ChangeRequestFeatureToggleChange
key={featureToggleChange.name}
@ -55,6 +54,7 @@ export const ChangeRequest: VFC<IChangeRequest> = ({
condition={change.action === 'updateEnabled'}
show={
<ToggleStatusChange
// @ts-expect-error TODO: fix types
enabled={change?.payload?.enabled}
onDiscard={onDiscard(change.id)}
/>

View File

@ -1,7 +1,7 @@
import { VFC } from 'react';
import { Link, Box, Typography } from '@mui/material';
import { PlaygroundResultChip } from 'component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip';
import { Link, Box } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Badge } from 'component/common/Badge/Badge';
interface IPlaygroundResultsTable {
enabled: boolean;
@ -16,11 +16,9 @@ export const ToggleStatusChange: VFC<IPlaygroundResultsTable> = ({
<Box sx={{ p: 1, display: 'flex', justifyContent: 'space-between' }}>
<Box>
New status:{' '}
<PlaygroundResultChip
showIcon={false}
label={enabled ? ' Enabled' : 'Disabled'}
enabled={enabled}
/>
<Badge color={enabled ? 'success' : 'error'}>
{enabled ? ' Enabled' : 'Disabled'}
</Badge>
</Box>
<ConditionallyRender
condition={Boolean(onDiscard)}

View File

@ -2,8 +2,8 @@ import { Box } from '@mui/material';
import { FC } from 'react';
import { Typography } from '@mui/material';
import TimeAgo from 'react-timeago';
import { resolveChangeRequestStatusIcon } from 'component/changeRequest/changeRequest.utils';
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
import { ChangeRequestStatusBadge } from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge';
import {
StyledPaper,
StyledContainer,
@ -22,7 +22,7 @@ export const ChangeRequestHeader: FC<{ changeRequest: IChangeRequest }> = ({
<StyledHeader variant="h1">
Change request #{changeRequest.id}
</StyledHeader>
{resolveChangeRequestStatusIcon(changeRequest.state)}
<ChangeRequestStatusBadge state={changeRequest.state} />;
</StyledContainer>
<StyledInnerContainer>
<Typography variant="body2" sx={{ margin: 'auto 0' }}>

View File

@ -65,6 +65,7 @@ export const ChangeRequestOverview: FC = () => {
padding: theme.spacing(2),
})}
>
Changes
<ChangeRequest changeRequest={changeRequest} />
<ChangeRequestReviewStatus
approved={
@ -72,7 +73,6 @@ export const ChangeRequestOverview: FC = () => {
changeRequest.state === 'Applied'
}
/>
<Button
variant="contained"
sx={{ marginTop: 2 }}

View File

@ -1,19 +1,29 @@
import { VFC } from 'react';
import { Box, Button, Typography, styled, Tooltip } from '@mui/material';
import {
Box,
Button,
Typography,
styled,
Tooltip,
Divider,
} from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { HelpOutline } from '@mui/icons-material';
import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon';
import { ChangeRequest } from '../ChangeRequest/ChangeRequest';
import { useChangeRequestDraft } from 'hooks/api/getters/useChangeRequestDraft/useChangeRequestDraft';
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
import { ChangeRequestStatusBadge } from '../ChangeRequestStatusBadge/ChangeRequestStatusBadge';
interface IChangeRequestSidebarProps {
open: boolean;
project: string;
onClose: () => void;
}
const StyledPageContent = styled(PageContent)(({ theme }) => ({
height: '100vh',
overflow: 'auto',
@ -41,6 +51,11 @@ const StyledHeaderHint = styled('div')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
}));
const BackButton = styled(Button)(({ theme }) => ({
marginTop: theme.spacing(2),
marginLeft: 'auto',
}));
export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
open,
project,
@ -85,6 +100,7 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
>
There are no changes to review.
{/* FIXME: empty state */}
<BackButton onClick={onClose}>Close</BackButton>
</StyledPageContent>
</SidebarModal>
);
@ -126,13 +142,23 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
`${theme.shape.borderRadiusLarge}px`,
}}
>
<Typography>
env: {environmentChangeRequest?.environment}
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: 'flex' }}>
<EnvironmentIcon enabled={true} />
<Typography component="span" variant="h2">
{environmentChangeRequest?.environment}
</Typography>
</Box>
<Box sx={{ ml: 'auto' }}>
<ChangeRequestStatusBadge
state={environmentChangeRequest?.state}
/>
</Box>
</Box>
<Divider sx={{ my: 3 }} />
<Typography variant="body1" color="text.secondary">
You request changes for these feature toggles:
</Typography>
<Typography>
state: {environmentChangeRequest?.state}
</Typography>
<hr />
<ChangeRequest
changeRequest={environmentChangeRequest}
onNavigate={() => {
@ -144,20 +170,21 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
<ConditionallyRender
condition={
environmentChangeRequest?.state ===
'APPROVED'
}
show={<Typography>Applied</Typography>}
/>
<ConditionallyRender
condition={
environmentChangeRequest?.state === 'CLOSED'
'Approved'
}
show={<Typography>Applied</Typography>}
/>
<ConditionallyRender
condition={
environmentChangeRequest?.state ===
'APPROVED'
'Applied'
}
show={<Typography>Applied</Typography>}
/>
<ConditionallyRender
condition={
environmentChangeRequest?.state ===
'Approved'
}
show={
<>
@ -201,6 +228,7 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
</Box>
</Box>
))}
<BackButton onClick={onClose}>Close</BackButton>
</StyledPageContent>
</SidebarModal>
);

View File

@ -1,18 +1,26 @@
import { ChangeRequestState } from './changeRequest.types';
import { VFC } from 'react';
import { ChangeRequestState } from '../changeRequest.types';
import { Badge } from 'component/common/Badge/Badge';
import { Check, CircleOutlined, Close } from '@mui/icons-material';
export const resolveChangeRequestStatusIcon = (state: ChangeRequestState) => {
const reviewRequired = (
<Badge color="secondary" icon={<CircleOutlined fontSize={'small'} />}>
Review required
</Badge>
);
interface IChangeRequestStatusBadgeProps {
state: ChangeRequestState;
}
const ReviewRequiredBadge: VFC = () => (
<Badge color="secondary" icon={<CircleOutlined fontSize={'small'} />}>
Review required
</Badge>
);
export const ChangeRequestStatusBadge: VFC<IChangeRequestStatusBadgeProps> = ({
state,
}) => {
switch (state) {
case 'Draft':
return reviewRequired;
return <ReviewRequiredBadge />;
case 'In review':
return reviewRequired;
return <ReviewRequiredBadge />;
case 'Approved':
return (
<Badge color="success" icon={<Check fontSize={'small'} />}>
@ -35,6 +43,6 @@ export const resolveChangeRequestStatusIcon = (state: ChangeRequestState) => {
</Badge>
);
default:
return reviewRequired;
return <ReviewRequiredBadge />;
}
};

View File

@ -1,10 +1,10 @@
import { VFC } from 'react';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import { resolveChangeRequestStatusIcon } from 'component/changeRequest/changeRequest.utils';
import { ChangeRequestState } from 'component/changeRequest/changeRequest.types';
import { ChangeRequestStatusBadge } from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge';
interface IChangeRequestStatusCellProps {
value?: string | null;
value?: string | null; // FIXME: proper type
}
export const ChangeRequestStatusCell: VFC<IChangeRequestStatusCellProps> = ({
@ -12,7 +12,7 @@ export const ChangeRequestStatusCell: VFC<IChangeRequestStatusCellProps> = ({
}) => {
const renderState = () => {
if (!value) return null;
return resolveChangeRequestStatusIcon(value as ChangeRequestState);
return <ChangeRequestStatusBadge state={value as ChangeRequestState} />;
};
if (!value) {

View File

@ -1,12 +1,15 @@
import { Chip } from '@mui/material';
import { useStyles } from './StatusChip.styles';
import { useStyles } from './FeatureStatusChip.styles';
interface IStatusChip {
stale: boolean;
showActive?: boolean;
}
const StatusChip = ({ stale, showActive = true }: IStatusChip) => {
export const FeatureStatusChip = ({
stale,
showActive = true,
}: IStatusChip) => {
const { classes: styles } = useStyles();
if (!stale && !showActive) {
@ -30,5 +33,3 @@ const StatusChip = ({ stale, showActive = true }: IStatusChip) => {
</div>
);
};
export default StatusChip;

View File

@ -1,208 +0,0 @@
import { memo } from 'react';
import classnames from 'classnames';
import { Link } from 'react-router-dom';
import { Chip, ListItem, Tooltip } from '@mui/material';
import { Undo } from '@mui/icons-material';
import TimeAgo from 'react-timeago';
import { IAccessContext } from 'contexts/AccessContext';
import StatusChip from 'component/common/StatusChip/StatusChip';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
import { IFlags } from 'interfaces/uiConfig';
import { getTogglePath } from 'utils/routePathHelpers';
import FeatureStatus from 'component/feature/FeatureView/FeatureStatus/FeatureStatus';
import FeatureType from 'component/feature/FeatureView/FeatureType/FeatureType';
import useProjects from 'hooks/api/getters/useProjects/useProjects';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { FeatureSchema } from 'openapi';
import { styles as themeStyles } from 'component/common';
import { useStyles } from './styles';
interface IFeatureToggleListItemProps {
feature: FeatureSchema;
onRevive?: (id: string) => void;
hasAccess: IAccessContext['hasAccess'];
flags?: IFlags;
inProject?: boolean;
className?: string;
}
/**
* @deprecated
*/
export const FeatureToggleListItem = memo<IFeatureToggleListItemProps>(
({
feature,
onRevive,
hasAccess,
flags = {},
inProject,
className,
...rest
}) => {
const { classes: styles } = useStyles();
const { projects } = useProjects();
const isArchive = Boolean(onRevive);
const {
name,
description,
type,
stale,
createdAt,
project,
lastSeenAt,
} = feature;
const projectExists = () => {
let projectExist = projects.find(proj => proj.id === project);
if (projectExist) {
return true;
}
return false;
};
const reviveFeature = () => {
if (projectExists() && onRevive) {
onRevive(feature.name);
}
};
return (
<ListItem
{...rest}
className={classnames(styles.listItem, className)}
>
<span className={styles.listItemMetric}>
<FeatureStatus
lastSeenAt={lastSeenAt}
tooltipPlacement="left"
/>
</span>
<span
className={classnames(
styles.listItemType,
themeStyles.hideLt600
)}
>
<FeatureType type={type as string} />
</span>
<span className={classnames(styles.listItemLink)}>
<ConditionallyRender
condition={!isArchive}
show={
<Link
to={getTogglePath(feature.project!, name)}
className={classnames(
themeStyles.listLink,
themeStyles.truncate
)}
>
<Tooltip title={description || ''} arrow>
<span className={themeStyles.toggleName}>
{name}&nbsp;
</span>
</Tooltip>
{/* <span className={styles.listItemToggle}></span> */}
<span></span>
<small>
<ConditionallyRender
condition={Boolean(createdAt)}
show={() => (
<TimeAgo
date={createdAt as Date}
live={false}
/>
)}
/>
</small>
<div>
<span className={themeStyles.truncate}>
<small>{description}</small>
</span>
</div>
</Link>
}
elseShow={
<>
<Tooltip title={description || ''} arrow>
<span className={themeStyles.toggleName}>
{name}&nbsp;{' '}
</span>
</Tooltip>
{/* <span className={styles.listItemToggle}></span> */}
<span></span>
<small>
<ConditionallyRender
condition={Boolean(createdAt)}
show={() => (
<TimeAgo
date={createdAt as Date}
live={false}
/>
)}
/>
</small>
<div>
<span className={themeStyles.truncate}>
<small>{description}</small>
</span>
</div>
</>
}
/>
</span>
<span
className={classnames(
styles.listItemStrategies,
themeStyles.hideLt920
)}
>
<StatusChip stale={Boolean(stale)} showActive={false} />
<ConditionallyRender
condition={!inProject}
show={
<Link
to={`/projects/${project}`}
style={{ textDecoration: 'none' }}
className={classnames({
[`${styles.disabledLink}`]:
!projectExists(),
})}
>
<Chip
color="primary"
variant="outlined"
style={{
marginLeft: '8px',
cursor: 'pointer',
}}
title={`Project: ${project}`}
label={project}
/>
</Link>
}
/>
</span>
<ConditionallyRender
condition={isArchive}
show={
<PermissionIconButton
permission={UPDATE_FEATURE}
projectId={project}
disabled={
!hasAccess(UPDATE_FEATURE, project) ||
!projectExists()
}
onClick={reviveFeature}
tooltipProps={{ title: 'Revive feature toggle' }}
>
<Undo />
</PermissionIconButton>
}
/>
</ListItem>
);
}
);

View File

@ -1,291 +0,0 @@
// Vitest Snapshot v1
exports[`renders correctly with one feature 1`] = `
[
<li
className="MuiListItem-root makeStyles-listItem-1 MuiListItem-gutters"
disabled={false}
>
<span
className="makeStyles-listItemMetric-2"
>
<div
aria-describedby={null}
className="makeStyles-container-8"
onBlur={[Function]}
onFocus={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
style={
{
"background": "#EDF0F1",
"fontSize": "0.8rem",
}
}
title="No usage reported from connected applications"
>
<span
style={
{
"fontSize": "1.4rem",
}
}
>
</span>
</div>
</span>
<span
className="makeStyles-listItemType-3 _hideLt600_1yorl_42"
>
<svg
aria-describedby={null}
aria-hidden={true}
className="MuiSvgIcon-root makeStyles-icon-9"
data-loading={true}
focusable="false"
onBlur={[Function]}
onFocus={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
title="This is a \\"\\" toggle"
viewBox="0 0 24 24"
>
<path
d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"
/>
</svg>
</span>
<span
className="makeStyles-listItemLink-5"
>
<a
className="_listLink_1yorl_25 _truncate_1yorl_2"
href="/projects/default/features/Another"
onClick={[Function]}
>
<span
aria-describedby={null}
className="_toggleName_1yorl_84"
onBlur={[Function]}
onFocus={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
title="another's description"
>
Another
 
</span>
<span />
<small>
<time
dateTime="2018-02-04T20:27:52.127Z"
title="2018-02-04T20:27:52.127Z"
>
4 years ago
</time>
</small>
<div>
<span
className="_truncate_1yorl_2"
>
<small>
another's description
</small>
</span>
</div>
</a>
</span>
<span
className="makeStyles-listItemStrategies-6 _hideLt920_1yorl_37"
>
<a
className="makeStyles-disabledLink-7"
href="/projects/default"
onClick={[Function]}
style={
{
"textDecoration": "none",
}
}
>
<div
className="MuiChip-root MuiChip-colorPrimary MuiChip-outlined MuiChip-outlinedPrimary"
onKeyDown={[Function]}
onKeyUp={[Function]}
style={
{
"cursor": "pointer",
"marginLeft": "8px",
}
}
title="Project: default"
>
<span
className="MuiChip-label"
>
default
</span>
</div>
</a>
</span>
</li>,
<div
aria-atomic={true}
aria-live="polite"
className="makeStyles-container-11"
data-testid="ANNOUNCER_ELEMENT_TEST_ID"
role="status"
/>,
]
`;
exports[`renders correctly with one feature without permission 1`] = `
[
<li
className="MuiListItem-root makeStyles-listItem-1 MuiListItem-gutters"
disabled={false}
>
<span
className="makeStyles-listItemMetric-2"
>
<div
aria-describedby={null}
className="makeStyles-container-8"
onBlur={[Function]}
onFocus={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
style={
{
"background": "#EDF0F1",
"fontSize": "0.8rem",
}
}
title="No usage reported from connected applications"
>
<span
style={
{
"fontSize": "1.4rem",
}
}
>
</span>
</div>
</span>
<span
className="makeStyles-listItemType-3 _hideLt600_1yorl_42"
>
<svg
aria-describedby={null}
aria-hidden={true}
className="MuiSvgIcon-root makeStyles-icon-9"
data-loading={true}
focusable="false"
onBlur={[Function]}
onFocus={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
title="This is a \\"\\" toggle"
viewBox="0 0 24 24"
>
<path
d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"
/>
</svg>
</span>
<span
className="makeStyles-listItemLink-5"
>
<a
className="_listLink_1yorl_25 _truncate_1yorl_2"
href="/projects/undefined/features/Another"
onClick={[Function]}
>
<span
aria-describedby={null}
className="_toggleName_1yorl_84"
onBlur={[Function]}
onFocus={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
title="another's description"
>
Another
 
</span>
<span />
<small>
<time
dateTime="2018-02-04T20:27:52.127Z"
title="2018-02-04T20:27:52.127Z"
>
4 years ago
</time>
</small>
<div>
<span
className="_truncate_1yorl_2"
>
<small>
another's description
</small>
</span>
</div>
</a>
</span>
<span
className="makeStyles-listItemStrategies-6 _hideLt920_1yorl_37"
>
<a
className="makeStyles-disabledLink-7"
href="/projects/undefined"
onClick={[Function]}
style={
{
"textDecoration": "none",
}
}
>
<div
className="MuiChip-root MuiChip-colorPrimary MuiChip-outlined MuiChip-outlinedPrimary"
onKeyDown={[Function]}
onKeyUp={[Function]}
style={
{
"cursor": "pointer",
"marginLeft": "8px",
}
}
title="Project: undefined"
>
<span
className="MuiChip-label"
/>
</div>
</a>
</span>
</li>,
<div
aria-atomic={true}
aria-live="polite"
className="makeStyles-container-11"
data-testid="ANNOUNCER_ELEMENT_TEST_ID"
role="status"
/>,
]
`;

View File

@ -1,37 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
listItem: {
padding: '0',
margin: '1rem 0',
'&:hover': {
backgroundColor: theme.palette.grey[200],
},
},
listItemMetric: {
width: '40px',
marginRight: '0.25rem',
flexShrink: 0,
},
listItemType: {
width: '40px',
textAlign: 'center',
marginRight: '0',
flexShrink: 0,
},
listItemSvg: {
fill: theme.palette.grey[300],
},
listItemLink: {
marginLeft: '0.25rem',
minWidth: '0',
},
listItemStrategies: {
marginLeft: 'auto',
display: 'flex',
alignItems: 'center',
},
disabledLink: {
pointerEvents: 'none',
},
}));

View File

@ -86,7 +86,7 @@ const FeatureOverviewEnvSwitch = ({
const toggleEnvironment = async (e: React.ChangeEvent) => {
if (uiConfig?.flags?.changeRequests && env.name === 'production') {
e.preventDefault();
onChangeRequestToggle(featureId, env.name, env.enabled);
onChangeRequestToggle(featureId, env.name, !env.enabled);
return;
}
if (env.enabled) {

View File

@ -26,7 +26,7 @@ import useLoading from 'hooks/useLoading';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { FeatureStaleDialog } from 'component/common/FeatureStaleDialog/FeatureStaleDialog';
import AddTagDialog from './FeatureOverview/AddTagDialog/AddTagDialog';
import StatusChip from 'component/common/StatusChip/StatusChip';
import { FeatureStatusChip } from 'component/common/FeatureStatusChip/FeatureStatusChip';
import { FeatureNotFound } from 'component/feature/FeatureView/FeatureNotFound/FeatureNotFound';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { FeatureArchiveDialog } from '../../common/FeatureArchiveDialog/FeatureArchiveDialog';
@ -111,7 +111,7 @@ export const FeatureView = () => {
<ConditionallyRender
condition={!smallScreen}
show={
<StatusChip
<FeatureStatusChip
stale={feature?.stale}
/>
}

View File

@ -1,10 +1,10 @@
import { VFC } from 'react';
import { Chip, styled, useTheme } from '@mui/material';
import { colors } from 'themes/colors';
import { useTheme } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ReactComponent as FeatureEnabledIcon } from 'assets/icons/isenabled-true.svg';
import { ReactComponent as FeatureDisabledIcon } from 'assets/icons/isenabled-false.svg';
import { WarningOutlined } from '@mui/icons-material';
import { Badge } from 'component/common/Badge/Badge';
interface IResultChipProps {
enabled: boolean | 'unevaluated' | 'unknown';
@ -13,51 +13,6 @@ interface IResultChipProps {
showIcon?: boolean;
}
export const StyledChip = styled(Chip)(({ theme, icon }) => ({
padding: theme.spacing(0, 1),
height: 24,
borderRadius: theme.shape.borderRadius,
fontWeight: theme.typography.fontWeightMedium,
['& .MuiChip-label']: {
padding: 0,
paddingLeft: Boolean(icon) ? theme.spacing(0.5) : 0,
},
}));
export const StyledFalseChip = styled(StyledChip)(({ theme }) => ({
border: `1px solid ${theme.palette.error.main}`,
backgroundColor: colors.red['200'],
['& .MuiChip-label']: {
color: theme.palette.error.main,
},
['& .MuiChip-icon']: {
color: theme.palette.error.main,
},
}));
export const StyledTrueChip = styled(StyledChip)(({ theme }) => ({
border: `1px solid ${theme.palette.success.main}`,
backgroundColor: colors.green['100'],
['& .MuiChip-label']: {
color: theme.palette.success.main,
},
['& .MuiChip-icon']: {
color: theme.palette.success.main,
marginRight: 0,
},
}));
export const StyledUnknownChip = styled(StyledChip)(({ theme }) => ({
border: `1px solid ${theme.palette.warning.main}`,
backgroundColor: colors.orange['100'],
['& .MuiChip-label']: {
color: theme.palette.warning.main,
},
['& .MuiChip-icon']: {
color: theme.palette.warning.main,
},
}));
export const PlaygroundResultChip: VFC<IResultChipProps> = ({
enabled,
label,
@ -92,25 +47,25 @@ export const PlaygroundResultChip: VFC<IResultChipProps> = ({
<ConditionallyRender
condition={enabled === 'unknown' || enabled === 'unevaluated'}
show={
<StyledUnknownChip
icon={showIcon ? icon : undefined}
label={label}
/>
<Badge icon={showIcon ? icon : undefined} color="warning">
{label}
</Badge>
}
elseShow={
<ConditionallyRender
condition={typeof enabled === 'boolean' && Boolean(enabled)}
show={
<StyledTrueChip
<Badge
color="success"
icon={showIcon ? icon : undefined}
label={label}
/>
>
{label}
</Badge>
}
elseShow={
<StyledFalseChip
icon={showIcon ? icon : undefined}
label={label}
/>
<Badge color="error" icon={showIcon ? icon : undefined}>
{label}
</Badge>
}
/>
}

View File

@ -2,37 +2,10 @@ import useSWR from 'swr';
import { useMemo } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
interface IChange {
id: number;
action: string;
payload: {
enabled: boolean; // FIXME: add other action types
};
createdAt: Date;
createdBy: {
id: number;
username?: any;
imageUrl?: any;
};
}
export interface IChangeRequestResponse {
id: number;
environment: string;
state: string;
project: string;
createdBy: {
id: number;
username?: any;
imageUrl?: any;
};
createdAt: Date;
features: Array<{
name: string;
changes: IChange[];
}>;
}
import {
ChangeRequestState,
IChangeRequest,
} from 'component/changeRequest/changeRequest.types';
const fetcher = (path: string) => {
return fetch(path)
@ -41,7 +14,7 @@ const fetcher = (path: string) => {
};
export const useChangeRequestDraft = (project: string) => {
const { data, error, mutate } = useSWR<IChangeRequestResponse[]>(
const { data, error, mutate } = useSWR<IChangeRequest[]>(
formatApiPath(`api/admin/projects/${project}/change-requests/draft`),
fetcher
);

View File

@ -28,6 +28,10 @@ export default createTheme({
fontSize: '1.5rem',
lineHeight: 1.875,
},
h2: {
fontSize: `${20 / 16}rem`,
fontWeight: '700',
},
h3: {
fontSize: '1rem',
fontWeight: '700',