mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
feat: project feature limit UI (#4220)
This commit is contained in:
parent
81c005013e
commit
469727bb19
@ -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}
|
||||
|
@ -126,6 +126,8 @@ const EditProject = () => {
|
||||
projectStickiness={projectStickiness}
|
||||
setProjectStickiness={setProjectStickiness}
|
||||
setProjectMode={setProjectMode}
|
||||
setFeatureLimit={() => {}}
|
||||
featureLimit={''}
|
||||
projectDesc={projectDesc}
|
||||
setProjectDesc={setProjectDesc}
|
||||
mode="Edit"
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { Box } from '@mui/material';
|
||||
import { FC } from 'react';
|
||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||
|
||||
export const FeatureTogglesLimitTooltip: FC = () => (
|
||||
<HelpIcon
|
||||
htmlTooltip
|
||||
tooltip={
|
||||
<Box>
|
||||
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.
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
);
|
@ -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<React.SetStateAction<string>>;
|
||||
setProjectMode?: React.Dispatch<React.SetStateAction<ProjectMode>>;
|
||||
setProjectId: React.Dispatch<React.SetStateAction<string>>;
|
||||
setProjectName: React.Dispatch<React.SetStateAction<string>>;
|
||||
setProjectDesc: React.Dispatch<React.SetStateAction<string>>;
|
||||
setFeatureLimit: React.Dispatch<React.SetStateAction<string>>;
|
||||
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<IProjectForm> = ({
|
||||
children,
|
||||
handleSubmit,
|
||||
@ -71,16 +94,20 @@ const ProjectForm: React.FC<IProjectForm> = ({
|
||||
projectDesc,
|
||||
projectStickiness,
|
||||
projectMode,
|
||||
featureLimit,
|
||||
featureCount,
|
||||
setProjectId,
|
||||
setProjectName,
|
||||
setProjectDesc,
|
||||
setProjectStickiness,
|
||||
setProjectMode,
|
||||
setFeatureLimit,
|
||||
errors,
|
||||
mode,
|
||||
validateProjectId,
|
||||
clearErrors,
|
||||
}) => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
return (
|
||||
<StyledForm onSubmit={handleSubmit}>
|
||||
<StyledContainer>
|
||||
@ -158,7 +185,7 @@ const ProjectForm: React.FC<IProjectForm> = ({
|
||||
<p>What is your project collaboration mode?</p>
|
||||
<CollaborationModeTooltip />
|
||||
</Box>
|
||||
<Select
|
||||
<StyledSelect
|
||||
id="project-mode"
|
||||
value={projectMode}
|
||||
label="Project collaboration mode"
|
||||
@ -170,11 +197,54 @@ const ProjectForm: React.FC<IProjectForm> = ({
|
||||
{ key: 'open', label: 'open' },
|
||||
{ key: 'protected', label: 'protected' },
|
||||
]}
|
||||
style={{ minWidth: '200px' }}
|
||||
></Select>
|
||||
></StyledSelect>
|
||||
</>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(uiConfig.flags.newProjectLayout)}
|
||||
show={
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: 1,
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<p>Feature toggles limit?</p>
|
||||
<FeatureTogglesLimitTooltip />
|
||||
</Box>
|
||||
<StyledSubtitle>
|
||||
Leave it empty if you don’t want to add a limit
|
||||
</StyledSubtitle>
|
||||
<StyledInputContainer>
|
||||
<StyledInput
|
||||
label={'Limit'}
|
||||
name="value"
|
||||
type={'number'}
|
||||
value={featureLimit}
|
||||
onChange={e =>
|
||||
setFeatureLimit(e.target.value)
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={
|
||||
featureCount !== undefined &&
|
||||
featureLimit !== undefined &&
|
||||
featureLimit.length > 0
|
||||
}
|
||||
show={
|
||||
<Box>
|
||||
({featureCount} of {featureLimit}{' '}
|
||||
used)
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</StyledContainer>
|
||||
|
||||
<StyledButtonContainer>{children}</StyledButtonContainer>
|
||||
</StyledForm>
|
||||
);
|
||||
|
@ -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}
|
||||
>
|
||||
|
@ -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<ProjectMode>(initialProjectMode);
|
||||
const [featureLimit, setFeatureLimit] =
|
||||
useState<string>(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,
|
||||
|
Loading…
Reference in New Issue
Block a user