From 469727bb198bc06a9a58defec423e71f43a32df8 Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Tue, 11 Jul 2023 14:55:43 +0300 Subject: [PATCH] feat: project feature limit UI (#4220) --- .../Project/CreateProject/CreateProject.tsx | 4 + .../Project/EditProject/EditProject.tsx | 2 + .../FeatureTogglesLimitTooltip.tsx | 16 ++++ .../Project/ProjectForm/ProjectForm.tsx | 78 ++++++++++++++++++- .../ProjectSettings/Settings/EditProject.tsx | 6 +- .../project/Project/hooks/useProjectForm.ts | 12 ++- 6 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 frontend/src/component/project/Project/ProjectForm/FeatureTogglesLimitTooltip.tsx diff --git a/frontend/src/component/project/Project/CreateProject/CreateProject.tsx b/frontend/src/component/project/Project/CreateProject/CreateProject.tsx index 03398aec40..bdc0ec4b6c 100644 --- a/frontend/src/component/project/Project/CreateProject/CreateProject.tsx +++ b/frontend/src/component/project/Project/CreateProject/CreateProject.tsx @@ -32,6 +32,7 @@ const CreateProject = () => { projectName, projectMode, projectDesc, + featureLimit, setProjectId, setProjectName, setProjectDesc, @@ -40,6 +41,7 @@ const CreateProject = () => { validateProjectId, validateName, setProjectStickiness, + setFeatureLimit, setProjectMode, projectStickiness, errors, @@ -105,7 +107,9 @@ const CreateProject = () => { projectName={projectName} projectMode={projectMode} projectStickiness={projectStickiness} + featureLimit={featureLimit} setProjectStickiness={setProjectStickiness} + setFeatureLimit={setFeatureLimit} setProjectMode={setProjectMode} setProjectName={setProjectName} projectDesc={projectDesc} diff --git a/frontend/src/component/project/Project/EditProject/EditProject.tsx b/frontend/src/component/project/Project/EditProject/EditProject.tsx index c3055e4692..7d8360833b 100644 --- a/frontend/src/component/project/Project/EditProject/EditProject.tsx +++ b/frontend/src/component/project/Project/EditProject/EditProject.tsx @@ -126,6 +126,8 @@ const EditProject = () => { projectStickiness={projectStickiness} setProjectStickiness={setProjectStickiness} setProjectMode={setProjectMode} + setFeatureLimit={() => {}} + featureLimit={''} projectDesc={projectDesc} setProjectDesc={setProjectDesc} mode="Edit" diff --git a/frontend/src/component/project/Project/ProjectForm/FeatureTogglesLimitTooltip.tsx b/frontend/src/component/project/Project/ProjectForm/FeatureTogglesLimitTooltip.tsx new file mode 100644 index 0000000000..01237bb26f --- /dev/null +++ b/frontend/src/component/project/Project/ProjectForm/FeatureTogglesLimitTooltip.tsx @@ -0,0 +1,16 @@ +import { Box } from '@mui/material'; +import { FC } from 'react'; +import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; + +export const FeatureTogglesLimitTooltip: FC = () => ( + + Enforce an upper limit of the number of feature toggles that may + be created for this project. You can create unlimited feature + toggle if there is no limit set. + + } + /> +); diff --git a/frontend/src/component/project/Project/ProjectForm/ProjectForm.tsx b/frontend/src/component/project/Project/ProjectForm/ProjectForm.tsx index 73b21b55d9..8cf1eeaf26 100644 --- a/frontend/src/component/project/Project/ProjectForm/ProjectForm.tsx +++ b/frontend/src/component/project/Project/ProjectForm/ProjectForm.tsx @@ -7,6 +7,8 @@ import { ProjectMode } from '../hooks/useProjectForm'; import { Box, styled, TextField } from '@mui/material'; import { CollaborationModeTooltip } from './CollaborationModeTooltip'; import Input from 'component/common/Input/Input'; +import { FeatureTogglesLimitTooltip } from './FeatureTogglesLimitTooltip'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; interface IProjectForm { projectId: string; @@ -14,11 +16,14 @@ interface IProjectForm { projectDesc: string; projectStickiness?: string; projectMode?: string; + featureLimit: string; + featureCount?: number; setProjectStickiness?: React.Dispatch>; setProjectMode?: React.Dispatch>; setProjectId: React.Dispatch>; setProjectName: React.Dispatch>; setProjectDesc: React.Dispatch>; + setFeatureLimit: React.Dispatch>; handleSubmit: (e: any) => void; errors: { [key: string]: string }; mode: 'Create' | 'Edit'; @@ -47,9 +52,17 @@ const StyledDescription = styled('p')(({ theme }) => ({ marginRight: theme.spacing(1), })); +const StyledSubtitle = styled('div')(({ theme }) => ({ + color: theme.palette.text.secondary, + fontSize: theme.fontSizes.smallerBody, + lineHeight: 1.25, + paddingBottom: theme.spacing(1), +})); + const StyledInput = styled(Input)(({ theme }) => ({ width: '100%', marginBottom: theme.spacing(2), + paddingRight: theme.spacing(1), })); const StyledTextField = styled(TextField)(({ theme }) => ({ @@ -57,12 +70,22 @@ const StyledTextField = styled(TextField)(({ theme }) => ({ marginBottom: theme.spacing(2), })); +const StyledSelect = styled(Select)(({ theme }) => ({ + marginBottom: theme.spacing(2), + minWidth: '200px', +})); + const StyledButtonContainer = styled('div')(() => ({ marginTop: 'auto', display: 'flex', justifyContent: 'flex-end', })); +const StyledInputContainer = styled('div')(() => ({ + display: 'flex', + alignItems: 'center', +})); + const ProjectForm: React.FC = ({ children, handleSubmit, @@ -71,16 +94,20 @@ const ProjectForm: React.FC = ({ projectDesc, projectStickiness, projectMode, + featureLimit, + featureCount, setProjectId, setProjectName, setProjectDesc, setProjectStickiness, setProjectMode, + setFeatureLimit, errors, mode, validateProjectId, clearErrors, }) => { + const { uiConfig } = useUiConfig(); return ( @@ -158,7 +185,7 @@ const ProjectForm: React.FC = ({

What is your project collaboration mode?

- + > + + +

Feature toggles limit?

+ +
+ + Leave it empty if you don’t want to add a limit + + + + setFeatureLimit(e.target.value) + } + /> + 0 + } + show={ + + ({featureCount} of {featureLimit}{' '} + used) + + } + /> + + + } + />
- {children}
); diff --git a/frontend/src/component/project/Project/ProjectSettings/Settings/EditProject.tsx b/frontend/src/component/project/Project/ProjectSettings/Settings/EditProject.tsx index ceb50575e2..015197cecb 100644 --- a/frontend/src/component/project/Project/ProjectSettings/Settings/EditProject.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/Settings/EditProject.tsx @@ -29,7 +29,6 @@ const EditProject = () => { const id = useRequiredPathParam('projectId'); const { project } = useProject(id); const { defaultStickiness } = useDefaultProjectSettings(id); - const navigate = useNavigate(); const { trackEvent } = usePlausibleTracker(); const { @@ -38,11 +37,13 @@ const EditProject = () => { projectDesc, projectStickiness, projectMode, + featureLimit, setProjectId, setProjectName, setProjectDesc, setProjectStickiness, setProjectMode, + setFeatureLimit, getProjectPayload, clearErrors, validateProjectId, @@ -113,6 +114,8 @@ const EditProject = () => { setProjectId={setProjectId} projectName={projectName} projectMode={projectMode} + featureLimit={featureLimit} + featureCount={project.features.length} setProjectName={setProjectName} projectStickiness={projectStickiness} setProjectStickiness={setProjectStickiness} @@ -120,6 +123,7 @@ const EditProject = () => { projectDesc={projectDesc} mode="Edit" setProjectDesc={setProjectDesc} + setFeatureLimit={setFeatureLimit} clearErrors={clearErrors} validateProjectId={validateProjectId} > diff --git a/frontend/src/component/project/Project/hooks/useProjectForm.ts b/frontend/src/component/project/Project/hooks/useProjectForm.ts index 8c7a670cf9..bdf74519c2 100644 --- a/frontend/src/component/project/Project/hooks/useProjectForm.ts +++ b/frontend/src/component/project/Project/hooks/useProjectForm.ts @@ -9,7 +9,8 @@ const useProjectForm = ( initialProjectName = '', initialProjectDesc = '', initialProjectStickiness = DEFAULT_PROJECT_STICKINESS, - initialProjectMode: ProjectMode = 'open' + initialProjectMode: ProjectMode = 'open', + initialFeatureLimit = '' ) => { const [projectId, setProjectId] = useState(initialProjectId); @@ -20,6 +21,8 @@ const useProjectForm = ( ); const [projectMode, setProjectMode] = useState(initialProjectMode); + const [featureLimit, setFeatureLimit] = + useState(initialFeatureLimit); const [errors, setErrors] = useState({}); const { validateId } = useProjectApi(); @@ -40,6 +43,10 @@ const useProjectForm = ( setProjectMode(initialProjectMode); }, [initialProjectMode]); + useEffect(() => { + setFeatureLimit(initialFeatureLimit); + }, [initialFeatureLimit]); + useEffect(() => { setProjectStickiness(initialProjectStickiness); }, [initialProjectStickiness]); @@ -50,6 +57,7 @@ const useProjectForm = ( name: projectName, description: projectDesc, defaultStickiness: projectStickiness, + featureLimit: featureLimit, mode: projectMode, }; }; @@ -87,11 +95,13 @@ const useProjectForm = ( projectDesc, projectStickiness, projectMode, + featureLimit, setProjectId, setProjectName, setProjectDesc, setProjectStickiness, setProjectMode, + setFeatureLimit, getProjectPayload, validateName, validateProjectId,