mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: integrate with API for suggest changes (#2286)
* feat: integrate with API for suggest changes * fix: suggestions table tabs state (#2287) * fix: suggestions table tabs state * fix suggestion header padding * fix: update snapshots * fix: pr comments * fix: revert store change * fix: revert store fix Co-authored-by: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									95779754fb
								
							
						
					
					
						commit
						15c22d7630
					
				| @ -6,7 +6,6 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         boxShadow: 'none', | ||||
|     }, | ||||
|     headerContainer: { | ||||
|         padding: theme.spacing(2, 4), | ||||
|         borderBottomStyle: 'solid', | ||||
|         borderBottomWidth: 1, | ||||
|         borderBottomColor: theme.palette.divider, | ||||
| @ -14,6 +13,9 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|             padding: '1.5rem 1rem', | ||||
|         }, | ||||
|     }, | ||||
|     headerPadding: { | ||||
|         padding: theme.spacing(2, 4), | ||||
|     }, | ||||
|     bodyContainer: { | ||||
|         padding: theme.spacing(4), | ||||
|         [theme.breakpoints.down('md')]: { | ||||
|  | ||||
| @ -19,6 +19,7 @@ interface IPageContentProps extends PaperProps { | ||||
|     disableBorder?: boolean; | ||||
|     disableLoading?: boolean; | ||||
|     bodyClass?: string; | ||||
|     headerClass?: string; | ||||
| } | ||||
| 
 | ||||
| const PageContentLoading: FC<{ isLoading: boolean }> = ({ | ||||
| @ -40,6 +41,7 @@ export const PageContent: FC<IPageContentProps> = ({ | ||||
|     disablePadding = false, | ||||
|     disableBorder = false, | ||||
|     bodyClass = '', | ||||
|     headerClass = '', | ||||
|     isLoading = false, | ||||
|     disableLoading = false, | ||||
|     className, | ||||
| @ -47,10 +49,15 @@ export const PageContent: FC<IPageContentProps> = ({ | ||||
| }) => { | ||||
|     const { classes: styles } = useStyles(); | ||||
| 
 | ||||
|     const headerClasses = classnames('header', styles.headerContainer, { | ||||
|         [styles.paddingDisabled]: disablePadding, | ||||
|         [styles.borderDisabled]: disableBorder, | ||||
|     }); | ||||
|     const headerClasses = classnames( | ||||
|         'header', | ||||
|         styles.headerContainer, | ||||
|         headerClass || styles.headerPadding, | ||||
|         { | ||||
|             [styles.paddingDisabled]: disablePadding, | ||||
|             [styles.borderDisabled]: disableBorder, | ||||
|         } | ||||
|     ); | ||||
| 
 | ||||
|     const bodyClasses = classnames( | ||||
|         'body', | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import { VFC } from 'react'; | ||||
| import { Chip, styled } from '@mui/material'; | ||||
| import { colors } from '../../../../../themes/colors'; | ||||
| import { TextCell } from '../../../../common/Table/cells/TextCell/TextCell'; | ||||
| import { colors } from 'themes/colors'; | ||||
| import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; | ||||
| import { Check, CircleOutlined, Close } from '@mui/icons-material'; | ||||
| 
 | ||||
| interface IChangesetStatusCellProps { | ||||
| @ -60,7 +61,10 @@ export const StyledReviewChip = styled(StyledChip)(({ theme }) => ({ | ||||
|         color: theme.palette.primary.main, | ||||
|     }, | ||||
| })); | ||||
| export const ChangesetStatusCell = ({ value }: IChangesetStatusCellProps) => { | ||||
| 
 | ||||
| export const ChangesetStatusCell: VFC<IChangesetStatusCellProps> = ({ | ||||
|     value, | ||||
| }) => { | ||||
|     const renderState = (state: string) => { | ||||
|         switch (state) { | ||||
|             case SuggestChangesetState.IN_REVIEW: | ||||
|  | ||||
| @ -2,6 +2,7 @@ import { TextCell } from '../../../../common/Table/cells/TextCell/TextCell'; | ||||
| import { Link, styled, Typography } from '@mui/material'; | ||||
| import { Link as RouterLink } from 'react-router-dom'; | ||||
| import { useTheme } from '@mui/system'; | ||||
| import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||||
| 
 | ||||
| interface IChangesetTitleCellProps { | ||||
|     value?: any; | ||||
| @ -18,9 +19,10 @@ export const ChangesetTitleCell = ({ | ||||
|     value, | ||||
|     row: { original }, | ||||
| }: IChangesetTitleCellProps) => { | ||||
|     const { id, features: changes, project } = original; | ||||
|     const projectId = useRequiredPathParam('projectId'); | ||||
|     const { id, features: changes } = original; | ||||
|     const theme = useTheme(); | ||||
|     const path = `projects/${project}/suggest-changes/${id}`; | ||||
|     const path = `/projects/${projectId}/suggest-changes/${id}`; | ||||
| 
 | ||||
|     if (!value) { | ||||
|         return <TextCell />; | ||||
|  | ||||
| @ -1,8 +1,12 @@ | ||||
| import { makeStyles } from 'tss-react/mui'; | ||||
| 
 | ||||
| export const useStyles = makeStyles()(theme => ({ | ||||
|     header: { | ||||
|         padding: theme.spacing(0, 4), | ||||
|     }, | ||||
|     tabContainer: { | ||||
|         paddingLeft: 0, | ||||
|         paddingBottom: 0, | ||||
|     }, | ||||
|     tabButton: { | ||||
|         textTransform: 'none', | ||||
|  | ||||
| @ -77,7 +77,7 @@ export const SuggestionsTabs = ({ | ||||
|         }, | ||||
|     ]; | ||||
| 
 | ||||
|     const [activeTab, setActiveTab] = useState(tabs[0]); | ||||
|     const [activeTab, setActiveTab] = useState(0); | ||||
| 
 | ||||
|     const columns = useMemo( | ||||
|         () => [ | ||||
| @ -137,7 +137,7 @@ export const SuggestionsTabs = ({ | ||||
|         data: searchedData, | ||||
|         getSearchText, | ||||
|         getSearchContext, | ||||
|     } = useSearch(columns, searchValue, activeTab.data); | ||||
|     } = useSearch(columns, searchValue, tabs[activeTab]?.data); | ||||
| 
 | ||||
|     const data = useMemo( | ||||
|         () => (loading ? featuresPlaceholder : searchedData), | ||||
| @ -206,34 +206,31 @@ export const SuggestionsTabs = ({ | ||||
|         setStoredParams({ id: sortBy[0].id, desc: sortBy[0].desc || false }); | ||||
|     }, [loading, sortBy, searchValue]); // eslint-disable-line react-hooks/exhaustive-deps
 | ||||
| 
 | ||||
|     const renderTabs = () => { | ||||
|         return ( | ||||
|             <div className={classes.tabContainer}> | ||||
|                 <Tabs | ||||
|                     value={activeTab?.title} | ||||
|                     indicatorColor="primary" | ||||
|                     textColor="primary" | ||||
|                 > | ||||
|                     {tabs.map(tab => ( | ||||
|                         <Tab | ||||
|                             key={tab.title} | ||||
|                             label={`${tab.title} (${tab.data.length})`} | ||||
|                             value={tab.title} | ||||
|                             onClick={() => setActiveTab(tab)} | ||||
|                             className={classes.tabButton} | ||||
|                         /> | ||||
|                     ))} | ||||
|                 </Tabs> | ||||
|             </div> | ||||
|         ); | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <PageContent | ||||
|             isLoading={loading} | ||||
|             headerClass={classes.header} | ||||
|             header={ | ||||
|                 <PageHeader | ||||
|                     titleElement={renderTabs()} | ||||
|                     titleElement={ | ||||
|                         <div className={classes.tabContainer}> | ||||
|                             <Tabs | ||||
|                                 value={tabs[activeTab]?.title} | ||||
|                                 indicatorColor="primary" | ||||
|                                 textColor="primary" | ||||
|                             > | ||||
|                                 {tabs.map((tab, index) => ( | ||||
|                                     <Tab | ||||
|                                         key={tab.title} | ||||
|                                         label={`${tab.title} (${tab.data.length})`} | ||||
|                                         value={tab.title} | ||||
|                                         onClick={() => setActiveTab(index)} | ||||
|                                         className={classes.tabButton} | ||||
|                                     /> | ||||
|                                 ))} | ||||
|                             </Tabs> | ||||
|                         </div> | ||||
|                     } | ||||
|                     actions={ | ||||
|                         <Search | ||||
|                             initialValue={searchValue} | ||||
|  | ||||
| @ -16,7 +16,7 @@ export const DraftBanner: VFC<IDraftBannerProps> = ({ project }) => { | ||||
|     const { draft, loading } = useSuggestedChangesDraft(project); | ||||
|     const environment = ''; | ||||
| 
 | ||||
|     if (!loading && !draft) { | ||||
|     if (!loading && draft?.length === 0) { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -37,8 +37,8 @@ export const SuggestedChangeHeader: FC<{ suggestedChange: any }> = ({ | ||||
|                 </Typography> | ||||
|                 <PlaygroundResultChip | ||||
|                     // icon={<ChangesAppliedIcon strokeWidth="0.25" />}
 | ||||
|                     label="Changes applied" | ||||
|                     enabled="unknown" | ||||
|                     label="Changes approved" | ||||
|                     enabled | ||||
|                 /> | ||||
|             </Box> | ||||
|             <Box sx={{ display: 'flex', verticalAlign: 'center', gap: 2 }}> | ||||
| @ -60,7 +60,7 @@ export const SuggestedChangeHeader: FC<{ suggestedChange: any }> = ({ | ||||
|                     </Typography>{' '} | ||||
|                     | Updates:{' '} | ||||
|                     <Typography display="inline" fontWeight="bold"> | ||||
|                         {suggestedChange?.changes.length} feature toggles | ||||
|                         {suggestedChange?.features.length} feature toggles | ||||
|                     </Typography> | ||||
|                 </Card> | ||||
|             </Box> | ||||
|  | ||||
| @ -1,13 +1,38 @@ | ||||
| import { FC } from 'react'; | ||||
| import { Box, Paper } from '@mui/material'; | ||||
| import { Box, Button, Paper } from '@mui/material'; | ||||
| import { useSuggestedChange } from 'hooks/api/getters/useSuggestChange/useSuggestedChange'; | ||||
| import { SuggestedChangeHeader } from './SuggestedChangeHeader/SuggestedChangeHeader'; | ||||
| import { SuggestedChangeTimeline } from './SuggestedChangeTimeline/SuggestedChangeTimeline'; | ||||
| import { SuggestedChangeReviewers } from './SuggestedChangeReviewers/SuggestedChangeReviewers'; | ||||
| import { SuggestedChangeset } from '../SuggestedChangeset/SuggestedChangeset'; | ||||
| import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||||
| import { useSuggestChangeApi } from 'hooks/api/actions/useSuggestChangeApi/useSuggestChangeApi'; | ||||
| import useToast from 'hooks/useToast'; | ||||
| import { formatUnknownError } from 'utils/formatUnknownError'; | ||||
| 
 | ||||
| export const SuggestedChangeOverview: FC = () => { | ||||
|     const { data: suggestedChange } = useSuggestedChange(); | ||||
|     const projectId = useRequiredPathParam('projectId'); | ||||
|     const id = useRequiredPathParam('id'); | ||||
|     const { data: suggestedChange } = useSuggestedChange(projectId, id); | ||||
|     const { applyChanges } = useSuggestChangeApi(); | ||||
|     const { setToastData, setToastApiError } = useToast(); | ||||
| 
 | ||||
|     if (!suggestedChange) { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     const onApplyChanges = async () => { | ||||
|         try { | ||||
|             await applyChanges(projectId, id); | ||||
|             setToastData({ | ||||
|                 type: 'success', | ||||
|                 title: 'Success', | ||||
|                 text: 'Changes appplied', | ||||
|             }); | ||||
|         } catch (error: unknown) { | ||||
|             setToastApiError(formatUnknownError(error)); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
| @ -40,6 +65,13 @@ export const SuggestedChangeOverview: FC = () => { | ||||
|                         })} | ||||
|                     > | ||||
|                         <SuggestedChangeset suggestedChange={suggestedChange} /> | ||||
|                         <Button | ||||
|                             variant="contained" | ||||
|                             sx={{ marginTop: 2 }} | ||||
|                             onClick={onApplyChanges} | ||||
|                         > | ||||
|                             Apply changes | ||||
|                         </Button> | ||||
|                     </Box> | ||||
|                 </Paper> | ||||
|             </Box> | ||||
|  | ||||
| @ -7,6 +7,7 @@ import { PageHeader } from 'component/common/PageHeader/PageHeader'; | ||||
| import { HelpOutline } from '@mui/icons-material'; | ||||
| import { SuggestedChangeset } from '../SuggestedChangeset/SuggestedChangeset'; | ||||
| import { useSuggestedChangesDraft } from 'hooks/api/getters/useSuggestedChangesDraft/useSuggestedChangesDraft'; | ||||
| import { useSuggestChangeApi } from 'hooks/api/actions/useSuggestChangeApi/useSuggestChangeApi'; | ||||
| 
 | ||||
| interface ISuggestedChangesSidebarProps { | ||||
|     open: boolean; | ||||
| @ -45,10 +46,20 @@ export const SuggestedChangesSidebar: VFC<ISuggestedChangesSidebarProps> = ({ | ||||
|     project, | ||||
|     onClose, | ||||
| }) => { | ||||
|     const { draft, loading } = useSuggestedChangesDraft(project); | ||||
|     const { | ||||
|         draft, | ||||
|         loading, | ||||
|         refetch: refetchSuggestedChanges, | ||||
|     } = useSuggestedChangesDraft(project); | ||||
|     const { changeState } = useSuggestChangeApi(); | ||||
| 
 | ||||
|     const onReview = async () => { | ||||
|         alert('approve'); | ||||
|     const onReview = async (draftId: number) => { | ||||
|         try { | ||||
|             await changeState(project, draftId, { state: 'In review' }); | ||||
|             refetchSuggestedChanges(); | ||||
|         } catch (e) { | ||||
|             console.log('something went wrong'); | ||||
|         } | ||||
|     }; | ||||
|     const onDiscard = async () => { | ||||
|         alert('discard'); | ||||
| @ -163,7 +174,11 @@ export const SuggestedChangesSidebar: VFC<ISuggestedChangesSidebarProps> = ({ | ||||
|                                         <Button | ||||
|                                             sx={{ mt: 2, ml: 'auto' }} | ||||
|                                             variant="contained" | ||||
|                                             onClick={onReview} | ||||
|                                             onClick={() => | ||||
|                                                 onReview( | ||||
|                                                     environmentChangeset.id | ||||
|                                                 ) | ||||
|                                             } | ||||
|                                         > | ||||
|                                             Request changes | ||||
|                                         </Button> | ||||
|  | ||||
| @ -10,7 +10,7 @@ exports[`renders an empty list correctly 1`] = ` | ||||
|       className="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 css-p9j8ie-MuiPaper-root-container" | ||||
|     > | ||||
|       <div | ||||
|         className="header css-1ywhhai-headerContainer" | ||||
|         className="header css-zq4ve2-headerContainer css-70tvrt-headerPadding" | ||||
|       > | ||||
|         <div | ||||
|           className="css-1ylehva-headerContainer" | ||||
|  | ||||
| @ -10,12 +10,13 @@ interface ISuggestChangeSchema { | ||||
|     payload: string | boolean | object | number; | ||||
| } | ||||
| 
 | ||||
| export const useSuggestChangeApi = (project: string) => { | ||||
| export const useSuggestChangeApi = () => { | ||||
|     const { makeRequest, createRequest, errors, loading } = useAPI({ | ||||
|         propagateErrors: true, | ||||
|     }); | ||||
| 
 | ||||
|     const addSuggestion = async ( | ||||
|         project: string, | ||||
|         environment: string, | ||||
|         payload: ISuggestChangeSchema | ||||
|     ) => { | ||||
| @ -26,7 +27,38 @@ export const useSuggestChangeApi = (project: string) => { | ||||
|         }); | ||||
|         try { | ||||
|             const response = await makeRequest(req.caller, req.id); | ||||
|             return await response.json(); | ||||
|             return response.json(); | ||||
|         } catch (e) { | ||||
|             throw e; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     const changeState = async ( | ||||
|         project: string, | ||||
|         suggestChangeId: number, | ||||
|         payload: any | ||||
|     ) => { | ||||
|         const path = `api/admin/projects/${project}/suggest-changes/${suggestChangeId}/state`; | ||||
|         const req = createRequest(path, { | ||||
|             method: 'PUT', | ||||
|             body: JSON.stringify(payload), | ||||
|         }); | ||||
|         try { | ||||
|             const response = await makeRequest(req.caller, req.id); | ||||
|             return response.json(); | ||||
|         } catch (e) { | ||||
|             throw e; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     const applyChanges = async (project: string, suggestChangeId: string) => { | ||||
|         const path = `api/admin/projects/${project}/suggest-changes/${suggestChangeId}/apply`; | ||||
|         const req = createRequest(path, { | ||||
|             method: 'PUT', | ||||
|         }); | ||||
|         try { | ||||
|             const response = await makeRequest(req.caller, req.id); | ||||
|             return response; | ||||
|         } catch (e) { | ||||
|             throw e; | ||||
|         } | ||||
| @ -34,6 +66,8 @@ export const useSuggestChangeApi = (project: string) => { | ||||
| 
 | ||||
|     return { | ||||
|         addSuggestion, | ||||
|         applyChanges, | ||||
|         changeState, | ||||
|         errors, | ||||
|         loading, | ||||
|     }; | ||||
|  | ||||
| @ -1,99 +1,18 @@ | ||||
| // import useSWR from 'swr';
 | ||||
| // import { formatApiPath } from 'utils/formatPath';
 | ||||
| import { ISuggestChangeset } from 'interfaces/suggestChangeset'; | ||||
| import useSWR from 'swr'; | ||||
| import { formatApiPath } from 'utils/formatPath'; | ||||
| import handleErrorResponses from '../httpErrorResponseHandler'; | ||||
| 
 | ||||
| // FIXME: mock
 | ||||
| const data: any = { | ||||
|     id: '12', | ||||
|     environment: 'production', | ||||
|     state: 'DRAFT', | ||||
|     project: 'default', | ||||
|     createdBy: { | ||||
|         email: 'mateusz@getunleash.ai', | ||||
|         avatar: 'https://gravatar-uri.com/1321', | ||||
|     }, | ||||
|     createdAt: '2020-10-20T12:00:00.000Z', | ||||
|     changes: [ | ||||
|         { | ||||
|             feature: 'my-feature-toggle', | ||||
|             changeSet: [ | ||||
|                 { | ||||
|                     id: 'f79d399f-cb38-4982-b9b6-4141sdsdaad', | ||||
|                     action: 'updateEnabled', | ||||
|                     payload: { data: { data: true } }, | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 'f79d399f-cb38-4982-b9b6-4141sdsdaad', | ||||
|                     action: 'addStrategy', | ||||
|                     payload: { | ||||
|                         name: 'flexibleRollout', | ||||
|                         constraints: [], | ||||
|                         parameters: { | ||||
|                             rollout: '50', | ||||
|                             stickiness: 'default', | ||||
|                             groupId: 'suggest-changes', | ||||
|                         }, | ||||
|                     }, | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 'f79d399f-cb38-4982-b9b6-4141sdsdaad', | ||||
|                     action: 'updateStrategy', | ||||
|                     payload: { | ||||
|                         data: {}, | ||||
|                     }, | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 'f79d399f-cb38-4982-b9b6-4141sdsdaad', | ||||
|                     action: 'deleteStrategy', | ||||
|                     payload: { | ||||
|                         data: {}, | ||||
|                     }, | ||||
|                 }, | ||||
|             ], | ||||
|         }, | ||||
|         { | ||||
|             feature: 'new-feature-toggle', | ||||
|             changeSet: [ | ||||
|                 { | ||||
|                     id: 'f79d399f-cb38-4982-b9b6-4141sdsdaad', | ||||
|                     action: 'updateEnabled', | ||||
|                     payload: { | ||||
|                         data: { data: false }, | ||||
|                         strategyId: '123-14', | ||||
|                     }, | ||||
|                 }, | ||||
|             ], | ||||
|         }, | ||||
|         { | ||||
|             feature: 'add-strategy-feature-toggle', | ||||
|             changeSet: [ | ||||
|                 { | ||||
|                     id: 'f79d399f-cb38-4982-b9b6-4141sdsdaad', | ||||
|                     action: 'addStrategy', | ||||
|                     payload: { | ||||
|                         data: {}, | ||||
|                     }, | ||||
|                 }, | ||||
|             ], | ||||
|         }, | ||||
|     ], | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated for draft: useSuggestedChangesDraft | ||||
|  */ | ||||
| export const useSuggestedChange = () => { | ||||
|     // const { data, error, mutate } = useSWR(
 | ||||
|     //     formatApiPath(`api/admin/suggest-changes/${id}`),
 | ||||
|     //     fetcher
 | ||||
|     // );
 | ||||
| export const useSuggestedChange = (projectId: string, id: string) => { | ||||
|     const { data, error, mutate } = useSWR( | ||||
|         formatApiPath(`api/admin/projects/${projectId}/suggest-changes/${id}`), | ||||
|         fetcher | ||||
|     ); | ||||
| 
 | ||||
|     return { | ||||
|         data, | ||||
|         // loading: !error && !data,
 | ||||
|         // refetchChangeRequest: () => mutate(),
 | ||||
|         // error,
 | ||||
|         loading: !error && !data, | ||||
|         refetchSuggestedChange: () => mutate(), | ||||
|         error, | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -2,10 +2,14 @@ import { useCallback, useState } from 'react'; | ||||
| import useToast from 'hooks/useToast'; | ||||
| import { formatUnknownError } from 'utils/formatUnknownError'; | ||||
| import { useSuggestChangeApi } from './api/actions/useSuggestChangeApi/useSuggestChangeApi'; | ||||
| import { useSuggestedChangesDraft } from './api/getters/useSuggestedChangesDraft/useSuggestedChangesDraft'; | ||||
| 
 | ||||
| export const useSuggestToggle = (project: string) => { | ||||
|     const { setToastData, setToastApiError } = useToast(); | ||||
|     const { addSuggestion } = useSuggestChangeApi(project); | ||||
|     const { addSuggestion } = useSuggestChangeApi(); | ||||
|     const { refetch: refetchSuggestedChange } = | ||||
|         useSuggestedChangesDraft(project); | ||||
| 
 | ||||
|     const [suggestChangesDialogDetails, setSuggestChangesDialogDetails] = | ||||
|         useState<{ | ||||
|             enabled?: boolean; | ||||
| @ -32,13 +36,18 @@ export const useSuggestToggle = (project: string) => { | ||||
| 
 | ||||
|     const onSuggestToggleConfirm = useCallback(async () => { | ||||
|         try { | ||||
|             await addSuggestion(suggestChangesDialogDetails.environment!, { | ||||
|                 feature: suggestChangesDialogDetails.featureName!, | ||||
|                 action: 'updateEnabled', | ||||
|                 payload: { | ||||
|                     enabled: Boolean(suggestChangesDialogDetails.enabled), | ||||
|                 }, | ||||
|             }); | ||||
|             await addSuggestion( | ||||
|                 project, | ||||
|                 suggestChangesDialogDetails.environment!, | ||||
|                 { | ||||
|                     feature: suggestChangesDialogDetails.featureName!, | ||||
|                     action: 'updateEnabled', | ||||
|                     payload: { | ||||
|                         enabled: Boolean(suggestChangesDialogDetails.enabled), | ||||
|                     }, | ||||
|                 } | ||||
|             ); | ||||
|             refetchSuggestedChange(); | ||||
|             setSuggestChangesDialogDetails({ isOpen: false }); | ||||
|             setToastData({ | ||||
|                 type: 'success', | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user