mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01: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:
		
							parent
							
								
									2f1f9cecc2
								
							
						
					
					
						commit
						0a855604af
					
				| @ -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)} | ||||
|                                     /> | ||||
|  | ||||
| @ -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)} | ||||
|  | ||||
| @ -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' }}> | ||||
|  | ||||
| @ -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 }} | ||||
|  | ||||
| @ -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> | ||||
|     ); | ||||
|  | ||||
| @ -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 />; | ||||
|     } | ||||
| }; | ||||
| @ -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) { | ||||
|  | ||||
| @ -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; | ||||
| @ -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}  | ||||
|                                     </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} {' '} | ||||
|                                     </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> | ||||
|         ); | ||||
|     } | ||||
| ); | ||||
| @ -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" | ||||
|   />, | ||||
| ] | ||||
| `; | ||||
| @ -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', | ||||
|     }, | ||||
| })); | ||||
| @ -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) { | ||||
|  | ||||
| @ -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} | ||||
|                                             /> | ||||
|                                         } | ||||
|  | ||||
| @ -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> | ||||
|                     } | ||||
|                 /> | ||||
|             } | ||||
|  | ||||
| @ -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 | ||||
|     ); | ||||
|  | ||||
| @ -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', | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user