From 82701662862a6103a4d1662209ac96147166404c Mon Sep 17 00:00:00 2001 From: sellinjaanus <107852002+sellinjaanus@users.noreply.github.com> Date: Mon, 24 Oct 2022 19:15:22 +0300 Subject: [PATCH] Review your changes - approval flow (#2215) * Initial changes * Fix * continue styling changes review draft * fix: remove unused import * update flags snapshot Co-authored-by: sjaanus Co-authored-by: Tymoteusz Czech Co-authored-by: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> --- .../FeatureOverviewEnvironmentMetrics.tsx | 3 +- .../src/component/project/Project/Project.tsx | 10 - .../ChangesHeader/ChangesHeader.tsx | 37 --- .../ChangesetDiff/ChangeItem/ChangeItem.tsx | 20 ++ .../ChangesetDiff/ChangesetDiff.tsx | 88 ++++--- .../DraftBanner/DraftBanner.tsx | 11 +- .../SuggestedChanges/SuggestedChanges.tsx | 238 +++++++++--------- .../useChangeRequest/useChangeRequest.ts | 2 +- frontend/src/interfaces/suggestChangeset.ts | 8 +- .../__snapshots__/create-config.test.ts.snap | 2 + src/lib/types/experimental.ts | 5 + 11 files changed, 219 insertions(+), 205 deletions(-) delete mode 100644 frontend/src/component/project/Project/SuggestedChanges/ChangesHeader/ChangesHeader.tsx create mode 100644 frontend/src/component/project/Project/SuggestedChanges/ChangesetDiff/ChangeItem/ChangeItem.tsx diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics.tsx index d15c0e6760..b64e38b290 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics.tsx @@ -51,7 +51,8 @@ const FeatureOverviewEnvironmentMetrics = ({ data-loading > The feature has been requested 0 times and - exposed 0 times in the last hour + exposed + 0 times in the last hour

{ path: basePath, name: 'overview', }, - ...(uiConfig?.flags?.suggestChanges - ? [ - { - title: 'Suggested changes', - path: `${basePath}/changes`, - name: 'changes', - }, - ] - : []), { title: 'Health', path: `${basePath}/health`, @@ -228,7 +219,6 @@ const Project = () => { }} /> - } /> } /> } /> } /> diff --git a/frontend/src/component/project/Project/SuggestedChanges/ChangesHeader/ChangesHeader.tsx b/frontend/src/component/project/Project/SuggestedChanges/ChangesHeader/ChangesHeader.tsx deleted file mode 100644 index 7f56c16dc0..0000000000 --- a/frontend/src/component/project/Project/SuggestedChanges/ChangesHeader/ChangesHeader.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { VFC } from 'react'; -import { Box } from '@mui/material'; -import { useLocationSettings } from 'hooks/useLocationSettings'; -import { formatDateYMDHMS } from 'utils/formatDate'; -import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; - -interface IChangesHeaderProps { - author?: string; - avatar?: string; - createdAt?: string; -} - -export const ChangesHeader: VFC = ({ - author, - avatar, - createdAt, -}) => { - const { locationSettings } = useLocationSettings(); - return ( - - -
Suggestion by
-
- -
-
{author}
-
- Submitted at:{' '} - {formatDateYMDHMS(createdAt || 0, locationSettings.locale)} -
-
-
- ); -}; diff --git a/frontend/src/component/project/Project/SuggestedChanges/ChangesetDiff/ChangeItem/ChangeItem.tsx b/frontend/src/component/project/Project/SuggestedChanges/ChangesetDiff/ChangeItem/ChangeItem.tsx new file mode 100644 index 0000000000..1ec58ae4ff --- /dev/null +++ b/frontend/src/component/project/Project/SuggestedChanges/ChangesetDiff/ChangeItem/ChangeItem.tsx @@ -0,0 +1,20 @@ +import { VFC } from 'react'; +import { ISuggestChange } from 'interfaces/suggestChangeset'; +import { Box } from '@mui/system'; +import { PlaygroundResultChip } from 'component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip'; // FIXME: refactor - extract to common + +export const ChangeItem: VFC = ({ action, id, payload }) => { + if (action === 'updateEnabled') { + return ( + + New status:{' '} + + + ); + } + return Change with ID: {id}; +}; diff --git a/frontend/src/component/project/Project/SuggestedChanges/ChangesetDiff/ChangesetDiff.tsx b/frontend/src/component/project/Project/SuggestedChanges/ChangesetDiff/ChangesetDiff.tsx index 89bffcd469..62080da9ed 100644 --- a/frontend/src/component/project/Project/SuggestedChanges/ChangesetDiff/ChangesetDiff.tsx +++ b/frontend/src/component/project/Project/SuggestedChanges/ChangesetDiff/ChangesetDiff.tsx @@ -1,30 +1,60 @@ import { VFC } from 'react'; -import { Box, Paper, Typography, Card } from '@mui/material'; -import { PlaygroundResultChip } from 'component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip'; // FIXME: refactor - extract to common +import { Box, Typography, Card, styled } from '@mui/material'; import { ISuggestChange } from 'interfaces/suggestChangeset'; +import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon'; +import StringTruncator from 'component/common/StringTruncator/StringTruncator'; +import { PlaygroundResultChip } from 'component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip'; // FIXME: refactor - extract to common +import { ChangeItem } from './ChangeItem/ChangeItem'; type ChangesetDiffProps = { - changeset?: ISuggestChange[]; + changes?: ISuggestChange[]; + state: string; }; -export const ChangesetDiff: VFC = ({ - changeset: changeSet, -}) => ( - ({ + display: 'flex', + alignItems: 'center', + [theme.breakpoints.down(560)]: { + flexDirection: 'column', + textAlign: 'center', + }, + paddingBottom: theme.spacing(1), +})); + +export const ChangesetDiff: VFC = ({ changes, state }) => ( + theme.palette.dividerAlternative, + p: 3, + border: '2px solid', + borderColor: theme => theme.palette.playgroundBackground, display: 'flex', gap: 2, flexDirection: 'column', - borderRadius: theme => `${theme.shape.borderRadius}px`, + borderRadius: theme => `${theme.shape.borderRadiusExtraLarge}px`, }} > - Changes - {/*// @ts-ignore FIXME: types */} - {changeSet?.map(item => ( + + + + + + + + + + + You request changes for these feature toggles: + + {/* TODO: group by feature name */} + {changes?.map(item => ( = ({ {item.feature} - {/* - // @ts-ignore FIXME: types */} - {item?.changes?.map(change => { - if (change?.action === 'updateEnabled') { - return ( - - New status:{' '} - - - ); - } - return ( - - Change with ID: {change.id} - - ); - })} + ))} - + ); diff --git a/frontend/src/component/project/Project/SuggestedChanges/DraftBanner/DraftBanner.tsx b/frontend/src/component/project/Project/SuggestedChanges/DraftBanner/DraftBanner.tsx index b708ae6b72..01d1e9ce7c 100644 --- a/frontend/src/component/project/Project/SuggestedChanges/DraftBanner/DraftBanner.tsx +++ b/frontend/src/component/project/Project/SuggestedChanges/DraftBanner/DraftBanner.tsx @@ -1,8 +1,10 @@ -import { VFC } from 'react'; +import { useState, VFC } from 'react'; import { Box, Button, Typography } from '@mui/material'; import { useStyles as useAppStyles } from 'component/App.styles'; import WarningAmberIcon from '@mui/icons-material/WarningAmber'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { EditGroupUsers } from '../../../../admin/groups/Group/EditGroupUsers/EditGroupUsers'; +import { SuggestedChanges } from '../SuggestedChanges'; interface IDraftBannerProps { environment?: string; @@ -10,6 +12,7 @@ interface IDraftBannerProps { export const DraftBanner: VFC = ({ environment }) => { const { classes } = useAppStyles(); + const [reviewChangesOpen, setReviewChangesOpen] = useState(false); return ( = ({ environment }) => { + ); }; diff --git a/frontend/src/component/project/Project/SuggestedChanges/SuggestedChanges.tsx b/frontend/src/component/project/Project/SuggestedChanges/SuggestedChanges.tsx index 9a7f96a0e6..7388ee096d 100644 --- a/frontend/src/component/project/Project/SuggestedChanges/SuggestedChanges.tsx +++ b/frontend/src/component/project/Project/SuggestedChanges/SuggestedChanges.tsx @@ -1,4 +1,4 @@ -import { useState, VFC } from 'react'; +import React, { useState, VFC } from 'react'; import { Box, Paper, @@ -9,35 +9,61 @@ import { FormControl, FormControlLabel, RadioGroup, + styled, + Tooltip, } from '@mui/material'; import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ChangesetDiff } from './ChangesetDiff/ChangesetDiff'; -import { ChangesHeader } from './ChangesHeader/ChangesHeader'; +import { SidebarModal } from 'component/common/SidebarModal/SidebarModal'; +import { PageContent } from '../../../common/PageContent/PageContent'; +import { PageHeader } from '../../../common/PageHeader/PageHeader'; +import { HelpOutline } from '@mui/icons-material'; +interface ISuggestedChangesProps { + open: boolean; + setOpen: React.Dispatch>; +} -export const SuggestedChanges: VFC = () => { - const [anchorEl, setAnchorEl] = useState(null); +const StyledPageContent = styled(PageContent)(({ theme }) => ({ + height: '100vh', + overflow: 'auto', + padding: theme.spacing(7.5, 6), + [theme.breakpoints.down('md')]: { + padding: theme.spacing(4, 2), + }, + '& .header': { + padding: theme.spacing(0, 0, 2, 0), + }, + '& .body': { + padding: theme.spacing(3, 0, 0, 0), + }, + borderRadius: `${theme.spacing(1.5, 0, 0, 1.5)} !important`, +})); + +const StyledHelpOutline = styled(HelpOutline)(({ theme }) => ({ + fontSize: theme.fontSizes.mainHeader, + marginLeft: '0.3rem', + color: theme.palette.grey[700], +})); + +const StyledHeaderHint = styled('div')(({ theme }) => ({ + color: theme.palette.text.secondary, + fontSize: theme.fontSizes.smallBody, +})); + +export const SuggestedChanges: VFC = ({ + open, + setOpen, +}) => { const [selectedValue, setSelectedValue] = useState(''); const { data: changeRequest } = useChangeRequest(); - const onClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); + const onReview = async () => { + console.log('approve'); }; - const onClose = () => setAnchorEl(null); - - const onRadioChange = (event: React.ChangeEvent) => { - setSelectedValue((event.target as HTMLInputElement).value); - }; - - const onSubmit = async (e: any) => { - e.preventDefault(); - if (selectedValue === 'approve') { - console.log('approve'); - } else if (selectedValue === 'requestChanges') { - console.log('requestChanges'); - } - // show an error if no action was selected + const onDiscard = async () => { + console.log('discard'); }; const onApply = async () => { @@ -49,101 +75,89 @@ export const SuggestedChanges: VFC = () => { }; return ( - `${theme.shape.borderRadiusLarge}px`, + { + setOpen(false); }} + label="Review changes" > - {changeRequest?.state} - Environment: {changeRequest?.environment} -
- {/* */} -
- - Applied} - /> - - - - } - /> - - - - - - - } - label="Approve" - /> - } - label="Request changes" - /> - - - - - - + + + + Make sure you are sending the right changes + suggestions to be reviewed + + + } + > } - /> -
+ > + {/* TODO: multiple environments (changesets) */} + {changeRequest?.state} +
+ + + Applied} + /> + Applied} + /> + + + + } + /> + + + + + } + /> + + + ); }; diff --git a/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts b/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts index d647b910b2..6d4ac42873 100644 --- a/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts +++ b/frontend/src/hooks/api/getters/useChangeRequest/useChangeRequest.ts @@ -7,7 +7,7 @@ import handleErrorResponses from '../httpErrorResponseHandler'; const data: ISuggestChangeset = { id: 123, environment: 'production', - state: 'REVIEW', + state: 'CREATED', createdAt: new Date('2021-03-01T12:00:00.000Z'), project: 'default', createdBy: '123412341', diff --git a/frontend/src/interfaces/suggestChangeset.ts b/frontend/src/interfaces/suggestChangeset.ts index 31df60c4c2..68cf611155 100644 --- a/frontend/src/interfaces/suggestChangeset.ts +++ b/frontend/src/interfaces/suggestChangeset.ts @@ -1,6 +1,12 @@ export interface ISuggestChangeset { id: number; - state: string; + state: + | 'CREATED' + | 'UPDATED' + | 'SUBMITTED' + | 'APPROVED' + | 'REJECTED' + | 'CLOSED'; project: string; environment: string; createdBy?: string; diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index 7559a12f89..a8e9eb40c0 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -75,6 +75,7 @@ exports[`should create default config 1`] = ` "personalAccessTokens": false, "publicSignup": false, "responseTimeWithAppName": false, + "suggestChanges": false, "syncSSOGroups": false, }, }, @@ -89,6 +90,7 @@ exports[`should create default config 1`] = ` "personalAccessTokens": false, "publicSignup": false, "responseTimeWithAppName": false, + "suggestChanges": false, "syncSSOGroups": false, }, "externalResolver": { diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index e8af3c9538..d095328b41 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -18,6 +18,10 @@ export const defaultExperimentalOptions = { process.env.UNLEASH_EXPERIMENTAL_SYNC_SSO_GROUPS, false, ), + suggestChanges: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_SUGGEST_CHANGES, + false, + ), embedProxyFrontend: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_EMBED_PROXY_FRONTEND, false, @@ -52,6 +56,7 @@ export interface IExperimentalOptions { anonymiseEventLog?: boolean; personalAccessTokens?: boolean; syncSSOGroups?: boolean; + suggestChanges?: boolean; cloneEnvironment?: boolean; }; externalResolver: IExternalFlagResolver;