mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-28 00:06:53 +01:00
Merge pull request #648 from Unleash/feat/addons
refactor: addons to hook
This commit is contained in:
commit
014cdaa7d4
@ -1,5 +1,4 @@
|
|||||||
import React, { useContext, useEffect } from 'react';
|
import { useContext, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import ConfiguredAddons from './ConfiguredAddons';
|
import ConfiguredAddons from './ConfiguredAddons';
|
||||||
import AvailableAddons from './AvailableAddons';
|
import AvailableAddons from './AvailableAddons';
|
||||||
import { Avatar } from '@material-ui/core';
|
import { Avatar } from '@material-ui/core';
|
||||||
@ -14,6 +13,8 @@ import webhooksIcon from '../../../assets/icons/webhooks.svg';
|
|||||||
import teamsIcon from '../../../assets/icons/teams.svg';
|
import teamsIcon from '../../../assets/icons/teams.svg';
|
||||||
import dataDogIcon from '../../../assets/icons/datadog.svg';
|
import dataDogIcon from '../../../assets/icons/datadog.svg';
|
||||||
import { formatAssetPath } from '../../../utils/format-path';
|
import { formatAssetPath } from '../../../utils/format-path';
|
||||||
|
import useAddons from '../../../hooks/api/getters/useAddons/useAddons';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
width: '40px',
|
width: '40px',
|
||||||
@ -73,18 +74,14 @@ const getIcon = name => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddonList = ({
|
const AddonList = () => {
|
||||||
addons,
|
|
||||||
providers,
|
|
||||||
fetchAddons,
|
|
||||||
removeAddon,
|
|
||||||
toggleAddon,
|
|
||||||
history,
|
|
||||||
}) => {
|
|
||||||
const { hasAccess } = useContext(AccessContext);
|
const { hasAccess } = useContext(AccessContext);
|
||||||
|
const { addons, providers, refetchAddons } = useAddons();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (addons.length === 0) {
|
if (addons.length === 0) {
|
||||||
fetchAddons();
|
refetchAddons();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [addons.length]);
|
}, [addons.length]);
|
||||||
@ -96,9 +93,7 @@ const AddonList = ({
|
|||||||
show={
|
show={
|
||||||
<ConfiguredAddons
|
<ConfiguredAddons
|
||||||
addons={addons}
|
addons={addons}
|
||||||
toggleAddon={toggleAddon}
|
|
||||||
hasAccess={hasAccess}
|
hasAccess={hasAccess}
|
||||||
removeAddon={removeAddon}
|
|
||||||
getIcon={getIcon}
|
getIcon={getIcon}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@ -115,13 +110,4 @@ const AddonList = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
AddonList.propTypes = {
|
|
||||||
addons: PropTypes.array.isRequired,
|
|
||||||
providers: PropTypes.array.isRequired,
|
|
||||||
fetchAddons: PropTypes.func.isRequired,
|
|
||||||
removeAddon: PropTypes.func.isRequired,
|
|
||||||
toggleAddon: PropTypes.func.isRequired,
|
|
||||||
history: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddonList;
|
export default AddonList;
|
||||||
|
@ -13,6 +13,7 @@ import { CREATE_ADDON } from '../../../providers/AccessProvider/permissions';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const AvailableAddons = ({ providers, getIcon, hasAccess, history }) => {
|
const AvailableAddons = ({ providers, getIcon, hasAccess, history }) => {
|
||||||
|
|
||||||
const renderProvider = provider => (
|
const renderProvider = provider => (
|
||||||
<ListItem key={provider.name}>
|
<ListItem key={provider.name}>
|
||||||
<ListItemAvatar>{getIcon(provider.name)}</ListItemAvatar>
|
<ListItemAvatar>{getIcon(provider.name)}</ListItemAvatar>
|
||||||
|
@ -17,15 +17,47 @@ import {
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import PageContent from '../../../common/PageContent/PageContent';
|
import PageContent from '../../../common/PageContent/PageContent';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import useAddons from '../../../../hooks/api/getters/useAddons/useAddons';
|
||||||
|
import useToast from '../../../../hooks/useToast';
|
||||||
|
import useAddonsApi from '../../../../hooks/api/actions/useAddonsApi/useAddonsApi';
|
||||||
|
|
||||||
|
const ConfiguredAddons = ({ addons, hasAccess, getIcon }) => {
|
||||||
|
const { refetchAddons } = useAddons();
|
||||||
|
const { updateAddon, removeAddon } = useAddonsApi();
|
||||||
|
const { setToastData, setToastApiError } = useToast();
|
||||||
|
|
||||||
|
const toggleAddon = async addon => {
|
||||||
|
try {
|
||||||
|
await updateAddon({ ...addon, enabled: !addon.enabled });
|
||||||
|
refetchAddons();
|
||||||
|
setToastData({
|
||||||
|
type: 'success',
|
||||||
|
title: 'Success',
|
||||||
|
text: 'Addon state switched successfully',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setToastApiError(e.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRemoveAddon = addon => async () => {
|
||||||
|
try {
|
||||||
|
await removeAddon(addon.id);
|
||||||
|
refetchAddons();
|
||||||
|
setToastData({
|
||||||
|
type: 'success',
|
||||||
|
title: 'Success',
|
||||||
|
text: 'Deleted addon successfully',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setToastData({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Error',
|
||||||
|
text: 'Can not delete addon',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const ConfiguredAddons = ({
|
|
||||||
addons,
|
|
||||||
hasAccess,
|
|
||||||
removeAddon,
|
|
||||||
getIcon,
|
|
||||||
toggleAddon,
|
|
||||||
}) => {
|
|
||||||
const onRemoveAddon = addon => () => removeAddon(addon);
|
|
||||||
const renderAddon = addon => (
|
const renderAddon = addon => (
|
||||||
<ListItem key={addon.id}>
|
<ListItem key={addon.id}>
|
||||||
<ListItemAvatar>{getIcon(addon.provider)}</ListItemAvatar>
|
<ListItemAvatar>{getIcon(addon.provider)}</ListItemAvatar>
|
||||||
@ -95,7 +127,6 @@ const ConfiguredAddons = ({
|
|||||||
ConfiguredAddons.propTypes = {
|
ConfiguredAddons.propTypes = {
|
||||||
addons: PropTypes.array.isRequired,
|
addons: PropTypes.array.isRequired,
|
||||||
hasAccess: PropTypes.func.isRequired,
|
hasAccess: PropTypes.func.isRequired,
|
||||||
removeAddon: PropTypes.func.isRequired,
|
|
||||||
toggleAddon: PropTypes.func.isRequired,
|
toggleAddon: PropTypes.func.isRequired,
|
||||||
getIcon: PropTypes.func.isRequired,
|
getIcon: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -10,15 +10,15 @@ import cloneDeep from 'lodash.clonedeep';
|
|||||||
|
|
||||||
import styles from './form-addon-component.module.scss';
|
import styles from './form-addon-component.module.scss';
|
||||||
import PageContent from '../common/PageContent/PageContent';
|
import PageContent from '../common/PageContent/PageContent';
|
||||||
|
import useAddonsApi from '../../hooks/api/actions/useAddonsApi/useAddonsApi';
|
||||||
|
import useToast from '../../hooks/useToast';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
|
const AddonFormComponent = ({ editMode, provider, addon, fetch }) => {
|
||||||
|
const { createAddon, updateAddon } = useAddonsApi();
|
||||||
|
const { setToastData, setToastApiError } = useToast();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
const AddonFormComponent = ({
|
|
||||||
editMode,
|
|
||||||
provider,
|
|
||||||
addon,
|
|
||||||
fetch,
|
|
||||||
cancel,
|
|
||||||
submit,
|
|
||||||
}) => {
|
|
||||||
const [config, setConfig] = useState(addon);
|
const [config, setConfig] = useState(addon);
|
||||||
const [errors, setErrors] = useState({
|
const [errors, setErrors] = useState({
|
||||||
parameters: {},
|
parameters: {},
|
||||||
@ -73,6 +73,10 @@ const AddonFormComponent = ({
|
|||||||
setErrors({ ...errors, events: undefined });
|
setErrors({ ...errors, events: undefined });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
history.goBack();
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = async evt => {
|
const onSubmit = async evt => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
if (!provider) return;
|
if (!provider) return;
|
||||||
@ -100,8 +104,25 @@ const AddonFormComponent = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await submit(config);
|
if (editMode) {
|
||||||
|
await updateAddon(config);
|
||||||
|
history.push('/addons');
|
||||||
|
setToastData({
|
||||||
|
type: 'success',
|
||||||
|
title: 'Addon updated successfully',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await createAddon(config);
|
||||||
|
history.push('/addons');
|
||||||
|
setToastData({
|
||||||
|
type: 'success',
|
||||||
|
title: 'Addon created successfully',
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
setToastApiError({
|
||||||
|
text: e.toString(),
|
||||||
|
});
|
||||||
setErrors({ parameters: {}, general: e.message });
|
setErrors({ parameters: {}, general: e.message });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -175,7 +196,7 @@ const AddonFormComponent = ({
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
<section className={styles.formSection}>
|
<section className={styles.formSection}>
|
||||||
<FormButtons submitText={submitText} onCancel={cancel} />
|
<FormButtons submitText={submitText} onCancel={handleCancel} />
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
|
65
frontend/src/hooks/api/actions/useAddonsApi/useAddonsApi.ts
Normal file
65
frontend/src/hooks/api/actions/useAddonsApi/useAddonsApi.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { IAddons } from '../../../../interfaces/addons';
|
||||||
|
import useAPI from '../useApi/useApi';
|
||||||
|
|
||||||
|
const useAddonsApi = () => {
|
||||||
|
const { makeRequest, createRequest, errors, loading } = useAPI({
|
||||||
|
propagateErrors: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const URI = 'api/admin/addons';
|
||||||
|
|
||||||
|
const createAddon = async (addonConfig: IAddons) => {
|
||||||
|
const path = URI;
|
||||||
|
const req = createRequest(path, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(addonConfig),
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await makeRequest(req.caller, req.id);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeAddon = async (id: number) => {
|
||||||
|
const path = `${URI}/${id}`;
|
||||||
|
const req = createRequest(path, {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const res = await makeRequest(req.caller, req.id);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAddon = async (addonConfig: IAddons) => {
|
||||||
|
const path = `${URI}/${addonConfig.id}`;
|
||||||
|
const req = createRequest(path, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify(addonConfig),
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const res = await makeRequest(req.caller, req.id);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
createAddon,
|
||||||
|
updateAddon,
|
||||||
|
removeAddon,
|
||||||
|
errors,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useAddonsApi;
|
37
frontend/src/hooks/api/getters/useAddons/useAddons.ts
Normal file
37
frontend/src/hooks/api/getters/useAddons/useAddons.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import useSWR, { mutate, SWRConfiguration } from 'swr';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { formatApiPath } from '../../../../utils/format-path';
|
||||||
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
|
|
||||||
|
const useAddons = (options: SWRConfiguration = {}) => {
|
||||||
|
const fetcher = async () => {
|
||||||
|
const path = formatApiPath(`api/admin/addons`);
|
||||||
|
const res = await fetch(path, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(handleErrorResponses('Addons'));
|
||||||
|
return res.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
const KEY = `api/admin/addons`;
|
||||||
|
|
||||||
|
const { data, error } = useSWR(KEY, fetcher, options);
|
||||||
|
const [loading, setLoading] = useState(!error && !data);
|
||||||
|
|
||||||
|
const refetchAddons = () => {
|
||||||
|
mutate(KEY);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(!error && !data);
|
||||||
|
}, [data, error]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
addons: data?.addons || [],
|
||||||
|
providers: data?.providers || [],
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
refetchAddons,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useAddons;
|
8
frontend/src/interfaces/addons.ts
Normal file
8
frontend/src/interfaces/addons.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface IAddons {
|
||||||
|
id: number;
|
||||||
|
provider: string;
|
||||||
|
description: string;
|
||||||
|
enabled: boolean;
|
||||||
|
events: string[];
|
||||||
|
parameters: object;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user