mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +02:00
Integrations - frontend adjustments (#4527)
## About the changes - [x] Create a feature flag - [x] Rename page title - [x] Rename menu item - [x] Update frontend URL (add redirect from old one) https://linear.app/unleash/issue/1-1263/integrations-frontend-adjustments
This commit is contained in:
parent
8a3889d570
commit
573518e48d
@ -1,19 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons';
|
|
||||||
import { AvailableAddons } from './AvailableAddons/AvailableAddons';
|
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
|
||||||
|
|
||||||
export const AddonList = () => {
|
|
||||||
const { providers, addons, loading } = useAddons();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={addons.length > 0}
|
|
||||||
show={<ConfiguredAddons />}
|
|
||||||
/>
|
|
||||||
<AvailableAddons loading={loading} providers={providers} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
|
|
||||||
|
export const AddonRedirect = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
navigate(`/integrations${location.pathname.replace('/addons', '')}`);
|
||||||
|
}, [location.pathname, navigate]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContent>
|
||||||
|
Addons where renamed to{' '}
|
||||||
|
<Link to="/integrations">/integrations</Link>
|
||||||
|
</PageContent>
|
||||||
|
);
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||||
import { AddonForm } from '../AddonForm/AddonForm';
|
import { IntegrationForm } from '../IntegrationForm/IntegrationForm';
|
||||||
import cloneDeep from 'lodash.clonedeep';
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
import { IAddon } from 'interfaces/addons';
|
import { IAddon } from 'interfaces/addons';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
@ -14,7 +14,7 @@ export const DEFAULT_DATA = {
|
|||||||
environments: [],
|
environments: [],
|
||||||
} as unknown as IAddon; // TODO: improve type
|
} as unknown as IAddon; // TODO: improve type
|
||||||
|
|
||||||
export const CreateAddon = () => {
|
export const CreateIntegration = () => {
|
||||||
const providerId = useRequiredPathParam('providerId');
|
const providerId = useRequiredPathParam('providerId');
|
||||||
const { providers, refetchAddons } = useAddons();
|
const { providers, refetchAddons } = useAddons();
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ export const CreateAddon = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AddonForm
|
<IntegrationForm
|
||||||
editMode={editMode}
|
editMode={editMode}
|
||||||
provider={provider}
|
provider={provider}
|
||||||
fetch={refetchAddons}
|
fetch={refetchAddons}
|
@ -1,11 +1,11 @@
|
|||||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||||
import { AddonForm } from '../AddonForm/AddonForm';
|
import { IntegrationForm } from '../IntegrationForm/IntegrationForm';
|
||||||
import cloneDeep from 'lodash.clonedeep';
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
import { IAddon } from 'interfaces/addons';
|
import { IAddon } from 'interfaces/addons';
|
||||||
import { DEFAULT_DATA } from '../CreateAddon/CreateAddon';
|
import { DEFAULT_DATA } from '../CreateIntegration/CreateIntegration';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
|
|
||||||
export const EditAddon = () => {
|
export const EditIntegration = () => {
|
||||||
const addonId = useRequiredPathParam('addonId');
|
const addonId = useRequiredPathParam('addonId');
|
||||||
const { providers, addons, refetchAddons } = useAddons();
|
const { providers, addons, refetchAddons } = useAddons();
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ export const EditAddon = () => {
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AddonForm
|
<IntegrationForm
|
||||||
editMode={editMode}
|
editMode={editMode}
|
||||||
provider={provider}
|
provider={provider}
|
||||||
fetch={refetchAddons}
|
fetch={refetchAddons}
|
@ -16,8 +16,8 @@ import {
|
|||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import { trim } from 'component/common/util';
|
import { trim } from 'component/common/util';
|
||||||
import { IAddon, IAddonProvider } from 'interfaces/addons';
|
import { IAddon, IAddonProvider } from 'interfaces/addons';
|
||||||
import { AddonParameters } from './AddonParameters/AddonParameters';
|
import { IntegrationParameters } from './IntegrationParameters/IntegrationParameters';
|
||||||
import { AddonInstall } from './AddonInstall/AddonInstall';
|
import { IntegrationInstall } from './IntegrationInstall/IntegrationInstall';
|
||||||
import cloneDeep from 'lodash.clonedeep';
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import useAddonsApi from 'hooks/api/actions/useAddonsApi/useAddonsApi';
|
import useAddonsApi from 'hooks/api/actions/useAddonsApi/useAddonsApi';
|
||||||
@ -25,7 +25,7 @@ import useToast from 'hooks/useToast';
|
|||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||||
import { AddonMultiSelector } from './AddonMultiSelector/AddonMultiSelector';
|
import { IntegrationMultiSelector } from './IntegrationMultiSelector/IntegrationMultiSelector';
|
||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||||
@ -42,7 +42,7 @@ import {
|
|||||||
StyledContainer,
|
StyledContainer,
|
||||||
StyledButtonContainer,
|
StyledButtonContainer,
|
||||||
StyledButtonSection,
|
StyledButtonSection,
|
||||||
} from './AddonForm.styles';
|
} from './IntegrationForm.styles';
|
||||||
import { useTheme } from '@mui/system';
|
import { useTheme } from '@mui/system';
|
||||||
import { GO_BACK } from 'constants/navigate';
|
import { GO_BACK } from 'constants/navigate';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
@ -54,7 +54,7 @@ interface IAddonFormProps {
|
|||||||
editMode: boolean;
|
editMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddonForm: VFC<IAddonFormProps> = ({
|
export const IntegrationForm: VFC<IAddonFormProps> = ({
|
||||||
editMode,
|
editMode,
|
||||||
provider,
|
provider,
|
||||||
addon: initialValues,
|
addon: initialValues,
|
||||||
@ -272,7 +272,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
|
|||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(installation)}
|
condition={Boolean(installation)}
|
||||||
show={() => (
|
show={() => (
|
||||||
<AddonInstall
|
<IntegrationInstall
|
||||||
url={installation!.url}
|
url={installation!.url}
|
||||||
title={installation!.title}
|
title={installation!.title}
|
||||||
helpText={installation!.helpText}
|
helpText={installation!.helpText}
|
||||||
@ -321,7 +321,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
|
|||||||
</StyledFormSection>
|
</StyledFormSection>
|
||||||
|
|
||||||
<StyledFormSection>
|
<StyledFormSection>
|
||||||
<AddonMultiSelector
|
<IntegrationMultiSelector
|
||||||
options={selectableEvents || []}
|
options={selectableEvents || []}
|
||||||
selectedItems={formValues.events}
|
selectedItems={formValues.events}
|
||||||
onChange={setEventValues}
|
onChange={setEventValues}
|
||||||
@ -333,7 +333,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
|
|||||||
/>
|
/>
|
||||||
</StyledFormSection>
|
</StyledFormSection>
|
||||||
<StyledFormSection>
|
<StyledFormSection>
|
||||||
<AddonMultiSelector
|
<IntegrationMultiSelector
|
||||||
options={selectableProjects}
|
options={selectableProjects}
|
||||||
selectedItems={formValues.projects || []}
|
selectedItems={formValues.projects || []}
|
||||||
onChange={setProjects}
|
onChange={setProjects}
|
||||||
@ -342,7 +342,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
|
|||||||
/>
|
/>
|
||||||
</StyledFormSection>
|
</StyledFormSection>
|
||||||
<StyledFormSection>
|
<StyledFormSection>
|
||||||
<AddonMultiSelector
|
<IntegrationMultiSelector
|
||||||
options={selectableEnvironments}
|
options={selectableEnvironments}
|
||||||
selectedItems={formValues.environments || []}
|
selectedItems={formValues.environments || []}
|
||||||
onChange={setEnvironments}
|
onChange={setEnvironments}
|
||||||
@ -351,7 +351,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
|
|||||||
/>
|
/>
|
||||||
</StyledFormSection>
|
</StyledFormSection>
|
||||||
<StyledFormSection>
|
<StyledFormSection>
|
||||||
<AddonParameters
|
<IntegrationParameters
|
||||||
provider={provider}
|
provider={provider}
|
||||||
config={formValues}
|
config={formValues}
|
||||||
parametersErrors={errors.parameters}
|
parametersErrors={errors.parameters}
|
@ -3,7 +3,7 @@ import {
|
|||||||
StyledFormSection,
|
StyledFormSection,
|
||||||
StyledHelpText,
|
StyledHelpText,
|
||||||
StyledTitle,
|
StyledTitle,
|
||||||
} from '../AddonForm.styles';
|
} from '../IntegrationForm.styles';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export interface IAddonInstallProps {
|
|||||||
helpText?: string;
|
helpText?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddonInstall = ({
|
export const IntegrationInstall = ({
|
||||||
url,
|
url,
|
||||||
title = 'Install addon',
|
title = 'Install addon',
|
||||||
helpText = 'Click this button to install this addon.',
|
helpText = 'Click this button to install this addon.',
|
@ -4,15 +4,15 @@ import { screen, waitFor, within } from '@testing-library/react';
|
|||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { render } from 'utils/testRenderer';
|
import { render } from 'utils/testRenderer';
|
||||||
import {
|
import {
|
||||||
IAddonMultiSelectorProps,
|
IIntegrationMultiSelectorProps,
|
||||||
AddonMultiSelector,
|
IntegrationMultiSelector,
|
||||||
} from './AddonMultiSelector';
|
} from './IntegrationMultiSelector';
|
||||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||||
|
|
||||||
const onChange = vi.fn();
|
const onChange = vi.fn();
|
||||||
const onFocus = vi.fn();
|
const onFocus = vi.fn();
|
||||||
|
|
||||||
const mockProps: IAddonMultiSelectorProps = {
|
const mockProps: IIntegrationMultiSelectorProps = {
|
||||||
options: [
|
options: [
|
||||||
{ label: 'Project1', value: 'project1' },
|
{ label: 'Project1', value: 'project1' },
|
||||||
{ label: 'Project2', value: 'project2' },
|
{ label: 'Project2', value: 'project2' },
|
||||||
@ -35,7 +35,9 @@ describe('AddonMultiSelector', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders with default state', () => {
|
it('renders with default state', () => {
|
||||||
render(<AddonMultiSelector {...mockProps} selectedItems={['*']} />);
|
render(
|
||||||
|
<IntegrationMultiSelector {...mockProps} selectedItems={['*']} />
|
||||||
|
);
|
||||||
|
|
||||||
const checkbox = screen.getByLabelText(
|
const checkbox = screen.getByLabelText(
|
||||||
/all current and future projects/i
|
/all current and future projects/i
|
||||||
@ -49,7 +51,9 @@ describe('AddonMultiSelector', () => {
|
|||||||
|
|
||||||
it('can toggle "ALL" checkbox', async () => {
|
it('can toggle "ALL" checkbox', async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(<AddonMultiSelector {...mockProps} selectedItems={['*']} />);
|
render(
|
||||||
|
<IntegrationMultiSelector {...mockProps} selectedItems={['*']} />
|
||||||
|
);
|
||||||
|
|
||||||
await user.click(screen.getByTestId('select-all-projects'));
|
await user.click(screen.getByTestId('select-all-projects'));
|
||||||
|
|
||||||
@ -70,7 +74,10 @@ describe('AddonMultiSelector', () => {
|
|||||||
|
|
||||||
it('renders with autocomplete enabled if default value is not a wildcard', () => {
|
it('renders with autocomplete enabled if default value is not a wildcard', () => {
|
||||||
render(
|
render(
|
||||||
<AddonMultiSelector {...mockProps} selectedItems={['project1']} />
|
<IntegrationMultiSelector
|
||||||
|
{...mockProps}
|
||||||
|
selectedItems={['project1']}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const checkbox = screen.getByLabelText(
|
const checkbox = screen.getByLabelText(
|
||||||
@ -87,7 +94,7 @@ describe('AddonMultiSelector', () => {
|
|||||||
it("doesn't show up for less than 3 options", async () => {
|
it("doesn't show up for less than 3 options", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(
|
render(
|
||||||
<AddonMultiSelector
|
<IntegrationMultiSelector
|
||||||
{...mockProps}
|
{...mockProps}
|
||||||
selectedItems={[]}
|
selectedItems={[]}
|
||||||
options={[
|
options={[
|
||||||
@ -108,7 +115,7 @@ describe('AddonMultiSelector', () => {
|
|||||||
it('can filter options', async () => {
|
it('can filter options', async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(
|
render(
|
||||||
<AddonMultiSelector
|
<IntegrationMultiSelector
|
||||||
{...mockProps}
|
{...mockProps}
|
||||||
selectedItems={[]}
|
selectedItems={[]}
|
||||||
options={[
|
options={[
|
@ -22,9 +22,9 @@ import {
|
|||||||
StyledHelpText,
|
StyledHelpText,
|
||||||
StyledSelectAllFormControlLabel,
|
StyledSelectAllFormControlLabel,
|
||||||
StyledTitle,
|
StyledTitle,
|
||||||
} from '../AddonForm.styles';
|
} from '../IntegrationForm.styles';
|
||||||
|
|
||||||
export interface IAddonMultiSelectorProps {
|
export interface IIntegrationMultiSelectorProps {
|
||||||
options: IAutocompleteBoxOption[];
|
options: IAutocompleteBoxOption[];
|
||||||
selectedItems: string[];
|
selectedItems: string[];
|
||||||
onChange: (value: string[]) => void;
|
onChange: (value: string[]) => void;
|
||||||
@ -44,7 +44,7 @@ const StyledCheckbox = styled(Checkbox)(() => ({
|
|||||||
|
|
||||||
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
|
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
|
||||||
|
|
||||||
export const AddonMultiSelector: VFC<IAddonMultiSelectorProps> = ({
|
export const IntegrationMultiSelector: VFC<IIntegrationMultiSelectorProps> = ({
|
||||||
options,
|
options,
|
||||||
selectedItems,
|
selectedItems,
|
||||||
onChange,
|
onChange,
|
||||||
@ -138,9 +138,9 @@ export const AddonMultiSelector: VFC<IAddonMultiSelectorProps> = ({
|
|||||||
|
|
||||||
const DefaultHelpText = () => (
|
const DefaultHelpText = () => (
|
||||||
<StyledHelpText>
|
<StyledHelpText>
|
||||||
Selecting {entityName}(s) here will filter events so that your addon
|
Selecting {entityName}(s) here will filter events so that your
|
||||||
will only receive events that are tagged with one of your{' '}
|
integration will only receive events that are tagged with one of
|
||||||
{entityName}s.
|
your {entityName}s.
|
||||||
</StyledHelpText>
|
</StyledHelpText>
|
||||||
);
|
);
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
import { TextField, Typography } from '@mui/material';
|
import { TextField, Typography } from '@mui/material';
|
||||||
import { IAddonConfig, IAddonProviderParams } from 'interfaces/addons';
|
import { IAddonConfig, IAddonProviderParams } from 'interfaces/addons';
|
||||||
import { ChangeEventHandler } from 'react';
|
import { ChangeEventHandler } from 'react';
|
||||||
import { StyledAddonParameterContainer } from '../../AddonForm.styles';
|
import { StyledAddonParameterContainer } from '../../IntegrationForm.styles';
|
||||||
|
|
||||||
const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
|
const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
|
||||||
if (sensitive && value === MASKED_VALUE) {
|
if (sensitive && value === MASKED_VALUE) {
|
||||||
@ -15,19 +15,19 @@ const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
|
|||||||
|
|
||||||
const MASKED_VALUE = '*****';
|
const MASKED_VALUE = '*****';
|
||||||
|
|
||||||
export interface IAddonParameterProps {
|
export interface IIntegrationParameterProps {
|
||||||
parametersErrors: Record<string, string>;
|
parametersErrors: Record<string, string>;
|
||||||
definition: IAddonProviderParams;
|
definition: IAddonProviderParams;
|
||||||
setParameterValue: (param: string) => ChangeEventHandler<HTMLInputElement>;
|
setParameterValue: (param: string) => ChangeEventHandler<HTMLInputElement>;
|
||||||
config: IAddonConfig;
|
config: IAddonConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddonParameter = ({
|
export const IntegrationParameter = ({
|
||||||
definition,
|
definition,
|
||||||
config,
|
config,
|
||||||
parametersErrors,
|
parametersErrors,
|
||||||
setParameterValue,
|
setParameterValue,
|
||||||
}: IAddonParameterProps) => {
|
}: IIntegrationParameterProps) => {
|
||||||
const value = config.parameters[definition.name] || '';
|
const value = config.parameters[definition.name] || '';
|
||||||
const type = resolveType(definition, value);
|
const type = resolveType(definition, value);
|
||||||
const error = parametersErrors[definition.name];
|
const error = parametersErrors[definition.name];
|
@ -1,26 +1,26 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IAddonProvider } from 'interfaces/addons';
|
import { IAddonProvider } from 'interfaces/addons';
|
||||||
import {
|
import {
|
||||||
AddonParameter,
|
IntegrationParameter,
|
||||||
IAddonParameterProps,
|
IIntegrationParameterProps,
|
||||||
} from './AddonParameter/AddonParameter';
|
} from './IntegrationParameter/IntegrationParameter';
|
||||||
import { StyledTitle } from '../AddonForm.styles';
|
import { StyledTitle } from '../IntegrationForm.styles';
|
||||||
|
|
||||||
interface IAddonParametersProps {
|
interface IIntegrationParametersProps {
|
||||||
provider?: IAddonProvider;
|
provider?: IAddonProvider;
|
||||||
parametersErrors: IAddonParameterProps['parametersErrors'];
|
parametersErrors: IIntegrationParameterProps['parametersErrors'];
|
||||||
editMode: boolean;
|
editMode: boolean;
|
||||||
setParameterValue: IAddonParameterProps['setParameterValue'];
|
setParameterValue: IIntegrationParameterProps['setParameterValue'];
|
||||||
config: IAddonParameterProps['config'];
|
config: IIntegrationParameterProps['config'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddonParameters = ({
|
export const IntegrationParameters = ({
|
||||||
provider,
|
provider,
|
||||||
config,
|
config,
|
||||||
parametersErrors,
|
parametersErrors,
|
||||||
setParameterValue,
|
setParameterValue,
|
||||||
editMode,
|
editMode,
|
||||||
}: IAddonParametersProps) => {
|
}: IIntegrationParametersProps) => {
|
||||||
if (!provider) return null;
|
if (!provider) return null;
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@ -33,7 +33,7 @@ export const AddonParameters = ({
|
|||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{provider.parameters.map(parameter => (
|
{provider.parameters.map(parameter => (
|
||||||
<AddonParameter
|
<IntegrationParameter
|
||||||
key={parameter.name}
|
key={parameter.name}
|
||||||
definition={parameter}
|
definition={parameter}
|
||||||
parametersErrors={parametersErrors}
|
parametersErrors={parametersErrors}
|
@ -16,10 +16,11 @@ import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
|||||||
import { sortTypes } from 'utils/sortTypes';
|
import { sortTypes } from 'utils/sortTypes';
|
||||||
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
||||||
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
||||||
import { ConfigureAddonButton } from './ConfigureAddonButton/ConfigureAddonButton';
|
import { ConfigureAddonsButton } from './ConfigureAddonButton/ConfigureAddonsButton';
|
||||||
import { AddonIcon } from '../AddonIcon/AddonIcon';
|
import { IntegrationIcon } from '../IntegrationIcon/IntegrationIcon';
|
||||||
import { AddonNameCell } from '../AddonNameCell/AddonNameCell';
|
import { IntegrationNameCell } from '../IntegrationNameCell/IntegrationNameCell';
|
||||||
import { IAddonInstallation } from 'interfaces/addons';
|
import { IAddonInstallation } from 'interfaces/addons';
|
||||||
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
|
||||||
interface IProvider {
|
interface IProvider {
|
||||||
name: string;
|
name: string;
|
||||||
@ -37,6 +38,9 @@ interface IAvailableAddonsProps {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Remove when integrationsRework flag is removed
|
||||||
|
*/
|
||||||
export const AvailableAddons = ({
|
export const AvailableAddons = ({
|
||||||
providers,
|
providers,
|
||||||
loading,
|
loading,
|
||||||
@ -70,7 +74,9 @@ export const AvailableAddons = ({
|
|||||||
},
|
},
|
||||||
}: any) => {
|
}: any) => {
|
||||||
return (
|
return (
|
||||||
<IconCell icon={<AddonIcon name={name as string} />} />
|
<IconCell
|
||||||
|
icon={<IntegrationIcon name={name as string} />}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -79,7 +85,7 @@ export const AvailableAddons = ({
|
|||||||
accessor: 'name',
|
accessor: 'name',
|
||||||
width: '90%',
|
width: '90%',
|
||||||
Cell: ({ row: { original } }: any) => (
|
Cell: ({ row: { original } }: any) => (
|
||||||
<AddonNameCell provider={original} />
|
<IntegrationNameCell provider={original} />
|
||||||
),
|
),
|
||||||
sortType: 'alphanumeric',
|
sortType: 'alphanumeric',
|
||||||
},
|
},
|
||||||
@ -88,7 +94,7 @@ export const AvailableAddons = ({
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
Cell: ({ row: { original } }: any) => (
|
Cell: ({ row: { original } }: any) => (
|
||||||
<ActionCell>
|
<ActionCell>
|
||||||
<ConfigureAddonButton provider={original} />
|
<ConfigureAddonsButton provider={original} />
|
||||||
</ActionCell>
|
</ActionCell>
|
||||||
),
|
),
|
||||||
width: 150,
|
width: 150,
|
@ -3,13 +3,16 @@ import { CREATE_ADDON } from 'component/providers/AccessProvider/permissions';
|
|||||||
import { IAddonProvider } from 'interfaces/addons';
|
import { IAddonProvider } from 'interfaces/addons';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
interface IConfigureAddonButtonProps {
|
interface IConfigureAddonsButtonProps {
|
||||||
provider: IAddonProvider;
|
provider: IAddonProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConfigureAddonButton = ({
|
/**
|
||||||
|
* @deprecated Remove when integrationsRework flag is removed
|
||||||
|
*/
|
||||||
|
export const ConfigureAddonsButton = ({
|
||||||
provider,
|
provider,
|
||||||
}: IConfigureAddonButtonProps) => {
|
}: IConfigureAddonsButtonProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
@ -0,0 +1,8 @@
|
|||||||
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
|
import { VFC } from 'react';
|
||||||
|
|
||||||
|
interface IAvailableIntegrationsProps {}
|
||||||
|
|
||||||
|
export const AvailableIntegrations: VFC<IAvailableIntegrationsProps> = () => {
|
||||||
|
return <PageContent>Available integrations</PageContent>;
|
||||||
|
};
|
@ -13,10 +13,13 @@ import { useTable, useSortBy } from 'react-table';
|
|||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import { SortableTableHeader, TablePlaceholder } from 'component/common/Table';
|
import { SortableTableHeader, TablePlaceholder } from 'component/common/Table';
|
||||||
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
||||||
import { AddonIcon } from '../AddonIcon/AddonIcon';
|
import { IntegrationIcon } from '../IntegrationIcon/IntegrationIcon';
|
||||||
import { ConfiguredAddonsActionsCell } from './ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell';
|
import { ConfiguredAddonsActionsCell } from './ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell';
|
||||||
import { AddonNameCell } from '../AddonNameCell/AddonNameCell';
|
import { IntegrationNameCell } from '../IntegrationNameCell/IntegrationNameCell';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Remove when integrationsRework flag is removed
|
||||||
|
*/
|
||||||
export const ConfiguredAddons = () => {
|
export const ConfiguredAddons = () => {
|
||||||
const { refetchAddons, addons, providers, loading } = useAddons();
|
const { refetchAddons, addons, providers, loading } = useAddons();
|
||||||
const { updateAddon, removeAddon } = useAddonsApi();
|
const { updateAddon, removeAddon } = useAddonsApi();
|
||||||
@ -73,7 +76,9 @@ export const ConfiguredAddons = () => {
|
|||||||
original: { provider },
|
original: { provider },
|
||||||
},
|
},
|
||||||
}: any) => (
|
}: any) => (
|
||||||
<IconCell icon={<AddonIcon name={provider as string} />} />
|
<IconCell
|
||||||
|
icon={<IntegrationIcon name={provider as string} />}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
},
|
},
|
||||||
@ -86,7 +91,7 @@ export const ConfiguredAddons = () => {
|
|||||||
original: { provider, description },
|
original: { provider, description },
|
||||||
},
|
},
|
||||||
}: any) => (
|
}: any) => (
|
||||||
<AddonNameCell
|
<IntegrationNameCell
|
||||||
provider={{
|
provider={{
|
||||||
...(providers.find(
|
...(providers.find(
|
||||||
({ name }) => name === provider
|
({ name }) => name === provider
|
@ -18,6 +18,9 @@ interface IConfiguredAddonsActionsCellProps {
|
|||||||
setDeletedAddon: React.Dispatch<React.SetStateAction<IAddon>>;
|
setDeletedAddon: React.Dispatch<React.SetStateAction<IAddon>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Remove when integrationsRework flag is removed
|
||||||
|
*/
|
||||||
export const ConfiguredAddonsActionsCell = ({
|
export const ConfiguredAddonsActionsCell = ({
|
||||||
toggleAddon,
|
toggleAddon,
|
||||||
setShowDelete,
|
setShowDelete,
|
@ -14,11 +14,11 @@ const style: React.CSSProperties = {
|
|||||||
marginRight: '16px',
|
marginRight: '16px',
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IAddonIconProps {
|
interface IIntegrationIconProps {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddonIcon = ({ name }: IAddonIconProps) => {
|
export const IntegrationIcon = ({ name }: IIntegrationIconProps) => {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'slack':
|
case 'slack':
|
||||||
case 'slack-app':
|
case 'slack-app':
|
@ -0,0 +1,28 @@
|
|||||||
|
import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons';
|
||||||
|
import { AvailableAddons } from './AvailableAddons/AvailableAddons';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||||
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import { AvailableIntegrations } from './AvailableIntegrations/AvailableIntegrations';
|
||||||
|
|
||||||
|
export const IntegrationList = () => {
|
||||||
|
const { providers, addons, loading } = useAddons();
|
||||||
|
const { uiConfig } = useUiConfig();
|
||||||
|
const integrationsRework = uiConfig?.flags?.integrationsRework || false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={addons.length > 0}
|
||||||
|
show={<ConfiguredAddons />}
|
||||||
|
/>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={integrationsRework}
|
||||||
|
show={<AvailableIntegrations />}
|
||||||
|
elseShow={
|
||||||
|
<AvailableAddons loading={loading} providers={providers} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -10,14 +10,19 @@ const StyledBadge = styled(Badge)(({ theme }) => ({
|
|||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IAddonNameCellProps {
|
interface IIntegrationNameCellProps {
|
||||||
provider: Pick<
|
provider: Pick<
|
||||||
IAddonProvider,
|
IAddonProvider,
|
||||||
'displayName' | 'description' | 'deprecated'
|
'displayName' | 'description' | 'deprecated'
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddonNameCell = ({ provider }: IAddonNameCellProps) => (
|
/**
|
||||||
|
* @deprecated Remove when integrationsRework flag is removed
|
||||||
|
*/
|
||||||
|
export const IntegrationNameCell = ({
|
||||||
|
provider,
|
||||||
|
}: IIntegrationNameCellProps) => (
|
||||||
<HighlightCell
|
<HighlightCell
|
||||||
value={provider.displayName}
|
value={provider.displayName}
|
||||||
subtitle={provider.description}
|
subtitle={provider.description}
|
@ -317,6 +317,34 @@ exports[`returns all baseRoutes 1`] = `
|
|||||||
"title": "Addons",
|
"title": "Addons",
|
||||||
"type": "protected",
|
"type": "protected",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"component": [Function],
|
||||||
|
"menu": {},
|
||||||
|
"parent": "/integrations",
|
||||||
|
"path": "/integrations/create/:providerId",
|
||||||
|
"title": "Create",
|
||||||
|
"type": "protected",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": [Function],
|
||||||
|
"menu": {},
|
||||||
|
"parent": "/integrations",
|
||||||
|
"path": "/integrations/edit/:addonId",
|
||||||
|
"title": "Edit",
|
||||||
|
"type": "protected",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": [Function],
|
||||||
|
"flag": "integrationsRework",
|
||||||
|
"hidden": false,
|
||||||
|
"menu": {
|
||||||
|
"advanced": true,
|
||||||
|
"mobile": true,
|
||||||
|
},
|
||||||
|
"path": "/integrations",
|
||||||
|
"title": "Integrations",
|
||||||
|
"type": "protected",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"component": [Function],
|
"component": [Function],
|
||||||
"flag": "SE",
|
"flag": "SE",
|
||||||
|
@ -2,7 +2,7 @@ import { FeatureToggleListTable } from 'component/feature/FeatureToggleList/Feat
|
|||||||
import { StrategyView } from 'component/strategies/StrategyView/StrategyView';
|
import { StrategyView } from 'component/strategies/StrategyView/StrategyView';
|
||||||
import { StrategiesList } from 'component/strategies/StrategiesList/StrategiesList';
|
import { StrategiesList } from 'component/strategies/StrategiesList/StrategiesList';
|
||||||
import { TagTypeList } from 'component/tags/TagTypeList/TagTypeList';
|
import { TagTypeList } from 'component/tags/TagTypeList/TagTypeList';
|
||||||
import { AddonList } from 'component/addons/AddonList/AddonList';
|
import { IntegrationList } from 'component/integrations/IntegrationList/IntegrationList';
|
||||||
import Login from 'component/user/Login/Login';
|
import Login from 'component/user/Login/Login';
|
||||||
import { EEA, P, SE } from 'component/common/flags';
|
import { EEA, P, SE } from 'component/common/flags';
|
||||||
import { NewUser } from 'component/user/NewUser/NewUser';
|
import { NewUser } from 'component/user/NewUser/NewUser';
|
||||||
@ -21,8 +21,8 @@ import EditFeature from 'component/feature/EditFeature/EditFeature';
|
|||||||
import { ApplicationEdit } from 'component/application/ApplicationEdit/ApplicationEdit';
|
import { ApplicationEdit } from 'component/application/ApplicationEdit/ApplicationEdit';
|
||||||
import ContextList from 'component/context/ContextList/ContextList/ContextList';
|
import ContextList from 'component/context/ContextList/ContextList/ContextList';
|
||||||
import RedirectFeatureView from 'component/feature/RedirectFeatureView/RedirectFeatureView';
|
import RedirectFeatureView from 'component/feature/RedirectFeatureView/RedirectFeatureView';
|
||||||
import { CreateAddon } from 'component/addons/CreateAddon/CreateAddon';
|
import { CreateIntegration } from 'component/integrations/CreateIntegration/CreateIntegration';
|
||||||
import { EditAddon } from 'component/addons/EditAddon/EditAddon';
|
import { EditIntegration } from 'component/integrations/EditIntegration/EditIntegration';
|
||||||
import { CopyFeatureToggle } from 'component/feature/CopyFeature/CopyFeature';
|
import { CopyFeatureToggle } from 'component/feature/CopyFeature/CopyFeature';
|
||||||
import { EventPage } from 'component/events/EventPage/EventPage';
|
import { EventPage } from 'component/events/EventPage/EventPage';
|
||||||
import { CreateStrategy } from 'component/strategies/CreateStrategy/CreateStrategy';
|
import { CreateStrategy } from 'component/strategies/CreateStrategy/CreateStrategy';
|
||||||
@ -300,12 +300,13 @@ export const routes: IRoute[] = [
|
|||||||
menu: { mobile: true, advanced: true },
|
menu: { mobile: true, advanced: true },
|
||||||
},
|
},
|
||||||
|
|
||||||
// Addons
|
// Integrations
|
||||||
{
|
{
|
||||||
path: '/addons/create/:providerId',
|
path: '/addons/create/:providerId',
|
||||||
parent: '/addons',
|
parent: '/addons',
|
||||||
title: 'Create',
|
title: 'Create',
|
||||||
component: CreateAddon,
|
component: CreateIntegration,
|
||||||
|
// TODO: use AddonRedirect after removing `integrationsRework` menu flag
|
||||||
type: 'protected',
|
type: 'protected',
|
||||||
menu: {},
|
menu: {},
|
||||||
},
|
},
|
||||||
@ -313,17 +314,45 @@ export const routes: IRoute[] = [
|
|||||||
path: '/addons/edit/:addonId',
|
path: '/addons/edit/:addonId',
|
||||||
parent: '/addons',
|
parent: '/addons',
|
||||||
title: 'Edit',
|
title: 'Edit',
|
||||||
component: EditAddon,
|
component: EditIntegration,
|
||||||
|
// TODO: use AddonRedirect after removing `integrationsRework` menu flag
|
||||||
type: 'protected',
|
type: 'protected',
|
||||||
menu: {},
|
menu: {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/addons',
|
path: '/addons',
|
||||||
title: 'Addons',
|
title: 'Addons',
|
||||||
component: AddonList,
|
component: IntegrationList,
|
||||||
|
// TODO: use AddonRedirect after removing `integrationsRework` menu flag
|
||||||
hidden: false,
|
hidden: false,
|
||||||
type: 'protected',
|
type: 'protected',
|
||||||
menu: { mobile: true, advanced: true },
|
menu: { mobile: true, advanced: true },
|
||||||
|
// TODO: remove 'addons' from menu after removing `integrationsRework` menu flag
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/integrations/create/:providerId',
|
||||||
|
parent: '/integrations',
|
||||||
|
title: 'Create',
|
||||||
|
component: CreateIntegration,
|
||||||
|
type: 'protected',
|
||||||
|
menu: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/integrations/edit/:addonId',
|
||||||
|
parent: '/integrations',
|
||||||
|
title: 'Edit',
|
||||||
|
component: EditIntegration,
|
||||||
|
type: 'protected',
|
||||||
|
menu: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/integrations',
|
||||||
|
title: 'Integrations',
|
||||||
|
component: IntegrationList,
|
||||||
|
hidden: false,
|
||||||
|
type: 'protected',
|
||||||
|
menu: { mobile: true, advanced: true },
|
||||||
|
flag: 'integrationsRework',
|
||||||
},
|
},
|
||||||
|
|
||||||
// Segments
|
// Segments
|
||||||
|
@ -58,6 +58,7 @@ export interface IFlags {
|
|||||||
changeRequestReject?: boolean;
|
changeRequestReject?: boolean;
|
||||||
lastSeenByEnvironment?: boolean;
|
lastSeenByEnvironment?: boolean;
|
||||||
newApplicationList?: boolean;
|
newApplicationList?: boolean;
|
||||||
|
integrationsRework?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVersionInfo {
|
export interface IVersionInfo {
|
||||||
|
@ -82,6 +82,7 @@ exports[`should create default config 1`] = `
|
|||||||
"filterInvalidClientMetrics": false,
|
"filterInvalidClientMetrics": false,
|
||||||
"frontendNavigationUpdate": false,
|
"frontendNavigationUpdate": false,
|
||||||
"googleAuthEnabled": false,
|
"googleAuthEnabled": false,
|
||||||
|
"integrationsRework": false,
|
||||||
"lastSeenByEnvironment": false,
|
"lastSeenByEnvironment": false,
|
||||||
"maintenanceMode": false,
|
"maintenanceMode": false,
|
||||||
"messageBanner": {
|
"messageBanner": {
|
||||||
@ -118,6 +119,7 @@ exports[`should create default config 1`] = `
|
|||||||
"filterInvalidClientMetrics": false,
|
"filterInvalidClientMetrics": false,
|
||||||
"frontendNavigationUpdate": false,
|
"frontendNavigationUpdate": false,
|
||||||
"googleAuthEnabled": false,
|
"googleAuthEnabled": false,
|
||||||
|
"integrationsRework": false,
|
||||||
"lastSeenByEnvironment": false,
|
"lastSeenByEnvironment": false,
|
||||||
"maintenanceMode": false,
|
"maintenanceMode": false,
|
||||||
"messageBanner": {
|
"messageBanner": {
|
||||||
|
@ -29,7 +29,8 @@ export type IFlagKey =
|
|||||||
| 'segmentChangeRequests'
|
| 'segmentChangeRequests'
|
||||||
| 'changeRequestReject'
|
| 'changeRequestReject'
|
||||||
| 'customRootRolesKillSwitch'
|
| 'customRootRolesKillSwitch'
|
||||||
| 'newApplicationList';
|
| 'newApplicationList'
|
||||||
|
| 'integrationsRework';
|
||||||
|
|
||||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||||
|
|
||||||
@ -133,6 +134,10 @@ const flags: IFlags = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_CUSTOM_ROOT_ROLES_KILL_SWITCH,
|
process.env.UNLEASH_EXPERIMENTAL_CUSTOM_ROOT_ROLES_KILL_SWITCH,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
integrationsRework: parseEnvVarBoolean(
|
||||||
|
process.env.UNLEASH_INTEGRATIONS,
|
||||||
|
false,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultExperimentalOptions: IExperimentalOptions = {
|
export const defaultExperimentalOptions: IExperimentalOptions = {
|
||||||
|
@ -13,6 +13,7 @@ yarn install
|
|||||||
```console
|
```console
|
||||||
yarn generate
|
yarn generate
|
||||||
```
|
```
|
||||||
|
|
||||||
Generate the Open API docs that live at Reference documentation > APIs > OpenAPI
|
Generate the Open API docs that live at Reference documentation > APIs > OpenAPI
|
||||||
|
|
||||||
## Local Development
|
## Local Development
|
||||||
@ -21,7 +22,7 @@ Generate the Open API docs that live at Reference documentation > APIs > OpenAPI
|
|||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
Start a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
Start a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user