diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.tsx index f68861211e..982202862e 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.tsx @@ -7,7 +7,6 @@ import { ConstraintAccordionView } from './ConstraintAccordionView/ConstraintAcc interface IConstraintAccordionProps { compact: boolean; editing: boolean; - environmentId?: string; constraint: IConstraint; onCancel: () => void; onEdit?: () => void; @@ -19,7 +18,6 @@ export const ConstraintAccordion = ({ constraint, compact = false, editing, - environmentId, onEdit, onCancel, onDelete, @@ -43,7 +41,6 @@ export const ConstraintAccordion = ({ constraint={constraint} onEdit={onEdit} onDelete={onDelete} - environmentId={environmentId} compact={compact} /> } diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx index bfc01d7ee7..a5ccbd9d24 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx @@ -3,20 +3,14 @@ import React, { forwardRef, useImperativeHandle } from 'react'; import { ConstraintAccordion } from 'component/common/ConstraintAccordion/ConstraintAccordion'; import produce from 'immer'; import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; -import PermissionButton from 'component/common/PermissionButton/PermissionButton'; -import { - CREATE_FEATURE_STRATEGY, - UPDATE_FEATURE_STRATEGY, -} from 'component/providers/AccessProvider/permissions'; import { useWeakMap } from 'hooks/useWeakMap'; import { objectId } from 'utils/objectId'; import { useStyles } from './ConstraintAccordionList.styles'; import { createEmptyConstraint } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint'; import ConditionallyRender from 'component/common/ConditionallyRender'; +import { Button } from '@material-ui/core'; interface IConstraintAccordionListProps { - projectId?: string; - environmentId?: string; constraints: IConstraint[]; setConstraints?: React.Dispatch>; showCreateButton?: boolean; @@ -40,115 +34,97 @@ export const constraintAccordionListId = 'constraintAccordionListId'; export const ConstraintAccordionList = forwardRef< IConstraintAccordionListRef | undefined, IConstraintAccordionListProps ->( - ( - { - projectId, - environmentId, - constraints, - setConstraints, - showCreateButton, - }, - ref - ) => { - const state = useWeakMap< - IConstraint, - IConstraintAccordionListItemState - >(); - const { context } = useUnleashContext(); - const styles = useStyles(); +>(({ constraints, setConstraints, showCreateButton }, ref) => { + const state = useWeakMap(); + const { context } = useUnleashContext(); + const styles = useStyles(); - const addConstraint = - setConstraints && - ((contextName: string) => { - const constraint = createEmptyConstraint(contextName); - state.set(constraint, { editing: true, new: true }); - setConstraints(prev => [...prev, constraint]); - }); + const addConstraint = + setConstraints && + ((contextName: string) => { + const constraint = createEmptyConstraint(contextName); + state.set(constraint, { editing: true, new: true }); + setConstraints(prev => [...prev, constraint]); + }); - useImperativeHandle(ref, () => ({ - addConstraint, - })); + useImperativeHandle(ref, () => ({ + addConstraint, + })); - const onAdd = - addConstraint && - (() => { - addConstraint(context[0].name); - }); + const onAdd = + addConstraint && + (() => { + addConstraint(context[0].name); + }); - const onEdit = - setConstraints && - ((constraint: IConstraint) => { - state.set(constraint, { editing: true }); - }); + const onEdit = + setConstraints && + ((constraint: IConstraint) => { + state.set(constraint, { editing: true }); + }); - const onRemove = - setConstraints && - ((index: number) => { - const constraint = constraints[index]; - state.set(constraint, {}); - setConstraints( - produce(draft => { - draft.splice(index, 1); - }) - ); - }); - - const onSave = - setConstraints && - ((index: number, constraint: IConstraint) => { - state.set(constraint, {}); - setConstraints( - produce(draft => { - draft[index] = constraint; - }) - ); - }); - - const onCancel = (index: number) => { + const onRemove = + setConstraints && + ((index: number) => { const constraint = constraints[index]; - state.get(constraint)?.new && onRemove?.(index); state.set(constraint, {}); - }; + setConstraints( + produce(draft => { + draft.splice(index, 1); + }) + ); + }); - if (context.length === 0) { - return null; - } + const onSave = + setConstraints && + ((index: number, constraint: IConstraint) => { + state.set(constraint, {}); + setConstraints( + produce(draft => { + draft[index] = constraint; + }) + ); + }); - return ( -
- { + const constraint = constraints[index]; + state.get(constraint)?.new && onRemove?.(index); + state.set(constraint, {}); + }; + + if (context.length === 0) { + return null; + } + + return ( +
+ + +
+ } + /> + {constraints.map((constraint, index) => ( + - {constraints.map((constraint, index) => ( - - ))} -
- ); - } -); + ))} + + ); +}); diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx index 0e60e957ae..996ba6cc12 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx @@ -17,7 +17,6 @@ import { import { useStyles } from '../ConstraintAccordion.styles'; interface IConstraintAccordionViewProps { - environmentId?: string; constraint: IConstraint; onDelete?: () => void; onEdit?: () => void; @@ -26,7 +25,6 @@ interface IConstraintAccordionViewProps { export const ConstraintAccordionView = ({ compact, - environmentId, constraint, onEdit, onDelete, @@ -49,7 +47,6 @@ export const ConstraintAccordionView = ({ > void; onEdit?: () => void; singleValue: boolean; - environmentId?: string; } export const ConstraintAccordionViewHeader = ({ @@ -30,11 +25,9 @@ export const ConstraintAccordionViewHeader = ({ onEdit, onDelete, singleValue, - environmentId, }: IConstraintAccordionViewHeaderProps) => { const styles = useStyles(); const { locationSettings } = useLocationSettings(); - const { projectId } = useParams(); const smallScreen = useMediaQuery(`(max-width:${790}px)`); const minWidthHeader = compact || smallScreen ? '100px' : '175px'; @@ -97,32 +90,19 @@ export const ConstraintAccordionViewHeader = ({
diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx index 15dd1c0cc9..9a34120aa0 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraints.tsx @@ -1,6 +1,11 @@ import { IConstraint, IFeatureStrategy } from 'interfaces/strategy'; -import React, { useMemo } from 'react'; +import React, { useMemo, useContext } from 'react'; import { ConstraintAccordionList } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList'; +import AccessContext from 'contexts/AccessContext'; +import { + UPDATE_FEATURE_STRATEGY, + CREATE_FEATURE_STRATEGY, +} from 'component/providers/AccessProvider/permissions'; interface IFeatureStrategyConstraintsProps { projectId: string; @@ -17,6 +22,8 @@ export const FeatureStrategyConstraints = ({ strategy, setStrategy, }: IFeatureStrategyConstraintsProps) => { + const { hasAccess } = useContext(AccessContext); + const constraints = useMemo(() => { return strategy.constraints ?? []; }, [strategy]); @@ -28,13 +35,23 @@ export const FeatureStrategyConstraints = ({ })); }; + const showCreateButton = hasAccess( + CREATE_FEATURE_STRATEGY, + projectId, + environmentId + ); + + const allowEditAndDelete = hasAccess( + UPDATE_FEATURE_STRATEGY, + projectId, + environmentId + ); + return ( ); }; diff --git a/frontend/src/component/segments/CreateSegment/CreateSegment.tsx b/frontend/src/component/segments/CreateSegment/CreateSegment.tsx index eb487e9b66..ec075cff94 100644 --- a/frontend/src/component/segments/CreateSegment/CreateSegment.tsx +++ b/frontend/src/component/segments/CreateSegment/CreateSegment.tsx @@ -89,9 +89,9 @@ export const CreateSegment = () => { setDescription={setDescription} constraints={constraints} setConstraints={setConstraints} - mode="Create" errors={errors} clearErrors={clearErrors} + mode="create" > { setDescription={setDescription} constraints={constraints} setConstraints={setConstraints} - mode="Edit" errors={errors} clearErrors={clearErrors} + mode="edit" > >; handleSubmit: (e: any) => void; errors: { [key: string]: string }; - mode: 'Create' | 'Edit'; clearErrors: () => void; + mode: SegmentFormMode; } export const SegmentForm: React.FC = ({ @@ -31,6 +33,7 @@ export const SegmentForm: React.FC = ({ handleSubmit, errors, clearErrors, + mode, }) => { const styles = useStyles(); const totalSteps = 2; @@ -61,6 +64,7 @@ export const SegmentForm: React.FC = ({ constraints={constraints} setConstraints={setConstraints} setCurrentStep={setCurrentStep} + mode={mode} > {children} diff --git a/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.tsx b/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.tsx index 90411120d8..aa03b568a6 100644 --- a/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.tsx +++ b/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.tsx @@ -1,11 +1,15 @@ -import React, { useRef, useState } from 'react'; +import React, { useRef, useState, useContext } from 'react'; import { Button } from '@material-ui/core'; import { Add } from '@material-ui/icons'; import ConditionallyRender from 'component/common/ConditionallyRender'; import PermissionButton from 'component/common/PermissionButton/PermissionButton'; import { SidebarModal } from 'component/common/SidebarModal/SidebarModal'; import { CreateUnleashContext } from 'component/context/CreateUnleashContext/CreateUnleashContext'; -import { CREATE_CONTEXT_FIELD } from 'component/providers/AccessProvider/permissions'; +import { + CREATE_CONTEXT_FIELD, + CREATE_SEGMENT, + UPDATE_SEGMENT, +} from 'component/providers/AccessProvider/permissions'; import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; import { IConstraint } from 'interfaces/strategy'; import { useHistory } from 'react-router-dom'; @@ -14,7 +18,7 @@ import { ConstraintAccordionList, IConstraintAccordionListRef, } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList'; -import { SegmentFormStep } from '../SegmentForm/SegmentForm'; +import { SegmentFormStep, SegmentFormMode } from '../SegmentForm/SegmentForm'; import { AutocompleteBox, IAutocompleteBoxOption, @@ -25,11 +29,13 @@ import { } from 'component/segments/SegmentDocs/SegmentDocs'; import { useSegmentValuesCount } from 'component/segments/hooks/useSegmentValuesCount'; import { SEGMENT_VALUES_LIMIT } from 'utils/segmentLimits'; +import AccessContext from 'contexts/AccessContext'; interface ISegmentFormPartTwoProps { constraints: IConstraint[]; setConstraints: React.Dispatch>; setCurrentStep: React.Dispatch>; + mode: SegmentFormMode; } export const SegmentFormStepTwo: React.FC = ({ @@ -37,14 +43,17 @@ export const SegmentFormStepTwo: React.FC = ({ constraints, setConstraints, setCurrentStep, + mode, }) => { const constraintsAccordionListRef = useRef(); const history = useHistory(); const styles = useStyles(); + const { hasAccess } = useContext(AccessContext); const { context = [] } = useUnleashContext(); const [open, setOpen] = useState(false); const segmentValuesCount = useSegmentValuesCount(constraints); const overSegmentValuesLimit = segmentValuesCount > SEGMENT_VALUES_LIMIT; + const modePermission = mode === 'create' ? CREATE_SEGMENT : UPDATE_SEGMENT; const autocompleteOptions = context.map(c => ({ value: c.name, @@ -122,7 +131,11 @@ export const SegmentFormStepTwo: React.FC = ({ diff --git a/frontend/src/component/segments/SegmentList/SegmentList.tsx b/frontend/src/component/segments/SegmentList/SegmentList.tsx index f863a3ee0f..5821567ad4 100644 --- a/frontend/src/component/segments/SegmentList/SegmentList.tsx +++ b/frontend/src/component/segments/SegmentList/SegmentList.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from 'react'; +import { useState } from 'react'; import { Table, TableBody, @@ -7,12 +7,8 @@ import { TableRow, Typography, } from '@material-ui/core'; -import AccessContext from 'contexts/AccessContext'; import usePagination from 'hooks/usePagination'; -import { - CREATE_SEGMENT, - UPDATE_SEGMENT, -} from 'component/providers/AccessProvider/permissions'; +import { CREATE_SEGMENT } from 'component/providers/AccessProvider/permissions'; import PaginateUI from 'component/common/PaginateUI/PaginateUI'; import { SegmentListItem } from './SegmentListItem/SegmentListItem'; import { ISegment } from 'interfaces/segment'; @@ -32,7 +28,6 @@ import { NAVIGATE_TO_CREATE_SEGMENT } from 'utils/testIds'; export const SegmentsList = () => { const history = useHistory(); - const { hasAccess } = useContext(AccessContext); const { segments = [], refetchSegments } = useSegments(); const { deleteSegment } = useSegmentsApi(); const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } = @@ -145,7 +140,7 @@ export const SegmentsList = () => { classes={{ root: styles.cell }} className={styles.lastHeader} > - {hasAccess(UPDATE_SEGMENT) ? 'Actions' : ''} + Action diff --git a/frontend/src/component/segments/SegmentList/SegmentListItem/SegmentListItem.tsx b/frontend/src/component/segments/SegmentList/SegmentListItem/SegmentListItem.tsx index 0cd9db65f6..ee71808fe3 100644 --- a/frontend/src/component/segments/SegmentList/SegmentListItem/SegmentListItem.tsx +++ b/frontend/src/component/segments/SegmentList/SegmentListItem/SegmentListItem.tsx @@ -2,14 +2,15 @@ import { useStyles } from './SegmentListItem.styles'; import { TableCell, TableRow, Typography } from '@material-ui/core'; import { Delete, Edit } from '@material-ui/icons'; import { - ADMIN, UPDATE_SEGMENT, + DELETE_SEGMENT, } from 'component/providers/AccessProvider/permissions'; import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; import TimeAgo from 'react-timeago'; import { ISegment } from 'interfaces/segment'; import { useHistory } from 'react-router-dom'; import { SEGMENT_DELETE_BTN_ID } from 'utils/testIds'; +import React from 'react'; interface ISegmentListItemProps { id: number; @@ -82,7 +83,7 @@ export const SegmentListItem = ({ }); setDelDialog(true); }} - permission={ADMIN} + permission={DELETE_SEGMENT} tooltip="Remove segment" data-testid={`${SEGMENT_DELETE_BTN_ID}_${name}`} >