diff --git a/frontend/src/component/addons/AddonForm/AddonForm.styles.tsx b/frontend/src/component/addons/AddonForm/AddonForm.styles.tsx new file mode 100644 index 0000000000..21b97f4357 --- /dev/null +++ b/frontend/src/component/addons/AddonForm/AddonForm.styles.tsx @@ -0,0 +1,57 @@ +import { styled } from '@mui/system'; +import { FormControlLabel, TextField } from '@mui/material'; +import Autocomplete from '@mui/material/Autocomplete'; + +export const StyledForm = styled('form')({ + display: 'flex', + flexDirection: 'column', + height: '100%', + gap: '1rem', +}); + +export const StyledAutocomplete = styled(Autocomplete)({ + paddingBottom: '36px', + marginTop: '0px', +}); +export const StyledFormSection = styled('section')({ + marginBottom: '36px', +}); + +export const StyledHelpText = styled('p')({ + marginBottom: '0.5rem', +}); + +export const StyledContainer = styled('div')({ + maxWidth: '600px', +}); + +export const StyledButtonContainer = styled('div')({ + marginTop: 'auto', + display: 'flex', + justifyContent: 'flex-end', +}); + +export const StyledButtonSection = styled('section')(({ theme }) => ({ + 'padding-top': '16px', + '& > *': { + marginRight: theme.spacing(1), + }, +})); + +export const StyledTextField = styled(TextField)({ + width: '100%', + marginBottom: '1rem', + marginTop: '0px', +}); + +export const StyledSelectAllFormControlLabel = styled(FormControlLabel)({ + paddingBottom: '16px', +}); + +export const StyledTitle = styled('h4')({ + marginBottom: '8px', +}); + +export const StyledAddonParameterContainer = styled('div')({ + marginTop: '25px', +}); diff --git a/frontend/src/component/addons/AddonForm/AddonForm.tsx b/frontend/src/component/addons/AddonForm/AddonForm.tsx index 541d0d5528..0909ae38d6 100644 --- a/frontend/src/component/addons/AddonForm/AddonForm.tsx +++ b/frontend/src/component/addons/AddonForm/AddonForm.tsx @@ -6,35 +6,39 @@ import React, { useState, VFC, } from 'react'; -import { Button, FormControlLabel, Switch, TextField } from '@mui/material'; +import { + Button, + Divider, + FormControlLabel, + Switch, + TextField, +} from '@mui/material'; import produce from 'immer'; -import { styles as themeStyles } from 'component/common'; import { trim } from 'component/common/util'; import { IAddon, IAddonProvider } from 'interfaces/addons'; import { AddonParameters } from './AddonParameters/AddonParameters'; import cloneDeep from 'lodash.clonedeep'; -import { PageContent } from 'component/common/PageContent/PageContent'; import { useNavigate } from 'react-router-dom'; import useAddonsApi from 'hooks/api/actions/useAddonsApi/useAddonsApi'; import useToast from 'hooks/useToast'; -import { makeStyles } from 'tss-react/mui'; import { formatUnknownError } from 'utils/formatUnknownError'; import useProjects from '../../../hooks/api/getters/useProjects/useProjects'; import { useEnvironments } from '../../../hooks/api/getters/useEnvironments/useEnvironments'; import { AddonMultiSelector } from './AddonMultiSelector/AddonMultiSelector'; - -const useStyles = makeStyles()(theme => ({ - nameInput: { - marginRight: '1.5rem', - }, - formSection: { padding: '10px 28px' }, - buttonsSection: { - padding: '10px 28px', - '& > *': { - marginRight: theme.spacing(1), - }, - }, -})); +import FormTemplate from 'component/common/FormTemplate/FormTemplate'; +import useUiConfig from '../../../hooks/api/getters/useUiConfig/useUiConfig'; +import PermissionButton from '../../common/PermissionButton/PermissionButton'; +import { ADMIN } from '../../providers/AccessProvider/permissions'; +import { + StyledForm, + StyledFormSection, + StyledHelpText, + StyledTextField, + StyledContainer, + StyledButtonContainer, + StyledButtonSection, +} from './AddonForm.styles'; +import { useTheme } from '@mui/system'; interface IAddonFormProps { provider?: IAddonProvider; @@ -52,7 +56,7 @@ export const AddonForm: VFC = ({ const { createAddon, updateAddon } = useAddonsApi(); const { setToastData, setToastApiError } = useToast(); const navigate = useNavigate(); - const { classes: styles } = useStyles(); + const theme = useTheme(); const { projects: availableProjects } = useProjects(); const selectableProjects = availableProjects.map(project => ({ value: project.id, @@ -67,6 +71,7 @@ export const AddonForm: VFC = ({ value: event, label: event, })); + const { uiConfig } = useUiConfig(); const [formValues, setFormValues] = useState(initialValues); const [errors, setErrors] = useState<{ containsErrors: boolean; @@ -81,6 +86,18 @@ export const AddonForm: VFC = ({ parameters: {}, }); const submitText = editMode ? 'Update' : 'Create'; + let url = `${uiConfig.unleashUrl}/api/admin/addons${ + editMode ? `/${formValues.id}` : `` + }`; + + const formatApiCode = () => { + return `curl --location --request ${ + editMode ? 'PUT' : 'POST' + } '${url}' \\ + --header 'Authorization: INSERT_API_KEY' \\ + --header 'Content-Type: application/json' \\ + --data-raw '${JSON.stringify(formValues, undefined, 2)}'`; + }; useEffect(() => { if (!provider) { @@ -162,7 +179,9 @@ export const AddonForm: VFC = ({ const onSubmit: FormEventHandler = async event => { event.preventDefault(); - if (!provider) return; + if (!provider) { + return; + } const updatedErrors = cloneDeep(errors); updatedErrors.containsErrors = false; @@ -222,97 +241,113 @@ export const AddonForm: VFC = ({ } = provider ? provider : ({} as Partial); return ( - -
- {description}  - - Read more - -

{errors.general}

-
-
-
- - - } - label={formValues.enabled ? 'Enabled' : 'Disabled'} - /> -
-
- -
+ + + + + + + + What is your addon description? + -
- -
-
- -
-
- -
-
- -
-
- - -
- -
+ + + + + + + + + + + + + + + + + + + + + {submitText} + + + + + + ); }; diff --git a/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx b/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx index 9aaa1b5092..0d657c2b7a 100644 --- a/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx +++ b/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx @@ -12,7 +12,6 @@ import { Box, capitalize, Checkbox, - FormControlLabel, Paper, TextField, } from '@mui/material'; @@ -20,6 +19,12 @@ import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; import CheckBoxIcon from '@mui/icons-material/CheckBox'; import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender'; import { SelectAllButton } from '../../../admin/apiToken/ApiTokenForm/SelectProjectInput/SelectAllButton/SelectAllButton'; +import { + StyledHelpText, + StyledSelectAllFormControlLabel, + StyledTitle, + StyledAutocomplete, +} from '../AddonForm.styles'; export interface IAddonMultiSelectorProps { options: IAutocompleteBoxOption[]; @@ -29,6 +34,7 @@ export interface IAddonMultiSelectorProps { onFocus?: () => void; entityName: string; selectAllEnabled: boolean; + description?: string; } const ALL_OPTIONS = '*'; @@ -47,6 +53,7 @@ export const AddonMultiSelector: VFC = ({ onFocus, entityName, selectAllEnabled = true, + description, }) => { const [isWildcardSelected, selectWildcard] = useState( selectedItems.includes(ALL_OPTIONS) @@ -117,68 +124,70 @@ export const AddonMultiSelector: VFC = ({ ); const SelectAllFormControl = () => ( - - - } - label={`ALL current and future ${entityName}s`} - /> - + + } + label={`ALL current and future ${entityName}s`} + /> ); - const HelpText = () => ( -

+ const DefaultHelpText = () => ( + Selecting {entityName}(s) here will filter events so that your addon will only receive events that are tagged with one of your{' '} {entityName}s. -

+ ); return ( -

{capitalize(entityName)}s

+ {capitalize(entityName)}s + {description}} + /> } + show={} /> {error} -
- - } - /> - label} - fullWidth - groupBy={() => 'Select/Deselect all'} - renderGroup={renderGroup} - PaperComponent={CustomPaper} - renderOption={renderOption} - renderInput={renderInput} - value={ - isWildcardSelected - ? options - : options.filter(option => - selectedItems.includes(option.value) - ) - } - onChange={(_, input) => { - const state = input.map(({ value }) => value); - onChange(state); - }} - /> - + } + /> + label} + fullWidth + groupBy={() => 'Select/Deselect all'} + renderGroup={renderGroup} + PaperComponent={CustomPaper} + //@ts-expect-error + renderOption={renderOption} + renderInput={renderInput} + value={ + isWildcardSelected + ? options + : options.filter(option => + selectedItems.includes(option.value) + ) + } + onChange={(_, input) => { + //@ts-expect-error + const state = input.map(({ value }) => value); + onChange(state); + }} + />
); }; diff --git a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx b/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx index a994709bf8..9a1f3b20f8 100644 --- a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx +++ b/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx @@ -1,6 +1,7 @@ import { TextField } from '@mui/material'; import { IAddonConfig, IAddonProviderParams } from 'interfaces/addons'; import { ChangeEventHandler } from 'react'; +import { StyledAddonParameterContainer } from '../../AddonForm.styles'; const resolveType = ({ type = 'text', sensitive = false }, value: string) => { if (sensitive && value === MASKED_VALUE) { @@ -32,7 +33,7 @@ export const AddonParameter = ({ const error = parametersErrors[definition.name]; return ( -
+ -
+ ); }; diff --git a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx b/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx index 1b327000f2..fb62c6894f 100644 --- a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx +++ b/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx @@ -4,6 +4,7 @@ import { AddonParameter, IAddonParameterProps, } from './AddonParameter/AddonParameter'; +import { StyledTitle } from '../AddonForm.styles'; interface IAddonParametersProps { provider?: IAddonProvider; @@ -21,10 +22,9 @@ export const AddonParameters = ({ editMode, }: IAddonParametersProps) => { if (!provider) return null; - return ( -

Parameters

+ Parameters {editMode ? (

Sensitive parameters will be masked with value "*****