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 }) => {
{}}
+ onClick={() => setReviewChangesOpen(true)}
sx={{ ml: 'auto' }}
>
Review changes
@@ -58,6 +61,10 @@ export const DraftBanner: VFC = ({ 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}
- />
-
-
- Apply changes
-
- >
- }
- />
-
-
- Review changes
-
-
-
-
-
- }
- label="Approve"
- />
- }
- label="Request changes"
- />
-
-
-
+ Review your changes
+
- Submit
-
-
-
- >
+
+
+
+ Make sure you are sending the right changes
+ suggestions to be reviewed
+
+ >
+ }
+ >
}
- />
-
+ >
+ {/* TODO: multiple environments (changesets) */}
+ {changeRequest?.state}
+
+
+
+ Applied}
+ />
+ Applied}
+ />
+
+
+ Apply changes
+
+ >
+ }
+ />
+
+
+ Request changes
+
+
+ Discard changes
+
+ >
+ }
+ />
+
+
+
);
};
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;